/* ============================================================
   NORTHBOUND — WEB DESIGN AGENCY TEMPLATE

   A studio site on warm greige paper. One dark band for the piece
   that explains the work, one recessed band for the projects, and
   exactly one saturated colour.

   THE GROUND is #D3CFC6, a warm greige deliberately deeper than
   the near-white a studio site usually sits on. It is dark enough
   to read as a chosen colour rather than as an absence of one,
   while still carrying ink at 11.30 with room for three dimmed
   variants above the AA floor.

   THE ACCENT IS THE POINT, and the rule around it is the whole
   design idea. This template is built to be re-pointed at another
   studio, so the brand colour is one token pair (--accent plus
   --on-accent) and everything keys off it. That only works if the
   accent is never asked to carry text, so it is not: it appears
   as a FILL with --on-accent type on top, or as a non-text mark
   (rules, underlines, dots, nodes, the progress fill, the focus
   ring). Never as a colour applied to type.

   That is stricter than WCAG requires and the extra strictness is
   deliberate. Large text is allowed 3:1, and the shipped blue
   clears it. But the exemption stops helping the moment the hue
   changes, which is the one thing guaranteed to happen here: the
   same figure measures 2.73 on a card in a mid orange and 1.47 in
   a mid yellow, failing even 3:1. An exemption that does not
   survive a reskin is not worth taking, so the stat figures and
   the mockup's "1.8s" are ink with an accent bar beside them.

   --on-accent is its own token for the same reason. White clears
   AA on a mid blue (5.01), red (4.52), green (5.02) and purple
   (6.41); near-black is what you need on orange (4.94), yellow
   (9.16) and lime (8.89). One value cannot cover both halves.

   MEASURED, composited, as used: ink on ground 11.30, on card
   13.45, on sunk 9.57. Dimmed ink on ground runs .78 = 6.54,
   .72 = 5.51, .68 = 4.94, which is the floor; on sunk the same
   ramp is tighter at 5.05 and 4.52, so nothing fainter than
   --ink-dim goes on a recessed surface. Paper on the deep band is
   15.03, on slate 12.78. The full table is inside :root below.

   WHAT IT REFUSES: gradient meshes, glassmorphism, glowing
   borders, neon, drop shadows used as decoration, a stock-photo
   hero, and the centred "AI startup landing page" arrangement.
   There is no bundled photography at all; the project cards are
   composed in CSS so a buyer drops in their own screenshots
   rather than replacing a graded photo set.

   Host Grotesk sets the display and the headings, Rethink Sans
   carries every word that is meant to be read, and Noto Sans Mono
   is kept to labels, section numbers, weeks and figures so that
   nothing in the reading column competes with the prose.
   ============================================================ */

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

:root {
  /* ========================================================
       THE PALETTE, AND THE ONE RULE THAT HOLDS IT TOGETHER

       Nine values. Repaint them and the site is yours — but repaint
       each hex and its `-rgb` triple IN STEP. Every translucent value
       on this page (hairlines, dimmed type, washes, the focus ring)
       is mixed from a triple rather than written as a literal
       rgba(...), which is the only reason a reskin is a short edit
       instead of a hunt.

       MEASURED CONTRAST, composited, at the size each pair is used:

         ink on ground .......................... 11.30
         ink on card ............................ 13.45
         ink on sunk ............................. 9.57
         ink @ .78 on ground ..................... 6.54   section ledes
         ink @ .72 on ground ..................... 5.51   labels, meta
         ink @ .68 on ground ..................... 4.94   THE FLOOR
         ink @ .72 on card ....................... 6.06
         ink @ .68 on card ....................... 5.35
         ink @ .72 on sunk ....................... 5.05
         ink @ .68 on sunk ....................... 4.52   only just AA
         paper on deep .......................... 15.03
         paper on slate ......................... 12.78
         paper @ .78 on deep ..................... 9.57
         paper @ .70 on deep ..................... 7.96
         white on accent ......................... 5.55

       .68 is the floor for ink and there is nothing under it. It has
       no headroom on --sunk (4.52), so do not add a fainter variant
       and do not lighten --sunk past #C3BFB5.

       ---- THE ACCENT RULE (this is the whole design idea) ----

       The accent is NEVER a text colour. Not body copy, not a label,
       not a caption, and not a big display figure either. It is only
       ever:

         • a FILL, with --on-accent type on top (buttons, chips, the
           active state, the tag on a project)
         • a non-text mark: rules, underlines, dots, nodes, the
           progress fill, the focus ring

       The first half of that is the obvious part. Measured on the
       shipped blue, the accent runs 3.57 on the ground, 4.25 on a card
       and 3.00 on the deep band, all below AA for body copy.

       The second half is the part worth explaining, because it looks
       over-strict until you swap the colour. "Large text may use 3:1"
       is a real rule, and on this blue a big figure does clear it. But
       the accent is the one value here designed to be replaced, and
       the relaxed bar stops helping the moment the hue changes: the
       same figure measures 2.73 on a card in a mid orange and 1.47 in
       a mid yellow, so it fails even 3:1 and reads as nothing at all.
       An exemption you cannot rely on after a reskin is not worth
       having, so this file does not use it. The stat figures and the
       "1.8s" in the third project mockup are ink, with an accent bar
       beside them doing the emphasis.

       Keep to that and swapping --accent for anything from navy to
       lime cannot break the page's legibility.

       ---- WHY --on-accent IS ITS OWN TOKEN ----

       Because the right value depends on your hue, and there is no
       single correct answer. Measured against nine plausible brand
       colours: blue, red, green, purple, teal and pink all want WHITE
       on them (4.60–7.10 against white). Orange, yellow and lime all
       want INK (4.94–9.16 against #1A1917). So a reskin is TWO values,
       not one — set --accent, then set --on-accent to whichever of
       #FFFFFF or #1A1917 clears 4.5:1 on it, and set --on-accent-rgb
       to match.

       ---- WHAT THIS TEMPLATE REFUSES TO LOOK LIKE ----

       No gradient meshes, no glassmorphism, no glowing borders, no
       neon, no decorative drop shadows, no stock-photo hero. Depth
       here comes from three flat tones — a recessed well, the page,
       a raised card — plus hairlines. If you find yourself adding a
       shadow to separate two things, change their tone instead.
       ======================================================== */

  --ground: #e6ece8; /* the page */
  --ground-rgb: 230, 236, 232;
  --card: #f5f8f6; /* raised card or panel, lighter than the page */
  --card-rgb: 245, 248, 246;
  --sunk: #d3dcd6; /* recessed wells */
  --sunk-rgb: 211, 220, 214;
  --deep: #1c2a25; /* the dark band */
  --deep-rgb: 28, 42, 37;
  --slate: #27362f; /* raised surface on a dark band */
  --slate-rgb: 39, 54, 47;
  --ink: #1c2a25; /* all type on the light side */
  --ink-rgb: 28, 42, 37;
  --paper: #f4f6f3; /* all type on the dark side */
  --paper-rgb: 244, 246, 243;
  --accent: #3f7f63; /* the one value you swap */
  --accent-rgb: 63, 127, 99;
  --on-accent: #ffffff; /* the type colour that sits ON the accent */
  --on-accent-rgb: 255, 255, 255;

  /* Derived tones. Every one resolves from a triple above. */
  --ink-soft: rgba(var(--ink-rgb), 0.78);
  --ink-dim: rgba(var(--ink-rgb), 0.72);
  --ink-faint: rgba(var(--ink-rgb), 0.68);
  --paper-soft: rgba(var(--paper-rgb), 0.78);
  --paper-dim: rgba(var(--paper-rgb), 0.7);

  /* Band-level tokens. Every component below styles itself from
       these four, so putting `band--deep` or `band--sunk` on a section
       retones everything inside it without a single override. */
  --tone: var(--ink);
  --tone-soft: var(--ink-soft);
  --tone-dim: var(--ink-dim);
  --tone-faint: var(--ink-faint);
  --surface: var(--card);
  --surface-rgb: var(--card-rgb);
  --hair: rgba(var(--ink-rgb), 0.16);
  --hair-strong: rgba(var(--ink-rgb), 0.3);
  --wash: rgba(var(--ink-rgb), 0.05);

  --font-display: "Plus Jakarta Sans", "Helvetica Neue", Arial, sans-serif;
  --font-body: "Plus Jakarta Sans", system-ui, -apple-system, sans-serif;
  --font-mono: "Noto Sans Mono", ui-monospace, "SFMono-Regular", monospace;

  --shell: 78rem;
  --pad: clamp(1.15rem, 4vw, 3.25rem);
  --gap: clamp(1.5rem, 3vw, 2.75rem);
  --measure: 36rem;
  --radius: 14px;
  --ease-settle: cubic-bezier(0.16, 1, 0.3, 1);

  color-scheme: light;
}

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

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

