/* ─── FONTS ─── */

/* JetBrains Mono, SIL Open Font License 1.1. One variable file per subset covers
   every weight the page uses (100–800); unicode-range means latin-ext is only
   fetched by visitors whose text actually needs it. Replaces FT System Mono
   Trial, whose trial build carried 66 glyphs — no accents, no punctuation — so
   every ç, ã, ê, –, :, &, © and @ on the page fell back to another typeface. */
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url('fonts/jetbrains-mono-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url('fonts/jetbrains-mono-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}


/* ─── VARIABLES ─── */

:root {
  --green:       #70e870;
  --orange:      #f59e0b;
  /* blue-500, not blue-600: #2563eb only reached 3.76:1 on --bg, which fails
     AA for the 11-13px labels that carry it (.day-num, .session-time,
     .pillar--academic h3). #3b82f6 is 5.28:1 at the same hue. */
  --blue:        #3b82f6;

  --bg:     #0d0d0d;
  --text:   #f0efed;
  --muted:  rgba(240, 239, 237, 0.52);  /* 5.12:1 on --bg; 0.45 was 4.09:1 and failed AA */
  --border: rgba(240, 239, 237, 0.1);

  --gap: 40px;
  --max-width: 1280px;

  --tail: clamp(88px, 13vw, 176px);  /* where the honeycomb field resolves, below the hero */
}


/* ─── RESET ─── */

*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
}

body {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  background-color: var(--bg);
  color: var(--text);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

img {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
}

/* Keyboard focus. The sheet defined none, so the UA ring was the only thing
   showing and it disappears against --bg on the filled buttons. */
.skip-link {
  position: absolute;
  left: var(--gap);
  top: -100%;
  z-index: 100;
  padding: 12px 20px;
  background: var(--green);
  color: var(--bg);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
}

.skip-link:focus {
  top: 8px;
}

:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 3px;
  border-radius: 1px;
}

.btn-primary:focus-visible,
.nav-cta:focus-visible,
.btn:focus-visible {
  outline-color: var(--text);
}

.container {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 var(--gap);
}

.section-label {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 48px;
}


/* ─── NAV ─── */

nav {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px var(--gap);
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}

/* the sticky bar would otherwise sit on top of whatever section you jump to */
section[id],
#showcase {
  scroll-margin-top: var(--nav-h, 72px);
}

/* hamburger — only ever shown at the mobile breakpoint */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  margin-left: -11px; /* optical: keeps the bars on the --gap text edge */
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  flex-shrink: 0;
}

.nav-toggle-bar {
  width: 22px;
  height: 1.5px;
  background: var(--text);
  transition: transform .25s ease, opacity .2s ease;
}

.nav-open .nav-toggle-bar:nth-child(1) {
  transform: translateY(6.5px) rotate(45deg);
}

.nav-open .nav-toggle-bar:nth-child(2) {
  opacity: 0;
}

.nav-open .nav-toggle-bar:nth-child(3) {
  transform: translateY(-6.5px) rotate(-45deg);
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 32px;
}

.nav-links a {
  /* 18px of text in a 24px target (WCAG 2.5.8); the bar is centred so the
     extra padding does not move anything. */
  padding: 3px 0;
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--muted);
  transition: color 0.2s;
}

.nav-links a:hover {
  color: var(--text);
}

.nav-cta {
  display: inline-flex;
  align-items: center;
  min-height: 44px;  /* was 38px: under the touch-target minimum */
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--bg);
  background: var(--green);
  padding: 10px 20px;
  transition: opacity 0.2s;
  white-space: nowrap;
}

.nav-cta:hover {
  opacity: 0.8;
}


/* ─── HERO ─── */

#hero {
  position: relative;
  z-index: 1;   /* above .hex-field, which is the .hero-stage backdrop */
  display: flex;
  flex-direction: column;
  min-height: calc(100svh - var(--nav-h, 72px)); /* nav height set precisely by JS */
  /* no border-bottom: the field continues past the hero into the tail band and
     a rule there would cut it in half */
}

.hero-split {
  flex: 1;
  position: relative;
  display: grid;
  grid-template-columns: 52% 1fr;
  grid-template-rows: 1fr auto;
  grid-template-areas:
    "content ."
    "colocated .";
  min-height: 0;
}

/* The blue wash and the 56px pin grid that used to live here are gone: the
   honeycomb field brings its own lattice and its own two washes, and stacking
   both read as two competing grids. */

/* ── Left content column ── */

.hero-content {
  grid-area: content;
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 48px var(--gap);
  gap: 24px;
}

