/* ==========================================================================
   v9: BlackCat on paper, rebuilt clean. "Imperial system, Ferrix hands."
   One stylesheet after v9-tokens.css. Numbered sections, one per part of the page, so any
   piece can be found and backed out on its own. No rule here depends on the retired
   tokens.css / base.css / crt.css / v6-v8.css stack.
   ========================================================================== */


/* ------------------------------------------------------------ 0. fonts */
/* The exact files the live site serves. Cook Conthic is Julio's customised cut: never swap it. */
@font-face { font-family: 'Cook Conthic'; src: url('/fonts/CookConthic.woff2') format('woff2');
             font-weight: 400; font-display: swap; }
@font-face { font-family: 'Host Grotesk'; src: url('/fonts/HostGrotesk-Variable.woff2') format('woff2');
             font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: 'Host Grotesk'; src: url('/fonts/HostGrotesk-Italic-Variable.woff2') format('woff2');
             font-weight: 100 900; font-style: italic; font-display: swap; }
@font-face { font-family: 'Roboto Mono'; src: url('/fonts/RobotoMono-Variable.woff2') format('woff2');
             font-weight: 100 700; font-display: swap; }
/* Julio's handwriting, for the margin notes. Built from his own sheets (_dev/handwriting/). */
@font-face { font-family: 'BlackCat Hand'; src: url('/fonts/BlackCatHand-2.woff2') format('woff2');
             font-weight: 400; font-display: swap; }


/* ------------------------------------------------------------ 1. base */
*, *::before, *::after { box-sizing: border-box; }
html { color-scheme: light; -webkit-text-size-adjust: 100%; }
body { margin: 0; background: var(--bg); color: var(--fg);
       font: 400 1rem/1.5 var(--font-text); -webkit-font-smoothing: antialiased;
       overflow-x: clip; }
h1, h2, h3, h4, p, figure, blockquote, dl, dd { margin: 0; }
ul, ol { list-style: none; margin: 0; padding: 0; }
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: .2em; }
button, input, select, textarea { font: inherit; color: inherit; background: none; border: 0; }
::selection { background: var(--green); color: var(--on-green); }

.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden;
                   clip: rect(0 0 0 0); white-space: nowrap; }
.skip:focus { position: fixed; z-index: 999; top: var(--space-1); left: var(--space-1);
              width: auto; height: auto; clip: auto; padding: var(--space-1) var(--space-2);
              background: var(--fg); color: var(--bg); }

/* Focus: green on ink everywhere; ink inside the green fields. */
:focus-visible { outline: 2px solid var(--green); outline-offset: 3px; }

/* .on-ink marks what were the black bands in the light build. Dark only, they are the page, so
   the class now only keeps those sections painting their own ground. */
.on-ink { background: var(--bg); color: var(--fg); }

/* GREEN FIELDS: Process and the record, the two breaks in the ink (Julio, Oct 30: none above
   Process). The roles are re-pointed, so every rule inside works unchanged: type and marks go
   ink, hairlines go ink tints. Green marks inside a field would vanish, so they go ink too. */
.process, .record {
  --bg: var(--green);
  --fg: var(--on-green);
  --fg-2: rgba(11, 10, 8, .72);          /* 7.3:1 on green */
  --line: rgba(11, 10, 8, .22);
  --grid: rgba(11, 10, 8, .1);
  background: var(--bg);
  color: var(--fg);
}
.process .stations__seg, .process .lamp, .record .lamp { background: var(--on-green); }
.process :focus-visible, .record :focus-visible { outline-color: var(--on-green); }
.process ::selection, .record ::selection { background: var(--on-green); color: var(--green); }

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
section[id] { scroll-margin-top: var(--header-h); }


/* ------------------------------------------------------------ 2. shared parts */

/* Mono label: case codes, counts, corner markers, spec notes. */
.label { font: 500 var(--step--1)/1 var(--font-mono); letter-spacing: .06em; text-transform: uppercase; }

/* Corner markers: "/ 01" top left, "P. 002" top right, on every section. */
.corners { display: flex; justify-content: space-between; gap: var(--space-2);
           padding: var(--space-2) var(--frame-pad) 0; }

/* The green indicator light. A dot NEXT TO text, never text itself. */
.lamp { display: inline-block; width: .5rem; height: .5rem; border-radius: 50%;
        background: var(--green); flex: 0 0 auto; }

/* The ruler. Section dividers are rulers, not plain lines: minor tick every 8px, major every
   80px, baseline underneath. Drawn with background patterns so it costs no markup. */
.ruler { position: relative; container-type: inline-size; height: 18px;
         margin: 0 var(--frame-pad);
         background:
           linear-gradient(currentColor, currentColor) left bottom / 100% 1px no-repeat,
           repeating-linear-gradient(to right, currentColor 0 1px, transparent 1px 80px) left bottom / 100% 14px no-repeat,
           repeating-linear-gradient(to right, currentColor 0 1px, transparent 1px 8px) left bottom / 100% 6px no-repeat; }
.ruler { color: var(--fg); }
.on-ink .ruler { color: var(--fg-2); }
/* One short green segment on the baseline. On scroll it steps forward like a ratchet. */
.ruler__seg { position: absolute; left: 0; bottom: 0; width: 32px; height: 4px; background: var(--green); }
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: scroll()) {
    .ruler--progress .ruler__seg { animation: rulerStep steps(40, end) both;
                                   animation-timeline: scroll(root); }
    @keyframes rulerStep { to { transform: translateX(calc(100cqi - 100%)); } }
  }
}


/* ------------------------------------------------------------ 3. top bar */
.site-header { position: sticky; top: 0; z-index: var(--z-header);
               display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
               gap: var(--space-3); min-height: 64px;
               padding: var(--space-1) var(--frame-pad);
               background: var(--bg); border-bottom: var(--rule) solid var(--line); }
.site-header__mark { display: block; width: 116px; height: 30px; justify-self: start; }
.logotype { display: block; width: 100%; height: 100%; background: var(--fg);
            -webkit-mask: url('/img/logotype.svg') left center / contain no-repeat;
            mask: url('/img/logotype.svg') left center / contain no-repeat; }

/* Open/closed badge. v9.js says whether the shop is open; without JS the badge reads the hours. */
.shop-clock { display: flex; align-items: center; gap: var(--space-3); white-space: nowrap; }
.shop-clock__state { display: inline-flex; align-items: center; gap: .5em;
                     padding: .75em .8em; background: var(--bg); color: var(--fg);
                     box-shadow: inset 0 0 0 1px var(--line); }
.shop-clock__state .lamp { background: var(--fg-2); }
.shop-clock.is-open .shop-clock__state .lamp { background: var(--green); }
/* Split-flap (v9.js, motion only): every character of the badge is a tile whose halves flip to
   the next one, a shade lighter than the badge so they read as tiles. The real text stays in the
   DOM, visually hidden; the tiles are aria-hidden. */
.flaps { display: inline-flex; gap: 1px; font-family: var(--font-mono); letter-spacing: 0; }
.flap { --flap-bg: var(--plate-tile); --flap-split: var(--bg);
        position: relative; width: 1em; height: 1.7em; perspective: 200px; color: var(--fg); }
.flap::after { content: ""; position: absolute; z-index: 3; left: 0; right: 0; top: calc(50% - .5px);
               height: 1px; background: var(--flap-split); }
.flap__h { position: absolute; left: 0; right: 0; height: 50%; overflow: hidden; background: var(--flap-bg);
           backface-visibility: hidden; }
