/* ==========================================================================
   IñigoSync — Auth Modal (Login / Sign Up / Verify OTP / Admin)
   Uses the same design tokens defined in LandingPage.css :root

   Revision A2 (implementation_plan.md) — fonts/sizes/weights come from
   Style/typography.css (linked before LandingPage.css, before this file, on
   every page that loads Auth.css). Every font-size below is an --fs-* token;
   --font-mono usages were re-homed to --font-display (labels) or
   --font-numeric (OTP digits/countdowns) since Space Mono was removed.
   ========================================================================== */

.auth-overlay {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: rgba(10, 8, 6, 0.72);
    backdrop-filter: blur(6px);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.25s ease;
}

.auth-overlay[data-open] {
    opacity: 1;
    pointer-events: auto;
}

.auth-overlay[hidden] {
    display: flex; /* overridden by JS toggling [data-open]; keep flex for transition */
    opacity: 0;
    pointer-events: none;
}

body.auth-lock {
    overflow: hidden;
}

.auth-modal {
    position: relative;
    width: 100%;
    max-width: 420px;
    max-height: calc(100vh - 48px);
    overflow-y: auto;
    background: var(--color-bg-elevated);
    border: 1px solid var(--color-line);
    border-radius: 20px;
    /* Vertical padding is part of the height budget (increment 10): the
       card is driven by Sign Up step 1, which cannot shed a field, a
       password rule or a control. Side padding is deliberately unchanged
       — it sets the password checklist's track width, whose two-column
       threshold is measured in the .auth-password-rules comment below. */
    padding: 24px 36px 22px;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
    transform: translateY(16px) scale(0.98);
    opacity: 0;
    transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.22s ease;
}

.auth-overlay[data-open] .auth-modal {
    transform: translateY(0) scale(1);
    opacity: 1;
}

.auth-close {
    position: absolute;
    /* Keep the close control above the later, positioned brand header. */
    z-index: 1;
    top: 18px;
    right: 18px;
    width: 34px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(244, 239, 230, 0.06);
    border: 1px solid var(--color-line);
    border-radius: 50%;
    color: var(--color-ink-dim);
    cursor: pointer;
    transition: background 0.2s, color 0.2s;
}

.auth-close:hover {
    background: rgba(244, 239, 230, 0.12);
    color: var(--color-ink);
}

/* --------------------------------------------------------------------------
   Brand lockup — mark tile, then the wordmark with the strapline under it,
   closed off by a short accent rule that separates the header from the tabs.
   Same visual language as the landing page: Sora display face, the #FF6115
   accent, and the surface/border/shadow token set.

   Set side by side rather than stacked so the whole header stays the same
   height as the plain two-line title it replaced — the sign-up panel already
   runs to the modal's `max-height: calc(100vh - 48px)` on a 900px-tall
   viewport, so a taller header would have cost it internal scrolling.

   Everything here is scoped to .auth-brand, so the .auth-tagline reused by the
   Verify / Admin / Forgot / Reset panels keeps its existing look.
   -------------------------------------------------------------------------- */
.auth-brand {
    position: relative;
    text-align: center;
    margin-bottom: 12px;
    padding-bottom: 10px;
}

/* inline-flex inside a text-align:center parent = a centred block that is only
   as wide as its content, with the text inside it left-aligned. */
.auth-brand-lockup {
    display: inline-flex;
    align-items: center;
    gap: 12px;
    max-width: 100%;
    min-width: 0;
    text-align: left;
}

/* Tile behind the mark, so the logo sits on a surface of its own instead of
   floating on the modal panel — the same card treatment used site-wide. */
.auth-brand-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 44px;
    height: 44px;
    border-radius: 13px;
    background: var(--color-bg-card);
    border: 1px solid var(--color-line);
    box-shadow: var(--shadow-card);
}

.auth-brand-mark img {
    display: block;
    width: 28px;
    height: 28px;
    object-fit: contain;
}

.auth-brand-text {
    display: flex;
    flex-direction: column;
    /* No gap: the wordmark's own 1.15 line box already leaves the
       strapline enough air, and 2px here made the lockup 46px — 2px
       taller than the 44px mark beside it, so the text, not the mark, was
       setting the header's height. */
    gap: 0;
    min-width: 0;
}

