/* ==========================================================================
   SYNCHRAVEST — stylesheet
   Single stylesheet. No build step, no frameworks. Edit the brand block
   below to rebrand.

   SECTION MAP
     1.  Brand constants
     2.  Reset / base
     3.  Draft banner            <- delete-me block
     4.  Layout primitives
     5.  Header / nav
     6.  Buttons
     7.  Hero + motion backdrop
     8.  Stat counters
     9.  Strategy grid
     10. Who we serve
     11. Process loop
     12. Marquee
     13. Pull quote
     14. Contact
     15. Portals
     16. Footer
     17. About page
     18. Scroll reveal
     19. Responsive
     20. Reduced motion
   ========================================================================== */

/* ==========================================================================
   1. EDIT ME: BRAND CONSTANTS
   Everything you're likely to change lives here. Swap a value, reload.
   ========================================================================== */
:root {
  /* --- Palette: navy base, warm ivory type, gold as the single accent --- */
  --ink:          #0A1628;  /* page base — deep navy                       */
  --ink-raised:   #10233F;  /* alternating section bands, cards            */
  --ink-deep:     #060F1D;  /* hero floor, behind the motion backdrop      */
  --ink-hover:    #162D4E;  /* card lift hover — one step above raised     */

  --paper:        #F5F0E6;  /* primary text — warm ivory, never pure white */
  --paper-dim:    #A8A29A;  /* body copy, subheads, muted meta             */

  --gold:         #C9A227;  /* accent — refined muted gold                 */
  --gold-bright:  #D4AF37;  /* accent hover / button fill                  */
  --gold-deep:    #8A6F1C;  /* accent borders, quiet strokes               */
  --rule:         #4A3D22;  /* hairline dividers — muted bronze            */
  --rule-bright:  #6E5A2E;  /* hairline on hover / emphasis                */

  /* Contrast on --ink: paper 15.96:1, paper-dim 7.17:1, gold 7.49:1,
     gold-bright 8.62:1. --gold-deep is 3.77:1 and is therefore borders and
     strokes only, never text. All clear WCAG AA. */

  /* DIALS: for a cooler, less gilded look drop --rule to #22334D and
     --rule-bright to #2C4257 — that reverts the hairlines to navy and
     leaves gold on the numbers, buttons and hovers only. To warm the whole
     thing further, raise --rule toward #6B5628. */

  /* Translucent navy for layers that sit over the hero backdrop. These
     track --ink by hand; a hex token can't be alpha'd without color-mix,
     and this needs to work without it. */
  --ink-veil:     rgba(10, 22, 40, 0.86);  /* sticky header                */
  --stat-veil:    rgba(10, 22, 40, 0.45);  /* stat cells over the canvas   */

  /* Draft-only colours. Unused once the banner is removed.
     Deliberately red, NOT amber — amber would read as brand gold and stop
     registering as a warning. */
  --draft-bg:     #2A1116;
  --draft-fg:     #E0705A;
  --draft-rule:   #5E2029;

  /* --- Typography ------------------------------------------------------ */
  --font-display: "Source Serif 4", Georgia, "Times New Roman", serif;
  --font-body:    "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI",
                  Roboto, Helvetica, Arial, sans-serif;

  /* --- Layout ---------------------------------------------------------- */
  --container:    1120px;   /* max content width                           */
  --gutter:       24px;     /* horizontal page padding on mobile           */
  --section-y:    clamp(64px, 10vw, 128px); /* vertical section rhythm     */
  --header-h:     72px;

  /* Height reserved for the draft banner. Stays 0 unless the banner is
     actually in the document — see the :has() rule below. This is what
     makes deleting the banner a genuine one-block delete. */
  --draft-banner-h: 0px;

  /* --- Motion ---------------------------------------------------------- */
  --reveal-duration: 600ms;
  --reveal-distance: 24px;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

/* The ONLY thing that gives the banner its space. Remove the .draft-banner
   element and this selector stops matching, the offset returns to 0, and the
   layout closes up with no further edits. */
:root:has(.draft-banner) { --draft-banner-h: 34px; }

/* ==========================================================================
   2. RESET / BASE
   ========================================================================== */
*,
*::before,
*::after { box-sizing: border-box; }

html {
  /* This site is dark-only. Declaring it makes the browser render
     scrollbars, form controls and the overscroll area to match instead of
     flashing a light default — and tells "force dark mode" tooling that the
     page already has a dark treatment. Paired with the <meta name="color-scheme">
     tag in both pages. */
  color-scheme: dark;
  scroll-behavior: smooth;
  /* Offset for the sticky header (and the draft banner, when present). */
  scroll-padding-top: calc(var(--header-h) + var(--draft-banner-h) + 16px);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  padding-top: var(--draft-banner-h);
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin: 0;
  text-wrap: balance;
}

p { margin: 0; }

a {
  color: var(--gold);
  text-decoration: none;
  transition: color 160ms ease;
}
a:hover { color: var(--paper); }

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

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

/* Screen-reader-only utility */
.visually-hidden {
  position: absolute;
  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;
  top: -100px;
  left: var(--gutter);
  z-index: 100;
  background: var(--ink-raised);
  color: var(--paper);
  padding: 12px 20px;
  border: 1px solid var(--rule);
}
.skip-link:focus { top: calc(var(--draft-banner-h) + 12px); }

/* ==========================================================================
   3. DRAFT BANNER  <-- DELETE-ME BLOCK
   Remove the .draft-banner element from the two HTML files (or set
   siteConfig.draftMode.enabled = false) and this whole rule goes unused.
   Safe to delete these declarations at the same time.
   ========================================================================== */
.draft-banner {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 200;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-inline: 12px;
  background: var(--draft-bg);
  border-bottom: 1px solid var(--draft-rule);
  color: var(--draft-fg);
  font-family: var(--font-body);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-align: center;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Matching draft-only footnote in the footer. Same delete-at-launch rule. */
.disclaimer__draft {
  color: var(--draft-fg);
  opacity: 0.75;
}

/* ==========================================================================
   4. LAYOUT PRIMITIVES
   ========================================================================== */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  padding-block: var(--section-y);
  border-top: 1px solid var(--rule);
  position: relative;
}

/* Tonal band — alternate against --ink for section separation */
.section--raised { background: var(--ink-raised); }

/* Edge-to-edge strip with no vertical padding (the marquee) */
.section--flush {
  padding-block: 0;
  border-top: 0;
}

/* First section on an interior page — a little extra headroom */
.section--lead { border-top: 0; padding-top: clamp(56px, 8vw, 96px); }

.section__eyebrow {
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 20px;
}

.section__title {
  font-size: clamp(1.75rem, 3.5vw, 2.5rem);
  max-width: 20ch;
}

.section__intro {
  color: var(--paper-dim);
  max-width: 60ch;
  margin-top: 20px;
  font-size: 1.0625rem;
}

/* ==========================================================================
   5. HEADER / NAV
   ========================================================================== */
.site-header {
  position: sticky;
  top: var(--draft-banner-h);
  z-index: 50;
  background: var(--ink-veil);
  backdrop-filter: blur(12px) saturate(140%);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
  border-bottom: 1px solid var(--rule);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  min-height: var(--header-h);
}

/* LOGO PLACEHOLDER: this is a text wordmark. To swap in an image logo,
   replace the <a class="wordmark"> contents with an <img> and delete the
   letter-spacing rules below. */
.wordmark {
  font-family: var(--font-display);
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  color: var(--paper);
  text-transform: uppercase;
  white-space: nowrap;
}
.wordmark:hover { color: var(--paper); }

.site-nav ul {
  display: flex;
  align-items: center;
  gap: clamp(16px, 3vw, 34px);
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-nav a {
  position: relative;
  display: inline-block;
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--paper-dim);
  padding-block: 8px;
}

/* Micro-interaction: underline wipes in from the left. */
.site-nav a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 2px;
  height: 1px;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 280ms var(--ease-out);
}
.site-nav a:hover::after,
.site-nav a[aria-current="page"]::after { transform: scaleX(1); }
.site-nav a:hover,
.site-nav a[aria-current="page"] { color: var(--paper); }