.flap__h b { display: block; height: 1.7em; line-height: 1.7em; text-align: center; font-weight: inherit; }
.flap__h--t { top: 0; border-radius: 2px 2px 0 0; transform-origin: 50% 100%; }
.flap__h--b { bottom: 0; border-radius: 0 0 2px 2px; transform-origin: 50% 0; }
.flap__h--b b { transform: translateY(-50%); }
.flap__h.is-moving { z-index: 2; }
.shop-clock.is-board .shop-clock__state { padding: .45em .5em .45em .7em; }
/* The nav needs ~405px beside a centred clock. Below 1280 the badge tiles say OPEN or CLOSED
   (v9.js), and below 1200 the place name goes too (the footer says Buckeye). */
@media (max-width: 1199px) { .shop-clock__place { display: none; } }

.site-nav__list { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-4); }
.site-nav a:not(.btn) { display: inline-flex; align-items: center; gap: .5em; text-decoration: none;
                        padding-block: .5rem; }
/* Hover and active: a green lamp switches on beside the word. The word itself stays ink. */
.site-nav a:not(.btn)::before { content: ""; width: .45rem; height: .45rem; background: var(--green);
                                transform: scale(0); }
.site-nav a:not(.btn):hover::before, .site-nav a:not(.btn):focus-visible::before,
.site-nav a[aria-current]::before { transform: none; }

/* The black button with the green dot. Hover inverts to a green field with ink type. */
.btn { display: inline-flex; align-items: center; gap: .75em; padding: .85em 1em;
       background: var(--fg); color: var(--bg); text-decoration: none; cursor: pointer; }
.btn:hover, .btn:focus-visible { background: var(--green); color: var(--on-green); }
.btn:hover .lamp, .btn:focus-visible .lamp { background: var(--on-green); }

.nav-toggle { display: none; }

/* Under 1024 the three links + button fold into a drawer; the clock keeps its badge only. */
@media (max-width: 1023px) {
  .site-header { grid-template-columns: auto 1fr auto; }
  .shop-clock { justify-self: end; }
  .shop-clock__place { display: none; }
  .nav-toggle { display: inline-flex; align-items: center; gap: .6em; padding: .55em 0 .55em .7em;
                cursor: pointer; }
  .nav-toggle__icon { display: grid; gap: 4px; }
  .nav-toggle__icon span { display: block; width: 18px; height: 2px; background: var(--fg); }
  .site-nav { position: fixed; z-index: var(--z-nav); inset: 0 0 0 auto; width: min(100%, 420px);
              display: flex; flex-direction: column; justify-content: space-between;
              padding: var(--space-3) var(--gutter) var(--space-5);
              background: var(--bg); color: var(--fg); transform: translateX(100%);
              visibility: hidden; }
  .site-nav.is-open { transform: none; visibility: visible; }
  .site-nav__list { flex-direction: column; align-items: stretch; gap: 0; }
  .site-nav__list li { border-bottom: var(--rule) solid var(--line); }
  .site-nav a:not(.btn) { padding-block: var(--space-2); font: 600 var(--step-2)/1 var(--font-text);
                          letter-spacing: -.01em; text-transform: none; }
  .site-nav__list li:last-child { border-bottom: 0; }
  .site-nav .btn { display: flex; margin-top: var(--space-4); justify-content: space-between;
                   background: var(--green); color: var(--on-green); }
  .site-nav .btn .lamp { background: var(--on-green); }
  .site-nav__close { align-self: flex-end; margin-bottom: var(--space-4); padding: .55em 0; cursor: pointer; }
  .site-nav :focus-visible { outline-color: var(--green); }
}
@media (min-width: 1024px) { .site-nav__close { display: none; } }
@media (max-width: 1023px) and (prefers-reduced-motion: no-preference) {
  .site-nav { transition: transform var(--t-open) var(--smooth), visibility 0s linear var(--t-open); }
  .site-nav.is-open { transition: transform var(--t-open) var(--smooth), visibility 0s; }
  .nav-scrim { transition: opacity var(--t-open) var(--smooth); }
}
/* Under the header, not under the drawer: the sticky header is its own stacking context, so the
   drawer's z-index only counts inside it. A scrim above --z-header would sit on the drawer. */
.nav-scrim { position: fixed; inset: 0; z-index: calc(var(--z-header) - 1); background: rgba(0, 0, 0, .6);
             opacity: 0; pointer-events: none; }
.nav-scrim.is-open { opacity: 1; pointer-events: auto; }


/* ------------------------------------------------------------ 4. hero (locked: ported from live) */
/* The BLACKCAT blackletter outline on graph paper. The SVG is the live file's, paths untouched.
   Its phosphor bloom group is kept in the markup verbatim but retired here with the CRT look.

   LOAD-IN, the one change: the outline is on screen from the first paint as a faint ghost, so the
   grid is never empty while scripts load. Then the outline is plotted over the ghost as points,
   one pen per letter with a crosshair and its live coordinates (v9.js, a canvas), and when every
   pen lands the whole mark locks to full weight in one step, like a shutter. */
.hero { position: relative; overflow: hidden;
        display: grid; grid-template-rows: auto auto; align-content: center; row-gap: var(--space-3);
        /* minus a sliver so the top of BUILT peeks above the fold: the promise that there is more */
        min-height: calc(100svh - var(--header-h) - 8rem);
        padding-top: var(--space-6);
        background-color: var(--bg);
        background-image:
          linear-gradient(to right,  var(--grid) 1px, transparent 1px),
          linear-gradient(to bottom, var(--grid) 1px, transparent 1px);
        background-size: var(--cell) var(--cell);
        background-position: center top; }
.hero > .corners { position: absolute; inset: 0 0 auto; }

/* Plotted dots: a few grid intersections marked, the way a draughtsman ticks reference points.
   --x counts cells from the centre line, --y counts cells from the top. */
.hero__plot { position: absolute; inset: 0; pointer-events: none; }
.hero__plot i { position: absolute; width: 5px; height: 5px; border-radius: 50%; background: var(--fg);
                left: calc(50% + var(--x) * var(--cell)); top: calc(var(--y) * var(--cell));
                translate: -50% -50%; }

.hero__mark { position: relative; justify-self: center;
              aspect-ratio: 1240/724; width: min(88%, 1080px); max-height: 62svh; }
.hero__mark svg { display: block; width: 100%; height: 100%; overflow: visible; }
.beam { fill: none; stroke: var(--fg); stroke-linejoin: round; stroke-linecap: round;
        vector-effect: non-scaling-stroke; }
.beam-pulse { display: none; }                 /* the retired phosphor bloom */

/* Settled: the outline as it sits on the live site today. This is also the no-JS state. */
.beam--trail { stroke-width: 2.2; }
.beam--head  { stroke-width: 1.2; opacity: 0; stroke-linecap: butt; }   /* round caps paint a dot at 0% */
/* With JS: the ghost from the first paint, the plotted points on a canvas over it, then
   .is-locked snaps the outline to full weight and the points drop out in three steps. The
   .beam--head paths were the DrawSVG trace: kept in the markup, never shown now. */
