/* ==========================================================================
   Site Swift Services Ltd: "Shelf Edge" design system
   Dark-first editorial register (brief §4g supersedes §4a-4e where they
   conflict). One stylesheet. No build step. No external requests.
   ==========================================================================

   COMPONENT INDEX
   Every class below is documented with the markup shape it expects, so a
   later page can be assembled from class names alone. Sections are listed
   in the order they appear in this file. The whole site is dark (--ink
   background) on every page; there are no light/paper sections.

   LAYOUT
   .container            max-width wrapper, fluid side gutters.
   .section              a page section: --ink bg, 1px --line-dark top rule,
                          clamp(72px,9vw,136px) vertical padding.
                          <section class="section"><div class="container">
                            <div class="section-grid"> ... </div>
                          </div></section>
   .section-grid         the editorial 2-column grid used inside a .section:
                          <div class="section-grid">
                            <div class="section-grid__aside">EYEBROW</div>
                            <div class="section-grid__content">H2 + body</div>
                          </div>
                          Aside = cols 1-5 (sticky on desktop). Content =
                          cols 6-12 with a vertical hairline on the boundary.
                          Stacks to one column under 900px.
   .section--flush        removes the top rule (first section after header).

   EYEBROW / TAG MARK (mono)
   .eyebrow               tag mark + mono uppercase label.
                          <p class="eyebrow"><span class="tag-mark"
                          aria-hidden="true"></span>Label</p>
   .tag-mark              the 12x8 accent rectangle used as a bullet/marker.

   BUTTONS (two tiers only)
   .btn-primary           `--text` surface, ink label, 44px tall, 6px radius,
                          an attached 44px accent square with an ink arrow.
                          One clickable element, two visual parts:
                          <a class="btn-primary" href="/contact/">
                            <span class="btn-primary__label">Book a call</span>
                            <span class="btn-primary__arrow" aria-hidden="true">&#8594;</span>
                          </a>
   .btn-secondary         1px --line-dark ghost, --text label, 44px tall.
                          <a class="btn-secondary" href="tel:+447711817486">Call +44 7711 817486</a>
   .btn-row               flex wrapper with gap for a row of buttons.

   LOGO (inline mark + HTML text; used in header and footer)
   .logo                  <a href="/" class="logo" aria-label="Site Swift
                          Services, home"> wrapping:
                            <svg class="logo__mark" aria-hidden="true">...</svg>
                            <span class="logo__text">
                              <span class="logo__name">SITE SWIFT</span>
                              <span class="logo__sub">SERVICES</span>
                            </span>
                          The mark SVG is the 28x18 accent tag with the ink
                          bar, identical to favicon.svg. Text is real HTML so
                          it always uses the self-hosted webfonts (an SVG
                          <text> loaded via <img> cannot reach them).

   HEADER / NAV (mono nav labels)
   .site-header, .site-header__inner, .logo (see above)
   .nav-primary, .nav-primary__list, .nav-item, .nav-item--has-submenu
   .nav-item__link, .nav-item__trigger (button, desktop Services toggle)
   .submenu, .submenu__list                 desktop hover/focus panel
   .header-actions, .phone-link
   .nav-toggle, .nav-toggle__bars           mobile hamburger button
   .mobile-panel, .mobile-panel__list, .mobile-panel__sublist
   #mobile-panel is a SIBLING of <header>, not a child: .site-header uses
   backdrop-filter, which creates a new containing block for any
   position:fixed descendant and would trap the full-height overlay
   inside the 76px header if nested. Both sit inside the HEADER START/END
   comment block. See index.html for the exact verbatim markup every page
   must copy.

   HERO (home, bespoke component, not the generic .section-grid)
   .hero, .hero__inner, .hero__content, .hero__actions
   .hero__title, .hero__lead
   .hero__frame (extends .frame), .hero__ribbon, .hero__diagram (SVG
   shelf-edge fallback if a render is missing)
   .facts-strip, .facts-strip__item          below the hero frame
   Reveal targets carry [data-reveal]. Section headings carry
   [data-reveal-heading].

   PHOTOGRAPHY FRAME (renders: hero + service rows)
   .frame                 14px radius, 1px --line-dark border, overflow
                          hidden, 1px inset top highlight, monochrome +
                          grain filter. No caption element (client
                          instruction, removed sitewide): the alt text
                          carries the description, and stays descriptive
                          rather than naming the image as a render. Add
                          data-reveal-frame for the clip-path frame
                          reveal (brief §9a); wrap the picture/video in
                          .frame__media on any frame that also parallaxes
                          (hero, service rows) so the scroll transform and
                          the hover-scale never collide:
                          <div class="frame frame--16-10" data-reveal-frame>
                            <div class="frame__media">
                              <picture>
                                <source srcset="*.webp" type="image/webp">
                                <img src="*.jpg" alt="..." width height loading="lazy">
                              </picture>
                            </div>
                          </div>
   .frame--3-2, .frame--16-10, .frame--4-5, .frame--21-9   aspect-ratio
                          modifiers. .hero__frame sets its own responsive
                          ratio directly (4:3 mobile, 16:9 from 768px,
                          21:9 cinematic band from 1200px) rather than
                          using a fixed modifier class.
   Colour on hover (brief §10a): every frame's media is grayscale at rest;
                          :hover and :focus-within fade the whole filter to
                          none over 600ms. Touch devices (hover: none) have
                          no hover, so site.js adds .is-inview to every
                          .frame once it has scrolled into the viewport
                          (independent of the reveal-armed/data-reveal-frame
                          clip-path system above) and that class alone
                          switches the filter under an (hover: none) media
                          query. Under reduced motion the swap still
                          happens, just without the 600ms fade (the global
                          reduced-motion block forces all transitions to
                          ~0ms).

   SERVICE ROWS (home; four stacked full-width rows, not a 2x2 grid)
   .service-rows                 full-width wrapper, breaks out of the
                                  section-grid content column.
   .service-row                  <article class="service-row"> one of four.
   .service-row__grid, .service-row__number ("/01"), .service-row__main,
   .service-row__text, .service-row__tags (ul of pills), .tag-pill,
   .service-row__media (.frame--16-10), .service-row__cta (the link),
   .service-row__cta-arrow (28px accent square)

   STAGGERED GRID (brief §10b/§10c/§10d: "How we work" on home, "How it
   runs" on each service page, "How we operate" on About)
   .stagger-grid                 full-width wrapper (breaks out of
                                  section-grid__content like .service-rows).
                                  CSS grid, 3 cols desktop / 2 tablet / 1
                                  mobile, auto-placed: DOM order IS the
                                  visual checkerboard order (text, tile,
                                  text, tile...), so no order/grid-column
                                  overrides are ever needed.
   .stagger-grid__step           a text cell: <div class="stagger-grid__step">
                                  <p class="stagger-grid__index">01.</p>
                                  <h3 class="stagger-grid__title">Survey</h3>
                                  <p class="stagger-grid__text">...</p></div>
   .tile                         a pattern cell, interleaved in DOM order
                                  between step cells: <div class="tile
                                  tile--arcs-1" aria-hidden="true"><svg
                                  class="tile__glyph">...(inline glyph,
                                  currentColor)...</svg></div>. aria-hidden
                                  so assistive tech reads the steps in
                                  sequence (01 to 05/04) without ever
                                  landing on a tile (brief §10f). Six
                                  background-image modifiers: .tile--arcs-1,
                                  -2, -3 and .tile--lines-1, -2, -3 (files
                                  in /assets/img/tiles/, 600x400, reusable
                                  across pages/sections).

   CARD GRIDS (brief §10b/§10c: the compliance pack, three ways to engage)
   .pack-grid, .engage-grid      3 cols desktop (pack: 2 tablet), 1 mobile.
   .pack-card, .engage-card      --panel surface, 1px --line-dark, 14px
                                  radius, mono index top-left
                                  (.pack-card__index / .engage-card__index).
   .pack-card__title, .pack-card__text, .pack-card__glyph (glyph-check,
   22px, bottom-right, inline SVG currentColor via --muted-dark).
   .engage-card__title, .engage-card__text, .engage-card__tag (optional
   "MOST COMMON" mono label, top-right, accent colour).
   .engage-list                  <ul class="engage-list"> of included
                                  items, tag-mark bullet + text, same
                                  pattern as .scope-list__item.

   SECTOR STRIP (brief §10c home + §10d About)
   .sector-strip                 full-width wrapper, horizontal scroll with
                                  scroll-snap-type:x; 5 visible desktop (no
                                  scroll needed at 1200px), 3 tablet, ~1.3
                                  mobile.
   .sector-strip__item           <div class="sector-strip__item"><div
                                  class="frame frame--4-5" data-reveal-frame>
                                  ...</div><p class="sector-strip__label">
                                  Grocery</p></div>

   STATEMENT BAND (brief §10c: full-bleed, home only)
   .statement-band                the <section>; zero vertical padding of
                                  its own (the frame supplies the height).
   .statement-band__media         <div class="frame statement-band__media">,
                                  100vw edge-to-edge (negative margin
                                  breakout), border-radius reset to 0, 4:3
                                  mobile / 21:9 from 900px. Same monochrome
                                  -to-colour hover treatment as every frame.
   .statement-band__overlay       flat --ink at 45% opacity (not a
                                  gradient), pointer-events:none, z-index 2.
   .statement-band__content       position:absolute, inset:0, z-index 3,
                                  pointer-events:none, flex + align-items
                                  center; contains a plain (non-absolute)
                                  <div class="container"> so the ordinary
                                  max-width/auto-margin centring applies
                                  (avoids the abspos width/margin
                                  over-constrained edge case).
   .statement-band__text          <div class="statement-band__text"> inside
                                  the container: eyebrow + h2 + p, left
                                  aligned, max 34-46ch.

   CREDIBILITY LIST (legacy; unused after the richness wave, kept for
   reference only)
   .credibility-list, .credibility-list__item (each item: <p><strong>lead
   phrase.</strong> sentence.</p>)

   PROCESS RAIL (legacy; unused after the richness wave, kept for
   reference only - superseded by the "How we work" .stagger-grid)
   .process-rail                 full-width wrapper, breaks out of the
                                  section-grid content column (like
                                  .service-rows). 5 cols desktop, 3 cols
                                  tablet, 1 col mobile, hairline dividers.
   .process-rail__list (ordered list), .process-rail__step,
   .process-rail__number, .process-rail__title, .process-rail__text

   DEFINITION LIST (generic, also used standalone on Legal page)
   .dl, .dl__row (dt + dd pair; dt is a fixed 200px mono column, dd sits
   beside it left-aligned), .dl__term, .dl__desc

   VERIFY BLOCK
   No special wrapper: h2 + p + dl (.dl) + .btn-primary stacked full width
   inside the normal .section-grid__content, spaced by the generic
   ".section-grid__content > * + *" rule.

   CTA BAND
   .cta-band__title, .cta-band__text, .cta-band__actions
   (sits inside a normal .section / .section-grid)

   FOOTER
   .site-footer, .site-footer__inner, .footer-col, .footer-col__title,
   .footer-wordmark (giant muted "SITE SWIFT"), .site-footer__bottom,
   .site-footer__legal
   See index.html FOOTER START/END block for verbatim markup.

   PAGE HERO (inner pages)
   .page-hero, .page-hero__inner, .page-hero__title, .page-hero__lead

   INSTRUMENT ROW (home, below the hero; was "facts strip")
   .instrument-row              <div class="instrument-row"> grid of 4.
   .instrument-row__item        vertical hairline between cells (desktop).
                                 <div class="instrument-row__item" data-rise>
                                   <p class="instrument-row__value">17068411</p>
                                   <p class="instrument-row__label">Company
                                     number &middot; England and Wales</p>
                                 </div>

   CLIENT STRIP (home + About, replaces the old scope-list workaround)
   Eyebrow lives in .section-grid__aside as normal; the
   <ul class="client-strip__list"> itself is a SIBLING of .section-grid,
   full container width (same breakout pattern as .service-rows and
   .process-rail below), so three equal columns always have room and a
   wordmark can never wrap. <li class="client-strip__name">Waitrose</li>,
   hairline dividers between items, stacks to one column under 640px.
   Text only, never logos.

   SECTION INDEX (home sections only)
   .section-index                 mono "01 / 06" counter placed under an
                                   .eyebrow inside .section-grid__aside.
                                   <p class="eyebrow" data-reveal-heading>...</p>
                                   <p class="section-index">02 / 06</p>

   CREDIBILITY MEDIA
   On the home credibility section, a full-width .frame--21-9
   (store-exterior-night) sits above .credibility-list, which is
   itself repeat(auto-fit, minmax(280px,1fr)) from 560px so items are
   never crushed. .credibility-layout is a separate, generic 2-col
   grid (list/dl beside a .frame) used on the Contact page only.

   HERO VIDEO (home hero, replaces/joins the still)
   .hero__video                   <video> inside .hero__frame, same
                                   monochrome+grain treatment as .frame img.
                                   Autoplay/pause controlled by site.js
                                   (IntersectionObserver + reduced-motion).

   THREE-UP GALLERY (ESL page, "What an engagement includes")
   .gallery-3up                   <div class="gallery-3up"> of three
                                   <div class="frame frame--3-2"> children.
                                   1 col mobile, 3 col from 768px.

   MOTION SYSTEM (brief §9a; every effect gated by prefers-reduced-motion
   and Save-Data; only the ribbon marquee and the estate light sweep loop)
   FAIL-SAFE RULE: every reveal below is VISIBLE by default. The hidden
   "about to reveal" state only ever exists on ".reveal-armed", which
   site.js adds immediately before it starts observing an element, and
   only when motion is on, the element is not already near the viewport,
   and IntersectionObserver exists. A 1500ms safety timer force-reveals
   anything still armed if IO never fires. A page with JS off, blocked or
   erroring never carries .reveal-armed at all, so nothing is ever stuck
   hidden (this was a real bug once: keep the gate on the JS-added class,
   never on the bare selector).
   .line, .line__inner            heading split into lines by site.js on
                                   .hero__title (on load) and
                                   .page-hero__title / every section h2
                                   (on first intersection, or immediately
                                   if already in view at bind time):
                                   span.line (overflow hidden) wraps
                                   span.line__inner (the words, translated
                                   from 110% to 0 only while an ancestor
                                   carries .reveal-armed). <mark class="hi">
                                   plus any punctuation glued to it with no
                                   space is kept as one atomic token so it
                                   can never split across lines. Re-splits
                                   on resize (debounced).
   .rise + .reveal-armed          rise reveal: opacity 0->1, translateY
                                   18px->0. Applied by site.js (no markup
                                   needed) to lead paragraphs, .dl__row,
                                   list items (.credibility-list__item,
                                   .scope-list__item, .faq-item), the
                                   instrument row and client strip items
                                   and .service-row__text, staggered 60ms
                                   within each shared parent, once on
                                   first intersection (or immediately if
                                   the group is already in view).
   .frame[data-reveal-frame]      frame reveal: clip-path inset(0 0 100% 0)
                                   -> inset(0), media scale 1.08 -> 1, but
                                   only once .reveal-armed is also present.
                                   Never used on the home hero frame (that
                                   one only ever gets the load stagger via
                                   its .hero__frame-wrap[data-reveal]
                                   ancestor); used on service-row, gallery,
                                   credibility and about/contact frames.
   .is-inview                     added once an armed target has entered
                                   the viewport (or by the safety timer).
   .ribbon-track                  the marquee: two copies of the ribbon
                                   words scrolling left, 40s linear loop,
                                   paused on hover/focus-within and under
                                   reduced motion / Save-Data.
   .site-header--compact           added past 80px of scroll: padding and
                                   logo shrink. .nav-item__link[aria-current]
                                   carries the 2px accent underline.
   .estate                         <canvas class="estate"> mounted in the
                                   hero by estate.js (home only). Absolutely
                                   positioned, pointer-events:none, fades in
                                   via opacity once the first frame renders.
   Grain: a single body::after fixed layer, SVG noise, 3.5% opacity,
   mix-blend-mode:overlay. Switched off by html.save-data (Save-Data header
   / navigator.connection.saveData, set by site.js).
   html.no-motion                 set by site.js when prefers-reduced-motion
                                   matches; belt-and-braces alongside the
                                   @media queries below (keeps JS-driven
                                   inline transforms, e.g. parallax, from
                                   ever being applied).

   SCOPE LIST
   .scope-list, .scope-list__item (two-column ul, tag-mark bullets)

   FAQ ACCORDION
   .faq-list                     <div class="faq-list"> of:
   .faq-item                     <details class="faq-item">
   .faq-item__summary            <summary class="faq-item__summary"> text
                                  + chevron via ::after
   .faq-item__answer             <div class="faq-item__answer"><p>...</p></div>

   CONTACT FORM
   .form-panel (.panel surface), .contact-form, .form-field, .form-label,
   .form-input, .form-select, .form-textarea, .form-hint, .form-error
   .form-consent (checkbox row), .form-hp (honeypot, hidden)
   .form-actions, .form-status (JS-inserted success/error banner)

   PHOTO PLACEHOLDER (About page, later wave)
   .photo-placeholder            4:5 flat panel with a centred tag mark.

   404
   .error-links                  inline list of links separated by a
                                  visible middot, used on 404.html.

   UTILITIES
   .visually-hidden, .skip-link, .measure, .panel (flat --panel surface,
   1px --line-dark border, 14px radius)

   RESPONSIVE
   Mobile-first. Breakpoints at 640px, 900px, 1200px. Search this file for
   "@media (min-width: 640px)" etc. Tested down to 360px and up to 1920px.
   ========================================================================== */