body {
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.6;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

img,
svg,
video {
  display: block;
  max-width: 100%;
}
img.is-missing {
  display: none;
}

::selection {
  background: var(--accent);
  color: var(--on-accent);
}

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

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 50;
  background: var(--accent);
  color: var(--on-accent);
  padding: 0.75rem 1.25rem;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
}
.skip-link:focus {
  left: 0;
}

/* ============================================================
   ANIMATION START STATES

   Everything that animates in is hidden here, under `.has-js`, which
   the small script in the <head> adds before the first paint. Two
   consequences worth knowing before you edit any of this:

     • Nothing is hidden unless JavaScript is running. With scripts
       off, or with the GSAP CDN blocked, every line below is inert
       and the page reads in full.
     • The reduced-motion block underneath puts it all back. If you
       add a new animated element, add it to BOTH lists or it will be
       invisible for anyone who has asked their system to stop motion.
   ============================================================ */

.has-js [data-hero-title],
.has-js [data-hero-fade],
.has-js [data-reveal],
.has-js [data-reveal-group] > * {
  opacity: 0;
}

/* The mockup in the Build section. Default (no JavaScript) is the
   FINISHED state: type and colour on, placeholder boxes off. The
   `.has-js` lines wind it back to the wireframe so it has somewhere
   to travel from. */
[data-wire] {
  opacity: 0;
}
.has-js [data-wire] {
  opacity: 1;
}
.has-js [data-real],
.has-js [data-colour] {
  opacity: 0;
}
.has-js [data-build-fill] {
  transform: scaleX(0);
}

@media (prefers-reduced-motion: reduce) {
  .has-js [data-hero-title],
  .has-js [data-hero-fade],
  .has-js [data-reveal],
  .has-js [data-reveal-group] > * {
    opacity: 1;
  }

  .has-js [data-wire] {
    opacity: 0;
  }
  .has-js [data-real],
  .has-js [data-colour] {
    opacity: 1;
  }
  .has-js [data-build-fill] {
    transform: scaleX(1);
  }
}

/* ============================================================
   LAYOUT SHELL AND BANDS
   ============================================================ */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--pad);
}

.band {
  position: relative;
  padding-block: clamp(4rem, 9vw, 7.5rem);
  background: var(--ground);
  color: var(--tone);
}

.band--deep {
  --tone: var(--paper);
  --tone-soft: var(--paper-soft);
  --tone-dim: var(--paper-dim);
  --tone-faint: var(--paper-dim);
  --surface: var(--slate);
  --surface-rgb: var(--slate-rgb);
  --hair: rgba(var(--paper-rgb), 0.16);
  --hair-strong: rgba(var(--paper-rgb), 0.32);
  --wash: rgba(var(--paper-rgb), 0.06);
  background: var(--deep);
  color: var(--paper);
}

