/* EXORDIUM — navy / bone / gold. Cormorant Garamond throughout. */

:root {
  --navy: #05050f;
  --bone: #eae6da;
  --bone-70: rgba(234, 230, 218, 0.72);
  /* Was 0.46, which lands around 3.8:1 on this navy — under the 4.5:1 WCAG AA
     floor for body text. 0.60 clears it while staying visibly secondary. */
  --bone-45: rgba(234, 230, 218, 0.60);
  --bone-18: rgba(234, 230, 218, 0.18);
  --bone-12: rgba(234, 230, 218, 0.12);
  --gold: #c9a84c;
  --ease-strong: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-inout: cubic-bezier(0.77, 0, 0.175, 1);
  --gutter: clamp(1.25rem, 5vw, 5rem);
  /* filled silhouette of the tree mark, used as the portal shape between acts */
  --tree-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 470'%3E%3Cg fill='%23fff'%3E%3Ccircle cx='200' cy='88' r='64'/%3E%3Ccircle cx='133' cy='116' r='50'/%3E%3Ccircle cx='267' cy='116' r='50'/%3E%3Ccircle cx='82' cy='152' r='40'/%3E%3Ccircle cx='318' cy='152' r='40'/%3E%3Crect x='184' y='120' width='32' height='300' rx='14'/%3E%3C/g%3E%3C/svg%3E");
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body { overflow-x: clip; background: var(--navy); color: var(--bone); }

/* ---------- fixed scroll-scrubbed background ---------- */
.bg-stage {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  /* approximates the clip's own backdrop so the letterbox area left by
     object-fit: contain blends instead of showing a seam */
  background: linear-gradient(to bottom, #0a1420 0%, #070d18 45%, #04060d 100%);
}
/* The canvas is the display; the video behind it only decodes. It keeps its
   layout box at zero opacity because a display:none video may stop decoding. */
.bg-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.bg-video.is-source { position: absolute; inset: 0; opacity: 0; pointer-events: none; }
.bg-video {
  width: 100%; height: 100%;
  /* contain, not cover: the canopy must never be cropped as it grows. The
     tree still reads large because contain fits the clip to the full viewport
     height, and the empty void around it matches the stage behind. */
  object-fit: contain;
}
.bg-scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(to bottom, rgba(5,5,15,0.62) 0%, rgba(5,5,15,0.44) 30%, rgba(5,5,15,0.48) 70%, rgba(5,5,15,0.74) 100%),
    radial-gradient(ellipse 74% 64% at 50% 45%, transparent 0%, rgba(5,5,15,0.38) 100%);
}
/* local scrim behind display type so it stays readable over bright footage.
   The service cards are deliberately not in this list — they carry their own
   blurred wash, and a second scrim on top would hide the tree they are
   supposed to be transparent to. */
.section-head, .hero-inner, .footer-headline { position: relative; }
.section-head::before, .hero-inner::before {
  content: "";
  position: absolute;
  inset: -14% -10%;
  background: radial-gradient(ellipse at center, rgba(5,5,15,0.80) 0%, rgba(5,5,15,0.42) 58%, transparent 100%);
  pointer-events: none;
}
.section-head > *, .hero-inner > * { position: relative; }
.service { position: relative; }
.hero-wordmark, .footer-headline, .section-title, .case-name, .service-name, .member-name {
  text-shadow: 0 2px 28px rgba(5, 5, 15, 0.9), 0 0 5px rgba(5, 5, 15, 0.6);
}
.hero-sub, .section-note, .service-body, .service-sub, .service-list, .case-label,
.member-role, .footer-phone, .footer-fine, .eyebrow {
  text-shadow: 0 1px 16px rgba(5, 5, 15, 0.9);
}
/* Layer order matters here. The background sits ABOVE the intro's scenes but
   BELOW all copy, so the second fly-through can reveal the tree through the
   mark while the office is still on screen. `main` deliberately has no
   z-index — a stacking context there would trap the intro scenes and make
   that impossible. */
/* The tree sits BEHIND the intro scene, so the office riding up off the top
   actually uncovers it. With the order reversed the tree just painted over
   the office and the pan revealed nothing. */
main { position: relative; }
.bg-stage { z-index: 0; }
.intro-stage { z-index: 1; }
.intro-acts { z-index: 2; }
.work, .services, .studio, .footer { position: relative; z-index: 2; }

body {
  font-family: "Cormorant Garamond", Garamond, Georgia, serif;
  font-weight: 400;
  font-size: 1.2rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--bone); color: var(--navy); }

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

a { color: inherit; text-decoration: none; }
a:focus-visible, [tabindex]:focus-visible { outline: 1px solid var(--bone); outline-offset: 5px; }

body::after {
  content: "";
  position: fixed; inset: 0;
  pointer-events: none; z-index: 200; opacity: 0.045;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.6'/%3E%3C/svg%3E");
}

/* ---------- shared type ---------- */
.eyebrow {
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--bone-45);
}
.section-head { max-width: 44rem; margin-bottom: clamp(2.5rem, 7vh, 5rem); }
.section-title {
  font-weight: 300;
  font-size: clamp(2.1rem, 4.6vw, 3.8rem);
  line-height: 1.1;
  letter-spacing: 0.02em;
}
.section-note { margin-top: 1rem; color: var(--bone-45); max-width: 38em; }

/* pill CTA */
.pill {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 52px;
  padding: 0 2.2rem;
  border-radius: 999px;
  background: var(--bone);
  color: var(--navy);
  font-size: 1.05rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  box-shadow: 0 0 0 rgba(234, 230, 218, 0), 0 14px 50px -12px rgba(234, 230, 218, 0.35);
  transition: transform 200ms var(--ease-strong), box-shadow 300ms var(--ease-strong);
}
.pill:hover { transform: translateY(-2px); box-shadow: 0 18px 60px -10px rgba(234, 230, 218, 0.5); }
.pill:active { transform: scale(0.975); }

/* ---------- gold: tree mark ----------
   The supplied brand artwork, keyed to transparency, rather than a traced SVG.
   It is a raster now, so height must be pinned to auto against the intrinsic
   width/height attributes or it distorts as the width clamps. */
.tree { display: block; height: auto; }

