/* ============================================================
   Luma — Shopping Made Accessible
   Design tokens adapted from the Acctual style reference:
   white canvas, near-monochrome text, one chromatic accent (#6054F3).
   ============================================================ */

:root {
  /* Brand */
  --brand:        #6054F3;   /* primary accent — 5.17:1 on white */
  --brand-strong: #4B3ED9;   /* hover / small text on white */
  --brand-deep:   #241E63;   /* dark panel base */
  --brand-tint:   #F2F1FE;   /* tinted surface */
  --brand-tint-2: #E4E1FD;   /* tinted surface, stronger */
  --brand-cyan:   #01A5E6;   /* secondary — the sparkle in the logo mark */

  /* ── Accent hues ───────────────────────────────────────
     Tuned for punch, not for maximum contrast. Everywhere
     these are used is either large bold text (the 19px and
     21px card titles) or a non-text glyph, so the bar is 3:1
     — which leaves room to run the hues bright.

     Link Blue, Plum Violet and the brand purple clear 3:1 at
     their full palette values, so they are used raw. Grass
     Green and Coral Pink do not (2.18, 2.89 on white — washed
     out for anyone, not just a compliance technicality), so
     those two are the brightest version of the hue that still
     holds up.

     Gold has no usable version at all. Yellow carries too much
     luminance to reach 3:1, so darkening it far enough turns it
     brown (#BC7F00, the old value) rather than gold. The warm
     accent is therefore an orange, which gets to stay vivid.
     ──────────────────────────────────────────────────── */
  --tone-blue:   #0086FC;   /* Link Blue, raw      — 3.61 on white */
  --tone-green:  #009E61;   /* from Grass Green    — 3.46 */
  --tone-orange: #E96A00;   /* replaces Gold       — 3.22 */
  --tone-pink:   #EA489B;   /* from Coral Pink     — 3.55 */
  --tone-violet: #9F4FFF;   /* Plum Violet, raw    — 4.17 */

  /* Matching chip fills */
  --tint-blue:   #E2F0FF;
  --tint-green:  #DEF5EA;
  --tint-orange: #FFEBD9;
  --tint-pink:   #FFE7F1;
  --tint-violet: #F4E9FF;

  /* Neutrals */
  --ink:      #16151D;
  --ink-soft: #2B2A36;
  --muted:    #5B5A69;
  --muted-2:  #7C7B88;
  --line:     #E6E5EE;
  --line-2:   #F0EFF5;

  /* Surfaces */
  --paper: #FFFFFF;
  --snow:  #FAFAFC;

  /* Type */
  --font: "Manrope", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  /* Radii */
  --r-pill:  999px;
  --r-card:  20px;
  --r-panel: 32px;
  --r-sm:    12px;

  /* Shadows */
  --shadow-sm:  0 1px 2px rgba(16, 15, 30, .04);
  --shadow-md:  0 2px 6px rgba(16, 15, 30, .05), 0 8px 24px rgba(16, 15, 30, .05);
  --shadow-lg:  0 8px 20px rgba(16, 15, 30, .06), 0 30px 60px rgba(16, 15, 30, .09);

  /* Layout */
  --wrap: 1160px;
  --gutter: clamp(20px, 5vw, 40px);
  --section-y: clamp(72px, 9vw, 128px);

  /* The sticky header's own height — 14px of padding either side of a
     47px nav row. Anchor scrolling subtracts it so a jumped-to heading
     lands below the bar rather than underneath it. */
  --header-h: 75px;
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Clears the sticky header, plus a little breathing room, for every
     anchor jump and for keyboard focus scrolled in from off-screen. */
  scroll-padding-top: calc(var(--header-h) + 16px);
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.6;
  font-weight: 500;
  letter-spacing: -0.011em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3 { margin: 0; font-weight: 800; line-height: 1.14; letter-spacing: -0.032em; }
p  { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, svg { display: block; max-width: 100%; }

a { color: inherit; text-decoration: none; }

/* ── Focus ─────────────────────────────────────────────── */
:where(a, button, [tabindex]):focus-visible {
  outline: 3px solid var(--brand);
  outline-offset: 3px;
  border-radius: 6px;
}

.skip-link {
  position: absolute;
  left: 16px; top: -80px;
  z-index: 999;
  padding: 12px 20px;
  background: var(--ink);
  color: #fff;
  border-radius: var(--r-pill);
  font-weight: 700;
  transition: top .18s ease;
}
.skip-link:focus { top: 16px; }

/* ── Layout helpers ────────────────────────────────────── */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--section-y); }

.section--tint {
  background: var(--snow);
  border-block: 1px solid var(--line-2);
}

.section__head {
  max-width: 720px;
  margin-inline: auto;
  text-align: center;
  margin-bottom: clamp(40px, 5vw, 64px);
}

.section__title {
  font-size: clamp(30px, 4.2vw, 46px);
  margin-block: 14px 0;
  text-wrap: balance;
}

.section__sub {
  margin-top: 18px;
  color: var(--muted);
  font-size: clamp(16px, 1.5vw, 18px);
  text-wrap: pretty;
}

.eyebrow {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--brand);
}
.eyebrow--on-dark { color: #C6C0FF; }

/* ── Header / nav ──────────────────────────────────────── */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  padding: 14px var(--gutter);
  background: rgba(255, 255, 255, .82);
  backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid transparent;
}
.site-header.is-stuck { border-bottom-color: var(--line); }

.nav-shell { max-width: var(--wrap); margin-inline: auto; }

.nav {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}

.brand { display: inline-flex; align-items: center; margin-right: auto; }
.brand__logo { width: auto; height: 34px; }
.brand__logo--sm { height: 30px; }

.nav__toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 10px 18px;
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  cursor: pointer;
}
.nav__toggle-bars { display: grid; gap: 4px; }
.nav__toggle-bars span {
  display: block; width: 16px; height: 2px;
  background: currentColor; border-radius: 2px;
}

.nav__menu {
  display: flex;
  align-items: center;
  gap: 8px;
}

.nav__links { display: flex; align-items: center; gap: 2px; }

.nav__links a {
  display: inline-block;
  padding: 9px 14px;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink-soft);
  border-radius: var(--r-pill);
}
.nav__links a:hover { background: var(--brand-tint); color: var(--brand-strong); }

.nav__cta { margin-left: 8px; }