.band--sunk {
  --surface: var(--card);
  --surface-rgb: var(--card-rgb);
  background: var(--sunk);
}

/* ============================================================
   TYPE ROLES
   ============================================================ */

.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--tone-dim);
  margin-bottom: 1.25rem;
}

.section-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.9rem, 4vw, 3.15rem);
  line-height: 1.06;
  letter-spacing: -0.026em;
  max-width: 22ch;
  text-wrap: balance;
}

.section-lede {
  margin-top: 1.5rem;
  max-width: var(--measure);
  color: var(--tone-soft);
  font-size: 1.0625rem;
  line-height: 1.65;
}

.section-head {
  margin-bottom: clamp(2.5rem, 5vw, 4rem);
}

.link {
  color: inherit;
  text-decoration: none;
  background-image: linear-gradient(var(--accent), var(--accent));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 1.5px;
  padding-bottom: 2px;
  transition:
    background-size 0.3s var(--ease-settle),
    color 0.3s var(--ease-settle);
}
/* On hover the underline grows into a full accent fill, so the type has
   to become --on-accent with it. Left as inherited ink it would be
   3.16:1 on the default accent, which is the exact failure the accent
   rule at the top of this file exists to prevent. */
.link:hover {
  background-size: 100% 100%;
  color: var(--on-accent);
}

.dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  display: inline-block;
  flex: none;
}

/* ============================================================
   BUTTONS

   The filled button is the accent's main job on this page. Its label
   is --on-accent, never --ink, because ink on the default accent is
   3.16:1 and fails.
   ============================================================ */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.95rem 1.6rem;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-family: var(--font-body);
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.005em;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color 0.25s var(--ease-settle),
    border-color 0.25s var(--ease-settle),
    color 0.25s var(--ease-settle);
}

.btn--fill {
  background: var(--accent);
  color: var(--on-accent);
}
.btn--fill:hover {
  background: rgba(var(--accent-rgb), 0.86);
}

.btn--line {
  background: transparent;
  color: var(--tone);
  border-color: var(--hair-strong);
}
.btn--line:hover {
  border-color: var(--tone);
  background: var(--wash);
}

/* The magnetic button is moved by GSAP, so its own transform is left
   free; only the label reacts to the press. */
[data-magnetic] {
  will-change: transform;
}
[data-magnetic]:active .btn-label {
  transform: scale(0.98);
}
.btn-label {
  display: block;
  transition: transform 0.2s var(--ease-settle);
}

/* ============================================================
   MASTHEAD
   ============================================================ */

.masthead {
  border-bottom: 1px solid var(--hair);
  padding-block: 1.15rem;
}

.masthead-row {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 3vw, 2.5rem);
  flex-wrap: wrap;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.0625rem;
  letter-spacing: -0.03em;
  color: inherit;
  text-decoration: none;
  margin-right: auto;
}

.wordmark-mark {
  width: 13px;
  height: 13px;
  background: var(--accent);
  /* The mark is a fill, not a glyph, so it stays legible whatever
       colour you set --accent to. */
  clip-path: polygon(0 100%, 50% 0, 100% 100%);
}

.masthead-nav {
  display: flex;
  gap: clamp(0.9rem, 2vw, 1.75rem);
  flex-wrap: wrap;
}
.masthead-nav a {
  color: var(--tone-dim);
  text-decoration: none;
  font-size: 0.9375rem;
  padding-block: 0.25rem;
  border-bottom: 1px solid transparent;
  transition:
    color 0.2s,
    border-color 0.2s;
}
.masthead-nav a:hover {
  color: var(--tone);
  border-bottom-color: var(--accent);
}

.masthead-note {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  color: var(--tone-dim);
}

@media (max-width: 860px) {
  .masthead-note {
    display: none;
  }
}
@media (max-width: 560px) {
  .masthead-row {
    gap: 0.7rem;
  }
  .masthead-nav {
    gap: 0.5rem 1.05rem;
    font-size: 0.875rem;
  }
  .masthead-nav a {
    padding-block: 0.1rem;
  }
}

/* ============================================================
   1. HERO
   ============================================================ */

.hero {
  padding-block: clamp(3rem, 7vw, 6rem) clamp(3rem, 6vw, 5rem);
}

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 0.85fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: start;
}

.hero-title {
  font-family: var(--font-display);
  font-weight: 500;
  /* 5rem is the ceiling. Larger reads as a poster, not a studio. */
  font-size: clamp(2.35rem, 5.6vw, 5rem);
  line-height: 1.02;
  letter-spacing: -0.035em;
  max-width: 15ch;
  text-wrap: balance;
}

/* SplitText wraps each line in an overflow-hidden mask. The lines
   move on yPercent AND y together in script.js — mixing a CSS
   percentage offset with a yPercent tween leaves a parsed pixel
   offset behind that never clears. */
.hero-title .line {
  will-change: transform;
}

.hero-lede {
  margin-top: 1.75rem;
  max-width: 40rem;
  font-size: clamp(1.0625rem, 1.3vw, 1.1875rem);
  line-height: 1.6;
  color: var(--ink-soft);
}

.hero-avail {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: 1.75rem;
  padding: 0.6rem 1rem;
  background: var(--card);
  border: 1px solid var(--hair);
  border-radius: 999px;
  font-size: 0.875rem;
  color: var(--ink-dim);
}
.hero-avail strong {
  color: var(--ink);
  font-weight: 600;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 2rem;
}

/* The hero's vertical rhythm has to answer to viewport HEIGHT, not just
   width. Sized on width alone, a wide-but-short laptop gets the type at
   full scale with nothing left underneath, and "Start a project" lands
   below the fold: measured at 1440x900, 1512x860 and 1366x768, which is
   most of the laptops this page will be read on.

   So on a short desktop viewport the padding, the gaps and the display
   size all step down together. Scoped to min-width so it can never touch
   the single-column phone layout, where the hero already fits and where a
   height query would fight the browser's collapsing address bar. */