/* ---------- Fonts (self-hosted, latin subset, woff2) -------------------- */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/inter-400.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/inter-500.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/fonts/inter-600.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/inter-700.woff2") format("woff2");
}
@font-face {
  font-family: "Inter Tight";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/inter-tight-500.woff2") format("woff2");
}
@font-face {
  font-family: "Inter Tight";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/fonts/inter-tight-600.woff2") format("woff2");
}
@font-face {
  font-family: "Inter Tight";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/inter-tight-700.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/plex-mono-400.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/plex-mono-500.woff2") format("woff2");
}

/* ---------- Tokens -------------------------------------------------------- */

:root {
  /* Dark-first surface (brief §4g.1) */
  --ink: #0b0c0e;
  --panel: #101216;
  --ink-2: #1a1d24;
  --line-dark: #1f2228;
  --text: #f4f3ef;
  --muted-dark: #9aa0a8;
  --accent: #f5b800;
  --accent-ink: #0b0c0e;
  --danger: #ff6b5e;

  /* Paper tokens retained but not used for sections (legacy / print) */
  --paper: #f5f2ec;
  --paper-2: #ebe7df;
  --white: #ffffff;
  --muted: #5b6068;
  --line: #d9d4ca;

  --radius-panel: 14px;
  --radius-btn: 6px;
  --radius-pill: 999px;
  --radius-mark: 3px;

  --measure: 70ch;
  --container: 1200px;
  --gutter: clamp(20px, 4vw, 48px);
  --header-h: 76px;
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);

  /* Motion system (brief §9a) */
  --ease-out: cubic-bezier(0.2, 0.7, 0.2, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-1: 500ms;
  --dur-2: 800ms;
  --dur-3: 1200ms;

  /* Scale corrected after client review (brief §9e): the display size was
     rendering headlines close to 104px and driving excessive page height. */
  --fs-display: clamp(2.5rem, 5.2vw, 4.75rem);
  --fs-h1: clamp(2.25rem, 4.6vw, 4rem);
  --fs-h2: clamp(1.9rem, 3.4vw, 2.9rem);
  --fs-h3: 1.25rem;
  --fs-lead: clamp(1.0625rem, 1.3vw, 1.2rem);
  --fs-body: 1.0625rem;
  --fs-small: 0.875rem;
  --fs-mono: 0.75rem;

  --font-head: "Inter Tight", "Inter", -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
  --font-body: "Inter", -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* ---------- Reset ---------------------------------------------------------- */

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

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

body,
h1,
h2,
h3,
h4,
p,
figure,
blockquote,
dl,
dd,
ul,
ol {
  margin: 0;
}

ul,
ol {
  padding: 0;
  list-style: none;
}

img,
svg {
  display: block;
  max-width: 100%;
}

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

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

input,
select,
textarea {
  font: inherit;
  color: inherit;
}

/* ---------- Base typography ------------------------------------------------ */

html {
  scroll-behavior: smooth;
}

body {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.55;
  font-weight: 400;
  background: var(--ink);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

h1,
h2 {
  font-family: var(--font-head);
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 1.0;
  color: var(--text);
  text-wrap: balance;
  hanging-punctuation: first;
}

/* Display sizes above 72px get slightly tighter tracking (brief §9d). */
.hero__title,
.page-hero__title {
  letter-spacing: -0.035em;
}

h3 {
  font-family: var(--font-head);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.15;
  font-size: var(--fs-h3);
  color: var(--text);
}

h1 {
  font-size: var(--fs-h1);
}

h2 {
  font-size: var(--fs-h2);
}

p {
  max-width: 62ch;
  color: var(--muted-dark);
}

.lead {
  font-size: var(--fs-lead);
  line-height: 1.5;
  color: var(--muted-dark);
  max-width: 52ch;
}

small,
.text-small {
  font-size: var(--fs-small);
}

strong {
  font-weight: 600;
  color: var(--text);
}

.measure {
  max-width: var(--measure);
}

/* The hi-vis highlight: exactly one phrase per display headline. */
mark.hi {
  background: var(--accent);
  color: var(--accent-ink);
  padding: 0.04em 0.12em;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  border-radius: 3px;
}

/* Inline text links (body copy, footer, legal pages) get the animated underline. */
main a:not(.btn-primary):not(.btn-secondary):not(.service-row__cta):not(.nav-item__link),
.site-footer a:not(.btn-primary):not(.btn-secondary) {
  text-decoration-line: underline;
  text-decoration-color: currentColor;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: text-decoration-color 0.15s var(--ease), text-decoration-thickness 0.15s var(--ease);
}

main a:not(.btn-primary):not(.btn-secondary):not(.service-row__cta):not(.nav-item__link):hover,
.site-footer a:not(.btn-primary):not(.btn-secondary):hover {
  text-decoration-color: var(--accent);
  text-decoration-thickness: 2px;
}

/* Mono labels drop to 11px below 640px (brief §9d.1). */
@media (max-width: 639px) {
  :root {
    --fs-mono: 0.6875rem;
  }
}

/* ---------- Focus ------------------------------------------------------------ */

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

/* ---------- Utilities --------------------------------------------------------- */

.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 1000;
  background: var(--accent);
  color: var(--accent-ink);
  padding: 12px 18px;
  border-radius: var(--radius-btn);
  font-weight: 600;
}

.skip-link:focus {
  left: 12px;
  top: 12px;
}

.panel {
  background: var(--panel);
  border: 1px solid var(--line-dark);
  border-radius: var(--radius-panel);
}

/* ---------- Layout ------------------------------------------------------------- */

.container {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Asymmetric on purpose (brief §9e correction): a section's own bottom
   padding is where the generous breathing room lives; its top padding is
   just enough to clear the hairline rule before the content starts. Two
   adjacent sections were stacking a full padding on each side of their
   shared edge (up to ~210px of dead air); this keeps the rule directly
   above its content instead of above a second block of padding. */
.section {
  background: var(--ink);
  color: var(--text);
  border-top: 1px solid var(--line-dark);
  padding-top: clamp(32px, 3.5vw, 48px);
  padding-bottom: clamp(56px, 6vw, 96px);
}

.section--flush {
  border-top: 0;
  padding-top: 0;
}

.section-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 32px;
}

.section-grid__aside {
  min-width: 0;
}

.section-grid__content {
  min-width: 0;
}

.section-grid__content > * + * {
  margin-top: 28px;
}

@media (min-width: 900px) {
  .section-grid {
    grid-template-columns: 5fr 7fr;
    column-gap: var(--gutter);
    align-items: start;
  }

  .section-grid__aside {
    position: sticky;
    top: calc(var(--header-h) + 32px);
    align-self: start;
  }

  .section-grid__content {
    border-left: 1px solid var(--line-dark);
    padding-left: var(--gutter);
  }
}

/* ---------- Buttons -------------------------------------------------------------- */

.btn-primary {
  display: inline-flex;
  align-items: stretch;
  height: 44px;
  border-radius: var(--radius-btn);
  overflow: hidden;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-body);
  background: var(--text);
  transition: background-color 0.15s var(--ease);
}

