/* ==========================================================================
   ROSALYX — COMPONENTS & LAYOUT
   Every value here comes from tokens.css. Selectors are kept flat and
   single-class so nothing silently overrides anything else.
   ========================================================================== */

/* --------------------------------------------------------------- 1. RESET */

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

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  min-height: 100dvh;
  background-color: var(--surface-base);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  font-synthesis-weight: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;      /* the thread is positioned wide; never scroll sideways */
}

img, svg, video, picture {
  display: block;
  max-width: 100%;
}

input, button, textarea, select { font: inherit; color: inherit; }

button { background: none; border: none; padding: 0; cursor: pointer; }

ul, ol { list-style: none; padding: 0; }

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

/* ---------------------------------------------------- 2. TYPOGRAPHY BASE */

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 500;
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
  color: var(--text-accent);
  /* Fraunces variable axes: a little softness on the terminals, a little wonk
     in the curves — the hand-cut quality that matches the wax-sealed paper. */
  font-variation-settings: 'SOFT' 28, 'WONK' 1, 'opsz' 72;
  text-wrap: balance;
}

p { text-wrap: pretty; }

strong { font-weight: 600; }

.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-weight: 500;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}
.eyebrow::before {
  content: '';
  width: var(--space-xl);
  height: 1px;
  background: linear-gradient(90deg, var(--amber), var(--border-strong));
  flex: none;
}
.eyebrow-center { justify-content: center; }
.eyebrow-center::after {
  content: '';
  width: var(--space-xl);
  height: 1px;
  background: linear-gradient(270deg, var(--amber), var(--border-strong));
  flex: none;
}

.lede {
  font-size: var(--text-lg);
  line-height: var(--leading-relaxed);
  color: var(--text-secondary);
  max-width: var(--measure);
}

/* The trademark mark.

   `line-height: 0` is the important part: a raised glyph would otherwise add
   leading to whichever line it sits on, nudging the whole paragraph. Sized in
   `em` so it tracks whatever text it follows, with overrides below where the
   type is large enough that a proportional mark would shout. */
.tm {
  font-size: 0.42em;
  vertical-align: super;
  line-height: 0;
  letter-spacing: 0.02em;
  margin-left: 0.06em;
  font-weight: 500;
}
/* At 112px the wordmark would carry a 47px trademark. Scale it right down. */
.hero-wordmark .tm { font-size: 0.17em; margin-left: 0.02em; }
.footer-brand .tm,
.nav-brand .tm    { font-size: 0.3em; }

/* ------------------------------------------------- 3. FOCUS & UTILITIES */

:focus-visible {
  outline: 2px solid var(--olive);
  outline-offset: 3px;
  border-radius: var(--radius-xs);
}
/* On the dark footer the olive ring would disappear into the background. */
.footer :focus-visible,
.btn-primary:focus-visible {
  outline-color: var(--ember);
}

.skip-link {
  position: absolute;
  left: var(--space-md);
  top: var(--space-md);
  z-index: var(--z-modal);
  transform: translateY(-200%);
  background: var(--surface-floating);
  color: var(--text-accent);
  padding: var(--space-sm) var(--space-lg);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-floating);
  font-size: var(--text-sm);
  font-weight: 600;
  transition: transform var(--dur-base) var(--ease-out);
}
.skip-link:focus { transform: translateY(0); }

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

.icon {
  width: var(--icon-md);
  height: var(--icon-md);
  stroke: currentColor;
  stroke-width: var(--icon-stroke);
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  flex: none;
}
.icon-sm { width: var(--icon-sm); height: var(--icon-sm); }
.icon-lg { width: var(--icon-lg); height: var(--icon-lg); }

/* ------------------------------------------------------------- 4. LAYOUT */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container-narrow { max-width: var(--container-narrow); }

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

.main {
  position: relative;
  z-index: var(--z-base);
}

/* --------------------------------------------------------------- 5. NAV */

.nav {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  padding-block: var(--space-md);
  transition: background-color var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              backdrop-filter var(--dur-base) var(--ease-out);
}
.nav-scrolled {
  background-color: rgba(241, 232, 219, 0.82);
  backdrop-filter: blur(14px) saturate(1.1);
  -webkit-backdrop-filter: blur(14px) saturate(1.1);
  box-shadow: var(--shadow-elevated);
}

.nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
}

.nav-brand {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 600;
  letter-spacing: var(--tracking-tight);
  color: var(--text-accent);
  font-variation-settings: 'SOFT' 34, 'WONK' 1, 'opsz' 40;
  padding-block: var(--space-2xs);
}

.nav-links {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

.nav-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-target);
  padding-inline: var(--space-md);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-secondary);
  transition: color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
.nav-link:hover { color: var(--text-accent); background-color: rgba(53, 64, 36, 0.06); }
.nav-link-active { color: var(--text-accent); font-weight: 600; }
.nav-link-active::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 0.35rem;
  width: 3px; height: 3px;
  border-radius: 50%;
  background: var(--amber);
  transform: translateX(-50%);
}

.nav-actions {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

.nav-icon-link {
  display: inline-grid;
  place-items: center;
  width: var(--tap-target);
  height: var(--tap-target);
  border-radius: var(--radius-pill);
  color: var(--text-secondary);
  transition: color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
.nav-icon-link:hover { color: var(--text-accent); background-color: rgba(53, 64, 36, 0.06); }

.nav-toggle {
  display: none;
  place-items: center;
  width: var(--tap-target);
  height: var(--tap-target);
  border-radius: var(--radius-pill);
  color: var(--text-accent);
}

/* ------------------------------------------------------------ 6. BUTTONS */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  min-height: var(--tap-target);
  padding: var(--space-sm) var(--space-xl);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  text-align: center;
  /* A button label is a single instruction and reads as one. "Make a
     consultation appointment" breaking across two lines turned the primary
     action into a paragraph. Narrow screens tighten the type and padding
     below rather than letting it wrap. */
  white-space: nowrap;
  transition: transform var(--dur-base) var(--ease-spring),
              background-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
  will-change: transform;
}
.btn:active { transform: scale(0.975); }

/* Primary sits on the floating plane — it is the one action on the page. */
.btn-primary {
  background-color: var(--olive);
  color: var(--bone);
  box-shadow: var(--shadow-floating);
}
.btn-primary:hover {
  background-color: var(--olive-soft);
  box-shadow: var(--shadow-floating), var(--glow-ember-soft);
}

.btn-secondary {
  background-color: transparent;
  color: var(--text-accent);
  border: 1px solid var(--border-strong);
}
.btn-secondary:hover {
  background-color: rgba(53, 64, 36, 0.06);
  border-color: var(--olive);
}

.btn-lg {
  font-size: var(--text-base);
  padding: var(--space-md) var(--space-2xl);
  min-height: 3.5rem;
}

.btn-icon-trail { transition: transform var(--dur-base) var(--ease-out); }
.btn:hover .btn-icon-trail { transform: translateX(3px); }

/* Floating action button — appears once the hero CTA has scrolled away, so
   the single primary action is never more than a thumb away. */
.fab {
  position: fixed;
  right: var(--gutter);
  bottom: var(--gutter);
  z-index: var(--z-floating);
  opacity: 0;
  visibility: hidden;
  transform: translateY(1rem) scale(0.96);
  transition: opacity var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-spring),
              visibility var(--dur-base);
}
.fab-visible {
  opacity: 1;
  visibility: visible;
  transform: translateY(0) scale(1);
}

/* ---------------------------------------------------------- 7. THE THREAD
   A single glowing filament that leaves the hero video and runs the length of
   the page. It sits above section fills but below every elevated surface, so
   it passes visibly *behind* the cards.
   Colours and blur radii are the sampled falloff from hero.mp4 — see tokens. */

.thread {
  position: absolute;
  inset: 0;
  z-index: var(--z-thread);
  pointer-events: none;
  overflow: visible;
}
.thread-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  filter: var(--glow-thread);
  /* Promotes the glowing layer to its own compositor layer, so scrolling
     repaints the page without re-rasterising the blur every frame. */
  transform: translateZ(0);
}
/* Width is set per filament from JS; round joins so no segment boundary can
   ever read as a corner. */