.auth-brand .logo-text {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: var(--fs-xl);
    line-height: 1.15;
    letter-spacing: -0.01em;
    color: var(--color-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.auth-tagline {
    font-size: var(--fs-md);
    color: var(--color-ink-faint);
    margin-top: 4px;
}

/* Stronger than --color-ink-faint: this line is the strapline half of the
   lockup, not a caption. `display: block` because the brand markup uses a
   <span> here (it is inline content inside the lockup), while the Verify /
   Admin / Forgot / Reset panels use a <p>. */
.auth-brand .auth-tagline {
    display: block;
    margin-top: 0;
    font-size: var(--fs-sm);
    line-height: 1.35;
    color: var(--color-ink-dim);
}

/* Tabs */
.auth-tabs {
    display: flex;
    background: var(--color-bg-card);
    border: 1px solid var(--color-line);
    border-radius: 999px;
    padding: 4px;
    margin-bottom: 14px;
}

/* Log In / Sign Up tabs are hidden while the Admin or Verify panel is active
   (Admin login is login-only; Verify is a one-off step after Sign Up). */
.auth-tabs[hidden] {
    display: none;
}

.auth-tab {
    flex: 1;
    /* 13 + 13 + an 18px line box = a 44px control. This is the modal's
       primary switch and it was a 38px target before increment 10. */
    padding: 13px 0;
    border-radius: 999px;
    border: none;
    background: transparent;
    color: var(--color-ink-faint);
    font-family: var(--font-body);
    font-weight: 600;
    font-size: var(--fs-md);
    cursor: pointer;
    transition: background 0.2s, color 0.2s;
}

.auth-tab.is-active {
    background: var(--color-primary);
    color: #14110d;
}

/* Forms */
.auth-form {
    display: none;
    flex-direction: column;
    gap: 14px;
}

.auth-form.is-active {
    display: flex;
}

/* --------------------------------------------------------------------------
   Log In / Sign Up share one card height
   --------------------------------------------------------------------------
   Both panels are placed in the SAME grid cell, so the row is sized by the
   taller of the two and the card no longer resizes when the tabs are switched.
   A min-height would do the same thing today and silently break the next time
   a field is added; this cannot.

   Which of the two wins has now flipped twice, so read this before quoting a
   number at anyone. Increment 3 let Sign Up win at 834px, which left Log In
   under ~160px of dead space. Increment 4 turned Sign Up into a stepped flow
   (see "Sign Up — stepped flow" below) with every step shorter than this
   panel, so the row resolved to Log In's own natural height, 715px at
   1440×900. Increment 8 then merged the email and password steps onto one
   screen at the user's request, which made that step taller than this panel
   and flipped the winner back to Sign Up: 850px at 1440/1024/768, 828px at
   390. Increment 9's two-column checklist clawed back 43px (807 / 786).

   Increment 10 (G1: dropped the "Already have an account?" line from Sign
   Up step 1; G3: a general padding/gap/line-height compaction) shrank BOTH
   panels, not just Sign Up — most of what moved (modal padding, brand
   spacing, tab padding, field/button padding and line-heights) is shared
   chrome and shared field/button rules, not panel-specific markup. SIGN UP
   IS STILL THE TALLER PANEL: panel-only 411px (Log In) vs 454px (Sign Up —
   heading + step indicator + tallest step, 392px), identical at every width
   tested since neither panel's own content reflows between breakpoints, only
   the modal's outer chrome does. Current row/card height, measured on
   .auth-modal.offsetHeight, identical in both themes: **636px at
   1440/1024/768, 628px at 480/420/390/375** (390-and-narrower turns
   non-monotonic below 480 — 668 at 361, 633 at 360, 685 at 320 — from the
   password checklist's own column collapse; see the width comment on
   .auth-password-rules below). Log In's dead space is now ~43px (panel-stack
   454 vs Log In's own panel 411), not the ~135px increment 8 left it with.
   That is a measured, deliberate trade for the merged first screen, not a
   regression that crept back in. Re-measure before citing a number; do not
   assume this one stays put either.

   The inactive panel keeps its box but is visibility:hidden, which — unlike
   opacity — also removes it from the tab order and from the accessibility
   tree, so the hidden form's inputs are never focusable or announced.

   These rules must come AFTER `.auth-form.is-active` above: both selectors
   have the same specificity (0,2,0), so source order decides `display`.

   Only Log In and Sign Up live here. Verify / Admin / Forgot / Reset are
   one-off panels that still shrink-wrap their own content, which is why
   includes/auth.js sets [hidden] on this wrapper whenever one of them is
   active — the stack's hidden child still occupies its grid cell, so leaving
   the wrapper laid out would pad those panels to sign-up height.
   -------------------------------------------------------------------------- */
.auth-panel-stack {
    display: grid;
    align-items: start;
}

/* `display: grid` above is an author declaration and would beat the UA
   `[hidden] { display: none }`, exactly like .auth-overlay/.court-viewer. */
.auth-panel-stack[hidden] {
    display: none;
}

.auth-panel-stack > .auth-form {
    grid-area: 1 / 1;
    display: flex;
    visibility: hidden;
}

.auth-panel-stack > .auth-form.is-active {
    visibility: visible;
}

/* --------------------------------------------------------------------------
   Sign Up — stepped flow
   --------------------------------------------------------------------------
   The same grid-stack technique as .auth-panel-stack above, one level down:
   both steps occupy ONE grid cell, so this panel is always as tall as its
   tallest step and the card does not resize between Continue and Back. With
   display:none the panel would shrink-wrap the current step instead and the
   card would jump on every step change — reintroducing exactly the bug the
   panel stack exists to fix.

   Height contract: the card is max(Log In panel, this panel), and this panel
   is its own heading + step indicator + tallest step. Since increment 8 merged
   email and password onto one screen, the maximum has been the SIGN UP side —
   step 1 was 531px against Log In's 477px at increment 8, then 489px after
   increment 9's two-column checklist, and is **392px against Log In's 411px**
   as of increment 10's G1/G3 compaction (see the "Log In / Sign Up share one
   card height" comment above for the full history and the current row height).
   Sign Up is still the taller PANEL even though step 1 alone now measures
   under Log In's panel height, because the panel also carries its own heading
   and step indicator above the step-stack. Anything added to a step still
   grows the card on BOTH panels. Measure it rather than assuming — and measure
   .auth-modal.offsetHeight, never getBoundingClientRect(), which returns the
   transformed box while the modal is still scaling in.

   VISIBILITY IS INHERITED — which is why the "make the active step visible"
   rule is scoped to `.auth-form.is-active`. Written unscoped, a
   `visibility: visible` here would override the visibility:hidden that the
   panel stack puts on the whole Sign Up panel while Log In is showing, and the
   active step would render straight through the Log In form on top of it.
   -------------------------------------------------------------------------- */
.auth-steps-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: -6px;
}

.auth-step-indicator {
    /* Revision A2 (implementation_plan.md) — "Step N of 3" reads as a label
       (uppercase, letter-spaced), so it takes --font-display. */
    font-family: var(--font-display);
    font-size: var(--fs-xs);
    line-height: 1.25;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-ink-faint);
}

/* Decorative duplicate of the "Step N of 3" text beside it — aria-hidden in
   the markup, so it carries no information of its own. */
.auth-step-dots {
    display: flex;
    align-items: center;
    gap: 5px;
}

.auth-step-dot {
    width: 20px;
    height: 4px;
    border-radius: 999px;
    background: var(--color-line);
    transition: background 0.2s ease;
}

.auth-step-dot.is-done {
    background: var(--color-primary-dim);
}

.auth-step-dot.is-current {
    background: var(--color-primary);
}

.auth-step-stack {
    display: grid;
    align-items: start;
}

.auth-step {
    grid-area: 1 / 1;
    display: flex;
    flex-direction: column;
    gap: 14px;
    visibility: hidden;
}

.auth-form.is-active .auth-step.is-active {
    visibility: visible;
}

