/* ==========================================================================
   frontfoot — site stylesheet  (2026-08-12, enterprise pass)

   This file replaces the style.css + v2.css pair the earlier builds loaded.
   Those now live in archive/css/ with the pages that use them; nothing here
   depends on either, and no selector is defined in both trees any more.

   What changed, and why:
     · One stylesheet, one cascade. The old pair had a base layer and a
       re-skin layer arguing about type sizes in two places.
     · The type scale went up. Every section headline used to top out at
       ~1.95rem, which reads as a blog post; enterprise pages carry their
       statements at display size and let whitespace do the rest.
     · One pinned scroll set-piece survives (the feed of AI advice). The
       product used to have a second one; two of them in a row read as a
       scrollytelling demo rather than a product page.
     · The narrative stays on one ground, with contrast coming from the
       product UI itself rather than a decorative site-wide grid. That ground
       was warm paper when this was written; the August 2026 reset near the
       bottom of the file turned it dark, and the Tokens block below now
       carries the dark values directly.
   ========================================================================== */

/* ==========================================================================
   Typeface — one self-hosted family throughout (SIL OFL 1.1; see
   assets/fonts/README.md). Weight, scale and colour create the hierarchy.
   ========================================================================== */
@font-face {
  font-family: 'Hanken Grotesk';
  src: url('../assets/fonts/hanken-grotesk-latin.woff2') format('woff2');
  font-weight: 300 800;
  font-style: normal;
  font-display: swap;
  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: 'Hanken Grotesk';
  src: url('../assets/fonts/hanken-grotesk-latin-ext.woff2') format('woff2');
  font-weight: 300 800;
  font-style: normal;
  font-display: swap;
  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;
}

/* ==========================================================================
   Tokens — the only place a palette value is set

   Ground is dark (#1d1d1d, the August 2026 reset), which inverts the brand
   colours' jobs from what they were on paper. Orange is 6.2:1 here, so it is
   the one that can carry meaning — emphasis, active states, anything that has
   to read. Crimson is 2.0:1 and cannot.

   NOTE: nothing references var(--brand-crimson) any more. The dark reset
   replaced the crimson buttons with off-white ones; the crimson rules left
   behind were dead overrides and were removed on 15 Aug 2026. Crimson itself
   is still on the page, but only as hardcoded values — the hero bloom
   gradient (~L385) and the top of the axis heat ramp (~L1010, L1081). Those
   are the exception to "the only place a palette value is set", and pointing
   them back at the token is unfinished business.

   One open design question, not cleanup: whether crimson returns as a named
   role. The heat ramp is settled — its crimson end sits at 1.9:1 on the axis's
   #222120 against 5.9:1 for the orange band beside it, and it stays that way by
   decision (ADR-0001). Leave those four values alone.

   Every token below is declared exactly once. If you are adding a value, add
   it here rather than in a second :root further down — the page is read
   top-to-bottom by people and by tools, and a later override is invisible to
   both.
   ========================================================================== */
:root {
  --brand-orange: #f67931;
  --brand-orange-dark: #df6620;
  --brand-crimson: #951344;
  --brand-crimson-dark: #780f36;

  --color-bg: #1d1d1d;
  --color-surface: #252525;
  --color-text: #f4f3ef;

  --gray-100: #262626;
  --gray-200: #2d2d2d;
  --gray-300: #383838;
  --gray-400: #747474;
  --gray-500: #929292;
  --gray-600: #c7c7c4;

  /* Darker still than the ground — used by the app captures' own chrome. */
  --ink-900: #14161a;
  --ink-700: #262a31;

  --dot-spacing: 18px;

  --traffic-red: #ff5f57;
  --traffic-yellow: #febc2e;
  --traffic-green: #28c840;

  --font-body: 'Hanken Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: var(--font-body);
  --font-display: var(--font-body);

  /* ── Type scale ──────────────────────────────────────────────────────────
     Nothing outside this block declares a raw font-size for page copy;
     components pick a role, so one edit here moves the whole page.

     Titles use the same humanist grotesk as the body, with weight and scale
     doing the separating. The mono face stays in the interface layer. */
  --fs-display: clamp(2.15rem, 5vw, 3.75rem);   /* the two statements that carry the argument */
  --fs-title:   clamp(1.6rem, 3.2vw, 2.5rem);   /* section headlines */
  --fs-sub:     clamp(1.35rem, 2.1vw, 1.6rem);  /* sub-headlines inside a section */
  --fs-lead:    clamp(1.0625rem, 1.4vw, 1.25rem);
  --fs-body:    clamp(0.9375rem, 1.1vw, 1.0625rem);
  --fs-small:   0.875rem;
  --fs-label:   0.6875rem;

  --lh-display: 1.04;
  --lh-title: 1.1;
  --lh-lead: 1.55;
  --lh-body: 1.65;

  --tracking-title: -0.025em;
  --tracking-label: 0.16em;

  --container-width: 1120px;
  --nav-width: 1440px;
  --section-y: clamp(4.5rem, 8vw, 8rem);

  --radius-full: 999px;
  --radius-md: 12px;

  --shadow-soft: 0 18px 54px rgba(0, 0, 0, 0.24);
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.22), 0 16px 40px rgba(0, 0, 0, 0.18);

  --transition-fast: 150ms ease;
}

/* ==========================================================================
   Reset & base
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; scroll-padding-top: 5.5rem; }

body {
  margin: 0;
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3, h4, p, figure { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }

button { font-family: inherit; border: none; background: none; cursor: pointer; }
img { max-width: 100%; height: auto; }

.container {
  max-width: var(--container-width);
  margin-inline: auto;
  padding-inline: 1.5rem;
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%);
  white-space: nowrap;
}

/* ==========================================================================
   Type roles
   ========================================================================== */
.display,
.title {
  font-weight: 700;
  letter-spacing: var(--tracking-title);
  text-wrap: balance;
}

.display { font-size: var(--fs-display); line-height: var(--lh-display); }
.title {
  font-size: var(--fs-title);
  line-height: var(--lh-title);
  color: var(--color-text);
}

.subtitle {
  font-family: var(--font-mono);
  font-size: var(--fs-sub);
  font-weight: 600;
  line-height: var(--lh-title);
  letter-spacing: var(--tracking-title);
}

.lead {
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
  max-width: 58ch;
}

.body { font-size: var(--fs-body); color: var(--gray-500); }

/* Emphasis carries in brand colour rather than slant so the display voice
   stays composed and direct. Orange, not crimson: on the dark ground crimson
   is 2.0:1 and would read as dark-on-dark. */
.display em, .title em, .lead em, .body em {
  font-style: normal;
  font-weight: inherit;
  color: var(--brand-orange);
}

/* ── Section label ─────────────────────────────────────────────────────────
   A quieter version of the body voice. The section title carries the
   hierarchy, so the label does not need numbering or a separate typeface. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--gray-500);
  margin-bottom: 1.25rem;
}

.eyebrow::before {
  content: '';
  width: 1.75rem;
  height: 2px;
  background: var(--brand-orange);
  flex-shrink: 0;
}

.section-head { max-width: 68ch; }
.section-head .title { max-width: 18em; }
.section-head .lead { margin-top: 1.25rem; }

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  height: 46px;
  padding-inline: 1.35rem;
  border-radius: var(--radius-full);
  border: 1px solid transparent;
  font-family: var(--font-body);
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--transition-fast),
              border-color var(--transition-fast),
              color var(--transition-fast);
}





:where(a, button):focus-visible {
  outline: 3px solid var(--brand-orange);
  outline-offset: 3px;
  border-radius: var(--radius-md);
}

/* ==========================================================================
   Nav
   The header stays deliberately spare: brand on the left, contact and
   download actions on the right.
   ========================================================================== */