/* ---------- gold: cursor ----------
   Above everything. The native cursor is hidden while this is on, so anything
   that paints over it leaves the pointer invisible — which is what happened
   against the chat widget at z-index 420. It never intercepts clicks
   (pointer-events: none), so sitting on top costs nothing. */
.cursor-dot, .cursor-ring {
  display: none; position: fixed; top: 0; left: 0;
  pointer-events: none; z-index: 900; border-radius: 50%; will-change: transform;
}
.cursor-dot { width: 6px; height: 6px; background: var(--gold); }
.cursor-ring { width: 32px; height: 32px; border: 1px solid var(--gold); opacity: 0.55; }
body.has-cursor .cursor-dot, body.has-cursor .cursor-ring { display: block; }
body.has-cursor, body.has-cursor a { cursor: none; }
/* Anything that sets cursor:pointer would otherwise show the native arrow next
   to the gold one. Text fields are left alone on purpose — hiding the I-beam
   where someone is about to type is worse than a second cursor. */
body.has-cursor button,
body.has-cursor .pill,
body.has-cursor .chat-chip,
body.has-cursor summary { cursor: none; }

/* ---------- accessibility ---------- */
/* First thing a keyboard reaches, so the intro can be skipped entirely. */
.skip-link {
  position: fixed; top: 0; left: 0; z-index: 400;
  padding: 0.9rem 1.2rem;
  background: var(--bone); color: var(--navy);
  font-size: 1rem;
  transform: translateY(-120%);
  transition: transform 160ms var(--ease-strong);
}
.skip-link:focus { transform: none; }
/* One visible focus ring everywhere, since the custom cursor hides the usual
   affordances and several controls sit on video. */
:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
  border-radius: 2px;
}

/* ---------- nav ---------- */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 100;
  display: flex; align-items: center; justify-content: space-between;
  gap: 2rem;
  height: 78px;
  padding: 0 var(--gutter);
  background: linear-gradient(to bottom, rgba(5, 5, 15, 0.8), transparent);
}
.nav-brand { display: flex; align-items: center; gap: 0.75rem; min-height: 44px; }
/* 28 rather than 24: the mark is five rings and a trunk, and below about this
   size the rings start merging into a blob. */
.tree-nav { width: 28px; height: auto; }
.nav-word {
  font-size: 1rem; font-weight: 500;
  letter-spacing: 0.34em; text-transform: uppercase;
}
.nav-links { display: flex; gap: clamp(1.5rem, 3vw, 2.8rem); }
.nav-links a {
  font-size: 1.02rem;
  color: var(--bone-70);
  min-height: 44px; display: inline-flex; align-items: center;
  transition: color 200ms var(--ease-strong);
}
.nav-links a:hover { color: var(--bone); }
.nav-toggle { display: none; }
.nav-cta {
  font-size: 1.02rem;
  color: var(--bone);
  white-space: nowrap;
  min-height: 44px; display: inline-flex; align-items: center;
  border-bottom: 1px solid var(--bone-12);
  transition: border-color 220ms var(--ease-strong);
}
.nav-cta:hover { border-color: var(--bone); }

/* ---------- hero ---------- */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex; align-items: center; justify-content: center;
  padding: clamp(5.5rem, 11vh, 8rem) var(--gutter) clamp(3.5rem, 9vh, 6rem);
  overflow: clip;
}
.hero-inner {
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
}

/* opening entrance — CSS so it survives a busy main thread and needs no JS */
.js .act-open .act-inner > * { animation: rise 1s var(--ease-strong) both; }
.js .act-open .act-inner > *:nth-child(1) { animation-delay: 0.15s; }
.js .act-open .act-inner > *:nth-child(2) { animation-delay: 0.30s; }
.js .act-open .act-inner > *:nth-child(3) { animation-delay: 0.45s; }
@keyframes rise {
  from { opacity: 0; transform: translateY(24px); }
  to { opacity: 1; transform: none; }
}
/* ---------- intro sequence ----------
   Three acts over a sticky stage. Each transition scales a layer that is
   masked to the tree mark, so the mark opens up and the camera appears to
   fly through it into the next scene. The inner scaler counter-scales so the
   destination image lands at natural size rather than hugely zoomed. */
.intro { position: relative; }
.intro-stage {
  position: sticky; top: 0;
  height: 100svh;
  overflow: clip;
  z-index: 0;
}
/* One continuous camera move, not a wipe: each scene scales toward the thing
   the next scene opens on (the laptop, then the tree beyond the glass), and
   they crossfade at matched framing so the push reads as unbroken. */
.scene {
  position: absolute; inset: 0;
  opacity: 0;
  will-change: transform, opacity;
}
.scene-img {
  width: 100%; height: 100%;
  object-fit: cover;
  filter: saturate(0.85) brightness(0.8) contrast(1.04);
}
/* Idle drift, independent of scroll, so a frame is never perfectly frozen —
   this is what stops a still reading as a slide. The scroll push lives on the
   parent, so these transforms don't fight each other. */
