/* ==========================================================================
   Perficio — Story Site
   Design tokens mirror storysite.pen variables exactly.
   ========================================================================== */

:root {
  /* Brand palette (from storysite.pen) */
  --bg-light: #ddcbb2;
  --bg-dark: #553030;
  --cream: #f2e7d8;
  --cream-muted: #c3a98f;
  --ink: #553030;
  --ink-muted: #6b4436;
  --accent-gold: #e8b96a;
  --accent-rust: #94351a;
  --ember: #c2542b; /* decorative strokes only — fails text contrast */
  --surface-on-light: #ebddc8;
  --surface-on-dark: #653c3c;
  --line-on-dark: #ddcbb23d;
  --line-on-light: #5530303d;

  --font-display: "Bokor", "Papyrus", fantasy;
  --font-body: "Instrument Sans", system-ui, -apple-system, sans-serif;

  /* Layout: mockups are 1440 wide with a 110px inset */
  --content-max: 1220px;
  --gutter: clamp(1.25rem, 5vw, 6.875rem);
  --menu-width: 30em;

  /* Fluid type scale, anchored to the 1440px mockup values */
  --fs-h1: clamp(2.25rem, 1.05rem + 4.03vw, 3.625rem);
  --fs-h2: clamp(2rem, 1.13rem + 2.78vw, 3.125rem);
  --fs-h3: clamp(1.375rem, 1.05rem + 0.9vw, 1.9375rem);
  --fs-lead: clamp(1.125rem, 1.02rem + 0.42vw, 1.4375rem);
  --fs-body: clamp(1rem, 0.96rem + 0.21vw, 1.1875rem);
  --fs-sm: clamp(0.9375rem, 0.91rem + 0.14vw, 1.0625rem);

  /* One standard rhythm above/below section content. Keyed to viewport WIDTH,
     not height — `vh` made every section collapse to the same cramped value on
     a short window regardless of how much content it held. */
  --section-pad: clamp(5rem, 3.5rem + 5.5vw, 10rem);

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

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

html {
  scroll-behavior: smooth;
  /* Sticky header clearance for anchor jumps */
  scroll-padding-top: 96px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg-dark);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.65;
  /* `clip`, not `hidden`. On iOS, overflow-x:hidden on body creates a scroll
     container, and momentum scrolling inside it snaps back toward the top.
     `clip` crops identically without making the element scrollable. */
  overflow-x: clip;
}

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

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

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

h1,
h2,
h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1.18;
}

