/* Direction A design system: foundational tokens + web fonts (issue #43).
   Source of truth for tone is docs/design/Direction A. The finder page must
   stay fast and fully functional without script (spec.md §13, stack-spec §3),
   so fonts are self-hosted (no fonts.gstatic.com round-trip on poor airport
   Wi-Fi, no third-party privacy cost) and loaded with font-display: swap so
   text paints immediately in the system-ui fallback and swaps in when ready. */

/* Newsreader (serif) — headings and the finder-facing note voice. Variable
   weight axis, latin subset; one file covers the 400–600 range we use. */
@font-face {
    font-family: "Newsreader";
    font-style: normal;
    font-weight: 400 600;
    font-display: swap;
    src: url("../fonts/newsreader-latin.woff2") format("woff2");
}
@font-face {
    font-family: "Newsreader";
    font-style: italic;
    font-weight: 400 600;
    font-display: swap;
    src: url("../fonts/newsreader-italic-latin.woff2") format("woff2");
}

/* Hanken Grotesk (body / UI). Variable weight axis, latin subset; one file
   covers the 400–700 range we use. */
@font-face {
    font-family: "Hanken Grotesk";
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url("../fonts/hanken-grotesk-latin.woff2") format("woff2");
}

/* IBM Plex Mono (codes, labels, eyebrows). Static instances, latin subset. */
@font-face {
    font-family: "IBM Plex Mono";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("../fonts/ibm-plex-mono-400-latin.woff2") format("woff2");
}
@font-face {
    font-family: "IBM Plex Mono";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("../fonts/ibm-plex-mono-500-latin.woff2") format("woff2");
}
@font-face {
    font-family: "IBM Plex Mono";
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url("../fonts/ibm-plex-mono-600-latin.woff2") format("woff2");
}

:root {
    /* Palette — Direction A (issue #43). */
    --bg: #f7f1e7; /* paper / page background */
    --ink: #241f1a; /* primary text. Reconciled: the comp's #241f1a wins over
                       the app's old #2b2620 — the comp is the source of truth,
                       and the darker value only raises contrast on --bg. */
    --muted: #6b6055; /* AA-compliant secondary text on --bg (spec.md §13) */
    --neutral: #a89c8d; /* meta / timestamps — large or non-essential text only */

    --accent: #c2613a; /* terracotta — the single brand accent */
    --accent-dark: #a8512f; /* pressed / emphasis terracotta */
    --accent-tint: #f3e4d8; /* terracotta wash — pills, chips */
    --accent-tint-warm: #fbe9da; /* warmer terracotta wash */
    --on-accent: #fff; /* text/icons on a terracotta fill */

    --card: #fffdf9; /* raised surfaces — cards, bubbles, inputs */
    --panel: #efe7d9; /* recessed surfaces — toolbars, chrome */
    --border: #ece2d2; /* hairline borders */
    --border-strong: #e0d5c4; /* input / interactive borders */

    --success: #5f8c4e; /* active / recovered green */

    /* #9a8f80 is the comp's eyebrow grey. On --bg it measures 2.83:1, below
       AA's 4.5:1, so it must NOT be used for small body text — restrict to
       large text only (≥18pt, or ≥14pt bold) per spec.md §13. Small eyebrows
       use --muted instead (see .eyebrow). */
    --eyebrow: #9a8f80;

    /* Spacing scale (4px base). */
    --space-1: 0.25rem; /* 4 */
    --space-2: 0.5rem; /* 8 */
    --space-3: 0.75rem; /* 12 */
    --space-4: 1rem; /* 16 */
    --space-5: 1.5rem; /* 24 */
    --space-6: 2rem; /* 32 */
    --space-7: 3rem; /* 48 */
    --space-8: 3.5rem; /* 56 */

    /* Radius scale — Direction A favours soft 14–18px corners. */
    --radius-sm: 0.5rem; /* 8 — small chips, code boxes */
    --radius: 0.875rem; /* 14 — inputs, buttons */
    --radius-lg: 1rem; /* 16 — cards, message bubbles */
    --radius-xl: 1.125rem; /* 18 — tags */
    --radius-pill: 999px; /* pills, status chips, round controls */

    /* Shadows — warm, soft, cast in the comp's rgba(50,38,24,…) ink. */
    --shadow-sm: 0 1px 2px rgba(50, 38, 24, 0.08);
    --shadow: 0 16px 40px -18px rgba(50, 38, 24, 0.4);
    --shadow-lg: 0 28px 64px -28px rgba(50, 38, 24, 0.45);

    /* Type families. System fallbacks keep the finder path readable before
       (or without) the web fonts loading. */
    --font-serif: "Newsreader", Georgia, "Times New Roman", serif;
    --font-sans: "Hanken Grotesk", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Type scale (rem). */
    --text-xs: 0.75rem; /* 12 — eyebrows, labels */
    --text-sm: 0.875rem; /* 14 — secondary body */
    --text-base: 1rem; /* 16 — body */
    --text-lg: 1.0625rem; /* 17 — lead paragraph */
    --text-xl: 1.625rem; /* 26 — h3 */
    --text-2xl: 1.875rem; /* 30 — h2 */
    --text-3xl: 2.125rem; /* 34 */
    --text-4xl: 2.25rem; /* 36 — display heading */

    /* Line heights. */
    --leading-tight: 1.05;
    --leading-snug: 1.45;
    --leading-normal: 1.55;

    /* Letter spacing / tracking. */
    --tracking-tight: -0.01em; /* large serif headings */
    --tracking-eyebrow: 0.14em; /* mono eyebrows */
    --tracking-code: 0.18em; /* code glyphs */
}

* {
    box-sizing: border-box;
}

/* Visually hidden, but available to screen readers — used to spell out icon-only signals like the
   unread dot in the conversation list. The standard clip-rect pattern. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0); /* legacy fallback */
    clip-path: inset(50%); /* modern equivalent; clip is deprecated */
    white-space: nowrap;
}

body {
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--text-base);
    color: var(--ink);
    background: var(--bg);
    line-height: 1.5;
}

/* Foundational type scale. Direction A speaks in a serif voice for headings,
   sans for body/UI, and mono for codes/labels/eyebrows. Per-page restyles
   build on these defaults; this issue just establishes the scale. */
h1,
h2,
h3 {
    font-family: var(--font-serif);
    font-weight: 600;
    color: var(--ink);
    letter-spacing: var(--tracking-tight);
    line-height: var(--leading-tight);
    margin: 0 0 var(--space-3);
}

h1 {
    font-size: var(--text-4xl);
}

h2 {
    font-weight: 500;
    font-size: var(--text-3xl);
}

h3 {
    font-weight: 500;
    font-size: var(--text-xl);
    line-height: var(--leading-snug);
}

/* Lead paragraph — the warm, human intro sentence. */
.lead {
    font-size: var(--text-lg);
    line-height: var(--leading-normal);
    color: var(--muted);
}

/* Mono eyebrow / kicker. Small, so it uses --muted (AA on --bg), NOT the
   comp's #9a8f80 which only clears AA at ≥18pt / ≥14pt bold (spec.md §13). */
.eyebrow {
    font-family: var(--font-mono);
    font-weight: 600;
    font-size: var(--text-xs);
    line-height: 1;
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--muted);
}

.site-header,
main,
.site-footer {
    max-width: 32rem;
    margin: 0 auto;
    padding: 1rem;
}

/* Wordmark — Newsreader serif in terracotta, the Direction A brand voice
   (outstanding §1, comp 01). One treatment for every header: finder, auth, and
   the owner chrome. --accent-dark keeps it AA on both --bg (finder/auth) and the
   owner header's --card (4.8–5.3:1); plain --accent measures ~4.1, under 4.5. */
.brand {
    font-family: var(--font-serif);
    font-weight: 400; /* base sits at book weight so the weighted initials read */
    font-size: var(--text-lg);
    letter-spacing: var(--tracking-tight);
    text-decoration: none;
    color: var(--accent-dark);
}

/* The initial of each word in "if you find me" (i·y·f·m) is weighted so the
   phrase reads at a glance while the wordmark stays lowercase. 600 is the top of
   the Newsreader axis we load (400–600, see @font-face); going higher would
   trigger a synthetic faux-bold that smears the serifs. */
.brand-ini {
    font-weight: 600;
}

/* The ".me" tail reads in plain --accent. The wordmark is large/bold, so it
   clears AA at the 3:1 floor (--accent is ~4.1:1) — the tail stays distinct and
   lands the "find me" pun while "ifyoufind" keeps the AA-safe --accent-dark. */
.brand-tld {
    color: var(--accent);
}

/* ── Finder code entry — Index.cshtml (Direction A, comp 01, issue #44) ───────
   The first screen a finder sees: a calm paper page with a serif headline, a
   framed code field with a terracotta text caret, a full-width terracotta CTA,
   and a privacy reassurance line. Mobile-first; no script (spec §13). */
/* The two co-equal entry paths, owner-first (issue #128 follow-up, reordered): a finder holding a tagged
   item nearly always arrives at /c/{code} via the QR or printed URL, so the bare-domain visitor is most
   often a prospective owner. Sign-up leads, then the spoken "or", then the code box. The spacing here is
   budgeted so BOTH cards land inside the first viewport of a phone — no scrolling to see the second path. */
/* The plain-language service description (issue #186) now sits BELOW the cards: the same SEO prose stays
   on /, but it no longer costs the first viewport. text-sm and --muted keep it quiet, orienting context. */
.home-intro {
    margin: var(--space-6) 0 0;
    max-width: 60ch;
    color: var(--muted);
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
}

.entry-paths {
    margin-top: var(--space-1);
    display: grid;
    gap: var(--space-2);
    align-items: start;
}

/* Each path is a raised card so neither reads as secondary chrome — both are first-class. The fields
   inside use --card, a shade lighter than the --panel card, so they stay legible surfaces-within-surfaces. */
.entry-path {
    padding: var(--space-3) var(--space-5) var(--space-4);
    background: var(--panel);
    border-radius: var(--radius-lg);
}

.entry-path h1,
.entry-path h2 {
    font-size: var(--text-xl);
    font-weight: 600;
    margin: 0 0 var(--space-1);
}

.entry-path .lead {
    font-size: var(--text-base);
    margin: 0 0 var(--space-3);
}

/* Side-by-side entry cards once the viewport can afford it: the home page opts its chrome into a wider
   column (BodyClass, Index.cshtml), and past that width the grid turns the stack into
   [owner | or | finder], which keeps both paths (and usually the footer) inside one viewport on desktop. */
.home-page .site-header,
.home-page main,
.home-page .site-footer {
    max-width: 52rem;
}

@media (min-width: 52rem) {
    .entry-paths {
        grid-template-columns: 1fr auto 1fr;
        align-items: stretch;
    }

    /* The "or" divider rotates with the layout: a vertical hairline with the word at its middle. */
    .entry-or {
        flex-direction: column;
    }

    .entry-or::before,
    .entry-or::after {
        width: 1px;
        height: auto;
    }
}

/* "or" divider between the cards — a centered word on a hairline rule. Hidden from assistive tech in the
   markup (the two headings already read as alternatives); this is a sighted-reader cue only. */
.entry-or {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin: 0;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--muted);
}

.entry-or::before,
.entry-or::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--border-strong);
}

/* The code label reads as a quiet mono eyebrow above the field. It carries the instruction the old
   lead paragraph used to ("the code on the tag"), so the card gets straight to the field. */
.finder-entry label {
    display: block;
    margin-bottom: var(--space-2);
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--muted);
}

/* Framed code field — big mono glyphs with generous tracking, and a terracotta
   caret. caret-color tints the real text cursor, so the comp's terracotta caret
   needs no extra markup and stays in the no-JS finder path. */
.finder-entry input {
    display: block;
    width: 100%;
    padding: var(--space-3) var(--space-4);
    font-family: var(--font-mono);
    font-size: var(--text-xl);
    font-weight: 500;
    letter-spacing: var(--tracking-code);
    text-transform: uppercase;
    color: var(--ink);
    background: var(--card);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius);
    caret-color: var(--accent);
}

.finder-entry input::placeholder {
    color: var(--neutral);
}

/* Full-width terracotta CTA with a trailing arrow (comp 01). */
.finder-entry button {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    width: 100%;
    min-height: 44px; /* ≥44px tap target (spec §13 / outstanding §8) */
    margin-top: var(--space-2);
    padding: var(--space-3) var(--space-5);
    font-family: var(--font-sans);
    font-weight: 600;
    font-size: var(--text-base);
    color: var(--on-accent);
    background: var(--accent);
    border: 0;
    border-radius: var(--radius);
    cursor: pointer;
}

.finder-entry button:hover {
    background: var(--accent-dark);
}

/* Submit readiness cue (issue #97): the box only enables submit once 8 in-alphabet characters are
   present — pure syntax, never a "valid!" claim (the keyed check symbol is server-only, spec §8). The
   disabled state reads as inert; .is-ready is a quiet lift, not a checkmark. JS-only — with no script
   the button stays its normal, always-enabled self. */
.finder-entry button:disabled {
    background: var(--neutral);
    cursor: not-allowed;
}

/* Live alphabet hint (issue #97): names the one out-of-alphabet character a finder typed, in the same
   warning voice as .form-error but quieter. Hidden until the script has something to say. */
