/* =========================================================
   NEXT — Garage Renovation
   Warm · minimal · nostalgic · polished · understated · bright
   ========================================================= */

/* ---------- fonts ---------- */

@font-face {
  font-family: "911Porscha";
  src: url("assets/fonts/911porschav3.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "AlteHaasGrotesk";
  src: url("assets/fonts/AlteHaasGroteskRegular.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "AlteHaasGrotesk";
  src: url("assets/fonts/AlteHaasGroteskBold.ttf") format("truetype");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ---------- tokens ---------- */

:root {
  --display: "911Porscha", "Arial Narrow", sans-serif;
  --body: "AlteHaasGrotesk", "Helvetica Neue", Arial, sans-serif;

  /* brand */
  --black: #000;
  --black-rgb: 0, 0, 0;
  --cream: #f2ecde;
  --cream-rgb: 242, 236, 222;
  --orange: #9c2b07;
  --orange-rgb: 156, 43, 7;

  --bg: var(--cream);
  --fg: var(--black);
  --muted: rgba(var(--black-rgb), 0.56);
  --rule: rgba(var(--black-rgb), 0.16);
  --raise: rgba(255, 255, 255, 0.5);

  /* the door: flat bands cut by open gaps — graphic, not rendered */
  --door-face: var(--orange);
  --door-gap: var(--cream);
  --door-band: 0.155em;
  --door-pitch: 0.2em;

  /* layout */
  --gutter: clamp(1.25rem, 4vw, 4rem);
  /*
    Content runs wider than the floating nav on purpose — the nav is a
    pill sitting on the page, not a frame the page has to live inside.
  */
  --measure: 94rem;
  --measure-tight: 74rem;
  --section: clamp(4.5rem, 13vh, 9rem);
  --radius: 14px;

  /* motion */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --speed: 0.25; /* intro timeline scale */
  --door-speed: 0.62;
  --handoff: 420ms;
  --hold: 620ms;
  /*
    Both nav heights are declared, not measured. The docked band is
    pulled up onto the photo by exactly its own height, so if that
    distance and the bar's real height could ever disagree the
    difference would hang below the fold. Fixing both here means they
    cannot.
  */
  --nav-h: 5.5rem;
  --nav-pill-h: 3.8rem;
  /*
    How far below the top edge the nav floats once stuck. Sticky only
    applies its offset while pinned, so this moves the collapsed pill
    and leaves the docked band exactly where it is.
  */
  --nav-float: 0.6in;

  --t-door-in: calc(1s * var(--speed));
  --t-door-dur: calc(1.15s * var(--door-speed));
  --t-sub-in: calc(var(--t-door-in) + var(--t-door-dur) + 0.1s);

  --mark-stretch: 1;
  --mark-condense: 1;
  --mark-track: 0.06em;
  --mark-gap: -0.22em;
}

/* ---------- base ---------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /*
    Anchor targets clear the floating nav instead of hiding under it.
    The collapsed height, not the docked one: anything you can jump to
    is below the photo, and by then the nav has long since collapsed.
  */
  scroll-padding-top: calc(var(--nav-pill-h) + var(--nav-float) + 1.5rem);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--body);
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

/*
  The UA sheet gives [hidden] display:none, but any class that sets
  display beats it on specificity — .done { display:flex } left the
  confirmation on screen from first paint. This wins it back.
*/
[hidden] {
  display: none !important;
}

::selection {
  background: var(--orange);
  color: var(--cream);
}

:focus-visible {
  outline: 2px solid var(--orange);
  outline-offset: 3px;
  border-radius: 2px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 0.75rem 1rem;
  background: var(--fg);
  color: var(--cream);
  font-weight: 700;
}

.skip:focus {
  left: 0.5rem;
  top: 0.5rem;
}

/* ---------- type ---------- */

.label {
  margin: 0;
  font-family: var(--display);
  font-weight: 400;
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--orange);
}

.sec__head {
  margin-bottom: clamp(2rem, 5vh, 3.25rem);
}

/*
  The measure lives on the heading, not the wrapper. `ch` resolves against
  the element's own font-size — on the wrapper it would resolve against
  body size and choke display type down to a few words per line.
*/
.sec__h {
  /* the top margin spaces it off the eyebrow; zeroed where there isn't one */
  margin: 0.75rem 0 0;
  max-width: 21ch;
  font-size: clamp(1.9rem, 4.6vw, 3.4rem);
  font-weight: 400;
  letter-spacing: -0.022em;
  line-height: 1.02;
}

.sec__h:first-child {
  margin-top: 0;
}

.sec__p {
  margin: 1rem 0 0;
  max-width: 46ch;
  color: var(--muted);
}

/* ---------- buttons ---------- */

/* the fill fades up under the label rather than sweeping across it */
.btn {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 1.05rem 2.1rem;
  border: 0;
  background: var(--fg);
  color: var(--cream);
  font: inherit;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  border-radius: var(--radius);
  corner-shape: squircle;
}

.btn::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  background: var(--orange);
  opacity: 0;
  transition: opacity 0.4s ease;
}

.btn:hover::before,
.btn:focus-visible::before {
  opacity: 1;
}

.btn--wide {
  width: 100%;
}

.btn--quiet {
  background: none;
  color: var(--fg);
  box-shadow: inset 0 0 0 1px var(--rule);
}

.btn--quiet:hover,
.btn--quiet:focus-visible {
  color: var(--cream);
}

/*
  For use on the hero scrim. Cream on a dark photo is the highest
  contrast the palette can make, which is what the one button that
  matters should be. The orange still arrives on hover, same as
  everywhere else.
*/
.btn--light {
  background: var(--cream);
  color: var(--fg);
}

.btn--light:hover,
.btn--light:focus-visible {
  color: var(--cream);
}


.btn:disabled {
  opacity: 0.45;
  pointer-events: none;
}

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

