/* ==========================================================================
   Base + Components — built only from tokens.css
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  /* Always reserve the scrollbar's space, so locking scroll (sheet) never
     changes the page width — and 100vw stays the same in both states. */
  scrollbar-gutter: stable;
}
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; } /* in-page links, e.g. menu → #services */
}

/* Lenis smooth scroll (js/smooth-scroll.js) */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: hidden; }
/* While the page glides, elements slide under a resting cursor; without this
   every button / row / link passing by would flash its hover state.
   Only during Lenis' own glides (.lenis-smooth: wheel, in-page links), not
   native scrolling (.lenis-scrolling also covers touch, and the clamp when
   the page gets shorter, e.g. an accordion closing near the end): there it
   would swallow taps and drop the hover of the row under the cursor. */
.lenis.lenis-smooth body { pointer-events: none; }

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-body);
  letter-spacing: var(--tracking-tight);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, p, ul, blockquote, figure { margin: 0; }
h1, h2, h3, h4 { font-size: inherit; font-weight: inherit; } /* weight comes from the type classes */
ul { padding: 0; list-style: none; }
img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; letter-spacing: inherit; }

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

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

/* ---- Type styles --------------------------------------------------------- */
.t-display-xl,
.t-display-l,
.t-display-m {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
  text-transform: uppercase;
}
.t-display-xl { font-size: var(--text-display-xl); }
.t-display-l  { font-size: var(--text-display-l); }
.t-display-m  { font-size: var(--text-display-m); }

.t-lead {
  font-family: var(--font-body);
  font-size: var(--text-lead);
  line-height: var(--leading-lead);
}

.t-body {
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-body);
}
.t-label {
  font-family: var(--font-mono);
  font-size: var(--text-base);
  line-height: var(--leading-mono);
  text-transform: uppercase;
}
/* Section eyebrows: [ABOUT]. Brackets are decoration, hidden from screen readers. */
.t-label--bracket::before { content: "["; content: "[" / ""; }
.t-label--bracket::after  { content: "]"; content: "]" / ""; }

/* ---- Button --------------------------------------------------------------
   Compact rectangle with an accent outline. Hover ("flip", after Locomotive's
   stenger-bike.de): the accent rises from the bottom as a rounded blob that
   settles into the full rectangle (::before clip-path), while the label rolls
   up and away and a copy in the hover colour rolls in from below
   (.button__label + its text-shadow). Icons roll the same way; the slider
   arrows roll along their direction.
   .button--filled starts filled and drains upwards on hover.               */
.button {
  --roll: 2.5em;                           /* label ↔ copy distance: past the edge even at 44px */
  --roll-color: var(--color-text-inverse); /* the copy: the hover text colour */
  --flip: 450ms var(--ease-reveal);
  --pad-x: calc(14 * var(--px));
  --line: var(--color-accent);             /* the outline */
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  min-height: calc(30 * var(--px)); /* fixed height, shared with .button--icon */
  padding: 0 var(--pad-x);
  /* The outline is an inset shadow, not a border: so the fill (::before)
     spans the whole box and shares its edge. Fill inside a border left a
     hairline of the ground at the seam (and at the corners). */
  border: 0;
  border-radius: var(--radius);
  box-shadow: inset 0 0 0 var(--border-width) var(--line);
  background: transparent;
  color: var(--color-accent);
  font-family: var(--font-mono);
  font-size: var(--text-base);
  line-height: var(--leading-mono);
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--duration-hover) var(--ease-out), scale var(--duration-fast) var(--ease-out);
}
/* Press: a small give while held, on touch too. */
.button:not(:disabled, [aria-disabled="true"]):active { scale: 0.92; }
.button::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--color-accent);
  clip-path: inset(100% calc(12 * var(--px)) 0 calc(12 * var(--px)) round calc(20 * var(--px)) calc(20 * var(--px)) 0 0);
  transition: clip-path var(--flip);
}
.button__label {
  display: inline-block;
  text-shadow: 0 var(--roll) 0 var(--roll-color);
  transition: translate var(--flip);
}
.button .icon {
  filter: drop-shadow(0 var(--roll) 0 var(--roll-color));
  transition: translate var(--flip);
}
[data-slider-next] .icon { filter: drop-shadow(calc(-1 * var(--roll)) 0 0 var(--roll-color)); }
[data-slider-prev] .icon { filter: drop-shadow(var(--roll) 0 0 var(--roll-color)); }
/* Optional wave (.button__wave holding 👋, footer): it rolls in with the
   label copy and waves beside it. The button keeps its room (--wave-slot,
   half each side at rest, so the label stays centred); the copy lands
   shifted left to make space. Only where it can hover: touch never sees it. */
