/* Konto-specific styles (shared design system provided by bubbla-style gem) */

/* The theme toggle in the header looks like the header links next to it
   (bubbla-style typography.css "Links": bold, pink underline, darker pink on
   hover), not like a button. */
.main-menu .theme-toggle {
    appearance: none;
    background: none;
    border: none;
    border-radius: 0;
    margin: 0;
    padding: 0;
    cursor: pointer;
    font: inherit;
    font-weight: 700;
    color: var(--black);
    text-decoration-line: underline;
    text-decoration-skip-ink: none;
    text-decoration-thickness: 2px;
    text-decoration-color: var(--alt-pink);

    &:hover {
        text-decoration-color: var(--pink);
    }
}

/* The theme toggle holds both icons (layouts/application): the sun in the
   dark theme, the moon in the light one, chosen by html[data-theme], which
   theme_controller.js sets. */
.theme-toggle .theme-icon-moon,
html[data-theme="light"] .theme-toggle .theme-icon-sun {
    display: none;
}

html[data-theme="light"] .theme-toggle .theme-icon-moon {
    display: inline;
}

/* The header link of the page shown (aria-current, "Översikt" on the
   dashboard) keeps the hover underline. */
.main-menu .menu a[aria-current="page"] {
    text-decoration-color: var(--pink);
}

/* Flash messages (shared/_flash): overlays directly under the header's main
   menu, over the space above the page content, centered and outside the page
   flow (.flash-anchor has no height). A notice is green, an alert red (pink in
   the dark theme, where the gem's --red is grey). */
:root {
    --flash-alert-accent: var(--red);
}

[data-theme="dark"] {
    --flash-alert-accent: var(--pink);
}

.flash-anchor {
    position: relative;
    height: 0;
}

.flash-overlay {
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1000;
    width: max-content;
    max-width: min(36rem, calc(100vw - 2rem));
    display: flex;
    flex-direction: column;
    gap: var(--golden-xxs);
    pointer-events: none;
}

.flash-overlay:empty {
    display: none;
}

.flash-overlay .flash {
    pointer-events: auto;
    display: flex;
    align-items: flex-start;
    gap: var(--golden-s);
    padding: var(--golden-xs) var(--golden-xs) var(--golden-xs) var(--golden-s);
    border: 1px solid var(--alt-gray);
    border-left-width: var(--golden-xxs);
    border-radius: var(--golden-xxs);
    background: var(--white);
    color: var(--black);
    box-shadow: 0 var(--golden-xxs) var(--golden-m) rgba(0, 0, 0, 0.25);
    overflow-wrap: anywhere;
}

.flash-overlay .flash.notice {
    background: var(--bg-green);
    border-color: var(--alt-green);
    border-left-color: var(--green);
}

.flash-overlay .flash.alert {
    background: var(--bg-red);
    border-color: var(--alt-red);
    border-left-color: var(--flash-alert-accent);
}

.flash-overlay .flash-message {
    flex: 1;
    margin: 0;
    font-size: var(--font-size-s);
    line-height: 1.4;
}

.flash-overlay .flash-close {
    flex: none;
    appearance: none;
    background: none;
    border: none;
    margin: 0;
    padding: 0 var(--golden-xxs);
    cursor: pointer;
    font: inherit;
    font-size: var(--font-size-m);
    line-height: 1.1;
    color: var(--gray);

    &:hover,
    &:focus-visible {
        color: var(--black);
    }
}

@media (max-width: 48rem) {
    .flash-overlay {
        left: 1rem;
        right: 1rem;
        transform: none;
        width: auto;
        max-width: none;
    }
}

/* Dark mode: buttons need white text on pink backgrounds */
[data-theme="dark"] .black-button,
[data-theme="dark"] a.black-button,
[data-theme="dark"] input.black-button {
    color: rgb(240, 240, 240);
}

[data-theme="dark"] .black-button:hover,
[data-theme="dark"] a.black-button:hover,
[data-theme="dark"] input.black-button:hover {
    color: rgb(255, 255, 255);
}

