/* ==========================================================================
   Pace Digital Works — main site stylesheet
   Direction: editorial / fashion-studio. Warm near-black ink and one
   confident clay accent on ivory paper. See DESIGN-SYSTEM.md.
   No frameworks, no build step. Google Fonts only.
   ========================================================================== */

/* ---------- 1. Tokens ---------------------------------------------------- */
:root {
  /* Ink — warm near-black, never pure black */
  --ink:        #1B1815;
  --ink-2:      #2A2521;
  --espresso:   #100E0C;   /* used once, for the single dark section */

  /* Burnt clay — the one confident colour */
  --clay:       #C4622D;   /* fills, rules, large type */
  --clay-ink:   #9E4A1C;   /* small type only — keeps text readable */
  --clay-lift:  #D4753E;   /* hover, large blocks only */
  --clay-btn:   #9E4A1C;   /* clay button fill — button text is 12px, so it
                              follows the small-type rule, not the fill rule */
  --clay-btn-lift: #8A3F17; /* clay button hover — darker, never lighter */
  --clay-wash:  #F4E3D8;   /* faint tint block */

  /* Aged brass — hairlines and the hover sweep. Never a large fill. */
  --brass:      #8C6A3B;
  --brass-lite: #D8B978;

  /* Paper */
  --ivory:      #FBF6EE;   /* page */
  --sand:       #F3EAD9;   /* alternating section */
  --paper:      #FFFCF6;   /* raised surface */

  /* Text */
  --body:       #5A524A;
  --muted:      #6E655C;   /* readable on both ivory and sand at 11px */
  --on-dark:    #EFE7DA;
  --on-dark-2:  #B8AEA2;

  /* Lines */
  --line:       #E3D8C6;
  --line-soft:  #EFE7D9;
  --line-dark:  rgba(239, 231, 218, .16);

  /* Newsreader is a low-contrast editorial serif: its thin strokes stay
     solid instead of thinning to a hairline, so headings hold up on a warm
     background and still read at 13px. It carries an optical-size axis,
     which quietly thickens the letters as they get smaller. */
  --font-display: 'Newsreader', Georgia, 'Times New Roman', serif;
  --font-body:    'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  /* ---- Spacing scale -----------------------------------------------------
     One 4px-based ladder. Every gap, padding and margin on the page comes
     from this list and nothing else, so the vertical rhythm cannot drift.
     If a value is not on the ladder, it is a size (a dot, an icon, a
     circle), not a space. */
  --sp-1:   4px;
  --sp-2:   8px;
  --sp-3:  12px;
  --sp-4:  16px;
  --sp-5:  20px;
  --sp-6:  24px;
  --sp-7:  32px;
  --sp-8:  40px;
  --sp-9:  48px;
  --sp-10: 64px;

  /* The three big vertical gaps. Fluid, so they shrink on a phone. */
  --sp-section: clamp(72px, 9vw, 128px);  /* air inside a section       */
  --sp-head:    clamp(40px, 5vw, 72px);   /* section heading → content  */
  --sp-block:   clamp(32px, 4vw, 48px);   /* block → block inside one   */

  /* Editorial radius: almost none. Corners are cut, not rounded. */
  --r-xs: 2px;
  --r-sm: 3px;

  --shadow-soft: 0 20px 60px -30px rgba(27, 24, 21, .40);
  --shadow-lift: 0 34px 90px -34px rgba(27, 24, 21, .52);

  --wrap: 1240px;
  --gut: clamp(20px, 5vw, 56px);
  --ease: cubic-bezier(.22, .61, .36, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
}

/* ---------- 2. Reset & base --------------------------------------------- */
*, *::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(--ivory);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16.5px;
  line-height: 1.72;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Paper grain — this is what stops ivory looking like a flat screen colour.
   Turned off on small screens, where a fixed layer costs scrolling speed. */
body::after {
  content: "";
  position: fixed; inset: 0; z-index: 9999; pointer-events: none;
  opacity: .045;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}
@media (max-width: 780px) { body::after { display: none; } }

img, svg, video { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; }
button { font: inherit; cursor: pointer; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 500;
  color: var(--ink);
  line-height: 1.1;
  letter-spacing: -.018em;
  margin: 0 0 .5em;
  /* Lets the font thicken itself at small sizes and slim down at large ones. */
  font-optical-sizing: auto;
}
p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

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

.sr-only {
  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; left: 12px; top: -100px; z-index: 300;
  background: var(--ink); color: var(--ivory);
  padding: var(--sp-3) var(--sp-5); text-decoration: none;
}
.skip-link:focus { top: 12px; }

/* ---------- 3. Layout & surfaces ---------------------------------------- */
.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut); }
.section { padding-block: var(--sp-section); position: relative; }
/* A section that sits closer to the one above it. Used instead of writing a
   one-off padding on the tag, so the shorter rhythm is defined in one place. */
.section--tight { padding-block: clamp(56px, 7vw, 88px); }
.section--flush-top { padding-top: 0; }

.surf--sand    { background: var(--sand); }
.surf--dark    { background: var(--espresso); color: var(--on-dark-2); }
.surf--dark h2, .surf--dark h3, .surf--dark h4 { color: var(--ivory); }
.surf--dark .lede { color: var(--on-dark-2); }

/* A hairline that runs the full page width, above a section head. */
.rule { height: 1px; background: var(--line); border: 0; margin: 0; }
.surf--dark .rule { background: var(--line-dark); }

/* ---------- 4. Editorial type ------------------------------------------- */
/* The section marker: 01 —— WHAT WE DO. The strongest editorial device here. */
.kicker {
  display: flex; align-items: center; gap: var(--sp-4);
  font-family: var(--font-body);
  font-size: 11px; font-weight: 600; letter-spacing: .26em; text-transform: uppercase;
  color: var(--brass);
  margin: 0 0 var(--sp-6);
}
.kicker__no { font-family: var(--font-display); font-size: 16px; font-weight: 600; letter-spacing: 0; color: var(--clay-ink); }
.kicker::after { content: ""; flex: 1; height: 1px; background: var(--line); max-width: 120px; }
.kicker--center { justify-content: center; }
.kicker--center::before { content: ""; flex: 1; height: 1px; background: var(--line); max-width: 120px; }
.surf--dark .kicker { color: var(--brass-lite); }
.surf--dark .kicker__no { color: var(--clay); }
.surf--dark .kicker::after, .surf--dark .kicker--center::before { background: var(--line-dark); }

/* Sizes are capped so a headline line never has to wrap. A wrapped line
   would be cut off by the reveal animation, which hides anything below
   its own single line. Keep each line under about 22 characters. */