/* Inline, per-step validation message. Deliberately NOT toggled with
   visibility (that would leak through the hidden panel exactly as described
   above) and NOT with [hidden] (which would make the step shorter while the
   message is absent and taller once it appears). It is always in flow with a
   reserved line's worth of height, and empty text when there is nothing to
   say. */
.auth-step-error {
    margin-top: -8px;
    /* Must be >= the line box this reserves. Originally tuned against a
       0.78rem font-size (0.78 x 1.35 = 16.85px); Revision A2
       (implementation_plan.md) maps that to --fs-xs (0.75rem), whose line
       box (0.75 x 1.35 = 16.2px) is smaller, not larger, so the existing
       1.06rem (16.96px) reservation below still safely bounds it (more
       slack than before, never less) — filling the line in still can't tip
       .auth-modal.offsetHeight the way an under-sized reservation would. */
    min-height: 1.06rem;
    font-size: var(--fs-xs);
    line-height: 1.35;
    /* Not --color-alert: that measures 4.20:1 on the dark modal surface.
       See the token's comment in LandingPage.css. */
    color: var(--color-alert-text);
}

.auth-step-nav {
    display: flex;
    align-items: stretch;
    gap: 10px;
}

/* .auth-submit is width:100% on its own; inside the nav row the row owns
   the width and the button just takes what is left beside Back. */
.auth-step-nav .auth-submit {
    flex: 1 1 auto;
    width: auto;
}

.auth-back {
    flex: 0 0 auto;
    padding: 13px 20px;
    border: 1px solid var(--color-line);
    border-radius: 999px;
    background: var(--color-bg-card);
    color: var(--color-ink-dim);
    font-family: var(--font-body);
    font-weight: 600;
    font-size: var(--fs-base);
    cursor: pointer;
    transition: background 0.2s, color 0.2s, border-color 0.2s;
}

.auth-back:hover {
    color: var(--color-ink);
    border-color: var(--color-primary);
}

.auth-back:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   Password requirement checklist (creation time only — Sign Up step 1 and the
   Reset Password panel; never Log In).

   The tick is coloured text on a filled pill rather than a colour swap on the
   label, so "met" is signalled by shape AND fill as well as by hue, and the
   label text keeps a token that clears AA in both themes. Each row also
   carries a visually hidden "met" / "not met" for screen readers, kept in sync
   by includes/auth.js.
   -------------------------------------------------------------------------- */
.auth-password-group {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.auth-password-rules {
    display: grid;
    /* TWO COLUMNS, sized by the list rather than by the viewport. A track has
       to hold the longest short rule on one line — "1 uppercase letter",
       104.97px in Inter (98.98 in the 'Segoe UI' fallback) — plus the 15px
       marker and its 8px gap = 127.97px, so 132px is the narrowest track that
       cannot wrap, whatever the font resolves to. auto-fit then drops to one
       column by itself wherever two of those plus the 14px column gap do not
       fit, with no new breakpoint: the deciding width is THIS LIST's, and the
       modal caps at 420px while shedding side padding at 480px and again at
       360px, so the same viewport can leave the list anywhere from 238px to
       374px wide. Measured list widths: 346px at 1440/1024/768, 374 at 480,
       326 at 420, 296 at 390, 281 at 375, 267 at 361, 278 at 360, 238 at 320.
       Swept a pixel at a time, two columns hold from 1440 down to 372 (list
       278 = exactly 2x132 + 14) and again at 360, where the existing 360px
       padding rule widens the list by 11px just as the viewport narrows; one
       column at 361-371 and at 359 and below. Three columns are unreachable:
       a third track needs 3x132 + 2x14 = 424px against a 374px maximum. */
    grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
    gap: 4px 14px;
    margin: 0;
    padding: 0;
    list-style: none;
}

/* The special-character rule is 177px of text, half again as wide as any
   other row, so it takes a row of its own instead of wrapping to two lines in
   a 166px track — wrapping it is why increment 8's two-column trial measured
   only ~28px of savings. Scoped to an ODD last child so it only spans when it
   would otherwise sit alone in a half-empty row; an even number of rules
   leaves a clean pair of columns. Collapses to a no-op in the one-column
   fallback, where 1 / -1 is the only column. */
.auth-password-rule:nth-child(odd):last-child {
    grid-column: 1 / -1;
}

.auth-password-rule {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: var(--fs-xs);
    line-height: 1.3;
    color: var(--color-ink-faint);
}

.auth-password-rule.is-met {
    color: var(--color-ink-dim);
}

.auth-password-rule-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 15px;
    height: 15px;
    /* currentColor (inherited from the row's label ink), not --color-line:
       the hairline token is ~1.5:1 on the dark modal surface, which left the
       unmet marker all but invisible. This keeps the outline at the label's
       own contrast (5.7:1 dark / 4.6:1 light) without making it shout. */
    border: 1px solid currentColor;
    border-radius: 50%;
    font-size: var(--fs-xs);
    font-weight: 700;
    line-height: 1;
    transition: background 0.2s ease, border-color 0.2s ease;
}

/* The tick is always in the box so the row never changes size; only its
   colour turns on. Colouring the glyph rather than showing/hiding it keeps
   every visibility/display toggle out of the step containers, where an
   inherited `visibility: visible` would punch through the hidden panel. */
.auth-password-rule-mark::before {
    content: '\2713';
    color: transparent;
    transition: color 0.2s ease;
}

.auth-password-rule.is-met .auth-password-rule-mark {
    background: var(--color-court-green);
    border-color: var(--color-court-green);
}

.auth-password-rule.is-met .auth-password-rule-mark::before {
    color: #ffffff;
}

@media (prefers-reduced-motion: reduce) {
    .auth-step-dot,
    .auth-back,
    .auth-password-rule-mark,
    .auth-password-rule-mark::before {
        transition: none;
    }
}

.auth-heading {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: var(--fs-xl);
    /* Tight, like every other display-face heading on the site. The
       inherited body line-height (--lh-normal, 1.55) would wrap this
       22px single-line heading in a ~34px line box — too loose for a
       heading — so it is explicitly tightened here instead. */
    line-height: 1.2;
}

