/**
 * BLADE BOSS — THE SCAN v2
 *
 * Restraint is not absence. A cockpit is restrained and it is not boring —
 * dense, purposeful, every element earning its place.
 *
 * Colour discipline:
 *   confidence colours carry the DATA layer (green/amber/red)
 *   --bb-orange is reserved for ACTIONS and the current sequence step
 * Type discipline:
 *   values are mono and large — instrument readouts, not a settings screen
 *
 * The hero bleeds to the true top of the viewport. The nav is a fixed
 * floating pill, so this section carries its own internal top padding
 * (same pattern as home.css — page wrapper stays at padding-top: 0).
 */

 /* ═══ SHELL OVERRIDES ════════════════════════════════════════════
   ROOT CAUSE 1 — TOP GAP. The shell wraps templates in
   <main id="bb-main" class="bb-main"> which carries
   padding-top: var(--nav-height) (base.css:149). A full-bleed hero must
   CANCEL that, not add to it. body.bb-page-scan comes from definePages().

   ROOT CAUSE 2 — HEADING COLOUR. base.css:86 sets
   h1,h2,h3,h4,h5,h6 { color: var(--bb-charcoal); }
   An element-level rule ALWAYS beats inheritance, so every heading inside
   .scan rendered near-black on a dark page. The reset below is mandatory —
   never rely on inheriting colour from .scan. */

body.bb-page-scan .bb-main { padding-top: 0; }
body.bb-page-scan { background: var(--bb-navy); }

.scan {
    background: var(--bb-navy);
    color: var(--bb-white);
}

.scan h1, .scan h2, .scan h3, .scan h4 { color: var(--bb-white); }

.sr-only {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}

.scan-placard {
    display: inline-block;
    font-family: var(--font-mono);
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.19em;
    text-transform: uppercase;
    color: var(--bb-green-muted);
    margin: 0;
}

/* Etched plate, not a pill. A pill reads like a marketing badge; a placard
   reads like an instrument label — and it survives a bright lawn behind it. */
.scan-placard--plate {
    padding: 7px 13px;
    background: rgba(8, 12, 18, 0.72);
    border: 1px solid rgba(255, 255, 255, 0.09);
    border-radius: var(--radius-sm);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    color: #A8C48A;
}


/* ═══ ACT 1 · HERO ═══════════════════════════════════════════════ */

.scan-hero {
    position: relative;
    min-height: 100svh;
    display: flex;
    align-items: flex-end;
    overflow: hidden;
    /* bleeds to top; clears the floating pill nav with its own padding */
    padding: calc(var(--nav-height-mobile) + var(--safe-top) + var(--space-8)) 0 var(--space-8);
}

/* — the cinematic: dead lawn wiped into striped lawn, on a loop — */

.scan-cine { position: absolute; inset: 0; }

.scan-cine-before,
.scan-cine-after {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    object-position: 62% 68%; /* keep the turf, not the sky, in frame on tall phones */
}

.scan-cine-before { animation: scanKen 28s var(--ease-in-out) infinite alternate; }

.scan-cine-after-wrap {
    position: absolute; inset: 0;
    clip-path: inset(0 100% 0 0);
    animation: scanWipe 14s var(--ease-in-out) infinite;
    will-change: clip-path;
}
.scan-cine-after { animation: scanKen 28s var(--ease-in-out) infinite alternate; }

.scan-cine-sweep {
    position: absolute; top: -10%; bottom: -10%;
    left: 0; width: 3px;
    background: linear-gradient(to bottom, transparent, rgba(255,255,255,0.9), transparent);
    box-shadow: 0 0 22px 4px rgba(255,255,255,0.35);
    animation: scanSweep 14s var(--ease-in-out) infinite;
    will-change: transform, opacity;
}

@keyframes scanWipe {
    0%, 8%   { clip-path: inset(0 100% 0 0); }
    38%, 78% { clip-path: inset(0 0% 0 0); }
    96%,100% { clip-path: inset(0 100% 0 0); }
}
@keyframes scanSweep {
    0%, 8%   { transform: translateX(0vw);   opacity: 0; }
    10%      { opacity: 1; }
    38%      { transform: translateX(100vw); opacity: 1; }
    42%, 74% { opacity: 0; }
    78%      { transform: translateX(100vw); opacity: 0; }
    96%,100% { transform: translateX(0vw);   opacity: 0; }
}
@keyframes scanKen { from { transform: scale(1.02); } to { transform: scale(1.12); } }

/* legibility shade — headline sits on the lower third */
.scan-cine-shade {
    position: absolute; inset: 0;
    background:
        linear-gradient(to top,
            var(--bb-navy) 0%,
            rgba(10,15,21,0.95) 20%,
            rgba(10,15,21,0.78) 42%,
            rgba(10,15,21,0.42) 68%,
            rgba(10,15,21,0.55) 100%);
}

.scan-hero-tags {
    position: absolute;
    left: 0; right: 0;
    bottom: var(--space-5);
    z-index: 3;
    display: flex; justify-content: flex-end; gap: var(--space-2);
    padding: 0 var(--page-gutter);
    pointer-events: none;
}
.scan-tag {
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    padding: 7px 12px;
    border-radius: var(--radius-sm);
    background: rgba(8, 12, 18, 0.8);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}
.scan-tag--before { color: #FFC46B; border: 1px solid rgba(245,158,11,0.4); }
.scan-tag--after  { color: #4EE0A4; border: 1px solid rgba(16,185,129,0.4); }

/* — hero content — */

.scan-hero-inner {
    position: relative;
    z-index: 2;
    width: 100%;
    max-width: var(--max-width-sm);
    margin: 0 auto;
    padding: 0 var(--page-gutter);
}

.scan-headline {
    font-family: var(--font-display);
    font-size: var(--text-5xl);
    font-weight: 800;
    line-height: 1.04;
    letter-spacing: -0.028em;
    margin: var(--space-4) 0;
    text-wrap: balance;
    text-shadow: 0 2px 30px rgba(0, 0, 0, 0.55);
}
.scan-headline em {
    font-style: normal;
    color: #4EE0A4;
}

.scan-sub {
    font-size: var(--text-base);
    line-height: 1.65;
    color: rgba(255, 255, 255, 0.9);
    max-width: 54ch;
    text-shadow: 0 1px 16px rgba(0, 0, 0, 0.7);
    margin: 0 0 var(--space-5);
}

/* — the field — */

.scan-form { max-width: 560px; }

.scan-field {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    background: rgba(8, 12, 18, 0.82);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: var(--radius-lg);
    padding: 0 var(--space-4);
    min-height: 58px;
    transition: border-color var(--duration-normal) var(--ease-out),
                box-shadow var(--duration-normal) var(--ease-out);
}
.scan-field:focus-within {
    border-color: var(--bb-green-bright);
    box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.15), var(--shadow-lg);
}
.scan-field.is-invalid { border-color: var(--bb-error); }

.scan-field-pin { color: var(--bb-green-muted); flex-shrink: 0; }

.scan-field input {
    flex: 1;
    min-width: 0;
    background: none;
    border: none;
    outline: none;
    color: var(--bb-white);
    font-family: var(--font-body);
    font-size: 16px; /* exactly 16 — smaller zooms on iOS focus */
    font-weight: 500;
    /* The caret sat ABOVE the box because the input had no explicit height and
       inherited a line-height that pushed its text box out of the flex centre —
       giving the appearance of two caret positions until focus. Explicit
       height + matching line-height pins it. */
    height: 56px;
    line-height: 56px;
    padding: 0;
    margin: 0;
    -webkit-appearance: none;
    appearance: none;
}
.scan-field input::placeholder { color: rgba(255, 255, 255, 0.42); }

/* Google injects its own dropdown; keep it above the map and on-brand */
.pac-container {
    z-index: var(--z-modal) !important;
    border-radius: var(--radius-md);
    border: 1px solid var(--bb-glass-border);
    background: #131B26;
    box-shadow: var(--shadow-xl);
    font-family: var(--font-body);
    margin-top: 6px;
}
.pac-item { border-top: 1px solid rgba(255,255,255,0.06); color: var(--bb-mid-gray); padding: 10px 12px; }
.pac-item:hover { background: rgba(255,255,255,0.05); }
.pac-item-query { color: var(--bb-white); }
.pac-icon { display: none; }

.scan-go {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 56px;
    margin-top: var(--space-3);
    padding: var(--space-3) var(--space-6);
    font-family: var(--font-display);
    font-size: var(--text-base);
    font-weight: 700;
    letter-spacing: 0.01em;
    color: var(--bb-navy);
    background: var(--bb-orange);
    border: none;
    border-radius: var(--radius-lg);
    cursor: pointer;
    box-shadow: 0 10px 28px -10px var(--bb-orange-glow);
    transition: filter var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-out);
}
.scan-go:hover  { filter: brightness(1.07); }
.scan-go:active { transform: scale(0.98); }