/* Newsreader carries more weight per letter than the old Didone did, so the
   sizes come down a step and the lines are given a little more room. */
.h-display { font-size: clamp(2.25rem, 4.8vw, 3.85rem); line-height: 1.08; letter-spacing: -.022em; }
.h-lg      { font-size: clamp(1.72rem, 3.5vw, 2.95rem); line-height: 1.1;  letter-spacing: -.02em; }
.h-md      { font-size: clamp(1.44rem, 2.3vw, 1.9rem);  line-height: 1.16; letter-spacing: -.016em; }
.h-sm      { font-size: clamp(1.22rem, 1.7vw, 1.46rem); line-height: 1.22; letter-spacing: -.012em; }

/* The accent word. Italic serif in clay — one idea, used sparingly. */
.acc { font-style: italic; color: var(--clay); font-weight: 400; }

.lede { font-size: clamp(1.03rem, 1.35vw, 1.17rem); color: var(--body); max-width: 62ch; }

/* A short spoken line — a promise or a turn in the argument, not another
   paragraph. Display serif, one step up from the lede, so the eye stops on it. */
.punch {
  font-family: var(--font-display);
  font-size: clamp(1.16rem, 1.7vw, 1.38rem);
  line-height: 1.35; letter-spacing: -.012em;
  color: var(--ink); max-width: 46ch;
}
.surf--dark .punch { color: var(--on-dark); }

.head { max-width: 880px; margin-bottom: var(--sp-head); }
.head--center { margin-inline: auto; text-align: center; }
.head--center .lede { margin-inline: auto; }

.note-sm { font-size: 14.6px; color: var(--muted); }

/* ---------- 5. Icons (one set: Lucide-style outline, stroke 1.5) --------- */
.ico {
  width: 24px; height: 24px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
}
.ico--sm { width: 17px; height: 17px; stroke-width: 1.8; }
.ico--lg { width: 28px; height: 28px; stroke-width: 1.35; }

/* ---------- 6. Buttons & links ------------------------------------------ */
.btn {
  position: relative; overflow: hidden; isolation: isolate;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-3);
  min-height: 54px; padding: var(--sp-4) var(--sp-7);
  border: 1px solid transparent; border-radius: var(--r-xs);
  font-size: 12px; font-weight: 600; letter-spacing: .17em; text-transform: uppercase;
  text-decoration: none; white-space: nowrap;
  transition: background-color .3s var(--ease), color .3s var(--ease),
              border-color .3s var(--ease), transform .3s var(--ease);
}
.btn__t, .btn .ico { position: relative; z-index: 2; }

/* The brushed-brass sweep. Metallic only ever appears as light passing over. */
.btn::after {
  content: ""; position: absolute; inset: -1px; z-index: 1;
  background: linear-gradient(104deg,
    transparent 32%,
    rgba(216, 185, 120, .00) 40%,
    rgba(216, 185, 120, .55) 49%,
    rgba(255, 250, 240, .30) 53%,
    rgba(216, 185, 120, .00) 62%,
    transparent 70%);
  transform: translateX(-130%);
  transition: transform .85s var(--ease-out);
}
.btn:hover::after, .btn:focus-visible::after { transform: translateX(130%); }

.btn--ink   { background: var(--ink); color: var(--ivory); }
.btn--ink:hover { background: var(--espresso); }
.btn--clay  { background: var(--clay-btn); color: #FFF7F1; }
.btn--clay:hover { background: var(--clay-btn-lift); }
.btn--line  { background: transparent; color: var(--ink); border-color: rgba(27, 24, 21, .30); }
.btn--line:hover { border-color: var(--ink); }
.surf--dark .btn--line { color: var(--ivory); border-color: rgba(239, 231, 218, .32); }
.surf--dark .btn--line:hover { border-color: var(--ivory); }
.btn--sm { min-height: 44px; padding: var(--sp-3) var(--sp-5); font-size: 11px; letter-spacing: .15em; }
.btn-row { display: flex; flex-wrap: wrap; gap: var(--sp-3); }

/* Text link with a clay rule that grows underneath it. */
.tlink {
  position: relative;
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-size: 11.5px; font-weight: 600; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink); text-decoration: none; padding-bottom: var(--sp-2);
}
.tlink::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  height: 1px; background: var(--line);
}
.tlink::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 0; height: 1px; background: var(--clay);
  transition: width .45s var(--ease-out);
}
.tlink:hover::after { width: 100%; }
.tlink:hover { color: var(--clay-ink); }
.tlink .ico { transition: transform .35s var(--ease-out); }
.tlink:hover .ico { transform: translateX(4px); }
.surf--dark .tlink { color: var(--ivory); }
.surf--dark .tlink::before { background: var(--line-dark); }
.surf--dark .tlink:hover { color: var(--brass-lite); }
.surf--dark .tlink:hover::after { background: var(--brass-lite); }

/* ---------- 7. Top strip, header, navigation ---------------------------- */
.strip {
  background: var(--espresso); color: var(--on-dark-2);
  font-size: 10.5px; font-weight: 500; letter-spacing: .22em; text-transform: uppercase;
}
.strip__in { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); height: 36px; }
.strip__dot {
  display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: #5CB874; margin-right: var(--sp-2); vertical-align: 1px;
}
@media (max-width: 720px) { .strip__in > :first-child { display: none; } .strip__in { justify-content: center; } }

.hdr {
  position: sticky; top: 0; z-index: 90;
  background: rgba(251, 246, 238, .88);
  backdrop-filter: saturate(160%) blur(12px);
  -webkit-backdrop-filter: saturate(160%) blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color .3s var(--ease), background-color .3s var(--ease);
}
.hdr.is-stuck { border-bottom-color: var(--line); }
.hdr__in { display: flex; align-items: center; gap: var(--sp-6); height: 82px; }

.brand { display: flex; align-items: baseline; gap: var(--sp-3); text-decoration: none; margin-right: auto; }
.brand__name {
  font-family: var(--font-display); font-size: 25px; font-weight: 600;
  color: var(--ink); letter-spacing: -.02em; line-height: 1; white-space: nowrap;
}
/* Only the middle word is clay and italic, so the header wordmark reads the
   same way as the big one in the footer (.ftr__mark i). Newsreader's italic
   is loaded at 400–600, so this is a real italic cut, not a faked slant. */
.brand__accent { color: var(--clay); font-style: italic; }
.brand__rule { width: 24px; height: 1px; background: var(--brass); flex: none; }
.brand__sub {
  font-size: 9.5px; font-weight: 600; letter-spacing: .24em; text-transform: uppercase;
  color: var(--muted); white-space: nowrap;
}
@media (max-width: 560px) { .brand__rule, .brand__sub { display: none; } }

