/*
 * Email Health: the "live audit" loading experience (landing page only).
 *
 * A visual port of the approved mockup (mockup/live-audit.html) onto the real
 * landing page, translated to the app's own tokens and self-hosted fonts. This
 * file is linked ONLY on the landing (see Renderer::shell), so none of its
 * `.la-*` rules can ever reach the report or the client report.
 *
 * Phase 1 (this file) ships the STATIC markup and styling: the idle landing is
 * unchanged (the hero, the form and the rail naming the six checks), while the
 * scanning / done chrome (slim status bar, score gauge, findings feed, result
 * bar) sits in the DOM but stays hidden under the idle mode class. With no
 * JavaScript the page never leaves idle, so that chrome is never reached and the
 * plain GET path is exactly as before. Phase 2 adds the client-side reveal that
 * switches the stage into `la-scanning` / `la-done` and fills these from the
 * real scan; every animation rule below stays dormant until then.
 *
 * CSP note: no inline <style>, no style="". The gauge arc geometry lives here
 * in CSS (Phase 2 drives it via the CSSOM, exactly like the report gauge in
 * app.js). SVG presentation attributes (cx/cy/r) are not the style attribute and
 * are allowed. UK English, no em dashes.
 *
 * Palette + font mapping (mockup token -> app token):
 *   indigo #4f46e5 -> --brand         · link #4338ca -> --brand-ink
 *   red    #c73a2e -> --fail          · amber text  -> --warn, amber dot -> --warn-dot
 *   green  #2f9e5b -> --pass          · gray -> --muted, gray-soft -> --faint
 *   JetBrains Mono -> --mono          · Helvetica Neue -> --display
 *   big uppercase hero -> --display-cond · kickers / labels -> --label
 *
 * Those names now resolve to the LANDING's values, not :root's (homepage parity,
 * D1): white ground, sumobaby's blue, Archivo and Lekton. Nothing here has to be
 * scoped for that, because `body.page-landing` and this file are linked by the
 * same flag (Renderer::shell), so a token declared for the landing is always in
 * scope here. What did have to move is anything that hard-coded a warm-skin
 * value; see the notes at .la-ring-track, .la-dot and .seg-track.
 */

/* ============ VISUALLY HIDDEN (the screen-reader status line) ============
   Renderer::liveAuditStage renders the live region as
       <p class="la-sr-status visually-hidden" role="status"></p>
   and this is the rule that hides it. Without it the element is an ordinary
   paragraph: idle that goes unnoticed because it is empty, but the moment
   live-audit.js announce() writes to it (beginScanning, finishScan) the text
   meant only for a screen reader renders as visible body copy under the lede.

   The standard clip-rect pattern: off screen and zero sized, but still painted,
   still in the accessibility tree and still announced. Do NOT swap it for
   `display: none` or `visibility: hidden`, which would remove it from the
   accessibility tree and silence the announcement, which is the whole point of
   the element.

   Landing-only home, deliberately: `visually-hidden` is used by exactly one
   element, on the one page this stylesheet is linked to (Renderer::shell, with
   liveAudit: true). If another page ever needs it, move the rule to style.css
   rather than linking this file more widely. */
.visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* ============ SCANNING HEADER (scanning / done only) ============
   Idle keeps today's hero exactly. When the reveal switches the stage into
   scanning, the hero COMPACTS rather than collapsing: it keeps the page's
   identity (headline, lede), gains a note naming the subject, and hands the
   room below to the status bar + audit. Inert until a mode class other than
   la-idle is set.

   This replaces an earlier collapse (max-height: 0 with opacity, margin and a
   lift), which carried a bug: it never zeroed .hero's `padding-top: 52px`
   (style.css). With the page-wide `* { box-sizing: border-box }`, max-height
   applies to the BORDER box, and a border box cannot shrink below its own
   padding, so the content box floored at 0 and the padding stood as 52px of
   void above the status bar.

   Zeroing that padding is only right where the hero is genuinely HIDDEN. A
   compacted hero is not hidden, so it keeps padding, just less of it than
   idle's 52px. An earlier draft zeroed it here and the breadcrumb sat straight
   on the headline with no air at all. */