p {
  margin: 0;
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

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

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

.skip-link {
  position: absolute;
  top: -100px;
  left: 1rem;
  z-index: 300;
  padding: 0.75rem 1.25rem;
  background: var(--bg-dark);
  color: var(--cream);
  border-radius: 0 0 8px 8px;
  transition: top 0.2s var(--ease);
}
.skip-link:focus {
  top: 0;
}

/* ==========================================================================
   Layout primitives
   ========================================================================== */

.main {
  position: relative;
  z-index: 2;
  background: var(--bg-dark);
}

.section {
  position: relative;
  overflow: hidden;
}

.section__inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: calc(var(--content-max) + var(--gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Scroll-parallax backdrop. Taller than the section so it can travel
   vertically without exposing an edge. */
.section__bg {
  position: absolute;
  inset: -12% 0;
  z-index: 0;
  background-size: cover;
  background-position: center;
  will-change: transform;
}

/* <picture> is only a source-selection wrapper; it must not introduce a box.
   Left as an inline element it broke `height: 100%` on the images inside and
   killed the luminosity blend on Kenny's portrait. */
picture {
  display: contents;
}

/* Section artwork positioned against the section box, mirroring the mockups
   where these images bleed past the content column. */
.section-art {
  position: absolute;
  top: 50%;
  z-index: 0;
  transform: translateY(-50%);
  pointer-events: none;
}
.section-art img {
  display: block;
  width: 100%;
  /* Required: the width/height attributes are presentational hints, and
     without this the declared height wins and squashes the image. */
  height: auto;
}

.section--light {
  background-color: var(--bg-light);
  color: var(--ink);
}

.section--dark {
  background-color: var(--bg-dark);
  color: var(--cream);
}

/* Paper-texture backdrops lifted from the mockup frames */
.section--paper-light {
  background-image: url("../assets/bg-light-paper.webp");
  background-size: cover;
  background-position: center;
}
.section--paper-dark {
  background-image: url("../assets/bg-dark-paper.webp");
  background-size: cover;
  background-position: center;
}

.h2 {
  font-size: var(--fs-h2);
  line-height: 1.2;
}

.lead {
  font-size: var(--fs-lead);
  line-height: 1.68;
}

/* ==========================================================================
   "Keep going" section link
   ========================================================================== */

/* Absolute, like the hero's scroll invite, so it sits in the section's own
   bottom padding without adding height. Several sections are sized to the
   mockup's frame (940/900/1000px) with their artwork centred against that
   box — pushing them taller in flow would drag the artwork out of position. */
/* In flow rather than absolutely positioned, so each link lines up with the
   content it follows and the section grows to make room for it. */
.keep-going {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  color: var(--cream);
  transition: opacity 0.45s var(--ease), visibility 0.45s var(--ease);
}

/* Left-aligned inside a content column, directly under what it refers to.
   The BLOCK hugs the left edge (fit-content + auto right margin) but its
   contents stay centred, so the chevron sits under the middle of the label —
   left-aligning the chevron itself is not how this component is built. */
.keep-going--start {
  align-items: center;
  width: fit-content;
  margin-top: clamp(2.25rem, 4vw, 3.25rem);
  margin-right: auto;
}

/* Centred at the foot of a section. The inner's bottom padding supplies the
   space above; this adds the matching space below. */
.section > .keep-going {
  width: fit-content;
  margin-inline: auto;
  margin-bottom: calc(var(--section-pad) * 0.62);
}

/* Sections that ask a question hold their link back until something is
   answered, so it reads as "done here, carry on" rather than "skip this". */
.keep-going[data-gated="true"] {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
/* Highest-contrast variant per background */
.keep-going--on-light {
  color: var(--ink);
}



.keep-going__label {
  font-family: var(--font-display);
  /* Mockup: 32px */
  font-size: clamp(1.375rem, 1.1rem + 0.97vw, 2rem);
  line-height: 1.1;
}

.keep-going__chevron {
  width: 26px;
  height: 26px;
  animation: nudge-down 2.1s var(--ease) infinite;
}
.keep-going:hover .keep-going__chevron {
  animation-play-state: paused;
  transform: translateY(5px);
  transition: transform 0.3s var(--ease);
}

/* A double-tap bounce with a long rest, so it reads as an invitation rather
   than a loop demanding attention. */
@keyframes nudge-down {
  0%, 55%, 100% { transform: translateY(0); }
  12% { transform: translateY(7px); }
  24% { transform: translateY(0); }
  34% { transform: translateY(4px); }
  44% { transform: translateY(0); }
}
.keep-going:focus-visible {
  outline: 3px solid currentColor;
  outline-offset: 6px;
  border-radius: 6px;
}

@media (prefers-reduced-motion: reduce) {
  .keep-going__chevron,
  .hero__scroll-arrow {
    animation: none;
  }
  .keep-going:hover .keep-going__chevron {
    transform: none;
  }
}

/* ==========================================================================
   Scroll reveal
   ========================================================================== */

/* Headings emerge from behind a mask sitting at their own left edge: the
   element's box stays put with overflow hidden while the inner span slides
   across it, so the text appears to come out from behind a wall. Only
   headings get the mask — wrapping arbitrary items would fight flex and grid
   layouts, so those slide and fade instead. */
[data-reveal-mask] {
  overflow: hidden;
}
[data-reveal-mask] > .reveal__inner {
  display: inline-block;
}
[data-reveal-mask]:not([data-revealed]) > .reveal__inner {
  will-change: transform;
}
html:not(.reveals-ready) [data-reveal-mask] > .reveal__inner,
[data-reveal-mask]:not([data-revealed]) > .reveal__inner {
  transform: translateX(-105%);
}
[data-reveal-mask][data-revealed] > .reveal__inner {
  transform: translateX(0);
  transition: transform 0.85s cubic-bezier(0.16, 0.84, 0.34, 1);
}

html:not(.reveals-ready) [data-reveal-item],
[data-reveal-item]:not([data-revealed]) {
  opacity: 0;
  transform: translateX(-34px);
}
/* will-change only while the element is still waiting to animate. Leaving it
   on afterwards pinned a GPU layer per element — 53 of them — which is real
   memory pressure on a phone for no benefit once the transition is done. */
[data-reveal-item]:not([data-revealed]) {
  will-change: transform, opacity;
}
[data-reveal-item][data-revealed] {
  opacity: 1;
  transform: translateX(0);
  transition: opacity 0.6s var(--ease), transform 0.7s cubic-bezier(0.16, 0.84, 0.34, 1);
  transition-delay: var(--reveal-delay, 0ms);
}

/* Nothing hidden if the animation never runs */
@media (prefers-reduced-motion: reduce) {
  [data-reveal-mask] > .reveal__inner,
  [data-reveal-item] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

/* ==========================================================================
   Fixed underlay navigation  (Osmo: fixed-underlay-navigation)
   ========================================================================== */

/* Gradient scrim rather than a solid bar: dark at the very top, fading out by
   127px, so the nav always has something to sit on without cutting a hard band
   across the artwork. */
.underlay-nav__header {
  position: fixed;
  inset: 0 0 auto;
  z-index: 100;
  color: var(--cream);
  /* Holds near-full strength through the band the logo and Menu sit in, then
     falls away. A straight linear fade left the cream text at 3:1 over the
     light CTA section; this keeps it above 4.5:1 everywhere. */
  background: linear-gradient(
    #542d2f 0%,
    #542d2fdb 45%,
    #542d2f8a 70%,
    #542d2f00 100%
  );
}

.underlay-nav__container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 127px; /* mockup nav band height, sets the gradient's falloff */
  padding: 2.5rem var(--gutter) 2.5rem;
}

.underlay-nav__logo {
  display: block;
  width: clamp(120px, 11vw, 158px);
  flex-shrink: 0;
}
.underlay-nav__logo img {
  width: 100%;
  height: auto;
}

.underlay-nav__toggle {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  padding: 0.5rem;
  margin-right: -0.5rem;
  /* Inherits the header colour, which is the single owner of nav contrast */
  color: inherit;
}

.underlay-nav__toggle-text {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: flex-start;
  height: 1.5em;
  overflow: hidden;
  flex: none;
}

.underlay-nav__toggle-label {
  font-size: clamp(1rem, 0.94rem + 0.28vw, 1.25rem);
  font-weight: 500;
  line-height: 1.5;
}

.underlay-nav__toggle-icon {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.4375rem;
  width: 26px;
  flex: none;
}

.underlay-nav__toggle-bar {
  width: 100%;
  height: 2px;
  background-color: currentColor;
  flex: none;
}

.underlay-nav__menu {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  width: var(--menu-width);
  color: var(--cream);
  background: var(--bg-dark);
  border-left: 1px solid var(--line-on-dark);
}

.underlay-nav__inner {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2em;
  width: 100%;
  height: 100%;
  padding: 6rem 4rem 2rem;
  overflow-y: auto;
}

.underlay-nav__list {
  display: flex;
  flex-direction: column;
  gap: 18px;
  width: 100%;
}

.underlay-nav__link-large {
  display: inline-block;
  font-size: clamp(1.5rem, 1.05rem + 1.5vw, 2.125rem);
  line-height: 1.29;
  color: var(--cream);
  transition: color 0.2s var(--ease);
}
.underlay-nav__link-large:hover {
  color: var(--accent-gold);
}

.underlay-nav__overlay {
  position: fixed;
  inset: 0 -1px 0 0;
  z-index: 100;
  visibility: hidden;
  pointer-events: none;
  overflow: clip;
  cursor: pointer;
}

.underlay-nav__dark {
  position: absolute;
  inset: 0;
  opacity: 0;
  background-color: #2e1919a6;
}

/* ==========================================================================
   Section 1 — Hero (Osmo: parallax-image-layers)
   ========================================================================== */

.hero {
  position: relative;
  /* Contains the layer/copy z-order so the hero copy can be interleaved
     between the sky and the airplane without leaking into other sections. */
  isolation: isolate;
  display: flex;
  align-items: flex-start;
  min-height: 100svh;
  /* Floor of 9rem (144px) clears the 127px nav band at any viewport height —
     a purely vh-based value collapsed under it on short windows. */
  padding-top: clamp(9rem, 15vh, 9.25rem);
  padding-bottom: clamp(6rem, 12vh, 8rem);
  background-color: var(--bg-light);
  overflow: hidden;
}

.hero__visuals {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* Keeps the 1440x960 composition intact at any viewport. Centred with
   `margin:auto` rather than a transform — a transform would create a stacking
   context and trap the layers' z-indices, preventing the copy from sitting
   between the sky and the airplane. */
/* Cover-sized and genuinely centred. `inset:0; margin:auto` does NOT centre an
   absolutely positioned box that is wider than its container — the margins
   resolve to 0 and it left-aligns, which threw the whole composition off and
   pushed the backpacker off-screen on narrow viewports. Negative margins are
   used rather than a translate because a transform would create a stacking
   context and trap the layers' z-indices. Both percentages resolve against the
   container's width, which is also what the height is derived from. */
.hero__stage {
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100%, 150svh);
  aspect-ratio: 3 / 2;
  margin-left: calc(-0.5 * max(100%, 150svh));
  margin-top: calc(-0.5 * max(100%, 150svh) / 1.5);
}

.hero__layer {
  position: absolute;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  will-change: transform;
}

/* Percentages derived from the mockup's 1440x960 frame */
.hero__layer--sky {
  left: -1.667%;
  top: 2.396%;
  width: 101.667%;
  height: 99.896%;
  z-index: 1;
  background-image: url("../assets/sky.webp");
}
/* The one layer NOT positioned purely against the stage. The subhead patch is
   sized from the viewport width while the stage is sized from its height, so
   the two drift apart as the window changes shape and the paper kept cutting
   across the plane (140px of it at 1280x720). The term below cancels the
   stage's vertical crop, which makes the plane land at a fixed
   `--plane-top` from the top of the viewport at every window shape. It still
   parallaxes normally: GSAP drives transform, this only sets `top`. */
.hero__layer--airplane {
  /* In px, not svh: the thing it has to clear — hero padding, H1, patch — is
     itself capped in px by the type scale, topping out around 392px. An svh
     value tracks the window instead of the copy and so sinks the plane into the
     rooftops on tall screens. */
  --plane-top: 420px;
  left: 18.681%;
  top: calc(var(--plane-top) + (max(66.667vw, 100svh) - 100svh) / 2);
  width: 19.792%;
  height: 21.042%;
  z-index: 3;
  opacity: 0.75; /* the mockup fades it into the sky */
  background-image: url("../assets/airplane.webp");
}
.hero__layer--city {
  left: -1.667%;
  top: 15.417%;
  width: 109.931%;
  height: 106.354%;
  z-index: 5;
  background-image: url("../assets/city.webp");
}
.hero__layer--guy {
  left: 31.181%;
  top: 40.521%;
  width: 35.972%;
  height: 70.938%;
  z-index: 6;
  background-image: url("../assets/backpack-guy.webp");
}

/* ---------- Light rays ----------
   The sky artwork is already lit: sampling it shows warmth (R-B) climbing
   -1 → 53 top to bottom, i.e. a low sunset behind the buildings. So the beams
   rise from that light rather than raking down from the top corner.

   The sun sits at 44%, not the artwork's own 58% warm spot: at 58% it is
   directly behind the backpacker and the rooftops, which occlude almost all of
   it (measured peak 13/255 vs 27/255 at 44%). 44% puts it in the open band of
   sky where the plane flies, which is the only sizeable clear area left.

   `screen` with a saturated gold, not soft-light. Soft-light caps out around
   +20/255 on a backdrop this light, and a smooth +20 spread over a 500px-wide
   beam is invisible — the eye reads edges and colour, not uniform offsets. The
   beams are narrow (7/5/4% vs the 19/15/11% first tried) so they carry an edge,
   and gold rather than cream so the lift shows as warmth rather than as wash.
   Measured: peak 31/255 against soft-light's 13/255 over the same area. */
.hero__rays {
  position: absolute;
  left: -1.667%;
  top: 2.396%;
  width: 101.667%;
  height: 99.896%;
  z-index: 2;
  overflow: hidden;
  pointer-events: none;
  mix-blend-mode: screen;
  will-change: transform;
  /* Masked by the sky artwork's own alpha. The sky is a torn-edge watercolour
     on a tan page, so an unmasked fan lights the paper outside the painting
     and destroys the illusion. Using the image as its own mask confines every
     shaft to where paint actually exists. Must track the sky layer's
     `contain`/`center` exactly or the deckled edge will not line up. */
  -webkit-mask-image: url("../assets/sky.webp");
  mask-image: url("../assets/sky.webp");
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* Implied sun, centred on the artwork's own warm spot (~58%, ~78%). A plain
   radial gradient is already smooth — no blur filter, which would cost a
   full-surface pass on every animated frame. */
.hero__glow {
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse 52% 44% at 44% 56%,
    rgba(255, 206, 140, 0.9),
    rgba(255, 206, 140, 0.3) 45%,
    transparent 72%
  );
  opacity: 1; /* static pose, used when motion is reduced */
  animation: ray-breathe 5.5s ease-in-out infinite alternate;
}

/* Beams are a repeating conic gradient rather than rotated rectangles. A conic
   converges on a real origin, so the shafts genuinely radiate from the sun
   instead of merely pointing near it, and its stops give hard edges — which is
   what makes a shaft read at all. Soft-edged beams measured "visible" in an A/B
   diff yet were invisible in isolation: the eye needs the edge, not the lift.

   The element is oversized (inset -40%) so the fan still covers the corners as
   it rotates; that shifts the sun to 46.67%/53.33% in the element's own box,
   which is the same point as the glow's 44%/56% in the wrapper. */
.hero__fan {
  position: absolute;
  inset: -40%;
  transform-origin: 46.67% 53.33%;
  /* Fades the shafts out with distance so they do not run to the frame edge */
  -webkit-mask-image: radial-gradient(
    ellipse 46% 46% at 46.67% 53.33%,
    rgba(0, 0, 0, 0.55) 4%,
    rgba(0, 0, 0, 0.95) 20%,
    rgba(0, 0, 0, 0.5) 40%,
    transparent 62%
  );
  mask-image: radial-gradient(
    ellipse 46% 46% at 46.67% 53.33%,
    rgba(0, 0, 0, 0.55) 4%,
    rgba(0, 0, 0, 0.95) 20%,
    rgba(0, 0, 0, 0.5) 40%,
    transparent 62%
  );
}

/* Broad shafts */
.hero__fan--wide {
  background: repeating-conic-gradient(
    from 6deg at 46.67% 53.33%,
    rgba(255, 201, 112, 0) 0deg,
    rgba(255, 201, 112, 0.85) 1.1deg,
    rgba(255, 201, 112, 0.85) 3.4deg,
    rgba(255, 201, 112, 0) 4.6deg,
    rgba(255, 201, 112, 0) 11deg
  );
  transform: rotate(-2.25deg);
  opacity: 0.8;
  animation: fan-sweep-a 6.5s ease-in-out infinite alternate;
}

/* Finer shafts, counter-rotating, to break up the regular rhythm */
.hero__fan--fine {
  background: repeating-conic-gradient(
    from 2deg at 46.67% 53.33%,
    rgba(255, 226, 168, 0) 0deg,
    rgba(255, 226, 168, 0.5) 0.5deg,
    rgba(255, 226, 168, 0.5) 1.4deg,
    rgba(255, 226, 168, 0) 2.1deg,
    rgba(255, 226, 168, 0) 5.5deg
  );
  transform: rotate(1.5deg);
  opacity: 0.65;
  animation: fan-sweep-b 8.5s ease-in-out infinite alternate;
}

/* Measured, not guessed: the first pass swept 3.2deg over 19s, which works out
   at 1.7px/second at the shaft tips — well under what the eye picks up in a
   still scene. ~7deg over 6.5-8.5s gives roughly 10px/second.

   Opacity carries most of the perceived life. A brightness change is far easier
   to notice than a slow rotation, and it is what sunlight through haze actually
   does. The three durations (13/17/11s) are mutually prime-ish so the fans and
   the glow never pulse in sync, which would read as a flicker. Halving all
   three keeps those ratios, so they still never line up. */
@keyframes fan-sweep-a {
  from {
    transform: rotate(-6deg);
    opacity: 0.55;
  }
  to {
    transform: rotate(1.5deg);
    opacity: 1;
  }
}
@keyframes fan-sweep-b {
  from {
    transform: rotate(5deg);
    opacity: 0.85;
  }
  to {
    transform: rotate(-2deg);
    opacity: 0.45;
  }
}
@keyframes ray-breathe {
  from {
    opacity: 0.6;
  }
  to {
    opacity: 1;
  }
}

/* Paused while the hero is off screen — this page is ~9300px tall, so the
   hero is out of view for most of a session. */
.hero__rays[data-rays-paused] .hero__glow,
.hero__rays[data-rays-paused] .hero__fan {
  animation-play-state: paused;
}

/* Off screen: stop rendering it entirely rather than merely freezing it. */
.hero__rays[data-rays-hidden] {
  visibility: hidden;
}

/* Sky (1), rays (2) and airplane (3) pass behind the copy; the city (5) and
   backpacker (6) pass in front of it. */
.hero .section__inner {
  z-index: 4;
}

.hero__copy {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  /* No gap: the subhead's painted patch tucks up under the headline, so the
     spacing is set by its own negative margin instead. */
  gap: 0;
  max-width: 1000px;
}

.hero__title {
  font-size: var(--fs-h1);
  line-height: 1.17;
  color: var(--ink);
  /* text-shadow, not filter: a filter would also blur the subhead's patch */
  text-shadow: 0 4px 4px rgba(0, 0, 0, 0.5);
}

/* Torn-paper patch, straight off the mockup frame: a 608x176 box with the text
   inset at (73.8, 52), the whole group rotated 2.876 degrees CCW. Every value
   is in `em` so the artwork, the text inset and the tilt all scale together and
   the composition holds identically at every breakpoint. The asset ships
   pre-flipped to match the frame's flipY. */
.hero__subhead {
  width: 20.267em; /* 608/30 */
  margin: -0.5em 0 0 -0.567em; /* 15px above the H1 box bottom, 17px left of it */
  /* 52 top / 42 right / 34 bottom / 73.8 left. Right is trimmed from the
     frame's 50.2 and bottom padded from 29 so the box lands at exactly
     608x176: the live text sets two 45px lines where the frame reserved 95,
     and the trim leaves ~10px of slack before the first line would wrap. */
  padding: 1.733em 1.4em 1.133em 2.46em;
  font-size: clamp(0.95rem, 0.45rem + 2.1vw, 1.875rem);
  font-weight: 500;
  line-height: 1.5;
  color: var(--ink);
  text-shadow: 0 0 10px #fff;
  background: url("../assets/subhead-patch.webp") no-repeat center / 100% 100%;
  transform: rotate(-2.876deg);
}

.hero__scroll {
  position: absolute;
  left: 50%;
  bottom: clamp(1.5rem, 4vh, 2.75rem);
  z-index: 7;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  transform: translateX(-50%);
}

.hero__scroll img:first-child {
  width: clamp(200px, 22vw, 315px);
  height: auto;
  /* The invite sits over the illustration, so it needs its own lift */
  filter: drop-shadow(0 2px 6px rgba(255, 255, 255, 0.85));
}

.hero__scroll-arrow {
  width: 35px;
  height: auto;
  transition: transform 0.35s var(--ease);
  animation: nudge-down 2.1s var(--ease) infinite;
}
.hero__scroll:hover .hero__scroll-arrow {
  transform: translateY(8px);
}

/* ==========================================================================
   Section 2 — Who are you
   ========================================================================== */

.who {
  display: flex;
  align-items: center;
  min-height: 940px; /* mockup frame height, keeps the art in proportion */
  padding-block: var(--section-pad);
}

.who__copy {
  display: flex;
  flex-direction: column;
  width: 100%;
  /* 567/1220 — proportional, not fixed px. The art beside it is positioned in
     percentages, so a fixed column width closes the gap as the viewport
     narrows and the text ends up on top of the illustration. */
  max-width: 46.5%;
}

.who__title {
  color: var(--cream);
}

.who__subhead {
  padding-block: 1rem 2.75rem;
  font-size: clamp(1.0625rem, 0.99rem + 0.32vw, 1.3125rem);
  font-weight: 500;
  color: var(--cream-muted);
}

/* Mockup: 1168x1101 box at (560, -81) in a 1440x940 frame, contain-fitted */
.who__art {
  left: 38.89%;
  width: 81.11%;
}

/* ---------- Persona chips ---------- */

/* Wide enough to hold three chips per row, matching the mockup's 3 / 3 / 1
   grouping without hard-coding row wrappers. */
.persona-options {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  max-width: 540px;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 24px;
  border-radius: 999px;
  border: 1.5px solid var(--cream-muted);
  background: transparent;
  color: var(--cream);
  font-size: var(--fs-sm);
  font-weight: 500;
  line-height: 1;
  transition: background-color 0.2s var(--ease), border-color 0.2s var(--ease),
    color 0.2s var(--ease), transform 0.2s var(--ease),
    box-shadow 0.25s var(--ease);
}

.chip__dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  flex-shrink: 0;
  transition: background-color 0.2s var(--ease), border-color 0.2s var(--ease);
}

/* Grows a little and fills the dot, so hover previews what selection looks
   like rather than only outlining the chip. */
.chip:hover {
  border-color: var(--accent-gold);
  transform: scale(1.045);
  box-shadow: 0 0 0 1px rgba(232, 185, 106, 0.18),
    0 8px 22px -12px rgba(232, 185, 106, 0.55);
}
.chip:hover .chip__dot {
  background-color: var(--accent-gold);
  border-color: var(--accent-gold);
}
.chip:active {
  transform: scale(1.01);
}

.chip[aria-pressed="true"] {
  background: var(--accent-gold);
  border-color: var(--accent-gold);
  color: var(--ink);
}
.chip[aria-pressed="true"] .chip__dot {
  background: var(--ink);
  border-color: var(--ink);
}

/* Light-background variant (budget chips in section 3) */
.chip--on-light {
  border-color: var(--ink-muted);
  color: var(--ink);
}
/* Gold reads weakly on the light section, so the fill follows the ink */
.chip--on-light:hover {
  border-color: var(--ink);
  box-shadow: 0 0 0 1px rgba(85, 48, 48, 0.14),
    0 8px 22px -12px rgba(85, 48, 48, 0.45);
}
.chip--on-light:hover .chip__dot {
  background-color: var(--ink);
  border-color: var(--ink);
}
.chip--on-light[aria-pressed="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--bg-light);
}
.chip--on-light[aria-pressed="true"] .chip__dot {
  background: var(--bg-light);
  border-color: var(--bg-light);
}

.persona-other {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 18px;
  width: 100%;
}

/* ---------- Underline text fields ---------- */

.field {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.field__label {
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.4px;
  color: var(--cream-muted);
}

.field__input {
  width: 100%;
  padding: 0 0 12px;
  font-family: inherit;
  font-size: clamp(1.0625rem, 1.01rem + 0.21vw, 1.1875rem);
  color: var(--cream);
  background: transparent;
  border: 0;
  border-bottom: 1.5px solid var(--cream-muted);
  border-radius: 0;
  transition: border-color 0.2s var(--ease);
}
.field__input::placeholder {
  color: var(--cream-muted);
  opacity: 1;
}
.field__input:hover {
  border-bottom-color: var(--cream);
}
.field__input:focus {
  outline: none;
  border-bottom-color: var(--accent-gold);
  border-bottom-width: 2px;
  padding-bottom: 11.5px;
}
.field__input:focus-visible {
  outline: none;
}

.field--inline {
  min-width: 180px;
  max-width: 420px;
}
/* Only stretch to fill when sitting in a row beside a chip. Applying `flex`
   globally would be read as a height inside the column-direction wrappers. */
.persona-other .field--inline {
  flex: 1 1 232px;
}
/* The free-text box only appears once "Something else?" is actually chosen */
/* Revealed by a left-to-right wipe. The field keeps its box at all times and
   only clip-path/opacity/transform animate, so nothing here triggers layout —
   an earlier version animated max-width and flex-basis, which reflowed the
   chip row on every frame.

   `visibility` rather than clipping alone: a field collapsed with
   `max-width: 0; overflow: hidden` is still in the tab order, so keyboard
   users landed inside an invisible input. */
.persona-other .field--inline {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateX(-10px);
  clip-path: inset(0 100% 0 0);
  transition: opacity 0.3s var(--ease), transform 0.45s var(--ease),
    clip-path 0.45s var(--ease), visibility 0.45s var(--ease);
}
.persona-other .chip[aria-checked="true"] ~ .field--inline {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateX(0);
  clip-path: inset(0 0 0 0);
}

@media (prefers-reduced-motion: reduce) {
  .persona-other .field--inline {
    transition: none;
  }
}
.field--inline .field__input {
  font-size: var(--fs-sm);
  padding-bottom: 10px;
}

/* Dark text on light backgrounds */
.field--on-light .field__label {
  color: var(--ink-muted);
}
.field--on-light .field__input {
  color: var(--ink);
  border-bottom-color: var(--ink-muted);
}
.field--on-light .field__input::placeholder {
  color: var(--ink-muted);
}
.field--on-light .field__input:hover {
  border-bottom-color: var(--ink);
}
.field--on-light .field__input:focus {
  border-bottom-color: var(--accent-rust);
}

.name-field {
  padding-top: 3.25rem;
  max-width: 420px;
}

/* ==========================================================================
   Section 3 — What brings you here
   ========================================================================== */

.brings {
  display: grid;
  grid-template-columns: minmax(0, 470px) minmax(0, 1fr);
  gap: clamp(2.5rem, 6vw, 6rem);
  padding-block: var(--section-pad);
}

.brings__title {
  color: var(--ink);
}

.brings__name-token {
  color: var(--accent-rust);
}

.brings__body {
  margin-top: 1.625rem;
  font-size: clamp(1.0625rem, 1rem + 0.28vw, 1.1875rem);
  font-weight: 500;
  line-height: 1.63;
  color: var(--ink-muted);
}

.question-title {
  font-size: clamp(1.0625rem, 0.99rem + 0.32vw, 1.25rem);
  font-weight: 700;
  line-height: 1.45;
  color: var(--ink);
}

.hunches {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-top: 20px;
}

/* ---------- Checkbox rows ---------- */

.check {
  display: flex;
  align-items: center;
  gap: 16px;
  padding-block: 12px;
  cursor: pointer;
}

.check input {
  position: absolute;
  opacity: 0;
  width: 22px;
  height: 22px;
  margin: 0;
  cursor: pointer;
}

.check__box {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  border: 1.5px solid var(--ink-muted);
  border-radius: 5px;
  background: transparent;
  transition: background-color 0.2s var(--ease), border-color 0.2s var(--ease),
    transform 0.2s var(--ease), box-shadow 0.25s var(--ease);
}

.check__box svg {
  width: 15px;
  height: 15px;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.18s var(--ease), transform 0.18s var(--ease);
}

/* Light fill and a soft glow, with the box growing a touch */
.check:hover .check__box {
  border-color: var(--accent-rust);
  background: rgba(148, 53, 26, 0.14);
  transform: scale(1.08);
  box-shadow: 0 0 0 3px rgba(148, 53, 26, 0.1),
    0 6px 18px -8px rgba(148, 53, 26, 0.5);
}
/* The line itself grows a touch, anchored left so it opens rightward.
   Targets .check__label — the old .check__text wrapper went away with the
   underline effect. */
.check__label {
  transform-origin: left center;
  transition: transform 0.2s var(--ease), color 0.2s var(--ease);
}
.check:hover .check__label {
  transform: translateX(3px) scale(1.025);
  color: var(--accent-rust);
}

@media (prefers-reduced-motion: reduce) {
  .check__label,
  .check:hover .check__label,
  .check:hover .check__box {
    transform: none;
  }
}

.check input:checked + .check__box {
  background: var(--ink);
  border-color: var(--ink);
}
.check input:checked + .check__box svg {
  opacity: 1;
  transform: scale(1);
}

.check input:focus-visible + .check__box {
  outline: 3px solid var(--accent-rust);
  outline-offset: 3px;
}

.check__label {
  font-size: clamp(1rem, 0.96rem + 0.21vw, 1.125rem);
  line-height: 1.45;
  color: var(--ink);
}


.hunch-other {
  display: flex;
  flex-direction: column;
}

.hunch-other__input-wrap {
  padding-left: 38px;
  max-width: 420px;
}

.budget {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding-top: 3.5rem;
}

.budget__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

/* ==========================================================================
   Section 4 — Once upon a time
   ========================================================================== */

.story {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  min-height: 900px; /* mockup frame height */
  padding-block: var(--section-pad);
}

/* Mockup: 1115x1019 box at (-279, -80) in a 1440x900 frame, contain-fitted */
.story__art {
  left: -19.375%;
  width: 77.43%;
}

.story__copy {
  display: flex;
  flex-direction: column;
  gap: 1.875rem;
  width: 100%;
  max-width: 42.6%; /* 520/1220 */
  color: var(--cream);
}
.story__copy p {
  font-size: clamp(1.0625rem, 1rem + 0.28vw, 1.1875rem);
  line-height: 1.68;
}

/* ==========================================================================
   Section 5 — And if you were curious
   ========================================================================== */

.curious {
  display: grid;
  grid-template-columns: minmax(0, 364px) minmax(0, 560px);
  align-items: center;
  gap: clamp(2.5rem, 6vw, 6rem);
  padding-block: var(--section-pad);
}

.curious__video {
  position: relative;
  aspect-ratio: 9 / 16;
  width: 100%;
  border-radius: 10px;
  overflow: hidden;
}

.curious__video iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.curious__copy {
  display: flex;
  flex-direction: column;
  gap: 1.625rem;
  color: #fff;
}
.curious__copy p {
  font-size: clamp(1.0625rem, 1rem + 0.32vw, 1.25rem);
  font-weight: 500;
  line-height: 1.65;
}

/* ==========================================================================
   Section 6 — Kenny
   ========================================================================== */

/* Contains the portrait's blend so it mixes with this section's backdrop and
   nothing beyond it. */
#kenny {
  isolation: isolate;
}

.kenny {
  display: flex;
  align-items: center;
  min-height: 780px; /* mockup frame is 639; opened up for breathing room */
}

.kenny__copy {
  display: flex;
  flex-direction: column;
  gap: 2rem;
  width: 100%;
  max-width: 39.3%; /* 480/1220 */
  padding-block: var(--section-pad);
}

.kenny__title {
  font-size: clamp(2rem, 1.2rem + 2.5vw, 3.25rem);
  color: #fff;
}

.kenny__body {
  max-width: 480px;
  font-size: clamp(1.0625rem, 0.98rem + 0.35vw, 1.3125rem);
  font-weight: 500;
  line-height: 1.62;
  color: var(--ink);
}

/* The portrait runs taller than the section and is clipped top and bottom,
   as in the mockup. */
/* Mockup: 716x791 frame at (614, -76) in a 1440x639 frame.
   Measured against the CONTENT column rather than the section, which is
   full-bleed. As a share of the viewport the portrait kept growing on wide
   screens — 1273px at 2560px wide — and object-fit:cover then cropped away
   most of the face. Tied to the 1220px content box it settles at the mockup's
   716px and stops. */
.kenny__art {
  --content-w: min(calc(100vw - var(--gutter) * 2), var(--content-max));
  left: calc((100% - var(--content-w)) / 2 + var(--content-w) * 0.413);
  top: -11.89%;
  width: calc(var(--content-w) * 0.587);
  height: 123.79%;
  transform: none;
  /* The mockup fills this rectangle with a luminosity blend, which is what
     gives the portrait its sepia grade against the warm backdrop. It has to
     sit on the wrapper, not the img: the wrapper's own z-index makes it a
     stacking context, so a blend on the img would have no backdrop to mix
     with. */
  mix-blend-mode: luminosity;
}
.kenny__art img {
  height: 100%;
  object-fit: cover;
}

/* ==========================================================================
   Section 8 — A few things to know
   ========================================================================== */

.things {
  padding-block: var(--section-pad);
}

/* ---------- Accordion ---------- */

/* The rule belongs to the whole item, not the header, so it travels down with
   the panel as it expands instead of staying pinned under the title. */
.accordion {
  padding-top: 2.875rem;
  border-bottom: 1px solid var(--line-on-dark);
}

.accordion__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  width: 100%;
  padding: 0 0 22px;
  text-align: left;
}

.accordion__title {
  font-size: clamp(1.375rem, 1.1rem + 0.97vw, 1.9375rem);
  line-height: 1.26;
  color: var(--accent-gold);
  transition: color 0.2s var(--ease);
}
.accordion__header:hover .accordion__title {
  color: var(--cream);
}

.accordion__toggle {
  position: relative;
  width: 26px;
  height: 26px;
  flex-shrink: 0;
}
.accordion__toggle::before,
.accordion__toggle::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 1.5px;
  background: var(--accent-gold);
  transform: translateY(-50%);
  transition: transform 0.3s var(--ease), opacity 0.3s var(--ease);
}
.accordion__toggle::after {
  transform: translateY(-50%) rotate(90deg);
}
.accordion__header[aria-expanded="true"] .accordion__toggle::after {
  transform: translateY(-50%) rotate(0deg);
  opacity: 0;
}

.accordion__panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.45s var(--ease);
}
.accordion__panel > div {
  overflow: hidden;
}
/* Driven by data-open on the wrapper, not an adjacent-sibling selector: the
   header lives inside an <h3> for heading semantics, so it is not a sibling
   of the panel. */