.js .scene-img { animation: sceneDrift 19s ease-in-out infinite alternate; }
/* the laptop screen is a live display, so the garage breathes a little light */
.js .scene-garage .scene-img {
  animation: sceneDrift 21s ease-in-out infinite alternate,
             screenFlicker 7s ease-in-out infinite;
}
@keyframes sceneDrift {
  from { transform: scale(1.03) translate3d(0.5%, 0.4%, 0); }
  to   { transform: scale(1.07) translate3d(-0.6%, -0.5%, 0); }
}
@keyframes screenFlicker {
  0%, 100% { filter: saturate(0.85) brightness(0.80) contrast(1.04); }
  43%      { filter: saturate(0.85) brightness(0.75) contrast(1.04); }
  62%      { filter: saturate(0.85) brightness(0.84) contrast(1.04); }
}
.scene-push { opacity: 1; background: #06090f; }
.scene-video { width: 100%; height: 100%; object-fit: cover; }
/* The canvas is what you see; the video behind it is only a decoder. It stays
   in the layout at zero opacity rather than display:none, because a hidden
   video is allowed to stop decoding. */
.scene-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.scene-video.is-source { position: absolute; inset: 0; opacity: 0; pointer-events: none; }
.scene-office { transform-origin: 50% 50%; background: #05070f; }
/* sits above the scenes, below the copy */
.intro-scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(to bottom, rgba(5,5,15,0.62) 0%, rgba(5,5,15,0.42) 35%, rgba(5,5,15,0.46) 70%, rgba(5,5,15,0.66) 100%),
    radial-gradient(ellipse 72% 62% at 50% 50%, rgba(5,5,15,0.18) 0%, rgba(5,5,15,0.58) 100%);
}

.intro-acts { position: relative; z-index: 1; margin-top: -100svh; }
/* Deliberately shorter than a full screen each, so the sequence moves without
   demanding much scrolling. Only the opening and closing acts get a full
   viewport, since those are the two moments meant to be held. */
.act {
  min-height: 36svh;
  display: flex; align-items: center; justify-content: center;
  padding: 0 var(--gutter);
  position: relative;
}
.act-open { min-height: 84svh; }
.act-mid { min-height: 42svh; }
.act-grow { min-height: 74svh; }
.act-inner { position: relative; text-align: center; display: flex; flex-direction: column; align-items: center; }
.act-open { flex-direction: column; }
.tree-open {
  width: clamp(70px, min(8vw, 12vh), 130px);
  height: auto;
  margin-bottom: clamp(1rem, 3vh, 2rem);
  filter: drop-shadow(0 0 34px rgba(201, 168, 76, 0.3));
}
.act-title {
  font-weight: 300;
  font-size: clamp(2rem, min(6vw, 9vh), 5rem);
  line-height: 1.08;
  max-width: 16em;
}
.act-body {
  margin-top: 1.4rem;
  max-width: 34em;
  color: var(--bone-70);
  font-size: clamp(1.05rem, 1.5vw, 1.35rem);
}
.note { max-width: 40rem; }
.note-lead {
  font-size: clamp(1.3rem, min(3vw, 4.5vh), 2.4rem);
  font-style: italic;
  line-height: 1.35;
}
.note-tail {
  margin-top: 1.1rem;
  color: var(--bone-45);
  font-size: clamp(1rem, 1.4vw, 1.2rem);
}
.note-list {
  list-style: none;
  margin-top: 2rem;
  display: grid; gap: 0.9rem;
  color: var(--bone-45);
  font-size: clamp(1rem, 1.4vw, 1.2rem);
  text-align: left;
}
.note-list li { padding-left: 1.4rem; position: relative; }
.note-list li::before {
  content: ""; position: absolute; left: 0; top: 0.62em;
  width: 0.6rem; height: 1px; background: var(--bone-45);
}
.grow-word {
  font-weight: 300;
  font-size: clamp(3.4rem, min(15vw, 20vh), 13rem);
  line-height: 0.95;
  letter-spacing: 0.06em;
}
.act-grow .pill { margin-top: clamp(1.6rem, 4vh, 3rem); }
.act-grow .hero-sub { margin-top: clamp(0.8rem, 2vh, 1.6rem); }

/* editorial frame: hairlines top and bottom of the hero, corner meta, and a
   ruled scroll cue, so the negative space reads as composed rather than blank */
.hero-rules {
  position: absolute;
  left: var(--gutter); right: var(--gutter);
  top: 88px; bottom: 2.4rem;
  z-index: 1;
  border-top: 1px solid var(--bone-12);
  border-bottom: 1px solid var(--bone-12);
  pointer-events: none;
}
.hero-meta {
  position: absolute;
  left: var(--gutter); right: var(--gutter);
  top: calc(88px + 1.1rem);
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 0.76rem;
  font-weight: 500;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--bone-45);
}
.hero-meta-mid { display: none; }
.hero-cue {
  position: absolute;
  left: var(--gutter); right: var(--gutter);
  bottom: 2.4rem;
  transform: translateY(50%);
  display: flex; align-items: center; gap: 1.4rem;
}
.cue-rule { flex: 1; height: 1px; background: var(--bone-12); }
.cue-label {
  font-size: 0.72rem;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--bone-45);
  text-shadow: 0 1px 14px rgba(5, 5, 15, 0.9);
}

.hero-wordmark {
  font-weight: 300;
  font-size: clamp(2.6rem, min(10vw, 14vh), 9.5rem);
  line-height: 1;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-indent: 0.2em; /* balance the trailing letter-space */
}
.hero-sub {
  margin-top: clamp(1rem, 3vh, 2.4rem);
  font-size: clamp(1.05rem, min(1.9vw, 3vh), 1.6rem);
  font-style: italic;
  color: var(--bone-70);
  max-width: 26em;
}
.hero .pill { margin-top: clamp(1.6rem, 4.5vh, 3.4rem); }

/* ---------- selected work ---------- */
.work { padding: clamp(6rem, 16vh, 12rem) 0; }
.work .section-head { padding: 0 var(--gutter); }
/* Bleeds past the gutters so cards run off both edges — a rail that stops
   short of the screen looks like a grid that failed to fill. */
.work-rail {
  margin-top: clamp(2.5rem, 6vh, 4rem);
  padding: 0 var(--gutter);
  /* A real scroller rather than a clipped box with an animation on top. The
     loop is driven by scrollLeft, which means it never runs out of cards, and
     it can be swiped at any moment instead of being a thing you watch. */
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--gutter), #000 calc(100% - var(--gutter)), transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 var(--gutter), #000 calc(100% - var(--gutter)), transparent 100%);
}
.work-rail::-webkit-scrollbar { display: none; }
.work-track { display: flex; width: max-content; }
/* Spacing is margin, not gap, on purpose: the loop translates exactly -50%,
   which only lands cleanly when every item owns its own trailing space. With
   `gap` the seam drifts by half a gap and the loop visibly jumps. */
.case {
  display: block;
  flex: 0 0 var(--case-w, clamp(280px, 24vw, 400px));
  margin-right: var(--case-gap, clamp(0.9rem, 3vw, 1.6rem));
}
/* The rail reveals as one object. Per-card reveals cannot work here: the track
   is already moving, so a transform on an individual card fights the loop. */