.js .hero:not(.is-locked) .beam--trail { stroke-width: 1.2; opacity: .22; }
.hero__pen { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.hero.is-locked .hero__pen { opacity: 0; transition: opacity 240ms steps(3, end) 80ms; }

.hero__foot { display: flex; justify-content: center; padding-bottom: var(--space-3); }
.cue { display: inline-flex; align-items: center; gap: .6em; padding: .75rem 0; text-decoration: none; }
.cue:hover, .cue:focus-visible { text-decoration: underline; text-underline-offset: .35em; }

@media (max-width: 767px) {
  .hero { min-height: calc(100svh - var(--header-h) - 9rem); padding-top: var(--space-5); }
  .hero__mark { width: 92%; }
}


/* ------------------------------------------------------------ 5. the wall (ported from live) */
/* BUILT ... TO / HOLD ... UP. / SINCE ... 2021. First word flush left, second flush right, read
   row by row. The split is deliberate. Sized off the container so a line always fills the frame.
   The real h1 is visually hidden; the painted lines are aria-hidden. */
.wall { container-type: inline-size; padding: var(--space-3) var(--frame-pad) var(--space-5);
        background: var(--bg); }
.wall__line { display: flex; align-items: center; justify-content: space-between; gap: .12em;
              font: 400 19cqi/.86 var(--font-display); text-transform: uppercase; white-space: nowrap; }
@media (max-width: 767px) {
  .wall__line { font-size: 24cqi; }
}
/* Two acts:
   1. FOCUS, per line on its own timeline. Each line arrives centred and sharpens, so the whole
      statement first reads as one block: BUILT TO / HOLD UP. / SINCE 2021.
   2. PART, on the wall's timeline. As the wall settles under the masthead the three rows split
      to the edges together, top row a hair first. That is how the eye learns the wide split is
      still one line (Julio, Oct 28: it was reading down the columns).
   The distance each word travels (--d, half the line's free space) is measured by js/v9.js, since
   CSS cannot know the word widths. Without it --d is 0 and the words simply sit at the edges.
   The blur sits on an inner span that never moves; the parting moves its parent. A filter on an
   element whose content is moving re-renders every frame, which is the WebKit lag the old site
   hit (CLAUDE.md: put the filter on a static child and animate the parent). */
@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: view()) and (animation-range: entry)) {
    .wall { view-timeline: --wall block;
            --part1: cover 34% cover 44%; --part2: cover 36% cover 47%; --part3: cover 38% cover 50%; }
    .wall__line { view-timeline: --wl block; }
    .wall__line > span > span { display: inline-block;
                                animation: wallFocus linear both; animation-timeline: --wl;
                                animation-range: entry 0% entry 45%; }
    .wall__line > span:first-child { animation: wallL var(--lock) both; animation-timeline: --wall; }
    .wall__line > span:last-child  { animation: wallR var(--lock) both; animation-timeline: --wall; }
    .wall__line:nth-child(1) > span { animation-range: var(--part1); }
    .wall__line:nth-child(2) > span { animation-range: var(--part2); }
    .wall__line:nth-child(3) > span { animation-range: var(--part3); }
    @keyframes wallFocus { from { filter: blur(14px); opacity: .25; transform: scale(1.04); }
                           to   { filter: none; opacity: 1; transform: none; } }
    @keyframes wallL { from { transform: translateX(var(--d, 0px)); } }
    @keyframes wallR { from { transform: translateX(calc(var(--d, 0px) * -1)); } }
  }
}
@media (max-width: 767px) and (prefers-reduced-motion: no-preference) {
  .wall { --part1: cover 40% cover 52%; --part2: cover 44% cover 56%; --part3: cover 48% cover 60%; }
}
.wall + .ruler { margin-bottom: var(--space-2); }


/* ------------------------------------------------------------ 6. services (P. 002) */
/* White, ink and one green hit (the ruler segment just above). Left: the parts-list index.
   Right: the exploded tee, generated by _dev/drawings/tee.py. */
.services { padding-bottom: var(--space-7); }
.services__grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
                  gap: var(--space-5); align-items: start;
                  padding: var(--space-5) var(--frame-pad) 0; }
.services__h { margin-bottom: var(--space-3); color: var(--fg-2); }

.parts { border-top: 2px solid var(--fg); }
.parts li { display: grid; grid-template-columns: 4rem minmax(0, 1fr); align-items: baseline;
            padding: var(--space-2) 0 calc(var(--space-2) + 2px);
            border-bottom: var(--rule) solid var(--line); }
.parts__name { font: 500 clamp(1.75rem, 3vw, 2.75rem)/1 var(--font-text); letter-spacing: -.025em; }

.tee { margin: 0; }
.tee svg { width: 100%; height: auto; overflow: visible; }
/* tee.py writes literal colours: ink lines, white faces, a few solid ink parts. Dark only they
   invert: lines and solid parts go to the type colour, faces to the page. The exposure greys
   v9.js sets are rgb() strings, so these never catch them. */
.tee svg [stroke="#151310"] { stroke: var(--fg); }
.tee svg [fill="#151310"] { fill: var(--fg); }
.tee svg [fill="#fff"] { fill: var(--bg); }
.tee__notes { font-family: var(--font-hand); font-size: 17px; }   /* Julio writes big: set down from 21 */
.tee__cap { display: grid; grid-template-columns: auto 1fr; gap: var(--space-2) var(--space-4);
            align-items: baseline; margin-top: var(--space-3); padding-top: var(--space-2);
            border-top: var(--rule) solid var(--line); }
.tee__legend { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: .5rem 1.25rem; }
.tee__legend b { font-weight: 700; margin-right: .35em; }
/* Exposure timer (v9.js, motion only): Julio's spec notes on layer D, run. It sits above the
   drawing because it plays while the top of the figure is what is on screen. */
.tee__timer { display: grid; gap: .5rem; width: 15rem; margin: 0 auto var(--space-3); padding: .6rem .75rem;
              background: var(--bg); color: var(--fg); box-shadow: inset 0 0 0 1px var(--line);
              font-variant-numeric: tabular-nums; }
.tee__timer > span:first-child { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; }
.tee__timer b { font: 500 1.35rem/1 var(--font-mono); }
.tee__bar { display: block; height: 4px; background: var(--line); }
.tee__bar > span { display: block; height: 100%; background: var(--green); transform: scaleX(0); transform-origin: left; }

@media (max-width: 1023px) {
  .services__grid { grid-template-columns: 1fr; gap: var(--space-6); }
}
@media (max-width: 767px) {
  .services__grid { padding-top: var(--space-4); }
  .tee { margin-inline: calc(var(--gutter) * -1); }        /* the drawing takes the full width */
  .tee__cap { grid-template-columns: 1fr; padding-inline: var(--gutter); }
  .tee__legend { justify-content: flex-start; }
}

/* The one big moment on this screen. The parts arrive stacked tight and ratchet apart into the
   exploded view as the drawing comes on screen: six hard steps, no easing. The leaders and
   letters ride with their part. The projection lines and pencil notes only appear once every
   part has locked. CSS scroll timeline, transform and opacity only, no JS. */
@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: view()) and (animation-range: entry)) {
    .tee { view-timeline: --tee block; }
    .tee .part { animation: teeOpen steps(6, end) both; animation-timeline: --tee;
                 animation-range: entry 5% cover 36%; }
    @keyframes teeOpen { from { transform: translateY(var(--d)); } }
    .tee__align, .tee__notes { animation: teeNote steps(1, end) both; animation-timeline: --tee;
                               animation-range: cover 36% cover 40%; }
    @keyframes teeNote { from { opacity: 0; } }
    /* the green highlighter under "mesh 110" swipes on once the notes are there */
    .tee__hl { stroke-dasharray: 1 1; animation: hlSwipe var(--settle) both; animation-timeline: --tee;
               animation-range: cover 40% cover 45%; }
    @keyframes hlSwipe { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
  }
}


/* ------------------------------------------------------------ 7. selected work (P. 003) */
/* Ink, hard switch from the white above. Two large case panels in black and white
   (_dev/grade.py). Hover or focus: the title brightens and the plate goes green. The client-hue
   hover was cut Oct 28 at Julio's call; the hue grades are in _dev/_retired/. */
.work { padding-bottom: var(--space-7); }
.work__head { display: flex; justify-content: space-between; align-items: center; gap: var(--space-3);
              padding: var(--space-5) var(--frame-pad) var(--space-3); }