@media (hover: hover) {
  .button:has(.button__wave) {
    --wave-slot: 2.2em;
    padding-inline: calc(var(--pad-x) + var(--wave-slot) / 2);
  }
  .button:has(.button__wave) .button__label { text-shadow: calc(-0.5 * var(--wave-slot)) var(--roll) 0 var(--roll-color); }
}
.button__wave {
  position: absolute;
  top: 50%;
  right: var(--pad-x);
  font-size: 1.3em;
  line-height: 1;
  translate: 0 calc(-50% + var(--roll));
  transform-origin: 70% 90%; /* the wrist */
  transition: translate var(--flip);
}

.button--filled { --roll-color: var(--color-accent); color: var(--color-text-inverse); }
.button--filled::before { clip-path: inset(0 round var(--radius)); }
/* Hover effects only where a real pointer hovers: on touch screens :hover
   sticks after a tap, and buttons would stay filled. */
@media (hover: hover) {
  .button:not(:disabled, [aria-disabled="true"]):hover { color: var(--color-text-inverse); }
  .button:not(:disabled, [aria-disabled="true"]):hover::before { clip-path: inset(0 round var(--radius)); }
  .button:not(:disabled, [aria-disabled="true"]):hover :is(.button__label, .icon) { translate: 0 calc(-1 * var(--roll)); }
  .button:not(:disabled, [aria-disabled="true"]):hover .button__wave { translate: 0 calc(-50% - 0.05em); } /* optical centre: between the hand's own and that with its motion lines */
  .button[data-slider-next]:not(:disabled, [aria-disabled="true"]):hover .icon { translate: var(--roll) 0; }
  .button[data-slider-prev]:not(:disabled, [aria-disabled="true"]):hover .icon { translate: calc(-1 * var(--roll)) 0; }
  .button--filled:not(:disabled, [aria-disabled="true"]):hover { color: var(--color-accent); }
  .button--filled:not(:disabled, [aria-disabled="true"]):hover::before {
    clip-path: inset(0 calc(12 * var(--px)) 100% calc(12 * var(--px)) round 0 0 calc(20 * var(--px)) calc(20 * var(--px)));
  }
}
.button:disabled,
.button[aria-disabled="true"] { opacity: 0.35; cursor: default; }

@media (prefers-reduced-motion: reduce) {
  .button, .button::before, .button__label, .button .icon, .button__wave { transition: none; }
}
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .button:not(:disabled, [aria-disabled="true"]):hover .button__wave { animation: wave 1.8s var(--ease-in-out) 300ms infinite; }
}
@keyframes wave {
  0%, 60%, 100% { rotate: 0deg; }
  10%, 30% { rotate: 14deg; }
  20% { rotate: -8deg; }
  40% { rotate: -4deg; }
  50% { rotate: 10deg; }
}
.button__icon { flex: none; width: calc(32 * var(--px)); height: calc(8 * var(--px)); }
/* Icon button (same height as .button) holding an SVG icon, so the
   glyph sits in the exact optical centre — font arrows sit low and narrow. */
.button--icon {
  width: calc(36 * var(--px));
  height: calc(30 * var(--px));
  padding: 0;
}
.icon {
  display: block;
  flex: none;
  width: calc(14 * var(--px));
  height: calc(14 * var(--px));
}

/* ---- Tag (small rectangle, like a mini button) ---------------------------- */
.tag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: calc(2 * var(--px)) var(--space-2);
  border: var(--border-width) solid var(--color-border-accent);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: var(--text-base);
  line-height: var(--leading-mono);
  text-transform: uppercase;
  white-space: nowrap;
}
.tag--outline { color: var(--color-accent); background: transparent; }
.tag--filled  { color: var(--color-text-inverse); background: var(--color-accent); }
.tag--neutral { color: var(--color-text); border-color: var(--color-border); background: transparent; }

