/* ═══════════════════════════════════════════════════════════════════════════
   styles/site.css — the page
   ═══════════════════════════════════════════════════════════════════════════

   Structure from the reference build the owner chose: a light ground, centred
   dot-eyebrow section headers, cards with 1rem corners and a soft shadow, and
   pill buttons. What it does not do is stack nine identical white card grids:
   the rhythm here is dark hero → strip of facts → white cards → a full-width
   photograph → grey specification tables → a DARK gallery → white process
   rail → grey enquiry card → white questions → dark contact and footer.

   Nothing in this file listens to the scroll.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── shared furniture ───────────────────────────────────────────────────── */

.wrap { max-width: var(--page); margin: 0 auto; padding: 0 var(--margin); }

section { padding: clamp(72px, 8.4vw, 144px) 0; }
.sec-alt  { background: var(--bg-soft); }
.sec-dark { background: var(--bg-dark); color: var(--fg-on-dark); }

/* ── the structural grid ──────────────────────────────────────────────────────
   Four bays, five gridlines, running the length of the page the way the
   gridlines of a general-arrangement drawing run the length of a building.
   Each line sits in the middle of a gutter, so the content of every bay
   starts half a gutter to the right of its line — on the grid, not on top
   of it. A section opts in with .gl; the photographs cover it, which is the
   point: the grid is the drawing, the photograph is the steel.

   Behind the content (z-index -1 inside an isolated section), decorative
   and pointer-transparent. Two lines — the page edges — below a tablet,
   where a line through the middle of the text column would cut the copy. */
.gl { position: relative; isolation: isolate; --gl-c: var(--gl-ink); }
.gl.on-dark, .gl.sec-dark, .on-dark .gl { --gl-c: var(--gl-ink-dark); }
.gl::before {
  content: ""; position: absolute; z-index: -1; top: 0; bottom: 0; left: 50%;
  width: calc(min(100%, var(--page)) - 2 * var(--margin) + var(--gut));
  transform: translateX(-50%); pointer-events: none;
  border-right: 1px solid var(--gl-c);
  background: repeating-linear-gradient(90deg, var(--gl-c) 0 1px, transparent 1px 25%);
}
@media (max-width: 900px) {
  .gl::before { background: linear-gradient(90deg, var(--gl-c) 0 1px, transparent 1px); }
}

/* Grid bubbles: the circled letters at the head of each gridline. Only at
   the two ends of the page — the top of the first sheet and the foot of the
   footer — as on a drawing. */
.gl-bub {
  position: relative; left: 50%; transform: translateX(-50%);
  width: calc(min(100%, var(--page)) - 2 * var(--margin) + var(--gut));
  height: 34px; pointer-events: none;
}
.gl-bub i {
  position: absolute; top: 4px; width: 26px; height: 26px; margin-left: -13px;
  border: 1px solid currentColor; border-radius: var(--pill);
  display: grid; place-items: center;
  font: 600 11px/1 Archivo, "Archivo Fallback", sans-serif; font-style: normal; letter-spacing: .02em;
  color: var(--fg-faint); background: var(--bg);
}
.gl-bub i:nth-child(1) { left: 0; }
.gl-bub i:nth-child(2) { left: 25%; }
.gl-bub i:nth-child(3) { left: 50%; }
.gl-bub i:nth-child(4) { left: 75%; }
.gl-bub i:nth-child(5) { left: 100%; }
@media (max-width: 900px) {
  .gl-bub i:nth-child(2), .gl-bub i:nth-child(3), .gl-bub i:nth-child(4) { display: none; }
}

/* A section opens like a drawing sheet's title band: a heavy rule, the
   sheet number and its name on it, then the heading at scale with the lede
   answering it on the right. */
.sec-head {
  display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, .7fr);
  column-gap: var(--gut); align-items: end;
  margin: 0 0 clamp(40px, 5vw, 80px); padding-top: 14px;
  border-top: 2px solid var(--fg);
}
.sec-head .eyebrow { grid-column: 1 / -1; }
.sec-head > p { max-width: 42ch; padding-bottom: .5em; }
.sec-dark .sec-head, .on-dark .sec-head { border-top-color: var(--fg-on-dark); }
@media (max-width: 900px) {
  .sec-head { grid-template-columns: 1fr; }
  .sec-head h2 { margin-bottom: 16px; }
}
/* The label on the title band: the sheet number in red, then the name. */
.eyebrow {
  /* `align-self: start` because an inline-flex element is blockified when it
     becomes a flex item, and in the workshop's flex column it stretched to
     476px — leaving its trailing dot sitting in the middle of the block. */
  display: inline-flex; align-self: start; align-items: baseline; gap: 14px;
  font: 600 13.5px/1.2 Archivo, "Archivo Fallback", sans-serif; font-stretch: 112%;
  letter-spacing: .15em; text-transform: uppercase; color: var(--fg-muted);
  margin-bottom: clamp(26px, 3vw, 44px);
}
.eyebrow .sn { color: var(--accent-ink); letter-spacing: .04em; font-variant-numeric: tabular-nums; }
.sec-dark .eyebrow .sn, .on-dark .eyebrow .sn { color: var(--accent-on-dark); }
.sec-dark .eyebrow, .on-dark .eyebrow { color: var(--fg-on-dark-muted); }

.sec-head h2 {
  font-size: var(--t-h2); line-height: .92; letter-spacing: -.045em;
  margin-bottom: 0; max-width: 11em;
}
@media (max-width: 900px) { .sec-head h2 { margin-bottom: 18px; } }
.sec-head p:not(.eyebrow) { font-size: var(--t-lede); color: var(--fg-muted); line-height: 1.5; }
.sec-head > .eyebrow { max-width: none; padding-bottom: 0; }
.sec-dark .sec-head p, .on-dark .sec-head p { color: var(--fg-on-dark-muted); }

/* ── buttons ────────────────────────────────────────────────────────────── */

/* Archivo, not Inter: the page's own lettering, a touch expanded, so a
   button reads as a stamped label rather than as interface chrome. */
.btn, .btn-ghost {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 50px; padding: 0 26px;
  border: 1.5px solid transparent; border-radius: 0;
  font: 700 15px/1 Archivo, "Archivo Fallback", sans-serif; font-stretch: 108%; letter-spacing: .01em;
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: background-color 180ms var(--ease), border-color 180ms var(--ease),
              color 180ms var(--ease), transform 150ms var(--ease);
}
.btn:active, .btn-ghost:active { transform: translateY(1px); }

.btn { background: var(--accent); color: var(--accent-on); }
.btn:hover { background: var(--accent-hover); }
/* The primary action carries an arrow that travels on hover. */
.btn > span + svg, .btn .go { transition: transform 220ms var(--ease); }
.btn:hover > span + svg, .btn:hover .go { transform: translateX(4px); }

.btn-ghost { background: transparent; color: var(--fg); border-color: var(--fg); }
.btn-ghost:hover { background: var(--fg); color: var(--bg); }
.on-dark .btn-ghost, .sec-dark .btn-ghost {
  color: var(--fg-on-dark); border-color: rgba(243, 245, 246, .5);
}
.on-dark .btn-ghost:hover, .sec-dark .btn-ghost:hover {
  background: var(--fg-on-dark); color: var(--bg-dark); border-color: var(--fg-on-dark);
}

/* Every art-directed photograph on the site is a <picture>: a 16:9 cut above
   900px and the uncut original below it. `display: contents` removes the
   wrapper box entirely, so every rule written against the image — sizing,
   object-fit, absolute positioning — still reaches it, and no empty inline box
   is left behind in a section that only meant to hold a backdrop. */
picture { display: contents; }

/* ── header ─────────────────────────────────────────────────────────────── */

/* The bar's full height, border included. The home page hero is pulled up by
   exactly this so the header sits OVER the opening scene rather than above it,
   so the two have to stay in step. */
:root { --hd-h: 77px; }

.hd {
  position: sticky; top: 0; z-index: 120;
  background: rgba(11, 14, 16, .86);
  backdrop-filter: saturate(160%) blur(14px);
  border-bottom: 1px solid var(--line);
  transition: background 260ms var(--ease), border-color 260ms var(--ease);
}
.hd-in {
  display: flex; align-items: center; gap: 34px;
  min-height: calc(var(--hd-h) - 1px);
  max-width: var(--page); margin: 0 auto; padding: 0 var(--margin);
}

/* ── the header at the top of the home page ───────────────────────────────────
   The first sheet is the same ground as the bar, so at the top of the page
   the bar has no edge at all — it is the head of the sheet, not a strip laid
   across it. It gains its rule the moment the hero passes under it. The class
   is only ever ADDED by an observer that ran (and by the inline script in
   <head> before first paint), so with JavaScript off the bar is simply the
   solid bar it is on every other page. See main.js. */
html.hd-over .hd {
  background: transparent;
  backdrop-filter: none;
  border-bottom-color: transparent;
}

@media (prefers-reduced-motion: reduce) {
  .hd { transition: none; }
}
/* The logo is artwork, not type: it keeps its own proportions and the anchor
   is sized by it rather than the reverse. */
.hd-mark { margin-right: auto; display: inline-flex; align-items: center; min-height: 48px; }
.hd-mark img { width: auto; }

/* Navigation: an underline drawn from the left on hover, in the accent, on
   the baseline — the same stroke the red-pencil markup uses. */
.hd-nav { display: flex; gap: 30px; }
.hd-nav a {
  position: relative; font-size: var(--t-ui); font-weight: 500;
  color: var(--fg-muted); white-space: nowrap; padding: 6px 0;
}
.hd-nav a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1.5px;
  background: var(--accent); transform: scaleX(0); transform-origin: 100% 50%;
  transition: transform 300ms var(--ease);
}
.hd-nav a:hover { color: var(--fg); }
.hd-nav a:hover::after, .hd-nav a[aria-current]::after { transform: scaleX(1); transform-origin: 0 50%; }
/* Between the phone menu (760px) and a full desktop bar the four links and
   two buttons do not fit: at 768 "What we do" broke onto two lines. The
   phone number is one tap away in the hero and the phone bar, so the header
   drops its Call button here and keeps Get a price. */
@media (min-width: 761px) and (max-width: 1000px) { .hd-act .btn-ghost { display: none; } }

.hd-act { display: flex; gap: 10px; }
.hd-act .btn, .hd-act .btn-ghost { min-height: 44px; padding: 0 20px; font-size: 14px; }
/* Widths that hold whichever face is drawing them, so the navigation beside
   them does not move when the webfont swaps in. */
.hd-act .btn { min-width: 132px; }
.hd-act .btn-ghost { min-width: 104px; }
.hd-act .btn-ghost { border-color: var(--line-strong); }
.hd-act .btn-ghost:hover { border-color: var(--fg); }
.hd-menu { display: none; }

/* ── hero ───────────────────────────────────────────────────────────────── */

/* The first sheet. The headline is set at the full width of the grid, as the
   title of a drawing is; the photograph under it is the detail the drawing
   calls out, and as the page scrolls that detail opens from the grid to the
   full width of the screen. */
.hero { position: relative; padding: 0; background: var(--bg); color: var(--fg); overflow: hidden; }
.hero .gl-bub { margin-top: clamp(8px, 1.1vw, 16px); }
.hero-in { position: relative; padding-top: clamp(14px, 1.6vw, 24px); }

.hero-top {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px 28px;
  flex-wrap: wrap; margin-bottom: clamp(14px, 1.8vw, 28px);
}
.hero-eyebrow, .hero-meta {
  font: 600 var(--t-label)/1.3 Archivo, "Archivo Fallback", sans-serif; font-stretch: 112%;
  letter-spacing: .14em; text-transform: uppercase; color: var(--fg-muted);
}
.hero-eyebrow::before {
  content: ""; display: inline-block; width: 9px; height: 9px; margin-right: 12px;
  background: var(--accent); vertical-align: 0;
}
.hero-meta { display: flex; flex-wrap: wrap; gap: 6px 26px; }

/* Sized to the grid: the longer line spans the four bays. */
.hero-title {
  font-size: clamp(50px, 11.2vw, 178px); line-height: .84; letter-spacing: -.05em;
  margin: 0 0 clamp(22px, 2.4vw, 36px); margin-left: -.045em;
}
.hero-title .ln { display: block; overflow: hidden; padding-bottom: .06em; margin-bottom: -.06em; }
.hero-title .ln > span { display: inline-block; }
.hero-title .ln + .ln { text-align: right; }
.hero-title .pt { color: var(--accent); }

.hero-foot {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); column-gap: var(--gut);
  align-items: end;
}
.hero-lede { grid-column: 1 / span 2; font-size: var(--t-lede); line-height: 1.5;
             color: var(--fg-muted); max-width: 46ch; }
.hero-act { grid-column: 3 / span 2; display: flex; flex-wrap: wrap; gap: 10px; justify-content: flex-end; }
@media (max-width: 900px) {
  .hero-foot { grid-template-columns: minmax(0, 1fr); row-gap: 22px; }
  .hero-lede, .hero-act { grid-column: 1; }
  .hero-act { justify-content: flex-start; }
}
@media (max-width: 760px) {
  /* The four disciplines are in the lede directly below; on a phone the
     reference line would only push the headline down. */
  .hero-meta { display: none; }
  .hero-title { margin-bottom: 18px; }
  /* Taller on a phone, where the frame is narrow: the uncut file and the
     portrait markup (see .hero-mark-tall). */
  .hero .hero-plate { height: min(125vw, 72svh); }
  .hero-cap { padding: 10px 14px 10px 0; font-size: 12.5px; }
  .hero-cap span:last-child { display: none; }
}

/* ── the plate ────────────────────────────────────────────────────────────────
   Full width in the layout, clipped to the grid at rest. As the page scrolls
   the clip opens to the screen edges and the photograph eases back from a
   close crop — the technique of the "scroll reveal image" pattern, done in
   CSS on the compositor's timeline rather than with a width animated from
   script. Browsers without scroll-driven animation get the plate at rest:
   clipped to the grid, which is the composition as designed. */
.hero-plate {
  --pi: max(var(--margin), calc((100% - var(--page)) / 2 + var(--margin)));
  position: relative; margin-top: clamp(28px, 3.4vw, 52px);
  /* A height, not aspect-ratio + max-height: with both, a short wide window
     capped the height and the browser narrowed the plate to keep the ratio,
     so the clip centred on a box that no longer spanned the screen. */
  width: 100%; height: min(56.25vw, calc(100vh - var(--hd-h)));
  overflow: hidden; background: var(--bg-dark);
  clip-path: inset(0 var(--pi));
}
.hero-zoom { position: absolute; inset: 0; transform-origin: 50% 40%; }
/* Centred, because the markup over it can only be registered to a centred
   crop (SVG slice has no 54% / 47%). */
.hero-img { position: absolute; inset: 0; width: 100%; height: 100%;
            object-fit: cover; object-position: 50% 50%;
            animation: hero-settle 2.4s cubic-bezier(.16, 1, .3, 1) both; }
/* The photograph arrives already visible and eases a few percent back to
   rest. No opacity fade: the first paint is the finished picture. */