/* ==========================================================================
   6. BUTTONS
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-family: var(--font-body);
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 16px 30px;
  border: 1px solid var(--gold-deep);
  color: var(--gold);
  background: transparent;
  cursor: pointer;
  transition: background-color 220ms ease, color 220ms ease,
              border-color 220ms ease;
}
.btn:hover {
  background: var(--gold-bright);
  border-color: var(--gold-bright);
  color: var(--ink);           /* navy on gold — 8.62:1 */
}

/* Micro-interaction: the arrow slides on hover. */
.btn__arrow {
  display: inline-block;
  transition: transform 280ms var(--ease-out);
}
.btn--arrow:hover .btn__arrow,
.btn--arrow:focus-visible .btn__arrow { transform: translateX(5px); }

/* Quieter variant for the custodian portal row */
.btn--ghost {
  border-color: var(--rule-bright);
  color: var(--paper-dim);
}
.btn--ghost:hover {
  background: transparent;
  border-color: var(--gold);
  color: var(--gold);
}

/* ==========================================================================
   7. HERO + MOTION BACKDROP
   ========================================================================== */
.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border-top: 0;
  background: var(--ink-deep);
  display: flex;
  align-items: center;
  min-height: min(92vh, 900px);
  /* Kept modest on purpose: min-height already sets the stage, and heavy
     padding on top of it pushes the stat row below the fold. */
  padding-block: clamp(72px, 8vw, 104px);
}