/*
  In normal flow directly under the hero image, so it lands on the bottom
  edge of the first screen. sticky pins it to the top the moment you
  scroll past it, and it stays there for the rest of the page.
*/
.topnav {
  position: sticky;
  /*
    Clear of the top edge once stuck. This lives on the sticky offset,
    not as a margin on the bar — a margin there collapses through this
    (no padding or border to stop it) and just moves the whole element,
    leaving the bar flush against the top.
  */
  top: var(--nav-float);
  z-index: 20;
  /*
    Flat height, not min-. This is the box the negative margin below
    cancels, so it has to be exactly the distance pulled up — and it has
    to stay put when the bar inside it collapses, or that shrink would
    reach the document, move everything below it, and shove the sentinel
    back across the trigger it just fired.
  */
  block-size: var(--nav-h);
  /*
    Once stuck, the bottom of that reserved strip is empty and invisible
    but still sits at z-index 20 across the page. It must not eat clicks
    meant for the content underneath it.
  */
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--handoff) ease;
}

.topnav__bar {
  pointer-events: auto;
}

.settled .topnav {
  opacity: 1;
}

/* zero-height marker for the docked → floating switch */
.nav-sentinel {
  display: block;
  height: 0;
}

/*
  Two states, and the gap between them is the point. Docked: a deep
  full-width band lying across the bottom of the photo, frosted so the
  picture still reads through it. Stuck: it collapses to roughly half
  that depth and two thirds the width, and lifts off the page as a pill.
  The transition runs on the bar, not the sticky wrapper, so sticky
  positioning is never animated.
*/
.topnav__bar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: clamp(0.85rem, 3vw, 2.25rem);
  width: 100%;
  margin-inline: auto;
  /* height is the thing that collapses, so it is set rather than left to
     the padding to imply */
  block-size: var(--nav-h);
  padding: 0 clamp(1rem, 4vw, 4rem);
  /* the page background exactly — docked, it should read as part of the
     page rather than a panel sitting on it */
  background: var(--bg);
  border: 1px solid transparent;
  border-bottom-color: var(--rule);
  border-radius: 0;
  corner-shape: squircle;
  box-shadow: 0 0 0 rgba(var(--black-rgb), 0);
  transition: width 1.15s var(--ease), block-size 1.15s var(--ease),
    padding 1.15s var(--ease), margin 1.15s var(--ease),
    border-radius 1.15s var(--ease), border-color 0.7s ease,
    box-shadow 1s ease, background 0.7s ease;
}

.topnav.is-stuck .topnav__bar {
  width: min(66.667%, 62rem);
  block-size: var(--nav-pill-h);
  padding-inline: clamp(1rem, 2.5vw, 2rem);
  background: var(--bg);
  border-color: var(--rule);
  border-radius: 28px;
  /* a tight contact shadow plus a soft cast — reads as lifted, not flat */
  box-shadow: 0 1px 2px rgba(var(--black-rgb), 0.05),
    0 10px 28px rgba(var(--black-rgb), 0.12),
    0 28px 56px rgba(var(--black-rgb), 0.07);
}

.topnav__group {
  display: flex;
  align-items: center;
  gap: clamp(0.9rem, 2.5vw, 2rem);
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
}

.topnav__group--end {
  justify-content: flex-end;
}

.topnav__group a {
  color: var(--muted);
  text-decoration: none;
  transition: color 0.18s ease;
}

.topnav__group a:hover,
.topnav__group a:focus-visible {
  color: var(--orange);
}

/* same fill-fade as the page buttons */
.topnav__cta {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: 0.6rem 1.1rem;
  background: var(--fg);
  color: var(--cream) !important;
  border-radius: 11px;
  corner-shape: squircle;
  font-weight: 700;
}

.topnav__cta::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  background: var(--orange);
  opacity: 0;
  transition: opacity 0.4s ease;
}

.topnav__cta:hover::before,
.topnav__cta:focus-visible::before {
  opacity: 1;
}

/*
  The mark carries the size difference between the two states. Scale
  rather than font-size, so the collapse is a transform the compositor
  can run instead of a reflow on every frame of it.
*/
.topnav__logo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.02rem;
  color: var(--fg);
  line-height: 1;
  text-decoration: none;
  scale: 1.1;
  transition: scale 1.15s var(--ease);
}

.topnav.is-stuck .topnav__logo {
  scale: 1;
}

.topnav__word {
  font-family: var(--display);
  font-size: 1.8rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  line-height: 1;
}

/* fitLine() sets the tracking from the wordmark's measured width, so
   this follows the size above on its own */
.topnav__sub {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  /* set in the markup as Garage Renovation; leave it that way */
  text-transform: none;
  color: var(--muted);
  line-height: 1;
  white-space: nowrap; /* fitLine() sets the tracking */
}

/* ---------- hero: picture first, wall to wall ---------- */

/*
  The first screen bar the nav, which sits under it rather than on it —
  so the collapse never leaves the pill floating on bare photograph.
*/
.hero {
  position: relative;
  isolation: isolate;
  margin: 0;
  height: calc(100svh - var(--nav-h));
  min-height: 28rem;
  overflow: hidden;
}

.hero__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* the doorway and the bench matter; the driveway is the part to lose */
  object-fit: cover;
  object-position: center 32%;
  /* the source is a touch flat and dim — lift it without washing it out */
  filter: brightness(1.05) saturate(1.04) contrast(1.02);
}

/*
  Type over a photograph is only ever as good as its scrim, and a flat
  wash would dull the whole picture. Two layers instead.

  Four layers, each with one job.

  Both left corners get an ellipse. A linear ramp is weakest exactly
  halfway along itself, which is where the words are; a radial holds
  near full strength across a whole corner and only lets go past it. The
  top-left one runs to near-opaque, which is what buries the trees and
  the blown-out light in that corner rather than trying to tone them
  down — nothing there survives to look fake.

  A left-hand wall ties the two corners together so the edge is a single
  dark field top to bottom rather than two dark blooms. Then an even
  bottom wash, lighter, giving the nav band something to sit on across
  the full width.

  Warm near-black throughout rather than true black, so all of it reads
  as shadow in this photograph rather than panels laid over it.
*/
/*
  The other half of the same idea. Laying a pale wash over the right
  would only fog it, so this lifts the photograph itself: a
  backdrop-filter masked to the right, fading in from the middle. The
  open bay and the driveway gain their light back, and the left is
  untouched because the mask never reaches it.

  Unsupported means no filter and no harm, so there is nothing to guard.
*/
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  -webkit-backdrop-filter: brightness(1.2) saturate(1.06);
  backdrop-filter: brightness(1.2) saturate(1.06);
  -webkit-mask-image: linear-gradient(
    to right,
    transparent 36%,
    rgba(0, 0, 0, 0.55) 47%,
    #000 60%
  );
  mask-image: linear-gradient(
    to right,
    transparent 36%,
    rgba(0, 0, 0, 0.55) 47%,
    #000 60%
  );
}