.thread-path {
  fill: none;
  stroke: var(--ember);
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* The faint full-length track shows where the filaments are going to go. */
.thread-track {
  fill: none;
  stroke: rgba(193, 146, 98, 0.20);
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Sparks drifting around the bundle, lit from the same palette as the
   filaments so they read as the same light rather than added glitter. */
.thread-sparkle {
  fill: var(--ember);
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  transition: opacity var(--dur-slow) var(--ease-out);
}
.thread-sparkle:nth-child(3n) { fill: var(--honey); }
.thread-sparkle:nth-child(5n) { fill: var(--amber); }

/* Only lit once the filaments have reached them. Peak brightness varies per
   particle (set from JS) so the field twinkles unevenly rather than in unison. */
.thread-sparkle-lit {
  opacity: var(--spark-peak, 0.8);
  animation-name: sparkle;
  animation-iteration-count: infinite;
  animation-timing-function: ease-in-out;
}

@keyframes sparkle {
  0%, 100% { opacity: 0.10; }
  50%      { opacity: var(--spark-peak, 0.9); }
}

/* The node the spiral closes on. */
.thread-node {
  fill: var(--ember);
  opacity: 0.9;
}

@media (prefers-reduced-motion: reduce) {
  .thread-sparkle-lit { animation: none; opacity: 0.6; }
}
/* Travelling bloom that rides the drawing tip. */
.thread-head {
  position: absolute;
  width: 15rem;
  height: 15rem;
  margin: -7.5rem 0 0 -7.5rem;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  /* Moved every frame — tell the compositor so it never triggers layout. */
  will-change: transform;
  background: radial-gradient(
    circle,
    rgba(255, 242, 201, 0.42) 0%,
    rgba(240, 200, 128, 0.22) 26%,
    rgba(217, 164, 95, 0.10) 48%,
    rgba(193, 146, 98, 0) 70%
  );
  transition: opacity var(--dur-slow) var(--ease-out);
}
.thread-head-visible { opacity: 1; }

/* ---------------------------------------------------------------- 8. HERO */

.hero {
  position: relative;
  padding-top: clamp(2rem, 1rem + 5vw, 5rem);
  padding-bottom: var(--section);
}

.hero-wordmark {
  font-family: var(--font-display);
  /* Sized to the copy column rather than the viewport, so the masthead can
     never outgrow the space beside the video. */
  font-size: clamp(3.25rem, 7.2vw, 7rem);
  font-weight: 300;
  line-height: 0.92;
  letter-spacing: -0.035em;
  color: var(--text-accent);
  /* Low optical size + maximum wonk: the wordmark is the one place the
     typeface is allowed to be strange. Everything else stays composed. */
  font-variation-settings: 'SOFT' 60, 'WONK' 1, 'opsz' 144;
  margin-block: var(--space-sm) var(--space-xs);
}

.hero-grid {
  display: grid;
  grid-template-columns: 1.02fr 0.98fr;
  gap: clamp(var(--space-xl), 4vw, var(--space-3xl));
  /* Stretch, not centre: the video column takes the copy column's exact
     height, which is what puts its edges on the eyebrow and the CTA. */
  align-items: stretch;
}

.hero-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  /* No gap: the vertical rhythm comes from each element's own margins, so the
     first and last items sit flush against the column's top and bottom edges
     and the video lines up with them. */
  gap: 0;
  min-width: 0;
}
.hero-copy .hero-support { margin-block: var(--space-md) var(--space-xl); }

.hero-slogan {
  font-size: var(--text-2xl);
  font-weight: 400;
  line-height: 1.14;
  color: var(--text-accent);
  font-variation-settings: 'SOFT' 40, 'WONK' 1, 'opsz' 60;
  max-width: 15ch;
}

.hero-support {
  font-size: var(--text-base);
  line-height: var(--leading-relaxed);
  color: var(--text-secondary);
  max-width: 34ch;
}

/* Above the thread, so the filament passes behind the primary action instead
   of drawing a line across it. Matters most on mobile, where the stacked
   layout puts the two in the same place. */
.hero-cta {
  position: relative;
  z-index: var(--z-elevated);
  margin-top: auto;
}

/* The portal. A rounded rectangle rather than an arch: the glass sphere in the
   footage reaches the top edge of the frame and an arch would crop its crown.
   The arch motif lives on the card imagery instead, where we control the crop. */
.hero-portal {
  position: relative;
  z-index: var(--z-elevated);
  /* On desktop the height comes from the stretched grid row, not from a ratio.
     object-fit: cover keeps the composition centred at whatever height the
     copy column happens to be. The ratio comes back once the columns stack. */
  height: 100%;
  min-height: 22rem;
  border-radius: var(--radius-xl);
  overflow: hidden;
  background-color: var(--travertine);
  box-shadow: var(--shadow-floating);
}
.hero-portal video,
.hero-portal img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Hairline that reads as the rim of the glass sphere. */
.hero-portal::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(255, 242, 201, 0.34),
              inset 0 0 60px -20px rgba(217, 164, 95, 0.5);
  pointer-events: none;
}