.nav { display: flex; align-items: center; gap: var(--sp-1); }
/* :not(.btn) matters — the menu drawer holds a button, which is also a link.
   Without it, that button would take the menu's dark text on its dark fill. */
.nav a:not(.btn) {
  position: relative; padding: var(--sp-3); white-space: nowrap;
  font-size: 10.5px; font-weight: 600; letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-2); text-decoration: none;
  transition: color .25s var(--ease);
}
.nav a:not(.btn)::after {
  content: ""; position: absolute; left: var(--sp-3); right: var(--sp-3); bottom: var(--sp-1);
  height: 1px; background: var(--clay); transform: scaleX(0); transform-origin: left;
  transition: transform .4s var(--ease-out);
}
.nav a:not(.btn):hover { color: var(--clay-ink); }
.nav a:not(.btn):hover::after { transform: scaleX(1); }
.nav > .btn { display: none; }
.nav > .btn::after { display: none; }

.burger {
  display: none; width: 46px; height: 46px;
  border: 1px solid var(--line); border-radius: var(--r-xs);
  background: transparent; color: var(--ink);
  align-items: center; justify-content: center;
}

@media (max-width: 1200px) {
  .nav, .hdr > .wrap > .btn { display: none; }
  .burger { display: flex; }
  .nav.is-open {
    display: flex; flex-direction: column; align-items: stretch;
    position: absolute; inset: 82px 0 auto; padding: var(--sp-3) var(--gut) var(--sp-7);
    background: var(--ivory); border-bottom: 1px solid var(--line);
    box-shadow: var(--shadow-soft); gap: 0;
  }
  .nav.is-open a:not(.btn) { padding: var(--sp-4) 0; font-size: 12px; border-bottom: 1px solid var(--line-soft); }
  .nav.is-open a:not(.btn)::after { display: none; }
  .nav.is-open .btn { display: inline-flex; margin-top: var(--sp-4); }
}

/* ---------- 8. Hero ------------------------------------------------------ */
/* The hero runs into the thin ticker band, so its bottom is one step tighter
   than a full section — the ticker's own padding makes up the difference. */
.hero { padding-block: clamp(48px, 6vw, 80px) clamp(56px, 7vw, 88px); position: relative; overflow: hidden; }
.hero::before {
  content: ""; position: absolute; z-index: 0; pointer-events: none;
  width: 900px; height: 900px; right: -300px; top: -380px; border-radius: 50%;
  background: radial-gradient(circle, rgba(196, 98, 45, .13), rgba(196, 98, 45, 0) 66%);
}
.hero__in {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
  gap: clamp(40px, 5vw, 72px); align-items: center;
}
.hero h1 { margin-bottom: var(--sp-7); }
.hero .lede { font-size: clamp(1.06rem, 1.5vw, 1.24rem); margin-bottom: var(--sp-5); max-width: 46ch; }
.hero .punch { margin-bottom: var(--sp-7); }
.hero .btn-row { margin-bottom: var(--sp-8); }

/* Three small facts on a hairline — a magazine standfirst, not a badge row. */
.facts { display: flex; flex-wrap: wrap; gap: 0 var(--sp-7); border-top: 1px solid var(--line); padding-top: var(--sp-5); }
.facts div { display: flex; align-items: center; gap: var(--sp-2); font-size: 13.2px; color: var(--body); }
.facts .ico { color: var(--clay); }

/* Hero visual: hairline-framed browser shot with a second project tucked under. */
.collage { position: relative; padding: 0 0 var(--sp-10) var(--sp-8); }
.collage__main { box-shadow: var(--shadow-soft); }
.collage__sub {
  position: absolute; left: 0; bottom: 0; width: 46%; z-index: 2;
  border: 6px solid var(--paper); box-shadow: var(--shadow-lift);
}
.collage img { width: 100%; }
.collage__cap {
  position: absolute; right: 0; bottom: var(--sp-4); z-index: 3;
  display: flex; align-items: center; gap: var(--sp-3);
  font-size: 10px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase;
  color: var(--muted);
}
.collage__cap::before { content: ""; width: 24px; height: 1px; background: var(--brass); }

@media (max-width: 940px) {
  .hero__in { grid-template-columns: 1fr; }
  .collage { margin-top: var(--sp-4); padding-left: var(--sp-6); padding-bottom: var(--sp-9); }
  .collage__sub { width: 46%; }
}

/* ---------- 9. Live-work ticker ----------------------------------------- */
.ticker {
  border-block: 1px solid var(--line); background: var(--paper);
  overflow: hidden; padding-block: var(--sp-5);
}
.ticker__track { display: flex; width: max-content; animation: slide 44s linear infinite; }
.ticker:hover .ticker__track, .ticker:focus-within .ticker__track { animation-play-state: paused; }
.ticker__group { display: flex; align-items: center; }
.ticker__item {
  display: inline-flex; align-items: center; gap: var(--sp-3);
  padding-inline: var(--sp-7); text-decoration: none;
  font-family: var(--font-display); font-size: clamp(1.24rem, 2.2vw, 1.75rem);
  font-weight: 400; color: var(--ink); letter-spacing: -.02em;
  transition: color .25s var(--ease);
}
.ticker__item:hover { color: var(--clay); }
.ticker__item .ico { color: var(--brass); }
.ticker__sep { color: var(--clay); font-size: 12px; }
@keyframes slide { to { transform: translateX(-50%); } }

/* ---------- 10. Stats band ---------------------------------------------- */
.stats {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px; background: var(--line); border: 1px solid var(--line);
}
@media (max-width: 780px) { .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.stat { background: var(--sand); padding: clamp(24px, 4vw, 48px) clamp(16px, 2.4vw, 32px); }
.stat b {
  display: block; font-family: var(--font-display); font-weight: 500;
  font-size: clamp(2.75rem, 5.4vw, 4.5rem); line-height: 1; letter-spacing: -.03em;
  color: var(--ink); margin-bottom: var(--sp-4);
}
.stat b.is-clay { color: var(--clay); }
.stat span { display: block; font-size: 13.6px; color: var(--body); max-width: 22ch; }

/* ---------- 11. Editorial hairline grid (services, included) ------------- */
.eg { display: grid; gap: 1px; background: var(--line); border-block: 1px solid var(--line); }
.eg--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 960px) { .eg--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .eg--3 { grid-template-columns: minmax(0, 1fr); } }