.auth-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/* --------------------------------------------------------------------------
   Name row — Surname / First name / Middle name, side by side (increment 11)
   --------------------------------------------------------------------------
   Signup step 2 collects the name in three boxes laid out horizontally, in
   one row of three equal tracks. Horizontal is not only the ask, it is also
   what keeps the card's height where increment 10 left it: each extra ROW of
   name fields costs 75px (measured), and step 2 has only 130px of headroom
   before it overtakes step 1 (392px) and starts driving the shared card, so
   three stacked fields (+150px) would. Laid out in one row the step costs
   nothing at all: 262px before this change, 262px after.

   The track floor is measured, not guessed, exactly as .auth-password-rules'
   is below: it must be >= "MIDDLE NAME" — the longest of the three labels —
   rendered width, so wherever three columns render, they are wrap-proof by
   construction rather than by measurement.

   Revision A2 (implementation_plan.md) update: .auth-label moved from
   Space Mono (85.16px measured for "MIDDLE NAME" at 0.72rem/0.06em, 86px
   floor) to --font-display (Sora) — Space Mono no longer loads at all, so
   the old measurement is gone regardless of which remaining token this rule
   picks. Re-measured live (real Chrome, the actual Google Fonts Sora file,
   document.fonts.ready awaited): "MIDDLE NAME" at 0.72rem/0.06em/uppercase
   renders at 92.39px at the effective weight (Sora only ships 500/600/700 —
   a requested 400 resolves to 500 — confirmed both compute to the same
   92.39px) and 93.44px if something ever forces weight 700 on it. The floor
   below is now 96px (>= the 700-weight worst case, ~3.6px of margin —
   proportionally similar to the old ~0.84px margin on a monospace face,
   sized up because a proportional face's cross-platform text-rendering
   variance is larger than a monospace one's). Re-verified with a live sweep
   of row widths 238–500px (2px steps) plus this comment's own named row
   widths below: zero wrapped labels anywhere.

   auto-fit off the ROW's own width, not a viewport media query, for the same
   reason the checklist uses it: the modal sheds side padding at 480px and
   again at 360px, so one viewport width does not map to one row width (346px
   at 1440/1024/768, 374 at 480, 296 at 390, 281 at 375, 278 at 360, 267 at
   361, 238 at 320). Pre-A2 (86px floor, Space Mono), a swept-pixel check
   from 320 to 480 found three columns at ≥372 and again at exactly 360, two
   columns at 361–371 and at ≤359 — a non-monotone band caused by the ≤360
   padding drop widening the row by 11px right where the viewport narrows.
   Post-A2 (96px floor, Sora — a wider face needs a wider floor, so the
   crossover point moves): 346/374 (1440/1024/768/480) still render three
   columns; 296/281/267/238 (390/375/361/320) still render two. The one cell
   that changes is exactly 278 (viewport 360): it now renders two columns
   instead of three, because 86px was only ever "just barely" enough for
   Space Mono at that one exact row width, and 96px no longer fits three
   tracks in 278px (3x96 + 2x10 = 308 > 278) — there is no floor that is
   simultaneously >= Sora's rendered width (needed to avoid wrapping) and
   <= 86px (needed to keep 278 in the three-column band), so this one real
   width now uses the same two-column fallback 359px-and-below already used
   safely. Every other real width is unaffected: 320 → two columns of ~113px
   (was 114px), 375/390/412/430/480+ → the same column count as before, and
   zero wrapped labels or horizontal overflow anywhere. Four tracks are
   still unreachable — a fourth now needs 4x96 + 3x10 = 414px against a
   374px maximum (was 4x86 + 3x10 = 374px, already exactly unreachable), and
   auto-fit (not auto-fill) collapses the empty one regardless. The
   narrowest a real track can render is now 96px (was 86px), still well
   above increment 10's 44px touch-target floor.

   Falling back to two columns rather than to one keeps the two-row form well
   inside step 2's headroom: the step is 262px against step 1's 392px, and
   two rows only reach 337px, so the card's height still comes from step 1 at
   every width — measured at all 168 widths swept, step 2 never once reached
   step 1, and every card height matches its pre-increment-11 value to the
   pixel (see the .auth-panel-stack comment for the table).

   minmax's floor also fixes what a plain 1fr cannot: a grid item's automatic
   minimum size is its min-content width, and an <input>'s min-content width
   is its intrinsic size-attribute width (~170px in Chrome, not its CSS
   width), so three plain 1fr tracks would demand ~510px of a 348px-wide card
   and overflow it sideways.

   The 10px gap matches .auth-step-nav's, the only other horizontal gap in
   this form.
   -------------------------------------------------------------------------- */
.auth-field-row {
    display: grid;
    /* 96px, not the original 86px — see the comment above (Revision A2,
       implementation_plan.md: .auth-label's font changed from Space Mono to
       Sora, which renders "MIDDLE NAME" wider, so the wrap-proof floor grew
       to match). */
    grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
    gap: 10px;
}

.auth-label {
    /* Revision A2 (implementation_plan.md) — label role, so the removed
       --font-mono is replaced with --font-display, not --font-numeric.
       font-size is deliberately kept as the literal 0.72rem (NOT mapped to
       an --fs-* token): the "Name-row top alignment fix" below computes an
       exact `min-height: 29.2px` from this exact value (0.72rem * 1.25
       line-height = 14.4px) plus .auth-label-hint's 0.64rem (also kept
       literal, same reason). Both --fs-xs (0.75rem) would round to the SAME
       token and erase the deliberate primary/secondary size difference,
       silently breaking that measured sum — see the comment above
       .auth-field-row .auth-label's min-height rule. */
    font-family: var(--font-display);
    font-size: 0.72rem;
    line-height: 1.25;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-ink-faint);
}