@media (max-width: 899px) {
  .nav__menu {
    display: none;
    flex-basis: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
    padding-top: 14px;
    margin-top: 12px;
    border-top: 1px solid var(--line);
  }
  .nav__menu.is-open { display: flex; }
  .nav__links { flex-direction: column; align-items: stretch; gap: 2px; }
  .nav__links a { padding: 12px 14px; font-size: 16px; }
  .nav__cta { margin-left: 0; text-align: center; }
}
@media (min-width: 900px) {
  .nav__toggle { display: none; }
}

/* ── Buttons ───────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-weight: 700;
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  cursor: pointer;
  transition: background-color .16s ease, color .16s ease, transform .16s ease;
}
.btn--primary { background: var(--ink); color: #fff; box-shadow: var(--shadow-sm); }
.btn--primary:hover { background: var(--brand); }
.btn--sm { min-height: 44px; padding: 10px 22px; font-size: 15px; }

/* ── Store badges ──────────────────────────────────────── */
.store-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 32px;
}

.store-badge {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  min-height: 56px;
  padding: 9px 22px 9px 18px;
  background: var(--ink);
  color: #fff;
  border-radius: 14px;
  border: 1px solid var(--ink);
  transition: background-color .16s ease, border-color .16s ease;
}
.store-badge:hover { background: #000; }

.store-badge__glyph { width: 24px; height: 24px; flex: none; }
.store-badge__text { display: grid; line-height: 1.15; text-align: left; }
.store-badge__pre  { font-size: 11px; font-weight: 600; opacity: .78; letter-spacing: .01em; }
.store-badge__name { font-size: 17px; font-weight: 800; letter-spacing: -0.02em; }

.store-badge--light {
  background: #fff;
  color: var(--ink);
  border-color: #fff;
}
.store-badge--light:hover { background: #F1F0FF; border-color: #F1F0FF; }

.store-note {
  align-self: center;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--muted-2);
}
.store-note--on-dark {
  align-self: auto;
  margin-top: 16px;
  color: rgba(255, 255, 255, .66);
}

/* ── Hero ──────────────────────────────────────────────── */
.hero {
  padding-block: clamp(48px, 7vw, 88px) clamp(56px, 7vw, 96px);
  overflow: hidden;
}

.hero__grid {
  display: grid;
  gap: clamp(48px, 6vw, 72px);
  align-items: center;
}
@media (min-width: 980px) {
  .hero__grid { grid-template-columns: 1.05fr .95fr; }
}

.hero__title {
  font-size: clamp(38px, 5.2vw, 60px);
  margin-block: 18px 0;
  letter-spacing: -0.038em;
  text-wrap: balance;
}

/* Sits straight under the headline now that the lead is gone, so it needs
   more air above it and a size that can carry the whole standfirst. */
.hero__caption {
  margin-top: 24px;
  max-width: 42ch;
  font-size: clamp(17px, 1.7vw, 20px);
  color: var(--muted);
  text-wrap: pretty;
}

.hero__note {
  margin-top: 20px;
  font-size: 14px;
  color: var(--muted-2);
  max-width: 62ch;   /* the short form fits one line at this column width */
}

/* ── Hero stage ────────────────────────────────────────────
   Per the reference: a soft rounded panel with the device
   dropped into it and cropped by the bottom edge, and one card
   of real UI floating clear of the panel's lower-left corner.

   The panel clips (that crop is the point), so the floating
   card can't live inside it — it is a sibling positioned
   against .hero__visual instead.
   ────────────────────────────────────────────────────────── */
.hero__visual { position: relative; }

.hero__stage {
  position: relative;
  display: grid;
  justify-items: center;
  align-content: start;
  overflow: hidden;
  aspect-ratio: 1 / 1.12;
  padding: clamp(34px, 8%, 66px) 20px 0;
  background: #F6F6F8;
  border-radius: clamp(28px, 3vw, 40px);
}
/* Light pooling behind the device so the panel isn't a flat swatch. */
.hero__stage::before {
  content: "";
  position: absolute;
  left: 50%; top: 4%;
  width: 86%;
  aspect-ratio: 1;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, .9), transparent 66%);
  pointer-events: none;
}

/* Above the panel's light pool. Its --w lives with the other .iphone
   sizes further down, so there is one place that sets device widths. */
.iphone--hero { position: relative; z-index: 1; }

/* Single column: the panel is the full content width, so it gets an
   explicit height rather than a square-ish ratio. */
@media (max-width: 979px) {
  .hero__stage { aspect-ratio: auto; height: clamp(360px, 96vw, 520px); }
}

/* ── "Just say" bubble ─────────────────────────────────────
   A speech bubble, which is a shape the page uses nowhere else,
   and the only animated thing in the hero besides the screens.
   ────────────────────────────────────────────────────────── */
.ask {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  width: fit-content;
  max-width: 100%;
  margin-top: 28px;
  padding: 14px 18px 14px 14px;
  border-radius: 20px;
  background: var(--paper);
  border: 1px solid var(--brand-tint-2);
  box-shadow: 0 1px 2px rgba(16, 15, 30, .05), 0 14px 30px rgba(96, 84, 243, .11);
}
/* Tail, a rotated square tucked under the bottom edge so its two lit
   sides carry on from the bubble's own border without a seam. */
.ask::after {
  content: "";
  position: absolute;
  left: 32px; bottom: -7px;
  width: 13px; height: 13px;
  background: var(--paper);
  border-right: 1px solid var(--brand-tint-2);
  border-bottom: 1px solid var(--brand-tint-2);
  border-bottom-right-radius: 3px;
  transform: rotate(45deg);
}

.ask__mic {
  display: grid;
  place-items: center;
  width: 40px; height: 40px;
  flex: none;
  border-radius: 50%;
  background: var(--brand);
  color: #fff;
}
.ask__mic svg { width: 21px; height: 21px; }

.ask__body { min-width: 0; }
.ask__label {
  margin-bottom: 3px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--brand);
}

/* One grid cell, every phrasing stacked in it — the bubble sizes to the
   longest one and cannot change height mid-cycle. */
.ask__lines {
  display: grid;
  font-size: clamp(15px, 1.5vw, 17px);
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--ink);
}
.ask__lines li {
  grid-area: 1 / 1;
  opacity: 0;
  animation: ask-cycle 13.5s ease-in-out infinite;
}
.ask__lines li:nth-child(1) { animation-delay: 0s; }
.ask__lines li:nth-child(2) { animation-delay: 4.5s; }
.ask__lines li:nth-child(3) { animation-delay: 9s; }

/* Holds ~3.6s, then swaps. Each line rises in and leaves upward, so the
   set reads as one voice carrying on rather than three cards shuffling. */