.la-stage.la-scanning .hero,
.la-stage.la-done .hero {
    /* No max-height cap, deliberately. The mockup proposes 400px, and measured
       across 1400px down to 360px (with a 58 character domain, so the note
       wraps) the compacted hero peaks at 302px, so that cap does not clip
       today. It is dropped all the same: it buys nothing, because idle's
       max-height is `none` and `none` cannot be interpolated, so the cap never
       animates anything. It only leaves a trap, roughly 98px from silently
       eating the lede the next time this copy grows. The compacted hero is
       content, so it is sized by its content; the transition rides padding,
       which is the property that actually moves. */
    opacity: 1;
    transform: none;
    pointer-events: auto;
    padding: 40px 0 34px;
    margin: 0;
    /* Stack the two columns so it reads top-down: headline, lede, note. The row
       gap is what separates the headline from the lede; 14px is too tight under
       our condensed face, which sets a heavier, more compact block than a
       regular face and needs more room to breathe beneath it. */
    grid-template-columns: minmax(0, 1fr);
    gap: 26px;
}
/* `padding` has to ease with everything else or the hero snaps mid-compact. The
   collapse this replaces never needed it, because it never moved its padding. */
.la-stage.la-scanning .hero,
.la-stage.la-done .hero,
.hero {
    transition: opacity 0.4s ease, margin 0.6s ease,
        transform 0.5s ease, padding 0.6s ease;
}
/* The headline flattens to one line at a size that no longer dominates the page
   but still reads as the page's title rather than a caption.

   Re-tuned for Archivo (homepage parity, Phase 6). The old clamp(34px, 4.2vw,
   56px) was sized for --display-cond, a CONDENSED face that buys visibly less
   presence at the same pixel size; the landing's display face is Archivo 900,
   which is both wider and heavier, so the same numbers drew a second headline
   rather than a caption. Measured across the sweep: at 56px the flattened line
   ran 68% of the content column at 2557px and 99 to 100% at 1280px and 780px,
   filling the row edge to edge, and it still wrapped to two lines at 640px.
   At this clamp it runs 47% at 2557px, 59% at 1280px, 74% at 780px, and holds
   ONE line all the way down to 640px, which the old value did not. Idle stays
   var(--fs-display), clamp(42px, 5.9vw, 88px). */
.la-stage.la-scanning .lede__title,
.la-stage.la-done .lede__title {
    font-size: clamp(26px, 2.6vw, 40px);
}
.la-stage.la-scanning .lede__title br,
.la-stage.la-done .lede__title br { display: none; }
.la-stage.la-scanning .lede__sub,
.la-stage.la-done .lede__sub { max-width: 620px; margin-bottom: 0; }
/* The form has done its job. */
.la-stage.la-scanning .check-form,
.la-stage.la-done .check-form { display: none; }
.la-stage.la-scanning .hero__search,
.la-stage.la-done .hero__search { padding-top: 0; }

/* The progress note: the scan states its own subject in the header, not only
   down in the status bar. It carries the domain as typed, where the status bar
   below shows the bare host. It sits after the form in the DOM, and the form is
   display: none while scanning, so it reads directly under the lede.

   Rendered ONLY by the landing (Renderer::hero's $progressNote flag), because
   this rule is the only thing hiding it and this stylesheet is linked only when
   liveAudit is true. The error page reuses the same hero() and links neither
   asset, so emitting the note there would render "Checking …" as visible body
   copy on an error page: the same shape of bug as the unstyled .visually-hidden
   line above. If the note is ever needed on another page, that page needs this
   rule, not just the markup. */
.la-progress-note {
    display: none;
    margin: 14px 0 0;
    font-size: 13px;
    color: var(--muted);
    letter-spacing: 0.02em;
}
.la-progress-note b {
    color: var(--ink);
    font-weight: 700;
    word-break: break-all;
}
.la-stage.la-scanning .la-progress-note { display: block; }