.hero-eyebrow {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Separators sit at --muted like the labels they divide. Dimming one or two
   punctuation marks below the AA floor buys nothing; they are hidden from
   assistive tech in the markup instead. */
.meta-divider,
.cred-sep {
  opacity: 1;
}

.hero-title {
  font-size: clamp(48px, 7.5vw, 100px);
  font-weight: 800;
  line-height: 0.88;
  letter-spacing: -0.025em;
  color: var(--text);
}

.hero-subtitle {
  font-size: clamp(12px, 1vw, 15px);
  font-weight: 300;
  color: var(--muted);
  line-height: 1.65;
  max-width: 380px;
}

.hero-actions {
  display: flex;
  gap: 12px;
  align-items: center;
}

.btn-primary {
  display: inline-block;
  padding: 14px 28px;
  background: var(--green);
  color: #0d0d0d;
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  transition: opacity 0.2s;
  white-space: nowrap;
}

.btn-primary:hover {
  opacity: 0.8;
}

.btn-secondary {
  display: inline-block;
  padding: 13px 28px;
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 400;
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  transition: border-color 0.2s;
  white-space: nowrap;
}

.btn-secondary:hover {
  border-color: var(--text);
}

.hero-colocated {
  grid-area: colocated;
  position: relative;
  z-index: 2;
  padding: 20px var(--gap);
  border-top: 1px solid var(--border);
}

.hero-colocated-label {
  display: block;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 14px;
}

.logos--hero {
  display: flex;
  gap: 20px;
  align-items: center;
  flex-wrap: wrap;
}

.logos--hero img {
  height: 26px;
  width: auto;
  max-width: none;
  filter: brightness(0) invert(1);
  opacity: 0.45;
}

/* ─── THE HONEYCOMB FIELD ───
   One continuous field behind the hero and the tail below it, rather than a
   strip glued under the hero. Three streams — one per pillar colour, the same
   three the original prototype used without anyone naming them — enter from the
   right of the hero at three heights and braid together at the bottom into a
   full-width band. That is the page's own title, drawn.

   The geometry is computed, not nudged. The lattice is a single 60x104 SVG tile
   anchored bottom-centre; with its hexagon at (30,35) and the neighbours at
   (0,87) and (60,87), hexagon centres land exactly at

       row a:  x = 60*col       y = 69 + 104*row     (y measured up from the
       row b:  x = 60*col + 30  y = 17 + 104*row      field's bottom edge)

   and the cells sit on those points, written as multiples of --u so one media
   query rescales the whole composition without anything drifting off the
   lattice. The previous pass had 5 of 14 cells half a column out of phase,
   which is exactly why they read as blobs floating over a grid instead of
   cells inside it.

   68 cells — 38 in the flow, 30 in the band, of which 12 survive below 900px —
   against the 403 at 1440x900 (and ~980 at 2560x1440) that the original JS
   generated every session. Each cell paints as two pseudo-element layers, the
   hexagon plus its bloom, so 136 elements animate; all of them animate opacity
   only, on content painted once, so the per-frame cost is 136 quad draws and no
   paint at all. */

.hero-stage {
  position: relative;
  isolation: isolate;
  padding-bottom: var(--tail);
}

.hex-field {
  --u: 60px;                  /* lattice column pitch; everything derives from it */
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;

  /* The cells are aria-hidden but were still the topmost hit target in the tail
     band and the side margins, so the browser was running 68 clip-path polygon
     tests on every pointer move and a text-selection drag caught them. One
     declaration covers the lattice, its glow, all 68 cells, both pseudo-layers
     each, and the dodge spot. It is also required by the spot, which sits above
     the whole field. */
  pointer-events: none;

  /* Paint containment lets the browser cull the 136-layer subtree once the field
     scrolls away. Never `size`: the box is inset:0 and takes its size from the
     containing block, so contain:size would collapse it to nothing. */
  contain: layout paint;

  /* Required by the color-dodge spot below, not decoration: inside an isolated
     group the initial backdrop is transparent black, and blending against
     alpha 0 degrades to plain source-over — a visible grey disc floating over
     the field. A visual no-op otherwise, because #hero sits above this with
     z-index 1 and nothing but body's --bg paints behind it. */
  background-color: var(--bg);
  isolation: isolate;

  /* Two very low-alpha washes at the convergence, for depth. Both sit on the
     right and bottom, away from the text, and neither exceeds 0.10. */
  background-image:
    radial-gradient(42% 30% at 58% 93%, rgba(237, 163, 44, 0.10), transparent 72%),
    radial-gradient(38% 46% at 82% 46%, rgba(52, 146, 234, 0.08), transparent 74%);
  /* Soft at both ends: the field emerges and dissolves instead of being cut by
     two hard horizontal edges, which is what made the old band read as a
     rectangle dropped onto the page. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 10%, #000 93%, transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0, #000 10%, #000 93%, transparent 100%);
}

.hex-lattice {
  position: absolute;
  inset: 0;                   /* same box as .hex-field, so the phase matches */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='104' viewBox='0 0 60 104'%3E%3Cg fill='none' stroke='%23f0efed' stroke-opacity='0.13' stroke-width='1'%3E%3Cpath d='M30 1 L59 18 L59 52 L30 69 L1 52 L1 18 Z'/%3E%3Cpath d='M0 53 L29 70 L29 104 M60 53 L31 70 L31 104'/%3E%3C/g%3E%3C/svg%3E");
  background-size: var(--u) calc(var(--u) * 26 / 15);
  background-position: 50% 100%;
  /* A soft hole over the text column, so the grid stays texture behind the type
     instead of a pattern competing with it. Sized to the column, not guessed:
     the ellipse clears the h1 at ~30% strength and still leaves the lattice at
     ~67% under the band at bottom-left, where the cells need something to sit
     in. Only the lattice is masked — the coloured cells are siblings, so the
     flow's brightness is set by placement alone. */
  -webkit-mask-image: radial-gradient(58% 55% at 22% 55%, transparent 0, rgba(0, 0, 0, 0.4) 65%, #000 100%);
          mask-image: radial-gradient(58% 55% at 22% 55%, transparent 0, rgba(0, 0, 0, 0.4) 65%, #000 100%);
}

/* ── the pointer light on the lattice ──
   The grid lines brighten where the cursor is: a honeycomb that answers to your
   hand, which is the most on-brand interaction this page has.

   Nested inside .hex-lattice rather than composed with its mask. A mask applies
   to the element AND its subtree, so the hole over the text column attenuates
   this automatically — dragging the cursor across the headline cannot re-light
   the grid behind the type, the 900px mask swap governs this too, and the
   declaration above is not touched. Composing instead would have meant
   mask-composite, whose unprefixed and -webkit- forms use different value sets.

   Same tile, same viewBox, same background-size and background-position, only
   stroke-opacity 0.13 -> 0.34. It reads as the same line getting brighter, not
   as a second line appearing.

   The spot centre lives inside the gradient's `at`, where a percentage is
   simply that fraction across the box and a pixel value is a plain offset — so
   the CSS rest (62% 34%) and the pixels JS writes share one declaration. The
   same expression written as mask-position would NOT work: there a percentage
   resolves against (box - mask), so calc(62% - 162px) would silently mean
   something else. */
.hex-lattice::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='104' viewBox='0 0 60 104'%3E%3Cg fill='none' stroke='%23f0efed' stroke-opacity='0.34' stroke-width='1'%3E%3Cpath d='M30 1 L59 18 L59 52 L30 69 L1 52 L1 18 Z'/%3E%3Cpath d='M0 53 L29 70 L29 104 M60 53 L31 70 L31 104'/%3E%3C/g%3E%3C/svg%3E");
  background-size: var(--u) calc(var(--u) * 26 / 15);
  background-position: 50% 100%;
  -webkit-mask-image: radial-gradient(circle calc(var(--u) * 5.4) at var(--pfx, 50%) var(--pfy, calc(42vh - var(--nav-h))), #000 0, rgba(0, 0, 0, 0.55) 34%, transparent 100%);
          mask-image: radial-gradient(circle calc(var(--u) * 5.4) at var(--pfx, 50%) var(--pfy, calc(42vh - var(--nav-h))), #000 0, rgba(0, 0, 0, 0.55) 34%, transparent 100%);
}

/* ── the pointer light on the cells ──
   One blended layer over the whole field, rather than distance maths per cell:
   writing --o-mul on 68 cells per frame would force the keyframe values to be
   recomputed on the main thread and destroy the compositor-only property that
   makes the flares free.

   color-dodge, not screen or plus-lighter. Those are additive and would take
   --bg from level 13 to 37 (+185%) while taking a lit cell from 61 to 80
   (+31%) — a wash, not a light. Dodge is multiplicative (Cb / (1 - Cs)), so it
   scales what is already there: at rgb(92,90,86) the gain is x1.56, taking the
   background 13 -> 20, a resting band cell 47 -> 74, a lit one 110 -> 172, and
   a lattice line 120 -> 188.

   The falloff is in the COLOUR, not the alpha. Dodge strength comes from the
   source colour; an alpha falloff on a near-white source blows out almost like
   screen. So the layer is opaque everywhere and its colour falls to pure black
   — gain x1.00, nothing to see — before its own edge.

   left/top rather than translate: a percentage in left/top resolves against the
   containing block, which is what makes the 62%/34% rest and the pixels JS
   writes mean the same thing. A percentage in translate would resolve against
   this element's own 540px instead. */
.hex-field::after {
  content: "";
  position: absolute;
  /* Same rest as the lattice glow and the surface lamp: 50vw across, and 42vh
     down minus the nav, because the field's box starts below the header. One
     lamp at rest rather than two centres a few hundred pixels apart. */
  left: var(--pfx, 50%);
  top:  var(--pfy, calc(42vh - var(--nav-h)));
  width:  calc(var(--u) * 9);
  height: calc(var(--u) * 9);
  translate: -50% -50%;
  background: radial-gradient(circle closest-side,
    rgb(92, 90, 86)   0,
    rgb(44, 43, 41)  38%,
    rgb(13, 13, 12)  66%,
    rgb(0,  0,  0)  100%);
  mix-blend-mode: color-dodge;
}

.hex {
  position: absolute;
  width:  calc(var(--u) * 29 / 30);   /* 58/60 */
  height: calc(var(--u) * 17 / 15);   /* 68/60 */
  translate: -50% 50%;                /* centre the cell on (left, bottom) */

  /* Nothing animates on .hex itself, and that is the point. opacity on .hex
     composites its whole subtree, so a bloom layer could never be independent
     of the flare; moving the flare onto two pseudo-elements makes them
     independent by construction. clip-path went with the solid hexagon onto
     ::after, which leaves ::before free to spill past the silhouette — which is
     also why there is no filter here: box-shadow is part of the element's own
     rendering and would be clipped away by the polygon, and 68 elements
     interpolating a drop-shadow blur leaves the compositor fast path entirely. */

  /* Two derived levels. --o-mul is the per-breakpoint dimmer, and it has to be
     folded in here rather than applied as its own rule: an animation's opacity
     beats a plain declaration, so a separate dimming rule is silently ignored. */
  --hex-lit: calc(var(--o, 0.3) * var(--o-mul, 1));
  --hex-dim: calc(var(--o, 0.3) * var(--o-mul, 1) * var(--dim-k, 0.28));
}

/* ── the palette ──
   Read off the reference photograph of three lit glass cubes. These are NOT the
   --academia/--industry/--showcase text tokens, and deliberately so: those were
   desaturated to clear 6.7:1 as TEXT, and a decorative cell carries no words, so
   the contrast budget that shaped them does not apply here. Saturation roughly
   doubles; the hue is the photograph's.

       academia  hsl(209 81% 56%)     industry  hsl(37 84% 55%)
       showcase  hsl(138 71% 46%)

   Luminance spread is 1.630, against 1.868 for the colours exactly as read and
   1.007 if forced into one band. Forcing parity was tried and rejected: a
   saturated blue at 52% lightness only reaches Y=0.24 because blue contributes
   0.0722 to luminance, so matching pushes it to a pale sky blue that stops
   looking like the reference at all. The blue is lifted four points of lightness
   from the raw read — just enough that the band's blue links do not read as
   holes — and the remaining imbalance is left alone, because in the photograph
   the blue cube IS the deeper one, and that is part of why it reads as glass
   rather than as a flat swatch.

   Cells carry a pillar class rather than an inline colour, so retuning the
   palette is three lines here instead of regenerating 68 spans. */
.hex--aca { --c:  52,146,234; --core: 231,237,243; }
.hex--ind { --c: 237,163, 44; --core: 243,239,231; }
.hex--sho { --c:  34,201, 84; --core: 232,242,235; }

/* Resting floor, per zone. The band IS the composition — it has to read as a
   full-width band between flares, and "between flares" is the resting state —
   so its floor barely moves: 0.38 instead of 0.40 takes the mean band cell from
   1.221:1 to 1.205:1 against --bg, a 1% change nobody can see. The flow is
   meant to read as sparks on a lattice, so its floor drops to 0.18 and its
   on/off ratio goes from 2.5:1 to 5.6:1 — a resting flow cell ends up dimmer
   than the lattice line it sits in, which is what reads unambiguously as a
   light that is currently off. */
.hex--band { --dim-k: 0.38; }
.hex--flow { --dim-k: 0.18; }

.hex::before,
.hex::after {
  content: "";
  position: absolute;
  opacity: var(--hex-dim);
  animation-name: hex-g1;
  animation-duration: var(--p, 11s);
  animation-timing-function: linear;  /* every ramp overrides this from inside
                                         the keyframes; linear here means a
                                         missed override is obvious, not subtle */
  animation-iteration-count: infinite;
  /* Negative, so every cell is already mid-cycle on the first frame. With a
     positive delay the field arrives uniformly dim and only reaches its steady
     mixed state after the longest delay (17.22s: DELAY_SPAN is 18s but the 23
     slots top out at 22/23 of it) — the first impression, which is the one that
     counts, would be the dim one. Both layers of a cell share it, so they stay
     in lockstep. */
  animation-delay: calc(-1 * var(--d, 0s));
}

/* the cell — lit from within, not a flat swatch.
   The reference photograph's defining feature is a near-white core with the hue
   living in the glass around it, so the cell is a radial gradient rather than a
   fill: --core in the middle third, --c from 60% out. The core is the hue at 93%
   lightness and 42% of its saturation, not pure white, so a blue cell still
   reads blue at its centre.

   This costs nothing at rest — the whole layer is at --hex-dim, so an unlit cell
   is dark coloured glass — and it is what makes a flare read as a light coming on
   rather than a rectangle changing opacity. */
.hex::after {
  inset: 0;
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  background: radial-gradient(circle closest-side,
    rgb(var(--core)) 0%,
    rgb(var(--core)) 16%,
    rgb(var(--c))   60%,
    rgb(var(--c))  100%);
}

/* the bloom: the same flare, unclipped, so lighting up is a glow and not an
   opacity step. It reuses the cell's keyframes verbatim — they read
   --hex-dim/--hex-lit and both are overridden here, so the identical three
   patterns drive 0 -> peak on this layer and dim -> lit on the one above. No
   extra keyframes, no extra DOM nodes, no filter, and both layers animate
   nothing but opacity, which is why 136 of them is cheaper than 68 blurs.

   Self-scaling in the right direction: just outside the silhouette the gradient
   reaches 0.29 of the layer alpha, so the brightest band cell blooms visibly
   while a faint flow head does not bloom at all. Bright cells glow; faint cells
   merely brighten. */
.hex::before {
  left: 50%;
  top:  50%;
  /* 120x128 at u=60 — sized to bound layer memory, not to look generous. */
  width:  calc(var(--u) * 2);
  height: calc(var(--u) * 32 / 15);
  translate: -50% -50%;
  background: radial-gradient(circle closest-side,
    rgba(var(--c), 0.62),
    rgba(var(--c), 0.30) 46%,
    rgba(var(--c), 0.10) 72%,
    rgba(var(--c), 0)   100%);
  --hex-dim: 0;                        /* no halo at rest: an unlit cell is off */
  --hex-lit: calc(var(--o, 0.3) * var(--o-mul, 1));
}

/* These must stay AFTER the .hex::before/.hex::after rule above, and there is
   no .hex--g1 rule by design: g1 is the base animation-name. .hex--g1::after
   would tie .hex::after on specificity — (0,1,1) each, since a pseudo-element
   counts as an element, not a class — and win on source order alone. If one is
   ever added for symmetry it goes below these two, not above. */
.hex--g2::before, .hex--g2::after { animation-name: hex-g2; }
.hex--g3::before, .hex--g3::after { animation-name: hex-g3; }

/* Scrolled away, the flares have nothing to say. */
.hex-field--asleep .hex::before,
.hex-field--asleep .hex::after { animation-play-state: paused; }

.hex--a {
  left:   calc(50% + var(--col) * var(--u));
  bottom: calc(var(--u) * (23 / 20 + var(--row) * 26 / 15));       /* 69/60 */
}

.hex--b {
  left:   calc(50% + (var(--col) + 0.5) * var(--u));
  bottom: calc(var(--u) * (17 / 60 + var(--row) * 26 / 15));
}

/* Cells light up and go out at random, with no timer and no JS.
   Each cell carries one of these three patterns, a period from a set of
   mutually non-harmonic values (7.3, 8.9, 10.1, 11.7, 13.3, 15.1, 16.9s) and
   its own delay, all three derived from its lattice position. The ensemble's
   repeat period is the LCM of those, i.e. hours, so the field never visibly
   cycles — and because the three patterns place their flares at different
   points, no single cell reads as metronomic either. A cell never goes fully
   dark: it rests at --dim-k of its lit level so the band stays a band.

   Each flare is a strike, a short hold, and a long glow-out — asymmetric,
   because a light coming on is not a light going off. Both curves are quoted in
   full at every ramp rather than hidden behind a custom property:
   animation-timing-function inside @keyframes is not an animatable property and
   var() there is not somewhere to be clever.

   g1 and g2 now flare once per cycle where they flared twice and once; g3 keeps
   two — one full flare straddling the loop seam plus a short blink — and that is
   what stops the field reading as metronomic. Halving the plateau while keeping
   a visible ramp is only possible by dropping flare count: the strike curve
   spends most of its length above the midpoint, so two ramped flares per cycle
   cannot fit in half the old budget.

   Duty cycle, plateau-lit / at-or-above the dim-lit midpoint:
     g1  9.00% -> 5.50%  |  24.00% -> 12.05%
     g2  8.00% -> 3.00%  |  16.50% -> 10.02%
     g3 13.00% -> 6.00%  |  29.50% -> 13.50%
   Across the 68 cells that is 6.80 -> 3.26 simultaneously on a plateau and
   15.78 -> 8.04 above the midpoint. Event cadence barely moves (10.05/s ->
   8.13/s): the field stays as busy, but the events become discrete instead of
   overlapping, which is the perceptual goal. */

@keyframes hex-g1 {                 /* strike 5%, hold 5.5%, glow-out 13.5% */
  0%,    34%   { opacity: var(--hex-dim); animation-timing-function: cubic-bezier(0.16, 0.75, 0.34, 1); }
  39%,   44.5% { opacity: var(--hex-lit); animation-timing-function: cubic-bezier(0.18, 0.66, 0.34, 1); }
  58%,  100%   { opacity: var(--hex-dim); }
}

@keyframes hex-g2 {                 /* the slow one: strike 3.5%, hold 3%, glow-out 24% */
  0%,    46%   { opacity: var(--hex-dim); animation-timing-function: cubic-bezier(0.16, 0.75, 0.34, 1); }
  49.5%, 52.5% { opacity: var(--hex-lit); animation-timing-function: cubic-bezier(0.18, 0.66, 0.34, 1); }
  76.5%, 100%  { opacity: var(--hex-dim); }
}

@keyframes hex-g3 {                 /* main flare across the loop seam, plus a blink */
  0%,     3%   { opacity: var(--hex-lit); animation-timing-function: cubic-bezier(0.18, 0.66, 0.34, 1); }
  11%,   55%   { opacity: var(--hex-dim); animation-timing-function: cubic-bezier(0.16, 0.75, 0.34, 1); }
  57%,   58.5% { opacity: var(--hex-lit); animation-timing-function: cubic-bezier(0.18, 0.66, 0.34, 1); }
  62%,   94%   { opacity: var(--hex-dim); animation-timing-function: cubic-bezier(0.16, 0.75, 0.34, 1); }
  98.5%, 100%  { opacity: var(--hex-lit); }
}

/* Below 768px the text column IS the full width (this site's mobile
   breakpoint), so the stream cells would sit behind the headline. The lattice
   texture stays, masked away from the type, and the band stays; only the
   streams go. Source used 900px, where its own column collapsed. */
@media (max-width: 768px) {
  .hex-field { --u: 52px; }
  .hex--flow { display: none; }
  .hex--dense { display: none; }   /* half the band, so the chain keeps its gaps */

  /* Narrow: every part of the box has text over it, so the lattice is capped
     rather than holed — at most 0.13 x 0.7 = 0.09 stroke opacity.
     .hex-lattice::after is a child, so this governs the pointer glow too. */
  .hex-lattice {
    -webkit-mask-image: radial-gradient(115% 58% at 45% 40%, transparent 0, rgba(0, 0, 0, 0.22) 62%, rgba(0, 0, 0, 0.7) 100%);
            mask-image: radial-gradient(115% 58% at 45% 40%, transparent 0, rgba(0, 0, 0, 0.22) 62%, rgba(0, 0, 0, 0.7) 100%);
  }
}

@media (max-width: 600px) {
  .hex-field { --u: 40px; --o-mul: 0.9; }
}

@media print, (forced-colors: active) {
  .hex-field { display: none; }
}


/* ── Credibility bar ── */

.credibility-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 16px;
  padding: 14px var(--gap);
  /* No border-top: the honeycomb field is full-bleed and continuous here, and a
     full-width rule sliced straight across it. */
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  /* was 0.22, which survived a plain background but not the band's top rows
     rising behind it once the field went full-bleed */
  color: var(--muted);
}



/* ─── ABOUT ─── */

#about {
  padding: 100px 0;
  border-bottom: 1px solid var(--border);
}

#about .section-label {
  color: var(--blue);
}

.about-heading {
  font-weight: 800;
  font-size: clamp(36px, 5vw, 72px);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin-bottom: 48px;
}

.about-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 56px;
  margin-bottom: 64px;
  align-items: start;
}

