/* ============================================================
   Physical AI Landscape Report launch — team briefing deck
   Hei Brand Mode. Palette and typefaces are locked to the
   Hei Style Spec (draft v1); layout is this deck's own, since
   the spec does not define a grid.

   Ground is black. That is a deliberate choice, not a default:
   Hei cyan #55FFE7 measures 1.25:1 on white (invisible) and
   16.84:1 on black. On a white deck the primary accent could
   not have carried a single word of text.
   ============================================================ */

/* ---------- Licensed webfonts (local files, not a CDN) -------
   Helvetica Now Display is commercially licensed and is not on
   any font CDN. These TTFs are copied from Angru's own licence.
   Freight, the secondary display italic, is NOT on this machine
   and is therefore absent from this deck — see the note in
   .accent-word below.
   ------------------------------------------------------------ */
@font-face {
    font-family: 'Helvetica Now Display';
    src: url('assets/fonts/HelveticaNowDisplay-Regular.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}
@font-face {
    font-family: 'Helvetica Now Display';
    src: url('assets/fonts/HelveticaNowDisplay-Medium.ttf') format('truetype');
    font-weight: 500;
    font-style: normal;
    font-display: swap;
}
@font-face {
    font-family: 'Helvetica Now Display';
    src: url('assets/fonts/HelveticaNowDisplay-Bold.ttf') format('truetype');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

/* ============================================================
   TOKENS
   ============================================================ */
:root {
    color-scheme: dark;

    /* --- Hei brand tokens. Do not add colours outside this set. --- */
    --hei-black:  #000000;
    --hei-white:  #FFFFFF;
    --hei-grey:   #555555;   /* spec's secondary text colour — white grounds only */
    --hei-cyan:   #55FFE7;
    --hei-blue:   #0000FF;
    --hei-violet: #B63CFF;

    /* --- Mapped onto the names the reveal.js base styles expect --- */
    --bg-primary:    var(--hei-black);
    --accent:        var(--hei-cyan);
    --text-primary:  var(--hei-white);
    /* The spec's #555555 sits at 2.8:1 on black and fails as body text.
       Rather than invent a lighter grey that is not in the brand set,
       secondary and muted text are the white token at reduced opacity. */
    --text-secondary: rgba(255, 255, 255, 0.72);
    --text-muted:     rgba(255, 255, 255, 0.46);
    --rule:           rgba(255, 255, 255, 0.16);
    --rule-soft:      rgba(255, 255, 255, 0.08);

    /* Accent roles on a black ground, by measured contrast:
       cyan 16.8:1 (text safe) · violet 5.1:1 (large text and fills)
       blue 2.4:1 (fills only, never text) */
    --accent-2: var(--hei-violet);
    --accent-3: var(--hei-blue);

    --font-display: 'Helvetica Now Display', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    --font-accent:  'Freight', 'Freight Big Pro', Georgia, serif;   /* absent — see .accent-word */

    /* --- Type scale (all clamp()-based for viewport fitting) --- */
    --title-size:  clamp(1.5rem, 5vw, 4rem);
    --h2-size:     clamp(1.25rem, 3.2vw, 2.4rem);
    --h3-size:     clamp(1rem, 2.2vw, 1.6rem);
    --body-size:   clamp(0.75rem, 1.4vw, 1.1rem);
    --small-size:  clamp(0.65rem, 1vw, 0.85rem);
    --micro-size:  clamp(0.55rem, 0.8vw, 0.7rem);
    --stat-size:   clamp(1.6rem, 4.4vw, 3.4rem);

    --slide-padding: clamp(1rem, 4vw, 4rem);
    --content-gap:   clamp(0.5rem, 1.8vw, 1.75rem);
    --element-gap:   clamp(0.25rem, 0.9vw, 0.9rem);

    --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
    --duration: 0.7s;
}

/* ============================================================
   VIEWPORT FITTING — mandatory base
   ============================================================ */
html, body {
    height: 100%;
    overflow: hidden;
}

.reveal { background: var(--bg-primary); font-family: var(--font-display); }
.reveal .controls { color: var(--accent); }
.reveal .progress { color: var(--accent); height: 3px; }
.reveal .slide-number {
    color: var(--text-muted);
    background: transparent;
    font-size: var(--micro-size);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.08em;
}

.reveal .slides section {
    height: 100vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding: 0;
}

.slide-content {
    /* reveal.js sets `display: block` INLINE on the present section, which
       beats the `display: flex` declared on `.reveal .slides section` above.
       The parent is therefore never a flex container and `flex: 1` is inert,
       so the content collapsed to its natural height and pinned to the top of
       every slide. It cannot be fixed with `!important` on the section, because
       reveal uses the same inline `display` property (`none`) to hide slides.
       Sidestep the parent instead: take height from the section's own definite
       `height: 100vh`, and count the padding inside it. */
    height: 100%;
    box-sizing: border-box;
    /* reveal.js also sets `text-align: center` on `.reveal .slides`, which
       cascades into every p, li and column. The headings override it, body copy
       did not — so the deck read as centre-set throughout and the `.center`
       utility below was a no-op. Left is the default; `.center` opts in. */
    text-align: left;
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    max-height: 100%;
    overflow: hidden;
    padding: var(--slide-padding);
    position: relative;
    z-index: 2;
    gap: var(--content-gap);
}

/* ============================================================
   TYPOGRAPHY — overrides reveal.js defaults
   ============================================================ */
.reveal h1 {
    font-family: var(--font-display);
    font-size: var(--title-size);
    font-weight: 700;
    line-height: 1.02;
    letter-spacing: -0.035em;
    color: var(--text-primary);
    text-transform: none;
    text-shadow: none;
    margin: 0;
}
.reveal h2 {
    font-family: var(--font-display);
    font-size: var(--h2-size);
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: -0.025em;
    color: var(--text-primary);
    text-transform: none;
    text-shadow: none;
    margin: 0;
    max-width: 22ch;
}
.reveal h2.wide { max-width: 32ch; }
.reveal h3 {
    font-family: var(--font-display);
    font-size: var(--h3-size);
    font-weight: 500;
    line-height: 1.2;
    letter-spacing: -0.015em;
    color: var(--text-primary);
    text-transform: none;
    margin: 0;
}
.reveal p, .reveal li {
    font-size: var(--body-size);
    line-height: 1.55;
    color: var(--text-secondary);
    margin: 0;
}
.reveal strong { color: var(--text-primary); font-weight: 700; }
.reveal em { font-style: italic; }

/* The Hei signature move is one italic Freight word inside an
   otherwise bold Helvetica Now Display line. Freight is not on
   this machine, so no word in this deck uses it. This rule is
   here so the effect can be switched on by dropping the licensed
   file into assets/fonts/ — it is not in use as built. */
.accent-word {
    font-family: var(--font-accent);
    font-style: italic;
    font-weight: 400;
}

/* ---- Eyebrow label above a heading ---- */
.eyebrow {
    font-size: var(--micro-size);
    font-weight: 500;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--accent);
}
.eyebrow.muted { color: var(--text-muted); }

/* ---- Deck-wide footnote / caption ---- */
.caption {
    font-size: var(--micro-size);
    color: var(--text-muted);
    line-height: 1.45;
    letter-spacing: 0.01em;
}

/* ============================================================
   BACKGROUND — the Hei light form
   This is the REAL brand artwork (Teams BG 3.jpg), not a CSS
   approximation. The source has the Hei lockup baked into its
   top-left; that strip is cropped off in assets/hei-keyvisual.jpg
   so the deck's own vector logo is the only wordmark on screen.
   Content slides run it heavily scrimmed so body copy stays at
   full contrast; hero slides let it come forward.
   ============================================================ */
.reveal .slides section::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    /* Scrim first, artwork second — the gradient paints OVER the
       image because earlier background layers sit on top. */
    background-image:
        linear-gradient(100deg,
            rgba(0,0,0,0.97) 0%,
            rgba(0,0,0,0.95) 42%,
            rgba(0,0,0,0.88) 68%,
            rgba(0,0,0,0.74) 100%),
        url('assets/hei-keyvisual.jpg');
    background-size: cover, cover;
    background-position: center, 68% 38%;
    background-repeat: no-repeat, no-repeat;
}

/* Selector must match `.reveal .slides section::before` in specificity
   (0,2,2) or it silently loses. A bare `section.hero::before` is (0,1,2)
   and never applied — that was true of the original stylesheet too, so
   the hero treatment defined there had never once rendered.
   Title and divider slides: artwork at close to full strength.
   Hero copy is bottom-anchored via `.push`, so the scrim is
   weighted to the bottom where the text actually lands. */
.reveal .slides section.hero::before {
    /* Directional scrim, not a flat one. Hero copy sits bottom-left
       (left-aligned, pushed down by `.push`), so the darkening is
       concentrated there and falls away to nothing toward the top
       right, where the light form is allowed to stay vivid. A flat
       scrim strong enough for the text turned the whole artwork to
       mud — this keeps both. */
    background-image:
        linear-gradient(to top right,
            rgba(0,0,0,0.95) 0%,
            rgba(0,0,0,0.88) 22%,
            rgba(0,0,0,0.52) 45%,
            rgba(0,0,0,0.12) 70%,
            rgba(0,0,0,0.00) 100%),
        url('assets/hei-keyvisual.jpg');
    background-size: cover, cover;
    background-position: center, 50% 45%;
}

/* The appendix gallery is dense with photographs; drop the
   artwork out entirely so nothing competes with the images. */
.reveal .slides section.plate::before {
    background-image: none;
    background-color: #000;
}
/* The corner logo is absolutely positioned at the padding origin. On a
   dense slide the content has no vertical slack, so it starts at that
   same origin and collides with the mark. Reserve the strip. */
section.plate .slide-content { padding-top: calc(var(--slide-padding) + 2.1rem); }

/* ============================================================
   LOGO
   The wordmark is a drawn mark, never live text. This is the
   full dark-ground lockup: white wordmark + cyan tagline.
   ============================================================ */
.logo {
    width: clamp(88px, 11vw, 150px);
    height: auto;
    flex: none;
}
.logo-corner {
    position: absolute;
    top: var(--slide-padding);
    left: var(--slide-padding);
    width: clamp(52px, 5.5vw, 76px);
    opacity: 0.9;
    z-index: 3;
}

/* ============================================================
   LAYOUT PRIMITIVES
   ============================================================ */
.stack { display: flex; flex-direction: column; gap: var(--element-gap); }
.stack-lg { display: flex; flex-direction: column; gap: var(--content-gap); }
.push { margin-top: auto; }
.center { text-align: center; align-items: center; justify-content: center; }
.lede { max-width: 60ch; font-size: clamp(0.85rem, 1.6vw, 1.25rem); color: var(--text-secondary); }

.rule-top { border-top: 1px solid var(--rule); padding-top: var(--element-gap); }

/* ---- Two-column split ---- */
.split {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
    gap: clamp(1rem, 3.5vw, 3.5rem);
    align-items: start;
    min-height: 0;
}
.split.even { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.split.narrow-left { grid-template-columns: minmax(0, 0.75fr) minmax(0, 1.4fr); }

/* ---- Bullet lists ---- */
.reveal ul.points {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--element-gap);
}
.reveal ul.points li {
    padding-left: 1.4em;
    position: relative;
    max-width: 62ch;
}
.reveal ul.points li::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0.62em;
    width: 0.5em;
    height: 1px;
    background: var(--accent);
}
.reveal ul.points.tight li { line-height: 1.42; }