/* --------------------------------------------------------------------------
   Middle name's "(optional)" marker (increment 13)
   --------------------------------------------------------------------------
   A same-line suffix was measured and rejected: "MIDDLE NAME" alone is
   85.16px (see the name-row comment below) against an 86-118px column, so
   there is 0-33px of same-line room at any width this row renders — not
   enough for "(OPT.)" let alone "(optional)" at any legible size. Forced
   onto its own line instead via display:block, which is deterministic
   (does not depend on exact font-metric rounding the way incidental
   wrapping would) and, since .auth-label is already blockified as a flex
   item of .auth-field, simply ends the label's first line rather than
   fighting any inline layout.

   text-transform/letter-spacing are reset to read as a distinct, lighter
   secondary line rather than a fourth shouted mono word — the primary
   "MIDDLE NAME" text is completely unchanged. Colour is deliberately the
   SAME --color-ink-faint token .auth-label already uses (already the
   faintest token that still clears AA on this modal surface — see
   LandingPage.css's --color-ink* comment — so there is no fainter AA-safe
   option to express "more muted" with; the lighter weight comes from size
   and case instead). Contrast is unaffected by font-size, only colour, so
   this is the same measured 4.55:1 light / 5.69:1 dark .auth-label already
   holds.

   Nested inside .auth-label (not a sibling in .auth-field's flex column) so
   it costs no extra flex `gap`, and so a screen reader announcing this
   <label>'s accessible name includes "optional" too, not just sighted
   users. Surname/First name are untouched by this rule; only Middle name's
   own field grows taller — see implementation_plan.md (increment 13) for
   the measured row/step-height deltas at every breakpoint. */
.auth-label-hint {
    display: block;
    text-transform: none;
    font-family: var(--font-body);
    /* Revision A2 (implementation_plan.md) — kept as the literal 0.64rem,
       not an --fs-* token; see .auth-label's comment above (this value is
       the other half of the exact-pixel min-height sum below). */
    font-size: 0.64rem;
    letter-spacing: normal;
    margin-top: 2px;
    color: var(--color-ink-faint);
}

/* --------------------------------------------------------------------------
   Name-row top alignment fix (increment 14)
   --------------------------------------------------------------------------
   User-reported and confirmed by screenshot: the .auth-label-hint above
   gives Middle name's label a second line that Surname's and First name's
   labels don't have, so in the three-column state (all three fields sharing
   one grid row) Middle name's own input rendered visibly lower than the
   other two. Measured before this rule: every input's top is identical
   between Surname/First name, and Middle name's is 14.65-14.78px lower
   than both, at every three-column width, in both themes.

   Fix: reserve Middle name's OWN rendered two-line label height on every
   label in the row, not just its own, so Surname's and First name's
   single-line labels give up the same vertical space Middle name's already
   needs and all three inputs start at the identical Y position. The number
   is measured, not guessed: getComputedStyle() on the live modal reports
   .auth-label's own line box at 14.4px (0.72rem font-size * the 1.25
   line-height above) plus .auth-label-hint's line box at 12.8px (0.64rem *
   the same inherited 1.25) plus the 2px margin-top between them = 29.2px,
   identical in both themes and at every three-column width from 320-1920,
   since the label text never wraps a third line (see .auth-label-hint's
   own comment above). The rounded integer (Middle name's own
   label.offsetHeight, which CSSOM rounds to 29) was tried first and
   rejected: it left a measured ~0.17px residual between Middle name's
   input and the other two, because Middle's real two-line box is the
   unrounded 29.2px internally — the exact sum is what a min-height on the
   OTHER two labels has to match. Verified after with the exact sum: 0px
   difference between all three inputs' getBoundingClientRect().top, at
   every three-column width, both themes.

   Scoped to the three-column state ONLY: >=372px, and again at exactly
   360px, the same non-monotone edge case .auth-field-row's own comment
   documents (re-confirmed live for this rule: 3 columns render at 372px
   and 360px, 2 at 361px and 359px). Below that the row falls back to two
   columns with Middle name alone on its own row underneath Surname/First
   name — there is no taller row-mate to match there (confirmed live:
   Surname's and First name's labelTop already equal each other with no
   rule needed), so reserving two-line height on their labels in that
   layout would only add ~15px of dead space above two inputs that have
   nothing to align against. Checked by rendering both ways, not assumed,
   and left out on purpose. */
@media (min-width: 372px), (min-width: 360px) and (max-width: 360px) {
    .auth-field-row .auth-label {
        min-height: 29.2px;
    }
}

.auth-field input {
    width: 100%;
    background: var(--color-bg-card);
    border: 1px solid var(--color-line);
    border-radius: 10px;
    /* 12 + 12 + a 19px line box + 2px of border = a 45px control. Do not
       go below 12px here: 44px is the floor for a text input's target. */
    padding: 12px 14px;
    color: var(--color-ink);
    font-family: var(--font-body);
    font-size: var(--fs-base);
    transition: border-color 0.2s;
}

.auth-field input::placeholder {
    color: var(--color-ink-faint);
}

.auth-field input:focus {
    outline: none;
    border-color: var(--color-primary);
}

/* --------------------------------------------------------------------------
   Autofill parity — why an autofilled box has to be repainted, not restyled
   --------------------------------------------------------------------------
   Chrome paints an autofilled control from its own UA sheet:

       input:-internal-autofill-selected {
           background-color: rgb(232, 240, 254) !important;
           color: fieldtext !important;
       }

   A UA !important declaration outranks an author !important, so the
   `background: var(--color-bg-card)` above cannot win the cascade — the box
   has to be painted over instead. Measured, not assumed: with the pseudo-class
   forced over CDP (CSS.forcePseudoState → "autofill"), an autofilled input
   rendered rgb(232,240,254) in BOTH themes, against rgb(36,29,22) dark /
   rgb(255,255,255) light for a non-autofilled one on the same card.

   That is the "Log In's boxes are a different colour from Sign Up's" report.
   Nothing panel-specific exists in this file — both panels share the one
   `.auth-field input` rule above and their resting computed backgrounds are
   byte-identical. What differs is the markup the browser reacts to: Log In
   carries autocomplete="email" + "current-password", exactly the pair a
   browser has saved and refills on sight, while Sign Up carries "email" +
   "new-password", which Chrome does not refill.

   Three declarations, each doing a different job:
     • box-shadow — an inset spread paints on top of the background box, which
       is the only way past the UA !important. 1000px is simply larger than any
       input this modal will ever have.
     • -webkit-text-fill-color — outranks the UA's forced `color` for painted
       glyphs, so the value stays legible on the dark card.
     • transition — Chrome fades its background in; a 5000s background-color
       transition never lands, so the fade is never seen. Both halves of the
       fix are kept because browsers differ over which one they honour.

   Scoped to `.auth-field input`: Auth.css is loaded only by Pages/Index.html
   today (checked — no dashboard page links it), and scoping keeps this off any
   form that borrows the file later. The OTP boxes are deliberately out of
   scope; they carry no autocomplete attribute and are never autofilled.

   A FOURTH declaration, added on top of the three above: an inset left
   accent stripe, layered in the same box-shadow property so it paints on
   top of (not instead of) the 1000px background repaint. Without it, an
   autofilled-but-not-yet-focused field is byte-identical to an empty one —
   which is exactly what made a real password look "pre-typed" (see the
   `placeholder="Enter your password"` fix on the inputs themselves). The
   stripe uses --color-primary-dim, the same token .auth-otp-box.is-filled
   already uses to mark "this control already holds a value" — --color-primary
   itself stays reserved for :focus below, so a focused-and-autofilled field
   still reads as "focused" first. */