.cell {
  position: relative; background: var(--ivory);
  padding: clamp(28px, 3vw, 40px) clamp(20px, 2.4vw, 32px);
  transition: background-color .4s var(--ease);
}
.surf--sand .cell { background: var(--sand); }
.cell:hover { background: var(--paper); }
.surf--sand .cell:hover { background: var(--paper); }

.cell__no {
  font-family: var(--font-display); font-size: 14px; font-weight: 600;
  color: var(--brass); letter-spacing: .04em; display: block; margin-bottom: var(--sp-6);
  transition: color .3s var(--ease);
}
.cell:hover .cell__no { color: var(--clay); }
.cell .ico--lg { color: var(--ink); margin-bottom: var(--sp-5); }
.cell:hover .ico--lg { color: var(--clay); }
.cell h3 { font-size: clamp(1.26rem, 1.7vw, 1.5rem); margin-bottom: var(--sp-3); }
.cell h3.h-sm { font-size: clamp(1.12rem, 1.4vw, 1.26rem); }
.cell p { font-size: 15px; color: var(--body); margin: 0; }

/* The one filled clay block on the page. */
/* Seven services plus the "Not sure what you need?" card is eight cells, which
   leaves one empty slot in a three-column grid. The clay card takes the spare
   width instead, so the row closes and the one call to action gets more room.
   At two columns (960px down) eight cells already divide evenly, so this only
   applies to the widest layout. */
@media (min-width: 961px) {
  .eg--3 .cell--clay:last-child { grid-column: span 2; }
}

