/* Godspeed Digital — marketing site
   Built on the design system in styles.css / tokens/.
   Every value here traces back to the approved prototype. */

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

html, body {
  margin: 0;
  padding: 0;
  background: var(--gs-paper);
  color: var(--gs-ink);
  font-family: var(--gs-font-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  max-width: 100%;
}

a { color: inherit; text-decoration: none; }

img { max-width: 100%; }
/* Every <img> carries its file's width and height attributes, so the browser
   knows its shape and reserves the space before it loads (no layout shift).
   The attributes must not set the size, though: most rules below fix only the
   height (the logos) or only the width, and the other side has to follow the
   aspect ratio, not the attribute. :where() keeps this at zero specificity, so
   any sizing rule anywhere still wins. */
:where(img[width][height]) { width: auto; height: auto; }

:focus-visible { outline: 2px solid var(--gs-ink); outline-offset: 4px; }

/* ---------------------------------------------------------------- layout */

/* The page gutter. Sections have always carried their own horizontal padding,
   because .wrap only centres and caps width, it never insets. Every section had
   independently arrived at the same two numbers, 48px and 22px on a phone, which
   worked right up until one section forgot: .legal shipped with `0` on both
   sides, so the privacy policy ran edge to edge on mobile with the text touching
   the screen.

   Naming the value once means the next section cannot quietly opt out of it.
   The override lives on :root rather than per-section so a section only ever
   states that it uses the gutter, not what the gutter is at each width. */
:root { --gs-pad-x: 48px; }
@media (max-width: 600px) { :root { --gs-pad-x: 22px; } }

.wrap { width: 100%; max-width: var(--gs-content-max); margin: 0 auto; }

.eyebrow {
  font-family: var(--gs-font-mono);
  font-size: 13px;
  letter-spacing: var(--gs-track-eyebrow);
  text-transform: uppercase;
  color: var(--gs-grey-500);
}

.eyebrow-sm {
  font-family: var(--gs-font-mono);
  font-size: 12px;
  letter-spacing: var(--gs-track-eyebrow);
  text-transform: uppercase;
}

/* ---------------------------------------------------------- interactions */

.gs-btn {
  transition: background var(--gs-duration-micro) var(--gs-ease-out),
              color var(--gs-duration-micro) var(--gs-ease-out);
  cursor: pointer;
  border: none;
  font-family: inherit;
  border-radius: var(--gs-radius-pill);
}
.gs-btn-ink { background: var(--gs-ink); color: var(--gs-paper); }
.gs-btn-ink:hover { background: var(--gs-signal); color: var(--gs-ink); }
.gs-btn-outline { background: transparent; color: var(--gs-ink); border: 1px solid var(--gs-ink); }
.gs-btn-outline:hover { background: var(--gs-ink); color: var(--gs-signal); }
.gs-btn-onsignal { background: var(--gs-ink); color: var(--gs-paper); }
.gs-btn-onsignal:hover { background: var(--gs-paper); color: var(--gs-ink); }

.gs-social { transition: color var(--gs-duration-micro) var(--gs-ease-out); display: flex; }
.gs-social:hover { color: var(--gs-signal); }

/* .gs-arrow lived here. It was the testimonial carousel's prev/next pair, and
   that carousel is now a marquee. Nothing else used the class. */

/* Scroll reveal. Attribute-driven so it survives any later re-render. */
[data-reveal] {
  opacity: 0;
  transform: translateY(34px);
  transition: opacity 900ms var(--gs-ease-out), transform 900ms var(--gs-ease-out);
}
[data-reveal][data-in] { opacity: 1; transform: none; }
[data-reveal] .gs-stagger {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 700ms var(--gs-ease-out), transform 700ms var(--gs-ease-out);
}
[data-reveal][data-in] .gs-stagger { opacity: 1; transform: none; }
[data-reveal][data-in] .gs-stagger:nth-child(2) { transition-delay: .09s; }
[data-reveal][data-in] .gs-stagger:nth-child(3) { transition-delay: .18s; }
[data-reveal][data-in] .gs-stagger:nth-child(4) { transition-delay: .27s; }

/* Scroll-velocity line. CSS scroll timeline where supported, JS fallback sets --p. */
.gs-progress {
  position: fixed;
  top: 0; left: 0;
  height: 3px;
  width: 100%;
  background: var(--gs-signal);
  transform-origin: 0 50%;
  transform: scaleX(var(--p, 0));
  z-index: 70;
}
@supports (animation-timeline: scroll(root)) {
  @keyframes gsGrow { to { transform: scaleX(1); } }
  .gs-progress {
    transform: scaleX(0);
    animation: gsGrow linear both;
    animation-timeline: scroll(root);
  }
}

/* ------------------------------------------------------------------- nav */

.gs-nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 15px 48px;
  background: var(--gs-paper-80);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  opacity: 0;
  transition: opacity 700ms var(--gs-ease-out),
              background var(--gs-duration-medium) var(--gs-ease-out);
}
.gs-nav[data-ready] { opacity: 1; }

