/* KaveLight — minimal, elevated. Small type, deep negative space, one cave. */

:root {
  --canvas: #05070c;
  --ink: #f2f3f5;
  --ink-60: rgba(242, 243, 245, 0.58);
  --ink-40: rgba(242, 243, 245, 0.38);
  --ink-25: rgba(242, 243, 245, 0.22);
  --line: rgba(242, 243, 245, 0.10);
  --fill: rgba(242, 243, 245, 0.035);
  --err: #fa9e70;
  --pad: clamp(28px, 6vw, 96px);
}

* { margin: 0; padding: 0; box-sizing: border-box; }
[hidden] { display: none !important; }

html { height: 100%; }

body {
  min-height: 100%;
  background: var(--canvas);
  color: var(--ink);
  /* Home runs a light palette and the subpages run dark, so crossing between
     them repaints the whole frame. Easing the repaint turns a hard cut into a
     dissolve. See the route-change group further down. */
  transition: background-color 0.42s ease;
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

::selection { background: rgba(237, 173, 77, 0.25); }

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

/* ---------- nav ---------- */

.nav {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: clamp(20px, 4vw, 44px);
  padding: 30px var(--pad);
}

.nav-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 500;
  font-size: 12.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(242, 243, 245, 0.9);
  transition: color 0.42s ease, gap 0.42s ease;
}

.nav-brand img {
  width: 26px;
  height: 26px;
  transition: opacity 0.32s ease, width 0.42s ease;
  -webkit-mask-image: radial-gradient(circle closest-side, #fff 55%, transparent 98%);
  mask-image: radial-gradient(circle closest-side, #fff 55%, transparent 98%);
}

.nav-tabs {
  display: flex;
  gap: clamp(20px, 3vw, 34px);
  margin-left: auto;
  transition: background 0.42s ease, padding 0.42s ease;
}

.nav-tabs a {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(242, 243, 245, 0.55);
  padding: 8px 2px;
  transition: color 0.42s ease;
}

.nav-tabs a:hover, .nav-tabs a.is-active { color: var(--ink); }

.nav-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--ink);
  background: transparent;
  cursor: pointer;
  padding: 10px 19px;
  border: 1px solid rgba(242, 243, 245, 0.22);
  border-radius: 999px;
  transition: border-color 0.42s ease, background 0.42s ease, color 0.42s ease;
}

.nav-cta span { transition: transform 0.2s ease; }
.nav-cta:hover { border-color: rgba(242, 243, 245, 0.55); background: rgba(242, 243, 245, 0.04); }
.nav-cta:hover span { transform: translateX(2px); }

/* ---------- hero ---------- */

.page { min-height: 100vh; min-height: 100dvh; }

/* Route change: the router only toggles [hidden], which swapped pages on a
   single frame. Fading the incoming page in covers the palette transition
   running underneath it, so the two changes read as one move. */
.page:not([hidden]) { animation: pageIn 0.36s ease both; }
@keyframes pageIn { from { opacity: 0; } to { opacity: 1; } }

.display, .lede, .note, .hero-legal { transition: color 0.42s ease; }

.hero {
  position: relative;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  align-items: center; /* copy floats mid-frame, biased a touch high */
  overflow: hidden;
}

/* The studio portrait sits centre-right, so the frame is nudged hard left
   (0%) to open a clean column for the copy. Its own layer, so a crop that
   ever needs more pan room than `cover` leaves can be zoomed here alone. */
.hero-photo {
  position: absolute;
  inset: 0;
  background-image: url("/assets/hero-studio.jpg");
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 0% 50%;
}

/* The scrim layer that used to sit here is gone entirely (with its element):
   the seamless is already near-white where the copy sits, measured 227+ luma
   under every element, so a paper wash only milked the photo. */

.hero-copy {
  position: relative;
  padding: 0 var(--pad) 7vh; /* slight upward bias off true center */
  max-width: 900px;
}

/* ---------- home: the inverted (paper) palette ----------
   Only the home route runs light, and home is nothing but the hero, so the
   flip is a whole-frame decision rather than a patch. The nav lives outside
   .hero and floats over every route, which is why it keys off body.is-home;
   the hero button is scoped INSIDE .hero so the dark modal's submit button
   keeps its own light-on-dark treatment. */

body.is-home {
  --hero-ink: #14161c;
  --hero-ink-60: rgba(20, 22, 28, 0.66);
  --hero-ink-35: rgba(20, 22, 28, 0.38);
  /* Overscroll and the sliver under a short hero read paper, not void. */
  background: #fafafb;
}

body.is-home .display { color: var(--hero-ink); }
body.is-home .lede { color: var(--hero-ink-60); }
body.is-home .hero-actions .note { color: var(--hero-ink-60); }
body.is-home .hero-legal { color: var(--hero-ink-35); }

body.is-home .cta {
  color: #fafafb;
  background: var(--hero-ink);
}
body.is-home .cta:hover { background: var(--canvas); }
body.is-home .cta:disabled { background: rgba(20, 22, 28, 0.22); }