.accordion[data-open="true"] .accordion__panel {
  grid-template-rows: 1fr;
}

.accordion__body {
  /* Bottom padding keeps the item's rule off the last line of content now that
     it sits below the panel rather than under the header. */
  padding: 2.125rem 0;
}

/* ---------- Services grid ---------- */

.services {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.75rem, 3.6vw, 3.25rem);
}

.service {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* Illustrated icons from the mockup, replacing the earlier line icons */
.service__icon {
  width: clamp(72px, 6vw, 92px);
  height: auto;
  margin-bottom: 4px;
}

.service__title {
  font-family: var(--font-body);
  font-size: clamp(1.0625rem, 0.99rem + 0.32vw, 1.25rem);
  font-weight: 700;
  line-height: 1.35;
  color: var(--cream);
}

.service__desc {
  font-size: var(--fs-sm);
  line-height: 1.65;
  color: var(--cream-muted);
}

/* ---------- What I don't do ---------- */

.dont {
  display: grid;
  grid-template-columns: minmax(0, 520px) minmax(0, 1fr);
  gap: clamp(2rem, 6vw, 6rem);
}

.dont__intro {
  font-size: clamp(1rem, 0.96rem + 0.21vw, 1.125rem);
  line-height: 1.72;
  color: var(--cream-muted);
}

.dont__list li {
  display: flex;
  align-items: center;
  gap: 14px;
  /* 13px per the mockup's 48px rows. Not --section-pad: that token is 80-160px
     and is only ever for section wrappers. */
  padding-block: 13px;
  border-bottom: 1px solid var(--line-on-dark);
  font-size: clamp(1rem, 0.96rem + 0.21vw, 1.125rem);
  color: var(--cream);
}

.dont__mark {
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  color: var(--ember);
}

/* ---------- Pricing ---------- */

.pricing__lead {
  margin-bottom: 1.75rem;
  font-size: var(--fs-lead);
  color: var(--cream-muted);
}

/* Inset so the selected card's glow and focus ring have somewhere to land —
   the accordion panel clips anything that escapes. */
.pricing {
  padding: 6px 22px 26px;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-items: center;
  gap: 24px;
}

/* Selectable. Sizing is driven by transform rather than padding/width so the
   three-column grid never reflows while a card grows or shrinks. */
.price-card {
  display: flex;
  flex-direction: column;
  padding: 36px;
  border: 1px solid var(--line-on-dark);
  border-radius: 14px;
  background: transparent;
  cursor: pointer;
  /* Selected tops out at scale(1) — its natural cell — and the others shrink
     below it. Scaling the selected card ABOVE 1 pushed it outside its grid
     cell, and both .section and the accordion panel wrapper clip (the wrapper
     needs overflow:hidden for the open/close animation), so the outer cards
     got sliced. Growing downward from 1 is the same relative change with
     nothing to clip. */
  transform: scale(0.94);
  transform-origin: center top;
  transition: transform 0.32s var(--ease), border-color 0.32s var(--ease),
    background-color 0.32s var(--ease), box-shadow 0.32s var(--ease);
}

/* Subtle grow + glow on the way to picking one */
.price-card:hover {
  transform: scale(0.97);
  border-color: rgba(232, 185, 106, 0.55);
  box-shadow: 0 0 0 1px rgba(232, 185, 106, 0.12),
    0 14px 40px -18px rgba(232, 185, 106, 0.5);
}

.price-card[aria-checked="true"] {
  transform: scale(1);
  padding: 40px;
  border: 2px solid var(--accent-gold);
  background: var(--surface-on-dark);
  box-shadow: 0 0 0 1px rgba(232, 185, 106, 0.18),
    0 22px 60px -24px rgba(232, 185, 106, 0.65);
}
/* Already at full size, so the hover lift is a nudge rather than a scale */
.price-card[aria-checked="true"]:hover {
  transform: scale(1) translateY(-3px);
}

.price-card:focus-visible {
  outline: 3px solid var(--accent-gold);
  outline-offset: 4px;
}



/* Icon sits above the name, as in the mockup, at each card's own size.
   Widths are the mockup's icon width over that card's content width:
   88/319, 161/311 and 110/319. */
.price-card__icon {
  width: 27.6%;
  /* Capped at the mockup's pixel size. The percentage is of the card's content
     width, so once the grid collapses to one column the cards get far wider
     than the mockup's 391px and the icons ballooned with them. */
  max-width: 88px;
  height: auto;
  flex-shrink: 0;
}
.price-card[data-plan="unlimited"] .price-card__icon {
  width: 51.8%;
  max-width: 161px;
}
.price-card[data-plan="double"] .price-card__icon {
  width: 34.5%;
  max-width: 110px;
}

.price-card__name {
  font-family: var(--font-display);
  /* Mockup: 30px on a 391px card */
  font-size: clamp(1.375rem, 1.1rem + 0.97vw, 1.875rem);
  line-height: 1.3;
  color: var(--cream);
}

.price-card__price {
  padding-top: 14px;
  font-size: clamp(1rem, 0.96rem + 0.21vw, 1.125rem);
  font-weight: 600;
  line-height: 1.5;
  color: var(--accent-gold);
}

.price-card__subhead {
  padding-top: 8px;
  font-size: 0.9375rem;
  line-height: 1.53;
  color: var(--cream-muted);
}

.price-card__points {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-top: 26px;
}

.price-card__points li {
  display: flex;
  gap: 12px;
  font-size: var(--fs-sm);
  line-height: 1.59;
  color: var(--cream);
}

.price-card__points li::before {
  content: "";
  width: 6px;
  height: 6px;
  margin-top: 11px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--accent-gold);
}