/* Logo, following news.bubb.la's .bubbla-logo (bubbla-style 0.3.2, bubbla/style/logo.css): in the light theme the logo sits on the page background with a faint shadow; in the dark theme it sits on a grey disc with drop shadow and inset shading. */
.main-menu .menu ul li.logo {
    margin-top: calc(var(--golden-xs) * 2);
}

.main-menu li.logo a {
    position: relative;
    display: block;
    width: var(--golden-xl);
    height: var(--golden-xl);
    border-radius: 50%;
    box-shadow: 0 1px 4px rgba(0,0,0,0.15);
}

.main-menu li.logo a::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    pointer-events: none;
}

[data-theme="dark"] .main-menu li.logo a {
    background: rgb(128, 128, 128);
    box-shadow: 0 2px 6px rgba(0,0,0,0.5);
}

[data-theme="dark"] .main-menu li.logo a::after {
    box-shadow:
        inset 0 -4px 8px rgba(0,0,0,0.4),
        inset 0 3px 6px rgba(255,255,255,0.15);
}

.main-menu .menu ul li.logo img {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 50%;
}

/* The session column (theme toggle, sign-out link) keeps "Logga ut" on one
   line. At phone width the gem's three-column header (1fr, max-content,
   1fr) squeezes the session column until the link breaks; there the
   columns are the logo, the links (wrapping onto a second line when they
   do not fit) and the session column at its own width. */
.main-menu .session {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--golden-xs);
}

.main-menu .session a {
    white-space: nowrap;
}

@media (max-width: 48rem) {
    .main-menu {
        grid-template-columns: auto minmax(0, 1fr) auto;
        margin-inline: var(--golden-xs) var(--golden-s);
    }

    .main-menu .menu ul {
        flex-wrap: wrap;
    }

    .main-menu .menu ul li {
        margin: var(--golden-xxs) var(--golden-xs);
    }

    .main-menu .menu ul li.logo {
        margin-top: var(--golden-xs);
    }
}

.radio-label {
    display: block;
}

/* Secondary button: a neutral variant in the pattern of bubbla-style's
   bubbla/style/button-colors.css (use with black-button), which has no
   secondary style. */
button.gray-button,
a.gray-button,
input.gray-button {
    background-color: var(--alt-gray);
    color: var(--black);

    &:hover {
        background-color: var(--gray);
        color: var(--white);
    }
}

/* ------------------------------------------------------------------
   Page layout (docs/archive/plans/page-layout-cards.md, "The layout").
   Headings on the page background, one .card per thing, content stacked.
   Class names are konto's own: neither the vendored bubbla-style 0.1.0
   nor bubbla-style 0.3.2 defines them, so a gem upgrade cannot restyle
   them. Tokens only, no literal colors.

   <div class="page-heading">            h1 (+ actions, + lead <p>)
   <section class="page-section">        one group of cards
     <h2 class="page-section-heading">
     <div class="card stacked-card">     h3, <p>s, then form or action
       <form class="stacked-form">       fields, then the submit button
     <div class="button-row">            buttons and links side by side
     <div class="item-grid">             equal items side by side
     <div class="table-scroll">          a table wider than its card
   <main class="narrow-main">            a one-form page in one narrow column
                                         (content_for :main_class)
   <main class="wide-main">              every admin page
   <section class="page-section form-width">  a form on a wide page
   ------------------------------------------------------------------ */

/* The page heading row on the background: the h1 on the left, page-level
   actions (links, buttons) beside it, wrapping under it on a phone. A
   <p> in it is a lead line under the heading, on its own row. */
.page-heading {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--golden-s) var(--golden-m);
    margin-bottom: var(--golden-xl);
}

.page-heading h1 {
    margin: 0;
    font-size: var(--font-size-xl);
    line-height: 1.2;
    overflow-wrap: anywhere;
}

.page-heading p {
    flex-basis: 100%;
    margin: 0;
}

