/* Hero demo, glass edition (2026-08-17): the dotted canvas is the engine
 * made visible (lattice, accent zones, comet feeds, the Althea disc
 * thinking); the chat rides over it as frosted-glass product surfaces —
 * the actual-UI register. The moving dots blur beneath each card.
 * Animation: transform + opacity only, on the house curve. Reveal is
 * class-driven: elements start .hd-pending, beats lift it. */

.hd-stage {
    font-size: 15px;   /* every mockup surface inherits the product step */
    position: relative;
    /* the stage runs wider than the content column: the answer's side
     * plumes and the field's lift carry on past it and fade out */
    width: calc(100% + 320px);
    margin-inline: -160px;
    height: 100%;
    display: flex;
    flex-direction: column;
}

/* The window (A), frameless: the ruled showcase cell IS the frame, so
 * the window adds no second box — the chrome elements float over the
 * wallpaper and the board runs edge to edge. */
.hd-window {
    position: relative;
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
}
.hd-canvas {
    position: absolute;
    inset: 0;
    z-index: 0;
    border-radius: inherit;
    /* the canvas LIFTS the page grid inside the mockup, so its edges must
     * dissolve exactly like the ground beneath — otherwise the lift ends
     * on a hard line and reads as a cutoff in the field */
    -webkit-mask-image:
        linear-gradient(to right, transparent 0, #000 14%, #000 86%, transparent 100%),
        linear-gradient(to bottom, transparent 0, #000 7%, #000 60%, transparent 100%);
    -webkit-mask-composite: source-in;
    mask-image:
        linear-gradient(to right, transparent 0, #000 14%, #000 86%, transparent 100%),
        linear-gradient(to bottom, transparent 0, #000 7%, #000 60%, transparent 100%);
    mask-composite: intersect;
}

.hd-chat {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 840px;
    margin: auto;   /* centered in the cell both ways */
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.hd-bubble { padding: 32px 36px; border-radius: 24px; }

/* A LIVING RIM (2026-08-24): the pointer-tracked specular already makes the
 * glass feel handled — but only once you touch it. hdLight's idle arc keeps
 * --hd-lx/--hd-ly drifting when nobody is there, and these two surfaces now
 * carry a second, slower highlight riding those same variables: a soft
 * sheen that wanders across the pane and breathes. It is the same light
 * source as the rim, so the two never disagree. */
.hd-bubble-user::before, .hd-response::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    z-index: 0;
    background: radial-gradient(
        62% 74% at var(--hd-lx, 30%) var(--hd-ly, 12%),
        rgba(255, 255, 255, 0.038) 0%,
        rgba(255, 255, 255, 0.013) 44%,
        rgba(255, 255, 255, 0) 78%);
    animation: hd-sheen 7.5s ease-in-out infinite;
    transition: background 0.3s linear;
}
.hd-bubble-user::before {
    background: radial-gradient(
        62% 74% at var(--hd-lx, 30%) var(--hd-ly, 12%),
        rgba(207, 212, 120, 0.055) 0%,
        rgba(207, 212, 120, 0.018) 46%,
        rgba(207, 212, 120, 0) 80%);
}
/* the breath: the sheen swells and settles, never fully off */
@keyframes hd-sheen { 0%, 100% { opacity: 0.5; } 50% { opacity: 0.9; } }
@media (prefers-reduced-motion: reduce) {
    .hd-bubble-user::before, .hd-response::before { animation: none; opacity: 0.7; }
}
/* the content rides above the sheen */
.hd-bubble-user > *, .hd-response > * { position: relative; z-index: 1; }

/* Glass surfaces: frosted product cards over the live canvas. The user's
 * bubble carries the citron tint; citron-register cards warm their
 * borders; the code screen goes deeper (a terminal, not a bubble). */
.hd-bubble-user::after, .hd-response::after, .hd-doc::after, .hd-table::after,
.hd-verdict::after, .hd-reply::after, .hd-tile::after,
.hd-trace::after, .hd-step::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 1px;
    pointer-events: none;
    background:
        radial-gradient(26% 26% at var(--hd-lx, 22%) var(--hd-ly, 12%),
            rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0) 72%),
        rgba(255, 255, 255, 0.12);
    transition: background 0.25s linear;
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
}
/* the voices tint their own lit edge */
.hd-bubble-user::after {
    background:
        radial-gradient(26% 26% at var(--hd-lx, 22%) var(--hd-ly, 12%),
            rgba(255, 255, 255, 0.88) 0%, rgba(255, 255, 255, 0) 72%),
        rgba(207, 212, 120, 0.34);
}
.hd-reply::after, .hd-trace::after, .hd-doc::after, .hd-step::after {
    background:
        radial-gradient(26% 26% at var(--hd-lx, 22%) var(--hd-ly, 12%),
            rgba(255, 255, 255, 0.85) 0%, rgba(255, 255, 255, 0) 72%),
        rgba(167, 215, 214, 0.3);
}
/* the answer's edge stays quiet — the darker slab needs less rim light */
.hd-response::after {
    background:
        radial-gradient(24% 24% at var(--hd-lx, 22%) var(--hd-ly, 12%),
            rgba(255, 255, 255, 0.45) 0%, rgba(255, 255, 255, 0) 72%),
        rgba(150, 195, 194, 0.15);
}
.hd-table::after {   /* the verdict left this rule when it became an answer bubble: a citron rim on a teal container */
    background:
        radial-gradient(26% 26% at var(--hd-lx, 22%) var(--hd-ly, 12%),
            rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0) 72%),
        rgba(207, 212, 120, 0.26);
}

.hd-bubble-user, .hd-response, .hd-doc, .hd-table, .hd-verdict,
.hd-reply, .hd-tile, .hd-trace, .hd-step {
    background: rgba(255, 255, 255, 0.065);   /* flat: the material is uniform */
    backdrop-filter: blur(34px) saturate(1.5);
    -webkit-backdrop-filter: blur(34px) saturate(1.5);
    border: 0;
    position: relative;
    box-shadow:
        0 8px 24px rgba(0, 0, 0, 0.1),
        0 30px 80px rgba(0, 0, 0, 0.16),
        0 60px 140px rgba(0, 0, 0, 0.12);
}
.hd-bubble-user {
    background: rgba(207, 212, 120, 0.13);
}

.hd-bubble-user {
    align-self: center;   /* B: one axis, everything on it */
    /* the bubble is measured, not stretched: a comfortable reading measure
     * and balanced wrapping, so a long enquiry breaks into even lines
     * instead of running the full width and leaving a short orphan */
    /* wrap only when out of room: the balancer broke lines early and the
     * narrow ch-cap forced two-line questions that fit on one */
    max-width: 85%;
    text-wrap: pretty;
    font-size: var(--text-regular-size);
    line-height: 1.55;
    color: var(--text-primary);
    min-height: 3em;
    min-width: 8em;
    transition:
        opacity 0.35s var(--motion-curve-house),
        transform 0.35s var(--motion-curve-house),
        filter 0.35s var(--motion-curve-house);
}
/* one bubble at a time: the question exits before the next phase enters */
.hd-bubble-user.hd-out { opacity: 0; transform: translateY(-12px); }
.hd-bubble-user[hidden] { display: none; }
/* the ABSORB: the bubble dissolves in place — its mass leaves as canvas
 * dots flying into the logo (the particles carry the motion, not the box) */
/* the absorb lives in ONE place, under .hd-chatlog below — every panel
 * renders inside a chatlog, so a second rule here was only ever a trap for
 * whoever tuned it next (2026-08-24) */
[data-hd-content][hidden] { display: none !important; }

.hd-bubble-bot { align-self: flex-start; max-width: 92%; }

/* Condensation arrival: surfaces form out of the field — blurred, a
 * touch small, then sharpening into place as the frost dims behind.
 * Small bits rise; Althea's surfaces arrive FROM THE SIDES (teal from
 * the left, derived citron artifacts from the right). */
.hd-pending {
    opacity: 0;
    transform: translateY(8px) scale(0.97);
    filter: blur(6px);
}
.hd-response.hd-pending, .hd-trace.hd-pending, .hd-reply.hd-pending,
.hd-doc.hd-pending {
    transform: translateX(-40px) scale(0.98);
}
.hd-table.hd-pending, .hd-verdict.hd-pending {
    transform: translateX(40px) scale(0.98);
}
/* hero only (the disc anchor marks it): the answer condenses in place,
 * slowly, where the logo stood — no side entrance */
.hd-disc-anchor ~ .hd-chat .hd-response.hd-pending {
    transform: translateY(8px) scale(0.96);
    filter: blur(12px);
}
.hd-disc-anchor ~ .hd-chat .hd-response {
    transition:
        opacity 0.8s cubic-bezier(0.22, 1, 0.36, 1),
        transform 0.8s cubic-bezier(0.22, 1, 0.36, 1),
        filter 0.8s cubic-bezier(0.22, 1, 0.36, 1);
}

.hd-tile, .hd-tile-line, .hd-answer-block, .hd-fig, .hd-chip,
.hd-doc, .hd-sec-label, .hd-sec-line, .hd-table, .hd-trow, .hd-rv {
    transition:
        opacity var(--motion-reveal-duration) var(--motion-curve-house),
        transform var(--motion-reveal-duration) var(--motion-curve-house),
        filter var(--motion-reveal-duration) var(--motion-curve-house);
}

/* Speaker accents run the full perimeter: the glass border itself
 * carries the voice — citron for the asking side, teal for answers. */
.hd-reply, .hd-trace, .hd-doc, .hd-step {
    background: rgba(18, 31, 31, 0.66);
}

/* The answer: a darker, denser glass — the artifact you keep, distinct
 * from the light working surfaces around it. */
.hd-response {
    /* Apple glass with Althea's teal tint — the mirror of the citron ask.
     * Deeper than the working surfaces around it (2026-08-24): the artifact
     * you keep should sit heavier than the scratchwork. */
    background: rgba(11, 23, 23, 0.78);
    backdrop-filter: blur(40px) saturate(1.6);
    -webkit-backdrop-filter: blur(40px) saturate(1.6);
    border: 0;
    box-shadow:
        0 10px 30px rgba(0, 0, 0, 0.12),
        0 40px 100px rgba(0, 0, 0, 0.18),
        0 80px 180px rgba(0, 0, 0, 0.14);
}
.hd-response {
    position: relative;
    width: 100%;
    max-width: 960px;
    align-self: center;   /* B: the answer settles on the same axis */
    border-radius: 28px;
    font-size: var(--text-small-size);   /* 14px: paragraphs in the demo blocks */
    line-height: 1.6;
    color: var(--text-primary);   /* the answer reads white, like the rest of the demo */
    transition:
        opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1),
        transform 0.7s cubic-bezier(0.22, 1, 0.36, 1),
        filter 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}

/* The thinking overlay: source tiles + the Althea disc across the FULL
 * stage (no outer container while thinking). Top padding clears the
 * question bubble; fades out as the answer arrives. */
.hd-thinkstage {
    position: absolute;
    inset: 0;
    z-index: 2;
    padding: 32px;
    display: flex;
    flex-direction: column;
    transition: opacity 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}

/* The Althea logo, full and centered: the canvas draws the disc at this
 * anchor and reads its opacity — so the logo materializes when the
 * message becomes it, and dissolves when it becomes the answer. */
.hd-disc-anchor {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 2px;
    height: 2px;
    opacity: 0;
    transition: opacity 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}
.hd-disc-anchor.hd-disc-on { opacity: 1; }
.hd-disc-anchor.hd-disc-out { opacity: 0; transition: opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1); }

