/* ============================================================
   T2Board — marketing site
   Direction: editorial / printed-page. Ivory ground, Smoke ink,
   Teal as the single accent. Hairline rules, sharp corners,
   generous whitespace, terse copy — one idea per block.

   Brand source: talk2view-brand skill.
   - Corner geometry is SQUARE for web/product UI (brand book p19–24).
   - Primary CTA is Smoke-on-Ivory, not white-on-Teal: the book
     specifies a white label on Teal for buttons, but that pairing
     is ~2.1:1 and fails contrast at button size. Smoke on Teal /
     Ivory on Smoke are both brand-sanctioned and pass.
   ============================================================ */

@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url(assets/fonts/SpaceGrotesk.woff2) format('woff2');
}

:root {
  /* ---- Talk2View core palette ---- */
  --teal:      #26C8B8;
  --highlight: #73FFFF;
  --harbor:    #608B8D;
  --mist:      #CAEBEB;
  --ivory:     #F2F0EC;
  --stone:     #9AA4A8;
  --slate:     #67777C;
  --ash:       #354951;
  --smoke:     #021C25;

  /* ---- roles ---- */
  --bg:        var(--ivory);
  --bg-raised: #FBFAF8;
  --bg-sunken: #EAE8E3;
  --ink:       var(--smoke);
  --ink-mute:  var(--slate);
  --ink-faint: var(--stone);
  --rule:      rgba(2, 28, 37, 0.13);
  --rule-soft: rgba(2, 28, 37, 0.07);

  /* ---- type ---- */
  --font: 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  --t-display: clamp(2.75rem, 6.6vw, 5.75rem);
  --t-h2:      clamp(1.9rem, 3.8vw, 3.25rem);
  --t-h3:      clamp(1.2rem, 1.6vw, 1.45rem);
  --t-lead:    clamp(1.05rem, 1.3vw, 1.2rem);
  --t-body:    1.0625rem;
  --t-small:   0.875rem;
  --t-micro:   0.75rem;

  /* ---- rhythm ---- */
  /* Height of the sticky nav. Sections subtract it and snapping offsets
     by it, so the two must stay in step — change it here only. */
  --nav-h:     4.5rem;
  --gutter:    clamp(1.25rem, 5vw, 4.5rem);
  --section-y: clamp(3.5rem, 6.5vw, 5.5rem);
  --maxw:      1240px;
}

/* ============================================================
   Reset & base
   ============================================================ */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Exactly the nav's height, so a snapped section's top edge lands
     flush beneath it. Any larger and every section is pushed down by
     the difference, throwing its centred content off and shunting its
     last stripe below the fold. */
  scroll-padding-top: var(--nav-h);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--t-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* `clip`, not `hidden`: overflow-x:hidden turns body into a scroll
     container, which silently breaks ScrollTrigger's pinning. `clip`
     suppresses the same sideways overflow without that side effect. */
  overflow-x: clip;
}

h1, h2, h3 {
  margin: 0;
  font-weight: 500;
  letter-spacing: -0.028em;
  line-height: 1.03;
  text-wrap: balance;
}

/* figure, blockquote and ul carry chunky UA margins (17px 40px) that
   punch holes in the hairline grids — zero them explicitly. */
p, figure, blockquote, ul { margin: 0; }
p, blockquote { text-wrap: pretty; }
img { max-width: 100%; height: auto; display: block; }
a { color: inherit; }

:focus-visible { outline: 2px solid var(--teal); outline-offset: 3px; }