.hero-portal-frame {
  position: relative;
  min-width: 0;
}
/* Where the filament leaves the video. */
.hero-portal-frame::after {
  content: '';
  position: absolute;
  right: 12%;
  bottom: -0.35rem;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--ember);
  box-shadow: 0 0 6px var(--honey), 0 0 16px rgba(217, 164, 95, 0.8);
  z-index: var(--z-elevated);
}

/* --------------------------------------------------------------- 9. CARDS */

.cards { position: relative; }

.card {
  position: relative;
  z-index: var(--z-elevated);
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: clamp(var(--space-xl), 4vw, var(--space-3xl));
  padding: clamp(var(--space-lg), 2.4vw, var(--space-2xl));
  background-color: var(--surface-elevated);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-elevated);
  transition: box-shadow var(--dur-slow) var(--ease-out),
              transform var(--dur-slow) var(--ease-out);
}
.card:hover {
  box-shadow: var(--shadow-elevated-hover);
  transform: translateY(-3px);
}

/* Z-pattern: the second card mirrors the first, so the eye crosses the page
   and the filament has a reason to weave from one side to the other. */
.card + .card { margin-top: clamp(var(--space-2xl), 6vw, var(--space-3xl)); }
.card-mirrored .card-media { order: 2; }

.card-media {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-arch);
  overflow: hidden;
  background-color: var(--bone-deep);
}
.card-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out);
}
.card:hover .card-media img { transform: scale(1.025); }

.card-body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-lg);
  padding-block: var(--space-md);
}

.card-title {
  font-size: var(--text-2xl);
  font-weight: 500;
  line-height: 1.1;
  max-width: 16ch;
}

.card-text {
  font-size: var(--text-lg);
  line-height: var(--leading-relaxed);
  color: var(--text-secondary);
  max-width: 38ch;
}

.card-meta {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-secondary);
  padding-top: var(--space-md);
  border-top: 1px solid var(--border-subtle);
  width: 100%;
}

/* ------------------------------------------------------------ 10. CLOSING */

.closing {
  position: relative;
  text-align: center;
}

.closing-inner {
  position: relative;
  z-index: var(--z-elevated);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-lg);
}

.closing-title {
  font-size: var(--text-3xl);
  font-weight: 400;
  line-height: 1.08;
  max-width: 18ch;
  font-variation-settings: 'SOFT' 44, 'WONK' 1, 'opsz' 96;
}

.closing-text {
  font-size: var(--text-lg);
  line-height: var(--leading-relaxed);
  color: var(--text-secondary);
  max-width: 46ch;
}

.closing-cta { margin-top: var(--space-md); }

/* Where the filaments finish their run and coil shut.

   This element draws nothing — it exists to reserve space. The coil is sized
   from it (see motion.js), so making it as wide as the coil is what keeps the
   spiral from spilling over the heading beneath. Previously it was 4.5rem while
   the coil had a radius of over 100px, so the turns ran straight through the
   text below it. */
.seal {
  position: relative;
  width: min(15rem, 62vw);
  aspect-ratio: 1;
  margin-inline: auto;
  margin-bottom: var(--space-md);
}

/* --------------------------------------------------------- 11. NEWSLETTER */

.newsletter {
  position: relative;
  z-index: var(--z-elevated);
  max-width: 44rem;
  margin: clamp(var(--space-2xl), 6vw, var(--space-3xl)) auto 0;
  padding: clamp(var(--space-xl), 3vw, var(--space-2xl));
  background-color: var(--surface-elevated);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-elevated);
}