@keyframes ask-cycle {
  0%   { opacity: 0; transform: translateY(7px); }
  3%   { opacity: 1; transform: none; }
  30%  { opacity: 1; transform: none; }
  34%  { opacity: 0; transform: translateY(-7px); }
  100% { opacity: 0; transform: translateY(7px); }
}

.ask__wave {
  display: flex;
  align-items: center;
  gap: 3px;
  flex: none;
  height: 26px;
}
.ask__wave i {
  width: 3px;
  height: 100%;
  border-radius: var(--r-pill);
  background: color-mix(in oklab, var(--brand) 50%, #fff);
  transform: scaleY(.26);
  animation: ask-wave 1.15s ease-in-out infinite;
}
.ask__wave i:nth-child(1) { animation-delay: -.9s; }
.ask__wave i:nth-child(2) { animation-delay: -.5s; }
.ask__wave i:nth-child(3) { animation-delay: -.15s; }
.ask__wave i:nth-child(4) { animation-delay: -.7s; }
.ask__wave i:nth-child(5) { animation-delay: -.3s; }
.ask__wave i:nth-child(6) { animation-delay: -.95s; }
.ask__wave i:nth-child(7) { animation-delay: -.45s; }

@keyframes ask-wave {
  0%, 100% { transform: scaleY(.26); }
  50%      { transform: scaleY(1); }
}

/* Lifted onto the device and overhanging the panel's lower-left corner,
   the way the reference's send-card overhangs its own panel. Anchored
   with left+right rather than a width, so it spans a fixed share of the
   panel instead of needing a magic number per breakpoint.

   Has to come after .ask — same specificity, so the later rule wins and
   the base would otherwise put it back in flow. */
.ask--float {
  position: absolute;
  z-index: 2;
  left: clamp(-56px, -8%, -22px);
  right: clamp(34px, 19%, 110px);
  bottom: clamp(22px, 7%, 54px);
  margin-top: 0;
  width: auto;
  max-width: none;
}

/* Single column: no gutter left to overhang into, so it tucks inside
   the panel's corner instead. */
@media (max-width: 979px) {
  .ask--float {
    left: clamp(10px, 4%, 26px);
    right: clamp(10px, 4%, 26px);
    bottom: clamp(14px, 4%, 26px);
  }
}

/* ── iPhone mockup ─────────────────────────────────────────
   Graphite frame art from assets/iphone-frame.png (644 × 1324,
   transparent screen cut-out). The side buttons and the Dynamic
   Island — green indicator and lens included — are painted into
   the asset, so there is no markup for them.

   Geometry measured off the asset's alpha channel: the cut-out
   sits at 4.969% / 2.115% and its corners are one of Apple's
   continuous curves, not a circle (the arc fits r=96px across
   and r=91px down). CSS can only draw an elliptical corner, so
   rather than chase the shape the screen is pushed a little way
   under the frame and the frame's opaque corners mask it.
   ────────────────────────────────────────────────────────── */
.iphone {
  --w: min(300px, 78vw);
  position: relative;
  width: var(--w);
  aspect-ratio: 644 / 1324;   /* the asset's own ratio */
  /* traces the frame's alpha, so the shadow takes the phone's shape */
  filter: drop-shadow(0 14px 28px rgba(18, 18, 18, .16))
          drop-shadow(0 40px 60px rgba(18, 18, 18, .12));
}
/* Percentage, not vw: the hero device is sized against its stage panel. */
.iphone--hero { --w: min(292px, 66%); }

/* Frame art, above the screen so it trims the screen's square corners */
.iphone::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  background: url("assets/iphone-frame.png") center / 100% 100% no-repeat;
  pointer-events: none;
}

.iphone__screen {
  position: absolute;
  /* ~1% proud of the cut-out on each side, so no seam can open up */
  inset: 1.5% 4.2%;
  z-index: 1;
  overflow: hidden;
  display: grid;
  place-items: center;
  border-radius: calc(var(--w) * 0.142);
  background: #000;
}
.iphone__screen > img { width: 100%; height: 100%; object-fit: cover; }

/* ── Hero screen slideshow ─────────────────────────────────
   Three app screens crossfading on a 12s loop, 4s each. The
   frames stack absolutely so no layout shift can happen, and
   each one's animation-delay is its slot in the cycle.
   See the reduced-motion block for the static fallback — it
   needs an explicit override, not just a shorter duration.
   ────────────────────────────────────────────────────────── */
.screen-slides { position: relative; width: 100%; height: 100%; }
.screen-slides img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  animation: screen-cycle 12s ease-in-out infinite;
}
.screen-slides img:nth-child(1) { animation-delay: 0s; }
.screen-slides img:nth-child(2) { animation-delay: 4s; }
.screen-slides img:nth-child(3) { animation-delay: 8s; }

/* Holds ~3.2s, then a ~0.4s crossfade into the next frame */
@keyframes screen-cycle {
  0%   { opacity: 0; }
  3%   { opacity: 1; }
  30%  { opacity: 1; }
  36%  { opacity: 0; }
  100% { opacity: 0; }
}


/* ── Trust strip ───────────────────────────────────────── */
.strip {
  padding-block: 22px;
  background: var(--snow);
  border-block: 1px solid var(--line-2);
}

.strip__row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px clamp(20px, 4vw, 44px);
}

.strip__item {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 14px;
  font-weight: 600;
  color: var(--muted);
}
.strip__dot {
  width: 7px; height: 7px; flex: none;
  border-radius: 50%;
  background: var(--brand);
}

/* ── Services bento ────────────────────────────────────────
   A pinwheel of four tiles — 7/5 across the first row, 5/7 across the
   second — instead of three equal columns. Every tile carries a single
   --accent that drives its fill, border, badge and corner watermark, so
   recolouring a tile is a one-line change. */

.sprite { display: none; }

/* Title left, standfirst dropped to the right and bottom-aligned, so the
   head sits off-centre against the grid below it. */
.bento__head {
  display: grid;
  gap: 18px;
  margin-bottom: clamp(32px, 4vw, 52px);
}
@media (min-width: 900px) {
  .bento__head {
    grid-template-columns: 1.15fr .85fr;
    gap: 56px;
    align-items: end;
  }
}

.eyebrow--pill {
  display: inline-block;
  padding: 7px 14px;
  border-radius: var(--r-pill);
  background: var(--brand-tint);
  color: var(--brand-strong);
}

.bento__title {
  font-size: clamp(32px, 5vw, 54px);
  line-height: 1.06;
  letter-spacing: -.02em;
  margin-top: 16px;
  text-wrap: balance;
}