.about-intro {
  font-size: clamp(15px, 1.4vw, 18px);
  line-height: 1.75;
  color: var(--muted);
}

.about-gallery-label {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 16px;
}

.about-gallery-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

.about-gallery-grid img {
  width: 100%;
  height: auto;          /* beats the height="427" attribute, so aspect-ratio governs */
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.04);
}

.pillars {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
}

.pillar {
  padding: 32px;
  border-top: 2px solid var(--border);
}

.pillar--academic { border-top-color: var(--blue); }
.pillar--industry { border-top-color: var(--orange); }
.pillar--showcase { border-top-color: var(--green); }

.pillar h3 {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 16px;
}

.pillar--academic h3 { color: var(--blue); }
.pillar--industry h3 { color: var(--orange); }
.pillar--showcase h3 { color: var(--green); }

.pillar p {
  font-size: 15px;
  line-height: 1.7;
  color: var(--muted);
}


/* ─── PROGRAM ─── */

#program {
  padding: 100px 0;
  border-bottom: 1px solid var(--border);
}

#program .section-label {
  color: var(--blue);
}

#program h2 {
  font-weight: 800;
  font-size: clamp(36px, 5vw, 72px);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin-bottom: 32px;
  color: var(--blue);
}

.program-intro {
  font-size: clamp(15px, 1.4vw, 18px);
  line-height: 1.7;
  color: var(--muted);
  max-width: 680px;
  margin-bottom: 64px;
}