@media (min-width: 900px) and (max-height: 940px) {
  .hero {
    padding-block: 3.25rem 2.75rem;
  }
  .hero-title {
    font-size: clamp(2.35rem, 4.6vw, 4.25rem);
  }
  .hero-lede {
    margin-top: 1.25rem;
  }
  .hero-avail {
    margin-top: 1.15rem;
  }
  .hero-actions {
    margin-top: 1.4rem;
  }
}

.studio-card {
  background: var(--card);
  border: 1px solid var(--hair);
  border-radius: var(--radius);
  padding: clamp(1.4rem, 2.5vw, 2rem);
}

.card-kicker {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--hair);
}

.factlist {
  margin-top: 0.35rem;
}

.fact {
  display: grid;
  gap: 0.15rem;
  padding-block: 0.85rem;
  border-bottom: 1px solid var(--hair);
}
.fact:last-child {
  border-bottom: 0;
}
.fact dt {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.fact dd {
  font-size: 0.9375rem;
  line-height: 1.45;
}

.card-foot {
  margin-top: 1.1rem;
  font-size: 0.9375rem;
}

.figures {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0;
  margin-top: clamp(3rem, 6vw, 5rem);
  border-top: 1px solid var(--hair-strong);
}

.figure {
  padding: 1.5rem clamp(0.75rem, 2vw, 1.5rem) 0 0;
  border-right: 1px solid var(--hair);
}
.figure:last-child {
  border-right: 0;
}
.figure:not(:first-child) {
  padding-left: clamp(0.75rem, 2vw, 1.5rem);
}

.figure-n {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: clamp(2rem, 3.4vw, 2.9rem);
  line-height: 1;
  letter-spacing: -0.04em;
  /* Ink, not the accent. The accent is a colour you are going to
       replace, and a figure this size still has to be readable in
       whatever you replace it with: a mid-yellow accent measures 1.47
       against this surface and a mid-orange 2.73, both of which fail
       even the relaxed 3:1 bar for large text. The accent does the
       emphasis from the rule above instead, where it carries no text. */
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* The accent as a pure mark: no text sits on it, so it is safe at any
   hue. The offset matches .figure's padding rule so the bar lines up
   with the numeral in both the first cell and the rest. */
.figure {
  position: relative;
}
.figure::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 1.75rem;
  height: 2px;
  background: var(--accent);
}
.figure:not(:first-child)::before {
  left: clamp(0.75rem, 2vw, 1.5rem);
}
.figure-unit {
  font-size: 0.62em;
}

.figure-label {
  margin-top: 0.65rem;
  font-size: 0.8125rem;
  line-height: 1.4;
  color: var(--ink-dim);
  max-width: 20ch;
}

@media (max-width: 900px) {
  .hero-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  .figures {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .figure:nth-child(2) {
    border-right: 0;
  }
  .figure:nth-child(-n + 2) {
    padding-bottom: 1.5rem;
    border-bottom: 1px solid var(--hair);
  }
  .figure:nth-child(3) {
    padding-left: 0;
  }
}
@media (max-width: 460px) {
  .figures {
    grid-template-columns: minmax(0, 1fr);
  }
  .figure {
    border-right: 0;
    padding-left: 0 !important;
  }
  .figure:not(:last-child) {
    padding-bottom: 1.35rem;
    border-bottom: 1px solid var(--hair);
  }
  .figure-label {
    max-width: none;
  }
}

/* ============================================================
   2. BUILD — the pinned mockup

   The pinned box is one viewport tall and holds the heading, the
   mockup and the stage readout. script.js pins it and scrubs the
   sequence over roughly three and a half viewports of scrolling, so
   each fidelity gets more than a viewport of travel: any less and one
   hard flick of the wheel carries a reader straight through all three
   and the section reads as a jump.
   ============================================================ */

.build {
  padding-block: 0;
}

.build-pin {
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding-block: clamp(2rem, 5vh, 4rem);
}

.build-inner {
  display: grid;
  gap: clamp(1.1rem, 2.4vh, 2rem);
  width: 100%;
}

.build-head {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: clamp(1rem, 3vw, 3rem);
  align-items: end;
}
.build-head .eyebrow {
  margin-bottom: 0.75rem;
}

.build-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.55rem, 3.1vw, 2.5rem);
  line-height: 1.08;
  letter-spacing: -0.028em;
  max-width: 20ch;
}

.build-note {
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--paper-soft);
  max-width: 34ch;
}

/* ---- the browser frame ----
   Width is capped by the available height as well as the column, so
   the mockup always fits inside the pinned viewport. Everything
   inside it is sized in cqw (container query units), which is what
   makes a 340px-wide phone render the same composition as a 960px
   desktop rather than a squashed one. */
.mock-frame {
  container-type: inline-size;
  width: min(100%, calc(var(--mock-h, 54svh) * 1.6));
  margin-inline: auto;
  aspect-ratio: 16 / 10;
  background: #ffffff;
  border: 1px solid rgba(var(--paper-rgb), 0.22);
  border-radius: 6px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: none;
}

.mock-chrome {
  flex: none;
  height: 5.2cqw;
  min-height: 20px;
  display: flex;
  align-items: center;
  gap: 1cqw;
  padding-inline: 1.6cqw;
  background: #edebe6;
  border-bottom: 1px solid rgba(var(--ink-rgb), 0.1);
}
.mock-dot {
  width: 1.1cqw;
  height: 1.1cqw;
  min-width: 4px;
  min-height: 4px;
  border-radius: 50%;
  background: rgba(var(--ink-rgb), 0.2);
  flex: none;
}
.mock-url {
  flex: 1;
  max-width: 26cqw;
  margin-left: 1.2cqw;
  background: #ffffff;
  border-radius: 999px;
  padding: 0.35cqw 1.2cqw;
  display: flex;
  align-items: center;
}