/* The waitlist modal follows the page it opens over: a dark sheet dropped on
   paper read like a different site mid-signup. */
body.is-home .modal-backdrop { background: rgba(232, 233, 236, 0.72); }
body.is-home .modal {
  background: #ffffff;
  border-color: rgba(20, 22, 28, 0.1);
  color: var(--hero-ink);
  box-shadow: 0 24px 60px rgba(20, 22, 28, 0.16);
}
body.is-home .modal-close:hover { background: rgba(20, 22, 28, 0.06); }
body.is-home .modal-close span::before,
body.is-home .modal-close span::after { background: rgba(20, 22, 28, 0.45); }
body.is-home .modal-close:hover span::before,
body.is-home .modal-close:hover span::after { background: var(--hero-ink); }

body.is-home .field span { color: rgba(20, 22, 28, 0.5); }
body.is-home .field input,
body.is-home .field select {
  color: var(--hero-ink);
  background: rgba(20, 22, 28, 0.035);
  border-color: rgba(20, 22, 28, 0.14);
}
body.is-home .field input:focus,
body.is-home .field select:focus {
  border-color: rgba(20, 22, 28, 0.4);
  background: rgba(20, 22, 28, 0.05);
}
body.is-home .field select:invalid { color: rgba(20, 22, 28, 0.45); }
body.is-home .field option { background: #ffffff; color: var(--hero-ink); }
body.is-home .select-wrap::after {
  border-right-color: rgba(20, 22, 28, 0.45);
  border-bottom-color: rgba(20, 22, 28, 0.45);
}

/* The dark theme's error orange sits at ~2:1 on white; this is the same
   warning read at a weight paper can carry. */
body.is-home .modal-error { color: #b3400f; }

/* The white modal needs its own ink for this too — the dark theme's --ink-40
   is near-invisible on paper. */
body.is-home .modal-sub { color: rgba(20, 22, 28, 0.55); }
body.is-home .modal-consent { color: rgba(20, 22, 28, 0.45); }
body.is-home .modal-consent a {
  color: rgba(20, 22, 28, 0.66);
  text-decoration-color: rgba(20, 22, 28, 0.28);
}
body.is-home .modal-consent a:hover { color: var(--hero-ink); }
body.is-home .joined-copy { color: rgba(20, 22, 28, 0.62); }

/* The emblem is a dark droplet with a soft black falloff — built for the dark
   canvas, and it reads as a smudge on paper. Home drops it and runs the
   wordmark alone. */
/* The emblem collapses rather than vanishing, so the wordmark slides into
   place instead of jumping when you cross between routes. */
body.is-home .nav-brand img { opacity: 0; width: 0; }
body.is-home .nav-brand { gap: 0; }

/* Her crown sits at the top of frame at every width (the source crops the bun
   at the edge, so no amount of panning clears it), and near 1024px the tabs
   run straight across it. These used to move to the left on home to dodge it,
   but a nav that relocates between routes reads as a harder cut than any
   colour change — so they hold the same right-hand position everywhere and
   take the CTA's frosted pill to stay legible over hair. (A `difference`
   blend was tried first and can't work here: .nav's z-index isolates it, so
   the type never sees the photo.) */
body.is-home .nav-tabs {
  /* Sideways padding only, so the pill's height stays the links' own and the
     centred nav doesn't shift vertically. Measured both routes after: the tab
     text lands on the identical pixel (882 at 1280 wide) with or without the
     pill, and the CTA and wordmark don't move either. */
  padding: 0 15px;
  border-radius: 999px;
  background: rgba(250, 250, 251, 0.78);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
body.is-home .nav-tabs a { color: rgba(20, 22, 28, 0.55); }
body.is-home .nav-tabs a:hover,
body.is-home .nav-tabs a.is-active { color: var(--hero-ink); }

body.is-home .nav-brand { color: rgba(20, 22, 28, 0.92); }
/* This crop puts her bun under the CTA around 1024px. A button is allowed its
   own surface (unlike a wash laid over the picture), so it carries a frosted
   paper fill: on the backdrop it reads as the outlined pill it always was,
   over hair it stays legible. */
body.is-home .nav-cta {
  color: var(--hero-ink);
  border-color: rgba(20, 22, 28, 0.2);
  background: rgba(250, 250, 251, 0.78);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
body.is-home .nav-cta:hover {
  border-color: rgba(20, 22, 28, 0.5);
  background: rgba(250, 250, 251, 0.94);
}

.display {
  /* Capped at 64. "Light the scene / before you step on set." is a longer line
     than the one this replaced, and measuring the photo behind it, the clean
     backdrop runs to x=760 at 1280 wide and collapses to p10 luma 9 after that
     (her hair). At 72px the line reached 819 and the tail died in it; 64 lands
     it around 736 with room to spare. */
  font-size: clamp(38px, 5.0vw, 64px);
  font-weight: 500;
  line-height: 1.06;
  letter-spacing: -0.028em;
  white-space: nowrap;
}

.lede {
  margin-top: 28px;
  font-size: 16px;
  line-height: 1.7;
  font-weight: 400;
  color: rgba(242, 243, 245, 0.78);
  max-width: 46ch;
}

.hero-legal {
  position: absolute;
  left: var(--pad);
  bottom: 32px;
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-25);
}

/* ---------- hero actions + CTA ---------- */

.hero-actions {
  margin-top: 38px;
  display: flex;
  align-items: center;
  gap: 22px;
}

.cta {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: #101319;
  background: rgba(242, 243, 245, 0.92);
  border: none;
  border-radius: 999px;
  padding: 12px 24px;
  cursor: pointer;
  transition: background 0.18s ease, transform 0.1s ease;
  white-space: nowrap;
}

.cta:hover { background: #ffffff; }
.cta:active { transform: scale(0.97); }
.cta:disabled { background: rgba(242, 243, 245, 0.22); cursor: default; }

.cta-arrow { display: inline-block; transition: transform 0.2s ease; }
.cta:hover .cta-arrow { transform: translateX(2px); }

.note {
  font-size: 13px;
  letter-spacing: 0.03em;
  color: var(--ink-25);
  font-variant-numeric: tabular-nums;
}

/* The live counter earns more presence than a footnote. */
.hero-actions .note { color: var(--ink-60); }

/* The counter's live-tracker period. */
.blink-dot {
  display: inline-block;
  animation: blinkDot 1.2s steps(1, end) infinite;
}
@keyframes blinkDot {
  0%, 60% { opacity: 1; }
  61%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .blink-dot { animation: none; }
}

/* ---------- modal ---------- */

.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(3, 4, 8, 0.72);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  animation: fadeIn 0.22s ease both;
}

.modal {
  position: relative;
  width: min(400px, 100%);
  background: #090c12;
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 32px;
  animation: modalIn 0.34s cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes modalIn {
  from { opacity: 0; transform: translateY(12px) scale(0.99); }
  to { opacity: 1; transform: none; }
}

.modal-close {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 30px;
  height: 30px;
  background: none;
  border: none;
  cursor: pointer;
  border-radius: 50%;
  transition: background 0.15s ease;
}

.modal-close:hover { background: rgba(242, 243, 245, 0.06); }

.modal-close span::before, .modal-close span::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 12px;
  height: 1px;
  background: var(--ink-40);
  transition: background 0.15s ease;
}

.modal-close span::before { transform: translate(-50%, -50%) rotate(45deg); }
.modal-close span::after { transform: translate(-50%, -50%) rotate(-45deg); }
.modal-close:hover span::before, .modal-close:hover span::after { background: var(--ink); }

.modal-title {
  font-size: 16.5px;
  font-weight: 600;
  letter-spacing: -0.005em;
  margin-bottom: 24px;
}

/* Sets the expectation before anyone types: the button says download, but what
   arrives is an email with the link. */
.modal-sub {
  margin: -14px 0 22px;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--ink-40);
}

.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.field {
  display: block;
  margin-bottom: 13px;
}

.field span {
  display: block;
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-40);
  margin-bottom: 7px;
}

.field input, .field select {
  width: 100%;
  font: inherit;
  font-size: 13.5px;
  color: var(--ink);
  background: var(--fill);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 11px 13px;
  outline: none;
  transition: border-color 0.18s ease, background 0.18s ease;
  appearance: none;
  -webkit-appearance: none;
}

.field input:focus, .field select:focus {
  border-color: rgba(242, 243, 245, 0.4);
  background: rgba(242, 243, 245, 0.055);
}

.select-wrap { position: relative; }

.select-wrap::after {
  content: "";
  position: absolute;
  right: 14px;
  top: 50%;
  width: 6px;
  height: 6px;
  border-right: 1px solid var(--ink-40);
  border-bottom: 1px solid var(--ink-40);
  transform: translateY(-70%) rotate(45deg);
  pointer-events: none;
}

.field select { cursor: pointer; }
.field select:invalid { color: var(--ink-40); }
.field option { background: #090c12; color: var(--ink); }

.modal-error {
  font-size: 12px;
  color: var(--err);
  margin: -3px 0 12px;
}

.modal-submit {
  width: 100%;
  justify-content: center;
  margin-top: 6px;
}

.joined { text-align: center; padding: 16px 0 8px; animation: fadeUp 0.4s ease both; }
.joined-title { font-size: 17px; font-weight: 600; letter-spacing: -0.005em; }
.joined-copy { margin-top: 7px; font-size: 13px; color: var(--ink-60); }

/* The link lives in the success panel as well as the email: delivery can fail
   silently, and the quiet-invited import cohort never got an email at all. */
.joined-dl { margin-top: 18px; }
.joined-note { margin-top: 12px; font-size: 11.5px; color: var(--ink-40); }
body.is-home .joined-note { color: rgba(20, 22, 28, 0.5); }

/* ---------- subpages (FAQ / The App) ---------- */

.subpage {
  max-width: 640px;
  margin: 0 auto;
  padding: clamp(130px, 20vh, 200px) var(--pad) clamp(64px, 12vh, 128px);
}

.display-sm {
  font-size: clamp(26px, 3.2vw, 36px);
  font-weight: 500;
  letter-spacing: -0.018em;
}

.subpage-lede { margin-top: 14px; }

.subpage-wide { max-width: 1300px; }

/* ---------- The App — demo reel ----------
   Two categories, each holding a horizontal slider. Stacking every demo
   vertically made the section run for screens; a slider keeps a category one
   screen tall no matter how many demos land in it, and the heading stays put
   above them so it's always clear which category you're in. */

.reel { margin-top: clamp(64px, 9vh, 104px); }
.reel-cat + .reel-cat { margin-top: clamp(84px, 13vh, 160px); }

.reel-cat-head { margin-bottom: clamp(20px, 3vh, 32px); }

.reel-cat-label {
  font-size: clamp(26px, 3.2vw, 36px);
  font-weight: 500;
  /* Uppercased in CSS, not in the markup, so the source stays readable and
     screen readers still get a normal word rather than initialisms. Caps need
     the tracking opened up: the -0.018em that suits mixed case sets capitals
     too tight. */
  text-transform: uppercase;
  letter-spacing: 0.01em;
}

/* No width of its own: it fills the subpage's content box, so the slider's
   left edge is the heading's left edge. */
.slider { width: 100%; }

/* Native scroll-snap rather than a transform carousel: trackpad and touch
   swipe work for free, and it degrades to a plain scroller without JS. */
.slides {
  display: flex;
  gap: clamp(16px, 2vw, 28px);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.slides::-webkit-scrollbar { display: none; }

.slide {
  flex: 0 0 89%;
  scroll-snap-align: start;
  min-width: 0;
  opacity: 0.32;
  transition: opacity 0.45s ease;
}

.slide.is-current { opacity: 1; }

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

.slide-cap {
  margin-top: 14px;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink-60);
}

.demo-frame {
  position: relative;
  overflow: hidden;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: #090c12;
  /* Each frame declares its clip's ratio, so height is reserved before the
     file lands and nothing below jumps when it does. */
  aspect-ratio: var(--ar, 1.565);
}

.demo-video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  cursor: zoom-in;
}

/* The scan slide is a phone shot beside the app. Growing each frame by its own
   aspect ratio lands the two at the SAME height at their natural widths — no
   cropping, no letterboxing — and keeps the pairing readable as one moment. */
.demo-pair { display: flex; gap: clamp(10px, 1.2vw, 16px); align-items: flex-start; }
.demo-pair > .demo-frame { flex: var(--ar) 1 0; min-width: 0; }

.demo-label {
  position: absolute;
  left: 12px;
  bottom: 11px;
  padding: 5px 10px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(242, 243, 245, 0.86);
  background: rgba(5, 7, 12, 0.55);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-radius: 999px;
  pointer-events: none;
}

/* The library slide is stills, not a clip: the three category panels fanned
   like cards on a bench. Same rotated-card language the App page already uses
   further down, so it doesn't read as a foreign element between the videos.
   Hovering opens the fan — the spread is the "there is more here" gesture. */

.asset-fan {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(12px, 2%, 28px);
}

.asset-fan img {
  width: 41%;
  height: auto;
  border-radius: 10px;
  border: 1px solid rgba(242, 243, 245, 0.14);
  box-shadow: 0 26px 60px rgba(0, 0, 0, 0.55);
  cursor: zoom-in;
  transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}

.asset-fan img:nth-child(1) { transform: rotate(-5deg) translateX(22%); z-index: 1; }
.asset-fan img:nth-child(2) { transform: translateY(-2%) scale(1.07); z-index: 3; }
.asset-fan img:nth-child(3) { transform: rotate(5deg) translateX(-22%); z-index: 2; }

.demo-frame:hover .asset-fan img:nth-child(1) { transform: rotate(-8deg) translateX(10%) translateY(1%); }
.demo-frame:hover .asset-fan img:nth-child(2) { transform: translateY(-4%) scale(1.09); }
.demo-frame:hover .asset-fan img:nth-child(3) { transform: rotate(8deg) translateX(-10%) translateY(1%); }

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

/* ---- slider controls ---- */

.slider-nav {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-top: 18px;
}

.slider-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  font: inherit;
  font-size: 13px;
  color: var(--ink-60);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 50%;
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.slider-arrow:hover:not(:disabled) { color: var(--ink); border-color: rgba(242, 243, 245, 0.32); }
.slider-arrow:disabled { opacity: 0.3; cursor: default; }

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

.slider-dot {
  width: 6px;
  height: 6px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(242, 243, 245, 0.22);
  cursor: pointer;
  transition: background 0.2s ease, width 0.2s ease;
}
.slider-dot.is-on { width: 20px; border-radius: 3px; background: rgba(242, 243, 245, 0.72); }

@media (max-width: 760px) {
  /* Side by side on a phone would make the portrait a sliver — stack instead. */
  .demo-pair { flex-direction: column; }
  .demo-pair > .demo-frame { flex: none; width: 100%; }
}

/* ---------- The App — the reveal reel ---------- */

.app-head { max-width: 720px; }

/* Status line — plain uppercase with a live blinking dot at the end. */
.soon-line {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-60);
}

