/* anstrich. v3 — the refresh (2026-09).
   Loaded instead of anstrich-components.css on pages flagged `v3` in pages.js.

   The language: ink ground, off-white type, hairlines, a lot of space.
   ONE paint gesture per view, and it is real paint: every .paint element is a
   photographed stroke used as a mask and filled with a token colour, so coral
   is always exactly --coral-500. Its reveal edge is a photographed paint
   boundary (/media/paint/wipe.avif), never a CSS gradient.
   Rules this file enforces:
   - nothing loops; every animation runs once
   - the period is drawn from --period and never shares a colour with anything
     it touches
   - no numbered eyebrows, no marquee, no metric bands
   - reduced motion = the final frame */

:root {
  --period: var(--coral-500);
  --gut: clamp(20px, 5vw, 64px);
  --max: 1320px;
  --hair: color-mix(in srgb, var(--paper) 12%, transparent);
  --hair-strong: color-mix(in srgb, var(--paper) 22%, transparent);
  --ease-out: cubic-bezier(.2, .7, .2, 1);
  --ease-lay: cubic-bezier(.55, .06, .18, 1);
  --section: clamp(96px, 13vw, 184px);
}

*, *::before, *::after { box-sizing: border-box }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto } }
body {
  margin: 0; background: var(--ink-900); color: var(--paper);
  font: 400 17px/1.6 'Hanken Grotesk', system-ui, sans-serif;
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
}
::selection { background: var(--coral-500); color: var(--ink-900) }
a { color: inherit }
img { display: block; max-width: 100%; height: auto }
:focus-visible { outline: 2px solid var(--coral-500); outline-offset: 3px; border-radius: 4px }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap }
.wrap { width: 100%; max-width: calc(var(--max) + var(--gut) * 2); margin: 0 auto; padding: 0 var(--gut) }
section[id] { scroll-margin-top: 96px }

/* ---- type ---- */
.display, .h2, .h3, .wordmark {
  font-family: 'Clash Grotesk', 'Hanken Grotesk', system-ui, sans-serif;
  font-weight: 600; text-transform: lowercase; margin: 0;
}
.wordmark { letter-spacing: -.045em; line-height: .78; white-space: nowrap }
.h2 { font-size: clamp(40px, 6.2vw, 92px); line-height: .95; letter-spacing: -.035em }
.h3 { font-size: clamp(24px, 2.4vw, 34px); line-height: 1.08; letter-spacing: -.02em; font-weight: 500 }
.pd { color: var(--period) }
.eyebrow { font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: lowercase; color: var(--grey-500) }
.lede { font-size: clamp(18px, 1.45vw, 21px); line-height: 1.55; color: var(--grey-400); margin: 0; max-width: 36ch }
.body { color: var(--grey-400); margin: 0; max-width: 58ch }
.body + .body { margin-top: 1em }
.body strong { color: var(--paper); font-weight: 600 }

/* ---- actions ---- */
.btn {
  display: inline-flex; align-items: center; gap: 10px; min-height: 48px; padding: 0 24px;
  border-radius: 999px; background: var(--paper); color: var(--ink-900);
  font: 700 15px/1 'Hanken Grotesk', sans-serif; text-transform: lowercase; text-decoration: none;
  border: 0; cursor: pointer; transition: transform .25s var(--ease-out), background .25s;
}
.btn .arr { color: var(--coral-600); transition: transform .25s var(--ease-out) }
.btn:hover .arr { transform: translateX(4px) }
.btn:active { transform: scale(.98) }
.btn[disabled] { opacity: .6; cursor: wait }
.link {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px;
  font-weight: 700; font-size: 15px; text-transform: lowercase; text-decoration: none; color: var(--paper);
}
.link .arr { color: var(--coral-500); transition: transform .25s var(--ease-out) }
.link:hover .arr { transform: translateX(4px) }
.link span:not(.arr) { background: linear-gradient(currentColor, currentColor) 0 100% / 0 1px no-repeat; transition: background-size .3s var(--ease-out) }
.link:hover span:not(.arr) { background-size: 100% 1px }

/* ---- paint ----
   <div class="paint paint--band" data-scrub><div class="paint__lay"></div></div>
   The shape mask is only attached once the element is near the viewport
   (.near, set by anstrich-v3.js) so below-the-fold paint never costs the
   first load. Colour: --paint on the element (default coral).            */
.paint:not(.near) { visibility: hidden }
.paint { position: absolute; pointer-events: none; -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-size: 100% 100%; mask-size: 100% 100% }
.paint--band { aspect-ratio: 1600 / 190 }
.paint--band.near { -webkit-mask-image: url(/media/paint/band.avif); mask-image: url(/media/paint/band.avif) }
.paint__lay {
  position: absolute; inset: 0; background: var(--paint, var(--coral-500));
  -webkit-mask: url(/media/paint/wipe.avif) 20% 0 / 300% 100% no-repeat;
  mask: url(/media/paint/wipe.avif) 20% 0 / 300% 100% no-repeat;
}
/* with JS, paint waits for .go; without JS it is simply there */
.js .paint:not(.go) .paint__lay { -webkit-mask-position: 80% 0; mask-position: 80% 0 }
.js .paint.go .paint__lay { animation: paint-lay var(--d, 950ms) var(--ease-lay) var(--w, 0ms) both }
@keyframes paint-lay {
  from { -webkit-mask-position: 80% 0; mask-position: 80% 0 }
  to { -webkit-mask-position: 20% 0; mask-position: 20% 0 }
}
/* one-time rise for supporting copy */
.js [data-rise] { opacity: 0; transform: translateY(14px) }
.js [data-rise].in { opacity: 1; transform: none; transition: opacity .7s var(--ease-out) var(--w, 0ms), transform .7s var(--ease-out) var(--w, 0ms) }