/* ============ SLIM STATUS BAR (scanning / done) ============ */
.la-statusbar {
    display: none; /* idle: hidden. Shown in scanning / done. */
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
    padding: 24px 0 16px;
    border-bottom: 1px solid var(--rule);
}
.la-stage.la-scanning .la-statusbar,
.la-stage.la-done .la-statusbar {
    display: flex;
    animation: la-fadein 0.5s ease;
}
.la-status-lead {
    display: flex; align-items: center; gap: 8px;
    font-family: var(--label); font-weight: 700; font-size: 11px;
    letter-spacing: 0.2em; text-transform: uppercase; color: var(--muted);
}
.la-host {
    font-family: var(--mono); font-weight: 500;
    font-size: clamp(20px, 2.5vw, 26px); letter-spacing: 0.02em; word-break: break-all;
}
.la-elapsed {
    font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums;
}
.la-elapsed b { color: var(--ink); font-weight: 700; }
.la-newscan {
    margin-left: auto;
    display: none; /* only the done state offers a fresh check */
    align-items: center; gap: 7px;
    font-family: var(--label); font-weight: 700; font-size: 12px;
    letter-spacing: 0.14em; text-transform: uppercase; color: var(--brand-ink);
}
.la-newscan:hover { color: var(--brand); text-decoration: none; }
.la-stage.la-done .la-newscan { display: inline-flex; }
.la-newscan .arr { transition: transform 0.14s ease; }
.la-newscan:hover .arr { transform: translate(2px, -2px); }

/* The pulsing "we are working" dot on the status lead. The glow needs the accent
   as an rgb triple, which no colour token can give it. That triple used to be a
   local `--la-brand-rgb: 75, 69, 230`, the warm skin's indigo, and it was never
   answered when the landing turned blue, so the dot pulsed an indigo halo around
   a #004AAD dot (homepage parity, Phase 6). The landing declares its own
   --brand-rgb beside --brand, so the two can no longer drift apart. */
.la-dot {
    width: 9px; height: 9px; border-radius: 50%;
    background: var(--brand); display: inline-block;
    box-shadow: 0 0 0 0 rgba(var(--brand-rgb), 0.5);
    animation: la-pulse 1.4s infinite;
}

/* ============ AUDIT STAGE: gauge + feed (scanning / done) ============ */
.la-audit {
    display: none; /* idle: hidden. */
    grid-template-columns: 300px 1fr;
    gap: 56px;
    align-items: start;
    padding-top: 34px;
}
.la-stage.la-scanning .la-audit,
.la-stage.la-done .la-audit {
    display: grid;
    animation: la-fadein 0.5s ease;
}

/* ---- gauge ---- */
.la-gauge { position: sticky; top: 20px; }
.la-gauge-ring { position: relative; width: 220px; height: 220px; margin: 0 auto; }
.la-gauge-ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
/* The empty ring. The stroke was a hard-coded #e7e6e0, a warm grey picked to sit
   a touch stronger than the warm skin's hairline; on white it reads at 1.15:1 and
   is close to invisible, which is the "gone invisible against white" case Phase 6
   went looking for. --line-soft is the landing's own quiet neutral rule (the same
   one the mockup gives the segment tracks): 1.53:1 on white, so the empty ring
   actually reads as a ring, and it stays subordinate to the page hairline. */
.la-ring-track { fill: none; stroke: var(--line-soft); stroke-width: 13; }
.la-ring-prog {
    fill: none; stroke: var(--faint); stroke-width: 13; stroke-linecap: round;
    /* Empty ring by default (offset == circumference, 2 * pi * 96 ~= 603): the
       honest pre-scan frame shows nothing formed. Phase 2 sets the offset +
       colour from the real score via the CSSOM. */
    stroke-dasharray: 603;
    stroke-dashoffset: 603;
    transition: stroke-dashoffset 0.7s ease, stroke 0.7s ease;
}
.la-gauge-center {
    position: absolute; inset: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.la-gauge-letter {
    font-family: var(--display); font-weight: 700; font-size: 78px; line-height: 0.8;
    letter-spacing: -0.03em; color: var(--faint); transition: color 0.6s ease;
}
/* Tone classes the reveal toggles on the letter; inert until then. The `none`
   tone is the honest "not rated" state (an incomplete report, Phase 3): a calm
   grey, never a red F, because the server withheld the grade. */
.la-gauge-letter--pass { color: var(--pass); }
.la-gauge-letter--warn { color: var(--warn); }
.la-gauge-letter--fail { color: var(--fail); }
.la-gauge-letter--none { color: var(--muted); }
.la-gauge-score {
    margin-top: 6px; font-family: var(--label); font-size: 13px;
    letter-spacing: 0.04em; color: var(--muted); font-variant-numeric: tabular-nums;
}
.la-gauge-cap {
    margin-top: 16px; text-align: center;
    font-family: var(--label); font-weight: 700; font-size: 11px;
    letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted);
}