.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    /* top-left: buries it */
    radial-gradient(
      76% 64% at 0% 0%,
      rgba(18, 12, 8, 0.8) 0%,
      rgba(18, 12, 8, 0.76) 44%,
      rgba(18, 12, 8, 0.54) 62%,
      rgba(18, 12, 8, 0.14) 78%,
      rgba(18, 12, 8, 0) 88%
    ),
    /* bottom-left: carries the copy, so it keeps the most of its weight */
      radial-gradient(
        96% 76% at 0% 100%,
        rgba(18, 12, 8, 0.88) 0%,
        rgba(18, 12, 8, 0.84) 46%,
        rgba(18, 12, 8, 0.58) 63%,
        rgba(18, 12, 8, 0.17) 78%,
        rgba(18, 12, 8, 0) 88%
      ),
    /* the wall between them */
      linear-gradient(
        to right,
        rgba(18, 12, 8, 0.64) 0%,
        rgba(18, 12, 8, 0.6) 20%,
        rgba(18, 12, 8, 0.42) 31%,
        rgba(18, 12, 8, 0.12) 41%,
        rgba(18, 12, 8, 0) 48%
      ),
    linear-gradient(
      to top,
      rgba(18, 12, 8, 0.6) 0%,
      rgba(18, 12, 8, 0.3) 20%,
      rgba(18, 12, 8, 0.06) 38%,
      rgba(18, 12, 8, 0) 52%
    );
}

/* lower left, sitting on the nav band. Above ::after, which is also 2 and
   would otherwise paint over it — ::after is the last child, not the
   first */
.hero__copy {
  position: relative;
  z-index: 3;
  display: flex;
  height: 100%;
  align-items: flex-end;
  padding: 0 var(--gutter) clamp(4.5rem, 14vh, 9.5rem);
}

/* the same column the rest of the page runs in, so the headline's left
   edge lines up with everything below it */
.hero__in {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
  color: var(--cream);
}

.hero__h {
  margin: 0;
  max-width: 16ch;
  font-size: clamp(2.1rem, 5.2vw, 4.4rem);
  font-weight: 400;
  letter-spacing: -0.026em;
  line-height: 1.02;
  /* the photo is busy; a soft cast keeps the counters clean over it */
  text-shadow: 0 1px 28px rgba(18, 12, 8, 0.45);
}

.hero__sub {
  margin: clamp(0.85rem, 2vh, 1.35rem) 0 0;
  max-width: 44ch;
  font-size: clamp(0.95rem, 1.5vw, 1.15rem);
  line-height: 1.5;
  color: rgba(var(--cream-rgb), 0.84);
}

.hero__act {
  display: flex;
  margin-top: clamp(1.35rem, 3vh, 2.1rem);
}

/* ---------- seam: the join between sections ---------- */

/*
  Two sections meeting on nothing but each other's padding reads as a
  gap rather than a join. A hairline gives the eye somewhere to land, and
  the mark sits on it — which is what the X has meant on this page from
  the opening: the place where two halves meet.

  The rule fades out at both ends so it never looks like a boxed border.
*/
.seam {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.seam::before {
  content: "";
  position: absolute;
  left: var(--gutter);
  right: var(--gutter);
  height: 1px;
  background: linear-gradient(
    to right,
    transparent,
    var(--rule) 18%,
    var(--rule) 82%,
    transparent
  );
}

/* sits on the rule and interrupts it, rather than floating over it */
.seam__x {
  position: relative;
  padding-inline: 1.15rem;
  background: var(--bg);
  font-family: var(--display);
  font-size: 1.2rem;
  line-height: 1;
  letter-spacing: 0.09em;
  color: rgba(var(--black-rgb), 0.3);
}

/* ---------- thesis ---------- */

.thesis {
  max-width: var(--measure);
  margin-inline: auto;
  padding: clamp(4rem, 13vh, 8rem) var(--gutter);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}

.thesis__shot {
  margin: 0;
  overflow: hidden;
  border-radius: var(--radius);
  corner-shape: squircle;
}

.thesis__shot img {
  width: 100%;
  height: auto;
}

/* the line breaks are explicit in the markup — a measure would double-break */
.thesis__q {
  margin: 0;
  max-width: none;
  font-size: clamp(1.5rem, 3.4vw, 2.9rem);
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1.12;
}

.thesis__a {
  margin: clamp(1.25rem, 3vh, 1.9rem) 0 0;
  max-width: 44ch;
  font-size: clamp(1rem, 1.4vw, 1.15rem);
  color: var(--muted);
}

/* what you actually get, before the ask */
.trust {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: clamp(1.25rem, 3vh, 1.75rem) 0 0;
  padding: 0;
  list-style: none;
  font-size: 0.88rem;
  color: var(--muted);
}

.trust li {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
}

/*
  A dot. An X next to a benefit reads as a negative or a close button —
  the wrong signal on a list of what you get.
*/
.trust li::before {
  content: "";
  flex: none;
  width: 5px;
  height: 5px;
  margin-top: 0.62em;
  border-radius: 50%;
  background: var(--orange);
}

.thesis__act {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: clamp(1.5rem, 4vh, 2.25rem);
}

/* ---------- sections ---------- */

.build,
.faq {
  max-width: var(--measure);
  margin-inline: auto;
  padding: var(--section) var(--gutter);
}

/*
  Full width, so the step images can run right out to the walls. Only the
  text inside gets the gutter.
*/
.work {
  /* the thesis closes with its own generous padding, so the two stack into
     a bigger gap than either intends. Half a section on top is enough */
  padding-block: calc(var(--section) * 0.5) var(--section);
}

.work > .sec__head {
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/*
  The split device: a seam down the middle of a before/after frame with
  the mark sitting on it, because that is what the X means — the before
  and the after meeting. The right half wipes out from the seam on scroll.
*/
.step__shot--split img {
  clip-path: inset(0 50% 0 0);
  transition: clip-path 1.6s var(--ease) 0.15s;
}

.is-in.step__shot--split img {
  clip-path: inset(0 0 0 0);
}

.split__seam {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 1px;
  background: rgba(var(--cream-rgb), 0.85);
  transform: scaleY(0);
  transform-origin: top;
  transition: transform 0.9s var(--ease);
}

.is-in .split__seam {
  transform: scaleY(1);
}

.split__x {
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  font-family: var(--display);
  font-size: clamp(1.35rem, 3.2vw, 2.3rem);
  line-height: 1;
  color: var(--cream);
  text-shadow: 0 2px 24px rgba(var(--black-rgb), 0.5);
  opacity: 0;
  transition: opacity 0.8s ease 0.5s;
}

.is-in .split__x {
  opacity: 1;
}

/* the "after" floor is pale — the captions need their own ground */
.split__caps {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  align-items: flex-end;
  min-height: 5.5rem;
  padding: 0 0 clamp(0.85rem, 2vw, 1.5rem);
  background: linear-gradient(
    to top,
    rgba(var(--black-rgb), 0.6),
    transparent
  );
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--cream);
  text-shadow: 0 1px 8px rgba(var(--black-rgb), 0.75);
}

.split__caps span {
  flex: 1;
  padding-inline: clamp(0.9rem, 2.5vw, 1.75rem);
}

.split__caps span:last-child {
  text-align: right;
}

/* ---------- process: the staircase ---------- */

.steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(3.5rem, 12vh, 8rem);
}