.work__head h2 { color: var(--fg-2); }
.work__all { display: inline-flex; align-items: center; gap: .6em; padding: .75em 0; text-decoration: none; }
.work__count { color: var(--fg-2); }
.work__all:hover, .work__all:focus-visible { color: var(--green); }

/* Three case studies, three across (Nov 4, Julio), ruled between. The space between is a grid
   gap with the rule drawn down its middle, so all three panels are the same width (padding each
   side of a border made the middle one narrower) and the outer two sit flush with the frame. */
.cases { --case-gap: calc(var(--space-4) * 2);
         display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--case-gap);
         padding: 0 var(--frame-pad); border-top: var(--rule) solid var(--line); }
.case { position: relative; display: flex; flex-direction: column; gap: var(--space-1); padding-top: var(--space-3);
        text-decoration: none; color: var(--fg); }
.case + .case::before { content: ""; position: absolute; top: 0; bottom: 0; left: calc(var(--case-gap) / -2);
                        border-left: var(--rule) solid var(--line); pointer-events: none; }
.case__meta { display: flex; justify-content: space-between; gap: var(--space-2); color: var(--fg-2); }
.case__name { font: 600 var(--step-2)/1.1 var(--font-text); letter-spacing: -.02em; margin-bottom: var(--space-2); }

/* 4:5 (all three pictures are portrait) and never taller than 62% of the screen, so a whole panel
   (code, name, image, title) reads in one view at 1440x900. Phones go square, below. */
.case__img { position: relative; display: block; aspect-ratio: 4 / 5; max-height: 62svh;
             overflow: hidden; background: var(--bg); }
.case__img img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.case__img--high img { object-position: 50% 54%; }   /* keeps the flag's lettering and mark in frame */

.case__foot { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-2);
              margin-top: var(--space-2); }
.case__title { font: 400 var(--step-1)/1.25 var(--font-text); color: var(--fg-2); }
.case__go { flex: 0 0 auto; padding: .75em .9em; border: var(--rule) solid var(--line); }
.case:hover .case__title, .case:focus-visible .case__title { color: var(--fg); }
.case:hover .case__go, .case:focus-visible .case__go { background: var(--green); border-color: var(--green); color: var(--on-green); }
.case:focus-visible { outline-offset: 8px; }
/* Small laptops: three columns are ~290px, so the title and its button stack instead of squeezing. */
@media (min-width: 1024px) and (max-width: 1199px) {
  .cases { --case-gap: calc(var(--space-3) * 2); }
  .case__foot { flex-direction: column; align-items: flex-start; }
}
/* Tablets: three columns would be ~210px and the codes and names wrap unevenly, so each case
   becomes a ruled row instead: code, name and title on the left, the picture on the right. */
@media (min-width: 768px) and (max-width: 1023px) {
  .cases { grid-template-columns: 1fr; border-top: 0; }
  .case { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: auto auto 1fr;
          column-gap: var(--space-4); padding: var(--space-3) 0; border-top: var(--rule) solid var(--line); }
  .case + .case::before { content: none; }
  .case__meta { grid-column: 1; grid-row: 1; }
  .case__name { grid-column: 1; grid-row: 2; }
  .case__img { grid-column: 2; grid-row: 1 / 4; }
  .case__foot { grid-column: 1; grid-row: 3; align-self: end; flex-direction: column; align-items: flex-start; }
}


@media (max-width: 767px) {
  .work__head { flex-wrap: wrap; }
  .cases { grid-template-columns: 1fr; border-top: 0; }
  .case { padding: var(--space-4) 0 0; }
  .case + .case::before { content: none; }
  .case { border-top: var(--rule) solid var(--line); }
  .case + .case { margin-top: var(--space-5); }
  .case__img { aspect-ratio: 1; max-height: none; }
}


/* ------------------------------------------------------------ 8. process (P. 004) */
/* White. Five stations drawn as a spec sheet: the track is a ruler, each station is a square node
   on it, and an elbow callout runs from the node down to the station number. Numbers and linework
   only, no icons. One green segment on the track marks the current station. */
.process { padding-bottom: var(--space-7); }
.process__head { padding: var(--space-5) var(--frame-pad) var(--space-4); }
.process__head h2 { color: var(--fg-2); }

.stations { position: relative; margin: 0 var(--frame-pad); --node: 9px; }
.stations__track { position: absolute; inset: 0 0 auto; height: 18px; translate: 0 -18px;
                   background:
                     linear-gradient(var(--fg), var(--fg)) left bottom / 100% 1px no-repeat,
                     repeating-linear-gradient(to right, var(--fg) 0 1px, transparent 1px 8px) left bottom / 100% 6px no-repeat;
                   color: var(--fg); }
/* One station wide. It steps to the next station in five hard positions (see the motion below). */
.stations__seg { position: absolute; left: 0; bottom: -1px; width: 20%; height: 4px; background: var(--green); }

.stations__list { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); }
.station { position: relative; padding: var(--space-6) var(--space-4) 0 calc(var(--node) + 22px); }
/* the node, centred on the track line */
.station::before { content: ""; position: absolute; left: 0; top: calc(var(--node) / -2);
                   width: var(--node); height: var(--node); background: var(--fg); }
/* the elbow callout: down from the node, then across to the number */
.station::after { content: ""; position: absolute; left: calc(var(--node) / 2 - .5px);
                  top: calc(var(--node) / 2); width: 16px; height: calc(var(--space-6) + .4rem);
                  border-left: var(--rule) solid var(--fg); border-bottom: var(--rule) solid var(--fg); }
.station__no { display: block; line-height: .8rem; margin-bottom: var(--space-2); }
.station__name { font: 600 var(--step-2)/1.05 var(--font-text); letter-spacing: -.025em;
                 text-transform: none; margin-bottom: var(--space-1); }
.station__copy { color: var(--fg-2); max-width: 22ch; }

/* Under 1024 the track turns vertical down the left edge and the callouts run across. */
@media (max-width: 1023px) {
  .stations { padding-left: 28px; }
  .stations__track { inset: 0 auto 0 0; width: 18px; height: auto; translate: 0;
                     background:
                       linear-gradient(var(--fg), var(--fg)) left top / 1px 100% no-repeat,
                       repeating-linear-gradient(to bottom, var(--fg) 0 1px, transparent 1px 8px) left top / 6px 100% no-repeat; }
  .stations__seg { left: -1px; top: 0; bottom: auto; width: 4px; height: 20%; }
  .stations__list { grid-template-columns: 1fr; grid-auto-rows: 1fr; }   /* equal rows: the 20% segment lands on each */
  .station { padding: 0 0 var(--space-5) 20px; }
  .station::before { left: calc(-28px - var(--node) / 2 + .5px); top: .15rem; }
  .station::after { left: calc(-28px + var(--node) / 2); top: calc(.15rem + var(--node) / 2);
                    width: 40px; height: 0; border-left: 0; }
  .station__no { margin-bottom: var(--space-1); }
}

/* The segment ratchets from station 01 to 05 while the whole section is on screen, from the
   moment its bottom edge arrives until its top nears the masthead. Five positions, no tween
   between them. Without support it sits on 01. */
@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: view()) and (animation-range: entry)) {
    .process { view-timeline: --proc block; }
    .stations__seg { animation: procStep steps(5, jump-none) both; animation-timeline: --proc;
                     animation-range: contain 0% contain 80%; }
    @keyframes procStep { to { transform: translateX(400%); } }
  }
}
@media (max-width: 1023px) and (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: view()) and (animation-range: entry)) {
    /* Taller than the screen here, so the steps follow the station nearest mid-screen. */
    @keyframes procStepY { to { transform: translateY(400%); } }
    .stations__seg { animation-name: procStepY; animation-range: cover 25% cover 75%; }
  }
}