/* Over the hero, the bar disappears into the orange instead of capping it. */
.gs-nav[data-onhero] {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
/* Ink-on-signal needs the inverse hover, same as the footer CTA. */
.gs-nav[data-onhero] .gs-nav__cta:hover { background: var(--gs-paper); color: var(--gs-ink); }
.gs-nav__logo { display: flex; align-items: center; }
.gs-nav__logo img { height: 25px; display: block; }
.gs-nav__actions { display: flex; align-items: center; gap: 36px; }
.gs-nav__cta { font-size: 14px; font-weight: 500; padding: 10px 20px; }

/* ------------------------------------------------------------------ hero */

/* PREVIEW 2026-10-06: the gallery hero. The brand line sits bottom-left with
   open space above it; Leighton's God Speed hangs on the wall to the right
   with a museum label under it. The wall is Sovereign Blue (Adam's pick over
   the paper, 2026-10-06). */
.hero {
  min-height: 100vh;
  /* Mobile browser chrome eats into vh, which pushes the scroll cue below the
     visible area. svh is the viewport with that chrome showing. */
  min-height: 100svh;
  background: var(--gs-signal);
  color: var(--on-signal);
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
  align-items: end;
  gap: clamp(32px, 5vw, 104px);
  padding: 112px var(--gs-pad-x) clamp(48px, 6vw, 88px);
  position: relative;
  overflow: hidden;
}
.hero__copy { align-self: end; max-width: 13.5em; font-size: clamp(44px, 6.3vw, 116px); }
.hero .hero__line {
  font-family: var(--gs-font-display);
  font-weight: 300; /* PREVIEW: Trust 3A Light, optical weight (see typography.css) */
  font-size: 1em;
  line-height: .94;
  letter-spacing: var(--gs-track-display);
  color: var(--on-signal);
  text-wrap: balance;
  margin: 0;
}
.hero__plain {
  font-family: var(--gs-font-body);
  font-size: clamp(16px, 1.35vw, 20px);
  line-height: 1.45;
  letter-spacing: 0;
  color: var(--on-signal-muted);
  max-width: 54ch;
  text-wrap: balance;
  margin: clamp(22px, 2.4vw, 34px) 0 0;
}

.hero__btn {
  /* Block, or it sits on a line box sized by the copy's huge font-size and
     floats ~60px below the tagline whatever its margin says. */
  display: block;
  width: fit-content;
  margin-top: clamp(16px, 1.6vw, 22px);
  padding: 16px 32px;
  font-size: 16px;
  font-weight: 600;
  background: var(--on-signal);
  color: var(--gs-signal);
}
.hero__btn:hover { background: var(--on-signal-hover); color: var(--gs-signal); }
.hero__btn:focus-visible { outline: 2px solid var(--on-signal); outline-offset: 4px; }

.hero__art {
  /* PREVIEW 2026-10-08: the painting takes what the old label used to; the
     title row and strip below it need about 160px of the 230. */
  --frame-h: clamp(420px, calc(100svh - 230px), 900px);
  margin: 0;
  align-self: center;
  width: 100%;
  /* The frame sizes against this box (cqw), so a portrait and a landscape
     painting can both fill it without a circular percentage. */
  container-type: inline-size;
}
.hero__piece {
  position: relative;
  /* Full column width, so the label always wraps the same way whichever
     painting is up; the painting itself sits at the left of the wall. */
  width: 100%;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
/* A picture light: a soft pool of light on the wall above and behind the work. */
.hero__piece::before {
  content: "";
  position: absolute;
  left: 50%; top: -18%;
  width: 160%; height: 80%;
  transform: translateX(-50%);
  /* closest-side, so the pool fades to nothing before the box edge. */
  background: radial-gradient(closest-side, var(--signal-glow), transparent);
  pointer-events: none;
  z-index: 0;
}
/* THE PAINTINGS (preview). All three sit in one box and cross-fade; only the
   one on show is visible. The box takes that painting's proportions (--ar,
   set by site.js), so a portrait fills the height cap and a landscape fills
   the column width. Clicking the painting, or a dot, shows the next. */
/* A fixed-height wall, so a landscape painting does not shorten the hero and
   jog the headline. Paintings hang with their bottom edges on one line, which
   keeps the label and arrows in the same place for every painting. */
.hero__wall { height: var(--frame-h); display: flex; align-items: flex-end; perspective: 1100px; }
.hero__stack {
  /* Tilt toward the mouse (site.js sets --rx/--ry, the glint --gx/--gy).
     A few degrees at most; at rest everything is 0 and the painting is flat. */
  --rx: 0deg; --ry: 0deg; --gx: 50%; --gy: 50%;
  --ex: 0px; --ey: 0px;   /* frame edge, on the side turning away (site.js) */
  transform: rotateX(var(--rx)) rotateY(var(--ry));
  transform-style: preserve-3d;
  will-change: transform;
  position: relative;
  z-index: 1;
  width: min(100cqw, var(--frame-h) * var(--ar));
  aspect-ratio: var(--ar);
  cursor: pointer;
  transition: width 700ms cubic-bezier(.4, 0, .2, 1);
}
.hero__stack:focus-visible { outline: 2px solid var(--on-signal); outline-offset: 10px; }
.hero__frame {
  position: absolute;
  inset: 0;
  /* Silver: a brushed, angled metal gradient with a light outer lip and a dark
     inner edge where the frame meets the canvas, after the titanium in the deck.
     The gradient is oversized and slides with the tilt, so the light moves
     across the metal as the painting turns. */
  padding: clamp(16px, 1.7vw, 26px);
  background:
    linear-gradient(135deg, #F5F6F8 0%, #B6B9BF 16%, #EDEFF2 30%, #8E9198 48%, #E2E4E8 63%, #A3A6AD 80%, #F2F3F5 100%)
    var(--gx) var(--gy) / 220% 220%;
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, .75),
    inset 0 0 0 2px rgba(70, 72, 78, .35),
    calc(var(--ex) * 1) calc(var(--ey) * 1) 0 #A9ACB2,
    calc(var(--ex) * 2) calc(var(--ey) * 2) 0 #A0A3A9,
    calc(var(--ex) * 3) calc(var(--ey) * 3) 0 #969A9F,
    calc(var(--ex) * 4) calc(var(--ey) * 4) 0 #8C8F95,
    calc(var(--ex) * 5) calc(var(--ey) * 5) 0 #82858B,
    calc(var(--ex) * 6) calc(var(--ey) * 6) 0 #787B81,
    calc(var(--ex) * 7) calc(var(--ey) * 7) 0 #6E7177,
    calc(var(--ex) * 8) calc(var(--ey) * 8) 0 #64676D,
    calc(var(--ex) * 14) calc(30px + var(--ey) * 14) 60px -22px rgba(0, 0, 0, .65);
  opacity: 0;
  transition: opacity 700ms cubic-bezier(.4, 0, .2, 1);
}
.hero__frame[data-pos="0"] { opacity: 1; z-index: 2; }
.hero__frame img {
  display: block; width: 100%; height: 100%; object-fit: cover; pointer-events: none;
  /* The slip: a dark line, a bright silver liner, and a second dark line where
     the moulding steps down to the canvas. */
  box-shadow: 0 0 0 1px rgba(30, 32, 36, .65), 0 0 0 3px #E6E8EC, 0 0 0 4px rgba(30, 32, 36, .45);
}
/* ORNAMENT. A band of raised silver beads (a pearl moulding) runs right round
   the frame, drawn with border-image so the beads space themselves evenly on
   any painting's proportions, and a silver rosette sits in each corner. All
   inline SVG, no files. */
.hero__frame::before {
  content: "";
  position: absolute;
  inset: clamp(4px, .45vw, 7px);
  border: 7px solid transparent;
  border-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='21' height='21'%3E%3Cdefs%3E%3CradialGradient id='b' cx='.35' cy='.32' r='.75'%3E%3Cstop offset='0' stop-color='%23FFFFFF'/%3E%3Cstop offset='.45' stop-color='%23C9CCD2'/%3E%3Cstop offset='1' stop-color='%235E6167'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cg fill='url(%23b)'%3E%3Ccircle cx='3.5' cy='3.5' r='2.9'/%3E%3Ccircle cx='10.5' cy='3.5' r='2.9'/%3E%3Ccircle cx='17.5' cy='3.5' r='2.9'/%3E%3Ccircle cx='3.5' cy='10.5' r='2.9'/%3E%3Ccircle cx='17.5' cy='10.5' r='2.9'/%3E%3Ccircle cx='3.5' cy='17.5' r='2.9'/%3E%3Ccircle cx='10.5' cy='17.5' r='2.9'/%3E%3Ccircle cx='17.5' cy='17.5' r='2.9'/%3E%3C/g%3E%3C/svg%3E") 7 round;
  pointer-events: none;
}
.hero__frame::after {
  content: "";
  position: absolute;
  inset: calc(clamp(16px, 1.7vw, 26px) * -.2);
  --rose: calc(clamp(16px, 1.7vw, 26px) * 1.4);
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cdefs%3E%3CradialGradient id='r' cx='.4' cy='.35' r='.8'%3E%3Cstop offset='0' stop-color='%23FFFFFF'/%3E%3Cstop offset='.5' stop-color='%23BFC2C8'/%3E%3Cstop offset='1' stop-color='%2363666C'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cg fill='url(%23r)' stroke='%234B4E54' stroke-width='.8'%3E%3Cpath d='M20 2c3 6 3 9 0 14-3-5-3-8 0-14zM20 38c-3-6-3-9 0-14 3 5 3 8 0 14zM2 20c6-3 9-3 14 0-5 3-8 3-14 0zM38 20c-6 3-9 3-14 0 5-3 8-3 14 0z'/%3E%3Cpath d='M7.3 7.3c6.2 2.3 8.4 4.5 9.2 9.2-4.7-.8-6.9-3-9.2-9.2zM32.7 32.7c-6.2-2.3-8.4-4.5-9.2-9.2 4.7.8 6.9 3 9.2 9.2zM32.7 7.3c-2.3 6.2-4.5 8.4-9.2 9.2.8-4.7 3-6.9 9.2-9.2zM7.3 32.7c2.3-6.2 4.5-8.4 9.2-9.2-.8 4.7-3 6.9-9.2 9.2z' opacity='.85'/%3E%3Ccircle cx='20' cy='20' r='4.6'/%3E%3C/g%3E%3C/svg%3E") left top / var(--rose) no-repeat,
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cdefs%3E%3CradialGradient id='r' cx='.4' cy='.35' r='.8'%3E%3Cstop offset='0' stop-color='%23FFFFFF'/%3E%3Cstop offset='.5' stop-color='%23BFC2C8'/%3E%3Cstop offset='1' stop-color='%2363666C'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cg fill='url(%23r)' stroke='%234B4E54' stroke-width='.8'%3E%3Cpath d='M20 2c3 6 3 9 0 14-3-5-3-8 0-14zM20 38c-3-6-3-9 0-14 3 5 3 8 0 14zM2 20c6-3 9-3 14 0-5 3-8 3-14 0zM38 20c-6 3-9 3-14 0 5-3 8-3 14 0z'/%3E%3Cpath d='M7.3 7.3c6.2 2.3 8.4 4.5 9.2 9.2-4.7-.8-6.9-3-9.2-9.2zM32.7 32.7c-6.2-2.3-8.4-4.5-9.2-9.2 4.7.8 6.9 3 9.2 9.2zM32.7 7.3c-2.3 6.2-4.5 8.4-9.2 9.2.8-4.7 3-6.9 9.2-9.2zM7.3 32.7c2.3-6.2 4.5-8.4 9.2-9.2-.8 4.7-3 6.9-9.2 9.2z' opacity='.85'/%3E%3Ccircle cx='20' cy='20' r='4.6'/%3E%3C/g%3E%3C/svg%3E") right top / var(--rose) no-repeat,
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cdefs%3E%3CradialGradient id='r' cx='.4' cy='.35' r='.8'%3E%3Cstop offset='0' stop-color='%23FFFFFF'/%3E%3Cstop offset='.5' stop-color='%23BFC2C8'/%3E%3Cstop offset='1' stop-color='%2363666C'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cg fill='url(%23r)' stroke='%234B4E54' stroke-width='.8'%3E%3Cpath d='M20 2c3 6 3 9 0 14-3-5-3-8 0-14zM20 38c-3-6-3-9 0-14 3 5 3 8 0 14zM2 20c6-3 9-3 14 0-5 3-8 3-14 0zM38 20c-6 3-9 3-14 0 5-3 8-3 14 0z'/%3E%3Cpath d='M7.3 7.3c6.2 2.3 8.4 4.5 9.2 9.2-4.7-.8-6.9-3-9.2-9.2zM32.7 32.7c-6.2-2.3-8.4-4.5-9.2-9.2 4.7.8 6.9 3 9.2 9.2zM32.7 7.3c-2.3 6.2-4.5 8.4-9.2 9.2.8-4.7 3-6.9 9.2-9.2zM7.3 32.7c2.3-6.2 4.5-8.4 9.2-9.2-.8 4.7-3 6.9-9.2 9.2z' opacity='.85'/%3E%3Ccircle cx='20' cy='20' r='4.6'/%3E%3C/g%3E%3C/svg%3E") left bottom / var(--rose) no-repeat,
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cdefs%3E%3CradialGradient id='r' cx='.4' cy='.35' r='.8'%3E%3Cstop offset='0' stop-color='%23FFFFFF'/%3E%3Cstop offset='.5' stop-color='%23BFC2C8'/%3E%3Cstop offset='1' stop-color='%2363666C'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cg fill='url(%23r)' stroke='%234B4E54' stroke-width='.8'%3E%3Cpath d='M20 2c3 6 3 9 0 14-3-5-3-8 0-14zM20 38c-3-6-3-9 0-14 3 5 3 8 0 14zM2 20c6-3 9-3 14 0-5 3-8 3-14 0zM38 20c-6 3-9 3-14 0 5-3 8-3 14 0z'/%3E%3Cpath d='M7.3 7.3c6.2 2.3 8.4 4.5 9.2 9.2-4.7-.8-6.9-3-9.2-9.2zM32.7 32.7c-6.2-2.3-8.4-4.5-9.2-9.2 4.7.8 6.9 3 9.2 9.2zM32.7 7.3c-2.3 6.2-4.5 8.4-9.2 9.2.8-4.7 3-6.9 9.2-9.2zM7.3 32.7c2.3-6.2 4.5-8.4 9.2-9.2-.8 4.7-3 6.9-9.2 9.2z' opacity='.85'/%3E%3Ccircle cx='20' cy='20' r='4.6'/%3E%3C/g%3E%3C/svg%3E") right bottom / var(--rose) no-repeat;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, .35));
  pointer-events: none;
}

.hero__browse { margin-top: 14px; display: flex; align-items: center; gap: 14px; position: relative; z-index: 1; color: var(--on-signal); }
.hero__arrow {
  width: 40px; height: 40px; display: grid; place-items: center;
  border-radius: 50%; border: 1px solid var(--on-signal-24); background: transparent; color: inherit; cursor: pointer;
  transition: background var(--gs-duration-micro) var(--gs-ease-out), border-color var(--gs-duration-micro) var(--gs-ease-out);
}
.hero__arrow:hover { background: rgba(248, 244, 239, .1); border-color: var(--on-signal); }
.hero__arrow:focus-visible { outline: 2px solid var(--on-signal); outline-offset: 3px; }
.hero__count { font-family: var(--gs-font-mono); font-size: 12px; letter-spacing: .12em; color: var(--on-signal-muted); min-width: 4.5ch; text-align: center; }

/* PREVIEW 2026-10-08: one fixed frame and a strip of thumbnails (Adam's pick
   of the movement drafts). The frame never changes shape, so moving between
   portrait and landscape paintings no longer jolts the hero; only the painting
   inside cross-fades, filling the frame (site.js holds each crop point). */
.hero__stack { --ar: 4 / 5; transition: none; }
.hero__count { display: none; }
.hero__browse { width: 100%; }
.hero__strip {
  position: relative; display: flex; align-items: center; gap: 5px;
  flex: 1 1 auto; min-width: 0; max-width: 100%; overflow-x: auto; scrollbar-width: none;
  padding: 6px 2px 2px;
}
.hero__strip::-webkit-scrollbar { display: none; }
.hero__thumb {
  /* share the row: shrink to fit all twelve, never past 52px, and scroll
     rather than go below 24px on a phone */
  flex: 1 1 0; min-width: 22px; max-width: 34px; aspect-ratio: 1;
  padding: 2px; border: 0; cursor: pointer;
  background: linear-gradient(135deg, #F5F6F8 0%, #B6B9BF 30%, #EDEFF2 55%, #8E9198 100%);
  opacity: .35;
  transition: opacity var(--gs-duration-micro) var(--gs-ease-out), transform var(--gs-duration-micro) var(--gs-ease-out);
}
.hero__thumb img { display: block; width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
.hero__thumb:hover { opacity: .85; }
.hero__thumb[aria-current="true"] { opacity: 1; transform: translateY(-3px); }
.hero__thumb:focus-visible { outline: 2px solid var(--on-signal); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .hero__thumb { transition: none; } }

/* The title row under the painting (PREVIEW 2026-10-08): the title set large
   in the manuscript face the Praise quotes use, the artist small beneath, and
   the controls on the right. The row and the strip below it take the frame's
   own width, so everything lines up with the painting. */
.hero__meta, .hero__browse { width: min(100cqw, var(--frame-h) * .8); }
.hero__meta {
  position: relative; z-index: 1;
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  margin-top: 18px;
}
.hero__label {
  min-width: 0;   /* site.js reserves the tallest title's height */
  display: flex; flex-direction: column; gap: 4px;
  font-family: var(--gs-font-body); font-size: 13px; line-height: 1.4;
  color: var(--on-signal-muted);
}
.hero__label-title {
  font-family: "IM Fell English", Georgia, serif; font-size: clamp(20px, 1.6vw, 27px); line-height: 1.12;
  color: var(--on-signal); text-wrap: balance;
}
.hero__label-year { font-family: var(--gs-font-body); font-size: 13px; color: var(--on-signal-muted); }
.hero__label-artist { color: var(--on-signal-muted); }
.hero__label-dates { color: var(--on-signal-muted); opacity: .8; }
.hero__controls { flex: none; display: flex; gap: 8px; padding-top: 2px; }
.hero__controls .hero__arrow { width: 34px; height: 34px; }
.hero__about-btn span { font: italic 17px/1 "IM Fell English", Georgia, serif; }
.hero__about-btn[aria-expanded="true"] { background: var(--on-signal); color: var(--gs-signal); border-color: var(--on-signal); }

/* The story panel rises over the foot of the painting, inside the frame's
   moulding, on hover or focus, or when pinned open with i or a tap. */
.hero__about {
  position: absolute; z-index: 4;
  left: clamp(16px, 1.7vw, 26px); right: clamp(16px, 1.7vw, 26px); bottom: clamp(16px, 1.7vw, 26px);
  padding: clamp(40px, 5cqw, 64px) clamp(16px, 2.4cqw, 24px) clamp(14px, 2cqw, 20px);
  background: linear-gradient(to top, rgba(1, 3, 38, .95) 0%, rgba(1, 3, 38, .86) 58%, rgba(1, 3, 38, 0) 100%);
  color: var(--on-signal); font-family: var(--gs-font-body); font-size: clamp(13px, 1.05vw, 15px); line-height: 1.5;
  opacity: 0; transform: translateY(10px); pointer-events: none;
  transition: opacity 380ms var(--gs-ease-out), transform 380ms var(--gs-ease-out);
}
.hero__about p { margin: 0; max-width: 46ch; }
.hero__about-where { margin-top: 8px !important; font-size: 12px; color: var(--on-signal-muted); }
@media (hover: hover) { .hero__stack:hover .hero__about { opacity: 1; transform: none; } }
.hero__stack:focus-visible .hero__about, [data-about] .hero__about { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .hero__about { transition: none; transform: none; } }
/* Quieter: on a mouse, the thumbnail strip only appears while you are over the
   painting area, and takes no room of its own, so the painting can have it.
   Touch screens keep it showing. The hero's own padding is trimmed on desktop
   for the same reason. */
@media (hover: hover) and (min-width: 821px) {
  .hero { padding-top: 88px; padding-bottom: 52px; }
  .hero__browse {
    position: absolute; left: 0; top: 100%; margin-top: 8px;
    opacity: 0; transition: opacity 280ms var(--gs-ease-out);
  }
  .hero__piece:hover .hero__browse, .hero__piece:focus-within .hero__browse { opacity: 1; }
}

@media (max-width: 820px) {
  .hero {
    /* minmax(0, 1fr), not 1fr: a bare 1fr column will not shrink below the
       frame's own width, which pushed the headline off a 320px screen. */
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
    gap: 40px;
    padding: 96px var(--gs-pad-x) 56px;
  }
  /* Headline, line and button first, then the painting (Adam, 8 Oct: someone
     landing on a phone expects an agency site, not a painting). */
  .hero__piece { margin-inline: 0; }
  /* On a phone the height cap is lower, so the painting never fills the screen. */
  .hero__art { --frame-h: min(46svh, 560px); }
  .hero__copy { font-size: clamp(40px, 11vw, 72px); }
}

.hero__markwrap { perspective: 900px; margin-bottom: 40px; }

/* The mark recedes in 3D space, then the tagline types on. */
@keyframes gsRecede {
  0%   { transform: scale(1.7) translateY(26px); filter: blur(7px); opacity: 0; }
  55%  { opacity: 1; }
  100% { transform: scale(1) translateY(0); filter: blur(0); opacity: 1; }
}
.gs-mark {
  height: 98px;
  width: 91px;
  display: block;
  /* Shortened with the intro (2026-07-28) so the mark settles before the
     tagline starts typing rather than competing with it. */
  animation: gsRecede 900ms var(--gs-ease-out) both;
}

.gs-h1 {
  font-family: var(--gs-font-display);
  font-weight: 300; /* PREVIEW: Trust 3A Light, optical weight (see typography.css) */
  font-size: clamp(38px, 6vw, 92px);
  line-height: 1.06;
  letter-spacing: var(--gs-track-tight);
  margin: 0;
  color: var(--gs-ink);
}
.gs-h1 > span { display: block; white-space: nowrap; min-height: 1.06em; }

@keyframes gsBlink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
.gs-caret { animation: gsBlink 1.05s step-end infinite; font-weight: 300; }
.gs-caret[hidden] { display: none; }

.hero__subline {
  font-family: var(--gs-font-mono);
  font-size: clamp(12px, 1.3vw, 15px);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: rgba(14, 15, 17, .48);
  margin: 26px 0 0;
  opacity: 0;
  transition: opacity 900ms var(--gs-ease-out);
}
.hero__subline[data-in] { opacity: 1; }

.hero__scroll {
  position: absolute;
  bottom: 40px; left: 0; right: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  opacity: 0;
  transition: opacity 900ms var(--gs-ease-out);
}
.hero__scroll[data-in] { opacity: 1; }
.hero__scroll span:first-child {
  font-family: var(--gs-font-mono);
  font-size: 11px;
  letter-spacing: var(--gs-track-eyebrow);
  text-transform: uppercase;
  color: var(--gs-ink-56);
}
.hero__track {
  width: 1px; height: 32px;
  background: var(--gs-ink-24);
  position: relative;
  overflow: hidden;
}
@keyframes gsScroll {
  0%   { transform: translateY(0); opacity: 0; }
  30%  { opacity: 1; }
  100% { transform: translateY(14px); opacity: 0; }
}
.hero__track i {
  position: absolute; top: 0; left: 0;
  width: 1px; height: 12px;
  background: var(--gs-ink);
  animation: gsScroll 1.8s var(--gs-ease-out) infinite;
}

/* ------------------------------------------------------------- big number */

.bignum {
  min-height: 92vh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* Bottom padding lifts the centred stack by half its value, clearing room
     for the cue below it. */
  padding: 100px 32px 256px;
  position: relative;
  overflow: hidden;
}
.bignum__inner { position: relative; z-index: 1; }
/* Draws the eye down into What We Do across the gap below the figure. */
/* Hairline that carries the eye from one section into the next. Absolute, so
   it lives inside existing whitespace and never adds to the gap. */
.gs-cue {
  position: absolute;
  left: 50%;
  bottom: 27px;
  transform: translateX(-50%);
  pointer-events: none;
}
.gs-cue span {
  display: block;
  width: 1px;
  height: 156px;
  background: linear-gradient(to bottom, transparent, var(--gs-ink-24));
}
.bignum__figure {
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular, set close like the deck's numerals */
  font-size: clamp(80px, 14vw, 220px);
  line-height: .9;
  letter-spacing: var(--gs-track-display);
  color: var(--gs-signal);
}
.bignum__label { margin-top: 32px; }
.bignum__note {
  font-size: clamp(17px, 2.2vw, 22px);
  color: var(--gs-ink-72);
  margin: 20px auto 0;
  max-width: 30ch;
}

/* --------------------------------------------------------------- services */

.services {
  /* Sized to content, not 100vh: centring inside a taller viewport would turn
     the trimmed top padding back into slack and float the eyebrow away from
     the cue line. This keeps that gap identical on every screen. */
  display: flex;
  align-items: center;
  /* Lighter on top so the cue line runs close into the eyebrow. */
  padding: 60px 48px 120px;
}
.services .eyebrow { margin-bottom: 56px; }

.gs-2col { display: grid; gap: 80px; }
.services .gs-2col { grid-template-columns: 1.2fr 1fr; align-items: center; }

.services__list { display: flex; flex-direction: column; gap: 6px; }
.gs-service {
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular, to match Work Philosophy (Adam, 2026-10-08) */
  font-size: clamp(30px, 7vw, 52px);
  line-height: 1.18;
  letter-spacing: var(--gs-track-tight);
  color: var(--gs-ink);
  transition: color var(--gs-duration-micro) var(--gs-ease-out),
              transform var(--gs-duration-medium) var(--gs-ease-out);
  transform-origin: left center;
  cursor: default;
}
@media (hover: hover) {
  .gs-service:hover { color: var(--gs-signal); transform: scale(1.05); }
}
.gs-svc.is-open .gs-service { color: var(--gs-signal); }

.gs-svc-desc {
  display: none;
  font-family: var(--gs-font-display);
  font-weight: 400;
  font-size: clamp(18px, 4.6vw, 22px);
  line-height: 1.3;
  color: var(--gs-ink-72);
  margin: 6px 0 10px;
}

.panel { border-left: 1px solid var(--gs-ink-12); padding-left: 56px; }
.services__cta { margin-top: 30px; }
.services__cta-note { color: var(--gs-grey-500); margin-bottom: 12px; }
.services__cta .gs-btn { font-size: 15px; font-weight: 500; padding: 14px 30px; }

.gs-svc-panel { min-height: 220px; }
.panel__label { color: var(--gs-signal); margin-bottom: 20px; }
.gs-svc-panel .panel__body {
  font-family: var(--gs-font-display);
  font-weight: 400;
  font-size: clamp(24px, 2.8vw, 34px);
  line-height: 1.28;
  letter-spacing: -0.01em;
  margin: 0;
}

/* ------------------------------------------------------------------- work */

.work { padding: 120px 48px; }
.work .eyebrow { margin-bottom: 56px; }
.gs-grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; }
/* Three across, so six cards make two even rows. minmax(0, …) for the same
   reason the galleries need it: a bare 1fr will not shrink below its content. */
.gs-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; }

/* "View all work", under the four selected cards. Sizing is copied from
   .services__cta .gs-btn deliberately, so this reads as the same button as
   "Build your quote" further up the page rather than a near-miss. It is an <a>
   rather than a <button>, so it also needs the inline-block and line-height
   that a real button gets for free. */
.work__more { margin-top: 56px; display: flex; justify-content: center; }
.work__more .gs-btn {
  font-size: 15px;
  font-weight: 500;
  padding: 14px 30px;
  display: inline-block;
  line-height: normal;
}

.gs-case { display: block; min-width: 0; }
.gs-case__media {
  width: 100%;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--gs-grey-100);
}
.gs-caseimg { transition: transform 700ms var(--gs-ease-out); }
.gs-case:hover .gs-caseimg { transform: scale(1.04); }
.gs-case__client {
  font-family: var(--gs-font-mono);
  font-size: 11px;
  letter-spacing: var(--gs-track-eyebrow);
  text-transform: uppercase;
  color: var(--gs-grey-500);
  margin: 22px 0 12px;
}
.gs-case__figure {
  font-family: var(--gs-font-display);
  font-weight: 500; /* PREVIEW: Trust 3A Medium, optical weight (see typography.css) */
  font-size: clamp(30px, 3vw, 44px);
  line-height: 1;
  color: var(--gs-signal);
}
.gs-case__desc { font-size: 15px; color: var(--gs-ink-72); margin-top: 10px; }
.gs-caseview {
  display: inline-flex;
  align-items: center;
  font-family: var(--gs-font-mono);
  font-size: 11px;
  letter-spacing: var(--gs-track-eyebrow);
  text-transform: uppercase;
  color: var(--gs-signal);
  margin-top: 14px;
  opacity: 0;
  transform: translateX(-8px);
  transition: opacity var(--gs-duration-medium) var(--gs-ease-out),
              transform var(--gs-duration-medium) var(--gs-ease-out);
}
.gs-case:hover .gs-caseview { opacity: 1; transform: none; }

/* ----------------------------------------------------------- testimonials */

/* A MARQUEE OF VOICES. Replaces the dot-and-arrow carousel with a portrait
   column. Two things that bought: every voice is on screen at once instead of
   one behind a click, and the section no longer depends on four portrait
   photographs that were never shot.

   The bed stays orange and the class stays .testi, because site.js inverts the
   quote tab over .hero, .testi and .cta. */

.testi {
  background: var(--gs-signal);
  color: var(--gs-ink);
  /* No side padding: the marquee bleeds. The lede carries the gutter itself.
     Note .testi is also in the 600px padding rule further down, which puts 22px
     back on a phone; the lede drops its own padding there so it does not double
     up. Overflow clips the track rather than letting it scroll the page. */
  padding: 130px 0;
  overflow: hidden;
}

.testi__lede {
  padding: 0 var(--gs-pad-x);
  max-width: var(--gs-content-max);
  margin: 0 auto;
}
.testi__h2 {
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular, optical weight (see typography.css) */
  font-size: clamp(40px, 5vw, 72px);
  line-height: 1;
  letter-spacing: var(--gs-track-tight);
  margin: 0 0 clamp(40px, 5vw, 64px);
}

.testi__marquee { position: relative; }

/* Fades the two cut edges so quotes enter and leave rather than being sliced
   off mid-word. Pointer-events off so it never eats a hover. */
.testi__marquee::before,
.testi__marquee::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  width: clamp(32px, 7vw, 120px);
  z-index: 2;
  pointer-events: none;
}
.testi__marquee::before { left: 0;  background: linear-gradient(to right, var(--gs-signal), transparent); }
.testi__marquee::after  { right: 0; background: linear-gradient(to left,  var(--gs-signal), transparent); }