/* "Not ready yet" state.
   Deliberately a CLASS, not the disabled attribute. A disabled button fires
   no click event, so pressing it can never explain WHY it is dim - which is
   exactly the dead end this hit. Dimmed but live: press it and the missing
   field shakes. Matches signup.css:2274 at 0.4 opacity. */
.scan-go.is-waiting {
    opacity: 0.4;
    box-shadow: none;
}
.scan-go.is-waiting:hover  { filter: none; }
.scan-go.is-waiting:active { transform: scale(0.98); }

/* Kept for the genuinely-disabled case: mid-submit, when a second press
   would double-post. That one SHOULD swallow clicks. */
.scan-go[disabled] {
    opacity: 0.4;
    cursor: not-allowed;
    transform: none;
    filter: none;
    box-shadow: none;
}

/* Rejection feedback on the button itself. The field turning red is easy to
   miss when the thumb is already over the button - the thing you just pressed
   should be the thing that reacts. */
.scan-go.is-rejected {
    animation: scan-go-shake 320ms var(--ease-out);
    background: var(--bb-error);
    color: var(--bb-white);
}

@keyframes scan-go-shake {
    20%  { transform: translateX(-5px); }
    45%  { transform: translateX(5px);  }
    70%  { transform: translateX(-3px); }
    100% { transform: translateX(0);    }
}

@media (prefers-reduced-motion: reduce) {
    .scan-go.is-rejected { animation: none; }
}

.scan-error {
    min-height: 20px;
    margin: var(--space-2) 0 0;
    font-size: var(--text-sm);
    color: #FF8A8A;
}

.scan-fineprint {
    margin: var(--space-3) 0 0;
    font-size: var(--text-xs);
    color: rgba(255, 255, 255, 0.72);
    text-shadow: 0 1px 12px rgba(0, 0, 0, 0.8);
}

.scan-proof {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    margin: var(--space-5) 0 0;
    padding: var(--space-4) 0 0;
    border-top: 1px solid rgba(255, 255, 255, 0.14);
}
.scan-proof li {
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: #A8C48A;
    text-shadow: 0 1px 12px rgba(0, 0, 0, 0.85);
}


/* ═══ ACT 2 · STAGE ══════════════════════════════════════════════ */

.scan-stage { position: relative; }

.scan-map-wrap {
    position: relative;
    background: var(--bb-navy-light);
}

/* Two classes deep ON PURPOSE.
   mapbox-gl.css is injected at runtime, so it lands AFTER scan.css and its
   `.mapboxgl-map { position: relative }` beats a single-class `.scan-map`.
   That silently killed `position:absolute; inset:0` and collapsed the
   container to 0px — GL then built a 0x0 canvas and reported nothing.
   Give the element its own height and enough specificity to keep it. */
.scan-map-wrap .scan-map {
    position: relative;
    width: 100%;
    height: 62svh;
    min-height: 400px;
}
@supports not (height: 1svh) {
    .scan-map-wrap .scan-map { height: 62vh; }
}

.scan-map-vignette {
    position: absolute; inset: 0;
    pointer-events: none;
    z-index: 2;
    /* Imagery emerges from the navy and dissolves back into it on all four
       edges, rather than sitting in a hard rectangle. */
    background:
        linear-gradient(to bottom,
            var(--bb-navy) 0%, rgba(10,15,21,0.72) 7%, rgba(10,15,21,0) 22%),
        linear-gradient(to top,
            var(--bb-navy) 0%, rgba(10,15,21,0.85) 10%, rgba(10,15,21,0) 34%),
        linear-gradient(to right,
            rgba(10,15,21,0.55) 0%, transparent 14%, transparent 86%, rgba(10,15,21,0.55) 100%);
}

.scan-map-caption {
    position: absolute;
    z-index: 3;
    top: calc(var(--nav-height-mobile) + var(--space-4));
    left: var(--page-gutter);
    margin: 0;
    padding: 6px 11px;
    border-radius: var(--radius-sm);
    background: rgba(8, 12, 18, 0.7);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: rgba(255,255,255,0.9);
}

/* Keep Mapbox attribution (licensing) but stop it shouting */
.scan-map .mapboxgl-ctrl-bottom-right,
.scan-map .mapboxgl-ctrl-bottom-left { z-index: 3; opacity: 0.5; }

/* — the instrument panel, overlapping the imagery — */

.scan-panel {
    position: relative;
    z-index: 3;
    max-width: var(--max-width-xs);
    margin: -110px auto 0;
    padding: var(--space-5);
    background: rgba(16, 23, 33, 0.92);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-2xl);
}

.scan-panel-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-3);
}

/* — working indicator: orange arc while reading, completes to a green ring — */
.scan-working { flex-shrink: 0; width: 34px; height: 34px; position: relative; margin-top: 2px; }
.scan-working svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.scan-working .sw-track { fill: none; stroke: rgba(255,255,255,0.09); stroke-width: 2.5; }
.scan-working .sw-arc {
    fill: none; stroke: var(--bb-orange); stroke-width: 2.5; stroke-linecap: round;
    stroke-dasharray: 88; stroke-dashoffset: 66;
    filter: drop-shadow(0 0 5px rgba(245,120,66,0.6));
    animation: scanArc 1.1s linear infinite;
    transform-origin: 50% 50%;
}
@keyframes scanArc { to { transform: rotate(360deg); } }
.scan-working.is-done .sw-arc {
    animation: none;
    stroke: var(--bb-green-bright);
    stroke-dashoffset: 0;
    filter: drop-shadow(0 0 6px rgba(16,185,129,0.55));
    transition: stroke-dashoffset 0.7s var(--ease-out), stroke 0.3s;
}
.scan-working-check {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    font-family: var(--font-mono); font-size: 13px;
    color: var(--bb-green-bright);
    opacity: 0;
    transition: opacity 0.35s var(--ease-out) 0.4s;
}
.scan-working.is-done .scan-working-check { opacity: 1; }