.program-intro a {
  color: var(--blue);
  text-decoration: none;
}

.program-intro a:hover {
  text-decoration: underline;
}

.program-days {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
}

.program-day {
  padding: 32px;
  border-top: 1px solid var(--border);
}

.day-header {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 24px;
  padding-bottom: 24px;
  border-bottom: 1px solid var(--border);
}

.day-num {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--blue);
}

.day-date {
  font-size: 22px;
  font-weight: 700;
}

.day-theme {
  font-size: 13px;
  color: var(--muted);
}

.day-sessions {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.day-sessions li {
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
  display: flex;
  gap: 10px;
}

.session-time {
  flex-shrink: 0;
  min-width: 76px;
  color: var(--blue);
  font-variant-numeric: tabular-nums;
}

/* Breaks stay at --muted like every other row. They are still distinguishable:
   only real sessions get the blue time accent. The old opacity: 0.45 stacked on
   --muted and dragged "Coffee Break" and "Lunch & Networking" to ~2.3:1, which
   is a legibility problem, not a hierarchy solution. */
.session--break .session-time {
  color: inherit;
}

.program-note {
  margin-top: 32px;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}


/* ─── SPEAKERS ─── */

#speakers {
  padding: 100px 0;
  border-bottom: 1px solid var(--border);
}