/* Highlighter stroke. A gradient sized to the bottom third of the line
   box and cloned across fragments, so it follows the text when the
   heading wraps rather than sitting behind one flat rectangle. */
.mark {
  font-style: normal;
  padding-inline: .06em;
  background-image: linear-gradient(100deg, rgba(255, 88, 174, .45), rgba(255, 178, 0, .45));
  background-repeat: no-repeat;
  background-size: 100% 32%;
  background-position: 0 86%;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

.bento__sub {
  color: var(--muted);
  font-size: clamp(16px, 1.5vw, 18px);
  text-wrap: pretty;
  padding-bottom: 8px;
}

.bento { display: grid; gap: 18px; }
.bento__list { display: contents; }

@media (min-width: 720px) {
  .bento { grid-template-columns: repeat(12, 1fr); }
  .tile { grid-column: span 6; }
}
@media (min-width: 1000px) {
  .tile       { grid-column: span 5; }
  .tile--wide { grid-column: span 7; }
  .tile--note { grid-column: span 7; }
}

.tile {
  --accent: var(--brand);
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  padding: clamp(24px, 2.6vw, 34px);
  border-radius: 26px;
  background: color-mix(in oklab, var(--accent) 14%, #fff);
  border: 1px solid color-mix(in oklab, var(--accent) 30%, #fff);
  transition: transform .22s cubic-bezier(.2, .8, .3, 1), box-shadow .22s ease;
}
.tile:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }

.tile--blue  { --accent: var(--tone-blue); }
.tile--green { --accent: var(--tone-green); }
.tile--pink  { --accent: var(--tone-pink); }

/* Oversized icon bleeding off the top-right corner, clipped by the tile.
   A span rather than the icon itself, so the Lottie stage can mount into
   it the same way it does in the badge. */
.tile__glyph {
  display: block;
  position: absolute;
  top: -28px; right: -32px;
  /* Sized against the tile, so it stays a corner mark on a narrow tile
     instead of washing across the copy. */
  width: clamp(148px, 42%, 210px);
  aspect-ratio: 1;
  height: auto;
  color: var(--accent);
  opacity: .17;
  transform: rotate(-10deg);
  transition: transform .38s cubic-bezier(.2, .8, .3, 1), opacity .38s ease;
  pointer-events: none;
}
.tile:hover .tile__glyph { transform: rotate(3deg) scale(1.08); opacity: .24; }

.tile__icon {
  position: relative;
  display: grid;
  place-items: center;
  width: 54px; height: 54px;
  margin-bottom: 22px;
  border-radius: 17px;
  background: var(--paper);
  color: var(--accent);
  box-shadow: 0 2px 10px rgba(16, 15, 30, .08);
  transform: rotate(-7deg);
  transition: transform .28s cubic-bezier(.2, .8, .3, 1);
}
.tile:hover .tile__icon { transform: rotate(0deg) scale(1.06); }
.tile__icon svg { width: 27px; height: 27px; }

/* Animated tile icons, same swap as the chips above. Both slots load the
   same file baked to the tile's accent — the badge plays it, the corner
   watermark holds frame 0 so the two never show different drawings, and
   its own opacity is what makes it read as a watermark. Both stages run
   wider than the inline icon: the Lottie art carries its own padding
   inside the 512-square. */
.tile__lottie { width: 35px; height: 35px; }
.tile__lottie svg { width: 100%; height: 100%; }
.tile__icon.has-lottie > svg { display: none; }

.tile__glyph-art { width: 100%; aspect-ratio: 1; }
.tile__glyph-art svg { width: 100%; height: 100%; }
.tile__glyph.has-lottie > svg { display: none; }

/* Text sits above the watermark rather than under it. */
.tile__title,
.tile__body,
.tile__meta { position: relative; }

.tile__title { font-size: clamp(20px, 1.9vw, 24px); margin-bottom: 10px; }
.tile--wide .tile__title { font-size: clamp(22px, 2.4vw, 30px); }

.tile__body {
  margin-bottom: 24px;
  max-width: 46ch;
  color: var(--ink-soft);
  font-size: 15.5px;
}

/* The meta line becomes a tag. Its colour is the dot, not the text —
   at 13.5px bold the raw tones would drop under the 4.5:1 bar. */
.tile__meta {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  align-self: flex-start;
  margin-top: auto;
  padding: 9px 15px 9px 12px;
  border-radius: var(--r-pill);
  background: var(--paper);
  color: var(--ink-soft);
  font-size: 13.5px;
  font-weight: 700;
}
.tile__dot {
  flex: none;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--accent);
}

/* The disclaimer is the fourth tile and the dark anchor of the pinwheel,
   rather than a grey footnote below the grid. */
.tile--note {
  justify-content: center;
  background: var(--brand-deep);
  border-color: transparent;
  color: #EDEBFF;
}
.tile--note::after {
  content: "";
  position: absolute;
  right: -70px; bottom: -90px;
  width: 280px; height: 280px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(96, 84, 243, .6), transparent 70%);
  pointer-events: none;
}