.finder-hint {
    margin: var(--space-2) 0 0;
    font-size: var(--text-sm);
    color: #8a2b1f; /* matches .form-error — darker than the AA floor on --bg */
}

/* Privacy reassurance — a small lock glyph + the promise the finder stays
   anonymous (spec §10). --muted clears AA on --bg; the comp's #9a8f80 would not
   at this size (spec §13). */
.finder-reassure {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    margin: var(--space-3) 0 0;
    font-size: var(--text-sm);
    color: var(--muted);
    text-align: left;
}

.finder-reassure .lock {
    flex: none;
    color: var(--accent);
}

/* The owner path — the second co-equal way in (issue #128 follow-up). It mirrors the finder card's
   field-then-button rhythm so the two read as siblings, but the email field uses ordinary sans text
   (not the finder's big mono code glyphs) and the submit is an outlined accent button, so a finder can
   still tell the urgent code lookup from the "get started" action at a glance. */
.owner-entry label {
    display: block;
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--muted);
}

/* Label row: the field label on the left, the "how it works" link on the right — the "forgot
   password?" pattern. Sharing the label's row means the explainer link costs the viewport budget
   nothing (the two entry cards must fit one phone screen). baseline keeps the mono eyebrow and the
   sans link sitting on the same line. */
.owner-label-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-2);
}

/* Quiet but discoverable. One shade below --accent-dark: at text-sm the AA floor is 4.5:1, and
   --accent-dark measures only ~4.4:1 on the --panel card (it clears on --bg and --card, where its
   palette comment vouches for it) — so this link uses #a04b28, the same terracotta a step darker,
   which measures ~4.8:1 on --panel. The underline keeps it reading as a link even at this size. */
.owner-how {
    font-size: var(--text-sm);
    line-height: 1; /* don't let the link's line box out-grow the eyebrow label's row */
    color: #a04b28;
    text-decoration: underline;
    text-underline-offset: 2px;
    white-space: nowrap;
}

/* Hover thickens the underline rather than lightening to --accent, which sits below AA on --panel. */
.owner-how:hover {
    text-decoration-thickness: 2px;
}

.owner-how:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 2px;
}

.owner-entry input {
    display: block;
    width: 100%;
    padding: var(--space-3) var(--space-4);
    font-family: var(--font-sans);
    font-size: var(--text-base);
    color: var(--ink);
    background: var(--card);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius);
    caret-color: var(--accent);
}

.owner-entry input::placeholder {
    color: var(--neutral);
}

/* Outlined accent CTA — same size and shape as the finder's filled button, so the paths weigh the same,
   but the lighter fill keeps the terracotta "look it up" the single loudest thing on the page. */
.owner-entry button {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    width: 100%;
    min-height: 44px; /* ≥44px tap target (spec §13) */
    margin-top: var(--space-2);
    padding: var(--space-3) var(--space-5);
    font-family: var(--font-sans);
    font-weight: 600;
    font-size: var(--text-base);
    color: var(--accent-dark);
    background: var(--card);
    border: 1.5px solid var(--accent);
    border-radius: var(--radius);
    cursor: pointer;
}

.owner-entry button:hover {
    color: var(--on-accent);
    background: var(--accent);
}

/* Hint under the owner email field — spells out what "get started" does (a one-time code, no password).
   Quiet --muted like the finder's privacy reassurance, so it informs without competing with the field. */
.owner-email-hint {
    margin: var(--space-2) 0 0;
    font-size: var(--text-sm);
    color: var(--muted);
}

/* Finder language switcher (issue #98), now a subtle disclosure in the footer. Collapsed it shows only
   the current language behind a globe glyph, so the footer never lists every language; opening it reveals
   the full set as quiet, wrap-friendly native-name links. A native <details>, so it needs no script on
   the finder critical path (stack-spec §3). The current language reads as plain emphasized text. The
   <nav> wrapper keeps the switcher a navigation landmark for assistive tech (it carries the label). */
.lang-switcher-nav {
    margin-top: var(--space-3);
}

.lang-switcher {
    font-size: var(--text-sm);
}

.lang-switcher > summary {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    width: fit-content;
    cursor: pointer;
    color: var(--muted);
    list-style: none; /* hide the default disclosure triangle — the globe carries the affordance */
}

.lang-switcher > summary::-webkit-details-marker {
    display: none;
}

.lang-switcher > summary:hover {
    color: var(--accent);
}

.lang-switcher .lang-globe {
    flex: none;
}

.lang-switcher ul {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    margin: var(--space-3) 0 0;
    padding: 0;
    list-style: none;
}

.lang-switcher a {
    color: var(--muted);
    text-decoration: none;
}

.lang-switcher a:hover {
    color: var(--accent);
    text-decoration: underline;
}

.lang-switcher .lang-current {
    color: var(--ink);
    font-weight: 600;
}

.lang-switcher > summary:focus-visible,
.lang-switcher a:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 2px;
}

.site-footer small {
    color: var(--muted);
}

/* Operator line (Operated by ..., _Layout.cshtml) — omitted entirely from markup until
   OperatorOptions:Name is configured, so this only ever appears once real. Block-level so it
   doesn't run into the tagline's <small> on the same line; sits above the nav row like the
   tagline it follows. */
.footer-operator {
    display: block;
    margin-top: var(--space-1);
}

/* Footer corporate links (About / Privacy / Contact). A quiet, wrap-friendly row
   under the tagline — muted until hover, matching the language switcher's voice. */
.footer-nav {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    margin-top: var(--space-3);
    font-size: var(--text-sm);
}

.footer-nav a {
    color: var(--muted);
    text-decoration: none;
}

.footer-nav a:hover {
    color: var(--accent);
    text-decoration: underline;
}

.footer-nav a:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 2px;
}

/* Deploy stamp ("build 123 · 3 hours ago", _Layout.cshtml). Deliberately the quietest
   line in the footer — an operator's glance at when production last shipped, not page
   content — so it sits below everything at eyebrow size. Quiet comes from size and
   placement only: it keeps the inherited --muted with no opacity fade, because 12px
   text must still clear AA on --bg (spec.md §13; faded --muted measured ~2.4:1). */
.footer-build {
    display: block;
    margin-top: var(--space-3);
    font-size: var(--text-xs);
}

.note {
    color: var(--muted);
    font-size: 0.9rem;
}

/* Finder messaging (spec §7). Stays legible and usable with no script (spec §13). */
.form-error {
    color: #8a2b1f; /* darker than the AA contrast floor on --bg */
    font-size: 0.9rem;
    margin: 0.25rem 0;
}

.form-notice {
    background: #e8efe3;
    padding: 0.5rem 0.75rem;
    border-radius: 0.25rem;
}

.auth label {
    display: block;
    margin-top: var(--space-3);
    font-weight: 600;
}

/* ── Finder conversation composer — Code.cshtml (the start-a-conversation page).
   Framed textarea + terracotta send, matching the owner composer. The finder and
   reply THREAD pages now use the shared chat composer instead. */
.finder-thread label {
    display: block;
    margin: var(--space-4) 0 var(--space-2);
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--muted);
}

.finder-thread textarea,
.finder-thread input[type="email"] {
    display: block;
    width: 100%;
    padding: var(--space-3) var(--space-4);
    font: inherit;
    color: var(--ink);
    background: var(--card);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius);
}

.finder-thread textarea {
    resize: vertical;
    line-height: var(--leading-snug);
}

.finder-thread textarea::placeholder,
.finder-thread input::placeholder {
    color: var(--neutral);
}

.finder-thread button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px; /* ≥44px tap target (spec §13) */
    margin-top: var(--space-3);
    padding: var(--space-3) var(--space-5);
    font-family: var(--font-sans);
    font-weight: 600;
    font-size: var(--text-sm);
    color: var(--on-accent);
    background: var(--accent);
    border: 0;
    border-radius: var(--radius);
    cursor: pointer;
}

.finder-thread button:hover {
    background: var(--accent-dark);
}

/* Visible focus rings on every restyled finder/reply control (spec §13). */
.finder-entry input:focus-visible,
.finder-entry button:focus-visible,
.owner-entry input:focus-visible,
.owner-entry button:focus-visible,
.finder-thread textarea:focus-visible,
.finder-thread input:focus-visible,
.finder-thread button:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Auth pages (sign-in / verify) — issue #46. Not in the Direction A comp, so
   they inherit the foundational scale (#43) and adopt the same terracotta
   button + soft radii used across the app. Kept token-driven so they stay in
   sync with the rest of the system. */
.auth input {
    display: block;
    width: 100%;
    padding: 0.75rem;
    font-size: 1.25rem;
    color: var(--ink);
    background: var(--card);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
}

.auth button {
    margin-top: 0.75rem;
    min-height: 2.75rem; /* ≥44px tap target (spec.md §13) */
    padding: 0.75rem 1.25rem;
    font: inherit;
    font-weight: 600;
    color: var(--on-accent);
    background: var(--accent);
    border: 0;
    border-radius: var(--radius);
    cursor: pointer;
}

.auth button:hover {
    background: var(--accent-dark);
}

/* Visible focus on the restyled controls (spec.md §13 / outstanding §8).
   Scoped to .auth so it stands alone without depending on the finder-path
   restyle's global focus rule. */
.auth input:focus-visible,
.auth button:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* "Send a new code" on the verify page: semantically a button (it POSTs and
   issues a code) but visually a link inside the "didn't get it?" note, so it
   never competes with the primary Verify action. Overrides the .auth button
   terracotta block styling above. */
.auth .resend button {
    display: inline;
    min-height: 0;
    margin: 0;
    padding: 0;
    font: inherit;
    color: inherit;
    background: none;
    border: 0;
    text-decoration: underline;
    cursor: pointer;
}

/* ── Corporate pages — About / Privacy / Contact ────────────────────────────
   Readable long-form prose and the contact form. Not in the Direction A comp;
   they inherit the foundational type scale and the shared terracotta controls so
   they stay in sync with the rest of the system. */
.content-page {
    line-height: var(--leading-normal);
}

.content-page h2 {
    margin-top: var(--space-6);
}

.content-page p,
.content-page ul,
.content-page ol {
    margin: 0 0 var(--space-4);
}

.content-page ul,
.content-page ol {
    padding-left: var(--space-5);
}

.content-page li {
    margin-bottom: var(--space-2);
}

.content-page a {
    color: var(--accent-dark);
}

/* Contact form. Block labels and full-width controls matching the auth pages,
   with a resizable message box like the finder/owner composers. */
.content-page label {
    display: block;
    margin-top: var(--space-4);
    font-weight: 600;
}

.content-page .optional {
    font-weight: 400;
    color: var(--muted);
}

.content-page input,
.content-page textarea {
    display: block;
    width: 100%;
    margin-top: var(--space-2);
    padding: 0.75rem;
    font: inherit;
    color: var(--ink);
    background: var(--card);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
}

.content-page textarea {
    resize: vertical;
    line-height: var(--leading-snug);
}

.content-page input::placeholder,
.content-page textarea::placeholder {
    color: var(--neutral);
}

.content-page button {
    display: block;
    margin-top: var(--space-4);
    min-height: 2.75rem; /* ≥44px tap target (spec.md §13) */
    padding: 0.75rem 1.25rem;
    font: inherit;
    font-weight: 600;
    color: var(--on-accent);
    background: var(--accent);
    border: 0;
    border-radius: var(--radius);
    cursor: pointer;
}

.content-page button:hover {
    background: var(--accent-dark);
}

.content-page input:focus-visible,
.content-page textarea:focus-visible,
.content-page button:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Contact-form honeypot wrapper: parked off-screen rather than display:none, which
   the smarter form bots detect and skip. Paired with aria-hidden and tabindex="-1"
   in the markup so people — including screen-reader and keyboard users — never
   encounter the field. */
.form-extra {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* Contact-form confirmation after a successful send (POST/redirect/GET). The
   calm-green notice voice used for confirmations elsewhere. */
.form-success {
    margin: 0 0 var(--space-4);
    padding: 0.5rem 0.75rem;
    background: #e8efe3;
    border-radius: var(--radius-sm);
    color: var(--ink);
}

/* ── Owner's finder-facing note ─────────────────────────────────────────────
   The warm "the owner left a note" card a finder sees first (Code.cshtml) and
   the owner's live setup-screen preview (CodeSetup). Serif italic in a card with a
   terracotta edge — the same finder-note voice used on the owner code detail. */
.owner-note-label {
    margin: 0 0 var(--space-2);
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--accent-dark); /* AA on --card (5.3:1); plain --accent would not */
}

.owner-message {
    margin: 0 0 var(--space-5);
    padding: var(--space-4);
    font-family: var(--font-serif);
    font-style: italic;
    font-size: var(--text-lg);
    line-height: var(--leading-snug);
    color: var(--ink);
    background: var(--card);
    border: 1.5px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: 0 var(--radius) var(--radius) 0;
}

/* ── Conversation bubbles ───────────────────────────────────────────────────
   One chat transcript, shared by the finder thread (Code/Thread.cshtml), the
   owner conversation views (_MessageBubble), and the email reply page
   (Reply.cshtml). `message-you` is always the current viewer — owner OR finder —
   so it sits right in terracotta; the other party sits left in cream; system
   notes center. (MessagePresentation / the page switch set the modifier.) */
.messages {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    list-style: none;
    padding: 0;
    margin: var(--space-5) 0;
}

.message {
    max-width: 82%;
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-lg);
}