#speakers .section-label {
  color: var(--orange);
}

.speakers-layout {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 80px;
  align-items: start;
}

.speakers-left h2 {
  font-weight: 800;
  font-size: clamp(36px, 5vw, 72px);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin-bottom: 32px;
  color: var(--orange);
}

.speakers-holding {
  font-size: clamp(15px, 1.4vw, 18px);
  line-height: 1.75;
  color: var(--muted);
}

.speakers-right {
  padding-top: 59px; /* aligns with h2 roughly */
}

.speakers-carousel-label {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 24px;
}

.speaker-carousel {
  position: relative;
}

.speaker-card {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s ease;
}

.speaker-card.active {
  position: relative;
  opacity: 1;
  pointer-events: auto;
}

.speaker-photo {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  aspect-ratio: 1;
  width: 100%;
  max-width: 320px;
  margin-bottom: 16px;
  overflow: hidden;
}

.speaker-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--pos, center 22%);
  filter: grayscale(1) contrast(1.05);
}

.speaker-name {
  font-size: 18px;
  font-weight: 600;
  margin-bottom: 6px;
}

.speaker-org {
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}

.carousel-dots {
  display: flex;
  gap: 0;
  margin-top: 20px;
  /* pull the first dot's padding back so the row still starts on the text edge */
  margin-left: -9px;
  flex-wrap: wrap;
}