/* ---- header ---- */
.hdr { position: fixed; inset: 0 0 auto; z-index: 80; display: flex; align-items: center; justify-content: space-between; padding: 20px var(--gut); pointer-events: none }
.hdr > * { pointer-events: auto }
.hdr__mark { font: 600 22px/1 'Clash Grotesk', sans-serif; letter-spacing: -.04em; text-decoration: none; padding: 10px 0 }
.hdr__pill {
  position: absolute; left: 50%; transform: translateX(-50%); display: flex;
  border: 1px solid var(--hair); border-radius: 999px; overflow: hidden;
  background: color-mix(in srgb, var(--ink-900) 55%, transparent);
  -webkit-backdrop-filter: blur(16px) saturate(160%); backdrop-filter: blur(16px) saturate(160%);
}
.hdr__pill a, .hdr__pill button {
  display: flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 20px;
  font: 600 14px/1 'Hanken Grotesk', sans-serif; color: var(--paper); text-decoration: none; text-transform: lowercase;
  background: none; border: 0; cursor: pointer;
}
.hdr__pill > * + * { border-left: 1px solid var(--hair) }
.hdr__pill .arr { color: var(--coral-500) }
.hdr__lang { font-size: 13px; font-weight: 700; letter-spacing: .12em; color: var(--grey-500); text-decoration: none; padding: 12px 0 12px 12px; transition: color .25s }
.hdr__lang:hover { color: var(--paper) }
@media (max-width: 700px) { .hdr__pill { position: static; transform: none } .hdr__pill a, .hdr__pill button { padding: 0 14px; font-size: 13px } }
@media (max-width: 360px) { .hdr__pill .arr { display: none } }

/* ---- menu overlay (built by anstrich-v3.js) ---- */
.menu { position: fixed; inset: 0; z-index: 120; display: none; flex-direction: column; justify-content: center; padding: 96px var(--gut) 48px; background: color-mix(in srgb, var(--ink-900) 94%, transparent); -webkit-backdrop-filter: blur(22px); backdrop-filter: blur(22px) }
.menu.open { display: flex }
.menu nav { display: flex; flex-direction: column; width: 100%; max-width: var(--max); margin: 0 auto }
.menu nav a { font: 600 clamp(28px, 4.2vw, 50px)/1.2 'Clash Grotesk', sans-serif; letter-spacing: -.02em; text-transform: lowercase; text-decoration: none; width: fit-content; transition: color .2s }
.menu nav a:hover, .menu nav a[aria-current] { color: var(--coral-500) }
.menu nav a.menu__cta { margin-top: 28px; font: 700 15px/1 'Hanken Grotesk', sans-serif; letter-spacing: .06em; color: var(--coral-500); border-bottom: 1px solid; padding-bottom: 6px }
.menu__close { position: absolute; top: 16px; right: var(--gut); width: 44px; height: 44px; border-radius: 999px; border: 1px solid var(--hair-strong); background: none; color: var(--paper); font-size: 22px; cursor: pointer }
.js .menu.open nav > * { animation: rise .5s var(--ease-out) both; animation-delay: calc(var(--i) * 40ms) }
@keyframes rise { from { opacity: 0; transform: translateY(12px) } to { opacity: 1; transform: none } }

/* ---- hero ---- */
.hero { position: relative; min-height: 100svh; display: grid; grid-template-rows: 1fr auto; padding: 128px var(--gut) clamp(28px, 6vh, 64px); overflow: hidden; isolation: isolate }
.hero__mark { align-self: end; font-size: clamp(56px, 10.5vw, 176px) }
/* the core film sits right and small enough that the wordmark's coral period never meets the coral cube */
.hero__film { --sx: 3vw; --ss: .74; --fx: 74% }
/* the offer stacks under the name on the left; the right side belongs to the film */
.hero__foot { display: flex; flex-direction: column-reverse; align-items: flex-start; gap: 32px; margin-top: clamp(16px, 4vh, 48px) }
.hero__act { display: flex; flex-direction: column; align-items: flex-start; gap: 24px }

/* ---- sections ---- */
.sec { padding: var(--section) 0; position: relative; overflow-x: clip }
.sec + .sec { border-top: 1px solid var(--hair) }
.sec__head { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); gap: clamp(32px, 6vw, 96px); align-items: end; margin-bottom: clamp(48px, 7vw, 104px) }
.sec__head .body, .sec__head .lede { justify-self: end }
@media (max-width: 860px) { .sec__head { grid-template-columns: 1fr } .sec__head .body, .sec__head .lede { justify-self: start } }

/* about teaser */
.about { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); gap: clamp(32px, 6vw, 96px) }
.about__quote { font: 500 clamp(22px, 2.2vw, 30px)/1.3 'Clash Grotesk', sans-serif; letter-spacing: -.01em; text-transform: lowercase; margin: 32px 0 0; max-width: 22ch }
.about__quote em { font-style: normal; color: var(--grey-500) }
.about .link { margin-top: 28px }
@media (max-width: 860px) { .about { grid-template-columns: 1fr } }

/* rows: why + process — hairline editorial columns */
.rows { display: grid; grid-template-columns: repeat(var(--n, 3), minmax(0, 1fr)); gap: 0 clamp(24px, 3vw, 48px) }
.rows > div { position: relative; padding-top: 24px }
.rows > div::before { content: ''; position: absolute; inset: 0 0 auto; height: 1px; background: var(--hair-strong); transform-origin: left }
.rows .h3 { margin-bottom: 14px }
.rows .body { font-size: 16px }
@media (max-width: 960px) { .rows { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 40px } }
@media (max-width: 600px) { .rows { grid-template-columns: 1fr } }