.btn-primary:hover,
.btn-primary:focus-visible {
  filter: brightness(1.06);
}

.btn-primary__label {
  display: flex;
  align-items: center;
  padding: 0 20px;
  color: var(--ink);
}

.btn-primary__arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  flex: none;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
  transition: background-color 0.15s var(--ease), transform 0.2s var(--ease-out);
}

.btn-primary:hover .btn-primary__arrow,
.btn-primary:focus-visible .btn-primary__arrow {
  transform: translateX(4px);
}

@media (prefers-reduced-motion: reduce) {
  .btn-primary:hover .btn-primary__arrow,
  .btn-primary:focus-visible .btn-primary__arrow {
    transform: none;
  }
}

.btn-secondary {
  display: inline-flex;
  align-items: center;
  height: 44px;
  padding: 0 22px;
  border-radius: var(--radius-btn);
  border: 1px solid var(--line-dark);
  color: var(--text);
  font-weight: 500;
  transition: border-color 0.15s var(--ease), background-color 0.15s var(--ease);
}

.btn-secondary:hover {
  border-color: var(--muted-dark);
  background: var(--panel);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

/* ---------- Eyebrow / tag mark (mono) ------------------------------------------------ */

.tag-mark {
  display: inline-block;
  width: 12px;
  height: 8px;
  background: var(--accent);
  border-radius: 1px;
  flex: none;
}

.eyebrow {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--fs-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted-dark);
}