.scan-panel-address {
    font-family: var(--font-display);
    font-size: var(--text-xl);
    font-weight: 700;
    letter-spacing: -0.015em;
    line-height: 1.25;
    margin: var(--space-2) 0 0;
}

/* — the pipe: rows are injected, land on a schedule — */

.scan-pipe { list-style: none; margin: 0; padding: 0; }

.scan-pl {
    display: grid;
    grid-template-columns: 16px 1fr auto;
    gap: var(--space-3);
    align-items: baseline;
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--bb-glass-border);
    opacity: 0;
    transform: translateY(8px);
}
.scan-pl:last-child { border-bottom: none; }

/* Landed rows are tappable — dotted underline is the quiet invitation,
   the hover wash is the confirmation. Rows are inert until data lands. */
.scan-pl[data-status] {
    cursor: pointer;
    margin: 0 calc(var(--space-3) * -1);
    padding-left: var(--space-3);
    padding-right: var(--space-3);
    border-radius: var(--radius-sm);
    transition: background var(--duration-fast) var(--ease-out);
    -webkit-tap-highlight-color: transparent;
}
.scan-pl[data-status] .scan-pl-src {
    border-bottom: 1px dotted rgba(255, 255, 255, 0.28);
    padding-bottom: 1px;
}
@media (hover: hover) {
    .scan-pl[data-status]:hover { background: rgba(255, 255, 255, 0.05); }
    .scan-pl[data-status]:hover .scan-pl-src {
        color: var(--bb-white);
        border-bottom-color: var(--bb-green-bright);
    }
}
.scan-pl[data-status]:focus-visible {
    outline: 2px solid var(--bb-green-bright);
    outline-offset: 2px;
    background: rgba(255, 255, 255, 0.05);
}

.scan-pl.in { animation: scanPlIn 0.55s var(--ease-out) forwards; }
@keyframes scanPlIn { to { opacity: 1; transform: none; } }

.scan-pl-tick {
    font-family: var(--font-mono);
    font-size: 13px;
    color: #5A6D66;
}
.scan-pl[data-status="measured"]  .scan-pl-tick { color: var(--bb-green-bright); }
.scan-pl[data-status="estimated"] .scan-pl-tick { color: var(--bb-warning); }
.scan-pl[data-status="missing"]   .scan-pl-tick { color: var(--bb-error); }

.scan-pl-spin {
    display: inline-block;
    width: 10px; height: 10px;
    border-radius: var(--radius-full);
    border: 1.5px solid rgba(255,255,255,0.14);
    border-top-color: rgba(234,241,237,0.7);
    animation: scanSpin 0.7s linear infinite;
}
@keyframes scanSpin { to { transform: rotate(360deg); } }

.scan-pl-src {
    font-family: var(--font-mono);
    font-size: 12px;
    letter-spacing: 0.03em;
    color: var(--bb-mid-gray);
}
.scan-pl[data-status] .scan-pl-src { color: var(--bb-white); }