/* The form is narrower than the panel and centred. A field running the full
   width of the card read as a search bar rather than an invitation.

   text-align is reset explicitly: this panel sits inside .closing, which is
   centred, and that inherits straight through to the field label — leaving
   "EMAIL ADDRESS" floating in the middle above a left-aligned input. The
   heading above stays centred on purpose; only the form is realigned. */
.newsletter-form {
  max-width: 30rem;
  margin-inline: auto;
  text-align: left;
}

/* Indented to the input's own text inset rather than to its outer edge, so the
   label starts where the placeholder starts — past the pill's curve — instead
   of sitting out over the rounded corner. Same value as the input's padding, so
   the two stay locked together if that ever changes. */
.newsletter-form .field-label { padding-left: var(--space-lg); }

.newsletter-head {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-bottom: var(--space-lg);
  text-align: center;
  align-items: center;
}

.newsletter-title {
  font-size: var(--text-xl);
  font-weight: 500;
}

.newsletter-note {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  max-width: 52ch;
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.field-label {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-secondary);
}

.field-row {
  display: flex;
  gap: var(--space-sm);
  align-items: stretch;
}

.input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 3.25rem;
  padding: var(--space-sm) var(--space-lg);
  background-color: var(--surface-floating);
  color: var(--text-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-pill);
  font-size: var(--text-base);   /* 16px minimum stops iOS zooming on focus */
  transition: border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.input::placeholder { color: var(--sage-ink); opacity: 0.66; }
.input:hover { border-color: var(--border-strong); }
.input:focus {
  outline: none;
  border-color: var(--olive);
  box-shadow: 0 0 0 3px rgba(53, 64, 36, 0.14);
}
.input-invalid { border-color: var(--clay); }
.input-invalid:focus { box-shadow: 0 0 0 3px rgba(142, 78, 50, 0.18); }

/* Honeypot. Off-screen rather than display:none so scripted browsers still
   render it and fill it in, but no person ever encounters it. */
.honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-status {
  display: flex;
  align-items: flex-start;
  gap: var(--space-xs);
  margin-top: var(--space-sm);
  min-height: 1.5rem;
  font-size: var(--text-sm);
  line-height: 1.5;
}
.form-status-error { color: var(--text-error); font-weight: 500; }
.form-status-success { color: var(--text-accent); font-weight: 500; }
.form-status-idle { color: var(--text-secondary); }

.btn-submit { flex: none; }
.btn[aria-busy='true'] { opacity: 0.7; pointer-events: none; }

.spinner {
  width: 1rem;
  height: 1rem;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 700ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 2s; }
}

/* ------------------------------------------------------------- 12. FOOTER */

.footer {
  position: relative;
  z-index: var(--z-elevated);
  background-color: var(--surface-inverse);
  color: var(--text-on-inverse);
  padding-block: var(--space-3xl) var(--space-xl);
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  margin-top: var(--section);
}

.footer-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: var(--space-2xl);
  padding-bottom: var(--space-2xl);
  border-bottom: 1px solid var(--border-inverse);
}

.footer-brand {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 400;
  color: var(--ember);
  font-variation-settings: 'SOFT' 50, 'WONK' 1, 'opsz' 72;
  margin-bottom: var(--space-sm);
}

.footer-tagline {
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
  color: rgba(229, 215, 196, 0.72);
  max-width: 32ch;
}

.footer-heading {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: rgba(229, 215, 196, 0.56);
  margin-bottom: var(--space-md);
}

.footer-list { display: flex; flex-direction: column; }

/* Full-width flex rather than inline-flex: a link reading "Plans" is only
   ~35px of text, which is well under the minimum tap target. Filling the
   column makes the whole row tappable instead of just the word. */
.footer-link {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: var(--tap-target);
  font-size: var(--text-sm);
  color: rgba(229, 215, 196, 0.86);
  transition: color var(--dur-fast) var(--ease-out);
}
.footer-link:hover { color: var(--ember); }

.footer-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding-top: var(--space-lg);
  font-size: var(--text-xs);
  color: rgba(229, 215, 196, 0.56);
  flex-wrap: wrap;
}

