/* ============================================================================
   BusTrakr — section layouts.

   Sections are transparent by design: the WebGL layer is the environment and
   these are the surfaces that float in it. Scroll length per section is the
   edit rhythm of the film — the transformation section is deliberately the
   longest because it carries the most change.
   ========================================================================= */

.sec {
  position: relative;
  /* Above both the veil (0) and the 3D canvas (1). */
  z-index: 2;
  display: block;
}

/* Every section's content is pinned for the length of that section. The page
   scrolls, the film advances, but the words hold still long enough to be read
   — which is the difference between a cinematic site and a fast slideshow. */
.sec__inner {
  position: sticky;
  top: 0;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Sections that switch to `display: grid` below inherit this instead. */
  align-content: center;
  width: 100%;
  max-width: 1320px;
  margin: 0 auto;
  padding: calc(var(--nav-h) + 5vh) var(--gutter) 10vh;
}

@media (min-width: 1100px) {
  /* Keeps text clear of the scroll rail on wide screens. */
  .sec__inner { padding-right: calc(var(--gutter) + var(--rail-w)); }
}

/* Copy scrim.
   The 3D layer is deliberately busy at some beats — a particle burst, a lit
   route crossing the frame — and body text sitting straight on it stops being
   readable. This is a very soft, very wide pool of the page's own ground
   colour behind the text column: enough to hold contrast, with no visible edge
   that would read as a panel. Behind the copy, in front of the canvas. */
.sec__inner::before {
  content: "";
  position: absolute;
  /* Vertical bleed only. Bleeding sideways past the centred 1320px column
     pushed the document's scrollWidth past the viewport. */
  inset: -14vh 0;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(
    95% 60% at 36% 50%,
    rgba(4, 6, 11, 0.90) 0%,
    rgba(4, 6, 11, 0.70) 38%,
    rgba(4, 6, 11, 0.30) 62%,
    rgba(4, 6, 11, 0) 80%
  );
}
/* Centre-aligned beats need the pool centred too. */
.answer__inner::before,
.connected__inner::before,
.finale__inner::before {
  background: radial-gradient(
    78% 54% at 50% 50%,
    rgba(4, 6, 11, 0.84) 0%,
    rgba(4, 6, 11, 0.54) 44%,
    rgba(4, 6, 11, 0) 76%
  );
}
/* The finale's pool must not dim the wordmark forming above it. */
.finale__inner::before { inset: 30% 0 -14vh; }

/* --------------------------------------------------------- 01 · intro */

/* Taller than one screen so the opening shot is *held* while the camera
   pushes in — a hero exactly one viewport tall has no pinned window at all. */
.sec--intro { min-height: 155vh; }
.intro__inner { justify-content: flex-end; padding-bottom: 14vh; }

.intro__title { margin-bottom: 26px; max-width: 15ch; }

.intro__lede {
  margin: 0 0 42px;
  max-width: 40ch;
  font-size: clamp(1rem, 1.35vw, 1.2rem);
  color: var(--text-dim);
  line-height: 1.6;
}

.intro__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(20px, 4vw, 56px);
}

/* Instrument-panel readout: label above value, monospaced, hairline dividers. */
.readout {
  display: flex;
  margin: 0;
  gap: clamp(18px, 3vw, 40px);
}
.readout > div {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding-left: 16px;
  border-left: 1px solid var(--line);
}
.readout dt {
  font: 500 9.5px/1 var(--font-mono);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.readout dd {
  margin: 0;
  font: 500 12.5px/1 var(--font-mono);
  letter-spacing: 0.08em;
  color: var(--text);
}
.readout .is-live { color: var(--live); }

/* --------------------------------------------------------- 02 · problem */

.sec--problem { min-height: 190vh; }
.problem__inner {
  display: grid;
  gap: clamp(40px, 7vw, 96px);
  align-items: end;
}
@media (min-width: 900px) {
  .problem__inner { grid-template-columns: minmax(0, 1.35fr) minmax(300px, 0.65fr); }
}

/* Sized to hold "Where is / my bus?" on two lines inside its column — at the
   shared .mega scale it broke into four, and a four-line question loses the
   punch the whole section depends on. */
.problem__inner .mega { font-size: clamp(2.6rem, 8.4vw, 7rem); }

.problem__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
}
.problem__list li {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding: 17px 0;
  border-top: 1px solid var(--line);
  color: var(--text-dim);
  font-size: 0.98rem;
}
.problem__list li:last-child {
  border-bottom: 1px solid var(--line);
  color: var(--text);
}