.step {
  display: flex;
  align-items: center;
  gap: clamp(1.5rem, 4vw, 4.5rem);
}

/* alternate sides so the sequence walks down the page */
.step:nth-child(even) {
  flex-direction: row-reverse;
}

/*
  Flush to the wall on its own side, so only the inner corners round.
  Narrower than it looks — the photos run at natural proportions, so
  width sets height, and this is what keeps the section scrollable.
*/
.step__shot {
  position: relative;
  flex: 0 0 38%;
  margin: 0;
  overflow: hidden;
}

.step:nth-child(odd) .step__shot {
  border-radius: 0 var(--radius) var(--radius) 0;
}

.step:nth-child(even) .step__shot {
  border-radius: var(--radius) 0 0 var(--radius);
}

.step__text {
  padding-inline: var(--gutter);
}

/* natural proportions — no forced ratio, no crop */
.step__shot img {
  width: 100%;
  height: auto;
  filter: brightness(1.08) saturate(1.06) contrast(1.01);
}

.step__text {
  flex: 1;
  min-width: 0;
}

/*
  Porscha's figures are geometric marks more than legible numerals, so
  they are set large enough to read as a deliberate device rather than a
  rendering fault. The heading beside them carries the meaning anyway.
*/
.step__n {
  margin: 0 0 0.55rem;
  font-family: var(--display);
  font-weight: 400;
  font-size: 1.05rem;
  letter-spacing: 0.12em;
  line-height: 1;
  color: var(--orange);
}

.step__h {
  margin: 0.5rem 0 0.6rem;
  font-size: clamp(1.35rem, 2.4vw, 1.9rem);
  font-weight: 400;
  letter-spacing: -0.014em;
  line-height: 1.05;
}

.step__p {
  margin: 0;
  max-width: 36ch;
  font-size: clamp(1.02rem, 1.2vw, 1.15rem);
  color: var(--muted);
}

/*
  Annotations: a dot on the thing, a hairline out, a label. The detail a
  spec sheet has and a brochure does not — it says we looked closely.
  Positioned per image with --x / --y in the markup.
*/
.anno {
  position: absolute;
  top: var(--y);
  left: var(--x);
  display: flex;
  align-items: center;
  pointer-events: none;
}

.anno::before {
  content: "";
  width: 7px;
  height: 7px;
  flex: none;
  border-radius: 50%;
  background: var(--cream);
  box-shadow: 0 0 0 1px rgba(var(--black-rgb), 0.45),
    0 1px 6px rgba(var(--black-rgb), 0.5);
  transform: scale(0);
  transition: transform 0.5s var(--ease) 0.5s;
}

.anno::after {
  content: "";
  height: 1px;
  width: clamp(1.1rem, 2.5vw, 2rem);
  background: rgba(var(--cream-rgb), 0.85);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.55s var(--ease) 0.62s;
}

/*
  order:1 puts the label after the leader line. ::after is always last in
  source order, so without this the line would draw past the text.
*/
.anno__label {
  order: 1;
  padding: 0.3rem 0.55rem;
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--cream);
  /* its own ground, so it stays legible over bright floors and dark walls */
  background: rgba(var(--black-rgb), 0.72);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  border-radius: 5px;
  opacity: 0;
  transform: translateX(-0.35rem);
  transition: opacity 0.5s ease 0.78s, transform 0.5s var(--ease) 0.78s;
}

.is-in .anno::before {
  transform: scale(1);
}
.is-in .anno::after {
  transform: scaleX(1);
}
.is-in .anno__label {
  opacity: 1;
  transform: none;
}

/* points that sit right of centre lead leftwards, so labels stay in frame */
.anno[data-side="left"] {
  flex-direction: row-reverse;
  translate: -100% 0;
}

.anno[data-side="left"] .anno__label {
  order: -1;
}

.anno[data-side="left"]::after {
  transform-origin: right;
}

.anno[data-side="left"] .anno__label {
  transform: translateX(0.35rem);
}

.is-in .anno[data-side="left"] .anno__label {
  transform: none;
}