/* ------------------------------------------------------------ 9. how we think (P. 005) */
/* Ink. The claim, the three stories as proof, and Fig. 02 beside them: the mark in construction
   (_dev/drawings/mark.py). Above the line, the BLACKCAT wordmark small, as people meet it; below,
   the same wordmark big, as the drawing it is built from. The class names (.sign*) are from the
   figure's first version, a monument sign; they are kept so the motion below did not change.
   Centred beside the stories; it plays its sequence as it crosses the screen: the visible part is
   plotted and fills, then the construction is dug out (motion block below). */
.think { padding-bottom: var(--space-7); }
.think__grid { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
               gap: var(--space-6); align-items: start;
               padding: var(--space-5) var(--frame-pad) 0; }
.think__eyebrow { color: var(--fg-2); margin-bottom: var(--space-3); }
.think__h { font: 600 clamp(2.5rem, 5.4vw, 5.25rem)/.98 var(--font-text); letter-spacing: -.04em;
            max-width: 12ch; }
.think__sub { font: 400 var(--step-1)/1.35 var(--font-text); color: var(--fg-2);
              margin: var(--space-2) 0 var(--space-6); max-width: 30ch; }

.stories { border-top: var(--rule) solid var(--line); }
.story { display: grid; grid-template-columns: 4rem minmax(0, 1fr); align-items: baseline;
         padding: var(--space-3) 0; border-bottom: var(--rule) solid var(--line); }
.story > .label { color: var(--fg-2); }
.story p { font-size: 1.125rem; line-height: 1.55; max-width: 54ch; }
.story__go { grid-column: 2; justify-self: start; display: inline-flex; align-items: center; gap: .6em;
             margin-top: var(--space-2); padding: .75em 0; text-decoration: none; }
.story__go:hover, .story__go:focus-visible { color: var(--green); }
/* Advisory door under the stories (Nov 4): the thinking sold on its own. Same plate as the
   Social Church door so "lamp + arrow" keeps one meaning. */
.think__advisory { margin: var(--space-4) 0 0; }
.think__text > .story__go { margin-top: var(--space-1); }

/* Centred against the stories and still: it used to ride down the page sticky, which read as a
   bug (Julio, Oct 28). */
.sign { margin: 0; position: relative; align-self: center; }
.sign svg { width: 100%; height: auto; color: var(--fg); }
.sign .o { fill: var(--bg); }
.sign__hand { font-family: var(--font-hand); font-size: 17px;     /* set down from 21 (SVG units) */
              fill: var(--green); fill-opacity: 1; }               /* on ink, the notes can be green */
.sign__soil { display: none; }   /* only exists while the dig can run; without it the drawing is whole */
.sign__cap { display: grid; gap: var(--space-2); margin-top: var(--space-3); padding-top: var(--space-2);
             border-top: var(--rule) solid var(--line); }
.sign__cap > p { color: var(--fg-2); }
.sign__legend { display: flex; flex-wrap: wrap; gap: .5rem 1.25rem; }
.sign__legend b { font-weight: 700; margin-right: .35em; color: var(--fg); }

@media (max-width: 1023px) {
  .think__grid { grid-template-columns: 1fr; gap: var(--space-6); }
  .sign { max-width: 560px; }
}
@media (max-width: 767px) {
  .story { grid-template-columns: 2.75rem minmax(0, 1fr); }
  .story p { font-size: 1.0625rem; }
}

/* Fig. 02 in motion, one scroll-driven sequence in two acts.
   PLOT: the visible part is drawn the way a pen plotter would, in three passes (B-L-A-C, K-C-A-T,
   then the line), each path from a single dash of length 1 (every path has pathLength="1").
   Then the wordmark fills in, and the A tag and "what people see" fade up with it.
   DIG: everything below the line starts under a solid ink block, so the mark stands on nothing you
   can see. The block slides down out of the frame (eased, Julio: "animate smoother", Nov 4; it
   was four hard cuts), uncovering the construction
   (guides, stem grid, anchors, handles, B-F) and last "what holds it up".
   It runs on the figure's own view timeline (--sign): the plot as the drawing comes up the screen,
   the dig while its lower half is in view. The ranges are custom properties so each layout sets
   its own.
   The dig is transform only. The plot animates stroke-dashoffset, a paint property, on ~20
   short paths, only while they are on screen. */
@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: view()) and (animation-range: entry)) {
    .sign { view-timeline: --sign block;
            --sign-tl: --sign;
            --plot1: cover 10% cover 23%;  --plot2: cover 15% cover 28%;  --plot3: cover 21% cover 31%;
            --tags: cover 28% cover 37%;   --dig: cover 34% cover 51%;    --note2: cover 46% cover 54%; }
    .sign .p1, .sign .p2, .sign .p3 { stroke-dasharray: 1 1; stroke-dashoffset: 1;
                                       animation: signPlot ease-in-out both; animation-timeline: var(--sign-tl); }
    .sign .p1 { animation-range: var(--plot1); }
    .sign .p2 { animation-range: var(--plot2); }
    .sign .p3 { animation-range: var(--plot3); }
    @keyframes signPlot { to { stroke-dashoffset: 0; } }

    .sign__tags, .sign__note1 { animation: signOn ease-out both; animation-timeline: var(--sign-tl);
                                animation-range: var(--tags); }
    .sign__note2 { animation: signOn ease-out both; animation-timeline: var(--sign-tl);
                   animation-range: var(--note2); }
    @keyframes signOn { from { opacity: 0; } }

    .sign__soil { display: block; animation: signDig cubic-bezier(.45, 0, .2, 1) both; animation-timeline: var(--sign-tl);
                  animation-range: var(--dig); }
    @keyframes signDig { to { transform: translateY(340px); } }   /* the block is 324.5 units tall (mark.py prints it) */
  }
}
@media (max-width: 1023px) and (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: view()) and (animation-range: entry)) {
    .sign { --sign-tl: --sign;
            --plot1: cover 9% cover 24%;   --plot2: cover 15% cover 30%;  --plot3: cover 22% cover 35%;
            --tags: cover 32% cover 41%;   --dig: cover 37% cover 54%;    --note2: cover 49% cover 57%; }
  }
}


/* ------------------------------------------------------------ 10. studio (P. 006) */
/* White. The crew as three square black-and-white portraits with mono tags: name on the left,
   role on the right, a hairline above. Under 768 the row becomes a native swipe rail (scroll
   snap, no JS) with the next portrait peeking, so three squares do not cost three screens.
   The Record (ink) follows, so the white/black switch holds without a divider here. */
.studio { padding-bottom: var(--space-6); }
.studio__head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-2);
                padding: var(--space-5) var(--frame-pad) var(--space-3); color: var(--fg-2); }

.crew { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3);
        padding: 0 var(--frame-pad); }
.crew__shot { position: relative; display: block; overflow: hidden; }   /* the shutter's frame */
.crew__member img { width: 100%; height: auto; aspect-ratio: 1; object-fit: cover; background: var(--bg); }
.crew__tag { display: flex; justify-content: space-between; gap: var(--space-2);
             margin-top: var(--space-1); padding-top: var(--space-1);
             border-top: var(--rule) solid var(--fg); }
.crew__role { color: var(--fg-2); text-align: right; }

@media (max-width: 767px) {
  .crew { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 78%;
          overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory;
          scroll-padding-inline: var(--gutter); scrollbar-width: none; padding-bottom: var(--space-1); }
  .crew::-webkit-scrollbar { display: none; }
  .crew__member { scroll-snap-align: start; }
  .crew__tag { flex-direction: column; gap: .35rem; }   /* name over role: the long role squeezed the name */
  .crew__role { text-align: left; }
}