.js .work-rail { opacity: 0; }
.js .work-rail.is-in {
  opacity: 1;
  transition: opacity 900ms var(--ease-strong);
}
.case-frame {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: clip;
  border: 1px solid var(--bone-12);
  border-radius: 4px;
  background: #0b0b18;
}
.case-img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: top center;
  opacity: 0.82;
  transform: scale(1.005);
  transition: transform 700ms var(--ease-inout), opacity 500ms var(--ease-strong);
}
.case-meta { margin-top: 1.1rem; display: flex; flex-direction: column; gap: 0.15rem; }
.case-name {
  font-weight: 400;
  font-size: clamp(1.4rem, 2.2vw, 1.9rem);
  letter-spacing: 0.01em;
}
.case-label { font-size: 1.05rem; font-style: italic; color: var(--bone-45); }

@media (hover: hover) and (pointer: fine) {
  .case:hover .case-img { transform: scale(1.045); opacity: 1; }
}
.case:focus-visible .case-img { transform: scale(1.045); opacity: 1; }

/* ---------- services ----------
   Two cards dealt in from opposite edges, offset so they read as a pair rather
   than a column: Genesis high and left, Websites low and right. */
.services { padding: clamp(5rem, 14vh, 10rem) var(--gutter); }
.services-deck {
  max-width: 1500px; margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}
.service {
  padding: clamp(1.6rem, 3vw, 2.6rem);
  border: 1px solid var(--bone-18);
  border-radius: 10px;
  /* Barely there, so the canopy reads through it as canopy. The blur that used
     to carry legibility is gone: backdrop-filter over a scrubbing video forces
     the compositor to re-blur a moving source every frame, which was a real
     part of the scroll stutter. Legibility now comes from a light wash plus the
     text shadow below, which cost nothing to composite. */
  background: rgba(7, 8, 20, 0.30);
  box-shadow: 0 30px 70px rgba(0, 0, 0, 0.30);
  /* the type does the work the panel used to */
  text-shadow: 0 1px 12px rgba(5, 5, 15, 0.95), 0 0 30px rgba(5, 5, 15, 0.8);
}
.service-a { margin-top: 0; }
.service-b { margin-top: clamp(3rem, 12vh, 9rem); }

/* Dealt in from off-screen, each from its own side, with a little rotation so
   they land like cards rather than sliding panels. */
.js .service { opacity: 0; will-change: transform, opacity; }
.js .service-a { transform: translate3d(-60%, -14%, 0) rotate(-7deg); }
.js .service-b { transform: translate3d(60%, 14%, 0) rotate(7deg); }
.js .service.is-in {
  opacity: 1;
  transform: none;
  transition: opacity 700ms var(--ease-strong), transform 1000ms var(--ease-strong);
}
.js .service-b.is-in { transition-delay: 160ms; }
.service-name {
  font-weight: 300;
  font-size: clamp(2rem, 4.2vw, 3.4rem);
  line-height: 1.05;
  letter-spacing: 0.02em;
}
.service-sub {
  font-size: clamp(1.1rem, 1.7vw, 1.45rem);
  font-style: italic; color: var(--bone-45);
  margin: 0.5rem 0 1.5rem;
}
/* The smallest type sits over the brightest part of the canopy, so it gets a
   tight dark edge on top of the card's soft glow — a wide blur alone smears
   without separating letterforms from foliage. */
.service-body, .service-list, .service-sub {
  text-shadow: 0 1px 2px rgba(5, 5, 15, 0.98), 0 1px 14px rgba(5, 5, 15, 0.95);
}
/* ---------- pricing ----------
   Prices live on their own page. The service cards only carry a link, so the
   home page stays a story and nobody scrolls a table they did not ask for. */
.pricing-link {
  margin-top: 1.8rem;
  border-top: 1px solid var(--bone-18);
  padding-top: 1.2rem;
  display: flex; align-items: center; gap: 0.5rem;
  min-height: 44px;
  font-size: 1.05rem;
  color: var(--gold);
}
.pricing-link span { transition: transform 240ms var(--ease-strong); }
.pricing-link:hover span, .pricing-link:focus-visible span { transform: translateX(4px); }

.tiers { list-style: none; display: grid; gap: 1.1rem; }
.tier {
  padding: 1.1rem 1.2rem;
  border: 1px solid var(--bone-12);
  border-radius: 8px;
  background: rgba(5, 5, 15, 0.34);
}
.tier-name {
  font-size: 1.15rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--bone);
}
.tier-price {
  font-size: clamp(1.9rem, 4vw, 2.4rem);
  line-height: 1.1; margin-top: 0.15rem;
  color: var(--gold);
}
.tier-price span { font-size: 0.9rem; letter-spacing: 0.06em; color: var(--bone-45); }
.tier-list { list-style: none; margin: 0.9rem 0 1.1rem; display: grid; gap: 0.4rem;
  font-size: 0.98rem; color: var(--bone-70); }
.tier-list li { padding-left: 1rem; position: relative; }
.tier-list li::before {
  content: ""; position: absolute; left: 0; top: 0.62em;
  width: 0.45rem; height: 1px; background: var(--gold);
}
.tier-cta {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 46px; padding: 0 1.4rem;
  border: 1px solid var(--bone-18); border-radius: 999px;
  font-size: 1rem; color: var(--bone);
  transition: background 220ms var(--ease-strong), border-color 220ms var(--ease-strong);
}
.tier-cta:hover, .tier-cta:focus-visible { background: var(--bone); color: var(--navy); border-color: var(--bone); }
/* the free-if-you-do-it-yourself alternative, kept out of the headline price so
   the number on the card is the number you actually pay */
.tier-alt {
  margin: 0 0 1.1rem;
  font-size: 0.9rem; font-style: italic;
  color: var(--bone-45);
}
.tier-note { margin-top: 1.4rem; font-size: 0.95rem; color: var(--bone-45); max-width: 46em; }
.tier-note strong { color: var(--bone-70); font-weight: 400; }
/* Vertical padding on an inline element grows the hit area without changing the
   line box, so the link becomes an easier thumb target and the paragraph still
   sets exactly as before. */
.tier-note a, .pricing-help a, .studio-reach a {
  padding: 7px 0;
  color: var(--gold);
}

/* ---------- pricing page ----------
   No scrubbed video here. The home page already carries several megabytes of
   it; a page someone opens to check a number should be instant. */