/* --------------------------------------------------------- 03 · answer */

/* Long, and pinned: the bus dissolving into the network is scrubbed across
   this whole section, so it needs travel without the copy moving. */
.sec--answer { min-height: 260vh; }
.answer__inner { text-align: center; align-items: center; }

.answer__label {
  margin: 34px 0 0;
  display: flex;
  justify-content: center;
}
.answer__label span {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 9px 20px;
  border: 1px solid rgba(56, 221, 245, 0.32);
  border-radius: 100px;
  background: rgba(56, 221, 245, 0.06);
  font: 600 11px/1 var(--font-mono);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--signal);
}

/* --------------------------------------------------------- 04 · live */

.sec--live { min-height: 175vh; }
.live__inner {
  display: grid;
  gap: clamp(22px, 3vw, 34px);
  align-items: start;
}
.live__head { margin-bottom: 6px; }

@media (min-width: 980px) {
  .live__inner {
    grid-template-columns: repeat(12, 1fr);
    grid-template-rows: auto auto;
  }
  /* Seven columns, not six: at six the headline broke to four lines and pushed
     the whole instrument row off the bottom of a 800px-tall viewport. The
     heading is also sized to its own track here rather than to the viewport
     width, so the break stays where the markup puts it. */
  .live__head     { grid-column: 1 / span 7; }
  .live__inner .big { font-size: clamp(2.1rem, 4.3vw, 3.5rem); }
  .hud--primary   { grid-column: 1 / span 5; grid-row: 2; }
  .hud--list      { grid-column: 7 / span 4; grid-row: 2; margin-top: clamp(20px, 3.2vw, 44px); }
  .hud--stat      { grid-column: 11 / span 2; grid-row: 2; margin-top: clamp(32px, 5.4vw, 80px); }
}

/* Instrument overlays, not product cards.
   A 14px radius over a filled panel reads as a SaaS dashboard dropped on top
   of the film. These are registered to the scene instead: an almost-square
   corner, a fill thin enough that the network still shows through, and corner
   register ticks rather than a boxed-in border — the language of something
   calibrated over a live image. */
.hud {
  position: relative;
  padding: clamp(20px, 2.4vw, 28px);
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--panel-thin);
  backdrop-filter: blur(26px) saturate(122%);
  -webkit-backdrop-filter: blur(26px) saturate(122%);
}
/* Register ticks: eight 11px hairlines pinned to the four corners. */
.hud::after {
  content: "";
  position: absolute;
  inset: -1px;
  pointer-events: none;
  --t: linear-gradient(var(--tick), var(--tick));
  background:
    var(--t) 0    0    / 1px 11px no-repeat, var(--t) 0    0    / 11px 1px no-repeat,
    var(--t) 100% 0    / 1px 11px no-repeat, var(--t) 100% 0    / 11px 1px no-repeat,
    var(--t) 0    100% / 1px 11px no-repeat, var(--t) 0    100% / 11px 1px no-repeat,
    var(--t) 100% 100% / 1px 11px no-repeat, var(--t) 100% 100% / 11px 1px no-repeat;
}
/* A single hairline of signal along the top edge marks the live panel. */
.hud--primary::before {
  content: "";
  position: absolute;
  inset: -1px auto auto -1px;
  width: calc(100% + 2px);
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--signal), transparent);
  border-radius: 3px 3px 0 0;
}

.hud__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 18px;
}
.hud__tag {
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 116%;
  font-size: clamp(1.5rem, 2.3vw, 2rem);
  letter-spacing: -0.02em;
  color: #fff;
}
.pill {
  padding: 5px 11px;
  border-radius: 100px;
  font: 600 10px/1 var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border: 1px solid currentColor;
}
.pill--live { color: var(--live); background: rgba(127, 184, 138, 0.08); }