/* The SPILL: thinking chips flow Pinterest-style — natural widths, side
 * by side, wrapping — each condensing slowly from Althea dots. */
/* the thinking reads as a live log inside the logo's body: each step
 * arrives BELOW the last, and finished steps dim and shrink away */
.hd-spill {
    display: flex;
    flex-direction: column;
    /* CENTRED on the disc (2026-08-21): the log used to stack up from the
     * bottom of its box, which put the whole cluster well below the logo it
     * belongs to. Centred, the thinking grows symmetrically around Althea. */
    justify-content: center;
    width: 100%;
    max-width: 620px;
    height: 100%;
    max-height: 430px;
    margin: auto;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 16%, #000 100%);
    mask-image: linear-gradient(to bottom, transparent 0%, #000 16%, #000 100%);
}
/* the track does the stacking; its transform does the glide (compositor),
 * while the mask stays put on the wrapper above */
.hd-spill-track {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    /* NO will-change here: a resident composited layer under ten
     * backdrop-filter chips forces their blurs to re-render every frame.
     * The transform (and with it the layer) exists only while a glide
     * runs — the JS clears it 700ms after each entrance. */
}
/* pending steps hold no space — the stack grows as Althea works */
.hd-spill .hd-step.hd-pending { display: none; }
.hd-spill .hd-step {
    flex: none;
    animation: hd-step-in 0.75s cubic-bezier(0.22, 1, 0.36, 1) backwards;
    transition:
        opacity 0.75s cubic-bezier(0.22, 1, 0.36, 1),
        transform 0.75s cubic-bezier(0.22, 1, 0.36, 1);   /* the glide lives on the track */
}
@keyframes hd-step-in {
    from { opacity: 0; transform: scale(0.965); filter: blur(7px); }
    to { opacity: 1; transform: none; filter: none; }
}
/* the finished step recedes, making room for the live one — except the
 * last, which stays lit while the sequence pauses on it */
.hd-spill .hd-step.hd-done { opacity: 0.42; transform: scale(0.94); }
/* Receded chips drop their LIVE blur (QA 2026-08-20). Every backdrop-filter
 * pane over the animating canvas re-blurs on every canvas frame, and with
 * ten stacked chips the GPU missed vsyncs — the measured "jerky after a few
 * blocks" was raster starvation, not script (draw() profiles at 0.6ms).
 * At 42% opacity the blur behind a dim chip is imperceptible; the canvas's
 * own frost pass already mutes the field inside the card. A slightly
 * deeper tint stands in for the lost diffusion. */
.hd-spill .hd-step.hd-done {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: rgba(26, 41, 41, 0.72);
}
.hd-spill .hd-step.hd-last.hd-done { opacity: 1; transform: none; }
/* the still-lit last chip keeps the real material while the log holds */
.hd-spill .hd-step.hd-last.hd-done {
    backdrop-filter: blur(34px) saturate(1.5);
    -webkit-backdrop-filter: blur(34px) saturate(1.5);
    background: rgba(26, 41, 41, 0.5);
}
/* the COMBINE: chips sink together toward the forming answer */
.hd-thinkstage.hd-out .hd-step {
    opacity: 0;
    transform: translateY(24px) scale(0.93);
    filter: blur(5px);
    transition:
        opacity 0.4s cubic-bezier(0.4, 0, 0.6, 1),
        transform 0.4s cubic-bezier(0.4, 0, 0.6, 1);
}
.hd-step {
    display: flex;
    gap: 12px;
    align-items: baseline;
    max-width: 680px;
    padding: 16px 26px;
    border-radius: 999px;   /* the thinking reads as capsules */
    font-size: var(--text-small-size);
    color: var(--text-secondary);
    line-height: 1.5;
}
.hd-step-i {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    /* the solid set carries more visual weight than the line set it
     * replaced, so it sits a step smaller (2026-08-24) */
    width: 17px;
    height: 17px;
    align-self: flex-start;
    margin-top: 2px;
    color: var(--text-tertiary);
}
.hd-step-svg { width: 100%; height: 100%; display: block; }
/* Solar icons carry the vocabulary; citron marks the conclusive kinds */
.hd-step[data-kind='insight'] { color: var(--text-primary); }
.hd-step[data-kind='insight'] .hd-step-i,
.hd-step[data-kind='map'] .hd-step-i,
.hd-step[data-kind='verify'] .hd-step-i { color: var(--citron-300); }

/* progress: the chip works (bar sweeps), then completes (bar rests full) */
.hd-step { position: relative; overflow: hidden; }
.hd-step-t { min-width: 0; }
.hd-step-bar {
    position: absolute;
    left: 0;
    bottom: 0;
    height: 2px;
    width: 100%;
    transform: scaleX(0);
    transform-origin: left;
    background: linear-gradient(90deg, rgba(167,215,214,0.15), rgba(167,215,214,0.75));
    transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.6s ease-out;
}
.hd-step.hd-running .hd-step-bar { transform: scaleX(0.72); }
.hd-step.hd-done .hd-step-bar {
    transform: scaleX(1);
    background: linear-gradient(90deg, rgba(207,212,120,0.2), rgba(207,212,120,0.55));
    opacity: 0.5;
}
.hd-step.hd-running .hd-step-i { color: var(--teal-300, #a3c3c4); }
.hd-step.hd-done .hd-step-i { color: var(--citron-300); }
.hd-step .hd-step-i { transition: color 0.5s ease-out; }
.hd-step[data-kind='code'] span:last-child {
    font-family: var(--font-family-code-mono);
    font-size: var(--text-micro-size);
}
.hd-disc-zone { flex: 0 0 220px; position: relative; min-height: 280px; }
.hd-disc-zone .hd-converge { left: 50%; top: 50%; bottom: auto; }
.hd-thinkstage.hd-out { opacity: 0; pointer-events: none; }
.hd-thinkstage[hidden] { display: none; }

/* Source tiles: 2x2 grid across the full stage width. */
.hd-tile-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 24px 28px;
    align-content: start;
}

/* Source tiles: small LED screens (registers tile / tileCitron / code).
 * Radius matches the SDF tile radius (18). */
.hd-tile {
    min-width: 0;
    padding: 16px 20px;
    border-radius: 18px;
    min-height: 88px;
}
.hd-tile-label {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-secondary);
    margin-bottom: 6px;
}
.hd-tile-line {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-quaternary);
    line-height: 1.6;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.hd-tile-code .hd-tile-line { font-family: var(--font-family-code-mono); }

/* Where the feeds converge: the canvas draws the Althea stipple disc
 * here (r 30 + drift, rotating while thinking), so it needs clearance
 * from the stage's foot. */
.hd-converge {
    position: absolute;
    left: 50%;
    bottom: 64px;
    width: 2px;
    height: 2px;
}

.hd-answer-block { margin: 0 0 0.8em; }

/* The LED meter figure: rows of grid dots, the anomaly row in citron. */
/* ---- the stakes line: the pain, stated before the demo answers it ---- */
.hd-stakes { opacity: 0; transition: opacity 0.7s ease-in-out; }
.hd-stakes.hd-st-on { opacity: 1; }
.hd-stakes.hd-out { opacity: 0; }
@media (prefers-reduced-motion: reduce) { .hd-stakes { transition: none; } }
.hd-stakes {
    /* IN FLOW, and it STAYS (2026-08-21): the problem statement and the
     * question now stand together — the pain on top in the page's serif
     * voice, the question beneath it in the researcher's own words. They
     * leave together too. No layout shift: the bubble reserves its space
     * from mount (it is pending, not absent), so nothing moves when it
     * fades in underneath. */
    align-self: center;
    max-width: 24ch;
    margin: 0 0 20px;
    text-align: center;
    font-family: var(--heading-section-family);
    font-optical-sizing: none;
    font-variation-settings: 'opsz' 14;
    font-size: 1.75rem;
    font-weight: 200;
    line-height: 1.25;
    letter-spacing: -0.015em;
    color: var(--text-primary);
    transition:
        opacity 0.7s var(--motion-curve-house),
        transform 0.7s var(--motion-curve-house),
        filter 0.7s var(--motion-curve-house);
}
/* each sentence keeps its own line; the STATEMENT arrives as one thought:
 * fade in 0.7 (ease both ways), hold, fade out 0.7 */
.hd-sl { display: block; }
/* they exit as one gesture, with the question they framed */
.hd-stakes.hd-out { opacity: 0; transform: translateY(-14px); filter: blur(7px); }

@media (width <= 940px) { .hd-stakes { font-size: 1.375rem; } }

/* ---- THE CHAT LOG (2026-08-21): a transcript that scrolls ---- */
.hd-chatlog {
    /* the mockup speaks one step down from the page: 14px (Ivan 2026-08-25,
     * was 15px), so a product surface never reads as page copy and the
     * blocks have room to breathe between sections */
    font-size: 14px;
    position: relative;
    z-index: 1;
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
    max-width: 840px;
    margin: 0 auto;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    /* older bubbles LEAVE through this: they fade as they ride up and out */
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 22%, #000 100%);
    mask-image: linear-gradient(to bottom, transparent 0, #000 22%, #000 100%);
}
/* the finale opens the frame (2026-08-21): the deep fade exists for the
 * scrolling chat, but the ANSWER must stand fully lit — once it lands, the
 * fade pulls back to a sliver and the dimmed chips above carry the receding
 * themselves, via their own opacity */
.hd-chatlog.hd-log-open {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 6%, #000 100%);
    mask-image: linear-gradient(to bottom, transparent 0, #000 6%, #000 100%);
}
.hd-log-track {
    display: flex;
    flex-direction: column;
    gap: 14px;
    /* the composition breathes: bubbles never touch the frame's edges */
    padding: 48px 20px 36px;
}
/* the statement is the OVERTURE: its own moment, centered on the stage,
 * out of the log's flow — the chat starts only after it has left */
.hd-window > .hd-stakes {
    position: absolute;
    left: 50%;
    top: 45%;
    transform: translate(-50%, -50%);
    z-index: 2;
    pointer-events: none;
    font-size: 3rem;
    max-width: 34ch;   /* every sentence holds its single line */
    padding-bottom: 0.16em;
}
/* The headings' gradient-lit glass — applied PER WORD, not on the parent:
 * the reveal blurs each word, and a filtered child lives in its own layer
 * where a parent's background-clip cannot reach it (that was the breakage:
 * words painting solid or not at all). The gradient is near-vertical, so
 * per-word clipping is indistinguishable from whole-line clipping. */
.hd-window > .hd-stakes .hd-sl {
    background-image: linear-gradient(
        178deg,
        #ffffff 0%,
        color-mix(in srgb, var(--text-primary) 92%, transparent) 42%,
        color-mix(in srgb, var(--text-primary) 62%, transparent) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
    text-shadow: 0 1px 14px rgba(0, 0, 0, 0.35);
    padding-bottom: 0.16em;
    margin-bottom: -0.16em;
}
/* THE FLOAT (Ivan 2026-08-25): the statement is never still. It fades in
 * already rising, drifts upward at ~7px/s through its whole stay, and the
 * fade-out happens WHILE the drift continues — .hd-out must not touch
 * transform, or the glide would snap to a new position mid-fade. The
 * distance is small on purpose: dynamic, not restless. */
.hd-window > .hd-stakes.hd-st-on { animation: hd-st-float 4.4s linear forwards; }
@keyframes hd-st-float {
    from { transform: translate(-50%, calc(-50% + 13px)); }
    to { transform: translate(-50%, calc(-50% - 17px)); }
}
.hd-window > .hd-stakes.hd-out { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
    .hd-window > .hd-stakes.hd-st-on { animation: none; }
}
/* the question sits bottom-RIGHT, like a chat you have actually had */
.hd-chatlog .hd-bubble-user { align-self: flex-end; margin-right: 4px; }
/* the thinking stacks bottom-LEFT, items left-aligned */
.hd-think2 {
    align-self: flex-start;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
    max-width: 640px;
    transition: opacity 0.7s var(--motion-curve-house);
}
.hd-think2 .hd-step.hd-pending { display: none; }
.hd-think2.hd-gone { opacity: 0; }
/* BARE lines (2026-08-21): the thinking is a log, not a stack of pills —
 * no glass, no rim, no frost; just the icon, the line, and its underbar */
.hd-think2 .hd-step {
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: none;
    border-radius: 0;
    padding: 12px 2px;
    animation: hd-step-in 1s cubic-bezier(0.22, 1, 0.36, 1) backwards;
}
.hd-think2 .hd-step::after { display: none; }
/* the thinking stays WHITE and fully lit while it moves; only the top
 * fade takes it as it leaves the frame */
.hd-think2 .hd-step, .hd-think2 .hd-step.hd-done { opacity: 1; }
.hd-think2 .hd-step .hd-step-t { color: var(--text-primary); }
/* the answer lands UNDER the thinking, on the assistant's side:
 * left-aligned like every assistant turn, a touch narrower than the log */
.hd-chatlog .hd-response { align-self: flex-start; width: min(100%, 748px); }
.hd-chatlog .hd-response.hd-pending {
    transform: translateY(20px) scale(0.99);
    filter: blur(9px);
    opacity: 0;
}
.hd-chatlog .hd-response {
    transition:
        opacity 0.85s cubic-bezier(0.22, 1, 0.36, 1),
        transform 0.85s cubic-bezier(0.22, 1, 0.36, 1),
        filter 0.85s cubic-bezier(0.22, 1, 0.36, 1);
}
/* the chart fills the answer's inner width exactly: its viewBox is drawn
 * at those proportions, so nothing upscales and no flank sits empty */
.hd-chatlog .hd-fig { margin: 20px 0 0; }   /* ...and further from the instrument below, so the light row reads as balanced (4px steps here, Ivan) */
/* the blocks carry a 0.8em bottom margin of their own; inside this flex
 * column that margin ADDS to the gap, which is why the paragraphs read far
 * apart no matter what the gap said. The gap alone governs here. */
.hd-chatlog .hd-ans-text { display: flex; flex-direction: column; gap: 8px; }
.hd-chatlog .hd-ans-text .hd-answer-block { margin: 0; }
/* THE ANSWER'S OWN RICHNESS (2026-08-24) — deliberately NOT the map's.
 * A thesis line up top, then prose in which each citation chip rides the
 * claim it supports; the un-cited third paragraph is the gap, structurally.
 * (A first pass borrowed the map's pills and meta row here, and the two
 * artifacts converged — tab 2's registers belong to tab 2.) */
/* one padding, all four sides */
.hd-chatlog .hd-response { padding: 24px 32px; }
/* a step answered recedes but stays legible until it leaves */
.hd-chatlog .hd-dim { opacity: 0.45; }
.hd-chatlog .hd-bubble-user.hd-out {
    opacity: 0;
    transform: translateY(-14px);
    filter: blur(6px);
}
/* the ABSORB: the bubble gives its mass to the dots leaving it */
/* THE ABSORB: the bubble dissolves in place while its mass leaves as canvas
 * dots flying into the logo. The particles carry the drama, so the box only
 * has to get out of the way — EVENLY. It used to leave on the house ease-out
 * (0.22, 1, 0.36, 1), which is so front-loaded that opacity fell 0.087 in a
 * single 60fps frame and then crawled: a snap, not a fade. A soft symmetric
 * S falls at a near-constant rate through the middle instead, and the drift
 * upward gets its own gentler curve so the box does not lurch. */
/* The question's exit is an ANIMATION, not a transition (2026-08-25). Every
 * panel parks the question off the track before it goes, and that DOM move
 * silently cancels a transition — which is why an attempt to give all four
 * the same travel snapped instead of moving. An animation survives the move,
 * because it starts when the class lands, after it. The 68px rise is demo 1's
 * own travel, measured; the other panels used to get 11px of it or, in demo
 * 2's case, 71px in the wrong direction. */
@keyframes hd-q-exit {
    from { opacity: 1; transform: none; }
    to   { opacity: 0; transform: translateY(-128px) scale(0.96); }
}
.hd-chatlog .hd-bubble-user.hd-absorb {
    /* opacity and transform only: the exit used to animate filter: blur(9px)
     * too, and an animated blur re-renders the layer every frame. Measured
     * p95 24.6ms with dropped frames while it ran; these two properties the
     * compositor can carry on its own. */
    animation: hd-q-exit 0.9s cubic-bezier(0.3, 0.35, 0.4, 1) forwards;
    transition: none;
    will-change: transform, opacity;
}
@media (prefers-reduced-motion: reduce) {
    .hd-chatlog .hd-bubble-user.hd-absorb { animation: none; opacity: 0; }
}
/* the progress wash leaves with it, rather than riding a dissolving box */
.hd-chatlog .hd-bubble-user.hd-absorb .hd-qbar { opacity: 0; transition: opacity 0.25s ease-out; }

/* ---- the answer in two columns: the finding, and its evidence ---- */
.hd-response.hd-2col {
    display: grid;
    grid-template-columns: minmax(0, 1.12fr) minmax(0, 0.88fr);
    column-gap: 40px;
    align-items: center;
}
.hd-response.hd-2col .hd-verdict-lead { grid-column: 1 / -1; grid-row: 1; }
.hd-response.hd-2col .hd-ans-text { grid-column: 1; grid-row: 2; display: flex; flex-direction: column; gap: 16px; }
.hd-response.hd-2col .hd-fig { grid-column: 2; grid-row: 2; margin: 0; }
@media (width <= 940px) {
    .hd-response.hd-2col { grid-template-columns: 1fr; row-gap: 18px; }
    .hd-response.hd-2col .hd-fig { grid-column: 1; grid-row: auto; }
}

/* ---- the LED meter: the loss as a length of light on the page's lattice ----
 * HTML spans (2026-08-24): the SVG's viewBox scaled its type with its
 * width — fine full-width, illegible in the answer's figure column. */
/* the figure's heading is a PILL — the same register chip the map uses —
 * so it divides the prose from the instrument instead of floating over it
 * as a caption (Ivan 2026-08-25) */
/* The figure's caption (2026-08-25). It sits UNDER the meter and says what
 * the two lengths mean. Icon + text, the same construction as the map's fact
 * row in tab 2 (Ivan), not a pill: the chips above are capsules for
 * REFERENCES and this is a reading, so it should not wear their shape.
 * Citron because the finding is the citron register throughout. */
.hd-fig .hd-fig-note {
    align-items: flex-start;
    gap: 8px;
    margin: 16px 0 0;
    line-height: 1.5;
    color: var(--text-primary);   /* the reading is white; the glyph keeps the citron */
    text-wrap: pretty;
}
.hd-fig .hd-fig-note .hd-step-svg {
    color: var(--citron-300);
    margin-top: 2px;   /* optical: align to the cap height, not the line box */
}
.hd-mrow { display: flex; flex-direction: column; gap: 8px; }
.hd-mrow + .hd-mrow { margin-top: 8px; }   /* the two series read as one instrument */
/* name and cost share the line above the track: the stage carries a second
 * bubble now, and the stacked three-line row spent height for nothing */
.hd-mrow-head { margin: 0; display: flex; justify-content: space-between; align-items: baseline; gap: 16px; }
.hd-mrow-name {
    font-family: var(--font-family-label-mono);
    font-size: 13px;
    color: var(--text-primary);
}
.hd-mrow-note {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: #a7d7d6;
}
.hd-mrow.hd-hot .hd-mrow-note { color: var(--citron-300); }
.hd-mtrack { display: flex; align-items: center; min-height: 14px; }
/* every dot owns a 10px slot — the page lattice's pitch — and is drawn the
 * way the field draws its dots: a radial gradient, not a box. The old
 * space-between track scattered 26 dots across the measure and read as a
 * dotted rule, not a length of light. */
.hd-led, .hd-led-off { width: 10px; height: 10px; flex: none; border-radius: 0; }
.hd-led-off { background: radial-gradient(circle, rgba(255, 255, 255, 0.14) 1.2px, transparent 1.6px); }
.hd-led {
    background: radial-gradient(circle, rgba(167, 215, 214, 0.9) 2.1px, transparent 2.5px);
    opacity: 0;
    /* the row CHARGES: each dot lights in turn, left to right */
    animation: hd-led-on 0.5s cubic-bezier(0.22, 1, 0.36, 1) forwards;
    animation-delay: calc(var(--row) * 420ms + var(--k) * 13ms + 300ms);
}
.hd-mrow.hd-hot .hd-led { background: radial-gradient(circle, rgba(207, 212, 120, 0.95) 2.1px, transparent 2.5px); }
@keyframes hd-led-on { from { opacity: 0; transform: scale(0.4); } to { opacity: 1; transform: none; } }
/* the row's leading dot carries the charge glow */
.hd-led.hd-lead { background: radial-gradient(circle, rgba(167, 215, 214, 1) 2.8px, transparent 3.3px); filter: drop-shadow(0 0 6px rgba(167, 215, 214, 0.85)); }
.hd-mrow.hd-hot .hd-led.hd-lead { background: radial-gradient(circle, rgba(207, 212, 120, 1) 2.8px, transparent 3.3px); filter: drop-shadow(0 0 7px rgba(207, 212, 120, 0.9)); }
@media (prefers-reduced-motion: reduce) {
    .hd-led { animation: none; opacity: 1; transform: none; }
}

/* ---- the terminal: prompts, typed commands, a caret that follows ---- */
.hd-code-line {
    display: flex;
    gap: 8px;
    align-items: baseline;
    padding: 3px 0;
    /* a run reads as a run because it is SET like one: the mono face is
     * inherited from .hd-runlog, but stated here too so a line is never
     * rendered in the brand sans if it lands outside that column */
    font-family: var(--font-family-code-mono);
    line-height: 1.5;
}
/* A waiting line holds its full box and shows nothing: the run reserves its
 * height on the first frame, so no line landing can move the ones above it.
 * (They used to enter the flow one at a time, which stepped the whole log
 * and re-centred it mid-glide — the stutter Ivan saw.) */
.hd-cl-wait { visibility: hidden; }
.hd-code-line { transition: opacity 0.32s ease; }
.hd-cl-p { color: #a7d7d6; opacity: 0.8; flex: none; }
.hd-cl-p.hd-cl-ok { color: var(--citron-300); opacity: 1; }
.hd-cl-cmd .hd-cl-t { color: var(--text-primary); }
.hd-cl-out .hd-cl-t { color: var(--text-primary); }   /* the run reads white, like every other line of work */
.hd-cl-ok .hd-cl-t { color: var(--citron-300); }
.hd-cl-out { animation: hd-out-land 0.32s ease-out backwards; }
@keyframes hd-out-land { from { opacity: 0; transform: translateY(-3px); } }
.hd-caret {
    display: inline-block;
    width: 7px;
    height: 1.05em;
    margin-left: 1px;
    transform: translateY(2px);
    background: #a7d7d6;
    opacity: 0.85;
    animation: hd-caret-blink 1.1s steps(1, end) infinite;
}
@keyframes hd-caret-blink { 0%, 55% { opacity: 0.85; } 56%, 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
    .hd-caret { animation: none; }
    .hd-cl-out { animation: none; }
}

/* ---- Run the claim: the fused verdict, claimed beside observed ---- */
.hd-compare { display: flex; align-items: flex-end; gap: 40px; flex-wrap: wrap; }
.hd-cmp-sub {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-primary);
}
/* the reading of the two figures, in the citron the finding always wears */
/* The claimed/observed scale: one axis, two spreads, drawn with the meter's
 * dots so it reads as the same instrument. The overlap is the finding. */
.hd-vscale { display: flex; flex-direction: column; gap: 8px; margin: 24px 0 0; }   /* +0.5rem above the scale */
.hd-vsrow { display: flex; align-items: center; gap: 16px; }
.hd-vsk {
    flex: none;
    width: 72px;
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-primary);
}
.hd-vstrack { display: flex; align-items: center; }
.hd-vsled, .hd-vsoff { width: 8px; height: 10px; flex: none; }
.hd-vsoff { background: radial-gradient(circle, rgba(255, 255, 255, 0.14) 1.1px, transparent 1.5px); }
.hd-vsled { background: radial-gradient(circle, rgba(167, 215, 214, 0.9) 1.9px, transparent 2.3px); }
.hd-vsmid { background: radial-gradient(circle, rgba(167, 215, 214, 1) 2.6px, transparent 3px); filter: drop-shadow(0 0 6px rgba(167, 215, 214, 0.8)); }
.hd-vsrow.hd-hot .hd-vsk { color: var(--citron-300); }
.hd-vsrow.hd-hot .hd-vsled { background: radial-gradient(circle, rgba(207, 212, 120, 0.95) 1.9px, transparent 2.3px); }
.hd-vsrow.hd-hot .hd-vsmid { background: radial-gradient(circle, rgba(207, 212, 120, 1) 2.6px, transparent 3px); filter: drop-shadow(0 0 7px rgba(207, 212, 120, 0.9)); }
.hd-cmp { display: flex; flex-direction: column; gap: 8px; }
.hd-cmp-k {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-primary);   /* nothing in this bubble reads grey */
}
.hd-cmp-v {
    font-family: var(--font-family-label-mono);
    font-size: 1.5rem;
    line-height: 1.2;
    color: var(--text-primary);
}
.hd-cmp.hd-hot .hd-cmp-v { color: var(--citron-300); text-shadow: 0 0 20px rgba(207, 212, 120, 0.35); }
/* An editorial arrow instead of the word "vs" (Ivan 2026-08-25): the shaft
 * fades IN toward the figure that was actually run, so the mark reads as a
 * direction rather than a label. */
.hd-cmp-arrow {
    position: relative;
    flex: none;
    align-self: flex-end;
    width: 56px;
    height: 16px;
    margin-bottom: 16px;
}
.hd-cmp-arrow::before {
    content: '';
    position: absolute;
    left: 0; right: 6px; top: 50%;
    height: 1px;
    background: linear-gradient(90deg, rgba(207, 212, 120, 0), rgba(207, 212, 120, 0.85));
}
.hd-cmp-arrow::after {
    content: '';
    position: absolute;
    right: 0; top: 50%;
    width: 6px; height: 6px;
    border-top: 1px solid rgba(207, 212, 120, 0.85);
    border-right: 1px solid rgba(207, 212, 120, 0.85);
    transform: translateY(-50%) rotate(45deg);
}

/* ---- Stay ahead: the standing order, and what it delivers ---- */
.hd-drow-b { display: flex; flex-direction: column; gap: 2px; }
/* the WHY is the product: it says what this finding does to THIS person's
 * work, so it gets the citron and a lit tick rather than reading as a
 * footnote under the headline it follows. */
.hd-why {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    line-height: 1.5;
    color: var(--citron-300);
    text-wrap: pretty;
}
/* the order is LIVE: a quiet pulse says it is running while you are away */
.hd-live-dot {
    display: inline-block;
    width: 6px; height: 6px;
    margin-right: 8px;
    border-radius: 999px;
    background: #a7d7d6;
    animation: hd-live 2.4s ease-in-out infinite;
}
@keyframes hd-live { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .hd-live-dot { animation: none; } }
.hd-orow {
    display: grid;
    grid-template-columns: 84px 1fr;
    gap: 12px;
    padding: 7px 0;
    align-items: baseline;
    font-size: var(--text-small-size);
}
.hd-orow + .hd-orow { box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.055); }
.hd-orow-k {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-tertiary);
}
.hd-orow-v { color: var(--text-primary); }
.hd-o-core .hd-orow-v { color: var(--citron-300); }
.hd-o-adj .hd-orow-v { color: #a7d7d6; }
.hd-o-src .hd-orow-v { color: var(--text-secondary); font-size: var(--text-mini-size); }
/* the brief: a finding first, then the three things behind it */
.hd-brief-lead {
    margin: 0 0 24px;
    font-size: var(--text-small-size);
    line-height: 1.55;
    color: var(--text-primary);
    text-wrap: pretty;
}
.hd-brow { display: flex; flex-direction: column; gap: 8px; padding: 0; }
.hd-brow-t { font-size: var(--text-small-size); line-height: 1.45; color: var(--text-primary); }   /* no grey, as elsewhere */
.hd-brow.hd-hot .hd-brow-t { color: var(--text-primary); }

/* ---- Reach a peer ---- (the candidate LIST that used to live here went
 * with the boxed-node thinking state; the routing block below owns it now) */
.hd-peer-close {
    margin: 4px 0 0;
    text-align: center;
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--citron-300);
}