/* ==========================================================================
   Section 9 — Who I've worked for (carousel)
   ========================================================================== */

.work {
  padding-block: var(--section-pad);
}

.work__title {
  color: #fff;
}

.work__carousel {
  /* Every card is the same width and the track only ever translates. Animating
     flex-basis (a big active card, narrow "peek" neighbours) reflowed the whole
     track on each step, which is what made the section jump around. */
  --card-w: min(620px, 78vw);
  --card-gap: 28px;
  position: relative;
  margin-top: 2.5rem;
}

/* Vertical padding gives the hover scale and its shadow somewhere to go —
   without it the clip box cropped the top and bottom card edges. The negative
   margin keeps the row's layout height unchanged. */
/* The scroller itself: auto-drift, drag, arrows and a trackpad swipe all move
   scrollLeft, so they can never disagree. Vertical padding gives the hover
   scale and its shadow room inside the clip box. */
.work__viewport {
  /* pan-y hands vertical gestures back to the page. Without it the drag
     handler captured the pointer on a downward swipe and the page could not
     scroll past the carousel. */
  touch-action: pan-y;
  overflow-x: auto;
  overflow-y: hidden;
  padding-block: 30px;
  margin-block: -30px;
  scroll-behavior: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  cursor: grab;
  overscroll-behavior-x: contain;
}
.work__viewport::-webkit-scrollbar {
  display: none;
}
[data-dragging] .work__viewport {
  cursor: grabbing;
  user-select: none;
}
[data-dragging] .work-card {
  pointer-events: none;
}

