/* ==========================================================================
   Quantumtec - stylesheet
   --------------------------------------------------------------------------
   Organised with @layer so specificity stays predictable: anything in a later
   layer beats an earlier one regardless of selector weight, which means no
   !important and no selector arms races.
   ========================================================================== */

@layer reset, tokens, base, layout, components, utilities;

/* --------------------------------------------------------------------------
   Font - self-hosted variable font. One 32 KB file covers weights 200-700,
   so there is no per-weight request and no third-party connection.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Outfit";
  src: url("../fonts/outfit-latin.woff2") format("woff2");
  font-weight: 200 700;          /* variable weight axis */
  font-style: normal;
  font-display: swap;            /* text paints immediately in the fallback */
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2191,
                 U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ========================================================================== */
@layer reset {
  *, *::before, *::after { box-sizing: border-box; }

  * { margin: 0; }

  html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    scroll-padding-block-start: calc(var(--header-h) + 1rem);
  }

  body {
    min-block-size: 100svb;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
  }

  img, picture, svg, video {
    display: block;
    max-inline-size: 100%;
    block-size: auto;
  }

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

  /* Long words and URLs should never force a horizontal scrollbar */
  p, li, h1, h2, h3, h4, dt, dd { overflow-wrap: break-word; }

  :where(ul, ol)[role="list"] { list-style: none; padding: 0; }

  @media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
      animation-duration: .01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: .01ms !important;
      scroll-behavior: auto !important;
    }
  }
}

/* ========================================================================== */
@layer tokens {
  :root {
    color-scheme: light dark;

    /* -- brand ----------------------------------------------------------- */
    /* Quantumtec Brand Guidelines (2026). The two fixed points below are the
       published hex values, converted to oklch - everything else on the ramp
       is interpolated between and around them, which is why oklch is used:
       lightness stays perceptually even, so the mid-steps do not muddy the way
       an interpolated hex ramp does.

           Anchor Navy  #07142A  ->  oklch(19.4% .048 260)   navy-900
           Deep Blue    #26317E  ->  oklch(35.4% .130 272)   navy-700

       Accent Indigo (#6861F9) is deliberately ABSENT. The guide reserves it
       for print and document collateral - the business card, letterhead and
       Word template - and never lists it for web. It is the most eye-catching
       swatch in the palette, so this note exists to stop a later pass reaching
       for it. Deep Blue is the web accent. */
    --navy-900: oklch(19.4% .048 260);  /* Anchor Navy - hero, footer, dark bg */
    --navy-800: oklch(26% .090 268);
    --navy-700: oklch(35.4% .130 272);  /* Deep Blue - links, buttons, the mark */
    --navy-600: oklch(44% .140 272);
    --navy-500: oklch(54% .135 273);
    --navy-200: oklch(88% .050 272);
    --navy-100: oklch(95% .020 272);

    /* Light tints of Deep Blue, for accents sitting ON the Anchor Navy
       sections, where Deep Blue itself (35% lightness) is far too dark to
       read. Same hue as Deep Blue rather than a new colour; chroma is held
       well below Accent Indigo's .219 so these read as the brand blue
       lightened, not as Indigo leaking onto the web. */
    --brand-300: oklch(82% .085 272);   /* hero particle glow */
    --brand-400: oklch(75% .110 272);   /* hero eyebrow, tick marks */

    --ink-950: oklch(17% .02 265);
    --ink-900: oklch(23% .02 265);
    --ink-700: oklch(42% .02 265);
    --ink-500: oklch(58% .015 265);
    --ink-300: oklch(80% .01 265);
    --ink-150: oklch(92% .006 265);
    --ink-100: oklch(96% .004 265);
    --white: oklch(100% 0 0);

    /* -- semantic (light-dark resolves against color-scheme) -------------- */
    /* Off-White #F6F8FD is the guide's content-section colour; the dark theme
       is anchored on Anchor Navy with raised/sunken steps either side of it,
       so both themes sit on published brand values rather than near-misses. */
    --bg:          light-dark(var(--white),         oklch(19.4% .048 260));
    --bg-sunken:   light-dark(oklch(97.9% .007 269), oklch(15% .040 260));
    --bg-raised:   light-dark(var(--white),         oklch(24% .050 264));
    --bg-invert:   light-dark(var(--navy-900),      oklch(13% .035 260));

    --text:        light-dark(var(--ink-950),   oklch(95% .008 265));
    --text-muted:  light-dark(var(--ink-700),   oklch(74% .012 265));
    --text-invert: light-dark(var(--white),     oklch(96% .006 265));

    /* --brand is a FOREGROUND colour: links, current nav item, arrows. It has
       to stay light in dark mode or body links stop being legible against the
       navy, so the dark value is a lightened brand blue rather than Deep Blue
       itself. */
    --brand:       light-dark(var(--navy-700),  oklch(72% .13 272));
    --brand-hover: light-dark(var(--navy-800),  oklch(80% .11 272));

    /* Deep Blue as a FILL sitting behind white text - the primary button and
       the skip link - and deliberately the same in both themes, which is the
       look Sandeep picked from the light-mode header.
       It cannot just reuse --brand: as a fill in dark mode that gave white text
       on the lightened blue at 2.66:1, under the 4.5:1 AA floor. White on Deep
       Blue measures 11.52:1.
       The edge exists because Deep Blue against the near-black dark header is
       only 1.60:1, so the filled shape would otherwise dissolve into the bar -
       WCAG 1.4.11 asks 3:1 for a UI component boundary. On light the white
       header already supplies that contrast, so no edge is drawn. */
    --brand-solid:       var(--navy-700);
    --brand-solid-hover: var(--navy-600);
    --brand-solid-edge:  light-dark(transparent, var(--brand-400));
    --accent:      light-dark(var(--navy-700), var(--brand-400));

    --border:      light-dark(var(--ink-150),   oklch(30% .02 274));
    --border-strong: light-dark(var(--ink-300), oklch(38% .02 274));

    /* -- fluid type ------------------------------------------------------ */
    /* clamp() replaces breakpoint-stepped font sizes: type scales smoothly
       with the viewport, so there is no "awkward width" between breakpoints. */
    --step--1: clamp(.83rem, .80rem + .16vi, .94rem);
    --step-0:  clamp(1rem, .96rem + .21vi, 1.13rem);
    --step-1:  clamp(1.2rem, 1.13rem + .35vi, 1.42rem);
    --step-2:  clamp(1.44rem, 1.33rem + .55vi, 1.8rem);
    --step-3:  clamp(1.73rem, 1.55rem + .87vi, 2.27rem);
    --step-4:  clamp(2.07rem, 1.81rem + 1.32vi, 2.87rem);
    --step-5:  clamp(2.49rem, 2.10rem + 1.94vi, 3.63rem);
    --step-6:  clamp(2.99rem, 2.43rem + 2.79vi, 4.6rem);

    /* -- fluid space ----------------------------------------------------- */
    --space-3xs: clamp(.25rem, .24rem + .06vi, .31rem);
    --space-2xs: clamp(.5rem, .48rem + .11vi, .56rem);
    --space-xs:  clamp(.75rem, .72rem + .16vi, .88rem);
    --space-s:   clamp(1rem, .96rem + .21vi, 1.13rem);
    --space-m:   clamp(1.5rem, 1.44rem + .32vi, 1.69rem);
    --space-l:   clamp(2rem, 1.91rem + .43vi, 2.25rem);
    --space-xl:  clamp(3rem, 2.87rem + .64vi, 3.38rem);
    --space-2xl: clamp(4rem, 3.83rem + .85vi, 4.5rem);
    --space-3xl: clamp(6rem, 5.74rem + 1.28vi, 6.75rem);

    /* -- layout ---------------------------------------------------------- */
    --measure: 68ch;               /* comfortable reading line length */
    --content: 76rem;              /* max page width */
    --gutter: clamp(1.25rem, 4vi, 3.5rem);
    --header-h: 4.5rem;
    --radius: 14px;
    --radius-lg: 22px;

    --shadow-sm: 0 1px 2px oklch(20% .03 274 / .06),
                 0 2px 8px oklch(20% .03 274 / .05);
    --shadow-md: 0 2px 4px oklch(20% .03 274 / .06),
                 0 12px 28px oklch(20% .03 274 / .09);
    --shadow-lg: 0 4px 8px oklch(20% .03 274 / .07),
                 0 24px 56px oklch(20% .03 274 / .13);

    --ease-out: cubic-bezier(.22, 1, .36, 1);
    --transition: 240ms var(--ease-out);
  }

  /* Respect an explicit user choice from the theme toggle over the OS setting */
  :root[data-theme="light"] { color-scheme: light; }
  :root[data-theme="dark"]  { color-scheme: dark; }
}