/* ============ THE FOUR INSTRUMENTS (2026-08-24) ============ */

/* HIDDEN MEANS GONE (2026-08-24). The `hidden` attribute is only styled by
 * the UA sheet, so ANY author rule that sets `display` beats it — and the
 * instruments below all set display:flex. A not-yet-arrived note or network
 * therefore kept its full height in the log, pushing everything above it up
 * and off centre (measured: an 86px bias on Map the field). One rule closes
 * the whole class of bug, on every panel. */
.hd-chatlog [hidden] { display: none !important; }

/* anything mid-dissolve leaves softly, whatever it is */
.hd-chatlog .hd-absorb {
    opacity: 0;
    transform: translateY(-16px) scale(0.96);
    filter: blur(10px);
    transition:
        opacity 0.8s cubic-bezier(0.22, 1, 0.36, 1),
        transform 0.8s cubic-bezier(0.22, 1, 0.36, 1),
        filter 0.8s cubic-bezier(0.22, 1, 0.36, 1);
}
.hd-chatlog .hd-gone { opacity: 0; transition: opacity 0.7s var(--motion-curve-house); }

/* --- the map BUILDING itself (Map the field) --- */
.hd-mapfield { position: relative; width: 100%; height: 340px; }
/* Althea holds the centre; only its dots travel */
.hd-disc-mid { top: 50%; left: 50%; }
.hd-medges { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.hd-medge {
    stroke: rgba(167, 215, 214, 0.22);
    stroke-width: 0.25;
    vector-effect: non-scaling-stroke;
    stroke-dasharray: 120;
    stroke-dashoffset: 120;
    transition: stroke-dashoffset 1.1s cubic-bezier(0.22, 1, 0.36, 1);
    transition-delay: calc(var(--ei) * 90ms);
}
.hd-medge.hd-e-on { stroke-dashoffset: 0; }
/* each paper: strewn at first, then it finds its cluster */
/* a paper lights where its dot from the logo lands — it never travels in
 * the DOM; the canvas particle does the travelling */
.hd-pdot {
    position: absolute;
    width: 4px; height: 4px;
    margin: -2px 0 0 -2px;
    border-radius: 999px;
    background: rgba(167, 215, 214, 0.75);
    opacity: 0;
    transform: translate(var(--jx), var(--jy)) scale(0.2);
    transition:
        opacity 0.3s ease-out,
        transform 0.42s cubic-bezier(0.34, 1.4, 0.5, 1),
        background 0.7s ease;
}
.hd-pdot.hd-p-on { opacity: 1; }
.hd-pdot.hd-p-home { transform: translate(var(--jx), var(--jy)) scale(1); }
/* the hub is only a target for the particle engine */
.hd-chub { position: absolute; width: 1px; height: 1px; opacity: 0; }
.hd-pdot.hd-c-lit.hd-p-settled { background: rgba(167, 215, 214, 0.85); }
.hd-pdot.hd-c-lit.hd-p-contested { background: rgba(255, 255, 255, 0.85); }
.hd-pdot.hd-c-lit.hd-p-open { background: var(--citron-300); }
/* the running tally while the literature arrives */
.hd-mcount {
    position: absolute;
    left: 50%; bottom: -6px;
    transform: translateX(-50%);
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-primary);
    transition: opacity 0.6s ease;
}
.hd-mcount.hd-count-out { opacity: 0; }
/* the cluster names, resolving once their papers land */
.hd-cnode {
    position: absolute;
    display: flex; flex-direction: column; align-items: center; gap: 5px;
    transform: translate(-50%, 22px);   /* clear of the cluster it names */
    text-align: center;
    transition: opacity 0.8s var(--motion-curve-house), filter 0.8s var(--motion-curve-house);
}
.hd-cnode.hd-pending { opacity: 0; filter: blur(5px); }
.hd-cnode b {
    font-family: var(--font-family-label-mono);
    font-size: 11.5px;
    font-weight: 400;
    letter-spacing: 0.01em;
    line-height: 1.3;
    color: var(--text-primary);
    white-space: nowrap;
}
/* the note is a flex ITEM of .hd-cnode, so inline-flex computes to flex and
 * it stretched full width with its content pinned left — the name above was
 * centred by text-align and the two read as ragged. Centred explicitly. */