.nav {
  position: fixed;
  inset-inline: 0;
  top: 0;
  padding-top: env(safe-area-inset-top);
  z-index: 50;
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--gray-300);
  transition: background-color var(--transition-fast), border-color var(--transition-fast);
}

.nav__inner {
  max-width: var(--nav-width);
  margin-inline: auto;
  padding: 0.7rem 1.5rem;
  align-items: center;
}

/* The live lockup stays as two independent vectors: the brandmark can evolve
   without retracing the wordmark, and both remain crisp at any zoom. */
.nav__brand {
  display: inline-flex;
  align-items: center;
  gap: 0.42rem;
  text-decoration: none;
  padding: 0.25rem;
  margin: -0.25rem;
  flex-shrink: 0;
}

.nav__brandmark,
.nav__wordmark {
  display: block;
  width: auto;
}

.nav__brandmark { height: 1.72rem; }
.nav__wordmark { height: 1.18rem; }

.nav__actions { display: flex; align-items: center; gap: 0.6rem;}

.nav .btn { height: 40px; padding-inline: 1.05rem; font-size: 0.875rem; }

/* Over the hero's orange field the nav carries no ground of its own and
   inverts. js/site.js toggles the class off scroll position. */
.nav--over-hero {
  background: transparent;
  backdrop-filter: none;
  border-bottom-color: transparent;
}





@media (max-width: 560px) {
  .nav__inner { padding: 0.65rem 1rem; gap: 0.75rem; }
  .nav__brand { gap: 0.35rem; }
  .nav__brandmark { height: 1.45rem; }
  .nav__wordmark { height: 0.98rem; }
  .nav .btn { height: 38px; padding-inline: 0.85rem; font-size: 0.8125rem; }
}

/* Both actions survive to the narrowest phone: "get in touch" is the only
   route to a human on the page, so it isn't the one that gets dropped. The
   download label shortens instead. */
@media (max-width: 420px) {
  .nav__actions { gap: 0.4rem; }
  .nav .btn { padding-inline: 0.7rem; font-size: 0.78125rem; }
  .nav .btn--primary .btn__long { display: none; }
}

/* ==========================================================================
   Hero field — brand orange with a crimson bloom rising from the bottom edge.
   Fixed rather than scrolling so the glow stays put while the hero's content
   moves over it; js/site.js fades the layer out as the hero leaves, so the
   page resolves to the paper the rest of the site is built on.
   ========================================================================== */
.hero-bg {
  position: fixed;
  /* Overscanned at the bottom so the gradient's edge never becomes visible
     when mobile browser chrome changes the visual viewport height. */
  inset: -1px 0 -140px;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
  background:
    linear-gradient(180deg, rgba(87, 30, 0, 0.13) 0%, rgba(87, 30, 0, 0) 46%),
    var(--brand-orange);
  will-change: opacity;
}

/* The bloom is its own layer so it can be animated with transform and opacity;
   gradient stops don't interpolate cheaply. Two rules keep the edge clean:
   crimson is a short hue trip from orange (both sit either side of red), and
   every zero-alpha stop is the orange itself rather than a transparent
   crimson — a stop fading to a distant colour drifts through neutral on the
   way, and that drift is what reads as a grey band. */
.hero-bg::before {
  content: '';
  position: absolute;
  inset: 0 0 110px 0;
  background:
    radial-gradient(64% 36% at 50% 108%,
      rgba(214, 108, 148, 0.42) 0%,
      rgba(181, 60, 106, 0.32) 26%,
      rgba(149, 19, 68, 0.18) 58%,
      rgba(246, 121, 49, 0) 100%),
    radial-gradient(104% 62% at 50% 110%,
      rgba(149, 19, 68, 0.26) 0%,
      rgba(176, 52, 90, 0.12) 44%,
      rgba(246, 121, 49, 0) 82%);
  transform-origin: 50% 100%;
  animation: hero-glow 22s ease-in-out infinite;
  will-change: transform, opacity;
}

@keyframes hero-glow {
  0%, 100% { transform: scale(1);                       opacity: 1; }
  50%      { transform: scale(1.035) translateY(-0.5%); opacity: 0.92; }
}

.hero-bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image: radial-gradient(circle, rgba(255, 255, 255, 0.16) 1px, transparent 1px);
  background-size: var(--dot-spacing) var(--dot-spacing);
}

@media (prefers-reduced-motion: reduce) {
  .hero-bg::before { animation: none; }
}

/* ==========================================================================
   Hero
   ========================================================================== */
.hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6rem 1.5rem 4rem;
}

/* The runner (js/runner.js) — a canvas between the hero field and the
   wordmark, drawn entirely in bars sampled on the page's dot lattice so it
   reads as the background finding a figure rather than a sprite over one. */
.hero__runner {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}

.hero__inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 100%;
}

.hero__copy {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
}

.hero__subtitle {
  font-family: var(--font-body);
  font-size: clamp(1.05rem, 2.2vw, 1.4rem);
  font-weight: 500;
  color: #fff;
  text-align: center;
  letter-spacing: 0.005em;
}

.hero__lead {
  font-size: clamp(0.9375rem, 1.5vw, 1.0625rem);
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.82);
  text-align: center;
  max-width: 46ch;
  text-wrap: balance;
}

/* ==========================================================================
   Section rhythm — one hairline between sections and one vertical padding,
   so each block reads as a distinct page.
   ========================================================================== */
.section { padding-block: var(--section-y); }
.section + .section { border-top: 1px solid var(--gray-300); }

/* ==========================================================================
   Theatre — the product answer, restored 2026-08-12 at Alex's request.
   A two-column
   grid: the left column (.theatre__stage) is sticky and the right column
   (.theatre__rail) scrolls past it, so the screen the copy is describing
   changes as you scroll rather than sitting still beside three static
   captions. js/site.js swaps the pane as a stop reaches the reading line.

   This is the same component the enterprise pass had briefly replaced with
   a static device + three claims underneath; that version is gone and this
   is back in its place, copy unchanged from the build before it.
   ========================================================================== */
.theatre {
  border-top: 1px solid var(--gray-300);
}


.theatre__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
}

/* ── The pinned stage ───────────────────────────────────────────────────── */
.theatre__stage {
  position: sticky;
  z-index: 2;
  /* Mobile default: pin just under the fixed nav. The desktop rule below
     re-centres it in the column. */
  top: calc(env(safe-area-inset-top) + 4.25rem);
  padding-block: 0.75rem;
}

.theatre__device {
  position: relative;
  width: 100%;
  max-width: 680px;
  margin-inline: auto;
}

.theatre__dots {
  display: flex;
  justify-content: center;
  gap: 0.1rem;
  margin-top: 0.5rem;
}

.theatre__dot {
  width: 44px;
  height: 44px;
  padding: 0;
  display: grid;
  place-items: center;
  background: transparent;
}

/* The dots sit on the theatre's own light ground (.theatre__stage is #faf6f2),
   so they run dark-on-light against the rest of the dark page. */