/* The dot the eye sees is still 6px; the thing the finger hits is 24x24, which
   is the WCAG 2.5.8 minimum. Painting the dot as a pseudo-element keeps the two
   independent instead of trading one for the other. */
.carousel-dot {
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  flex-shrink: 0;
}

.carousel-dot::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--border);
  transition: background 0.2s;
}

.carousel-dot.active::before {
  background: var(--orange);
}


/* ─── VENUE ─── */

#venue {
  padding: 100px 0;
  border-bottom: 1px solid var(--border);
}

#venue .section-label {
  color: var(--green);
}

#venue h2 {
  font-weight: 800;
  font-size: clamp(36px, 5vw, 72px);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin-bottom: 24px;
  color: var(--green);
}

.venue-tagline {
  font-size: clamp(15px, 1.4vw, 18px);
  font-weight: 300;
  font-style: italic;
  color: var(--muted);
  margin-bottom: 56px;
  max-width: 62ch;  /* ran the full 1200px container, ~111 characters a line */
}

.venue-grid {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 80px;
  align-items: start;
}

.venue-body {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.venue-body p {
  font-size: clamp(15px, 1.4vw, 18px);
  line-height: 1.75;
  color: var(--muted);
}

.venue-italic {
  font-style: italic;
}

.venue-aside {
  display: flex;
  flex-direction: column;
  gap: 32px;
}

.venue-address {
  border-left: 1px solid var(--green);
  padding-left: 24px;
}

/* .venue-address p is (0,1,1) and beat both of the single-class rules below,
   so the label was written green and rendered grey, and .venue-distance's
   0.22 alpha (~1.9:1) sat dead behind --muted. Both are now qualified. */
.venue-address p {
  font-size: 14px;
  line-height: 1.7;
  color: var(--muted);
  margin-bottom: 10px;
}

.venue-address .venue-address-label {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--green);
  margin-bottom: 12px;
}

.venue-address .venue-distance {
  font-size: 12px;
  color: var(--muted);
}

.venue-carousel {
  position: relative;
}

.venue-slide {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.6s ease;
}

.venue-slide.active {
  position: relative;
  opacity: 1;
  pointer-events: auto;
}

.venue-slide img {
  width: 100%;
  height: auto;          /* beats the height="533" attribute, so aspect-ratio governs */
  aspect-ratio: 3 / 2;   /* matches the source photos exactly — no crop */
  object-fit: cover;
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.04);
}

.venue-caption {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: 10px;
}

#venue .carousel-dot.active {
  background: var(--green);
}


/* ─── REGISTER ─── */

#register {
  padding: 100px 0;
  border-bottom: 1px solid rgba(13, 13, 13, 0.15);
  background: var(--orange);
  color: #0d0d0d;
}

#register .section-label {
  color: rgba(13, 13, 13, 0.72);  /* 4.9:1 on --orange; 0.4 was 2.51:1 */
}

#register h2 {
  font-weight: 800;
  font-size: clamp(36px, 5vw, 72px);
  line-height: 0.9;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  margin-bottom: 32px;
}

.register-desc {
  font-size: 18px;
  font-weight: 300;
  color: rgba(13, 13, 13, 0.72);  /* 4.9:1 on --orange; 0.6 was 3.88:1 */
  margin-bottom: 40px;
  max-width: 560px;
}

.btn {
  display: inline-block;
  padding: 16px 36px;
  background: #0d0d0d;
  color: var(--orange);
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  transition: opacity 0.2s;
}

.btn:hover {
  opacity: 0.75;
}


/* ─── PARTNERS ─── */