.scan-pl-val {
    font-family: var(--font-mono);
    font-size: var(--text-base);
    font-weight: 600;
    letter-spacing: -0.01em;
    text-align: right;
    white-space: nowrap;
    opacity: 0;
    transition: opacity 0.45s var(--ease-out) 0.08s;
    color: var(--bb-green-bright);
}
.scan-pl[data-status] .scan-pl-val { opacity: 1; }
.scan-pl[data-status="estimated"] .scan-pl-val { color: #FFC46B; }
.scan-pl[data-status="missing"]   .scan-pl-val { color: #FF8A8A; font-size: var(--text-sm); }

/* Data-quality note — validateSoilDataSanity()'s verdict, in plain English.
   Appears only when quality is 'low' or 'suspect'. The engine already refuses
   flagged NRCS values; this gives the Scan the same discipline instead of
   printing them with a confident green tick. */
.scan-quality[hidden] { display: none; }

.scan-quality {
    margin: var(--space-4) 0 0;
    padding: var(--space-3) var(--space-4);
    border-left: 3px solid var(--bb-warning);
    background: rgba(245, 158, 11, 0.07);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    font-size: var(--text-sm);
    line-height: 1.6;
    color: rgba(255, 255, 255, 0.88);
}
.scan-quality b { color: #FFC46B; font-weight: 600; }

.scan-quality.is-suspect {
    border-left-color: var(--bb-error);
    background: rgba(239, 68, 68, 0.08);
}
.scan-quality.is-suspect b { color: #FF8A8A; }

.scan-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    margin: var(--space-4) 0 0;
    font-size: var(--text-xs);
    color: #5A6D66;
}
.scan-legend span { display: inline-flex; align-items: center; gap: 6px; }
.scan-dot { width: 6px; height: 6px; border-radius: var(--radius-full); display: inline-block; }
.scan-dot--measured  { background: var(--bb-green-bright); }
.scan-dot--estimated { background: var(--bb-warning); }
.scan-dot--missing   { background: var(--bb-error); }


/* ═══ ACT 3 · VERDICT ════════════════════════════════════════════ */

.scan-verdict {
    max-width: var(--max-width-sm);
    margin: 0 auto;
    padding: var(--space-12) var(--page-gutter) var(--space-8);
    animation: scanPlIn 0.6s var(--ease-out);
}

.scan-verdict-tag { color: #FFC46B; }
.scan-verdict.is-ok .scan-verdict-tag { color: var(--bb-green-bright); }

.scan-verdict-line {
    font-family: var(--font-display);
    font-size: var(--text-4xl);
    font-weight: 800;
    line-height: 1.08;
    letter-spacing: -0.025em;
    margin: var(--space-4) 0;
    text-wrap: balance;
}

.scan-verdict-body {
    font-size: var(--text-lg);
    line-height: 1.65;
    color: var(--bb-mid-gray);
    max-width: 58ch;
    margin: 0 0 var(--space-6);
}
.scan-verdict-body b { color: var(--bb-white); font-weight: 600; }

.scan-wedge {
    font-family: var(--font-display);
    font-size: var(--text-xl);
    font-weight: 700;
    letter-spacing: -0.015em;
    color: var(--bb-white);
    margin: 0;
    padding-left: var(--space-4);
    border-left: 3px solid var(--bb-orange);
}


/* ═══ ACT 4 · THE ORDER ══════════════════════════════════════════ */

.scan-order {
    max-width: var(--max-width-sm);
    margin: 0 auto;
    padding: var(--space-8) var(--page-gutter);
}

.scan-order-title {
    font-family: var(--font-display);
    font-size: var(--text-3xl);
    font-weight: 800;
    line-height: 1.12;
    letter-spacing: -0.022em;
    margin: 0 0 var(--space-6);
    text-wrap: balance;
}
.scan-order-title em { font-style: normal; color: var(--bb-orange); }

.scan-rail {
    list-style: none;
    margin: 0;
    padding: 0;
    position: relative;
}
/* the connective spine */
.scan-rail::before {
    content: '';
    position: absolute;
    left: 15px; top: 18px; bottom: 18px;
    width: 2px;
    background: linear-gradient(to bottom, var(--bb-green-bright), rgba(255,255,255,0.08) 55%);
}

.scan-rail li {
    position: relative;
    padding: var(--space-3) var(--space-2) var(--space-3) var(--space-8);
    margin: 0 calc(var(--space-2) * -1);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: background var(--duration-fast) var(--ease-out);
    -webkit-tap-highlight-color: transparent;
}
@media (hover: hover) {
    .scan-rail li:hover { background: rgba(255, 255, 255, 0.04); }
    .scan-rail li:hover .scan-rail-node {
        border-color: var(--bb-green-bright);
        color: var(--bb-green-bright);
    }
    .scan-rail li.is-current:hover .scan-rail-node {
        border-color: var(--bb-orange);
        color: var(--bb-navy);
    }
}
.scan-rail li:focus-visible {
    outline: 2px solid var(--bb-green-bright);
    outline-offset: 2px;
}

.scan-rail-node {
    position: absolute;
    left: 0; top: var(--space-3);
    width: 32px; height: 32px;
    border-radius: var(--radius-full);
    display: flex; align-items: center; justify-content: center;
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 600;
    background: var(--bb-navy-light);
    border: 1px solid var(--bb-glass-border);
    color: var(--bb-green-muted);
}
.scan-rail li.is-current .scan-rail-node {
    background: var(--bb-orange);
    border-color: var(--bb-orange);
    color: var(--bb-navy);
    box-shadow: 0 0 0 5px var(--bb-orange-subtle), var(--shadow-orange-glow);
}

.scan-rail-title {
    display: block;
    font-family: var(--font-display);
    font-size: var(--text-lg);
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--bb-white);
    margin-bottom: 2px;
}
.scan-rail li.is-current .scan-rail-title { color: var(--bb-orange); }

.scan-rail-meta {
    font-size: var(--text-sm);
    line-height: 1.6;
    color: var(--bb-mid-gray);
}

.scan-rail-you {
    display: inline-block;
    margin-left: var(--space-2);
    padding: 3px 9px;
    border-radius: var(--radius-full);
    background: rgba(239, 68, 68, 0.12);
    border: 1px solid rgba(239, 68, 68, 0.4);
    color: var(--bb-error);
    font-family: var(--font-mono);
    font-size: 9.5px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    vertical-align: middle;
}

.scan-order-note {
    margin: var(--space-6) 0 0;
    font-size: var(--text-lg);
    line-height: 1.65;
    font-style: italic;
    color: rgba(234, 241, 237, 0.9);
    padding-left: var(--space-4);
    border-left: 2px solid var(--bb-glass-border);
}


/* ═══ THE ASK ════════════════════════════════════════════════════ */

.scan-save {
    max-width: var(--max-width-xs);
    margin: 0 auto;
    padding: var(--space-6) var(--page-gutter) var(--space-8);
    text-align: center;
}

.scan-save-card {
    text-align: left;
    padding: var(--space-6);
    background: rgba(19, 27, 38, 0.9);
    border: 1px solid var(--bb-glass-border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-xl);
    position: relative;
    overflow: hidden;
}
.scan-save-card::after {
    content: '';
    position: absolute; inset: 0;
    pointer-events: none;
    background: radial-gradient(340px 140px at 10% 0%, var(--bb-orange-subtle), transparent 70%);
}

.scan-save-title {
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    font-weight: 800;
    letter-spacing: -0.02em;
    margin: var(--space-2) 0;
}

.scan-save-sum {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    line-height: 1.7;
    color: var(--bb-green-muted);
    margin: 0 0 var(--space-4);
}

.scan-save-form { position: relative; z-index: 2; }

.scan-save-form input {
    width: 100%;
    min-height: 54px;
    padding: var(--space-3) var(--space-4);
    font-size: 16px;
    font-family: var(--font-body);
    color: var(--bb-white);
    background: var(--bb-navy);
    border: 1px solid var(--bb-glass-border);
    border-radius: var(--radius-md);
    outline: none;
    transition: border-color var(--duration-normal) var(--ease-out);
    -webkit-appearance: none;
}
.scan-save-form input:focus { border-color: var(--bb-orange); }
.scan-save-form input.is-invalid {
    border-color: var(--bb-error);
    animation: scan-field-shake 320ms var(--ease-out);
}

@keyframes scan-field-shake {
    20%  { transform: translateX(-5px); }
    45%  { transform: translateX(5px);  }
    70%  { transform: translateX(-3px); }
    100% { transform: translateX(0);    }
}

@media (prefers-reduced-motion: reduce) {
    .scan-save-form input.is-invalid { animation: none; }
}

.scan-save-note {
    margin: var(--space-3) 0 0;
    font-size: var(--text-xs);
    color: #5A6D66;
    position: relative;
    z-index: 2;
}
.scan-save-note.is-done { color: var(--bb-green-bright); }

/* ── TERMS CHECKBOX ───────────────────────────────────────────────
   This button creates an account, so assent gets recorded the same way
   the free signup modal records it. Structurally identical markup, but
   styled to the Scan's own language rather than importing the modal's.

   The native input stays in the DOM and stays focusable - it is only
   visually replaced. Never appearance:none on a checkbox with nothing
   accessible behind it. */

.scan-terms {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    margin: var(--space-4) 0 0;
    position: relative;
    z-index: 2;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

/* Visually hidden, functionally present. */
.scan-terms input {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
}

.scan-terms-box {
    flex-shrink: 0;
    display: grid;
    place-items: center;
    width: 22px;
    height: 22px;
    margin-top: 1px;
    background: var(--bb-navy);
    border: 1px solid var(--bb-glass-border);
    border-radius: 7px;
    color: var(--bb-navy);
    transition:
        background var(--duration-fast) var(--ease-out),
        border-color var(--duration-fast) var(--ease-out),
        transform 300ms var(--ease-out-back);
}

.scan-terms-box svg {
    width: 12px;
    height: 10px;
    /* pathLength=1 in the markup normalises this to one keyframe. */
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
}

.scan-terms input:checked + .scan-terms-box {
    background: var(--bb-green-bright);
    border-color: var(--bb-green-bright);
    transform: scale(1.08);
}

.scan-terms input:checked + .scan-terms-box svg path {
    animation: scan-terms-draw 240ms var(--ease-out) 40ms forwards;
}

@keyframes scan-terms-draw {
    to { stroke-dashoffset: 0; }
}

.scan-terms input:focus-visible + .scan-terms-box {
    outline: 2px solid var(--bb-orange);
    outline-offset: 3px;
}

.scan-terms.is-invalid .scan-terms-box {
    border-color: var(--bb-error);
    animation: scan-terms-shake 300ms var(--ease-out);
}

@keyframes scan-terms-shake {
    20%  { transform: translateX(-4px); }
    45%  { transform: translateX(4px);  }
    70%  { transform: translateX(-2px); }
    100% { transform: translateX(0);    }
}

.scan-terms-text {
    font-size: var(--text-xs);
    line-height: 1.55;
    color: #5A6D66;
    user-select: none;
}

.scan-terms-text a {
    color: var(--bb-green-bright);
    text-decoration: underline;
    text-underline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    .scan-terms-box { transition: none; }
    .scan-terms input:checked + .scan-terms-box { transform: none; }
    .scan-terms input:checked + .scan-terms-box svg path {
        animation: none;
        stroke-dashoffset: 0;
    }
    .scan-terms.is-invalid .scan-terms-box { animation: none; }
}

/* Post-save CTA. Appears in place of the email form once the account exists.
   Deliberately NOT an auto-redirect: the session cookie is already set server
   side, so nothing is at risk by letting them stay and take the share link.
   Sized to the same 44px floor as every other touch target on the page. */
.scan-save-cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 48px;
    margin-top: var(--space-3);
    padding: var(--space-3) var(--space-6);
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: 700;
    letter-spacing: 0.01em;
    text-decoration: none;
    color: var(--bb-navy);
    background: var(--bb-orange);
    border-radius: var(--radius-lg);
    box-shadow: 0 10px 28px -10px var(--bb-orange-glow);
    transition: filter var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-out);
}

.scan-save-cta:hover  { filter: brightness(1.07); }
.scan-save-cta:active { transform: scale(0.98); }

.scan-save-cta:focus-visible {
    outline: 2px solid var(--bb-orange);
    outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
    .scan-save-cta { transition: none; }
    .scan-save-cta:active { transform: none; }
}

/* Point-of-capture disclosure. Deliberately plain, deliberately BEFORE the
   click. This is the first surface on the site that stores a street address
   from someone who never registers — they typed it in voluntarily to get a
   result they asked for, which is a clean basis, but only if we say what we
   keep. Do not shrink below readable or move below the button. */
.scan-save-privacy {
    margin: var(--space-3) 0 0;
    font-size: var(--text-xs);
    line-height: 1.65;
    color: #6C7F78;
    position: relative;
    z-index: 2;
}
.scan-save-privacy a { color: #A8C48A; text-decoration: underline; text-underline-offset: 2px; }
.scan-save-privacy a:hover { color: var(--bb-white); }

/* ── SHARE LINK ──────────────────────────────────────────────────
   A visible URL beats a promised email: no deliverability risk, no token
   system, works cross-device today. The link is just /scan/?address=...,
   which re-runs the scan — cheap, because the geocode is cached for a
   year and every soil call is free. */
.scan-share[hidden] { display: none; }

.scan-share {
    margin: 0 0 var(--space-4);
    padding: var(--space-4);
    background: rgba(16, 185, 129, 0.06);
    border: 1px solid rgba(16, 185, 129, 0.28);
    border-radius: var(--radius-md);
    position: relative;
    z-index: 2;
}
.scan-share-label {
    margin: 0 0 var(--space-2);
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: #4EE0A4;
}
.scan-share-row { display: flex; gap: var(--space-2); }
.scan-share-row input {
    flex: 1;
    min-width: 0;
    min-height: 44px;
    padding: 0 var(--space-3);
    font-family: var(--font-mono);
    font-size: 12px;
    color: rgba(255,255,255,0.9);
    background: var(--bb-navy);
    border: 1px solid rgba(255,255,255,0.12);
    border-radius: var(--radius-sm);
    outline: none;
    text-overflow: ellipsis;
}
.scan-share-btn {
    flex-shrink: 0;
    min-height: 44px;
    min-width: 76px;
    padding: 0 var(--space-4);
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--bb-navy);
    background: #4EE0A4;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: filter var(--duration-fast) var(--ease-out);
}
.scan-share-btn:hover { filter: brightness(1.08); }
.scan-share-btn.is-copied { background: var(--bb-green-bright); }
.scan-share-note {
    margin: var(--space-2) 0 0;
    font-size: var(--text-xs);
    color: #6C7F78;
}

/* ── RESUME CHIP ─────────────────────────────────────────────────
   The scan used to die with the tab. Completion now writes to
   localStorage, so a returning visitor is met with their own lot
   instead of an empty field. Most people return in the same browser,
   which makes this a better recovery path than an emailed token. */
.scan-resume[hidden] { display: none; }

.scan-resume {
    position: absolute;
    z-index: 4;
    left: var(--page-gutter);
    right: var(--page-gutter);
    top: calc(var(--nav-height-mobile) + var(--safe-top) + var(--space-3));
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    background: rgba(8, 12, 18, 0.86);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid rgba(16, 185, 129, 0.3);
    animation: scanPlIn 0.5s var(--ease-out);
}
.scan-resume-text {
    flex: 1;
    min-width: 0;
    font-size: var(--text-sm);
    color: rgba(255,255,255,0.9);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.scan-resume-btn {
    flex-shrink: 0;
    min-height: 36px;
    padding: 0 var(--space-4);
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--bb-navy);
    background: #4EE0A4;
    border: none;
    border-radius: var(--radius-full);
    cursor: pointer;
}
.scan-resume-x {
    flex-shrink: 0;
    width: 32px; height: 32px;
    background: none;
    border: none;
    color: var(--bb-mid-gray);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
}
.scan-resume-x:hover { color: var(--bb-white); }

@media (min-width: 900px) {
    .scan-resume {
        left: 50%; right: auto;
        transform: translateX(-50%);
        max-width: 620px;
        width: calc(100% - var(--page-gutter) * 2);
        top: calc(var(--nav-height) + var(--space-5));
    }
}

.scan-restart {
    margin-top: var(--space-6);
    min-height: 44px;
    background: none;
    border: none;
    color: var(--bb-mid-gray);
    font-size: var(--text-sm);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
}
.scan-restart:hover { color: var(--bb-white); }


/* ═══ COVERAGE BANNER ════════════════════════════════════════════
   Three tones, and the tone IS the message:
     .is-note  green  — full agronomic support, rarely shown
     .is-warn  amber  — borrowed timing data, real soil data
     .is-stop  red    — wrong grass family, soil read still honest
   Never hides the soil read. NRCS is address-level and nationally
   accurate; withholding correct data over an unrelated gap would be
   the same sin the page accuses the lawn companies of. */

.scan-coverage[hidden] { display: none; }

.scan-coverage {
    position: relative;
    z-index: 4;
    max-width: var(--max-width-xs);
    margin: -110px auto var(--space-3);
    padding: var(--space-4);
    display: flex;
    gap: var(--space-3);
    align-items: flex-start;
    border-radius: var(--radius-lg);
    border: 1px solid rgba(255,255,255,0.12);
    background: rgba(16, 23, 33, 0.94);
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    box-shadow: var(--shadow-xl);
    animation: scanPlIn 0.5s var(--ease-out);
}

/* When the banner shows, IT takes the overlap and the panel sits under it */
.scan-coverage:not([hidden]) ~ .scan-panel { margin-top: 0; }

.scan-coverage-icon {
    flex-shrink: 0;
    width: 26px; height: 26px;
    border-radius: var(--radius-full);
    display: flex; align-items: center; justify-content: center;
    font-family: var(--font-mono);
    font-size: 13px;
    font-weight: 700;
    margin-top: 1px;
}

.scan-coverage-title {
    margin: 0 0 4px;
    font-family: var(--font-display);
    font-size: var(--text-base);
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--bb-white);
}
.scan-coverage-body {
    margin: 0;
    font-size: var(--text-sm);
    line-height: 1.6;
    color: var(--bb-mid-gray);
}
.scan-coverage-body b { color: rgba(255,255,255,0.92); font-weight: 600; }

.scan-coverage.is-note { border-color: rgba(16,185,129,0.32); }
.scan-coverage.is-note .scan-coverage-icon {
    background: rgba(16,185,129,0.14); color: #4EE0A4;
}
.scan-coverage.is-warn { border-color: rgba(245,158,11,0.32); }
.scan-coverage.is-warn .scan-coverage-icon {
    background: rgba(245,158,11,0.14); color: #FFC46B;
}
.scan-coverage.is-stop { border-color: rgba(239,68,68,0.34); }
.scan-coverage.is-stop .scan-coverage-icon {
    background: rgba(239,68,68,0.14); color: #FF8A8A;
}

@media (max-width: 899px) {
    .scan-coverage { margin-top: -80px; border-radius: var(--radius-md); }
}
@media (min-width: 900px) {
    .scan-coverage { margin-top: -150px; max-width: 660px; }
}


/* ═══ INFO MODAL ═════════════════════════════════════════════════
   Same physics as the Soil Test Lab info cards (.lab-info-modal):
   overlay fade + card scale on cubic-bezier(0.34, 1.56, 0.64, 1),
   circular X pinned top-right. Desktop centers; mobile is a bottom
   sheet. body.scan-modal-open locks background scroll. */

/* CRITICAL: the [hidden] rule must exist. Author CSS overrides the browser's
   built-in [hidden]{display:none}, so `display:flex` below un-hides this
   overlay — leaving an invisible full-screen layer that eats every click and
   locks scrolling. That exact bug shipped on 2026-08-24. */
.scan-info-overlay[hidden] { display: none; }

body.scan-modal-open { overflow: hidden; }

.scan-info-overlay {
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
    background: rgba(4, 7, 11, 0.7);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    opacity: 0;
    transition: opacity 0.28s var(--ease-out);
}
.scan-info-overlay.active { opacity: 1; }

.scan-info-modal {
    position: relative;
    width: 100%;
    max-width: 520px;
    max-height: 82svh;
    background: rgba(16, 23, 33, 0.97);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-2xl);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    transform: scale(0.94) translateY(10px);
    transition: transform 0.32s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.scan-info-overlay.active .scan-info-modal { transform: scale(1) translateY(0); }

.scan-info-close {
    position: absolute;
    top: 12px;
    right: 12px;
    z-index: 2;
    width: 38px;
    height: 38px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: var(--radius-full);
    background: rgba(255, 255, 255, 0.08);
    color: var(--bb-white);
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
    transition: background var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-out);
}
.scan-info-close:hover { background: rgba(245, 120, 66, 0.35); transform: scale(1.08); }
.scan-info-close:focus-visible { outline: 2px solid var(--bb-green-bright); outline-offset: 2px; }