.theatre__dot::after {
  content: '';
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #c9c1ba;
  transition: background-color var(--transition-fast), transform var(--transition-fast);
}

.theatre__dot.is-active::after {
  background: #1d1d1d;
  transform: scale(1.35);
}

/* ── The copy rail ──────────────────────────────────────────────────────── */
.theatre__rail {
  display: flex;
  flex-direction: column;
}

.stop {
  padding-block: 3rem 4rem;
  /* Inactive stops recede rather than disappear, so you can always see that
     there's another one coming - the rail reads as a list of three. */
  opacity: 0.34;
  transition: opacity 0.45s ease;
}

.stop.is-active { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .stop { transition: none; }
}

.stop__title {
  margin: 0;
  font-size: var(--fs-title);
  line-height: var(--lh-title);
  letter-spacing: var(--tracking-title);
  text-wrap: balance;
}

.stop__body {
  max-width: 51ch;
  margin-top: 1.15rem;
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
}

/* ── Desktop: side-by-side, stage centred in the viewport ───────────────── */
@media (min-width: 800px) {
  .theatre__grid {
    align-items: start;
  }

  /* No order override: .theatre__stage is already first in source (so the
     product pins above the copy on mobile), and that source order is also
     what's wanted on desktop - product left, copy right. */
  .theatre__stage {
    padding-block: 0;
    background: none;
  }

  .theatre__dots { display: none; }

  /* Each stop takes roughly a screen, so exactly one is "the current one"
     at any point and the screen beside it has time to register. */
  .stop {
    min-height: 70vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-block: 2rem;
  }

  /* Trims the dead space at the two ends of the rail so the first stop is
     already active when the section arrives, and the last one doesn't hold
     the pin open after it's finished. */
  .theatre__rail { padding-block: 0 4rem; }
}

@media (min-width: 1200px) {
  .theatre__grid { max-width: 1180px; }
}

@media (max-width: 799px) {
  .stop {
    padding-block: 2.5rem 3rem;
    /* Nothing is side-by-side any more, so dimming would just make the copy
       you're actually reading hard to read as it enters. */
    opacity: 1;
  }

  .stop + .stop { border-top: 1px solid var(--gray-300); }

  .theatre__rail { padding-bottom: 1rem; }
}

/* ==========================================================================
   Back foot — the pinned feed.

   A 280vh scroll track with the content held by position:sticky; the feed
   fills across it (js/site.js), one item at a time, until it is spilling off
   the top. The reveal is spread evenly over whatever this track's height is,
   so the two can be changed independently.
   ========================================================================== */
.backfoot {
  position: relative;
  height: 280vh;
  border-top: 1px solid var(--gray-300);
}

.backfoot__sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  align-items: center;
  overflow: hidden;
}

.backfoot__grid {
  /* .backfoot__sticky is a flex row and a flex child with no explicit width
     shrinks to max-content along the main axis — align-items is cross-axis
     only. Without this the grid collapses and stacks its columns. */
  width: 100%;
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(1rem, 3vh, 2.5rem);
  align-items: center;
}

.backfoot__closing { margin-top: 1rem; }

@media (min-width: 860px) {
  .backfoot__grid { grid-template-columns: 1fr 1fr; gap: 4rem; }
}

/* ── The feed ──────────────────────────────────────────────────────────────
   No device frame. The advice doesn't arrive on one phone — it arrives
   everywhere at once, so it's drawn as cards on the page's own ground with
   the newest at the bottom and the older ones dissolving off the top.
   ========================================================================== */
.wire { width: 100%; max-width: 420px; margin-inline: auto; }

.wire__viewport {
  height: clamp(260px, 46vh, 460px);
  overflow: hidden;
  padding-bottom: 1.5rem;
  margin-bottom: -1.5rem;
  display: flex;
  align-items: flex-end;
  /* Older items dissolve rather than being guillotined by the clip edge. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 22%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 22%);
}

.wire__stack {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.6rem;
  width: 100%;
}

/* Every item is incoming — each one is something being told TO you. Visible
   by default so a no-JS render is a full feed rather than an empty column;
   .wire--js (added by js/site.js) hands visibility over to the script. */
.msg {
  max-width: 92%;
  background: var(--color-surface);
  border-radius: 1.15rem;
  box-shadow: var(--shadow-card);
  color: var(--gray-600);
  font-size: 0.9375rem;
  line-height: 1.4;
  transform-origin: 0 100%;
  transition: opacity 0.28s ease, transform 0.34s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* The plain kind: a spoken line, nothing else. Every variant declares its own
   display, because js/site.js hides an undelivered item with an inline
   display:none and restores it by removing that override — so the stylesheet
   has to be the one holding the real value. */
.msg--text { display: inline-block; padding: 0.8rem 1.1rem; }

.wire--js .msg { opacity: 0; transform: translateY(8px) scale(0.94); }
.wire--js .msg.is-in { opacity: 1; transform: none; }

/* ── Link cards ────────────────────────────────────────────────────────────
   Two shapes so the feed doesn't read as one long text message. A video
   preview is vertical (thumbnail over title, the way every platform draws
   one); an article is horizontal (favicon tile beside the headline). Both
   are decorative mock-ups, not real links — the section is about the volume
   of this stuff, not about sending anyone to it. */
.msg--video { display: block; width: 15.5rem; max-width: 92%; overflow: hidden; padding: 0; }

.msg__thumb {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  background:
    radial-gradient(circle, rgba(255, 255, 255, 0.05) 1px, transparent 1px),
    linear-gradient(140deg, var(--ink-700), var(--ink-900));
  background-size: 14px 14px, auto;
}

.msg__play {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 2.4rem;
  height: 1.7rem;
  border-radius: 0.45rem;
  background: #ff0033;
  display: grid;
  place-items: center;
}

.msg__play::after {
  content: '';
  width: 0; height: 0;
  border-left: 0.55rem solid #fff;
  border-top: 0.34rem solid transparent;
  border-bottom: 0.34rem solid transparent;
  margin-left: 0.1rem;
}

.msg__stamp {
  position: absolute;
  right: 0.4rem;
  bottom: 0.4rem;
  padding: 0.1rem 0.3rem;
  border-radius: 0.25rem;
  background: rgba(0, 0, 0, 0.72);
  color: #fff;
  font-family: var(--font-mono);
  font-size: 0.625rem;
  line-height: 1.4;
}

.msg--video .msg__meta { display: block; padding: 0.7rem 0.85rem 0.8rem; }

.msg--article {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.7rem 0.9rem 0.7rem 0.7rem;
  width: 19rem;
  max-width: 96%;
}

.msg__favicon {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 0.6rem;
  background: var(--gray-100);
  border: 1px solid var(--gray-200);
  font-family: var(--font-mono);
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--brand-orange);
}

.msg__title {
  display: block;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--color-text);
}

.msg__source {
  display: block;
  margin-top: 0.2rem;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.02em;
  color: var(--gray-400);
}

/* ── Typing indicator ──────────────────────────────────────────────────────
   Decorative, so it stays hidden unless the script is driving. It is the last
   child of the stack and every undelivered item is display:none, so it always
   renders directly under the newest one without being repositioned. */
.msg--typing {
  display: none;
  gap: 0.28rem;
  align-items: center;
  padding: 0.95rem 1rem;
}