/* The current viewer's own messages — right-aligned terracotta. --accent-dark
   keeps white body text AA (5.4:1); plain --accent is 4.15:1, under the 4.5
   floor for body text (spec §13). */
.message-you {
    align-self: flex-end;
    color: var(--on-accent);
    background: var(--accent-dark);
    border-bottom-right-radius: 4px;
}

/* The other party — left-aligned cream card (owner-view: finder; finder-view: owner). */
.message-owner,
.message-finder {
    align-self: flex-start;
    color: var(--ink);
    background: var(--card);
    border: 1.5px solid var(--border);
    border-bottom-left-radius: 4px;
}

/* System notes ("Owner notified…") — a centered, quiet line, no bubble. */
.message-system {
    align-self: center;
    max-width: 100%;
    padding: var(--space-1) 0;
    text-align: center;
    background: none;
}

.message-meta {
    margin: 0 0 var(--space-1);
    font-size: var(--text-xs);
    color: var(--muted);
}

/* On the terracotta bubble the meta reads in a warm tint that still clears AA
   on --accent-dark (4.6:1). */
.message-you .message-meta {
    color: var(--accent-tint-warm);
}

.message-system .message-meta {
    font-family: var(--font-mono);
    letter-spacing: 0.04em;
}

.message-body {
    margin: 0;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* "Read" receipt — a small, right-aligned line under the viewer's own latest message, the side their
   bubbles sit on (issue #79). Server-rendered for the no-script case; site.js toggles it live. */
.read-receipt {
    margin: 0 0 var(--space-4);
    text-align: right;
    font-size: var(--text-xs);
    color: var(--muted);
}

.read-receipt[hidden] {
    display: none;
}

.read-receipt-label {
    font-weight: 600;
}

/* Slot site.js fills with the sound toggle; it lives in the chat header's tool cluster, and with no
   script it is empty and collapses. */
.conversation-controls {
    display: flex;
    justify-content: flex-end;
    margin: 0;
}

.conversation-controls:empty {
    display: none;
}

/* Modern mute toggle — a compact icon + label pill. Scoped under .conversation-controls so it wins over
   the full-width terracotta .finder-thread button styling on the finder path. The bell rings in the
   brand accent while sound is on and goes quiet grey once muted; the icon itself flips to a struck bell. */
.conversation-controls .chime-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: 36px;
    margin: 0;
    padding: var(--space-2) var(--space-3);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-pill);
    background: var(--card);
    color: var(--muted);
    font: inherit;
    font-size: var(--text-xs);
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}

.conversation-controls .chime-toggle:hover {
    color: var(--ink);
    background: var(--panel);
    border-color: var(--border-strong);
}

.conversation-controls .chime-toggle:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.conversation-controls .chime-toggle.is-muted {
    color: var(--neutral);
}

.chime-toggle-icon {
    display: inline-flex;
    color: var(--accent); /* the bell rings in the brand accent while sound is on */
}

.chime-toggle.is-muted .chime-toggle-icon {
    color: var(--neutral);
}

.chime-toggle-icon svg {
    display: block;
}

code {
    overflow-wrap: anywhere;
}

.validation {
    color: #a3201b; /* readable error red on --bg */
}

/* ── "Save this link" — the finder's only way back (no account) ──────────────
   The bookmarkable thread URL, framed so it reads as something to keep. With no
   script it can't bookmark for them, so it presents the link clearly to save. */
.save-link {
    margin: 0 0 var(--space-5);
    padding: var(--space-3) var(--space-4);
    background: var(--card);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: 0 var(--radius) var(--radius) 0;
}

.save-link-label {
    margin: 0 0 var(--space-2);
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--accent-dark); /* AA on --card (5.3:1) */
}

.save-link-url {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    color: var(--ink);
    overflow-wrap: anywhere;
}

/* ── Optional "email me when they reply" (Code.cshtml, comp 02) ──────────────
   A distinct warm inline box so it reads as a quiet opt-in, not a required
   field. The owner never sees it (spec §10), as the help line states. */
.finder-callback {
    margin-top: var(--space-5);
    padding: var(--space-4);
    background: var(--accent-tint-warm);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.finder-callback label {
    margin-top: 0;
}

.finder-callback input[type="email"] {
    background: var(--card);
}

.finder-callback .note {
    margin-bottom: 0;
}

/* ── Finder dead-ends & read-only notices ───────────────────────────────────
   Used as a full screen (no-longer-active / already-home / not-found) and as a
   single read-only line (closed/frozen threads). A calm paper card either way. */
/* The full-screen variant (no-longer-active / already-home / not-found) is a
   calm paper card. Scoped to section. so the inline status <p> below — same
   class — never inherits the card background, border, or radius. */
section.finder-notice {
    margin: var(--space-5) 0;
    padding: var(--space-6);
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    text-align: center;
}

section.finder-notice h1 {
    margin-bottom: var(--space-3);
}

section.finder-notice p {
    color: var(--muted);
}

section.finder-notice a {
    color: var(--accent-dark);
    font-weight: 600;
    text-decoration: none;
}

section.finder-notice a:hover {
    text-decoration: underline;
}

/* The inline read-only / status variant (closed/frozen threads) — one quiet
   centered line, not a card. */
p.finder-notice {
    margin: var(--space-5) 0;
    color: var(--muted);
    text-align: center;
}


/* ── Owner desktop chrome (issue #45) ───────────────────────────────────────
   The owner area is a desktop product (Direction A, comp A5/A6 ≈ 1040px), not
   the finder's narrow mobile column. The shared layout tags <body> with
   .owner-page on every authenticated page (OwnerPageModel); these rules widen
   the container and dress the chrome, while finder/auth pages keep the 32rem
   column above. Still server-rendered and script-free, like the finder path. */
.owner-page .site-header,
.owner-page main,
.owner-page .site-footer {
    max-width: 65rem; /* ≈1040px — the comp's desktop width */
}

/* Top nav bar — wordmark left, links + "+ New code" right (comp A5). Three flex
   children (brand, nav, form): the nav's auto start-margin soaks up the free
   space, so the links and the button cluster right with the header's gap
   spacing them — visually identical to when the form lived inside the nav. */
.owner-page .site-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-5); /* matches the nav's own link gap, so Account ↔ "+ New code" reads as one group */
    margin-top: var(--space-4);
    padding: var(--space-3) var(--space-5);
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}

.owner-nav {
    display: inline-flex;
    align-items: center;
    gap: var(--space-5);
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--text-sm);
}

.owner-page .site-header .owner-nav {
    margin-inline-start: auto;
}

.owner-nav a {
    color: var(--muted);
    text-decoration: none;
}

.owner-nav a:hover {
    color: var(--ink);
}

/* Dashboard is the primary action — a tinted terracotta pill so it reads as the
   obvious home, with the other controls sitting quieter around it. */
.owner-nav a.owner-nav-primary {
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-pill);
    background: var(--accent-tint);
    color: var(--accent-dark);
    font-weight: 600;
}

.owner-nav a.owner-nav-primary:hover {
    background: var(--accent);
    color: var(--on-accent);
}

/* "+ New code" lives in the header as a primary action (a POST that mints a code). The form is
   layout-transparent so the button sits inline beside the nav. On the finder/auth pages an
   authenticated owner may visit, the header isn't a flexbox — the start margin recreates the
   link-gap spacing there (the owner header zeroes it and lets its own gap do the job). */
.owner-nav-new-form {
    display: inline-flex;
    margin: 0;
    margin-inline-start: var(--space-5);
}

.owner-page .site-header .owner-nav-new-form {
    margin-inline-start: 0;
}

/* A compact solid-terracotta pill — the bar's call to action. The appearance is styled in full here so it
   reads correctly even on the finder/auth pages an authenticated owner may visit (where the owner-page
   submit-button rule that would otherwise paint it doesn't apply). */
.owner-nav-new {
    display: inline-flex;
    align-items: center;
    font-family: var(--font-sans);
    font-weight: 600;
    font-size: var(--text-sm);
    color: var(--on-accent);
    background: var(--accent);
    border: 0;
    cursor: pointer;
}

.owner-nav-new:hover {
    background: var(--accent-dark);
}

/* Pill geometry, one source of truth: the bare class covers other pages, and the three-classes-deep
   selector carries the same values past the global submit-button rule (44px min-height, wider padding,
   square radius) on owner pages — so it can't drift between the two. */
.owner-nav-new,
.owner-page .owner-nav-new-form .owner-nav-new {
    min-height: 0;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-pill);
}

/* The Conversations link carries an unread count, so it lays out the text and badge in a row. */
.owner-nav-conversations {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

/* Unread count badge — a small terracotta pill, matching the dashboard's "Needs reply" flag, sized to
   sit comfortably against the nav text without enlarging the bar. */
.nav-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.25rem;
    height: 1.25rem;
    padding: 0 var(--space-2);
    border-radius: var(--radius-pill);
    background: var(--accent);
    color: var(--on-accent);
    font-size: var(--text-xs);
    font-weight: 700;
    line-height: 1;
}

button.link {
    border: 0;
    background: none;
    padding: 0;
    color: var(--muted);
    text-decoration: none;
    cursor: pointer;
    font: inherit;
}

button.link:hover {
    color: var(--ink);
}

/* Section wrappers. The dashboard and code detail span the full desktop width;
   the form/reading pages (account, new code, thread) stay in a comfortable
   reading column even inside the wide owner container. */
.owner-account,
.owner-code-new,
.owner-thread {
    max-width: 40rem;
    margin: 0 auto;
}

/* Solid terracotta button — primary owner action (also the finder CTA shape). */
a.button,
.owner-page button[type="submit"],
.owner-page .code-actions a.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: 44px; /* tap target ≥44px (§8) */
    padding: var(--space-3) var(--space-5);
    font-family: var(--font-sans);
    font-weight: 600;
    font-size: var(--text-sm);
    color: var(--on-accent);
    background: var(--accent);
    border: 0;
    border-radius: var(--radius);
    text-decoration: none;
    cursor: pointer;
}

a.button:hover,
.owner-page button[type="submit"]:hover {
    background: var(--accent-dark);
}

/* A disabled submit (e.g. at the per-owner code cap, spec §6 §14.4) reads as inert, not actionable. */
.owner-page button[type="submit"]:disabled,
.owner-page button[type="submit"]:disabled:hover {
    background: var(--muted);
    cursor: not-allowed;
    opacity: 0.55;
}

/* Small usage line under the create-code button: "N of M codes used" (spec §6, §14.4). */
.code-quota {
    margin: var(--space-3) 0 0;
    color: var(--muted);
    font-size: var(--text-sm);
}

/* ── Dashboard (comp A5) ────────────────────────────────────────────────────
   Header row (title + count, "+ New" on the right) over a 3-up card grid. */
.owner-dashboard-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-4);
    margin: var(--space-5) 0 var(--space-5);
}

.owner-dashboard-head h1 {
    margin: 0 0 var(--space-2);
    font-size: var(--text-2xl);
}

.owner-dashboard-sub {
    margin: 0;
    color: var(--muted);
    font-size: var(--text-sm);
}

/* The 3-up item grid. Collapses gracefully on narrower viewports. */
.code-grid {
    list-style: none;
    margin: 0 0 var(--space-7);
    padding: 0;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-5);
}

@media (max-width: 60rem) {
    .code-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 40rem) {
    .owner-page .site-header,
    .owner-page main,
    .owner-page .site-footer {
        max-width: 32rem;
    }

    /* Phone-width header: one row can't hold the wordmark plus four nav items
       (the bar used to clip — "+ New code" fell off the right edge entirely).
       It becomes two calm rows instead: wordmark left / "+ New code" right on
       top (justify-content: space-between spreads the pair), and the three
       links on their own full-width row below. Every label survives intact. */
    .owner-page .site-header {
        flex-wrap: wrap;
        row-gap: var(--space-3);
        padding: var(--space-3) var(--space-4);
    }

    /* order pushes the nav below the button's row; the 100% basis forces the
       wrap. Focus order still follows the DOM (links before button) — only the
       visual row order changes. */
    .owner-page .site-header .owner-nav {
        order: 1;
        flex-basis: 100%;
        margin-inline-start: 0;
        justify-content: space-between;
        gap: var(--space-3);
    }

    /* Thicker tap targets for the plain links on touch screens (§8). Matches
       the primary pill's block padding, so the row stays one height. */
    .owner-page .site-header .owner-nav a {
        padding-block: var(--space-2);
    }

    .code-grid {
        grid-template-columns: 1fr;
    }
}

.code-card {
    position: relative;
    background: var(--card);
    border: 1.5px solid var(--border);
    border-radius: var(--radius-lg);
}

/* A card with unread/open conversations is ringed in terracotta (comp). */
.code-card.has-new {
    border-color: var(--accent);
}

/* Recovered / disabled items recede — recessed surface, no pull (comp). */
.code-card.status-recovered,
.code-card.status-disabled {
    background: var(--panel);
    border-color: var(--border-strong);
}

.code-card-link {
    display: block;
    padding: var(--space-5);
    text-decoration: none;
    color: inherit;
}