/* services — big rows; hover lays one thin coral stroke under the name */
.svc { list-style: none; margin: 0; padding: 0; border-bottom: 1px solid var(--hair) }
.svc li { border-top: 1px solid var(--hair) }
.svc a { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; align-items: center; gap: 24px; padding: clamp(24px, 3vw, 40px) 0; text-decoration: none }
.svc__name { position: relative; isolation: isolate; font: 600 clamp(30px, 4.2vw, 60px)/1 'Clash Grotesk', sans-serif; letter-spacing: -.03em; text-transform: lowercase; transition: transform .45s var(--ease-out) }
.svc__name .paint { left: -3%; bottom: -.34em; width: 106%; --paint: var(--coral-500); z-index: -1 }
.svc__desc { color: var(--grey-400); font-size: 16px; max-width: 34ch }
.svc__arr { font-size: 22px; color: var(--grey-500); transition: color .25s, transform .45s var(--ease-out) }
@media (hover: hover) and (pointer: fine) {
  .svc a:hover .svc__name { transform: translateX(10px) }
  .svc a:hover .svc__arr { color: var(--coral-500); transform: translateX(6px) }
}
@media (max-width: 760px) { .svc a { grid-template-columns: 1fr auto; row-gap: 8px } .svc__desc { grid-column: 1 } .svc__arr { grid-row: 1; grid-column: 2 } }

/* selected work — spur, framed as a brief */
.work { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, .8fr); gap: clamp(32px, 5vw, 80px); align-items: center; text-decoration: none }
.work__img { border-radius: 6px; overflow: hidden; background: var(--ink-800); aspect-ratio: 1058 / 394 }
.work__img img { width: 100%; height: 100%; object-fit: cover; transition: transform .8s var(--ease-out) }
.work__meta { display: flex; flex-direction: column; gap: 18px }
.work__title { margin: 0; font: 600 clamp(48px, 6vw, 88px)/.9 'Clash Grotesk', sans-serif; letter-spacing: -.035em }
.tags { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 0; padding: 0 }
.tags li { font-size: 13px; font-weight: 600; color: var(--grey-400); border: 1px solid var(--hair-strong); border-radius: 999px; padding: 6px 12px; text-transform: lowercase }
@media (max-width: 860px) { .work { grid-template-columns: 1fr } }

/* contact */
.contact { display: grid; grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); gap: clamp(40px, 7vw, 112px); align-items: start }
.contact__side { position: relative }
.contact__mail { font: 600 clamp(20px, 1.8vw, 26px)/1.2 'Clash Grotesk', sans-serif; letter-spacing: -.01em; text-decoration: none; background: linear-gradient(var(--coral-500), var(--coral-500)) 0 100% / 100% 1px no-repeat; padding-bottom: 4px }
/* the homepage core film, framed tight on the block; its edges melt into the ink */
.contact__film { margin: clamp(20px, 3vw, 36px) 0 clamp(20px, 3vw, 32px); aspect-ratio: 5 / 4; max-width: 460px; overflow: hidden }
.contact__film video { width: 100%; height: 100%; object-fit: cover; object-position: 74% 45%;
  -webkit-mask-image: radial-gradient(closest-side, #000 72%, transparent); mask-image: radial-gradient(closest-side, #000 72%, transparent) }
.contact__side .h2 + .body { margin-top: clamp(24px, 3vw, 40px) }
@media (max-width: 860px) { .contact { grid-template-columns: 1fr } }

.form { display: grid; grid-template-columns: 1fr 1fr; gap: 28px 24px }
.field { display: flex; flex-direction: column; gap: 8px }
.field--full { grid-column: 1 / -1 }
.field label { font-size: 13px; font-weight: 700; letter-spacing: .08em; color: var(--grey-500); text-transform: lowercase }
.field input, .field select, .field textarea {
  font: 400 17px/1.4 'Hanken Grotesk', sans-serif; color: var(--paper); background: transparent;
  border: 0; border-bottom: 1px solid var(--hair-strong); border-radius: 0; padding: 10px 0 12px; min-height: 48px;
  transition: border-color .25s; -webkit-appearance: none; appearance: none;
}
.field select { background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%238c8982' stroke-width='1.5'/%3E%3C/svg%3E") right 4px center no-repeat; padding-right: 24px; cursor: pointer }
.field select option { background: var(--ink-800); color: var(--paper) }
.field textarea { min-height: 132px; resize: vertical }
.field input:focus, .field select:focus, .field textarea:focus { outline: none; border-bottom-color: var(--coral-500) }
.form__foot { grid-column: 1 / -1; display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap; margin-top: 8px }
.form__note { font-size: 14px; color: var(--grey-500); margin: 0 }
.form__err { grid-column: 1 / -1; margin: 0; font-size: 15px; color: var(--paper); border-left: 2px solid var(--coral-500); padding: 4px 0 4px 14px }
.form__err a { color: var(--paper); text-decoration-color: var(--coral-500); text-underline-offset: 3px }
.form__gotcha { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden }
.sent { padding: 8px 0 }
.sent .h2 { font-size: clamp(36px, 4.4vw, 64px) }
.sent .body { margin: 18px 0 24px }
@media (max-width: 600px) { .form { grid-template-columns: 1fr } }

/* ---- footer ---- */
.ftr { border-top: 1px solid var(--hair); padding: clamp(48px, 6vw, 80px) 0 40px }
.ftr__top { display: flex; justify-content: space-between; align-items: flex-start; gap: 32px; flex-wrap: wrap }
.ftr { overflow-x: clip }
.ftr__mark { font: 600 clamp(72px, 21vw, 340px)/.78 'Clash Grotesk', sans-serif; letter-spacing: -.05em; margin: clamp(40px, 6vw, 96px) 0 0; padding-bottom: .06em; white-space: nowrap }
.ftr__line { color: var(--grey-500); margin: 0; font-size: 15px }
.ftr nav { display: flex; gap: 8px 28px; flex-wrap: wrap; align-content: flex-start }
.ftr nav a { text-decoration: none; color: var(--grey-400); font-size: 15px; min-height: 44px; display: inline-flex; align-items: center; transition: color .2s }
.ftr nav a:hover { color: var(--paper) }
.ftr__base { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-top: 48px; font-size: 13px; color: var(--grey-600) }
.ftr__base a { color: var(--grey-400); text-decoration: none }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important }
  .js .paint:not(.go) .paint__lay { -webkit-mask-position: 20% 0; mask-position: 20% 0 }
  .js [data-rise] { opacity: 1; transform: none }
  .svc__name .paint { display: none }   /* the row highlight only makes sense while scrolling */
}
@supports not (animation-timeline: view()) { .svc__name .paint { display: none } }


/* =====================================================================
   SCROLL — the page is painted by the scroll wheel.
   Everything here is scrubbed: it moves with the scroll, both directions,
   and never parks-then-plays. Native CSS scroll-driven animations (no JS,
   no smooth-scroll hijack). Browsers without support, and reduced motion,
   get the finished state; anstrich-v3.js lays [data-scrub] paint once.
   ===================================================================== */
@supports (animation-timeline: view()) {
@media (prefers-reduced-motion: no-preference) {

  /* hero — leaves in layers: the mark lifts, the stroke is pulled away left */
  /* the hero's timeline is shared with the header (timeline-scope) for the wordmark handoff */
  body { timeline-scope: --hero }
  .hero, .svch { view-timeline: --hero block }
  /* service pages: the mark is already in the header (no wordmark handoff), only the backing animates */
  body:has(.svch) .hdr__mark { animation: none }
  .hdr__mark { animation: hdr-mark linear both; animation-timeline: --hero; animation-range: exit 26% exit 46% }
  @keyframes hdr-mark { from { opacity: 0; transform: translateY(8px) } to { opacity: 1; transform: none } }
  /* once the hero has gone, the header gets an ink backing so content never runs through the nav */
  .hdr { animation: hdr-bg linear both; animation-timeline: --hero; animation-range: exit 40% exit 70% }
  @keyframes hdr-bg { from { background: transparent; -webkit-backdrop-filter: blur(0); backdrop-filter: blur(0) } to { background: color-mix(in srgb, var(--ink-900) 84%, transparent); -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px) } }
  .hero__mark, .hero__foot { animation-timeline: --hero; animation-range: exit 0% exit 100%; animation-fill-mode: both; animation-timing-function: linear }
  .hero__mark { animation-name: hero-mark }
  .hero__foot { animation-name: hero-foot }
  /* the mark lags the scroll (depth) */
  @keyframes hero-mark { 0% { transform: none; opacity: 1 } 28% { opacity: 0 } 100% { transform: translateY(26vh) scale(.9); opacity: 0 } }
  @keyframes hero-foot { to { transform: translateY(-12vh); opacity: 0 } }

  /* headings rise out of their own baseline */
  .sec .h2 { padding-bottom: .14em; margin-bottom: -.14em; animation: head-in linear both; animation-timeline: view(); animation-range: entry 5% cover 38% }
  @keyframes head-in { from { clip-path: inset(0 0 100% 0); transform: translateY(.45em) } to { clip-path: inset(0 0 0 0); transform: none } }

  /* rows — the hairline draws, the column follows it up (why + process) */
  .rows > div { animation: col-in linear both; animation-timeline: view(); animation-range: entry 0% cover 34% }
  .rows > div:nth-child(2) { animation-range: entry 12% cover 40% }
  .rows > div:nth-child(3) { animation-range: entry 24% cover 46% }
  .rows > div:nth-child(4) { animation-range: entry 36% cover 52% }
  .rows > div::before { animation: line-in linear both; animation-timeline: view(); animation-range: entry 0% cover 30% }
  @keyframes col-in { from { opacity: 0; transform: translateY(48px) } to { opacity: 1; transform: none } }
  @keyframes line-in { from { transform: scaleX(0) } to { transform: scaleX(1) } }

  /* paint laid by the scroll: about wash, ground-up coat, process band */
  [data-scrub] .paint__lay { animation: paint-lay linear both; animation-timeline: view(); animation-range: entry 30% cover 55% }

  /* footer — the wordmark rises out of the page edge */
  .ftr__mark { animation: head-in linear both; animation-timeline: view(); animation-range: entry 0% entry 100% }
}
}