.hd-cnode em {
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    font-family: var(--font-family-label-mono);
    font-size: 10.5px;
    font-style: normal;
    line-height: 1.3;
    color: var(--text-primary);
    opacity: 0.78;
    white-space: nowrap;
    transition: color 0.7s ease, opacity 0.4s ease;
}
/* exploring, then what was found there */
.hd-cnode .hd-cn { display: none; }
.hd-cnode.hd-c-done .hd-cx { display: none; }
.hd-cnode.hd-c-done .hd-cn { display: inline-flex; }
.hd-cspin {
    width: 7px; height: 7px;
    border-radius: 999px;
    border: 1.5px solid rgba(167, 215, 214, 0.35);
    border-top-color: #a7d7d6;
    animation: hd-spin 0.9s linear infinite;
}
@keyframes hd-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .hd-cspin { animation: none; } }
/* the DEAD END: explored, then dropped. Muted, never alarming — this is a
 * search that came up empty, not an error. */
.hd-cnode.hd-c-dead.hd-c-lit b { color: var(--text-primary); opacity: 0.5; }
.hd-cnode.hd-c-dead.hd-c-lit .hd-cn { color: rgba(214, 118, 118, 0.92); opacity: 1; }
.hd-cnode.hd-c-dead.hd-c-lit .hd-cn::before {
    content: '';
    width: 5px; height: 5px;
    border-radius: 999px;
    background: rgba(214, 118, 118, 0.9);
    flex: none;
}
.hd-pdot.hd-c-lit.hd-p-dead { background: rgba(214, 118, 118, 0.4); }
.hd-c-dead.hd-c-lit .hd-cspin { border-top-color: rgba(214, 118, 118, 0.9); }