.testi__track {
  display: flex;
  width: max-content;
  animation: gsTestiMarquee 52s linear infinite;
}
/* Reading a moving line is hard, so hovering anywhere stops it. Also stops on
   focus-within, or a keyboard user would be chasing a quote across the screen. */
.testi__marquee:hover .testi__track,
.testi__marquee:focus-within .testi__track { animation-play-state: paused; }

@keyframes gsTestiMarquee {
  /* -50%, not -100%: the track holds TWO identical passes, so half a translate
     is exactly one full set and the seam is invisible. */
  to { transform: translateX(-50%); }
}

.testi__item {
  display: flex;
  align-items: baseline;
  gap: clamp(12px, 1.4vw, 20px);
  padding: 0 clamp(24px, 3.4vw, 52px);
  border-left: 1px solid var(--gs-ink-24);
}

.testi__quote {
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular, optical weight (see typography.css) */
  font-size: clamp(21px, 2.4vw, 34px);
  line-height: 1.16;
  letter-spacing: var(--gs-track-tight);
  color: var(--gs-ink);
  white-space: nowrap;
}

/* Full-strength ink, not a faded tint. Separation comes from scale, face and
   tracking instead, because ink at 72% over this orange measures 3.95:1 and a
   12px attribution needs to clear 4.5:1. */
.testi__who {
  font-family: var(--gs-font-mono);
  font-size: clamp(11px, .85vw, 12px);
  letter-spacing: var(--gs-track-eyebrow);
  text-transform: uppercase;
  color: var(--gs-ink);
  white-space: nowrap;
}

/* No motion: a plain readable list. A paused marquee would show one and a half
   quotes clipped at the edges, which is worse than no marquee at all. site.js
   also skips the duplicate pass here, so nothing reads twice. */
@media (prefers-reduced-motion: reduce) {
  .testi__track {
    animation: none;
    width: auto;
    flex-wrap: wrap;
    gap: clamp(20px, 3vw, 32px) 0;
    padding: 0 var(--gs-pad-x);
    max-width: var(--gs-content-max);
    margin: 0 auto;
  }
  .testi__quote, .testi__who { white-space: normal; }
  .testi__marquee::before, .testi__marquee::after { display: none; }
}

/* The lede already gets 22px from the .testi entry in the 600px padding rule
   below, so it drops its own here rather than adding to it. */
@media (max-width: 600px) {
  .testi__lede { padding: 0; }
}

/* ------------------------------------------------------------- philosophy */

.faq {
  background: var(--gs-paper);
  color: var(--gs-ink);
  min-height: 100vh;
  display: flex;
  align-items: center;
  padding: 120px 48px;
}
.faq .eyebrow { margin-bottom: 56px; }
/* PREVIEW 2026-10-08: only the label above each answer ("Who we work best
   with" and the rest) is set in IM Fell English italic, the face of the
   painting titles, Adam's call. 14px reads the same size as the 12px mono
   capitals it replaces; sentence case. */
.gs-faq-panel .panel__label {
  font-family: 'IM Fell English', Georgia, serif; font-style: italic;
  font-size: 14px; letter-spacing: 0; text-transform: none;
}
.faq .gs-2col { grid-template-columns: 1.1fr 1fr; align-items: stretch; }
.faq__list { display: flex; flex-direction: column; gap: 14px; }
.gs-faq {
  text-align: left;
  /* A button is only as wide as its text, which left ~6% of the row tappable.
     Full width gives the same whole-row hit area the services list has, while
     keeping the button itself as the focusable element. */
  width: 100%;
  /* Buttons get iOS's default tap highlight, plain divs do not. Across a
     full-width row that reads as a grey box flashing on every tap, so it is
     suppressed here. The row turning orange is the feedback. */
  -webkit-tap-highlight-color: transparent;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular; Medium read slightly too bold here (Adam, 2026-10-08) */
  font-size: clamp(30px, 7vw, 52px);
  line-height: 1.16;
  letter-spacing: var(--gs-track-tight);
  color: var(--gs-ink);
  transition: color var(--gs-duration-micro) var(--gs-ease-out),
              transform var(--gs-duration-medium) var(--gs-ease-out);
  transform-origin: left center;
}
@media (hover: hover) {
  .gs-faq:hover { color: var(--gs-signal); transform: scale(1.05); }
}
.gs-faqi.is-open .gs-faq { color: var(--gs-signal); }
.gs-faq-desc {
  display: none;
  font-size: clamp(17px, 4.4vw, 20px);
  line-height: 1.5;
  color: var(--gs-ink-72);
  margin: 8px 0 10px;
}
.gs-faq-panel { min-height: 280px; display: flex; flex-direction: column; justify-content: center; }
.gs-faq-panel .panel__label { margin-bottom: 22px; }
/* The Process answer breaks into named steps rather than running as prose. */
.gs-step { display: block; }
.gs-step:first-of-type { margin-top: .7em; }
.gs-step + .gs-step { margin-top: .45em; }

.gs-faq-panel .panel__body strong { font-weight: 500; }
.gs-faq-panel .panel__body {
  /* PREVIEW 2026-10-08: Trust 3A, like the What We Do panel (Adam). The trial
     cut has no Bold, so the lead-ins are Medium. */
  font-family: var(--gs-font-display); font-weight: 400;
  font-size: clamp(18px, 1.9vw, 25px);
  line-height: 1.55;
  color: var(--gs-ink-72);
  margin: 0;
}

/* -------------------------------------------------------------- trusted by */

.trusted { background: var(--gs-paper); color: var(--gs-ink); padding: 120px 48px; }
.trusted .eyebrow { margin-bottom: 48px; }
.gs-grid-6 { display: grid; grid-template-columns: repeat(6, 1fr); gap: 20px; }
.gs-logo {
  aspect-ratio: 3 / 2;
  min-width: 0;
  /* --logo-dim darkens the resting state of marks whose brand colour greyscales
     too light to sit level with the rest of the grid. Hover drops the filter
     entirely, so the real colour still comes through. */
  filter: grayscale(1) brightness(var(--logo-dim, 1));
  opacity: .5;
  transition: opacity var(--gs-duration-medium) var(--gs-ease-out),
              filter var(--gs-duration-medium) var(--gs-ease-out);
}
.gs-logo:hover { opacity: 1; filter: none; }
/* Marks carry wildly different amounts of their own padding, so each can be
   nudged to a similar optical weight via --logo-scale on the cell. */
.gs-logo .slot img { transform: scale(var(--logo-scale, 1)); }

/* ------------------------------------------------------------------- team */

.team { background: var(--gs-paper); color: var(--gs-ink); padding: 130px 48px; }
.team .eyebrow { color: var(--gs-ink-56); margin-bottom: 16px; }
/* Six across, so the whole team sits on one line (Adam, 2026-09-29). Three
   across between 901px and 1200px, two across at 900px and below, so the
   rows always stay full. */
.gs-team-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 40px 24px; }
@media (max-width: 1200px) { .gs-team-grid { grid-template-columns: repeat(3, 1fr); gap: 40px 28px; } }
.gs-team-grid .gs-teamcard__name { font-size: clamp(15px, 1.6vw, 26px); }
.gs-teamcard { min-width: 0; }
.gs-teamcard__media {
  position: relative;
  aspect-ratio: 4 / 5;
  width: 100%;
  background: var(--gs-ink-12);
  overflow: hidden;
  margin-bottom: 20px;
}
.gs-teamcard__row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}
.gs-teamcard__name {
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular, optical weight (see typography.css) */
  font-size: clamp(15px, 1.9vw, 32px);
  letter-spacing: var(--gs-track-tight);
}
.gs-teamcard__role {
  font-family: var(--gs-font-mono);
  font-size: 12px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--gs-ink-56);
  margin-top: 8px;
}
.gs-teamcard__socials { display: flex; align-items: center; gap: 14px; padding-top: 6px; }

/* ----------------------------------------------------------- project pages */

.case-hero { padding: 132px 48px 0; }
.case-back {
  display: inline-block;
  font-family: var(--gs-font-mono);
  font-size: 12px;
  letter-spacing: var(--gs-track-eyebrow);
  text-transform: uppercase;
  color: var(--gs-ink-56);
  margin-bottom: 44px;
  transition: color var(--gs-duration-micro) var(--gs-ease-out);
}
.case-back:hover { color: var(--gs-signal); }
.case-hero__client { margin-bottom: 20px; }
.case-hero__h1 {
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular, optical weight (see typography.css) */
  font-size: clamp(34px, 5.2vw, 76px);
  line-height: 1.04;
  letter-spacing: var(--gs-track-tight);
  margin: 0;
  max-width: 20ch;
}
.case-hero__services {
  font-family: var(--gs-font-mono);
  font-size: 12px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--gs-ink-56);
  margin: 30px 0 0;
}
.case-hero__media { margin-top: 64px; }
.case-hero__media .slot { aspect-ratio: 16 / 9; }

.case-stats__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 44px; }
/* The results sit between the headline and the lead image, not in their own band. */
.case-hero__stats { margin-top: 64px; }
.case-stat__figure {
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular, optical weight (see typography.css) */
  font-size: clamp(34px, 4vw, 60px);
  line-height: 1;
  letter-spacing: var(--gs-track-tight);
  color: var(--gs-signal);
}
.case-stat__label { color: var(--gs-ink-56); margin-top: 16px; display: block; }

/* Source credit under a stat. Only used where a figure makes a competitive
   claim and needs to say who measured it. Hierarchy comes from size and case,
   not from fading the text out, so it stays readable. */
.case-stat__source {
  display: block;
  margin-top: 6px;
  font-family: var(--gs-font-mono);
  font-size: 11px;
  line-height: 1.4;
  color: var(--gs-ink-56);
  text-transform: none;
  letter-spacing: normal;
}

/* Client quote on a project page. The four original case pages had no quote
   because none of their source pages carried one. The ported FTS case studies
   do, and dropping it would lose the strongest thing on the page. Sits on paper
   between the story and the gallery, quieter than the orange testimonials band
   on the home page so it supports the case rather than competing with it. */
.case-quote { padding: 0 var(--gs-pad-x) 110px; }
.case-quote__inner { max-width: 860px; }
.case-quote__mark {
  font-family: var(--gs-font-display);
  font-size: 72px;
  line-height: .5;
  color: var(--gs-ink-24);
  margin-bottom: 20px;
}
.case-quote__body {
  font-family: var(--gs-font-display);
  font-weight: 400;
  font-size: clamp(21px, 2.4vw, 32px);
  line-height: 1.3;
  letter-spacing: -0.01em;
  margin: 0 0 12px;
}
.case-quote__attr { color: var(--gs-ink-56); margin-top: 26px; display: block; }

@media (max-width: 600px) {
  .case-quote { padding-bottom: 76px; }
  .case-quote__mark { font-size: 56px; }
}

/* Click-to-load video. privacy.html publicly states this site sets no tracking
   cookies and does not follow visitors to other sites. A normal YouTube or
   Instagram embed loads Google's or Meta's script on page load and does exactly
   that, so a plain <iframe> would make a published legal statement untrue.
   Instead the page ships a self-hosted poster frame and only creates the iframe
   after a deliberate click, at which point the visitor has chosen it. Also
   means no third-party JS on a page nobody watches the video on. */
.case-embed { padding: 0 var(--gs-pad-x) 110px; }
.case-embed__frame {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--gs-ink);
  overflow: hidden;
  cursor: pointer;
  border: 0;
  padding: 0;
  width: 100%;
  display: block;
}
.case-embed__frame img,
.case-embed__frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; display: block; border: 0; }
.case-embed__frame img { object-fit: cover; }
.case-embed__play {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 88px; height: 88px;
  border-radius: 50%;
  background: var(--gs-signal);
  display: grid;
  place-items: center;
  transition: transform .3s cubic-bezier(.2,.7,.3,1);
}
.case-embed__play::after {
  content: '';
  border-style: solid;
  border-width: 15px 0 15px 25px;
  border-color: transparent transparent transparent var(--gs-paper);
  margin-left: 6px;
}
.case-embed__frame:hover .case-embed__play { transform: translate(-50%, -50%) scale(1.08); }
.case-embed__frame:focus-visible { outline: 2px solid var(--gs-ink); outline-offset: 4px; }
/* Two films side by side. minmax(0, …) for the same reason the gallery needs
   it: a bare 1fr will not shrink below its content and blows the track out. */
.case-embed__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }
.case-embed__cap { color: var(--gs-ink-56); margin-top: 14px; display: block; }

/* Reels are shot vertical, so the frame is too. Capped and centred rather than
   stretched across a desktop column, where a 9:16 frame at full width would be
   taller than the viewport. */
.case-embed__reels { display: flex; gap: 24px; justify-content: center; flex-wrap: wrap; }
.case-embed__reel { width: 100%; max-width: 340px; }
.case-embed__reel .case-embed__frame { aspect-ratio: 9 / 16; }

/* Shared by: the outlets that picked the work up. Same greyscale-until-hover
   treatment as "Trusted by" on the home page, at about half the size, because
   these are a footnote to the result rather than a client list. */
.case-sharedby { padding: 0 var(--gs-pad-x) 110px; }
.case-sharedby .eyebrow { margin-bottom: 40px; }
.case-sharedby__row { display: flex; align-items: center; gap: 64px; flex-wrap: wrap; }
.case-sharedby__logo {
  width: 150px;
  aspect-ratio: 3 / 2;
  filter: grayscale(1);
  opacity: .5;
  transition: opacity var(--gs-duration-medium) var(--gs-ease-out),
              filter var(--gs-duration-medium) var(--gs-ease-out);
}
.case-sharedby__logo .slot { padding: 0; }

@media (hover: hover) {
  .case-sharedby__logo:hover { opacity: 1; filter: none; }
}