.tile__kicker {
  position: relative;
  margin-bottom: 12px;
  color: #C6C0FF;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.tile__prose {
  position: relative;
  font-size: clamp(15px, 1.5vw, 17px);
  line-height: 1.62;
  text-wrap: pretty;
}
.tile__prose strong { color: #FFF; font-weight: 800; }

/* ── Promises ──────────────────────────────────────────────
   Deliberately box-less: the bento above is all filled rectangles, so
   this one is typographic instead — outlined numerals, a hairline dot
   field, and nothing to draw a border around. */

/* Dot field, masked so it only shows at the edges and never sits behind
   the copy in the middle. */
.section--dots { position: relative; }
.section--dots::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 1px 1px, rgba(96, 84, 243, .18) 1px, transparent 0) 0 0 / 24px 24px;
  -webkit-mask-image: radial-gradient(ellipse 78% 62% at 50% 42%, transparent 30%, #000 100%);
  mask-image: radial-gradient(ellipse 78% 62% at 50% 42%, transparent 30%, #000 100%);
  pointer-events: none;
}
.section--dots > .wrap { position: relative; z-index: 1; }

.promises__head {
  max-width: 780px;
  margin-inline: auto;
  margin-bottom: clamp(48px, 6vw, 76px);
  text-align: center;
}

/* Eyebrow flanked by rules, rather than the bento's solid pill. */
.eyebrow--rule {
  display: inline-flex;
  align-items: center;
  gap: 14px;
}
.eyebrow--rule::before,
.eyebrow--rule::after {
  content: "";
  width: 28px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  opacity: .45;
}

.promises__title {
  font-size: clamp(31px, 4.6vw, 50px);
  line-height: 1.08;
  letter-spacing: -.02em;
  margin-top: 16px;
  text-wrap: balance;
}

/* Crossing out the wrong idea. Drawn as a pseudo-element rather than
   <s>, so the sentence still reads straight through to a screen reader
   instead of being announced as removed content. */
.struck {
  position: relative;
  white-space: nowrap;
}
.struck::after {
  content: "";
  position: absolute;
  left: -.05em; right: -.05em;
  top: 54%;
  height: .07em;
  min-height: 3px;
  border-radius: 99px;
  background: var(--tone-pink);
  transform: rotate(-1.8deg);
}

.promises__sub {
  margin-top: 18px;
  color: var(--muted);
  font-size: clamp(16px, 1.5vw, 18px);
  text-wrap: pretty;
}

.promises {
  display: grid;
  gap: clamp(40px, 5vw, 60px) clamp(40px, 6vw, 92px);
}
@media (min-width: 860px) {
  /* Fixed tracks rather than 1fr: the copy only fills ~360px, so stretched
     columns would push the two halves to the outer edges of the wrap and
     leave a gutter down the middle. Sized to the content, then centred. */
  .promises {
    grid-template-columns: repeat(2, minmax(0, 420px));
    justify-content: center;
    padding-bottom: 56px;
  }
  /* Drop the right-hand column so the two columns interleave rather than
     landing as six items on a tidy 3×2 grid. Offset via `top` rather than
     a transform, which .reveal already owns on these elements. */
  .promise:nth-child(even) { top: 56px; }
}

.promise {
  --accent: var(--brand);
  position: relative;
}
.promise--blue   { --accent: var(--tone-blue); }
.promise--orange { --accent: var(--tone-orange); }
.promise--green  { --accent: var(--tone-green); }
.promise--pink   { --accent: var(--tone-pink); }
.promise--violet { --accent: var(--tone-violet); }

/* Outlined at rest, flooding solid on hover — the icon carries the mark
   on its own, so nothing here needs a number to lead it. */
.promise__icon {
  display: grid;
  place-items: center;
  width: 72px; height: 72px;
  margin-bottom: 18px;
  border: 2px solid var(--accent);
  border-radius: 50%;
  /* Opaque so the Lottie art, which paints its own occluders in --paper,
     has a known surface under it rather than the section's dot pattern. */
  background: var(--paper);
  color: var(--accent);
  transition: background-color .3s ease, color .3s ease,
              transform .3s cubic-bezier(.2, .8, .3, 1);
}
.promise:hover .promise__icon {
  background: var(--accent);
  color: var(--paper);
  transform: rotate(6deg) scale(1.06);
}
.promise__icon svg { width: 34px; height: 34px; }

/* ── Animated chip icons ───────────────────────────────────
   The Lottie files are baked to their card's accent, so the chip
   matches at rest with no runtime colour work. Hover flips the
   chip solid, and the two rules below flip the art with it:
   strokes go to paper, and the occluder fills — the only filled
   shapes in the set — follow the chip. Lottie writes its colours
   as presentation attributes, which any CSS rule outranks.
   ────────────────────────────────────────────────────────── */
.promise__lottie { width: 44px; height: 44px; }
.promise__lottie svg { width: 100%; height: 100%; }

/* Swapped in only once the animation has mounted, so the inline
   SVG stays visible if the script or the JSON never arrives. */
.promise__icon.has-lottie > svg { display: none; }

.promise:hover .promise__lottie path[stroke]:not([stroke="none"]) { stroke: var(--paper); }
.promise:hover .promise__lottie path[fill]:not([fill="none"])     { fill: var(--accent); }

/* Underline wipes in from the left on hover. */
.promise__title {
  display: inline;
  font-size: clamp(19px, 1.7vw, 22px);
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  background-image: linear-gradient(var(--accent), var(--accent));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0 3px;
  transition: background-size .32s cubic-bezier(.2, .8, .3, 1);
}
.promise:hover .promise__title { background-size: 100% 3px; }

.promise__body {
  margin-top: 10px;
  max-width: 40ch;
  color: var(--muted);
  font-size: 15.5px;
  text-wrap: pretty;
}

/* ── Showcase — one row per step ────────────────────────────
   Three screens abreast left each one too small to read, and
   they are the whole argument here. So each gets a full row,
   copy alternating sides down the page, and a soft stage that
   crops the device at its bottom edge. --tone drives the step
   numeral, the icon chip and the stage wash.
   ────────────────────────────────────────────────────────── */
.showcase {
  display: grid;
  gap: clamp(56px, 7vw, 100px);
  margin-top: clamp(48px, 6vw, 84px);
}

.showcase__cell {
  --tone: var(--brand);
  display: grid;
  gap: clamp(26px, 4vw, 60px);
  align-items: center;
}
.showcase__cell--blue   { --tone: var(--tone-blue); }
.showcase__cell--orange { --tone: var(--tone-orange); }
.showcase__cell--green  { --tone: var(--tone-green); }

@media (min-width: 900px) {
  /* Screen side a little wider than the copy — the copy tops out at
     46ch anyway, and the device needs the room. */
  .showcase__cell { grid-template-columns: minmax(0, .88fr) minmax(0, 1.12fr); }

  /* Copy swaps sides row to row. Only the visual order moves: the DOM
     stays copy-then-screen, so nothing changes for a screen reader or
     for tab order. The tracks mirror with it, so the screen keeps the
     wider one on both sides. */
  .showcase__cell:nth-child(even) {
    grid-template-columns: minmax(0, 1.12fr) minmax(0, .88fr);
  }
  .showcase__cell:nth-child(even) .showcase__text { order: 2; justify-self: end; }
}

.showcase__stage {
  position: relative;
  display: grid;
  justify-items: center;
  align-content: start;
  overflow: hidden;               /* crops the device at the bottom edge */
  aspect-ratio: 1 / .95;
  padding: clamp(30px, 7%, 58px) 18px 0;
  background: color-mix(in oklab, var(--tone) 8%, #FBFAF8);
  border-radius: var(--r-panel);
}
/* Tone bloom behind the device, so the panel reads as lit rather than
   as a flat tinted rectangle. */
.showcase__stage::before {
  content: "";
  position: absolute;
  left: 50%; top: 8%;
  width: 82%;
  aspect-ratio: 1;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(circle, color-mix(in oklab, var(--tone) 24%, transparent), transparent 68%);
  pointer-events: none;
}
/* Flat inside the stage — the frame's own edge is enough definition here,
   and the drop-shadow muddied the panel. The hero device keeps its shadow. */
.iphone--showcase {
  position: relative;
  z-index: 1;
  --w: min(272px, 64%);
  filter: none;
}

/* Single column. A square-ish stage is the whole content width here, which
   is both enormous and — because the device scales with it — a crop that
   lands mid-keyboard. Size the panel directly instead, and hold the device
   back from the panel's width so roughly four fifths of it stays visible. */
@media (max-width: 899px) {
  .showcase__stage {
    aspect-ratio: auto;
    height: clamp(330px, 92vw, 470px);
    /* width, not an auto margin: an auto inline margin stops a grid item
       stretching, and the device's own width is a percentage of this. */
    width: 100%;
    max-width: 520px;
    justify-self: center;
  }
  .iphone--showcase { --w: min(250px, 56%); }
}

.showcase__text { max-width: 46ch; }

/* Numeral, label, and a rule running out to the column edge. */
.showcase__step {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-bottom: 18px;
}
/* 30px/800 clears the 3:1 large-text bar on all four tones — at the
   12px of the label beside it, none of them would. */
.showcase__num {
  font-size: 30px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -.03em;
  color: var(--tone);
}
.showcase__label {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
}
.showcase__step::after {
  content: "";
  flex: 1;
  height: 2px;
  border-radius: 2px;
  background: color-mix(in oklab, var(--tone) 34%, transparent);
}

.showcase__title {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-bottom: 14px;
  font-size: clamp(24px, 2.8vw, 34px);
  line-height: 1.15;
  letter-spacing: -.02em;
  color: var(--tone);
}
.showcase__body {
  color: var(--muted);
  font-size: clamp(16px, 1.5vw, 17.5px);
  text-wrap: pretty;
}
/* Bare glyph, no chip behind it — the title is large enough here to
   carry a filled circle badly, and the tone reads better on the mark
   itself. */
.showcase__icon {
  display: grid;
  place-items: center;
  width: 30px; height: 30px;
  flex: none;
  color: var(--tone);
}
.showcase__icon svg { width: 30px; height: 30px; }

/* Animated icons, same swap as the promise chips. These files are baked
   to white — they were drawn to sit inside a filled chip — so with the
   chip gone the colour has to come from here. Lottie writes its colours
   as presentation attributes, which any CSS rule outranks.
   All three files draw at roughly three quarters of their own square and
   sit a few pixels down and right of its centre, so the stage is sized
   past the 30px icon box and nudged back — that lands the drawn glyph at
   the same size and place as the inline fallback (measured: ~27px of ink,
   centred). It overflows the box rather than growing it, so the title
   never moves.

   Matched on the baked white specifically, not on "any fill": filters.json
   carries red shapes inside a <mask>, where the colour is the mask's own
   luminance rather than something on screen. Repainting those breaks the
   mask; repainting only the white leaves them alone. */
.showcase__lottie {
  width: 36px; height: 36px;
  transform: translate(-3px, -3px);
}
.showcase__lottie svg { width: 100%; height: 100%; }
.showcase__lottie path[fill="rgb(255,255,255)"],
.showcase__lottie path[fill="#ffffff"],
.showcase__lottie path[fill="#fff"]     { fill: var(--tone); }
.showcase__lottie path[stroke="rgb(255,255,255)"],
.showcase__lottie path[stroke="#ffffff"],
.showcase__lottie path[stroke="#fff"]   { stroke: var(--tone); }
.showcase__icon.has-lottie > svg { display: none; }

/* ── Who it's for — the badge wall ─────────────────────────
   The heading claims the app is built for specific people, so
   each one gets a visitor badge with their name filled in on
   the line, pinned to the board at its own small angle. One
   --accent per badge drives the band, the rule and the wash.
   ──────────────────────────────────────────────────────── */

/* Eyebrow as a name-tag, so the motif is introduced before the
   first badge rather than arriving with it. */
.eyebrow--tag {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 9px 16px 8px;
  border-radius: 10px;
  background: var(--brand);
  color: #fff;
}
/* The punch slot, same detail the badges carry. */
.eyebrow--tag::before {
  content: "";
  width: 15px;
  height: 4px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, .55);
}

.badges {
  display: grid;
  gap: clamp(20px, 2.6vw, 30px);
}
@media (min-width: 640px)  { .badges { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) {
  .badges { grid-template-columns: repeat(3, 1fr); padding-bottom: 28px; }
  /* Middle column dropped, so the six read as pinned up by hand
     rather than dealt onto a 3×2 grid. Offset via `top` — .reveal
     already owns `transform` on these elements. */
  .badge:nth-child(3n+2) { top: 28px; }
}

.badge {
  --accent: var(--brand);
  position: relative;
  display: flex;
}
.badge--blue   { --accent: var(--tone-blue); }
.badge--green  { --accent: var(--tone-green); }
.badge--orange { --accent: var(--tone-orange); }
.badge--pink   { --accent: var(--tone-pink); }
.badge--violet { --accent: var(--tone-violet); }

/* The tilt lives on the card, not the <li>, for the same reason
   the stagger above uses `top`. */
.badge__card {
  --tilt: -1.4deg;
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  width: 100%;
  overflow: hidden;
  padding-bottom: clamp(22px, 2.4vw, 28px);
  border-radius: 22px;
  background: var(--paper);
  border: 1px solid color-mix(in oklab, var(--accent) 22%, var(--line));
  box-shadow: 0 1px 2px rgba(16, 15, 30, .05), 0 14px 30px rgba(16, 15, 30, .07);
  transform: rotate(calc(var(--tilt) * var(--tilt-scale, 1)));
  transition: transform .3s cubic-bezier(.2, .8, .3, 1), box-shadow .3s ease;
}
/* Single column, so each card runs the full width and the same angle
   stops reading as a pinned badge and starts reading as a skew. */
@media (max-width: 639px) { .badge__card { --tilt-scale: .45; } }
/* Straightens up when you look at it. */
.badge__card:hover,
.badge:focus-within .badge__card {
  transform: rotate(0deg) translateY(-6px);
  box-shadow: var(--shadow-lg);
}

.badge:nth-child(2) .badge__card { --tilt:  1.1deg; }
.badge:nth-child(3) .badge__card { --tilt:  -.8deg; }
.badge:nth-child(4) .badge__card { --tilt:  1.5deg; }
.badge:nth-child(5) .badge__card { --tilt: -1.1deg; }
.badge:nth-child(6) .badge__card { --tilt:  1.7deg; }

/* Accent wash bleeding up from the bottom corner. Behind the copy
   but above the card's own white — hence the isolate above. */
.badge__card::after {
  content: "";
  position: absolute;
  right: -70px; bottom: -80px;
  width: 200px; height: 200px;
  z-index: -1;
  border-radius: 50%;
  background: radial-gradient(circle, color-mix(in oklab, var(--accent) 20%, transparent), transparent 70%);
  pointer-events: none;
}

/* Printed lettering across the top of the badge, in the raw tone
   rather than a darkened one. The size is doing the work: white
   on these six lands between 3.2 and 5.2:1, so at 19px/800 the
   large-text bar of 3:1 applies and every band clears it at full
   vividness. Shrink this text and the bar becomes 4.5, which the
   raw tones miss — the tones would have to darken to compensate. */
.badge__band {
  position: relative;
  padding: 32px 20px 16px;
  background: var(--accent);
  color: #fff;
  font-size: 19px;
  font-weight: 800;
  letter-spacing: .16em;
  line-height: 1.1;
  text-transform: uppercase;
  text-align: center;
}
/* The slot the lanyard would go through. */
.badge__band::before {
  content: "";
  position: absolute;
  top: 12px; left: 50%;
  width: 46px; height: 6px;
  transform: translateX(-50%);
  border-radius: var(--r-pill);
  background: rgba(0, 0, 0, .3);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, .28);
}

.badge__face { padding: clamp(20px, 2.2vw, 25px) clamp(20px, 2.2vw, 25px) 0; }

/* The name, written on the badge's ruled line. */
.badge__name {
  font-size: clamp(19px, 1.8vw, 22px);
  line-height: 1.24;
  letter-spacing: -.02em;
  padding-bottom: 13px;
  border-bottom: 2px solid color-mix(in oklab, var(--accent) 62%, #fff);
}

.badge__body {
  margin-top: 15px;
  color: var(--muted);
  font-size: 15.5px;
  text-wrap: pretty;
}

/* ── Quotes ────────────────────────────────────────────── */
.quotes { display: grid; gap: 20px; }
@media (min-width: 760px)  { .quotes { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1040px) { .quotes { grid-template-columns: repeat(3, 1fr); } }

.quote {
  margin: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: 30px 28px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-sm);
}

.quote__text { margin: 0 0 24px; }
.quote__text p {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.5;
  color: var(--ink);
  text-wrap: pretty;
}

.quote__by {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: auto;
  padding-top: 20px;
  border-top: 1px solid var(--line-2);
}
.quote__avatar {
  display: grid;
  place-items: center;
  width: 38px; height: 38px;
  flex: none;
  border-radius: 50%;
  background: var(--brand-tint);
  color: var(--brand);
  font-weight: 800;
  font-size: 15px;
}
.quote__name { display: block; font-size: 15px; font-weight: 700; }
.quote__role { display: block; font-size: 13.5px; color: var(--muted-2); }

/* ── CTA ───────────────────────────────────────────────── */
.cta { padding-block: 0 var(--section-y); }

.cta__panel {
  position: relative;
  overflow: hidden;
  padding: clamp(48px, 7vw, 88px) clamp(28px, 5vw, 72px);
  background: var(--brand-deep);
  border-radius: var(--r-panel);
  text-align: center;
  color: #fff;
}
.cta__panel::before {
  content: "";
  position: absolute;
  inset: -40% -10% auto -10%;
  height: 420px;
  background: radial-gradient(ellipse at 50% 0%, rgba(96, 84, 243, .85), transparent 62%);
  pointer-events: none;
}
.cta__panel > * { position: relative; }

.cta__title {
  font-size: clamp(30px, 4.4vw, 48px);
  margin-block: 16px 0;
  text-wrap: balance;
}

.cta__body {
  margin: 18px auto 0;
  max-width: 46ch;
  color: rgba(255, 255, 255, .82);
  font-size: clamp(16px, 1.5vw, 18px);
}

.store-badges--on-dark { justify-content: center; }

/* ── Footer ────────────────────────────────────────────── */
.site-footer {
  padding-block: clamp(48px, 6vw, 72px) 32px;
  background: var(--snow);
  border-top: 1px solid var(--line);
}

/* ── Community panel ───────────────────────────────────────
   A white card sitting on the footer's grey, so the panel
   reads as one last piece of the page rather than as the
   first row of the link lists.

   It is pulled up across the footer's top rule so it straddles
   the join — half on the page above, half on the footer. The
   card is a descendant of the footer, so it paints over that
   rule without needing a stacking context of its own. ──── */
.community {
  display: grid;
  gap: clamp(32px, 5vw, 48px);
  align-items: center;
  margin-top: -100px;
  margin-bottom: clamp(48px, 6vw, 72px);
  padding: clamp(32px, 5vw, 60px);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-panel);
  box-shadow: var(--shadow-sm);
}
@media (min-width: 860px) {
  .community { grid-template-columns: 1.05fr .95fr; }
}

.community__title {
  margin-top: 16px;
  font-size: clamp(26px, 3.2vw, 38px);
  text-wrap: balance;
}
.community__body {
  margin-top: 16px;
  max-width: 46ch;
  color: var(--muted);
  font-size: clamp(16px, 1.5vw, 18px);
  text-wrap: pretty;
}
.community__cta { margin-top: 28px; }
.community__cta-arrow { transition: translate .16s ease; }
.community__cta:hover .community__cta-arrow { translate: 3px 0; }

/* The orbit is centred in its own column on wide screens and
   sits under the copy on narrow ones. */
.community__stage {
  display: grid;
  place-items: center;
  justify-self: center;
}

/* ── The orbit ─────────────────────────────────────────────
   One square box; everything inside is sized as a share of it,
   so the whole constellation scales from a single value. ── */
.orbit {
  --size: clamp(248px, 32vw, 380px);
  --face: calc(var(--size) * .105);   /* an orbiting person's diameter */

  position: relative;
  width: var(--size);
  height: var(--size);
}

/* A soft pool of brand light under the rings, echoing the hero's
   ripple — it keeps the card from reading as a bare diagram. */
.orbit::before {
  content: "";
  position: absolute;
  inset: 8%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(96, 84, 243, .09), transparent 68%);
}