/* ---------- Header / nav (mono nav) ------------------------------------------------------ */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(11, 12, 14, 0.92);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  border-bottom: 1px solid var(--line-dark);
  transition: padding 0.3s var(--ease-out);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--header-h);
  gap: 20px;
  transition: height 0.3s var(--ease-out);
}

/* Past 80px of scroll the header compresses (brief §9a/§9d). JS-toggled;
   harmless if JS never runs (header simply never compresses). */
.site-header--compact .site-header__inner {
  height: 60px;
}

.site-header--compact .logo {
  transform: scale(0.9);
  transform-origin: left center;
}

.logo {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex: none;
  transition: transform 0.3s var(--ease-out);
}

.site-header .logo {
  view-transition-name: logo;
}

.logo__mark {
  flex: none;
  height: 16px;
  width: auto;
}

.logo__text {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
}

.logo__name {
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -0.02em;
  color: var(--text);
  line-height: 1;
}

.logo__sub {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--muted-dark);
  line-height: 1;
}

.nav-primary {
  display: none;
}

.header-actions {
  display: none;
  align-items: center;
  gap: 22px;
}

.phone-link {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  white-space: nowrap;
}

@media (min-width: 900px) {
  .nav-primary {
    display: block;
  }

  .nav-primary__list {
    display: flex;
    align-items: center;
    gap: 32px;
  }

  .nav-item {
    position: relative;
  }

  .nav-item__link,
  .nav-item__trigger {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: var(--header-h);
    font-family: var(--font-mono);
    font-size: var(--fs-mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    transition: height 0.3s var(--ease-out);
  }

  .site-header--compact .nav-item__link,
  .site-header--compact .nav-item__trigger {
    height: 60px;
  }

  /* Current-page indicator: 2px accent underline (brief §9a/§9d). */
  .nav-item__link[aria-current="page"]::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 22px;
    height: 2px;
    background: var(--accent);
  }

  .site-header--compact .nav-item__link[aria-current="page"]::after {
    bottom: 14px;
  }

  .nav-item__trigger::after {
    content: "";
    width: 6px;
    height: 6px;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: rotate(45deg) translateY(-2px);
    transition: transform 0.15s var(--ease);
  }

  .nav-item--has-submenu:hover .nav-item__trigger::after,
  .nav-item--has-submenu:focus-within .nav-item__trigger::after {
    transform: rotate(225deg) translateY(2px);
  }

  .submenu {
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    min-width: 220px;
    background: var(--panel);
    border: 1px solid var(--line-dark);
    border-radius: var(--radius-panel);
    padding: 8px;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.15s var(--ease), visibility 0.15s;
  }

  .nav-item--has-submenu:hover .submenu,
  .nav-item--has-submenu:focus-within .submenu,
  .nav-item--has-submenu.is-open .submenu {
    opacity: 1;
    visibility: visible;
  }

  .submenu__list li a {
    display: block;
    padding: 10px 14px;
    border-radius: var(--radius-btn);
    font-family: var(--font-mono);
    font-size: var(--fs-mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
  }

  .submenu__list li a:hover {
    background: var(--ink-2);
  }

  .header-actions {
    display: flex;
  }

  .nav-toggle,
  .mobile-panel {
    display: none !important;
  }
}

/* Mobile hamburger */

.nav-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  flex: none;
}

.no-js .nav-toggle {
  display: none;
}

.nav-toggle__bars {
  position: relative;
  width: 22px;
  height: 2px;
  background: currentColor;
}

.nav-toggle__bars::before,
.nav-toggle__bars::after {
  content: "";
  position: absolute;
  left: 0;
  width: 22px;
  height: 2px;
  background: currentColor;
  transition: transform 0.2s var(--ease), opacity 0.2s var(--ease), top 0.2s var(--ease);
}

.nav-toggle__bars::before {
  top: -7px;
}

