/* ==========================================================================
   Fettle marketing site - tokens
   ========================================================================== */
:root {
  --bg: #0F1115;
  --panel: #16181D;
  --panel-2: #1C1F26;
  --border: #2A2D34;
  --text: #F4F1EC;
  --text-muted: #9B958C;
  --text-subtle: #6B6660;
  --accent: #E08A50;
  --accent-soft: #3A2A20;
  --success: #7CAC8B;
  --font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace;
  --max: 1320px;

  /* Brief-spec token names (2026-07-15 rebuild) - same values, added as
     aliases rather than a destructive rename so every pre-existing rule
     below keeps working untouched. Prefer these names in new rules. */
  --surface: var(--panel);
  --surface-2: var(--panel-2);
  --copper: var(--accent);
  --copper-deep: #C8682E;
  --copper-soft: var(--accent-soft);
  --warning: #E8B45C;
  --error: #C8675F;
  --nav-h: 76px;
  --ease: cubic-bezier(.22, 1, .36, 1);
}

* { box-sizing: border-box; }

/* Note: overflow-x lives on body only, not html - overflow-x:hidden on BOTH
   html and body breaks position:sticky for descendants (confirmed while
   building the Identify Moment's sticky scroll-driver in Task 4). */
html { scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

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

a { color: inherit; }

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

.hidden { display: none !important; }

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

/* ==========================================================================
   Reveal utility (contract for Tasks 4-5)
   ========================================================================== */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}
.reveal-in {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ==========================================================================
   Shared bits
   ========================================================================== */
.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--copper);
  margin: 0 0 16px;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 999px;
  padding: 8px 14px;
  font-size: 0.8125rem;
  font-weight: 500;
  white-space: nowrap;
  text-decoration: none;
}

.pill-badge {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-muted);
}

.pill-ghost {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text);
  text-decoration: none;
}
.pill-ghost:hover { border-color: var(--copper); color: var(--copper); }

/* ==========================================================================
   Nav
   ========================================================================== */
.nav {
  position: sticky;
  top: 0;
  z-index: 40;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  background: rgba(15, 17, 21, 0.78);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--border);
}

.nav-inner {
  width: 100%;
  max-width: var(--max);
  margin: 0 auto;
  padding: 0 32px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: var(--text);
  font-weight: 700;
  font-size: 1.0625rem;
  letter-spacing: 0.18em;
  flex-shrink: 0;
}

.wordmark-sm { font-size: 0.95rem; }

img.wordmark-mark { display: block; }

.nav-links {
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: nowrap;
}

.nav-links a:not(.pill) {
  text-decoration: none;
  font-size: 0.9375rem;
  color: var(--text);
  flex-shrink: 0;
}
.nav-links a:not(.pill):hover { color: var(--copper); }

/* ==========================================================================
   Atmosphere - a single persistent copper light sweep that threads through
   the hero and into the Identify Moment, so the two signature sections read
   as one continuous cinematic shot rather than isolated boxes. Positioned
   per-section (a true fixed full-page layer would fight sticky positioning
   inside #identify), tuned so the gradients hand off at the section seam.
   ========================================================================== */
/* overflow:hidden must NOT be applied to #identify - it is the direct
   ancestor of the sticky-positioned .id-sticky, and overflow:hidden (or
   auto/scroll) on any ancestor of a position:sticky element disables its
   sticking entirely (same class of bug already noted for html/body above -
   confirmed live: this exact rule silently turned the pinned Identify
   Moment into a normally-scrolling block that scrolled itself off-screen
   entirely by the end of the sequence). .hero has no sticky descendants,
   so it can keep overflow:hidden safely. */
#identify {
  position: relative;
}

/* ==========================================================================
   Hero - full viewport, cinematic (brief S8)

   The background is a full-bleed layer (.hero-bg, a sibling of .hero-inner,
   NOT nested inside the max-width wrapper) so the copper-lit atmosphere
   spans the entire browser width and sits behind both the copy and the
   phone as one continuous scene. Earlier versions only gradient-lit the
   .hero element itself at low intensity, so the phone image's own baked-in
   stage lighting (dark floor + concentrated copper glow under the device)
   read as a brighter, separate "island" against a flatter surrounding
   section - exactly the "picture placed on a flat page" look this fixes.
   No photographic background asset exists (or is needed) - layered
   gradients alone create the sweep; the phone's own mask (further below)
   handles blending its edges into this now much richer field.
   ========================================================================== */