.soon-blink {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--ink);
  animation: blinkDot 1.2s steps(1, end) infinite;
}

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

.app-display {
  margin-top: 18px;
  font-size: clamp(48px, 7.4vw, 96px);
  font-weight: 500;
  line-height: 1.02;
  letter-spacing: -0.03em;
}

.app-lede {
  margin-top: 24px;
  font-size: clamp(15px, 1.5vw, 18px);
  line-height: 1.65;
  color: var(--ink-60);
  max-width: 46ch;
}

/* Moments — floating images that drift as you scroll (ol.studio-style):
   each moment places its image at a distinct width/offset; the [data-drift]
   wrapper gets a scroll-parallax translate from app.js, the inner figure
   keeps the reveal transition, so the two transforms never fight. */

.moment { position: relative; }

.m-1 { margin-top: clamp(72px, 11vh, 128px); }
.m-1 .float { width: min(86%, 1020px); }

.m-2 { margin-top: clamp(96px, 14vh, 176px); }
.m-2 .float { width: min(60%, 680px); margin-left: auto; }

.m-3 { margin-top: clamp(96px, 14vh, 176px); }
.m-3 .float { width: min(64%, 720px); }

.m-4 { margin-top: clamp(96px, 14vh, 176px); }
.m-4 .float { width: min(88%, 1060px); margin-left: auto; }