.scan-info-scroll {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: var(--space-6);
    overscroll-behavior: contain;
}

.scan-info-title {
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1.15;
    margin: var(--space-2) 0 var(--space-3);
    padding-right: var(--space-8); /* clear the X */
    text-wrap: balance;
}

/* Their number, front and centre — the modal is about THEIR lawn */
.scan-info-reading {
    display: inline-block;
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    font-weight: 600;
    color: #FFC46B;
    background: rgba(245, 158, 11, 0.09);
    border: 1px solid rgba(245, 158, 11, 0.3);
    border-radius: var(--radius-sm);
    padding: 6px 11px;
    margin: 0 0 var(--space-4);
}
.scan-info-reading.is-good {
    color: #4EE0A4;
    background: rgba(16, 185, 129, 0.09);
    border-color: rgba(16, 185, 129, 0.3);
}

.scan-info-body {
    font-size: var(--text-base);
    line-height: 1.7;
    color: var(--bb-mid-gray);
}
.scan-info-body p { margin: 0 0 var(--space-3); }
.scan-info-body p:last-child { margin-bottom: 0; }
.scan-info-body b { color: var(--bb-white); font-weight: 600; }

/* Capability line — the machine, glimpsed. Quieter than the pointer,
   never a CTA verb. Evidence, not pitch. */