/* ========================================================================== */
@layer base {
  body {
    font-family: "Outfit", ui-sans-serif, system-ui, -apple-system,
                 "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
    font-size: var(--step-0);
    font-weight: 350;
    line-height: 1.65;
    color: var(--text);
    background: var(--bg);
    /* Opsz/letter-spacing tuned once here rather than per-component */
    letter-spacing: -0.005em;
  }

  h1, h2, h3, h4 {
    font-weight: 400;
    line-height: 1.1;
    letter-spacing: -0.025em;
    text-wrap: balance;            /* avoids a lone word on the last line */
    color: var(--text);
  }

  h1 { font-size: var(--step-6); }
  h2 { font-size: var(--step-4); }
  h3 { font-size: var(--step-2); }
  h4 { font-size: var(--step-1); }

  p { text-wrap: pretty; }         /* prevents orphans without ragged lines */

  a {
    color: var(--brand);
    text-decoration-thickness: 1px;
    text-underline-offset: .22em;
    transition: color var(--transition);
  }
  a:hover { color: var(--brand-hover); }

  /* One visible focus style everywhere, only for keyboard users. */
  :focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 3px;
    border-radius: 4px;
  }

  ::selection {
    background: color-mix(in oklch, var(--brand) 25%, transparent);
  }

  strong, b { font-weight: 600; }

  hr {
    border: 0;
    border-block-start: 1px solid var(--border);
    margin-block: var(--space-xl);
  }
}

/* ========================================================================== */
@layer layout {
  /* A single grid that every full-width section opts into. Children land in
     the centred content column by default; `full` and `wide` break out.
     This replaces the wrapper-div-inside-wrapper-div pattern entirely. */
  .layout-grid {
    display: grid;
    grid-template-columns:
      [full-start] minmax(var(--gutter), 1fr)
      [wide-start] minmax(0, 4rem)
      [content-start] min(100% - var(--gutter) * 2, var(--content))
      [content-end] minmax(0, 4rem)
      [wide-end] minmax(var(--gutter), 1fr)
      [full-end];
  }
  .layout-grid > * { grid-column: content; }
  .layout-grid > .full { grid-column: full; }
  .layout-grid > .wide { grid-column: wide; }

  .section { padding-block: var(--space-3xl); }
  .section--tight { padding-block: var(--space-xl); }
  /* Keep the landing page brisk without compressing long-form inner pages. */
  .home > .section { padding-block: var(--space-2xl); }

  .stack > * + * { margin-block-start: var(--flow, var(--space-m)); }

  .prose { max-inline-size: var(--measure); }
  .prose > * + * { margin-block-start: 1.1em; }
  .prose h2 { margin-block-start: 1.8em; }
  .prose h3 { margin-block-start: 1.5em; }

  /* Auto-fitting grid: the number of columns is decided by available space,
     not by hard-coded breakpoints. */
  .auto-grid {
    display: grid;
    gap: var(--gap, var(--space-m));
    grid-template-columns: repeat(auto-fit, minmax(min(var(--min, 17rem), 100%), 1fr));
  }

  .cluster {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap, var(--space-s));
    align-items: center;
  }
}