.page-pricing { background: var(--navy); }
.pricing-page { padding: calc(78px + clamp(3rem, 9vh, 6rem)) var(--gutter) 0; }
.pricing-hero { max-width: 44em; }
.pricing-title {
  font-weight: 300;
  font-size: clamp(2.8rem, 9vw, 6rem);
  line-height: 1.02;
  letter-spacing: 0.02em;
}
.pricing-lead { margin-top: 1.1rem; font-size: clamp(1.15rem, 2vw, 1.5rem); color: var(--bone-70); }
.pricing-help { margin-top: 0.8rem; color: var(--bone-45); }
.pricing-help a { color: var(--gold); border-bottom: 1px solid var(--bone-18); }

/* The nav is fixed, so an anchor jump would park the heading underneath it. */
.price-block { margin-top: clamp(3.5rem, 10vh, 7rem); scroll-margin-top: 96px; }
.price-head { border-top: 1px solid var(--bone-12); padding-top: 1.4rem; margin-bottom: 2rem; }
.price-kicker {
  font-weight: 300;
  font-size: clamp(1.9rem, 4vw, 3rem);
  letter-spacing: 0.02em;
}
.price-sub { margin-top: 0.4rem; color: var(--bone-45); font-style: italic; }

/* Cards are equal-height columns on desktop so the buttons line up; the CTA is
   pushed to the bottom because tiers have different numbers of lines. */
@media (min-width: 760px) {
  .tiers { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.5rem; align-items: stretch; }
  .tier { display: flex; flex-direction: column; }
  .tier-cta { margin-top: auto; align-self: flex-start; }
}
.tier-featured { border-color: rgba(201, 168, 76, 0.42); }
.tier-flag {
  font-size: 0.72rem; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--gold); margin-bottom: 0.5rem;
}
.tier-cta-strong { background: var(--bone); color: var(--navy); border-color: var(--bone); }
.tier-cta-strong:hover, .tier-cta-strong:focus-visible { background: transparent; color: var(--bone); }