.m-5 { margin-top: clamp(96px, 14vh, 176px); }
.m-5 .float { width: min(86%, 1020px); }

.m-6 { margin-top: clamp(96px, 14vh, 176px); }
.m-6 .float { width: min(60%, 680px); margin-left: auto; }

.m-7 { margin-top: clamp(96px, 14vh, 176px); padding-bottom: clamp(120px, 18vh, 200px); }
.m-7 > .float:first-child { width: min(62%, 760px); }
.m-7 .fig { max-width: 58%; }

.float { will-change: transform; }

/* Small UI cards live in m-5's empty right column — never over captions */
.float-card { position: absolute; }
.float-card .ui-card { transform: rotate(-1.6deg); }
.float-card-a { width: min(30%, 320px); right: 0; top: 4%; }
.float-card-a .ui-card { transform: rotate(1.8deg); }
.float-card-b { width: min(26%, 270px); right: 4%; top: 58%; }

.m-7 .ui-note {
  position: absolute;
  left: 0;
  bottom: 5%;
  max-width: 24ch;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--ink-60);
}

.shot { margin: 0; }

.shot-frame {
  overflow: hidden;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: #090c12;
}

.shot-frame img {
  display: block;
  width: 100%;
  height: auto;
  transform: scale(1.015);
  transition: transform 1.1s cubic-bezier(0.22, 1, 0.36, 1);
}