/* the cleanout frame keeps the wipe — it is the before/after gesture */
.wipe {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--bg);
  transform-origin: right;
  transform: scaleX(1);
  transition: transform 1.15s var(--ease) 0.1s;
}

.is-in .wipe {
  transform: scaleX(0);
}

/* ---------- configurator ---------- */

.cfg {
  position: relative;
  padding: clamp(1.5rem, 4vw, 2.75rem);
  background: var(--raise);
  border: 1px solid var(--rule);
  border-radius: 22px;
  corner-shape: squircle;
}

.cfg__rail {
  display: flex;
  gap: 6px;
  margin-bottom: clamp(1.5rem, 4vh, 2.25rem);
}

.cfg__tick {
  flex: 1;
  height: 2px;
  background: var(--rule);
  transition: background 0.4s ease;
}

.cfg__tick.is-on {
  background: var(--orange);
}

.cfg__step {
  display: none;
  margin: 0;
  padding: 0;
  border: 0;
}

.cfg__step.is-on {
  display: block;
  animation: step-in 0.5s var(--ease) both;
}

@keyframes step-in {
  from {
    opacity: 0;
    transform: translateY(0.6rem);
  }
}

.cfg__q {
  padding: 0;
  font-size: clamp(1.3rem, 3vw, 2rem);
  font-weight: 400;
  letter-spacing: -0.018em;
  line-height: 1.1;
}

.cfg__q--sm {
  font-size: clamp(1.1rem, 2.2vw, 1.5rem);
}

/* where a legend is followed straight by fields, with no sub between */
.cfg__q + .row {
  margin-top: clamp(1.25rem, 3vh, 1.75rem);
}

.cfg__sub {
  margin: 0.6rem 0 1.5rem;
  max-width: 44ch;
  font-size: 0.95rem;
  color: var(--muted);
}

.opts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 0.6rem;
  margin-top: clamp(1.25rem, 3vh, 1.75rem);
}

/* big, quiet targets — one tap advances, so this is the whole interface */
.opt {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 1.1rem 1.25rem;
  text-align: left;
  font: inherit;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  corner-shape: squircle;
  cursor: pointer;
  transition: border-color 0.2s ease, transform 0.2s var(--ease),
    background 0.2s ease;
}

.opt:hover,
.opt:focus-visible {
  border-color: var(--orange);
  transform: translateY(-2px);
}

.opt.is-picked {
  border-color: var(--orange);
  background: rgba(var(--orange-rgb), 0.06);
}

.opt__t {
  font-weight: 700;
  letter-spacing: -0.01em;
}

.opt__s {
  font-size: 0.84rem;
  color: var(--muted);
}

/* --- estimate + form --- */

.cfg__step--final.is-on {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: start;
}

.quote__n {
  margin: 0.5rem 0 0;
  font-size: clamp(1.9rem, 4.5vw, 2.9rem);
  font-weight: 400;
  letter-spacing: -0.026em;
  line-height: 1;
}

.quote__n--text {
  font-size: clamp(1.4rem, 2.6vw, 1.9rem);
  letter-spacing: -0.018em;
  line-height: 1.1;
  max-width: 16ch;
}

.quote__note {
  margin: 0.6rem 0 0;
  font-size: 0.86rem;
  color: var(--muted);
}

.sheet {
  margin: 1.5rem 0 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule);
  font-size: 0.86rem;
}

.sheet div {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.4rem 0;
}

.sheet dt {
  color: var(--muted);
  letter-spacing: 0.04em;
}

.sheet dd {
  margin: 0;
  font-weight: 700;
  text-align: right;
}

.row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
}

.field {
  margin: 0 0 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.field label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
}

.field__opt span {
  letter-spacing: 0.04em;
  text-transform: none;
  font-weight: 400;
  opacity: 0.8;
}

.field input[type="text"],
.field input[type="tel"],
.field input[type="email"] {
  width: 100%;
  padding: 0.85rem 0.95rem;
  font: inherit;
  font-size: 1rem;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  corner-shape: squircle;
  transition: border-color 0.15s ease;
}

.field input:focus {
  outline: none;
  border-color: var(--orange);
}

/*
  :user-invalid, not :invalid — :invalid matches empty required fields on
  load, so every box would come up flagged before anyone has typed.
*/
.field input:user-invalid {
  border-color: var(--orange);
}

.drop {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  padding: 1.25rem 1rem;
  text-align: center;
  background: var(--bg);
  border: 1px dashed rgba(var(--black-rgb), 0.28);
  border-radius: var(--radius);
  corner-shape: squircle;
  cursor: pointer;
  transition: border-color 0.15s ease;
}

.drop:hover,
.drop.is-over {
  border-color: var(--orange);
}

.drop input {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}

.drop__t {
  font-weight: 700;
  font-size: 0.95rem;
}

.drop__s {
  font-size: 0.82rem;
  color: var(--muted);
}

.files {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
  font-size: 0.82rem;
  color: var(--muted);
}

.files li {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.25rem 0;
}

.note {
  margin: 0 0 0.75rem;
  min-height: 1.2em;
  font-size: 0.88rem;
  color: var(--muted);
}

.note--bad {
  color: var(--orange);
  font-weight: 700;
}

.fine {
  margin: 0.85rem 0 0;
  font-size: 0.78rem;
  color: var(--muted);
}

/* six outcomes, three across, two clean lines */
.opts--goal {
  grid-template-columns: repeat(3, 1fr);
}

.opt__ic {
  width: 22px;
  height: 22px;
  margin-bottom: 0.65rem;
  color: var(--orange);
  flex: none;
}

/*
  Garages are wide, and these carry a count as well as a shape, so they
  get roughly three times the area of the goal icons and render 1:1 —
  64 units into 64px — which is what keeps a 1.2 stroke crisp instead of
  landing between pixels. Black rather than the orange the goal icons
  use: at this size orange would pull the eye to the picture, and the
  question here is the label.
*/
.opt__ic--bay {
  width: 64px;
  height: 40px;
  color: var(--fg);
}