.nav-toggle__bars::after {
  top: 7px;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars {
  background: transparent;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars::before {
  top: 0;
  transform: rotate(45deg);
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars::after {
  top: 0;
  transform: rotate(-45deg);
}

/* Mobile panel: no-JS renders it as a plain static block under the header;
   JS turns it into an overlay controlled by the hamburger button. */

.mobile-panel {
  border-top: 1px solid var(--line-dark);
  background: var(--ink);
}

.js .mobile-panel {
  position: fixed;
  inset: 76px 0 0 0;
  z-index: 200;
  overflow-y: auto;
  visibility: hidden;
  opacity: 0;
  transform: translateY(-8px);
  /* visibility is intentionally not transitioned: it must switch the
     instant is-open is toggled, so focus can move into the panel on the
     same tick. Only opacity/transform animate. */
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease);
}

.js .mobile-panel.is-open {
  visibility: visible;
  opacity: 1;
  transform: translateY(0);
}

.mobile-panel__list {
  padding: 8px var(--gutter) 32px;
}

.mobile-panel__list > li {
  border-bottom: 1px solid var(--line-dark);
}

.mobile-panel__list a,
.mobile-panel__list .mobile-panel__group-title {
  display: block;
  padding: 16px 0;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 1.0625rem;
}

.mobile-panel__group-title {
  color: var(--muted-dark);
  font-weight: 500;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding-bottom: 4px;
}

.mobile-panel__sublist {
  padding-bottom: 8px;
}

.mobile-panel__sublist a {
  padding: 10px 0 10px 20px;
  font-weight: 500;
  font-size: var(--fs-body);
}

.mobile-panel__actions {
  padding: 20px 0 8px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

@media (min-width: 900px) {
  .mobile-panel {
    display: none;
  }
}

/* ---------- Hero (home, bespoke) --------------------------------------------------------- */

.hero {
  position: relative;
  background: var(--ink);
  overflow: hidden;
}

.hero__inner {
  padding-block: clamp(48px, 6.5vw, 88px);
}

.hero__content {
  max-width: 960px;
}

.hero__title {
  margin-top: 20px;
  max-width: 920px;
  font-size: var(--fs-display);
  letter-spacing: -0.03em;
  line-height: 1.0;
}

.hero__lead {
  margin-top: 24px;
  max-width: 48ch;
}

.hero__actions {
  margin-top: 36px;
}

.hero__frame-wrap {
  margin-top: clamp(40px, 5vw, 64px);
}

@media (min-width: 900px) {
  .hero__title {
    margin-top: 24px;
  }
}

/* Hero frame aspect ratio (brief §9e correction): a cinematic band at
   desktop widths rather than a tall 3:2 block, tightening page height. */
.hero__frame {
  aspect-ratio: 4 / 3;
}

@media (min-width: 768px) {
  .hero__frame {
    aspect-ratio: 16 / 9;
  }
}

@media (min-width: 1200px) {
  .hero__frame {
    aspect-ratio: 21 / 9;
  }
}

/* ---------- Reveal system (brief §9a/§9e: every motion physical, intentional, cheap,
   and every reveal fail-safe). The default state for every heading, list item and
   frame is always VISIBLE. A hidden "about to reveal" state exists only on the
   ".reveal-armed" class, which site.js adds at bind time immediately before it
   starts observing an element, only when: motion is on, the element is not
   already in (or near) the viewport, and IntersectionObserver is available. If
   IO throws, is missing, or never fires, site.js reveals the element anyway
   inside 1500ms. Nothing can be permanently stuck hidden: a page with JS
   disabled, blocked, or erroring never carries ".reveal-armed" at all. -------- */

/* Line reveal: heading split into masked lines. */
.line {
  display: block;
  overflow: hidden;
}

.line__inner {
  display: block;
}

.reveal-armed .line__inner {
  transform: translateY(110%);
  transition: transform var(--dur-2) var(--ease-out);
  transition-delay: calc(var(--line-i, 0) * 70ms);
}

.reveal-armed.is-inview .line__inner {
  transform: translateY(0);
}

/* Rise reveal: opacity + 18px translateY, applied by site.js. */
.rise.reveal-armed {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
  transition-delay: calc(var(--rise-i, 0) * 60ms);
}

.rise.reveal-armed.is-inview {
  opacity: 1;
  transform: none;
}

/* ---------- Photography frame: monochrome + grain, 14px radius --------------------------- */

.frame {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-panel);
  border: 1px solid var(--line-dark);
  background: var(--panel);
  /* 1px inner top highlight for depth (brief §9d). */
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}

.frame picture,
.frame img,
.frame video {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  filter: grayscale(1) contrast(1.06) brightness(0.95);
  transition: transform 0.7s var(--ease-out), filter 0.6s var(--ease-out);
}

/* Frame hover: the media scales up slightly, never the frame itself. */
.frame:hover picture,
.frame:hover img,
.frame:hover video {
  transform: scale(1.03);
}

/* Greyscale at rest, colour on hover / keyboard focus (brief §10a). The
   whole filter is transitioned, not just grayscale, so the contrast and
   brightness correction fades out with it rather than snapping. */
.frame:hover picture,
.frame:hover img,
.frame:hover video,
.frame:focus-within picture,
.frame:focus-within img,
.frame:focus-within video {
  filter: none;
}

/* Touch devices have no hover: a frame fades into colour once it has
   entered the viewport instead (site.js adds .is-inview to every .frame,
   independent of the reveal-armed clip-path system, whenever the browser
   reports no hover capability). */
@media (hover: none) {
  .frame.is-inview picture,
  .frame.is-inview img,
  .frame.is-inview video {
    filter: none;
  }
}

/* Frame reveal (brief §9a/§9e): clip-path wipe + media scale-down-to-rest,
   once on first intersection. Space is already reserved by the frame's
   aspect-ratio, so this never shifts layout. Visible by default (see the
   fail-safe note above): a frame is only ever clipped once site.js has
   added .reveal-armed, immediately before it starts observing it. */
.frame[data-reveal-frame].reveal-armed {
  clip-path: inset(0 0 100% 0);
  transition: clip-path var(--dur-3) var(--ease-out);
}

.frame[data-reveal-frame].reveal-armed picture,
.frame[data-reveal-frame].reveal-armed img,
.frame[data-reveal-frame].reveal-armed video {
  transform: scale(1.08);
}

.frame[data-reveal-frame].reveal-armed.is-inview {
  clip-path: inset(0);
}

.frame[data-reveal-frame].reveal-armed.is-inview picture,
.frame[data-reveal-frame].reveal-armed.is-inview img,
.frame[data-reveal-frame].reveal-armed.is-inview video {
  transform: scale(1);
}

.frame[data-reveal-frame].reveal-armed.is-inview:hover picture,
.frame[data-reveal-frame].reveal-armed.is-inview:hover img,
.frame[data-reveal-frame].reveal-armed.is-inview:hover video {
  transform: scale(1.03);
}

.frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  mix-blend-mode: overlay;
  opacity: 0.07;
  pointer-events: none;
}

.frame--3-2 {
  aspect-ratio: 3 / 2;
}

.frame--16-10 {
  aspect-ratio: 16 / 10;
}

.frame--4-5 {
  aspect-ratio: 4 / 5;
}

.frame--21-9 {
  aspect-ratio: 21 / 9;
}

.hero__frame {
  position: relative;
}

.hero__ribbon {
  position: absolute;
  left: 0;
  right: 0;
  top: 45%;
  transform: translateY(-50%);
  z-index: 2;
  height: 40px;
  overflow: hidden;
  background: var(--accent);
  color: var(--accent-ink);
}

/* The ribbon marquee: the one permitted loop besides the estate light
   sweep. Two copies of the word list sit side by side; the track scrolls
   left by exactly half its width, so the seam is invisible. */
.ribbon-track {
  display: flex;
  align-items: center;
  height: 100%;
  width: max-content;
  animation: ribbon-scroll 40s linear infinite;
}

.ribbon-track__set {
  display: flex;
  align-items: center;
  flex: none;
}

.ribbon-track__set span {
  display: inline-flex;
  align-items: center;
  padding-inline: 20px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  white-space: nowrap;
}

.hero__ribbon:hover .ribbon-track,
.hero__ribbon:focus-within .ribbon-track {
  animation-play-state: paused;
}

@keyframes ribbon-scroll {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}

.hero__video {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  filter: grayscale(1) contrast(1.06) brightness(0.95);
}

.hero__diagram {
  width: 100%;
  height: auto;
  display: block;
}

/* ---------- Instrument row (home, below the hero) --------------------------------- */

.instrument-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
}

.instrument-row__item {
  padding-top: 16px;
  border-top: 1px solid var(--line-dark);
  padding-left: 0;
  border-left: 0;
  min-width: 0;
}

.instrument-row__value {
  font-family: var(--font-head);
  font-weight: 500;
  font-size: clamp(1.2rem, 1.8vw, 1.6rem);
  letter-spacing: -0.01em;
  line-height: 1.2;
  color: var(--text);
  max-width: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The clients cell is the one value long enough that it may need to wrap;
   it wraps only at the middots (a <wbr> follows each one in the markup). */
.instrument-row__value--wrap {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}

.instrument-row__label {
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted-dark);
  max-width: none;
}