.hero {
  position: relative;
  isolation: isolate;
  overflow: clip;
  min-height: calc(100svh - var(--nav-h));
  min-height: calc(100vh - var(--nav-h));
  display: flex;
  align-items: center;
  padding: 56px 32px 64px;
  background: var(--bg);
  border-bottom: 1px solid rgba(244, 241, 236, 0.07);
}

@supports (height: 100svh) {
  .hero { min-height: max(760px, calc(100svh - var(--nav-h))); }
}

.hero-bg {
  position: absolute;
  z-index: -3;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(
      90deg,
      rgba(15, 17, 21, 0.94) 0%,
      rgba(15, 17, 21, 0.80) 29%,
      rgba(15, 17, 21, 0.34) 57%,
      rgba(15, 17, 21, 0.08) 78%,
      rgba(15, 17, 21, 0.16) 100%
    );
  background-repeat: no-repeat;
  background-size: cover;
}

.hero::before {
  content: '';
  position: absolute;
  z-index: -2;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 58% 52% at 76% 78%, rgba(224, 138, 80, 0.30) 0%, rgba(224, 138, 80, 0.12) 40%, transparent 74%),
    radial-gradient(ellipse 42% 34% at 42% 100%, rgba(200, 104, 46, 0.11), transparent 74%),
    radial-gradient(1300px 900px at 108% -10%, rgba(224, 138, 80, 0.16), transparent 58%);
}

.hero::after {
  content: '';
  position: absolute;
  z-index: -1;
  left: 0;
  right: 0;
  bottom: 0;
  height: 32%;
  pointer-events: none;
  background: linear-gradient(to bottom, transparent, rgba(15, 17, 21, 0.18) 54%, var(--bg) 100%);
}

.hero-inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: var(--max);
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.hero-copy { flex: 0 1 680px; max-width: 720px; }

/* Brief S5 gives a max readable width of ~760px for the headline itself,
   wider than a strict 46% column at this container's max-width would
   allow - so the headline is intentionally let to claim more horizontal
   room than the visual column, rather than being force-wrapped into
   awkward 2-3 word lines to fit a stricter box. */
.hero-copy h1 {
  margin: 0 0 24px;
  font-size: clamp(2.6rem, 5.6vw, 5.6rem);
  line-height: 0.98;
  font-weight: 740;
  letter-spacing: -0.045em;
  max-width: 760px;
}

.hero-sub {
  margin: 0 0 36px;
  max-width: 42ch;
  font-size: 1.125rem;
  line-height: 1.6;
  color: var(--text-muted);
}

.hero-ctas {
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
}