.cell--clay { background: var(--clay); color: #FBEDE3; }
.cell--clay:hover { background: var(--clay-lift); }
.cell--clay h3 { color: #FFF9F4; }
.cell--clay p { color: #F6DFD1; margin-bottom: var(--sp-6); }
.cell--clay .cell__no, .cell--clay:hover .cell__no { color: #F0CDB6; }
.cell--clay .ico--lg, .cell--clay:hover .ico--lg { color: #FFF9F4; }

/* ---------- 12. Journey timeline ---------------------------------------- */
.flow { position: relative; display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 0; }
.flow::before {
  /* 26px = half the 52px circle, so the rule runs through their centres.
     This is a size calculation, not a spacing step. */
  content: ""; position: absolute; left: 0; right: 0; top: 26px; height: 1px;
  background: var(--line);
}
.flow__step { position: relative; padding: 0 var(--sp-3); text-align: left; }
.flow__no {
  position: relative; z-index: 1; display: inline-flex; align-items: center; justify-content: center;
  width: 52px; height: 52px; border-radius: 50%;
  background: var(--sand); border: 1px solid var(--line);
  font-family: var(--font-display); font-size: 16px; font-weight: 600; color: var(--ink);
  margin-bottom: var(--sp-5);
  transition: background-color .35s var(--ease), border-color .35s var(--ease), color .35s var(--ease);
}
.flow__step:hover .flow__no { background: var(--clay); border-color: var(--clay); color: #FFF7F1; }
.flow__step:last-child .flow__no { background: var(--ink); border-color: var(--ink); color: var(--brass-lite); }
.flow__step b { display: block; font-family: var(--font-display); font-size: 1.1rem; font-weight: 600; color: var(--ink); line-height: 1.28; }
/* :not() keeps this away from .flow__no, which is also a span and would
   otherwise be turned back into a plain block and lose its centring. */
.flow__step span:not(.flow__no) { display: block; font-size: 13.2px; color: var(--body); margin-top: var(--sp-2); line-height: 1.5; }

@media (max-width: 1080px) {
  .flow { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-7) 0; }
  .flow::before { display: none; }
  .flow__step { padding-left: 0; }
}
/* ---- Phones: the journey becomes a real vertical timeline --------------
   Two columns of small boxes did not read as a sequence. Below 640px the six
   steps stack into one line running down the page, with a track that fills in
   clay as you scroll and circles that light up as the fill reaches them.

   The three lengths below are set by JavaScript from the real measured
   positions of the first and last circles, so the track starts and stops
   exactly on their centres whatever the text length. The fallbacks keep a
   sensible line if JavaScript never runs.                                */
@media (max-width: 640px) {
  .flow {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }

  /* The unfilled track. 25px puts a 2px line through the centre of the
     52px circle. That is a size calculation, not a spacing step. */
  .flow::before {
    display: block;
    left: 25px; right: auto;
    top: var(--flow-top, 26px);
    height: var(--flow-track, calc(100% - 52px));
    width: 2px;
    background: var(--line);
  }

  /* The filled part. Brass at the start, deepening to clay — so the line
     itself shows how far through the journey the reader has come. */
  .flow::after {
    content: ""; position: absolute;
    left: 25px;
    top: var(--flow-top, 26px);
    width: 2px;
    height: var(--flow-fill, 0px);
    background: linear-gradient(180deg, var(--brass) 0%, var(--clay) 100%);
  }

  /* Circle in a fixed left column, its two lines of text beside it. The
     positions are explicit because auto-placement would drop the second
     line of text underneath the circle instead of next to the first. */
  .flow__step {
    display: grid;
    grid-template-columns: 52px minmax(0, 1fr);
    column-gap: var(--sp-5);
    align-items: start;
    padding: 0 0 var(--sp-8);
  }
  .flow__step:last-child { padding-bottom: 0; }

  .flow__no {
    grid-column: 1; grid-row: 1 / span 2;
    margin-bottom: 0;
    /* Sand, not transparent, so the track passes behind the circle
       instead of through the number. */
    background: var(--sand);
  }
  .flow__step b { grid-column: 2; grid-row: 1; align-self: center; min-height: 52px; display: flex; align-items: center; }
  .flow__step span:not(.flow__no) { grid-column: 2; grid-row: 2; margin-top: 0; }

  /* Not reached yet: quiet. Reached: full colour. This is the colour change
     that happens as you scroll. */
  .js .flow__step b { color: var(--muted); transition: color .45s var(--ease); }
  .js .flow__step span:not(.flow__no) { color: var(--muted); transition: color .45s var(--ease); }
  .js .flow__step.is-on b { color: var(--ink); }
  .js .flow__step.is-on span:not(.flow__no) { color: var(--body); }

  .js .flow__step.is-on .flow__no {
    background: var(--clay); border-color: var(--clay); color: #FFF7F1;
  }
  /* The last step is the destination, so it lands on ink rather than clay —
     and only once it is actually reached, unlike on desktop. */
  .js .flow__step:last-child .flow__no { background: var(--sand); border-color: var(--line); color: var(--ink); }
  .js .flow__step.is-on:last-child .flow__no { background: var(--ink); border-color: var(--ink); color: var(--brass-lite); }
}

/* Parallax: the text beside each circle drifts in from the right as the step
   crosses the screen. `animation-timeline: view()` ties an animation to how
   far an element has travelled through the viewport, so the phone's own
   compositor drives it — no scroll handler, nothing recalculated per frame.
   Phones without it simply get the colour change, which is the main effect. */
@supports (animation-timeline: view()) {
  @media (max-width: 640px) and (prefers-reduced-motion: no-preference) {
    .js .flow__step b,
    .js .flow__step span:not(.flow__no) {
      animation: flowDrift linear both;
      animation-timeline: view();
      animation-range: entry 10% cover 45%;
    }
    @keyframes flowDrift {
      from { transform: translateX(14px); }
      to   { transform: none; }
    }
  }
}

@media (prefers-reduced-motion: reduce) {
  .flow::after { transition: none; }
  .js .flow__step b, .js .flow__step span:not(.flow__no), .js .flow__step .flow__no { transition: none; }
}

.flow__caption {
  margin-top: var(--sp-block); padding-top: var(--sp-6); border-top: 1px solid var(--line);
  font-size: 15.6px; color: var(--body); max-width: 74ch;
}
.flow__caption strong { color: var(--ink); font-weight: 600; }

/* ---------- 13. Browser frame (every preview uses this) ------------------ */
.frame { overflow: hidden; background: var(--paper); border: 1px solid var(--line); }
.frame__bar {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3); background: var(--sand); border-bottom: 1px solid var(--line);
}
.frame__dot { width: 8px; height: 8px; border-radius: 50%; background: #D2C5AE; flex: none; }
.frame__url {
  margin-left: var(--sp-2); flex: 1; min-width: 0;
  background: var(--paper); border: 1px solid var(--line-soft);
  padding: var(--sp-1) var(--sp-3); font-size: 11px; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  letter-spacing: .02em;
}
.frame__shot { position: relative; aspect-ratio: 16 / 10; overflow: hidden; background: var(--sand); }
.frame__shot img, .frame__shot video {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .9s var(--ease-out);
}
.surf--dark .frame { border-color: rgba(239, 231, 218, .18); background: rgba(239, 231, 218, .04); }
.surf--dark .frame__bar { background: rgba(239, 231, 218, .07); border-bottom-color: rgba(239, 231, 218, .12); }
.surf--dark .frame__url { background: rgba(16, 14, 12, .5); border-color: rgba(239, 231, 218, .12); color: var(--on-dark-2); }
.surf--dark .frame__dot { background: rgba(239, 231, 218, .3); }

/* ---------- 14. Website examples ---------------------------------------- */
.filters { display: flex; flex-wrap: wrap; gap: 0; margin-bottom: var(--sp-block); border-top: 1px solid var(--line); }
.chip {
  border: 0; border-bottom: 1px solid transparent; background: transparent;
  min-height: 48px; padding: var(--sp-4) var(--sp-5) var(--sp-4) 0; margin-right: var(--sp-6);
  font-size: 11px; font-weight: 600; letter-spacing: .15em; text-transform: uppercase;
  color: var(--muted);
  transition: color .25s var(--ease), border-color .25s var(--ease);
}
.chip:hover { color: var(--ink); }
.chip[aria-pressed="true"] { color: var(--clay-ink); border-bottom-color: var(--clay); }

.exgrid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(32px, 4vw, 56px) clamp(24px, 2.6vw, 32px); }
@media (max-width: 1000px) { .exgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .exgrid { grid-template-columns: minmax(0, 1fr); } }

/* No boxes. The picture and its caption, the way a magazine sets a plate. */
.ex { display: flex; flex-direction: column; }
.ex[hidden] { display: none; }
.ex:hover .frame__shot img { transform: scale(1.05); }
.ex__body { padding-top: var(--sp-6); display: flex; flex-direction: column; flex: 1; }
.ex__top { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-3); }
.ex__ico { color: var(--brass); }
.ex__cat { font-size: 10px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: var(--brass); }
.ex h3 { font-size: clamp(1.28rem, 1.7vw, 1.52rem); margin-bottom: var(--sp-3); }
.ex p { font-size: 14.8px; color: var(--body); margin-bottom: var(--sp-6); }
.ex__foot { margin-top: auto; display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); flex-wrap: wrap; }

.tag {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-size: 10px; font-weight: 600; letter-spacing: .16em; text-transform: uppercase;
  padding: var(--sp-1) 0;
}
.tag::before { content: ""; width: 6px; height: 6px; border-radius: 50%; flex: none; }
.tag--live { color: var(--ink); }
.tag--live::before { background: var(--clay); }
.tag--demo { color: var(--muted); }
.tag--demo::before { background: var(--brass); }

/* ---------- 15. Software & SaaS (the single dark section) ---------------- */
.cap-list {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0 clamp(24px, 3vw, 48px);
  margin: 0 0 var(--sp-head); padding: 0; list-style: none;
}
@media (max-width: 900px) { .cap-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* A checklist is meant to be scanned at a glance, so it keeps two columns
   down to the smallest phones instead of becoming one tall stack. */
@media (max-width: 379px) { .cap-list { grid-template-columns: minmax(0, 1fr); } }
.cap-list li {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4) 0; font-size: 14.8px; color: var(--on-dark);
  border-bottom: 1px solid var(--line-dark);
}
@media (max-width: 619px) {
  .cap-list li { padding: var(--sp-3) 0; align-items: flex-start; }
  .cap-list .ico { margin-top: 3px; }
}
.cap-list .ico { color: var(--clay); }

.saas { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(32px, 3.4vw, 48px); }
@media (max-width: 900px) { .saas { grid-template-columns: minmax(0, 1fr); } }
.saas__card .frame { margin-bottom: var(--sp-6); }
/* 16:9, so the screen recording is shown whole instead of having its edges
   cropped. Both software cards use it, so the pair still lines up. */
.saas__card .frame__shot { aspect-ratio: 16 / 9; }
.saas__card:hover .frame__shot img { transform: scale(1.045); }
.saas__meta {
  display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-4);
  font-size: 10px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: var(--brass-lite);
}
.saas__card h3 { font-size: clamp(1.42rem, 2vw, 1.8rem); margin-bottom: var(--sp-3); }
.saas__card p { color: var(--on-dark-2); font-size: 15.2px; margin-bottom: var(--sp-6); }

/* ---------- 16. Process steps -------------------------------------------- */
.steps { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: clamp(24px, 3vw, 40px); counter-reset: s; }
@media (max-width: 940px) { .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .steps { grid-template-columns: minmax(0, 1fr); } }
.step { position: relative; padding-top: var(--sp-6); border-top: 1px solid var(--ink); counter-increment: s; }
.step::before {
  content: "0" counter(s);
  font-family: var(--font-display); font-size: clamp(2.4rem, 3.6vw, 3.15rem); font-weight: 500;
  color: var(--clay); line-height: 1; display: block; margin-bottom: var(--sp-5); letter-spacing: -.025em;
}
.step h3 { font-size: 1.28rem; margin-bottom: var(--sp-3); }
.step p { font-size: 14.9px; color: var(--body); }

/* ---------- 17. Panels, checklist, contact ------------------------------- */
.split { display: grid; grid-template-columns: minmax(0, 1.08fr) minmax(0, .92fr); gap: clamp(32px, 5vw, 72px); align-items: start; }
@media (max-width: 940px) { .split { grid-template-columns: minmax(0, 1fr); } }

.panel { background: var(--paper); border: 1px solid var(--line); padding: clamp(28px, 3.2vw, 40px); }

.check { list-style: none; margin: 0; padding: 0; }
.check li {
  display: flex; align-items: flex-start; gap: var(--sp-4);
  font-size: 15px; color: var(--body);
  padding: var(--sp-4) 0; border-bottom: 1px solid var(--line-soft);
}
.check li:last-child { border-bottom: 0; padding-bottom: 0; }
.check .ico { color: var(--clay); margin-top: var(--sp-1); }
.check b { color: var(--ink); font-weight: 600; }

/* Three ways to reach me, side by side. They stack on a narrow screen so the
   text inside never has to squeeze. */
.cboxes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-3); }
@media (max-width: 820px) { .cboxes { grid-template-columns: minmax(0, 1fr); } }