.work__controls {
  display: flex;
  justify-content: center;
  gap: 1rem;
  margin-top: 1.75rem;
}

.work__track {
  display: flex;
  /* The row has to be wider than the viewport for it to scroll at all. */
  width: max-content;
  /* stretch, not center: all cards take the tallest card's height, so the
     track height is constant and nothing shifts vertically between slides. */
  align-items: stretch;
  gap: var(--card-gap);
}

.work-card {
  flex: 0 0 var(--card-w);
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line-on-light);
  border-radius: 14px;
  background: var(--surface-on-light);
  overflow: hidden;
  opacity: 0.38;
  transition: opacity 0.45s var(--ease), transform 0.3s var(--ease),
    box-shadow 0.3s var(--ease);
}
.work-card[data-active="true"] {
  opacity: 1;
}

/* Hovering lifts a card slightly out of the row */
.work-card:hover {
  opacity: 0.7;
  transform: scale(1.035);
  box-shadow: 0 18px 44px -22px rgba(0, 0, 0, 0.7);
}
.work-card[data-active="true"]:hover {
  opacity: 1;
  transform: scale(1.025);
}

@media (prefers-reduced-motion: reduce) {
  .work-card,
  .work-card:hover {
    transform: none;
  }
}

.work-card__image {
  width: 100%;
  /* height:auto is required — the width/height attributes are presentational
     hints, and a fixed height would cancel aspect-ratio. */
  height: auto;
  aspect-ratio: 620 / 330;
  object-fit: cover;
  background: #c9b49a;
}