.msg--typing i {
  width: 0.32rem;
  height: 0.32rem;
  border-radius: 50%;
  background: var(--gray-400);
  animation: msg-typing 1.15s infinite ease-in-out;
}

.msg--typing i:nth-child(2) { animation-delay: 0.16s; }
.msg--typing i:nth-child(3) { animation-delay: 0.32s; }

@keyframes msg-typing {
  0%, 60%, 100% { opacity: 0.32; transform: translateY(0); }
  30%           { opacity: 1;    transform: translateY(-3px); }
}

/* ── Composer ──────────────────────────────────────────────────────────────
   The point of keeping it: it's the bit of the interface that implies a reply
   is expected of you. That's the whole feeling this section is about. */
.wire__composer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 0.9rem;
  padding: 0.55rem 0.55rem 0.55rem 1.1rem;
  border-radius: var(--radius-full);
  background: var(--color-surface);
  box-shadow: var(--shadow-card);
  color: var(--gray-400);
  font-size: 0.9375rem;
}

.wire__send {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: var(--gray-100);
  color: var(--gray-400);
  flex-shrink: 0;
}

.wire__send svg { width: 1rem; height: 1rem; }

@media (prefers-reduced-motion: reduce) {
  .msg { transition: opacity 0.01ms; }
  .msg--typing i { animation: none; }
}

/* ==========================================================================
   The difference — a full-width change of pace without changing the title
   system. It stays light so every section title has the same colour.
   ========================================================================== */
.turn {
  position: relative;
  background-color: var(--color-bg);
  color: var(--color-text);
  border-top: 1px solid var(--gray-300);
  overflow: hidden;
}

/* `em`, not `ch`: the measure has to track the display size, not the body
   size the container resolves `ch` against. */

.turn__body {
  display: grid;
  gap: 1.25rem;
  max-width: 58ch;
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
}

/* Two columns from the tablet breakpoint up: at display size the statement is
   only ever four or five words a line, so stacking the argument under it left
   the right half of a full-bleed band empty. */
.turn__grid {
  display: grid;
  gap: clamp(1.75rem, 4vw, 3.5rem);
  margin-top: clamp(1.5rem, 3vw, 2.25rem);
}

@media (min-width: 900px) {
  .turn__grid { grid-template-columns: 1.02fr 0.98fr; }
  .turn__statement { max-width: none; }
}

.turn__body strong { color: var(--color-text); font-weight: 600; }

/* ==========================================================================
   Axes — what Frontfoot actually reads, drawn with the app's own meter.
   The cells are the same ten-cell meter the panel captures show, so the page
   and the product are speaking one visual language.
   ========================================================================== */
.axes {
  display: grid;
  gap: 1.25rem;
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
}

.axis {
  --axis-duration: 11s;
  --axis-delay: 0s;
  display: grid;
  position: relative;
  isolation: isolate;
  overflow: hidden;
  gap: 1.2rem;
  padding: clamp(1.25rem, 2.5vw, 1.75rem);
  border: 1px solid #2d2a28;
  border-radius: var(--radius-md);
}

/* The panel's dot field sits quietly behind the reading. It fades before the
   definition so the copy remains easy to read rather than becoming UI chrome. */