.cbox {
  display: flex; align-items: center; gap: var(--sp-4);
  background: var(--ink); border: 1px solid var(--ink);
  padding: var(--sp-6); text-decoration: none;
  transition: background-color .3s var(--ease), transform .3s var(--ease);
}
.cbox:hover { background: var(--espresso); transform: translateX(4px); }
.cbox__ico { color: var(--brass-lite); flex: none; }
.cbox b { display: block; color: var(--ivory); font-size: 15.4px; font-weight: 600; }
.cbox span { display: block; font-size: 12.6px; color: var(--on-dark-2); word-break: break-word; letter-spacing: .04em; }

.closer { text-align: center; margin-top: clamp(56px, 7vw, 80px); padding-top: var(--sp-block); border-top: 1px solid var(--line); }
.closer h3 { margin-bottom: var(--sp-6); }

/* ---------- 17b. Enquiry form -------------------------------------------
   The only form on the site. It writes an email rather than posting to a
   server, so it needs no service, no key and no cookie banner.

   It sits on ink rather than paper on purpose: the form is the one thing on
   this page that asks the visitor to do work, and the dark panel lifts it off
   the ivory so it reads as a separate, deliberate step. Fields keep the
   editorial look — a hairline underneath, no box, no rounded corner. */
.fbox {
  margin-top: clamp(40px, 5vw, 64px);
  background: var(--ink); border: 1px solid var(--ink);
  padding: clamp(28px, 3.4vw, 48px);
}
.fbox__head { max-width: 62ch; margin-bottom: var(--sp-8); }
.fbox__head h3 { margin-bottom: var(--sp-4); }
.fbox .note-sm { color: var(--on-dark-2); }

.form__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-6) var(--sp-7); }
@media (max-width: 700px) { .form__grid { grid-template-columns: minmax(0, 1fr); } }
.fld--wide { grid-column: 1 / -1; }

.fld { display: flex; flex-direction: column; gap: var(--sp-2); margin: 0; }
.fld label {
  font-family: var(--font-body); font-size: 12.4px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ivory);
  display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap;
}
.fld__req, .fld__opt {
  font-size: 10.6px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase;
}
.fld__req { color: var(--brass-lite); }
.fld__opt { color: var(--on-dark-2); }

.fld input, .fld textarea {
  font-family: var(--font-body); font-size: 15.5px; color: var(--ivory);
  background: transparent; border: 0; border-bottom: 1px solid rgba(239, 231, 218, .22);
  padding: var(--sp-3) 0; width: 100%; border-radius: 0;
  transition: border-color .25s var(--ease), background-color .25s var(--ease);
}
.fld textarea { resize: vertical; line-height: 1.5; }
.fld input::placeholder, .fld textarea::placeholder { color: #7E756B; opacity: 1; }

/* The focus mark has to be obvious for anyone tabbing through with a keyboard,
   so the hairline thickens to brass and the field lifts on a faint wash. */
.fld input:focus, .fld textarea:focus {
  outline: 0;
  border-bottom: 2px solid var(--brass-lite);
  padding-bottom: calc(var(--sp-3) - 1px);
  background: rgba(239, 231, 218, .05);
}
.fld.is-bad input, .fld.is-bad textarea { border-bottom-color: var(--clay); }

/* A dark field is still a light field to the browser, so the yellow autofill
   wash has to be painted over or the boxes turn pale mid-form. */
.fld input:-webkit-autofill,
.fld input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--ivory);
  box-shadow: 0 0 0 1000px var(--ink) inset;
  caret-color: var(--ivory);
}

.form__note {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  font-size: 14.4px; color: var(--on-dark-2);
  margin: var(--sp-8) 0 var(--sp-6);
  padding-top: var(--sp-6); border-top: 1px solid var(--line-dark);
}
.form__note .ico { color: var(--brass-lite); margin-top: 3px; }

.form__msg {
  margin-top: var(--sp-5); font-size: 14.6px; color: var(--on-dark);
  border-left: 2px solid var(--brass-lite); padding-left: var(--sp-4);
}
/* The fallback link only ever appears inside this message, so it is styled
   here rather than as a component of its own. */
.form__msg a {
  display: inline-block; margin-top: var(--sp-3);
  color: var(--brass-lite); font-weight: 600;
  text-decoration: underline; text-underline-offset: 4px;
}
.form__msg a:hover { color: var(--ivory); }