.work-card__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 16px;
  padding: 26px;
}

.work-card__logo {
  height: 46px;
  width: auto;
  max-width: 240px;
  object-fit: contain;
  object-position: left center;
}

.work-card__meta {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.work-card__meta p {
  font-size: var(--fs-sm);
  line-height: 1.53;
  color: var(--ink-muted);
}

.work__controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
}

.work__arrow {
  position: absolute;
  top: 50%;
  z-index: 3;
  display: grid;
  place-items: center;
  width: 58px;
  height: 58px;
  flex-shrink: 0;
  color: var(--cream);
  border: 1.5px solid var(--ember);
  border-radius: 999px;
  background: rgba(85, 48, 48, 0.72);
  backdrop-filter: blur(4px);
  transform: translateY(-50%);
  transition: background-color 0.25s var(--ease), color 0.25s var(--ease),
    border-color 0.25s var(--ease), opacity 0.25s var(--ease);
}
.work__arrow:hover:not(:disabled) {
  color: var(--bg-light);
  background: var(--ember);
  border-color: var(--ember);
}
.work__arrow:disabled {
  opacity: 0.3;
  cursor: default;
}
.work__arrow--prev {
  left: calc(var(--gutter) * -1 + 1rem);
}
.work__arrow--next {
  right: calc(var(--gutter) * -1 + 1rem);
}
.work__arrow svg {
  width: 24px;
  height: 24px;
}