/* "N new" badge — top-right pill (comp). */
.code-card-badge {
    position: absolute;
    top: var(--space-4);
    right: var(--space-4);
    padding: var(--space-1) var(--space-3);
    font-weight: 600;
    font-size: var(--text-xs);
    color: var(--on-accent);
    background: var(--accent);
    border-radius: var(--radius-pill);
}

.code-card-name {
    display: block;
    margin-bottom: var(--space-1);
    font-weight: 600;
    font-size: var(--text-lg);
    color: var(--ink);
}

.code-card-code {
    display: block;
    margin-bottom: var(--space-4);
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-sm);
    letter-spacing: var(--tracking-eyebrow);
    color: var(--muted);
}

.code-card-foot {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.code-card-meta {
    margin-left: auto;
    font-size: var(--text-xs);
    color: var(--neutral);
}

/* ── Status dot + label (comp) ──────────────────────────────────────────────
   Markup emits `status-<state>` on the status span; the leading dot is drawn
   with ::before so no extra markup is needed. Colour is paired with the text
   label already in the markup (never colour alone — §8). */
.code-status,
.thread-status {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-family: var(--font-sans);
    font-weight: 500;
    font-size: var(--text-sm);
    text-transform: none;
    letter-spacing: 0;
    color: var(--muted);
}

.code-status::before,
.thread-status::before {
    content: "";
    flex: none;
    width: 7px;
    height: 7px;
    border-radius: var(--radius-pill);
    background: currentColor;
}

/* Live, healthy states read green; ended/closed states read neutral grey. */
.status-active,
.status-open {
    color: var(--success);
}

.status-recovered,
.status-disabled,
.status-closedbyowner,
.status-frozen {
    color: var(--neutral);
}

/* "Needs reply" — promoted from plain red text to a terracotta pill (§4). */
.flag {
    display: inline-flex;
    align-items: center;
    padding: var(--space-1) var(--space-3);
    font-weight: 600;
    font-size: var(--text-xs);
    color: var(--accent-dark);
    background: var(--accent-tint);
    border-radius: var(--radius-pill);
    white-space: nowrap; /* "Needs reply" stays on one line, even in the narrow summary sidebar */
}

.muted-flag {
    font-size: var(--text-xs);
    color: var(--neutral);
}

/* The one-line helper under the code's Manage actions (e.g. the pointer to notification settings). */
.action-note {
    margin: var(--space-3) 0 0;
    font-size: var(--text-sm);
    color: var(--muted);
}

/* ── Account · Notifications settings ───────────────────────────────────────
   Per-channel cards (email always-on, SMS and webhook opt-in) each listing their registered
   destinations with a verify/confirm form and an on/off toggle (spec §9). */
.notify-channel {
    margin: 0 0 var(--space-5);
    padding: var(--space-4);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--card);
}

.notify-channel h3 {
    margin: 0 0 var(--space-2);
    font-size: var(--text-lg, 1.05rem);
}

.notify-channel-state {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--muted);
}

.notify-hint {
    margin: 0 0 var(--space-3);
    font-size: var(--text-sm);
    color: var(--muted);
}

.notify-dest {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    padding: var(--space-3) 0;
    border-top: 1px solid var(--border);
}

.notify-dest-value {
    font-family: var(--font-mono, ui-monospace, monospace);
    word-break: break-all;
}

.notify-dest-badge {
    font-size: var(--text-xs);
    font-weight: 600;
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-pill);
    background: var(--accent-tint);
    color: var(--accent-dark);
}

.notify-dest.is-pending .notify-dest-badge {
    background: var(--surface-2, var(--accent-tint));
    color: var(--muted);
}

.notify-confirm {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    gap: var(--space-2);
}

.notify-add {
    margin-top: var(--space-4);
}

/* A quiet, text-link "Remove" beside a registered destination — not a prominent button. */
.notify-dest .link-button {
    width: auto;
    margin-left: auto;
    padding: 0;
    background: none;
    border: none;
    color: var(--muted);
    font-size: var(--text-sm);
    text-decoration: underline;
    cursor: pointer;
}

.notify-dest .link-button:hover {
    color: var(--text, inherit);
}

/* ── Recent activity / thread lists ─────────────────────────────────────────
   A calm, carded row list — the dashboard's recent activity. */
.owner-dashboard h2 {
    font-size: var(--text-xl);
    margin-bottom: var(--space-4);
}

.activity-list {
    list-style: none;
    margin: 0 0 var(--space-6);
    padding: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--card);
}

.activity-item {
    border-bottom: 1px solid var(--border);
}

.activity-item:last-child {
    border-bottom: 0;
}

.activity-item.flagged {
    border-left: 3px solid var(--accent);
}

/* The whole row is one link, so the entire area is clickable — not just the name.
   The flex layout and padding live on the anchor so its hit area fills the row. */
.activity-item > a {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-4);
    padding: var(--space-4) var(--space-5);
    color: var(--ink);
    text-decoration: none;
    transition: background 0.15s ease;
}

/* :focus-visible mirrors :hover so keyboard users get the same full-row affordance. */
.activity-item > a:hover,
.activity-item > a:focus-visible {
    background: var(--panel);
}

/* The code's label leads the row in bold; it picks up the accent on hover/focus. */
.activity-item-name {
    font-weight: 600;
}

.activity-item > a:hover .activity-item-name,
.activity-item > a:focus-visible .activity-item-name {
    color: var(--accent-dark);
}

.preview {
    flex-basis: 100%;
    color: var(--muted);
    font-size: var(--text-sm);
}

.activity-item time {
    margin-left: auto;
    color: var(--neutral);
    font-size: var(--text-xs);
}

/* ── Code detail — main rail + compact conversations summary ─────────────────
   The code (QR, finder note, actions) is the page; conversations is now just a
   summary linking to the Inbox, so it sits in a narrow sidebar rather than
   claiming half the width (comp A6 paired it with a full thread list). The panel
   is capped to a focused width and centred, like the owner form pages. */
.owner-code-detail {
    margin-top: var(--space-5);
    max-width: 50rem;
    margin-inline: auto;
}

.owner-code-detail .back {
    margin: 0 0 var(--space-4);
}

.owner-code-detail .back a {
    color: var(--muted);
    text-decoration: none;
    font-size: var(--text-sm);
}

.code-detail-rail {
    padding: var(--space-6);
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
}

/* Identity header — the QR beside the item's name, its code, and its status: everything
   that says "this is the tag on your blue suitcase" in one glance, each fact once. */
.code-identity {
    display: flex;
    align-items: center;
    gap: var(--space-4);
}

.code-identity-text {
    min-width: 0; /* let the heading wrap instead of pushing past the rail on a phone */
}

.code-identity h1 {
    margin: 0;
    font-size: var(--text-2xl);
}

/* An unnamed code is headlined by the code itself — mono like every code in the app,
   a step smaller than the serif headline since mono runs wider and louder. */
.code-identity h1.untitled-code {
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-xl);
    letter-spacing: var(--tracking-eyebrow);
}

/* The code sits in a real <code> element for semantics; the heading carries the type. */
.code-identity h1.untitled-code code {
    font: inherit;
    letter-spacing: inherit;
}

.code-identity-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin: var(--space-2) 0 0;
}

.code-identity-code {
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-sm);
    letter-spacing: var(--tracking-eyebrow);
    color: var(--ink);
}

.rail-section {
    margin-top: var(--space-5);
}

.rail-section > .eyebrow {
    display: block;
    margin-bottom: var(--space-3);
}

/* QR thumbnail — the code's real QR (generated by the /qr handler), shown in the identity
   header. A white-matted tile so the dark modules read clearly. */
.qr-thumb {
    display: block;
    width: 64px;
    height: 64px;
    border-radius: var(--radius-sm);
    background: #fff;
    border: 1px solid var(--border-strong);
    padding: 4px;
    box-sizing: border-box;
    image-rendering: pixelated; /* keep module edges crisp when the PNG is scaled */
}

/* The thumbnail is a link: it opens the up-close QR dialog (owner.js), or without script goes to
   the print-labels page. zoom-in cursor + a slight lift say "see it bigger"; the corner magnifier
   badge says so on touch screens, where hover can't. */
a.qr-thumb-link {
    position: relative;
    flex: none;
    display: block;
    border-radius: var(--radius-sm);
    cursor: zoom-in;
    transition: transform 0.12s ease;
}

a.qr-thumb-link:hover {
    transform: translateY(-1px) scale(1.03);
}

a.qr-thumb-link:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.qr-thumb-zoom {
    position: absolute;
    right: -6px;
    bottom: -6px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    color: var(--muted);
    background: var(--card);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-sm);
}

a.qr-thumb-link:hover .qr-thumb-zoom {
    color: var(--accent);
}

@media (prefers-reduced-motion: reduce) {
    a.qr-thumb-link,
    a.qr-thumb-link:hover {
        transition: none;
        transform: none;
    }
}

/* ── The QR up close (Owner/CodeDetail dialog) ───────────────────────────────
   Opened from the identity thumbnail (owner.js). Native <dialog>, so the open
   state lives in the top layer — the rail's overflow can't clip it. Padding
   lives on .qr-dialog-body: a click that lands on the dialog element itself is
   therefore unambiguously the backdrop, which owner.js treats as a dismissal. */
.qr-dialog {
    width: min(24rem, calc(100vw - 2 * var(--space-5)));
    padding: 0;
    color: var(--ink);
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
}

.qr-dialog::backdrop {
    background: rgba(36, 31, 26, 0.45); /* --ink at 45% — a warm dim, not pure black */
}

.qr-dialog-body {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: var(--space-6);
    text-align: center;
}

.qr-dialog-close {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    padding: 0;
    font-size: 1.25rem;
    line-height: 1;
    color: var(--muted);
    background: transparent;
    border: 0;
    border-radius: var(--radius-pill);
    cursor: pointer;
}

.qr-dialog-close:hover {
    color: var(--ink);
    background: var(--panel);
}

.qr-dialog-close:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.qr-dialog-title {
    margin: 0;
    font-size: var(--text-xl);
    overflow-wrap: anywhere; /* a long unspaced label must wrap, not push past the card */
}

.qr-dialog-hint {
    margin: var(--space-2) 0 0;
    color: var(--muted);
    font-size: var(--text-sm);
}

/* The QR itself: the thumbnail's white matte, sized so a phone camera locks on from a
   comfortable distance (the inline SVG scales losslessly and keeps its quiet zone). */
.qr-dialog-qr {
    width: min(16rem, 100%);
    margin: var(--space-5) 0 0;
    padding: var(--space-3);
    background: #fff;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
}

.qr-dialog-qr svg {
    display: block;
    width: 100%;
    height: auto;
}

.qr-dialog-code {
    margin: var(--space-4) 0 0;
}

.qr-dialog-code code {
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-lg);
    letter-spacing: var(--tracking-code);
}

.qr-dialog-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-3);
    margin-top: var(--space-5);
}

/* The copy button is type="button", so it misses the .owner-page button[type="submit"] base —
   give it the same shape here; the .secondary class supplies its quiet colours. */
.qr-dialog-actions button[type="button"] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: 44px;
    padding: var(--space-3) var(--space-5);
    font-family: var(--font-sans);
    font-weight: 600;
    font-size: var(--text-sm);
    border-radius: var(--radius);
    cursor: pointer;
}

/* Finder-facing note — the warm, serif-italic message shown on the tag. */
.finder-note {
    padding: var(--space-4);
    font-family: var(--font-serif);
    font-style: italic;
    font-size: var(--text-base);
    line-height: var(--leading-snug);
    color: var(--ink);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.finder-note.is-empty {
    font-style: normal;
    color: var(--muted);
}

/* Caption under the note naming where the shown greeting comes from (account default / built-in). */
.finder-note-source {
    margin: var(--space-2) 0 0;
    font-size: var(--text-sm);
    color: var(--muted);
}

/* Structured owner fields — quiet definition rows (account page). */
.account-fields {
    margin: var(--space-5) 0 0;
}

.account-fields dt {
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--muted);
}

.account-fields dd {
    margin: var(--space-1) 0 var(--space-4);
    color: var(--ink);
}

/* The sign-out controls (moved here from the header) sit side by side, wrapping on a narrow screen. */
.account-signout {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}

/* "Edit name & finder note" link on the code detail rail (spec §11): a quiet secondary control that
   opens the dedicated setup editor — the single place a code's metadata is authored. */
.code-edit-link {
    margin: var(--space-4) 0 0;
    font-family: var(--font-sans);
    font-weight: 600;
    font-size: var(--text-sm);
}

.code-edit-link a {
    color: var(--accent);
    text-decoration: none;
}

.code-edit-link a:hover {
    text-decoration: underline;
}

/* Action cluster — primary (Mark recovered) plus quieter secondaries. */
.code-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin: var(--space-5) 0 0;
}

.code-actions form {
    display: inline-flex;
    margin: 0;
}

/* Quiet/secondary owner buttons — tertiary actions sit below the terracotta
   primary so it stays the obvious next step. Marked with .secondary in markup. */
.owner-page button.secondary,
.owner-page a.button.secondary {
    color: var(--muted);
    background: var(--card);
    border: 1.5px solid var(--border-strong);
}

.owner-page button.secondary:hover,
.owner-page a.button.secondary:hover {
    color: var(--ink);
    background: var(--panel);
}