.shell {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.center { text-align: center; }

/* Available to screen readers, removed from the visual layout. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ============================================================
   Brand motifs
   ============================================================ */

/* Trailing underscore: a short bar, roughly one lowercase char
   wide, at the headline's stem weight, sitting on the baseline.

   A real inline element, not an ::after. SplitText rewraps a
   headline's text into per-line divs, and a pseudo-element on the
   heading itself would render after all of them — orphaning the
   underscore onto a line of its own. As inline content it stays
   with the last word, split or not. */
.u-mark {
  display: inline-block;
  width: 0.42em;
  height: 0.075em;
  margin-left: 0.1em;
  vertical-align: baseline;
  background: var(--teal);
}
.u-mark--smoke { background: var(--smoke); }

/* ============================================================
   Buttons & links
   ============================================================ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6em;
  padding: 0.95em 1.6em;
  border: 1px solid transparent;
  font-family: inherit;
  font-size: var(--t-small);
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.2s ease, transform 0.2s ease;
}
.btn:active { transform: translateY(1px); }

.btn--primary { background: var(--smoke); color: var(--ivory); }
.btn--primary:hover { background: #0a3340; }

.btn--on-teal { background: var(--smoke); color: var(--ivory); }
.btn--on-teal:hover { background: #001219; }

.btn__stack { text-align: left; }
.btn__sub {
  display: block;
  font-size: 0.625rem;
  font-weight: 400;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  opacity: 0.72;
  line-height: 1.25;
}
.btn__main { display: block; font-size: 0.95rem; line-height: 1.2; }

/* Text link with a trailing arrow — used instead of a second button. */
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  font-size: var(--t-small);
  text-decoration: none;
  color: var(--ink);
  border-bottom: 1px solid var(--rule);
  padding-bottom: 0.25em;
  transition: border-color 0.2s ease;
}
.link-arrow::after { content: '→'; transition: transform 0.2s ease; }
.link-arrow:hover { border-bottom-color: var(--teal); }
.link-arrow:hover::after { transform: translateX(3px); }

/* ============================================================
   Nav
   ============================================================ */

.nav {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(242, 240, 236, 0.86);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.25s ease;
}
.nav[data-stuck='true'] { border-bottom-color: var(--rule); }

.nav__inner { display: flex; align-items: center; gap: 2rem; height: 4.5rem; }
/* Product lockup: T2Board app icon + name, with the company credited
   beneath it. The icon keeps its own rounded-square shape — that is the
   mark's geometry, not a UI corner, so the brand's square-corner rule
   for interface elements doesn't apply to it. */
.nav__logo {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-shrink: 0;
  text-decoration: none;
}
.nav__logo img { width: 30px; height: 30px; }

.nav__lockup { display: flex; flex-direction: column; line-height: 1.05; }
.nav__lockup-name {
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--ink);
}
.nav__lockup-by {
  margin-top: 3px;
  font-size: 0.625rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.nav__links {
  display: flex;
  gap: 2rem;
  margin-left: auto;
  list-style: none;
  padding: 0;
}
.nav__links a {
  position: relative;
  font-size: var(--t-small);
  text-decoration: none;
  color: var(--ink-mute);
  transition: color 0.18s ease;
}
.nav__links a::after {
  content: '';
  position: absolute;
  left: 0; bottom: -0.35em;
  width: 100%; height: 1px;
  background: var(--teal);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.25s ease;
}
.nav__links a:hover { color: var(--ink); }
.nav__links a:hover::after { transform: scaleX(1); }

.nav__cta { flex-shrink: 0; padding: 0.7em 1.25em; }

.nav__toggle {
  display: none;
  margin-left: auto;
  padding: 0.5rem;
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--ink);
}

@media (max-width: 820px) {
  .nav__links, .nav__cta { display: none; }
  /* 44px square — the icon is only 22px, but the hit area shouldn't be. */
  .nav__toggle { display: grid; place-items: center; width: 44px; height: 44px; padding: 0; }

  .nav[data-open='true'] .nav__links {
    display: flex;
    position: absolute;
    inset: 4.5rem 0 auto;
    flex-direction: column;
    gap: 0;
    margin: 0;
    padding: 0.5rem var(--gutter) 1.5rem;
    background: var(--bg);
    border-bottom: 1px solid var(--rule);
  }
  .nav[data-open='true'] .nav__links li { border-top: 1px solid var(--rule-soft); }
  .nav[data-open='true'] .nav__links a {
    display: block;
    padding: 1rem 0;
    font-size: 1.05rem;
    color: var(--ink);
  }
  .nav[data-open='true'] .nav__cta {
    display: inline-flex;
    position: absolute;
    inset: auto 0 0;
    margin: 0 var(--gutter) 1.5rem;
  }
}

/* ============================================================
   Section rail
   ============================================================ */

.rail {
  position: fixed;
  right: clamp(1rem, 2vw, 2rem);
  top: 50%;
  transform: translateY(-50%);
  z-index: 90;
  /* Below 1100px the gutter is too tight — the hover labels would run
     over the content. The nav links cover navigation there. */
  display: none;
}
@media (min-width: 1100px) { .rail { display: block; } }