.shot:hover .shot-frame img { transform: scale(1.035); }

/* Reveal choreography: frame rises, image settles from a slight zoom */
.shot.reveal .shot-frame img { transform: scale(1.07); }
.shot.reveal.is-in .shot-frame img { transform: scale(1.015); }

.ui-card {
  border-radius: 12px;
  border: 1px solid var(--line);
  background: #090c12;
  overflow: hidden;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
}

.ui-card img { display: block; width: 100%; height: auto; }

/* FIG captions — floating editorial labels (always above floating cards) */
.fig {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-top: 14px;
}

.fig-num {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: #edad4d;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.fig-tag {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
}

.fig-text {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--ink-60);
}

/* Interludes — big casual statements between moments */
.interlude {
  margin-top: clamp(88px, 15vh, 168px);
  font-size: clamp(30px, 4.6vw, 58px);
  font-weight: 500;
  line-height: 1.14;
  letter-spacing: -0.025em;
  max-width: 16ch;
}

.m-4 + .moment .float,
.interlude + .moment { margin-top: clamp(64px, 10vh, 120px); }

/* Features — numbered editorial rows */

.feature-list {
  margin-top: clamp(88px, 15vh, 160px);
  border-top: 1px solid var(--line);
}

.feature-row {
  display: grid;
  grid-template-columns: 64px minmax(0, 1fr) minmax(0, 1.2fr);
  gap: clamp(18px, 3vw, 48px);
  align-items: baseline;
  padding: clamp(28px, 4.5vh, 44px) 0;
  border-bottom: 1px solid var(--line);
}

.feature-num {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: #edad4d;
  font-variant-numeric: tabular-nums;
}

.feature-title {
  font-size: clamp(20px, 2.6vw, 30px);
  font-weight: 500;
  letter-spacing: -0.018em;
  transition: color 0.25s ease;
}

.feature-copy {
  font-size: 13.5px;
  line-height: 1.8;
  color: var(--ink-60);
}