/* ---- Numbered "at a glance" list ---- */
.reveal ol.glance {
    list-style: none;
    margin: 0;
    padding: 0;
    counter-reset: g;
    display: flex;
    flex-direction: column;
    gap: clamp(0.4rem, 1.2vw, 1rem);
}
.reveal ol.glance li {
    counter-increment: g;
    display: grid;
    grid-template-columns: 2.2em minmax(0, 1fr);
    gap: 0.6em;
    align-items: baseline;
    line-height: 1.45;
}
.reveal ol.glance li::before {
    content: counter(g, decimal-leading-zero);
    font-size: var(--micro-size);
    font-weight: 500;
    color: var(--accent);
    letter-spacing: 0.1em;
    font-variant-numeric: tabular-nums;
}
.reveal ol.glance li b { color: var(--text-primary); font-weight: 700; }
.glance-start { counter-reset: g 5; }

/* ============================================================
   DATA DISPLAY
   Charts are drawn in CSS rather than photographed from the
   original slides: they stay legible at any size and print clean.
   ============================================================ */

/* ---- Big single figure ---- */
.stat { display: flex; flex-direction: column; gap: 0.15em; }
.stat .fig {
    font-size: var(--stat-size);
    font-weight: 700;
    line-height: 0.92;
    letter-spacing: -0.045em;
    color: var(--accent);
    font-variant-numeric: tabular-nums;
}
.stat .fig.plain { color: var(--text-primary); }
.stat .lab { font-size: var(--small-size); color: var(--text-secondary); line-height: 1.35; max-width: 30ch; }