@keyframes hero-settle { from { transform: scale(1.07); } to { transform: none; } }

/* ── the first sheet being drawn ──────────────────────────────────────────────
   Once, on load, in the order a drawing is made: the gridlines run down the
   sheet, the bubbles are set at their heads, the title is lettered one line
   at a time from behind its own datum, then the notes. About a second in
   all; the photograph under it is never held back. Reduce Motion gets the
   finished sheet. */
@media (prefers-reduced-motion: no-preference) {
  .hero.gl::before { transform-origin: 50% 0; animation: gl-draw 1500ms var(--ease-power) both; }
  .hero .gl-bub i { animation: bub-in 520ms var(--ease) both; }
  .hero .gl-bub i:nth-child(2) { animation-delay: 70ms; }
  .hero .gl-bub i:nth-child(3) { animation-delay: 140ms; }
  .hero .gl-bub i:nth-child(4) { animation-delay: 210ms; }
  .hero .gl-bub i:nth-child(5) { animation-delay: 280ms; }
  .hero-title .ln > span { animation: ln-rise 1000ms var(--ease-heavy) 120ms both; }
  .hero-title .ln + .ln > span { animation-delay: 240ms; }
  .hero-top, .hero-foot { animation: note-in 800ms var(--ease) 420ms both; }
}
/* Held back until Archivo is in — see the inline script in <head>. The
   title's own rise starts when it is released, not before. */
html.fl .hero-title, html.fl .sp-title { visibility: hidden; }
html.fl .hero-title .ln > span { animation: none; }
@keyframes gl-draw { from { transform: translateX(-50%) scaleY(0); } to { transform: translateX(-50%) scaleY(1); } }
@keyframes bub-in { from { opacity: 0; transform: scale(.6); } }
@keyframes ln-rise { from { transform: translateY(104%); } }
@keyframes note-in { from { opacity: 0; transform: translateY(10px); } }

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 761px) {
    .hero-plate {
      animation: plate-open linear both;
      animation-timeline: scroll(root block); animation-range: 0 75vh;
    }
    /* The resting scale is also the static style: a scroll timeline attaches
       after the first frame, and a plate that painted at 1 and then jumped
       to 1.16 was scored as a layout shift even though its clip hid it. */
    .hero-zoom {
      transform: scale(1.16);
      animation: plate-zoom linear both;
      animation-timeline: scroll(root block); animation-range: 0 90vh;
    }
  }
}
@keyframes plate-open { from { clip-path: inset(0 var(--pi)); } to { clip-path: inset(0 0); } }
@keyframes plate-zoom { from { transform: scale(1.16); } to { transform: none; } }

/* ── the markup: red pencil on the photograph ─────────────────────────────
   Same box and same settle as the image, so it stays registered while the
   photograph eases in. Each callout draws once — ring, leader, label — then
   holds. The drawn state is the default; motion only ever hides it for the
   length of the animation, and reduced motion skips that entirely. */
.hero-mark { position: absolute; inset: 0; z-index: 2; width: 100%; height: 100%;
             pointer-events: none; animation: hero-settle 2.4s cubic-bezier(.16, 1, .3, 1) both; }
.hero-mark-tall { display: none; }
@media (max-width: 899.98px) { .hero-mark-wide { display: none; } .hero-mark-tall { display: block; } }
.hero-mark circle, .hero-mark path {
  fill: none; stroke: var(--accent); stroke-width: 1.6; vector-effect: non-scaling-stroke;
  stroke-dasharray: 1.05 1; stroke-dashoffset: 0;
}
.hero-mark text {
  font-family: Archivo, "Archivo Fallback", sans-serif; font-size: 12px; font-weight: 600; fill: #fff;
  paint-order: stroke; stroke: rgba(10, 12, 11, .82); stroke-width: 4px; stroke-linejoin: round;
}
.hero-mark .mk-k { fill: #ff4d53; }
.hero-mark-tall text { font-size: 14px; }
@keyframes mk-draw { from { stroke-dashoffset: 1.05; } }
@keyframes mk-show { from { opacity: 0; } }
.hero-mark circle { stroke-dasharray: none; animation: mk-show 220ms var(--ease) both; }
.hero-mark path   { animation: mk-draw 520ms var(--ease) both; }
.hero-mark text   { animation: mk-show 300ms var(--ease) both; }
.hero-mark g:nth-of-type(1) > * { animation-delay: 700ms; }
.hero-mark g:nth-of-type(1) path { animation-delay: 900ms; }
.hero-mark g:nth-of-type(1) text { animation-delay: 1350ms; }
.hero-mark g:nth-of-type(2) > * { animation-delay: 950ms; }
.hero-mark g:nth-of-type(2) path { animation-delay: 1150ms; }
.hero-mark g:nth-of-type(2) text { animation-delay: 1600ms; }
.hero-mark g:nth-of-type(3) > * { animation-delay: 1200ms; }
.hero-mark g:nth-of-type(3) path { animation-delay: 1400ms; }
.hero-mark g:nth-of-type(3) text { animation-delay: 1850ms; }
.hero-mark g:nth-of-type(4) > * { animation-delay: 1450ms; }
.hero-mark g:nth-of-type(4) path { animation-delay: 1650ms; }
.hero-mark g:nth-of-type(4) text { animation-delay: 2100ms; }
@media (prefers-reduced-motion: reduce) {
  .hero-img, .hero-mark,
  .hero-mark circle, .hero-mark path, .hero-mark text { animation: none; }
}

/* A plate caption, the way a drawing labels a detail: number, title, note.
   On its own dark field at the foot of the frame, so it does not depend on
   what the photograph is doing behind it. */
.hero-cap {
  position: absolute; z-index: 3; left: var(--pi); bottom: 0;
  display: flex; align-items: baseline; gap: 6px 18px; flex-wrap: wrap;
  padding: 12px 18px 12px 0; background: var(--bg-dark);
  font-size: var(--t-small); color: var(--fg-on-dark-muted);
}
.hero-cap b {
  font: 600 var(--t-label)/1 Archivo, "Archivo Fallback", sans-serif; font-stretch: 112%;
  letter-spacing: .14em; text-transform: uppercase; color: var(--red-on-dark);
}
.hero-cap span:first-of-type { color: var(--fg-on-dark); font-weight: 500; }

/* ── the title block ─────────────────────────────────────────────────────────
   Four facts on the steel band directly under the photograph, one to a bay,
   so a buyer has the machine, the crane, the address and the hours before
   scrolling once. Read like the title block of a drawing: a small label, the
   figure large in Archivo's condensed width, then the detail. */

.strip { background: var(--bg-dark); color: var(--fg-on-dark); }
.strip-in {
  max-width: var(--page); margin: 0 auto; padding: 0 var(--margin);
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); column-gap: var(--gut);
}
.strip-in li {
  padding: clamp(24px, 2.6vw, 40px) 0 clamp(26px, 2.8vw, 44px);
  display: grid; gap: 6px; align-content: start;
}
.strip-k {
  font: 600 var(--t-label)/1.2 Archivo, "Archivo Fallback", sans-serif; font-stretch: 112%;
  letter-spacing: .14em; text-transform: uppercase; color: var(--fg-on-dark-muted);
}
.strip-f { font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 800; font-stretch: 62%;
           font-size: clamp(40px, 4.4vw, 72px); line-height: .95; color: var(--fg-on-dark);
           letter-spacing: -.01em; margin: 6px 0 4px; }
.strip-v { font-size: 14px; color: var(--fg-on-dark-muted); line-height: 1.45; max-width: 30ch; }

/* ═══════════════════════════════════════════════════════════════════════════
   CAPABILITY CHAPTERS
   ═══════════════════════════════════════════════════════════════════════════
   Five compositions, deliberately NOT one template. The card grid this
   replaced gave a full-bleed structure photograph, a pair of to-scale
   drawings and a 25-tonne crane the same 340px box, and the page read as a
   catalogue. Each chapter below decides its own ground, its own media and its
   own proportions.
   ═══════════════════════════════════════════════════════════════════════════ */

.ch { position: relative; overflow: hidden; }
.ch-light { background: var(--bg); --plate-cover: var(--bg); }
.ch-dark  { background: var(--bg-dark); color: var(--fg-on-dark);
            --plate-cover: var(--bg-dark); }

.ch-copy { max-width: 44ch; }
.ch-title { font-size: clamp(38px, 4.9vw, 80px); line-height: .92; letter-spacing: -.045em;
            margin-bottom: 22px; }
.ch-lede { font-size: var(--t-lede); color: var(--fg-muted); margin-bottom: 28px; line-height: 1.5; }
.ch-dark .ch-lede { color: var(--fg-on-dark-muted); }

/* ── the large figure ─────────────────────────────────────────────────────────
   Only where the capability earned one. Tabular numerals so a tolerance does
   not wobble, and the unit set smaller than the number it qualifies. */
.ch-big {
  font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 800; font-stretch: 62%;
  font-size: clamp(64px, 9.4vw, 148px); line-height: .88;
  letter-spacing: -.02em; font-variant-numeric: tabular-nums;
  color: var(--accent); margin-bottom: 10px;
}
.ch-dark .ch-big { color: var(--accent-on-dark); }
.ch-big b { font-weight: 700; font-size: .42em; letter-spacing: 0; font-stretch: 100%; }
.ch-big-label {
  font-size: var(--t-small); color: var(--fg-faint); margin-bottom: 28px;
}
.ch-dark .ch-big-label { color: var(--fg-on-dark-muted); }

/* ── the figures rail ─────────────────────────────────────────────────────── */
.ch-figures { display: grid; margin-bottom: 26px; }
.ch-figures > div {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 20px; padding: 11px 0; border-top: 1px solid var(--line);
  font-size: var(--t-ui);
}
.ch-dark .ch-figures > div { border-top-color: var(--line-dark); }
.ch-figures dt { color: var(--fg-muted); }
.ch-dark .ch-figures dt { color: var(--fg-on-dark-muted); }
.ch-figures dd { font-weight: 500; text-align: right; font-variant-numeric: tabular-nums; }

.ch-ask {
  display: flex; gap: 11px; font-size: var(--t-ui); font-weight: 500;
  padding-top: 18px; border-top: 1px solid var(--line);
}
.ch-dark .ch-ask { border-top-color: var(--line-dark); }
.ch-ask::before {
  content: ""; flex: none; width: 3px; align-self: stretch; margin-top: 3px;
  border-radius: var(--pill); background: var(--accent);
}
.ch-dark .ch-ask::before { background: var(--accent-on-dark); }

/* ── drawn geometry — the engineering-drawing register ─────────────────────── */

.ch-draw { display: grid; gap: clamp(26px, 3.2vw, 44px); }
.pf-fig { display: grid; gap: 8px; }
.pf-fig figcaption {
  font-size: 12px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--fg-faint); font-weight: 600;
}
.pf { width: 100%; height: auto; overflow: visible; }
.pf-line {
  fill: none; stroke: var(--fg); stroke-width: 6;
  stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke;
}
.ch-dark .pf-line { stroke: var(--fg-on-dark); }
.pf-datum {
  fill: none; stroke: var(--line-strong); stroke-width: 1;
  stroke-dasharray: 14 8; vector-effect: non-scaling-stroke;
}
.pf-dim path {
  fill: none; stroke: var(--accent); stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
}
.pf-dim text {
  fill: var(--accent-ink); font-family: Inter, sans-serif; font-size: 24px;
  font-weight: 500; text-anchor: middle;
  font-variant-numeric: tabular-nums;
}
.pf-note { font-size: var(--t-small); color: var(--fg-faint); max-width: 52ch; }

.pf-folds {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(18px, 2.6vw, 38px);
  padding: clamp(22px, 2.6vw, 36px) clamp(20px, 2.4vw, 34px);
  background: var(--bg-soft); border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.pf-fold { display: grid; gap: 10px; }
.pf-fold svg { width: 100%; height: auto; min-height: clamp(96px, 11vw, 152px); }
.pf-fold figcaption {
  font-size: 12px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--fg-faint); font-weight: 600;
}
/* The drawings are the subject of this chapter; the photograph supports them,
   so it goes first and the sections read as the detail drawn off it. */
.ch-draw { grid-auto-flow: row; }

/* ── 03 · the laser strip ─────────────────────────────────────────────────────
   Four 3:4 photographs stood upright. A portrait source cropped into a 16:9
   band shows 41% of its frame; upright and side by side the whole frame is
   used, and the row reads as a machine bed rather than as four thumbnails. */

/* No top padding: this chapter opens on its establishing photograph, which
   runs to the section's own top edge. The head below supplies the space the
   padding used to. */
.ch-cut { padding: 0; }

/* ── the establishing frame ─────────────────────────────────────────────────
   The machine cutting, full width, with ±0.1 MM set across it. What was here
   before was 380px of flat black with the tolerance stated beside nothing.

   The scrim is a wash from the left rather than a flat tint over the whole
   frame. Type needs an opaque field under it; the right of this photograph —
   the DXTECH badge, the nozzle, the sparks off the plate — is the evidence,
   and nothing is laid over that. The stops are set from measured contrast
   behind the glyphs, not by eye. */
.cut-scene { position: relative; overflow: hidden; isolation: isolate; }
.cut-scene .zoom { cursor: zoom-in; }
/* The band takes the wide cut's own 16:9 rather than a height of its own.
   Measured the other way round first: a 620px band at 1440 is 2.32:1, which
   re-crops the 16:9 file by 23% of its height — and the two things this
   photograph is in the page for, the DXTECH badge at the top of the frame and
   the sparks at the bottom, are exactly what that trims off. A scene that
   loses its subject to fit a height is not a scene. */
.cut-scene img {
  width: 100%; aspect-ratio: 16 / 9; max-height: 86vh;
  object-fit: cover; display: block;
}
.cut-scene::after {
  content: ''; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background:
    linear-gradient(to top, rgba(8, 10, 9, .84) 0%, rgba(8, 10, 9, .52) 20%,
      rgba(8, 10, 9, 0) 46%),
    linear-gradient(to right,
      rgba(8, 10, 9, .96) 0%, rgba(8, 10, 9, .93) 24%,
      rgba(8, 10, 9, .58) 44%, rgba(8, 10, 9, .14) 66%,
      rgba(8, 10, 9, 0) 82%);
}
/* Low in the frame, not centred: centred puts the figure across the cutting
   head, and the head is the one thing in here worth not covering. */
.cut-tol {
  position: absolute; inset: 0; z-index: 3; pointer-events: none;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding-bottom: clamp(38px, 5.5vw, 84px);
}
/* The figure is already `.ch-big`; only its measure and its trailing space
   change in here. */
.cut-tol .ch-big { margin-bottom: 8px; max-width: 12ch; }
.cut-tol .ch-big-label { margin-bottom: 0; max-width: 32ch; }

.ch-cut-head { padding-top: clamp(56px, 6.4vw, 104px); }
/* On the grid: the copy takes bays A–B, the cutting data bays C–D, so the
   figures start on gridline C rather than wherever the prose ended. */