/* =====================================================================
   PINNED SECTIONS (refresh v3.1)
   Static layout first — that is what Firefox and reduced motion get.
   The pinning + scrubbing lives in the @supports block at the end.
   ===================================================================== */
.pin { position: relative; border-top: 1px solid var(--hair) }
.pin__stage { padding: var(--section) 0 }

/* gestures (all cropped tight to their paint, so the box IS the stroke) */
.paint--tick { aspect-ratio: 1004 / 290 }
.paint--tick.near { -webkit-mask-image: url(/media/paint/tick.avif); mask-image: url(/media/paint/tick.avif) }
.svc2__paint--brand .paint { aspect-ratio: 824 / 506 }   .svc2__paint--brand .paint.near { -webkit-mask-image: url(/media/paint/svc-brand.avif); mask-image: url(/media/paint/svc-brand.avif) }
.svc2__paint--social .paint { aspect-ratio: 1400 / 178 } .svc2__paint--social .paint.near { -webkit-mask-image: url(/media/paint/svc-social.avif); mask-image: url(/media/paint/svc-social.avif) }
.svc2__paint--web .paint { aspect-ratio: 1400 / 250 }    .svc2__paint--web .paint.near { -webkit-mask-image: url(/media/paint/svc-web.avif); mask-image: url(/media/paint/svc-web.avif) }
.svc2__paint--consult .paint { aspect-ratio: 1000 / 656 } .svc2__paint--consult .paint.near { -webkit-mask-image: url(/media/paint/svc-consult.avif); mask-image: url(/media/paint/svc-consult.avif) }

/* about — the statement */
.about2__label { display: block; margin: 0 0 clamp(24px, 3vw, 40px); font-size: 13px }
.about2__say { position: relative; isolation: isolate; margin: 0; max-width: 26ch; font: 500 clamp(28px, 4.3vw, 66px)/1.1 'Clash Grotesk', sans-serif; letter-spacing: -.022em; text-transform: lowercase; color: var(--paper) }
.about2__mark { position: relative; white-space: nowrap }
.about2__stroke { position: absolute; left: -3%; width: 106%; bottom: -.22em; z-index: -1 }
.about2__stroke .paint { position: relative; display: block; width: 100% }
.about2__more { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 16px 40px; margin-top: clamp(28px, 4vw, 56px) }
.about2__more .body { max-width: 46ch }