.hero__media {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}

/* The animated canvas. Swap for .hero__video and nothing else changes. */
.hero__canvas,
.hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  opacity: 0;
  animation: backdrop-in 1600ms ease-out 120ms forwards;
}

@keyframes backdrop-in { to { opacity: 1; } }

/* Keeps type legible over the motion and blends the hero into the next
   section. Tune the stops here rather than dimming the canvas itself. */
.hero__scrim {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 90% at 18% 30%,
      rgba(6, 15, 29, 0.92) 0%,
      rgba(6, 15, 29, 0.62) 42%,
      rgba(6, 15, 29, 0.15) 100%),
    linear-gradient(to bottom,
      rgba(6, 15, 29, 0.85) 0%,
      rgba(6, 15, 29, 0.25) 30%,
      rgba(6, 15, 29, 0.55) 78%,
      var(--ink) 100%);
}

.hero__inner { position: relative; }

.hero__eyebrow {
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 24px;
}

.hero__title {
  font-size: clamp(2.5rem, 6vw, 4.5rem);
  line-height: 1.08;
  /* Wide enough that the hard <br> in the markup is what breaks the line,
     not the measure. Narrower and the headline splits into four lines. */
  max-width: 15em;
}

.hero__subhead {
  margin-top: 28px;
  max-width: 54ch;
  font-size: clamp(1.0625rem, 1.6vw, 1.25rem);
  color: var(--paper-dim);
}

.hero__cta { margin-top: 44px; }

/* ==========================================================================
   8. STAT COUNTERS
   Values are injected from config.js and animated by main.js.
   ========================================================================== */
.stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background: var(--rule);
  border-top: 1px solid var(--rule);
  margin-top: clamp(56px, 8vw, 96px);
  max-width: 760px;
}

.stat {
  background: var(--stat-veil);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  padding: 28px 24px 30px;
}