/* ------------------------------------------------------------ 11. partners (in Selected work) */
/* The eleven partner marks under the case panels, white on ink, standing still. The source SVGs
   are drawn in black, so a static filter turns them white (never animated, so it costs nothing
   after first paint). Each mark sits in a fixed-height cell and keeps its own proportions. */
.partners { margin: var(--space-6) var(--frame-pad) 0; padding-top: var(--space-3);
            border-top: var(--rule) solid var(--line); }
.partners__h { color: var(--fg-2); margin-bottom: var(--space-4); }
.partners__marks { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr));
                   gap: var(--space-5) var(--space-5); align-items: center; }
.partners__marks li { display: flex; align-items: center; justify-content: center; height: 52px; }
.partners__marks img { max-width: 100%; max-height: 100%; width: auto; height: auto;
                       filter: brightness(0) invert(1); opacity: .86; }
@media (max-width: 1023px) {
  .partners__marks { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (max-width: 767px) {
  .partners__marks { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4) var(--space-3); }
  .partners__marks li { height: 36px; }
}


/* ------------------------------------------------------------ 12. the record (P. 007) */
/* Ink. The four testimonials as a ruled ledger: REF code, the quote, who said it. On desktop two
   columns, so all four read in one screen without a rail or arrows; one column on phones. */
.record { padding-bottom: var(--space-7); }
.record__head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-2);
                padding: var(--space-5) var(--frame-pad) var(--space-3); color: var(--fg-2); }
.quotes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--space-6);
          padding: 0 var(--frame-pad); }
.quote { display: grid; grid-template-rows: auto 1fr auto; gap: var(--space-2);
         padding: var(--space-3) 0 var(--space-4); border-top: var(--rule) solid var(--line); }
.quote__code { color: var(--fg-2); }
.quote blockquote { margin: 0; }
.quote blockquote p { font: 500 var(--step-1)/1.4 var(--font-text); letter-spacing: -.01em; max-width: 44ch; }
.quote__who { display: flex; justify-content: space-between; gap: var(--space-2); }
.quote__org { color: var(--fg-2); text-align: right; }
@media (max-width: 767px) {
  .quotes { grid-template-columns: 1fr; }
  .quote blockquote p { font-size: 1.125rem; }
}


/* ------------------------------------------------------------ 13. start a project (P. 008) */
/* White. The page closes in the voice it opened with: Cook Conthic, as live text from the same
   woff2. Left: the heading, the line under it and the shop's terms. Right: the work order. Boxed
   fields with the label inside the box, a tear line, SEND IT on the end of it. Under both, the FAQ
   runs the full width (Julio, Nov 4). */
.start { padding-bottom: var(--space-7); }
.start__grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
               grid-template-areas: "lead form" "faq faq";
               gap: var(--space-6);
               padding: var(--space-5) var(--frame-pad) 0; }
.start__lead { grid-area: lead; }
.start__h { font: 400 clamp(3.5rem, 7.4vw, 7.5rem)/.86 var(--font-display); text-transform: uppercase;
            letter-spacing: 0; }
.start__sub { font: 400 var(--step-1)/1.35 var(--font-text); color: var(--fg-2);
              margin: var(--space-3) 0 var(--space-4); max-width: 30ch; }

.facts { border-top: var(--rule) solid var(--fg); }
.facts > div { display: grid; grid-template-columns: 5.5rem minmax(0, 1fr); align-items: baseline;
               padding: .7rem 0; border-bottom: var(--rule) solid var(--line); }
.facts dt { color: var(--fg-2); }
.facts a { text-underline-offset: .25em; }

/* the work order */
.wo { grid-area: form; align-self: start; border: var(--rule) solid var(--fg); padding: var(--space-3); }
.wo__head { display: flex; justify-content: space-between; gap: var(--space-2);
            padding-bottom: var(--space-2); margin-bottom: var(--space-3); border-bottom: 2px solid var(--fg); }
.wo__hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }   /* honeypot */
.wo__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2); }
.field { position: relative; display: flex; flex-direction: column; gap: .5rem; margin: 0; min-width: 0;
         padding: .75rem .9rem .8rem; border: var(--rule) solid var(--fg); }
.field--wide { grid-column: 1 / -1; }
.field legend { float: left; width: 100%; margin-bottom: .5rem; padding: 0; }   /* keeps the legend inside the box */
.field input, .field select, .field textarea { width: 100%; padding: 0; outline: 0;
                                               font: 400 1rem/1.4 var(--font-text); color: var(--fg); }
.field textarea { resize: vertical; min-height: 7.5rem; }
/* when validation jumps to a field, land it below the sticky masthead, label showing */
.field input, .field select, .field textarea { scroll-margin-top: calc(var(--header-h) + 3.5rem); }
.field select { appearance: none; cursor: pointer; padding-right: 1.5rem; }
.field:has(select)::after { content: "\25BE"; position: absolute; right: .9rem; bottom: .85rem; pointer-events: none; }
/* Focus lands on the box, ink on white. */
.field:focus-within { outline: 2px solid var(--fg); outline-offset: 2px; }
.field:has(.tick input:focus-visible) { outline: 0; }
/* Invalid: a heavier rule and a tag in the corner, so it is never signalled by colour alone. */
.field:has([aria-invalid="true"]) { border-width: 2px; padding: calc(.75rem - 1px) calc(.9rem - 1px) calc(.8rem - 1px); }
.field:has([aria-invalid="true"])::before { content: "Check this"; position: absolute; top: -1px; right: -1px;
                                            padding: .35rem .5rem; background: var(--fg); color: var(--bg);
                                            font: 500 var(--step--1)/1 var(--font-mono); letter-spacing: .06em;
                                            text-transform: uppercase; }

/* project type: square ticks */
.ticks { clear: both; display: flex; flex-wrap: wrap; gap: .5rem 1.25rem; }
.tick { display: inline-flex; align-items: center; gap: .55rem; padding: .3rem 0; cursor: pointer; }
.tick input { appearance: none; flex: 0 0 auto; width: 15px; height: 15px; margin: 0;
              border: var(--rule) solid var(--fg); background: var(--bg); cursor: pointer; }
.tick input:checked { background: var(--fg); box-shadow: inset 0 0 0 3px var(--bg); }
.tick input:focus-visible { outline: 2px solid var(--fg); outline-offset: 2px; }

/* the tear line, with SEND IT at its end */
.wo__tear { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-3); }
.wo__tear > .label { flex: 1; display: flex; align-items: center; gap: var(--space-2); color: var(--fg-2); }
.wo__tear > .label::after { content: ""; flex: 1; border-top: 1.5px dashed var(--fg); }
.wo__send { flex: 0 0 auto; font: 500 var(--step--1)/1 var(--font-mono); letter-spacing: .06em;
            text-transform: uppercase; padding: 1.05em 1.2em; }
.wo__send:disabled { cursor: progress; }
.wo__state { margin-top: var(--space-2); padding: .8rem .9rem; border: var(--rule) solid var(--fg); }   /* boxed like the fields */
@media (prefers-reduced-motion: no-preference) {
  .wo__state { transition: opacity 320ms var(--smooth), translate 320ms var(--smooth), display 320ms allow-discrete; }
  @starting-style { .wo__state { opacity: 0; translate: 0 -6px; } }
}

/* FAQ: native details, eased open (below). The open question becomes a green field with ink type
   (Julio, Nov 4), the same field as Process and the record: the roles are re-pointed, so the
   answer, the rule and the +/- all go ink. It bleeds half a gap into the gutter so the text never
   moves when it turns green. Closed questions light green on hover, green on ink being 15.5:1. */