@media (min-width: 640px) {
  .instrument-row {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--gutter) var(--gutter);
  }

  .instrument-row__item {
    padding-top: 0;
    padding-left: var(--gutter);
    border-top: 0;
    border-left: 1px solid var(--line-dark);
  }

  .instrument-row__item:nth-child(odd) {
    padding-left: 0;
    border-left: 0;
  }

  .instrument-row__item:nth-child(n + 3) {
    padding-top: 20px;
    border-top: 1px solid var(--line-dark);
  }
}

@media (min-width: 900px) {
  .instrument-row {
    grid-template-columns: repeat(4, 1fr);
  }

  .instrument-row__item:nth-child(n) {
    padding-top: 0;
    border-top: 0;
  }

  .instrument-row__item:nth-child(odd) {
    padding-left: var(--gutter);
    border-left: 1px solid var(--line-dark);
  }

  .instrument-row__item:first-child {
    padding-left: 0;
    border-left: 0;
  }
}

/* ---------- Client strip (home + About): full container width, like the
   service rows and process rail, so three equal columns always have room.
   Never 2 + 1; stacked only below 640px. ------------------------------- */

.client-strip__list {
  margin-top: 32px;
  display: grid;
  grid-template-columns: 1fr;
  border-top: 1px solid var(--line-dark);
}

.client-strip__name {
  padding: 16px 0;
  border-bottom: 1px solid var(--line-dark);
  font-family: var(--font-head);
  font-weight: 500;
  font-size: clamp(1.5rem, 2.6vw, 2.5rem);
  letter-spacing: -0.02em;
  color: var(--text);
  white-space: nowrap;
}

@media (min-width: 640px) {
  .client-strip__list {
    grid-template-columns: repeat(3, 1fr);
    border-bottom: 1px solid var(--line-dark);
  }

  .client-strip__name {
    padding: 24px 0 24px 28px;
    border-bottom: 0;
    border-left: 1px solid var(--line-dark);
  }

  .client-strip__name:first-child {
    padding-left: 0;
    border-left: 0;
  }
}

/* ---------- Section index counter (home only) --------------------------------------- */

.section-index {
  margin-top: 10px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--muted-dark);
}

/* ---------- Credibility media: list beside the night-exterior frame ----------------- */

.credibility-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 32px;
  align-items: start;
}

@media (min-width: 1000px) {
  .credibility-layout {
    grid-template-columns: 1.2fr 1fr;
    gap: var(--gutter);
  }
}

/* ---------- Three-up editorial gallery (ESL page) ------------------------------------ */

.gallery-3up {
  margin-top: 32px;
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

@media (min-width: 768px) {
  .gallery-3up {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* ---------- Staggered grid (brief §10b/§10c/§10d): text block / pattern tile /
   text block, repeated. Breaks out of .section-grid__content to full container
   width, like .service-rows and .process-rail. DOM order is the reading
   order (01 to 05, or 01 to 04 on inner pages); tiles are aria-hidden and
   sit between the step cells in source order, so a screen reader or
   keyboard user simply steps through 01, 02, 03... never through a tile.
   CSS grid auto-placement (no order/grid-column overrides) keeps the visual
   checkerboard and the DOM order identical at every breakpoint. --------- */

.stagger-grid {
  margin-top: 48px;
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
}

.stagger-grid__step {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 10px;
  padding: 28px 0;
  border-top: 1px solid var(--line-dark);
}

.stagger-grid__index {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: 0.08em;
  color: var(--muted-dark);
}

.stagger-grid__title {
  font-family: var(--font-head);
  font-weight: 500;
  font-size: 1.75rem;
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: var(--text);
}

.stagger-grid__text {
  color: var(--muted-dark);
  max-width: 32ch;
}

@media (min-width: 640px) {
  .stagger-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--gutter);
  }

  .stagger-grid__step {
    padding: 32px 0;
  }
}

@media (min-width: 900px) {
  .stagger-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* ---------- Pattern tiles (brief §10b): fine line-work SVGs (concentric
   arcs or angled parallel lines) as a background-image, one centred glyph
   in --text on top. Decorative: aria-hidden in markup, never a tab stop. */

.tile {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 140px;
  aspect-ratio: 3 / 2;
  border-radius: var(--radius-panel);
  border: 1px solid var(--line-dark);
  background-color: var(--panel);
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  transition: border-color 0.3s var(--ease);
}

.tile:hover {
  border-color: #2a2f38;
}

.tile__glyph {
  width: 40px;
  height: 40px;
  color: var(--text);
}

.tile--arcs-1 { background-image: url("/assets/img/tiles/tile-arcs-1.svg"); }
.tile--arcs-2 { background-image: url("/assets/img/tiles/tile-arcs-2.svg"); }
.tile--arcs-3 { background-image: url("/assets/img/tiles/tile-arcs-3.svg"); }
.tile--lines-1 { background-image: url("/assets/img/tiles/tile-lines-1.svg"); }
.tile--lines-2 { background-image: url("/assets/img/tiles/tile-lines-2.svg"); }
.tile--lines-3 { background-image: url("/assets/img/tiles/tile-lines-3.svg"); }

/* ---------- Card grids (brief §10b/§10c): the compliance pack and the
   three ways to engage. --panel surface, 1px --line-dark, 14px radius,
   generous padding, mono index top-left. No shadows, no gradients. ------ */

.pack-grid,
.engage-grid {
  margin-top: 40px;
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
}

@media (min-width: 640px) {
  .pack-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 900px) {
  .engage-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (min-width: 1000px) {
  .pack-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

.pack-card,
.engage-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 28px;
  background: var(--panel);
  border: 1px solid var(--line-dark);
  border-radius: var(--radius-panel);
}

.pack-card {
  min-height: 176px;
}

.pack-card__index,
.engage-card__index {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: 0.08em;
  color: var(--muted-dark);
}

.pack-card__title,
.engage-card__title {
  font-family: var(--font-head);
  font-weight: 500;
  font-size: 1.35rem;
  letter-spacing: -0.01em;
  color: var(--text);
}

.pack-card__text,
.engage-card__text {
  color: var(--muted-dark);
}

.pack-card__glyph {
  position: absolute;
  right: 22px;
  bottom: 22px;
  width: 22px;
  height: 22px;
  color: var(--muted-dark);
}

.engage-card__tag {
  position: absolute;
  top: 28px;
  right: 28px;
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accent);
}

.engage-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.engage-list li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: var(--fs-small);
  color: var(--muted-dark);
}

.engage-list .tag-mark {
  margin-top: 6px;
}

.engage-card .btn-primary {
  margin-top: auto;
  align-self: flex-start;
}

/* ---------- Sector strip (brief §10c, About page too): a horizontally
   scrollable row of .frame.frame--4-5 images with a mono label under each.
   Five across on desktop (no scroll needed at 1200px), three visible on
   tablet, about 1.3 visible with swipe on mobile. ------------------------ */

.sector-strip {
  margin-top: 40px;
  display: flex;
  gap: 16px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  padding-bottom: 6px;
  -webkit-overflow-scrolling: touch;
}

.sector-strip__item {
  flex: 0 0 76%;
  min-width: 0;
  scroll-snap-align: start;
}

.sector-strip__label {
  margin-top: 12px;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted-dark);
}

@media (min-width: 640px) {
  .sector-strip__item {
    flex-basis: calc((100% - 32px) / 3);
  }
}

@media (min-width: 1100px) {
  .sector-strip__item {
    flex-basis: calc((100% - 64px) / 5);
  }
}