/* ---- Testimonial card ---------------------------------------------------- */
.testimonial {
  display: flex;
  flex-direction: column;
  min-height: calc(319 * var(--px));
  padding: var(--space-8) var(--space-6) var(--space-5) var(--space-8);
  background: var(--color-surface);
}
.testimonial__mark {
  display: block;
  height: calc(25 * var(--px));
  color: var(--color-accent);
  font-family: var(--font-body);
  font-size: var(--text-quote-mark);
  line-height: 1;
}
.testimonial__quote { max-width: calc(592 * var(--px)); }
.testimonial__author {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1-5);
  margin-top: auto;
  padding-top: var(--space-6);
  align-self: flex-end;
}

/* ---- Service card -------------------------------------------------------- */
.service-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, calc(340 * var(--px)));
  grid-template-rows: auto 1fr;
  gap: var(--space-12);
  min-height: calc(319 * var(--px));
  padding: var(--space-8);
  background: var(--color-surface);
}
.service-card__meta {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.service-card__title,
.service-card__text { align-self: end; }
.service-card__title { text-wrap: balance; }
/* No lone word on the last line: `pretty` where supported (keeps the lines
   full, only fixes the end), `balance` as the fallback. */
.service-card__text { text-wrap: balance; text-wrap: pretty; }

@media (max-width: 700px) {
  .service-card {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto 1fr auto;
    gap: var(--space-6);
    min-height: calc(410 * var(--px));
  }
}

/* ---- Process list (native <details name>, one open at a time) ------------
   Full-width rows between hairlines: number pill · centred title · +/× pill. */
.process { border-bottom: var(--border-width) solid rgb(255 255 255 / 0.12); }
.process__item { border-top: var(--border-width) solid rgb(255 255 255 / 0.12); }
.process__summary {
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  list-style: none;
  cursor: pointer;
}
/* Hover: the whole row fills with the accent, rising from the bottom with a
   straight edge; number, title and +/× turn dark on it. */
.process__summary::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--color-accent);
  clip-path: inset(100% 0 0 0);
  transition: clip-path 450ms var(--ease-reveal);
}
.process__summary::-webkit-details-marker { display: none; }
/* Number and +/× control: equal squares at both ends of the row. */
.process__summary .tag,
.process__icon {
  box-sizing: border-box;
  width: calc(32 * var(--px));
  height: calc(32 * var(--px));
  padding: 0;
}
.process__summary .tag { justify-self: start; }
.process .tag { border-color: var(--color-border-accent); } /* numbers and skill/hobby tags */
.process__summary .tag {
  transition: color var(--duration-hover) var(--ease-out), border-color var(--duration-hover) var(--ease-out);
}
.process__title { text-align: center; transition: color var(--duration-hover) var(--ease-out); }

/* Behaves like a button, with the same flip fill: the accent rises from the
   bottom as a rounded blob (::before) on hover and while open; the + (::after)
   turns into × when open. */
.process__icon {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  justify-self: end;
  display: grid;
  place-items: center;
  border: var(--border-width) solid var(--color-accent);
  border-radius: var(--radius);
  color: var(--color-text); /* white glyph, accent outline — like the buttons */
  transition: color var(--duration-hover) var(--ease-out), border-color var(--duration-hover) var(--ease-out);
}
.process__icon::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--color-accent);
  clip-path: inset(100% calc(6 * var(--px)) 0 calc(6 * var(--px)) round calc(12 * var(--px)) calc(12 * var(--px)) 0 0);
  transition: clip-path 450ms var(--ease-reveal), background-color var(--duration-hover) var(--ease-out);
}
.process__icon::after {
  content: "";
  width: calc(10 * var(--px));
  height: calc(10 * var(--px));
  background:
    linear-gradient(currentColor, currentColor) center / 100% var(--border-width) no-repeat,
    linear-gradient(currentColor, currentColor) center / var(--border-width) 100% no-repeat;
  transition: rotate var(--duration-tilt) var(--ease-out);
}
.process__item[open]:not(.is-closing) .process__icon { color: var(--color-text-inverse); }
.process__item[open]:not(.is-closing) .process__icon::before { clip-path: inset(0 round var(--radius)); }
.process__item[open]:not(.is-closing) .process__icon::after { rotate: 45deg; }
/* While rows slide (.is-animating, js/accordion.js) only the clicked row keeps
   its hover; the others would flash as they pass under a resting cursor. */