.rail ul {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.45rem;
  list-style: none;
  padding: 0;
}
/* padding-block takes each marker past WCAG 2.5.8's 24px pointer target.
   These are graphical controls, not inline text, so the text-link
   exemption does not cover them. The list gap is reduced to match, so
   the visual rhythm is unchanged. */
.rail a {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  padding-block: 5px;
  text-decoration: none;
}

.rail__label {
  font-size: var(--t-micro);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--ink-mute);
  opacity: 0;
  transform: translateX(6px);
  transition: opacity 0.25s ease, transform 0.25s ease, color 0.25s ease;
}

/* The brand dash: sharp rectangle, never a dot or a pill. */
.rail__dash {
  display: block;
  width: 18px;
  height: 2px;
  background: var(--stone);
  transition: width 0.35s cubic-bezier(0.22, 1, 0.36, 1),
              background 0.25s ease;
}

.rail a:hover .rail__dash,
.rail a:focus-visible .rail__dash { width: 26px; background: var(--ink); }
.rail a:hover .rail__label,
.rail a:focus-visible .rail__label { opacity: 1; transform: none; color: var(--ink); }

.rail a[aria-current='true'] .rail__dash { width: 34px; background: var(--teal); }
.rail a[aria-current='true'] .rail__label { opacity: 1; transform: none; color: var(--ink); }

/* Over the teal CTA panel, Stone and Teal both disappear into the
   background — swap to Smoke, which the brand pairs with Teal at 8.4:1. */
.rail[data-invert='true'] .rail__dash { background: rgba(2, 28, 37, 0.4); }
.rail[data-invert='true'] a:hover .rail__dash,
.rail[data-invert='true'] a[aria-current='true'] .rail__dash { background: var(--smoke); }
.rail[data-invert='true'] .rail__label { color: var(--smoke); }

/* ============================================================
   Next-section button
   ============================================================ */