@media (max-width: 600px) {
  .case-sharedby { padding-bottom: 76px; }
  .case-sharedby__row { gap: 32px; justify-content: space-between; }
  .case-sharedby__logo { width: 92px; }
}

@media (max-width: 600px) {
  .case-embed { padding-bottom: 76px; }
  .case-embed__play { width: 64px; height: 64px; }
  .case-embed__play::after { border-width: 11px 0 11px 18px; }
}

.case-story { padding: 40px 48px 120px; }
.case-story .gs-2col { grid-template-columns: 1fr 1.1fr; gap: 64px; align-items: start; }
.case-story__h2 {
  font-family: var(--gs-font-display);
  font-weight: 500; /* PREVIEW: Trust 3A Medium, optical weight (see typography.css) */
  font-size: clamp(30px, 3.6vw, 52px);
  line-height: 1.06;
  letter-spacing: var(--gs-track-tight);
  margin: 0;
}
.case-story__body {
  font-size: clamp(17px, 1.5vw, 20px);
  line-height: 1.6;
  color: var(--gs-ink-72);
  margin: 0 0 20px;
}
.case-story__cta { display: inline-block; font-size: 15px; font-weight: 500; padding: 14px 30px; margin-top: 12px; }

.case-gallery { padding: 0 48px 130px; }
.case-gallery .eyebrow { margin-bottom: 44px; }
/* minmax(0, 1fr), not 1fr. A bare `1fr` is `minmax(auto, 1fr)`, so a track can
   refuse to shrink below its content's min-content size and blow past its
   share. With a full row of 4:5 portraits the columns resolved to 662px and
   1241px, the row height followed the wider one, and every figure rendered
   1552px tall, which pushed the gallery so far down the page it read as broken.
   Landscape rows never hit it, which is why it only showed up on the first
   gallery that opens with two portraits. minmax(0, …) lets the track shrink. */
.case-gallery__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }
.case-gallery__item { margin: 0; }
.case-gallery__item .slot { aspect-ratio: 3 / 2; }
/* Portrait frames keep their shape rather than being cropped to landscape.
   Rows are paired by orientation in the markup so heights stay even. */
.case-gallery__item--portrait .slot { aspect-ratio: 4 / 5; }
/* Wide frame for design work: brand bible pages, palettes and identity slides
   are 16:9 documents, not photographs. Cover-cropping one into a 3:2 frame
   slices the edge off the artwork, which on a branding case study removes the
   thing being shown. Matching the frame to the source makes the crop a no-op.
   Photography stays 3:2, where a crop costs nothing. */
.case-gallery__item--wide .slot { aspect-ratio: 16 / 9; }
/* Square source, usually a motion identity frame or a social crop. */
.case-gallery__item--square .slot { aspect-ratio: 1 / 1; }

/* "At a glance" facts on a project page, added 2026-09-28. Adam chose this
   treatment from three rendered prototypes ("b looks better"). One box, in the
   same place on every case study: stacked under the story heading in the left
   column, which was otherwise empty space beside the body copy. On a phone it
   follows the body, so the story still reads heading then copy.
   Fields: Client, Location, What we did, Results, Year. A field that cannot be
   sourced is left out rather than guessed, so some pages carry fewer rows. It
   is a real <dl> in the HTML, visible on load, so search and AI engines can
   lift the facts; keep "glance" in the class name, the structured-data
   generator looks for it. Labels borrow the Insights table heads
   (.article__table th) and the rules borrow its row borders. */
.case-story .gs-2col.case-story--glance { grid-template-columns: 1fr 1.1fr; grid-template-rows: auto 1fr; row-gap: 48px; }
.case-story--glance > .case-story__h2 { grid-column: 1; grid-row: 1; }
.case-story--glance > div { grid-column: 2; grid-row: 1 / span 2; }
.case-story--glance > .case-glance { grid-column: 1; grid-row: 2; }
.case-glance { margin: 0; max-width: 420px; }
.case-glance dt {
  padding-top: 16px;
  border-top: 1px solid var(--gs-ink-12);
  font-family: var(--gs-font-mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: var(--gs-track-eyebrow);
  text-transform: uppercase;
  color: var(--gs-grey-500);
}
.case-glance dd { margin: 8px 0 20px; font-size: 1.05rem; line-height: 1.5; color: var(--gs-ink); }
.case-glance__results { list-style: none; margin: 0; padding: 0; }
.case-glance__results li + li { margin-top: 4px; }
@media (max-width: 900px) {
  .case-story .gs-2col.case-story--glance { grid-template-columns: 1fr; grid-template-rows: none; }
  .case-story--glance > .case-story__h2,
  .case-story--glance > div,
  .case-story--glance > .case-glance { grid-column: 1; grid-row: auto; }
}

/* -------------------------------------------------------------------- CTA */

.cta {
  min-height: 100vh;
  background: var(--gs-signal);
  color: var(--gs-ink);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 120px 32px;
}
.cta__inner { display: flex; flex-direction: column; align-items: center; }
.cta__mark { height: 84px; margin-bottom: 44px; }
.cta__h2 {
  font-family: var(--gs-font-display);
  font-weight: 300; /* PREVIEW: Trust 3A Light, optical weight (see typography.css) */
  font-size: clamp(44px, 6.6vw, 112px);
  line-height: .98;
  letter-spacing: var(--gs-track-tight);
  margin: 0 0 52px;
  max-width: 15ch;
}
.cta__actions { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; justify-content: center; }
.cta__actions .gs-btn { font-size: 17px; }
.cta__actions .gs-btn-onsignal { font-weight: 600; padding: 17px 34px; }
.cta__actions .gs-btn-outline { font-weight: 500; padding: 16px 33px; }

/* ----------------------------------------------------------------- footer */

.footer {
  background: var(--gs-signal);
  padding: 36px 48px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}
.footer img { height: 18px; }
.footer__right { display: flex; align-items: center; gap: 28px; flex-wrap: wrap; row-gap: 16px; }
/* Country acknowledgement. Full width beneath the row, quieter than the rest. */
.footer__ack {
  flex-basis: 100%;
  margin: 26px 0 0;
  max-width: 62ch;
  font-size: 13px;
  line-height: 1.55;
  color: var(--gs-ink-72);
}
.footer__contact {
  font-family: var(--gs-font-mono);
  font-size: 12px;
  letter-spacing: .04em;
  color: var(--gs-ink-72);
}
.footer__socials { display: flex; align-items: center; gap: 16px; }

/* -------------------------------------------------------------- quote tab */

.gs-quotetab {
  position: fixed;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  z-index: 60;
  writing-mode: vertical-rl;
  background: var(--gs-signal);
  color: var(--gs-ink);
  font-family: var(--gs-font-mono);
  font-weight: 500;
  text-transform: uppercase;
  font-size: 12px;
  letter-spacing: .12em;
  padding: 22px 11px;
  border: none;
  cursor: pointer;
  border-radius: 6px 0 0 6px;
  transition: background var(--gs-duration-micro) var(--gs-ease-out);
}
.gs-quotetab:hover { background: var(--gs-signal-ink); }
/* Over the orange beds (hero, testimonials, CTA) the signal plate vanishes
   into the background, so it flips to ink. Toggled in site.js. */
.gs-quotetab[data-onsignal] { background: var(--gs-ink); color: var(--gs-paper); }
.gs-quotetab[data-onsignal]:hover { background: var(--gs-grey-600); }

/* ----------------------------------------------------------------- modals */

.gs-overlay {
  position: fixed;
  inset: 0;
  z-index: 120;
  background: rgba(14, 15, 17, .55);
  display: flex;
  justify-content: center;
  padding: 24px;
}
.gs-overlay[hidden] { display: none; }
.gs-overlay--center { align-items: center; }
.gs-overlay--top { align-items: flex-start; overflow: auto; }

.gs-modal {
  background: var(--gs-paper);
  color: var(--gs-ink);
  width: 100%;
  padding: 40px;
  box-shadow: var(--gs-shadow-1);
}
.gs-modal--quote {
  max-width: 540px;
  max-height: 90vh;
  /* vh is the address-bar-hidden height on mobile, so 90vh overflows the frame
     you can actually see. svh is the visible height; less the overlay padding. */
  max-height: calc(100svh - 48px);
  overflow: auto;
}
.gs-modal--project { max-width: 560px; margin: auto; }

.gs-modal__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 28px;
}
.gs-modal__head img { height: 16px; display: block; margin-bottom: 10px; }
.gs-modal__head h3 {
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular, optical weight (see typography.css) */
  font-size: 32px;
  margin: 0 0 6px;
}
.gs-modal__head p { font-size: 14px; color: var(--gs-ink-72); margin: 0; }
.gs-modal__close {
  background: none;
  border: none;
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
  color: var(--gs-ink);
  padding: 0;
}

.gs-modal__group { color: var(--gs-grey-500); margin-bottom: 14px; }
.gs-modal__group--tight { margin-bottom: 8px; }
.gs-modal__chips { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 28px; }
.gs-modal__chips--tight { margin-bottom: 26px; }

.gs-chip {
  font-family: var(--gs-font-body);
  font-size: 14px;
  padding: 9px 16px;
  border-radius: var(--gs-radius-pill);
  border: 1px solid var(--gs-ink-24);
  background: transparent;
  color: var(--gs-ink);
  cursor: pointer;
  transition: background var(--gs-duration-micro) var(--gs-ease-out),
              color var(--gs-duration-micro) var(--gs-ease-out);
}
.gs-chip[aria-pressed="true"] { background: var(--gs-ink); color: var(--gs-paper); }

.gs-field {
  width: 100%;
  font-family: var(--gs-font-body);
  font-size: 15px;
  padding: 12px 14px;
  border: 1px solid var(--gs-ink-24);
  border-radius: var(--gs-radius-sm);
  background: var(--gs-white);
  color: var(--gs-ink);
  margin-bottom: 12px;
}
.gs-field:focus { outline: 2px solid var(--gs-signal); outline-offset: 2px; }
.gs-field--gap { margin-bottom: 22px; }
/* Honeypot for bots, see api/lead.mjs. Off-screen rather than display:none,
   because some bots skip fields that are display:none. */
.gs-trap { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }
/* A textarea inherits none of the input sizing, and defaults to a baseline
   offset and horizontal-only resize that break the field rhythm. */
.gs-field--area {
  display: block;
  line-height: 1.45;
  resize: vertical;
  min-height: 84px;
}
/* "Where did you find us?" is the only select. It keeps .gs-field's box, and
   drops the native control chrome, which ignores padding in Safari. While the
   empty "(optional)" option is chosen it reads as a placeholder. */
select.gs-field {
  -webkit-appearance: none;
  appearance: none;
  padding-right: 38px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%230e0f11' stroke-width='1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  cursor: pointer;
}
select.gs-field:has(option[value=""]:checked) { color: var(--gs-ink-56); }
select.gs-field option { color: var(--gs-ink); }
/* Visually hidden, still read out. Used for the select's label, since every
   other field here is labelled by its placeholder. */
.gs-vh {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.gs-formrow { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.gs-formrow .gs-field { margin-bottom: 12px; }
.gs-formrow--gap { margin-bottom: 14px; }

.gs-estimate {
  border: 1px solid var(--gs-ink-12);
  padding: 18px 20px;
  margin-bottom: 10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
}
.gs-estimate > span:first-child { color: var(--gs-grey-500); }
/* The full price struck through and the discount under the estimate, so the
   saving is shown rather than quietly applied (Adam, 2026-10-08). */
.gs-estimate__figures { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; text-align: right; }
.gs-estimate__deal { font-size: 13px; color: var(--gs-grey-500); }
.gs-estimate__deal s { color: var(--gs-ink-72); }
.gs-estimate__deal[hidden] { display: none; }
/* The questions that follow the engagement and services (PREVIEW 2026-10-08):
   asked as plain sentences, answered with one tap, all optional. */
.gs-qq:empty { display: none; }
.gs-qq__q { font-size: 15px; line-height: 1.35; color: var(--gs-ink); margin: 0 0 10px; }
.gs-qq .gs-modal__chips { margin-bottom: 22px; }
.gs-qq .gs-chip { font-size: 14px; }
.gs-fineprint--flag { color: var(--gs-ink-72); margin-bottom: 6px; }
.gs-fineprint--flag[hidden] { display: none; }
.gs-estimate__value {
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular, optical weight (see typography.css) */
  font-size: 26px;
  color: var(--gs-signal);
}

.gs-fineprint { font-size: 12px; color: var(--gs-grey-500); margin: 0 0 28px; }
.gs-fineprint--center { text-align: center; margin: 16px 0 0; }
.gs-note { font-size: 13px; color: var(--gs-ink-72); margin: 0 0 14px; }

.gs-submit { width: 100%; font-size: 16px; font-weight: 600; padding: 15px; }

.gs-sent { text-align: center; padding: 32px 0; }
.gs-sent[hidden] { display: none; }
.gs-sent img { height: 72px; margin-bottom: 24px; }
.gs-sent h3 {
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular, optical weight (see typography.css) */
  font-size: 34px;
  margin: 0 0 12px;
}
.gs-sent p { color: var(--gs-ink-72); margin: 0 0 28px; font-size: 16px; }
.gs-sent .gs-btn { font-size: 15px; font-weight: 600; padding: 13px 28px; }
.gs-form[hidden] { display: none; }

/* Opening beat: the mark holds for a moment, shrinks to nothing, then the form
   fades up. Overlaid rather than swapped in, so the modal keeps the form's
   height throughout and never resizes mid-animation. */
.gs-modal__loader {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--gs-paper);
  opacity: 0;
  visibility: hidden;
  transition: opacity 200ms var(--gs-ease-out), visibility 0s linear 200ms;
}
/* Collapsed is the resting state, so once the mark shrinks it stays shrunk;
   were it the other way round it would scale back up as the loader fades. */
.gs-modal__loader img {
  height: 76px;
  transform: scale(0);
  opacity: 0;
  transition: transform 180ms var(--gs-ease-out), opacity 180ms var(--gs-ease-out);
}
.gs-modal { position: relative; }
.gs-modal[data-loading] .gs-modal__loader {
  opacity: 1;
  visibility: visible;
  transition: opacity 120ms var(--gs-ease-out), visibility 0s;
}
/* First beat: the mark is simply there, no entrance. Only the collapse animates. */
.gs-modal[data-loading=""] .gs-modal__loader img {
  transform: none;
  opacity: 1;
  transition: none;
}
.gs-modal .gs-form { transition: opacity 260ms var(--gs-ease-out); }
.gs-modal[data-loading] .gs-form { opacity: 0; }
/* The project modal runs taller than the viewport, so centring the mark inside
   it would drop it below the fold. Hold it near the top instead. */
.gs-modal--project .gs-modal__loader { align-items: flex-start; padding-top: clamp(110px, 28vh, 300px); }

/* ------------------------------------------------------------ image slots */

/* Placeholder until a real file lands in assets/img/. Drop the named file in
   and it replaces the label automatically. See README.md. */
.slot {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;
  text-align: center;
  font-family: var(--gs-font-mono);
  font-size: 11px;
  letter-spacing: var(--gs-track-eyebrow);
  text-transform: uppercase;
  color: var(--gs-grey-400);
  background: rgba(127, 127, 127, .08);
  overflow: hidden;
}
/* Once the real file lands, drop the caption and the placeholder tint. A
   transparent logo would otherwise sit on top of its own label. The alt text
   carries the name from here. */
.slot[data-loaded] { background: none; }
.slot[data-loaded] > span { display: none; }
.slot img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
.slot[data-fit="cover"] img { object-fit: cover; }
.slot[data-fit="contain"] img { object-fit: contain; padding: 14%; }

/* Monogram fallback for a person whose photo has not landed yet. Reads as an
   intentional tile, not a missing asset. Replaced automatically by the real
   photo once the file exists. */
.slot[data-empty] {
  background: var(--gs-grey-100);
  border: 1px solid var(--gs-ink-08);
  container-type: inline-size;
}
.slot[data-empty] > span:not(.slot__mono) { display: none; }
.slot__mono {
  font-family: var(--gs-font-display);
  font-weight: 700;
  font-size: clamp(1.75rem, 22cqw, 5.5rem);
  line-height: 1;
  letter-spacing: .01em;
  text-transform: uppercase;
  color: var(--gs-ink-24);
}

/* Shown only when a submission actually failed. */
.gs-formerror {
  margin: 0 0 12px;
  padding: 11px 14px;
  border: 1px solid var(--gs-signal);
  background: rgba(220, 112, 52, .07);
  font-size: .92rem;
  line-height: 1.5;
  color: var(--gs-signal-ink);
}

/* --------------------------------------------------------- legal / privacy */

.legal {
  background: var(--gs-paper);
  padding: 140px var(--gs-pad-x) 110px;
}
.legal__wrap { max-width: 760px; }
.legal__h1 {
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular, optical weight (see typography.css) */
  font-size: clamp(2.4rem, 5vw, 3.6rem);
  line-height: 1.04;
  letter-spacing: -.02em;
  color: var(--gs-ink);
  margin: 10px 0 6px;
}
.legal__meta {
  font-family: var(--gs-font-mono);
  font-size: 12px;
  letter-spacing: var(--gs-track-eyebrow);
  text-transform: uppercase;
  color: var(--gs-grey-500);
  margin-bottom: 38px;
}
.legal__lead {
  font-size: 1.22rem;
  line-height: 1.6;
  color: var(--gs-ink);
  padding-bottom: 34px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--gs-ink-12);
}
.legal__h2 {
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular, optical weight (see typography.css) */
  font-size: 1.32rem;
  letter-spacing: -.01em;
  color: var(--gs-ink);
  margin: 40px 0 12px;
}
.legal__body, .legal__list {
  font-size: 1.02rem;
  line-height: 1.68;
  color: var(--gs-grey-600);
}
.legal__body { margin-bottom: 14px; }
.legal__list { margin: 0 0 16px; padding-left: 22px; }
.legal__list li { margin-bottom: 10px; }
.legal__list li strong, .legal__body strong { color: var(--gs-ink); font-weight: 500; }
.legal a { color: var(--gs-signal-ink); text-decoration: underline; text-underline-offset: 3px; }
.legal a:hover { color: var(--gs-ink); }
.legal .case-back { display: inline-block; margin-bottom: 26px; }