.work__dots {
  display: none;
}
.work__dots--unused {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-top: 2.25rem;
}
.work__dot {
  /* flex-shrink:0 — without it the dots squash into ovals when space is tight.
     padding/min-width:0 matter just as much: a flex item's automatic minimum
     size is its min-content width, and the browser's default button padding
     was widening the dots past their 10px into ovals. */
  flex: 0 0 auto;
  min-width: 0;
  padding: 0;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  /* Light: this section's backdrop is the dark stone from the mockup */
  background: rgba(242, 231, 216, 0.3);
  /* Scale rather than width/height so the active dot cannot nudge the row */
  transition: background-color 0.25s var(--ease), transform 0.25s var(--ease);
}
.work__dot[aria-current="true"] {
  background: var(--cream);
  transform: scale(1.3);
}


/* ==========================================================================
   Section 10 — Collaborators
   ========================================================================== */

.collab {
  padding-block: var(--section-pad);
}

.collab__list {
  padding-top: 2.5rem;
}

.collab .accordion {
  padding-top: 0;
}
.collab .accordion__header {
  padding: 30px 0 34px;
}

.collab .accordion__title {
  font-family: var(--font-body);
  font-size: clamp(1.25rem, 1.05rem + 0.69vw, 1.8125rem);
  line-height: 1.31;
}

.collab .accordion__body {
  padding: 0 0 34px;
}

.collab__desc {
  max-width: 820px;
  font-size: clamp(1rem, 0.96rem + 0.21vw, 1.125rem);
  line-height: 1.72;
  color: var(--cream-muted);
}

/* ==========================================================================
   Section 11 — CTA
   ========================================================================== */

.cta {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  min-height: 1000px; /* mockup frame height */
  padding-block: var(--section-pad);
}

/* Mockup: 898x838 box at (-72, 81) in a 1440x1000 frame, contain-fitted */
.cta__art {
  left: -5%;
  width: 62.36%;
}

.cta__copy {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 41.3%; /* 504/1220 */
}

/* The closing heading gets a shine instead of the standard slide-in reveal:
   a bright band travelling left to right across the letters. The base text
   keeps its own colour and the sweep rides on a pseudo-element above it, so
   if the animation never runs the heading is simply the heading. */
/* Chiselled relief: a light edge below each glyph and a dark one above, which
   is how carved-in-stone lettering catches overhead light. Keeps the ink
   colour (7.9:1 on this backdrop) rather than switching to gold, which drops
   to 2.05:1 at its mid tone. */
.cta__title {
  position: relative;
  color: #4d2f24;
  isolation: isolate;
  text-shadow: 0 1px 0 rgba(255, 250, 236, 0.95),
    0 -1px 1px rgba(48, 20, 14, 0.6), 0 3px 10px rgba(48, 20, 14, 0.22);
}
.cta__title::after {
  content: attr(data-shine);
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* text-shadow is inherited, so without this the clone repaints the chiselled
     relief on top of the sweep — its opaque cream highlight sits directly over
     the travelling band and smothers it. The relief belongs to the base text
     only; this layer carries nothing but the shine. */
  text-shadow: none;
  background: linear-gradient(
    100deg,
    rgba(255, 244, 214, 0) 38%,
    rgba(255, 243, 208, 0.65) 46%,
    #fffdf4 50%,
    rgba(255, 243, 208, 0.65) 54%,
    rgba(255, 244, 214, 0) 62%
  );
  background-size: 250% 100%;
  background-position: 150% 0;
  background-repeat: no-repeat;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  opacity: 0;
}
.cta__title[data-shine-run]::after {
  animation: cta-shine 3.4s cubic-bezier(0.3, 0.6, 0.2, 1) 0.25s both;
}

@keyframes cta-shine {
  0% { background-position: 150% 0; opacity: 0; }
  12% { opacity: 1; }
  88% { opacity: 1; }
  100% { background-position: -50% 0; opacity: 0; }
}

/* Back to the original, softer bloom — the problem was never its strength,
   it was that a single uniform blob fading in and out reads as static. Two
   lobes at different periods, each drifting as well as breathing, give the
   light internal movement: the local change at a gradient's edge is far more
   visible than a whole shape changing opacity together. Same lesson as the
   hero rays. */
.cta__glow,
.cta__glow::after {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
}

.cta__glow {
  left: 50%;
  top: 58%;
  width: 78%;
  aspect-ratio: 1 / 1;
  z-index: -1;
  transform: translate(-50%, -50%) scale(0.72);
  opacity: 0;
  background: radial-gradient(
    circle at 50% 50%,
    rgba(255, 253, 247, 0.75),
    rgba(255, 250, 240, 0.32) 38%,
    rgba(255, 248, 236, 0) 68%
  );
  transition: opacity 1.4s var(--ease), transform 1.6s var(--ease);
}

/* The second lobe: wider, weaker, and deliberately off-centre so the two
   overlap differently through the cycle. */
.cta__glow::after {
  content: "";
  left: 46%;
  top: 54%;
  width: 118%;
  aspect-ratio: 1 / 1;
  transform: translate(-50%, -50%);
  background: radial-gradient(
    circle at 50% 50%,
    rgba(255, 255, 252, 0.5),
    rgba(255, 252, 245, 0.2) 42%,
    rgba(255, 250, 240, 0) 70%
  );
}

.cta__art[data-glow] .cta__glow {
  opacity: 1;
  animation: glow-core 3.4s ease-in-out 0.9s infinite;
}
.cta__art[data-glow] .cta__glow::after {
  animation: glow-halo 4.9s ease-in-out infinite;
}

@keyframes glow-core {
  0%,
  100% {
    transform: translate(-50%, -50%) scale(0.86) translate(-3%, 2%);
    opacity: 0.45;
  }
  50% {
    transform: translate(-50%, -50%) scale(1.18) translate(3%, -2%);
    opacity: 1;
  }
}

@keyframes glow-halo {
  0%,
  100% {
    transform: translate(-50%, -50%) scale(1.14) translate(2.5%, 3%);
    opacity: 0.38;
  }
  50% {
    transform: translate(-50%, -50%) scale(0.84) translate(-2.5%, -3%);
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cta__title[data-shine-run]::after {
    animation: none;
  }
  .cta__glow {
    transition: none;
  }
  .cta__art[data-glow] .cta__glow,
  .cta__art[data-glow] .cta__glow::after {
    animation: none;
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
  }
}

.cta__body {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding-top: 1.5rem;
}
.cta__body p {
  font-size: clamp(1.0625rem, 1rem + 0.28vw, 1.1875rem);
  line-height: 1.68;
  color: var(--ink-muted);
}

.cta__prompt {
  font-weight: 600;
  color: var(--ink) !important;
}

.cta__fields {
  display: flex;
  flex-direction: column;
  gap: 26px;
  padding-top: 2.125rem;
}

.cta__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.25rem;
  padding-top: 2.5rem;
}

.button {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 22px 42px;
  border-radius: 999px;
  background: var(--bg-dark);
  color: var(--bg-light);
  font-size: clamp(1.0625rem, 1rem + 0.28vw, 1.1875rem);
  font-weight: 600;
  line-height: 1;
  transition: background-color 0.25s var(--ease), transform 0.25s var(--ease);
}
.button:hover {
  background: #3f2323;
  transform: translateY(-2px);
}
.button:active {
  transform: translateY(0);
}
.button[disabled] {
  opacity: 0.6;
  cursor: progress;
  transform: none;
}
.button svg {
  width: 20px;
  height: 20px;
  transition: transform 0.25s var(--ease);
}
.button:hover svg {
  transform: translateX(4px);
}