/* ---------- 18. Footer --------------------------------------------------- */
.ftr { background: var(--espresso); color: var(--on-dark-2); padding-block: clamp(56px, 7vw, 80px) var(--sp-7); }
.ftr__mark {
  font-family: var(--font-display); font-weight: 500;
  font-size: clamp(2.4rem, 7.2vw, 4.85rem); line-height: 1.04; letter-spacing: -.03em;
  color: var(--ivory); margin: 0 0 var(--sp-head);
}
.ftr__mark i { font-style: italic; color: var(--clay); }
.ftr__in { display: grid; grid-template-columns: minmax(0, 1.5fr) repeat(2, minmax(0, 1fr)); gap: var(--sp-8); padding-top: var(--sp-block); border-top: 1px solid var(--line-dark); }
@media (max-width: 820px) { .ftr__in { grid-template-columns: minmax(0, 1fr); gap: var(--sp-7); } }
.ftr h4 {
  color: var(--brass-lite); font-family: var(--font-body); font-size: 10px; font-weight: 600;
  letter-spacing: .24em; text-transform: uppercase; margin-bottom: var(--sp-5);
}
/* :not() keeps this off the big wordmark, which is also a paragraph. */
.ftr p:not(.ftr__mark) { font-size: 14.6px; max-width: 44ch; }
.ftr ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-3); }
.ftr a { color: var(--on-dark-2); text-decoration: none; font-size: 14.6px; transition: color .25s var(--ease); }
.ftr a:hover { color: var(--brass-lite); }
.ftr__base {
  margin-top: var(--sp-block); padding-top: var(--sp-6); border-top: 1px solid var(--line-dark);
  display: flex; justify-content: space-between; gap: var(--sp-4); flex-wrap: wrap;
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
}

/* ---------- 19. Motion ---------------------------------------------------
   Everything that starts hidden is guarded by the .js class, which is set
   in the page head. If JavaScript never runs, nothing is ever hidden. */

/* Blocks rise into place, in sequence, once they reach the screen. */
.js .rv { opacity: 0; transform: translateY(var(--sp-6)); }
.rv.is-in {
  opacity: 1; transform: none;
  transition: opacity .8s var(--ease-out), transform .8s var(--ease-out);
  transition-delay: var(--d, 0ms);
}

/* Headline lines slide up from behind a hidden edge, one after another. */
/* The hidden edge each headline line slides up from. The padding has to clear
   the font's descenders (the tails on g, y, p) or they get sliced off — this
   serif has deeper tails than the old one, so it needs more room. */
.ln { display: block; overflow: hidden; padding-bottom: .16em; margin-bottom: -.16em; }
.ln > span { display: block; }
.js .ln > span { transform: translateY(108%); }
.is-in > .ln > span {
  transform: none;
  transition: transform 1s var(--ease-out);
  transition-delay: var(--d, 0ms);
}
.ln:nth-child(2) > span { --d: 90ms; }
.ln:nth-child(3) > span { --d: 180ms; }
.ln:nth-child(4) > span { --d: 270ms; }

/* The hero is already on screen when the page opens, so it plays on load
   instead of waiting for a scroll. Pure CSS — no watcher, nothing to fail. */
@keyframes rise    { from { opacity: 0; transform: translateY(26px); } to { opacity: 1; transform: none; } }
@keyframes lineUp  { from { transform: translateY(108%); }             to { transform: none; } }
@keyframes uncover { from { clip-path: inset(0 0 100% 0); }            to { clip-path: inset(0 0 0 0); } }

.lo     { animation: rise .9s var(--ease-out) both; animation-delay: var(--d, 0ms); }
.lo-img { animation: uncover 1.15s var(--ease-out) both; animation-delay: var(--d, 0ms); }
.lo-lines > .ln > span { animation: lineUp 1s var(--ease-out) both; }
.lo-lines > .ln:nth-child(1) > span { animation-delay: 100ms; }
.lo-lines > .ln:nth-child(2) > span { animation-delay: 190ms; }
.lo-lines > .ln:nth-child(3) > span { animation-delay: 280ms; }
.lo-lines > .ln:nth-child(4) > span { animation-delay: 370ms; }

/* Pictures uncover from the bottom edge instead of just fading. */
.js .rv-img { clip-path: inset(0 0 100% 0); }
.rv-img.is-in {
  clip-path: inset(0 0 0 0);
  transition: clip-path 1.1s var(--ease-out);
  transition-delay: var(--d, 0ms);
}

@media (prefers-reduced-motion: reduce) {
  .js .rv, .js .rv-img, .rv, .rv-img { opacity: 1; transform: none; clip-path: none; }
  .rv.is-in, .rv-img.is-in { transition: none; }
  .js .ln > span, .ln > span, .is-in > .ln > span { transform: none; transition: none; }
  .lo, .lo-img, .lo-lines > .ln > span { animation: none; }
  .ticker__track { animation: none; }
  .btn::after { display: none; }
  .btn:hover, .cbox:hover { transform: none; }
  .frame__shot img { transition: none; }
}

/* ---------- 20. Sideways-swipe rows (phones only) ------------------------
   On a phone the card grids used to become one tall column — 15 example
   cards stacked is around 6,000px of scrolling. Below the widths where each
   grid already dropped to a single column, the cards are laid side by side
   instead and swiped with a thumb, the way a photo gallery works.

   `scroll-snap-type` is a browser feature that gently clicks the row into
   place on each card, so a swipe never stops halfway between two cards.
   The swipe itself is the phone's own native scrolling — no JavaScript
   drives it, so it stays smooth on a cheap Android phone and needs no
   reduced-motion switch (scrolling is not an animation).                  */
.rail-prog { display: none; }
.rail-aside { display: none; }

/* Both rows switch on at the same width on purpose. If one switched at 619px
   and the other at 639px, the shared "01 / 08" counter would appear under a
   grid that was still a grid. One breakpoint, no mismatch. */