.orbit__rings {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.orbit__rings circle { stroke: var(--line); stroke-width: 1.5; }

.orbit__core {
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  display: grid;
  place-items: center;
  width: calc(var(--size) * .13);
  height: calc(var(--size) * .13);
}
.orbit__core svg { width: 100%; height: 100%; }

/* Sized to its own orbit and spun about the box's centre. */
.orbit__track {
  position: absolute;
  inset: 0;
  margin: auto;
  width: var(--d);
  height: var(--d);
  animation: orbit-turn var(--dur) linear infinite;
  animation-delay: calc(var(--dur) * var(--turn) * -1);
}

/* Same clock, run backwards, so the letter never tips over. Under
   `reverse` the negative delay winds the spin back by the same
   fraction it winds the track forward, which is what keeps the two
   exactly opposite rather than merely the same speed. */
.orbit__person {
  position: absolute;
  top: 0;
  left: 50%;
  translate: -50% -50%;
  display: grid;
  place-items: center;
  width: var(--face);
  height: var(--face);
  border-radius: 50%;
  font-size: calc(var(--size) * .046);
  font-weight: 800;
  animation: orbit-turn var(--dur) linear infinite reverse;
  animation-delay: calc(var(--dur) * var(--turn) * -1);
}

/* `rotate` rather than a transform, so the centring `translate`
   above survives every frame instead of being overwritten. */
@keyframes orbit-turn { to { rotate: 360deg; } }

.orbit__person--brand  { background: var(--brand-tint);  color: var(--brand-strong); }
.orbit__person--violet { background: var(--tint-violet); color: var(--tone-violet); }
.orbit__person--blue   { background: var(--tint-blue);   color: var(--tone-blue); }
.orbit__person--green  { background: var(--tint-green);  color: var(--tone-green); }
.orbit__person--orange { background: var(--tint-orange); color: var(--tone-orange); }
.orbit__person--pink   { background: var(--tint-pink);   color: var(--tone-pink); }

.footer__grid {
  display: grid;
  gap: 40px 32px;
  grid-template-columns: 1fr;
}
@media (min-width: 640px)  { .footer__grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 900px)  { .footer__grid { grid-template-columns: 1.6fr repeat(3, 1fr); } }

.footer__brand .brand { margin-right: 0; }
.footer__tagline { margin-top: 14px; color: var(--muted); font-size: 15px; }

.footer__heading {
  font-size: 13px;
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted-2);
  margin-bottom: 14px;
}
.footer__col li + li { margin-top: 10px; }
.footer__col a {
  font-size: 15px;
  color: var(--ink-soft);
  border-radius: 4px;
}
.footer__col a:hover { color: var(--brand-strong); text-decoration: underline; text-underline-offset: 3px; }