@media (hover: hover) {
  :is(.process:not(.is-animating) .process__item, .process__item.is-current) > .process__summary:hover::before {
    clip-path: inset(0);
  }
  :is(.process:not(.is-animating) .process__item, .process__item.is-current) > .process__summary:hover :is(.process__title, .tag, .process__icon) {
    color: var(--color-text-inverse);
    border-color: var(--color-text-inverse);
  }
  /* Open row on hover: the control's fill turns dark, the × accent. */
  :is(.process:not(.is-animating) .process__item, .process__item.is-current)[open]:not(.is-closing) > .process__summary:hover .process__icon {
    color: var(--color-accent);
  }
  :is(.process:not(.is-animating) .process__item, .process__item.is-current)[open]:not(.is-closing) > .process__summary:hover .process__icon::before {
    background-color: var(--color-text-inverse);
  }
}

/* A row of tags, e.g. skills inside a process panel. */
.tag-list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2);
}
.process__panel:has(.tag-list) { max-width: calc(560 * var(--px)); }

.process__panel {
  max-width: calc(440 * var(--px));
  margin-inline: auto;
  padding: var(--space-2) var(--space-4) var(--space-8);
  text-align: center;
}
.process__panel > p { text-align: left; } /* running text reads better ragged-right; tag rows stay centred */
.process__panel > p + p { margin-top: var(--space-4); }

/* Open/close motion lives in js/accordion.js. */
@media (prefers-reduced-motion: reduce) {
  .process__summary::before,
  .process__icon::before,
  .process__icon::after { transition: none; }
}

/* ---- Slider (js/slider.js) ----------------------------------------------- */
.slider { position: relative; }
.slider__track {
  position: relative;
  display: flex;
  gap: var(--space-5);
  padding-inline: var(--page-gutter);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--page-gutter);
  scrollbar-width: none;
}
.slider__track::-webkit-scrollbar { display: none; }
.slider__track > * { flex: none; scroll-snap-align: start; }
.slider__track:focus-visible { outline-offset: -2px; }
.slider__track.is-dragging { scroll-snap-type: none; cursor: grabbing; user-select: none; }
@media (hover: hover) and (pointer: fine) {
  .slider__track { cursor: grab; }
}
.slider__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}
.slider__controls {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.slider__count { margin-right: var(--space-4); }

/* ---- Feature item -------------------------------------------------------- */
.feature {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-width: calc(216 * var(--px));
}

/* ---- ASCII field (js/ascii-field.js) ------------------------------------- */
.has-ascii-field {
  position: relative;
  isolation: isolate;
}
.ascii-field {
  position: absolute;
  inset: 0;
  z-index: -1;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
  font-family: var(--font-mono);
  font-size: var(--text-base);
}

/* ---- Sheet (js/sheet.js) -------------------------------------------------
   3D side sheet: flies in from the right with a slight tilt out of depth,
   leaves with a softer tilt. States: hidden → .is-open → .is-closing → hidden */
html.has-sheet { overflow: hidden; }

.sheet {
  position: fixed;
  inset: 0;
  z-index: 20;
  perspective: calc(1200 * var(--px));
}
.sheet[hidden] { display: none; }
.sheet.is-closing { pointer-events: none; }

.sheet__backdrop {
  position: absolute;
  inset: 0;
  background: rgb(0 0 0 / 0.6);
  opacity: 0;
}

.sheet__panel {
  position: absolute;
  top: var(--page-gutter);
  right: var(--page-gutter);
  bottom: var(--page-gutter);
  display: flex;
  flex-direction: column;
  width: min(calc(560 * var(--px)), calc(100% - 2 * var(--page-gutter)));
  padding: var(--space-8);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--color-surface-sunken);
  translate: 120% 0 calc(100 * var(--px));
  rotate: y -35deg;
}
.sheet__panel:focus { outline: none; }

.sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}
.sheet__body { flex: 1; display: flex; flex-direction: column; justify-content: center; padding-block: var(--space-12); }
.sheet__foot { display: flex; justify-content: space-between; gap: var(--space-4); }

/* Items reveal one after another once the panel has landed (set --i per item). */
.sheet__item { opacity: 0; translate: 0 var(--reveal-distance); }

.sheet.is-open .sheet__backdrop {
  opacity: 1;
  transition: opacity var(--duration-fade) var(--ease-out);
}
.sheet.is-open .sheet__panel {
  translate: 0 0 0;
  rotate: y 0deg;
  transition:
    translate var(--duration-sheet-in) var(--ease-reveal) 50ms,
    rotate var(--duration-tilt) var(--ease-reveal) 50ms;
}
.sheet.is-open .sheet__item {
  opacity: 1;
  translate: 0 0;
  transition:
    opacity var(--duration-tilt) var(--ease-out),
    translate var(--duration-reveal) var(--ease-reveal);
  transition-delay: calc(250ms + var(--i, 0) * var(--stagger-items));
}

.sheet.is-closing .sheet__backdrop {
  opacity: 0;
  transition: opacity var(--duration-fade) var(--ease-out) 200ms;
}
.sheet.is-closing .sheet__panel {
  translate: 120% 0 0;
  rotate: y -10deg;
  transition:
    translate var(--duration-sheet-out) var(--ease-in-out),
    rotate var(--duration-tilt) var(--ease-in-out) 100ms;
}
.sheet.is-closing .sheet__item { opacity: 1; translate: 0 0; }

@media (prefers-reduced-motion: reduce) {
  .sheet .sheet__backdrop,
  .sheet .sheet__panel,
  .sheet .sheet__item { transition: none !important; }
}

.sheet__head > :last-child { margin-left: auto; } /* Close sits right, with or without a label */

/* Menu content inside a sheet: links turn accent on hover. */
.menu-links { display: flex; flex-direction: column; gap: var(--space-2); }
.menu-link {
  display: inline-flex;
  align-items: center;
  transition: color var(--duration-hover) var(--ease-out);
}
@media (hover: hover) {
  .menu-link:not(.menu-link--soon):hover { color: var(--color-accent); }
}
@media (max-width: 700px) {
  /* Rows sit flush; the links' own touch padding keeps them apart. */
  .menu-links { gap: 0; }
  .menu-link .t-display-l { font-size: 44px; } /* display-l bottoms out at 34px */
  .menu-link--soon > .tag { font-size: 12px; }
}

/* Not-yet-live entries: dimmed, not a link, with a small accent "coming soon" note. */
.menu-link--soon { cursor: default; }
.menu-link--soon > :not(.tag) { opacity: 0.45; }
.menu-link--soon > .tag { margin-left: var(--space-5); }

/* ---- Intro (js/intro.js) -------------------------------------------------
   html.is-intro      → start poses (set in <head> before first paint)
   html.is-intro-play → everything transitions home, each with --intro-delay
   Both classes are removed when the intro is over.                          */
.intro-word {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  padding-block: 0.06em;    /* room for accents and the period, */
  margin-block: -0.06em;    /* without changing the line box     */
}
.intro-word__inner { display: inline-block; }
/* Letter wave (TextFX.splitLetters, js/letter-scrub.js) */
.wave-word { white-space: nowrap; }
.wave-letter { display: inline-block; } /* scale + rotate need a box */

/* One motion language for the whole page (intro, scroll reveals, menu):
   words rise out of a mask, labels scramble, everything else rises by
   --reveal-distance, all over --duration-reveal with --ease-out.
   Only the slider cards deal in sideways, along their scroll direction. */
.is-intro [data-intro="words"]:not(.is-split) { opacity: 0; }
.is-intro [data-intro="scramble"] { opacity: 0; }
.is-intro [data-intro="rise"] { opacity: 0; translate: 0 var(--reveal-distance); }
.is-intro [data-intro="deal"] > * { opacity: 0; translate: calc(4 * var(--reveal-distance)) 0; }
/* Snap points follow transformed children: without this a scroll-snap track
   would scroll along with the shifted start pose and jump back afterwards. */