.stat__value {
  font-family: var(--font-display);
  font-size: clamp(2rem, 4.4vw, 3rem);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--gold);
  /* Tabular figures stop the number jittering as it counts up. */
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.stat__label {
  margin-top: 14px;
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--paper-dim);
  line-height: 1.5;
}

/* ==========================================================================
   9. STRATEGY GRID — 4 up / 2 up / 1 up
   ========================================================================== */
.strategy-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;                      /* the gap IS the hairline rule */
  background: var(--rule);
  border: 1px solid var(--rule);
  margin-top: 56px;
}

.strategy-item {
  background: var(--ink-raised);
  padding: 36px 28px 40px;
}

.strategy-item__num {
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  color: var(--gold-deep);
  margin-bottom: 20px;
  transition: color 220ms ease;
}

.strategy-item__title {
  font-size: 1.25rem;
  margin-bottom: 14px;
}

.strategy-item__body {
  color: var(--paper-dim);
  font-size: 0.9375rem;
  line-height: 1.7;
}

/* Micro-interaction: cards lift a touch on hover. Applied via .card-lift so
   the reveal transform and the hover transform never fight — the reveal
   finishes at transform:none, then this takes over. */
.card-lift {
  transition: transform 300ms var(--ease-out),
              background-color 300ms ease;
  will-change: transform;
}
.card-lift:hover {
  transform: translateY(-4px);
  background: var(--ink-hover);
}
.card-lift:hover .strategy-item__num { color: var(--gold); }

/* ==========================================================================
   10. WHO WE SERVE — row of items divided by thin vertical rules
   ========================================================================== */
.clients {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  margin-top: 48px;
  border-top: 1px solid var(--rule);
}

.clients__item {
  padding: 32px 20px 32px 24px;
  font-family: var(--font-display);
  font-size: 1.0625rem;
  line-height: 1.35;
  color: var(--paper);
  border-right: 1px solid var(--rule);
  transition: color 220ms ease;
}
.clients__item:first-child { padding-left: 0; }
.clients__item:last-child { border-right: 0; }
.clients__item:hover { color: var(--gold); }

/* ==========================================================================
   11. PROCESS LOOP
   ==========================================================================
   Desktop: seven nodes sit on a ring. Their positions are hard-coded below
   as :nth-child rules — they are cos/sin of 7 evenly spaced angles on a
   circle of r = 40% starting at 12 o'clock. CHANGE THE STEP COUNT AND YOU
   MUST RECOMPUTE THESE SEVEN PAIRS.
   Mobile (<=900px): the ring is dropped and the nodes stack into a spine.
   ========================================================================== */
.process-loop {
  position: relative;
  width: min(720px, 100%);
  aspect-ratio: 1 / 1;
  margin: clamp(48px, 7vw, 88px) auto 0;
}