/*
  Label left, building right. The three icons then sit in a column down
  the right-hand edge of the step, which is what makes them read as one
  widening thing rather than three separate drawings.
*/
.opts--size .opt {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 0.2rem 1rem;
}

.opts--size .opt__t {
  grid-area: 1 / 1;
}

.opts--size .opt__s {
  grid-area: 2 / 1;
}

.opts--size .opt__ic {
  grid-area: 1 / 2 / span 2 / 2;
  margin-bottom: 0;
}

/* the fork: two equal choices, neither pushed */
.opts--fork {
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}

.opts--fork .opt {
  text-decoration: none;
}

/* offered only once the lead is already saved, so it can be ignored */
.extras {
  margin-top: clamp(1.5rem, 4vh, 2.25rem);
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule);
}

.extras summary {
  cursor: pointer;
  list-style: none;
  font-size: 0.88rem;
  font-weight: 700;
  color: var(--muted);
}

.extras summary::-webkit-details-marker {
  display: none;
}

.extras summary::after {
  content: " +";
  color: var(--orange);
}

.extras[open] summary::after {
  content: " –";
}

.extras > * + * {
  margin-top: 0.85rem;
}

.cfg__back {
  margin-top: 1.5rem;
  padding: 0;
  background: none;
  border: 0;
  font: inherit;
  font-size: 0.82rem;
  letter-spacing: 0.08em;
  color: var(--muted);
  cursor: pointer;
}

.cfg__back:hover {
  color: var(--orange);
}

/* centred and resolved — it should read as an ending, not a pause */
.done {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: clamp(1rem, 4vh, 2.5rem) 0;
  animation: step-in 0.6s var(--ease) both;
}

.done__mark {
  display: grid;
  place-items: center;
  width: 3.25rem;
  height: 3.25rem;
  margin-bottom: 1.25rem;
  font-family: var(--display);
  font-size: 1.15rem;
  color: var(--cream);
  background: var(--orange);
  border-radius: 50%;
}

.done__who {
  margin: 0.85rem 0 0;
  font-size: 0.9rem;
  letter-spacing: 0.04em;
  color: var(--muted);
}

.done .extras {
  width: min(100%, 32rem);
  text-align: left;
}

.done__h {
  margin: 0.6rem 0 0;
  max-width: 22ch;
  font-size: clamp(1.4rem, 3vw, 2.1rem);
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1.08;
}

/* ---------- faq ---------- */

.faq__list {
  max-width: var(--measure-tight);
  border-top: 1px solid var(--rule);
}

.faq details {
  border-bottom: 1px solid var(--rule);
}

.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 1.35rem 0;
  font-size: clamp(1.02rem, 1.6vw, 1.2rem);
  font-weight: 400;
  letter-spacing: -0.015em;
  cursor: pointer;
  list-style: none;
}

.faq summary::-webkit-details-marker {
  display: none;
}

.faq summary::after {
  content: "+";
  flex: none;
  font-size: 1.3rem;
  font-weight: 400;
  color: var(--orange);
  transition: transform 0.3s var(--ease);
}

.faq details[open] summary::after {
  transform: rotate(45deg);
}

.faq p {
  margin: 0 0 1.5rem;
  max-width: 62ch;
  color: var(--muted);
}

/* ---------- footer ---------- */

.foot {
  border-top: 1px solid var(--rule);
}

.foot__in {
  max-width: var(--measure);
  margin-inline: auto;
  padding: clamp(2.5rem, 7vh, 4rem) var(--gutter);
  display: grid;
  gap: 1.75rem;
}

.foot__word {
  font-family: var(--display);
  font-size: 1.6rem;
  letter-spacing: 0.09em;
}

.foot__p {
  margin: 0.4rem 0 0;
  color: var(--muted);
}

.foot__nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.75rem;
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.foot__nav a {
  color: var(--muted);
  text-decoration: none;
}

.foot__nav a:hover {
  color: var(--orange);
}

.foot__areas {
  margin: 0;
  max-width: 60ch;
  font-size: 0.82rem;
  color: var(--muted);
}

/* ---------- contact bar ---------- */

/*
  Sits in the bottom-right corner as one object, not three. Hidden until
  the hero is behind you, so it never lands on the opening.
*/
.hail {
  position: fixed;
  z-index: 15;
  right: clamp(0.75rem, 2.5vw, 1.75rem);
  bottom: clamp(0.75rem, 2.5vw, 1.75rem);
  opacity: 0;
  visibility: hidden;
  transform: translateY(140%);
  transition: opacity 0.35s ease, transform 0.5s var(--ease),
    visibility 0s linear 0.5s;
}

.hail.is-up {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity 0.35s ease, transform 0.5s var(--ease),
    visibility 0s;
}

/* same lifted treatment as the stuck nav, so they read as one family */
.hail__bar {
  position: relative;
  display: flex;
  align-items: stretch;
  background: rgba(var(--cream-rgb), 0.92);
  -webkit-backdrop-filter: blur(16px) saturate(1.5);
  backdrop-filter: blur(16px) saturate(1.5);
  border: 1px solid var(--rule);
  border-radius: 28px;
  corner-shape: squircle;
  overflow: hidden;
  /* the last shadow is a breath of warmth under the bar rather than a
     fourth cast — it lifts the whole object half a step off the page */
  box-shadow: 0 1px 2px rgba(var(--black-rgb), 0.05),
    0 10px 28px rgba(var(--black-rgb), 0.12),
    0 28px 56px rgba(var(--black-rgb), 0.07),
    0 6px 22px rgba(var(--orange-rgb), 0.09);
}

/*
  A specular sweep: a narrow band of light crossing the bar every few
  seconds, then a long wait. It has to read as a highlight catching a
  surface, not as a notification — so it is brief, low contrast, and
  mostly absent. The animation only runs while the bar is actually up,
  which keeps it off the compositor for the whole first screen.
*/
.hail__bar::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(
    100deg,
    transparent 40%,
    rgba(255, 255, 255, 0.5) 47%,
    rgba(255, 255, 255, 0.72) 50%,
    rgba(255, 255, 255, 0.5) 53%,
    transparent 60%
  );
  transform: translateX(-130%);
}