.mock-page {
  flex: 1;
  min-height: 0;
  padding: 2cqw 2.6cqw;
  display: flex;
  flex-direction: column;
  gap: 2cqw;
  background: #ffffff;
  color: var(--ink);
  font-family: var(--font-body);
}

/* A slot stacks the placeholder bar and the real type in one grid
   cell, so the line occupies exactly the same box in every stage and
   nothing shifts as the mockup resolves. */
.mslot {
  display: grid;
  align-items: center;
  justify-items: start;
}
.mslot > * {
  grid-area: 1 / 1;
}

.mwire {
  display: block;
  width: 100%;
  height: 0.62em;
  border-radius: 2px;
  background: rgba(var(--ink-rgb), 0.16);
}
.mreal {
  display: block;
  line-height: 1.15;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

.mslot--url {
  width: 100%;
  font-size: 1.7cqw;
  color: rgba(var(--ink-rgb), 0.6);
  font-family: var(--font-mono);
}
.mslot--url .mwire {
  height: 0.72em;
}

.mock-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2cqw;
  padding-bottom: 1.4cqw;
  border-bottom: 1px solid rgba(var(--ink-rgb), 0.12);
}
.mock-brand {
  display: flex;
  align-items: center;
  gap: 1cqw;
}
.mock-mark {
  position: relative;
  width: 2.2cqw;
  height: 2.2cqw;
  flex: none;
  background: var(--ink);
  clip-path: polygon(0 100%, 50% 0, 100% 100%);
}
.mock-mark-fill,
.mock-mark-wire {
  position: absolute;
  inset: 0;
}
.mock-mark-fill {
  background: var(--accent);
}
.mock-mark-wire {
  background: #d6d3cd;
}

.mslot--brand {
  width: 17cqw;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 2.1cqw;
  letter-spacing: -0.03em;
}
.mock-links {
  display: flex;
  gap: 2.4cqw;
}
.mslot--link {
  width: 9cqw;
  font-size: 1.65cqw;
  color: rgba(var(--ink-rgb), 0.72);
}
.mslot--link .mreal {
  font-weight: 500;
}

.mock-hero {
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(0, 1fr);
  gap: 3cqw;
  align-items: center;
  flex: 1;
  min-height: 0;
}
.mock-copy {
  display: grid;
  gap: 0.85cqw;
  justify-items: start;
  align-content: center;
}

.mock-kicker {
  display: flex;
  align-items: center;
  gap: 1cqw;
  width: 100%;
}
.mock-kicker-rule {
  width: 3.6cqw;
  height: 2px;
  background: var(--accent);
  flex: none;
}
.mslot--kicker {
  width: 18cqw;
  font-family: var(--font-mono);
  font-size: 1.35cqw;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(var(--ink-rgb), 0.68);
}
.mslot--h1 {
  width: 100%;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 4cqw;
  letter-spacing: -0.035em;
}
.mslot--h1 .mwire {
  height: 0.68em;
}
.mslot--sub {
  width: 100%;
  font-size: 1.6cqw;
  color: rgba(var(--ink-rgb), 0.72);
}
.mslot--sub .mreal {
  white-space: normal;
  line-height: 1.4;
}

.mock-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-top: 0.5cqw;
  padding: 0.85cqw 2.2cqw;
  border-radius: 3px;
  background: var(--ink);
  overflow: hidden;
}
.mock-btn-fill,
.mock-btn-wire {
  position: absolute;
  inset: 0;
}
.mock-btn-fill {
  background: var(--accent);
}
.mock-btn-wire {
  background: #dcd9d3;
}
.mock-btn-label {
  position: relative;
  font-size: 1.6cqw;
  font-weight: 600;
  color: var(--on-accent);
  white-space: nowrap;
}

.mock-media {
  position: relative;
  height: 100%;
  min-height: 12cqw;
  border-radius: 3px;
  overflow: hidden;
  background: rgba(var(--ink-rgb), 0.86);
}
.mock-media-art,
.mock-media-wire {
  position: absolute;
  inset: 0;
  display: block;
}

/* Stage 1: the classic crossed placeholder box. */
.mock-media-wire {
  background:
    linear-gradient(
      to top right,
      transparent calc(50% - 0.5px),
      rgba(var(--ink-rgb), 0.22) 50%,
      transparent calc(50% + 0.5px)
    ),
    linear-gradient(
      to bottom right,
      transparent calc(50% - 0.5px),
      rgba(var(--ink-rgb), 0.22) 50%,
      transparent calc(50% + 0.5px)
    ),
    #e9e7e2;
}

/* Stage 3: the composed image. Flat shapes on an accent field, so it
   reads as photography arriving without pretending to be a photo. */
.mock-media-art {
  background: var(--accent);
  overflow: hidden;
}
.mma {
  position: absolute;
  display: block;
}
.mma--sun {
  width: 34%;
  aspect-ratio: 1;
  right: 12%;
  top: 14%;
  border-radius: 50%;
  background: rgba(var(--on-accent-rgb), 0.9);
}
.mma--loaf {
  left: -6%;
  bottom: -14%;
  width: 76%;
  aspect-ratio: 1.5;
  border-radius: 50% 50% 0 0;
  background: rgba(var(--deep-rgb), 0.82);
}
.mma--bar {
  right: 0;
  bottom: 22%;
  width: 46%;
  height: 8%;
  background: rgba(var(--on-accent-rgb), 0.55);
}