.app-store-badge {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: var(--surface);
  border: 1px solid var(--border-strong, #3A3E47);
  border-radius: 10px;
  padding: 12px 20px;
  color: var(--text);
  cursor: pointer;
  text-decoration: none;
  font-family: var(--font-sans);
  transition: border-color 0.25s var(--ease), transform 0.25s var(--ease);
}
.app-store-badge:hover { border-color: var(--copper); transform: translateY(-1px); }

/* Android "coming soon" - deliberately NOT a link. The Play closed-test opt-in
   URL only admits accounts on the tester email list, so pointing the public at
   it would be a dead end. This is a status marker, not a call to action. */
.badge-soon {
  cursor: default;
  opacity: 0.62;
  border-style: dashed;
}
.badge-soon:hover { border-color: var(--border-strong, #3A3E47); transform: none; }

.badge-text { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.2; }
.badge-line-1 { font-size: 0.6875rem; color: var(--text-muted); }
.badge-line-2 { font-size: 1.0625rem; font-weight: 600; }

.hero-secondary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text-muted);
  text-decoration: none;
  font-size: 0.9375rem;
  border-bottom: 1px solid transparent;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.hero-secondary:hover { color: var(--text); border-color: var(--border); }
.hero-secondary svg { flex-shrink: 0; transition: transform 0.2s var(--ease); }
.hero-secondary:hover svg { transform: translateY(2px); }

/* Visual column - the phone is not a boxed image, it's a masked cutout that
   dissolves into the section's own copper light field so no rectangular
   PNG edge is ever visible (brief S8 mask recipe, used verbatim). */
/* flex:1 with min-width:0 lets this column shrink below its content size,
   which is what allows the (larger-than-column) phone image to overflow
   its own cell and bleed toward the viewport edge below, instead of being
   squeezed down to column width - the exact bug that made the phone look
   small and boxed in the first implementation. */
.hero-visual {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  /* flex-start, not flex-end: when this item overflows its container (by
     design - it's larger than the column), flex-end pins the item's END
     edge to the container's end edge, which pushes the overflow out the
     START (left) side, straight over the headline text. flex-start pins
     the START edge instead, so overflow spills out the right - away from
     the copy column - which is the bleed direction we actually want. */
  justify-content: flex-start;
  min-height: 100%;
  overflow: visible;
}

.hero-visual::before {
  content: '';
  position: absolute;
  inset: -10%;
  background:
    radial-gradient(closest-side, rgba(224, 138, 80, 0.28), transparent 72%);
  filter: blur(10px);
  opacity: 0.9;
  z-index: 0;
}

/* A CSS-drawn device frame containing the real screenshot, not a
   photographic render with its own baked-in dark stage/floor lighting. A
   masked photographic render was tried first (per the brief's fallback
   path) and tuned repeatedly, but a real render's own texture/vignette
   kept creating a visible seam against the section's CSS gradient no
   matter how wide the mask's fade zone got - there was always a rectangle
   of "flat photo background" that didn't quite match. A phone SHAPE has
   no such rectangle to hide: it's just a bezel floating directly on the
   .hero-bg/::before environment, containing nothing but the real app
   screenshot. Sized by height to hit the brief's 580-720px target. */
.hero-phone-frame {
  position: relative;
  z-index: 1;
  width: auto;
  height: min(680px, 72vh);
  aspect-ratio: 9 / 19.4;
  border-radius: 46px;
  padding: 14px;
  background: linear-gradient(160deg, #23262E, #101216);
  border: 1px solid #33363F;
  box-shadow:
    0 50px 100px rgba(0, 0, 0, 0.55),
    0 0 0 1px rgba(224, 138, 80, 0.08),
    0 40px 90px rgba(224, 138, 80, 0.18);
  opacity: 0;
  transform: translateY(22px) scale(0.98);
  animation: hero-render-in 1s var(--ease) 0.15s forwards;
}

.hero-phone-notch {
  position: absolute;
  top: 18px;
  left: 50%;
  transform: translateX(-50%);
  width: 32%;
  height: 24px;
  background: #0B0C0F;
  border-radius: 999px;
  z-index: 2;
}

.hero-render {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 34px;
  background: var(--surface);
}

@keyframes hero-render-in {
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .hero-phone-frame { opacity: 1; transform: none; animation: none; }
}

@media (max-width: 860px) {
  .hero {
    padding: 40px 20px 56px;
    align-items: flex-start;
    padding-top: 48px;
  }
  .hero-inner {
    flex-direction: column;
    align-items: stretch;
    text-align: left;
    gap: 8px;
  }
  .hero-copy { flex-basis: auto; max-width: none; }
  .hero-copy h1 { max-width: none; }
  /* min-height:100% (desktop) needs a definite-height ancestor to resolve
     against. .hero-inner is column-flex/height:auto on mobile, which is
     NOT definite for percentage purposes even though it renders to a real
     pixel height - so 100% silently resolves to 0, collapsing this flex
     row to zero height and leaving the image centered around that phantom
     zero-height line (half rendered above it, overlapping the headline
     above). Same root cause as the Identify Moment's .id-scene bug above -
     the fix is the same: don't lean on a percentage height here. */
  .hero-visual { order: -1; margin-bottom: 12px; justify-content: center; min-height: auto; }
  .hero-phone-frame { width: min(92vw, 380px); height: auto; }

  /* Stacked layout (phone below text, not beside it) - the horizontal
     dark-left/light-right sweep no longer matches, so switch to a vertical
     top-dark/bottom-lit gradient and recentre the copper glow under the
     phone's new position instead of the desktop's lower-right. */
  .hero-bg {
    background-image:
      linear-gradient(
        180deg,
        rgba(15, 17, 21, 0.91) 0%,
        rgba(15, 17, 21, 0.62) 43%,
        rgba(15, 17, 21, 0.18) 72%,
        var(--bg) 100%
      );
  }
  .hero::before {
    background:
      radial-gradient(ellipse 70% 42% at 50% 92%, rgba(224, 138, 80, 0.22) 0%, rgba(224, 138, 80, 0.08) 40%, transparent 74%);
  }
}

/* ==========================================================================
   Shared section rhythm
   ========================================================================== */
#recordings, #features, #onboarding-demo, #tiers, #final-cta { scroll-margin-top: var(--nav-h); }

#recordings { padding: 128px 32px; }
#features { padding: 32px 32px 120px; }
#onboarding-demo { padding: 32px 32px 128px; }
#tiers { padding: 32px 32px 0; }
#final-cta { padding: 140px 32px; }

/* Second real-recording moment (brief-adjacent addition) - mirrors the
   #recordings phone-frame component exactly, just with the video on the
   left and copy on the right for rhythm variety against the first one. */
.recordings-inner-reverse { grid-template-columns: minmax(0, 1fr) minmax(0, 0.82fr); }
.recordings-inner-reverse .rec-phone-wrap { order: -1; }

@media (max-width: 860px) {
  .recordings-inner-reverse { grid-template-columns: 1fr; }
  .recordings-inner-reverse .rec-phone-wrap { order: 0; }
}

/* Mirror the glow and tilt so the two moments don't read as identical. */
.rec-phone-alt { transform: rotate(4deg); }
@media (prefers-reduced-motion: reduce) { .rec-phone-alt { transform: none; } }
.recordings-inner-reverse .rec-phone-wrap::before { left: auto; right: 10%; }

/* ==========================================================================
   The Identify Moment - signature section (brief S9)
   ========================================================================== */
#identify {
  height: 220vh;
  scroll-margin-top: var(--nav-h);
  background:
    radial-gradient(1100px 800px at -8% 0%, rgba(224, 138, 80, 0.10), transparent 60%),
    var(--bg);
}

.id-sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  min-height: 820px;
  display: flex;
  align-items: center;
  gap: 0;
  width: 100%;
  overflow: hidden;
}

/* margin-left, not padding-left: this section is full-bleed (no max-width
   wrapper), so the copy column needs its own offset to align with the
   site's content grid at wide viewports. Padding would count against
   max-width under box-sizing:border-box - at wide/ultrawide viewports the
   large calculated offset ate into the same 420px box as the text itself,
   squeezing it to near-zero width and wrapping one word per line. Margin
   sits outside the box, so max-width keeps governing the text area alone. */
.id-copy {
  flex: 0 0 32%;
  min-width: 280px;
  max-width: 420px;
  margin-left: max(32px, calc((100vw - var(--max)) / 2));
  padding-right: 40px;
  z-index: 2;
}

.id-copy h2 {
  margin: 0 0 20px;
  font-size: clamp(2.2rem, 3.6vw, 3.4rem);
  line-height: 1.05;
  font-weight: 740;
  letter-spacing: -0.03em;
}

.id-body {
  margin: 0 0 36px;
  max-width: 32ch;
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--text-muted);
}