.scan-info-body .scan-info-cap {
    margin-top: var(--space-4);
    font-size: var(--text-sm);
    font-style: italic;
    line-height: 1.65;
    color: #A8C48A;
}
.scan-info-body .scan-info-cap b { color: var(--bb-green-bright); font-style: normal; }

/* Every modal ends by pointing back at the funnel */
.scan-info-body .scan-info-pointer {
    margin-top: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-left: 3px solid var(--bb-orange);
    background: rgba(245, 120, 66, 0.06);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    color: rgba(255, 255, 255, 0.92);
    font-size: var(--text-sm);
}

/* Mobile: bottom sheet */
@media (max-width: 899px) {
    .scan-info-overlay { align-items: flex-end; padding: 0; }
    .scan-info-modal {
        max-width: none;
        max-height: 84svh;
        border-radius: var(--radius-xl) var(--radius-xl) 0 0;
        border-bottom: none;
        transform: translateY(48px);
        padding-bottom: var(--safe-bottom);
    }
    .scan-info-overlay.active .scan-info-modal { transform: translateY(0); }
    /* X stays put while content scrolls beneath it */
    .scan-info-close { position: sticky; top: 12px; margin-left: auto; float: right; }
}


/* ═══ ATTRIBUTION — licensing requirement, do not remove ═════════ */

.scan-attribution {
    max-width: var(--max-width-sm);
    margin: 0 auto;
    padding: var(--space-6) var(--page-gutter) var(--space-10);
    font-size: var(--text-xs);
    line-height: 1.7;
    color: #4A4A47;
    border-top: 1px solid var(--bb-glass-border);
}


/* ═══ DESKTOP ════════════════════════════════════════════════════ */

@media (max-width: 899px) {
    /* Tags were landing on top of the placard. Park them at the very bottom,
       clear of every other hero element. */
    .scan-hero-tags {
        bottom: calc(var(--safe-bottom) + var(--space-2));
        justify-content: center;
    }
    .scan-hero { padding-bottom: var(--space-12); }
    .scan-panel { margin-top: -80px; border-radius: var(--radius-lg); }
    .scan-map-wrap .scan-map { height: 52svh; min-height: 340px; }
}


@media (min-width: 900px) {

    .scan-hero { padding-top: calc(var(--nav-height) + var(--space-10)); align-items: center; }
    .scan-hero-inner { max-width: var(--max-width); }
    .scan-headline { max-width: 16ch; }

    .scan-form { display: flex; gap: var(--space-3); max-width: 640px; align-items: flex-start; }
    .scan-field { flex: 1; }
    .scan-go { width: auto; margin-top: 0; min-width: 190px; }

    .scan-map-wrap .scan-map { height: 74vh; min-height: 540px; }
    .scan-panel { margin-top: -150px; max-width: 660px; padding: var(--space-6); }

    /* verdict earns the width of the page */
    .scan-verdict { padding-top: var(--space-14); }
    .scan-verdict-line { font-size: var(--text-5xl); max-width: 18ch; }

    .scan-save-form { display: flex; gap: var(--space-3); }
    .scan-save-form input { flex: 1; }
    .scan-save-form .scan-go { min-width: 180px; }
}


/* ═══ REDUCED MOTION ═════════════════════════════════════════════ */

@media (prefers-reduced-motion: reduce) {
    .scan-cine-before,
    .scan-cine-after { animation: none; }
    .scan-cine-after-wrap { animation: none; clip-path: inset(0 0 0 0); } /* rest on the good lawn */
    .scan-cine-sweep { display: none; }
    .scan-pl { opacity: 1; transform: none; animation: none !important; }
    .scan-info-overlay,
    .scan-info-modal { transition: none; }
    .scan-pl-val { transition: none; }
    .scan-working .sw-arc { animation: none; }
}

/* ═══ BOARDING SEQUENCE ═════════════════════════════════════════════
   The save-moment takeover. One continuous story: a thread fills down
   the step chips as each completes, hands off to a blooming check, a
   masked title rise, and a warp-out into the dashboard.

   Contract with scan.js:
     .is-open / .is-closing      overlay lifecycle
     .is-warp                    exit-through transition, then redirect
     row .is-on / .is-done       chip working / chip filled
     --brd-p (0..1) on #scanBrd  drives the hairline bar and the thread
     .scan-brd-ring.is-live      countdown drain; its animationend IS
                                 the redirect signal (reduced motion
                                 never auto-navigates, by construction) */