.page-heading .black-button {
    margin: 0;
}

/* Phone width: a side gutter, so cards do not touch the screen edges (the
   gem's main has a max width but no padding). */
@media (max-width: 48rem) {
    main {
        padding-inline: var(--golden-s);
    }
}

/* A group of cards under an h2 on the background. Cards in one section
   are --golden-m apart; sections are --golden-xxl apart. */
.page-section {
    display: flex;
    flex-direction: column;
    gap: var(--golden-m);
}

.page-section + .page-section {
    margin-top: var(--golden-xxl);
}

.page-section:last-child {
    margin-bottom: var(--golden-xxl);
}

.page-section > .card,
.page-section > .item-grid,
.page-section > .table-scroll {
    margin: 0;
}

.page-section-heading {
    margin: 0;
}

/* Inside a card: heading, description, form or action, one under the
   other at full width, with paragraph spacing (the gem's .card p sets
   margin 0) and no rules between them. Buttons, links and status labels
   keep their own width and sit on the left. Flex gap ignores hidden
   children (hidden inputs, display: none), so the spacing stays even.
   .stack is the same arrangement for a group that is not itself a card:
   a partial rendered inside a card (subscriptions/_table), or a Stimulus
   controller's element inside one. */
.stacked-card,
.stack {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--golden-m);
}

.stacked-card > *,
.stack > * {
    margin: 0;
}

/* The card heading is one step below the section heading (the gem gives
   h1, h2 and h3 the same size). */
.stacked-card > h3,
.stack > h3 {
    margin: 0;
    font-size: var(--font-size-m);
    line-height: var(--font-size-l);
}

/* Icons (Lucide through rails_icons; IconHelper#icon, docs/PAGE_LAYOUT.md
   "Icons"). An icon is an inline SVG with class .icon, sized to the text
   and drawn in the text colour, so the light and dark themes need no rules
   of their own. A card, section or form error heading whose first element
   is an icon sets it beside the text as a flex row, and so does a line of
   text that begins with an icon (.icon-line, e.g. the activity on /account),
   so text that wraps stays beside the icon, not under it. The icon is
   centred on the first line (1lh is the line height), not on the whole
   row, so it stays level with the first line when the text wraps. Text
   that holds markup (a link) is wrapped in one <span>, so it stays one flex
   item. */
.icon {
    width: 1.1em;
    height: 1.1em;
    flex-shrink: 0;
    fill: none;
    stroke: currentColor;
    vertical-align: -0.15em;
}

.card > h3:has(> svg.icon:first-child),
.page-section-heading:has(> svg.icon:first-child),
.form-errors > h3:has(> svg.icon:first-child),
.icon-line {
    display: flex;
    align-items: flex-start;
    gap: var(--golden-xs);
}

.card > h3 > svg.icon:first-child,
.page-section-heading > svg.icon:first-child,
.form-errors > h3 > svg.icon:first-child,
.icon-line > svg.icon:first-child {
    margin-block-start: calc((1lh - 1.1em) / 2);
}

.stacked-card > a,
.stacked-card > button,
.stacked-card > input[type="submit"],
.stacked-card > .black-button,
.stacked-card > .status-badge,
.stack > a,
.stack > button,
.stack > .black-button {
    align-self: flex-start;
    margin: 0;
}

/* A card that needs the member's action now (subscriptions/_lapsed_notice:
   an unpaid subscription), at the top of the page: the gem's warning
   tokens, which the dark theme redefines, as a heavy border with a thicker
   left edge, and a larger heading. */
.card.warning-card {
    border: 2px solid var(--orange);
    border-left-width: var(--golden-xs);
}

.warning-card > h3 {
    font-size: var(--font-size-l);
    line-height: 1.3;
}

/* A status line under a button whose request takes a while (the card form,
   the 3D Secure confirmation, "Betala nu"): empty and hidden until the
   Stimulus controller fills it, then a spinner before the text. Not in a
   table cell ("Betala nu" in subscriptions/_table), where the sentence
   would widen the table; the button's own text says "Betalar …". */