.id-footer {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  color: var(--copper);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
}
.id-footer svg { flex-shrink: 0; }

/* Scene: cabinet backdrop + drawn-on boxes, bleeding to the viewport's right
   edge (brief: image occupies ~66-72%, may extend close to the right edge).
   aspect-ratio MUST match cabinet-scene.webp's native ratio (1672x941) -
   the .id-box --x/--y/--w/--h percentages are measured directly against the
   source image's pixels. If this ratio drifts from the container's actual
   ratio, background-size:cover crops the sides and every box coordinate
   goes visibly wrong (confirmed while re-tuning boxes to the real cabinet
   photo in the asset-swap task - a mismatched ratio silently shifted every
   box off its tool). height is capped, width flexes to fill the remaining
   row width right up to the viewport edge. */
.id-scene {
  position: relative;
  flex: 1 1 auto;
  align-self: stretch;
  width: 100%;
  height: 100%;
  background-color: var(--surface);
  background-size: cover;
  background-position: center;
  border-left: 1px solid var(--border);
}

.id-scene::before {
  content: '';
  position: absolute;
  inset: 0;
  box-shadow:
    inset 0 0 0 1px rgba(224, 138, 80, 0),
    inset 140px 0 140px -100px rgba(15, 17, 21, 0.9);
  transition: box-shadow 0.6s var(--ease);
  pointer-events: none;
}