.footer__base {
  margin-top: clamp(40px, 5vw, 64px);
  padding-top: 28px;
  border-top: 1px solid var(--line);
  display: grid;
  gap: 16px;
}
.footer__legal { font-size: 13px; color: var(--muted-2); max-width: 82ch; }
.footer__copy  { font-size: 13px; color: var(--muted-2); }

/* ── Reveal on scroll ──────────────────────────────────── */
.reveal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity .55s ease, transform .55s ease;
}
.reveal.is-visible { opacity: 1; transform: none; }

/* ── Motion & contrast preferences ─────────────────────── */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .reveal { opacity: 1; transform: none; }

  /* The blanket .001ms duration above would run screen-cycle to its 100%
     keyframe — opacity 0 — and leave the phone blank. Kill the animation
     and pin the first frame instead. */
  .screen-slides img { animation: none; opacity: 0; }
  .screen-slides img:first-child { opacity: 1; }

  /* Same trap in the bubble: run to the 100% frame and all three
     phrasings end up invisible. Pin the first and hold the waveform
     at a readable spread rather than a flat line. */
  .ask__lines li { animation: none; opacity: 0; transform: none; }
  .ask__lines li:first-child { opacity: 1; }
  .ask__wave i { animation: none; }
  .ask__wave i:nth-child(odd)  { transform: scaleY(.72); }
  .ask__wave i:nth-child(even) { transform: scaleY(.4); }

  /* The same trap once more, and the worst-looking version of it: every
     track would run to its 100% frame — a full 360° — and land back at
     the top of its ring, stacking all six people on one spot. Stop the
     clock and place each one at the angle --turn already describes. */
  .orbit__track, .orbit__person { animation: none; }
  .orbit__track  { rotate: calc(var(--turn) * 360deg); }
  .orbit__person { rotate: calc(var(--turn) * -360deg); }
}