.progress-status:empty,
td .progress-status {
    display: none;
}

.progress-status {
    display: flex;
    align-items: center;
    gap: var(--golden-xs);
    margin: var(--golden-xs) 0 0;
    font-weight: 700;
}

.progress-status::before {
    content: "";
    flex: none;
    width: 1em;
    height: 1em;
    border: 3px solid var(--alt-gray);
    border-top-color: var(--pink);
    border-radius: 50%;
    animation: progress-spin 0.8s linear infinite;
}

@keyframes progress-spin {
    to {
        transform: rotate(360deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    .progress-status::before {
        animation-duration: 3s;
    }
}

/* A group of paragraphs inside a stacked card or form keeps normal
   paragraph spacing. Two paragraphs that are both direct children of the
   flex column are already --golden-m apart through its gap; the negative
   margin brings them back to --golden-xs, as in a wrapped group. */
.stacked-card p + p,
.stack p + p,
.stacked-form p + p {
    margin-top: var(--golden-xs);
}

.stacked-card > p + p,
.stack > p + p,
.stacked-form > p + p {
    margin-top: calc(var(--golden-xs) - var(--golden-m));
}

/* A form inside a stacked card (and button_to's form): the gem centers
   forms and caps them at --golden-line-length; here they fill the card. */
.stacked-card form {
    max-width: none;
    margin: 0;
}

/* A form: labels over full-width fields, one field under the other, the
   submit button under the fields on the left with extra space above. */
.stacked-form {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--golden-m);
    max-width: none;
    margin: 0;
}

.stacked-form > *,
.stacked-form .field,
.stacked-form p {
    margin: 0;
}

.stacked-form label {
    margin-bottom: var(--golden-xxs);
}

.stacked-form label:has(> input[type="checkbox"]),
.stacked-form label:has(> input[type="radio"]) {
    margin: 0;
    font-weight: 400;
}

.stacked-form > input[type="submit"],
.stacked-form > button,
.stacked-form > .black-button,
.stacked-form > .button-row {
    align-self: flex-start;
    margin: var(--golden-xs) 0 0;
    font-size: var(--font-size-s);
}

/* A radio button followed by its label (form.radio_button, then
   form.label) stays on one line with it. */
.stacked-form input[type="radio"] + label {
    display: inline;
    margin: 0;
    font-weight: 400;
}

/* A fieldset groups fields under its legend without a box around them
   (no rules inside a card); a list of options has no bullets. */
.stacked-form fieldset {
    margin: 0;
    padding: 0;
    border: 0;
    min-width: 0;
}

/* A legend has the typography of a card heading (.stacked-card > h3). */
.stacked-form legend {
    padding: 0;
    margin-bottom: var(--golden-xs);
    font-family: "Crete Round", serif;
    font-weight: 400;
    font-size: var(--font-size-m);
    line-height: var(--font-size-l);
}

/* A part of a form shown only when the member opens it: the summary reads
   as a link (the donation on /subscriptions/new). */
.stacked-form details > summary {
    cursor: pointer;
    text-decoration-line: underline;
    text-decoration-skip-ink: none;
    text-decoration-thickness: 2px;
    text-decoration-color: var(--alt-pink);
    font-weight: 700;
    color: var(--black);
}

.stacked-form details > summary:hover {
    text-decoration-color: var(--pink);
}

.stacked-form details[open] > summary {
    margin-bottom: var(--golden-xs);
}

/* /subscriptions/new: one text size, the paragraph size, for everything
   but the headings (the legends, a step larger) and the buttons; labels
   are not bold. */
.subscription-form .stacked-form legend {
    font-size: var(--font-size-l);
}

.subscription-form .stacked-form label,
.subscription-form .stacked-form summary,
.subscription-form .stacked-form small,
.subscription-form .stacked-form a:not(.black-button),
.subscription-form .stacked-form input.amount-field {
    font-size: var(--font-size-m);
    line-height: var(--font-size-l);
}

.subscription-form .stacked-form label {
    font-weight: 400;
}

/* A field for an amount of money, a few digits wide. */
.stacked-form input.amount-field {
    display: block;
    width: 8em;
    max-width: 100%;
}

.stacked-form fieldset ul {
    margin: 0;
    padding: 0;
    list-style: none;
}

.stacked-form fieldset li + li {
    margin-top: var(--golden-xs);
}

/* A list read as prose, with bullets: bubbla-style's typography.css takes
   bullets and padding off every ul. */
.bullet-list {
    list-style-type: disc;
    padding-left: 1.5em;
}

/* Buttons and links side by side, wrapping on a phone, on the left. Holds
   black-buttons, button_to forms and links wrapping a button. */
.button-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--golden-s);
    margin: 0;
    text-align: left;
}