.stat-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(clamp(120px, 18vw, 220px), 1fr));
    gap: clamp(0.75rem, 2.5vw, 2.5rem);
}

/* ---- Horizontal bar chart ---- */
.bars { display: flex; flex-direction: column; gap: clamp(0.35rem, 1.1vw, 0.85rem); width: 100%; }
.bar-row {
    display: grid;
    grid-template-columns: clamp(4.5em, 8vw, 7em) minmax(0, 1fr) auto;
    gap: clamp(0.5rem, 1.4vw, 1.1rem);
    align-items: center;
}
.bar-row .name {
    font-size: var(--small-size);
    color: var(--text-secondary);
    text-align: right;
    line-height: 1.2;
}
.bar-track { position: relative; height: clamp(14px, 2.4vw, 26px); background: var(--rule-soft); }
.bar-fill {
    position: absolute;
    inset: 0 auto 0 0;
    background: var(--accent);
    width: var(--w);
    transform-origin: left center;
    transform: scaleX(0);
    transition: transform 0.9s var(--ease-out-expo);
}
section.present .bar-fill { transform: scaleX(1); }
.bar-fill.pale   { background: rgba(85, 255, 231, 0.28); }
.bar-fill.violet { background: var(--accent-2); }
.bar-fill.blue   { background: var(--accent-3); }
.bar-fill.muted  { background: rgba(255, 255, 255, 0.30); }
/* Stacked second segment, drawn from the right edge of the first */
.bar-fill.stack-2 { left: var(--x); width: var(--w); }
.bar-row .val {
    font-size: var(--small-size);
    font-weight: 700;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.01em;
    white-space: nowrap;
}
.bar-row .val small { font-weight: 400; color: var(--text-muted); font-size: 0.85em; }

/* Stagger the bars in sequence */
.bars .bar-row:nth-child(1) .bar-fill { transition-delay: 0.10s; }
.bars .bar-row:nth-child(2) .bar-fill { transition-delay: 0.18s; }
.bars .bar-row:nth-child(3) .bar-fill { transition-delay: 0.26s; }
.bars .bar-row:nth-child(4) .bar-fill { transition-delay: 0.34s; }
.bars .bar-row:nth-child(5) .bar-fill { transition-delay: 0.42s; }
.bars .bar-row:nth-child(6) .bar-fill { transition-delay: 0.50s; }

/* ---- Heatmap ---- */
.heat { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.heat th, .heat td {
    padding: clamp(0.2rem, 0.7vw, 0.5rem) clamp(0.15rem, 0.5vw, 0.4rem);
    text-align: center;
    font-size: var(--micro-size);
    font-weight: 500;
}
.heat th { color: var(--text-muted); font-weight: 500; line-height: 1.15; vertical-align: bottom; }
.heat th.rowhead, .heat td.rowhead {
    text-align: right;
    color: var(--text-secondary);
    font-size: var(--small-size);
    padding-right: clamp(0.4rem, 1vw, 0.9rem);
    white-space: nowrap;
}
.heat td.cell { color: var(--text-primary); border: 1px solid var(--hei-black); }
/* Intensity steps: cyan for over-indexed, flat white wash for under */
.heat td.i0 { background: rgba(255, 255, 255, 0.04); color: var(--text-muted); }
.heat td.i1 { background: rgba(255, 255, 255, 0.09); color: var(--text-secondary); }
.heat td.i2 { background: rgba(85, 255, 231, 0.16); }
.heat td.i3 { background: rgba(85, 255, 231, 0.34); }
.heat td.i4 { background: rgba(85, 255, 231, 0.62); color: var(--hei-black); font-weight: 700; }
.heat td.i5 { background: var(--accent); color: var(--hei-black); font-weight: 700; }

.legend { display: flex; gap: clamp(0.5rem, 1.5vw, 1.25rem); align-items: center; flex-wrap: wrap; }
.legend .key { display: flex; gap: 0.4em; align-items: center; font-size: var(--micro-size); color: var(--text-muted); }
.legend .sw { width: 0.9em; height: 0.9em; flex: none; }

/* ---- Card grid (max 6 per slide) ---- */
.cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(clamp(140px, 22vw, 260px), 1fr));
    gap: clamp(0.5rem, 1.5vw, 1.25rem);
    min-height: 0;
}
.card {
    border-top: 2px solid var(--accent);
    padding-top: clamp(0.4rem, 1vw, 0.8rem);
    display: flex;
    flex-direction: column;
    gap: 0.35em;
    min-width: 0;
}
.card h4 {
    font-size: var(--small-size);
    font-weight: 700;
    color: var(--text-primary);
    margin: 0;
    line-height: 1.2;
    letter-spacing: -0.01em;
}
.card p { font-size: var(--micro-size); line-height: 1.45; color: var(--text-secondary); }
.card .eg { color: var(--text-muted); }
.card:nth-child(2) { border-color: var(--accent-2); }
.card:nth-child(4) { border-color: var(--accent-2); }
.card:nth-child(6) { border-color: var(--accent-2); }