/* ── Conversations inbox ─────────────────────────────────────────────────────
   A messaging-app two-pane: the chat list (every conversation across all codes,
   newest first) beside the open conversation. Desktop shows both; below 52rem it
   becomes one pane at a time like a phone — the list, or the open thread with a
   back link. Still server-rendered and script-free (spec §13). */
.conversations-head {
    margin: var(--space-5) 0 var(--space-5);
}

.conversations-head h1 {
    margin: 0 0 var(--space-2);
    font-size: var(--text-2xl);
}

.conversations-sub {
    margin: 0;
    color: var(--muted);
    font-size: var(--text-sm);
}

.conversations-pane {
    display: grid;
    grid-template-columns: 22rem 1fr;
    gap: 0;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    /* A fixed app-shell height so both columns share one height and each scrolls on its own — the
       left rail's border/background then always reaches the bottom, however long the open thread is.
       min-height is the floor on short viewports. The mobile layout below resets this to auto. */
    height: 75vh;
    min-height: 30rem;
}

/* Left rail — the chat list, scrolling within the pane's height. */
.conversation-list {
    border-right: 1px solid var(--border);
    overflow-y: auto;
}

.conversation-list ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

.conversation-empty {
    padding: var(--space-6);
}

.conversation-row + .conversation-row {
    border-top: 1px solid var(--border);
}

/* A row waiting on the owner gets the terracotta edge used elsewhere for flags. */
.conversation-row.flagged {
    border-left: 3px solid var(--accent);
}

/* An unread conversation reads bolder, like a chat/mail list, and carries a terracotta dot before its
   name. The dot is the same accent used by the nav badge and "Needs reply" flag, so "new" looks the
   same everywhere. */
.conversation-row.is-unread .conversation-name {
    font-weight: 700;
    color: var(--ink);
}

.conversation-row.is-unread .conversation-preview {
    color: var(--ink);
}

.conversation-unread-dot {
    display: inline-block;
    width: 0.5rem;
    height: 0.5rem;
    margin-right: var(--space-2);
    border-radius: var(--radius-pill);
    background: var(--accent);
    vertical-align: middle;
}

.conversation-row > a {
    display: flex;
    gap: var(--space-3);
    align-items: flex-start;
    padding: var(--space-4);
    text-decoration: none;
    color: inherit;
}

.conversation-row > a:hover {
    background: var(--panel);
}

/* The open conversation is held lit in the list, like a desktop mail/chat client. */
.conversation-row.is-active > a {
    background: var(--accent-tint);
}

/* Round avatar swatch — the woven-tape motif from the code cards, in a disc. */
.conversation-swatch {
    flex: none;
    width: 38px;
    height: 38px;
    border-radius: var(--radius-pill);
    background: repeating-linear-gradient(
        135deg,
        var(--accent-tint) 0,
        var(--accent-tint) 6px,
        var(--accent-tint-warm) 6px,
        var(--accent-tint-warm) 12px
    );
}

.conversation-main {
    flex: 1 1 auto;
    min-width: 0; /* let the children truncate instead of overflowing the rail */
}

.conversation-row-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-2);
}

.conversation-name {
    font-weight: 600;
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.conversation-row-top time {
    flex: none;
    color: var(--neutral);
    font-size: var(--text-xs);
}

/* One-line message preview — truncates with an ellipsis like a chat list. */
.conversation-preview {
    display: block;
    margin-top: var(--space-1);
    color: var(--muted);
    font-size: var(--text-sm);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.conversation-preview.is-empty {
    color: var(--neutral);
}

.conversation-row-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    margin-top: var(--space-2);
}

.conversation-row-meta .thread-status {
    font-size: var(--text-xs);
}

/* Right pane — the open conversation (the shared _ConversationDetail partial). Scrolls within the
   pane's height so a long thread never stretches the rail beside it. The chat shell inside brings its
   own spacing, so the pane itself is flush — that lets the shell's sticky header and composer dock hug
   the pane's scrollport edges. */
.conversation-detail {
    padding: 0;
    min-width: 0;
    overflow-y: auto;
}

/* Empty-state prompt shown on desktop when no conversation is open. */
.conversation-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 18rem;
    height: 100%;
    padding: var(--space-6);
    text-align: center;
}

/* In the desktop two-pane the chat list is already beside the thread, so the chat header's back
   control is redundant chrome — the phone layout below restores it. */
.conversations-pane .chat-back {
    display: none;
}

@media (max-width: 52rem) {
    /* One pane at a time, full width, scrolling with the page — drop the desktop app-shell height
       AND its 30rem floor so the visible pane is exactly as tall as its content (no empty space on a
       short list/thread, no overflow in landscape) instead of a fixed viewport slice. */
    .conversations-pane {
        grid-template-columns: 1fr;
        height: auto;
        min-height: auto;
    }

    .conversation-list {
        border-right: 0;
        border-bottom: 1px solid var(--border);
    }

    .conversation-detail {
        overflow-y: visible;
    }

    /* One pane at a time: the list, or — once a conversation is open — the thread. */
    .conversations.has-selection .conversation-list {
        display: none;
    }

    .conversations:not(.has-selection) .conversation-detail {
        display: none;
    }

    /* One pane at a time again, so back to the list matters — show the chat header's back control. */
    .conversations-pane .chat-back {
        display: inline-flex;
    }
}

/* ── Owner forms (new code, account) ────────────────────────────────────────
   Carded reading column with framed inputs in the Direction A field style. */
.owner-code-new,
.owner-account {
    padding: var(--space-6);
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    margin-top: var(--space-5);
}

.owner-code-new label,
.owner-account label,
.owner-thread form label,
.conversation-detail form label {
    display: block;
    margin: var(--space-4) 0 var(--space-2);
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--muted);
}

.owner-code-new input[type="text"],
.owner-code-new textarea,
.owner-account input[type="text"],
.owner-account textarea,
.owner-thread form textarea,
.conversation-detail form textarea {
    display: block;
    width: 100%;
    padding: var(--space-3) var(--space-4);
    font: inherit;
    color: var(--ink);
    background: var(--card);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius);
}

/* The delete-confirmation box sits directly above its danger button; keep them
   grouped and give the button its usual top gap. */
.account-delete button.danger {
    margin-top: var(--space-3);
}

.owner-code-new textarea,
.owner-account textarea,
.owner-thread form textarea,
.conversation-detail form textarea {
    resize: vertical;
    line-height: var(--leading-snug);
}

.owner-code-new input::placeholder,
.owner-code-new textarea::placeholder,
.owner-account textarea::placeholder,
.owner-thread form textarea::placeholder,
.conversation-detail form textarea::placeholder {
    color: var(--neutral);
}

.owner-code-new button[type="submit"],
.account-default-message button[type="submit"],
.owner-thread form button[type="submit"],
.conversation-detail form button[type="submit"] {
    margin-top: var(--space-3);
}

.owner-code-new .back,
.owner-account .back {
    margin: 0 0 var(--space-4);
    font-size: var(--text-sm);
}

/* Back links across owner pages get a thicker tap slab on touch screens,
   like the header nav links — the text alone is well under 44px. */
.owner-page .back a {
    display: inline-block;
    padding-block: var(--space-2);
}

/* Account sections are settings labels, not page titles: step them down to the
   dashboard's h2 size and give every section the same breathing room above
   (the sign-out button row otherwise sits flush against the next heading). */
.owner-account h2 {
    margin-top: var(--space-6);
    font-size: var(--text-xl);
}

/* Destructive action — outlined, never the loud terracotta of a safe action. */
button.danger {
    min-height: 44px;
    padding: var(--space-3) var(--space-5);
    font-family: var(--font-sans);
    font-weight: 600;
    font-size: var(--text-sm);
    color: #8a2b1f; /* AA-safe danger red on --card */
    background: var(--card);
    border: 1.5px solid #d8b3a8;
    border-radius: var(--radius);
    cursor: pointer;
}

button.danger:hover {
    color: var(--on-accent);
    background: #8a2b1f;
    border-color: #8a2b1f;
}

/* Disabled while the confirmation phrase is unmet (owner.js): mute it and drop the
   hover fill so it reads as unavailable, not armed. */
button.danger:disabled {
    color: var(--neutral);
    border-color: var(--border);
    cursor: not-allowed;
}

button.danger:disabled:hover {
    color: var(--neutral);
    background: var(--card);
    border-color: var(--border);
}

/* Notices. Confirmations read calm-green; alerts reuse .form-error above. */
.form-notice {
    color: #1f5130; /* readable confirmation green */
    background: #e8efe3;
    border-radius: var(--radius-sm);
    padding: var(--space-2) var(--space-3);
}

/* Generic small-print classes (also used by the standalone admin view). */
.metric {
    font-size: var(--text-2xl);
    font-weight: 600;
    color: var(--ink);
}

.muted {
    color: var(--muted);
}

/* ── Printable QR labels (issue #54, spec §12/§15) ───────────────────────────
   The three Direction A label designs (comp 05–07) plus the print stylesheet.
   On screen: a preview with a design switcher. In print (@media print, below):
   only the .label-sheet survives, so "Save as PDF" yields a clean tag sheet. */
.label-page-chrome {
    margin-bottom: var(--space-6);
}

.label-page-chrome .back {
    margin: 0 0 var(--space-4);
    font-size: var(--text-sm);
}

.label-howto {
    max-width: 46rem;
    font-size: var(--text-sm);
}

.label-howto code {
    font-family: var(--font-mono);
    color: var(--ink);
}

/* Configurator — one calm card: chip toggles (types), a segmented control (size), and switch toggles
   (QR / web address). A GET form so the configured sheet stays shareable; owner.js re-fetches the
   preview on every change and intercepts "Print" to open the print dialog on the PDF. */
.label-config {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    max-width: 44rem;
    margin-top: var(--space-4);
    padding: var(--space-5);
    background: var(--card);
    border: 1.5px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}

.label-config-group {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

/* Size + toggles sit side by side on wide screens, stack on narrow. */
.label-config-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-6);
}

.label-config-label {
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--eyebrow);
}

.label-config-hint {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--muted);
}

/* The real inputs drive state but are visually replaced by the styled chips/segments/switches. */
.label-chip input,
.label-seg input,
.label-ink input,
.label-switch > input[type="checkbox"] {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

/* Type chips — pill toggles that fill terracotta when selected. */
.label-chip-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.label-chip {
    display: inline-flex;
    align-items: center;
    padding: 0.45rem 0.95rem;
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-pill);
    background: var(--bg);
    color: var(--muted);
    font-size: var(--text-sm);
    font-weight: 500;
    cursor: pointer;
    user-select: none;
    transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}

.label-chip:hover {
    color: var(--ink);
    border-color: var(--accent);
}

.label-chip:has(input:checked) {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
}

.label-chip:has(input:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Size — a segmented control (connected pills on a recessed track). */
.label-segmented {
    display: inline-flex;
    flex-wrap: wrap; /* the size options stay one row when they fit, wrapping on narrow screens */
    gap: 2px;
    padding: 3px;
    background: var(--panel);
    border-radius: var(--radius-pill);
}

.label-seg {
    position: relative;
    cursor: pointer;
}

.label-seg span {
    display: block;
    padding: 0.35rem 0.95rem;
    border-radius: var(--radius-pill);
    font-size: var(--text-sm);
    color: var(--muted);
    transition: background 0.12s ease, color 0.12s ease;
}

.label-seg:has(input:checked) span {
    background: var(--card);
    color: var(--ink);
    box-shadow: var(--shadow-sm);
}

.label-seg:has(input:focus-visible) span {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Toggle switches — QR / web address. */
.label-switch-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-5);
}

.label-switch {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    color: var(--ink);
    cursor: pointer;
}

.label-switch-track {
    position: relative;
    flex: none;
    width: 38px;
    height: 22px;
    border-radius: var(--radius-pill);
    background: var(--border-strong);
    transition: background 0.15s ease;
}

.label-switch-track::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: #fff;
    box-shadow: var(--shadow-sm);
    transition: transform 0.15s ease;
}

.label-switch:has(input:checked) .label-switch-track {
    background: var(--accent);
}

.label-switch:has(input:checked) .label-switch-track::after {
    transform: translateX(16px);
}

.label-switch:has(input:focus-visible) .label-switch-track {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Ink — round colour swatches (one per LabelInk); the chosen one carries a ring. The colour applies to
   the QR modules (re-encoded server-side) and the code text (via --label-ink on the sheet). */
.label-ink-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.label-ink {
    display: inline-flex;
    cursor: pointer;
}

.label-ink-swatch {
    display: block;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    border: 2px solid var(--card);
    box-shadow: 0 0 0 1.5px var(--border-strong);
    transition: box-shadow 0.12s ease, transform 0.12s ease;
}

.label-ink:hover .label-ink-swatch {
    box-shadow: 0 0 0 1.5px var(--accent);
}

.label-ink:has(input:checked) .label-ink-swatch {
    box-shadow: 0 0 0 2px var(--ink);
    transform: scale(1.08);
}

.label-ink:has(input:focus-visible) .label-ink-swatch {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Action row — the primary "Print" and a quiet note. */
.label-config-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: center;
}

.label-config-note {
    font-size: var(--text-xs);
    color: var(--muted);
}

/* The sheet of repeated labels. Flex-wrap so it reflows; each label avoids a
   page break so it never splits across printed pages. */
.label-sheet {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-5);
    align-items: flex-start;
}