.scan-brd[hidden] { display: none; }

.scan-brd {
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-6) var(--space-4);
    background: rgba(8, 13, 22, 0.94);
    -webkit-backdrop-filter: blur(20px) saturate(150%);
    backdrop-filter: blur(20px) saturate(150%);
    opacity: 0;
    transition: opacity 320ms var(--ease-out);
    overflow: hidden;
}

.scan-brd.is-open    { opacity: 1; }
.scan-brd.is-closing { opacity: 0; transition-duration: 200ms; }

/* Ambient light. Two soft fields drifting against each other - the
   motion is barely conscious, which is exactly the register. */
.scan-brd-glow {
    position: absolute;
    width: 520px;
    height: 520px;
    border-radius: var(--radius-full);
    filter: blur(8px);
    pointer-events: none;
}

.scan-brd-glow--a {
    top: -220px;
    left: -160px;
    background: radial-gradient(circle,
        color-mix(in srgb, var(--bb-orange) 13%, transparent) 0%, transparent 62%);
    animation: scan-brd-drift-a 9s var(--ease-in-out) infinite alternate;
}

.scan-brd-glow--b {
    bottom: -240px;
    right: -180px;
    background: radial-gradient(circle,
        color-mix(in srgb, var(--bb-green-bright) 10%, transparent) 0%, transparent 62%);
    animation: scan-brd-drift-b 11s var(--ease-in-out) infinite alternate;
}

@keyframes scan-brd-drift-a { to { transform: translate(46px, 30px); } }
@keyframes scan-brd-drift-b { to { transform: translate(-40px, -26px); } }

/* The card. Gradient surface, hairline light edge, layered depth. */
.scan-brd-inner {
    position: relative;
    width: 100%;
    max-width: 420px;
    padding: var(--space-8) var(--space-6);
    background: linear-gradient(165deg, rgba(27, 37, 52, 0.94) 0%, rgba(13, 20, 30, 0.97) 60%);
    border: 1px solid rgba(255, 255, 255, 0.09);
    border-radius: var(--radius-2xl);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.06),
        0 1px 2px rgba(0, 0, 0, 0.3),
        0 16px 40px -12px rgba(0, 0, 0, 0.5),
        0 48px 100px -28px rgba(0, 0, 0, 0.6);
    opacity: 0;
    transform: scale(0.955) translateY(16px);
    transition:
        opacity 320ms var(--ease-out),
        transform 520ms var(--ease-spring),
        filter 300ms var(--ease-out);
    will-change: transform, opacity;
}

.scan-brd.is-open .scan-brd-inner {
    opacity: 1;
    transform: scale(1) translateY(0);
}

/* Warp-out: the exit IS a transition into the next page. The card
   scales through the viewer and softens while the overlay clears. */
.scan-brd.is-warp .scan-brd-inner {
    opacity: 0;
    transform: scale(1.06) translateY(-6px);
    filter: blur(7px);
    transition:
        opacity 280ms var(--ease-in-out),
        transform 300ms var(--ease-in-out),
        filter 280ms var(--ease-in-out);
}

.scan-brd.is-warp { opacity: 0; transition: opacity 340ms var(--ease-in-out) 60ms; }


/* ── Phase 1: the sequence ────────────────────────────────────────── */

.scan-brd-eyebrow { margin: 0 0 var(--space-3); }

/* Overall progress hairline. Driven by --brd-p from JS. */
.scan-brd-bar {
    height: 2px;
    margin-bottom: var(--space-6);
    border-radius: var(--radius-full);
    background: rgba(255, 255, 255, 0.08);
    overflow: hidden;
}

.scan-brd-bar-fill {
    display: block;
    height: 100%;
    width: 100%;
    border-radius: inherit;
    background: linear-gradient(90deg, var(--bb-green-bright), var(--bb-green-mint));
    transform-origin: left center;
    transform: scaleX(var(--brd-p, 0));
    transition: transform 640ms var(--ease-out);
}

/* The track: thread on the left, rows beside it. The thread is the
   connective tissue a plain checklist lacks - one object completing. */
.scan-brd-track { position: relative; }

.scan-brd-thread {
    position: absolute;
    top: 14px;
    bottom: 14px;
    left: 13px;
    width: 2px;
    border-radius: var(--radius-full);
    background: rgba(255, 255, 255, 0.08);
    overflow: hidden;
}

.scan-brd-thread-fill {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: inherit;
    background: linear-gradient(180deg, var(--bb-green-bright), var(--bb-green-mint));
    transform-origin: top center;
    transform: scaleY(var(--brd-p, 0));
    transition: transform 640ms var(--ease-out);
    box-shadow: 0 0 12px color-mix(in srgb, var(--bb-green-bright) 55%, transparent);
}

.scan-brd-rows {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--space-5);
}

.scan-brd-row {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    opacity: 0;
    transform: translateX(-6px);
    filter: blur(3px);
    transition:
        opacity 380ms var(--ease-out),
        transform 380ms var(--ease-out),
        filter 380ms var(--ease-out);
}

.scan-brd-row.is-on {
    opacity: 1;
    transform: translateX(0);
    filter: blur(0);
}

/* Chips: iOS-settings-tile language. Outline while working, solid
   green with a drawn navy tick when done, with a spring pop. */
.scan-brd-chip {
    position: relative;
    z-index: 1;                        /* sits over the thread */
    flex-shrink: 0;
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    background: var(--bb-navy);
    border: 1px solid var(--bb-glass-border);
    border-radius: 9px;
    transition:
        background 220ms var(--ease-out),
        border-color 220ms var(--ease-out),
        box-shadow 220ms var(--ease-out);
}

.scan-brd-chip svg { position: absolute; }

.scan-brd-arc-svg { inset: 2px; }

.scan-brd-arc {
    fill: none;
    stroke: var(--bb-green-bright);
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-dasharray: 0.7 0.3;
    opacity: 0;
    transform-origin: center;
}

.scan-brd-row.is-on .scan-brd-arc {
    opacity: 1;
    animation: scan-brd-spin 850ms linear infinite;
}

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

.scan-brd-tick {
    width: 12px;
    height: 10px;
    fill: none;
    stroke: var(--bb-navy);
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
}

.scan-brd-row.is-done .scan-brd-chip {
    background: var(--bb-green-bright);
    border-color: var(--bb-green-bright);
    box-shadow: 0 0 16px -2px color-mix(in srgb, var(--bb-green-bright) 60%, transparent);
    animation: scan-brd-pop 380ms var(--ease-out-back);
}

@keyframes scan-brd-pop {
    0%   { transform: scale(0.88); }
    55%  { transform: scale(1.09); }
    100% { transform: scale(1); }
}

.scan-brd-row.is-done .scan-brd-arc { animation: none; opacity: 0; }

.scan-brd-row.is-done .scan-brd-tick {
    animation: scan-brd-draw 240ms var(--ease-out) 90ms forwards;
}

@keyframes scan-brd-draw { to { stroke-dashoffset: 0; } }