/* ---- Funnel ---- */
.funnel { display: flex; flex-direction: column; gap: clamp(0.5rem, 1.6vw, 1.2rem); width: 100%; }
.funnel-step { display: flex; flex-direction: column; gap: 0.3em; }
.funnel-bar {
    height: clamp(26px, 4.5vw, 52px);
    background: var(--accent);
    width: var(--w);
    transform-origin: left center;
    transform: scaleX(0);
    transition: transform 0.9s var(--ease-out-expo);
}
section.present .funnel-bar { transform: scaleX(1); }
.funnel-step:nth-child(2) .funnel-bar { background: var(--accent-2); transition-delay: 0.15s; }
.funnel-step:nth-child(3) .funnel-bar { background: var(--accent-3); transition-delay: 0.3s; }
.funnel-step .lab {
    display: flex;
    gap: 0.6em;
    align-items: baseline;
    font-size: var(--small-size);
    color: var(--text-secondary);
    flex-wrap: wrap;
}
.funnel-step .lab b {
    font-size: clamp(0.95rem, 2vw, 1.5rem);
    color: var(--text-primary);
    font-weight: 700;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
}

/* ---- Callout box ---- */
.callout {
    border-left: 2px solid var(--accent-2);
    padding: 0 0 0 clamp(0.6rem, 1.6vw, 1.2rem);
    display: flex;
    flex-direction: column;
    gap: 0.3em;
    max-width: 70ch;
}
.callout .lab {
    font-size: var(--micro-size);
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--accent-2);
    font-weight: 500;
}
.callout p { font-size: var(--small-size); line-height: 1.5; }

/* ---- Pull quote ---- */
/* No max-width on the container. `ch` resolves against the element's OWN
   font-size, and the container sits at the 16px base while the blockquote
   inside runs at ~34px. A 24ch container is therefore ~240px while the
   blockquote wants ~400px+ — the parent always won, crushing every quote into
   a narrow ragged column and making the `.wide` modifier below a no-op.
   Let the blockquote's own max-width govern the measure. */
.quote { max-width: none; }
.quote blockquote {
    font-size: clamp(1.05rem, 2.8vw, 2.1rem);
    font-weight: 500;
    line-height: 1.2;
    letter-spacing: -0.025em;
    color: var(--text-primary);
    margin: 0;
    quotes: none;
    max-width: 22ch;
}
.quote.wide blockquote { max-width: 30ch; }
.quote cite {
    display: block;
    margin-top: clamp(0.5rem, 1.5vw, 1.1rem);
    font-style: normal;
    font-size: var(--small-size);
    color: var(--accent);
}

/* ---- Definition pair (what this part counts) ---- */
.def { display: flex; flex-direction: column; gap: 0.4em; max-width: 46ch; }
.def .term {
    font-size: clamp(1.1rem, 2.6vw, 1.9rem);
    font-weight: 700;
    letter-spacing: -0.03em;
    color: var(--accent);
    line-height: 1;
}
.def p { font-size: var(--small-size); }

/* ============================================================
   ENTRANCE ANIMATIONS
   Trigger is section.present, added by reveal.js.
   ============================================================ */
.anim-up    { opacity: 0; transform: translateY(24px);
              transition: opacity var(--duration) var(--ease-out-expo), transform var(--duration) var(--ease-out-expo); }
.anim-scale { opacity: 0; transform: scale(0.92);
              transition: opacity var(--duration) var(--ease-out-expo), transform var(--duration) var(--ease-out-expo); }
.anim-left  { opacity: 0; transform: translateX(-30px);
              transition: opacity var(--duration) var(--ease-out-expo), transform var(--duration) var(--ease-out-expo); }
.anim-right { opacity: 0; transform: translateX(30px);
              transition: opacity var(--duration) var(--ease-out-expo), transform var(--duration) var(--ease-out-expo); }
.anim-blur  { opacity: 0; filter: blur(10px);
              transition: opacity 0.8s var(--ease-out-expo), filter 0.8s var(--ease-out-expo); }

section.present .anim-up,
section.present .anim-scale,
section.present .anim-left,
section.present .anim-right,
section.present .anim-blur {
    opacity: 1;
    transform: translateY(0) translateX(0) scale(1);
    filter: blur(0);
}