.faq { grid-area: faq; align-self: start; }
.faq__h { color: var(--fg-2); margin-bottom: var(--space-2); }
.faq__item { border-top: var(--rule) solid var(--line); }
.faq__item:last-child { border-bottom: var(--rule) solid var(--line); }
.faq__q { display: flex; justify-content: space-between; gap: var(--space-2); padding: .9rem 0;
          font: 600 1.0625rem/1.3 var(--font-text); cursor: pointer; list-style: none; }
.faq__q::-webkit-details-marker { display: none; }
/* + and - drawn as two bars, so opening can fold the upright bar away instead of swapping glyphs */
.faq__q::after { content: ""; flex: 0 0 auto; width: 11px; height: 11px; margin-top: .3em;
                 background: linear-gradient(currentColor, currentColor) center / 11px 1.5px no-repeat,
                             linear-gradient(currentColor, currentColor) center / 1.5px 11px no-repeat; }
.faq__item[open] .faq__q::after { background-size: 11px 1.5px, 1.5px 0; }
.faq__a { padding: 0 0 1rem; color: var(--fg-2); max-width: 68ch; }   /* full width now: a readable measure, not the row */
.faq__item[open] { --fg: var(--on-green); --fg-2: rgba(11, 10, 8, .72); --line: rgba(11, 10, 8, .22);
                   background: var(--green); color: var(--on-green); border-color: var(--green);
                   margin-inline: calc(var(--space-1) * -1); padding-inline: var(--space-1); }
.faq__item[open] :focus-visible { outline-color: var(--on-green); }
.faq__item[open] ::selection { background: var(--on-green); color: var(--green); }
.faq__item:not([open]) .faq__q:hover { color: var(--green); }
/* Opening eases the panel to its height and fades the answer in just behind it; closing runs it
   back. Native in Chromium (interpolate-size + ::details-content); js/v9.js does the same with
   the Web Animations API where that is missing, so Safari and Firefox get it too. */
@media (prefers-reduced-motion: no-preference) {
  .faq__q::after { transition: background-size 320ms var(--smooth); }
  .faq__item { transition: background-color 320ms var(--smooth), color 320ms var(--smooth); }
  .faq__q { transition: color 160ms var(--lock); }
  .faq__a { transition: opacity 320ms var(--smooth), translate 420ms var(--smooth); }
  .faq__item:not([open]) .faq__a, .faq__item.is-closing .faq__a { opacity: 0; translate: 0 -6px; }
  /* a closed panel is not rendered, so the fade needs a starting point when it first appears */
  @starting-style { .faq__item[open] .faq__a { opacity: 0; translate: 0 -6px; } }
  @supports (interpolate-size: allow-keywords) {
    .faq__item { interpolate-size: allow-keywords; }
    .faq__item::details-content { height: 0; overflow: clip;
      transition: height var(--t-open) var(--smooth), content-visibility var(--t-open) allow-discrete; }
    .faq__item[open]::details-content { height: auto; }
    .faq__item[open] .faq__a { transition-delay: 60ms; }
  }
}

@media (max-width: 1023px) {
  .start__grid { grid-template-columns: 1fr; grid-template-areas: "lead" "form" "faq"; gap: var(--space-5); }
}
@media (max-width: 767px) {
  .wo { padding: var(--space-2); }
  .wo__grid { grid-template-columns: 1fr; }
  .wo__head { flex-direction: column; gap: .4rem; }
}


/* ------------------------------------------------------------ 14. footer */
/* Ink. Email, place, year; the links (store, socials, all work) as plain mono text; the ruler
   strip with its green segment as scroll progress, the same one that sits under the hero. */
.site-footer { padding: var(--space-5) 0 var(--space-3); }
.foot__row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: baseline; gap: var(--space-2);
             padding: 0 var(--frame-pad) var(--space-4); }
.foot__row > :nth-child(2) { justify-self: center; }
.foot__row > :last-child { justify-self: end; }
.foot__mail { font: 600 clamp(1.25rem, 2.4vw, 2rem)/1.1 var(--font-text); letter-spacing: -.02em;
              text-decoration: none; justify-self: start; }
.foot__mail:hover, .foot__mail:focus-visible { color: var(--green); }
.foot__links { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap;
               gap: var(--space-2) var(--space-4); padding: 0 var(--frame-pad) var(--space-4); }
.foot__text { display: flex; flex-wrap: wrap; gap: .25rem var(--space-4); }
.foot__text a { padding: .6rem 0; text-decoration: none; color: var(--fg-2); }
.foot__text a:hover, .foot__text a:focus-visible { color: var(--green); }
/* Socials as live's solid marks. 44px targets around 24px icons; the paths carry no fill of their
   own, so they take the link colour. */
.foot__social { display: flex; gap: .25rem; margin-right: -10px; }
.foot__social a { display: grid; place-items: center; width: 44px; height: 44px; color: var(--fg-2); }
.foot__social svg { width: 24px; height: 24px; fill: currentColor; }
.foot__social a:hover, .foot__social a:focus-visible { color: var(--green); }
.foot__legal { padding: var(--space-2) var(--frame-pad) 0; color: var(--fg-2); }
/* Year 05 (v9.js): a globe of digits turns, then every digit drops into place as 05, five years
   since 2021, and the number lights green. A canvas, played once on arrival; reduced motion gets
   the finished 05. Without JS the band is not shown at all. */
.foot__year { display: none; margin: 0 var(--frame-pad) var(--space-5); border-bottom: var(--rule) solid var(--line); }
.js .foot__year { display: block; }
.foot__year canvas { display: block; width: 100%; height: clamp(220px, 30vw, 420px); }
.foot__year-cap { display: flex; justify-content: space-between; padding: var(--space-2) 0; color: var(--fg-2); }

@media (max-width: 767px) {
  .foot__row { grid-template-columns: 1fr 1fr; }
  .foot__mail { grid-column: 1 / -1; margin-bottom: var(--space-1); }
  .foot__row > :nth-child(2) { justify-self: start; }
  .foot__text { gap: 0 var(--space-3); }
  .foot__social { margin-left: -10px; margin-right: 0; }
}


/* ------------------------------------------------------------ 15. motion: the print shop */
/* Julio's picks from _dev/motion-lab.html (Oct 28): squeegee pull, mechanical counters,
   misregistration, camera shutter, drafting crosshair. Each is one shot, fired by js/v9.js when
   its element is on screen, and js/v9.js only builds the extra markup when motion is welcome. So
   with reduced motion, no JS or an old browser the page is exactly as approved, and still.
   Squeegee prints the headings on white; misregistration jogs the headings on ink, where its
   green plate is allowed. The earlier fade-up layer (drawn rules, rise, lens rack) is gone. */