#identify.id-scanning .id-scene::before {
  box-shadow:
    inset 0 0 0 1px rgba(224, 138, 80, 0.5),
    inset 0 0 80px rgba(224, 138, 80, 0.10),
    inset 140px 0 140px -100px rgba(15, 17, 21, 0.9);
}

.id-scene::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 40%, rgba(224, 138, 80, 0.16) 50%, transparent 60%);
  transform: translateX(-120%);
  opacity: 0;
  pointer-events: none;
}

#identify.id-scanning .id-scene::after {
  opacity: 1;
  animation: id-shimmer 2.6s ease-in-out infinite;
}

@keyframes id-shimmer {
  0% { transform: translateX(-120%); }
  100% { transform: translateX(120%); }
}

.id-box {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w);
  height: var(--h);
  border: 1.5px solid transparent;
  opacity: 0;
  transform: scale(0.9);
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease), border-color 0.35s var(--ease);
  pointer-events: none;
}

.id-box.on {
  opacity: 1;
  transform: scale(1);
  border-color: var(--copper);
}

.id-box::before,
.id-box::after {
  content: '';
  position: absolute;
  width: 12px;
  height: 12px;
  opacity: 0;
  transition: opacity 0.3s ease 0.1s;
}
.id-box.on::before,
.id-box.on::after { opacity: 1; }

.id-box::before {
  top: -1.5px;
  left: -1.5px;
  border-top: 1.5px solid var(--copper);
  border-left: 1.5px solid var(--copper);
}

.id-box::after {
  bottom: -1.5px;
  right: -1.5px;
  border-bottom: 1.5px solid var(--copper);
  border-right: 1.5px solid var(--copper);
}

.id-chip {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 0;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  background: rgba(22, 24, 29, 0.92);
  backdrop-filter: blur(4px);
  border: 1px solid var(--border);
  white-space: nowrap;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.35s ease 0.1s, transform 0.35s ease 0.1s, border-color 0.4s ease, box-shadow 0.4s ease;
}

.id-box.on .id-chip {
  opacity: 1;
  transform: translateY(0);
}

/* Stage 3: as boxes complete, their labels pick up a touch more copper
   glow (brief S9 stage sequencing). */
#identify.id-done .id-box .id-chip {
  border-color: rgba(224, 138, 80, 0.4);
  box-shadow: 0 0 24px rgba(224, 138, 80, 0.14);
}

/* The real cabinet's clearest tools sit close together in the busy
   pegboard - a chip always drawn above its box collides with the row
   above when boxes are this tight. For the lower cluster, drop the
   chip below the box instead, where the shelves give it clear air. */
.id-box--chip-below .id-chip {
  bottom: auto;
  top: calc(100% + 8px);
  transform: translateY(-6px);
}

.id-chip-text { display: flex; flex-direction: column; line-height: 1.3; }

.id-chip-label {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--text);
}

.id-chip-sub {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  color: var(--copper);
}

.id-tick {
  flex-shrink: 0;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  border: 1.5px solid var(--copper);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  line-height: 1;
  color: var(--copper);
  transition: border-color 0.4s ease, color 0.4s ease;
}
.id-tick::before { content: '+'; }

#identify.id-done .id-tick {
  border-color: var(--success);
  color: var(--success);
}
#identify.id-done .id-tick::before { content: '\2713'; }