/* ========================================================================== */
@layer components {

  /* -- skip link ------------------------------------------------------- */
  .skip-link {
    position: absolute;
    inset-block-start: var(--space-2xs);
    inset-inline-start: var(--space-2xs);
    z-index: 100;
    padding: var(--space-2xs) var(--space-s);
    background: var(--brand-solid);
    border: 1px solid var(--brand-solid-edge);
    color: var(--white);
    border-radius: var(--radius);
    font-weight: 500;
    text-decoration: none;
    translate: 0 -160%;
    transition: translate var(--transition);
  }
  .skip-link:focus-visible { translate: 0 0; }

  /* -- header ---------------------------------------------------------- */
  .site-header {
    position: sticky;
    inset-block-start: 0;
    z-index: 50;
    background: color-mix(in oklch, var(--bg) 82%, transparent);
    border-block-end: 1px solid transparent;
    transition: border-color var(--transition), background var(--transition);
  }
  @supports (backdrop-filter: blur(1px)) {
    .site-header { backdrop-filter: blur(14px) saturate(1.4); }
  }
  .site-header[data-scrolled] {
    border-block-end-color: var(--border);
    background: color-mix(in oklch, var(--bg) 92%, transparent);
  }

  .site-header__inner {
    min-block-size: var(--header-h);
    display: flex;
    align-items: center;
    gap: var(--space-m);
  }

  .brand { display: inline-flex; align-items: center; flex-shrink: 0; }
  .brand img { inline-size: clamp(9rem, 14vi, 11.5rem); block-size: auto; }
  /* The wordmark is dark navy artwork; invert it for dark mode rather than
     shipping a second file. */
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .brand img {
      filter: brightness(0) invert(1);
    }
  }
  :root[data-theme="dark"] .brand img {
    filter: brightness(0) invert(1);
  }

  .nav { margin-inline-start: auto; }

  .header-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2xs);
    margin-inline-start: var(--space-s);
  }
  .nav__list {
    display: flex;
    align-items: center;
    /* Tighter than before: the bar now carries 7 top-level items rather than
       4, so the gap has to give way before the labels do. */
    gap: clamp(.35rem, 1.1vi, 1.1rem);
    list-style: none;
    margin: 0; padding: 0;
  }
  .nav__link {
    display: inline-block;
    padding: var(--space-2xs) var(--space-3xs);
    font-size: var(--step--1);
    font-weight: 500;
    letter-spacing: .01em;
    color: var(--text);
    text-decoration: none;
    position: relative;
  }
  .nav__link::after {
    content: "";
    position: absolute;
    inset-inline: var(--space-3xs);
    inset-block-end: 0;
    block-size: 2px;
    background: var(--accent);
    scale: 0 1;
    transform-origin: left;
    transition: scale var(--transition);
  }
  .nav__link:hover::after,
  .nav__link[aria-current]::after { scale: 1 1; }
  .nav__link[aria-current="page"] { color: var(--brand); }
  /* An ancestor section of the current page: present, but quieter than the
     exact page so the two states stay distinguishable. */
  .nav__link.is-section { color: var(--brand); }
  .nav__link.is-section::after { opacity: .45; }

  /* Services dropdown - CSS-only, opens on hover and on keyboard focus */
  .nav__item--has-menu { position: relative; }
  .nav__menu {
    position: absolute;
    inset-block-start: calc(100% + .5rem);
    inset-inline-start: 50%;
    translate: -50% 0;
    min-inline-size: 20rem;
    max-inline-size: min(26rem, 90vw);
    padding: var(--space-xs);
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    list-style: none;
    margin: 0;
    display: grid;
    gap: 2px;
    opacity: 0;
    visibility: hidden;
    translate: -50% -6px;
    transition: opacity var(--transition), translate var(--transition),
                visibility var(--transition);
  }
  .nav__item--has-menu:hover .nav__menu,
  .nav__item--has-menu:focus-within .nav__menu {
    opacity: 1;
    visibility: visible;
    translate: -50% 0;
  }
  .nav__menu a {
    display: block;
    padding: var(--space-xs) var(--space-s);
    border-radius: 9px;
    font-size: var(--step-0);
    line-height: 1.35;
    text-decoration: none;
    color: var(--text);
  }
  .nav__menu a:hover { background: var(--bg-sunken); color: var(--brand); }
  .nav__menu a[aria-current="page"] {
    color: var(--brand);
    background: color-mix(in oklch, var(--brand) 8%, transparent);
  }

  /* "All <section>" row at the top of each dropdown - the section's own page
     stays reachable once the parent link is also a menu trigger. */
  .nav__menu-head {
    border-block-end: 1px solid var(--border);
    margin-block-end: var(--space-3xs);
    padding-block-end: var(--space-3xs);
  }
  .nav__menu-head a {
    display: flex;
    align-items: center;
    gap: .5em;
    font-weight: 550;
    color: var(--brand);
    white-space: nowrap;
  }
  .nav__menu-head a:hover .btn__arrow { translate: 3px 0; }


  /* -- buttons --------------------------------------------------------- */
  .btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2xs);
    padding: .72em 1.5em;
    border: 1px solid transparent;
    border-radius: 999px;
    font-size: var(--step--1);
    font-weight: 500;
    letter-spacing: .01em;
    text-decoration: none;
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--transition), color var(--transition),
                border-color var(--transition), box-shadow var(--transition),
                translate var(--transition);
  }
  .btn:hover { translate: 0 -2px; }
  .btn:active { translate: 0 0; }

  .btn--primary {
    background: var(--brand-solid);
    border-color: var(--brand-solid-edge);
    color: var(--white);
    box-shadow: var(--shadow-sm);
  }
  .btn--primary:hover {
    background: var(--brand-solid-hover);
    color: var(--white);
    box-shadow: var(--shadow-md);
  }

  .btn--ghost { border-color: var(--border-strong); color: var(--text); }
  .btn--ghost:hover {
    border-color: var(--brand);
    color: var(--brand);
    background: color-mix(in oklch, var(--brand) 6%, transparent);
  }

  .btn--on-dark {
    background: var(--white);
    color: var(--navy-900);
  }
  .btn--on-dark:hover { background: var(--navy-100); color: var(--navy-900); }

  .btn--lg { padding: .85em 1.9em; font-size: var(--step-0); }

  /* Arrow that slides on hover - the original had a static circled arrow.
     display:inline-block overrides the reset's svg{display:block}, which
     otherwise drops the arrow onto its own line in any non-flex container.
     flex:none stops it being squashed inside flex parents. */
  .btn__arrow {
    display: inline-block;
    flex: none;
    vertical-align: -0.12em;
    transition: translate var(--transition);
  }
  .btn:hover .btn__arrow { translate: 3px 0; }


  /* -- hero ------------------------------------------------------------ */
  .hero {
    position: relative;
    display: grid;
    isolation: isolate;
    background: var(--bg-invert);
    color: var(--text-invert);
    overflow: clip;
  }
  .hero__media {
    position: absolute;
    inset: 0;
    z-index: -2;
  }
  .hero__media img {
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
  }
  /* Two-stop scrim: keeps text contrast above 4.5:1 wherever the photo is
     bright, without flattening the image into mud. */
  .hero__media::after {
    content: "";
    position: absolute;
    inset: 0;
    background:
      linear-gradient(to right,
        oklch(15% .03 260 / .92) 0%,
        oklch(15% .03 260 / .72) 45%,
        oklch(15% .03 260 / .35) 100%),
      linear-gradient(to top, oklch(15% .03 260 / .75), transparent 55%);
  }
  .hero__inner {
    padding-block: clamp(var(--space-l), 5.5vb, var(--space-2xl));
    display: grid;
    align-content: center;
    /* Tighter than the --space-m used elsewhere: measured, the old rhythm put
       the proof row at 744px, below the fold on a 720px-tall viewport, which
       defeated the point of moving it up there. The saving comes from the gaps
       and padding rather than from shrinking any of the content. */
    gap: var(--space-s);
    /* Was 74svb / 44rem max. The hero now carries a phone number and three
       proof points, so keeping the old height would have pushed the logo band
       and the stats section further below the fold - the opposite of the point.
       Trimmed so the whole offer, both CTAs and the proof fit on the first
       screen on a laptop, with the logo strip just breaking the fold as a cue
       that there is more. */
    min-block-size: clamp(22rem, 56svb, 34rem);
  }
  .hero h1, .hero h2 { color: var(--text-invert); }

  .hero__eyebrow {
    font-size: var(--step--1);
    font-weight: 500;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--brand-400);
  }
  .hero__lede {
    max-inline-size: 46ch;
    font-size: var(--step-1);
    font-weight: 300;
    color: oklch(92% .01 265);
  }
  .hero__actions { margin-block-start: var(--space-s); }

  /* Click-to-call. Sits directly under the buttons because the visitor with an
     urgent problem is the one most likely to convert and least likely to fill
     in a form - and the number was previously not on the first screen at all.
     A real <a href="tel:"> so it dials on a phone and is copyable on desktop. */
  .hero__tel {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2xs) var(--space-s);
    margin-block-start: var(--space-xs);
  }
  .hero__tel a {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2xs);
    font-size: var(--step-1);
    font-weight: 500;
    color: var(--text-invert);
    text-decoration: none;
  }
  .hero__tel a:hover,
  .hero__tel a:focus-visible { text-decoration: underline; }
  .tel__icon { flex: none; }
  .hero__hours {
    font-size: var(--step--1);
    color: oklch(84% .01 265);
  }

  /* Proof above the fold. The stats section and the certifications list both
     already exist, but a visitor who does not scroll never sees either. */
  .hero__trust {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2xs) var(--space-m);
    margin-block-start: var(--space-s);
    padding-block-start: var(--space-s);
    border-block-start: 1px solid oklch(100% 0 0 / .18);
    font-size: var(--step--1);
    color: oklch(88% .01 265);
  }
  .hero__trust li {
    display: flex;
    align-items: center;
    gap: var(--space-2xs);
  }
  /* Tick drawn in CSS rather than 3 more inline SVGs - it is decorative, and
     the list already reads correctly without it. */
  .hero__trust li::before {
    content: "";
    inline-size: .95em;
    block-size: .5em;
    flex: none;
    border-inline-start: 2px solid var(--brand-400);
    border-block-end: 2px solid var(--brand-400);
    rotate: -45deg;
    translate: 0 -.15em;
  }

  /* Compact hero for interior pages */
  .hero--page .hero__inner { min-block-size: clamp(18rem, 46svb, 26rem); }
  .hero--page h1 { font-size: var(--step-5); }

  /* -- breadcrumbs ----------------------------------------------------- */
  .crumbs { font-size: var(--step--1); color: oklch(85% .01 265); }
  .crumbs ol {
    display: flex; flex-wrap: wrap; gap: .5em;
    list-style: none; margin: 0; padding: 0;
  }
  .crumbs li + li::before { content: "/"; margin-inline-end: .5em; opacity: .55; }
  .crumbs a { color: inherit; text-decoration: none; }
  .crumbs a:hover { color: var(--white); text-decoration: underline; }
  .crumbs [aria-current="page"] { color: var(--white); }

  /* -- section heading ------------------------------------------------- */
  .section-head { max-inline-size: 46ch; margin-block-end: var(--space-xl); }
  /* Mirrors the homepage-only section rhythm above. */
  .home .section-head { margin-block-end: var(--space-l); }
  .eyebrow {
    display: block;
    font-size: var(--step--1);
    font-weight: 500;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--accent);
    margin-block-end: var(--space-2xs);
  }

  /* -- stats ----------------------------------------------------------- */
  .stat { container-type: inline-size; }
  .stat__value {
    display: block;
    font-size: var(--step-6);
    font-weight: 200;
    line-height: 1;
    letter-spacing: -.04em;
    color: var(--brand);
    font-variant-numeric: tabular-nums;
  }
  .stat__label {
    font-size: var(--step-0);
    font-weight: 500;
    margin-block-start: var(--space-xs);
  }
  .stat__desc { font-size: var(--step--1); color: var(--text-muted); }
  /* Container query: the description is dropped when the tile itself is
     narrow, regardless of how wide the viewport happens to be. */
  @container (max-width: 12rem) {
    .stat__desc { display: none; }
  }

  /* -- service cards --------------------------------------------------- */
  .card {
    container-type: inline-size;
    position: relative;
    display: grid;
    /* minmax(0, 1fr) rather than a bare implicit track: an auto track takes
       its minimum from min-content, which lets a wide child push the column
       past the card's own box. This pins the column to the container. */
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto 1fr auto;
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    transition: border-color var(--transition), box-shadow var(--transition),
                translate var(--transition);
  }
  .card:hover {
    border-color: color-mix(in oklch, var(--brand) 40%, var(--border));
    box-shadow: var(--shadow-md);
    translate: 0 -4px;
  }
  .card__media { aspect-ratio: 16 / 10; background: var(--bg-sunken); }
  .card__media img {
    inline-size: 100%; block-size: 100%;
    object-fit: cover;
    transition: scale 500ms var(--ease-out);
  }
  .card:hover .card__media img { scale: 1.04; }
  .card__body {
    padding: var(--space-m);
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-2xs);
  }
  .card__num {
    font-size: var(--step--1);
    font-weight: 500;
    letter-spacing: .12em;
    color: var(--accent);
  }
  .card__title { font-size: var(--step-2); }
  .card__title a { color: inherit; text-decoration: none; }
  /* Stretched link: the whole card is clickable, but the accessible name and
     the keyboard tab stop stay on the single real <a>. */
  .card__title a::after { content: ""; position: absolute; inset: 0; }
  .card:has(a:focus-visible) {
    outline: 3px solid var(--accent);
    outline-offset: 3px;
  }
  .card__text { color: var(--text-muted); font-size: var(--step--1); }
  .card__footer {
    /* flex, because the reset makes SVG display:block - inside a plain <p>
       the arrow would otherwise drop onto its own line */
    display: flex;
    align-items: center;
    gap: .5em;
    padding: 0 var(--space-m) var(--space-m);
    font-size: var(--step--1);
    font-weight: 500;
    color: var(--brand);
  }
  .card:hover .card__footer .btn__arrow { translate: 3px 0; }
  /* An element cannot query the container it establishes itself - a container
     query always resolves against an ANCESTOR container. So these rules target
     the card's children, which do see .card as their container, and the card
     adapts to the width it was actually given rather than to the viewport. */
  @container (min-width: 30rem) {
    .card__media { aspect-ratio: 21 / 9; }
    .card__body { padding: var(--space-l); gap: var(--space-xs); }
    .card__footer { padding: 0 var(--space-l) var(--space-l); }
  }
  @container (max-width: 20rem) {
    .card__text { display: none; }     /* very narrow: title and image only */
  }

  /* The homepage pillar cards keep their original image treatment, but their
     copy area is deliberately compact so the navigation tiles do not become
     unnecessarily tall. The extra class avoids changing cards elsewhere. */
  .home-services .card__body {
    padding: var(--space-s) var(--space-m);
    gap: var(--space-3xs);
  }
  .home-services .card__footer {
    padding: 0 var(--space-m) var(--space-s);
  }

  /* -- link list (child services) -------------------------------------- */
  .link-list {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-2xs);
    list-style: none;
    padding: 0;
  }
  .link-list a {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-m);
    padding: var(--space-m);
    line-height: 1.4;
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    text-decoration: none;
    color: var(--text);
    font-weight: 450;
    transition: border-color var(--transition), background var(--transition),
                padding-inline-start var(--transition);
  }
  .link-list a:hover {
    border-color: color-mix(in oklch, var(--brand) 45%, var(--border));
    background: color-mix(in oklch, var(--brand) 5%, var(--bg-raised));
    padding-inline-start: calc(var(--space-m) + 6px);
    color: var(--brand);
  }
  .link-list span[aria-hidden] { color: var(--accent); }

  /* -- feature list ---------------------------------------------------- */
  .feature-list { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-m); }
  .feature-list > div {
    display: grid;
    gap: var(--space-3xs);
    padding-inline-start: var(--space-m);
    border-inline-start: 2px solid
      color-mix(in oklch, var(--accent) 55%, transparent);
  }
  .feature-list dt { font-weight: 550; color: var(--text); }
  .feature-list dd { margin: 0; color: var(--text-muted); }

  /* -- clients --------------------------------------------------------- */
  /* These four logos are white artwork on transparency, so on a light
     background they would be invisible. brightness(0) knocks them to solid
     black and the opacity softens them into a normal logo strip; in dark
     mode they are left as-is. */
  .clients { --gap: var(--space-xl); justify-content: center; }
  .clients img {
    block-size: clamp(1.9rem, 4vi, 2.6rem);
    inline-size: auto;
    opacity: .5;
    filter: brightness(0);
    transition: opacity var(--transition);
  }
  .clients img:hover { opacity: .85; }

  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .clients img {
      filter: none;
      opacity: .7;
    }
  }
  :root[data-theme="dark"] .clients img {
    filter: none;
    opacity: .7;
  }
  :root[data-theme="light"] .clients img {
    filter: brightness(0);
    opacity: .5;
  }

  /* -- partner marquee -------------------------------------------------- */
  /* Full-bleed scrolling logo strip on the homepage.

     Two identical rows sit in one track and the track translates by exactly
     -50%, which lands the second copy where the first started: a seamless
     loop with no JS, no cloning at runtime and no layout thrash.

     The DEFAULT state here is the static one - a centred, wrapping row. The
     animation is only switched on inside `prefers-reduced-motion:
     no-preference`. Doing it the other way round would leave anyone with
     reduced motion looking at a frozen, half-scrolled strip, because the
     global reset clamps animation-duration to .01ms rather than removing the
     animation. */
  /* The rail is deliberately black in both themes. Most of the supplied
     vendor artwork is dark, so each mark keeps a small white plate rather than
     being recoloured or disappearing into the rail. */
  /* Tighter than section--tight, so the rail reads as a thin band between two
     sections rather than as a section of its own.
     (This padding was originally chosen to make the strip break the fold when
     the band sat directly under the hero. It now sits below the stats section,
     so the fold argument no longer applies - the tight padding is kept purely
     because a logo rail wants less room than a content section.) */
  .partners-band { padding-block: var(--space-xs); }

  .marquee {
    --marquee-gap: var(--space-s);
    --marquee-duration: 32s;
    --marquee-slot: 12.5rem;
    padding-inline: var(--gutter);
    padding-block: var(--space-xs);
    background: #000;
    border-block: 1px solid oklch(28% 0 0);
  }
  .marquee__track {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--marquee-gap);
  }
  .marquee__row {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: var(--marquee-gap);
    margin: 0;
    padding: 0;
  }
  /* The duplicate exists only to close the loop. With no loop, it would just
     be the same eight logos a second time. */
  .marquee__row[aria-hidden="true"] { display: none; }

  /* Each logo gets an identical slot and is centred inside it, rather than
     each one being as wide as its own artwork. These marks range from 1.67:1
     (the stacked AWS block) to 6.88:1 (the RingCentral wordmark); sizing them
     all to a common height makes the wide ones enormous and sizing to a common
     width makes the square ones tiny. A fixed slot with `contain` normalises
     them the way a logo wall is normally set.

     The slot also gives the flex item a definite width, which sidesteps the
     intrinsic-sizing trap noted below. */
  .marquee__item {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    inline-size: var(--marquee-slot);
    block-size: 4rem;
    padding: .35rem .7rem;
    background: #fff;
    border-radius: .55rem;
  }
  /* The caps size each mark within the smaller plate: whichever limit it hits
     first wins. The Microsoft raster is downscaled rather than enlarged, and
     the portrait Cyber Essentials badges remain comfortably oversampled. */
  .marquee__item img {
    max-inline-size: 11rem;
    max-block-size: 3.25rem;
    inline-size: auto;
    block-size: auto;
    object-fit: contain;
  }

  @media (prefers-reduced-motion: no-preference) {
    .marquee {
      overflow: hidden;
      padding-inline: 0;
      position: relative;
    }
    /* Logos fade in and out at the edges instead of being guillotined.
       Done with two gradient overlays rather than a mask: masking .marquee
       would fade its white background too and dissolve the band into the page,
       and putting the mask on the track is worse still - the track is
       max-content wide, so `100%` would resolve against ~3400px and the fade
       would land somewhere off-screen rather than at the viewport edge. The
       band is a known flat black, so painting black-to-transparent over it is
       exact. */
    .marquee::before,
    .marquee::after {
      content: "";
      position: absolute;
      inset-block: 0;
      inline-size: 4rem;
      z-index: 1;
      pointer-events: none;
    }
    .marquee::before {
      inset-inline-start: 0;
      background: linear-gradient(to right, #000, transparent);
    }
    .marquee::after {
      inset-inline-end: 0;
      background: linear-gradient(to left, #000, transparent);
    }
    .marquee__track {
      flex-wrap: nowrap;
      justify-content: flex-start;
      inline-size: max-content;
      gap: var(--marquee-gap);
      animation: marquee-scroll var(--marquee-duration) linear infinite;
      /* Promote once, up front, rather than letting the compositor discover
         the moving layer mid-animation. */
      will-change: transform;
    }
    .marquee__row {
      flex-wrap: nowrap;
      justify-content: flex-start;
    }
    .marquee__row[aria-hidden="true"] { display: flex; }

    /* Pause on hover, and on keyboard focus landing anywhere inside, so a
       logo can actually be read. */
    .marquee:hover .marquee__track,
    .marquee:focus-within .marquee__track { animation-play-state: paused; }
  }

  @keyframes marquee-scroll {
    /* -50% of the track is exactly one row plus one gap, because the track's
       own gap sits between the two rows. */
    from { transform: translateX(0); }
    to   { transform: translateX(calc(-50% - var(--marquee-gap) / 2)); }
  }

  /* -- testimonials: CSS-only scroll-snap carousel --------------------- */
  .testimonials {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: min(88%, 26rem);
    gap: var(--space-m);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--gutter);
    padding-block-end: var(--space-s);
    /* hide the scrollbar chrome without disabling scrolling */
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
    overscroll-behavior-x: contain;
  }
  .testimonial {
    scroll-snap-align: start;
    display: grid;
    grid-template-rows: 1fr auto;
    gap: var(--space-s);
    padding: var(--space-l);
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
  }
  .testimonial blockquote {
    margin: 0;
    font-size: var(--step-1);
    font-weight: 300;
    line-height: 1.45;
  }
  .testimonial blockquote::before { content: "\201C"; }
  .testimonial blockquote::after { content: "\201D"; }
  .testimonial__meta {
    display: flex;
    align-items: end;
    justify-content: space-between;
    gap: var(--space-m);
    min-inline-size: 0;
  }
  .testimonial figcaption {
    min-inline-size: 0;
    font-size: var(--step--1);
    color: var(--text-muted);
  }
  .testimonial__byline { flex: 1 1 auto; min-inline-size: 0; }
  .testimonial cite { font-style: normal; font-weight: 600; color: var(--text); display: block; }

  .testimonial__logo {
    display: flex;
    align-items: center;
    justify-content: end;
    flex: 0 1 8rem;
    min-inline-size: 0;
    block-size: 2.1rem;
  }
  /* Same reasoning as the cert chips: these four client logos are white
     artwork on transparency, so they are knocked to a solid tone that works
     on the card in either theme. */
  .testimonial__logo img {
    max-block-size: 2.1rem;
    inline-size: auto;
    max-inline-size: 8rem;
    object-fit: contain;
    opacity: .55;
    filter: brightness(0);
  }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .testimonial__logo img {
      filter: none;
      opacity: .8;
    }
  }
  :root[data-theme="dark"] .testimonial__logo img {
    filter: none;
    opacity: .8;
  }
  :root[data-theme="light"] .testimonial__logo img {
    filter: brightness(0);
    opacity: .55;
  }

  /* Boss Pets and Burman Environmental Law arrive as colour SVGs, while the
     original client artwork is white-on-transparent. Normalise the vectors to
     the same black-on-light / white-on-dark treatment so the full client row
     reads as one restrained logo family. */
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .clients img.client-logo--vector,
    :root:not([data-theme="light"]) .testimonial__logo img.client-logo--vector {
      filter: brightness(0) invert(1);
    }
  }
  :root[data-theme="dark"] .clients img.client-logo--vector,
  :root[data-theme="dark"] .testimonial__logo img.client-logo--vector {
    filter: brightness(0) invert(1);
  }
  :root[data-theme="light"] .clients img.client-logo--vector,
  :root[data-theme="light"] .testimonial__logo img.client-logo--vector {
    filter: brightness(0);
  }

  @media (min-width: 62rem) {
    .testimonials {
      grid-auto-flow: row;
      grid-template-columns: repeat(2, 1fr);
      overflow: visible;
    }
  }

  /* -- FAQ: native disclosure, no JavaScript --------------------------- */
  .faq { display: grid; gap: var(--space-2xs); }
  .faq details {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg-raised);
    overflow: hidden;
  }
  .faq details[open] {
    border-color: color-mix(in oklch, var(--brand) 35%, var(--border));
  }
  .faq summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-s);
    padding: var(--space-s) var(--space-m);
    cursor: pointer;
    font-weight: 500;
    list-style: none;
  }
  .faq summary::-webkit-details-marker { display: none; }
  .faq summary::after {
    content: "";
    inline-size: .6em;
    block-size: .6em;
    border-inline-end: 2px solid var(--accent);
    border-block-end: 2px solid var(--accent);
    rotate: 45deg;
    flex-shrink: 0;
    transition: rotate var(--transition);
  }
  .faq details[open] summary::after { rotate: -135deg; }
  .faq summary:hover { color: var(--brand); }
  .faq__answer { padding: 0 var(--space-m) var(--space-m); color: var(--text-muted); }

  /* -- certifications -------------------------------------------------- */
  .certs {
    display: grid;
    gap: var(--space-m);
    margin-block-start: var(--space-m);
    grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
    /* start, not stretch: the AWS group has five entries and the others one
       or two, so equal-height cards would leave large empty boxes. */
    align-items: start;
  }
  .cert-group {
    padding: var(--space-s) var(--space-m);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg);
  }
  .cert__issuer { margin-block-end: var(--space-s); }

  /* Vendor marks sit on a fixed white chip rather than the page background:
     the AWS and ISC2 wordmarks are near-black and would disappear in dark
     mode, and recolouring a third-party logo is not ours to do. */
  .cert__chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-2xs) var(--space-xs);
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 10px;
    block-size: 3rem;
    min-inline-size: 6.5rem;
  }
  .cert__chip img {
    max-block-size: 1.9rem;
    inline-size: auto;
    block-size: auto;
    max-inline-size: 8rem;
    object-fit: contain;
  }
  .cert-list {
    display: grid;
    gap: var(--space-xs);
    list-style: none;
    padding: 0;
    margin: 0;
  }
  .cert__name {
    font-weight: 500;
    color: var(--text);
    line-height: 1.35;
    padding-inline-start: var(--space-s);
    position: relative;
  }
  .cert__name::before {
    content: "";
    position: absolute;
    inset-inline-start: 0;
    inset-block-start: .62em;
    inline-size: .4rem;
    block-size: .4rem;
    border-radius: 50%;
    background: var(--accent);
  }


  /* Animate the disclosure open/closed where the browser supports sizing
     to a keyword. Purely additive - it still works without this. */
  @supports (interpolate-size: allow-keywords) {
    :root { interpolate-size: allow-keywords; }
    .faq details::details-content {
      block-size: 0;
      overflow: hidden;
      transition: block-size var(--transition), content-visibility var(--transition);
      transition-behavior: allow-discrete;
    }
    .faq details[open]::details-content { block-size: auto; }
  }

  /* -- forms ----------------------------------------------------------- */
  .form { display: grid; gap: var(--space-m); }
  .field { display: grid; gap: var(--space-3xs); }
  .field label { font-size: var(--step--1); font-weight: 500; }
  .field .hint { font-size: var(--step--1); color: var(--text-muted); }
  .field :is(input, textarea, select) {
    inline-size: 100%;
    padding: .75em 1em;
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    transition: border-color var(--transition), box-shadow var(--transition);
  }
  .field :is(input, textarea, select):focus {
    outline: none;
    border-color: var(--brand);
    box-shadow: 0 0 0 3px color-mix(in oklch, var(--brand) 22%, transparent);
  }
  .field textarea { min-block-size: 7rem; resize: vertical; }
  @supports (field-sizing: content) {
    .field textarea { field-sizing: content; min-block-size: 5rem; max-block-size: 20rem; }
  }
  /* Only flag an invalid value once the user has actually typed something */
  .field input:user-invalid, .field textarea:user-invalid {
    border-color: oklch(58% .2 25);
  }
  .field input:user-invalid + .error, .field textarea:user-invalid + .error {
    display: block;
  }
  .error { display: none; font-size: var(--step--1); color: oklch(52% .2 25); }

  .form__row { display: grid; gap: var(--space-m); }
  @media (min-width: 40rem) {
    .form__row { grid-template-columns: 1fr 1fr; }
  }

  /* -- contact details ------------------------------------------------- */
  .contact-details { display: grid; gap: var(--space-m); }
  .contact-details dt {
    font-size: var(--step--1);
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-block-end: var(--space-3xs);
  }
  .contact-details dd { margin: 0; font-size: var(--step-1); }
  /* The email and phone are already the exact same Deep Blue as the "Enquire
     now" button fill - identical computed value - but they did not read that
     way: a solid block of a colour looks far denser than the same colour drawn
     as thin 350-weight text. Weight 500 gives the strokes enough body for the
     blue to register as the same deliberate brand colour, without changing the
     colour at all. */
  .contact-details a { text-decoration: none; font-weight: 500; }
  .contact-details a:hover { text-decoration: underline; }

  /* -- CTA band -------------------------------------------------------- */
  .cta-band {
    position: relative;
    isolation: isolate;
    background: var(--bg-invert);
    color: var(--text-invert);
    overflow: clip;
  }
  .cta-band::before {
    content: "";
    position: absolute;
    inset-block-start: -40%;
    inset-inline-end: -10%;
    inline-size: min(48rem, 70%);
    aspect-ratio: 1;
    background: radial-gradient(circle,
      color-mix(in oklch, var(--brand-300) 30%, transparent), transparent 65%);
    z-index: -1;
  }
  .cta-band h2 { color: var(--text-invert); }
  .cta-band p { color: oklch(88% .01 265); max-inline-size: 52ch; }

  /* -- footer ---------------------------------------------------------- */
  .site-footer {
    background: var(--bg-sunken);
    border-block-start: 1px solid var(--border);
    padding-block: var(--space-2xl) var(--space-l);
    font-size: var(--step--1);
  }
  .site-footer__grid {
    display: grid;
    gap: var(--space-xl);
    grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
  }
  .site-footer h2 {
    font-size: var(--step--1);
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-block-end: var(--space-s);
  }
  .site-footer ul { list-style: none; padding: 0; display: grid; gap: var(--space-2xs); }
  .site-footer a { color: var(--text); text-decoration: none; }
  .site-footer a:hover { color: var(--brand); text-decoration: underline; }
  .site-footer__bottom {
    margin-block-start: var(--space-xl);
    padding-block-start: var(--space-m);
    border-block-start: 1px solid var(--border);
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-s) var(--space-m);
    justify-content: space-between;
    align-items: center;
    color: var(--text-muted);
  }

  /* -- mobile nav (Popover API - no JS needed to open or close) -------- */
  .mobile-nav {
    border: 0;
    padding: 0;
    inline-size: min(22rem, 88vw);
    max-block-size: 100dvb;
    margin: 0 0 0 auto;
    background: var(--bg-raised);
    color: var(--text);
    box-shadow: var(--shadow-lg);
  }
  .mobile-nav:popover-open {
    display: grid;
    grid-template-rows: auto 1fr;
  }
  .mobile-nav::backdrop {
    background: oklch(15% .02 274 / .45);
    backdrop-filter: blur(3px);
  }
  .mobile-nav__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-s) var(--space-m);
    border-block-end: 1px solid var(--border);
  }
  .mobile-nav__body { padding: var(--space-m); overflow-y: auto; }
  .mobile-nav ul { list-style: none; padding: 0; display: grid; gap: var(--space-3xs); }
  .mobile-nav a {
    display: block;
    padding: var(--space-s) var(--space-s);
    border-radius: var(--radius);
    text-decoration: none;
    color: var(--text);
    font-size: var(--step-1);
    line-height: 1.35;
  }
  .mobile-nav a:hover, .mobile-nav a:focus-visible { background: var(--bg-sunken); }
  .mobile-nav a[aria-current="page"] {
    color: var(--brand);
    background: color-mix(in oklch, var(--brand) 8%, transparent);
    font-weight: 550;
  }
  .mobile-nav .sub {
    padding-inline-start: var(--space-s);
    margin-block: var(--space-3xs) var(--space-2xs);
    border-inline-start: 2px solid var(--border);
  }
  .mobile-nav .sub a {
    font-size: var(--step-0);
    color: var(--text-muted);
    padding-block: var(--space-xs);
  }

  .icon-btn {
    display: inline-grid;
    place-items: center;
    inline-size: 2.75rem;      /* >= 44px: comfortable touch target */
    block-size: 2.75rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    background: transparent;
    color: var(--text);
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition);
  }
  .icon-btn:hover { background: var(--bg-sunken); border-color: var(--brand); }

  /* Declared after .icon-btn so it wins on source order: the burger is a
     .icon-btn too, and both selectors carry the same specificity. */
  .nav-toggle { display: none; }

  /* Raised from 62rem: with seven top-level items the horizontal bar needs
     roughly 1250px before it stops feeling cramped, so the drawer takes over
     earlier than it used to. */
  @media (max-width: 78rem) {
    .nav { display: none; }
    .nav-toggle { display: inline-grid; }
    .header-cta { display: none; }
    .header-actions { margin-inline-start: auto; }
  }

  /* -- theme toggle ---------------------------------------------------- */
  #theme-toggle .icon-sun { display: none; }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) #theme-toggle .icon-sun { display: block; }
    :root:not([data-theme="light"]) #theme-toggle .icon-moon { display: none; }
  }
  :root[data-theme="dark"] #theme-toggle .icon-sun { display: block; }
  :root[data-theme="dark"] #theme-toggle .icon-moon { display: none; }
  :root[data-theme="light"] #theme-toggle .icon-sun { display: none; }
  :root[data-theme="light"] #theme-toggle .icon-moon { display: block; }

  /* -- media ----------------------------------------------------------- */
  .figure-media {
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--bg-sunken);
  }
  .figure-media img { inline-size: 100%; aspect-ratio: 3 / 2; object-fit: cover; }

  .split {
    display: grid;
    gap: var(--space-xl);
    align-items: center;
  }
  @media (min-width: 52rem) {
    .split { grid-template-columns: 1fr 1fr; }
    .split--media-first > :first-child { order: 2; }
  }
}