@media (prefers-contrast: more) {
  :root { --muted: #3F3E4C; --muted-2: #4A4957; --line: #B9B8C6; }
  .quote { border-color: #A9A8B8; }
  .badge__card { border-color: color-mix(in oklab, var(--accent) 60%, #6D6C7C); }
  .tile { border-color: var(--accent); }
  .tile__glyph { opacity: .1; }
}

@media (forced-colors: active) {
  .quote, .showcase__stage { border: 1px solid CanvasText; }
  .tile { border: 1px solid CanvasText; }
  .strip__dot, .showcase__icon { forced-color-adjust: none; }

  /* The band's fill collapses to Canvas, so the badge loses the edge
     between its lettering and the name. Rule it back in, and drop the
     corner wash — a gradient survives here as a solid block. */
  .badge__card { border: 1px solid CanvasText; }
  .badge__band { border-bottom: 1px solid CanvasText; }
  .badge__card::after { display: none; }

  /* Same story as the badge wash: a gradient survives here as a solid
     block, and either stage's light pool would land on top of its
     device rather than behind it. */
  .showcase__stage::before,
  .hero__stage::before { display: none; }

  /* The panel's fill collapses to Canvas, so it stops being a panel —
     and the card floating on it stops having anything to float on. */
  .hero__stage { border: 1px solid CanvasText; }

  .ask { border: 1px solid CanvasText; }
  .ask__wave i { forced-color-adjust: none; }

  /* The dot field collapses to CanvasText and reads as noise. */
  .section--dots::before { display: none; }

  /* Every colour collapses to CanvasText here, so the watermark stops
     being a wash and becomes a solid shape over the copy. */
  .tile__glyph { display: none; }

  /* The card's fill goes to Canvas and its shadow disappears, so the
     panel needs a drawn edge to stay a panel. Each person's tint goes
     the same way, which would leave six bare letters floating on the
     rings — the circle is ruled back in instead. The light pool is a
     gradient, so it would arrive as a solid disc over the rings. */
  .community { border: 1px solid CanvasText; }
  .orbit__person { border: 1px solid CanvasText; }
  .orbit::before { display: none; }
}