.auth-field input:-webkit-autofill,
.auth-field input:-webkit-autofill:hover,
.auth-field input:-webkit-autofill:focus,
.auth-field input:-webkit-autofill:active {
    -webkit-text-fill-color: var(--color-ink);
    -webkit-box-shadow: inset 3px 0 0 var(--color-primary-dim), 0 0 0 1000px var(--color-bg-card) inset;
    box-shadow: inset 3px 0 0 var(--color-primary-dim), 0 0 0 1000px var(--color-bg-card) inset;
    caret-color: var(--color-ink);
    transition: background-color 5000s ease-in-out 0s, border-color 0.2s;
}

/* The 5000s background-color transition STAYS under reduced motion, on
   purpose: it exists to SUPPRESS Chrome's autofill repaint, not to animate
   anything, and dropping it here would let the pale blue flash back in for
   exactly the visitors who asked for less movement. Only the border-color fade
   inherited from `.auth-field input` is removed. */
@media (prefers-reduced-motion: reduce) {
    .auth-field input:-webkit-autofill,
    .auth-field input:-webkit-autofill:hover,
    .auth-field input:-webkit-autofill:focus,
    .auth-field input:-webkit-autofill:active {
        transition: background-color 5000s ease-in-out 0s;
    }
}

.auth-input-wrap {
    position: relative;
    display: flex;
    align-items: center;
}

.auth-input-wrap input {
    padding-right: 44px;
}

.auth-toggle-visibility {
    position: absolute;
    right: 10px;
    width: 30px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    color: var(--color-ink-faint);
    cursor: pointer;
    border-radius: 8px;
    transition: color 0.2s, background 0.2s;
}

.auth-toggle-visibility:hover {
    color: var(--color-ink);
    background: rgba(244, 239, 230, 0.08);
}

.auth-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: var(--fs-md);
}

.auth-checkbox {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: var(--fs-md);
    color: var(--color-ink-dim);
    cursor: pointer;
}

.auth-checkbox input {
    margin-top: 2px;
    accent-color: var(--color-primary);
}

.auth-terms {
    line-height: 1.4;
}

.auth-link {
    background: none;
    border: none;
    padding: 0;
    color: var(--color-primary);
    font-family: var(--font-body);
    font-size: inherit;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
}

.auth-link:hover {
    color: var(--color-primary-dim);
    text-decoration: underline;
}

.auth-submit {
    width: 100%;
    /* 13 + 13 + a 20px line box = a 46px control. */
    padding: 13px 0;
    border: none;
    border-radius: 999px;
    background: var(--color-primary);
    color: #14110d;
    font-family: var(--font-body);
    font-weight: 700;
    font-size: var(--fs-base);
    cursor: pointer;
    transition: background 0.2s, transform 0.2s;
}

.auth-submit:hover {
    background: var(--color-primary-dim);
    transform: translateY(-1px);
}

.auth-submit:disabled {
    opacity: 0.58;
    cursor: not-allowed;
    transform: none;
}

.auth-login-lockout {
    margin: 0;
    padding: 10px 12px;
    border-left: 3px solid var(--color-accent-text);
    color: var(--color-ink);
    background: var(--color-bg-elevated);
    font-size: var(--fs-sm);
    line-height: 1.5;
}

.auth-login-lockout[hidden] {
    display: none;
}

.auth-switch {
    text-align: center;
    font-size: var(--fs-md);
    color: var(--color-ink-faint);
}

/* Divider between the credential form and the Google button */
.auth-divider {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: -6px 0;
    color: var(--color-ink-faint);
    font-size: var(--fs-xs);
    line-height: 1.2;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.auth-divider::before,
.auth-divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--color-line);
}

/* Google auth action */
.auth-google-btn {
    display: flex;
    justify-content: center;
    width: 100%;
    min-height: 46px;
}

.auth-google-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    width: 100%;
    min-height: 46px;
    padding: 12px 16px;
    border: 1px solid var(--color-line);
    border-radius: 999px;
    background: var(--color-bg-card);
    color: var(--color-ink);
    font-family: var(--font-body);
    font-size: var(--fs-base);
    font-weight: 700;
    cursor: pointer;
    transition: transform 0.2s ease, background 0.2s ease, border-color 0.2s ease;
}

.auth-google-button:hover {
    transform: translateY(-1px);
    background: rgba(244, 239, 230, 0.06);
    border-color: var(--color-primary);
}

.auth-google-button:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.auth-google-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 20px;
    height: 20px;
}

.auth-google-icon svg {
    width: 100%;
    height: 100%;
}

/* Auth toast — floats above the modal, doesn't push form layout, and
   auto-dismisses on its own (see setAuthNotice in auth.js) */
.auth-status {
    position: fixed;
    top: 28px;
    left: 50%;
    z-index: 200;
    max-width: min(420px, calc(100vw - 32px));
    margin: 0;
    padding: 12px 22px;
    border-radius: 999px;
    background: var(--color-bg-elevated);
    border: 1px solid var(--color-line);
    color: var(--color-ink);
    font-family: var(--font-body);
    font-size: var(--fs-md);
    font-weight: 600;
    text-align: center;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
    opacity: 0;
    pointer-events: none;
    transform: translate(-50%, -12px);
    transition: opacity 0.25s ease, transform 0.25s ease;
}