/* Closing CTA */

.app-cta {
  margin-top: clamp(72px, 12vh, 128px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 26px;
  text-align: center;
}

.app-cta-line {
  font-size: clamp(26px, 3.6vw, 44px);
  font-weight: 500;
  letter-spacing: -0.022em;
}

@media (max-width: 700px) {
  /* Floats stack full-width on phones; the drift parallax is disabled in JS */
  .m-1 .float, .m-2 .float, .m-3 .float, .m-4 .float, .m-5 .float, .m-6 .float,
  .m-7 > .float:first-child { width: 100%; margin-left: 0; }
  .m-3 { margin-top: clamp(40px, 7vh, 64px); }
  .float-card { position: static; width: 70%; margin: 18px auto 0; }
  .float-card .ui-card, .float-card-a .ui-card { transform: none; }
  .m-7 .ui-note { position: static; margin: 14px auto 0; text-align: center; }
  .fig { flex-wrap: wrap; }
  .feature-row { grid-template-columns: 40px 1fr; }
  .feature-copy { grid-column: 2; }
}

/* FAQ — animated accordion */

.faq-list { margin-top: clamp(34px, 6vh, 56px); border-top: 1px solid var(--line); }

.faq-item { border-bottom: 1px solid var(--line); }

.faq-q {
  position: relative;
  width: 100%;
  text-align: left;
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
  background: none;
  border: none;
  cursor: pointer;
  padding: 21px 40px 21px 0;
  transition: color 0.15s ease;
}

.faq-q:hover { color: rgba(242, 243, 245, 0.72); }

.faq-x {
  position: absolute;
  right: 4px;
  top: 50%;
  width: 11px;
  height: 11px;
  transform: translateY(-50%);
}

.faq-x::before, .faq-x::after {
  content: "";
  position: absolute;
  background: var(--ink-40);
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}

.faq-x::before { left: 0; right: 0; top: 5px; height: 1px; }
.faq-x::after { top: 0; bottom: 0; left: 5px; width: 1px; }

.faq-item.is-open .faq-x::after { transform: scaleY(0); }

.faq-a {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.36s cubic-bezier(0.4, 0, 0.2, 1);
}

.faq-item.is-open .faq-a { grid-template-rows: 1fr; }

.faq-a-inner { overflow: hidden; }

.faq-a-inner p {
  padding: 0 36px 22px 0;
  font-size: 13.5px;
  line-height: 1.8;
  color: var(--ink-60);
  max-width: 60ch;
}

/* ---------- Light it: clip, then its panels spread underneath ----------
   Not a slider and not a column. The clip runs through fixtures, gels and
   diffusion too fast to read, so the panels it drives sit below it, fanned and
   overlapping like stills dropped on a bench. Each one opens full size in the
   lightbox, which is why cropping the previews costs nothing. */

/* Clip on top, fan underneath. A plain block, but named so the section's
   shape is legible in the markup. */
.light-strip {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  margin-top: clamp(20px, 2.6vw, 34px);
  padding: 18px 0 30px;
}

.light-card {
  flex: 0 0 auto;
  width: clamp(124px, 15.4vw, 208px);
  margin: 0 0 0 -22px;
  border-radius: 10px;
  overflow: hidden;
  background: #0b0e14;
  border: 1px solid var(--line);
  box-shadow: 0 16px 34px rgba(0, 0, 0, 0.42);
  cursor: zoom-in;
  transform: rotate(var(--rot)) translateY(var(--dy));
  transition: transform 0.28s ease, border-color 0.2s ease, box-shadow 0.28s ease;
}

.light-card:first-child { margin-left: 0; }


/* Fanned by hand rather than by formula: an even sequence reads mechanical. */
.light-card:nth-child(1) { --rot: -3.2deg; --dy: 6px;  z-index: 1; }
.light-card:nth-child(2) { --rot: 2.4deg;  --dy: 22px; z-index: 2; }
.light-card:nth-child(3) { --rot: -1.4deg; --dy: 0;    z-index: 3; }
.light-card:nth-child(4) { --rot: 3.1deg;  --dy: 26px; z-index: 4; }
.light-card:nth-child(5) { --rot: -2.2deg; --dy: 10px; z-index: 5; }

/* Straighten and lift on hover, and come to the front so the neighbour it
   overlaps can't clip it. */
.light-card:hover {
  transform: rotate(0deg) translateY(-6px) scale(1.03);
  border-color: rgba(242, 243, 245, 0.26);
  box-shadow: 0 24px 48px rgba(0, 0, 0, 0.55);
  z-index: 10;
}

/* height:auto is load-bearing — the markup carries width/height attributes for
   layout stability, and without it the attribute height wins over max-height
   and the tall panels render at full natural size. */
.light-card img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 250px;
  object-fit: cover;
  object-position: top;
}

.light-card figcaption {
  padding: 7px 10px 9px;
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-40);
}