.label-sheet > * {
    break-inside: avoid;
    page-break-inside: avoid; /* legacy spelling for older print engines */
}

/* Size scales the whole tile in the preview (the PDF lays out a denser/looser grid). zoom reflows the
   flex wrap, unlike transform: scale, so the sheet stays tightly packed at any size. The luggage tag and
   wallet card are excluded: they always print at their own fixed real-world size (see .lt and .wc below),
   so the Size picker never scales them — matching LabelSizes.FixedCardSize server-side. */
.label-sheet.size-small > *:not(.design-luggage):not(.design-wallet) {
    zoom: 0.78;
}

.label-sheet.size-large > *:not(.design-luggage):not(.design-wallet) {
    zoom: 1.22;
}

/* Avery 8293 is a sheet of small 1½″ round labels, so the preview tiles read smaller than "Small". */
.label-sheet.size-avery8293 > *:not(.design-luggage):not(.design-wallet) {
    zoom: 0.62;
}

/* Avery 35523 is a sheet of larger 2″×4″ rectangular labels, so the preview tiles read a touch larger. */
.label-sheet.size-avery35523 > *:not(.design-luggage):not(.design-wallet) {
    zoom: 1.1;
}

/* When the QR is off the re-fetched sheet just omits the QR markup and carries a .qr-hidden marker; this
   rule is a belt-and-suspenders hide for any QR node that lingers mid-swap. */
.label-sheet.qr-hidden .label-qr {
    display: none;
}

/* The owner's ink option (?ink=) lands as an inline --label-ink on the sheet — set only for a
   non-default ink, so each design's own palette holds otherwise. It recolours the code text on the
   dark-on-light designs (the QR is re-encoded server-side in the same colour); the cream-on-terracotta
   text (luggage tag, clay stickers) keeps its design colour, where a dark ink would vanish. */

/* Shared finder-URL line printed beside the code (ifyoufind.me/XXXX-XXXX). Mono and quiet so it reads
   as the "where to go" under the prominent code. */
.label-url,
.lt-url,
.st-url,
.pt-url,
.bs-url,
.wc-url {
    font-family: var(--font-mono);
    font-size: 9px;
    letter-spacing: 0.01em;
    word-break: break-all;
}

/* The QR itself: the SVG is viewBox-sized, so it fills whatever box it sits in. */
.label-qr {
    line-height: 0;
}

.label-qr svg {
    display: block;
    width: 100%;
    height: auto;
}

/* 05 · Luggage tag — terracotta card, cream ink, prominent QR. Fixed to the standard luggage-tag /
   business-card insert size (2in × 3.5in) rather than the Size picker, matching
   LabelSizes.FixedCardSize — see the .label-sheet.size-* zoom exclusions above. */
.lt {
    width: 2in;
    height: 3.5in;
    box-sizing: border-box;
    background: var(--accent);
    border-radius: 18px;
    padding: 0.3in 0.22in;
    box-shadow: 0 10px 24px -10px rgba(120, 60, 30, 0.6);
    position: relative;
    display: flex;
    flex-direction: column;
    text-align: center;
}

.lt-hole {
    display: block;
    width: 26px;
    height: 26px;
    border: 4px solid var(--bg);
    border-radius: 50%;
    margin: 0 auto 14px;
    flex: none;
}

/* Fills the space below the punch hole and centres its content (QR/url/caption) within it. */
.lt-body {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    flex: 1;
    width: 100%;
}

.lt-qr {
    width: 120px;
    background: var(--card);
    border-radius: 10px;
    padding: 12px;
    margin: 0 auto 12px;
}

.lt-tagline {
    font-size: 10px;
    color: #f3cdb8;
    margin-top: 8px;
    max-width: 120px; /* never wider than .lt-qr, the caption's anchor */
}

/* 06 · Sticker set — round two-tone stickers, alternating paper / clay. */
.label-sheet.design-sticker {
    max-width: 460px;
}

.st {
    width: 150px;
    height: 150px;
    border-radius: 50%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 14px;
    box-sizing: border-box;
}

.st.paper {
    background: var(--bg);
    border: 1.5px solid var(--border);
}

.st.clay {
    background: var(--accent);
}

.st-qr {
    width: 58px;
    background: var(--card);
    border-radius: 6px;
    padding: 4px;
    margin-bottom: 6px;
}

.st-tagline {
    font-size: 8px;
    margin-top: 3px;
    max-width: 58px; /* never wider than .st-qr, the caption's anchor */
}

.st.paper .st-tagline {
    color: var(--muted);
}

.st.clay .st-tagline {
    color: var(--card);
}

/* 07 · Pet collar tag — round disc, warm gradient, "scan to reach my human". */
.pt {
    width: 168px;
    height: 168px;
    border-radius: 50%;
    background: radial-gradient(circle at 35% 30%, #fbe9da, #e9b694 70%);
    border: 3px solid #d9a787;
    box-shadow: inset 0 2px 6px rgba(255, 255, 255, 0.6), 0 8px 18px -8px rgba(120, 60, 30, 0.5);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 18px;
    box-sizing: border-box;
    position: relative;
}

.pt-hole {
    display: block;
    width: 13px;
    height: 13px;
    border: 2.5px solid #b9805e;
    border-radius: 50%;
    margin-bottom: 4px;
}

.pt-qr {
    width: 56px;
    background: var(--card);
    border-radius: 6px;
    padding: 4px;
    margin-bottom: 5px;
}

.pt-tagline {
    font-size: 8.5px;
    color: #9a5230;
    margin-top: 5px;
    max-width: 56px; /* never wider than .pt-qr, the caption's anchor */
}

/* Per-design finder-URL colours. The URL is the QR-less fallback, so it takes each design's
   highest-contrast text colour (its own code colour) rather than the dimmer tagline tint — as legible
   as the brand terracotta allows (size, not a faint colour, keeps it secondary to the code). */
.lt-url {
    color: var(--card);
    margin-top: 4px;
}

.st-url {
    font-size: 8px;
    margin-top: 3px;
    max-width: 100%;
}

.st.paper .st-url {
    color: var(--ink);
}

.st.clay .st-url {
    color: var(--card);
}

.pt-url {
    color: #5c3a26;
    font-size: 8px;
    margin-top: 3px;
    max-width: 128px;
}

/* Basic — a plain rectangular label: optional QR, finder URL, caption. "Just the basics," no tag styling,
   no brand wordmark (the URL carries it). The most economical option. */
.bs {
    width: 188px;
    box-sizing: border-box;
    background: var(--card);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-sm);
    padding: 18px 16px;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
}

.bs-qr {
    width: 96px;
    background: var(--card);
    margin-bottom: 10px;
}

.bs-url {
    color: var(--muted);
    margin-top: 6px;
}

.bs-tagline {
    color: var(--muted);
    font-size: 9px;
    margin-top: 6px;
    max-width: 96px; /* never wider than .bs-qr, the caption's anchor */
}

/* Wallet card — a credit-card-proportioned insert for a wallet, bag pocket, or laptop sleeve. QR beside
   the URL, a short caption below. Fixed to a hair under the ISO/IEC 7810 ID-1 credit-card size
   (3.3in × 2.1in) rather than the Size picker, matching LabelSizes.FixedCardSize — see the
   .label-sheet.size-* zoom exclusions above. */
.wc {
    width: 3.3in;
    height: 2.1in;
    box-sizing: border-box;
    background: var(--card);
    border: 1.5px solid var(--accent);
    border-radius: 12px;
    padding: 16px;
    box-shadow: var(--shadow-sm);
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.wc-main {
    display: flex;
    align-items: center;
    gap: 14px;
}

.wc-qr {
    width: 64px;
    flex: none;
    background: var(--card);
}

.wc-text {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 3px;
    min-width: 0;
}

.wc-url {
    color: var(--muted);
}

.wc-tagline {
    margin: 10px auto 0;
    font-size: 9px;
    color: var(--muted);
    text-align: center;
    max-width: 64px; /* never wider than .wc-qr, the caption's anchor */
}

/* Print: strip every chrome element so the page is just the tag sheet. The owner
   prints to PDF or paper from here. No app today carries any other @media print
   rules — these are scoped tightly to the label sheet. */
@media print {
    /* Drop the warm page background so tags print on white without wasting ink. */
    body {
        background: #fff;
    }

    .site-header,
    .site-footer,
    .label-page-chrome {
        display: none !important;
    }

    main {
        margin: 0;
        padding: 0;
        max-width: none;
    }

    .label-sheet {
        gap: 16px;
    }

    /* Keep the colored tag fills when printing (owners expect the terracotta). */
    .lt,
    .st,
    .pt,
    .bs,
    .wc {
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }

    /* Size scaling is a preview affordance only; the page-print fallback uses the unscaled tags. */
    .label-sheet.size-small > *,
    .label-sheet.size-large > *,
    .label-sheet.size-avery8293 > *,
    .label-sheet.size-avery35523 > * {
        zoom: 1;
    }
}

/* ---------------------------------------------------------------------------
   Code-authoring UX: the setup-screen form, suggestion chips, character counters,
   the collapsible finder preview, the detail-page finder URL + print CTA, and the
   dashboard welcome/empty state. Progressively enhanced by wwwroot/js/owner.js.
   --------------------------------------------------------------------------- */

/* One-tap label suggestions. */
.suggest-chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: 0 0 var(--space-3);
}

.suggest-chips .chip {
    padding: var(--space-1) var(--space-3);
    font-family: var(--font-sans);
    font-size: var(--text-sm);
    color: var(--accent-dark);
    background: var(--accent-tint);
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    cursor: pointer;
}

.suggest-chips .chip:hover {
    border-color: var(--accent);
}

/* Live character counter appended after a [data-char-count] field. */
.char-count {
    display: block;
    margin-top: var(--space-1);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--neutral);
    text-align: right;
}

.char-count.is-full {
    color: var(--accent-dark);
}

/* The finder preview, demoted to a quiet disclosure on the setup screen (issue: signup UX): a calm,
   underlined "unimportant link" by default that reveals the "what a finder sees" card only on demand,
   instead of a card competing with the form. */
.finder-preview-disclosure {
    margin: var(--space-4) 0;
}

.finder-preview-disclosure > summary {
    display: inline-block;
    cursor: pointer;
    font-family: var(--font-sans);
    font-size: var(--text-sm);
    color: var(--muted);
    text-decoration: underline;
    list-style: none;
}

.finder-preview-disclosure > summary::-webkit-details-marker {
    display: none;
}

.finder-preview-disclosure > summary:hover {
    color: var(--ink);
}

.finder-preview-disclosure[open] > summary {
    margin-bottom: var(--space-3);
}

.finder-preview-card {
    padding: var(--space-5);
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}

.finder-preview-brand {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--accent-dark);
}

.finder-preview-card h2 {
    margin: var(--space-2) 0 var(--space-3);
    font-family: var(--font-serif);
    font-size: var(--text-xl);
    line-height: var(--leading-tight);
}

.finder-preview-card .owner-message.is-empty {
    color: var(--muted);
    font-style: italic;
}

/* Honour the owner's line breaks and wrap long unbroken tokens — in the live previews AND the real
   finder landing (both use .owner-message), so the preview matches exactly what a finder sees. */
.owner-message,
.finder-note,
[data-preview-target] {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.finder-preview-box {
    margin-top: var(--space-4);
    padding: var(--space-3) var(--space-4);
    color: var(--neutral);
    background: var(--bg);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius);
}

.finder-preview-btn {
    margin-top: var(--space-3);
    padding: var(--space-2) var(--space-4);
    font-weight: 600;
    font-size: var(--text-sm);
    text-align: center;
    color: var(--on-accent);
    background: var(--accent);
    border-radius: var(--radius);
}

/* The setup screen: the inline "your code is ready" chip and the quiet "skip for now" link. */
.code-inline {
    font-family: var(--font-mono);
    font-weight: 500;
    letter-spacing: var(--tracking-eyebrow);
    color: var(--accent-dark);
}

.code-setup-skip {
    margin: var(--space-4) 0 0;
    font-size: var(--text-sm);
}

.code-setup-skip a {
    color: var(--muted);
    text-decoration: underline;
}

.code-setup-skip a:hover {
    color: var(--ink);
}

/* Detail page: the "add this to your item" instruction and the "print a label" action. */
.finder-hint {
    margin: var(--space-3) 0 0;
    font-size: var(--text-sm);
    color: var(--muted);
}

/* The code address itself is monospace so it reads as a literal thing to copy onto the item. */
.finder-hint-url {
    font-family: var(--font-mono);
    color: var(--accent-dark);
}

/* Content-width, not full-bleed: one action among the rail's controls, sized to its label. */
.code-print-cta {
    margin-top: var(--space-4);
}

/* The lifecycle actions are secondary chrome under a quiet "Manage" eyebrow. */
.code-actions > .eyebrow {
    display: block;
    margin-bottom: var(--space-2);
}

/* Dashboard empty state — teach the concept, then the 1-2-3, then the CTA. */
.empty-state {
    max-width: 42rem;
    padding: var(--space-6);
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}