.form-status {
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--ink-muted);
}
.form-status[data-state="error"] {
  color: var(--accent-rust);
  font-weight: 600;
}

/* ==========================================================================
   Footer
   ========================================================================== */

.footer {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: clamp(2rem, 5vw, 5rem);
  padding-block: clamp(3rem, 2.5rem + 2.5vw, 5.625rem);
}

.footer__contact {
  display: flex;
  flex-direction: column;
  gap: 26px;
  max-width: 520px;
}

.footer__logo {
  width: 176px;
  height: auto;
}

.footer__label {
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.4px;
  color: var(--cream-muted);
}

.footer__details {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.footer__details a {
  font-size: clamp(1.0625rem, 1.01rem + 0.21vw, 1.1875rem);
  color: var(--cream);
  transition: color 0.2s var(--ease);
}
.footer__details a:hover {
  color: var(--accent-gold);
}

.footer__links {
  display: flex;
  gap: clamp(2rem, 5vw, 4.5rem);
}

.footer__column {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.footer__column a {
  font-size: var(--fs-sm);
  color: var(--cream-muted);
  transition: color 0.2s var(--ease);
}
.footer__column a:hover {
  color: var(--cream);
}

/* ==========================================================================
   Osmo draw-random-underline (hover)
   ========================================================================== */





/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 1100px) {
  .services,
  .pricing {
    grid-template-columns: minmax(0, 1fr);
  }
  .price-card--featured {
    order: -1;
  }
  .dont {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* The art sections hold two columns only while there is room for both. Below
   this the illustration is stacked under the copy. */
@media (max-width: 1024px) {
  .who,
  .story,
  .cta,
  .kenny {
    display: block;
    min-height: 0;
  }
  .who__copy,
  .story__copy,
  .cta__copy,
  .kenny__copy {
    max-width: none;
  }

  /* Copy first, artwork after it — the art is earlier in the DOM so it can be
     positioned against the section on desktop. */
  .section--with-art {
    display: flex;
    flex-direction: column;
  }
  .section-art {
    order: 2;
    position: static;
    left: auto;
    top: auto;
    height: auto;
    /* Fits fully now rather than bleeding past the section: the art was being
       cropped on the narrowest screens. Size is preserved by serving
       edge-trimmed variants here (the full-size files carry up to 30%
       transparent margin), so the visible artwork is no smaller. */
    width: min(100%, 580px);
    /* The glow is positioned against this box, so it must stay a containing
       block once the art drops into normal flow. */
    position: relative;
    max-width: none;
    margin: 0 auto clamp(1.25rem, 3vw, 2rem);
    transform: none;
  }
  /* All four pieces of artwork sit flush with the copy's left edge once
     stacked, rather than floating centred. */
  .who__art,
  .story__art,
  .kenny__art,
  .cta__art {
    margin-left: var(--gutter);
    margin-right: 0;
  }

  /* Half the previous gap between the copy and the artwork below it. The
     sections carry their vertical rhythm as padding on .section__inner, whose
     bottom edge is what separates the two once they stack. */
  .who,
  .story,
  .cta {
    padding-bottom: calc(var(--section-pad) / 2);
  }
  .kenny__copy {
    padding-bottom: clamp(1.5rem, 4vh, 2rem);
  }

  /* Section-level links keep flowing last, below the artwork */
  .section > .keep-going {
    order: 3;
    margin-bottom: clamp(2rem, 5vw, 3rem);
  }

  .section-art img {
    height: auto;
  }
  /* Kenny's portrait is cover-cropped on desktop; let it size naturally here */
  .kenny__art img {
    height: auto;
    object-fit: contain;
  }
}

@media (max-width: 900px) {
  :root {
    --menu-width: 80vw;
  }

  .brings,
  .curious {
    grid-template-columns: minmax(0, 1fr);
  }

  /* The hero stays one composition at every width — copy over artwork, never
     split into a text block above a letterboxed strip. The stage keeps its
     cover sizing, so a narrow viewport crops in on the backpacker instead of
     rearranging the layout. Nothing to override here; it is intentional. */


  .curious__video {
    max-width: 320px;
    margin-inline: auto;
  }

  /* Stacked: let the portrait sit in normal flow instead of bleeding */
  .kenny,
  .kenny__art {
    min-height: 0;
  }
  .kenny__art {
    position: static;
    max-width: 420px;
    /* Flush with the copy, like the forest and headstone. This rule follows the
       shared one above, so it has to repeat the alignment or it would win with
       `auto` and re-centre the portrait. */
    margin-inline: var(--gutter) 0;
  }
  .kenny__art img {
    position: static;
    width: 100%;
    height: auto;
    transform: none;
  }

  /* Arrows would land on top of the card at this width, so they join the
     dots in a control row underneath instead. */
  .work__arrow {
    position: static;
    transform: none;
  }
  .work__dots {
  display: none;
}
.work__dots--unused {
    margin-top: 0;
  }

  .footer {
    flex-direction: column;
  }
}

@media (max-width: 560px) {
  .underlay-nav__inner {
    padding: 5rem 1.75rem 1.75rem;
  }

  .chip {
    padding: 12px 18px;
  }

  .persona-other {
    gap: 12px;
  }

  .hunch-other__input-wrap {
    padding-left: 0;
  }

  .price-card,
  .price-card--featured {
    padding: 28px;
  }

  .work-card__body {
    padding: 22px;
  }

  .button {
    width: 100%;
    justify-content: center;
    padding: 20px 28px;
  }

  .work__arrow {
    width: 46px;
    height: 46px;
  }
  .work__arrow svg {
    width: 20px;
    height: 20px;
  }
}

/* ==========================================================================
   Motion preferences
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .work__track {
    transition: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .chip,
  .chip:hover,
  .chip:active {
    transform: none;
  }
}

/* Phones fetch smaller sources for every large background. Decoding ~4.8MB of
   artwork while scrolling was the main remaining cost on a real device — it
   does not show up in frame timing on a fast desktop. */
@media (max-width: 900px) {
  .hero__layer--sky { background-image: url("../assets/sky-sm.webp"); }
  .hero__layer--city { background-image: url("../assets/city-sm.webp"); }
  .hero__layer--guy { background-image: url("../assets/backpack-guy-sm.webp"); }
  .hero__layer--airplane { background-image: url("../assets/airplane-sm.webp"); }
  .section--paper-light { background-image: url("../assets/bg-light-paper-sm.webp"); }
  .section--paper-dark { background-image: url("../assets/bg-dark-paper-sm.webp"); }
  .hero__rays {
    -webkit-mask-image: url("../assets/sky-sm.webp");
    mask-image: url("../assets/sky-sm.webp");
  }
}

/* Moved off inline styles so these can be swapped per breakpoint too */
[data-bg="cta"] { background-image: url("../assets/bg-cta.webp"); }
[data-bg="kenny"] { background-image: url("../assets/bg-kenny.webp"); }
@media (max-width: 900px) {
  [data-bg="cta"] { background-image: url("../assets/bg-cta-sm.webp"); }
  [data-bg="kenny"] { background-image: url("../assets/bg-kenny-sm.webp"); }
}
[data-bg="stone"] { background-image: url("../assets/bg-stone.webp"); }
[data-bg="work"] { background-image: url("../assets/bg-work.webp"); }
@media (max-width: 900px) {
  [data-bg="stone"] { background-image: url("../assets/bg-stone-sm.webp"); }
  [data-bg="work"] { background-image: url("../assets/bg-work-sm.webp"); }
}

/* The subhead patch is a background on the hero copy */
@media (max-width: 900px) {
  .hero__subhead::before { background-image: url("../assets/subhead-patch-sm.webp"); }
}