.ch-cut-head {
  padding-bottom: clamp(44px, 5vw, 80px);
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: var(--gut); row-gap: 36px; align-items: end;
}
.ch-cut-head > .ch-copy { grid-column: 1 / span 2; max-width: none; }
.ch-cut-head .ch-lede { max-width: 46ch; }
.ch-cut-data { grid-column: 3 / span 2; max-width: none; }
@media (max-width: 900px) {
  .ch-cut-head { grid-template-columns: minmax(0, 1fr); }
  .ch-cut-head > .ch-copy, .ch-cut-data { grid-column: 1; }
}
.ch-cut-data .ch-big { margin-top: -.1em; }

/* Every rule here is scoped to the list, not to the section. */
.ch-strip {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 10px; padding: 0 clamp(10px, 1.4vw, 18px) clamp(10px, 1.4vw, 18px);
}
.ch-strip > li { position: relative; overflow: hidden; border-radius: var(--r); }
html.reveal-ready .ch-strip > li::after { transition-delay: var(--d, 0ms); }
.ch-strip > li img {
  width: 100%; height: clamp(360px, 54vh, 640px); object-fit: cover; display: block;
}
/* The caption's own field. Probed against the brightest pixels in these
   photographs — the laser's white casing runs to rgb(255,255,255) — where a
   scrim needs to hold 0.58 for white 13.5px type to clear AA. The old
   gradient reached about 0.35 where the glyphs actually sit, so it failed by
   a wide margin while looking fine. */
.ch-strip > li span:not(.zoom-cue) {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 26px 16px 13px;
  font-size: var(--t-small); color: var(--fg-on-dark);
  background: linear-gradient(to top,
    rgba(8, 10, 9, .96) 0%, rgba(8, 10, 9, .94) 62%,
    rgba(8, 10, 9, .62) 84%, rgba(8, 10, 9, 0) 100%);
}

/* ── the workshop ───────────────────────────────────────────────────────────── */

/* The copy on the grid's left half, the photograph running off the right edge
   of the screen from gridline C. The place name is set the way it is
   stencilled on the overalls in the photograph beside it. */
.ws { padding: 0; }
.ws .ws-img, .ws .ws-copy { min-width: 0; }
.ws {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: stretch;
}
.ws-img { overflow: hidden; order: 2; }
.ws-img img { width: 100%; height: 100%; min-height: clamp(420px, 70vh, 820px); object-fit: cover; }
.ws-copy {
  padding: clamp(64px, 7vw, 120px) var(--gut)
           clamp(64px, 7vw, 120px) max(var(--margin), calc((100vw - var(--page)) / 2 + var(--margin)));
  display: flex; flex-direction: column; justify-content: center;
}
.ws-place {
  font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 800; font-stretch: 125%; line-height: .95;
  font-size: clamp(30px, 3.4vw, 56px); letter-spacing: -.02em; text-transform: uppercase;
  margin-bottom: 26px;
}
.ws-place b { display: block; color: var(--accent); font-weight: 800; }
.ws-copy h2 { font-size: clamp(22px, 1.9vw, 30px); letter-spacing: -.02em; line-height: 1.15;
              margin-bottom: 14px; max-width: 24ch; }
.ws-lede { color: var(--fg-muted); margin-bottom: 26px; max-width: 48ch; }
.ws-figures { margin-bottom: 30px; max-width: 520px; }
.ws-act { display: flex; flex-wrap: wrap; gap: 12px; }

/* ── specifications ─────────────────────────────────────────────────────────
   Native <details>. They work with JavaScript off, and every figure is in the
   DOM whether the group is open or shut. */

/* A data sheet: every group open, flowing down two columns on a wide screen
   so all the figures are on view at once. On a phone main.js folds all but
   the first; with JavaScript off everything stays open. */
.spec-list { display: grid; gap: 14px; }
@media (min-width: 1024px) {
  .spec-list:has(> .spec-group + .spec-group) { display: block; columns: 2; column-gap: 14px; }
  .spec-list > .spec-group { break-inside: avoid; margin-bottom: 14px; }
  /* One group on its own (most service pages): its rows take the two columns. */
  .spec-list:not(:has(> .spec-group + .spec-group)) .spec-rows { columns: 2; column-gap: 48px; }
  .spec-list:not(:has(> .spec-group + .spec-group)) .spec-rows > div { break-inside: avoid; }
}
.spec-group {
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm); overflow: hidden;
}
.spec-group > summary, .faq-item > summary {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding: 19px 24px; cursor: pointer; list-style: none;
  font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 700; font-size: 17px;
  letter-spacing: -.008em;
}
.spec-group > summary::-webkit-details-marker,
.faq-item > summary::-webkit-details-marker { display: none; }
.spec-group > summary::after, .faq-item > summary::after {
  content: ""; flex: none; width: 11px; height: 11px;
  border-right: 2px solid var(--accent); border-bottom: 2px solid var(--accent);
  transform: rotate(45deg) translateY(-2px);
  transition: transform 180ms var(--ease);
}
.spec-group[open] > summary::after, .faq-item[open] > summary::after {
  transform: rotate(-135deg) translateY(-2px);
}
.spec-group > summary:hover, .faq-item > summary:hover { background: var(--bg-soft); }

.spec-rows { padding: 0 24px 8px; }
.spec-rows > div {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 24px; padding: 13px 0; border-top: 1px solid var(--line);
}
.spec-rows dt { color: var(--fg-muted); font-size: var(--t-ui); }
.spec-rows dd { font-weight: 500; text-align: right;
                font-variant-numeric: tabular-nums; }

/* A drawing schedule: each group opens on a black title bar carrying its
   schedule number, and the rows under it are ruled, not boxed. */
.spec-list { counter-reset: sg; }
.spec-group { counter-increment: sg; background: var(--surface); border: 0; box-shadow: none; }
.spec-group > summary {
  background: #222A2F; color: var(--fg);
  font-size: 13px; font-stretch: 112%; letter-spacing: .12em; text-transform: uppercase;
  padding: 15px 20px;
}
.spec-group > summary:hover { background: #2A3338; }
.spec-group > summary > span { display: flex; }
.spec-group > summary > span::before {
  content: "S" counter(sg); flex: none; min-width: 3.2em;
  color: var(--red-on-dark); letter-spacing: .06em;
}
.spec-group .spec-rows { padding: 0 20px 6px; }
.spec-group .spec-rows > div:first-child { border-top: 0; }

/* ── the zoom control ─────────────────────────────────────────────────────────
   Every photograph on the page is cropped to its composition, so the whole
   frame has to stay reachable from wherever it appears. A real <button>, so it
   is focusable and announced; the cue only appears on hover or focus. */

.zoom {
  display: block; width: 100%; height: 100%; padding: 0; border: 0;
  background: none; color: inherit; cursor: zoom-in; position: relative;
  font: inherit; text-align: inherit;
}
/* Deliberately NO rules on `.zoom > img`. Each container already states how
   tall its photograph is, and those rules are descendant selectors that still
   match through this wrapper. A `height: 100%` here silently beat all of them
   on equal specificity and collapsed every photograph to its intrinsic size. */
.zoom-cue {
  position: absolute; top: 12px; right: 12px; z-index: 4;
  width: 34px; height: 34px; border-radius: var(--pill);
  display: grid; place-items: center;
  background: rgba(8, 10, 9, .72); color: #fff;
  opacity: 0; transform: scale(.9);
  transition: opacity 200ms var(--ease), transform 200ms var(--ease);
}
.zoom:hover .zoom-cue, .zoom:focus-visible .zoom-cue { opacity: 1; transform: none; }
@media (hover: none) { .zoom-cue { opacity: .82; transform: none; } }

/* ── how it works ───────────────────────────────────────────────────────────
   Five commitments, so not five equal cards: a rail, read top to bottom, which
   is the order they happen in. */

.proc { background: var(--bg-soft); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
/* Five commitments laid out as a rail, left to right, in the order they
   happen. Large outlined numerals carry the sequence; the rail joins them. */
.steps {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 0; position: relative; counter-reset: none;
}
.steps::before {
  content: ""; position: absolute; left: 0; right: 0; top: 36px; height: 2px;
  background: linear-gradient(to right, var(--accent) 0 20%, var(--line) 20% 100%);
}
.step { position: relative; padding: 0 clamp(12px, 1.6vw, 22px) 0 0; }
.step-n {
  display: inline-flex; align-items: center; justify-content: center;
  width: 74px; height: 74px; border-radius: var(--pill);
  background: var(--bg); border: 2px solid var(--line);
  font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 800; font-size: 26px;
  letter-spacing: -.03em; color: var(--fg); position: relative; z-index: 1;
  transition: background-color 200ms var(--ease), color 200ms var(--ease), border-color 200ms var(--ease);
}
.step:first-child .step-n { background: var(--accent); border-color: var(--accent); color: var(--accent-on); }
.step:hover .step-n { border-color: var(--accent); color: var(--accent-ink); }
.step:first-child:hover .step-n { color: var(--accent-on); }
.step-body { padding-top: 22px; }
.step h3 { font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 700;
           font-size: clamp(17px, 1.5vw, 20px); letter-spacing: -.018em; line-height: 1.25; margin-bottom: 8px; }
.step p { color: var(--fg-muted); font-size: var(--t-ui); }

/* ── the programme chart (home page, desktop) ──────────────────────────────
   One row per commitment, a numbered balloon, and a bar on a broken axis:
   the first working week in days, a break for the customer's decision to
   order, then weeks from order with the range hatched. Positions come from
   render.js (--x0/--x1 as % of the chart column). Below 901px the list is the
   vertical rail above and the chart is not drawn. */
.g-axis, .g-lines, .g-bar { display: none; }
@media (min-width: 901px) {
  .gantt { position: relative; margin-top: clamp(32px, 4vw, 52px);
           --ball: 38px; --gap: 28px; --lab: 30%;
           --chart: calc(var(--ball) + var(--lab) + 2 * var(--gap)); }
  .g-axis { display: block; position: relative; margin-left: var(--chart); height: 64px;
            border-bottom: 1.5px solid var(--fg); }
  .g-zone { position: absolute; top: 0; left: var(--x0); width: calc(var(--x1) - var(--x0));
            font-size: 13px; font-weight: 600; color: var(--fg); }
  .g-zone { padding-left: 8px; border-left: 2px solid var(--accent); line-height: 1.2; }
  /* Under the break, at the foot of the chart: the one place it never collides. */
  .g-gap-label { position: absolute; bottom: -26px; left: calc((var(--x0) + var(--x1)) / 2); transform: translateX(-50%);
                 white-space: nowrap; color: var(--fg-faint); font-weight: 500; font-size: 12px; }
  .g-tick { position: absolute; left: var(--x); bottom: 8px; padding-left: 7px;
            font-size: 12px; color: var(--fg-faint); white-space: nowrap; }
  .g-tick-end { transform: translateX(-100%); padding: 0 7px 0 0; }
  .g-lines { display: block; position: absolute; top: 0; bottom: 0; left: var(--chart); right: 0; pointer-events: none; }
  .g-lines i { position: absolute; top: 34px; bottom: 0; left: var(--x); border-left: 1px solid var(--line); }
  /* The break: the time between quotation and order is the customer's, so
     the axis does not pretend to measure it. A drawing's break line. */
  .g-gap { position: absolute; top: 34px; bottom: 0; left: var(--x0); width: calc(var(--x1) - var(--x0));
           background:
             url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='24'%3E%3Cpath d='M8 0 L3 8 L13 16 L8 24' fill='none' stroke='%2371717a' stroke-width='1.2'/%3E%3C/svg%3E") center top / 16px 24px repeat-y,
             var(--bg-soft); }

  .steps-gantt { display: block; }
  .steps-gantt::before { display: none; }
  .steps-gantt .step {
    display: grid; grid-template-columns: var(--ball) var(--lab) 1fr; column-gap: var(--gap);
    align-items: center; padding: 16px 0; border-bottom: 1px solid var(--line);
  }
  .steps-gantt .step-n { width: var(--ball); height: var(--ball); font-size: 14px; border-width: 1.5px;
                         font-stretch: 62%; font-size: 16px; }
  .steps-gantt .step-body { padding-top: 0; }
  .steps-gantt .step h3 { font-size: 16px; margin-bottom: 2px; }
  .steps-gantt .step p { font-size: 14px; }
  .g-bar { display: block; position: relative; height: 16px; }
  .g-fill { position: absolute; top: 0; bottom: 0; left: var(--x0); width: calc(var(--x1) - var(--x0));
            background: var(--accent); transform-origin: 0 50%; }
  .g-range { background: repeating-linear-gradient(135deg, var(--accent) 0 2px, transparent 2px 7px);
             box-shadow: inset 0 0 0 1.5px var(--accent); }
  .g-ms { position: absolute; top: 50%; left: var(--x0); width: 14px; height: 14px; background: var(--accent);
          transform: translate(-7px, -50%) rotate(45deg); }
  /* The bars run out once, in order, when the list arrives. */
  html.reveal-ready .steps-gantt .g-fill { transform: scaleX(0); transition: transform 700ms var(--ease-power); }
  html.reveal-ready .steps-gantt.is-revealed .g-fill { transform: none; }
  .steps-gantt .step:nth-child(2) .g-fill { transition-delay: 150ms; }
  .steps-gantt .step:nth-child(3) .g-fill { transition-delay: 300ms; }
  .steps-gantt .step:nth-child(4) .g-fill { transition-delay: 450ms; }
  .steps-gantt .step:nth-child(4) .g-range { transition-delay: 800ms; }
}
@media (prefers-reduced-motion: reduce) { html.reveal-ready .steps-gantt .g-fill { transform: none; transition: none; } }

/* The rail ends on the action it describes, rather than in white space. */
.proc-close {
  margin: clamp(40px, 5vw, 60px) 0 0; padding: clamp(20px, 2.4vw, 28px) clamp(20px, 2.6vw, 32px);
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--r-lg);
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px 24px;
}
.proc-close p { color: var(--fg); font-weight: 500; flex: 1 1 260px; font-size: var(--t-lede); }
@media (max-width: 900px) {
  .steps { grid-template-columns: 1fr; gap: 26px; }
  .steps::before { left: 27px; right: auto; top: 10px; bottom: 10px; width: 2px; height: auto;
                   background: linear-gradient(to bottom, var(--accent) 0 18%, var(--line) 18% 100%); }
  .step { display: flex; gap: 18px; padding: 0; }
  .step-n { width: 56px; height: 56px; font-size: 20px; flex: none; }
  .step-body { padding-top: 6px; }
}

/* ── enquiry ──────────────────────────────────────────────────────────────────
   The closing act. The hero opened on a frame being erected; this is the same
   photograph, dimmed right down, with the form standing on it — so the page
   ends where it began and the last screen is not a form on flat grey.

   The card itself stays opaque and light. Every input, label, placeholder and
   error message keeps exactly the contrast it was measured at; nothing about
   the form's legibility depends on the photograph behind it. */

.enq { position: relative; overflow: hidden; }
.enq-bg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 54% 42%; opacity: .3;
}
.enq::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to bottom, rgba(8, 10, 9, .78) 0%,
                              rgba(8, 10, 9, .58) 45%, rgba(8, 10, 9, .86) 100%);
}
.enq > .wrap { position: relative; z-index: 1; }