@media (prefers-reduced-motion: no-preference) {

  /* SQUEEGEE. The heading waits as a faint stencil; one hard-edged pass prints it left to right
     and the blade lifts at the end of the stroke. */
  .sq { position: relative; display: inline-block; }
  .sq__stencil { position: absolute; inset: 0; opacity: .08; pointer-events: none; }
  .sq__ink { display: inline-block; clip-path: inset(-10% 100% -10% 0); }
  .sq__blade { position: absolute; top: -6%; bottom: -6%; left: 0; width: 5px; background: currentColor;
               border-right: 3px solid var(--green); opacity: 0; translate: -50% 0; pointer-events: none; }
  .sq.is-on .sq__ink { animation: sqInk 760ms cubic-bezier(.55, 0, .3, 1) var(--dl, 0ms) forwards; }
  .sq.is-on .sq__blade { animation: sqBlade 760ms cubic-bezier(.55, 0, .3, 1) var(--dl, 0ms) forwards; }
  @keyframes sqInk { to { clip-path: inset(-10% 0 -10% 0); } }
  @keyframes sqBlade { 0% { left: 0; opacity: 1; } 94% { left: 100%; opacity: 1; } 100% { left: 100%; opacity: 0; } }

  /* MISREGISTRATION. A green second plate prints a few points out, jogs into register in three
     hard steps and drops out under the white. Ink sections only. */
  .mis { position: relative; display: inline-block; isolation: isolate; }
  .mis__ghost { position: absolute; inset: 0; z-index: -1; color: var(--green); opacity: 0; pointer-events: none; }
  .mis.is-on .mis__ghost { animation: misLock 480ms steps(3, end) var(--dl, 0ms) forwards; }
  @keyframes misLock { 0% { opacity: 1; transform: translate(.06em, -.035em); }
                       99% { opacity: 1; transform: none; } 100% { opacity: 0; transform: none; } }

  /* COUNTERS. Each digit is a wheel 0-9 twice over in a one-digit window. At rest it shows its
     digit; on cue it spins one full turn and locks, wheels stopping left to right. */
  .ctr__col { display: inline-block; height: 1em; overflow: hidden; vertical-align: top; }
  .ctr__strip { display: grid; transform: translateY(calc(var(--n) * -1em)); }
  .ctr__strip > span { height: 1em; line-height: 1em; }
  .is-on > .ctr .ctr__strip { animation: ctrRoll var(--t) steps(var(--s), end) both; }
  @keyframes ctrRoll { from { transform: translateY(0); } to { transform: translateY(calc(var(--s) * -1em)); } }

  /* SHUTTER. Two ink blades over the photo open in three clicks. The frame never moves. */
  .blade { position: absolute; left: 0; right: 0; height: 50.5%; z-index: 1; background: var(--bg); pointer-events: none; }
  .blade--t { top: 0; border-bottom: 1px solid rgba(255, 255, 255, .22); }
  .blade--b { bottom: 0; border-top: 1px solid rgba(255, 255, 255, .22); }
  .is-open > .blade--t { animation: shutT 330ms steps(3, end) var(--dl, 0ms) forwards; }
  .is-open > .blade--b { animation: shutB 330ms steps(3, end) var(--dl, 0ms) forwards; }
  @keyframes shutT { to { transform: translateY(-101%); } }
  @keyframes shutB { to { transform: translateY(101%); } }

  /* HOVER. Arrows step forward, the button lamp blinks twice. */
  .case__go span, .work__all > span:last-child, .story__go > span:last-child, .foot__links a span {
    display: inline-block; transition: transform 160ms var(--lock); }
  .case:hover .case__go span, .case:focus-visible .case__go span,
  .work__all:hover > span:last-child, .work__all:focus-visible > span:last-child,
  .story__go:hover > span:last-child, .story__go:focus-visible > span:last-child { transform: translateX(4px); }
  .foot__links a:hover span, .foot__links a:focus-visible span { transform: translate(2px, -2px); }
  .btn:hover .lamp, .btn:focus-visible .lamp { animation: lampBlink 520ms steps(1, end) 2; }
  @keyframes lampBlink { 50% { opacity: .15; } }
}

/* CROSSHAIR. Over Fig. 01 and Fig. 02, on a mouse: a thin crosshair and a readout in the
   drawing's own units. Pure pointer feedback, so it is not a motion question; it only needs a
   fine pointer that can hover. */
.tee { position: relative; }
.has-x svg { cursor: none; }
.xh { position: absolute; z-index: 2; overflow: hidden; pointer-events: none; opacity: 0; }
.is-x > .xh { opacity: 1; }
.xh__h, .xh__v { position: absolute; background: currentColor; opacity: .5; }
.xh__h { left: 0; right: 0; top: 0; height: 1px; }
.xh__v { top: 0; bottom: 0; left: 0; width: 1px; }
.xh__read { position: absolute; left: 0; top: 0; padding: .35rem .5rem; white-space: nowrap;
            background: var(--fg); color: var(--bg); }
.on-ink .xh__read { background: var(--green); color: var(--on-green); }


/* ------------------------------------------------------------ 16. links (/links.html) */
/* The link-in-bio page, reached from Instagram, so it is built for a thumb first. One line, then
   every channel as a ruled row: number, name, a mono note, the arrow. The row you press becomes
   a green field with ink type, the same field as an open FAQ question; it bleeds half a gap into
   the gutter so the text never moves when it turns. */
.links { padding-bottom: var(--space-6); }
/* No site header here, so the logotype stands in for it, at the top, sized to lead the page. */
.links__mark { display: block; width: 186px; height: 48px; margin: var(--space-4) var(--frame-pad) var(--space-1); }
.links__mark:hover .logotype, .links__mark:focus-visible .logotype { background: var(--green); }
.links__head { padding: var(--space-6) var(--frame-pad) var(--space-5); max-width: calc(var(--content-max) + 2 * var(--gutter)); }
.links__eyebrow { color: var(--fg-2); margin-bottom: var(--space-3); }
.links__h { font: 600 clamp(2.75rem, 7vw, 6.25rem)/.95 var(--font-text); letter-spacing: -.045em; }
.links__sub { font: 400 var(--step-1)/1.35 var(--font-text); color: var(--fg-2); margin-top: var(--space-3); max-width: 32ch; }
.links__list { margin: 0 var(--frame-pad); border-top: var(--rule) solid var(--fg); }
.links__list li { border-bottom: var(--rule) solid var(--line); }
.link-row { display: grid; grid-template-columns: 3rem minmax(0, 1fr) auto 2rem; align-items: center;
            gap: var(--space-2); min-height: 4.5rem; padding: var(--space-2) var(--space-1);
            margin-inline: calc(var(--space-1) * -1); text-decoration: none; color: var(--fg); }
.link-row__no, .link-row__meta { color: var(--fg-2); }
.link-row__name { display: inline-flex; align-items: center; gap: .6rem;
                  font: 600 clamp(1.5rem, 3.2vw, 2.5rem)/1.05 var(--font-text); letter-spacing: -.03em; }
.link-row__go { justify-self: end; font: 500 1.25rem/1 var(--font-mono); }
.link-row:hover, .link-row:focus-visible, .link-row:active {
  --fg: var(--on-green); --fg-2: rgba(11, 10, 8, .72);
  background: var(--green); color: var(--on-green); }
.link-row:hover .lamp, .link-row:focus-visible .lamp, .link-row:active .lamp { background: var(--on-green); }
.link-row:focus-visible { outline-color: var(--on-green); outline-offset: -4px; }
@media (prefers-reduced-motion: no-preference) {
  .link-row { transition: background-color 160ms var(--lock), color 160ms var(--lock); }
  .link-row__go { transition: translate 160ms var(--lock); }
  .link-row:hover .link-row__go, .link-row:focus-visible .link-row__go { translate: 4px 0; }
}
/* Phones: the note drops under the name so the name keeps its size, and the spacing is set so all
   seven rows sit on one 375x812 screen (68px rows, still well over a 44px target). */
@media (max-width: 767px) {
  .links__mark { width: 140px; height: 36px; margin-top: var(--space-3); }
  .links__head { padding-block: var(--space-4) var(--space-3); }
  .links__eyebrow { margin-bottom: var(--space-2); }
  .links__sub { margin-top: var(--space-2); }
  .link-row { grid-template-columns: 2.25rem minmax(0, 1fr) 1.5rem; grid-template-areas: "no name go" "no meta go";
              row-gap: .3rem; min-height: 4.25rem; padding-block: .75rem; }
  .link-row__no { grid-area: no; align-self: start; padding-top: .45rem; }
  .link-row__name { grid-area: name; }
  .link-row__meta { grid-area: meta; }
  .link-row__go { grid-area: go; }
}