.button-row > *,
.button-row form,
.button-row .black-button {
    margin: 0;
}

.page-heading .button-row {
    flex-basis: auto;
}

/* Equal items side by side (plan cards, stat tiles): as many columns of
   at least --item-grid-min as fit, one column at phone width. */
.item-grid {
    --item-grid-min: 14rem;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--item-grid-min)), 1fr));
    gap: var(--golden-m);
}

.item-grid > * {
    margin: 0;
}

/* The profile page (accounts/show): the avatar, or the initials in a disc
   when there is none, and the three figures. */
.avatar-image,
.avatar-placeholder {
    width: calc(var(--golden-xxl) * 2);
    height: calc(var(--golden-xxl) * 2);
    border-radius: 50%;
}

.avatar-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--bg-pink);
    font-size: var(--font-size-l);
    font-weight: 700;
}

.profile-stats .item-grid {
    --item-grid-min: 9rem;
}

/* A table wider than its card scrolls inside the card, not the page. The
   card's gap spaces it; the gem's table margin is not needed. */
.table-scroll {
    max-width: 100%;
    overflow-x: auto;
}

.table-scroll > table {
    margin: 0;
}

/* A one-form page (payment_methods/new) in one narrow column: the view
   sets content_for :main_class, "narrow-main" and layouts/application adds
   it to <main>. The width is the golden section of bubbla-style's
   --golden-line-length (46.35rem / φ = 28.65rem, about 458px). In a
   container this narrow the Stripe Payment Element stacks its card fields
   in three rows: card number; expiry date and CVC; country. Wider (tested:
   690px; 560px still stacks) it puts card number, expiry and CVC on one
   row and the country dropdown alone under them. */
main.narrow-main {
    max-width: calc(var(--golden-line-length) / var(--golden-ratio));
}

/* Every admin page (content_for :main_class, "wide-main"): the main column
   is --golden-line-length times φ (about 1200px), so an eight-column table
   fits beside its card edges on a desktop instead of scrolling; narrower
   screens still scroll it inside .table-scroll. All admin pages use it, so
   the admin menu stays in one place from page to page. */
main.wide-main {
    max-width: calc(var(--golden-line-length) * var(--golden-ratio));
}

/* A section holding one form on a wide page (the admin new/edit pages):
   the form card keeps the normal --golden-line-length width, on the left
   under the page heading. */
.page-section.form-width {
    max-width: var(--golden-line-length);
}

/* Room for the Payment Element before Stripe.js has mounted it. */
[data-setup-intent-target="paymentElement"] {
    min-height: 100px;
}

/* A status label (a session's "current", a card's "default"): room around
   its text for the background color the view gives it. */
.status-badge {
    display: inline-block;
    padding: var(--golden-xxs) var(--golden-xs);
    border-radius: var(--golden-xxs);
}

/* A money amount or a date in a table cell stays on one line; a table
   that then gets wider than its card scrolls inside .table-scroll. */
td.table-nowrap {
    white-space: nowrap;
}