/* why — one reason at a time */
.why2 { display: grid; grid-template-columns: minmax(0, .75fr) minmax(0, 1.25fr); gap: clamp(32px, 6vw, 96px); align-items: center }
.why2__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 48px }
.why2__head { position: relative; isolation: isolate; margin: 0 0 18px; font: 600 clamp(36px, 5.4vw, 88px)/1 'Clash Grotesk', sans-serif; letter-spacing: -.03em; text-transform: lowercase; width: fit-content }
.why2__stroke { position: absolute; left: -3%; width: 106%; bottom: -.18em; z-index: -1 }
.why2__stroke .paint { position: relative; display: block; width: 100% }
.why2__item .body { font-size: clamp(17px, 1.4vw, 20px); max-width: 40ch }
@media (max-width: 860px) { .why2 { grid-template-columns: 1fr } }

/* services — a track of panels (stacked when not pinned) */
.svc2__track { display: flex; flex-direction: column }
.svc2__intro { padding: 0 var(--gut) clamp(40px, 5vw, 72px); max-width: calc(var(--max) + var(--gut) * 2); margin: 0 auto; width: 100% }
.svc2__intro .lede { margin-top: 20px }
.svc2__panel { position: relative; isolation: isolate; display: flex; flex-direction: column; justify-content: center; gap: 18px; padding: clamp(40px, 6vw, 80px) var(--gut); text-decoration: none; overflow: hidden }
.svc2__name { font: 600 clamp(48px, 8.4vw, 156px)/.92 'Clash Grotesk', sans-serif; letter-spacing: -.04em; text-transform: lowercase; max-width: 9ch }
.svc2__desc { font-size: clamp(18px, 1.5vw, 22px); color: var(--grey-400); max-width: 34ch }
.svc2__list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px }
.svc2__list li { font-size: 13px; font-weight: 600; color: var(--grey-400); border: 1px solid var(--hair-strong); border-radius: 999px; padding: 6px 12px; text-transform: lowercase }
.svc2__paint { position: absolute; z-index: -1; pointer-events: none }
.svc2__paint .paint { position: relative; display: block; width: 100% }
.svc2__paint--brand   { width: clamp(220px, 30vw, 480px); left: 38%; top: 8% }
.svc2__paint--social  { width: clamp(320px, 62vw, 1000px); left: 2%; top: 30% }
.svc2__paint--web     { width: clamp(300px, 56vw, 900px); left: 4%; top: 26% }
.svc2__paint--consult { width: clamp(220px, 28vw, 440px); left: 44%; top: 4% }
.svc2__end { display: none }

/* work — the frame */
.work2 { display: flex; flex-direction: column; align-items: center; gap: clamp(24px, 3vw, 40px) }
.work2__title { align-self: flex-start; padding: 0 var(--gut); width: 100%; max-width: calc(var(--max) + var(--gut) * 2); margin: 0 auto }
.work2__frame { display: block; width: 100vw; aspect-ratio: 1058 / 394; background: var(--ink-800); overflow: hidden }
.work2__frame img { width: 100%; height: 100%; object-fit: cover }
.work2__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 28px }
.work2__meta .body { max-width: 52ch }

/* process — the stroke is the progress bar */
.proc2__stroke { position: relative; margin: clamp(-24px, -2vw, -8px) 0 clamp(8px, 2vw, 24px) }
.proc2__stroke .paint { position: relative; display: block; width: 100% }
.proc2__steps { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 24px clamp(24px, 3vw, 48px) }
.proc2__steps .h3 { margin-bottom: 10px }
.proc2__steps .body { font-size: 16px }
@media (max-width: 760px) { .proc2__steps { grid-template-columns: repeat(2, minmax(0, 1fr)) } }