/* ---- triage tally (revealed on done) ---- */
.la-triage {
    display: none; margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--line);
}
.la-triage.la-show { display: block; animation: la-fadein 0.5s ease; }
.la-tri-row { display: flex; justify-content: space-between; padding: 5px 0; font-size: 13.5px; }
.la-tri-row b { font-weight: 700; font-variant-numeric: tabular-nums; }
.la-tri-dot {
    display: inline-block; width: 8px; height: 8px; border-radius: 50%;
    margin-right: 12px; vertical-align: middle;
}
.la-tri-dot--fail { background: var(--fail); }
.la-tri-dot--warn { background: var(--warn-dot); }
.la-tri-dot--pass { background: var(--pass); }

/* ---- findings feed ---- */
.la-feed-head {
    display: flex; align-items: baseline; justify-content: space-between;
    padding-bottom: 12px; border-bottom: 1px solid var(--rule);
}
.la-feed-t {
    font-family: var(--label); font-weight: 700; font-size: 11px;
    letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink);
}
.la-feed-n { font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; }
/* min-height holds the feed's space so what sits below it does not jump as rows
   tick over. 104px, where this shipped with 380px: 380px was reserved for a
   growing list of findings, and the feed is now a single self-replacing line
   (decision D2), so it only ever needs one row's worth plus headroom for a row
   that wraps to two lines.
   Measured, not guessed: a real row is 77.1px on one line and 102.2px on two, at
   every width from 1400px down to 640px. 104px therefore holds the tallest row
   we can draw, so no row can move the strip below. (The proposal in mockup.html
   says 96px, which is short: it lands between the two heights, so every wrapping
   row nudged the page by 6.2px.) */
.la-feed { list-style: none; margin: 0; padding: 0; min-height: 104px; }
.la-feed li {
    display: grid; grid-template-columns: 22px 1fr auto; gap: 14px; align-items: baseline;
    padding: 13px 2px; border-bottom: 1px solid var(--line);
    opacity: 0; transform: translateY(8px); animation: la-feedin 0.45s ease forwards;
}
.la-feed .la-g {
    font-family: var(--display); font-weight: 800; font-size: 15px;
    text-align: center; line-height: 1.4;
}
.la-feed .la-g--pass { color: var(--pass); }
.la-feed .la-g--fail { color: var(--fail); }
.la-feed .la-g--warn { color: var(--warn); }
.la-feed .la-g--info { color: var(--muted); }
.la-feed .la-cat {
    display: block; margin-bottom: 3px;
    font-family: var(--label); font-weight: 700; font-size: 10.5px;
    letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted);
}
.la-feed .la-txt { font-size: 15px; line-height: 1.5; }
.la-feed .la-txt b { font-weight: 700; }
.la-feed .la-txt--pass b { color: var(--pass); }
.la-feed .la-txt--fail b { color: var(--fail); }
.la-feed .la-txt--warn b { color: var(--warn); }
.la-feed .la-tm {
    font-size: 12px; color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums;
}
/* The closing "weighing what we found" line. */
.la-feed li.la-finalize { grid-template-columns: 22px 1fr; color: var(--muted); font-style: italic; }
.la-feed li.la-finalize .la-g { color: var(--brand); font-style: normal; }