@media (max-width: 900px) {
  #identify { height: auto; }
  .id-sticky {
    position: static;
    height: auto;
    min-height: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 72px 0;
  }
  .id-copy {
    flex: 0 0 auto;
    max-width: none;
    margin-left: 0;
    padding: 0 24px;
    margin-bottom: 32px;
  }
  /* No min-height here: the .id-box --x/--y/--w/--h coordinates are
     percentages measured against this element's own box, which is only
     correct when the box's rendered ratio matches the source image's
     native ratio (1672:941). A min-height taller than that ratio would
     produce for this width forces the browser to prioritize min-height
     over aspect-ratio, distorting the effective crop and visibly
     scattering every label off its tool (confirmed live - this exact
     regression happened while widening this section for the mobile pass:
     see the aspect-ratio warning on the base .id-scene rule above). */
  .id-scene {
    width: 100%;
    height: auto;
    aspect-ratio: 1672 / 941;
    border-left: none;
  }
  .id-scene::before { box-shadow: inset 0 0 0 1px rgba(224, 138, 80, 0.5); }
  /* At this width the cabinet image (edge-to-edge, aspect-ratio-locked to
     stay pixel-accurate) is only ~220px tall - not enough room for six
     text chips to sit near their boxes without piling on top of each
     other into an unreadable stack (confirmed live). The copper boxes
     themselves still read fine overlapping slightly since they're thin
     outlines, not text, so keep those and drop the chip text on mobile
     rather than ship illegible overlapping labels. */
  .id-chip { display: none; }

  /* The JS scroll-driver's progress formula (p = -top / (sectionHeight -
     viewportHeight)) assumes the section is TALLER than the viewport, which
     is only true for the desktop sticky-pin layout. On mobile #identify
     collapses to its auto content height (~760px), which is routinely
     SHORTER than the viewport (844px) - the denominator goes negative and
     the resulting progress value flickers the boxes on and off
     unpredictably as the user scrolls (confirmed live: all six boxes
     toggle from "on" at load to "off" a few hundred px later). Force the
     labelled final state permanently instead of fighting a formula that
     doesn't apply at this width - same resolution as the reduced-motion
     fallback above. */
  .id-box, .id-box::before, .id-box::after, .id-chip {
    opacity: 1 !important;
    transform: none !important;
  }
  .id-box { border-color: var(--copper) !important; }
  .id-tick { border-color: var(--success) !important; color: var(--success) !important; }
  .id-tick::before { content: '\2713' !important; }
  .id-scene::after { display: none; }
}

/* The nav is a space-between row with fixed-width children - the standing
   Fettle lesson: the widest state must never push controls off-screen.
   Below 620px the "Coming soon to iPhone" pill duplicates the hero badge
   directly beneath it, so drop it and tighten gaps instead of overflowing. */
@media (max-width: 620px) {
  .nav-links { gap: 12px; }
  .nav-links .pill-badge { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  /* Collapse the sticky-scroll pin entirely - show the labelled final
     state immediately instead of making the user scroll through 220vh
     of pinned section for a sequence they won't see animate anyway. */
  #identify { height: auto; }
  .id-sticky { position: static; height: auto; min-height: 0; padding: 96px 0; }
  /* .id-scene normally fills 100% of .id-sticky's explicit 100vh height -
     with .id-sticky collapsed to height:auto above, that percentage has
     nothing definite to resolve against and silently computes to 0
     (verified live: rendered at h:0 in this exact environment, since
     Chromium here runs with prefers-reduced-motion: reduce by default).
     Give it its own definite size via aspect-ratio instead. */
  .id-scene { height: auto; aspect-ratio: 1672 / 941; min-height: 460px; align-self: center; }
  .id-box, .id-box::before, .id-box::after, .id-chip {
    opacity: 1 !important;
    transform: none !important;
  }
  .id-box { border-color: var(--copper); }
  .id-scene::after { display: none; }
  #identify.id-scanning .id-scene::before,
  .id-scene::before {
    box-shadow: inset 0 0 0 1px rgba(224, 138, 80, 0.5);
  }
}

/* ==========================================================================
   Footer
   ========================================================================== */
.site-footer {
  border-top: 1px solid var(--border);
  padding: 44px 32px;
  background: var(--bg);
}

.footer-inner {
  max-width: var(--max);
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
}

.footer-links {
  display: flex;
  gap: 22px;
  margin-left: auto;
}

.footer-links a {
  text-decoration: none;
  color: var(--text-muted);
  font-size: 0.875rem;
}
.footer-links a:hover { color: var(--copper); }

.footer-copy {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--text-subtle);
  flex-basis: 100%;
}

@media (min-width: 640px) {
  .footer-copy { flex-basis: auto; margin-left: auto; }
  .footer-links { margin-left: 0; }
}

/* ==========================================================================
   Recordings - "Your recordings" (brief S10)
   ========================================================================== */
.recordings-inner {
  max-width: var(--max);
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1fr);
  align-items: center;
  gap: 64px;
}

.rec-copy h2 {
  margin: 0 0 20px;
  font-size: clamp(2rem, 3.4vw, 3rem);
  line-height: 1.08;
  font-weight: 740;
  letter-spacing: -0.03em;
}

.rec-body {
  margin: 0;
  max-width: 38ch;
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--text-muted);
}

.rec-phone-wrap {
  display: flex;
  justify-content: center;
  position: relative;
}