/* ---------------- pinning + scrubbing ---------------- */
@supports (animation-timeline: view()) {
@media (prefers-reduced-motion: no-preference) {
  /* the stage centres its content, but never under the fixed header (84px): the top spacer is at
     least --pin-top. Where a section is taller than the room below the header, its big picture gives
     way instead — the spur frame gets shorter (cover-cropped), the process stroke smaller — so nothing
     is cut off at the bottom either. With room to spare it's centred exactly as before. */
  .pin:not(.pin--svc) .pin__stage { --pin-top: 104px; --pin-foot: 24px; position: sticky; top: 0; height: 100svh; padding: 0; display: grid; grid-template-rows: minmax(var(--pin-top), 1fr) auto minmax(var(--pin-foot), 1fr); overflow: hidden }
  .pin:not(.pin--svc) .pin__stage > * { grid-row: 2 }
  .pin .pin__stage > * { width: 100% }
  .pin--work .work2, .pin--proc .proc2 { max-height: calc(100svh - var(--pin-top) - var(--pin-foot)) }
  .pin--work .work2 > *, .pin--proc .proc2 > * { flex-shrink: 0 }
  .pin--work .work2__frame { flex-shrink: 1; min-height: 0 }
  .pin--proc .proc2 { display: flex; flex-direction: column }
  .pin--proc .proc2__stroke { flex-shrink: 1; min-height: 0; aspect-ratio: 1004 / 290 }
  .pin--proc .proc2__stroke .paint { position: absolute; top: 0; left: 0; width: auto; height: 100%; max-width: 100% }

  /* about: 27 words light up in order; the stroke is laid under "the final coat of paint." */
  .pin--about { height: 250vh; view-timeline: --about block }
  .about2__say .w { animation: w-in linear both; animation-timeline: --about; animation-range: contain calc(var(--i) * 2.5%) contain calc(var(--i) * 2.5% + 7%) }
  @keyframes w-in { from { color: color-mix(in srgb, var(--paper) 14%, transparent) } to { color: var(--paper) } }
  .about2__stroke .paint__lay { animation: paint-lay linear both; animation-timeline: --about; animation-range: contain 15% contain 33% }
  .about2__more { animation: fade-up linear both; animation-timeline: --about; animation-range: contain 74% contain 88% }
  @keyframes fade-up { from { opacity: 0; transform: translateY(24px) } to { opacity: 1; transform: none } }

  /* why: each reason owns the screen; its stroke is laid while it's there */
  .pin--why { height: 310vh; view-timeline: --why block }
  .why2__list { position: relative; min-height: 52vh; display: block }
  .why2__item { position: absolute; left: 0; right: 0; top: 50%; translate: 0 -50%; animation: why-item linear both; animation-timeline: --why }
  .why2__item:nth-child(1) { animation-range: contain 0% contain 36% }
  .why2__item:nth-child(2) { animation-range: contain 32% contain 68% }
  .why2__item:nth-child(3) { animation-name: why-last; animation-range: contain 64% contain 100% }
  @keyframes why-item { 0% { opacity: 0; transform: translateY(35%) } 16% { opacity: 1; transform: none } 84% { opacity: 1; transform: none } 100% { opacity: 0; transform: translateY(-35%) } }
  @keyframes why-last { 0% { opacity: 0; transform: translateY(35%) } 16% { opacity: 1; transform: none } 100% { opacity: 1; transform: none } }
  .why2__item .paint__lay { animation: paint-lay linear both; animation-timeline: --why }
  .why2__item:nth-child(1) .paint__lay { animation-range: contain 4% contain 20% }
  .why2__item:nth-child(2) .paint__lay { animation-range: contain 38% contain 54% }
  .why2__item:nth-child(3) .paint__lay { animation-range: contain 70% contain 86% }
  @media (max-width: 860px) { .why2__list { min-height: 58vh } }

  /* work: the frame grows from a card to the full width, then the brief arrives */
  .pin--work { height: 210vh; view-timeline: --work block }
  .work2__frame { animation: work-grow linear both; animation-timeline: --work; animation-range: contain 0% contain 58% }
  @keyframes work-grow { from { transform: scale(.46); border-radius: 14px } to { transform: none; border-radius: 0 } }
  .work2__frame img { animation: work-img linear both; animation-timeline: --work; animation-range: contain 0% contain 100% }
  @keyframes work-img { from { transform: scale(1.3) } to { transform: scale(1) } }
  .work2__meta { animation: fade-up linear both; animation-timeline: --work; animation-range: contain 58% contain 80% }

  /* process: the stroke is laid left to right; each step arrives when the paint edge reaches its column */
  .pin--proc { height: 240vh; view-timeline: --proc block }
  .proc2__stroke .paint__lay { animation: paint-lay linear both; animation-timeline: --proc; animation-range: contain 5% contain 85% }
  .proc2__steps li { animation: fade-up linear both; animation-timeline: --proc }
  .proc2__steps li:nth-child(1) { animation-range: contain 14% contain 26% }
  .proc2__steps li:nth-child(2) { animation-range: contain 31% contain 43% }
  .proc2__steps li:nth-child(3) { animation-range: contain 47% contain 59% }
  .proc2__steps li:nth-child(4) { animation-range: contain 64% contain 76% }
}
}

/* ---- services slider ----
   Driven by anstrich-v3.js, NOT by CSS scroll timelines: Safari released the pin half-way
   through the slide. JS writes two numbers on the section — --p (scroll progress 0..1) and
   --travel (measured sideways distance in px) — plus each panel's paint window --s..--e
   (from its measured centre). Everything below is derived from those, so the track and the
   paint can't disagree, in any browser. Without JS / with reduced motion: stacked list. */
.svc-js { height: var(--svc-h, 380vh) }
.svc-js .pin__stage { position: sticky; top: 0; height: 100svh; padding: 0; display: flex; align-items: stretch; overflow: hidden }
.svc-js .svc2__track { flex-direction: row; width: max-content; height: 100%; transform: translate3d(calc(var(--p, 0) * var(--travel, 0) * -1px), 0, 0); will-change: transform }
.svc-js .svc2__intro { flex: 0 0 50vw; max-width: none; margin: 0; padding: 0 var(--gut); display: flex; flex-direction: column; justify-content: center }
.svc-js .svc2__panel { flex: 0 0 80vw; padding: 0 6vw }
.svc-js .svc2__end { display: block; flex: 0 0 30vw }
.js .svc-js .svc2__panel .paint__lay {
  --t: clamp(0, (var(--p, 0) - var(--s, 0)) / (var(--e, 1) - var(--s, 0)), 1);
  -webkit-mask-position: calc(80% - 60% * var(--t)) 0; mask-position: calc(80% - 60% * var(--t)) 0; animation: none }
@media (max-width: 700px) {
  .svc-js { --svc-h: 440vh }
  .svc-js .svc2__intro { flex-basis: 100vw }
  .svc-js .svc2__panel { flex-basis: 88vw; padding: 0 7vw }
  .svc-js .svc2__end { flex-basis: 12vw }
}

/* =====================================================================
   MOTION (motion.js / GSAP) — initial states + the new pieces
   ===================================================================== */
/* hidden until GSAP has split/staged them; revealed if motion.js never arrives (motion-fail) */
.motion [data-split], .motion .hero__mark .wm-txt, .motion [data-rise], .motion .hero__in { visibility: hidden }
.motion-fail [data-split], .motion-fail .hero__mark .wm-txt, .motion-fail [data-rise], .motion-fail .hero__in { visibility: visible !important; opacity: 1 !important; transform: none !important }
.motion .sec .h2, .motion .ftr__mark { animation: none }            /* GSAP split replaces the CSS clip reveal */
.ln { padding-bottom: .08em; margin-bottom: -.08em }               /* line masks keep descenders */
@media (prefers-reduced-motion: reduce) {
  .motion [data-split], .motion .hero__mark .wm-txt, .motion [data-rise], .motion .hero__in { visibility: visible !important; opacity: 1 !important; transform: none !important }
}