.mock-cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 2.2cqw;
  flex: none;
}
.mock-card {
  display: grid;
  gap: 0.7cqw;
  justify-items: start;
}
.mock-thumb {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 6;
  border-radius: 3px;
  overflow: hidden;
  background: rgba(var(--ink-rgb), 0.8);
}
.mock-thumb-art,
.mock-thumb-wire {
  position: absolute;
  inset: 0;
  display: block;
}
.mock-thumb-wire {
  background: #e9e7e2;
}
.mock-thumb-art--a {
  background: linear-gradient(120deg, var(--accent) 0 55%, rgba(var(--deep-rgb), 0.9) 55% 100%);
}
.mock-thumb-art--b {
  background: rgba(var(--accent-rgb), 0.82);
}
.mock-thumb-art--c {
  background: linear-gradient(0deg, var(--accent) 0 40%, rgba(var(--deep-rgb), 0.88) 40% 100%);
}
.mslot--ct {
  width: 100%;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 2cqw;
  letter-spacing: -0.02em;
}
.mslot--cb {
  width: 100%;
  font-size: 1.45cqw;
  color: rgba(var(--ink-rgb), 0.7);
}

/* ---- stage readout ---- */
.build-readout {
  display: grid;
  gap: 1rem;
}

.build-stages {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(0.75rem, 2vw, 2rem);
  list-style: none;
}

.build-stage {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 0.5rem 0.7rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--hair);
  transition: border-color 0.35s var(--ease-settle);
}
.build-stage-n {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  padding: 0.2rem 0.45rem;
  border-radius: 2px;
  background: var(--wash);
  color: var(--paper-dim);
  transition:
    background-color 0.35s var(--ease-settle),
    color 0.35s var(--ease-settle);
}
.build-stage-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: -0.02em;
  color: var(--paper-dim);
  transition: color 0.35s var(--ease-settle);
}
.build-stage-say {
  grid-column: 1 / -1;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: rgba(var(--paper-rgb), 0.55);
  transition: color 0.35s var(--ease-settle);
}

/* The current stage is marked by an accent FILL behind its number and
   by full-strength type — never by colouring the label itself. */
.build-stage.is-current {
  border-top-color: var(--accent);
}
.build-stage.is-current .build-stage-n {
  background: var(--accent);
  color: var(--on-accent);
}
.build-stage.is-current .build-stage-name {
  color: var(--paper);
}
.build-stage.is-current .build-stage-say {
  color: var(--paper-soft);
}

.build-bar {
  position: relative;
  height: 2px;
  background: var(--hair);
  overflow: hidden;
}
.build-bar-fill {
  position: absolute;
  inset: 0;
  background: var(--accent);
  transform-origin: 0 50%;
}

@media (max-width: 780px) {
  .build-head {
    grid-template-columns: minmax(0, 1fr);
  }
  .build-note {
    max-width: none;
  }
  .build-stages {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.5rem;
  }
  /* Only the current stage keeps its sentence on a phone; three at
       once would push the mockup off the pinned screen. */
  .build-stage:not(.is-current) .build-stage-say {
    display: none;
  }
}
@media (max-width: 560px) {
  .mock-cards {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1.6cqw;
  }
  .mslot--cb {
    display: none;
  }
}

/* ============================================================
   3. SERVICES
   ============================================================ */

.svc-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1rem, 2vw, 1.5rem);
}

.svc {
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--hair);
  border-radius: var(--radius);
  padding: clamp(1.5rem, 3vw, 2.35rem);
}

.svc-n {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
}

.svc-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.35rem, 2.2vw, 1.75rem);
  letter-spacing: -0.028em;
  line-height: 1.1;
  margin-top: 0.85rem;
}

.svc-who {
  margin-top: 0.75rem;
  color: var(--ink-soft);
  font-size: 0.9375rem;
  line-height: 1.55;
}

.svc-scope {
  list-style: none;
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--hair);
  display: grid;
  gap: 0.7rem;
  flex: 1;
}
.svc-scope li {
  position: relative;
  padding-left: 1.35rem;
  font-size: 0.9375rem;
  line-height: 1.45;
  color: var(--ink-dim);
}
.svc-scope li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.6em;
  width: 8px;
  height: 1.5px;
  background: var(--accent);
}

.svc-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 1.75rem;
  margin-top: 1.6rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--hair);
}
.svc-meta dt {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 0.25rem;
}
.svc-meta dd {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.0625rem;
  letter-spacing: -0.02em;
}