/* The brief is a request-for-quotation sheet: the form takes bays B–D, and
   bay A carries the other way in — the phone — for anyone who would rather
   talk it through. */
.enq-body {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: var(--gut); row-gap: 28px; align-items: start;
}
.enq-aside { grid-column: 1; display: grid; gap: 14px; padding-top: 4px; }
.enq-aside-k { font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 700; font-size: 20px;
               letter-spacing: -.01em; line-height: 1.2; }
.enq-aside-t { color: var(--fg-on-dark-muted); font-size: var(--t-ui); }
.enq-aside-act { display: grid; gap: 10px; margin-top: 6px; }
.enq-aside-act .btn-ghost { justify-content: flex-start; }
.enq-aside-meta { display: grid; gap: 2px; font-size: var(--t-small); color: var(--fg-on-dark-muted);
                  border-top: 1px solid var(--line-dark); padding-top: 14px; margin-top: 8px; }
.enq-aside-meta b { color: var(--fg-on-dark); font-weight: 500; }

.enq-card {
  grid-column: 2 / span 3; margin: 0;
  background: var(--bg); border: 0; border-top: 3px solid var(--accent); border-radius: 0;
  padding: clamp(26px, 3.4vw, 52px); box-shadow: var(--shadow-lg);
  /* The card is light but the section around it is dark, so without this the
     card inherits --fg-on-dark. Everything inside used to name its own colour
     and get away with it; "Ready to send." did not, and was white on white. */
  color: var(--fg);
}
@media (max-width: 1100px) {
  .enq-body { grid-template-columns: minmax(0, 1fr); }
  .enq-aside, .enq-card { grid-column: 1; }
  .enq-aside { order: 2; }
  .enq-aside-act { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
}
/* Every field on the sheet is numbered, the way an RFQ's items are. */
.form { display: grid; gap: 22px; counter-reset: fld; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }
.field { display: grid; gap: 8px; align-content: start; counter-increment: fld; }
.field label { font-size: var(--t-small); font-weight: 500; color: var(--fg-muted); }
.field > label::before {
  content: counter(fld, decimal-leading-zero); display: inline-block; min-width: 2.3em;
  font: 600 11.5px/1 Archivo, "Archivo Fallback", sans-serif; letter-spacing: .06em; color: var(--accent-ink);
}
.field label .opt { font-weight: 400; color: var(--fg-faint); }
.field input, .field textarea, .field select {
  width: 100%; background: var(--surface); color: var(--fg);
  border: 1px solid var(--line-strong); border-radius: 0;
  padding: 13px 15px; min-height: 50px;
  transition: border-color 140ms var(--ease), box-shadow 140ms var(--ease);
}
.field input:hover, .field textarea:hover, .field select:hover { border-color: var(--fg-faint); }
.field textarea { min-height: 142px; resize: vertical; }
.field input:focus, .field textarea:focus, .field select:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.field input::placeholder, .field textarea::placeholder { color: #8F999F; }

/* A select with the platform arrow replaced, so it matches the inputs. */
.select { position: relative; }
.select select { appearance: none; padding-right: 44px; cursor: pointer; }
.select::after {
  content: ""; position: absolute; right: 18px; top: 50%; pointer-events: none;
  width: 9px; height: 9px; margin-top: -7px;
  border-right: 2px solid var(--fg-faint); border-bottom: 2px solid var(--fg-faint);
  transform: rotate(45deg);
}

.field-error { font-size: var(--t-small); color: var(--accent-ink); font-weight: 500; }
[aria-invalid="true"] { border-color: var(--accent) !important; }

.error-summary {
  padding: 15px 18px; border: 1px solid var(--accent); background: var(--accent-soft);
  border-radius: var(--r); color: var(--accent-ink); font-size: var(--t-ui);
  font-weight: 500;
}
.files-note {
  font-size: var(--t-small); color: var(--fg-faint);
  border-top: 1px solid var(--line); padding-top: 18px;
}
.privacy-note { font-size: var(--t-small); color: var(--fg-faint); margin-top: -6px; }
.privacy-note a { color: var(--fg-muted); text-decoration: underline; text-underline-offset: 2px; overflow-wrap: anywhere; }
.form-actions { display: flex; }
.form-actions .btn { min-width: 220px; }

.draft { display: none; margin-top: 28px; padding-top: 28px;
         border-top: 1px solid var(--line); }
.draft[data-open="true"] { display: block; }
.draft h3 { font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 700;
            font-size: var(--t-h3); margin-bottom: 14px; }
.draft-body {
  white-space: pre-wrap; font-size: 14.5px; line-height: 1.65; color: var(--fg-muted);
  background: var(--bg-soft); border: 1px solid var(--line); border-radius: var(--r);
  padding: 20px 22px; max-height: 320px; overflow: auto;
}
.draft-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 20px; }
.draft-status { margin-top: 14px; font-size: var(--t-ui); color: var(--fg-muted);
                min-height: 1.5em; }
.draft-gate { font-size: var(--t-ui); color: var(--fg-muted); flex: 1 0 100%; }

/* ── questions ──────────────────────────────────────────────────────────── */

/* Two columns: the heading and a way to ask stay put on the left while the
   questions scroll on the right. Numbered, because a phone call often goes
   "the fourth one on your site". */
.faq-wrap {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: var(--gut); row-gap: 32px; align-items: start;
}
.faq-side { grid-column: 1 / span 2; padding-right: clamp(0px, 4vw, 72px); }
.faq-list { grid-column: 3 / span 2; }
.faq-side { position: sticky; top: calc(var(--hd-h) + 32px); }
.faq-side .sec-head { text-align: left; margin: 0 0 28px; max-width: none; }
.faq-side .sec-head h2 { font-size: clamp(34px, 4.2vw, 58px); line-height: 1.02; }
.faq-side .eyebrow { justify-content: flex-start; }
.faq-ask {
  background: var(--bg-dark); color: var(--fg-on-dark);
  border-radius: var(--r-lg); padding: clamp(20px, 2.4vw, 28px);
  border-left: 3px solid var(--accent);
}
.faq-ask-k { font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 700; font-size: 19px; margin-bottom: 6px; }
.faq-ask-t { color: var(--fg-on-dark-muted); font-size: var(--t-ui); margin-bottom: 18px; }
.faq-ask-act { display: flex; flex-wrap: wrap; gap: 10px; }
.faq-ask .btn-ghost { color: var(--fg-on-dark); border-color: rgba(250, 250, 250, .34); }
.faq-ask .btn-ghost:hover { border-color: #fff; }

.faq-list { counter-reset: faq; border-top: 2px solid var(--fg); }
.faq-item { border-bottom: 1px solid var(--line); counter-increment: faq; }
.faq-item > summary { padding: 24px 4px; font-size: clamp(17px, 1.5vw, 20px); gap: 18px; }
.faq-item > summary > span::before {
  content: counter(faq, decimal-leading-zero);
  display: inline-block; min-width: 2.4em; color: var(--accent-ink);
  font-size: .78em; font-weight: 600; font-variant-numeric: tabular-nums;
}
.faq-item[open] > summary { color: var(--accent-ink); }
.faq-item > p { padding: 0 4px 26px calc(4px + 2.4em * .78); color: var(--fg-muted);
                max-width: 64ch; font-size: var(--t-body, 16px); line-height: 1.7; }
@media (max-width: 900px) {
  .faq-wrap { grid-template-columns: 1fr; }
  .faq-side, .faq-list { grid-column: 1; padding-right: 0; }
  /* On one column the questions come first and the "ask us" panel follows
     them, so a phone reader meets the answers before the fallback. */
  .faq-side { display: contents; }
  .faq-side .sec-head { order: 0; margin-bottom: 0; }
  .faq-list { order: 1; }
  .faq-ask { order: 2; }
}

/* ── contact (dark) ─────────────────────────────────────────────────────── */

/* A directory on the grid: the four ways to reach a person across the four
   bays, set large enough to read across a room; then the two addresses, the
   workshop and the hours at two bays each. */
.ct-list {
  /* minmax(0, 1fr), not 1fr: a bare `1fr` track has an `auto` minimum, so one
     unbreakable 34-character email address widened the column past the page
     and pushed the whole document into horizontal scroll at 768px. */
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: var(--gut);
  max-width: none; margin: 0;
}
.ct-list dd { overflow-wrap: anywhere; }
.ct-list li { border-top: 1px solid var(--line-dark); }
.ct-list li:nth-child(n+5) { grid-column: span 2; }
.ct-list dl { display: grid; gap: 12px; padding: 18px 0 30px; }
.ct-list dt {
  font: 600 var(--t-label)/1.2 Archivo, "Archivo Fallback", sans-serif; font-stretch: 112%;
  letter-spacing: .14em; text-transform: uppercase; color: var(--fg-on-dark-muted);
}
.ct-list dd {
  font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 700; font-stretch: 104%;
  font-size: clamp(19px, 1.75vw, 27px); line-height: 1.2; letter-spacing: -.01em; text-align: left;
}
@media (max-width: 1100px) {
  .ct-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ct-list li:nth-child(n+5) { grid-column: span 2; }
}
/* WCAG 2.5.8 wants a 24px minimum pointer target; on a phone these get 44px
   (see the small-screen block). A bare inline link measured 21px. */
.ct-list a, .ft a { display: inline-flex; align-items: center; min-height: 24px; }
.ct-list a { border-bottom: 1px solid rgba(250, 250, 250, .32); }
.ct-list a:hover { border-bottom-color: var(--fg-on-dark); }
.ct-map { margin: 40px 0 0; border-top: 1px solid var(--line-dark); padding-top: 24px; }
.ct-map iframe {
  display: block; width: 100%; height: clamp(280px, 38vw, 460px); border: 0;
  filter: grayscale(1) contrast(1.05); background: #1a1a1a;
}
.ct-map figcaption {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px; padding: 14px 0 0;
  font: 600 var(--t-label)/1.2 Archivo, "Archivo Fallback", sans-serif; letter-spacing: .14em;
  text-transform: uppercase; color: var(--fg-on-dark-muted);
}
.ct-map figcaption a { display: inline-flex; align-items: center; min-height: 44px; color: var(--fg-on-dark); border-bottom: 1px solid rgba(250, 250, 250, .32); }
.ct-map figcaption a:hover { border-bottom-color: var(--fg-on-dark); }

.ft {
  background: var(--bg-dark); color: var(--fg-on-dark-muted);
  border-top: 1px solid var(--line-dark); padding: 34px 0 0;
  font-size: var(--t-small); overflow: hidden;
}
.ft-in {
  max-width: var(--page); margin: 0 auto; padding: 0 var(--margin);
  display: flex; align-items: center; flex-wrap: wrap; gap: 18px 40px;
}
.ft-logo img { width: auto; }
.ft-meta { display: flex; flex-wrap: wrap; gap: 6px 26px; margin-left: auto; }
.ft a { border-bottom: 1px solid rgba(250, 250, 250, .32); }

/* ── menu ───────────────────────────────────────────────────────────────── */

/* The phone menu is a sheet of steel dropped over the page: the sections in
   large numbered type, the six services one level in, and the three ways to
   reach the workshop at the foot. It drops in with the heavy ease and its
   rows arrive in order; Reduce Motion gets it in place. */
.menu {
  position: fixed; inset: 0; z-index: 200;
  background: var(--bg-dark); color: var(--fg-on-dark);
  padding: 14px var(--margin) calc(24px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column;
  transform: translateY(-100%); transition: transform 460ms var(--ease-heavy);
  counter-reset: mi;
}
.menu[data-open="true"] { transform: none; }
.menu-top { display: flex; align-items: center; justify-content: space-between; gap: 20px; }
.menu-logo img { width: auto; }
.menu-close { background: none; border: 0; cursor: pointer; font-size: var(--t-ui);
              font-weight: 500; min-height: 44px; padding: 0 6px; color: var(--fg-on-dark); }
.menu-list { display: grid; margin-top: 3vh; overflow-y: auto; border-top: 1px solid var(--line-dark); }
.menu-list a {
  display: flex; align-items: baseline; gap: 14px;
  font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 800; font-stretch: 118%;
  font-size: clamp(24px, 7vw, 36px); letter-spacing: -.03em; line-height: 1.1;
  padding: 12px 0; border-bottom: 1px solid var(--line-dark);
}
.menu-list a:not(.menu-sub) { counter-increment: mi; }
.menu-list a:not(.menu-sub)::before {
  content: counter(mi, decimal-leading-zero); flex: none; min-width: 1.9em;
  font: 600 12px/1 Archivo, "Archivo Fallback", sans-serif; letter-spacing: .08em; color: var(--red-on-dark);
}
.menu-contact { margin-top: auto; padding-top: 20px; display: grid; gap: 10px; }
.menu .btn-ghost { color: var(--fg-on-dark); border-color: rgba(243, 245, 246, .5); }
@media (prefers-reduced-motion: no-preference) {
  .menu-list a, .menu-contact > * {
    opacity: 0; transform: translateY(12px);
    transition: opacity 360ms var(--ease), transform 460ms var(--ease-power);
  }
  .menu[data-open="true"] .menu-list a, .menu[data-open="true"] .menu-contact > * { opacity: 1; transform: none; }
  .menu[data-open="true"] .menu-list a:nth-child(1) { transition-delay: 120ms; }
  .menu[data-open="true"] .menu-list a:nth-child(2) { transition-delay: 150ms; }
  .menu[data-open="true"] .menu-list a:nth-child(3) { transition-delay: 180ms; }
  .menu[data-open="true"] .menu-list a:nth-child(4) { transition-delay: 210ms; }
  .menu[data-open="true"] .menu-list a:nth-child(5) { transition-delay: 240ms; }
  .menu[data-open="true"] .menu-list a:nth-child(6) { transition-delay: 270ms; }
  .menu[data-open="true"] .menu-list a:nth-child(7) { transition-delay: 300ms; }
  .menu[data-open="true"] .menu-list a:nth-child(n+8) { transition-delay: 330ms; }
  .menu[data-open="true"] .menu-contact > * { transition-delay: 360ms; }
}

/* ── viewer ─────────────────────────────────────────────────────────────── */

.viewer {
  position: fixed; inset: 0; z-index: 250; background: rgba(10, 12, 11, .97);
  color: var(--fg-on-dark); display: none; flex-direction: column;
}
.viewer[data-open="true"] { display: flex; }
.viewer-head, .viewer-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px; padding: 16px var(--margin);
}
.viewer-head button {
  background: none; border: 1px solid rgba(250, 250, 250, .34);
  border-radius: var(--r-sm); color: var(--fg-on-dark);
  padding: 0 18px; min-height: 44px; cursor: pointer; font-size: var(--t-ui);
}
.viewer-caption { font-size: var(--t-small); color: var(--fg-on-dark-muted); }
.viewer-body { flex: 1; min-height: 0; display: grid; place-items: center;
               padding: 0 var(--margin); }
.viewer-body img { max-width: 100%; max-height: 100%; object-fit: contain;
                   border-radius: var(--r); }

/* ── the phone action bar ───────────────────────────────────────────────── */

.bar { display: none; }

/* ── medium screens ─────────────────────────────────────────────────────── */

@media (max-width: 1100px) {
  .strip-in { grid-template-columns: repeat(2, 1fr); }
  .strip-in li:nth-child(3) { border-left: 0; padding-left: 0; }
  .strip-in li:nth-child(-n+2) { border-bottom: 1px solid var(--line-dark); }
}

/* ── tablet and below: the chapters stack ────────────────────────────────────
   Deliberate, not a fallback. A two-column chapter at 390px gives the copy a
   100px column and crushes the drawings; the phone composition is a single
   column where the PORTRAIT photographs finally get to be full width, which
   is the format they were shot in. */

@media (max-width: 900px) {
  .hd-nav { gap: 20px; }

    .ch-cut-head {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(28px, 5vw, 44px); align-items: start;
  }
  .ch-copy, .ch-cut-data { max-width: 62ch; }

  /* Four portrait plates as a 2x2 block rather than four slivers. */
  .ch-strip { grid-template-columns: repeat(2, 1fr); }
  .ch-strip > li img { height: clamp(240px, 38vh, 340px); }

  /* The dimension annotations come OFF the drawing here rather than being
     scaled up to stay legible. SVG text is sized in user units, so making it
     readable in a 350px-wide 968-unit viewBox pushed every label over its
     leader line and off the edge. Below 900px the drawing does the one thing
     it does well at this size — show the shape and the relative scale of the
     two profiles — and every figure it was annotating is stated in the rows
     directly beneath it, in real type.

     Nothing is lost: `Rib height` and `Crest height` are in the chapter's own
     figures list, so the numbers are on screen either way. */
  .pf-dim { display: none; }
  .pf { margin-bottom: -18px; }

  .ws { grid-template-columns: minmax(0, 1fr); }
  .ws-img { order: 0; }
  .ws-img img { min-height: clamp(280px, 44vh, 420px); }
  .ws-copy { max-width: none; padding: clamp(48px, 9vw, 72px) var(--margin); }
  /* Below 900 the establishing frame serves the uncut portrait file, so the
     wash comes up from the bottom instead of across from the left — a
     left-to-right scrim on a 390px frame covers the machine, which is the
     one thing the photograph is here to show. */
  .cut-scene img {
    aspect-ratio: auto;
    height: clamp(400px, 64svh, 580px); height: clamp(400px, calc(64 * var(--svh, 1vh)), 580px);
    max-height: none;
  }
  .cut-scene::after {
    background: linear-gradient(to top,
      rgba(8, 10, 9, .97) 0%, rgba(8, 10, 9, .94) 26%,
      rgba(8, 10, 9, .70) 44%, rgba(8, 10, 9, .20) 66%,
      rgba(8, 10, 9, 0) 84%);
  }
  .cut-tol { justify-content: flex-end; padding-bottom: clamp(22px, 4vw, 34px); }
  .cut-tol .ch-big, .cut-tol .ch-big-label { max-width: none; }
}

/* ── small screens ──────────────────────────────────────────────────────── */

@media (max-width: 760px) {
  html { scroll-padding-top: 78px; }

  .hd-nav, .hd-act { display: none; }
  .hd-menu {
    display: inline-flex; align-items: center; gap: 9px; min-height: 44px;
    padding: 0 4px; background: none; border: 0; cursor: pointer;
    font-size: var(--t-ui); font-weight: 500;
  }
  .hd-menu-bars { width: 18px; height: 2px; background: var(--fg); position: relative; }
  .hd-menu-bars::before, .hd-menu-bars::after {
    content: ""; position: absolute; left: 0; width: 18px; height: 2px; background: var(--fg);
  }
  .hd-menu-bars::before { top: -6px; }
  .hd-menu-bars::after  { top: 6px; }
  :root { --hd-h: 65px; }
  .hd-in { gap: 16px; }

  .hero {
    min-height: calc(80svh + var(--hd-h));
    min-height: calc(80 * var(--svh, 1vh) + var(--hd-h));
  }
  /* Lighter at the top than the desktop scrim: the copy block is taller here
     and the full-strength gradient buried the photograph entirely. */
  /* The copy block is taller here, so the mid-band has to carry more than it
     does on desktop — the eyebrow measured 2.96:1 when this fell away at 56%.
     The very top stays open so the photograph still announces itself. */
  .hero::after {
    background:
      linear-gradient(to bottom,
        rgba(10, 12, 11, .80) 0%, rgba(10, 12, 11, .64) 5%,
        rgba(10, 12, 11, .38) 10%, rgba(10, 12, 11, .12) 15%,
        rgba(10, 12, 11, 0) 22%),
      linear-gradient(to top,
        rgba(10, 12, 11, .95) 0%,  rgba(10, 12, 11, .92) 38%, rgba(10, 12, 11, .86) 62%,
        rgba(10, 12, 11, .62) 80%, rgba(10, 12, 11, .22) 93%, rgba(10, 12, 11, .06) 100%);
  }
  /* The price CTA takes its own row; call and WhatsApp share the next one. */
  .hero-act { gap: 10px; }
  .hero-act > :first-child { flex: 1 0 100%; }
  .hero-act > :nth-child(n+2) { flex: 1 1 0; min-width: 0; }
  .hero-act .btn-ghost span { overflow: hidden; text-overflow: ellipsis; }

  .strip-in { grid-template-columns: 1fr; }
  .strip-in li { border-left: 0; padding: 16px 0; border-top: 1px solid var(--line-dark); }
  .strip-in li:first-child { border-top: 0; }
  .strip-in li:nth-child(-n+2) { border-bottom: 0; }

  .proc-close .btn { width: 100%; }

  /* One column of plates on a phone: two 170px tiles lose the machine. */
  .ch-strip { grid-template-columns: 1fr; gap: 8px; }
  .ch-strip > li img { height: 54svh; height: calc(54 * var(--svh, 1vh)); max-height: 460px; }

  .pf-folds { grid-template-columns: repeat(3, 1fr); gap: 12px; padding: 18px 14px; }
  .pf-fold figcaption { font-size: 10.5px; letter-spacing: .1em; }

  .ch-big { font-size: clamp(44px, 13vw, 64px); }
  .ch-figures > div { flex-direction: column; gap: 2px; padding: 10px 0; }
  .ch-figures dd { text-align: left; }
  .enq-bg { opacity: .22; }


  .field-row { grid-template-columns: 1fr; }
  .field input, .field textarea, .field select { font-size: 16px; }  /* no zoom on focus */
  .enq-card { padding: 24px 18px 28px; border-radius: var(--r-lg); }
  .form-actions .btn { width: 100%; }

  .spec-group > summary, .faq-item > summary { padding: 17px 18px; font-size: 16px; }
  .spec-rows { padding: 0 18px 6px; }
  .spec-rows > div { flex-direction: column; gap: 3px; padding: 11px 0; }
  .spec-rows dd { text-align: left; }
  .faq-item > summary { padding-left: 0; padding-right: 0; }
  .faq-item > p { padding-left: 0; padding-right: 0; }

  .ct-list { grid-template-columns: 1fr; }
  .ct-list li:nth-child(n+5) { grid-column: auto; }
  /* Stacked, the label and the value each get a full line — a long email
     address has no room to break mid-word beside its label. */
  .ct-list dl { gap: 6px; padding: 14px 0 18px; }
  .ct-list dd { text-align: left; overflow-wrap: anywhere; font-size: 20px; }
  /* The addresses are the longest unbroken strings on the site. */
  .ct-list dd a[href^="mailto"] { font-size: 16.5px; }
  /* The contact values are the most-tapped links on the page. */
  .ct-list a, .ft a { display: inline-flex; align-items: center; min-height: 44px; }

  .ft-in { flex-direction: column; align-items: flex-start; gap: 20px; }
  .ft-meta { margin-left: 0; gap: 4px 0; }
  .ft-meta span { display: block; }

  main { padding-bottom: 78px; }
  .bar {
    display: flex; gap: 10px; position: fixed; left: 0; right: 0; bottom: 0; z-index: 150;
    padding: 10px var(--margin) calc(10px + env(safe-area-inset-bottom));
    background: rgba(11, 14, 16, .94);
    backdrop-filter: saturate(180%) blur(12px);
    border-top: 1px solid var(--line);
  }
  .bar a { flex: 1; min-height: 50px; }
  .bar .btn-ghost { color: var(--fg); border-color: var(--line-strong); }
}

@media (prefers-reduced-motion: reduce) {
   .zoom:hover { transform: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   SERVICE ROUTES
   ═══════════════════════════════════════════════════════════════════════════
   Five dedicated pages. They reuse the chapter system's chrome — the same
   bleed hero, the same figure rails, the same disclosure groups — because a
   service page and a homepage chapter are the same company talking about the
   same work, and two visual languages for that would be a mistake.

   What is different is the ORDER. A chapter is a scene in a sequence; a
   service page answers one question, in the order it gets asked: can you do
   this, what exactly can you do, what do you need from me, when, where, and
   how do I start.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── breadcrumb ─────────────────────────────────────────────────────────── */

.crumbs { background: var(--bg); padding: 0; }
.crumbs ol {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding-block: 14px 0; font-size: var(--t-small); color: var(--fg-faint);
}
.crumbs li { display: flex; align-items: center; gap: 8px; }
.crumbs li + li::before { content: "/"; color: var(--line-strong); }
.crumbs a { color: var(--fg-muted); min-height: 24px; display: inline-flex; align-items: center; }
.crumbs a:hover { color: var(--accent-ink); }
.crumbs [aria-current] { color: var(--fg); font-weight: 500; }

/* ── the service hero ─────────────────────────────────────────────────────── */

/* The service pages open on the same sheet as the home page (.hero, .gl-bub,
   .hero-plate): only the title is set smaller, because it is a sentence,
   not two words. */
.sp-title {
  font-size: clamp(34px, 6.4vw, 104px); line-height: .9; letter-spacing: -.045em;
  max-width: 11.8em; margin: 0 0 clamp(26px, 3vw, 44px); margin-left: -.04em;
}
.hero-plate .zoom { position: absolute; inset: 0; }
.hero-plate .zoom img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.hero-plate .zoom-cue { top: auto; bottom: 14px; right: calc(var(--pi) + 14px); }

/* ── the brief: what is confirmed, and what we need ─────────────────────── */

.sp-brief { padding: clamp(52px, 6vw, 88px) 0; }
.sp-brief-in {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(32px, 5vw, 76px); align-items: start;
}
.sp-brief h2 { font-size: clamp(21px, 2vw, 27px); margin-bottom: 20px; }

.sp-rail { display: grid; }
.sp-rail > div {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 20px; padding: 13px 0; border-top: 1px solid var(--line);
  font-size: var(--t-ui);
}
.sp-rail dt { color: var(--fg-muted); }
.sp-rail dd { font-weight: 500; text-align: right; font-variant-numeric: tabular-nums; }
.on-dark .sp-rail > div { border-top-color: var(--line-dark); }
.on-dark .sp-rail dt { color: var(--fg-on-dark-muted); }

.sp-send { display: grid; gap: 0; }
.sp-send li {
  display: flex; gap: 13px; padding: 13px 0; border-top: 1px solid var(--line);
  color: var(--fg-muted);
}
.sp-send li::before {
  content: ""; flex: none; width: 3px; align-self: stretch; margin-top: 4px;
  border-radius: var(--pill); background: var(--accent);
}

/* ── the drawings ───────────────────────────────────────────────────────── */

.sp-drawing { padding: clamp(52px, 6vw, 88px) 0; }
.sp-draw { display: grid; gap: clamp(24px, 3vw, 40px); max-width: 980px; margin: 0 auto; }
.sp-bed { max-width: 1060px; }
.bed { width: 100%; height: auto; overflow: visible; }
/* The bed itself reads as plate: a filled ground with a cut edge. */
.bed-plate { fill: var(--bg); stroke: var(--fg); stroke-width: 3; vector-effect: non-scaling-stroke; }
.bed-part {
  fill: none; stroke: var(--accent); stroke-width: 2.5;
  stroke-linejoin: round; vector-effect: non-scaling-stroke;
}
html.reveal-ready [data-reveal="draw"] .bed-part { transition-delay: var(--d, 0ms); }

/* ── the plate strip on a service page ──────────────────────────────────── */

.sp-plates { padding: 0; }
.sp-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); padding: clamp(10px, 1.4vw, 18px); }
.sp-strip > li img { height: clamp(300px, 44vh, 520px); }

/* ── what the page covers, where one page carries more than one service ── */

.sp-parts { padding: clamp(52px, 6vw, 88px) 0; }
.sp-parts-list { display: grid; gap: clamp(26px, 3vw, 44px); grid-template-columns: repeat(3, minmax(0, 1fr)); }
.sp-parts-list h2 { font-size: clamp(19px, 1.7vw, 23px); margin-bottom: 10px; }
.sp-parts-list p { color: var(--fg-muted); }
.sp-parts-list article { padding-top: 20px; border-top: 3px solid var(--accent); }

/* ── process and place ──────────────────────────────────────────────────── */

.sp-proc { padding: clamp(52px, 6vw, 88px) 0; }
.sp-proc-in {
  display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr);
  gap: clamp(34px, 5vw, 76px); align-items: start;
}
.sp-proc h2 { font-size: clamp(24px, 2.4vw, 34px); margin-bottom: 12px; }
.sp-lede { color: var(--fg-muted); margin-bottom: 30px; max-width: 52ch; }
.sp-area {
  background: var(--bg-soft); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: clamp(24px, 2.6vw, 34px);
}
.sp-area h2 { font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 700;
              font-size: var(--t-h3); letter-spacing: -.012em; margin-bottom: 12px; }
.sp-area p { color: var(--fg-muted); margin-bottom: 20px; }
.sp-area .sp-rail > div:first-child { border-top: 0; padding-top: 0; }

/* ── related services ───────────────────────────────────────────────────── */

.sp-related { padding: clamp(48px, 5.5vw, 80px) 0; }
.sp-related > .wrap > h2 { font-size: clamp(21px, 2vw, 28px); margin-bottom: 22px; }
/* The same object as the capability index, three to a row: a heavy rule,
   the name at size, an arrow box that fills on hover. */
.sp-related-list {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--gut);
  border-top: 2px solid var(--fg);
}
.sp-related-list a {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  min-height: 92px; padding: 18px 0;
  border-bottom: 1px solid var(--line-strong);
}
.sp-related-name {
  font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 800; font-stretch: 115%;
  font-size: clamp(20px, 1.9vw, 28px); letter-spacing: -.03em; line-height: 1.05;
  transition: transform 360ms var(--ease-heavy);
}
.sp-related-go {
  flex: none; width: 48px; height: 48px; display: grid; place-items: center;
  border: 1.5px solid var(--fg); color: var(--fg);
  transition: background-color 220ms var(--ease), border-color 220ms var(--ease), color 220ms var(--ease);
}
.sp-related-go svg { width: 18px; height: 18px; transition: transform 300ms var(--ease); }
.sp-related-list a:hover .sp-related-name { transform: translateX(6px); }
.sp-related-list a:hover .sp-related-go { background: var(--accent); border-color: var(--accent); color: #fff; }
.sp-related-list a:hover .sp-related-go svg { transform: rotate(-45deg); }
@media (prefers-reduced-motion: reduce) {
  .sp-related-list a:hover .sp-related-name { transform: none; }
}
.sp-back { margin-top: 22px; }
.sp-back a {
  display: inline-flex; align-items: center; gap: 9px; min-height: 44px;
  font-size: var(--t-ui); font-weight: 500; color: var(--accent-ink);
  border-bottom: 1px solid currentColor;
}
.sp-tail { padding-top: 0; }
.sp-aside { border-radius: var(--r-lg); overflow: hidden; }
.sp-aside img { width: 100%; height: clamp(220px, 30vw, 420px); object-fit: cover; }

/* ── the chapter's link to its own page ─────────────────────────────────── */

.ch-more { margin-top: 20px; }
.ch-more a {
  display: inline-flex; align-items: center; gap: 9px; min-height: 44px;
  font-size: var(--t-ui); font-weight: 500;
  color: var(--accent-ink); border-bottom: 1px solid currentColor;
}
.ch-dark .ch-more a { color: var(--accent-on-dark); }
.ch-more svg { transition: transform 160ms var(--ease); }
.ch-more a:hover svg { transform: translateX(3px); }

/* ── the footer, now carrying real navigation ───────────────────────────── */

/* On the grid like every sheet above it: the mark in bay A, the services in
   bay B, the ways to reach us across C and D. */
.ft { padding-top: clamp(48px, 5vw, 80px); }
.ft-nav {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: var(--gut); row-gap: 32px;
  padding-bottom: clamp(32px, 3.6vw, 56px);
  border-bottom: 1px solid var(--line-dark);
  margin-bottom: clamp(22px, 2.6vw, 32px);
}
.ft-col:last-child { grid-column: span 2; }
.ft-col h2 {
  font: 600 var(--t-label)/1.2 Archivo, "Archivo Fallback", sans-serif; font-stretch: 112%;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--fg-on-dark-muted); margin-bottom: 16px;
  padding-top: 12px; border-top: 1px solid var(--line-dark);
}
.ft-col ul { display: grid; gap: 2px; }
.ft-col a {
  display: inline-flex; align-items: center; min-height: 32px;
  color: var(--fg-on-dark); font-size: var(--t-ui);
  border-bottom: 1px solid transparent;
}
.ft-col a:hover { border-bottom-color: var(--fg-on-dark); }
.ft-logo img { width: auto; }