/* ---------- SEO landing pages ---------- */
.lp-title { max-width: 14em; }
.lp-points, .lp-plans, .lp-links { list-style: none; display: grid; gap: 1.1rem; }
.lp-point {
  padding: 1.2rem 1.3rem;
  border: 1px solid var(--bone-12);
  border-radius: 8px;
  background: rgba(5, 5, 15, 0.34);
}
.lp-point h3 { font-size: 1.2rem; font-weight: 400; color: var(--bone); }
.lp-point p { margin-top: 0.45rem; color: var(--bone-70); }
.lp-plan {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem 1rem;
  padding-bottom: 1.1rem;
  border-bottom: 1px solid var(--bone-12);
}
.lp-plan-name { font-size: 1.05rem; letter-spacing: 0.14em; text-transform: uppercase; }
.lp-plan-price { font-size: 1.7rem; color: var(--gold); line-height: 1; }
.lp-plan-price span { font-size: 0.85rem; letter-spacing: 0.06em; color: var(--bone-45); }
.lp-plan-note { flex: 1 1 16rem; color: var(--bone-70); font-size: 0.98rem; }
.lp-faqs { display: grid; gap: 1.6rem; max-width: 46em; }
.lp-faq h3 { font-size: 1.15rem; font-weight: 400; color: var(--bone); }
.lp-faq p { margin-top: 0.4rem; color: var(--bone-70); }
.lp-links a {
  display: inline-flex; align-items: center;
  min-height: 44px;
  color: var(--gold);
  border-bottom: 1px solid var(--bone-18);
}
.lp-links a:hover { color: var(--bone); }
@media (min-width: 760px) {
  .lp-points { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .lp-links { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.price-outro {
  margin: clamp(4rem, 12vh, 8rem) 0 clamp(4rem, 10vh, 7rem);
  padding-top: clamp(2rem, 5vh, 3rem);
  border-top: 1px solid var(--bone-12);
  display: flex; flex-direction: column; align-items: flex-start; gap: 1.4rem;
}
.price-outro p { color: var(--bone-70); max-width: 34em; font-size: 1.1rem; }
/* ---------- studio: reach ---------- */
.studio-reach {
  margin: 0 0 clamp(2rem, 5vh, 3rem);
  color: var(--bone-70);
  max-width: 40em;
}
.studio-reach a { color: var(--gold); border-bottom: 1px solid var(--bone-18); }
.member-tel {
  display: inline-flex; align-items: center;
  min-height: 44px;
  font-size: 0.98rem;
  color: var(--bone-45);
}
.member-tel:hover, .member-tel:focus-visible { color: var(--bone); }

.service-body { max-width: 34em; color: var(--bone-70); }
.service-list {
  list-style: none;
  margin-top: 1.6rem;
  display: grid; gap: 0.5rem;
  font-size: 1.1rem;
  color: var(--bone-45);
}
.service-list li::before { content: "·  "; color: var(--bone); }
.service-client {
  margin-top: 1.8rem;
  font-size: 1.02rem; font-style: italic;
  color: var(--bone-45);
  padding-top: 1.2rem;
  border-top: 1px solid var(--bone-12);
}

/* ---------- studio / team ---------- */
.studio { position: relative; padding: clamp(5rem, 14vh, 10rem) var(--gutter); overflow: clip; }
.studio-glow {
  position: absolute; top: 10%; left: 50%; transform: translateX(-50%);
  width: min(120vw, 1200px); aspect-ratio: 1;
  background: radial-gradient(circle at center, rgba(24, 32, 74, 0.5) 0%, transparent 62%);
  filter: blur(30px); pointer-events: none;
}
.studio-head { position: relative; }
.team {
  position: relative;
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: clamp(1.5rem, 4vw, 4rem);
  max-width: 1100px;
}
.member-frame {
  position: relative;
  aspect-ratio: 3 / 4;
  overflow: clip;
  border: 1px solid var(--bone-12);
  border-radius: 4px;
  background: #0b0b18;
}
.js .member-frame { clip-path: inset(0 0 100% 0); }
.js .member.is-in .member-frame { clip-path: inset(0 0 0 0); transition: clip-path 1100ms var(--ease-inout); }
.member-img {
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0.9;
  transform: scale(1.12);
  transition: transform 1400ms var(--ease-inout), opacity 600ms var(--ease-strong);
}
.member.is-in .member-img { transform: scale(1); }
@media (hover: hover) and (pointer: fine) {
  .member:hover .member-img { transform: scale(1.05); opacity: 1; }
}
/* shown only while the real photo is missing */
.member-fallback {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.85rem; letter-spacing: 0.3em; text-transform: uppercase;
  color: var(--bone-45);
  background: repeating-linear-gradient(135deg, #0b0b18 0 12px, #0d0d1c 12px 24px);
}
.member-meta { margin-top: 1.1rem; }
.member-name { font-weight: 400; font-size: clamp(1.35rem, 2.2vw, 1.9rem); }
.member-role { font-size: 1.02rem; font-style: italic; color: var(--bone-45); }

/* ---------- footer ---------- */
.footer {
  position: relative;
  min-height: 100svh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center;
  padding: clamp(5rem, 12vh, 8rem) var(--gutter) 3rem;
  overflow: clip;
}
.footer-glow {
  position: absolute; bottom: -30%; left: 50%; transform: translateX(-50%);
  width: min(140vw, 1400px); aspect-ratio: 1;
  background: radial-gradient(circle at center, rgba(201, 168, 76, 0.13) 0%, rgba(201, 168, 76, 0.04) 34%, transparent 64%);
  filter: blur(24px); pointer-events: none;
}
.footer-headline {
  position: relative;
  font-weight: 300;
  font-size: clamp(2.3rem, 6.4vw, 6rem);
  line-height: 1.12;
  letter-spacing: 0.01em;
  max-width: 16em;
}
.footer-headline em { font-style: italic; color: var(--gold); }
/* ---------- contact form ---------- */
.contact-form {
  position: relative;
  width: 100%; max-width: 40rem;
  margin-top: clamp(2.5rem, 6vh, 4rem);
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem 1.1rem;
  text-align: left;
}
.field { display: flex; flex-direction: column; gap: 0.4rem; }
.field-wide { grid-column: 1 / -1; }
.field label { font-size: 0.9rem; letter-spacing: 0.04em; color: var(--bone-70); }
.field .opt { color: var(--bone-45); letter-spacing: 0; }
.contact-form input,
.contact-form select,
.contact-form textarea {
  /* 16px is the floor that stops iOS zooming the page on focus */
  font: inherit; font-size: 16px;
  color: var(--bone);
  background: rgba(5, 5, 15, 0.55);
  border: 1px solid var(--bone-18);
  border-radius: 6px;
  padding: 0.7rem 0.85rem;
  min-height: 48px;
  width: 100%;
  transition: border-color 200ms var(--ease-strong), background 200ms var(--ease-strong);
}
.contact-form textarea { min-height: 112px; resize: vertical; }
.contact-form ::placeholder { color: var(--bone-45); opacity: 1; }
.contact-form select {
  /* the native arrow disappears against this background on some platforms */
  appearance: none; -webkit-appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--bone-45) 50%),
                    linear-gradient(135deg, var(--bone-45) 50%, transparent 50%);
  background-position: right 1.1rem center, right 0.75rem center;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: 2.4rem;
}
.contact-form option { background: #0b0b18; color: var(--bone); }
.contact-form input:focus, .contact-form select:focus, .contact-form textarea:focus {
  border-color: var(--gold); background: rgba(5, 5, 15, 0.8);
}
.form-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; margin-top: 0.4rem; }
.contact-form .pill { cursor: pointer; border: 0; }
.form-status { font-size: 0.95rem; color: var(--bone-45); }
.form-status.is-error { color: #f0a9a0; }
.form-status.is-ok { color: var(--gold); }
.contact-form.is-sent { display: none; }
.form-thanks { margin-top: clamp(2.5rem, 6vh, 4rem); font-size: 1.15rem; color: var(--bone-70); }

@media (max-width: 559px) {
  .contact-form { grid-template-columns: 1fr; }
}

.footer-contact {
  position: relative;
  margin-top: clamp(2.5rem, 6vh, 4rem);
  display: flex; flex-direction: column; align-items: center; gap: 1.2rem;
}
.footer-phone {
  font-size: 1.15rem; color: var(--bone-45);
  min-height: 44px; display: inline-flex; align-items: center;
  transition: color 200ms var(--ease-strong);
}
.footer-phone:hover { color: var(--bone); }
.footer-socials {
  position: relative;
  list-style: none;
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: clamp(1.5rem, 4vw, 3rem);
  margin-top: clamp(2rem, 5vh, 3rem);
}
.footer-socials a {
  font-size: 0.85rem; letter-spacing: 0.28em; text-transform: uppercase;
  color: var(--bone-45);
  min-height: 44px; display: inline-flex; align-items: center;
  transition: color 200ms var(--ease-strong);
}
.footer-socials a:hover { color: var(--bone); }
.footer-fine {
  position: relative;
  margin-top: auto; padding-top: clamp(3rem, 8vh, 5rem);
  font-size: 0.98rem; color: var(--bone-45);
}
/* Must come after .footer: the home page footer is a full screen because it is
   the closing beat, but on the pricing page it is just a footer, and a 100svh
   one leaves a dead gap under the phone number. Declared here rather than
   earlier in the file so it actually wins on source order. */
.footer-slim {
  min-height: auto;
  padding-top: clamp(4rem, 10vh, 7rem);
  padding-bottom: clamp(2.5rem, 6vh, 4rem);
}
.footer-slim .footer-fine { margin-top: clamp(2.5rem, 6vh, 4rem); padding-top: 0; }

/* ---------- chat widget ----------
   No backdrop-filter anywhere in here. It sits over the scrubbing tree video,
   and blurring a moving source every frame is exactly what was removed from the
   nav and the service cards to stop the scroll stuttering. */
.chat-launch {
  position: fixed; right: clamp(1rem, 4vw, 2rem); bottom: clamp(1rem, 4vw, 2rem);
  z-index: 420;
  width: 56px; height: 56px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  border: 1px solid rgba(201, 168, 76, 0.5);
  background: #0b0b18;
  color: var(--gold);
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.5);
  cursor: pointer;
  transition: transform 240ms var(--ease-strong), border-color 240ms var(--ease-strong);
}
.chat-launch svg { width: 24px; height: 24px; }
.chat-launch:hover { transform: translateY(-2px); border-color: var(--gold); }
.chat-launch.is-open svg { opacity: 0.5; }

.chat-panel {
  position: fixed; z-index: 430;
  right: clamp(1rem, 4vw, 2rem); bottom: calc(clamp(1rem, 4vw, 2rem) + 68px);
  width: min(380px, calc(100vw - 2rem));
  max-height: min(560px, calc(100svh - 8rem));
  display: flex; flex-direction: column;
  background: #080814;
  border: 1px solid var(--bone-18);
  border-radius: 12px;
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
  overflow: hidden;
}
/* A class with `display` beats the browser's own [hidden] { display: none },
   so the panel would sit open on every page load. This puts it back. */
.chat-panel[hidden] { display: none; }
.chat-panel.is-open { animation: chatIn 240ms var(--ease-strong) both; }
.chat-panel.no-motion { animation: none; }
@keyframes chatIn {
  from { opacity: 0; transform: translateY(10px) scale(0.98); }
  to { opacity: 1; transform: none; }
}
.chat-head {
  display: flex; align-items: center;
  gap: 0.8rem; padding: 0.9rem 0.9rem 0.9rem 1.1rem;
  border-bottom: 1px solid var(--bone-12);
  /* a thin warm wash so the header reads as part of the brand, not a title bar */
  background: linear-gradient(to bottom, rgba(201, 168, 76, 0.10), transparent);
}
.chat-brand {
  flex: 0 0 30px; width: 30px; height: 30px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid rgba(201, 168, 76, 0.35);
  border-radius: 50%;
}
.chat-brand img { width: 17px; height: auto; display: block; }
.chat-heading { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.1rem; }
.chat-title {
  font-size: 0.9rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--bone);
}
.chat-sub { font-size: 0.78rem; font-style: italic; color: var(--bone-45); }
/* the drag affordance on the mobile sheet; meaningless on desktop */
.chat-grip { display: none; }
.chat-close {
  width: 44px; height: 44px; display: flex; align-items: center; justify-content: center;
  background: none; border: 0; color: var(--bone-45); cursor: pointer;
}
.chat-close:hover { color: var(--bone); }
.chat-close svg { width: 18px; height: 18px; }

.chat-log {
  flex: 1; min-height: 0; overflow-y: auto;
  padding: 1rem; display: flex; flex-direction: column; gap: 0.7rem;
  overscroll-behavior: contain;
}
.chat-msg {
  max-width: 88%; padding: 0.65rem 0.9rem;
  border-radius: 12px; font-size: 1rem; line-height: 1.5;
  white-space: pre-wrap;
}
.chat-bot {
  align-self: flex-start;
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--bone-12);
  border-left: 2px solid rgba(201, 168, 76, 0.55);
  color: var(--bone-70);
  border-bottom-left-radius: 4px;
}
.chat-you {
  align-self: flex-end;
  background: var(--bone); color: var(--navy);
  border-bottom-right-radius: 4px;
}
.chat-msg.is-thinking { color: var(--gold); letter-spacing: 0.25em; }
.js .chat-msg { animation: chatMsg 260ms var(--ease-strong) both; }
@keyframes chatMsg {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

.chat-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; padding: 0 1rem 0.6rem; }
.chat-chips:empty { display: none; }
.chat-chip {
  font: inherit; font-size: 0.85rem;
  padding: 0.45rem 0.75rem; min-height: 36px;
  border: 1px solid var(--bone-18); border-radius: 999px;
  background: none; color: var(--bone-70); cursor: pointer;
  transition: border-color 200ms var(--ease-strong), color 200ms var(--ease-strong);
}
.chat-chip:hover { border-color: var(--gold); color: var(--bone); }