.rec-phone-wrap::before {
  content: '';
  position: absolute;
  left: 10%;
  right: 10%;
  bottom: -6%;
  height: 40%;
  background: radial-gradient(closest-side, rgba(224, 138, 80, 0.28), transparent 75%);
  filter: blur(6px);
  z-index: 0;
}

/* Portrait device frame - real iPhone screen recordings are portrait
   (9:19.5-ish), not landscape. An earlier pass framed this as a 16:10
   landscape box; once real footage (portrait, 384x848) was wired in that
   would have cropped to an unrecognizable sliver under object-fit:cover.
   Sized narrower than the hero phone so it reads as a distinct, smaller
   "secondary" device moment rather than repeating the hero. */
.rec-phone {
  position: relative;
  z-index: 1;
  width: min(300px, 74vw);
  aspect-ratio: 9 / 19.4;
  border-radius: 40px;
  padding: 12px;
  background: linear-gradient(160deg, #23262E, #101216);
  border: 1px solid #33363F;
  box-shadow:
    0 50px 100px rgba(0, 0, 0, 0.55),
    0 0 0 1px rgba(224, 138, 80, 0.08),
    0 34px 70px rgba(224, 138, 80, 0.14);
  transform: rotate(-4deg);
}

.rec-phone-notch {
  position: absolute;
  top: 16px;
  left: 50%;
  transform: translateX(-50%);
  width: 34%;
  height: 22px;
  background: #0B0C0F;
  border-radius: 999px;
  z-index: 2;
}

.rec-phone video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 30px;
  background: var(--surface);
  display: block;
}

@media (prefers-reduced-motion: reduce) {
  .rec-phone { transform: none; }
}

@media (max-width: 860px) {
  .recordings-inner { grid-template-columns: 1fr; gap: 32px; justify-items: center; }
  .rec-phone { transform: rotate(0deg); width: min(280px, 72vw); }
}

/* ==========================================================================
   Feature quartet - four alternating cinematic rows (brief S11)
   ========================================================================== */
.features-inner {
  max-width: var(--max);
  margin: 0 auto;
}

.features-eyebrow {
  text-align: center;
  margin: 0 0 64px;
}

.feature-panel {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: 64px;
  min-height: 640px;
}

.feature-panel + .feature-panel {
  margin-top: 56px;
  padding-top: 56px;
  border-top: 1px solid var(--border);
}

.feature-panel-reverse .feature-media { order: 2; }
.feature-panel-reverse .feature-copy { order: 1; }

.feature-media {
  position: relative;
  border-radius: 22px;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--surface);
  box-shadow: 0 40px 90px rgba(0, 0, 0, 0.45);
}

.feature-media img { width: 100%; height: auto; }

/* Landscape duo renders (two phones side by side) need more width than the
   default 1:1 split - the old portrait screenshots didn't. */
.feature-panel-wide {
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
}
.feature-panel-wide.feature-panel-reverse {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr);
}

/* Bare portrait screenshots (the lending panel has no dedicated cinematic
   render, per brief S11 feature 4 - closest accurate real screenshot used
   instead) get a lightweight CSS device shell so they don't read as a
   plain cropped image next to the other panels' pre-composed renders. */
.feature-media-shell {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 44px;
  background:
    radial-gradient(600px 420px at 30% 20%, rgba(224, 138, 80, 0.14), transparent 60%),
    var(--surface);
}

.feature-media-shell .device-frame {
  position: relative;
  width: min(320px, 100%);
  border-radius: 34px;
  padding: 12px;
  background: linear-gradient(160deg, #262932, #101216);
  border: 1px solid #33363F;
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(224, 138, 80, 0.06);
}

.feature-media-shell .device-frame img {
  border-radius: 24px;
  display: block;
}

/* A real exported document, not app UI - white paper on the dark shell
   reads as "here is the actual deliverable", distinct from the phone
   frames used everywhere else on the page. */
.paper-frame {
  position: relative;
  width: min(300px, 100%);
  border-radius: 10px;
  padding: 8px;
  background: #F4F1EC;
  box-shadow: 0 30px 70px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.06);
  transform: rotate(-2deg);
}

.paper-frame img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
  .paper-frame { transform: none; }
}

.feature-copy h3 {
  margin: 0 0 16px;
  font-size: clamp(1.6rem, 2.6vw, 2.25rem);
  font-weight: 720;
  letter-spacing: -0.02em;
  line-height: 1.1;
}