/* the FIND: the open direction is the one worth having */
.hd-cnode.hd-c-found b { color: var(--citron-300); }
.hd-cnode.hd-c-found .hd-cn { color: var(--citron-300); opacity: 1; }
.hd-cnode.hd-c-found {
    animation: hd-found 1.6s cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes hd-found {
    0% { filter: drop-shadow(0 0 0 rgba(207, 212, 120, 0)); }
    35% { filter: drop-shadow(0 0 14px rgba(207, 212, 120, 0.75)); }
    100% { filter: drop-shadow(0 0 5px rgba(207, 212, 120, 0.3)); }
}
.hd-c-settled.hd-c-lit em { color: #a7d7d6; }
.hd-c-open.hd-c-lit em { color: var(--citron-300); }
.hd-c-open.hd-c-lit b { color: var(--citron-300); }
.hd-c-contested.hd-c-lit em { color: var(--text-primary); }

/* --- the watch (Stay ahead): points that breathe while the week passes --- */
.hd-watch { position: relative; width: 100%; height: 280px; }
.hd-wpoint {
    position: absolute;
    left: var(--x); top: var(--y);
    display: flex; align-items: center; gap: 8px;
    transition: opacity 0.9s var(--motion-curve-house), filter 0.9s var(--motion-curve-house);
}
.hd-wpoint.hd-pending { opacity: 0; filter: blur(6px); }
.hd-wdot {
    width: 8px; height: 8px; border-radius: 999px;
    background: #a7d7d6;
    animation: hd-live 2.6s ease-in-out infinite;
}
.hd-wpoint b {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    font-weight: 400;
    color: var(--text-primary);
}
.hd-wpoint.hd-w-flare .hd-wdot {
    background: var(--citron-300);
    animation: none;
    box-shadow: 0 0 14px rgba(207, 212, 120, 0.9);
}
.hd-wpoint.hd-w-flare b { color: var(--citron-300); }
.hd-days {
    position: absolute;
    left: 50%; bottom: 8px;
    transform: translateX(-50%);
    display: flex; gap: 22px;
}
.hd-day {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-quaternary);
    transition: color 0.5s ease;
}
.hd-day.hd-day-on { color: var(--text-primary); }

/* --- routing (Reach a peer): your coordinator asks other coordinators ---
 * The canvas draws every disc: the mark at .hd-disc-anchor and a small one
 * of the same stipple centred on each .hd-peer-orb. Nothing here paints —
 * this file only says WHERE, so the composition can be re-laid for a
 * narrow screen and the drawing follows without touching the script. */
/* 300px, not taller: the log fades its top 22%, and a peer's LABEL sitting
 * up in that band reads as half-erased (the orbs are canvas, so they are
 * never masked, but the text is DOM and is) */
.hd-route { position: relative; width: 100%; height: 310px; }
/* the coordinator sits left of centre: the question travels rightward to
 * the three, and the composition still balances across the stage. It rides
 * a little above the middle so its lower rim clears the line beneath —
 * the mark's dots drift about 6px, so "just touching" is an overlap. */
.hd-route .hd-disc-anchor { left: 25%; top: 44%; }
/* all three the same distance out — a shallow fan rather than a ring,
 * because the stage is wide and short and a ring cannot fit one */
.hd-peer[data-p='0'] { --x: 57%; --y: 14%; }
.hd-peer[data-p='1'] { --x: 59%; --y: 50%; }
.hd-peer[data-p='2'] { --x: 57%; --y: 86%; }
.hd-peer {
    position: absolute;
    left: var(--x); top: var(--y);
    transform: translateY(-50%);
    display: flex; align-items: center; gap: 14px;
    width: calc(100% - var(--x) - 8px);
    transition: opacity 0.9s var(--motion-curve-house), filter 0.9s var(--motion-curve-house);
}
.hd-peer-orb { flex: 0 0 var(--orb, 96px); height: var(--orb, 96px); }
.hd-peer-t { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
/* ONE size for every word in this stage — 13px (Ivan 2026-08-25). The
 * labels were 12px against a 14px line under the mark, which is a
 * difference too small to read as hierarchy and too large to read as one
 * voice. Colour does the ranking here; the type does not. */
.hd-peer-w, .hd-peer-y {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-mini-size);
    line-height: 1.45;
    transition: color 0.8s var(--motion-curve-house);
}
/* what they work on, then why them — the second line takes the field's own
 * teal, and turns citron on the one who answers */
.hd-peer-w { color: var(--text-primary); }
.hd-peer-y { color: #a7d7d6; }
.hd-peer.hd-p-yes .hd-peer-y { color: var(--citron-300); }
.hd-peer.hd-p-quiet { opacity: 0.3; }              /* passing owes no explanation */
.hd-peer.hd-p-yes .hd-peer-w { color: var(--text-primary); }
/* last, so it beats hd-p-quiet at equal specificity: the two who passed
 * have to leave with the one who answered, not linger at a third */
.hd-peer.hd-pending { opacity: 0; filter: blur(6px); }
/* the recession runs slower than the arrival, and the mark outlasts the
 * three — the thinking state should ebb while the answer opens, not cut */
.hd-route .hd-peer.hd-out { transition-duration: 1.4s; }
.hd-route .hd-disc-anchor.hd-disc-out { transition: opacity 1.4s var(--motion-curve-house); }
.hd-route-say.hd-out > * { transition-duration: 1.1s; }

/* one slot under the coordinator, carrying the phase's line: the limit,
 * then the request that goes out, then the consent that comes back. They
 * are grid-stacked so the tallest reserves the space and nothing shifts
 * when one replaces another. */
.hd-route-say {
    position: absolute;
    top: 92%;   /* clear of the mark's lower rim, not under it */
    /* CAPPED, and hung from the mark's own centre line rather than from the
     * left of the box: at 56% of a wide stage the measure ran most of the
     * way to the peers and stopped reading as the coordinator's own line
     * (Ivan 2026-08-25). --x tracks the anchor, so the phone layout moves
     * both together. */
    left: var(--x, 25%);
    width: min(50%, 340px);
    transform: translateX(-50%);
    display: grid;
    text-align: center;
    text-wrap: balance;   /* the limit line otherwise drops one word alone */
}
.hd-route-say > * {
    grid-area: 1 / 1;
    margin: 0;
    align-self: start;
    /* opacity and transform ONLY: this slot swaps its line three times over
     * a live canvas, and an animated blur re-renders the layer every frame */
    transition: opacity 0.42s var(--motion-curve-house), transform 0.42s var(--motion-curve-house);
}
.hd-route-say > .hd-pending { opacity: 0; transform: translateY(6px); }
/* ONE register for all three (Ivan 2026-08-25): the slot used to mix a
 * 14px sentence, a mono meta line and a citron mono note, so the panel
 * read as three different voices. Same size, same face, every phase —
 * colour is the only thing that changes, and it means what it means
 * everywhere else here: citron is consent. */
.hd-say { font-size: var(--text-mini-size); line-height: 1.55; color: var(--text-primary); }
.hd-say-yes { color: var(--citron-300); }
.hd-chatlog .hd-peer-close { text-align: left; margin: 10px 0 0; }

.hd-chatlog .hd-verdict, .hd-chatlog .hd-reply, .hd-chatlog .hd-map, .hd-chatlog .hd-brief { width: 100%; }
.hd-chatlog .hd-trial-claim.hd-pending,
.hd-chatlog .hd-verdict.hd-pending, .hd-chatlog .hd-reply.hd-pending,
.hd-chatlog .hd-map.hd-pending, .hd-chatlog .hd-brief.hd-pending {
    opacity: 0;
    transform: translateY(18px) scale(0.99);
    filter: blur(8px);
}
.hd-chatlog .hd-trial-claim, .hd-chatlog .hd-verdict,
.hd-chatlog .hd-reply, .hd-chatlog .hd-map, .hd-chatlog .hd-brief {
    transition:
        opacity 0.85s cubic-bezier(0.22, 1, 0.36, 1),
        transform 0.85s cubic-bezier(0.22, 1, 0.36, 1),
        filter 0.85s cubic-bezier(0.22, 1, 0.36, 1);
}

/* ---- Map the field: ONE bubble, nothing boxed inside it ---- */
.hd-chatlog .hd-map { padding: 30px; }
.hd-map-title { margin: 0 0 8px; font-size: var(--text-regular-size); color: var(--text-primary); text-wrap: pretty; }
.hd-map-meta {
    display: flex; flex-wrap: wrap; gap: 8px 24px;
    margin: 0 0 24px;
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-primary);
}
.hd-map-meta.hd-meta-snug { margin-bottom: 16px; }   /* the answer stage is tighter than the map's */
.hd-mfact { display: inline-flex; align-items: center; gap: 8px; }
.hd-mfact .hd-step-svg { width: 14px; height: 14px; flex: none; color: #a7d7d6; }
.hd-chatlog .hd-mcol {
    /* no surface, no rule: colour and type carry the register */
    background: none;
    box-shadow: none;
    border-radius: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    transition:
        opacity 0.8s cubic-bezier(0.22, 1, 0.36, 1),
        transform 0.8s cubic-bezier(0.22, 1, 0.36, 1),
        filter 0.8s cubic-bezier(0.22, 1, 0.36, 1);
}
.hd-chatlog .hd-mcol.hd-pending { opacity: 0; transform: translateY(12px); filter: blur(6px); }
.hd-map-cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 34px; margin: 0; }
.hd-mcol {
    display: flex; flex-direction: column; gap: 10px;
    padding: 12px 14px 14px;
    border-radius: 12px;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06);
    transition: opacity 0.5s var(--motion-curve-house), transform 0.5s var(--motion-curve-house), filter 0.5s var(--motion-curve-house), box-shadow 0.5s ease;
}
/* the register is a PILL at the top of its column */
.hd-mpill {
    position: relative;
    display: inline-flex; align-items: center; gap: 7px;
    align-self: flex-start;
    margin: 0 0 4px;
    padding: 2px 11px;
    line-height: 1.6;
    border-radius: 999px;
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    letter-spacing: 0.02em;
    color: var(--text-primary);
    /* glass, like the chips: a flat tint, a small blur, and the rim doing
     * the lifting (the inset outline it used to carry is now the ::after) */
    background: rgba(255, 255, 255, 0.06);
    backdrop-filter: blur(6px) saturate(1.3);
    -webkit-backdrop-filter: blur(6px) saturate(1.3);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
.hd-mcol-settled .hd-mpill { background: rgba(167, 215, 214, 0.14); }
.hd-mcol-settled .hd-mdot { background: #a7d7d6; }
.hd-mcol-contested .hd-mdot { background: var(--text-primary); }
.hd-mcol-open .hd-mpill { background: rgba(207, 212, 120, 0.15); }
.hd-mcol-open .hd-mdot { background: var(--citron-300); }
/* the items: room to breathe, and every word white */
.hd-mcol .hd-mitem { display: flex; flex-direction: column; gap: 6px; margin: 0; }
.hd-chatlog .hd-mcol { gap: 20px; }   /* the sections breathe */
/* the pill's own space matches that rhythm exactly — no extra margin */
.hd-chatlog .hd-mpill { margin: 0; }
.hd-mdot { width: 5px; height: 5px; border-radius: 999px; background: currentColor; opacity: 0.9; flex: none; }
.hd-mcol-settled { box-shadow: inset 0 0 0 1px rgba(167, 215, 214, 0.2); }
.hd-mcol-settled .hd-mcol-h { color: #a7d7d6; }
.hd-mcol-contested { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1); }
.hd-mcol-open { box-shadow: inset 0 0 0 1px rgba(207, 212, 120, 0.28); }
.hd-mcol-open .hd-mcol-h { color: var(--citron-300); }
.hd-mitem { display: flex; flex-direction: column; gap: 3px; }
.hd-mitem-t {
    font-size: var(--text-mini-size);
    line-height: 1.5;
    color: var(--text-primary);
    text-wrap: pretty;   /* never a single word left alone on the last line */
}
/* the meta carries an icon and stays white, like everything else here */
.hd-mitem-n {
    display: inline-flex; align-items: center; gap: 7px;
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-primary);
}
.hd-mitem-n .hd-step-svg { width: 15px; height: 15px; flex: none; opacity: 0.9; }
.hd-svg-solid { stroke: none; }
.hd-mcol-settled .hd-mitem-n .hd-step-svg { color: #a7d7d6; opacity: 1; }
.hd-mcol-open .hd-mitem-n .hd-step-svg { color: var(--citron-300); opacity: 1; }
.hd-mcol-contested .hd-mitem-n .hd-step-svg { color: var(--text-primary); }
/* the disagreement: two papers set against each other. They used to pulse
 * alternately (the argument "made visible"), but a glow that never stops
 * pulls the eye away from the map it sits inside — the `vs` between them
 * already says it. They rest as plain chips (2026-08-24). */
.hd-vs { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 4px 0 0; }
.hd-vs-x { font-family: var(--font-family-label-mono); font-size: var(--text-micro-size); color: var(--text-primary); opacity: 0.55; }
.hd-vs .hd-chip { transition: opacity 0.6s ease; }
/* the commons note: its own message, arriving after the map */
.hd-chatlog .hd-note {
    align-self: flex-start;
    width: auto;
    max-width: 100%;
    text-wrap: pretty;
    padding: 15px 26px;
    font-size: var(--text-small-size);
    line-height: 1.5;
    color: var(--text-primary);
}
.hd-chatlog .hd-note.hd-pending { opacity: 0; transform: translateY(14px); filter: blur(7px); }
/* the commons note carries the site's primary button, not a hyperlink */
.hd-chatlog .hd-note { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
/* the close CTA is the design system button (button button-primary button-small),
 * exactly the nav's Get Started — the hand-rolled 36px copy that lived here
 * had its own height, padding and text colour (Ivan 2026-08-25). */
@media (width <= 940px) { .hd-map-cols { grid-template-columns: 1fr; } }

.hd-fig { margin: 0.4rem 0 1rem; }
.hd-fig figcaption {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-primary);   /* white inside the citron pill, as the map's pills read */
}
.hd-fig-row { display: flex; align-items: center; gap: 16px; line-height: 2; }
.hd-fig-label {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-secondary);
    width: 4em;
    flex: none;
}
.hd-meter {
    height: 8px;
    background-image: radial-gradient(circle, rgba(167, 215, 214, 0.9) 1.6px, transparent 1.9px);
    background-size: 8px 8px;
    background-position: left center;
    background-repeat: repeat-x;
}
.hd-fig-row.hd-hot .hd-meter {
    background-image: radial-gradient(circle, rgba(207, 212, 120, 0.95) 1.6px, transparent 1.9px);
}
.hd-fig-val {
    font-family: var(--font-family-code-mono);
    font-size: var(--text-micro-size);
    color: var(--text-secondary);
}
.hd-fig-row.hd-hot .hd-fig-val { color: var(--citron-300); }

.hd-chip-row { display: flex; flex-wrap: wrap; gap: 8px; }
[data-hd="answer"] .hd-chip-row { margin-top: 12px; }   /* the pills sit closer to the prose they cite... */

/* GLASS PILLS (Ivan 2026-08-24). This block used to read "never glass on
 * glass — pills inside a glass card stay flat outlines". Reversed: the
 * pills join the material. The blur stays SMALL on purpose — a nested
 * backdrop-filter samples what the card has already composited rather than
 * the page, so a large radius only muddies it, and the directional rim is
 * what actually reads as glass in this system. */
.hd-chip {
    position: relative;
    background: rgba(255, 255, 255, 0.055);
    backdrop-filter: blur(6px) saturate(1.3);
    -webkit-backdrop-filter: blur(6px) saturate(1.3);
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-primary);   /* the sources read white, like the answer */
    border: 0;
    border-radius: 999px;
    padding: 4px 11px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.22);
}
/* the rim: lit from the top-left, masked to the edge — the same ring the
 * bubbles and buttons carry, at pill scale */