#partners {
  padding: 100px 0;
  border-bottom: 1px solid var(--border);
}

#partners .section-label {
  color: var(--green);
}

.partners-desc {
  font-size: clamp(15px, 1.4vw, 18px);
  line-height: 1.75;
  color: var(--muted);
  max-width: 800px;
  margin-bottom: 64px;
}

.logo-groups {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 60px 80px;
  align-items: flex-start;
}

.logo-group h3 {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 20px;
}

.logos {
  display: flex;
  flex-wrap: wrap;
  gap: 32px;
  align-items: center;
}

.logos img {
  height: 48px;
  width: auto;
  max-width: none;
}


/* ─── SPONSOR CTA ─── */

#sponsor-cta {
  padding: 100px 0;
  border-bottom: 1px solid var(--border);
}

#sponsor-cta .section-label {
  color: var(--orange);
}

#sponsor-cta h2 {
  font-weight: 800;
  font-size: clamp(36px, 5vw, 72px);
  text-transform: uppercase;
  letter-spacing: -0.03em;
  line-height: 0.9;
  margin-bottom: 32px;
  color: var(--orange);
}

.sponsor-desc {
  font-size: 18px;
  font-weight: 300;
  color: var(--muted);
  margin-bottom: 28px;
  max-width: 560px;
}

.sponsor-email {
  display: inline-block;
  font-size: clamp(18px, 2.5vw, 32px);
  font-weight: 600;
  color: var(--orange);
  text-decoration: none;
  border-bottom: 1px solid currentColor;
  padding-bottom: 4px;
  transition: opacity 0.2s;
}

.sponsor-email:hover {
  opacity: 0.7;
}


/* ─── FOOTER ─── */

footer {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 40px;
  padding: 40px var(--gap);
}

.footer-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.footer-body p {
  font-size: 12px;
  line-height: 1.6;
  color: var(--muted);
}

.footer-body a {
  color: var(--text);
  text-decoration: none;
}

.footer-body a:hover {
  text-decoration: underline;
}

.footer-logo {
  height: 56px;
  width: auto;
  max-width: none;
  flex-shrink: 0;
}


/* ─── RESPONSIVE ─── */

@media (max-width: 768px) {
  :root {
    --gap: 20px;
  }

  /* nav: the links used to be a hidden-scrollbar strip that clipped half the
     menu with nothing hinting it could scroll. They are now a panel behind the
     hamburger, and the bar itself sticks so Register stays reachable. */
  nav {
    gap: 12px;
  }

  .nav-toggle {
    display: flex;
  }

  .nav-links {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    display: none;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 0 var(--gap) 10px;
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    /* safety net: never let the panel run past the bottom of the screen */
    max-height: calc(100svh - var(--nav-h, 72px));
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }

  .nav-open .nav-links {
    display: flex;
  }

  .nav-links a {
    padding: 15px 0;
    font-size: 13px;
    color: var(--text);
    border-top: 1px solid var(--border);
  }

  .nav-links a:first-child {
    border-top: 0;
  }

  .nav-cta {
    flex-shrink: 0;
    margin-left: auto;
  }

  /* the sticky bar would otherwise sit on top of whatever section you jump to */
  section[id],
  #showcase {
    scroll-margin-top: var(--nav-h, 72px);
  }

  /* hero: not forced to fill the viewport. Forcing it made every pixel of
     leftover height pile up below the content as a void of bare hexagons
     (36% of the hero on a phone, 50% on a tall tablet). The field now gets a
     bounded reveal instead, and the next section peeks in to invite scrolling. */
  #hero {
    min-height: 0;
  }

  /* No reveal band here any more: --tail on .hero-stage is what the field
     resolves into, and stacking both left a double gap under the content. */
  .hero-split {
    display: flex;
    flex-direction: column;
    flex: 0 0 auto;
  }

  .hero-content {
    width: 100%;
    /* the old 4px was a squeeze from when the hero was forced to 100svh */
    padding: clamp(18px, 3.5vh, 32px) var(--gap) 24px;
    gap: 20px;
    justify-content: flex-start;
  }

  .hero-title {
    font-size: clamp(40px, 13vw, 72px);
  }

  .hero-subtitle {
    max-width: none;
  }

  .hero-actions {
    flex-direction: column;
  }

  .btn-primary,
  .btn-secondary {
    display: block;
    text-align: center;
    width: 100%;
  }


  .hero-colocated {
    padding: 16px var(--gap);
  }

  .credibility-bar {
    font-size: 11px;
    gap: 6px 12px;
  }

  /* section labels */
  .section-label {
    margin-bottom: 28px;
  }

  /* uniform section headings on mobile */
  .about-heading,
  #program h2,
  .speakers-left h2,
  #venue h2,
  #register h2,
  #sponsor-cta h2 {
    font-size: clamp(28px, 8vw, 36px);
  }

  /* uniform body text on mobile */
  .about-intro,
  .program-intro,
  .speakers-holding,
  .venue-tagline,
  .venue-body p,
  .register-desc,
  .sponsor-desc,
  .partners-desc {
    font-size: 15px;
  }

  /* about */
  #about {
    padding: 60px 0;
  }

  .about-heading {
    margin-bottom: 32px;
  }

  .about-grid {
    grid-template-columns: 1fr;
    gap: 32px;
    margin-bottom: 40px;
  }

  .pillars {
    grid-template-columns: 1fr;
  }

  .pillar {
    padding: 24px 0;
    border-top-width: 1px;
  }

  /* program */
  #program {
    padding: 60px 0;
  }

  .program-days {
    grid-template-columns: 1fr;
  }

  .program-day {
    padding: 24px 0;
  }

  /* speakers */
  #speakers {
    padding: 60px 0;
  }

  .speakers-layout {
    grid-template-columns: 1fr;
    gap: 48px;
  }

  .speakers-right {
    padding-top: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
  }

  .speaker-carousel {
    width: 100%;
    max-width: 320px;
  }

  .speaker-photo {
    max-width: 100%;
  }

  .carousel-dots {
    margin-top: 16px;
    position: static;
    justify-content: center;
  }

  /* venue */
  #venue {
    padding: 60px 0;
  }

  .venue-grid {
    grid-template-columns: 1fr;
    gap: 40px;
  }

  .venue-aside {
    gap: 24px;
  }

  /* register */
  #register {
    padding: 60px 0;
  }

  .btn {
    display: block;
    text-align: center;
  }

  /* partners */
  #partners {
    padding: 60px 0;
  }

  .logo-groups {
    flex-direction: column;
    gap: 48px;
  }

  .logos img {
    height: 36px;
    max-width: none;
  }

  /* sponsor cta */
  #sponsor-cta {
    padding: 60px 0;
  }

  .sponsor-email {
    font-size: 16px;
    word-break: break-all;
  }

  /* footer */
  footer {
    flex-direction: column;
    align-items: flex-start;
    gap: 24px;
  }

  .footer-logo {
    height: 40px;
  }
}