@media (max-width: 760px) {
  .svc-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ============================================================
   4. WORK

   The filter is CSS-only: a checked radio hides everything that does
   not carry the matching tag. That means it works with JavaScript
   off. When Flip is available, script.js records the layout before
   the change and animates the reflow.
   ============================================================ */

.filter {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  border: 0;
  margin-bottom: clamp(1.5rem, 3vw, 2.25rem);
}

.chip {
  display: inline-flex;
  align-items: center;
  padding: 0.55rem 1.05rem;
  border: 1px solid var(--hair-strong);
  border-radius: 999px;
  background: transparent;
  color: var(--ink-dim);
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  user-select: none;
  transition:
    background-color 0.2s,
    color 0.2s,
    border-color 0.2s;
}
.chip:hover {
  background: var(--card);
  color: var(--ink);
}

/* The active chip is an accent FILL with --on-accent type on it, not
   accent-coloured text. */
.filter-input:checked + .chip {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.filter-input:focus-visible + .chip {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

[data-work]:has(#wf-marketing:checked) [data-work-item]:not([data-tags~="marketing"]),
[data-work]:has(#wf-shop:checked) [data-work-item]:not([data-tags~="shop"]),
[data-work]:has(#wf-rebuild:checked) [data-work-item]:not([data-tags~="rebuild"]) {
  display: none;
}

.work-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.25rem, 2.5vw, 2rem);
}

.work-item {
  display: flex;
  flex-direction: column;
}

.frame {
  background: var(--card);
  border: 1px solid var(--hair);
  border-radius: 5px;
  overflow: hidden;
}

.frame-chrome {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 0.5rem 0.75rem;
  background: rgba(var(--ink-rgb), 0.06);
  border-bottom: 1px solid var(--hair);
}
.frame-chrome span {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(var(--ink-rgb), 0.22);
  flex: none;
}
.frame-chrome em {
  margin-left: 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-style: normal;
  color: var(--ink-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.frame-shot {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
}

/* Drop an <img class="shot"> in as the first child of .frame-shot and
   it covers the plate. Nothing else to change. */
.shot {
  position: absolute;
  inset: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 0;
}

/* ---- the designed plates ----
   Six compositions, so an un-swapped grid still looks art-directed
   rather than like six empty boxes. Each is flat colour and type;
   there is nothing here you cannot delete. */
.plate {
  position: absolute;
  inset: 0;
  padding: clamp(0.9rem, 2vw, 1.4rem);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 0.3rem;
  overflow: hidden;
}
.plate-word {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.4rem, 3.4vw, 2.1rem);
  line-height: 1;
  letter-spacing: -0.04em;
  position: relative;
  z-index: 2;
}
.plate-word--sm {
  font-size: clamp(1.1rem, 2.6vw, 1.6rem);
}
.plate-sub {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  position: relative;
  z-index: 2;
}
.plate-num {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: clamp(2.2rem, 6vw, 3.6rem);
  line-height: 1;
  letter-spacing: -0.05em;
  margin-bottom: auto;
  position: relative;
  z-index: 2;
}

.plate--1 {
  background: var(--deep);
  color: var(--paper);
}
.plate--1 .plate-sub {
  color: var(--paper-dim);
}
.plate--1 .plate-slab {
  position: absolute;
  right: -10%;
  top: -20%;
  width: 58%;
  aspect-ratio: 1;
  background: var(--accent);
  transform: rotate(18deg);
}
.plate--1 .plate-rule {
  position: absolute;
  left: 0;
  right: 46%;
  top: 26%;
  height: 1px;
  background: rgba(var(--paper-rgb), 0.32);
}

.plate--2 {
  background: var(--accent);
  color: var(--on-accent);
}
.plate--2 .plate-sub {
  color: rgba(var(--on-accent-rgb), 0.85);
}
.plate--2 .plate-grid {
  position: absolute;
  inset: 0 0 auto 0;
  height: 58%;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: rgba(var(--deep-rgb), 0.2);
  opacity: 0.9;
}
.plate--2 .plate-grid i {
  background: rgba(var(--on-accent-rgb), 0.12);
}
.plate--2 .plate-grid i:nth-child(2) {
  background: rgba(var(--on-accent-rgb), 0.26);
}
.plate--2 .plate-grid i:nth-child(4) {
  background: rgba(var(--deep-rgb), 0.35);
}

.plate--3 {
  background: var(--card);
  color: var(--ink);
}
/* Ink, for the same reason as .figure-n: this numeral has to survive
   whatever accent you swap in, and a light one would leave it invisible
   on this surface. The accent underline beneath it carries no text, so
   it stays legible at any hue. */
.plate--3 .plate-num {
  color: var(--ink);
}
.plate--3 .plate-num::after {
  content: "";
  display: block;
  width: 2.2em;
  height: 2px;
  margin-top: 0.3em;
  background: var(--accent);
}
.plate--3 .plate-sub {
  color: var(--ink-dim);
}

.plate--4 {
  background: var(--sunk);
  color: var(--ink);
}
.plate--4 .plate-sub {
  color: var(--ink-dim);
}
.plate--4 .plate-arc {
  position: absolute;
  left: 50%;
  top: 8%;
  width: 74%;
  aspect-ratio: 1;
  transform: translateX(-50%);
  border-radius: 50%;
  border: 10px solid var(--accent);
  border-bottom-color: transparent;
  border-right-color: transparent;
}

.plate--5 {
  background: var(--deep);
  color: var(--paper);
}
.plate--5 .plate-sub {
  color: var(--paper-dim);
}
.plate--5 .plate-stack {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 62%;
  display: grid;
  grid-template-rows: repeat(3, 1fr);
}
.plate--5 .plate-stack i {
  border-bottom: 1px solid rgba(var(--paper-rgb), 0.16);
}
.plate--5 .plate-stack i:nth-child(2) {
  background: var(--accent);
}

.plate--6 {
  background: var(--card);
  color: var(--ink);
}
.plate--6 .plate-sub {
  color: var(--ink-dim);
}
.plate--6 .plate-bars {
  position: absolute;
  left: clamp(0.9rem, 2vw, 1.4rem);
  right: clamp(0.9rem, 2vw, 1.4rem);
  top: 18%;
  height: 42%;
  display: flex;
  align-items: flex-end;
  gap: 6%;
}
.plate--6 .plate-bars i {
  flex: 1;
  background: rgba(var(--ink-rgb), 0.18);
}
.plate--6 .plate-bars i:nth-child(1) {
  height: 45%;
}
.plate--6 .plate-bars i:nth-child(2) {
  height: 72%;
}
.plate--6 .plate-bars i:nth-child(3) {
  height: 100%;
  background: var(--accent);
}
.plate--6 .plate-bars i:nth-child(4) {
  height: 58%;
}

.work-meta {
  padding-top: 1.1rem;
}
.work-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.125rem;
  letter-spacing: -0.024em;
}
.work-line {
  margin-top: 0.5rem;
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--ink-dim);
}
.work-tags {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: 0.9rem;
}
.tag {
  display: inline-flex;
  padding: 0.25rem 0.6rem;
  border-radius: 2px;
  background: var(--accent);
  color: var(--on-accent);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
}
.work-year {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  color: var(--ink-faint);
  letter-spacing: 0.04em;
}

@media (max-width: 980px) {
  .work-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 620px) {
  .work-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ============================================================
   5. PROCESS
   ============================================================ */

.rail-wrap {
  position: relative;
  padding-left: clamp(1.85rem, 4vw, 3.5rem);
}

/* The rail is a two-unit-wide viewBox stretched to the full height of
   the list (preserveAspectRatio="none"), so one straight path fits any
   number of steps. Stroke width is left in user units rather than
   pinned with vector-effect: the horizontal scale is exactly 1, so 2
   user units renders as 2px, and DrawSVG's dash lengths stay in the
   same coordinate system it measured them in. */
.rail {
  position: absolute;
  left: 5px;
  top: 0;
  width: 2px;
  /* An explicit height, not top+bottom: an SVG is a replaced element,
       so `height: auto` would resolve to the viewBox's intrinsic 100px
       and the rail would stop a fifth of the way down the list. */
  height: 100%;
  overflow: visible;
}
.rail-track {
  stroke: var(--hair);
  stroke-width: 2;
  fill: none;
}
.rail-line {
  stroke: var(--accent);
  stroke-width: 2;
  fill: none;
}

.steps {
  list-style: none;
  display: grid;
}

.step {
  position: relative;
  padding-block: clamp(1.5rem, 3vw, 2.4rem);
  display: grid;
  grid-template-columns: 7rem minmax(0, 1fr);
  gap: 0.35rem clamp(1.5rem, 3vw, 3rem);
  border-bottom: 1px solid var(--hair);
}
.step:last-child {
  border-bottom: 0;
}

.step-node {
  position: absolute;
  left: calc(-1 * clamp(1.85rem, 4vw, 3.5rem));
  top: calc(clamp(1.5rem, 3vw, 2.4rem) + 0.45rem);
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--ground);
  border: 2px solid var(--hair-strong);
  transition:
    background-color 0.4s var(--ease-settle),
    border-color 0.4s var(--ease-settle);
}
.step.is-lit .step-node {
  background: var(--accent);
  border-color: var(--accent);
}

.step-week {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  padding-top: 0.3rem;
}
.step-name {
  grid-column: 2;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.25rem, 2.1vw, 1.6rem);
  letter-spacing: -0.028em;
  line-height: 1.12;
}
.step-body {
  grid-column: 2;
  margin-top: 0.5rem;
  max-width: var(--measure);
  color: var(--ink-soft);
  font-size: 0.9375rem;
  line-height: 1.6;
}

@media (max-width: 700px) {
  .step {
    grid-template-columns: minmax(0, 1fr);
  }
  .step-name,
  .step-body {
    grid-column: 1;
  }
  .step-week {
    padding-top: 0;
    margin-bottom: 0.35rem;
  }
}

/* ============================================================
   6. CONTACT AND FOOTER
   ============================================================ */

.contact-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: clamp(2.25rem, 5vw, 5rem);
  align-items: start;
}