.hd-chip::after, .hd-mpill::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 1px;
    pointer-events: none;
    background:
        radial-gradient(44% 70% at 24% 8%,
            rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0) 74%),
        rgba(255, 255, 255, 0.14);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
}
/* the register pills tint their own rim, so settled / contested / open stay
 * legible as registers rather than becoming three identical chips */
.hd-mcol-settled .hd-mpill::after {
    background:
        radial-gradient(44% 70% at 24% 8%, rgba(255, 255, 255, 0.7) 0%, rgba(255, 255, 255, 0) 74%),
        rgba(167, 215, 214, 0.34);
}
.hd-mcol-open .hd-mpill::after {
    background:
        radial-gradient(44% 70% at 24% 8%, rgba(255, 255, 255, 0.7) 0%, rgba(255, 255, 255, 0) 74%),
        rgba(207, 212, 120, 0.38);
}

/* ---- "Enter a field": the artifact ladder (survey doc -> table) ---- */
.hd-chat-wide { max-width: 980px; }

.hd-artifacts { display: flex; gap: 24px; align-items: flex-start; }

/* The survey document: an LED sheet (register "sheet", r 18). */
.hd-doc {
    flex: 1.5;
    min-width: 0;
    padding: 24px 28px;
    border-radius: 18px;
}
/* Mockups speak the product's voice: Inter, not the site serif. */
.hd-doc-title {
    font-size: var(--text-large-size);
    font-weight: var(--font-weight-medium);
    color: var(--text-primary);
    margin-bottom: 4px;
}
.hd-doc-meta {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-quaternary);
    margin-bottom: 10px;
}
.hd-sec-label {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-quaternary);
    margin-top: 12px;
}
.hd-sec-line {
    font-size: var(--text-mini-size);
    color: var(--text-secondary);
    line-height: 1.6;
    padding: 2px 0;
}

/* The narrowed table: the ladder's second rung (register "tileCitron"). */
.hd-table {
    flex: 1;
    min-width: 0;
    position: relative;   /* anchors the converge point for the pulses */
    padding: 20px 24px;
    border-radius: 18px;
}
.hd-table .hd-converge { left: -2px; top: 50%; bottom: auto; }
.hd-table-cap {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--citron-300);
    margin-bottom: 10px;
}
.hd-trow {
    display: grid;
    grid-template-columns: 4.5em 1fr;
    gap: 10px;
    font-size: var(--text-mini-size);
    color: var(--text-secondary);
    line-height: 1.6;
    padding: 3px 0;
}
.hd-thead {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-quaternary);
}
.hd-table .hd-chip-row { margin-top: 16px; }

/* ---- "Run the code": claim -> sandbox -> verdict, top to bottom ---- */
.hd-stack {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 16px;
}
/* ---- "Run the claim": the work stage is a LOG, not a stack of boxes ----
 * Rebuilt on .hd-think2's grammar (2026-08-24): the claim under trial and
 * the run that tests it are bare type on the field, left-aligned in one
 * column, fed from the disc. Only the verdict takes glass. */
.hd-trial {
    align-self: flex-start;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 18px;
    max-width: 640px;
    transition: opacity 0.7s var(--motion-curve-house);
}
.hd-trial.hd-gone { opacity: 0; }
.hd-trial-claim { max-width: 640px; }
.hd-quote {
    margin: 0;
    font-size: var(--text-regular-size);
    line-height: 1.55;
    color: var(--text-primary);
}
.hd-quote-src {
    margin: 6px 0 0;
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-quaternary);
}
/* the run: bare mono lines that stack as they land — no frame, no reserved
 * height, so the log never shows an empty box waiting to be filled */
.hd-runlog {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    width: 100%;
    font-family: var(--font-family-code-mono);
    font-size: var(--text-micro-size);
}
.hd-verdict {
    position: relative;   /* anchors the converge point */
    align-self: center;   /* the closing message settles on the stage's axis */
}
.hd-verdict .hd-converge { left: 50%; top: -2px; bottom: auto; }
/* The verdict, rebuilt on the answer grammar (2026-08-25). The finding is
 * the headline and wears the house verify glyph; the line under it carries
 * the reasoning; the two numbers are the exhibit and sit in their own well,
 * because the whole argument is that they are the same number. */
.hd-vd-head {
    margin: 0 0 8px;
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: var(--font-family-sans);
    font-size: var(--text-regular-size);
    /* no weight of its own: the map title in demos 1 and 2 inherits the
     * normal weight, and this read heavier than every other answer head */
    color: var(--text-primary);   /* the head reads white like every other answer head; the glyph keeps the citron */
}
.hd-verdict .hd-meta-snug { margin-bottom: 24px; }   /* +0.5rem above the figures */
/* No inner box (Ivan 2026-08-25): a panel inside the bubble made the exhibit
 * look like a widget dropped into the answer. The two figures stand on the
 * bubble itself, sharing one baseline; the comparison is carried by the lit
 * rule under the one that was actually run. */
.hd-compare { display: flex; align-items: flex-end; gap: 32px; }
.hd-verdict-refs {
    margin-top: 16px;
    font-family: var(--font-family-code-mono);
    font-size: var(--text-micro-size);
    color: var(--text-primary);
}
.hd-verdict .hd-chip-row { margin-top: 24px; }   /* +0.5rem below the scale, matching above */   /* matches the 16px above the scale */   /* pills sit closer to what cites them, as in demo 1 */

/* ---- "Catch up": the digest rows carry dates in the gutter ---- */
.hd-drow {
    display: grid;
    grid-template-columns: 3em 1fr;
    gap: 10px;
    font-size: var(--text-mini-size);
    color: var(--text-secondary);
    line-height: 1.6;
    padding: 4px 0;
}
.hd-drow-d {
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-quaternary);
    padding-top: 1px;
}
.hd-followup-body {
    font-size: var(--text-mini-size);
    color: var(--text-secondary);
    line-height: 1.6;
}
.hd-table .hd-followup-body + .hd-chip-row { margin-top: 16px; }

/* ---- "Reach a peer": the routed answer ---- */
.hd-reply {
    position: relative;   /* anchors the converge point */
    padding: 24px 32px;   /* the answer family's box, not its own 24/28 at r24 */
}
.hd-reply .hd-converge { left: -2px; top: 50%; bottom: auto; }
.hd-reply-body {
    font-size: var(--text-small-size);   /* 14px, as every other answer body */
    color: var(--text-primary);
    line-height: 1.6;
    margin: 0;
}
/* the answer's own rhythm, on the grid the other bubbles use: 24 above the
 * figure and 24 below it, so the instrument sits in its own air */
.hd-reply .hd-meta-snug { margin-bottom: 24px; }
.hd-reply .hd-fig { margin: 24px 0 0; }
.hd-chatlog .hd-reply .hd-peer-close { margin: 24px 0 0; }

/* ---- paired columns: tab 5's instrument (see hdBars) ----
 * The LEDs stand up here instead of running along, because the finding is
 * a comparison at each step rather than one quantity per row. Same 10px
 * lattice pitch and the same citron-is-the-finding rule as the meter. */