@media (max-width: 900px) {
  /* Overlap and rotation both shrink; at phone width a fan turns into a pile. */
  .light-strip {
    justify-content: flex-start;
    overflow-x: auto;
    scrollbar-width: none;
    padding: 14px 0 22px;
  }
  .light-strip::-webkit-scrollbar { display: none; }
  .light-card { margin-left: -12px; width: clamp(116px, 34vw, 168px); }
  .light-card:nth-child(odd)  { --rot: -1.6deg; }
  .light-card:nth-child(even) { --rot: 1.6deg; }
  .light-card img { max-height: 190px; }
}

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

}

/* The App — updates */

.log { margin-top: clamp(34px, 6vh, 56px); border-top: 1px solid var(--line); }

.log-entry {
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: clamp(14px, 3vw, 32px);
  padding: clamp(26px, 4vh, 36px) 0;
  border-bottom: 1px solid var(--line);
}

.log-date {
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-25);
  padding-top: 4px;
  white-space: nowrap;
}

.log-body h2 { font-size: 15.5px; font-weight: 600; letter-spacing: -0.005em; }

.log-body p {
  margin-top: 7px;
  font-size: 13.5px;
  line-height: 1.75;
  color: var(--ink-60);
  max-width: 56ch;
}

/* ---------- footer (subpages only) ---------- */

.foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  max-width: 640px;
  margin: 0 auto;
  padding: 22px var(--pad) 40px;
}

.foot-legal { font-size: 11px; letter-spacing: 0.04em; color: var(--ink-25); }

.foot-tabs { display: flex; gap: 20px; }

.foot-tabs a {
  font-size: 11.5px;
  color: var(--ink-40);
  transition: color 0.15s ease;
}

.foot-tabs a:hover { color: var(--ink); }

/* ---------- reveals ---------- */

.reveal {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 1.1s cubic-bezier(0.16, 1, 0.3, 1),
              transform 1.1s cubic-bezier(0.16, 1, 0.3, 1);
}

.reveal.is-in { opacity: 1; transform: none; }

@keyframes fadeUp {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  .faq-a { transition: none; }
  /* The route dissolve is comfort, not information — drop it wholesale here. */
  .page:not([hidden]) { animation: none; }
  body, .nav-brand, .nav-brand img, .nav-tabs, .nav-tabs a, .nav-cta,
  .display, .lede, .note, .hero-legal { transition: none; }
}

/* ---------- small screens ---------- */

@media (max-width: 760px) {
  .nav { padding: 20px 22px; gap: 14px; }
  .nav-brand span { display: none; }
  .nav-cta { display: none; }

  /* Phone: a portrait viewport leaves no column beside her, and copy laid over
     the face buried both. So the frame splits on a crisp edge — copy on top,
     photo below. */
  .hero { align-items: flex-start; }
  .hero-photo {
    inset: auto 0 0 0;
    /* Sized to what the copy needs above it, plus a band for the legal line. */
    height: 42%;
    background-position: 72% 16%;
  }
  /* The legal line is absolutely bottom-anchored, which now lands it on her
     jacket. Park it just above the photo, in the paper. */
  body.is-home .hero-legal { bottom: calc(42% + 16px); }
  /* Phone normally runs the emblem alone, but home hides the emblem, so the
     wordmark has to come back or the brand disappears entirely. With the
     wordmark present there is no room beside it, so the tabs keep the right
     (over backdrop at this crop) instead of moving left as they do on desktop. */
  body.is-home .nav-brand span { display: inline; }
  body.is-home .nav-tabs { margin-left: auto; }
  /* Copy now sits at the top, so it needs clearance under the nav rather than
     padding beneath it. */
  .hero-copy { padding-top: 11vh; padding-bottom: 0; }

  /* The paper takes the photo's own backdrop colour, sampled at the seam:
     rgb(240,237,236). The site's #fafafb is cooler and 10 levels brighter,
     which read as a step exactly where the two meet. Matching it makes the
     split one surface with a picture in the bottom half. */
  body.is-home { background: #f0edec; }
  .display { font-size: clamp(34px, 9.6vw, 46px); white-space: normal; }
  .hero-actions { flex-direction: column; align-items: flex-start; gap: 14px; }
  .hero-legal { bottom: 20px; }
  .field-row { grid-template-columns: 1fr; gap: 0; }
  .modal { padding: 26px 22px; }
  .log-entry { grid-template-columns: 1fr; gap: 6px; }
}

/* ---------- support: a contact form, nothing else ----------
   The fields sat naked on the page and read like a draft. They now live in a
   panel with its own edge, which gives the form a shape to occupy instead of
   floating in the column. Controls still come from .field / .field-row so this
   and the waitlist modal stay the same control. */

.support-form,
.support-sent {
  margin-top: clamp(26px, 4.5vh, 40px);
  max-width: 560px;
  padding: clamp(24px, 3.4vw, 38px);
  background: var(--fill);
  border: 1px solid var(--line);
  border-radius: 16px;
}

.support-form .field { margin-bottom: 16px; }
.support-form .field-row { gap: 14px; }

.support-form .field span {
  font-size: 10px;
  letter-spacing: 0.08em;
  margin-bottom: 8px;
}

/* Taller than the modal's: this is the page's whole job, not a step in a
   sign-up, so it can afford the room. */
.support-form input,
.support-form textarea {
  padding: 12px 14px;
  border-radius: 10px;
}

.support-form textarea {
  width: 100%;
  font: inherit;
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--ink);
  background: rgba(242, 243, 245, 0.035);
  border: 1px solid var(--line);
  outline: none;
  resize: vertical;
  min-height: 150px;
  transition: border-color 0.18s ease, background 0.18s ease, box-shadow 0.18s ease;
}