/* ============ THE REPORT'S TOKEN ISLAND ============
   Homepage parity plan, D3. The landing wears its own skin from
   `body.page-landing` in style.css: white ground, sumobaby's blue, Archivo and
   Lekton, 18px body. The report mounts INSIDE that, so without this block the
   same report would look one way inline and another at `?domain=`. Every name the
   landing overrides is declared back to its `:root` value here, and the three
   inherited text properties with them, so the report is the report on both routes.

   Keep this list in step with the landing block in style.css: a token added there
   and not answered here leaks the landing's look into the report.

   Phase 6 is where the seam between the white landing and this warm block gets
   looked at by eye; if it reads as a mistake rather than a handoff, that is a note
   for the whole-skin decision, not a fix here. */
.la-report {
    --brand: #4b45e6;
    --brand-ink: #3b37c9;
    --ink: #141414;
    --muted: #6f6f6c;
    --faint: #9a9a95;
    --line: #ddddd7;
    --rule: #26262a;
    --bg: #f3f2ef;
    --accent-yellow: #feffb9;
    --pass: #1c7c46;
    --warn: #8a6300;
    --fail: #bd2c2c;
    --display: "Helvetica Neue", "Switzer", Helvetica, Arial, sans-serif;
    --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    --text: var(--mono-stack);
    --label: "Lekton", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Tokens alone cannot undo what the landing set on <body>, so the report's
       own ground and body type are re-stated as real properties. */
    background: var(--bg);
    color: var(--ink);
    font-family: var(--text);
    font-size: 15.5px;
    line-height: 1.62;
}

/* ============ REPORT UNFURLS UNDERNEATH (done handoff) ============
   Scan -> Mockup parity, Phase 5. When a fully scored scan settles, the reveal
   (live-audit.js) injects the report-body fragment into `#la-report` and adds
   `report-mode` to `<main>`. The finished scan stays fully in place above; the
   report opens below it, parted by a rule so it reads as more of the same page,
   never a new screen (the mockup's `main.report-mode .rk-report` handoff).

   The mount is hidden until then, so with no JavaScript, an ungraded report or a
   failed fragment fetch it never shows and the result bar's link-out stands. On
   reveal the report fades and rises a touch into place (the `.la-report-in`
   end-state below, which live-audit.js adds a frame after mounting) while the
   page eases down to it, so the two read as one motion rather than a pop. Reduced
   motion drops the transition via style.css's global kill-switch, so the report
   simply appears at its settled state. The seam uses `--line-2` (the app's
   stronger warm rule), a section boundary that is clearer than an inner hairline
   but quieter than the near-black `--rule`. */
.la-report { display: none; }
main.report-mode .la-report {
    display: block;
    margin-top: 40px;
    padding-top: 40px;
    border-top: 1px solid var(--line-2);
    /* Fade & rise start state. `display` cannot transition, so the settled state
       lives on `.la-report-in`; live-audit.js forces a reflow after mount, then
       adds that class on the next frame so the transition runs from here. */
    opacity: 0;
    transform: translateY(20px);
    transition: opacity 0.5s ease, transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}
main.report-mode .la-report.la-report-in {
    opacity: 1;
    transform: none;
}

/* ============ THE RAIL: THE CHECKS AT REST, THE METER WHILE SCANNING ============
   One component, two faces (homepage parity, Phase 4). At rest the head reads
   "What we check" over a numbered directory of the six checks; the moment a scan
   starts the directory gives way to the six-segment meter and the "n of 6"
   count. That is what folded the separate `.about` bucket strip away: the
   landing used to ship this module hidden AND a strip saying the same thing in a
   different shape directly beneath it.

   The meter: one slim segment per check, in the server's run order. Each fills,
   then locks to its status colour as its finding lands (green pass, ochre worth
   a look, red fix now), giving the "ticking to done" read in a fraction of the
   strip's height. Ported from the mockup's `.seg-*` (namespace kept), translated
   to the app's own tokens.

   The meter is decorative: `.seg-bar` is aria-hidden, as are the head's caption
   and count, so the findings feed stays the one announced (aria-live) region.
   The directory is not, because it is the only place the resting landing says
   what it checks. */
.seg-module { margin-top: 32px; }
.seg-head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 16px; padding: 12px 0; border-top: 1px solid var(--rule);
}
/* The display face at rest as well as in flight: this label is the landing's
   section heading now, and the mockup sets it in Archivo over the near-black
   rule rather than in the mono the other kickers use. */