.auth-status.is-visible {
    opacity: 1;
    transform: translate(-50%, 0);
}

/* The border stays --color-alert (a 1px rule, not text); only the copy moves
   to --color-alert-text, which is the lighter red dark mode needs to clear AA
   on this surface. Same split as .auth-step-error above — see the token's
   comment in LandingPage.css. */
.auth-status.is-error {
    border-color: var(--color-alert);
    color: var(--color-alert-text);
}

/* Field-level validation state */
.auth-field.has-error input {
    border-color: var(--color-alert);
}

.auth-field-error {
    font-size: var(--fs-xs);
    /* Text, so --color-alert-text rather than --color-alert. */
    color: var(--color-alert-text);
    display: none;
}

.auth-field.has-error .auth-field-error {
    display: block;
}

/* ==========================================================================
   OTP Verification
   ========================================================================== */
.auth-otp-row {
    display: flex;
    gap: 10px;
    justify-content: space-between;
}

.auth-otp-box {
    width: 100%;
    aspect-ratio: 1;
    text-align: center;
    /* Revision A2 (implementation_plan.md) — a single OTP digit, so it takes
       --font-numeric (tabular-nums) instead of the removed --font-mono. */
    font-family: var(--font-numeric);
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-xl);
    background: var(--color-bg-card);
    border: 1px solid var(--color-line);
    border-radius: 10px;
    color: var(--color-ink);
    transition: border-color 0.2s, transform 0.15s;
}

.auth-otp-box:focus {
    outline: none;
    border-color: var(--color-primary);
    transform: translateY(-1px);
}

.auth-otp-box.is-filled {
    border-color: var(--color-primary-dim);
}

.auth-otp-error {
    display: none;
    font-size: var(--fs-sm);
    /* Text, so --color-alert-text rather than --color-alert. */
    color: var(--color-alert-text);
    margin-top: -8px;
}

.auth-otp-error.is-visible {
    display: block;
}

.auth-switch [data-otp-timer] {
    color: var(--color-ink-faint);
    /* Revision A2 (implementation_plan.md) — a "00:45" countdown, so it
       takes --font-numeric (tabular-nums) instead of the removed
       --font-mono. */
    font-family: var(--font-numeric);
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-sm);
    margin-left: 4px;
}

.auth-switch [data-otp-resend][disabled] {
    color: var(--color-ink-faint);
    cursor: not-allowed;
    text-decoration: none;
    pointer-events: none;
}

/* ==========================================================================
   Terms & Conditions dialog

   Stacks on top of the auth modal (z-index 110 vs .auth-overlay's 100) and is
   a sibling of it in the DOM, not a child — see the markup comment in
   Pages/Index.html.

   Ownership while both are open:
     • body scroll lock  — stays with the auth modal (body.auth-lock). This
       dialog never adds or removes it, so closing it cannot unlock the page
       behind a still-open modal. Its own long copy scrolls inside
       .auth-terms-body.
     • Escape and the Tab focus trap — taken over by this dialog while it is
       open and handed straight back on close (see includes/auth.js).

   The blur lives on .auth-terms-backdrop rather than on the overlay itself,
   the same way .court-viewer does it, so the overlay does not become a
   containing block for anything positioned inside it.
   ========================================================================== */
.auth-terms-overlay {
    position: fixed;
    inset: 0;
    z-index: 110;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.25s ease;
}

.auth-terms-overlay[data-open] {
    opacity: 1;
    pointer-events: auto;
}

/* `display: flex` above is an author declaration and beats the UA
   `[hidden] { display: none }`, so [hidden] has to be restated — otherwise
   the dialog would stay laid out, focusable and exposed to screen readers
   while closed. */
.auth-terms-overlay[hidden] {
    display: none;
}

.auth-terms-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(10, 8, 6, 0.72);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}

.auth-terms-dialog {
    position: relative;
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 560px;
    max-height: calc(100vh - 48px);
    background: var(--color-bg-elevated);
    border: 1px solid var(--color-line);
    border-radius: 20px;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
    transform: translateY(16px) scale(0.98);
    opacity: 0;
    transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.22s ease;
}

.auth-terms-overlay[data-open] .auth-terms-dialog {
    transform: translateY(0) scale(1);
    opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
    .auth-terms-overlay,
    .auth-terms-dialog {
        transition: none;
    }

    .auth-terms-dialog {
        transform: none;
    }
}

.auth-terms-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-shrink: 0;
    padding: 22px 24px 16px;
    border-bottom: 1px solid var(--color-line);
}

.auth-terms-title {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: var(--fs-lg);
    line-height: 1.2;
    color: var(--color-ink);
}

.auth-terms-close {
    flex-shrink: 0;
    width: 34px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--color-bg-card);
    border: 1px solid var(--color-line);
    border-radius: 50%;
    color: var(--color-ink-dim);
    cursor: pointer;
    transition: background 0.2s, color 0.2s, border-color 0.2s;
}

.auth-terms-close:hover {
    color: var(--color-ink);
    border-color: var(--color-primary);
}

/* The long copy scrolls HERE, not on the page or the dialog, so the header
   and footer stay put and the dialog can never grow past the viewport. */
.auth-terms-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 20px 24px 4px;
}

.auth-terms-foot {
    display: flex;
    justify-content: flex-end;
    flex-shrink: 0;
    padding: 16px 24px 20px;
    border-top: 1px solid var(--color-line);
}

.auth-terms-done {
    padding: 11px 22px;
    border: none;
    border-radius: 999px;
    background: var(--color-primary);
    color: #14110d;
    font-family: var(--font-body);
    font-weight: 700;
    font-size: var(--fs-md);
    cursor: pointer;
    transition: background 0.2s, transform 0.2s;
}

.auth-terms-done:hover {
    background: var(--color-primary-dim);
    transform: translateY(-1px);
}

@media (prefers-reduced-motion: reduce) {
    .auth-terms-close,
    .auth-terms-done {
        transition: none;
    }

    .auth-terms-done:hover {
        transform: none;
    }
}