.chat-form { display: flex; gap: 0.5rem; padding: 0.75rem; border-top: 1px solid var(--bone-12); }
.chat-form input {
  flex: 1; min-width: 0;
  /* 16px keeps iOS from zooming the page when the field takes focus */
  font: inherit; font-size: 16px;
  padding: 0.6rem 0.8rem; min-height: 46px;
  color: var(--bone); background: rgba(255,255,255,0.05);
  border: 1px solid var(--bone-18); border-radius: 8px;
}
.chat-form input:focus { border-color: var(--gold); }
.chat-send {
  width: 46px; height: 46px; flex: 0 0 46px;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: 8px;
  background: var(--bone); color: var(--navy); cursor: pointer;
}
.chat-send svg { width: 20px; height: 20px; }

/* A sheet, not a takeover. Full screen made a question feel like leaving the
   site; this keeps the page visible above it so the chat reads as part of the
   page rather than a separate app. Sized in svh so the browser chrome
   appearing does not resize it mid-conversation. */
@media (max-width: 759px) {
  .chat-panel {
    left: 0; right: 0; bottom: 0; top: auto;
    width: 100%;
    height: min(78svh, 620px); max-height: none;
    border: 0;
    border-top: 1px solid rgba(201, 168, 76, 0.32);
    border-radius: 18px 18px 0 0;
    box-shadow: 0 -20px 60px rgba(0, 0, 0, 0.7);
  }
  .chat-panel.is-open { animation: chatSheet 280ms var(--ease-strong) both; }
  @keyframes chatSheet {
    from { transform: translateY(14%); opacity: 0; }
    to { transform: none; opacity: 1; }
  }
  .chat-grip {
    display: block;
    width: 38px; height: 4px; margin: 8px auto 0;
    border-radius: 999px;
    background: var(--bone-18);
  }
  .chat-head { padding-top: 0.7rem; }
  .chat-form { padding-bottom: max(0.75rem, env(safe-area-inset-bottom)); }
  /* the page behind must not scroll under the sheet */
  html.chat-open, html.chat-open body { overflow: hidden; }
  .chat-launch.is-open { display: none; }
}