.hd-bars { display: flex; flex-direction: column; gap: 10px; }
.hd-bkey {
    display: flex; flex-wrap: wrap; gap: 8px 20px;
    margin: 0;
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
    color: var(--text-primary);
}
.hd-bkey-i { display: inline-flex; align-items: center; gap: 8px; }
.hd-bkey-i i {
    width: 8px; height: 8px; flex: none; border-radius: 999px;
    background: rgba(167, 215, 214, 0.9);
}
.hd-bkey-i.hd-hot i { background: var(--citron-300); box-shadow: 0 0 8px rgba(207, 212, 120, 0.5); }
/* three equal cells across the FULL bubble width, each group centred in
 * its own: laid out as a flex row the whole instrument packed into the
 * left third and left the bubble lopsided (Ivan 2026-08-25) */
.hd-bset { display: grid; grid-template-columns: repeat(3, 1fr); align-items: end; }
.hd-bgrp { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.hd-bpair { display: flex; align-items: flex-end; gap: 12px; }
/* column-reverse: the run of lit cells grows from the baseline up, so the
 * bars share a floor the way a chart's bars must */
.hd-bcol { display: flex; flex-direction: column-reverse; align-items: center; }
/* three dots wide at the lattice's own 10px pitch — a single-dot column
 * read as a dotted line rather than a bar, and two looked slight against
 * the width of the bubble */
.hd-bled, .hd-boff { width: 30px; height: 10px; flex: none; background-size: 10px 10px; background-repeat: repeat-x; }
.hd-boff { background-image: radial-gradient(circle, rgba(255, 255, 255, 0.14) 1.2px, transparent 1.6px); }
.hd-bled {
    background-image: radial-gradient(circle, rgba(167, 215, 214, 0.9) 2.1px, transparent 2.5px);
    opacity: 0;
    animation: hd-led-on 0.5s cubic-bezier(0.22, 1, 0.36, 1) forwards;
    animation-delay: calc(var(--k) * 45ms);
}
.hd-bcol.hd-hot .hd-bled { background-image: radial-gradient(circle, rgba(207, 212, 120, 0.95) 2.1px, transparent 2.5px); }
.hd-bled.hd-blead { background-image: radial-gradient(circle, rgba(167, 215, 214, 1) 2.8px, transparent 3.3px); filter: drop-shadow(0 0 6px rgba(167, 215, 214, 0.85)); }
.hd-bcol.hd-hot .hd-bled.hd-blead { background-image: radial-gradient(circle, rgba(207, 212, 120, 1) 2.8px, transparent 3.3px); filter: drop-shadow(0 0 7px rgba(207, 212, 120, 0.9)); }
.hd-bval {
    font-family: var(--font-family-code-mono);
    font-size: var(--text-micro-size);
    font-weight: inherit;
    color: var(--text-primary);
    margin-bottom: 6px;   /* column-reverse: this is the gap above the bar */
}
.hd-bcol.hd-hot .hd-bval { color: var(--citron-300); }
/* one label line under each pair, not two rows */
.hd-blab {
    margin: 0;
    display: flex; align-items: baseline; gap: 8px;
    font-family: var(--font-family-label-mono);
    font-size: var(--text-micro-size);
}
.hd-bk { color: var(--text-primary); }
.hd-bsub { color: var(--text-secondary); }
.hd-bsub-open { color: var(--citron-300); }   /* what only the network has */
@media (prefers-reduced-motion: reduce) {
    .hd-bled { animation: none; opacity: 1; }
}
@media (width <= 700px) {
    .hd-bpair { gap: 6px; }
    .hd-bled, .hd-boff { width: 20px; }   /* two dots, not three: the cells are a third the width */
    .hd-blab { flex-direction: column; align-items: center; gap: 2px; }
}
.hd-chip:where(a) { text-decoration: none; }
.hd-chip:where(a):hover { border-color: var(--citron-300); color: var(--text-primary); }

/* THE SEND (2026-08-24). The question is thrown, not faded. It used to type
 * itself, and the keystrokes were what gave the moment its weight; with the
 * typing gone, the weight has to live in the arrival. So: real travel from
 * below, a spring that overshoots by a hair and settles, and the words
 * arriving a beat behind the bubble carrying them — so the bubble reads as
 * having mass rather than being a flat plane that faded up. */
.hd-bubble-user.hd-pending {
    opacity: 0;
    transform: translateY(42px) scale(0.92);
    filter: blur(10px);
}
/* the :not() guards matter: .hd-absorb and .hd-out are declared earlier in
 * this sheet at equal specificity, so without them the arrival would win
 * the cascade and the bubble could never leave */
.hd-bubble-user.hd-arrive:not(.hd-absorb):not(.hd-out) {
    opacity: 1;
    transform: none;
    filter: none;
    transition:
        opacity 0.32s ease-out,
        transform 0.6s cubic-bezier(0.22, 1, 0.36, 1),   /* no overshoot: the elastic settle read as cheap (Ivan 2026-08-25) */
        
        filter 0.42s ease-out;
}
.hd-qtext {
    display: inline-block;
    white-space: pre-wrap;
    transition:
        opacity 0.4s ease-out 0.09s,
        transform 0.66s cubic-bezier(0.22, 1, 0.36, 1) 0.09s;
}
.hd-bubble-user.hd-pending .hd-qtext { opacity: 0; transform: translateY(14px); }
.hd-bubble-user.hd-arrive .hd-qtext { opacity: 1; transform: none; }

/* THE HOLD, MADE VISIBLE (2026-08-24). The pause before the work starts is
 * deliberate — it is reading time — but with nothing moving it reads as a
 * stall. So the BUBBLE'S OWN FILL is the progress indicator: a citron wash
 * sweeps across it for exactly as long as the question will stay up, with a
 * brighter leading edge so the wavefront is legible. It sits under the text
 * (the bubble's children are z-index 1) and takes the pill's own radius, so
 * the sweep ends by blending into the bottom curve rather than stopping at
 * a hard edge. */
.hd-bubble-user { overflow: hidden; }   /* the wash takes the bubble's corners */
/* Progress on the STROKE, not the surface (Ivan 2026-08-25). The wash tinted
 * the whole bubble, which fought its own citron glass; the rim is the part
 * of this design language that already carries light, so the progress rides
 * it. Built with the same xor-mask rim recipe as the chips, sitting a layer
 * above the bubble's own ::after so it lights the same 1px edge.
 * --hd-p is the head position: everything behind it is lit stroke, the head
 * itself is the bright point, ahead of it the rim stays dark. It travels
 * edge to edge over exactly the hold, so the position still reports the
 * wait honestly, which is the one thing this may not fake. */
@property --hd-p {
    syntax: '<percentage>';
    inherits: false;
    initial-value: 0%;
}
.hd-bubble-user .hd-qbar {
    position: absolute;
    inset: 0;
    z-index: 2;   /* above the bubble's own rim, lighting the same edge */
    border-radius: inherit;
    padding: 1px;
    opacity: 0;
    pointer-events: none;
    --hd-p: 0%;
    background: linear-gradient(
        135deg,
        rgba(207, 212, 120, 0.30) 0%,
        rgba(207, 212, 120, 0.52) max(0%, calc(var(--hd-p) - 34%)),
        rgba(207, 212, 120, 0.66) max(0%, calc(var(--hd-p) - 15%)),
        rgba(228, 233, 165, 0.86) max(0%, calc(var(--hd-p) - 5%)),
        rgba(244, 248, 205, 1) var(--hd-p),
        rgba(207, 212, 120, 0.22) min(100%, calc(var(--hd-p) + 4%)),
        rgba(207, 212, 120, 0.10) min(100%, calc(var(--hd-p) + 13%)),
        rgba(207, 212, 120, 0.08) 100%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
}
.hd-bubble-user.hd-holding .hd-qbar { opacity: 1; }
/* the wash has no business surviving the question it measured */
.hd-bubble-user.hd-absorb .hd-qbar,
.hd-bubble-user.hd-out .hd-qbar { opacity: 0; transition: opacity 0.3s ease-out; }
@media (prefers-reduced-motion: reduce) {
    .hd-bubble-user .hd-qbar { display: none; }
}
@media (prefers-reduced-motion: reduce) {
    .hd-bubble-user.hd-pending, .hd-bubble-user.hd-pending .hd-qtext {
        opacity: 1; transform: none; filter: none;
    }
    .hd-bubble-user.hd-arrive, .hd-qtext { transition: none; }
}

/* Full question for AT from mount; the typed span is aria-hidden. */
.hd-sr {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

@media (width <= 940px) {
    .hd-chat { max-width: 100%; }
    .hd-bubble-user, .hd-bubble-bot { max-width: 100%; }
    .hd-tile-grid { grid-template-columns: 1fr; }
    .hd-artifacts { flex-direction: column; }
    .hd-doc, .hd-table { width: 100%; }
}

/* Phones (2026-08-19): the stage's side bleed is wider than the screen and
 * nothing clips it (.show-panel is overflow:visible by design), so it would
 * scroll the whole page sideways. On a phone the stage stays in its column
 * and the ask bubble gives up its overhang. */
@media (width <= 700px) {
    .hd-stage { width: 100%; margin-inline: 0; }
    .hd-bubble-user { margin-right: 0; }
    .hd-disc-zone { flex: 0 0 150px; min-height: 190px; }

    /* Routing re-composes rather than shrinks: a phone cannot hold
     * "coordinator | strand | neighbour | label" across 374px, so the three
     * stack and the question travels DOWN to them. The coordinator sits high
     * enough to reach into the log's top fade, which costs nothing — it is
     * drawn on the canvas, and only the DOM labels are masked.
     * hero-demo.js reads the SAME breakpoint for the mark's scale. */
    .hd-route { height: 340px; }
    .hd-route .hd-disc-anchor { left: 50%; top: 12%; }   /* clear of the first peer's label */
    .hd-peer { gap: 12px; }
    .hd-peer-orb { flex: 0 0 var(--orb, 56px); height: var(--orb, 56px); }
    .hd-peer[data-p='0'] { --x: 4%; --y: 40%; }
    .hd-peer[data-p='1'] { --x: 4%; --y: 60%; }
    .hd-peer[data-p='2'] { --x: 4%; --y: 80%; }
    .hd-route-say { --x: 50%; width: min(96%, 340px); top: 92%; }
}

/* Reduce Transparency: the material collapses to a solid surface */
@media (prefers-reduced-transparency: reduce) {
    .hd-bubble-user, .hd-response, .hd-doc, .hd-table, .hd-verdict,
    .hd-reply, .hd-tile, .hd-trace, .hd-step {
        background: #141717;
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
    }
    .hd-bubble-user { background: #1b1d14; }
}
/* Increase Contrast: the lit edge becomes a definite outline */
@media (prefers-contrast: more) {
    .hd-bubble-user::after, .hd-response::after, .hd-doc::after,
    .hd-table::after, .hd-verdict::after, .hd-reply::after,
    .hd-tile::after, .hd-trace::after, .hd-step::after {
        background: rgba(255, 255, 255, 0.55);
    }
}

@media (prefers-reduced-motion: reduce) {
    .hd-pending { opacity: 1; transform: none; filter: none; }
    .hd-tile, .hd-tile-line, .hd-answer-block, .hd-fig, .hd-chip,
    .hd-doc, .hd-sec-label, .hd-sec-line, .hd-table, .hd-trow,
    .hd-rv, .hd-response, .hd-thinkstage { transition: none; }
}

/* ---- overture timing: the last word (2026-08-24) ----
 * fade in 0.7 ease-in-out, hold, fade out 0.7 — a PURE fade; any transform
 * or blur inherited from older statement treatments is pinned off so the
 * only thing moving is opacity. */
.hd-window > .hd-stakes {
    transition: opacity 0.7s ease-in-out;
    transform: translate(-50%, -50%);
    filter: none;
}
.hd-window > .hd-stakes.hd-st-on { opacity: 1; }
.hd-window > .hd-stakes.hd-out { opacity: 0; transform: translate(-50%, -50%); filter: none; }
@media (prefers-reduced-motion: reduce) { .hd-window > .hd-stakes { transition: none; } }

/* the map and the brief wear the same deep teal glass as the answer */
.hd-chatlog .hd-map, .hd-chatlog .hd-brief, .hd-chatlog .hd-verdict, .hd-chatlog .hd-reply {
    background: rgba(11, 23, 23, 0.78);
    backdrop-filter: blur(40px) saturate(1.6);
    -webkit-backdrop-filter: blur(40px) saturate(1.6);
    border-radius: 28px;
    box-shadow:
        0 10px 30px rgba(0, 0, 0, 0.12),
        0 40px 100px rgba(0, 0, 0, 0.18),
        0 80px 180px rgba(0, 0, 0, 0.14);
}

/* Demo 4's question is 91 characters — at the shared measure it fills one
 * line and spills a short orphan onto a second. Capping the measure here
 * splits it into two lines of comparable length (Ivan 2026-08-25). */
#h-away .hd-bubble-user { max-width: 34rem; }
#h-away .hd-bubble-user .hd-qtext { text-wrap: balance; }

/* ---- Demo 4's week: every piece read, three lifted (Ivan 2026-08-25) ----
 * Each dot is one thing that came in. They arrive as a field, Althea reads
 * along them one at a time, and the pieces that bear on this researcher's
 * own work stay lit while the rest settle back. The reading is the point:
 * the brief is earned by what was passed over, not just by what was kept. */
/* being read: it brightens and lifts for a moment, then is set down again */
/* passed over: still there, just no longer under the light */
/* kept: the three that become the brief */
@media (prefers-reduced-motion: reduce) {
            }

/* Demo 4's rows: no rules between them (Ivan 2026-08-25). Even 24px steps
 * carry the separation, matching the rhythm the other answers use. */
.hd-brief .hd-brow + .hd-brow { margin-top: 16px; }   /* three paragraphs, not sections */
.hd-brief .hd-map-meta { margin-bottom: 24px; }
.hd-brief .hd-chip-row { margin-top: 24px; }

/* demo 4's reading stage: the mark is drawn on the canvas from the anchor,
 * so the stage only has to reserve the room it needs */
.hd-watch { min-height: 280px; display: flex; flex-direction: column; justify-content: center; }

/* demo 5's meta sat on --text-quaternary: the last grey left in any answer */
.hd-reply .hd-doc-meta { color: var(--text-primary); }

/* ---- phone composition (2026-08-31, Ivan: rebuild the content layout the
 * way the infra diagram does, rather than squeezing the desktop one). The
 * surfaces keep their grammar; paddings and type step down one notch so the
 * settled answers fit the taller window. ---- */
@media (width <= 700px) {
    .hd-bubble { padding: 20px 16px; border-radius: 18px; }   /* 16px sides (Ivan 2026-08-31): more measure, less height */
    /* the answer surfaces carry their own heavier padding at (0,2,0), which
     * outranked the line above; matched here so 16px sides actually land */
    .hd-chatlog .hd-bubble, .hd-chatlog .hd-response, .hd-chatlog .hd-reply { padding-inline: 16px; }
    /* likewise the type: .hd-response pins 14px on itself, so the log's 13px
     * never reached the output bubbles (Ivan 2026-08-31) */
    .hd-chatlog .hd-response, .hd-chatlog .hd-reply { font-size: 13px; line-height: 1.55; }
    /* the closer with its CTA is dropped on phones (Ivan 2026-08-31): the
     * answer itself is the payload, and the page's own CTAs are a scroll
     * away. Dropping it frees the height the answers actually need. */
    .hd-chatlog [data-hd="close"] { display: none; }
    /* the question stands alone on stage, so it is CENTRED here rather than
     * bottom-anchored: the log's flex-end is what pinned it low. Once other
     * blocks arrive the track fills and this has no effect. */
    /* :only-child fails where a demo pre-renders its blocks hidden, so the
     * test is "no VISIBLE sibling besides the question". Auto margins absorb
     * the free space before justify-content, so this centres on its own. */
    /* the track's block padding was 48 top / 36 bottom, which skewed every
     * settled stack ~13px high; symmetric and tighter, so the tallest demo
     * (map the field, 685px) also stops touching the edges */
    .hd-chatlog .hd-log-track { padding-block: 24px; }
    /* the stepper row lives inside the frame but outside the log, so content
     * centred in the log reads low against the frame; lift by half the row */
    .hd-chatlog { padding-bottom: 36px; }
    /* demo 1 writes an inline margin-bottom to park the opening question,
     * so the phone rule needs !important to reclaim it for centring */
    .hd-chatlog .hd-log-track:not(:has(> *:not([hidden]):not(.hd-pending):not(.hd-bubble-user):not(.hd-think2))) { margin-block: auto !important; }
    /* the question and the thinking steps match the answer's 13px (Ivan) */
    .hd-chatlog .hd-bubble-user, .hd-chatlog .hd-trace, .hd-chatlog .hd-step,
    .hd-chatlog .hd-doc, .hd-chatlog .hd-table, .hd-chatlog .hd-verdict { font-size: 13px; line-height: 1.55; }
    /* the answer headings sat at 15px against 13px body (Ivan) */
    .hd-chatlog .hd-map-title { font-size: 13px; }
    /* the rest of the heading/body outliers, brought onto the same 13px:
     * the verify head and quote sat at 15px, the browse row and reply body
     * at 14px, against 13px everywhere else (Ivan) */
    .hd-chatlog .hd-vd-head, .hd-chatlog .hd-quote,
    .hd-chatlog .hd-brow-t, .hd-chatlog .hd-reply-body { font-size: 13px; line-height: 1.55; }
    /* every output surface takes ONE radius; .hd-response ships 28px and
     * .hd-bubble 24px, so both bubbles read differently side by side */
    .hd-chatlog .hd-bubble, .hd-chatlog .hd-response, .hd-chatlog .hd-reply { border-radius: 20px; }
    /* the source pills go on phones (Ivan 2026-08-31): they are the widest
     * and least load-bearing rows in every answer, and the provenance is
     * already stated in the meta line above the prose */
    .hd-chatlog .hd-chip-row, .hd-chatlog .hd-vs { display: none; }
    /* backdrop-filter is what made the bubbles blink on scroll and the whole
     * section feel heavy: mobile Safari re-rasterises the blurred backdrop as
     * the page moves. The surfaces keep their look with a slightly denser
     * fill instead, and nothing behind them was ever legible through 40px of
     * blur anyway (Ivan 2026-08-31). */
    .hd-chatlog .hd-response, .hd-chatlog .hd-reply, .hd-chatlog .hd-bubble,
    .hd-chatlog .hd-chip, .hd-chatlog .hd-doc, .hd-chatlog .hd-trace {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
    }
    .hd-chatlog .hd-response { background: rgba(11, 23, 23, 0.94); }
    /* demo 2 settles at 685px in a 704px cell: the map's sections and the
     * gaps inside each item are what make it tall, so both tighten here */
    /* demo 2 was 590px against 410-500 for the rest, and almost all of the
     * difference was gaps: 69px between the three map sections, 14px inside
     * each, 24px under the meta, 30px of bubble padding (Ivan 2026-08-31) */
    .hd-chatlog .hd-mcol { gap: 10px; }
    .hd-chatlog .hd-mcol .hd-mitem { gap: 2px; }
    .hd-chatlog .hd-map-cols { gap: 16px; }
    /* A RAIL PER STATE (Ivan 2026-08-31): the map's three sections are its
     * structure, so each gets a vertical stroke in its own colour and the
     * content indents past it. The rail runs the section's full height, which
     * is what makes the three read as three at a glance. */
    .hd-chatlog .hd-mcol { position: relative; padding-left: 14px; }
    .hd-chatlog .hd-mcol::before {
        content: '';
        position: absolute;
        left: 0; top: 0; bottom: 0;
        width: 1px;
        /* tapered at both ends, the same grammar the page's own rules use,
         * so the rail reads as a mark rather than a bar (Ivan 2026-08-31) */
        background: linear-gradient(180deg,
            transparent 0,
            currentColor 14%,
            currentColor 86%,
            transparent 100%);
        opacity: 0.55;
    }
    .hd-chatlog .hd-mcol-settled::before { color: #a7d7d6; }
    .hd-chatlog .hd-mcol-contested::before { color: var(--text-primary); opacity: 0.4; }
    .hd-chatlog .hd-mcol-open::before { color: var(--citron-300); }
    .hd-chatlog .hd-map-meta { margin-bottom: 14px; }
    .hd-chatlog .hd-response { padding-block: 20px; }
    /* the stakes lines ran five lines tall on a phone */
    .hd-stakes { font-size: 1.125rem; }
    /* the overture's own 3rem outranked the line above (0,1,1 vs 0,1,0).
     * And the 24ch measure read as a thin ribbon on a phone: 24px side
     * paddings instead, so the statement uses the window (Ivan 2026-08-31) */
    /* the overture is absolutely positioned, so it shrink-wraps its text:
     * max-width cannot widen it, width can (Ivan: "too narrow") */
    /* The card is width-bound, so its type is too (Ivan 2026-09-01: the
     * section token alone "reads too small"; the fixed 1.75rem before it
     * broke four of five statements onto a third line at 360px). 6.9vw
     * lands on 27px at 390, 26px at 375, 25px at 360: the largest size at
     * which every two-line statement still holds, measured in the real
     * card. Floor is the section-heading token; ceiling is the old 1.75rem.
     * Side gutters go 24px -> 12px, which is what buys the size step. */
    .hd-window > .hd-stakes {
        font-size: clamp(var(--heading-section-size, 1.375rem), 6.9vw, 1.75rem);
        width: calc(100% - 24px);
        max-width: none;
    }
    .hd-stakes { max-width: calc(100% - 48px); }
    /* the 22% top fade ate the settled answer's first line in the shorter
     * phone window; keep just enough to soften the exit ride. And the log
     * SCROLLS here: some demos settle taller than any phone window (verify:
     * 665px measured), so the head must be reachable, not dissolved.
     * flex-end makes top overflow unscrollable; flex-start plus the track's
     * auto top margin keeps the bottom anchoring AND a working scrollbar. */
    /* Height from CONTENT, not the viewport (Ivan 2026-08-31). svh looked
     * fine headless but on a real phone it is the viewport MINUS Safari's
     * chrome, and the tab row lives inside the frame, so the log got far
     * less than the tests showed and every answer overflowed. A floor keeps
     * the shorter demos from bouncing the layout as you page between them. */
    /* No inner scroll (Ivan 2026-08-31): session history is DESIGNED to ride
     * out through the top mask; the taller window fits the final composition
     * and the fade takes the rest, exactly as on desktop. */
    .hd-chatlog {
        -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 9%, #000 100%);
        mask-image: linear-gradient(to bottom, transparent 0, #000 9%, #000 100%);
    }
    .hd-chatlog { font-size: 13px; }
    .hd-reply { padding: 20px 16px; }
    .hd-response { border-radius: 20px; }
}