.contact-details {
  display: grid;
  gap: 1.15rem;
  margin-top: 2.5rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--hair);
}
.contact-details dt {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--paper-dim);
  margin-bottom: 0.3rem;
}
.contact-details dd {
  font-size: 0.9375rem;
  line-height: 1.5;
}

.enquiry {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
  background: var(--slate);
  border: 1px solid var(--hair);
  border-radius: var(--radius);
  padding: clamp(1.4rem, 3vw, 2.25rem);
}
.field {
  display: grid;
  gap: 0.4rem;
}
.field--wide {
  grid-column: 1 / -1;
}

.field label {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--paper-dim);
}
.field .opt {
  text-transform: none;
  letter-spacing: 0.02em;
  opacity: 0.75;
}

.field input,
.field select,
.field textarea {
  width: 100%;
  font-family: var(--font-body);
  font-size: 0.9375rem;
  color: var(--paper);
  background: rgba(var(--deep-rgb), 0.6);
  border: 1px solid rgba(var(--paper-rgb), 0.22);
  border-radius: var(--radius);
  padding: 0.7rem 0.85rem;
  transition: border-color 0.2s;
}
.field select {
  appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--paper-dim) 50%),
    linear-gradient(135deg, var(--paper-dim) 50%, transparent 50%);
  background-position:
    calc(100% - 18px) 55%,
    calc(100% - 13px) 55%;
  background-size:
    5px 5px,
    5px 5px;
  background-repeat: no-repeat;
  padding-right: 2.25rem;
}
.field textarea {
  resize: vertical;
  min-height: 7rem;
}
.field input:hover,
.field select:hover,
.field textarea:hover {
  border-color: rgba(var(--paper-rgb), 0.4);
}

.field--submit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  margin-top: 0.35rem;
}
.form-note {
  font-size: 0.8125rem;
  color: var(--paper-dim);
}

/* The footer carries its own dark treatment and its own spacing rather
   than inheriting them from the section above, because it sits outside
   that section so it survives the section being deleted. It uses
   band--deep for the tone tokens but NOT .band, whose large padding-block
   is meant for a full content section and would balloon the footer. */
.footer {
  padding-top: 2rem;
  padding-bottom: clamp(2rem, 4vw, 3rem);
  border-top: 1px solid var(--hair);
}
.footer-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 1rem clamp(1rem, 3vw, 2.5rem);
}
.wordmark--foot {
  margin-right: auto;
}
.footer-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
}
.footer-nav a {
  color: var(--paper-dim);
  text-decoration: none;
  font-size: 0.875rem;
}
.footer-nav a:hover {
  color: var(--paper);
}
.footer-legal {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  color: var(--paper-dim);
  width: 100%;
}

@media (max-width: 820px) {
  .contact-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}
@media (max-width: 520px) {
  .enquiry {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ============================================================
   REDUCED MOTION

   Belt and braces with the JavaScript branch: transitions off, and
   the two states that are otherwise only set by an animation are set
   here instead — the process line drawn, and the last stage of the
   mockup marked as current.
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .rail-line {
    stroke-dasharray: none;
  }
  .step .step-node {
    background: var(--accent);
    border-color: var(--accent);
  }
}