/* The service list in the menu, one level in. */
.menu-list .menu-sub {
  font-size: clamp(16px, 4.4vw, 20px); font-weight: 600; font-stretch: 100%; letter-spacing: -.01em;
  padding: 9px 0 9px calc(1.9em + 14px); color: var(--fg-on-dark-muted); align-items: center;
}
.menu-list .menu-sub::before {
  content: ""; width: 10px; height: 1.5px; flex: none;
  background: var(--red-on-dark); margin-left: -24px; margin-right: 0;
}

/* ── service routes and the footer, below a laptop ─────────────────────────────
   Every grid added for the service pages is declared here at each width it has
   to change at. Without these the footer's email column alone pushed the
   document to 541px inside a 390px viewport — a horizontal scrollbar on every
   page on the site, which is the single most obvious mobile defect there is. */

@media (max-width: 1024px) {
  .sp-parts-list   { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sp-related-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ft-nav          { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .ft-col:first-child { grid-column: 1 / -1; }
}

@media (max-width: 900px) {
  .sp-brief-in, .sp-proc-in { grid-template-columns: minmax(0, 1fr); }
  .sp-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sp-strip > li img { height: clamp(240px, 34vh, 340px); }
}

@media (max-width: 760px) {
  .sp-parts-list, .sp-related-list, .ft-nav { grid-template-columns: minmax(0, 1fr); }
  .ft-col:last-child { grid-column: auto; }
  .ft-nav { gap: 26px; }
  .sp-strip { grid-template-columns: 1fr; gap: 8px; }
  .sp-strip > li img { height: 46svh; height: calc(46 * var(--svh, 1vh)); max-height: 400px; }

  /* Stacked, so a 34-character email address has a full line rather than
     fighting a label for a 98px column. */
  .sp-rail > div { flex-direction: column; gap: 2px; padding: 11px 0; }
  .sp-rail dd { text-align: left; }
  .sp-aside img { height: 34svh; height: calc(34 * var(--svh, 1vh)); max-height: 320px; }
  .crumbs ol { padding-block: 1px; font-size: 12.5px; }
  .crumbs a { min-height: 44px; min-width: 44px; }
  .sp-related-list a { min-height: 72px; padding: 14px 0; }
}

/* A long address has to be allowed to break, wherever it appears. */
.ft-col a, .sp-rail dd, .crumbs a { overflow-wrap: anywhere; }

/* ═══════════════════════════════════════════════════════════════════════════
   SCENE CONTINUITY
   ═══════════════════════════════════════════════════════════════════════════
   Sections used to meet at a flat horizontal line, which is the edge a
   CATALOGUE has. A portal frame does not have a horizontal top: it has a
   pitch. Where a dark scene hands over to a light one, the cut is taken off
   that pitch instead — the section's own ground carries the angle, so the
   boundary belongs to the building rather than to the page grid.

   Used FOUR times and no more, alternating direction, at the four dark-to-
   light handovers. Any more and a deliberate cut becomes a tic.

   The angle is shallow on purpose. The real rafter runs at about 12°, which
   across a 1440px viewport is 300px of travel and a triangle, not a building.
   This is that idea at a readable scale: a 34px drop, about 1.4°, which reads
   as intent at any width and never eats a line of type.
   ═══════════════════════════════════════════════════════════════════════════ */

:root { --cut: 34px; }

@media (max-width: 760px) {
  /* A 34px drop across 390px is 5°, which starts to look like a mistake.
     Narrow screens get a smaller cut rather than a steeper one. */
  :root { --cut: 20px; }
}


/* ── depth ──────────────────────────────────────────────────────────────────
   A parallax element needs somewhere to travel, so the ones that move are
   oversized inside their own clipping frame. The extra 9% is exactly the 4%
   of travel each way plus a margin, so an edge can never be exposed. */

[data-parallax] { will-change: transform; }
.ws-img[data-parallax] { overflow: hidden; }
.ws-img[data-parallax] img { height: 109%; margin-top: -4.5%; }
.enq-bg[data-parallax] { height: 109%; top: -4.5%; }

@media (prefers-reduced-motion: reduce) {
  [data-parallax] { transform: none !important; will-change: auto; }
}


/* ── the plate strip on a phone ───────────────────────────────────────────────
   Four full-width portraits stacked came to 2 849px — 3.4 screens for one
   chapter, and the single biggest block of length on the mobile homepage.
   Sideways is the right axis for a row of machine photographs: all four keep a
   usable size, the next one is visibly half in frame so the gesture is
   discoverable, and the chapter drops to about a screen and a half.

   This is the only horizontal scroll on the site, and it is a contained one —
   the document itself never scrolls sideways at any width. The container is
   focusable, so arrow keys scroll it for anyone not using a touchscreen. */

@media (max-width: 760px) {
  .ch-strip {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 78%;
    grid-template-columns: none;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--margin);
    padding-inline: var(--margin);
    gap: 10px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .ch-strip::-webkit-scrollbar { display: none; }
  .ch-strip > li { scroll-snap-align: center; }
  .ch-strip > li img,
  .sp-strip > li img { height: 52svh; height: calc(52 * var(--svh, 1vh)); max-height: 430px; }
  .ch-strip:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
}

/* ── the enquiry outcome ────────────────────────────────────────────────────
   Two states, one block. Green is already this site's "confirmed" signal —
   it marks what Kingson has verified and nothing else — so it is the right
   colour for an enquiry the database has actually accepted. Red is the same
   red the field errors use.

   The honeypot is positioned off-screen rather than `display: none`: a bot
   reading the DOM fills anything it can find, while `display:none` is the
   first thing the better ones skip. It is out of the tab order and hidden
   from the accessibility tree, so nobody using the page ever meets it. */

.hp {
  position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden; white-space: nowrap;
}

.sent {
  margin-top: 28px; padding: 20px 22px;
  border: 1px solid var(--line); border-left-width: 3px;
  border-radius: var(--r); background: var(--bg-soft);
}
.sent:focus { outline: none; }
.sent:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.sent h3 {
  font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 700;
  font-size: var(--t-h3); margin-bottom: 8px;
}
.sent p { color: var(--fg-muted); font-size: var(--t-ui); max-width: 62ch; }

.sent[data-state="ok"] {
  border-color: var(--green-on-soft); background: var(--green-soft);
}
.sent[data-state="ok"] h3 { color: var(--green-on-soft); }
.sent[data-state="ok"] p  { color: #BFE3CC; }

.sent[data-state="failed"] {
  border-color: var(--accent); background: var(--accent-soft);
}
.sent[data-state="failed"] h3 { color: var(--accent-ink); }
.sent[data-state="failed"] p  { color: #F2C4C6; }

/* Once the enquiry is in, the draft below it is a convenience, not the act.
   It recedes. */
.sent:not([hidden]) + .draft { border-top-color: var(--line); }
.sent[data-state="ok"]:not([hidden]) + .draft h3 { color: var(--fg-muted); font-size: var(--t-ui); }

/* ── touch targets ──────────────────────────────────────────────────────────
   Last in the file on purpose.

   The rule for these lives in the max-width: 760px block, which is two
   hundred lines above the footer navigation that was added afterwards.
   `.ft-col a` and `.ft a` have the same specificity, so the later one won at
   every width and quietly cancelled it: both phone numbers and both email
   addresses were 32px tall on a phone. On a steel fabricator's website the
   tap on a phone number is the conversion, and it was the smallest target on
   the page.

   Keyed on `pointer: coarse` rather than on width, because the thing that
   decides how big a target needs to be is the finger, not the viewport. A
   768px tablet is a touch device and was getting 27px contact links; a narrow
   window on a desktop is not, and does not need a taller footer.

   44px is the iOS minimum and the figure the design guidance uses. Android
   asks for 48. The gap between rows carries the 8px separation.            */

@media (pointer: coarse) {
  .ft-col a,
  .ct-list a,
  .hd-nav a          { min-height: 44px; display: inline-flex; align-items: center; }
  .ft-col ul         { gap: 4px; }
  .hd .btn,
  .hd .btn-ghost     { min-height: 44px; }
}

/* ── how far down you are ───────────────────────────────────────────────────
   The home page is about seventeen screens tall. A reader who has committed
   to the chapters has no way of telling whether the specifications are next
   or whether there are five more scenes first, and the header — which is the
   one thing on screen the whole way down — was saying nothing about it.

   Driven by `animation-timeline: scroll()`, so the browser runs it off the
   compositor with no scroll listener, no rAF loop and no JavaScript of any
   kind. That is the same reason the rest of this site uses an
   IntersectionObserver rather than listening to scroll: a handler that fires
   on every scroll event is the most reliable way to make a page feel heavy on
   a mid-range phone.

   Browsers without scroll-driven animations get a bar that stays at zero
   width, which is indistinguishable from the border that was already there.
   Nobody is worse off, so there is no JavaScript fallback to maintain.      */

.hd-progress {
  position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  pointer-events: none; overflow: hidden;
}
.hd-progress > i {
  display: block; height: 100%; width: 100%;
  background: var(--accent);
  transform: scaleX(0); transform-origin: 0 50%;
}

@supports (animation-timeline: scroll()) {
  .hd-progress > i {
    animation: hd-progress-grow linear both;
    animation-timeline: scroll(root block);
  }
}

@keyframes hd-progress-grow {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* On the dark hero the red still reads, but the track needs to not look like
   a gap in the border. */
html.hd-over .hd-progress { mix-blend-mode: normal; }

/* It reports position rather than animating anything, so Reduce Motion has no
   quarrel with it — but a scroll-linked transform is still motion to some
   people, and the site's rule is that the whole vocabulary goes quiet. */
@media (prefers-reduced-motion: reduce) {
  .hd-progress { display: none; }
}

/* ── the service chooser ────────────────────────────────────────────────────
   Six tiles under the hero. The job it does is the one the site was missing:
   letting somebody see, on one screen, whether Kingson makes the thing they
   came about — instead of scrolling seven screens of chapters to find out.

   The figure leads, not the name. A buyer scanning six tiles is looking for
   "±0.1 mm" or "25 t", and reads the service name to confirm what they have
   already found. Tabular numerals so the six line up down the grid. */

.svc { background: var(--bg); }


/* ── the portfolio ──────────────────────────────────────────────────────────
   Styled and unused. content/projects.js is empty until the office says which
   photograph belongs to which job, so /projects is not written and none of
   this is served. It exists so that adding a project is typing, not building.

   A project is a photograph and a column of facts, not a card. The photograph
   is given real size because at thumbnail scale a finished portal frame and a
   stock image look identical, and the whole argument of this page is that ours
   are not stock. */

.pj-head { padding-top: clamp(38px, 6vw, 84px); }
.pj-head h1 { font-size: var(--t-h1); margin-bottom: 16px; }
.pj-lede { font-size: var(--t-lede); color: var(--fg-muted); max-width: 62ch; }

.pj-list { padding-top: clamp(30px, 4vw, 54px); }

.pj {
  display: grid; gap: clamp(22px, 3vw, 44px);
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  align-items: start;
  padding-bottom: clamp(44px, 6vw, 88px);
  margin-bottom: clamp(44px, 6vw, 88px);
  border-bottom: 1px solid var(--line);
}
.pj:last-child { border-bottom: 0; margin-bottom: 0; }

.pj-shot { border-radius: var(--r-lg); overflow: hidden; background: var(--bg-soft); }
.pj-shot img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; }

.pj-title { font-size: var(--t-h2); margin-bottom: 10px; }
.pj-credit {
  font-size: var(--t-ui); color: var(--fg-faint);
  padding-bottom: 14px; margin-bottom: 16px; border-bottom: 1px solid var(--line);
}
.pj-scope { font-size: var(--t-lede); color: var(--fg); margin-bottom: 12px; }
.pj-body  { color: var(--fg-muted); max-width: 54ch; }

.pj-figures { margin-top: 20px; }
.pj-figures > div {
  display: flex; justify-content: space-between; gap: 20px;
  padding: 11px 0; border-top: 1px solid var(--line);
}
.pj-figures dt { color: var(--fg-faint); font-size: var(--t-small); }
.pj-figures dd { font-weight: 600; font-variant-numeric: tabular-nums; }

.pj-points { margin-top: 18px; display: grid; gap: 8px; }
.pj-points li {
  position: relative; padding-left: 20px;
  font-size: var(--t-ui); color: var(--fg-muted);
}
.pj-points li::before {
  content: ""; position: absolute; left: 0; top: .62em;
  width: 10px; height: 1px; background: var(--accent);
}

.pj-svc {
  margin-top: 20px; font-size: var(--t-small); color: var(--fg-faint);
  text-transform: none; letter-spacing: .01em;
}

/* The gallery spans both columns: the remaining frames, smaller, because by
   this point the reader has already seen the work at size. */
.pj-gallery {
  grid-column: 1 / -1;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(10px, 1.2vw, 18px);
}
.pj-gallery img {
  width: 100%; aspect-ratio: 3 / 2; object-fit: cover;
  border-radius: var(--r); display: block;
}

@media (max-width: 900px) {
  .pj { grid-template-columns: 1fr; }
  .pj-gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── the 404 ─────────────────────────────────────────────────────────────────
   It reuses the service-page hero wholesale — same photograph treatment, same
   scrim, same copy column. It only needs to be shorter, because the six
   services sit directly under it and a lost visitor should see them without
   scrolling on a laptop. */
/* A 404 does not deserve a full photograph: a thin band of it, so the index
   of services under it is one short scroll away on a laptop. */
.hero.nf-hero .hero-plate { height: min(28vw, 50vh); }
/* The chooser under it is the whole point of the page, so it gets the tighter
   of the two section rhythms — the first row of tiles should be reachable in
   one short scroll on a laptop, not two. */
.nf-svc { padding-block: clamp(40px, 4.4vw, 64px); }
@media (max-width: 760px) { .hero.nf-hero .hero-plate { height: 56vw; } }

/* ── three actions in the phone bar ──────────────────────────────────────────
   Call, WhatsApp and Get a price. At 360px three equal thirds leave about
   103px each, which is not enough for a label beside an icon, so the bar
   gets its own tighter metrics rather than the page's button padding. The
   quote button keeps a little more room because its label is the longest and
   it is the one that has to stay unambiguous. */
@media (max-width: 760px) {
  .bar { gap: 8px; }
  .bar a { padding-inline: 8px; gap: 6px; font-size: 14px; letter-spacing: 0; }
  .bar a.btn { flex: 1.25; }
  .bar a svg { flex: none; }
}
@media (max-width: 360px) {
  .bar a { font-size: 13px; gap: 4px; padding-inline: 6px; }
}

/* The bar steps out of the frame while the hero's own buttons are on screen —
   see main.js. `html.cta-near` is only ever added by an observer that ran, so
   with JavaScript off the bar stays where it is, which is the safe way round.
   It keeps its space in the layout via main's padding-bottom, so nothing
   reflows when it leaves or returns. */
@media (max-width: 760px) {
  .bar { transition: transform .22s cubic-bezier(.4, 0, .2, 1); }
  html.cta-near .bar, html.form-active .bar { transform: translateY(115%); }
}
@media (prefers-reduced-motion: reduce) {
  .bar { transition: none; }
}

/* The questions header sits in a side column: one column. */
.faq-side .sec-head { grid-template-columns: 1fr; }

/* ═══════════════════════════════════════════════════════════════════════════
   INDUSTRIAL SYSTEM — frames, plates and rails
   ═══════════════════════════════════════════════════════════════════════════ */

/* Crop marks: the four L-shaped corners a drawing or a proof is trimmed to.
   They frame a panel without boxing it in. Ink, not red — the accent is kept
   for the things you can press. */
.frame {
  --mk: var(--fg); --mk-len: 18px; --mk-w: 1.5px;
  background:
    linear-gradient(var(--mk), var(--mk)) 0 0 / var(--mk-len) var(--mk-w) no-repeat,
    linear-gradient(var(--mk), var(--mk)) 0 0 / var(--mk-w) var(--mk-len) no-repeat,
    linear-gradient(var(--mk), var(--mk)) 100% 0 / var(--mk-len) var(--mk-w) no-repeat,
    linear-gradient(var(--mk), var(--mk)) 100% 0 / var(--mk-w) var(--mk-len) no-repeat,
    linear-gradient(var(--mk), var(--mk)) 0 100% / var(--mk-len) var(--mk-w) no-repeat,
    linear-gradient(var(--mk), var(--mk)) 0 100% / var(--mk-w) var(--mk-len) no-repeat,
    linear-gradient(var(--mk), var(--mk)) 100% 100% / var(--mk-len) var(--mk-w) no-repeat,
    linear-gradient(var(--mk), var(--mk)) 100% 100% / var(--mk-w) var(--mk-len) no-repeat;
}
.on-dark .frame, .sec-dark .frame { --mk: rgba(250, 250, 250, .7); }
.proc-close.frame, .sp-area.frame { border: 0; border-radius: 0; background-color: transparent; }

/* The page's close-up photograph, captioned, after the brief. */
.sp-photo { padding: 0 0 clamp(48px, 5.5vw, 80px); }
.sp-aside.frame { padding: 12px; margin: 0; }
.sp-aside.frame img { width: 100%; height: clamp(300px, 52vh, 580px); object-fit: cover; display: block; }
.sp-aside figcaption { margin-top: 12px; font-size: var(--t-small); color: var(--fg-faint); }

/* Plates on a wide screen: one row, and the plate under the pointer or the
   keyboard opens out. It shows the photograph you are looking at at a useful
   width without a carousel. Touch and reduced motion get the even row. */
@media (min-width: 901px) {
  .sp-strip { display: flex; gap: 10px; }
  .sp-strip > li { flex: 1 1 0; min-width: 0; }
  .sp-strip > li img { height: clamp(360px, 58vh, 620px); }
}
@media (min-width: 901px) and (hover: hover) and (prefers-reduced-motion: no-preference) {
  .sp-strip > li { transition: flex-grow 520ms var(--ease-power); }
  .sp-strip > li:hover, .sp-strip > li:focus-within { flex-grow: 2.4; }
}

/* On a service page the commitments sit in a half-width column: a vertical
   rail there, at every width. Four columns in that space ran the labels
   into each other ("acknowledgedarranged"). */
.sp-proc .steps { grid-template-columns: 1fr; gap: 22px; }
.sp-proc .steps::before { left: 27px; right: auto; top: 10px; bottom: 10px; width: 2px; height: auto;
  background: linear-gradient(to bottom, var(--accent) 0 18%, var(--line) 18% 100%); }
.sp-proc .step { display: flex; gap: 18px; padding: 0; }
.sp-proc .step-n { width: 56px; height: 56px; font-size: 20px; flex: none; }
.sp-proc .step-body { padding-top: 6px; }

/* The name, set once at plate-lettering scale across the foot of every page
   and cropped by the page edge — the way a fabricator's name is stamped
   into a base plate. Decorative (aria-hidden); the logo above is the name. */
.ft-mark {
  max-width: var(--page); margin: clamp(28px, 4vw, 56px) auto 0; padding: 0 var(--margin);
  font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 800; font-stretch: 125%;
  /* Sized to the content width: the name measures 12.0 × its font size. */
  font-size: calc((min(100vw, var(--page)) - 2 * var(--margin)) / 12.1);
  line-height: .78; letter-spacing: -.035em;
  white-space: nowrap; color: rgba(243, 245, 246, .11);
  transform: translateY(12%); user-select: none;
}
/* Drawn from an attribute rather than written as text: it is a logotype set
   as texture, and text in the document would be read, copied and measured
   for contrast as if it were copy. */
.ft-mark::before { content: attr(data-mark); }

/* The figures band on a phone: two by two, not one long column. */
@media (max-width: 760px) {
  .strip-in { grid-template-columns: 1fr 1fr; column-gap: 20px; }
  .strip-in li { border-left: 0; padding: 16px 0; border-top: 1px solid var(--line-dark); }
  .strip-in li:nth-child(-n+2) { border-top: 0; border-bottom: 0; }
  .strip-f { font-size: 32px; }
}

/* On a phone the fixed action bar covers the last ~78px of the viewport; the
   footer's last line has to clear it at full scroll. */
@media (max-width: 760px) { .ft { padding-bottom: calc(78px + env(safe-area-inset-bottom)); } }

/* ═══════════════════════════════════════════════════════════════════════════
   THE DRAWING SET — sheet index and capability index
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── the sheet index ──────────────────────────────────────────────────────────
   A ruler in the left margin of a wide screen: one tick per sheet, the
   current sheet's tick drawn long and red, and its name set up the margin
   the way a drawing's title runs up its border. Real links. Hidden while the
   first sheet is on screen (it has nothing to index yet) and wherever the
   margin is too narrow to hold it clear of the content. main.js sets
   aria-current and the ground; without it the ruler is never shown. */
.si { display: none; }
@media (min-width: 1280px) {
  html.si-ready .si {
    display: flex; flex-direction: column; gap: 1px;
    position: fixed; z-index: 90; left: clamp(14px, 1.3vw, 26px); top: 50%;
    transform: translateY(-50%);
    opacity: 0; visibility: hidden;
    transition: opacity 300ms var(--ease), visibility 0s linear 300ms;
    --si-c: var(--fg-faint); --si-on: var(--accent-ink);
  }
  html.si-ready .si.is-on { opacity: 1; visibility: visible; transition: opacity 300ms var(--ease); }
  html.si-ready .si[data-ground="dark"] { --si-c: var(--fg-on-dark-muted); --si-on: var(--red-on-dark); }
  .si a {
    display: flex; align-items: center; gap: 7px;
    height: 24px; min-width: 40px; color: var(--si-c);
    font: 600 10px/1 Archivo, "Archivo Fallback", sans-serif; letter-spacing: .06em; font-variant-numeric: tabular-nums;
    transition: color 240ms var(--ease);
  }
  .si a::after {
    content: ""; width: 12px; height: 1.5px; background: currentColor;
    transition: width 320ms var(--ease);
  }
  .si a:hover, .si a[aria-current] { color: var(--si-on); }
  .si a[aria-current]::after { width: 22px; }
  /* The current sheet's name, up the margin under the ruler. Every label
     sits in the same place; only the current one is shown. */
  .si-t {
    position: absolute; left: 1px; top: calc(100% + 18px);
    writing-mode: vertical-rl; transform: rotate(180deg);
    font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase; white-space: nowrap;
    opacity: 0; transition: opacity 240ms var(--ease); pointer-events: none;
  }
  .si a[aria-current] .si-t { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .si, .si a, .si a::after, .si-t { transition: none !important; }
}

/* ── the capability index ─────────────────────────────────────────────────────
   Six services as a schedule, not six cards: a ruled list read across — the
   number, the name at size, the one figure a buyer of that work asks about,
   and the way through to the page, with a photograph of the work in every
   row. Hovered or focused, a row lights red and its photograph comes up in
   colour. */
.svc { background: var(--bg); }
.cx { border-top: 2px solid var(--fg); position: relative; }
.cx-row { border-bottom: 1px solid var(--line-strong); }
.cx-hit {
  position: relative; display: grid; align-items: center;
  grid-template-columns: minmax(0, .42fr) minmax(0, 2.1fr) minmax(0, 1.25fr) minmax(0, 1.05fr) 64px;
  column-gap: var(--gut);
  padding: clamp(20px, 2.2vw, 34px) 0; color: var(--fg);
  isolation: isolate;
}
/* The inversion is a panel that rises from the row's baseline, so the
   change reads as a plate slid under the line, not a colour flip. */
.cx-hit::before {
  content: ""; position: absolute; z-index: -1; inset: -1px calc(var(--gut) / -2);
  background: var(--bg-dark); transform: scaleY(0); transform-origin: 50% 100%;
  transition: transform 420ms var(--ease-heavy);
}
.cx-n {
  font: 600 13px/1 Archivo, "Archivo Fallback", sans-serif; letter-spacing: .08em;
  color: var(--accent-ink); font-variant-numeric: tabular-nums; align-self: start;
  padding-top: .6em; transition: color 260ms var(--ease);
}
.cx-name {
  font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 800; font-stretch: 118%;
  font-size: clamp(26px, 2.9vw, 46px); line-height: .98; letter-spacing: -.035em;
  transition: transform 420ms var(--ease-heavy);
}
.cx-lead { display: grid; gap: 4px; }
.cx-lead b {
  font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 800; font-stretch: 62%;
  font-size: clamp(30px, 2.8vw, 46px); line-height: .9; letter-spacing: -.01em;
  font-variant-numeric: tabular-nums;
}
.cx-lead span, .cx-body { font-size: 14px; line-height: 1.45; color: var(--fg-muted);
                          transition: color 260ms var(--ease); }
.cx-body { max-width: 36ch; }
.cx-go {
  justify-self: end; width: 56px; height: 56px; display: grid; place-items: center;
  border: 1.5px solid var(--fg); color: var(--fg);
  transition: background-color 260ms var(--ease), border-color 260ms var(--ease), color 260ms var(--ease);
}
.cx-go svg { width: 20px; height: 20px; transition: transform 320ms var(--ease); }
.cx-hit > .cx-media { display: none; }

.cx-hit:hover, .cx-hit:focus-visible { color: var(--fg-on-dark); }
.cx-hit:hover::before, .cx-hit:focus-visible::before { transform: none; }
.cx-hit:hover .cx-n, .cx-hit:focus-visible .cx-n { color: var(--red-on-dark); }
.cx-hit:hover .cx-lead span, .cx-hit:hover .cx-body,
.cx-hit:focus-visible .cx-lead span, .cx-hit:focus-visible .cx-body { color: var(--fg-on-dark-muted); }
.cx-hit:hover .cx-go, .cx-hit:focus-visible .cx-go {
  background: var(--accent); border-color: var(--accent); color: #fff;
}
.cx-hit:hover .cx-go svg, .cx-hit:focus-visible .cx-go svg { transform: rotate(-45deg); }
.cx-hit:hover .cx-name, .cx-hit:focus-visible .cx-name { transform: translateX(10px); }
.cx-hit:focus-visible { outline-offset: -3px; }



.cx-plate {
  display: flex; flex-direction: column; justify-content: flex-end; gap: 6px;
  padding: 18px; background: var(--bg-dark-2); color: var(--fg-on-dark);
  border-bottom: 3px solid var(--accent);
}
.cx-plate b {
  font-family: Archivo, "Archivo Fallback", sans-serif; font-weight: 800; font-stretch: 62%;
  font-size: clamp(40px, 4vw, 64px); line-height: .9;
}
.cx-plate span { font-size: 13px; color: var(--fg-on-dark-muted); }


@media (prefers-reduced-motion: reduce) {
  .cx-hit::before, .cx-name, .cx-go, .cx-go svg { transition: none; animation: none; }
  .cx-hit:hover .cx-name, .cx-hit:focus-visible .cx-name { transform: none; }
}

/* A laptop: the description gives way before the name has to. */
@media (max-width: 1180px) {
  .cx-hit { grid-template-columns: minmax(0, .36fr) minmax(0, 2fr) minmax(0, 1.2fr) 56px; }
  .cx-body { display: none; }
  .cx-go { width: 50px; height: 50px; }
}

/* Touch, and anything narrower than a laptop: the photograph moves into the
   row. There is no hover here to reveal it with, and none is needed. */
@media (hover: none), (max-width: 900px) {
  .cx-hit {
    grid-template-columns: minmax(0, 1fr) clamp(104px, 28vw, 180px);
    grid-template-areas: "n media" "name media" "lead media";
    align-items: start; align-content: start; row-gap: 8px; padding: 22px 0;
  }
  .cx-n { grid-area: n; padding-top: 0; }
  .cx-name { grid-area: name; font-size: clamp(24px, 6.4vw, 34px); }
  .cx-lead { grid-area: lead; margin-top: 4px; }
  .cx-lead b { font-size: clamp(26px, 7vw, 36px); }
  .cx-body, .cx-go { display: none; }
  .cx-hit > .cx-media {
    display: block; grid-area: media; position: relative; overflow: hidden;
    aspect-ratio: 4 / 5; background: var(--bg-dark);
  }
  .cx-media img { width: 100%; height: 100%; object-fit: cover; }
  .cx-hit > .cx-media.cx-plate { display: flex; padding: 12px; }
  .cx-media.cx-plate b { font-size: clamp(24px, 6.6vw, 34px); }
  .cx-media.cx-plate span { font-size: 11.5px; line-height: 1.3; }
  .cx-hit:hover .cx-name, .cx-hit:focus-visible .cx-name { transform: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   CINEMA — the first screen
   ═══════════════════════════════════════════════════════════════════════════
   Kingson's photographs, full bleed, each drifting slowly (a Ken Burns move)
   and cross-fading into the next; the title set over them; an index of the
   photographs along the foot with a progress rule on the one showing, and a
   pause control, because anything that moves on its own for more than five
   seconds has to be stoppable (WCAG 2.2.2). Reduce Motion gets the first
   photograph, still. scenes/cinema.js runs the sequence. */

.hero.cin {
  position: relative; padding: 0; overflow: hidden; isolation: isolate;
  height: 100vh; height: 100svh; min-height: 620px; max-height: 1180px;
  margin-top: calc(var(--hd-h) * -1);
  display: flex; flex-direction: column; justify-content: flex-end;
  background: #07090A; color: #F3F5F6;
}
.cin-slides, .cin-slide { position: absolute; inset: 0; }
.cin-slide { margin: 0; opacity: 0; z-index: 0; transition: opacity 1500ms var(--ease); }
.cin-slide.was { opacity: 1; z-index: 1; }
.cin-slide.is-on { opacity: 1; z-index: 2; }
.cin-slide img, .cin-slide .hero-mark {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transform-origin: var(--ox, 50%) var(--oy, 50%);
}
.cin-slide[data-i="0"] { --kx: 1.2%;  --ky: .8%;  --ox: 55%; --oy: 40%; }
.cin-slide[data-i="1"] { --kx: -1.8%; --ky: 0%;   --ox: 45%; --oy: 55%; }
.cin-slide[data-i="2"] { --kx: 1%;    --ky: -1.2%; --ox: 60%; --oy: 50%; }
.cin-slide[data-i="3"] { --kx: -1%;   --ky: 1%;   --ox: 40%; --oy: 45%; }
@media (prefers-reduced-motion: no-preference) {
  .cin-slide.is-on img, .cin-slide.is-on .hero-mark,
  .cin-slide.was img, .cin-slide.was .hero-mark { animation: cin-kb 9s linear both; }
  .hero.cin.is-paused .cin-slide img, .hero.cin.is-paused .hero-mark { animation-play-state: paused; }
}
@keyframes cin-kb {
  from { transform: scale(1.14) translate(var(--kx, 0), var(--ky, 0)); }
  to   { transform: scale(1.03) translate(0, 0); }
}
/* The markup draws once, on the first photograph, as it always has. */
.cin .hero-mark { z-index: 1; pointer-events: none; }

/* Dark at the head for the navigation, darker at the foot for the title,
   and a wash from the left behind the copy — measured behind the glyphs on
   the brightest of the four photographs (the sky over the frame). */
.cin-shade {
  position: absolute; inset: 0; z-index: 3; pointer-events: none;
  background:
    linear-gradient(180deg, rgba(5,7,8,.72) 0, rgba(5,7,8,.2) 16%, rgba(5,7,8,0) 30%,
                    rgba(5,7,8,.35) 52%, rgba(5,7,8,.86) 76%, #07090A 100%),
    linear-gradient(90deg, rgba(5,7,8,.62) 0, rgba(5,7,8,.25) 45%, rgba(5,7,8,0) 70%);
}
.cin-in { position: relative; z-index: 4; width: 100%; padding-bottom: clamp(120px, 14vh, 170px); }
.cin .hero-eyebrow { color: #D5DCE0; margin-bottom: clamp(16px, 2vw, 26px); }
.cin .hero-title {
  font-size: clamp(46px, 9.4vw, 170px); line-height: .84; letter-spacing: -.055em;
  text-transform: uppercase; color: #F7F8F8; margin: 0 0 clamp(22px, 2.6vw, 40px) -.04em;
  text-shadow: 0 2px 30px rgba(0,0,0,.35);
}
.cin .hero-title .ln { white-space: nowrap; }
.cin .hero-title .ln + .ln { text-align: left; }
/* "Structural steelwork. Fabricated here." is four words where the first
   title was two, so it is set smaller: one sentence to a line from a tablet
   up, and one word to a line on a phone, sized so "STRUCTURAL" fills it. */
.cin .hero-title:not(.sp-title) { font-size: clamp(38px, 5.3vw, 78px); line-height: .88; }
@media (max-width: 760px) {
  .cin .hero-title:not(.sp-title) { font-size: 10.2vw; }
  .cin .hero-title:not(.sp-title) .ln { white-space: normal; }
}
.cin .hero-lede { color: #D0D7DB; }
.cin .hero-act .btn-ghost { color: #F3F5F6; border-color: rgba(243,245,246,.6); background: rgba(7,9,10,.25); }
.cin .hero-act .btn-ghost:hover { background: #F3F5F6; color: #07090A; }
.cin .hero-meta {
  margin-top: 22px; display: inline-flex; gap: 10px; padding: 8px 12px;
  background: rgba(7,9,10,.55); border-left: 3px solid var(--accent);
  font: 600 var(--t-label)/1.2 Archivo, "Archivo Fallback", sans-serif; font-stretch: 112%;
  letter-spacing: .14em; text-transform: uppercase; color: #F3F5F6;
}
/* The service pages' titles are sentences, not two words. */
.cin .hero-title.sp-title { text-transform: none; font-size: clamp(38px, 6.2vw, 104px); line-height: .92;
                           white-space: normal; max-width: 12.6em; }

/* The index of photographs along the foot. */
.cin-ctl {
  position: absolute; z-index: 4; left: 0; right: 0; bottom: 0;
  display: flex; align-items: flex-end; gap: var(--gut); padding-bottom: clamp(20px, 3vh, 34px);
}
.cin-dots { flex: 1; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); column-gap: var(--gut); }
.cin-dots button {
  position: relative; width: 100%; display: flex; align-items: baseline; gap: 12px;
  padding: 14px 0 0; background: none; border: 0; border-top: 1px solid rgba(243,245,246,.28);
  color: rgba(243,245,246,.62); text-align: left; cursor: pointer; min-height: 44px;
  font: 500 13.5px/1.35 Inter, "Inter Fallback", sans-serif; transition: color 240ms var(--ease);
}
.cin-dots button:hover, .cin-dots button[aria-current] { color: #F3F5F6; }
.cin-n { font: 700 12px/1 Archivo, "Archivo Fallback", sans-serif; letter-spacing: .08em; color: var(--red-on-dark); }
.cin-dots i { position: absolute; left: 0; right: 0; top: -1px; height: 2px; background: var(--accent);
              transform: scaleX(0); transform-origin: 0 50%; }
.cin-dots button[aria-current] i { transform: scaleX(1); }
.cin-dots button.run i { animation: cin-prog var(--cin-dur, 7000ms) linear both; }
@keyframes cin-prog { from { transform: scaleX(0); } }
.cin-pause {
  flex: none; width: 44px; height: 44px; border-radius: var(--pill); cursor: pointer;
  border: 1px solid rgba(243,245,246,.55); background: rgba(7,9,10,.35); display: grid; place-items: center;
}
.cin-pause span { width: 10px; height: 12px; border-left: 3px solid #F3F5F6; border-right: 3px solid #F3F5F6; }
.cin-pause[aria-pressed="true"] span { width: 0; height: 0; border: 0; border-left: 11px solid #F3F5F6;
                                       border-top: 7px solid transparent; border-bottom: 7px solid transparent; margin-left: 3px; }
.cin-cap { display: flex; gap: 12px; align-items: baseline; font-size: 13.5px; color: #D0D7DB;
           border-top: 1px solid rgba(243,245,246,.28); padding-top: 14px; }

/* Scrolling away: the title lifts and fades as the photograph is left
   behind. Scroll-driven, so it costs the main thread nothing. */
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .cin-in, .cin-ctl { animation: cin-leave linear both; animation-timeline: scroll(root block); animation-range: 0 70vh; }
  }
}
@keyframes cin-leave { to { opacity: 0; transform: translateY(-70px); } }

/* Service pages and the 404 keep their breadcrumb above the photograph. */
.hero.cin.cin-page { margin-top: 0; height: calc(100svh - var(--hd-h) - 48px); min-height: 560px; max-height: 1000px; }
.hero.cin.nf-hero { height: 70svh; min-height: 480px; }

@media (max-width: 900px) {
  .cin-dots .cin-t { display: none; }
  .cin-dots button[aria-current] .cin-t { display: inline; }
  .cin-dots { grid-template-columns: repeat(4, auto) ; }
  .cin-dots li:has(button[aria-current]) { flex: 1; }
}
@media (max-width: 760px) {
  .hero.cin { min-height: 560px; }
  .cin .hero-title { font-size: 11.4vw; }
  .cin .hero-title.sp-title { font-size: clamp(32px, 9vw, 44px); }
  .cin-in { padding-bottom: 96px; }
  /* Two lines in Archivo at this width, one in the fallback: the space is
     reserved so the swap does not move the title (measured 0.0105 CLS). */
  .cin .hero-eyebrow { font-size: 11px; min-height: 2.6em; }
  /* Four numbered rules, the progress running along the one showing. The
     captions stay in each button's accessible name. */
  .cin-dots { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); column-gap: 10px; }
  .cin-dots .cin-t, .cin-dots button[aria-current] .cin-t { display: none; }
  .hero.cin.cin-page { height: auto; min-height: 88svh; }
}

/* ── cinema: the rest of the page on the dark ground ───────────────────────── */
.skip { color: var(--bg); }
.sent[data-state="ok"] { background: #0F2418; border-color: #3DAA6A; }
.sent[data-state="ok"] h3 { color: #6FD39A; }
.enq-card { background: var(--surface); box-shadow: 0 40px 80px -40px rgba(0,0,0,.8); }
.field input, .field textarea, .field select { background: var(--bg); border-color: var(--line-strong); }
.enq-bg { opacity: .42; }
/* The capability index: a hovered row lights red, the colour of the one
   thing you can press. */
.cx-hit::before { background: var(--accent); }
.cx-hit:hover .cx-n, .cx-hit:focus-visible .cx-n { color: #fff; }
.cx-hit:hover .cx-lead span, .cx-hit:hover .cx-body,
.cx-hit:focus-visible .cx-lead span, .cx-hit:focus-visible .cx-body { color: rgba(255,255,255,.88); }
.cx-hit:hover .cx-go, .cx-hit:focus-visible .cx-go { background: #fff; border-color: #fff; color: var(--accent); }
.cx-hit:focus-visible { outline-color: #fff; }
.cx { border-top-color: var(--fg); }
.cx-plate { background: #1E252A; }

/* ── the crane, full bleed ──────────────────────────────────────────────────── */
.band-ch { position: relative; padding: 0; overflow: hidden; isolation: isolate;
           min-height: 100vh; min-height: 100svh; display: flex; align-items: flex-end; }
.band-img { position: absolute; inset: -5% 0; z-index: -2; }
.band-img .zoom, .band-img img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.band-ch::before { content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(0deg, #07090A 0, rgba(7,9,10,.82) 24%, rgba(7,9,10,.2) 55%, rgba(7,9,10,.45) 100%),
              linear-gradient(90deg, rgba(7,9,10,.7) 0, rgba(7,9,10,0) 60%); }
.band-in { width: 100%; padding-block: clamp(80px, 12vh, 140px) clamp(56px, 8vh, 96px); pointer-events: none; }
.band-in > * { pointer-events: auto; }
.band-fig { font: 800 clamp(120px, 22vw, 360px)/.78 Archivo, "Archivo Fallback", sans-serif; font-stretch: 62%;
            letter-spacing: -.02em; color: #F3F5F6; margin: 0 0 8px -.04em; text-shadow: 0 4px 40px rgba(0,0,0,.4); }
.band-fig span { display: inline-block; }
.band-title { font-size: clamp(30px, 3.6vw, 56px); line-height: .95; letter-spacing: -.04em; text-transform: uppercase; margin-bottom: 16px; }
.band-lede { color: #D0D7DB; font-size: var(--t-lede); max-width: 46ch; margin-bottom: 8px; }
.band-ch .ch-more a { color: var(--red-on-dark); }

/* ── the capability index, photographs at rest ──────────────────────────────── */
@media (hover: hover) and (min-width: 901px) {
  .cx-hit { grid-template-columns: minmax(0, .34fr) minmax(0, 1.9fr) minmax(0, 1.1fr) minmax(0, 1fr) 176px 56px; }
  .cx-hit > .cx-media {
    display: block; grid-column: 5; grid-row: 1; order: 0; position: relative; overflow: hidden;
    height: 104px; background: #1E252A;
  }
  .cx-go { grid-column: 6; }
  .cx-media img { width: 100%; height: 100%; object-fit: cover; filter: grayscale(1) brightness(.75);
                  transform: scale(1.02); transition: filter 420ms var(--ease), transform 900ms var(--ease); }
  .cx-hit:hover .cx-media img, .cx-hit:focus-visible .cx-media img { filter: none; transform: scale(1.12); }
  .cx-hit > .cx-media.cx-plate { display: flex; padding: 10px 12px; border-bottom-width: 2px; }
  .cx-media.cx-plate b { font-size: 30px; }
  .cx-media.cx-plate span { font-size: 11.5px; line-height: 1.3; }
}
@media (hover: hover) and (min-width: 901px) and (max-width: 1180px) {
  .cx-hit { grid-template-columns: minmax(0, .3fr) minmax(0, 1.8fr) minmax(0, 1.1fr) 150px 50px; }
  .cx-hit > .cx-media { grid-column: 4; }
  .cx-go { grid-column: 5; }
}
@media (prefers-reduced-motion: reduce) { .cx-media img { transition: none; } .cx-hit:hover .cx-media img { transform: none; } }
/* Two ghost buttons share a row on a phone; the stamped lettering is wider
   than the old face, so they give up side padding before the label. */
@media (max-width: 760px) { .hero-act .btn-ghost { padding: 0 12px; } }