.empty-state h2 {
    margin: 0 0 var(--space-3);
    font-family: var(--font-serif);
}

.empty-steps {
    margin: var(--space-4) 0 var(--space-5);
    padding-left: var(--space-5);
    display: grid;
    gap: var(--space-2);
    line-height: var(--leading-snug);
}

/* The dashboard "+ New code" button is a POST form; keep it laying out like the link it replaced so the
   header row's baseline alignment is unchanged. */
.dashboard-new-code {
    margin: 0;
}

/* First-run welcome: the auto-created starter code, surfaced inside "Tag your first thing" so the owner
   sees the real code they're about to name and print. */
.first-run-code {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin: var(--space-4) 0;
    padding: var(--space-4);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.first-run-code .code-card-code {
    margin: 0;
}

/* ── Phone-width card padding ───────────────────────────────────────────────
   Carded surfaces relax their 32px frame on a phone, where it costs about a
   fifth of the line length inside main's own padding. Last in the file so it
   wins the source-order tie against every card's base padding rule above. */
@media (max-width: 40rem) {
    .owner-code-new,
    .owner-account,
    .empty-state,
    .code-detail-rail,
    section.finder-notice {
        padding: var(--space-5) var(--space-4);
    }
}

/* ── Chat shell — the conversation screens (threads redesign) ────────────────
   /threads/{id}, the Inbox's open pane, and the finder thread all render one
   messaging-app layout: a pinned header that names the item, the scrolling
   transcript, and a composer docked along the bottom of the screen. Everything
   is plain flow + position: sticky, so the shape holds with no script at all
   (spec §13); site.js only adds the live niceties. Last in the file so these
   rules win source-order ties against the generic form/button styling above. */
.chat {
    display: flex;
    flex-direction: column;
    /* Fill the viewport below the site chrome so the dock sits at the bottom of
       the screen even for a two-message thread. --chat-chrome is the one knob:
       it approximates the header + page padding above the shell and deliberately
       errs small — too tall just adds a touch of dead scroll, while too short
       would leave the dock floating above the screen edge. Per-context values
       override only the variable, never the calc, so a header-height change is
       a one-line retune per context. svh keeps phone URL-bar collapse from
       jumping the layout. */
    --chat-chrome: 6.5rem;
    min-height: calc(100vh - var(--chat-chrome));
    min-height: calc(100svh - var(--chat-chrome));
}

/* The finder thread and the owner's bearer reply page sit under the slimmest
   chrome of all (just the wordmark). */
.finder-thread.chat,
.owner-reply.chat {
    --chat-chrome: 5rem;
}

/* Inside the Inbox's fixed-height desktop pane the shell fills the pane, not
   the viewport (the pane is the scrollport the sticky edges pin to). The shell
   carries the pane's former padding so the sticky header/dock can span it. */
.conversation-detail .chat {
    min-height: 100%;
    padding-inline: var(--space-5);
}

/* ── Pinned header ── */
.chat-head {
    position: sticky;
    top: 0;
    /* One above the dock's 5: the "⋯" menu panel lives inside this stacking
       context, and on a short viewport it can reach the composer dock — the
       later-DOM dock would otherwise paint over (and click-block) the panel. */
    z-index: 6;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) 0;
    background: var(--bg);
    border-bottom: 1px solid var(--border);
}

/* The Inbox pane sits on the card surface, so its pinned edges match it. */
.conversation-detail .chat-head,
.conversation-detail .chat-dock {
    background: var(--card);
}

/* Round back control — the "to the list" affordance every messenger leads with. */
.chat-back {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem; /* ≥44px tap target (spec §13) */
    height: 2.75rem;
    margin-inline-start: calc(-1 * var(--space-2));
    border-radius: var(--radius-pill);
    color: var(--accent-dark);
    text-decoration: none;
}

.chat-back:hover {
    background: var(--accent-tint);
}

.chat-back svg {
    display: block;
}

/* The identity block: swatch + item name + code/status. On owner views it is a
   link to the item's code page — tapping the name opens the "contact details",
   like a chat app. The finder header reuses it as a plain <div>. */
.chat-ident {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    color: inherit;
    text-decoration: none;
    border-radius: var(--radius);
}

a.chat-ident:hover .chat-title,
a.chat-ident:focus-visible .chat-title {
    color: var(--accent-dark);
}

.chat-ident .conversation-swatch {
    width: 40px;
    height: 40px;
}

.chat-ident-text {
    min-width: 0;
}

/* The title is the item, not a generic "Conversation": the owner's label, or
   the printed code when the item was never labeled. */
.chat-title {
    margin: 0;
    font-family: var(--font-serif);
    font-weight: 600;
    font-size: var(--text-xl);
    line-height: 1.15;
    letter-spacing: var(--tracking-tight);
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chat-sub {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1) var(--space-3);
    margin-top: 2px;
    font-size: var(--text-xs);
    color: var(--muted);
}

.chat-code {
    font-family: var(--font-mono);
    font-weight: 500;
    letter-spacing: 0.08em;
    color: var(--muted);
}

.chat-sub .thread-status {
    font-size: var(--text-xs);
}

/* Right-side tool cluster: the sound toggle slot and the "⋯" options menu. */
.chat-tools {
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-inline-start: auto;
}

/* ── "⋯" options menu — a native <details> dropdown, script-free ── */
.chat-menu {
    position: relative;
}

.chat-menu > summary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-pill);
    background: var(--card);
    color: var(--muted);
    cursor: pointer;
    list-style: none; /* the dots glyph carries the affordance */
}

.chat-menu > summary::-webkit-details-marker {
    display: none;
}

.chat-menu > summary svg {
    display: block;
}

.chat-menu > summary:hover,
.chat-menu[open] > summary {
    color: var(--ink);
    background: var(--panel);
}

.chat-menu > summary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.chat-menu-panel {
    position: absolute;
    inset-inline-end: 0;
    top: calc(100% + var(--space-2));
    z-index: 10;
    width: min(19rem, 82vw);
    /* On a short viewport (phone landscape, soft keyboard up) the panel scrolls
       inside itself instead of running under the composer dock. */
    max-height: calc(100vh - 8rem);
    max-height: calc(100svh - 8rem);
    overflow-y: auto;
    padding: var(--space-2);
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
}

.chat-menu-panel form {
    margin: 0;
}

/* Menu rows — quiet, full-width text actions (each one a tiny POST form). The
   .owner-page twin outranks the global terracotta submit styling. */
.chat-menu-actions button[type="submit"],
.owner-page .chat-menu-actions button[type="submit"] {
    display: block;
    width: 100%;
    min-height: 2.75rem; /* ≥44px tap target (spec §13) */
    margin: 0;
    padding: var(--space-2) var(--space-3);
    font: inherit;
    font-size: var(--text-sm);
    font-weight: 500;
    text-align: start;
    color: var(--ink);
    background: none;
    border: 0;
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.chat-menu-actions button[type="submit"]:hover,
.owner-page .chat-menu-actions button[type="submit"]:hover {
    background: var(--panel);
}

/* The report block lives at the foot of the menu — present but never loud. */
.chat-menu-report {
    margin-top: var(--space-2);
    padding: var(--space-3) var(--space-3) var(--space-2);
    border-top: 1px solid var(--border);
}

/* The .owner-page twins outrank the legacy `.owner-thread form label` /
   `.conversation-detail form textarea` rules (0,1,2), which would otherwise
   restyle the dropdown's report form oversized on the owner views — the same
   conflict the menu buttons below already guard against. */
.chat-menu-report label,
.owner-page .chat-menu-report label {
    display: block;
    margin: 0 0 var(--space-2);
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--muted);
}

.chat-menu-report textarea,
.owner-page .chat-menu-report textarea {
    display: block;
    width: 100%;
    padding: var(--space-2) var(--space-3);
    font: inherit;
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
    color: var(--ink);
    background: var(--card);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-sm);
    resize: vertical;
}

.chat-menu-report button[type="submit"],
.owner-page .chat-menu-report button[type="submit"],
.finder-thread .chat-menu-report button[type="submit"] {
    display: inline-flex;
    align-items: center;
    width: auto;
    min-height: 2.75rem; /* ≥44px tap target (spec §13) */
    margin: var(--space-2) 0 0;
    padding: var(--space-2) var(--space-4);
    font: inherit;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--muted);
    background: var(--card);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius);
    cursor: pointer;
}

.chat-menu-report button[type="submit"]:hover,
.owner-page .chat-menu-report button[type="submit"]:hover,
.finder-thread .chat-menu-report button[type="submit"]:hover {
    color: var(--ink);
    background: var(--panel);
}

/* ── Body: notices + transcript ── */
.chat-notices {
    margin-top: var(--space-3);
}

.chat-notices > :first-child {
    margin-top: 0;
}

/* The finder's "save this link" note, compacted into a slim pinned bar over the transcript: the label
   and URL on the left, the (script-injected) Copy button hugging the right. */
.chat .save-link {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin: var(--space-3) 0 0;
    padding: var(--space-2) var(--space-3);
}

.save-link-main {
    flex: 1 1 auto;
    min-width: 0;
}

.chat .save-link .save-link-label {
    margin-bottom: 2px;
}

.chat .save-link-url {
    font-size: var(--text-xs);
}

.save-link-note {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--muted);
}

.save-link-copy {
    flex: none;
}

.save-link-copy:empty {
    display: none;
}

/* The Copy button — a quiet pill; flips to the confirmation green once the link is on the clipboard.
   The element+class selector outranks the finder page's generic full-width button styling. */
.save-link button.copy-link {
    display: inline-flex;
    align-items: center;
    width: auto;
    min-height: 2.75rem; /* ≥44px tap target (spec §13) */
    margin: 0;
    padding: var(--space-2) var(--space-3);
    font-family: var(--font-sans);
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--muted);
    background: var(--card);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-pill);
    cursor: pointer;
}

.save-link button.copy-link:hover {
    color: var(--ink);
    background: var(--panel);
}

.save-link button.copy-link:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.save-link button.copy-link.is-copied {
    color: var(--success);
    border-color: var(--success);
}

/* The transcript takes the slack, pushing the dock to the bottom edge. */
.chat > .messages {
    flex: 1 0 auto;
    margin: var(--space-4) 0 var(--space-2);
}

/* Softer, rounder bubbles inside the chat shell — the messenger look. The
   sender's-side corner stays pinched so alignment reads even in monochrome. */
.chat .message {
    border-radius: var(--radius-xl);
}

.chat .message-you {
    border-bottom-right-radius: 6px;
}

.chat .message-owner,
.chat .message-finder {
    border-bottom-left-radius: 6px;
}

.chat .message-system {
    border-radius: 0;
}

.chat .read-receipt {
    margin: 0 0 var(--space-2);
}

/* ── Composer dock — pinned along the bottom of the screen ── */
.chat-dock {
    position: sticky;
    bottom: 0;
    z-index: 5;
    margin-top: auto; /* a short thread still docks the composer at the bottom */
    padding: var(--space-3) 0;
    padding-bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
    background: var(--bg);
    border-top: 1px solid var(--border);
}

.chat-dock .finder-notice {
    margin: 0;
}

form.chat-composer {
    margin: 0;
}

form.chat-composer .form-error {
    margin: 0 0 var(--space-2);
}

.chat-input-row {
    display: flex;
    align-items: flex-end;
    gap: var(--space-2);
}

/* The message field is a rounded pill that grows with the draft (site.js caps
   it via max-height; with no script it is a calm two-line box). The .chat-dock
   prefix outranks the generic finder/owner form framing above by specificity —
   not by source-order tie, which a reordering or a later appended section
   would silently flip. */
.chat-dock form.chat-composer textarea {
    flex: 1 1 auto;
    display: block;
    width: auto;
    min-height: 2.75rem; /* one comfy line, ≥44px */
    max-height: 10rem;
    margin: 0;
    padding: 0.65rem var(--space-4);
    font: inherit;
    line-height: var(--leading-snug);
    color: var(--ink);
    background: var(--card);
    border: 1.5px solid var(--border-strong);
    border-radius: 1.375rem;
    resize: none;
    overflow-y: auto;
    caret-color: var(--accent);
}

.chat-dock form.chat-composer textarea::placeholder {
    color: var(--neutral);
}

/* Round terracotta send — the up-arrow button every phone thumb knows. Same
   .chat-dock specificity armor as the field above. */
.chat-dock form.chat-composer button.chat-send {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem; /* ≥44px tap target (spec §13) */
    height: 2.75rem;
    min-height: 0;
    margin: 0;
    padding: 0;
    color: var(--on-accent);
    background: var(--accent);
    border: 0;
    border-radius: var(--radius-pill);
    cursor: pointer;
}

.chat-dock form.chat-composer button.chat-send:hover {
    background: var(--accent-dark);
}

.chat-dock form.chat-composer button.chat-send:disabled {
    background: var(--neutral);
    cursor: default;
}

.chat-dock form.chat-composer button.chat-send svg {
    display: block;
}

form.chat-composer textarea:focus-visible,
form.chat-composer button.chat-send:focus-visible,
.chat-menu-report textarea:focus-visible,
.chat-menu-report button[type="submit"]:focus-visible,
.chat-menu-actions button[type="submit"]:focus-visible,
.chat-back:focus-visible,
a.chat-ident:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Below 52rem the Inbox becomes one pane scrolling with the page (no fixed-height pane), so its chat
   shell fills the viewport like the focused thread page, keeping the composer docked to the bottom of
   the screen. After the desktop `min-height: 100%` rule above, so it wins the source-order tie; the
   offset itself comes from the shared --chat-chrome knob. */