.hud__route {
  margin: 0 0 24px;
  font: 500 11.5px/1.4 var(--font-mono);
  letter-spacing: 0.14em;
  color: var(--text-mute);
}

.hud__eta { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.hud__etaValue {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: clamp(2.4rem, 4.6vw, 3.5rem);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--signal);
  font-variant-numeric: tabular-nums;
}
.hud__etaLabel {
  font: 500 10.5px/1 var(--font-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-mute);
}

.hud__bar {
  position: relative;
  height: 3px;
  margin: 22px 0;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.07);
  overflow: hidden;
}
.hud__bar span {
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--fill);
  border-radius: 2px;
  background: linear-gradient(90deg, var(--signal-soft), var(--signal));
}
.hud__bar span::after {
  content: "";
  position: absolute;
  right: 0;
  top: 50%;
  width: 7px;
  height: 7px;
  margin-top: -3.5px;
  margin-right: -3.5px;
  border-radius: 50%;
  background: var(--signal);
  box-shadow: 0 0 12px 2px rgba(56, 221, 245, 0.6);
}

.hud__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
  margin: 0;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}
.hud__grid dt {
  font: 500 9.5px/1 var(--font-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin-bottom: 7px;
}
.hud__grid dd {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}

.hud__caption {
  margin: 0 0 16px;
  font: 600 10px/1 var(--font-mono);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.hud--list ul { list-style: none; margin: 0; padding: 0; }
.hud--list li {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 0;
  border-top: 1px solid var(--line);
  font-size: 13px;
  color: var(--text-dim);
}
.hud--list li:first-child { border-top: 0; }
.hud--list em {
  margin-left: auto;
  font: 500 10.5px/1 var(--font-mono);
  font-style: normal;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--live);
}
.hud--list em.is-warn { color: var(--warn); }
.dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--c, var(--signal));
  box-shadow: 0 0 10px 1px color-mix(in srgb, var(--c, var(--signal)) 60%, transparent);
  flex: none;
}

.hud--stat { text-align: left; }
.hud__statValue {
  display: block;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 116%;
  font-size: clamp(3rem, 5vw, 4.2rem);
  line-height: 0.86;
  letter-spacing: -0.04em;
  color: #fff;
}
.hud__statLabel {
  display: block;
  margin-top: 10px;
  font: 500 10px/1.5 var(--font-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-mute);
}

/* --------------------------------------------------------- 05 · students */

.sec--students { min-height: 175vh; }
.students__inner {
  display: grid;
  gap: clamp(34px, 5vw, 60px);
  align-items: center;
}
@media (min-width: 900px) {
  .students__inner { grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr); }
}
/* Reserves the space the 3D phone occupies so the copy never collides with
   it; the device itself is rendered in the WebGL layer. */
.students__deviceSlot { min-height: clamp(280px, 46vh, 520px); }

.students__copy .big { margin-bottom: 24px; }
.students__copy .lede { margin-bottom: 34px; }

.ticks { list-style: none; margin: 0; padding: 0; }
.ticks li {
  position: relative;
  padding: 13px 0 13px 30px;
  border-top: 1px solid var(--line);
  font-size: 0.96rem;
  color: var(--text-dim);
}
.ticks li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 50%;
  width: 9px;
  height: 9px;
  margin-top: -4px;
  border: 1px solid var(--signal);
  border-radius: 2px;
  transform: rotate(45deg);
}
.ticks li:last-child { border-bottom: 1px solid var(--line); }

/* --------------------------------------------------------- 06 · parents */

.sec--parents { min-height: 170vh; }
.parents__inner { max-width: 1080px; }
/* Held a step below the other display headlines: this block carries a
   two-column grid underneath it and at the full .mega scale the pair
   overflowed the pinned viewport. */
.parents__inner .mega { font-size: clamp(2.5rem, 7.4vw, 6.1rem); }
.parents__grid {
  display: grid;
  gap: clamp(28px, 4vw, 64px);
  margin-top: clamp(30px, 4vw, 56px);
}
@media (min-width: 860px) {
  .parents__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; }
}
.parents__note {
  margin: 0;
  font-size: clamp(1rem, 1.3vw, 1.14rem);
  line-height: 1.7;
  color: var(--text-dim);
  max-width: 44ch;
}
.parents__list { list-style: none; margin: 0; padding: 0; }
.parents__list li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  padding: 16px 0;
  border-top: 1px solid var(--line);
}
.parents__list li:last-child { border-bottom: 1px solid var(--line); }
.parents__list span { font-size: 1rem; color: var(--text); font-weight: 600; }
.parents__list em {
  font: 500 10.5px/1 var(--font-mono);
  font-style: normal;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--signal);
}