@media (max-width: 639px) {
  .eg--rail {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    gap: var(--sp-3);
    background: none;
    border-block: 0;
    /* Bleed past the page gutter so a card can touch the true screen edge.
       That is what lets the next card peek in and hint "there is more". */
    margin-inline: calc(var(--gut) * -1);
    padding-inline: var(--gut);
    padding-bottom: var(--sp-2);
    scroll-padding-inline: var(--gut);
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .eg--rail::-webkit-scrollbar { display: none; }

  .eg--rail .cell {
    flex: 0 0 82%;                /* leaves 18% so the next card peeks in */
    scroll-snap-align: start;
    scroll-snap-stop: always;     /* a fast flick cannot skip past a card */
    border: 1px solid var(--line);
  }

  /* The "Not sure what you need?" card is last in the row, so this line
     repeats the offer where nobody has to swipe to find it. */
  .rail-aside { display: block; margin-top: var(--sp-block); }
}

/* ---- Website examples: a stacked deck on phones ------------------------
   Fifteen cards in one column was an endless ribbon, and sideways swiping
   was worse to look at. Instead each card is `position: sticky`, so it holds
   still in place while the next card rises up and settles over it — the
   reader is always looking at one card, held at the same spot, instead of
   chasing a moving list.

   This is ordinary vertical scrolling. Nothing is hijacked, no JavaScript
   drives it, and the phone's own compositor does the work, so it stays
   smooth on a cheap Android handset.

   Honest limit: stacking does not shorten the scroll much — fifteen cards
   is still fifteen cards. What it changes is how it *feels*, and the filter
   buttons above are what actually cut fifteen down to two or three.      */
@media (max-width: 639px) {
  .exgrid--stack { display: block; }

  .exgrid--stack .ex {
    /* 90px clears the 82px sticky header plus a little air. */
    position: sticky;
    top: 90px;
    /* A card in a deck has to be opaque, or the one underneath shows
       through. This is the one place cards get a fill and a shadow — it is
       what makes the stack read as a stack. Hairlines alone cannot. */
    background: var(--ivory);
    border: 1px solid var(--line);
    padding: var(--sp-4) var(--sp-4) var(--sp-5);
    box-shadow: 0 -14px 34px -22px rgba(27, 24, 21, .45);
  }
  .surf--sand .exgrid--stack .ex { background: var(--sand); }

  /* The gap between cards is the scroll distance between one settling and
     the next arriving. Smaller gap, quicker succession. */
  .exgrid--stack .ex + .ex { margin-top: var(--sp-7); }

  /* A card hidden by the industry filter must not hold its slot open. */
  .exgrid--stack .ex[hidden] { display: none; }

  /* Shorter picture on a phone, so more of each card is words rather than
     image and the deck moves through at a sensible pace. */
  .exgrid--stack .ex .frame__shot { aspect-ratio: 16 / 9; }

  /* Room after the last card so it can settle before the section ends,
     instead of being shoved off by the paragraph below. */
  .exgrid--stack { padding-bottom: var(--sp-9); }
}

@media (max-width: 639px) {
  /* ---- The "01 / 08" mark under the services swipe row ----
     A hairline that fills in clay as you swipe, plus a serif number pair —
     the same language as the page's other hairlines and section numbers,
     not a bolted-on progress bar. */
  .rail-prog {
    display: flex; align-items: center; gap: var(--sp-3);
    margin-top: var(--sp-5);
  }
  .rail-prog__track { flex: 1; height: 1px; background: var(--line); position: relative; }
  .rail-prog__fill {
    position: absolute; left: 0; top: 0; height: 1px; background: var(--clay);
    width: 0%; transition: width .3s var(--ease-out);
  }
  .rail-prog__count {
    font-family: var(--font-display); font-size: 13px; color: var(--brass);
    letter-spacing: .06em; white-space: nowrap;
  }
}

@media (prefers-reduced-motion: reduce) {
  .rail-prog__fill { transition: none; }
}

/* ---------- 21. Client quotes -------------------------------------------
   Three cells in a hairline row, matching the .eg grid language rather than
   floating cards. The pull-quote line does the work; the paragraph under it
   carries the detail. Quotes are never edited, so the type has to cope with
   whatever length an owner actually wrote.                                */
.quotes {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px; background: var(--line); border-block: 1px solid var(--line);
}
@media (max-width: 900px) { .quotes { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .quotes { grid-template-columns: minmax(0, 1fr); } }

.quote {
  margin: 0; padding: clamp(24px, 2.6vw, 36px);
  background: var(--sand);
  display: flex; flex-direction: column; gap: var(--sp-5);
}
.quote blockquote { margin: 0; flex: 1; }
.quote__lead {
  font-family: var(--font-display); font-size: clamp(1.2rem, 1.6vw, 1.42rem);
  font-weight: 500; line-height: 1.24; letter-spacing: -.012em;
  color: var(--ink); margin: 0 0 var(--sp-4);
}
.quote__body { font-size: 14.8px; line-height: 1.62; color: var(--body); margin: 0; }

.quote__who { padding-top: var(--sp-4); border-top: 1px solid var(--line); }
.quote__who b {
  display: block; font-family: var(--font-display); font-size: 1.02rem;
  font-weight: 600; color: var(--ink);
}
.quote__who span { display: block; font-size: 13px; color: var(--body); margin-top: var(--sp-1); }
/* What was actually built, so a quote is never just a nice feeling. */
.quote__what {
  font-size: 11.5px !important; text-transform: uppercase; letter-spacing: .1em;
  color: var(--brass) !important; margin-top: var(--sp-2) !important;
}

/* ---------- 22. Fold-out list -------------------------------------------
   Replaces a wall of 12 boxes with 10 thin lines that open on tap. Built on
   the browser's own <details> and <summary> tags, so opening and closing
   needs no JavaScript, works with the Tab key, and is announced correctly
   by a screen reader without any extra work.                              */
.fold { border-top: 1px solid var(--line); }
.fold__item { border-bottom: 1px solid var(--line); }
.fold__item > summary { list-style: none; cursor: pointer; }
/* Removes the browser's own default triangle, so the page keeps to one
   icon style — the outline chevron drawn inline with the others. */
.fold__item > summary::-webkit-details-marker { display: none; }
.fold__item > summary::marker { content: ""; }

.fold__q {
  display: flex; align-items: center; gap: var(--sp-4);
  min-height: 56px;                 /* comfortably clears the 44px tap target */
  padding-block: var(--sp-4);
}
.fold__no {
  font-family: var(--font-display); font-size: 14px; font-weight: 600;
  color: var(--brass); letter-spacing: .04em; flex: none; width: 28px;
}
.fold__q .ico--lg { color: var(--ink); flex: none; }
.fold__t {
  font-family: var(--font-display); font-size: clamp(1.06rem, 1.4vw, 1.2rem);
  font-weight: 500; color: var(--ink); flex: 1; margin: 0; line-height: 1.3;
}
.fold__chev {
  color: var(--brass); flex: none;
  transition: transform .3s var(--ease-out);
}
.fold__item[open] .fold__chev { transform: rotate(180deg); }
.fold__item[open] .fold__q .ico--lg,
.fold__item[open] .fold__t { color: var(--clay); }
.fold__item > summary:hover .fold__q .ico--lg,
.fold__item > summary:hover .fold__t { color: var(--clay); }

.fold__body {
  margin: 0 0 var(--sp-5) 44px;     /* lines up under the heading text */
  font-size: 15px; color: var(--body); max-width: 62ch;
}
.fold__body strong { color: var(--ink); font-weight: 600; }

/* On a narrow phone the number and the big icon eat the width the heading
   needs, so the icon steps out and the text gets the room. */
@media (max-width: 480px) {
  .fold__q { gap: var(--sp-3); }
  .fold__q .ico--lg { display: none; }
  .fold__body { margin-left: 40px; }
}

@media (prefers-reduced-motion: reduce) {
  .fold__chev { transition: none; }
}