.seg-head-label {
    font-family: var(--display); font-weight: 800; font-size: 13px;
    letter-spacing: 2px; text-transform: uppercase; color: var(--muted);
}
.seg-head-now { font-size: 15px; color: var(--ink); font-variant-numeric: tabular-nums; }
.seg-head-now b { font-weight: 700; }
.la-stage.la-done .seg-head-now { color: var(--pass); }
/* The honest interrupted ending (streaming plan, Phase 7): a stream that died
   after verdicts had been shown lands in done-mode without a result, and its
   "The scan stopped early" caption must not borrow done's green all-is-well. */
.la-stage.la-done .seg-head-now.la-stopped { color: var(--muted); }
.seg-head-status {
    font-size: 13px; color: var(--brand-ink);
    font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* The rest of the head is the resting label's alone: at idle the count would
   read "6 checks" beside a directory that has just listed all six. */
.la-stage.la-idle .seg-head-status { display: none; }
.seg-bar { display: grid; grid-template-columns: repeat(6, 1fr); gap: 5px; }
/* The empty track, now the mockup's own pair (homepage parity, Phase 6): a
   recessed fill under the landing's quiet rule. It was a warm #e7e6e0 under
   --line, and --line on the landing is #AAAAAA, the page hairline, so every
   pending segment read as a grey box in a hard outline rather than a recess.
   --sunken and --line-soft were ported in Phase 1 for exactly this and had no
   rule spending them until now. */
.seg-track {
    height: 11px; background: var(--sunken);
    border: 1px solid var(--line-soft); border-radius: 2px;
    overflow: hidden; position: relative;
}
.seg-fill {
    position: absolute; inset: 0; width: 0; background: var(--brand);
    transition: width 0.5s ease, background 0.35s ease;
}
/* No per-segment labels: the active segment shows a pulsing partial fill so it
   reads as "in progress" without a caption; the head names the current check
   and the feed names every finding. The active width is deliberately not set
   here (streaming plan, Phase 4): on the streamed path the trickle in
   live-audit.js creeps it toward, but never to, full (decision D5), and the
   fallback reveal sets its own brief 60% inline. Done segments fill fully and
   lock to their status colour, matching the triage dots (pass / worth a look /
   fix now); the lock clears any inline width, so the done rule owns 100%. */
.seg.active .seg-fill {
    background: var(--brand);
    animation: seg-active 1.4s ease-in-out infinite;
}
.seg.done .seg-fill { width: 100%; background: var(--pass); animation: none; }
/* The mockup's own value, not the triage dot's (homepage parity, Phase 6). The
   dot is a small mark beside its own label and can afford to be bright; a locked
   segment is the ONE place that check's verdict is stated in the rail, and
   --warn-dot (#cf9a1a) sits at 2.49:1 on white, under the 3:1 a graphical object
   carrying meaning needs. --warn is 6.5:1 and keeps the row of six in one
   family, beside the dark green and the dark red. */
.seg.done.hasissue .seg-fill { background: var(--warn); }
.seg.done.critical .seg-fill { background: var(--fail); }
/* A check that produced no verdict: "info" (for information only) and "unknown"
   ("we could not look reliably"). Neither scores, so neither may take the plain
   done fill, which is --pass: without this an unread check locks GREEN and the
   rail says a check passed that nobody could read. The strip this rail replaced
   had the same rule (`.bucket.is-done.is-unknown`, painted grey with a comment
   saying never green) and it was lost with the strip, so it is restated here on
   the surface that inherited the job. --muted is the neutral the rest of the
   audit already uses for "no verdict": the feed's info glyph and the gauge's
   own not-rated letter. */
.seg.done.unrated .seg-fill { background: var(--muted); }
@keyframes seg-active { 0%, 100% { opacity: 0.5; } 50% { opacity: 1; } }

/* ============ THE DIRECTORY (idle only) ============
   The resting face: six numbered names on one row, keyed by the same frozen
   checkId as the segment above them. Six across holds to 860px because the
   longest name is 20 characters, which is why the acronyms come off here
   (decision D6) even though the report prints every title in full.

   The mode gate is the stage's own class, so the reveal needs no new hook: the
   directory at idle, the meter in every other state. Six empty tracks at rest
   read as a broken progress bar, which is the whole reason the two faces swap
   rather than stack. */
.dir-row {
    display: none;
    grid-template-columns: repeat(6, 1fr); gap: 24px; padding-top: 8px;
}
.dir-item { display: flex; gap: 8px; align-items: baseline; }
.dir-num {
    flex: 0 0 auto; font-family: var(--display); font-weight: 800;
    font-size: 13px; letter-spacing: 1px; color: var(--brand);
}
.dir-name { font-size: 15px; color: var(--ink); line-height: 1.3; }

.la-stage.la-idle .dir-row { display: grid; }
.la-stage.la-idle .seg-bar { display: none; }

/* The three "what we check" cells were styled from here down: a status dot, a
   rolled-up category verdict and a chip per member check, plus the rules that
   compacted the strip once a scan started. The strip is gone (homepage parity,
   Phase 4, folded into the rail above) and the code that painted it went with it
   in Phase 5, so every one of those rules had lost its element. */

/* ============ animations (all dormant until Phase 2) ============ */
@keyframes la-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(var(--brand-rgb), 0.45); }
    70%  { box-shadow: 0 0 0 7px rgba(var(--brand-rgb), 0); }
    100% { box-shadow: 0 0 0 0 rgba(var(--brand-rgb), 0); }
}
@keyframes la-fadein {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}
@keyframes la-feedin {
    to { opacity: 1; transform: none; }
}