/* --------------------------------------------------- 07 · institutions */

.sec--institutions { min-height: 180vh; }
.inst__head { margin-bottom: clamp(34px, 5vw, 56px); }

/* The command strip: four live counters in one continuous bar. */
.inst__counters {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  /* A readout strip ruled top and bottom, not a boxed card — the figures are
     part of the environment's instrumentation, not a pricing table. */
  border-block: 1px solid var(--line);
  margin-bottom: clamp(36px, 5vw, 60px);
}
@media (min-width: 760px) {
  .inst__counters { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.counter {
  padding: clamp(20px, 2.6vw, 30px);
  background: rgba(8, 12, 20, 0.72);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
}
.counter__value {
  display: block;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: clamp(2.2rem, 3.6vw, 3.1rem);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--signal);
  font-variant-numeric: tabular-nums;
}
.counter--warn .counter__value { color: var(--warn); }
.counter__label {
  display: block;
  margin-top: 12px;
  font: 500 10px/1 var(--font-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-mute);
}

.inst__caps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0 clamp(24px, 4vw, 60px);
}
@media (min-width: 700px)  { .inst__caps { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1080px) { .inst__caps { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.inst__caps li {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 16px 0;
  border-top: 1px solid var(--line);
  font-size: 0.98rem;
  color: var(--text);
}

/* --------------------------------------------------------- 08 · connected */

.sec--connected { min-height: 150vh; }
.connected__inner { text-align: center; align-items: center; }
.connected__inner { max-width: 1100px; }
.connected__note {
  margin: 34px auto 0;
  max-width: 40ch;
  color: var(--text-dim);
  font-size: clamp(1rem, 1.25vw, 1.12rem);
  /* Stops a one-word orphan on the last line. */
  text-wrap: pretty;
}

/* --------------------------------------------------------- 09 · finale */

.sec--finale { min-height: 175vh; }
.finale__inner { text-align: center; align-items: center; }

/* The wordmark at the end of the film is drawn by the particle system, not by
   CSS — the whole point of the finale is watching the network become the name.
   This heading stays in the document for screen readers and search engines,
   and becomes the visible wordmark whenever the 3D layer isn't available. */
.finale__word {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.no-webgl .finale__word {
  position: static;
  width: auto;
  height: auto;
  margin: 0;
  overflow: visible;
  clip-path: none;
  white-space: normal;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 122%;
  text-transform: uppercase;
  font-size: clamp(2.8rem, 12vw, 10rem);
  line-height: 0.86;
  letter-spacing: 0.02em;
  color: #fff;
  text-shadow: 0 0 60px rgba(56, 221, 245, 0.35);
}

/* Reserves the upper half of the finale for the particles to resolve into. */
.finale__inner { justify-content: flex-end; }
.finale__stage {
  flex: 0 0 auto;
  min-height: clamp(180px, 34vh, 380px);
}
.no-webgl .finale__stage { min-height: 0; }

/* Scroll-cued, not observer-cued: held back through the collapse and the
   silence that follows it. */
.finale__line,
#finale-cta {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 1.1s var(--ease), transform 1.1s var(--ease);
  will-change: opacity, transform;
}
.finale__line.is-cued,
#finale-cta.is-cued {
  opacity: 1;
  transform: none;
}

.finale__line {
  margin: 26px 0 44px;
  font-family: var(--font-serif);
  font-style: italic;
  font-size: clamp(1.4rem, 3vw, 2.3rem);
  line-height: 1.2;
  color: var(--signal);
}

/* Sits outside <main>, on its own opaque ground, so the last pinned block
   can never slide over it. */
.foot {
  position: relative;
  z-index: 3;
  background: var(--ink);
  border-top: 1px solid var(--line);
  padding: clamp(36px, 5vw, 64px) var(--gutter) clamp(40px, 5vw, 64px);
}
.foot__inner {
  width: 100%;
  max-width: 1320px;
  margin: 0 auto;
  display: grid;
  gap: 18px;
  text-align: left;
}
@media (min-width: 820px) {
  .foot__inner {
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 30px;
  }
  .foot__note { grid-column: 1 / -1; }
}
.foot__brand {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 118%;
  font-size: 14px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text);
}
.foot__links { display: flex; flex-wrap: wrap; gap: 22px; }
.foot__links a {
  font: 500 12px/1 var(--font-mono);
  letter-spacing: 0.06em;
  color: var(--text-dim);
  text-decoration: none;
  transition: color 0.25s var(--ease);
}
.foot__links a:hover { color: var(--signal); }
.foot__note {
  margin: 0;
  font-size: 11.5px;
  line-height: 1.6;
  color: var(--text-mute);
  max-width: 70ch;
}

/* ------------------------------------------------------------- responsive */

@media (max-width: 899px) {
  /* Phone: the film still plays, but the camera framing is simplified and the
     copy takes the foreground. Sections shorten so the story doesn't become a
     marathon on a small screen. */
  /* Every section keeps a real pinned window on phones too. `min-height: auto`
     would let a section collapse to exactly one viewport, leaving sticky with
     zero travel — and story time would then jump a whole beat in a single
     pixel of scroll instead of easing through it. */
  .sec--intro        { min-height: 150vh; }
  .sec--problem      { min-height: 165vh; }
  .sec--answer       { min-height: 215vh; }
  .sec--live         { min-height: 190vh; }
  .sec--students     { min-height: 175vh; }
  .sec--parents      { min-height: 165vh; }
  .sec--institutions { min-height: 200vh; }
  .sec--connected    { min-height: 145vh; }
  .sec--finale       { min-height: 165vh; }

  .sec { padding-top: calc(var(--nav-h) + 8vh); padding-bottom: 12vh; }

  .rail { display: none; }
  .nav__links { display: none; }
  .sound__label { display: none; }
  .sound { padding: 9px 12px; }

  .students__deviceSlot { min-height: 32vh; order: -1; }
  .readout { width: 100%; }
  .readout > div:last-child { display: none; }
}

@media (max-width: 560px) {
  .intro__foot { gap: 24px; align-items: flex-start; flex-direction: column; }
  .readout { gap: 16px; }
  .hud__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .scroll-cue { display: none; }
}

/* ------------------------------------------------------- typographic layer */
/* One display word per act, set in outline behind the copy and moved against
   the 3D on scroll. It is set INSIDE the pinned block, so it holds with the
   section instead of sliding past it, and at z-index -2 it sits under the
   copy scrim — which is what makes it read as part of the environment rather
   than a caption laid over the top. Purely decorative: every word here is
   already spoken by the heading or the body copy beside it. */
.glyph {
  position: absolute;
  z-index: -2;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
  white-space: nowrap;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 112%;
  line-height: 0.78;
  letter-spacing: -0.01em;
  font-size: clamp(76px, 15vw, 230px);
  color: transparent;
  -webkit-text-stroke: 1px rgba(184, 222, 255, 0.19);
  opacity: 0;
  transform: translate3d(0, 5vh, 0);
  transition: opacity 1s var(--ease), transform 1.6s var(--ease);
  will-change: opacity, transform;
}
.glyph.is-cued { opacity: 1; transform: translate3d(0, 0, 0); }

.glyph--right { right: var(--gutter); top: 14%; }
.glyph--low   { left: var(--gutter); bottom: 9%; }
/* A nine-letter word needs a smaller setting than a four-letter one, or it
   runs the width of the column and stops being a shape. */
.glyph--tail  {
  right: var(--gutter);
  bottom: 6%;
  font-size: clamp(56px, 9vw, 150px);
  letter-spacing: 0.02em;
  -webkit-text-stroke-color: rgba(184, 222, 255, 0.15);
}

/* Narrow screens have no spare space for a display word beside the copy, and
   a 15vw outline behind a stacked layout is just noise. */
@media (max-width: 860px) {
  .glyph { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .glyph { transition: none; }
}