.next {
  position: fixed;
  left: 50%;
  bottom: clamp(1.25rem, 2.5vw, 2rem);
  transform: translateX(-50%);
  z-index: 90;
  display: inline-flex;
  align-items: center;
  gap: 0.65em;
  padding: 0.8em 1.2em;
  background: var(--smoke);
  color: var(--ivory);
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  transition: opacity 0.35s ease, visibility 0.35s ease,
              transform 0.25s ease, background 0.2s ease;
}
.next:hover { background: #0a3340; }
.next svg { transition: transform 0.25s ease; }
.next:hover svg { transform: translateY(2px); }

/* Faded rather than display:none so it can transition, and
   visibility keeps it out of the tab order while hidden. */
.next[data-hidden='true'] {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* On the teal panel the Smoke button still passes, but the last
   section hides the button anyway. */
@media (max-width: 600px) {
  .next { display: none; }
}

/* ============================================================
   Hero
   ============================================================ */

/* Light padding only. The section is full-height and centres its own
   content, so generous padding here just pushes past one screen. */
.hero { padding-block: 2rem; }

.hero__grid {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}

.hero__title { font-size: var(--t-display); }

.hero__lead {
  max-width: 30ch;
  margin-top: 1.5rem;
  font-size: var(--t-lead);
  color: var(--ink-mute);
}

.hero__actions { margin-top: 2.25rem; }

.proof {
  margin-top: 1.5rem;
  font-size: var(--t-small);
  color: var(--ink-mute);
}

.phone { position: relative; justify-self: center; width: min(100%, 320px); }
.phone img { width: 100%; border: 1px solid var(--rule); background: var(--bg-raised); }
/* Teal keyline behind the device — the one flourish in the hero. */
.phone::before {
  content: '';
  position: absolute;
  inset: auto -1.25rem -1.25rem auto;
  width: 60%; height: 52%;
  border-right: 1px solid var(--teal);
  border-bottom: 1px solid var(--teal);
  opacity: 0.45;
  z-index: -1;
}

@media (max-width: 900px) {
  .hero__grid { grid-template-columns: 1fr; }
  .hero__lead { max-width: 42ch; }
  /* Headline leads on mobile — the device follows, so the value
     proposition sits above the fold rather than the screenshot.
     250px, not 290: the taller device pushed the hero past one phone
     screen, and the section is meant to own its screen. */
  .phone { width: min(100%, 250px); margin-top: 0.75rem; }
  .phone::before { display: none; }
}

/* ============================================================
   Touch targets

   WCAG 2.5.8 and Apple's HIG both want ~44px. These are text links
   that render around 22px tall, so the hit area is padded without
   moving the text itself.
   ============================================================ */

@media (hover: none), (max-width: 900px) {
  .footer__links a,
  .link-arrow { padding-block: 0.7rem; }
  .footer__bottom a { padding-block: 0.95rem; }
  .next { padding-block: 1.05em; }
  .footer__links { gap: 0 1.75rem; }
  .footer__col li + li { margin-top: 0; }

  /* Logo links wrap a ~23px image; pad the anchor, not the mark. */
  .nav__logo, .footer__brand { padding-block: 0.7rem; }
}

/* ============================================================
   Showcase — one big centred idea, device bleeding off the panel
   ============================================================ */

/* No padding of its own — the next section's top padding supplies the
   gap, otherwise the two stack and the page opens a canyon. */
.showcase { padding-block: 0; }

.showcase__panel {
  padding: clamp(3rem, 6vw, 5.5rem) var(--gutter) 0;
  background: var(--bg-sunken);
  text-align: center;
  overflow: hidden;
}

.showcase__title { font-size: var(--t-h2); }
.showcase__lead {
  max-width: 40ch;
  margin: 1.25rem auto 0;
  color: var(--ink-mute);
}

/* Fixed height shorter than the image, so the device runs off the
   bottom edge of the panel rather than stopping inside it. */
.showcase__media {
  display: flex;
  justify-content: center;
  height: clamp(200px, 26vw, 300px);
  margin-top: clamp(2.5rem, 4vw, 3.5rem);
}
.showcase__media img {
  width: clamp(230px, 26vw, 300px);
  height: auto;
  align-self: start;
}

/* ============================================================
   One section per screen

   Each band fills the viewport and centres its own content, so you
   never see the tail of one section and the head of the next at the
   same time. min-height, not height: a section whose content is
   taller than the screen (the stacked cards on a phone) simply grows
   instead of clipping.

   svh, not vh: on mobile browsers vh is measured against the LARGEST
   viewport, so 100vh sections sit taller than the visible area while
   the URL bar is showing. svh tracks the smallest, so nothing hides
   behind browser chrome.
   ============================================================ */

.hero,
.showcase,
.section,
.cta {
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* The screen a section gets is the viewport MINUS the sticky nav that
     sits over it. Using the full viewport height centred content against
     space the nav was already covering, leaving everything sitting low. */
  min-height: calc(100vh - var(--nav-h));
  min-height: calc(100svh - var(--nav-h));
}

/* Snapping is proximity, never mandatory. Mandatory hijacks trackpad
   scrolling and can strand a user inside a section taller than their
   screen. Proximity lands cleanly when you stop near a boundary and
   otherwise stays out of the way.

   Desktop-only. On a phone the hero (headline, lead, button and device)
   and the stacked feature cards genuinely exceed a screen, and snapping
   a section taller than the viewport drags the reader back to its top
   every time they try to reach the rest. Phones scroll normally; the
   full-height floor still keeps sections from crowding each other. */
@media (min-width: 821px) and (min-height: 640px) and (prefers-reduced-motion: no-preference) {
  html { scroll-snap-type: y proximity; }
  .hero, .showcase, .section, .cta { scroll-snap-align: start; }
}

/* ============================================================
   Section furniture
   ============================================================ */

/* Same reasoning as .hero — full-height sections supply the rhythm, so
   this is a floor for when content outgrows the screen, not spacing. */
.section { padding-block: clamp(2.5rem, 4vw, 3.5rem); }

.section__head { margin-bottom: clamp(2.5rem, 4vw, 3.5rem); }
.section__head--center { text-align: center; }

/* Measure is set per-element, not on the wrapper: `ch` resolves against
   each element's own font-size, so one wrapper width would strangle the
   big headline while leaving the small lead too wide. */
.section__title { font-size: var(--t-h2); max-width: 20ch; }
.section__lead { margin-top: 1.1rem; max-width: 44ch; font-size: var(--t-lead); color: var(--ink-mute); }
.section__head--center .section__title,
.section__head--center .section__lead { margin-inline: auto; }

/* ============================================================
   Feature cards — media on top, one line of copy
   ============================================================ */

.bento {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
}

.card {
  display: flex;
  flex-direction: column;
  padding: clamp(1.75rem, 3vw, 2.5rem);
  background: var(--bg-raised);
  text-align: center;
  transition: background 0.3s ease;
}
.card:hover { background: #fff; }

.card__media {
  height: clamp(180px, 20vw, 240px);
  margin-bottom: clamp(1.75rem, 3vw, 2.5rem);
  overflow: hidden;
  display: flex;
  justify-content: center;
}
.card__media img {
  width: clamp(160px, 17vw, 200px);
  height: auto;
  align-self: start;
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.card:hover .card__media img { transform: translateY(-6px); }

.card__title { font-size: var(--t-h3); }
.card__text { margin-top: 0.6rem; color: var(--ink-mute); font-size: var(--t-small); }

@media (max-width: 820px) {
  .bento { grid-template-columns: 1fr; }
}

/* ============================================================
   What's new — draggable ticker
   ============================================================ */

.ticker {
  margin-bottom: clamp(2rem, 4vw, 3rem);
  cursor: grab;
  overflow: hidden;
  -webkit-user-select: none;
  user-select: none;
}
.ticker[data-dragging='true'] { cursor: grabbing; }

.ticker__track {
  display: flex;
  padding-inline: var(--gutter);
  border-block: 1px solid var(--rule);
  will-change: transform;
}

/* Divider comes from a border, not the gap-over-background trick —
   that trick would paint grey into the gutter padding here. */
.release {
  flex: 0 0 clamp(240px, 25vw, 310px);
  padding: clamp(1.5rem, 2.5vw, 2rem);
  background: var(--bg-raised);
  border-right: 1px solid var(--rule);
}
.release:first-child { border-left: 1px solid var(--rule); }

.release__meta {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-size: var(--t-micro);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink);
  font-weight: 500;
}
.release__dot { width: 6px; height: 6px; background: var(--teal); flex-shrink: 0; }
.release__title { margin-top: 1.25rem; font-size: 1.1rem; }
.release__text { margin-top: 0.5rem; font-size: var(--t-small); color: var(--ink-mute); }

/* ============================================================
   Reviews — copy left, staggered cards right
   ============================================================ */

.voices {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: clamp(2.5rem, 6vw, 5rem);
  align-items: center;
}
.voices__intro .link-arrow { margin-top: 2rem; }

.voices__cards { display: flex; flex-direction: column; gap: 1.25rem; }

.quote {
  padding: clamp(1.5rem, 2.5vw, 2.25rem);
  background: var(--bg-raised);
  border: 1px solid var(--rule);
}
/* Offset the second card so the pair reads as a conversation
   rather than a stack. */
.quote--offset { margin-left: clamp(1.5rem, 6vw, 4rem); }

.quote__text { font-size: 1.05rem; line-height: 1.45; }
.quote__by { margin-top: 1.5rem; font-size: var(--t-small); color: var(--ink-faint); }
.quote__by strong { display: block; color: var(--ink); font-weight: 500; }

@media (max-width: 900px) {
  .voices { grid-template-columns: 1fr; }
  .quote--offset { margin-left: 0; }
}

/* ============================================================
   Closing CTA — Teal wind-field panel
   ============================================================ */

/* Plain Teal field — the wind-field dash pattern was removed here.
   The generator lived in main.js and is recoverable from git if it is
   ever wanted back (see commit 3f8aa4d and earlier). */
.cta { position: relative; overflow: hidden; background: var(--teal); color: var(--smoke); }

.cta__grid {
  display: grid;
  grid-template-columns: 1fr 0.85fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
  padding-block: clamp(2.5rem, 5vw, 4rem);
}

/* White display type on Teal is sanctioned for large headlines only. */
.cta__title { font-size: var(--t-h2); color: #fff; }
.cta__lead { max-width: 32ch; margin-top: 1.25rem; color: var(--smoke); opacity: 0.85; }
.cta__actions { margin-top: 2.25rem; }

.cta__aside {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(1.5rem, 2.5vw, 2rem);
}

/* Ivory surface so the Talk2View lockup keeps its contrast — both
   official variants use a teal glyph, which would vanish on the Teal
   field. Square corners, per the brand's web/product UI rule. */
.cta__brandcard {
  display: grid;
  place-items: center;
  width: min(100%, 320px);
  padding: clamp(2.25rem, 4vw, 3.25rem) clamp(1.5rem, 3vw, 2.5rem);
  background: var(--ivory);
}
.cta__brandcard img { width: min(100%, 210px); height: auto; }

/* Cross-link to the parent product. Deliberately quieter than the badge —
   it is the secondary path out of this section, and the download is the
   primary one. Smoke on Teal is the brand's safe pairing at 8.4:1. */
.cross-link {
  display: block;
  max-width: 300px;
  padding-top: 1.25rem;
  border-top: 1px solid rgba(2, 28, 37, 0.25);
  color: var(--smoke);
  text-decoration: none;
}
.cross-link__eyebrow {
  display: block;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  opacity: 0.7;
}
.cross-link__text {
  display: block;
  margin-top: 0.6rem;
  font-size: var(--t-small);
  line-height: 1.45;
  opacity: 0.9;
}
.cross-link__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  margin-top: 0.9rem;
  font-size: var(--t-small);
  font-weight: 500;
  border-bottom: 1px solid rgba(2, 28, 37, 0.35);
  padding-bottom: 0.15em;
  transition: border-color 0.2s ease;
}
.cross-link__cta::after { content: '↗'; transition: transform 0.2s ease; }
.cross-link:hover .cross-link__cta { border-bottom-color: var(--smoke); }
.cross-link:hover .cross-link__cta::after { transform: translate(2px, -2px); }

@media (max-width: 900px) {
  .cta__grid { grid-template-columns: 1fr; text-align: center; }
  .cta__lead { margin-inline: auto; }
  .cta__actions { display: flex; justify-content: center; }
  .cta__brandcard { width: min(100%, 280px); }
  .cross-link { text-align: center; }
  .cross-link__cta { margin-inline: auto; }
}

/* ============================================================
   App Store badge

   Apple's own artwork, used unmodified per their identity guidelines:
   never recoloured, redrawn or restyled, and given clear space of at
   least 10% of its height. The link is padded rather than the image so
   the hit area is comfortable without encroaching on that clear space.
   ============================================================ */

.badge {
  display: inline-block;
  padding: 4px;
  line-height: 0;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.badge img { width: auto; height: 44px; }
.badge:hover { opacity: 0.85; }
.badge:active { transform: translateY(1px); }

/* ============================================================
   Footer
   ============================================================ */

.footer {
  padding-block: clamp(3rem, 5vw, 4rem) 2rem;
  background: var(--smoke);
  color: var(--ivory);
}
.footer__top {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem 2rem;
  align-items: center;
  justify-content: space-between;
  padding-bottom: 2rem;
  border-bottom: 1px solid rgba(242, 240, 236, 0.13);
}
.footer__brand img { height: 1.5rem; width: auto; }

.footer__links { display: flex; flex-wrap: wrap; gap: 1.5rem 2rem; }
.footer__links a {
  font-size: var(--t-small);
  color: var(--ivory);
  text-decoration: none;
  transition: color 0.18s ease;
}
.footer__links a:hover { color: var(--highlight); }

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 2rem;
  justify-content: space-between;
  padding-top: 1.5rem;
  font-size: var(--t-micro);
  color: var(--stone);
}
.footer__bottom a { text-decoration: none; }
.footer__bottom a:hover { color: var(--highlight); }

/* ============================================================
   Scroll reveal
   ============================================================ */

/* Hidden only when JS is present to animate them back in — otherwise a
   script failure would leave the whole page blank. main.js sets .js on
   <html> as its first statement. GSAP owns the transform from there. */
.js [data-reveal] { opacity: 0; }

/* SplitText masks each line in its own clipping wrapper so lines can
   rise from behind their own baseline. */
.split-line { display: block; overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  /* Belt and braces — main.js also bails out of every animation. */
  .js [data-reveal] { opacity: 1 !important; transform: none !important; }
}