/* --------------------------------------------------------------------------
   Typography for the copy injected from Pages/terms.html.

   That page styles .terms-* in its own inline <style> block, which nothing
   outside it can reach, so the rules a wall of policy text needs are restated
   here — scoped under .auth-terms-body so they can never leak into the
   dashboards that also load this file. Sizes are a touch smaller than the
   standalone page's because this is a 560px dialog, not a 720px article
   column. The *copy* still has exactly one source of truth: terms.html.
   -------------------------------------------------------------------------- */
.auth-terms-body .terms-updated {
    /* Revision A2 (implementation_plan.md) — a "Last updated: <date>"
       sentence, not a bare number/label, so it inherits the body font. */
    font-size: var(--fs-xs);
    color: var(--color-ink-faint);
    margin-bottom: 24px;
}

.auth-terms-body .terms-section {
    margin-bottom: 26px;
}

.auth-terms-body .terms-section h2 {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: var(--fs-lg);
    line-height: 1.3;
    color: var(--color-ink);
    margin-bottom: 10px;
}

.auth-terms-body .terms-section p,
.auth-terms-body .terms-section li {
    font-size: var(--fs-md);
    line-height: 1.7;
    color: var(--color-ink-dim);
}

.auth-terms-body .terms-section p {
    margin-bottom: 12px;
}

.auth-terms-body .terms-section ul {
    padding-left: 20px;
    margin: 0 0 12px;
}

.auth-terms-body .terms-section li {
    margin-bottom: 6px;
}

.auth-terms-body .terms-placeholder {
    color: var(--color-accent-text);
    font-weight: 600;
}

.auth-terms-body .terms-section a {
    color: var(--color-accent-text);
    font-weight: 600;
    text-decoration: underline;
}

/* Responsive */
@media (max-width: 480px) {
    .auth-modal {
        padding: 22px 22px 20px;
        border-radius: 16px;
    }

    .auth-brand {
        margin-bottom: 12px;
        padding-bottom: 10px;
    }

    .auth-brand-mark {
        width: 40px;
        height: 40px;
        border-radius: 12px;
    }

    .auth-brand-mark img {
        width: 26px;
        height: 26px;
    }

    .auth-brand .logo-text {
        font-size: var(--fs-xl);
    }

    .auth-brand .auth-tagline {
        font-size: var(--fs-xs);
    }

    .auth-terms-overlay {
        padding: 16px;
    }

    .auth-terms-dialog {
        max-height: calc(100vh - 32px);
        border-radius: 16px;
    }

    .auth-terms-head {
        padding: 18px 18px 14px;
    }

    .auth-terms-title {
        font-size: var(--fs-lg);
    }

    .auth-terms-body {
        padding: 16px 18px 4px;
    }

    .auth-terms-foot {
        padding: 14px 18px 16px;
    }

    .auth-google-button {
        min-height: 46px;
        font-size: var(--fs-md);
        padding: 12px 14px;
    }

    .auth-otp-row {
        gap: 6px;
    }

    .auth-otp-box {
        font-size: var(--fs-lg);
    }
}

@media (max-width: 360px) {
    .auth-modal {
        padding: 18px 16px 16px;
    }

    /* .auth-close is absolutely positioned and reaches 52px in from the
       modal's right edge. At and above 375px the strapline clears it
       vertically (the button sits in the band beside the wordmark), but below
       360px the modal's side padding drops to 16px, which lifts the whole
       lockup into that band. Capping the centred lockup at
       `content width - 80px` leaves a constant 4px gap to the button at any
       width, and the strapline wraps to a second line instead of running
       underneath it. */
    .auth-brand-lockup {
        gap: 10px;
        max-width: calc(100% - 80px);
    }

    .auth-brand .logo-text {
        font-size: var(--fs-lg);
    }

    .auth-brand .auth-tagline {
        font-size: var(--fs-xs);
    }

    .auth-google-button {
        font-size: var(--fs-md);
        gap: 8px;
    }
}

.auth-modal { --auth-verified-color: #78dba4; }
:root[data-theme="light"] .auth-modal { --auth-verified-color: #226b42; }
.auth-email-label { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
[data-signup-email-status] { flex-shrink: 0; min-height: 1.5em; font-size: var(--fs-xs); line-height: 1.5; text-transform: none; letter-spacing: normal; font-weight: 600; }
[data-signup-email-status][data-state="available"] { color: var(--auth-verified-color); }
[data-signup-email-status]:is([data-state="taken"], [data-state="invalid"], [data-state="error"], [data-state="rate_limited"]) { color: var(--color-alert-text); }
.auth-field input[data-availability="available"] { border-color: var(--auth-verified-color); box-shadow: 0 0 0 1px var(--auth-verified-color); }
.auth-field input:is([data-availability="taken"], [data-availability="invalid"]) { border-color: var(--color-alert-text); box-shadow: 0 0 0 1px var(--color-alert-text); }
.auth-phone-row { display: flex; align-items: stretch; gap: 8px; }
.auth-phone-row input { flex: 1; min-width: 0; padding-inline: 10px; }
.auth-phone-button { flex: 0 0 96px; min-height: 44px; padding: 8px; border: 1px solid var(--color-line); border-radius: 10px; color: var(--color-ink); background: var(--color-bg-elevated); font: inherit; font-size: var(--fs-xs); font-weight: 600; cursor: pointer; }
.auth-phone-button:disabled { opacity: .6; cursor: default; }
.auth-phone-button.is-verified { color: var(--auth-verified-color); border-color: var(--auth-verified-color); opacity: 1; }
.auth-phone-button:focus-visible { outline: 2px solid var(--color-accent-text); outline-offset: 3px; }

.auth-access-error { color: var(--color-ink); border-left: 3px solid var(--color-accent-text); padding: 10px 12px; margin: 0 0 16px; font-size: var(--fs-sm); line-height: 1.5; }
[data-auth-panel="phone"] [hidden], .auth-access-error[hidden] { display: none; }
[data-auth-panel="phone"] > * + * { margin-top: 14px; }

@media (pointer: coarse) {
    .auth-close {
        width: 44px;
        height: 44px;
        top: 13px;
        right: 13px;
    }
}