/* ---------- Statement band (brief §10c): full-bleed frame, edge to edge of
   the viewport, with a flat 45% ink overlay (opacity, not a gradient) and
   the statement left-aligned over it. The overlay and text sit above the
   frame's own grain layer (z-index 2/3) so the copy stays legible while
   the photograph keeps the same monochrome-to-colour hover treatment as
   every other frame. -------------------------------------------------- */

.statement-band {
  padding-top: 0;
  padding-bottom: 0;
}

.statement-band__media {
  position: relative;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  aspect-ratio: 4 / 3;
  border-radius: 0;
}

@media (min-width: 900px) {
  .statement-band__media {
    aspect-ratio: 21 / 9;
  }
}

.statement-band__overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: var(--ink);
  opacity: 0.45;
  pointer-events: none;
}

/* A plain, non-positioned .container sits inside this flex wrapper so its
   own max-width + auto margins centre it the ordinary way, rather than
   relying on the width/margin over-constrained edge case for absolutely
   positioned boxes. */
.statement-band__content {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  pointer-events: none;
}

.statement-band__text {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.statement-band__text > * {
  max-width: 34ch;
}

.statement-band__text .eyebrow {
  color: var(--muted-dark);
}

.statement-band__text p {
  color: var(--text);
  max-width: 46ch;
}

/* ---------- Service rows (home) ------------------------------------------------------------ */

.service-rows {
  margin-top: 56px;
}

.service-row {
  border-top: 1px solid var(--line-dark);
  padding-block: 48px;
}

.service-row:last-child {
  border-bottom: 1px solid var(--line-dark);
}

.service-row__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
}

.service-row__number {
  font-family: var(--font-head);
  font-weight: 500;
  font-size: 3rem;
  line-height: 1;
  color: var(--muted-dark);
  letter-spacing: -0.02em;
}

.service-row__main {
  display: grid;
  grid-template-columns: 1fr;
  gap: 28px;
}

.service-row__text > * + * {
  margin-top: 14px;
}

.service-row__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.tag-pill {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 12px;
  border: 1px solid var(--line-dark);
  border-radius: var(--radius-pill);
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted-dark);
}

.service-row__cta {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin-top: 20px;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text);
}

.service-row__cta-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  flex: none;
  background: var(--accent);
  color: var(--accent-ink);
  border-radius: var(--radius-btn);
  font-weight: 700;
  transition: background-color 0.15s var(--ease);
}

.service-row__cta:hover .service-row__cta-arrow {
  background: var(--text);
}

@media (min-width: 900px) {
  .service-row__grid {
    grid-template-columns: 96px 1fr;
    column-gap: var(--gutter);
    align-items: start;
  }
}

@media (min-width: 1200px) {
  .service-row__main {
    grid-template-columns: 1fr 1fr;
    column-gap: var(--gutter);
    align-items: center;
  }
}

/* ---------- Credibility list ------------------------------------------------------------------ */

.credibility-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: 28px;
}

.credibility-list__item p {
  color: var(--muted-dark);
  max-width: 44ch;
}

.credibility-list__item strong {
  color: var(--text);
  font-weight: 600;
}

@media (min-width: 560px) {
  .credibility-list {
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    column-gap: var(--gutter);
    row-gap: 32px;
  }
}

/* ---------- Process rail (full-width, breaks out of section-grid__content) ------------------------ */

.process-rail {
  margin-top: 56px;
}

.process-rail__list {
  display: grid;
  grid-template-columns: 1fr;
  border-top: 1px solid var(--line-dark);
}

.process-rail__step {
  padding: 24px 0;
  border-bottom: 1px solid var(--line-dark);
}

.process-rail__number {
  font-family: var(--font-head);
  font-weight: 500;
  font-size: 1.5rem;
  color: var(--muted-dark);
}

.process-rail__title {
  margin-top: 10px;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 1.0625rem;
}

.process-rail__text {
  margin-top: 8px;
  font-size: var(--fs-small);
  color: var(--muted-dark);
}

@media (min-width: 640px) {
  .process-rail__list {
    grid-template-columns: repeat(3, 1fr);
    border-bottom: 1px solid var(--line-dark);
  }

  .process-rail__step {
    padding: 24px;
    border-bottom: 0;
  }

  .process-rail__step:first-child,
  .process-rail__step:nth-child(4) {
    padding-left: 0;
  }

  .process-rail__step:nth-child(2),
  .process-rail__step:nth-child(3),
  .process-rail__step:nth-child(5) {
    border-left: 1px solid var(--line-dark);
  }
}

@media (min-width: 1200px) {
  .process-rail__list {
    grid-template-columns: repeat(5, 1fr);
  }

  .process-rail__step:nth-child(4) {
    padding-left: 24px;
    border-left: 1px solid var(--line-dark);
  }
}

/* ---------- Definition list (generic) -------------------------------------------------------------- */

.dl__row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 20px;
  padding: 16px 0;
  border-bottom: 1px solid var(--line-dark);
}

.dl__term {
  flex: 0 0 200px;
  max-width: 100%;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted-dark);
}

.dl__desc {
  flex: 1 1 240px;
  font-weight: 500;
  color: var(--text);
  text-align: left;
  max-width: none;
}

/* ---------- Verify block: h2 + p + dl + button stacked full width in
   .section-grid__content, no extra wrapper needed. On the About page the
   same block uses .verify-block__columns for a 2-col layout instead. ---- */

.verify-block__cta {
  white-space: nowrap;
  width: max-content;
  max-width: 100%;
}

.verify-block__columns {
  display: grid;
  grid-template-columns: 1fr;
  gap: 32px;
}

.verify-block__columns > * + * {
  margin-top: 0;
}

.verify-block__intro > * + * {
  margin-top: 16px;
}

.verify-block__list {
  margin-bottom: 24px;
}

@media (min-width: 800px) {
  .verify-block__columns {
    grid-template-columns: 1fr 1fr;
    gap: var(--gutter);
  }
}

/* ---------- Footer ------------------------------------------------------------------------------------ */

.site-footer {
  background: var(--ink);
  color: var(--muted-dark);
  border-top: 1px solid var(--line-dark);
  padding-block: clamp(56px, 8vw, 88px) 32px;
  overflow: hidden;
}

.site-footer__inner {
  display: grid;
  grid-template-columns: 1fr;
  gap: 40px;
}

.footer-col__title {
  color: var(--text);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--fs-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 18px;
}

.footer-col p {
  color: var(--muted-dark);
  max-width: 32ch;
}

.footer-col ul li {
  padding: 6px 0;
}

.footer-col--brand .site-header__logo {
  margin-bottom: 16px;
}

.footer-wordmark {
  margin-top: 64px;
  font-family: var(--font-head);
  font-weight: 500;
  font-size: clamp(3rem, 12vw, 10rem);
  line-height: 0.9;
  letter-spacing: -0.03em;
  color: var(--muted-dark);
  opacity: 0.4;
  white-space: nowrap;
  overflow: hidden;
}