section.present [data-d="1"] { transition-delay: 0.05s; }
section.present [data-d="2"] { transition-delay: 0.15s; }
section.present [data-d="3"] { transition-delay: 0.25s; }
section.present [data-d="4"] { transition-delay: 0.35s; }
section.present [data-d="5"] { transition-delay: 0.45s; }
section.present [data-d="6"] { transition-delay: 0.55s; }
section.present [data-d="7"] { transition-delay: 0.65s; }
section.present [data-d="8"] { transition-delay: 0.75s; }
section.present [data-d="9"] { transition-delay: 0.85s; }

/* ============================================================
   RESPONSIVE — height-first, because slides are viewport-locked
   ============================================================ */
@media (max-height: 700px) {
    :root {
        --slide-padding: clamp(0.75rem, 3vw, 2rem);
        --content-gap: clamp(0.4rem, 1.4vw, 1rem);
        --title-size: clamp(1.25rem, 4.5vw, 2.6rem);
        --h2-size: clamp(1rem, 3vw, 1.75rem);
        --stat-size: clamp(1.4rem, 4vw, 2.4rem);
    }
}
@media (max-height: 600px) {
    :root {
        --slide-padding: clamp(0.5rem, 2.5vw, 1.5rem);
        --content-gap: clamp(0.3rem, 1vw, 0.75rem);
        --title-size: clamp(1.1rem, 4vw, 2rem);
        --body-size: clamp(0.7rem, 1.2vw, 0.95rem);
    }
}
@media (max-height: 500px) {
    :root {
        --slide-padding: clamp(0.4rem, 2vw, 1rem);
        --title-size: clamp(1rem, 3.5vw, 1.5rem);
        --h2-size: clamp(0.9rem, 2.5vw, 1.25rem);
        --body-size: clamp(0.65rem, 1vw, 0.85rem);
    }
}
@media (max-width: 700px) {
    .cards { grid-template-columns: 1fr 1fr; }
    /* The corner mark used to be hidden outright below this width. On a
       Hei-branded deck that leaves every content slide unbranded on a
       phone, which is the wrong saving. It is shrunk in the mobile block
       below instead. Type scale and column collapse also live there, so
       they are not repeated here. */
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.2s !important;
    }
    .bar-fill, .funnel-bar { transform: scaleX(1) !important; }
}

/* ============================================================
   PRINT / PDF EXPORT
   Reached via ?print-pdf, or Cmd+P with background graphics on.
   ============================================================ */

/* ============================================================
   SOURCE FIGURES — the 22 slide photographs
   The charts in this deck are redrawn. These are the original
   projected slides, photographed at the event: evidence, not
   presentation graphics. They are deliberately kept out of the
   main flow and reachable on demand.
   ============================================================ */

/* Small chip on a redrawn chart slide: "FIG 4" opens the photo. */
.fig-ref {
    display: inline-flex;
    align-items: center;
    gap: 0.45em;
    padding: 0.28em 0.7em;
    border: 1px solid rgba(85, 255, 231, 0.32);
    border-radius: 2em;
    background: rgba(85, 255, 231, 0.06);
    color: var(--accent);
    font-size: var(--micro-size);
    font-weight: 500;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    cursor: pointer;
    transition: background 0.18s ease, border-color 0.18s ease;
    -webkit-appearance: none;
    appearance: none;
}
.fig-ref:hover,
.fig-ref:focus-visible {
    background: rgba(85, 255, 231, 0.16);
    border-color: rgba(85, 255, 231, 0.7);
    outline: none;
}
.fig-ref::before {
    content: '';
    width: 0.62em;
    height: 0.62em;
    border: 1px solid currentColor;
    border-radius: 1px;
    flex: none;
}
.fig-row { display: flex; flex-wrap: wrap; gap: 0.5em; align-items: center; }