/* ========================================================================== */
@layer utilities {
  .visually-hidden:not(:focus):not(:active) {
    position: absolute !important;
    inline-size: 1px; block-size: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .text-center { text-align: center; }
  .mx-auto { margin-inline: auto; }
  .measure { max-inline-size: var(--measure); }
  .muted { color: var(--text-muted); }
  .mbs-l { margin-block-start: var(--space-l); }
  .mbs-xl { margin-block-start: var(--space-xl); }

  /* Long pages: let the browser skip layout/paint for offscreen sections.
     contain-intrinsic-size supplies a placeholder height so the scrollbar
     stays stable. */
  .defer-render {
    content-visibility: auto;
    contain-intrinsic-size: auto 42rem;
  }
}

/* ==========================================================================
   Motion - strictly progressive enhancement.
   Scroll-driven animations run entirely on the compositor: no scroll
   listeners, no IntersectionObserver, nothing on the main thread. If the
   browser lacks support the content is simply already visible, which is the
   behaviour the original site got wrong (its sections stayed blank until a
   script decided otherwise).
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .reveal {
      animation: reveal linear both;
      animation-timeline: view();
      animation-range: entry 10% cover 26%;
    }
    @keyframes reveal {
      from { opacity: 0; translate: 0 1.5rem; }
      to   { opacity: 1; translate: 0 0; }
    }
  }
}

/* Cross-document view transitions: navigating between pages cross-fades
   instead of flashing white. Ignored by browsers that don't support it. */
@view-transition { navigation: auto; }

@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root) { animation-duration: 180ms; }
  ::view-transition-new(root) { animation-duration: 180ms; }
  /* The header keeps its own identity so it doesn't cross-fade with the page */
  .site-header { view-transition-name: site-header; }
}

@media print {
  .site-header, .site-footer, .cta-band, .nav-toggle, .skip-link { display: none; }
  body { color: #000; background: #fff; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: .85em; }
}