/* -------------------------------------------------------- 13. SCROLL REVEAL
   Content is visible by default. The `js` class is only present once the
   script has run, so a visitor without JavaScript sees a complete page. */

.js .reveal {
  opacity: 0;
  transform: translateY(22px);
}
.js .reveal-in {
  opacity: 1;
  transform: none;
  transition: opacity 620ms var(--ease-out), transform 620ms var(--ease-out);
}
@media (prefers-reduced-motion: reduce) {
  .js .reveal { opacity: 1; transform: none; }
}

/* --------------------------------------------------- 14. SECONDARY PAGES */

.simple-page {
  display: grid;
  place-items: center;
  min-height: 68vh;
  text-align: center;
}
.simple-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-lg);
  max-width: 44ch;
}
.simple-title {
  font-size: var(--text-3xl);
  font-weight: 400;
  font-variation-settings: 'SOFT' 44, 'WONK' 1, 'opsz' 96;
}
.simple-text {
  font-size: var(--text-lg);
  line-height: var(--leading-relaxed);
  color: var(--text-secondary);
}
.simple-code {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-widest);
  color: var(--text-secondary);
}

/* ------------------------------------------------------- 15. RESPONSIVE */

@media (max-width: 60rem) {
  /* Stacked, the hero becomes one column and the video has to land directly
     above the CTA. The copy is a wrapper, not a visual group, so it is dissolved
     with display: contents — its children become items of this flex column and
     can be ordered individually. Without that the video could only go above the
     whole copy block or below it, never between two of its parts. */
  .hero-grid {
    display: flex;
    flex-direction: column;
    gap: var(--space-lg);
  }
  .hero-copy { display: contents; }

  .hero-copy .eyebrow   { order: 1; }
  .hero-wordmark        { order: 2; }
  .hero-slogan          { order: 3; }
  .hero-support         { order: 4; }
  .hero-portal-frame    { order: 5; }
  .hero-cta             { order: 6; }

  /* Stacked, the video is no longer matching a neighbouring column, so it goes
     back to holding its own shape. */
  .hero-portal {
    height: auto;
    aspect-ratio: 4 / 3;
    min-height: 0;
  }
  .hero-copy .hero-support { margin-block: 0; }
  .hero-cta { margin-top: var(--space-xs); }

  .card,
  .card-mirrored {
    grid-template-columns: 1fr;
    gap: var(--space-xl);
  }
  /* Image first on every card once stacked — alternating order only makes
     sense while the eye is travelling horizontally. */
  .card-media,
  .card-mirrored .card-media { order: 1; }
  .card-body { order: 2; }
  .card-media { aspect-ratio: 3 / 2; }

  .footer-grid { grid-template-columns: 1fr 1fr; gap: var(--space-xl); }
}

@media (max-width: 48rem) {
  .nav-links { display: none; }
  .nav-toggle { display: grid; }

  /* Mobile menu sheet */
  .nav-links-open {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    flex-direction: column;
    align-items: stretch;
    justify-content: center;
    gap: var(--space-xs);
    padding: var(--gutter);
    background-color: var(--surface-elevated);
  }
  .nav-links-open .nav-link {
    justify-content: center;
    min-height: 3.5rem;
    font-size: var(--text-lg);
    border-bottom: 1px solid var(--border-subtle);
    border-radius: 0;
  }
  .nav-links-open .nav-link:last-child { border-bottom: none; }

  .field-row { flex-direction: column; }
  .btn-submit { width: 100%; }

  .fab {
    left: var(--gutter);
    right: var(--gutter);
  }
  .fab .btn { width: 100%; }

  .footer-grid { grid-template-columns: 1fr; gap: var(--space-xl); }
  .footer-bottom { justify-content: flex-start; }
}

@media (max-width: 30rem) {
  .hero-portal { aspect-ratio: 5 / 4; }
  .card-media { aspect-ratio: 4 / 5; }

  /* Keeps the long CTA on one line at phone widths: the label shrinks a step
     and the generous padding gives way, rather than the text wrapping. */
  .btn-lg {
    font-size: var(--text-sm);
    padding-inline: var(--space-lg);
    gap: var(--space-xs);
  }
  .btn-lg .btn-icon-trail { display: none; }
}