.site-footer__bottom {
  margin-top: 40px;
  padding-top: 24px;
  border-top: 1px solid var(--line-dark);
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.site-footer__legal {
  max-width: 90ch;
  color: var(--muted-dark);
  text-transform: none;
  letter-spacing: normal;
  font-family: var(--font-body);
  font-size: var(--fs-small);
}

@media (min-width: 900px) {
  .site-footer__inner {
    grid-template-columns: repeat(4, 1fr);
    gap: var(--gutter);
  }

  .site-footer__bottom {
    flex-direction: row;
    justify-content: space-between;
    align-items: baseline;
  }
}

/* ---------- Page hero (inner pages) ------------------------------------------------------------------- */

.page-hero {
  background: var(--ink);
}

.page-hero__inner {
  padding-block: clamp(56px, 8vw, 96px) clamp(40px, 6vw, 64px);
}

.page-hero__title {
  margin-top: 20px;
  max-width: 22ch;
}

.page-hero__lead {
  margin-top: 20px;
}

/* ---------- Scope list -------------------------------------------------------------------------------- */

.scope-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

.scope-list__item {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  padding-top: 14px;
  border-top: 1px solid var(--line-dark);
}

.scope-list__item .tag-mark {
  margin-top: 7px;
}

@media (min-width: 640px) {
  .scope-list {
    grid-template-columns: 1fr 1fr;
    column-gap: var(--gutter);
  }
}

/* ---------- FAQ accordion -------------------------------------------------------------------------------- */

.faq-list {
  border-top: 1px solid var(--line-dark);
}

.faq-item {
  border-bottom: 1px solid var(--line-dark);
}

.faq-item__summary {
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 22px 0;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 1.0625rem;
  cursor: pointer;
}

.faq-item__summary::-webkit-details-marker {
  display: none;
}

.faq-item__summary::after {
  content: "";
  flex: none;
  width: 9px;
  height: 9px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.2s var(--ease);
}

.faq-item[open] .faq-item__summary::after {
  transform: rotate(225deg);
}

.faq-item__answer {
  padding-bottom: 24px;
}

.faq-item__answer p {
  color: var(--muted-dark);
}

/* ---------- Contact form ------------------------------------------------------------------------------------ */

.form-panel {
  padding: clamp(24px, 4vw, 40px);
  margin-top: 40px;
}

.contact-form {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
}

.form-field {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.form-label {
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted-dark);
}

.form-input,
.form-select,
.form-textarea {
  height: 48px;
  padding: 0 14px;
  background: var(--ink-2);
  color: var(--text);
  border: 1px solid var(--line-dark);
  border-radius: var(--radius-btn);
  width: 100%;
}

.form-textarea {
  height: auto;
  min-height: 140px;
  padding: 12px 14px;
  resize: vertical;
}

.form-input:focus,
.form-select:focus,
.form-textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-color: var(--accent);
}

.form-hint {
  color: var(--muted-dark);
  font-size: var(--fs-small);
}

.form-error {
  color: var(--danger);
  font-size: var(--fs-small);
  display: none;
}

.form-field.is-invalid .form-error {
  display: block;
}

.form-field.is-invalid .form-input,
.form-field.is-invalid .form-select,
.form-field.is-invalid .form-textarea {
  border-color: var(--danger);
}

.form-consent {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.form-consent input {
  margin-top: 4px;
  flex: none;
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
}

.form-consent label {
  font-size: var(--fs-small);
  color: var(--muted-dark);
}

.form-hp {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  left: -9999px;
}

.form-actions {
  margin-top: 8px;
}

.form-status {
  padding: 14px 16px;
  border-radius: var(--radius-btn);
  font-size: var(--fs-small);
  font-weight: 500;
  display: none;
}

.form-status.is-visible {
  display: block;
}

.form-status--success {
  background: var(--ink-2);
  border: 1px solid var(--line-dark);
  color: var(--text);
}

.form-status--error {
  background: var(--ink-2);
  border: 1px solid var(--danger);
  color: var(--danger);
}

@media (min-width: 640px) {
  .contact-form {
    grid-template-columns: 1fr 1fr;
  }

  .form-field--full,
  .form-consent,
  .form-actions,
  .form-status {
    grid-column: 1 / -1;
  }
}

/* ---------- Photo placeholder (About, later wave) ---------------------------------------------------------------- */

.photo-placeholder {
  aspect-ratio: 4 / 5;
  background: var(--panel);
  border: 1px solid var(--line-dark);
  border-radius: var(--radius-panel);
  display: flex;
  align-items: center;
  justify-content: center;
}

.photo-placeholder .tag-mark {
  width: 32px;
  height: 20px;
  border-radius: var(--radius-mark);
}

/* ---------- 404 ------------------------------------------------------------------------------------------------------ */

.error-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 14px;
  margin-top: 32px;
}

.error-links li:not(:first-child)::before {
  content: "\00b7";
  margin-right: 14px;
  color: var(--muted-dark);
}

.error-links li {
  display: flex;
}

/* ---------- Estate canvas (home hero, Three.js field, brief §9b) -------------------- */

/* Scoped to .hero__content (the headline column only, not the frame
   below it) so it can never draw over the hero video/still: negative
   z-index keeps it behind the static text within that one stacking
   context (.hero__content already establishes one via z-index:2). */
.estate {
  position: absolute;
  inset: 0;
  z-index: -1;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out);
}

.estate.is-ready {
  opacity: 1;
}

@media (min-width: 900px) {
  .estate {
    left: 45%;
    width: 55%;
  }
}

@media (max-width: 899px) {
  .estate {
    opacity: 0.45;
  }

  .estate.is-ready {
    opacity: 0.45;
  }
}

.hero__content {
  position: relative;
  z-index: 2;
}

/* ---------- View transitions (brief §9a) --------------------------------------------- */

@view-transition {
  navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 220ms;
  animation-timing-function: var(--ease-out);
}

/* ---------- Global grain (brief §9a): one fixed full-viewport noise layer, in
   addition to the per-frame grain above. Off on Save-Data (html.save-data,
   set by site.js from the Save-Data header / navigator.connection.saveData). */

body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  mix-blend-mode: overlay;
  opacity: 0.035;
}

html.save-data body::after {
  display: none;
}

/* ---------- Parallax hooks: .frame__media wraps the picture/video inside a
   parallaxed frame so the scroll-linked translateY (site.js) and the CSS
   hover-scale on the media itself never fight over the same transform. --- */

.frame__media {
  display: block;
  width: 100%;
  height: 100%;
}

.footer-wordmark {
  will-change: auto;
}

@media (max-width: 767px) {
  /* Parallax is off below 768px (brief §9a). */
  .frame__media {
    transform: none !important;
  }
}

/* ---------- Reduced motion + Save-Data (brief §9a: the whole motion layer switches
   off; the site must be complete and everything visible without it) ---------------- */

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

  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  /* Belt-and-braces: force-reveal anything that might carry .reveal-armed
     (site.js already skips adding it at all when motion is off). */
  .reveal-armed,
  .reveal-armed.is-inview {
    opacity: 1 !important;
    transform: none !important;
    clip-path: inset(0) !important;
  }

  .reveal-armed .line__inner {
    transform: none !important;
  }

  .reveal-armed picture,
  .reveal-armed img,
  .reveal-armed video {
    transform: none !important;
  }

  .estate {
    display: none;
  }

  body::after {
    display: none;
  }
}

/* html.no-motion is set by site.js in browsers where the media query above
   already applies; it exists so JS-driven inline transforms (parallax,
   cursor tilt) can check one flag instead of re-querying matchMedia. */
html.no-motion .estate {
  display: none;
}


/* ---------- Logo mark: the swift (brand decision 7 Sep 2026) ---------- */
/* Replaces the 28x18 tag rectangle in the header and footer lockups. The
   tag rectangle remains the eyebrow marker and bullet elsewhere. */
.logo__mark--swift {
  width: auto;
  height: 26px;
  flex: 0 0 auto;
}
.site-header.is-compact .logo__mark--swift { height: 23px; }
.site-footer .logo__mark--swift { height: 24px; }
/* The lockup is a link; it never takes the text underline. */
footer .logo, .site-footer .logo, .site-header .logo,
footer .logo:hover, .site-footer .logo:hover, .site-header .logo:hover {
  text-decoration: none;
}

/* Logo lockups are links but never underline (footer link rule otherwise wins). */
.site-header a.logo, .site-footer a.logo, .site-header a.logo:hover, .site-footer a.logo:hover,
.site-header a.logo:focus-visible, .site-footer a.logo:focus-visible {
  text-decoration: none;
}
/* Specificity match for .site-footer a:not(.btn-primary):not(.btn-secondary); later wins. */
.site-footer a.logo.logo, .site-footer a.logo.logo:hover, .site-footer a.logo.logo:focus-visible {
  text-decoration: none;
}