.process-loop__ring {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.process-loop__ring-base,
.process-loop__ring-draw {
  fill: none;
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.process-loop__ring-base { stroke: var(--rule); }

/* The accent arc draws itself once the section scrolls into view.
   251.33 = 2 * PI * r(40). */
.process-loop__ring-draw {
  stroke: var(--gold-deep);
  stroke-dasharray: 251.33;
  stroke-dashoffset: 251.33;
  transform: rotate(-90deg);
  transform-origin: 50% 50%;
  transition: stroke-dashoffset 2400ms var(--ease-out) 200ms;
}
.process-loop.is-drawn .process-loop__ring-draw { stroke-dashoffset: 0; }

/* --- Centre icon placeholder ------------------------------------------- */
.process-loop__center {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: min(200px, 30%);
  text-align: center;
}
.process-loop__center svg {
  width: 56px;
  height: 56px;
  margin-inline: auto;
  fill: none;
  stroke: var(--gold-deep);
  stroke-width: 1;
  overflow: visible;
}
.process-loop__center svg .is-filled {
  fill: var(--gold);
  stroke: none;
}
.process-loop__center-label {
  margin-top: 16px;
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--paper-dim);
  line-height: 1.6;
}

/* --- Nodes -------------------------------------------------------------- */
.process-loop__nodes {
  list-style: none;
  margin: 0;
  padding: 0;
  position: absolute;
  inset: 0;
}

.process-node {
  position: absolute;
  width: 150px;
  /* -25px lifts the block so the numbered disc — not the whole block —
     lands on the ring. Half the disc height. */
  transform: translate(-50%, -25px);
  text-align: center;
}

.process-node__num {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 50px;
  height: 50px;
  margin-inline: auto;
  border: 1px solid var(--rule-bright);
  border-radius: 50%;
  /* Opaque so the disc punches a clean hole in the ring behind it. */
  background: var(--ink-raised);
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  color: var(--gold);
  transition: border-color 260ms ease, color 260ms ease,
              background-color 260ms ease;
}

.process-node__label {
  display: block;
  margin-top: 14px;
  font-family: var(--font-display);
  font-size: 0.9375rem;
  line-height: 1.35;
  color: var(--paper);
  text-wrap: balance;
}

.process-node:hover .process-node__num {
  border-color: var(--gold-bright);
  background: var(--gold-bright);
  color: var(--ink);
}

/* Ring coordinates — r = 40%, 7 steps, first at 12 o'clock. */
.process-node:nth-child(1) { left: 50.0%; top: 10.0%; }
.process-node:nth-child(2) { left: 81.3%; top: 25.1%; }
.process-node:nth-child(3) { left: 89.0%; top: 58.9%; }
.process-node:nth-child(4) { left: 67.4%; top: 86.0%; }
.process-node:nth-child(5) { left: 32.6%; top: 86.0%; }
.process-node:nth-child(6) { left: 11.0%; top: 58.9%; }
.process-node:nth-child(7) { left: 18.7%; top: 25.1%; }

/* ==========================================================================
   12. MARQUEE
   Track contents are built from siteConfig.marqueeTags by main.js, then
   cloned once so the -50% translate loops seamlessly. Decorative only.
   ========================================================================== */
.marquee-section { background: var(--ink); }

.marquee {
  overflow: hidden;
  border-block: 1px solid var(--rule);
  padding-block: 26px;
  /* Fade the strip out at both edges instead of hard-clipping it. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 10%,
                                      #000 90%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0%, #000 10%,
                                      #000 90%, transparent 100%);
}

.marquee__inner {
  display: flex;
  width: max-content;
}
/* The class is added by main.js only after the track has been duplicated —
   animating a single track would leave a visible gap. */
.marquee__inner.is-looping {
  animation: marquee-scroll 46s linear infinite;
}
.marquee:hover .marquee__inner.is-looping { animation-play-state: paused; }

.marquee__track {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
}

.marquee__item {
  font-family: var(--font-body);
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--paper-dim);
  white-space: nowrap;
  padding-inline: 26px;
}

.marquee__sep {
  color: var(--gold-deep);
  font-size: 0.8125rem;
  line-height: 1;
}

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

/* ==========================================================================
   13. PULL QUOTE
   ========================================================================== */
.pullquote { text-align: center; }

.pullquote__block {
  position: relative;
  margin: 0 auto;
  max-width: 900px;
  padding-top: clamp(48px, 7vw, 72px);
}

/* The oversized opening quotation mark. */
.pullquote__block::before {
  content: "\201C";
  position: absolute;
  top: clamp(-28px, -2vw, -8px);
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--font-display);
  font-size: clamp(7rem, 14vw, 11rem);
  line-height: 1;
  color: var(--gold-deep);
  opacity: 0.35;
  pointer-events: none;
  user-select: none;
}

.pullquote__text {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3.6vw, 2.5rem);
  line-height: 1.3;
  letter-spacing: -0.015em;
  color: var(--paper);
  /* em, not ch — the measure has to track the quote's own display size,
     and `ch` on a serif resolves to roughly half what you'd expect. */
  max-width: 17em;
  margin-inline: auto;
  text-wrap: balance;
}