/* A ring rather than just a brighter edge: on a dark panel a 1px border change
   is easy to miss, and this form is the only thing on the page. */
.support-form input:focus,
.support-form textarea:focus {
  border-color: rgba(242, 243, 245, 0.45);
  background: rgba(242, 243, 245, 0.06);
  box-shadow: 0 0 0 3px rgba(242, 243, 245, 0.07);
}

.support-form .cta {
  width: 100%;
  justify-content: center;
  margin-top: 8px;
  padding: 13px 24px;
}

.support-form .modal-error { margin: 2px 0 12px; }

/* Sent state takes the panel's place, so the page doesn't jump. */
.support-sent { text-align: center; }

.support-sent svg {
  width: 34px;
  height: 34px;
  margin-bottom: 12px;
  stroke: var(--ink);
  stroke-width: 1.25;
  fill: none;
  opacity: 0.85;
}

@media (max-width: 560px) {
  .support-form .field-row { grid-template-columns: 1fr; gap: 0; }
}

/* ---------- legal pages (privacy / terms) ---------- */

.legal { padding-bottom: 40px; }

.legal-date {
  margin-top: 10px;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--ink-40);
}

.legal p {
  margin-top: 16px;
  font-size: 13.5px;
  line-height: 1.7;
  color: var(--ink-60);
}

.legal-summary {
  margin-top: 22px !important;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--ink) !important;
}

.legal h2 {
  margin-top: 40px;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.legal ul {
  margin-top: 12px;
  padding-left: 20px;
}

.legal li {
  margin-top: 7px;
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--ink-60);
}

.legal strong { color: var(--ink); font-weight: 500; }

.legal a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--ink-25);
  text-underline-offset: 3px;
  transition: text-decoration-color 0.15s ease;
}

.legal a:hover { text-decoration-color: var(--ink); }

/* consent line inside the join modal */
.modal-consent {
  margin: 10px 0 0;
  font-size: 11px;
  line-height: 1.5;
  text-align: center;
  color: var(--ink-40);
}

.modal-consent a {
  color: var(--ink-60);
  text-decoration: underline;
  text-decoration-color: var(--ink-25);
  text-underline-offset: 2px;
}

.modal-consent a:hover { color: var(--ink); }

/* ---------- download button (App page) ---------- */

.dl-row {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  margin-top: 26px;
}

.dl-row-center {
  justify-content: center;
  margin-top: 4px;
}

.app-cta .dl-note { margin-top: 14px; }

.cta-ghost {
  color: var(--ink);
  background: transparent;
  border: 1px solid rgba(242, 243, 245, 0.28);
  transition: border-color 0.18s ease, background 0.18s ease, transform 0.1s ease;
}

.cta-ghost:hover {
  background: rgba(242, 243, 245, 0.06);
  border-color: rgba(242, 243, 245, 0.5);
}

@media (max-width: 700px) {
  .dl-row { gap: 12px; }
  .dl-row-center { flex-direction: column; }
}

/* ---------- feature index (App page) ---------- */

.feature-index {
  margin-top: clamp(88px, 14vh, 160px);
}

.fi-head {
  font-size: clamp(22px, 2.6vw, 30px);
  font-weight: 500;
  letter-spacing: -0.015em;
  margin-bottom: 8px;
}

.fi-group { margin-top: 44px; }

.fi-cat {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-40);
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
}

.fi-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 28px 40px;
  margin-top: 24px;
}

.fi-card h3 {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.fi-card p {
  margin-top: 7px;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--ink-60);
}

@media (max-width: 700px) {
  .fi-grid { grid-template-columns: 1fr; gap: 22px; }
  .fi-group { margin-top: 36px; }
}

/* ---------- lightbox (click any shot for full size) ---------- */

.shot-frame img, .ui-card img { cursor: zoom-in; }

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(16px, 4vw, 48px);
  background: rgba(3, 4, 8, 0.88);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  cursor: zoom-out;
  animation: fadeIn 0.18s ease both;
}

.lightbox img,
.lightbox video {
  max-width: 100%;
  max-height: 100%;
  border-radius: 8px;
  box-shadow: 0 32px 90px rgba(0, 0, 0, 0.6);
}

/* The enlarged clip is the one place controls belong, so it keeps its own
   cursor rather than inheriting the backdrop's zoom-out. */
.lightbox video { cursor: default; }