.scan-brd-label {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    letter-spacing: 0.01em;
    color: #5A6D66;
    transition: color 320ms var(--ease-out);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.scan-brd-row.is-done .scan-brd-label { color: var(--bb-white); }

.scan-brd-seq {
    transition: opacity 300ms var(--ease-out), transform 300ms var(--ease-out);
}

.scan-brd-seq.is-exit { opacity: 0; transform: translateY(-16px); }


/* ── Phase 2: the landing ─────────────────────────────────────────── */

.scan-brd-finale { text-align: center; }

.scan-brd-finale > * { opacity: 0; transform: translateY(14px); }

.scan-brd-finale.is-in > * {
    opacity: 1;
    transform: translateY(0);
    transition: opacity 560ms var(--ease-out), transform 560ms var(--ease-out);
}

.scan-brd-finale.is-in > :nth-child(2) { transition-delay: 360ms; }
.scan-brd-finale.is-in > :nth-child(3) { transition-delay: 500ms; }
.scan-brd-finale.is-in > :nth-child(4) { transition-delay: 680ms; }
.scan-brd-finale.is-in > :nth-child(5) { transition-delay: 840ms; }

/* The check: drawn ring and mark, a soft bloom, and a halo that
   expands once and dies - the single loudest frame of the sequence. */
.scan-brd-check {
    position: relative;
    width: 84px;
    height: 84px;
    margin: 0 auto var(--space-5);
    color: var(--bb-green-bright);
}

.scan-brd-check svg {
    width: 100%;
    height: 100%;
    filter: drop-shadow(0 0 14px color-mix(in srgb, var(--bb-green-bright) 45%, transparent));
}

.scan-brd-halo {
    position: absolute;
    inset: -6px;
    border: 1.5px solid var(--bb-green-bright);
    border-radius: var(--radius-full);
    opacity: 0;
    transform: scale(0.7);
    pointer-events: none;
}

.scan-brd-finale.is-in .scan-brd-halo {
    animation: scan-brd-halo 700ms var(--ease-out) 860ms forwards;
}

@keyframes scan-brd-halo {
    0%   { opacity: 0.7; transform: scale(0.7); }
    100% { opacity: 0;   transform: scale(1.45); }
}

.scan-brd-check-ring,
.scan-brd-check-mark {
    fill: none;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
}

.scan-brd-check-ring { stroke-width: 2.5; }
.scan-brd-check-mark { stroke-width: 3; }

.scan-brd-finale.is-in .scan-brd-check-ring {
    animation: scan-brd-draw 520ms var(--ease-out) 100ms forwards;
}

.scan-brd-finale.is-in .scan-brd-check-mark {
    animation: scan-brd-draw 320ms var(--ease-out-back) 580ms forwards;
}

/* Masked title rise: the line clips, the text climbs out of it. */
.scan-brd-title {
    margin: 0 0 var(--space-2);
    font-family: var(--font-display);
    font-size: var(--text-3xl);
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--bb-white);
}

.scan-brd-title-line { display: block; overflow: hidden; }

.scan-brd-title-text {
    display: block;
    transform: translateY(112%);
}

.scan-brd-finale.is-in .scan-brd-title-text {
    transform: translateY(0);
    transition: transform 640ms cubic-bezier(0.19, 1, 0.22, 1) 420ms;
}

.scan-brd-sub {
    margin: 0 0 var(--space-6);
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    line-height: 1.6;
    color: var(--bb-green-muted);
}

.scan-brd-cta {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    min-height: 56px;
    padding: var(--space-3) var(--space-6);
    font-family: var(--font-display);
    font-size: var(--text-base);
    font-weight: 700;
    letter-spacing: 0.01em;
    text-decoration: none;
    color: var(--bb-navy);
    background: var(--bb-orange);
    border-radius: var(--radius-lg);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.28),
        0 10px 28px -10px var(--bb-orange-glow);
    transition: filter 160ms var(--ease-out), transform 160ms var(--ease-out), box-shadow 160ms var(--ease-out);
}

.scan-brd-cta:hover {
    filter: brightness(1.07);
    transform: translateY(-1px);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.28),
        0 14px 34px -10px var(--bb-orange-glow);
}

.scan-brd-cta:active { transform: scale(0.98); }

.scan-brd-cta:focus-visible { outline: 2px solid var(--bb-white); outline-offset: 3px; }

.scan-brd-ring { display: grid; place-items: center; width: 24px; height: 24px; }
.scan-brd-ring[hidden] { display: none; }
.scan-brd-ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }

.scan-brd-ring-track,
.scan-brd-ring-fill {
    fill: none;
    stroke-width: 3;
    stroke-linecap: round;
    stroke-dasharray: 1;
}

.scan-brd-ring-track { stroke: rgba(11, 18, 28, 0.25); stroke-dashoffset: 0; }
.scan-brd-ring-fill  { stroke: var(--bb-navy); stroke-dashoffset: 0; }

/* The drain IS the timer: scan.js redirects on animationend. Under
   reduced motion it never runs - no auto-navigation, by construction. */
.scan-brd-ring.is-live .scan-brd-ring-fill {
    animation: scan-brd-drain 4s linear forwards;
}

@keyframes scan-brd-drain { to { stroke-dashoffset: 1; } }

.scan-brd-stay {
    display: inline-block;
    margin-top: var(--space-4);
    padding: var(--space-2) var(--space-3);
    border: 0;
    background: none;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    letter-spacing: 0.02em;
    color: #5A6D66;
    cursor: pointer;
    transition: color 160ms var(--ease-out);
}

.scan-brd-stay:hover { color: var(--bb-ash); text-decoration: underline; text-underline-offset: 3px; }
.scan-brd-stay[hidden] { display: none; }


/* ── Mobile: bottom sheet ─────────────────────────────────────────── */

@media (max-width: 560px) {
    .scan-brd { align-items: flex-end; padding: 0; }

    .scan-brd-inner {
        max-width: none;
        border-left: 0;
        border-right: 0;
        border-bottom: 0;
        border-radius: var(--radius-2xl) var(--radius-2xl) 0 0;
        padding: var(--space-8) var(--space-5)
                 calc(var(--space-8) + env(safe-area-inset-bottom));
        transform: translateY(28px);
    }

    .scan-brd.is-open .scan-brd-inner { transform: translateY(0); }

    .scan-brd-inner::before {
        content: '';
        position: absolute;
        top: 10px;
        left: 50%;
        transform: translateX(-50%);
        width: 40px;
        height: 4px;
        border-radius: var(--radius-full);
        background: rgba(255, 255, 255, 0.22);
    }
}


/* ── Reduced motion ───────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
    .scan-brd,
    .scan-brd-inner,
    .scan-brd-row,
    .scan-brd-seq,
    .scan-brd-bar-fill,
    .scan-brd-thread-fill,
    .scan-brd-finale > * { transition: none; transform: none; filter: none; }

    .scan-brd-glow--a,
    .scan-brd-glow--b,
    .scan-brd-row.is-done .scan-brd-chip,
    .scan-brd-finale.is-in .scan-brd-halo { animation: none; }

    .scan-brd-row.is-on .scan-brd-arc { animation: none; opacity: 1; }

    .scan-brd-row.is-done .scan-brd-tick,
    .scan-brd-finale.is-in .scan-brd-check-ring,
    .scan-brd-finale.is-in .scan-brd-check-mark { animation: none; stroke-dashoffset: 0; }

    .scan-brd-title-text,
    .scan-brd-finale.is-in .scan-brd-title-text { transform: none; transition: none; }

    .scan-brd-ring.is-live .scan-brd-ring-fill { animation: none; }
}