/* link underline paints in from the left, lifts off to the right */
.link span:not(.arr) { background: linear-gradient(var(--coral-500), var(--coral-500)) 100% 100% / 0 1px no-repeat; transition: background-size .45s var(--ease-out) }
.link:hover span:not(.arr) { background-position: 0 100%; background-size: 100% 1px }

/* page transitions: the next page is painted on behind a real paint edge (Chrome, Safari 18.2+) */
@view-transition { navigation: auto }
.hdr { view-transition-name: hdr }
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root) { animation: vt-out .75s cubic-bezier(.6, 0, .2, 1) both }
  ::view-transition-new(root) {
    animation: vt-in .9s cubic-bezier(.6, 0, .2, 1) both;
    -webkit-mask: url(/media/paint/wipe.avif) 80% 0 / 300% 100% no-repeat; mask: url(/media/paint/wipe.avif) 80% 0 / 300% 100% no-repeat;
  }
  @keyframes vt-in { from { -webkit-mask-position: 80% 0; mask-position: 80% 0 } to { -webkit-mask-position: 20% 0; mask-position: 20% 0 } }
  @keyframes vt-out { to { transform: scale(.98); opacity: .35 } }
}

/* ground up: the four stages as coats (pinned + scrubbed by motion.js) */
.gu3 { position: relative; border-top: 1px solid var(--hair); overflow-x: clip }
.gu3__in { min-height: 100svh; display: grid; grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr); gap: clamp(32px, 5vw, 88px); align-items: center; padding-block: clamp(88px, 12vh, 140px) }
.gu3__text { display: flex; flex-direction: column; align-items: flex-start; gap: 18px }
.gu3__title { font-size: clamp(56px, 8.2vw, 136px) }
.gu3__coats { list-style: none; margin: 0; padding: 0; display: grid; gap: clamp(4px, .8vw, 12px) }
.gu3__coat { position: relative; isolation: isolate }
.gu3__coat:nth-child(2) { margin-left: 5% } .gu3__coat:nth-child(3) { margin-left: 1.5% } .gu3__coat:nth-child(4) { margin-left: 7% }
.gu3__coat .paint { position: relative; display: block; width: 100% }
.paint--web { aspect-ratio: 1400 / 250 }
.paint--web.near { -webkit-mask-image: url(/media/paint/svc-web.avif); mask-image: url(/media/paint/svc-web.avif) }
.gu3__label { position: absolute; left: 7%; top: 50%; translate: 0 -50%; font: 500 clamp(18px, 1.8vw, 28px)/1.1 'Clash Grotesk', sans-serif; letter-spacing: -.01em; text-transform: lowercase; color: var(--lc, var(--paper)); white-space: nowrap }
.gu3__label em { font-style: normal; opacity: .72 }
.gu3--static .paint__lay { -webkit-mask-position: 20% 0 !important; mask-position: 20% 0 !important }
@media (max-width: 860px) { .gu3__in { grid-template-columns: 1fr; align-content: center } .gu3__coats { margin-top: 24px } }

/* =====================================================================
   SERVICE PAGES (components/ServiceV3.astro)
   ===================================================================== */