/* ---- Appendix contact sheet ---- */
.gallery {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: clamp(0.5rem, 1.1vw, 0.9rem);
    min-height: 0;
    /* Rows share whatever height is left rather than each taking their
       natural height — without this the third row pushed past the slide
       and the caption printed on top of the images. */
    grid-auto-rows: minmax(0, 1fr);
    align-content: start;
}
.gallery.six { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.gfig {
    display: flex;
    flex-direction: column;
    gap: 0.4em;
    cursor: pointer;
    border: none;
    padding: 0;
    background: none;
    text-align: left;
    -webkit-appearance: none;
    appearance: none;
}
.gfig img {
    width: 100%;
    min-height: 0;
    flex: 1;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    display: block;
    border: 1px solid var(--rule);
    background: #111;
    transition: border-color 0.18s ease, transform 0.18s ease;
}
.gfig:hover img,
.gfig:focus-visible img {
    border-color: var(--accent);
    transform: scale(1.015);
    outline: none;
}
.gfig .gcap {
    font-size: clamp(0.5rem, 0.82vw, 0.68rem);
    line-height: 1.35;
    color: var(--text-muted);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.gfig .gnum {
    color: var(--accent);
    font-weight: 500;
    letter-spacing: 0.06em;
}

/* ---- Lightbox ---- */
.lightbox {
    /* Outside .reveal, so nothing inherits the deck typeface — set it
       here or captions fall back to the browser serif. */
    font-family: var(--font-display);
    position: fixed;
    inset: 0;
    z-index: 300;
    display: none;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    gap: 1rem;
    padding: clamp(1.5rem, 4vw, 3.5rem);
    background: rgba(0, 0, 0, 0.94);
    backdrop-filter: blur(6px);
}
.lightbox[data-open="true"] { display: flex; }
.lightbox img {
    max-width: 100%;
    max-height: 78vh;
    object-fit: contain;
    border: 1px solid var(--rule);
}
.lightbox figcaption {
    max-width: 90ch;
    text-align: center;
    font-size: var(--small-size);
    color: var(--text-secondary);
    line-height: 1.5;
}
.lightbox figcaption b { color: var(--accent); font-weight: 500; letter-spacing: 0.06em; }
.lb-close {
    position: absolute;
    top: clamp(1rem, 2.5vw, 2rem);
    right: clamp(1rem, 2.5vw, 2rem);
    width: 2.4rem;
    height: 2.4rem;
    border-radius: 50%;
    border: 1px solid var(--rule);
    background: rgba(0,0,0,0.6);
    color: var(--text-primary);
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
}
.lb-close:hover { border-color: var(--accent); color: var(--accent); }
.lb-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 3rem;
    height: 3rem;
    border: none;
    background: none;
    color: var(--accent);
    font-size: 2rem;
    cursor: pointer;
    opacity: 0.55;
}
.lb-nav:hover { opacity: 1; }
.lb-prev { left: 0.6rem; }
.lb-next { right: 0.6rem; }


/* ============================================================
   ASSISTANT — persistent across slide changes
   Lives OUTSIDE .reveal in the DOM so reveal.js never unmounts
   or transforms it. Grounded strictly in this report; the model
   call happens server-side (see api/ask.js), never here.
   ============================================================ */
.asst-launch {
    position: fixed;
    right: clamp(1rem, 2vw, 1.75rem);
    bottom: clamp(1rem, 2vw, 1.75rem);
    z-index: 200;
    display: flex;
    align-items: center;
    gap: 0.55em;
    padding: 0.7em 1.05em;
    border-radius: 2em;
    border: 1px solid rgba(85, 255, 231, 0.38);
    background: rgba(6, 10, 12, 0.88);
    backdrop-filter: blur(10px);
    color: var(--accent);
    font-family: var(--font-display);
    font-size: clamp(0.62rem, 1.05vw, 0.78rem);
    font-weight: 500;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    cursor: pointer;
    transition: border-color 0.18s ease, background 0.18s ease;
}
.asst-launch:hover { border-color: var(--accent); background: rgba(85,255,231,0.10); }
.asst-launch .dot {
    width: 0.5em; height: 0.5em; border-radius: 50%;
    background: var(--accent); flex: none;
}
.asst-open .asst-launch { display: none; }

.asst {
    font-family: var(--font-display);
    position: fixed;
    right: clamp(1rem, 2vw, 1.75rem);
    bottom: clamp(1rem, 2vw, 1.75rem);
    z-index: 201;
    width: min(430px, calc(100vw - 2rem));
    max-height: min(620px, calc(100vh - 3rem));
    display: none;
    flex-direction: column;
    border: 1px solid var(--rule);
    border-radius: 14px;
    overflow: hidden;
    background: rgba(6, 9, 11, 0.96);
    backdrop-filter: blur(16px);
    box-shadow: 0 24px 70px rgba(0,0,0,0.7);
}
.asst-open .asst { display: flex; }

.asst-head {
    display: flex;
    align-items: center;
    gap: 0.6em;
    padding: 0.85rem 1rem;
    border-bottom: 1px solid var(--rule);
    flex: none;
}
.asst-head .ttl {
    font-size: 0.72rem;
    font-weight: 500;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--text-primary);
}
.asst-head .sub {
    font-size: 0.62rem;
    color: var(--text-muted);
    letter-spacing: 0.03em;
}
.asst-head .spacer { margin-left: auto; }
.asst-x {
    border: none; background: none; color: var(--text-muted);
    font-size: 1.15rem; line-height: 1; cursor: pointer; padding: 0 0.2em;
}
.asst-x:hover { color: var(--accent); }