.pullquote__attr {
  margin-top: 36px;
  font-family: var(--font-body);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold);
}

/* ==========================================================================
   14. CONTACT
   ========================================================================== */
.contact { text-align: center; }

.contact__title {
  font-size: clamp(2rem, 4.5vw, 3rem);
  max-width: none;
}

.contact__intro {
  margin-inline: auto;
  text-align: center;
}

.contact__actions {
  margin-top: 44px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 28px;
}

.contact__phone {
  font-size: 0.875rem;
  letter-spacing: 0.06em;
  color: var(--paper-dim);
}
.contact__phone a { color: var(--paper-dim); }
.contact__phone a:hover { color: var(--paper); }

/* ==========================================================================
   15. PORTALS
   ========================================================================== */
.portals__actions {
  margin-top: 44px;
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

.portals__note {
  margin-top: 28px;
  font-size: 0.8125rem;
  color: var(--paper-dim);
  letter-spacing: 0.04em;
}

/* ==========================================================================
   16. FOOTER
   ========================================================================== */
.site-footer {
  border-top: 1px solid var(--rule);
  padding-block: 56px 64px;
  background: var(--ink-raised);
}

.site-footer__top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding-bottom: 32px;
  border-bottom: 1px solid var(--rule);
}

.site-footer__nav {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
}
.site-footer__nav a {
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--paper-dim);
}
.site-footer__nav a:hover { color: var(--paper); }

.site-footer__copy {
  font-size: 0.8125rem;
  color: var(--paper-dim);
  letter-spacing: 0.04em;
}

.disclaimer {
  margin-top: 32px;
  font-size: 0.8125rem;
  line-height: 1.75;
  color: var(--paper-dim);
  max-width: 88ch;
}
.disclaimer p + p { margin-top: 12px; }

/* ==========================================================================
   17. ABOUT PAGE
   ========================================================================== */
.bio-layout {
  display: grid;
  grid-template-columns: 1fr 1.6fr;
  gap: clamp(32px, 5vw, 72px);
  align-items: start;
  margin-top: 56px;
}

/* HEADSHOT PLACEHOLDER: replace .placeholder-block with an <img> at a 4:5
   aspect ratio. The dashed treatment is intentional — it should look
   obviously unfinished until a real photo lands. */
.placeholder-block {
  aspect-ratio: 4 / 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  text-align: center;
  padding: 24px;
  background: var(--ink-raised);
  border: 1px dashed var(--gold-deep);
  color: var(--gold-deep);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  position: sticky;
  top: calc(var(--header-h) + var(--draft-banner-h) + 32px);
}
.placeholder-block__spec {
  font-size: 0.625rem;
  letter-spacing: 0.1em;
  opacity: 0.7;
}

/* Real portrait, once it lands — same box, no dashed border. */
.bio-portrait {
  aspect-ratio: 4 / 5;
  object-fit: cover;
  width: 100%;
  position: sticky;
  top: calc(var(--header-h) + var(--draft-banner-h) + 32px);
}

.bio__name {
  font-size: clamp(1.75rem, 3.5vw, 2.5rem);
}

.bio__role {
  margin-top: 12px;
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold);
}

.bio__body {
  margin-top: 36px;
  max-width: 68ch;
}
.bio__body p {
  color: var(--paper-dim);
  font-size: 1.0625rem;
  line-height: 1.8;
}
.bio__body p + p { margin-top: 24px; }

/* Lead paragraph gets slightly more presence */
.bio__body p:first-child { color: var(--paper); }