/* header: the 3D film is the whole background; the name sits bottom-left over the ink */
.svch { position: relative; min-height: 100svh; display: grid; align-items: end; padding: 128px var(--gut) clamp(40px, 8vh, 96px); overflow: hidden; isolation: isolate }
.svch__film { position: absolute; inset: 0; z-index: -1; background: var(--ink-900) }
.svch__film video { width: 100%; height: 100%; object-fit: cover; object-position: 72% 50%;
  /* per-service staging (--sx/--ss from services-v3.js): keep the object clear of the name; edges melt into the ink */
  transform: translateX(var(--sx, 0)) scale(var(--ss, 1)); transform-origin: 100% 100%;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 22%), linear-gradient(180deg, transparent, #000 16%);
  -webkit-mask-composite: source-in; mask-image: linear-gradient(90deg, transparent, #000 22%), linear-gradient(180deg, transparent, #000 16%); mask-composite: intersect }
.svch__film::after { content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg, var(--ink-900) 0%, color-mix(in srgb, var(--ink-900) 65%, transparent) 30%, transparent 56%),
              linear-gradient(0deg, var(--ink-900) 0%, transparent 24%) }
/* type-only header (pages whose hero film isn't chosen yet): shorter, the name carries it */
.svch--text { min-height: 76svh }
.svch__text { display: flex; flex-direction: column; align-items: flex-start; gap: 22px; max-width: min(760px, 100%) }
.svch__back { font-size: 13px; font-weight: 700; letter-spacing: .12em; color: var(--grey-400); text-decoration: none; text-transform: lowercase }
.svch__back:hover { color: var(--paper) }
.svch__name { margin: 0; font: 600 clamp(56px, 9vw, 150px)/.9 'Clash Grotesk', sans-serif; letter-spacing: -.045em; text-transform: lowercase }
.motion [data-chars] { visibility: hidden }
[data-chars] .wd { white-space: nowrap }   /* a long word (German compounds) never wraps between its letters */
/* German on narrow phones: names are capped so the longest compound ("markendesign", ~6.2em) still
   fits whole — inside the gutters here, inside the panel for the homepage services (.svc2__name) */
:lang(de) .svch__name { font-size: min(clamp(56px, 9vw, 150px), 14vw) }
:lang(de) .svc2__name { font-size: min(clamp(48px, 8.4vw, 156px), 11.8vw) }
.motion-fail [data-chars] { visibility: visible !important }
@media (prefers-reduced-motion: reduce) { .motion [data-chars] { visibility: visible !important } }
@media (max-width: 700px) {
  /* phones: the film takes the top of the header, so the object sits above the text, not under it */
  .svch__film { inset: 64px 0 auto; height: 58svh }
  .svch__film video { object-position: var(--fx, 80%) 50%; transform: none }
  .svch__film::after { background: linear-gradient(0deg, var(--ink-900) 0%, color-mix(in srgb, var(--ink-900) 60%, transparent) 16%, transparent 42%) }
}

/* PinSay / PinList (components) reuse the homepage's about/why blocks; these generalise their
   scroll timing from fixed counts to the page's own (--step per word, --ki: last word of the
   payoff, --n: items) */
.why2--n .why2__head { font-size: clamp(34px, 4.6vw, 72px) }
@supports (animation-timeline: view()) { @media (prefers-reduced-motion: no-preference) {
  .about2--n .about2__say .w { animation-range: contain calc(var(--i) * var(--step)) contain calc(var(--i) * var(--step) + 7%) }
  .about2--n .about2__mark .paint__lay { animation-range: contain calc(var(--ki) * var(--step) + 2%) contain calc(var(--ki) * var(--step) + 14%) }
  .pin--why.why2--n { height: calc(40vh + var(--n) * 90vh) }
  /* windows don't overlap: the outgoing item clears before the next arrives (longer texts than the homepage's) */
  .why2--n .why2__item { animation-name: why-item; animation-range: contain calc(var(--k) * 100% / var(--n) + 1%) contain calc((var(--k) + 1) * 100% / var(--n) - 1%) }
  .why2--n .why2__item:last-child { animation-name: why-last }
  .why2--n .why2__item .paint__lay { animation-range: contain calc(var(--k) * 100% / var(--n) + 4%) contain calc(var(--k) * 100% / var(--n) + 48% / var(--n)) }
} }

/* how it works with five steps (social): paint edge reaches column k at a different point */
.proc2__steps { grid-template-columns: repeat(var(--n, 4), minmax(0, 1fr)) }
@media (max-width: 760px) { .proc2__steps { grid-template-columns: repeat(2, minmax(0, 1fr)) } }
@supports (animation-timeline: view()) { @media (prefers-reduced-motion: no-preference) {
  .proc2--5 .proc2__steps li:nth-child(1) { animation-range: contain 12% contain 24% }
  .proc2--5 .proc2__steps li:nth-child(2) { animation-range: contain 26% contain 38% }
  .proc2--5 .proc2__steps li:nth-child(3) { animation-range: contain 39% contain 51% }
  .proc2--5 .proc2__steps li:nth-child(4) { animation-range: contain 52% contain 64% }
  .proc2--5 .proc2__steps li:nth-child(5) { animation: fade-up linear both; animation-timeline: --proc; animation-range: contain 66% contain 78% }
} }

/* what you get / who it's for */
.getwho { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(32px, 6vw, 96px) }
.getwho .eyebrow { display: block; margin-bottom: 18px }
.getwho__say { margin: 0; font: 500 clamp(24px, 2.6vw, 40px)/1.15 'Clash Grotesk', sans-serif; letter-spacing: -.015em; max-width: 24ch }
@media (max-width: 760px) { .getwho { grid-template-columns: 1fr } }

/* questions */
.faqwrap { display: grid; grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); gap: clamp(32px, 6vw, 96px); align-items: start }
.faq { border-bottom: 1px solid var(--hair) }
.faq details { border-top: 1px solid var(--hair) }
.faq summary { list-style: none; cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 24px; padding: 24px 0; font: 500 clamp(19px, 1.8vw, 26px)/1.25 'Clash Grotesk', sans-serif; letter-spacing: -.01em; text-transform: lowercase }
.faq summary::-webkit-details-marker { display: none }
.faq summary::after { content: '+'; flex: none; color: var(--coral-500); font-size: 1.3em; line-height: 1; transition: transform .35s var(--ease-out) }
.faq details[open] summary::after { transform: rotate(45deg) }
.faq details p { margin: 0 0 26px; color: var(--grey-400); max-width: 60ch }
.faq summary:hover { color: var(--paper) }
@media (max-width: 860px) { .faqwrap { grid-template-columns: 1fr } }

/* case study: spur's three routes, drawn as you scroll (the main route carries the rider) */
.route2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(40px, 6vw, 96px); align-items: center }
.route2__text .h2 { margin: 16px 0 24px }
.route2__key { list-style: none; margin: 32px 0 0; padding: 0; display: grid; gap: 12px; color: var(--grey-400); font-size: 15px }
.route2__key li::before { content: ''; display: inline-block; width: 28px; height: 3px; margin-right: 12px; border-radius: 2px; vertical-align: middle; background: var(--coral-500); opacity: var(--o) }
.route2__key span { color: var(--paper); font-weight: 600 }
.route2__map { width: 100%; height: auto; view-timeline: --route block }
.route2__grid line { stroke: var(--hair-strong); stroke-width: 1 }
.route2__r { fill: none; stroke: var(--coral-500); stroke-width: 2; stroke-linecap: round; stroke-opacity: var(--o); stroke-dasharray: 1 }
.route2__r--main { stroke-width: 3 }
.route2__pin { fill: var(--coral-500) }
.route2__rider { fill: var(--paper) }
@media (max-width: 860px) { .route2 { grid-template-columns: 1fr } }
@supports (animation-timeline: view()) { @media (prefers-reduced-motion: no-preference) {
  .route2__r { animation: route-draw linear both; animation-timeline: --route; animation-range: entry calc(30% + var(--d) * 10%) cover calc(50% + var(--d) * 8%) }
  @keyframes route-draw { from { stroke-dashoffset: 1 } to { stroke-dashoffset: 0 } }
} }

/* closing call */
.svc-close .contact { align-items: center }
/* the headline's column is never narrower than its longest word — a long German compound
   ("funktionierenden") would otherwise be cut off by the line masks */
@media (min-width: 861px) { .svc-close .contact { grid-template-columns: minmax(min-content, .9fr) minmax(0, 1.1fr) } }
.svc-close__act { display: flex; flex-direction: column; align-items: flex-start; gap: 24px }

.nowrap { white-space: nowrap }