/* ============ responsive (matches the app's 780px breakpoint) ============
   The mockup collapses its scan chrome at 820px; the live sheet holds 780px,
   deliberately. 780px is where the app's OWN `.la-audit` grid (300px + 56px gap
   + feed, its own dimensions, not the mockup's) stops fitting, and it is the
   value `style.css` already uses to stack the landing hero. Keeping the two in
   step means the whole landing folds at one width, which is the mockup's intent
   even though its number is 820 tuned to its own grid. This is the Phase 1 rule
   again: the app's translation of a mockup value is deliberate, not drift.

   At this width the scan chrome collapses together, mirroring the mockup: the
   gauge + feed stack to one column, the gauge stops being sticky, and the
   six-segment bar folds from 6 columns to 3 (two rows of three). */
/* The directory folds first, at the mockup's own 860px: six names across stop
   fitting before the scan chrome does, and this is the one number the mockup
   keeps rather than translating (the hero stacks with it there). */
@media (max-width: 860px) {
    .dir-row { grid-template-columns: repeat(2, 1fr); gap: 16px; }
}

@media (max-width: 780px) {
    .la-audit { grid-template-columns: 1fr; gap: 32px; }
    .la-gauge { position: static; }
    .seg-bar { grid-template-columns: repeat(3, 1fr); }
    /* This block also re-stated the compacted strip's side padding here, because a
       media query adds no specificity and the two-class compacting rule outranked
       the bare stacking rule, leaving a ragged stack. Both rules went with the
       strip (homepage parity, Phases 4 and 5). */
}

/* Motion-safe (Phase 3): no pulse, no fade or reveal drift, no feed rise, and no
   gauge sweep or colour transition. The reveal (live-audit.js) already skips the
   stagger and the count-up under this query and paints the finished state at
   once; these rules make sure nothing the stylesheet drives animates either, so
   the whole experience settles instantly with no motion. */
@media (prefers-reduced-motion: reduce) {
    .la-dot { animation: none !important; }
    .la-statusbar,
    .la-audit,
    .la-triage,
    .la-feed li { animation: none !important; }
    .la-feed li { opacity: 1; transform: none; }
    /* No pulse on the in-progress segment: it holds its partial fill, still. */
    .seg.active .seg-fill { animation: none !important; opacity: 1; }
    /* A fill changes width and colour in a step, never a slide. */
    .seg-fill { transition: none !important; }
    .hero,
    .la-gauge-letter,
    .la-ring-prog { transition: none !important; }
}