.axis::before {
  content: '';
  position: absolute;
  z-index: -1;
  inset: 0;
  background-image: radial-gradient(circle, #393532 1.35px, transparent 1.5px);
  background-position: 12px 12px;
  background-size: 36px 36px;
  opacity: 0.72;
  -webkit-mask-image: linear-gradient(to bottom, #000 0, #000 58%, transparent 86%);
  mask-image: linear-gradient(to bottom, #000 0, #000 58%, transparent 86%);
}

.axis--clarity {
  --axis-duration: 10.8s;
  --axis-delay: -2.7s;
}

.axis--imagination {
  --axis-duration: 12.4s;
  --axis-delay: -6.2s;
}

.axis--judgement {
  --axis-duration: 11.6s;
  --axis-delay: -2.9s;
}

.axis__instrument {
  display: grid;
  gap: 0.55rem;
  min-width: 0;
  padding-bottom: 1.15rem;
  border-bottom: 1px solid #2d2a28;
}

.axis__state {
  overflow: hidden;
  height: 1.25em;
  font-size: clamp(1.55rem, 3vw, 1.9rem);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.035em;
}

.axis__state-track {
  display: grid;
  animation: axis-state-scroll var(--axis-duration) linear var(--axis-delay) infinite;
  animation-play-state: paused;
  will-change: transform;
}

.axes.is-active .axis__state-track,
.axes.is-active .axis__meter-on {
  animation-play-state: running;
}

.axis__band {
  display: block;
  height: 1.25em;
  line-height: 1.25;
}

/* The heat ramp. Held at these values by decision — see ADR-0001. The crimson
   top band is 1.88:1 on the axis ground, which is known and accepted; it is not
   a contrast bug to fix. The meter cells below run the same four colours and
   move with these. */
.axis__band--low { color: #f3a868; }
.axis__band--medium { color: #f67931; }
.axis__band--high { color: #c94336; }
.axis__band--very-high { color: #951344; }

@keyframes axis-state-scroll {
  0%, 19% { transform: translateY(0); }
  24%, 44% { transform: translateY(-20%); }
  49%, 69% { transform: translateY(-40%); }
  74%, 94% { transform: translateY(-60%); }
  100% { transform: translateY(-80%); }
}

.axis__name {
  overflow: hidden;
  font-size: 0.75rem;
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: 0.18em;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
  color: #736d68;
}

.axis__line {
  position: relative;
  max-width: 34ch;
}

/* The app uses ten individually coloured cells. The neutral and lit rows sit
   directly on top of one another; revealing the lit row keeps every gap and
   rounded edge locked to the product as the value changes. */
.axis__meter {
  position: relative;
  display: block;
  height: 7px;
}

.axis__meter-off,
.axis__meter-on {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: repeat(10, minmax(0, 1fr));
  gap: 3px;
}

.axis__meter-off {
  background-image: repeating-linear-gradient(90deg,
    #2a2725 0 calc(10% - 3px),
    transparent calc(10% - 3px) 10%);
}

.axis__meter-on {
  overflow: hidden;
  animation: axis-meter-clarity var(--axis-duration) linear var(--axis-delay) infinite;
  animation-play-state: paused;
  will-change: clip-path;
}

.axis__meter-on i {
  display: block;
  min-width: 0;
  border-radius: 1px;
  background: #f9a961;
}

.axis__meter-on i:nth-child(3),
.axis__meter-on i:nth-child(4) { background: #f67931; }

.axis__meter-on i:nth-child(5),
.axis__meter-on i:nth-child(6) { background: #c94336; }

.axis__meter-on i:nth-child(n + 7) { background: #951344; }

.axis--imagination .axis__meter-on { animation-name: axis-meter-imagination; }
.axis--judgement .axis__meter-on { animation-name: axis-meter-judgement; }

@keyframes axis-meter-clarity {
  0%, 19% { clip-path: inset(0 80% 0 0); }
  24%, 44% { clip-path: inset(0 60% 0 0); }
  49%, 69% { clip-path: inset(0 40% 0 0); }
  74%, 94% { clip-path: inset(0 20% 0 0); }
  100% { clip-path: inset(0 80% 0 0); }
}

@keyframes axis-meter-imagination {
  0%, 19% { clip-path: inset(0 90% 0 0); }
  24%, 44% { clip-path: inset(0 70% 0 0); }
  49%, 69% { clip-path: inset(0 30% 0 0); }
  74%, 94% { clip-path: inset(0 0 0 0); }
  100% { clip-path: inset(0 90% 0 0); }
}

@keyframes axis-meter-judgement {
  0%, 19% { clip-path: inset(0 80% 0 0); }
  24%, 44% { clip-path: inset(0 50% 0 0); }
  49%, 69% { clip-path: inset(0 30% 0 0); }
  74%, 94% { clip-path: inset(0 10% 0 0); }
  100% { clip-path: inset(0 80% 0 0); }
}

@media (min-width: 860px) {
  .axes { grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .axis__state-track,
  .axis__meter-on { animation: none; }

  .axis--clarity .axis__state-track,
  .axis--judgement .axis__state-track { transform: translateY(-20%); }

  .axis--imagination .axis__state-track { transform: translateY(-40%); }
  .axis--clarity .axis__meter-on { clip-path: inset(0 60% 0 0); }
  .axis--imagination .axis__meter-on { clip-path: inset(0 30% 0 0); }
  .axis--judgement .axis__meter-on { clip-path: inset(0 50% 0 0); }
}

/* ==========================================================================
   Creed — the mission behind the product.
   ========================================================================== */


.creed__body p {font-size: var(--fs-lead); line-height: var(--lh-lead); }
.creed__body strong { color: var(--color-text); font-weight: 600; }

/* ==========================================================================
   CTA
   ========================================================================== */
.cta__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}

/* The gate switches to a success message on submit via [hidden] in JS; an
   author rule setting `display` on it always beats the UA stylesheet's plain
   `[hidden] { display: none }`, so that override has to be written by hand.
   The trigger button (`.btn`, display: inline-flex) needs the same override
   for when JS hides it in favour of the gate. */
.cta__gate[hidden] { display: none; }
.cta__download .btn[hidden] { display: none; }

.cta .btn {
  height: 40px;
  padding-inline: 1.05rem;
  font-size: 0.875rem;
}

.cta__statement {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 0.22em;
}

.cta__wordmark {
  display: block;
  width: auto;
  max-width: 100%;
  height: 0.8em;
  transform: translateY(-0.01em);
}

/* ── Download gate: email in, .dmg out ─────────────────────────────────
   The only download form on the page — the nav button just scrolls here
   and focuses it, rather than opening a second one. See #36. */
.cta__download {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.6rem;
  position: relative;
}

/* Sized and positioned in JS (see data-download-confetti in site.js) to
   exactly cover whichever of the trigger button / email gate is visible,
   then clipped to it — the burst on submit never spills past that pill. */
.cta__confetti {
  position: absolute;
  overflow: hidden;
  border-radius: var(--radius-full);
  pointer-events: none;
}

.cta__confetti-piece {
  position: absolute;
  top: -12%;
  left: var(--start-x, 50%);
  width: 5px;
  height: 9px;
  border-radius: 1px;
  opacity: 0;
  animation-name: cta-confetti-fall;
  animation-timing-function: ease-in;
  animation-fill-mode: forwards;
}

@keyframes cta-confetti-fall {
  0% { opacity: 1; transform: translate(0, 0) rotate(0deg); }
  100% { opacity: 0; transform: translate(var(--drift, 0px), 170%) rotate(var(--rotate, 260deg)); }
}

.cta__gate {
  display: flex;
  gap: 0.5rem;
  width: min(100%, 22rem);
}

.cta__gate-input {
  flex: 1 1 auto;
  min-width: 0;
  height: 40px;
  padding-inline: 0.9rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--gray-300);
  background: var(--color-surface);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 0.875rem;
}

.cta__gate-input::placeholder { color: var(--gray-500); }

.cta__gate-input:focus-visible {
  outline: 3px solid var(--brand-orange);
  outline-offset: 2px;
}

.cta__gate-submit {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-full);
  border: none;
  background: var(--brand-orange);
  color: #fff;
  cursor: pointer;
}

.cta__gate-submit svg { width: 18px; height: 18px; }

.cta__gate-submit:disabled { opacity: 0.6; cursor: default; }

.cta__gate-submit:focus-visible {
  outline: 3px solid var(--brand-orange);
  outline-offset: 2px;
}

.cta__gate-status {
  font-size: var(--fs-small);
  color: var(--gray-600);
}

.cta__requirements {
  font-size: var(--fs-label);
  color: var(--gray-500);
}

/* Taken out of flex flow on wider screens so its always-visible text no
   longer adds to .cta__download's height — otherwise that column outgrows
   the single-line heading it sits beside and .cta__inner's align-items:
   center centers the taller box, pushing the button above the heading. */
@media (min-width: 641px) {
  .cta__requirements {
    position: absolute;
    top: 100%;
    right: 0;
    margin-top: 0.6rem;
  }
}

@media (max-width: 640px) {
  .cta__inner {
    align-items: flex-start;
    flex-direction: column;
  }

  .cta .btn {
    height: 38px;
    padding-inline: 0.85rem;
    font-size: 0.8125rem;
  }

  .cta__download {
    align-items: flex-start;
    width: 100%;
  }

  .cta__gate {
    width: 100%;
  }
}

/* ==========================================================================
   Footer
   ========================================================================== */
.footer {
  border-top: 1px solid var(--gray-300);
  padding: 1.5rem 0 calc(1.5rem + env(safe-area-inset-bottom));
}

.footer__inner {
  display: flex;
  align-items: center;
  justify-content: center;
}

.footer__legal {
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  line-height: 1.5;
  text-align: center;
  text-transform: uppercase;
  color: var(--gray-400);
}

/* ==========================================================================
   App mock — a faithful recreation of the real menu-bar app, not an
   abstraction. Every colour mirrors frontfoot-app's SwiftUI Palette; the app
   is already on the Frontfoot brand, so these match it exactly. Only the
   chrome around the real screenshots is drawn in CSS.

   Scoped to .app, not :root — this is the app's palette, not the page's, and
   the two grounds are opposites. Every consumer is a .app descendant.
   ========================================================================== */
.app {
  --app-paper: #f7f4f1;
  --app-paper2: #ffffff;
  --app-dot: #e9e2da;
  --app-reg: #d9cec3;
  --app-cell-off: #efe9e3;
  --app-ink: #2b211c;
  --app-ink2: #7b6a60;
  --app-ink3: #a89a90;
  --app-hair: #e7dfd7;
  --app-ember: #d25e15;
  --app-up: #35b37e;
}

.mac-window {
  aspect-ratio: 16 / 10;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--gray-300);
  border-radius: 12px;
  overflow: hidden;
  box-shadow: var(--shadow-soft);
}

.mac-window__bar {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  padding: 0.65rem 0.75rem;
  border-bottom: 1px solid var(--gray-300);
}

.mac-window__dot { width: 10px; height: 10px; border-radius: 50%; }
.mac-window__dot--red { background: var(--traffic-red); }
.mac-window__dot--yellow { background: var(--traffic-yellow); }
.mac-window__dot--green { background: var(--traffic-green); }

.mac-window__title {
  position: absolute;
  inset-inline: 3.25rem;
  color: rgba(244, 243, 239, 0.66);
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.01em;
  text-align: center;
  white-space: nowrap;
}

.mac-window__body {
  position: relative;
  flex: 1;
  /* The app screen sizes itself off the window's width (cqw), so the whole
     recreated UI scales as one piece instead of breaking up at small widths. */
  container-type: inline-size;
}

/* One pane per theatre screen, stacked and cross-faded rather than swapped
   in the DOM, so js/site.js only ever has to toggle a class. */
.mac-window__pane {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: scale(0.97);
  pointer-events: none;
  transition: opacity 0.35s ease, transform 0.35s ease;
}

.mac-window__pane.is-active {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
}

@media (prefers-reduced-motion: reduce) {
  .mac-window__pane { transition: opacity 0.01ms; }
}

.app {
  position: absolute;
  inset: 0;
  font-family: var(--font-body);
  /* Everything inside .app is sized in `em` off this one value, so the whole
     recreated desktop — menu bar, chat, and the panel capture over it — scales
     as a single piece with the window rather than drifting out of proportion
     as the window grows. The upper bound is high enough that the panel still
     occupies ~40% of the window at the widest layout. */
  font-size: clamp(9px, 1.85cqw, 18px);
  color: var(--app-ink);
  background-color: var(--app-paper);
  background-image:
    radial-gradient(circle, var(--app-reg) 1.05px, transparent 1.05px),
    radial-gradient(circle, var(--app-dot) 0.85px, transparent 0.85px);
  background-size: 72px 72px, 18px 18px;
  overflow: hidden;
}

.app__menubar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 1em;
  height: 2.3em;
  padding-inline: 1em;
  background: rgba(255, 255, 255, 0.72);
  border-bottom: 1px solid var(--app-hair);
}

.app__mb-glyph { width: 1.15em; height: 1.15em; color: var(--app-ink3); }
.app__mb-clock { color: var(--app-ink2); }

/* The glance — ember dot, multiplier, trend triangle. Cascadia, because that
   IS Frontfoot's typeface: it matches the real panel captures sitting beside
   it. The contrast is the point — Frontfoot is the monospace thing on your
   screen, everything else is the OS. */
.glance { display: inline-flex; align-items: center; gap: 0.45em; font-family: var(--font-mono); }
.glance__dot { width: 0.6em; height: 0.6em; border-radius: 50%; background: var(--app-ember); }
.glance__value { color: var(--app-ember); font-weight: 700; }
.glance__tri {
  width: 0; height: 0;
  border-left: 0.32em solid transparent;
  border-right: 0.32em solid transparent;
  border-bottom: 0.38em solid var(--app-up);
}

/* The menu-bar item runs one cycle of the real interaction: a halo pulse to
   invite the click, the pressed fill while the panel is open, then release.
   Shares the 7s clock with panel-cycle below. */
.glance--ring {
  padding: 0.2em 0.5em;
  border-radius: 5px;
  outline: 2px solid var(--brand-orange);
  outline-offset: 2px;
  animation: glance-cycle 7s ease-in-out infinite;
}

@keyframes glance-cycle {
  0%   { background-color: rgba(246, 121, 49, 0);    box-shadow: 0 0 0 0 rgba(246, 121, 49, 0.45); }
  10%  { background-color: rgba(246, 121, 49, 0);    box-shadow: 0 0 0 0.6em rgba(246, 121, 49, 0); }
  13%  { background-color: rgba(246, 121, 49, 0);    box-shadow: 0 0 0 0 rgba(246, 121, 49, 0); }
  18%  { background-color: rgba(246, 121, 49, 0.24); box-shadow: 0 0 0 0 rgba(246, 121, 49, 0); }
  55%  { background-color: rgba(246, 121, 49, 0.24); box-shadow: 0 0 0 0 rgba(246, 121, 49, 0); }
  63%, 100% { background-color: rgba(246, 121, 49, 0); box-shadow: 0 0 0 0 rgba(246, 121, 49, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .glance--ring { animation: none; }
}

/* ── The desktop behind Frontfoot ──────────────────────────────────────────
   A generic AI chat — the thing you're actually doing while Frontfoot is
   reading. Deliberately RECESSED (dimmed, desaturated, no accent colour) so
   it reads as context, not subject. */
.chat {
  position: absolute;
  inset: 2.3em 0 0 0;
  margin: 0.85em;
  display: flex;
  flex-direction: column;
  background: var(--app-paper2);
  border: 1px solid var(--app-hair);
  border-radius: 10px;
  overflow: hidden;
  opacity: 0.55;
  filter: saturate(0.6);
}

.chat__head {
  padding: 0.5em 0.8em;
  border-bottom: 1px solid var(--app-hair);
  color: var(--app-ink3);
  font-size: 0.9em;
  flex-shrink: 0;
}

.chat__thread {
  flex: 1;
  display: flex;
  flex-direction: column;
  /* Bottom-anchored like a real thread — also keeps the messages clear of the
     Frontfoot panel, which drops in over the top-right. */
  justify-content: flex-end;
  gap: 0.55em;
  padding: 0.85em;
  min-height: 0;
  overflow: hidden;
}

.bubble {
  margin: 0;
  padding: 0.5em 0.7em;
  border-radius: 9px;
  font-size: 0.9em;
  line-height: 1.45;
  color: var(--app-ink2);
}

.bubble--me { align-self: flex-end; max-width: 74%; background: var(--app-cell-off); }

.bubble--ai {
  align-self: flex-start;
  width: 78%;
  display: flex;
  flex-direction: column;
  gap: 0.45em;
  background: var(--app-paper);
  border: 1px solid var(--app-hair);
}

.bubble__text { margin: 0; }

/* Skeleton lines rather than more invented copy — reads as "a reply
   continues" without putting words in the model's mouth. */
.skel { display: block; height: 0.45em; border-radius: 999px; background: var(--app-hair); }
.skel--short { width: 55%; }

.chat__composer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5em;
  margin: 0 0.85em 0.85em;
  padding: 0.5em 0.8em;
  border: 1px solid var(--app-hair);
  border-radius: 999px;
  color: var(--app-ink3);
  font-size: 0.9em;
  flex-shrink: 0;
}

.chat__send { width: 1.15em; height: 1.15em; border-radius: 50%; background: var(--app-hair); flex-shrink: 0; }

/* The real capture, cropped to the panel. The PNG carries its own transparent
   rounded corners; the radius here only exists so the ring hugs it. */
.panel-shot {
  position: absolute;
  top: 2.9em;
  right: 1em;
  width: 22em;
  height: auto;
  display: block;
  border-radius: 0.95em;
  z-index: 2;
  box-shadow:
    0 0 0 1.5px rgba(246, 121, 49, 0.55),
    0 0 0 9px rgba(246, 121, 49, 0.10),
    0 14px 32px rgba(43, 33, 28, 0.28);
}

/* Screen 1 demonstrates the claim instead of asserting it: the panel drops
   in, sits for a beat, and leaves. Timed against glance-cycle so the press
   and the panel move together. */
.panel-shot--cycle {
  transform-origin: 100% 0;
  animation: panel-cycle 7s ease-in-out infinite;
  will-change: transform, opacity;
}

@keyframes panel-cycle {
  0%, 14%   { opacity: 0; transform: translateY(-0.5em) scale(0.97); }
  21%, 55%  { opacity: 1; transform: translateY(0) scale(1); }
  63%, 100% { opacity: 0; transform: translateY(-0.5em) scale(0.97); }
}

@media (prefers-reduced-motion: reduce) {
  .panel-shot--cycle { animation: none; opacity: 1; }
}

/* Screen 2's expanded stats view is a much taller capture. Driving it from
   height (with width auto) keeps its aspect intact and stops it running past
   the bottom of the window, which a fixed width would. */
.panel-shot--tall {
  width: auto;
  height: calc(100% - 4.4em);
}

/* Screen 3's Imagination panel occupies the right side. Keep the recessed
   chat conversation in the clear left column so the two layers never form
   accidental-looking text collisions. */
.chat--panel-clear .chat__thread { padding-right: 42%; }
.chat--panel-clear .bubble--me {
  align-self: flex-start;
  max-width: 100%;
}
.chat--panel-clear .bubble--ai { width: 100%; }

/* ==========================================================================
   Mobile
   `svh` — not `dvh` — for the hero: `dvh` tracks the live viewport, so it
   grows and shrinks as the address bar slides in and out mid-scroll, which
   shoves everything below it around as you scroll. `svh` is fixed to the
   bar-visible height, so nothing resizes during a scroll.
   ========================================================================== */
@media (max-width: 859px) {
  .hero { min-height: 100svh; }

  .backfoot__sticky {
    height: 100dvh;
    align-items: flex-start;
    padding-top: calc(env(safe-area-inset-top) + 5.25rem);
    padding-bottom: 1.25rem;
  }

  .backfoot__grid { align-content: start; justify-items: center; }
  .backfoot__copy { width: 100%; }
  .backfoot__art { width: 100%; }
  .wire { max-width: 360px; }
}

@media (max-width: 859px) and (max-height: 720px) {
  .backfoot__sticky { padding-top: calc(env(safe-area-inset-top) + 4.75rem); }
  .wire__viewport { height: clamp(200px, 38vh, 320px); }
}

/* ==========================================================================
   Minimal dark system — August 2026 reset

   One typeface and one ground. Narrative sections use the same sentence
   pattern throughout: the thought to retain is light, its context recedes.
   The only colourful surfaces left are Frontfoot itself.

   This section's palette now lives in the Tokens block at the top of the
   file; what remains here is the component work the reset changed. Those
   rules still supersede earlier ones by source order — folding them into
   the components they override is the other half of this cleanup.
   ========================================================================== */
body { background: var(--color-bg); }

.display,
.title,
.stop__title,
.axis__state,
.axis__name,
.footer__legal {
  font-family: var(--font-body);
}

/* ── Header: navigation balanced around a centred mark ─────────────────── */
.nav {
  background: color-mix(in srgb, var(--color-bg) 88%, transparent);
  border-bottom-color: var(--gray-300);
}

.nav__inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 1.5rem;
}

.nav__links {
  grid-column: 1;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  justify-self: start;
}

.nav__link {
  color: var(--gray-500);
  font-size: 0.875rem;
  font-weight: 500;
  text-decoration: none;
  transition: color var(--transition-fast);
}

.nav__link:hover { color: var(--color-text); }

.nav__brand {
  grid-column: 2;
  justify-self: center;
}

.nav__actions {
  grid-column: 3;
  justify-self: end;
  margin-left: 0;
}

.nav--over-hero .nav__link { color: rgba(255, 255, 255, 0.78); }
.nav--over-hero .nav__link:hover { color: #fff; }

.btn--primary,
.nav--over-hero .btn--primary {
  background: #f4f3ef;
  border-color: #f4f3ef;
  color: #1d1d1d;
}

.btn--primary:hover,
.nav--over-hero .btn--primary:hover {
  background: #deddd8;
  border-color: #deddd8;
  color: #1d1d1d;
}

/* ── One-screen statements ─────────────────────────────────────────────── */
.story {
  min-height: 100svh;
  display: flex;
  align-items: center;
  border-top: 1px solid var(--gray-300);
}

.story__inner { width: 100%; }

.story__statement,
.section-statement {
  margin: 0;
  color: var(--gray-500);
  font-family: var(--font-body);
  font-size: clamp(2.08rem, 5.12vw, 4.4rem);
  font-weight: 400;
  line-height: 1.04;
  letter-spacing: -0.045em;
  text-align: center;
  text-wrap: balance;
}

.story__statement {
  max-width: 17ch;
  margin-inline: auto;
}

.story__bright,
.section-statement__bright { color: var(--color-text); }

.story__line { display: block; }

/* Match the fixed header to the hero field instead of placing a dark strip
   across the top of the orange opening screen. */
.nav.nav--over-hero {
  background: var(--brand-orange);
  backdrop-filter: none;
  border-bottom-color: rgba(255, 255, 255, 0.12);
}

.section-statement--left {
  max-width: 17ch;
  text-align: left;
}

/* ── Product showcase ──────────────────────────────────────────────────── */
.theatre,
.turn,
.section,
.footer { background: var(--color-bg); }

.theatre__head {
  max-width: var(--container-width);
  margin-bottom: clamp(4rem, 8vw, 7rem);
}

.theatre__head .section-statement {
  max-width: 17ch;
  margin-inline: auto;
}


.stop__title {
  color: var(--color-text);
  font-weight: 500;
}

.stop__body,
.lead,
.turn__body,
.creed__body p { color: var(--gray-500); }

.turn { padding-block: clamp(7rem, 14vw, 12rem); }
.turn__grid { align-items: end; }
.turn__statement { max-width: 17ch; }

.axis {
  border-color: #353331;
  background: #222120;
}

.axis__line { color: #aaa5a0; }

.mac-window {
  background: #242424;
  border-color: #3a3a3a;
}

.mac-window__bar {
  background: #272727;
  border-bottom-color: #3a3a3a;
}

/* ── Ending ────────────────────────────────────────────────────────────── */
.creed__grid { max-width: var(--container-width); }

@media (max-width: 760px) {
  .nav__inner { gap: 0.75rem; }
  .nav__links { display: none; }
  .nav__brandmark { height: 1.4rem; }
  .nav__wordmark { height: 0.94rem; }

  .story__statement,
  .section-statement {
    font-size: clamp(1.84rem, 8.8vw, 3.4rem);
    line-height: 1.02;
  }
}

/* ==========================================================================
   Opening sequence refinements
   ========================================================================== */
.hero__statement {
  max-width: 13ch;
  color: #fff;
  font-family: var(--font-body);
  font-size: clamp(2.35rem, 5.4vw, 4.75rem);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.045em;
  text-align: center;
  text-wrap: balance;
}

.hero__inner { transform: translateY(-6vh); }

/* The message feed owns most of the viewport; the smaller statement sits
   beneath it, so the feeling arrives before the explanation. */
.story--messages {
  height: 280vh;
  min-height: 0;
  background: var(--color-bg);
}

.story--messages .backfoot__sticky {
  height: 100svh;
  align-items: center;
  padding: calc(env(safe-area-inset-top) + 5.25rem) 0 2rem;
}

.story-messages__inner {
  width: 100%;
  height: 100%;
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  align-items: end;
  justify-items: center;
  gap: clamp(1.25rem, 3vh, 2.5rem);
}

.story--messages .backfoot__art {
  width: min(680px, 100%);
  min-height: 0;
}

.story--messages .wire { max-width: 620px; }

.story--messages .wire__viewport {
  height: clamp(220px, 42vh, 390px);
}

.story--messages .wire__stack { align-items: flex-start; }

.story--messages .wire__composer { justify-content: flex-end; }

.story--messages .story__statement {
  max-width: 27ch;
  padding-bottom: clamp(0.5rem, 2vh, 1.5rem);
  font-size: clamp(1.6rem, 3vw, 2.5rem);
  line-height: 1.1;
  letter-spacing: -0.035em;
}

/* Product is the light mission screen: the app remains left, while each
   piece of copy is centred against the middle of the preview on the right. */
.theatre {
  padding-top: 0;
  background: #faf6f2;
  border-top-color: #ded8d2;
  color: #1d1d1d;
}

.theatre__grid { max-width: 1320px; }
.theatre__stage { background: #faf6f2; }

.theatre .stop__title {
  color: #1d1d1d;
  font-weight: 500;
}

.theatre .stop__body { color: #77736f; }
.theatre .stop + .stop { border-color: #ded8d2; }

@media (min-width: 800px) {
  .theatre__grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }


  .theatre .stop {
    box-sizing: border-box;
    padding-top: calc(18vh + clamp(7.5rem, 14vw, 11.5rem) - 4rem);
  }
}

@media (max-width: 760px) {
  .hero__statement { font-size: clamp(2.2rem, 10vw, 3.5rem); }

  .story--messages .backfoot__sticky {
    padding: calc(env(safe-area-inset-top) + 4.75rem) 0 1.25rem;
  }

  .story--messages .wire__viewport {
    height: clamp(190px, 38vh, 320px);
  }

  .story--messages .story__statement {
    max-width: 24ch;
    font-size: clamp(1.45rem, 6vw, 2rem);
  }
}

/* ==========================================================================
   Full-screen composition pass — August 2026
   ========================================================================== */
.section-label {
  margin: 0 0 1.25rem;
  color: var(--gray-500);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* The pressure screen is one balanced composition on desktop: the feed is
   intentionally smaller and the statement sits beside it, not underneath. */
@media (min-width: 800px) {
  .story--messages .backfoot__sticky {
    padding: calc(env(safe-area-inset-top) + 4rem) 0 0;
  }

  .story-messages__inner {
    height: calc(100svh - 4rem);
    grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
    grid-template-rows: 1fr;
    align-items: center;
    justify-items: stretch;
    gap: clamp(3rem, 7vw, 7rem);
  }

  .story--messages .backfoot__art {
    width: min(460px, 100%);
    justify-self: end;
  }

  .story--messages .wire { max-width: 460px; }

  .story--messages .wire__viewport {
    height: clamp(250px, 46vh, 330px);
  }

  .story--messages .story__statement {
    max-width: none;
    margin-inline: 0;
    padding: 0;
    font-size: clamp(1.6rem, 2.28vw, 2.08rem);
    line-height: 1.08;
    text-align: left;
  }

  .story--messages .story__line { white-space: nowrap; }
}

/* The proposition belongs to the website, not the product chrome. It sits
   above the device and remains visible while all three product stages pass. */
@media (min-width: 800px) {
  .theatre__grid {
    gap: clamp(4rem, 7vw, 7rem);
  }

  .theatre__stage {
    top: 4rem;
    height: calc(100svh - 4rem);
    padding: clamp(2rem, 5vh, 4rem) 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }

  .theatre .stop {
    min-height: calc(100svh - 4rem);
    padding: clamp(2rem, 6vh, 4rem) 0;
    justify-content: center;
  }

  .theatre__rail { padding: 0; }
}

/* Mission and strengths chapters each earn a full viewport and use a calmer
   scale than the earlier oversized statement treatment. */
.skills,
.belief {
  min-height: 100svh;
  display: flex;
  align-items: center;
}

.belief .creed__grid > div {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  column-gap: clamp(3rem, 8vw, 8rem);
  align-items: center;
}

.belief .section-label {
  grid-column: 1 / -1;
}

.belief .section-statement {
  max-width: 14ch;
  font-size: clamp(2.16rem, 4.2vw, 3.84rem);
  text-align: left;
}

.creed__body {
  display: grid;
  gap: 1rem;
  margin-top: 0;
  max-width: 54ch;
  color: var(--gray-500);
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
}

.skills__intro {
  margin-bottom: clamp(1.5rem, 3.5vh, 2.25rem);
}

.skills__statement {
  max-width: none;
  font-size: clamp(1.9rem, 3vw, 2.7rem);
}

.cta .section-statement {
  font-size: clamp(2.16rem, 4.2vw, 3.84rem);
}

@media (max-width: 799px) {
  .theatre { padding-top: clamp(2.5rem, 8vh, 4.5rem); }

  .belief .creed__grid > div {
    grid-template-columns: 1fr;
    gap: 2rem;
  }

  .belief .section-statement {
    font-size: clamp(1.88rem, 8vw, 3rem);
  }

  .skills,
  .belief {
    min-height: auto;
    padding-block: clamp(6rem, 18vw, 9rem);
  }

  .skills__statement { font-size: clamp(1.8rem, 8vw, 2.8rem); }
}

/* ==========================================================================
   Privacy
   ========================================================================== */
.privacy-page {
  background: var(--color-bg);
  color: var(--color-text);
}

.privacy-page .nav {
  background: color-mix(in srgb, var(--color-bg) 94%, transparent);
}

.privacy-main {
  min-height: 100svh;
  padding: clamp(8.5rem, 13vw, 11rem) 0 clamp(5rem, 9vw, 7rem);
}

.privacy-shell {
  max-width: 860px;
}

.privacy-hero {
  padding-bottom: clamp(3rem, 6vw, 4.5rem);
  border-bottom: 1px solid var(--gray-300);
}

.privacy-title {
  max-width: 13ch;
  color: var(--color-text);
  font-size: clamp(2.6rem, 5.2vw, 4.6rem);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.05em;
}

.privacy-intro {
  max-width: 58ch;
  margin-top: clamp(1.5rem, 3vw, 2.25rem);
  color: var(--gray-500);
  font-size: clamp(1.05rem, 1.7vw, 1.2rem);
  line-height: 1.55;
}

.privacy-updated {
  margin-top: 1rem;
  color: var(--gray-400);
  font-size: var(--fs-small);
}

.privacy-sections {
  display: grid;
  max-width: 720px;
}

.privacy-section {
  padding: clamp(2rem, 4vw, 2.75rem) 0;
  border-top: 1px solid var(--gray-300);
}

.privacy-section:first-child { border-top: 0; }

.privacy-section h2 {
  margin-bottom: 1rem;
  color: var(--color-text);
  font-size: clamp(1.15rem, 1.8vw, 1.35rem);
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.025em;
}

.privacy-section p,
.privacy-section li {
  color: var(--gray-500);
  font-size: 1rem;
  line-height: 1.65;
}

.privacy-section p + p { margin-top: 0.9rem; }

.privacy-section ul {
  display: grid;
  gap: 0.65rem;
  padding-left: 1.15rem;
  list-style: disc;
}

.privacy-section a {
  color: var(--gray-600);
  text-underline-offset: 0.2em;
}

.privacy-section a:hover { color: var(--color-text); }

.privacy-section code {
  overflow-wrap: anywhere;
  color: var(--gray-600);
  font-family: inherit;
  font-size: 0.92em;
}

@media (max-width: 799px) {
  .privacy-title { max-width: none; }
}