.feature-copy p {
  margin: 0;
  max-width: 40ch;
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--text-muted);
}

.feature-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  margin-bottom: 20px;
  background: var(--copper-soft);
  color: var(--copper);
}

@media (max-width: 780px) {
  .feature-panel,
  .feature-panel-reverse,
  .feature-panel-wide {
    grid-template-columns: 1fr;
    gap: 28px;
    min-height: 0;
  }
  .feature-panel-reverse .feature-media,
  .feature-panel-reverse .feature-copy { order: initial; }
  .feature-panel + .feature-panel { margin-top: 48px; padding-top: 48px; }
}

/* ==========================================================================
   Free / Pro strip (brief S12)
   ========================================================================== */
.tiers-inner {
  max-width: var(--max);
  margin: 0 auto;
  padding: 96px 0 120px;
}

.tiers-head {
  text-align: center;
  margin: 0 auto 48px;
  max-width: 560px;
}

.tiers-head .eyebrow { justify-content: center; }

.tiers-head h2 {
  margin: 0;
  font-size: clamp(1.8rem, 3vw, 2.6rem);
  font-weight: 740;
  letter-spacing: -0.025em;
  line-height: 1.15;
}

.tier-cards {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 24px;
}

.tier-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 22px;
  padding: 40px 36px;
  background-image: linear-gradient(rgba(22, 24, 29, 0.86), rgba(22, 24, 29, 0.86)), var(--tier-bg);
  background-size: cover;
  background-position: center;
}

.tier-card:not(.tier-card-pro) { --tier-bg: url(/images/marketing/tier-texture-free.webp); }
.tier-card-pro { --tier-bg: url(/images/marketing/tier-texture-pro.webp); }

.tier-card h3 {
  margin: 0 0 6px;
  font-size: 1.6rem;
  font-weight: 800;
  letter-spacing: -0.01em;
}

.tier-sub {
  margin: 0 0 24px;
  font-size: 0.875rem;
  color: var(--text-muted);
}

.tier-card:not(.tier-card-pro) h3 { margin-bottom: 24px; }

.tier-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 15px;
}

.tier-list li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 0.9375rem;
  line-height: 1.4;
  color: var(--text);
}

.tier-check {
  flex-shrink: 0;
  margin-top: 2px;
  color: var(--success);
}

.tier-card-pro {
  background-image:
    linear-gradient(165deg, rgba(224, 138, 80, 0.18), rgba(22, 24, 29, 0.82) 55%),
    var(--tier-bg);
  border: 1px solid rgba(224, 138, 80, 0.45);
  box-shadow: 0 0 0 1px rgba(224, 138, 80, 0.08), 0 34px 80px rgba(224, 138, 80, 0.14);
}

.tier-card-pro .tier-check { color: var(--copper); }

.tier-tag {
  position: absolute;
  top: -13px;
  left: 36px;
  margin: 0;
  padding: 5px 13px;
  border-radius: 999px;
  background: var(--copper);
  color: #191008;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

@media (max-width: 700px) {
  .tier-cards { grid-template-columns: 1fr; }
}

/* ==========================================================================
   Final CTA - compact band before the footer (brief S13, new section)
   ========================================================================== */
#final-cta {
  position: relative;
  text-align: center;
  background:
    radial-gradient(900px 600px at 50% 0%, rgba(224, 138, 80, 0.16), transparent 65%),
    var(--bg);
  border-top: 1px solid var(--border);
}

.final-cta-inner {
  max-width: 640px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.final-cta-mark {
  width: 168px;
  height: auto;
  margin: 0 0 32px;
  mix-blend-mode: screen;
}

.final-cta-inner h2 {
  margin: 0 0 14px;
  font-size: clamp(1.9rem, 3.4vw, 2.75rem);
  font-weight: 740;
  letter-spacing: -0.03em;
  line-height: 1.12;
}

.final-cta-inner p {
  margin: 0 0 36px;
  font-size: 1.0625rem;
  color: var(--text-muted);
}

@media (max-width: 500px) {
  .final-cta-mark { width: 132px; }
}

/* ==========================================================================
   Global reduced-motion floor (brief S17, applied in addition to every
   per-component reduced-motion rule above)
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
    transition-duration: 0.01ms !important;
  }
}