/* ==========================================================================
   18. SCROLL REVEAL — see main.js. Elements start hidden ONLY when JS is
   active (the .js class is set by the script), so no-JS users see content.
   ========================================================================== */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(var(--reveal-distance));
  transition: opacity var(--reveal-duration) var(--ease-out),
              transform var(--reveal-duration) var(--ease-out);
}
.js [data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

/* ==========================================================================
   19. RESPONSIVE
   ========================================================================== */
@media (max-width: 1024px) {
  .site-nav ul { gap: 18px; }
}

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

  .clients { grid-template-columns: repeat(2, 1fr); }
  .clients__item {
    padding-left: 0;
    padding-right: 24px;
    border-right: 0;
    border-bottom: 1px solid var(--rule);
  }

  /* --- Process loop collapses to a vertical spine --------------------- */
  .process-loop {
    aspect-ratio: auto;
    width: 100%;
    max-width: 520px;
    padding-left: 0;
  }
  .process-loop__ring,
  .process-loop__center { display: none; }

  .process-loop__nodes {
    position: relative;   /* anchors the vertical spine below */
    display: flex;
    flex-direction: column;
    gap: 0;
  }

  /* The descendant selector is deliberate. The ring coordinates are set by
     .process-node:nth-child(n) — specificity (0,2,0) — so a bare
     .process-node here would lose and the nodes would stay scattered off
     the right edge of the page. This matches that specificity and wins on
     source order. `inset: auto` is what actually clears left/top; they
     still offset a relatively positioned box. */
  .process-loop__nodes .process-node {
    position: relative;
    inset: auto;
    z-index: 1;
    width: 100%;
    transform: none;
    display: grid;
    grid-template-columns: 50px 1fr;
    align-items: center;
    gap: 20px;
    text-align: left;
    padding-block: 14px;
  }
  .process-node__num { margin-inline: 0; }
  .process-node__label { margin-top: 0; font-size: 1.0625rem; }

  /* Vertical rule threaded behind the discs */
  .process-loop__nodes::before {
    content: "";
    position: absolute;
    left: 25px;
    top: 40px;
    bottom: 40px;
    width: 1px;
    background: var(--rule);
    z-index: 0;
  }

  .bio-layout { grid-template-columns: 1fr; }
  .placeholder-block,
  .bio-portrait {
    position: static;
    max-width: 320px;
    aspect-ratio: 1 / 1;
  }
}

@media (max-width: 700px) {
  .stats { grid-template-columns: 1fr; max-width: none; }
  .stat { padding: 22px 20px 24px; }
}

@media (max-width: 640px) {
  :root { --gutter: 20px; --header-h: 64px; }

  .draft-banner {
    font-size: 0.5625rem;
    letter-spacing: 0.1em;
  }

  .site-header__inner {
    min-height: var(--header-h);
    flex-direction: column;
    justify-content: center;
    gap: 8px;
    padding-block: 12px;
  }

  .site-nav ul { gap: 16px; flex-wrap: wrap; justify-content: center; }
  .site-nav a { font-size: 0.6875rem; letter-spacing: 0.08em; }

  .hero { min-height: auto; }

  .strategy-grid { grid-template-columns: 1fr; }

  .clients { grid-template-columns: 1fr; }

  .btn { padding: 15px 24px; font-size: 0.75rem; }

  .portals__actions { flex-direction: column; align-items: flex-start; }
  .portals__actions .btn { width: 100%; justify-content: space-between; }

  .site-footer__top { flex-direction: column; align-items: flex-start; }
}

/* ==========================================================================
   20. REDUCED MOTION
   Reveals render visible, counters snap (handled in main.js), the backdrop
   paints one static frame (also main.js), and the marquee stops moving.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  .js [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .hero__canvas,
  .hero__video { opacity: 1; animation: none; }

  /* Explicit: without this the blanket rule below would run the loop once
     at 0.01ms and leave the strip parked at -50%. */
  .marquee__inner.is-looping { animation: none !important; }

  .process-loop__ring-draw {
    stroke-dashoffset: 0;
    transition: none;
  }

  .card-lift:hover { transform: none; }

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