.asst-log {
    flex: 1;
    overflow-y: auto;
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    min-height: 0;
}
.asst-msg { font-size: 0.8rem; line-height: 1.55; }
.asst-msg.u { color: var(--text-primary); }
.asst-msg.u .who { color: var(--text-muted); }
.asst-msg.a { color: var(--text-secondary); }
.asst-msg .who {
    display: block;
    font-size: 0.58rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    margin-bottom: 0.3em;
    color: var(--accent);
}
.asst-msg.err { color: #ff8f8f; }
.asst-msg p { margin: 0 0 0.55em; }
.asst-msg p:last-child { margin-bottom: 0; }
.asst-msg .cite {
    display: inline-block;
    margin-top: 0.3em;
    font-size: 0.6rem;
    letter-spacing: 0.05em;
    color: var(--text-muted);
}

.asst-seed { display: flex; flex-wrap: wrap; gap: 0.4em; }
.asst-seed button {
    font-size: 0.64rem;
    line-height: 1.3;
    text-align: left;
    padding: 0.45em 0.7em;
    border-radius: 1.2em;
    border: 1px solid var(--rule);
    background: none;
    color: var(--text-secondary);
    cursor: pointer;
}
.asst-seed button:hover { border-color: var(--accent); color: var(--accent); }

.asst-form {
    flex: none;
    display: flex;
    gap: 0.5rem;
    padding: 0.75rem;
    border-top: 1px solid var(--rule);
    align-items: flex-end;
}
.asst-form textarea {
    flex: 1;
    resize: none;
    background: rgba(255,255,255,0.04);
    border: 1px solid var(--rule);
    border-radius: 8px;
    color: var(--text-primary);
    font-family: var(--font-display);
    font-size: 0.78rem;
    line-height: 1.4;
    padding: 0.6em 0.7em;
    max-height: 5.5em;
    min-height: 2.4em;
}
.asst-form textarea:focus { outline: none; border-color: var(--accent); }
.asst-form textarea::placeholder { color: var(--text-muted); }
.asst-send {
    flex: none;
    border: 1px solid rgba(85,255,231,0.4);
    background: rgba(85,255,231,0.10);
    color: var(--accent);
    border-radius: 8px;
    padding: 0.62em 0.85em;
    font-size: 0.72rem;
    font-weight: 500;
    cursor: pointer;
}
.asst-send:disabled { opacity: 0.4; cursor: default; }
.asst-send:not(:disabled):hover { background: rgba(85,255,231,0.2); }

.asst-foot {
    flex: none;
    padding: 0 1rem 0.7rem;
    font-size: 0.56rem;
    line-height: 1.45;
    color: var(--text-muted);
    letter-spacing: 0.02em;
}

/* Typing indicator */
.asst-typing { display: inline-flex; gap: 0.25em; }
.asst-typing i {
    width: 0.32em; height: 0.32em; border-radius: 50%;
    background: var(--accent); opacity: 0.5;
    animation: asstblink 1.1s infinite ease-in-out;
}
.asst-typing i:nth-child(2) { animation-delay: 0.16s; }
.asst-typing i:nth-child(3) { animation-delay: 0.32s; }
@keyframes asstblink { 0%,80%,100% { opacity: 0.25; } 40% { opacity: 1; } }

/* Passcode step, shown inside the assistant on first use.
   This authenticates the ASSISTANT only, and it is checked on the
   server. It is not a content lock: the deck HTML is served
   statically, so anyone with the URL can read the slides. For real
   access control put Vercel Deployment Protection in front of the
   whole deployment. */
.asst-auth { display: flex; flex-direction: column; gap: 0.6rem; }
.asst-auth p { font-size: 0.72rem; color: var(--text-muted); line-height: 1.5; margin: 0; }
.asst-auth form { display: flex; gap: 0.45rem; }
.asst-auth input {
    flex: 1;
    background: rgba(255,255,255,0.05);
    border: 1px solid var(--rule);
    border-radius: 8px;
    color: var(--text-primary);
    font-family: var(--font-display);
    font-size: 0.78rem;
    padding: 0.6em 0.7em;
}
.asst-auth input:focus { outline: none; border-color: var(--accent); }
.asst-auth .err { color: #ff8f8f; font-size: 0.68rem; min-height: 1em; }

@media (max-width: 620px) {
    .asst { width: calc(100vw - 1.4rem); right: 0.7rem; bottom: 0.7rem; max-height: 72vh; }
}
@media (prefers-reduced-motion: reduce) {
    .asst-typing i { animation: none; }
}


/* ============================================================
   LIGHT SLIDES — the second half of the Hei ground rule
   Black, blue and grey on white. Used at the natural pauses (the
   "at a glance" summaries, the framing slide, the disputed-numbers
   warning) so the deck is not 31 slides of the same black.

   The palette shifts because it has to, not for variety:
     cyan  #55FFE7  1.25:1 on white — unusable, graphic only
     blue  #0000FF  8.6:1  on white — carries every accent here
     grey  #555555  7.5:1  on white — secondary text, passes AA
   ============================================================ */
.reveal .slides section.light {
    --text-primary: #000000;
    --text-secondary: #555555;
    --text-muted: rgba(0, 0, 0, 0.55);
    --accent: #0000FF;
    --rule: rgba(0, 0, 0, 0.14);
    color: #000000;
    background-color: #FFFFFF;
}
/* Must match the (0,2,2) specificity of the base ::before rule. */
.reveal .slides section.light::before {
    background-image: none;
    background-color: #FFFFFF;
}
.reveal .slides section.light .fig-ref {
    border-color: rgba(0, 0, 255, 0.35);
    background: rgba(0, 0, 255, 0.05);
}
.reveal .slides section.light .fig-ref:hover,
.reveal .slides section.light .fig-ref:focus-visible {
    background: rgba(0, 0, 255, 0.12);
    border-color: #0000FF;
}
.reveal .slides section.light .bar-track { background: rgba(0, 0, 0, 0.07); }
.reveal .slides section.light .bar-fill { background: #0000FF; }

/* reveal's chrome lives outside the section, so the section's variable
   overrides cannot reach it. A body class set on slidechanged does. */
body.on-light .reveal .controls { color: #0000FF; }
body.on-light .reveal .progress { color: #0000FF; background: rgba(0, 0, 0, 0.10); }
body.on-light .reveal .slide-number { color: rgba(0, 0, 0, 0.45); }

/* ============================================================
   ILLUSTRATED DIVIDERS
   Section breaks only. The illustration is set per slide with
   --illus, and scrimmed bottom-left where the heading lands.
   ============================================================ */
.reveal .slides section.illus::before {
    background-image:
        /* Top band: headings sit here and an illustration's bright area
           cannot be predicted, so guarantee them a dark backing. */
        linear-gradient(to bottom,
            rgba(0,0,0,0.80) 0%,
            rgba(0,0,0,0.55) 16%,
            rgba(0,0,0,0.10) 34%,
            rgba(0,0,0,0.00) 46%),
        /* Diagonal: the credits block sits bottom-left. */
        linear-gradient(to top right,
            rgba(0,0,0,0.92) 0%,
            rgba(0,0,0,0.80) 20%,
            rgba(0,0,0,0.42) 46%,
            rgba(0,0,0,0.12) 72%,
            rgba(0,0,0,0.00) 100%),
        var(--illus);
    background-size: cover, cover, cover;
    background-position: center, center, center;
    background-repeat: no-repeat, no-repeat, no-repeat;
}


/* ============================================================
   HEI TITLE CASE — headings are set in capitals
   Applied with text-transform rather than by rewriting the copy, so
   the underlying sentence case survives for screen readers, for the
   assistant's corpus, and for anyone editing the source.
   Large caps need looser tracking than the tight negative values the
   mixed-case scale uses, or the letters lock together.
   ============================================================ */
.reveal h1,
.reveal h2,
.reveal h3 {
    text-transform: uppercase;
}
.reveal h1 { letter-spacing: -0.012em; }
.reveal h2 { letter-spacing: -0.005em; }
.reveal h3 { letter-spacing: 0.005em; }

/* ============================================================
   ASSISTANT LAUNCHER — clear of reveal's arrow cluster
   reveal puts four 36px arrows in the bottom-right, spanning roughly
   110x130px. The launcher sat on top of them, swallowing the back and
   forward clicks. Shift it inboard on desktop; on small screens the
   arrows are hidden entirely (swipe replaces them) so it returns to
   the corner.
   ============================================================ */
.asst-launch { right: calc(clamp(1rem, 2vw, 1.75rem) + 7.25rem); }

.asst-launch .bubble {
    width: 1.05em;
    height: 1.05em;
    flex: none;
    display: block;
}

/* ============================================================
   MOBILE
   The deck is dense. On a phone the honest trade is smaller type,
   single-column layouts, and letting the few genuinely tall slides
   scroll inside themselves rather than clipping their last line.
   ============================================================ */
@media (max-width: 900px) {
    /* Swipe and tap replace the arrows; they only steal space here. */
    .reveal .controls { display: none !important; }
    .asst-launch { right: clamp(0.75rem, 2vw, 1.75rem); }
}

@media (max-width: 768px) {
    :root {
        --slide-padding: clamp(0.9rem, 3.5vw, 1.6rem);
        --title-size: clamp(1.5rem, 8.5vw, 2.6rem);
        --h2-size:    clamp(1.1rem, 5.6vw, 1.7rem);
        --h3-size:    clamp(0.95rem, 4.4vw, 1.3rem);
        --content-gap: clamp(0.6rem, 2.5vw, 1.1rem);
        --element-gap: clamp(0.4rem, 1.8vw, 0.8rem);
    }

    /* Two columns cannot hold a sentence at this width. */
    .split,
    .split.even,
    .split.narrow-left { grid-template-columns: minmax(0, 1fr); gap: var(--element-gap); }

    .gallery, .gallery.six { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .gfig .gcap { -webkit-line-clamp: 3; }

    /* Bar rows: give the label less, the track more. */
    .bar-row { gap: 0.5em; }
    .bar-row .name { font-size: 0.72em; }
    .bar-row .val small { display: block; }

    .logo-corner { width: 58px; }

    /* Last resort for the few slides that still will not fit. The
       gallery carries data-prevent-swipe so a vertical drag scrolls it
       instead of being eaten by reveal's touch navigation. */
    .slide-content { overflow-y: auto; -webkit-overflow-scrolling: touch; }
    .gallery { overflow-y: auto; }

    .asst { width: calc(100vw - 1.2rem); right: 0.6rem; bottom: 0.6rem; max-height: 78vh; }
    .asst-launch { font-size: 0.6rem; padding: 0.6em 0.85em; }

    .lightbox { padding: 0.8rem; }
    .lightbox img { max-height: 62vh; }
    .lb-nav { width: 2.2rem; font-size: 1.5rem; }
    .lightbox figcaption { font-size: 0.66rem; }
}

@media (max-width: 480px) {
    .gallery, .gallery.six { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .asst-launch .label { display: none; }
    .asst-launch { padding: 0.7em; border-radius: 50%; }
}

/* Landscape phones: height is the scarce dimension, not width. */
@media (max-height: 430px) and (orientation: landscape) {
    :root {
        --slide-padding: clamp(0.6rem, 2.5vh, 1.1rem);
        --title-size: clamp(1.1rem, 6.5vh, 1.9rem);
        --h2-size:    clamp(0.95rem, 5vh, 1.4rem);
        --content-gap: clamp(0.4rem, 1.6vh, 0.8rem);
    }
    .logo-corner { width: 48px; }
    /* Not an overflow fix: hero slides fit here already. A 150px logo
       simply eats a third of a 390px-tall screen, so it comes down. */
    .logo { width: clamp(58px, 13vh, 96px); }
    .hero .caption { font-size: 0.58rem; line-height: 1.35; }
}

@media print {
    @page { size: landscape; margin: 0; }
    * {
        -webkit-print-color-adjust: exact !important;
        print-color-adjust: exact !important;
    }
    html, body { height: auto !important; overflow: visible !important; background: #000 !important; }
    .reveal .slides section {
        position: relative !important;
        opacity: 1 !important;
        pointer-events: auto !important;
        z-index: auto !important;
        transform: none !important;
        page-break-after: always;
        break-after: page;
    }
    .reveal .slides section:last-child { page-break-after: auto; }
    .anim-up, .anim-scale, .anim-left, .anim-right, .anim-blur {
        opacity: 1 !important;
        transform: none !important;
        filter: none !important;
        transition: none !important;
    }
    /* Bars are drawn with scaleX and would print empty otherwise */
    .bar-fill, .funnel-bar { transform: scaleX(1) !important; transition: none !important; }
    *, *::before, *::after { animation: none !important; transition: none !important; }
    .reveal .controls, .reveal .progress, .reveal .slide-number { display: none !important; }
    /* Interactive chrome has no meaning in a PDF. */
    .asst, .asst-launch, .lightbox, .fig-row { display: none !important; }
    /* Photographs must survive the export, so keep the gallery. */
    .gallery { break-inside: avoid; }
}