.footer__legal {
  font-family: var(--gs-font-mono);
  font-size: 11px;
  letter-spacing: var(--gs-track-eyebrow);
  text-transform: uppercase;
  color: var(--gs-grey-500);
  text-decoration: none;
}
.footer__legal:hover { color: var(--gs-ink); }

@media (max-width: 900px) {
  .legal { padding: 104px var(--gs-pad-x) 76px; }
}

/* ------------------------------------------------------------- responsive */

@media (max-width: 900px) {
  .gs-progress { display: none; }
  .gs-team-grid { grid-template-columns: repeat(2, 1fr); }
  .gs-svc-panel, .gs-faq-panel { display: none; }
  .gs-svc.is-open .gs-svc-desc, .gs-faqi.is-open .gs-faq-desc { display: block; }
  /* Too prominent on a phone, and the nav's "Start a project" covers the same
     job. Hidden here; the in-section CTA under Consulting still offers pricing. */
  .gs-quotetab { display: none; }
  /* Give the philosophy rows a thumb-sized tap target. */
  .gs-faq { padding: 6px 0; }
  /* .services/.testi/.faq each set their own columns, so the plain .gs-2col
     override below loses on specificity. Match it to actually collapse. */
  .gs-2col,
  .services .gs-2col,
  .testi .gs-2col,
  .faq .gs-2col,
  .case-story .gs-2col { grid-template-columns: 1fr; gap: 44px; }
  /* Four stat columns cannot fit a phone: the last one lands off-canvas and
     body overflow-x:hidden clips it away entirely. */
  .case-stats__grid { grid-template-columns: repeat(2, 1fr); gap: 32px 24px; }
  .gs-grid-4 { grid-template-columns: repeat(2, 1fr); gap: 28px; }
  .gs-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px; }
  /* Two cards sit side by side on a phone, and the longer client names wrap to
     a second line while their neighbour stays on one. That pushed the big
     figure 13px out of line with the card beside it, on 3 of the 5 rows on
     /work. Reserving two lines keeps every figure on the same baseline. Not
     needed above 600px: measured at 900 and 1400, every label fits one line and
     nothing misaligns, so the space is not spent where it buys nothing. */
  .gs-case__client { min-height: 2.3em; }
  .gs-grid-6 { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 600px) {
  /* Each of these sets its own class-level padding, which outranks a bare
     `section` selector no matter what the media query says. Named to match.
     The hero, bignum & cta are centred and already sit at 32px, so they keep
     their own rhythm. */
  .services, .work, .testi, .faq, .trusted, .team {
    padding-top: 76px;
    padding-bottom: 76px;
    padding-left: 22px;
    padding-right: 22px;
  }
  .gs-nav { padding-left: 22px; padding-right: 22px; }
  /* Heavier bottom padding lifts the centred stack clear of the scroll cue.
     The offset from true centre is (padding-top - padding-bottom) / 2. */
  /* Same tightened top as desktop, scaled to the mobile rhythm. */
  .services { padding-top: 38px; }
  /* Project pages: match the section padding used everywhere else on a phone,
     and let the gallery run one image wide so nothing is postage-stamp sized. */
  .case-hero { padding: 96px 22px 0; }
  .case-story { padding: 32px 22px 76px; }
  .case-gallery { padding: 0 22px 90px; }
  .case-gallery__grid { grid-template-columns: 1fr; gap: 16px; }
  .case-embed__grid { grid-template-columns: 1fr; gap: 28px; }
  .case-hero__media { margin-top: 44px; }
  .case-hero__stats { margin-top: 44px; }
  .case-back { margin-bottom: 32px; }
  .gs-formrow { grid-template-columns: 1fr; }
  .gs-h1 { font-size: clamp(28px, 7.5vw, 92px); }
  .gs-modal { padding: 26px 22px; }
  .gs-team-grid { gap: 24px 14px; }
  .gs-teamcard__row { flex-direction: column; gap: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  [data-reveal] { opacity: 1; transform: none; }
}

/* -------------------------------------------------------- insights articles */

/* Long-form reading column for /insights articles, added 2026-09-28. Headings,
   paragraphs and lists reuse the privacy page's .legal__h2 / __lead / __body /
   __list, so the only new pieces are the section padding, the reading measure
   (kept near 70 characters a line), links, the FAQ h3 and the price tables. */
.article { padding: 64px var(--gs-pad-x) 110px; }
.article__body { max-width: 680px; margin: 0 auto; }
/* Adam, 2026-09-28: the 31em column read as squashed on desktop. Wider column,
   larger body text so lines stay near 70 characters, and more air between
   paragraphs, sections and tables. Scoped to .article so the privacy page is
   unchanged. */
.article .legal__lead { margin-bottom: 36px; }
.article .legal__body,
.article .legal__list { font-size: 1.14rem; line-height: 1.75; }
.article .legal__body { margin-bottom: 22px; }
.article .legal__list li { margin-bottom: 12px; }
.article .legal__h2 { margin-top: 64px; margin-bottom: 18px; }
.article .article__table-wrap { margin: 16px 0 36px; }
.article .article__table { font-size: 1rem; }
.article__h3 { margin-top: 36px; }
.article__body a { color: var(--gs-signal-ink); text-decoration: underline; text-underline-offset: 3px; }
.article__body a:hover { color: var(--gs-ink); }
.article__body .case-story__cta { margin: 4px 0 8px; }
.article__h3 {
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular, optical weight (see typography.css) */
  font-size: 1.12rem;
  line-height: 1.3;
  color: var(--gs-ink);
  margin: 28px 0 8px;
}
.article__table-wrap { overflow-x: auto; margin: 8px 0 24px; }
.article__table { width: 100%; border-collapse: collapse; font-size: .95rem; line-height: 1.5; color: var(--gs-grey-600); }
.article__table th,
.article__table td { text-align: left; vertical-align: top; padding: 12px 16px 12px 0; border-bottom: 1px solid var(--gs-ink-12); }
.article__table th {
  font-family: var(--gs-font-mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: var(--gs-track-eyebrow);
  text-transform: uppercase;
  color: var(--gs-grey-500);
}
.article__table td:first-child { color: var(--gs-ink); }
@media (max-width: 900px) {
  .article { padding: 44px var(--gs-pad-x) 76px; }
}

/* ============================================================ PREVIEW

   2026-10-06. Sovereign Blue replaces the orange as the one colour. Every
   section that set ink on the orange measures 1.3:1 on the blue, so here they
   flip to paper. Kept as one block so the change is easy to read and easy to
   fold back into the rules above if Adam approves it.

   Everything that sits ON the signal colour reads these tokens, so the accent
   can change in one place. Blue for the whole site, Adam's call 2026-10-06;
   the orange and sash-red colourways were tried and set aside. */

:root {
  --on-signal: var(--gs-paper);
  --on-signal-muted: var(--gs-paper-72);
  --on-signal-24: var(--gs-paper-24);
  --on-signal-hover: var(--gs-white);
  --on-signal-logo: invert(1);
  --signal-glow: rgba(110, 130, 255, .28);
}

.gs-btn-ink:hover { background: var(--gs-signal); color: var(--on-signal); }
.gs-btn-outline:hover { background: var(--gs-ink); color: var(--gs-paper); }

.testi, .testi__quote, .testi__who { color: var(--on-signal); }
.testi__item { border-left-color: var(--on-signal-24); }

.cta { color: var(--on-signal); }
/* The CTA mark is the silver token now, which reads on the blue as it is. */
.cta .gs-btn-onsignal { background: var(--on-signal); color: var(--gs-signal); }
.cta .gs-btn-onsignal:hover { background: var(--on-signal-hover); color: var(--gs-signal); }
.cta .gs-btn-outline { color: var(--on-signal); border-color: var(--on-signal); }
.cta .gs-btn-outline:hover { background: var(--on-signal); color: var(--gs-signal); }

.footer { color: var(--on-signal); }
.footer img { filter: var(--on-signal-logo); }
.footer a { color: inherit; }
.footer__ack, .footer__contact { color: var(--on-signal-muted); }

.gs-quotetab { color: var(--on-signal); }
.gs-quotetab[data-onsignal] { background: var(--on-signal); color: var(--gs-signal); }
.gs-quotetab[data-onsignal]:hover { background: var(--on-signal-hover); }

/* Over the blue hero: the logo goes light and the ink nav pill turns paper,
   since ink on Sovereign Blue is 1.3:1 and the pill's edge would vanish. */
.gs-nav[data-onhero] .gs-nav__logo img { filter: var(--on-signal-logo); }
.gs-nav[data-onhero] .gs-nav__cta { background: var(--on-signal); color: var(--gs-signal); }
.gs-nav[data-onhero] .gs-nav__cta:hover { background: var(--on-signal-hover); color: var(--gs-signal); }

.gs-nav__logo { flex-shrink: 0; }

/* Visually hidden, still read by screen readers and search. */
.gs-sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* ============================================ PREVIEW: brand sections (2, 3)
   2026-10-06. Two short sections between the hero and What We Do: the name
   and the promise in it, then the four words. Paper ground, the colourway
   colour for the big type and the token, so both follow the switch. */
/* Section 2: brand philosophy, after the deck slide. A deeper room than the
   hero: the same blue with pools of brighter light and a blurred Godspeed bolt
   drifting in the corner, kept clear of the text so it stays legible. */
.philo {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  color: var(--on-signal);
  background:
    radial-gradient(60% 70% at 8% 100%, rgba(52, 104, 255, .55), transparent 70%),
    radial-gradient(45% 55% at 100% 0%, rgba(52, 104, 255, .45), transparent 70%),
    linear-gradient(180deg, #02066F 0%, #010447 55%, #02066F 100%);
  padding: clamp(80px, 9vw, 140px) var(--gs-pad-x) clamp(96px, 11vw, 170px);
}
/* The blurred bolts. The blur sits on the wrapper and the mask on the child:
   a browser masks AFTER it filters, so blur and mask on one element give a
   crisp shape. */
.philo__glow { position: absolute; z-index: -1; filter: blur(28px); opacity: .5; pointer-events: none; }
.philo__glow i {
  display: block; width: 100%; height: 100%;
  background: #3F7BFF;
  -webkit-mask: url(assets/logos/godspeed-mark.png) center / contain no-repeat;
          mask: url(assets/logos/godspeed-mark.png) center / contain no-repeat;
}
.philo__glow--a { width: clamp(260px, 34vw, 520px); aspect-ratio: 1; left: -8%; bottom: -22%; transform: rotate(-18deg); }
.philo__glow--b { width: clamp(140px, 16vw, 240px); aspect-ratio: 1; right: 4%; top: 6%; transform: rotate(24deg); opacity: .4; }
.philo__inner { max-width: var(--gs-content-max); margin: 0 auto; }
/* The statement: four sentences, one per line, big enough to read from across
   the room. The first is the thesis, so it is the largest. */
.philo__statement p {
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular */
  font-size: clamp(26px, 3.3vw, 50px);
  line-height: 1.18;
  letter-spacing: -.02em;
  color: var(--on-signal);
  max-width: 24ch;
  margin: 0 0 .7em;
  text-wrap: balance;
}
.philo__statement p:last-child { margin-bottom: 0; }
.philo__statement .philo__lead {
  font-weight: 300; /* PREVIEW: Trust 3A Light at 90px+, optical weight */
  font-size: clamp(40px, 6vw, 96px);
  line-height: 1;
  letter-spacing: var(--gs-track-display);
  max-width: 15ch;
  margin-bottom: clamp(36px, 4vw, 64px);
}
.philo__h2 {
  font-family: var(--gs-font-display);
  font-weight: 400; /* PREVIEW: Trust 3A Regular at 57-76px, optical weight */
  font-size: clamp(40px, 5vw, 72px);
  line-height: 1;
  letter-spacing: var(--gs-track-display);
  margin: 0 0 clamp(56px, 7vw, 110px);
}
.philo__flow {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(48px, 9vw, 150px);
  text-align: center;
  font-family: var(--gs-font-display);
}
.philo__step { position: relative; }
/* The arrow between steps sits in the gap, pointing on to the next. */
.philo__step:not(:last-child)::after {
  content: "";
  position: absolute;
  top: calc(clamp(24px, 2.3vw, 34px) * .62 - 5px); /* level with the middle of the label line */
  left: calc(100% + clamp(10px, 1.4vw, 24px));
  width: calc(clamp(48px, 9vw, 150px) - 2 * clamp(10px, 1.4vw, 24px));
  height: 10px;
  background:
    linear-gradient(currentColor, currentColor) left center / calc(100% - 8px) 1.5px no-repeat,
    conic-gradient(from 225deg at 100% 50%, currentColor 0 90deg, transparent 0) right center / 9px 10px no-repeat;
  opacity: .9;
}
.philo__k { margin: 0; font-weight: 500; font-size: clamp(24px, 2.3vw, 34px); line-height: 1.15; letter-spacing: -.01em; }
.philo__k span { display: block; font-weight: 300; font-size: .72em; color: var(--on-signal-muted); margin-top: 4px; }
.philo__v { margin: clamp(16px, 1.8vw, 26px) 0 0; font-weight: 400; font-size: clamp(19px, 1.7vw, 25px); line-height: 1.45; color: var(--on-signal); }

@media (max-width: 820px) {
  .philo__flow { grid-template-columns: 1fr; gap: 76px; }
  .philo__step:not(:last-child)::after {
    top: auto; bottom: -52px; left: 50%;
    width: 10px; height: 34px; transform: translateX(-50%);
    background:
      linear-gradient(currentColor, currentColor) center top / 1.5px calc(100% - 8px) no-repeat,
      conic-gradient(from 135deg at 50% 100%, currentColor 0 90deg, transparent 0) center bottom / 10px 9px no-repeat;
  }
}

.gets { padding: clamp(80px, 9vw, 140px) var(--gs-pad-x) clamp(72px, 8vw, 130px); }
.gets__title { max-width: var(--gs-content-max); margin: 0 auto clamp(28px, 3vw, 44px); font-weight: inherit; }
.gets__list { max-width: var(--gs-content-max); margin: 0 auto; }
.gets__row {
  --gets-t: clamp(48px, 7.4vw, 116px); /* the word's size, shared with the mid-width column below */
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  gap: clamp(20px, 6vw, 110px);
  align-items: center; /* the line sits on the middle of its word (Adam, 8 Oct; was baseline) */
  padding: clamp(14px, 1.6vw, 22px) 0;
  border-top: 1px solid var(--gs-ink-12);
}
.gets__row:last-child { border-bottom: 1px solid var(--gs-ink-12); }
.gets__row dt {
  font-family: var(--gs-font-display);
  font-weight: 300; /* PREVIEW: Trust 3A Light at 90px+, optical weight */
  font-size: var(--gets-t);
  line-height: .95;
  letter-spacing: var(--gs-track-display);
  color: var(--gs-signal);
}
.gets__row dd { margin: 0; font-size: clamp(17px, 1.45vw, 21px); line-height: 1.45; color: var(--gs-ink-72); white-space: nowrap; } /* one line each on desktop (Adam, 8 Oct) */

/* Below 1180px the line no longer fits its 1fr column on one line, while the
   words never fill theirs: size the word column to the widest word ("Confidence."
   is 4.64em) and give the line the rest. That holds down to about 880px; below
   it the rows stack, word above line. */
@media (max-width: 1179px) {
  .gets__row { grid-template-columns: calc(var(--gets-t) * 4.75) minmax(0, 1fr); }
}
@media (max-width: 880px) {
  .gets__row { grid-template-columns: 1fr; gap: 8px; align-items: start; }
  .gets__row dt { font-weight: 400; } /* under ~60px Light reads thin */
  .gets__row dd { white-space: normal; max-width: 32ch; } /* stacked on a phone, so the line may wrap */
}

/* ================================= PREVIEW: manuscript quotes (2026-10-06)
   "What they say" as each client's full words, set like a page from an old
   book: IM Fell English, and a blackletter first letter three lines tall in
   silver, after the frame on the hero painting. No paper behind them. */
.testi__notes {
  max-width: var(--gs-content-max);
  margin: 0 auto;
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  outline: none;
}
.testi__notes::-webkit-scrollbar { display: none; }
.testi__notes:focus-visible { outline: 2px solid var(--on-signal); outline-offset: 6px; }
/* Each note is one full-width page; the track takes the tallest note's height,
   so moving between short and long quotes never changes the section. */
.testi__note { flex: 0 0 100%; scroll-snap-align: start; scroll-snap-stop: always; padding: 0 var(--gs-pad-x); margin: 0; box-sizing: border-box; }
/* Arrows sit beside the heading, so they stay next to the quote however long
   it runs (the track holds the tallest quote's height). */
.testi__lede { display: flex; justify-content: space-between; align-items: flex-start; gap: 24px; }
.testi__browse { display: flex; align-items: center; gap: 14px; color: var(--on-signal); margin-top: clamp(6px, 1.2vw, 18px); flex-shrink: 0; }
.testi__note blockquote { margin: 0; }
.testi__note p {
  font-family: 'IM Fell English', Georgia, serif;
  font-size: clamp(24px, 2.7vw, 42px);
  line-height: 1.36;
  color: var(--on-signal);
  margin: 0;
  max-width: 36ch;
}
.testi__note p::first-letter {
  float: left;
  font-family: 'UnifrakturMaguntia', 'IM Fell English', serif;
  font-size: 3.55em;
  line-height: .8;
  margin: .07em .12em 0 0;
  color: #C9CCD3;
}
.testi__note figcaption { margin-top: 18px; display: flex; flex-direction: column; gap: 2px; }
/* The chrome capital (PREVIEW 2026-10-08): an image in place of the flat
   blackletter, floated and sized like it, three lines deep. */
.testi__note p.has-cap::first-letter { float: none; font-family: inherit; font-size: inherit; line-height: inherit; margin: 0; color: inherit; }
.testi__cap { float: left; width: auto; height: 3.5em; margin: .08em .14em 0 -.04em; }
.testi__cap img { display: block; height: 100%; width: auto; filter: drop-shadow(0 6px 10px rgba(0, 0, 0, .35)); }
/* The small-caps face loads with the quotes, not on arrival (speed pass, 15 Oct). */
.gs-near .testi__note .testi__who { font-family: 'IM Fell English SC', Georgia, serif; }
.testi__note .testi__who {
  font-family: Georgia, serif;
  font-size: clamp(17px, 1.3vw, 20px);
  letter-spacing: .06em;
  text-transform: none;
  color: var(--on-signal);
  white-space: normal;
}
.testi__org { font-family: 'IM Fell English', Georgia, serif; font-style: italic; font-size: clamp(15px, 1.15vw, 17px); color: var(--on-signal-muted); }
/* Same as the lede: on a phone .testi itself carries the 22px gutter, so the
   notes and arrows drop their own or it doubles up. */
@media (max-width: 600px) { .testi__note { padding-left: 0; padding-right: 0; } }

/* ================ PREVIEW: the statement, Adam's pick C (2026-10-06)
   One continuous paragraph on flat Sovereign Blue with a fine grain and one big
   blurred bolt. Each word starts in soft silver and turns full white in turn as
   the paragraph scrolls through the screen (site.js wraps the words and sets
   data-lit). Without JS, or with reduced motion, it is simply all white. */
.philo {
  background:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 .07 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>"),
    #02066F;
}
.philo__glow--b { display: none; }
.philo__glow--a { left: auto; right: -6%; bottom: auto; top: 10%; width: clamp(320px, 42vw, 640px); transform: rotate(14deg); opacity: .42; }
.philo__statement { max-width: 21em; margin: 0; font-size: clamp(28px, 4.1vw, 66px); }
.philo__statement p,
.philo__rest { display: inline; }
.philo__statement p { font-size: 1em; line-height: 1.16; font-weight: 300; letter-spacing: -.03em; max-width: none; margin: 0; color: rgba(248, 244, 239, .58); }
.philo__statement .philo__lead { font-size: 1em; line-height: 1.16; max-width: none; margin: 0; color: var(--on-signal); }
.philo__statement p::after { content: " "; }

.philo__statement .philo__lead { color: var(--on-signal); }
.philo__statement p { color: var(--on-signal); }
.philo__statement[data-scrub] p,
.philo__statement[data-scrub] .philo__lead { color: inherit; }
.philo__statement[data-scrub] .w { color: rgba(248, 244, 239, .42); transition: color 260ms cubic-bezier(.4, 0, .2, 1); }
.philo__statement[data-scrub] .w[data-lit] { color: var(--on-signal); }

/* The token, small, above the statement: the blessing and the promise, sealed.
   Moved here from above the hero line, Adam's call 2026-10-06. */
.philo__token { display: block; width: clamp(38px, 3.2vw, 50px); height: auto; margin: 0 0 clamp(22px, 2.4vw, 34px); }

/* ================= PREVIEW: the big number, silver on blue (2026-10-06)
   Adam picked B of three: the figure cast in the frame's brushed silver, huge
   and left aligned on flat blue with grain and one blurred bolt, the words in a
   column beside it. (A, the token in a spotlight, and C, the figure struck in a
   coin with an inscription round the rim, were set aside.) */
.bignum { color: var(--gs-paper); }
.bignum__label { color: var(--gs-paper-72); }
.gs-cue span { background: linear-gradient(to bottom, transparent, var(--gs-paper-24)); }
.bignum {
  background:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 .07 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>"),
    #02066F;
  justify-content: flex-start; text-align: left; padding-left: var(--gs-pad-x); padding-right: var(--gs-pad-x);
}
.bignum__glow { display: block; position: absolute; left: -10%; bottom: -30%; width: clamp(360px, 48vw, 720px); aspect-ratio: 1; filter: blur(30px); opacity: .4; transform: rotate(-16deg); pointer-events: none; }
.bignum__glow i { display: block; width: 100%; height: 100%; background: #3F7BFF; -webkit-mask: url(assets/logos/godspeed-mark.png) center / contain no-repeat; mask: url(assets/logos/godspeed-mark.png) center / contain no-repeat; }
.bignum__inner {
  width: 100%; max-width: var(--gs-content-max); margin: 0 auto;
  display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: clamp(28px, 5vw, 90px); align-items: end;
}
.bignum__figure {
  grid-row: 1 / span 2; font-size: clamp(84px, 17vw, 270px); font-weight: 300; line-height: .82;
  background: linear-gradient(135deg, #F5F6F8 0%, #B6B9BF 18%, #EDEFF2 34%, #8E9198 52%, #E2E4E8 68%, #A3A6AD 84%, #F2F3F5 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.bignum__label { margin: 0 0 12px; align-self: end; }
.bignum__note { margin: 0; max-width: 26ch; font-size: clamp(18px, 1.6vw, 24px); line-height: 1.4; color: var(--gs-paper); } /* Geist, not Trust: Trust's 1 reads as a capital I in "1M+" */
.gs-cue { left: var(--gs-pad-x); transform: none; }

@media (max-width: 820px) {
  .bignum__inner { grid-template-columns: 1fr; row-gap: 18px; }
  .bignum__figure { grid-row: auto; }
}


/* ============== PREVIEW 2026-10-08: blending sections (?blend=, see site.js)
   The edges sit in the background layer (negative z-index inside an isolated
   section, like the backgrounds kit), so text always stays on top. */
.blend-host { position: relative; isolation: isolate; }
.blend-edge { position: absolute; left: 0; right: 0; z-index: -1; pointer-events: none; }
.blend-edge--bottom { bottom: 0; }
.blend-edge--top { top: 0; }
/* Dark to paper through a band of Sovereign Blue and pale first light, like
   the Void horizon, rather than straight through grey. Height set in site.js.
   Clean, not banded (Adam, 2026-10-08): the stops are a smooth curve sampled
   at 25 points (no kinks for the eye to catch as bands), blended in OKLab where
   the browser can, and a fine grain on top dithers away the 8-bit steps. */
[data-blend="fade"] .blend-edge--bottom { background: linear-gradient(to bottom, rgba(2, 6, 111, 0.000) 0.0%, rgba(1, 5, 110, 0.044) 4.2%, rgba(0, 3, 108, 0.106) 8.3%, rgba(0, 0, 106, 0.180) 12.5%, rgba(0, 0, 104, 0.260) 16.7%, rgba(0, 0, 103, 0.339) 20.8%, rgba(0, 0, 104, 0.411) 25.0%, rgba(0, 2, 107, 0.470) 29.2%, rgba(5, 9, 114, 0.513) 33.3%, rgba(15, 24, 129, 0.534) 37.5%, rgba(28, 43, 148, 0.537) 41.7%, rgba(44, 65, 170, 0.531) 45.8%, rgba(61, 88, 191, 0.525) 50.0%, rgba(79, 110, 209, 0.529) 54.2%, rgba(97, 129, 223, 0.553) 58.3%, rgba(118, 148, 231, 0.600) 62.5%, rgba(141, 167, 236, 0.664) 66.7%, rgba(166, 186, 239, 0.736) 70.8%, rgba(189, 203, 240, 0.808) 75.0%, rgba(210, 218, 241, 0.874) 79.2%, rgba(226, 229, 242, 0.924) 83.3%, rgba(240, 240, 243, 0.967) 87.5%, rgba(246, 244, 242, 0.988) 91.7%, rgba(247, 244, 240, 0.995) 95.8%, rgba(248, 244, 239, 1.000) 100.0%); }
[data-blend="fade"] .blend-edge--top { background: linear-gradient(to top, rgba(2, 6, 111, 0.000) 0.0%, rgba(1, 5, 110, 0.044) 4.2%, rgba(0, 3, 108, 0.106) 8.3%, rgba(0, 0, 106, 0.180) 12.5%, rgba(0, 0, 104, 0.260) 16.7%, rgba(0, 0, 103, 0.339) 20.8%, rgba(0, 0, 104, 0.411) 25.0%, rgba(0, 2, 107, 0.470) 29.2%, rgba(5, 9, 114, 0.513) 33.3%, rgba(15, 24, 129, 0.534) 37.5%, rgba(28, 43, 148, 0.537) 41.7%, rgba(44, 65, 170, 0.531) 45.8%, rgba(61, 88, 191, 0.525) 50.0%, rgba(79, 110, 209, 0.529) 54.2%, rgba(97, 129, 223, 0.553) 58.3%, rgba(118, 148, 231, 0.600) 62.5%, rgba(141, 167, 236, 0.664) 66.7%, rgba(166, 186, 239, 0.736) 70.8%, rgba(189, 203, 240, 0.808) 75.0%, rgba(210, 218, 241, 0.874) 79.2%, rgba(226, 229, 242, 0.924) 83.3%, rgba(240, 240, 243, 0.967) 87.5%, rgba(246, 244, 242, 0.988) 91.7%, rgba(247, 244, 240, 0.995) 95.8%, rgba(248, 244, 239, 1.000) 100.0%); }
@supports (background: linear-gradient(in oklab, red, blue)) {
  [data-blend="fade"] .blend-edge--bottom { background: linear-gradient(in oklab to bottom, rgba(2, 6, 111, 0.000) 0.0%, rgba(1, 5, 110, 0.044) 4.2%, rgba(0, 3, 108, 0.106) 8.3%, rgba(0, 0, 106, 0.180) 12.5%, rgba(0, 0, 104, 0.260) 16.7%, rgba(0, 0, 103, 0.339) 20.8%, rgba(0, 0, 104, 0.411) 25.0%, rgba(0, 2, 107, 0.470) 29.2%, rgba(5, 9, 114, 0.513) 33.3%, rgba(15, 24, 129, 0.534) 37.5%, rgba(28, 43, 148, 0.537) 41.7%, rgba(44, 65, 170, 0.531) 45.8%, rgba(61, 88, 191, 0.525) 50.0%, rgba(79, 110, 209, 0.529) 54.2%, rgba(97, 129, 223, 0.553) 58.3%, rgba(118, 148, 231, 0.600) 62.5%, rgba(141, 167, 236, 0.664) 66.7%, rgba(166, 186, 239, 0.736) 70.8%, rgba(189, 203, 240, 0.808) 75.0%, rgba(210, 218, 241, 0.874) 79.2%, rgba(226, 229, 242, 0.924) 83.3%, rgba(240, 240, 243, 0.967) 87.5%, rgba(246, 244, 242, 0.988) 91.7%, rgba(247, 244, 240, 0.995) 95.8%, rgba(248, 244, 239, 1.000) 100.0%); }
  [data-blend="fade"] .blend-edge--top { background: linear-gradient(in oklab to top, rgba(2, 6, 111, 0.000) 0.0%, rgba(1, 5, 110, 0.044) 4.2%, rgba(0, 3, 108, 0.106) 8.3%, rgba(0, 0, 106, 0.180) 12.5%, rgba(0, 0, 104, 0.260) 16.7%, rgba(0, 0, 103, 0.339) 20.8%, rgba(0, 0, 104, 0.411) 25.0%, rgba(0, 2, 107, 0.470) 29.2%, rgba(5, 9, 114, 0.513) 33.3%, rgba(15, 24, 129, 0.534) 37.5%, rgba(28, 43, 148, 0.537) 41.7%, rgba(44, 65, 170, 0.531) 45.8%, rgba(61, 88, 191, 0.525) 50.0%, rgba(79, 110, 209, 0.529) 54.2%, rgba(97, 129, 223, 0.553) 58.3%, rgba(118, 148, 231, 0.600) 62.5%, rgba(141, 167, 236, 0.664) 66.7%, rgba(166, 186, 239, 0.736) 70.8%, rgba(189, 203, 240, 0.808) 75.0%, rgba(210, 218, 241, 0.874) 79.2%, rgba(226, 229, 242, 0.924) 83.3%, rgba(240, 240, 243, 0.967) 87.5%, rgba(246, 244, 242, 0.988) 91.7%, rgba(247, 244, 240, 0.995) 95.8%, rgba(248, 244, 239, 1.000) 100.0%); }
}
[data-blend="fade"] .blend-edge::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='d'><feTurbulence type='fractalNoise' baseFrequency='1.1' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='100%' height='100%' filter='url(%23d)'/></svg>");
  background-size: 160px; mix-blend-mode: overlay; opacity: .22;
}
/* Out of the sky the fade runs through pale sky blue instead: the Sovereign
   band is darker than the sky and read as a stripe under it. */
[data-blend="fade"] [data-gs-bg="sky"] .blend-edge--bottom { background: linear-gradient(to bottom, rgba(40, 110, 220, 0.000) 0.0%, rgba(44, 113, 221, 0.028) 4.2%, rgba(49, 117, 222, 0.065) 8.3%, rgba(56, 121, 223, 0.108) 12.5%, rgba(62, 126, 224, 0.157) 16.7%, rgba(70, 132, 225, 0.209) 20.8%, rgba(78, 138, 227, 0.263) 25.0%, rgba(87, 144, 228, 0.318) 29.2%, rgba(96, 150, 230, 0.371) 33.3%, rgba(105, 156, 231, 0.422) 37.5%, rgba(114, 163, 233, 0.471) 41.7%, rgba(126, 171, 234, 0.526) 45.8%, rgba(139, 179, 236, 0.583) 50.0%, rgba(153, 188, 239, 0.641) 54.2%, rgba(167, 197, 241, 0.697) 58.3%, rgba(181, 205, 242, 0.750) 62.5%, rgba(193, 213, 244, 0.799) 66.7%, rgba(205, 220, 245, 0.843) 70.8%, rgba(214, 226, 246, 0.880) 75.0%, rgba(224, 232, 246, 0.919) 79.2%, rgba(232, 236, 245, 0.947) 83.3%, rgba(238, 239, 243, 0.967) 87.5%, rgba(242, 241, 242, 0.980) 91.7%, rgba(245, 243, 240, 0.991) 95.8%, rgba(248, 244, 239, 1.000) 100.0%); }
[data-blend="fade"] [data-gs-bg="sky"] .blend-edge--top { background: linear-gradient(to top, rgba(40, 110, 220, 0.000) 0.0%, rgba(44, 113, 221, 0.028) 4.2%, rgba(49, 117, 222, 0.065) 8.3%, rgba(56, 121, 223, 0.108) 12.5%, rgba(62, 126, 224, 0.157) 16.7%, rgba(70, 132, 225, 0.209) 20.8%, rgba(78, 138, 227, 0.263) 25.0%, rgba(87, 144, 228, 0.318) 29.2%, rgba(96, 150, 230, 0.371) 33.3%, rgba(105, 156, 231, 0.422) 37.5%, rgba(114, 163, 233, 0.471) 41.7%, rgba(126, 171, 234, 0.526) 45.8%, rgba(139, 179, 236, 0.583) 50.0%, rgba(153, 188, 239, 0.641) 54.2%, rgba(167, 197, 241, 0.697) 58.3%, rgba(181, 205, 242, 0.750) 62.5%, rgba(193, 213, 244, 0.799) 66.7%, rgba(205, 220, 245, 0.843) 70.8%, rgba(214, 226, 246, 0.880) 75.0%, rgba(224, 232, 246, 0.919) 79.2%, rgba(232, 236, 245, 0.947) 83.3%, rgba(238, 239, 243, 0.967) 87.5%, rgba(242, 241, 242, 0.980) 91.7%, rgba(245, 243, 240, 0.991) 95.8%, rgba(248, 244, 239, 1.000) 100.0%); }
@supports (background: linear-gradient(in oklab, red, blue)) {
  [data-blend="fade"] [data-gs-bg="sky"] .blend-edge--bottom { background: linear-gradient(in oklab to bottom, rgba(40, 110, 220, 0.000) 0.0%, rgba(44, 113, 221, 0.028) 4.2%, rgba(49, 117, 222, 0.065) 8.3%, rgba(56, 121, 223, 0.108) 12.5%, rgba(62, 126, 224, 0.157) 16.7%, rgba(70, 132, 225, 0.209) 20.8%, rgba(78, 138, 227, 0.263) 25.0%, rgba(87, 144, 228, 0.318) 29.2%, rgba(96, 150, 230, 0.371) 33.3%, rgba(105, 156, 231, 0.422) 37.5%, rgba(114, 163, 233, 0.471) 41.7%, rgba(126, 171, 234, 0.526) 45.8%, rgba(139, 179, 236, 0.583) 50.0%, rgba(153, 188, 239, 0.641) 54.2%, rgba(167, 197, 241, 0.697) 58.3%, rgba(181, 205, 242, 0.750) 62.5%, rgba(193, 213, 244, 0.799) 66.7%, rgba(205, 220, 245, 0.843) 70.8%, rgba(214, 226, 246, 0.880) 75.0%, rgba(224, 232, 246, 0.919) 79.2%, rgba(232, 236, 245, 0.947) 83.3%, rgba(238, 239, 243, 0.967) 87.5%, rgba(242, 241, 242, 0.980) 91.7%, rgba(245, 243, 240, 0.991) 95.8%, rgba(248, 244, 239, 1.000) 100.0%); }
  [data-blend="fade"] [data-gs-bg="sky"] .blend-edge--top { background: linear-gradient(in oklab to top, rgba(40, 110, 220, 0.000) 0.0%, rgba(44, 113, 221, 0.028) 4.2%, rgba(49, 117, 222, 0.065) 8.3%, rgba(56, 121, 223, 0.108) 12.5%, rgba(62, 126, 224, 0.157) 16.7%, rgba(70, 132, 225, 0.209) 20.8%, rgba(78, 138, 227, 0.263) 25.0%, rgba(87, 144, 228, 0.318) 29.2%, rgba(96, 150, 230, 0.371) 33.3%, rgba(105, 156, 231, 0.422) 37.5%, rgba(114, 163, 233, 0.471) 41.7%, rgba(126, 171, 234, 0.526) 45.8%, rgba(139, 179, 236, 0.583) 50.0%, rgba(153, 188, 239, 0.641) 54.2%, rgba(167, 197, 241, 0.697) 58.3%, rgba(181, 205, 242, 0.750) 62.5%, rgba(193, 213, 244, 0.799) 66.7%, rgba(205, 220, 245, 0.843) 70.8%, rgba(214, 226, 246, 0.880) 75.0%, rgba(224, 232, 246, 0.919) 79.2%, rgba(232, 236, 245, 0.947) 83.3%, rgba(238, 239, 243, 0.967) 87.5%, rgba(242, 241, 242, 0.980) 91.7%, rgba(245, 243, 240, 0.991) 95.8%, rgba(248, 244, 239, 1.000) 100.0%); }
}
/* Dark into dark (hero into the sky, Adam 2026-10-08): the hero's foot and
   the sky's head both ease into one horizon blue, so they meet on the same
   colour. Smoothstep alpha, with the same grain as the paper fades. */
[data-blend="fade"] .blend-edge--join.blend-edge--bottom { height: clamp(120px, 14vw, 200px); background: linear-gradient(to bottom, rgba(28, 60, 186, 0.000) 0.0%, rgba(28, 60, 186, 0.007) 5.0%, rgba(28, 60, 186, 0.028) 10.0%, rgba(28, 60, 186, 0.061) 15.0%, rgba(28, 60, 186, 0.104) 20.0%, rgba(28, 60, 186, 0.156) 25.0%, rgba(28, 60, 186, 0.216) 30.0%, rgba(28, 60, 186, 0.282) 35.0%, rgba(28, 60, 186, 0.352) 40.0%, rgba(28, 60, 186, 0.425) 45.0%, rgba(28, 60, 186, 0.500) 50.0%, rgba(28, 60, 186, 0.575) 55.0%, rgba(28, 60, 186, 0.648) 60.0%, rgba(28, 60, 186, 0.718) 65.0%, rgba(28, 60, 186, 0.784) 70.0%, rgba(28, 60, 186, 0.844) 75.0%, rgba(28, 60, 186, 0.896) 80.0%, rgba(28, 60, 186, 0.939) 85.0%, rgba(28, 60, 186, 0.972) 90.0%, rgba(28, 60, 186, 0.993) 95.0%, rgba(28, 60, 186, 1.000) 100.0%); }
[data-blend="fade"] .blend-edge--join.blend-edge--top { height: clamp(200px, 24vw, 340px); background: linear-gradient(to top, rgba(28, 60, 186, 0.000) 0.0%, rgba(28, 60, 186, 0.007) 5.0%, rgba(28, 60, 186, 0.028) 10.0%, rgba(28, 60, 186, 0.061) 15.0%, rgba(28, 60, 186, 0.104) 20.0%, rgba(28, 60, 186, 0.156) 25.0%, rgba(28, 60, 186, 0.216) 30.0%, rgba(28, 60, 186, 0.282) 35.0%, rgba(28, 60, 186, 0.352) 40.0%, rgba(28, 60, 186, 0.425) 45.0%, rgba(28, 60, 186, 0.500) 50.0%, rgba(28, 60, 186, 0.575) 55.0%, rgba(28, 60, 186, 0.648) 60.0%, rgba(28, 60, 186, 0.718) 65.0%, rgba(28, 60, 186, 0.784) 70.0%, rgba(28, 60, 186, 0.844) 75.0%, rgba(28, 60, 186, 0.896) 80.0%, rgba(28, 60, 186, 0.939) 85.0%, rgba(28, 60, 186, 0.972) 90.0%, rgba(28, 60, 186, 0.993) 95.0%, rgba(28, 60, 186, 1.000) 100.0%); }
/* The grain fades in with the colour, or its own rectangle shows as a line
   where the fade starts. */
[data-blend="fade"] .blend-edge--bottom::after { -webkit-mask-image: linear-gradient(to bottom, transparent, #000 45%); mask-image: linear-gradient(to bottom, transparent, #000 45%); }
[data-blend="fade"] .blend-edge--top::after { -webkit-mask-image: linear-gradient(to top, transparent, #000 45%); mask-image: linear-gradient(to top, transparent, #000 45%); }
[data-blend="deckle"] .blend-edge { height: clamp(16px, 2.2vw, 30px); }
[data-blend="deckle"] .blend-edge svg { display: block; width: 100%; height: 100%; fill: var(--gs-paper); }
[data-blend="deckle"] .blend-edge--bottom svg { filter: drop-shadow(0 -2px 3px rgba(0, 0, 0, .35)); }
[data-blend="deckle"] .blend-edge--top svg { filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .35)); }
[data-blend="stack"] .blend-rise {
  position: relative; z-index: 2;
  margin-top: calc(-1 * clamp(28px, 3vw, 44px));
  border-radius: clamp(28px, 3vw, 44px) clamp(28px, 3vw, 44px) 0 0;
  box-shadow: 0 -18px 40px rgba(0, 0, 0, .3);
}
/* Paper sections have no fill of their own (they show the page), so a rising
   one needs one, or its rounded corners and shadow have nothing to draw on. */
[data-blend="stack"] .blend-rise--paper { background-color: var(--gs-paper); }


/* ============ PREVIEW 2026-10-08: Work Philosophy on deep-water (colour pass)
   Every colour in the section turned for a dark ground: the headings dim until
   chosen, the chosen one full paper; the answer in paper at 82%. */
.faq[data-gs-bg] { color: var(--on-signal); }
.faq[data-gs-bg] .eyebrow { color: var(--on-signal-muted); }
.faq[data-gs-bg] .gs-faq { color: rgba(248, 244, 239, .55); }
@media (hover: hover) { .faq[data-gs-bg] .gs-faq:hover { color: var(--on-signal); } }
.faq[data-gs-bg] .gs-faqi.is-open .gs-faq { color: var(--on-signal); }
.faq[data-gs-bg] .panel { border-left-color: rgba(248, 244, 239, .18); }
.faq[data-gs-bg] .gs-faq-panel .panel__label { color: var(--on-signal-muted); }
.faq[data-gs-bg] .gs-faq-panel .panel__body,
.faq[data-gs-bg] .gs-faq-desc { color: rgba(248, 244, 239, .82); }
.faq[data-gs-bg] .panel__body strong, .faq[data-gs-bg] .gs-faq-desc strong { color: var(--on-signal); }
.faq[data-gs-bg] a { color: var(--on-signal); }

/* PREVIEW 2026-10-08: the ornate hero frame, the live token, and the hero
   entrance. Adam's picks: "C · Antique frame", most reflective, with the
   antique token in its crest; bigger than the trial ("with the new details
   we've lost scale from the original frame"). The frame is one supersampled
   WebP over the painting (source: _type/frames/src). */
.hero__stack { container-type: inline-size; }
/* The moulding is drawn by the ornament now: 24 of the frame's 400 units. */
.hero__frame { padding: 6cqw; box-shadow: none; background: #14161d; }   /* dark until the ornament covers it, so no grey flash */
.hero__frame::before, .hero__frame::after { display: none; }
.hero__about { left: 6cqw; right: 6cqw; bottom: 6cqw; }
.hero__ornament, .hero__seal { position: absolute; max-width: none; pointer-events: none; user-select: none; -webkit-user-select: none; }
/* The ornament's box is its drawing's viewBox (-130 -150 660 790) laid over
   the 400 x 500 painting box. */
.hero__ornament { z-index: 3; left: -32.5%; top: -30%; width: 165%; height: 158%; transform: translateZ(1px); }
/* The token, sized to the bezel it was cut for (the render's coin is 81% of
   the image). */
.hero__seal { z-index: 5; left: 38.27%; top: -13.38%; width: 23.46%; height: auto; transform: translateZ(2px); filter: drop-shadow(0 3px 4px rgba(0, 0, 0, .45)); }

/* Bigger: the painting takes the height the old frame had, and the crest,
   spikes and title row get their own room above and below it. */
/* About 12% smaller than at launch (Adam, 2026-10-08: it took up a little too much of the
   screen on arrival). */
/* 10% smaller (Adam, 8 Oct; was clamp(390px, calc((100svh - 210px) * .88), 790px)). */
.hero__art { --frame-h: clamp(351px, calc((100svh - 210px) * .792), 711px); padding-top: calc(var(--frame-h) * .3 - 64px); }
/* The frame's lowest spike ends 13.5% of --frame-h below the painting, so the
   visible gap to the title is this margin less that. .186 (was .2) makes the
   gap 30% smaller in pixels than before the frame shrank (Adam, 8 Oct). */
.hero__meta { margin-top: calc(var(--frame-h) * .186); }

/* The live coins: above the statement, and in the closing CTA. */
godspeed-token.philo__token { display: block; width: var(--coin, clamp(112px, 9vw, 140px)); height: auto; margin: 0 0 clamp(18px, 2vw, 28px); }
.cta__mark--coin { height: auto; display: flex; justify-content: center; }
.cta__mark--coin godspeed-token { width: clamp(120px, 10vw, 160px); }

/* Hero entrance, once on load: the painting fades up, the frame settles in
   around it, the token drops into its crest, then the words rise. Pure CSS:
   it keys off the first painting (data-art="0" never changes), so the gallery's
   cross-fade never re-runs it, and "backwards" means nothing is held after it
   ends. Off with reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  .hero .hero__frame[data-art="0"] { animation: gs-in-paint 1100ms cubic-bezier(.2, .7, .2, 1) backwards; }
  .hero .hero__ornament { animation: gs-in-frame 1400ms 150ms cubic-bezier(.2, .7, .2, 1) backwards; }
  .hero .hero__seal { animation: gs-in-seal 900ms 700ms cubic-bezier(.3, 1.4, .5, 1) backwards; }
  .hero .hero__copy > * { animation: gs-in-copy 900ms cubic-bezier(.2, .7, .2, 1) backwards; }
  /* The token key is the first child, shown only while the frame is locked:
     it rises first, then the words in their usual order. */
  .hero .hero__copy > :nth-child(1) { animation-delay: 200ms; }
  .hero .hero__copy > :nth-child(2) { animation-delay: 350ms; }
  .hero .hero__copy > :nth-child(3) { animation-delay: 520ms; }
  .hero .hero__copy > :nth-child(4) { animation-delay: 680ms; }
  .hero .hero__meta { animation: gs-in-copy 900ms 900ms cubic-bezier(.2, .7, .2, 1) backwards; }
}
@keyframes gs-in-paint { from { opacity: 0; transform: scale(.97); filter: brightness(.6); } }
@keyframes gs-in-frame { from { opacity: 0; transform: translateZ(1px) scale(1.06); } }
@keyframes gs-in-seal { from { opacity: 0; transform: translateZ(2px) translateY(-40%) scale(.8); } }
@keyframes gs-in-copy { from { opacity: 0; transform: translateY(22px); } }

@media (max-width: 820px) {
  .hero__art { --frame-h: min(39svh, 460px); padding-top: calc(var(--frame-h) * .3 - 24px); }
  .hero__piece { align-items: center; }
  .hero__meta, .hero__browse { width: 100%; }
}

/* Scroll reveals, extended (PREVIEW 2026-10-08): grids arrive card by card
   rather than as one block. Builds on the [data-reveal] system at the top of
   this file; nothing here runs without it. */
@media (prefers-reduced-motion: no-preference) {
  .gs-grid-4[data-reveal] > *, [data-reveal] .gs-grid-6 > * {
    opacity: 0; transform: translateY(18px);
    transition: opacity 700ms var(--gs-ease-out), transform 700ms var(--gs-ease-out);
  }
  .gs-grid-4[data-reveal][data-in] > *, [data-reveal][data-in] .gs-grid-6 > * { opacity: 1; transform: none; }
  [data-reveal][data-in] :is(.gs-grid-4, .gs-grid-6) > :nth-child(2), .gs-grid-4[data-reveal][data-in] > :nth-child(2) { transition-delay: 70ms; }
  [data-reveal][data-in] :is(.gs-grid-4, .gs-grid-6) > :nth-child(3), .gs-grid-4[data-reveal][data-in] > :nth-child(3) { transition-delay: 140ms; }
  [data-reveal][data-in] :is(.gs-grid-4, .gs-grid-6) > :nth-child(4), .gs-grid-4[data-reveal][data-in] > :nth-child(4) { transition-delay: 210ms; }
  [data-reveal][data-in] :is(.gs-grid-4, .gs-grid-6) > :nth-child(5), .gs-grid-4[data-reveal][data-in] > :nth-child(5) { transition-delay: 280ms; }
  [data-reveal][data-in] :is(.gs-grid-4, .gs-grid-6) > :nth-child(6), .gs-grid-4[data-reveal][data-in] > :nth-child(6) { transition-delay: 350ms; }
  [data-reveal][data-in] :is(.gs-grid-4, .gs-grid-6) > :nth-child(n+7), .gs-grid-4[data-reveal][data-in] > :nth-child(n+7) { transition-delay: 420ms; }
}

/* With the bigger frame the hero runs taller than the screen, so the copy is
   anchored to the bottom of the FIRST screen rather than the bottom of the
   hero: the headline is always in view on arrival, and the painting's title
   row sits just below the fold. Desktop only; phones stack as before. */
@media (min-width: 821px) {
  .hero { align-items: start; }
  .hero__copy {
    align-self: start;
    min-height: calc(100svh - 112px - clamp(48px, 6vw, 88px));
    display: flex; flex-direction: column; justify-content: flex-end; align-items: flex-start;
  }
}
@media (hover: hover) and (min-width: 821px) {
  .hero__copy { min-height: calc(100svh - 88px - 52px); }
}
/* Keep the headline to its three lines at every width, so a wide screen never
   runs it into the frame's spikes ("We create / momentum for / ambitious brands."). */
@media (min-width: 821px) { .hero__line { max-width: 7.6em; } }

/* ============== PREVIEW 2026-10-08: the token intro (Adam's idea)
   The token spins in the centre of a Void Black screen while the page loads,
   then flies into the crest of the hero frame as the screen lifts and the hero
   entrance plays. The head script adds .intro (show the screen) and .intro-hold
   (hold the hero entrance) once per visit; site.js runs the flight. */
.gs-intro { display: none; }
html.intro .gs-intro { display: grid; place-items: center; position: fixed; inset: 0; z-index: 9999; pointer-events: none; }
.gs-intro__veil {
  position: absolute; inset: 0;
  background: radial-gradient(60% 45% at 50% 56%, rgba(28, 60, 186, .32), rgba(2, 6, 111, .12) 45%, transparent 75%), #02020a;
}
.gs-intro__coin { position: relative; width: clamp(150px, 15vw, 220px); transform-origin: 50% 50%; will-change: transform; }
.gs-intro__coin godspeed-token { display: block; width: 100%; }
.gs-intro__coin godspeed-token img { display: block; width: 100%; height: auto; }
/* Nothing shows until the 3D coin has drawn its first frame, then it fades in
   already turning (Adam, 8 Oct: a flat stand-in spinning first read as 2D
   popping into 3D). If the 3D coin never comes, site.js adds .intro-still and
   the still fades in, without a flat spin. */
html.intro .gs-intro__coin godspeed-token { opacity: 0; transition: opacity 380ms cubic-bezier(.2, .7, .2, 1); }
html.intro .gs-intro__coin godspeed-token[data-ready], html.intro-still .gs-intro__coin godspeed-token { opacity: 1; }
/* The other live coins (statement, closing section): the still waits for the
   3D coin, or for .gs-tokens-late after 3 s. */
godspeed-token:not([data-ready]) > img { opacity: 0; transition: opacity 300ms ease; }
html.gs-tokens-late godspeed-token:not([data-ready]) > img { opacity: 1; }
html.intro-still .gs-intro__coin godspeed-token > img { opacity: 1; }
/* If site.js never takes over, the screen clears on its own. */
html.intro .gs-intro { animation: gs-intro-failsafe 500ms 4.6s forwards; }
@keyframes gs-intro-failsafe { to { opacity: 0; visibility: hidden; } }
/* Hold the hero entrance until the token leaves the centre. */
html.intro-hold .hero .hero__frame[data-art="0"],
html.intro-hold .hero .hero__ornament,
html.intro-hold .hero .hero__copy > *,
html.intro-hold .hero .hero__meta { animation-play-state: paused; }
/* The flying token becomes the crest coin: the frame's own token stays hidden
   until it lands, and never runs its drop-in. */
html.intro .hero .hero__seal, html.intro-done .hero .hero__seal { animation: none; }
/* While the coin is landing, the frame only fades in: its usual settle from
   106% would slide the crest under the arriving coin (Adam: a jitter as it
   lands). Kept after the intro too, so the animation never restarts. */
html.intro .hero .hero__ornament, html.intro-done .hero .hero__ornament { animation-name: gs-in-fade; }
@keyframes gs-in-fade { from { opacity: 0; } }
html.intro:not(.intro-landed) .hero .hero__seal { opacity: 0; }

/* ============== PREVIEW 2026-10-08: the token key (Adam's idea)
   On a first visit the painting is shut behind ten chrome plates and the
   frame's crest is an empty socket. The token spins above the headline; drag
   it into the socket (or click it, or press Enter on it) and the plates split
   on their seams and swing out into the frame's sides. Picked by Adam from a
   prototype: "Suit", 10 plates, the "halfway" chrome. The head script adds
   .lock on every load; site.js runs it and
   adds .lock-seated then .lock-open. The plates are one sprite drawn in the
   frame's own lighting (_type/frames/src/lock-plates.cjs, git-excluded). */
.hero__keyslot, .hero__lock, .hero__socket, .hero__flash { display: none; }
html.lock .hero__keyslot { display: block; }
/* Opened: on a desktop the key's place closes up unseen (the copy hangs from
   the bottom of the screen). On a phone the frame sits under the copy, so the
   place is kept, empty, rather than jolting the opening painting upward. */
html.lock-open .hero__keyslot { visibility: hidden; }
@media (min-width: 821px) { html.lock-open .hero__keyslot { display: none; } }
html.lock:not(.lock-open) .hero__lock { display: block; }
html.lock:not(.lock-seated) .hero__socket { display: block; }
html.lock .hero__flash { display: block; }

.hero__keyslot {
  position: relative; flex: none;
  width: clamp(84px, 7.2vw, 124px);
  height: clamp(84px, 7.2vw, 124px);
  /* the coin is 81% of its box: pull it left so its edge lines up with the W */
  margin: 0 0 clamp(14px, 1.6vw, 26px) calc(clamp(84px, 7.2vw, 124px) * -.093);
  font-size: 16px;
}
.hero__key {
  position: absolute; inset: 0;
  cursor: grab; touch-action: none;
  -webkit-user-select: none; user-select: none;
  border-radius: 50%; outline: none;
}
/* On a touchscreen it is a button, so a swipe over it scrolls the page (site.js). */
@media (pointer: coarse) { .hero__key { touch-action: manipulation; } }
/* Phones get no key (Adam, 15 Oct: "keep it simple so the page works"). The
   head script never locks below 821px, so the painting starts open; the token
   just spins above the headline and a swipe over it scrolls the page. */
@media (max-width: 820px) {
  .hero__keyslot { display: block; }
  .hero__key { pointer-events: none; cursor: auto; }
  .hero__key::after { display: none; }
}
.hero__key:focus-visible { box-shadow: 0 0 0 2px var(--on-signal); }
.hero__key.is-dragging { cursor: grabbing; }
.hero__key godspeed-token { display: block; width: 100%; height: 100%; }
/* The key is carried, not flicked: the coin's own drag-to-spin stays off here. */
.hero__key godspeed-token, .hero__key godspeed-token > canvas { pointer-events: none !important; }
.hero__key godspeed-token img { display: block; width: 100%; height: auto; }
.hero__key::after {   /* a soft shadow, so it reads as a thing you can pick up */
  content: ""; position: absolute; left: 22%; right: 22%; bottom: 2%; height: 9%;
  border-radius: 50%; background: radial-gradient(closest-side, rgba(0, 0, 0, .55), transparent);
  filter: blur(3px); z-index: -1; transition: opacity 300ms ease;
}
.hero__key.is-dragging::after, .hero__key.is-flying::after { opacity: 0; }
/* While it moves it is lifted out of the column and carried over the page. */
.hero__key.is-carried { position: fixed; inset: auto; z-index: 9000; will-change: transform; }

/* The socket and the flash share the crest token's box (.hero__seal), so
   the coin lands exactly where the crest's still will show. */
.hero__socket, .hero__flash { position: absolute; left: 38.27%; top: -13.38%; width: 23.46%; aspect-ratio: 1; pointer-events: none; }
.hero__socket { z-index: 5; transform: translateZ(2px); --near: 0; }
.hero__socket::before {   /* the recess, cut into the bezel's flat disc */
  content: ""; position: absolute; inset: 9.35%; border-radius: 50%;
  background:
    radial-gradient(circle at 50% 34%, rgba(120, 150, 255, calc(.10 + var(--near) * .35)), transparent 62%),
    radial-gradient(circle at 50% 50%, #121830 0%, #070a15 64%, #03040a 100%);
  box-shadow: inset 0 7px 12px rgba(0, 0, 0, .9), inset 0 -2px 3px rgba(255, 255, 255, .28), 0 0 0 1.5px rgba(8, 10, 18, .85), 0 1px 0 2px rgba(255, 255, 255, .35);
}
.hero__socket::after {    /* the waiting glow */
  content: ""; position: absolute; inset: 9.35%; border-radius: 50%; opacity: .8;
  box-shadow: 0 0 0 1px rgba(150, 175, 255, calc(.25 + var(--near) * .6)), 0 0 calc(10px + var(--near) * 26px) calc(1px + var(--near) * 6px) rgba(80, 120, 255, calc(.35 + var(--near) * .5));
}
@media (prefers-reduced-motion: no-preference) {
  html.lock:not(.lock-seated) .hero__socket::after { animation: gs-socket-wait 2.4s ease-in-out infinite; }
}
@keyframes gs-socket-wait { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
.hero__flash {
  z-index: 6; transform: translateZ(3px); border-radius: 50%; opacity: 0;
  background: radial-gradient(closest-side, rgba(255, 255, 255, .95), rgba(150, 180, 255, .55) 40%, transparent 72%);
  mix-blend-mode: screen;
}
/* The crest's still token waits for the key, and never replays its drop-in. */
html.lock .hero .hero__seal { animation: none; }
html.lock:not(.lock-seated) .hero .hero__seal { opacity: 0; }
/* Nothing about the painting shows through the plates. */
html.lock:not(.lock-open) .hero__about { visibility: hidden; }

/* The plates sit over the canvas, tucked a touch under the moulding so they
   vanish into the frame as they leave, in a flat layer of their own so a
   plate turning toward you never cuts through the ornament. The lock is a
   size container: the sprite is sized to it in cqw/cqh. */
.hero__lock {
  position: absolute; z-index: 2; inset: 5.4cqw;
  transform: translateZ(.5px);
  overflow: hidden; perspective: 700px;
  container-type: size;
  background: #07090f;
  cursor: not-allowed;
}
html.lock-seated .hero__lock { background: transparent; }
.lock__panel {
  position: absolute; left: 0; right: 0;
  top: calc(var(--i) * 10%); height: calc(10% + .5px);
  will-change: transform;
}
.lock__half {
  position: absolute; top: 0; bottom: 0;
  /* Sized to the frame (speed pass, 15 Oct): the box is at most 390px wide, so
     the 800px plates cover a 2x screen; phones (251px box) take the 600px. */
  background-image: url("assets/img/lock-plates-600.webp");
  background-image: image-set(url("assets/img/lock-plates-600.webp") 1x, url("assets/img/lock-plates-800.webp") 2x);
  background-repeat: no-repeat;
  background-size: 100cqw 100cqh;
  will-change: transform;
}
@media (max-width: 820px) { .lock__half { background-image: url("assets/img/lock-plates-600.webp"); } }
.lock__half--l { left: 0; width: calc(var(--s) * 1%); background-position: 0 calc(var(--i) * -10cqh); }
.lock__half--r { right: 0; width: calc((100 - var(--s)) * 1%); background-position: calc(var(--s) * -1cqw) calc(var(--i) * -10cqh); }
/* A sheen sweeps the plates as the lock lets go (--sweep, set by site.js),
   and each half darkens as it turns edge on into the frame. */
.lock__panel::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 2;
  background: linear-gradient(105deg, transparent 38%, rgba(255, 255, 255, .75) 49%, rgba(190, 210, 255, .45) 52%, transparent 62%) var(--sweep, 160%) 0 / 300% 100% no-repeat;
  mix-blend-mode: screen;
}
.lock__shade { position: absolute; inset: 0; pointer-events: none; background: #000; opacity: 0; }
/* A click on the plates while locked gives a small knock. */
@keyframes gs-lock-knock { 0%, 100% { transform: translateZ(.5px) translateX(0); } 20% { transform: translateZ(.5px) translateX(-3px); } 45% { transform: translateZ(.5px) translateX(3px); } 70% { transform: translateZ(.5px) translateX(-1.5px); } }
.hero__lock.is-knocked { animation: gs-lock-knock 320ms cubic-bezier(.3, .7, .4, 1); }
/* During the intro the key waits, unseen, for the flying coin to land in its
   place, and does not run its own entrance. */
html.intro .hero__keyslot { animation: none; }
html.intro:not(.intro-landed) .hero__key { opacity: 0; }

/* PREVIEW 2026-10-08: the stars over Sydney thin out behind the headline
   (Adam: "less stars near the text"). A soft hole in the star layer over the
   hero's text block; site.js measures the block and sets --sky-* on resize,
   so it follows the text at every width. */
.hero .gsb-southsky {
  -webkit-mask-image: radial-gradient(ellipse var(--sky-rx, 30%) var(--sky-ry, 26%) at var(--sky-x, 23%) var(--sky-y, 54%), rgba(0, 0, 0, .08) 45%, #000 100%);
          mask-image: radial-gradient(ellipse var(--sky-rx, 30%) var(--sky-ry, 26%) at var(--sky-x, 23%) var(--sky-y, 54%), rgba(0, 0, 0, .08) 45%, #000 100%);
}

/* PREVIEW 2026-10-08: the team in oval silver frames (Adam: "a similar
   material frame to the header ... oval shapes like this", after two
   Victorian oval frames). The frame is drawn by the hero's generator
   (_type/frames, O()) in the same antique silver and rasterised. Its box is
   the photo box grown by 60 units left and right, 70 top and bottom, on a
   400 x 500 photo, so the photo sits at 76.9% of the column and the frame
   fills the rest. The photo is clipped to the frame's opening (rx 136,
   ry 186). */
.gs-teamcard__media {
  /* 90.5%, up from 76.9% (Adam: "could be bigger"): the drawn frame is about
     110% of the photo's width (the image's box is 130%, with clear margins),
     so at 90.5% neighbouring frames keep about 24px between them. */
  width: 90.5%; margin: 9% auto calc(9% + 8px);
  background: none; overflow: visible;
}
.gs-teamcard__media .slot { clip-path: ellipse(34% 37.2% at 50% 50%); background: var(--gs-ink-12); }
.gs-teamcard__media::after {
  content: ""; position: absolute; z-index: 2; pointer-events: none;
  left: -15%; top: -14%; width: 130%; height: 128%;
}
/* The oval frame loads with the team section, not on arrival (speed pass, 15 Oct). */
.gs-near .gs-teamcard__media::after { background: url(assets/img/frame-oval.webp) center / 100% 100% no-repeat; }
.gs-teamcard__row { justify-content: center; align-items: center; text-align: center; }

/* ================================================= 404: the coin in the dark
   Adam, 8 Oct 2026: a custom 404 "to do with the token". Option A of two
   rendered (centred coin on the closing section's night sky; the other set
   the words bottom-left like the hero). The live coin can be flicked. The nav
   goes transparent over it through [data-nav-hero] in site.js. */
.lost { position: relative; min-height: 100svh; display: grid; color: var(--gs-paper); padding: 120px var(--gs-pad-x) 72px; }
.lost__inner { position: relative; z-index: 1; max-width: var(--gs-content-max); width: 100%; margin: 0 auto; }
.lost h1 { font-family: var(--gs-font-display); font-weight: 300; letter-spacing: var(--gs-track-display); line-height: .95; margin: 0; }
.lost p { color: rgba(248,244,239,.78); font-size: clamp(17px, 1.4vw, 20px); line-height: 1.5; margin: 18px 0 0; max-width: 34ch; }
.lost__go { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 28px; margin-top: 32px; }
.lost__go a:not(.gs-btn) { color: var(--gs-paper); text-underline-offset: 4px; font-size: 15px; }
.lost__hint { font-size: 13px !important; color: rgba(248,244,239,.6) !important; margin-top: 14px !important; }
.lost--a { place-items: center; text-align: center; }
.lost--a .lost__inner { display: grid; justify-items: center; }
.lost--a godspeed-token { width: clamp(150px, 18vw, 220px); margin-bottom: 28px; }
.lost--a h1 { font-size: clamp(56px, 9vw, 128px); }
.lost--a p { margin-inline: auto; }
.lost--a .lost__go { justify-content: center; }