.hail.is-up .hail__bar::after {
  animation: hail-sheen 7s ease-in-out 1.8s infinite;
}

@keyframes hail-sheen {
  0% {
    transform: translateX(-130%);
  }
  /* the sweep is over in the first sixth; the rest is the pause */
  17%,
  100% {
    transform: translateX(130%);
  }
}

.hail__act {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.85rem clamp(0.95rem, 2.2vw, 1.35rem);
  color: var(--fg);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.3s ease;
}

/* the fill fades up under the label, the way the buttons do */
.hail__act::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  background: var(--orange);
  opacity: 0;
  transition: opacity 0.35s ease;
}

.hail__act:hover,
.hail__act:focus-visible {
  color: var(--cream);
}

.hail__act:hover::before,
.hail__act:focus-visible::before {
  opacity: 1;
}

.hail__icon {
  width: 1.05rem;
  height: 1.05rem;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.hail__sep {
  width: 1px;
  margin-block: 0.55rem;
  background: var(--rule);
}

/* revealed only when a mailto: click went nowhere */
.hail__fall {
  position: absolute;
  right: 0;
  bottom: calc(100% + 0.6rem);
  width: max-content;
  max-width: min(20rem, calc(100vw - 2rem));
  padding: 0.9rem 1rem;
  background: var(--fg);
  color: var(--cream);
  border-radius: var(--radius);
  corner-shape: squircle;
  box-shadow: 0 10px 28px rgba(var(--black-rgb), 0.22);
}

.hail__fall[hidden] {
  display: none;
}

.hail__fall-note {
  margin: 0 0 0.5rem;
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  opacity: 0.62;
}

.hail__copy {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  font-size: 0.86rem;
  font-weight: 700;
  text-align: left;
  cursor: pointer;
  transition: color 0.3s ease;
}

/* the brand orange is too dark to read on the black panel, so the
   affordance is the underline rather than a colour shift */
.hail__copy:hover,
.hail__copy:focus-visible {
  text-decoration: underline;
  text-underline-offset: 0.22em;
}

/* ---------- reveal ---------- */

[data-reveal] {
  opacity: 0;
  transform: translateY(1.1rem);
  transition: opacity 0.45s ease var(--d, 0ms),
    transform 0.6s var(--ease) var(--d, 0ms);
}

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

/* =========================================================
   the opening — the X is a garage door
   ========================================================= */

.intro-lock {
  overflow: hidden;
}

.stage {
  position: fixed;
  inset: 0;
  z-index: 30;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: clamp(2rem, 8vh, 6rem) var(--gutter);
  cursor: pointer;
  transition: opacity var(--handoff) ease, transform var(--handoff) ease;
}

.settled .stage {
  opacity: 0;
  transform: translateY(-1.25rem);
  pointer-events: none;
}

.mark {
  display: flex;
  align-items: baseline;
  margin: 0;
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.8rem, 5.4vw, 3.9rem);
  line-height: 1;
  text-transform: uppercase;
  gap: var(--mark-track);
  margin-top: calc((var(--mark-stretch) - 1) * 0.4em);
  margin-bottom: var(--mark-gap);
}

.mark__l,
.mark__x-letter,
.mark__door {
  display: inline-block;
  transform: scaleX(var(--mark-condense)) scaleY(var(--mark-stretch));
  transform-origin: center bottom;
}

.mark__sub {
  margin: 0;
  font-size: clamp(0.64rem, 1.65vw, 1.08rem);
  font-weight: 700;
  letter-spacing: 0.3em;
  /* matches the nav lockup: set as Garage Renovation in the markup */
  text-transform: none;
  color: var(--muted);
  white-space: nowrap; /* fitLine() sets the tracking */
}

.mark__x {
  position: relative;
  display: inline-block;
}

.mark__x-letter {
  position: relative;
  z-index: 1;
}