/* short portrait phones (iPhone SE 320x568, older Androids 360x640): there is
   not enough height for the decorative tail below the CTA, so shorten the tail
   and drop the credibility strip rather than squeeze the type */
@media (max-height: 700px) and (max-width: 768px) {
  :root {
    --tail: 64px;
  }
  .hero-colocated {
    padding-top: 14px;
    padding-bottom: 14px;
  }
  .logos--hero img {
    height: 22px;
  }
  .credibility-bar {
    display: none;
  }
}

/* keep co-located logos visible on short/laptop screens */
@media (max-height: 780px) and (min-width: 769px) {
  .hero-content {
    padding-top: 28px;
    padding-bottom: 28px;
    gap: 16px;
  }
  .hero-title {
    font-size: clamp(40px, 6vw, 80px);
  }
  .hero-colocated {
    padding-top: 14px;
    padding-bottom: 14px;
  }
}

/* ── Short viewports (phone in landscape, squat desktop windows) ──
   A 360px-tall viewport leaves ~280px under the nav, so the hero used to push
   its CTAs ~250px below the fold. These three blocks compress it to fit. */

/* shared: the nav alone ate 22% of the height. --nav-h follows it via JS. */
@media (max-height: 540px) {
  /* 13vw of a wide-but-short viewport is a lot of decorative band before the
     first section — 110px against 390px of height */
  :root {
    --tail: 56px;
  }
  nav {
    padding-top: 10px;
    padding-bottom: 10px;
  }
  .nav-cta {
    padding: 7px 16px;
  }
  /* keep the bar short and fit all six links without scrolling the panel */
  .nav-toggle {
    height: 36px;
  }
  .nav-links a {
    padding: 9px 0;
    font-size: 12px;
  }
  #hero {
    min-height: 0;
  }
  .hero-eyebrow {
    font-size: 11px;
  }
  .hero-colocated {
    padding: 6px var(--gap);
  }
  .hero-colocated-label {
    margin-bottom: 5px;
  }
  .logos--hero img {
    height: 18px;
  }
  .credibility-bar {
    padding-top: 6px;
    padding-bottom: 6px;
  }
}

/* landscape on the desktop layout: give the text column the width the
   honeycomb gives back, and shrink the cells to suit the shorter field */
@media (max-height: 540px) and (min-width: 769px) {
  .hero-split {
    grid-template-columns: 62% 1fr;
  }
  .hero-content {
    padding: 10px var(--gap);
    gap: 8px;
  }
  .hero-title {
    font-size: clamp(28px, 4.6vw, 42px);
  }
  .hero-subtitle {
    font-size: 12px;
    line-height: 1.45;
    max-width: 460px;
  }
  .hero-actions {
    gap: 10px;
  }
  .btn-primary {
    padding: 10px 20px;
  }
  .btn-secondary {
    padding: 9px 20px;
  }
  /* the field is full-bleed now, so only its scale needs to come down */
  .hex-field {
    --u: 46px;
  }
}

/* landscape below the desktop breakpoint (e.g. iPhone SE landscape, 667x375):
   the stacked portrait layout is far too tall here — drop the reveal band and
   put the buttons back side by side */
@media (max-height: 540px) and (max-width: 768px) {
  :root {
    --tail: 40px;
  }
  .hero-content {
    padding: 10px var(--gap);
    gap: 8px;
  }
  .hero-title {
    font-size: clamp(26px, 7.5vw, 40px);
  }
  .hero-subtitle {
    font-size: 12px;
    line-height: 1.45;
  }
  .hero-actions {
    flex-direction: row;
    gap: 10px;
  }
  .btn-primary,
  .btn-secondary {
    display: inline-block;
    width: auto;
    padding: 9px 18px;
  }
  .hex-field {
    --o-mul: 0.85;
  }
}

/* under ~380px tall there is no room left for the decorative credibility strip,
   and keeping it only got it clipped mid-line */
@media (max-height: 380px) {
  .credibility-bar {
    display: none;
  }
}

/* One plausible frame, not the brightest one. .hex--on marks six cells — one
   per pillar, far enough apart to read — and everything else keeps the resting
   opacity already on .hex::before/.hex::after. With animation:none plain
   opacity governs again, and .hex--on::after at (0,2,1) beats .hex::after's
   (0,1,1) outright. The pointer lamp stays: it is direct manipulation, moving
   only while the user moves, which is the same category of thing as a
   scrollbar. */
@media (prefers-reduced-motion: reduce) {
  .hex::before,
  .hex::after     { animation: none; }
  .hex--on::before,
  .hex--on::after { opacity: var(--hex-lit); }

  html { scroll-behavior: auto; }

  *, *::before, *::after {
    transition-duration: 0.01ms !important;
  }
}