.is-intro [data-intro="deal"] { scroll-snap-type: none; }
.is-intro .intro-word__inner { translate: 0 110%; }
.is-intro .ascii-field { opacity: 0; }

/* The ASCII field simply fades in, slower than the rest, from the very start. */
.is-intro-play .ascii-field {
  opacity: 1;
  transition: opacity calc(var(--duration-reveal) * 2) var(--ease-out);
}

.is-intro-play [data-intro="rise"],
.is-intro-play [data-intro="deal"] > *,
.is-intro-play .intro-word__inner {
  opacity: 1;
  translate: 0 0;
  transition:
    opacity var(--duration-reveal) var(--ease-out),
    translate var(--duration-reveal) var(--ease-reveal);
  transition-delay: var(--intro-delay, 0ms);
}

/* ---- Reveal on scroll (js/reveal.js) -------------------------------------
   html.js-reveal → start poses until an element gets .is-revealed; the
   script animates from these poses to the rest pose (Web Animations), all
   but words: they transition while .is-revealing lives (rule below).       */
.js-reveal [data-reveal="scramble"]:not(.is-revealed) { opacity: 0; }
.js-reveal [data-reveal="rise"]:not(.is-revealed) { opacity: 0; translate: 0 var(--reveal-distance); }
.js-reveal [data-reveal="stagger"]:not(.is-revealed) > * { opacity: 0; translate: 0 var(--reveal-distance); }
.js-reveal [data-reveal="words"]:not(.is-split) { opacity: 0; }
.js-reveal [data-reveal="words"]:not(.is-revealed) .intro-word__inner { translate: 0 110%; }
.js-reveal [data-reveal="clip"]:not(.is-revealed) { clip-path: inset(100% 0 0 0); }
.js-reveal [data-reveal="clip"]:not(.is-revealed) img { scale: 1.4; }

[data-reveal="words"].is-revealing .intro-word__inner {
  transition: translate var(--duration-reveal) var(--ease-reveal);
  transition-delay: var(--reveal-delay, 0ms);
}

/* clip with data-reveal-wipe lays this accent panel over the media while the
   entrance plays; the media needs position: relative (and overflow: hidden,
   like .about__media). */
.reveal-wipe {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--color-accent);
  pointer-events: none;
}

/* ---- CTA block ----------------------------------------------------------- */
.cta-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-6);
  padding: calc(var(--space-8) + var(--space-1)) var(--space-5) calc(var(--space-16) + var(--space-1));
  background: var(--color-surface-sunken);
  text-align: center;
}

/* ---- Accordion (native <details>) --------------------------------------- */
.accordion {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}
.accordion__summary {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-5);
  list-style: none;
  cursor: pointer;
}
.accordion__summary::-webkit-details-marker { display: none; }
@media (hover: hover) {
  .accordion__summary:hover .accordion__title { text-decoration: underline; text-underline-offset: 4px; }
}
.accordion__panel { padding: var(--space-12) 0 var(--space-6); }

/* ---- Parallax (js/parallax.js) --------------------------------------------
   Scroll-driven: the scope's way through the viewport (cover range) drives
   each item from -½ to +½ of its travel. js/parallax.js sets --parallax from
   data-parallax; travel scales with --px like the rest of the layout. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    [data-parallax-scope] { view-timeline: --parallax-scope block; }
    [data-parallax] {
      animation: parallax linear both;
      animation-timeline: --parallax-scope;
      animation-range: cover;
    }
  }
}
@keyframes parallax {
  from { transform: translate3d(0, calc(-0.5 * var(--parallax, 0) * var(--px)), 0); }
  to   { transform: translate3d(0, calc(0.5 * var(--parallax, 0) * var(--px)), 0); }
}

/* ---- Touch targets ---------------------------------------------------------
   On touch screens everything tappable gets at least a 44px hit area
   (Apple HIG); the desktop sizes above stay as designed. */
@media (pointer: coarse) {
  .button { min-height: 44px; --pad-x: 18px; }
  .button--icon { width: 48px; height: 44px; padding: 0; }
  .process__summary { padding-block: var(--space-4); }
  /* Full-width rows, so a tap anywhere along the line counts. */
  .menu-link { display: flex; padding-block: var(--space-1); }
}