@media (max-width: 52rem) {
    .conversation-detail .chat {
        min-height: calc(100vh - var(--chat-chrome));
        min-height: calc(100svh - var(--chat-chrome));
    }
}

/* ── Phone-width chat ── */
@media (max-width: 40rem) {
    /* The owner header wraps to two rows on a phone, so the owner surfaces sit under taller chrome.
       Still erring small (the real chrome is ≈8.5rem) — see the .chat comment above. Only the knob
       changes; the finder shell keeps its slimmer value (its two-class selector outranks bare .chat). */
    .chat {
        --chat-chrome: 8rem;
    }

    /* The sound toggle drops its text on a phone and reads by its bell alone
       (the accessible name and title stay on the button). */
    .chat-tools .chime-toggle-label {
        display: none;
    }

    .chat-title {
        font-size: var(--text-lg);
    }

    .conversation-detail .chat {
        padding-inline: var(--space-4);
    }
}

/* ── How it works (/how-it-works) ──────────────────────────────────────────────
   The deep, demonstrative companion to the /about summary (plan docs/how-it-works-
   page-plan.md). Server-rendered baseline (Treatment A): all copy, the 3 steps, the
   broker diagram and the transcript are in the HTML, styled with the existing
   Direction A tokens — no new tokens or fonts. Motion/interactive/video treatments
   (B/C/D) layer on top later; nothing here depends on JavaScript. */
.how-it-works .hiw-hero {
    margin-bottom: var(--space-6);
}

.how-it-works .hiw-hero h1 {
    margin: var(--space-2) 0 var(--space-3);
}

.how-it-works .hiw-cta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: center;
}

.how-it-works .hiw-cta-primary {
    display: inline-block;
    padding: 0.75rem 1.25rem;
    min-height: 2.75rem; /* ≥44px tap target (spec.md §13) */
    font-weight: 600;
    color: var(--on-accent);
    background: var(--accent);
    border-radius: var(--radius);
    text-decoration: none;
}

.how-it-works .hiw-cta-primary:hover {
    background: var(--accent-dark);
    color: var(--on-accent);
}

.how-it-works .hiw-cta-secondary {
    font-weight: 600;
}

/* Step cards — the reveal-on-scroll targets a later motion pass would enhance. */
.how-it-works .hiw-steps {
    list-style: none;
    padding-left: 0;
    display: grid;
    gap: var(--space-3);
}

.how-it-works .hiw-steps li {
    padding: var(--space-4);
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    margin-bottom: 0;
}

/* Broker glyph — the centerpiece diagram; the animated/interactive treatments reuse
   this same motif (plan §9). Its end-state is fully legible on its own (§10). */
.broker-figure {
    margin: var(--space-5) 0;
    text-align: center;
}

.broker-figure .broker-glyph {
    display: block;
    width: 100%;
    max-width: 22rem;
    height: auto;
    margin: 0 auto var(--space-2);
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space-3);
    box-sizing: border-box;
}

.broker-glyph .broker-wire {
    stroke: var(--border-strong);
    stroke-width: 2;
    stroke-dasharray: 4 4;
}

.broker-glyph .broker-hub {
    fill: var(--accent-tint);
    stroke: var(--accent);
    stroke-width: 1.5;
}

.broker-glyph .broker-hub-mark {
    fill: var(--accent-dark);
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 600;
}

.broker-glyph .broker-label {
    fill: var(--muted);
    font-family: var(--font-sans);
    font-size: 12px;
}

.broker-glyph .broker-locked rect {
    fill: var(--card);
    stroke: var(--border-strong);
    stroke-width: 1;
}

.broker-glyph .broker-locked text {
    fill: var(--muted);
    font-family: var(--font-sans);
    font-size: 9px;
}

.broker-glyph .broker-locked-note {
    fill: var(--muted);
    font-family: var(--font-mono);
    font-size: 9px;
    letter-spacing: 0.04em;
}

.broker-figure figcaption {
    color: var(--muted);
    font-size: var(--text-sm);
}

.how-it-works .hiw-closing {
    font-family: var(--font-serif);
    font-size: var(--text-lg);
    margin-top: var(--space-6);
}

/* ── How it works — playable demo (Treatment C, plan §6) ───────────────────────
   A canned finder↔owner exchange the visitor can play through. Everything here is
   server-rendered and legible on its own; how-it-works.js only choreographs the
   reveal. All motion is opacity/transform (or SVG stroke) so hidden nodes keep
   their space and stay in the a11y tree, and the whole thing is neutralised under
   prefers-reduced-motion (block at the end) — the page's first @keyframes. */
.hiw-demo {
    margin: var(--space-5) 0;
    padding: var(--space-4) var(--space-4) var(--space-5);
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}

.hiw-demo > h2 {
    margin: var(--space-1) 0 var(--space-2);
}

.hiw-demo .hiw-demo-note {
    margin-bottom: var(--space-4);
}

/* The code as printed on a tag — the terracotta chip the finder types in. It shows an
   obviously-fake sample (DEMO-CODE), never a real code. */
.hiw-demo-code {
    display: inline-block;
    padding: var(--space-2) var(--space-3);
    font-size: var(--text-lg);
    color: var(--accent-dark);
    background: var(--accent-tint);
    border: 1px solid var(--accent);
    border-radius: var(--radius);
}

.hiw-demo-note-card {
    width: 100%;
    max-width: 26rem;
    margin: 0 auto;
}

.hiw-demo-note-card .owner-message {
    margin: 0;
}

.hiw-demo-thread {
    width: 100%;
    margin: 0;
}

/* The relay glyph reuses .broker-figure/.broker-glyph, sized down so it sits inside the
   phone-width stage rather than reading as a full-width diagram. */
.hiw-demo-relay {
    margin: 0;
}

.hiw-demo-relay .broker-glyph {
    max-width: 17rem;
    margin-bottom: var(--space-1);
}

.hiw-demo-relay figcaption {
    font-size: var(--text-sm);
}

/* ── The stage — a compact, phone-width card the beats play through ─────────────────
   Narrow on purpose so the exchange reads like a real screen, not a wide diagram. With
   no script it's just a bordered card and the beats stack inside it (the legible
   no-JS / reduced-motion end-state); the script adds .is-carousel to show one at a time. */
.hiw-stage {
    max-width: 22rem;
    margin: var(--space-4) auto 0;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
}

/* A slim app-style bar sells the "real screen" read and labels the card a demo. */
.hiw-stage-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-2) var(--space-3);
    background: var(--panel);
    border-bottom: 1px solid var(--border);
}

.hiw-stage-brand {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--accent-dark);
}

.hiw-stage-tag {
    font-size: var(--text-xs);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    padding: 0.1rem 0.4rem;
}

.hiw-slides {
    list-style: none;
    margin: 0;
    padding: var(--space-4) var(--space-3);
}

/* Default (no-JS / reduced-motion): beats stack, each under its caption, all legible. */
.hiw-slide {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--space-2);
}

.hiw-slide + .hiw-slide {
    margin-top: var(--space-4);
    padding-top: var(--space-4);
    border-top: 1px solid var(--border);
}

.hiw-slide-caption {
    margin: 0;
    color: var(--muted);
    font-size: var(--text-sm);
}

/* Carousel mode — the script adds .is-carousel and pins the frame to the tallest beat's
   height (measured while stacked), so the beats sit on top of one another and only the
   current one shows. Hidden beats keep the DOM (opacity, not display) so nothing reflows. */
.hiw-stage.is-carousel .hiw-slides {
    position: relative;
}

.hiw-stage.is-carousel .hiw-slide {
    position: absolute;
    inset: var(--space-4) var(--space-3);
    justify-content: center;
    margin: 0;
    padding: 0;
    border-top: 0;
    opacity: 0;
    transform: translateY(8px);
    transition: opacity 0.4s ease, transform 0.4s ease;
    pointer-events: none;
}

.hiw-stage.is-carousel .hiw-slide.is-current {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

/* The relay wire brightens and its dashes flow while the relay beat is on screen; the
   locked email/phone cards give a small nudge — the beat the page exists for. */
.hiw-stage.is-relaying [data-hiw-wire] {
    stroke: var(--accent);
    animation: hiw-relay-pulse 0.65s linear;
}

.hiw-stage.is-relaying [data-hiw-locked] {
    animation: hiw-lock 0.6s ease;
}

@keyframes hiw-relay-pulse {
    from { stroke-dashoffset: 16; }
    to   { stroke-dashoffset: 0; }
}

@keyframes hiw-lock {
    0%   { transform: translateY(0); }
    30%  { transform: translateY(-3px); }
    60%  { transform: translateY(0); }
}

/* Controls — step dots and the replay button, centred beneath the stage. */
.hiw-demo-controls {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    margin-top: var(--space-4);
}

/* The dots are script-built, so an empty list (no-JS) collapses to nothing. The gap gives
   the enlarged (invisible) tap targets room so adjacent dots stay independently tappable. */
.hiw-dots {
    display: flex;
    gap: var(--space-3);
    list-style: none;
    margin: 0;
    padding: 0;
}

.hiw-dots:empty {
    display: none;
}

/* The parent selector (.hiw-dots / .hiw-demo-controls) lifts these above the shared
   `.content-page button` box (accent fill, 44px min tap-size) so the dots read as small
   circles and the replay button as a quiet secondary action, not a second primary. */
.hiw-dots .hiw-dot {
    position: relative;
    display: block;
    width: 0.7rem;
    height: 0.7rem;
    min-height: 0;
    margin: 0;
    padding: 0;
    border: 1px solid var(--border-strong);
    border-radius: 50%;
    background: transparent;
    cursor: pointer;
}

/* A transparent overlay grows the tap target well past the WCAG 2.5.8 minimum (24px)
   without enlarging the visible dot — kept modest so neighbouring targets stay distinct. */
.hiw-dots .hiw-dot::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 1.75rem;
    height: 1.75rem;
    transform: translate(-50%, -50%);
}

.hiw-dots .hiw-dot.is-current {
    background: var(--accent);
    border-color: var(--accent);
}

.hiw-dots .hiw-dot:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

/* The replay button — a real button styled as a quiet secondary action with a
   visible focus ring (keyboard operable, plan §10). */
.hiw-demo-controls .hiw-demo-play {
    display: inline-block;
    margin: 0;
    padding: 0.6rem 1.1rem;
    min-height: 2.75rem; /* ≥44px tap target */
    color: var(--accent-dark);
    background: var(--card);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    cursor: pointer;
    font: inherit;
    font-weight: 600;
}

.hiw-demo-controls .hiw-demo-play:hover {
    background: var(--accent-tint);
}

.hiw-demo-controls .hiw-demo-play:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* The display:inline-block above would otherwise beat the UA [hidden] rule, leaving
   a dead "Replay" button on screen with no script (and under reduced motion, where
   the script bails before wiring it). Keep hidden meaning hidden. */
.hiw-demo-play[hidden] {
    display: none;
}

/* Secondary: step cards fade up as they scroll in (script adds .hiw-reveal, then
   .is-revealed). Staggered so the three don't move as one block. */
.hiw-reveal {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity 0.5s ease, transform 0.5s ease;
}

.hiw-reveal.is-revealed {
    opacity: 1;
    transform: translateY(0);
}

.hiw-steps li.hiw-reveal:nth-child(2) { transition-delay: 0.08s; }
.hiw-steps li.hiw-reveal:nth-child(3) { transition-delay: 0.16s; }

/* Reduced motion is first-class (plan §10): the script bails before adding .is-carousel,
   so the beats already stack legibly; guarantee no residual animation regardless. */
@media (prefers-reduced-motion: reduce) {
    .hiw-stage.is-carousel .hiw-slide,
    .hiw-stage.is-carousel .hiw-slide.is-current,
    .hiw-reveal,
    .hiw-reveal.is-revealed {
        opacity: 1;
        transform: none;
        transition: none;
        animation: none;
    }

    .hiw-stage.is-relaying [data-hiw-wire],
    .hiw-stage.is-relaying [data-hiw-locked] {
        animation: none;
    }
}

/* ── Analytics opt-out toggle (Pages/Shared/_AnalyticsPreference.cshtml, consent.js) ──
   The per-browser on/off control shown on the Privacy and Terms pages when analytics is configured.
   There is no consent popup — analytics runs by default (unless the browser sends Do Not Track), and
   this is where a visitor changes that. It starts [hidden] and consent.js reveals it once wired. */
.analytics-preference {
    margin: var(--space-4) 0;
    padding: var(--space-4) var(--space-5);
    background: var(--card);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
}

.analytics-preference-control {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: 44px; /* tap target ≥44px (spec.md §13) */
    font-weight: 600;
    color: var(--ink);
    cursor: pointer;
}

.analytics-preference-control input {
    width: 1.15rem;
    height: 1.15rem;
    flex-shrink: 0;
    accent-color: var(--accent);
    cursor: pointer;
}

.analytics-preference-control input:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.analytics-preference-status {
    margin: var(--space-2) 0 0;
}