/* ---------- mobile ---------- */
@media (max-width: 759px) {
  .nav-word { font-size: 0.9rem; letter-spacing: 0.28em; }

  /* Previously the links were simply display:none here, which left a phone
     with no way to reach any section at all. Now they collapse into a
     disclosure panel that reports its own state. */
  .nav-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px;
    margin-right: calc(var(--gutter) * -0.35);
    background: none; border: 0; color: var(--bone);
    cursor: pointer;
  }
  .nav-toggle-bars { display: block; width: 22px; }
  .nav-toggle-bars i {
    display: block; height: 1px; background: currentColor;
    transition: transform 240ms var(--ease-strong), opacity 200ms linear;
  }
  .nav-toggle-bars i:first-child { margin-bottom: 6px; }
  .nav-toggle[aria-expanded="true"] .nav-toggle-bars i:first-child { transform: translateY(3.5px) rotate(45deg); }
  .nav-toggle[aria-expanded="true"] .nav-toggle-bars i:last-child { transform: translateY(-3.5px) rotate(-45deg); }

  .nav-cta { display: none; }
  .nav-links {
    position: fixed; top: 64px; left: 0; right: 0;
    flex-direction: column; gap: 0;
    padding: 0.5rem var(--gutter) 1.25rem;
    /* solid, not translucent: at 0.97 the hero's bright pill ghosted through */
    background: var(--navy);
    border-bottom: 1px solid var(--bone-12);
    /* hidden from tab order and screen readers until opened, not just unseen */
    visibility: hidden; opacity: 0;
    transform: translateY(-10px);
    transition: opacity 200ms var(--ease-strong), transform 240ms var(--ease-strong), visibility 0s 240ms;
  }
  .nav.is-open .nav-links {
    visibility: visible; opacity: 1; transform: none;
    transition: opacity 200ms var(--ease-strong), transform 240ms var(--ease-strong), visibility 0s;
  }
  .nav-links a {
    min-height: 52px;
    font-size: 1.15rem;
    border-bottom: 1px solid var(--bone-12);
  }
  .nav-links a:last-child { border-bottom: 0; }

  /* Cards stack, and the offsets flip to a small horizontal stagger so the
     dealt-in pairing still reads without pushing anything off-screen. */
  .services-deck { grid-template-columns: 1fr; gap: 1.5rem; }
  .service-b { margin-top: 0; }
  .service-a { margin-right: 1.25rem; }
  .service-b { margin-left: 1.25rem; }
  .js .service-a { transform: translate3d(-42%, -8%, 0) rotate(-6deg); }
  .js .service-b { transform: translate3d(42%, 8%, 0) rotate(6deg); }
  /* The tree is roughly square and a phone is tall, so object-fit sizes it by
     width and strands it in the middle of the screen. Push the frame past the
     edges, anchored low so it crops empty sky rather than the roots. When JS is
     driving, this scale is animated against the growth (see paintZoom); this
     value is the still fallback. */
  .bg-video, .bg-canvas { transform: scale(1.34); transform-origin: 50% 62%; }

  /* A soft gradient is enough on desktop, where body text never reaches the
     wordmark. On a phone every line passes straight under it, and at this
     width the two collide into an unreadable smear. */
  /* No backdrop-filter here either: it is fixed over the scrubbing video, so
     the blur was being recomputed against moving pixels on every frame of every
     scroll. The gradient alone separates the wordmark well enough. */
  .nav {
    height: 64px;
    background: linear-gradient(to bottom, rgba(5,5,15,0.97) 0%, rgba(5,5,15,0.92) 58%, rgba(5,5,15,0) 100%);
  }
  .bg-scrim { background:
      linear-gradient(to bottom, rgba(5,5,15,0.72) 0%, rgba(5,5,15,0.34) 32%, rgba(5,5,15,0.38) 68%, rgba(5,5,15,0.80) 100%); }

  /* On desktop the act copy has room beside the tree; at this width it lands
     directly on the lit canopy, where bone-on-gold stops being readable — the
     dimmed bullets worst of all. A text-shadow alone is not enough against
     foliage that bright, so the copy carries a soft pool of shade with it.
     Feathered to nothing at the edges so it never reads as a panel. */
  .act { text-shadow: 0 1px 14px rgba(5,5,15,0.95); }
  .note { position: relative; }
  .act-inner::before, .note::before {
    content: "";
    position: absolute; inset: -14% -12%;
    background: radial-gradient(ellipse at center,
      rgba(5,5,15,0.82) 0%, rgba(5,5,15,0.66) 45%, rgba(5,5,15,0.22) 78%, rgba(5,5,15,0) 100%);
    z-index: -1;
    pointer-events: none;
  }
  .note-list, .note-tail { color: var(--bone-70); }

  /* Shorter than desktop, but not so short that three beats share a screen —
     with the copy cut back, the point of the gaps is that the film shows
     through between them. */
  .act { min-height: 40svh; }
  .act-open { min-height: 70svh; }
  .act-mid { min-height: 42svh; }
  .act-grow { min-height: 66svh; }
  .hero-wordmark { font-size: clamp(2.4rem, 13vw, 4.4rem); letter-spacing: 0.14em; }
  .hero-meta { font-size: 0.62rem; letter-spacing: 0.2em; justify-content: center; gap: 0.6rem; }
  .hero-meta-mid { display: inline; }
  .hero-rules { top: 80px; }
  /* roughly half the old full-width card, so two are in view at once and the
     row reads as a row rather than a stack */
  /* Four wide letter-spaced labels cannot fit one row at this width, and
     flex-wrap leaves them 3 + 1, which reads as a mistake. A 2x2 is a choice. */
  .footer-socials {
    display: grid; grid-template-columns: repeat(2, auto);
    justify-content: center; justify-items: center;
    gap: 0.25rem 2rem;
  }

  .work-rail { --case-w: clamp(168px, 46vw, 250px); }
  .case-name { font-size: 1.15rem; }
  .case-label { font-size: 0.86rem; }
  .case-meta { margin-top: 0.7rem; }
  .team { grid-template-columns: 1fr; gap: 2.5rem; }
  .footer-headline { font-size: 10.5vw; }
}

/* ---------- reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  /* cards must not stay parked off-screen if the deal-in never plays */
  .js .service { opacity: 1; transform: none; transition: none; }
  .case-img, .member-img, .member-frame, .pill { transition: none; }
  .js .scene-img, .js .scene-garage .scene-img { animation: none; }
  .member-frame { clip-path: inset(0 0 0 0); }
  .member-img { transform: none; }
  .hero-hint { display: none; }
}