/* A form in a table cell (subscriptions/_actions' button_to, the billing
   method select on /admin/users/:id/edit) sits on the line with the links
   and buttons next to it. */
td form {
    display: inline-block;
    max-width: none;
    margin: 0;
}

/* Plan cards on /plans: the subscribe button, or the current-plan label,
   at the bottom of each card, so the buttons line up across the row. */
.item-grid > .stacked-card > :last-child {
    margin-top: auto;
}

/* ------------------------------------------------------------------
   Admin pages (app/views/admin). The same layout as above; these are the
   parts only the admin pages have.
   ------------------------------------------------------------------ */

/* The admin menu (admin/shared/_nav) above the page heading. The current
   page's link is bold (aria-current). */
.admin-nav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--golden-s) var(--golden-l);
    margin-bottom: var(--golden-xl);
    padding-bottom: var(--golden-s);
    border-bottom: 1px solid var(--alt-gray);
}

.admin-nav a[aria-current="page"] {
    font-weight: 700;
}

/* The count after a menu link (open alerts, flagged members); the view
   gives it its background color. */
.nav-count {
    display: inline-block;
    padding: 0 var(--golden-xs);
    border-radius: 999px;
    font-size: 0.75em;
}

/* A filter form's fields and its submit button side by side, wrapping,
   one per line at phone width. A field grows; a select keeps the width
   of its options. */
.field-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--golden-s);
}

.field-row > *,
.field-row label {
    margin: 0;
}

.field-row input,
.field-row select,
.field-row > .black-button {
    margin: 0;
    font-size: var(--font-size-s);
}

.field-row > .black-button {
    align-self: stretch;
}

.field-row > input[type="text"],
.field-row > input[type="search"],
.field-row > input[type="email"] {
    flex: 1 1 12rem;
    width: auto;
}

.field-row > select,
.field-row > div > input {
    width: auto;
    max-width: 100%;
}

/* Stat tiles on /admin: equal cards in an .item-grid, the number large
   over its label. The view gives the number its color. */
/* auto-fit: a row of fewer tiles than fit stretches them across the
   card width instead of leaving empty columns on the right. */
.item-grid.stat-tiles {
    --item-grid-min: 9rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--item-grid-min)), 1fr));
}

.stat-tile {
    text-align: center;
}

.stat-tile-number {
    font-size: var(--font-size-xl);
    font-weight: 700;
    line-height: 1.2;
}

/* A small select or button in a table cell. */
select.compact-control,
button.compact-control,
input.compact-control {
    width: auto;
    padding: var(--golden-xxs) var(--golden-xs);
    font-size: var(--font-size-s);
}

/* Secondary text: a long message or payload in a table cell, a note. */
.small-text {
    font-size: 0.85rem;
}

/* A table cell cut to one line with an ellipsis (sync payloads). */
td.table-truncate {
    max-width: 12rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The correspondence checkbox button in the admin user tables
   (admin/users/_correspondence_toggle). */
button.correspondence-toggle {
    padding: 0;
    font-size: var(--font-size-m);
}

/* A long token in a card (an OAuth application's UID and secret on
   /oauth/applications/:id) breaks instead of widening the page. */
.stacked-card code {
    overflow-wrap: anywhere;
}

/* A hint under a field (admin/organizations/_form). */
.stacked-form .field-hint {
    margin-top: var(--golden-xxs);
}

/* Printing a receipt (receipts/show, the print button; docs/archive/plans/member-receipts.md,
   decision 6): the header, the main menu, the flash and anything .print-hidden
   (the receipt page's heading with its print button) are hidden, and the
   .receipt-card prints alone, black on white in either theme. */
@media print {
    .main-menu,
    .flash-anchor,
    .print-hidden {
        display: none !important;
    }

    html,
    body,
    main {
        background: rgb(255, 255, 255) !important;
    }

    .receipt-card,
    .receipt-card * {
        background: rgb(255, 255, 255) !important;
        color: rgb(0, 0, 0) !important;
        box-shadow: none !important;
    }
}