/*
  The door IS the X: same glyph, same position, painted with slats via
  background-clip:text, so the panel never exists outside the letterform.
*/
.mark__door {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  pointer-events: none;

  background-color: var(--door-face);
  background-image: repeating-linear-gradient(
    to bottom,
    var(--door-face) 0 var(--door-band),
    var(--door-gap) var(--door-band) var(--door-pitch)
  );
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;

  /* the mask edge is the door's bottom lip; shrinking it retracts the door */
  -webkit-mask-image: linear-gradient(#000, #000);
  mask-image: linear-gradient(#000, #000);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: top left;
  mask-position: top left;
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;

  will-change: mask-size, background-position;
}

@keyframes letter-in {
  from {
    opacity: 0;
    transform: translateY(0.22em) scaleX(var(--mark-condense))
      scaleY(var(--mark-stretch));
  }
  to {
    opacity: 1;
    transform: translateY(0) scaleX(var(--mark-condense))
      scaleY(var(--mark-stretch));
  }
}

@keyframes wrap-in {
  from {
    opacity: 0;
    transform: translateY(0.22em);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes fade-up {
  from {
    opacity: 0;
    transform: translateY(0.6em);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/*
  The door retracts into the top of the X: the mask edge rises while the
  slats travel with it. It drops a hair before it lifts — motors do that.
*/
@keyframes door-roll-up {
  0% {
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
    background-position: 0 0;
  }
  14% {
    -webkit-mask-size: 100% 103%;
    mask-size: 100% 103%;
    background-position: 0 0.02em;
  }
  /* travel is deliberately not a whole multiple of --door-pitch, or the
     bands land back in phase and the movement reads as static */
  100% {
    -webkit-mask-size: 100% 0%;
    mask-size: 100% 0%;
    background-position: 0 -0.71em;
  }
}

/*
  The bay light strikes behind the letter as the seal breaks. Two stacked
  shadows — a tight core and a wider bloom — so it reads as light rather
  than a blurred copy of the glyph.
*/
@keyframes bay-light {
  0% {
    text-shadow: none;
  }
  18% {
    text-shadow: 0 0 0.1em rgba(var(--orange-rgb), 0.95),
      0 0 0.34em rgba(var(--orange-rgb), 0.5);
  }
  26% {
    text-shadow: 0 0 0.04em rgba(var(--orange-rgb), 0.3),
      0 0 0.12em rgba(var(--orange-rgb), 0.15);
  }
  38% {
    text-shadow: 0 0 0.14em rgba(var(--orange-rgb), 1),
      0 0 0.5em rgba(var(--orange-rgb), 0.75);
  }
  100% {
    text-shadow: 0 0 0.1em rgba(var(--orange-rgb), 0.8),
      0 0 0.34em rgba(var(--orange-rgb), 0.4);
  }
}

/* start state — also the pre-play state */
.mark__l,
.mark__x,
.mark__sub {
  opacity: 0;
}

.play .mark__l {
  animation: letter-in calc(0.6s * var(--speed)) cubic-bezier(0.2, 0.7, 0.3, 1)
    both;
}

.play .mark__x {
  animation: wrap-in calc(0.6s * var(--speed)) cubic-bezier(0.2, 0.7, 0.3, 1)
    both;
}

.play .mark__l:nth-child(1) {
  animation-delay: calc(0.15s * var(--speed));
}
.play .mark__l:nth-child(2) {
  animation-delay: calc(0.23s * var(--speed));
}
.play .mark__x {
  animation-delay: calc(0.31s * var(--speed));
}
.play .mark__l:nth-child(4) {
  animation-delay: calc(0.39s * var(--speed));
}

.play .mark__door {
  animation: door-roll-up var(--t-door-dur) cubic-bezier(0.62, 0, 0.2, 1)
    var(--t-door-in) both;
}

.play .mark__x-letter {
  animation: bay-light calc(1.4s * var(--door-speed)) ease-out
    calc(var(--t-door-in) + 0.05s) both;
}

.play .mark__sub {
  animation: fade-up calc(0.7s * var(--speed)) ease-out var(--t-sub-in) both;
}

/* returning visitor, or intro skipped: no opening at all */
.no-intro .stage {
  display: none;
}

/* ---------- responsive ---------- */

@media (max-width: 60rem) {
  .cfg__step--final.is-on {
    grid-template-columns: 1fr;
  }

  /* three across gets cramped before the grid would wrap on its own */
  .opts--goal {
    grid-template-columns: repeat(2, 1fr);
  }

  .thesis {
    grid-template-columns: 1fr;
    gap: clamp(1.75rem, 5vh, 2.75rem);
  }
}

@media (max-width: 46rem) {
  /* a two-thirds pill is too cramped on a phone — stay a full-width bar */
  .topnav.is-stuck .topnav__bar {
    width: 100%;
    margin-top: 0;
    border-radius: 0;
    border-color: transparent;
    border-bottom-color: var(--rule);
    box-shadow: none;
  }

  /* a smaller version of the same collapse */
  :root {
    --nav-h: 4.6rem;
    --nav-pill-h: 3.5rem;
  }

  .topnav__bar {
    grid-template-columns: auto 1fr;
    gap: 0.75rem 1rem;
  }

  .topnav__logo {
    order: -1;
    align-items: flex-start;
    /* it sits against the left edge here, so grow rightward from it */
    transform-origin: left center;
    scale: 1.06;
  }

  .topnav__group {
    font-size: 0.72rem;
    gap: 0.9rem;
  }

  /* the two link groups collapse into one row beside the mark */
  .topnav__group:not(.topnav__group--end) {
    display: none;
  }

  .topnav__word {
    font-size: 1.55rem;
  }

  .hero__h {
    max-width: none;
  }

  .hero__act .btn {
    width: 100%;
  }

  /*
    A corner ellipse has nowhere to go on a narrow screen — the copy
    spans the full width, so the darkening has to as well.
  */
  .hero::after {
    background: linear-gradient(
      to top,
      rgba(18, 12, 8, 0.92) 0%,
      rgba(18, 12, 8, 0.82) 26%,
      rgba(18, 12, 8, 0.5) 50%,
      rgba(18, 12, 8, 0.14) 72%,
      rgba(18, 12, 8, 0) 88%
    );
  }

  .step,
  .step:nth-child(even) {
    flex-direction: column;
    align-items: stretch;
    gap: 1.25rem;
  }

  .step__shot,
  .step:nth-child(odd) .step__shot,
  .step:nth-child(even) .step__shot {
    flex: none;
    border-radius: 0;
  }

  .step__p {
    max-width: none;
  }

  .row {
    grid-template-columns: 1fr;
  }

  /* full width on a phone: the three targets get room to be tapped */
  .hail {
    left: 0.75rem;
    right: 0.75rem;
    bottom: 0.75rem;
  }

  .hail__act {
    flex: 1;
    justify-content: center;
    padding-inline: 0.5rem;
  }

  /* clear the contact bar */
  .foot__in {
    padding-bottom: 6rem;
  }
}

/* ---------- reduced motion ---------- */

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

  .mark__x,
  .mark__sub {
    opacity: 1;
    animation: none !important;
  }

  /* the sheen is decoration; there is nothing to preserve without it */
  .hail__bar::after {
    animation: none !important;
    opacity: 0;
  }

  .mark__l,
  .mark__x-letter {
    opacity: 1;
    animation: none !important;
    transform: scaleX(var(--mark-condense)) scaleY(var(--mark-stretch));
  }

  /* door already retracted */
  .mark__door {
    -webkit-mask-size: 100% 0%;
    mask-size: 100% 0%;
    animation: none !important;
  }

  /* everything lands finished — no wipes, no draws, no travel */
  [data-reveal],
  .step__shot--split img,
  .split__seam,
  .split__x,
  .anno::before,
  .anno::after,
  .anno__label,
  .cfg__step,
  .done {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    transition: none !important;
    animation: none !important;
  }

  .wipe {
    display: none;
  }

  .stage,
  .topnav,
  .topnav__bar,
  .topnav__logo,
  .hail,
  .hail__act::before,
  .btn,
  .btn::before,
  .topnav__cta::before,
  .opt {
    transition: none !important;
  }
}
