/* ─────────────────────────────────────────────────────────────
   Base styles — resets, typography, layout, components.
   Phase 1 covers: nav, hero (incl. reticle), proposition,
   delivery modes, footer, animations, reduced-motion.
   ───────────────────────────────────────────────────────────── */

/* Self-hosted EB Garamond (subset: A-Z, a-z, 0-9, space, & , . -).
   Used for the wordmark only. Variable font, full weight axis
   preserved. Newsreader + JetBrains Mono still load from Google
   Fonts (see index.html). */
@font-face {
  font-family: 'EB Garamond';
  src: url('../media/fonts/EBGaramond-Subset.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html {
  scroll-behavior: smooth;
  /* "Pages-not-document" feel: snap to nearest section only when the
     user settles close to a boundary. Proximity (not mandatory) so tall
     sections — especially the catalogue with expanded pillars — never
     trap the user mid-content. scroll-padding-top accounts for the
     fixed nav so anchored headings clear it on hash navigation. */
  scroll-snap-type: y proximity;
  scroll-padding-top: 80px;
}

/* Phase 6: every section is a flex column with content vertically and
   horizontally centred in its 100dvh viewport. The ::before eyebrow
   sits in flow as the first flex child, the section's *-inner wrapper
   second — justify-content: center floats the pair as a group. Tall
   sections (catalogue, engagement) overflow downward as normal; flex
   centring is a no-op once content exceeds the container.

   Hairline border-top on every non-first section is the visible
   "you've crossed into a new section" cue, replacing the alternating
   shade dropped in Phase 5. */
main > section {
  position: relative;
  scroll-snap-align: start;
  scroll-snap-stop: normal;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* `safe center` falls back to flex-start when content exceeds the
     container — important for the catalogue and engagement sections
     which can grow taller than 100dvh. Without `safe`, justify-content
     would centre and clip the top off. */
  justify-content: safe center;
  padding: 96px var(--gutter);
  border-top: 1px solid rgba(125,129,128,0.18);
}
main > section:first-of-type { border-top: 0; }

/* Section index eyebrow ("02 / PROPOSITION"), sourced from
   data-index/data-label. Sits centred above the inner content with
   generous breathing room — Phase 6 moved it from absolute-top-left
   to in-flow so its spacing is uniform across sections. Decorative;
   the section's own h2 keeps the heading outline. */
main > section::before {
  content: attr(data-index) "  /  " attr(data-label);
  align-self: center;
  margin-bottom: 64px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
  pointer-events: none;
}
@media (max-width: 720px) {
  main > section { padding: 72px var(--gutter); }
  main > section::before { margin-bottom: 40px; font-size: 10px; }
}
/* Hero suppresses the eyebrow entirely — its headline owns the
   first-impression and shouldn't be preceded by a section label. */
main > section.hero::before { content: none; display: none; }

/* Phase 5 dropped the alternating section shade — the page-wide
   constellation now provides separation between sections, so a flat
   transparent stack lets the lattice show through uniformly. The
   --bg-soft token in dark.css remains defined but is no longer used. */

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--serif);
  font-weight: 400;
  font-size: 19px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

/* Site-wide stacking: the body-level <canvas id="constellation"> is
   position:fixed at z-index:0. Every other top-level region (main,
   footer) lifts to z-index:1 so content paints over the lattice. nav
   already sits at z-index:50 (its own rule). */
main, footer {
  position: relative;
  z-index: 1;
}

/* skip link for keyboard users — appears on focus only */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  padding: 12px 16px;
  background: var(--panel);
  color: var(--text);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
}
.skip-link:focus { left: 12px; top: 12px; outline: 1px solid var(--accent-hi); }

/* shared focus ring — restrained, mono-coloured */
:focus-visible {
  outline: 1px solid var(--accent-hi);
  outline-offset: 3px;
}

.eyebrow {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ───────── nav ─────────
   Idle (top-of-page): brand mark and wordmark are large — they set the
   page's first impression. As soon as the user scrolls past 30px the
   .scrolled class fires (js/nav.js) and the brand smoothly compacts to
   a quiet utility size that stays out of the way of section content.
   Nav links keep their 12px size in both states so the change reads as
   "the brand stepping back," not as the whole bar resizing. */
nav {
  position: fixed; top: 0; left: 0; right: 0;
  z-index: 50;
  display: flex; align-items: center; justify-content: space-between;
  padding: 28px var(--gutter);
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  background: linear-gradient(to bottom, rgba(25,28,28,0.92), rgba(25,28,28,0));
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition:
    padding   240ms cubic-bezier(.25,.1,.25,1),
    background 300ms ease,
    border-color 300ms ease;
}
nav.scrolled {
  padding: 16px var(--gutter);
  background: rgba(25,28,28,0.94);
  border-bottom: 1px solid rgba(125,129,128,0.12);
}
nav .brand {
  display: flex; align-items: center; gap: 16px;
  color: var(--text);
  /* EB Garamond wordmark — closest free analogue to the paid Selune
     Clair used in the brand's emblem+text lockup files. Distinct
     from Newsreader (the body serif) so the brand mark reads as a
     dedicated wordmark rather than a body-text echo. */
  font-family: 'EB Garamond', 'Newsreader', Georgia, serif;
  font-weight: 500;
  font-size: 26px;
  letter-spacing: 0.04em;
  text-decoration: none;
  transition:
    font-size      240ms cubic-bezier(.25,.1,.25,1),
    letter-spacing 240ms cubic-bezier(.25,.1,.25,1),
    gap            240ms cubic-bezier(.25,.1,.25,1);
}
nav.scrolled .brand {
  font-size: 15px;
  letter-spacing: 0.06em;
  gap: 14px;
}
nav .brand .mark {
  width: 52px; height: 52px;
  display: block;
  flex-shrink: 0;
  transition:
    width  240ms cubic-bezier(.25,.1,.25,1),
    height 240ms cubic-bezier(.25,.1,.25,1);
}
nav.scrolled .brand .mark {
  width: 22px; height: 22px;
}
@media (max-width: 720px) {
  /* tame the idle brand on phones so the wordmark doesn't wrap */
  nav .brand { font-size: 16px; gap: 12px; }
  nav .brand .mark { width: 40px; height: 40px; }
}
nav .nav-links {
  display: flex; gap: 32px;
  color: var(--muted);
}
nav .nav-links a {
  color: var(--muted);
  text-decoration: none;
  transition: color 200ms ease;
}
nav .nav-links a:hover { color: var(--text); }

/* ───────── mobile nav toggle ─────────
   Phase 6. ≤720px the inline link row becomes a slide-down panel
   below the nav bar, opened by the typographic .nav-toggle button.
   Desktop hides the toggle; mobile hides the inline row. */
.nav-toggle {
  display: none;
  background: transparent;
  border: 0;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text);
  cursor: pointer;
  padding: 8px 4px;
  transition: color 200ms ease;
}
.nav-toggle:hover { color: var(--accent-hi); }

@media (max-width: 720px) {
  .nav-toggle { display: inline-block; }
  nav .nav-links {
    /* Override the desktop inline-flex row — become a fixed slide-down
       panel under the nav bar. js/nav.js sets --nav-h on the panel from
       the live nav height so the panel always starts flush, regardless
       of idle vs .scrolled padding. */
    display: flex;
    flex-direction: column;
    gap: 24px;
    position: fixed;
    top: var(--nav-h, 72px);
    left: 0;
    right: 0;
    background: var(--bg);
    padding: 32px var(--gutter) 40px;
    border-bottom: 1px solid rgba(125,129,128,0.18);
    transform: translateY(-12px);
    opacity: 0;
    pointer-events: none;
    transition: opacity 240ms ease-out, transform 240ms ease-out;
  }
  nav .nav-links[data-open="true"] {
    transform: translateY(0);
    opacity: 1;
    pointer-events: auto;
  }
  nav .nav-links a {
    font-size: 16px;
    color: var(--text-dim);
  }
}

/* ───────── hero ───────── */
.hero {
  position: relative;
  /* min-height comes from `main > section` (100dvh) — kept here only
     conceptually; the snap rule owns sizing now. */
  display: flex; align-items: center;
  padding: 120px var(--gutter) 80px;
}

/* Phase 5: the canvas is body-level and fixed to the viewport so the
   lattice persists across the whole scroll. js/hero.js sizes the canvas
   from getBoundingClientRect() — with fixed positioning the rect equals
   the viewport, so density and cursor-glow math need no changes. */
#constellation {
  position: fixed; inset: 0;
  width: 100vw; height: 100vh;
  pointer-events: none;
  z-index: 0;
}

/* Phase 6 follow-up: hero text column is narrower than the rest of the
   site (which uses --max-w / 1200). The narrower column leaves gutters
   of empty constellation field on either side at typical desktop
   viewports, so the kw-panel can dock outside the text column without
   overlapping the headline or sub. */
.hero-inner {
  position: relative; z-index: 2;
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
}

.hero .eyebrow {
  opacity: 0;
  transform: translateY(6px);
  animation: fadeUp 600ms ease-out 200ms forwards;
}

.hero h1 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(40px, 7.2vw, 92px);
  line-height: 1.02;
  letter-spacing: -0.02em;
  margin-top: 28px;
  max-width: 14ch;
  min-height: 1em;
}
.hero h1 .typed { white-space: pre-wrap; }
.hero h1 .cursor {
  display: inline-block;
  width: 2px;
  height: 0.85em;
  background: var(--accent-hi);
  vertical-align: -0.06em;
  margin-left: 4px;
  opacity: 1;
}
.hero h1 .cursor.done {
  animation: cursorFade 1800ms ease-in 300ms forwards;
}

.hero .sub {
  margin-top: 32px;
  max-width: 665px;
  font-size: clamp(17px, 1.5vw, 21px);
  line-height: 1.55;
  color: var(--text-dim);
  opacity: 0;
  transform: translateY(8px);
}
.hero .sub.visible {
  animation: fadeUp 700ms ease-out forwards;
}

.hero .cta-row {
  margin-top: 48px;
  display: flex; align-items: center; gap: 24px;
  opacity: 0;
}
.hero .cta-row.visible {
  animation: fadeUp 700ms ease-out forwards;
}

.cta-primary {
  display: inline-flex; align-items: center; gap: 14px;
  padding: 16px 26px;
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text);
  background: transparent;
  border: 1px solid var(--muted);
  text-decoration: none;
  cursor: pointer;
  transition: border-color 250ms ease, color 250ms ease, background 250ms ease;
  position: relative;
}
.cta-primary:hover {
  border-color: var(--text);
  background: rgba(255,255,255,0.03);
}
.cta-primary .arrow {
  position: relative; display: inline-block; width: 18px; height: 1px;
  background: currentColor;
  transition: width 250ms ease;
}
.cta-primary .arrow::after {
  content: '';
  position: absolute; right: -1px; top: -3px;
  width: 7px; height: 7px;
  border-top: 1px solid currentColor;
  border-right: 1px solid currentColor;
  transform: rotate(45deg);
}
.cta-primary:hover .arrow { width: 32px; }

.scroll-hint {
  position: absolute;
  bottom: 32px; left: var(--gutter);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.2em;
  color: var(--muted);
  text-transform: uppercase;
  display: flex; align-items: center; gap: 12px;
  opacity: 0;
  animation: fadeUp 800ms ease-out 2200ms forwards;
}
.scroll-hint::before {
  content: '';
  width: 32px; height: 1px;
  background: var(--muted);
}

.hero-meta {
  position: absolute;
  bottom: 32px; right: var(--gutter);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  color: var(--muted);
  text-transform: uppercase;
  text-align: right;
  opacity: 0;
  animation: fadeUp 800ms ease-out 2200ms forwards;
}
.hero-meta div + div { margin-top: 4px; }

/* ───────── hero keyword reveal ─────────
   Five inline tokens in the hero sub line — sources, systems, analytics,
   playbooks, expertise — become buttons. Hover (or focus on keyboard, or
   tap on touch) opens .kw-panel which lists the catalogue pillars that
   deliver that capability. The dotted underline is a restrained "this is
   interactive" hint. */
.kw {
  font: inherit;
  color: var(--text);
  background: transparent;
  border: 0;
  padding: 0 1px;
  margin: 0;
  cursor: pointer;
  border-bottom: 1px dotted var(--muted);
  text-underline-offset: 4px;
  transition: color 150ms ease, border-color 150ms ease, border-style 150ms ease;
}
.kw:hover,
.kw:focus-visible,
.kw[aria-expanded="true"] {
  color: var(--text);
  border-bottom-color: var(--accent-hi);
  border-bottom-style: solid;
  outline: none;
}
.kw:focus-visible {
  outline: 1px solid var(--accent-hi);
  outline-offset: 3px;
}

/* Desktop: the panel docks in the .hero gutter outside .hero-inner —
   side picked by keywords.js based on the active kw's x-position
   (.kw-panel--left / --right). The narrower .hero-inner (720px) leaves
   real empty space on both sides at typical desktop viewports, so the
   panel sits in the constellation field rather than over the text.
   Phase 6 dropped the bordered-chip look in favour of a soft dark
   rectangle with stronger backdrop blur — reads as "the constellation
   thickening into form" rather than a UI panel snapping in. Below
   1100px the panel re-parents into .hero-inner after .sub and flows
   inline (see media query and js/keywords.js). */
.kw-panel {
  position: absolute;
  /* --kw-y is set by keywords.js to the active keyword's mid-y relative
     to .hero, so the panel rises beside the keyword's row instead of
     pinning to hero centre. Default 50% keeps the layout sane before
     JS runs. --kw-jitter-x/y add a small random offset on each open so
     the panel doesn't materialise in exactly the same spot. */
  top: var(--kw-y, 50%);
  transform: translate(var(--kw-jitter-x, 0px), calc(-50% + var(--kw-jitter-y, 0px)));
  width: clamp(240px, 18vw, 280px);
  background: rgba(25,28,28,0.55);
  padding: 22px 24px 20px;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  /* Soft, tight feather — small offset, modest blur, low alpha. Reads
     as an ambient lift off the surface rather than a heavy plate. */
  box-shadow: 0 6px 22px rgba(0,0,0,0.32);
  opacity: 0;
  pointer-events: none;
  z-index: 3;
  /* Tokens (--panel-curve, --panel-swap-ms) live in dark.css and are
     shared with .pillar-panel below — both surfaces feel like one
     gesture in two rooms. */
  transition: opacity var(--panel-swap-ms) var(--panel-curve),
              transform var(--panel-swap-ms) var(--panel-curve);
}
/* Side variants — anchor to the .hero-inner edge (half-width 360px
   from horizontal centre) plus --kw-gap, so the panel always sits a
   short, fixed distance from the text column at any viewport width.
   Earlier revs anchored to var(--gutter), which capped at 64px and
   left the panel ~277px away from text on a 1920px monitor. */
.kw-panel.kw-panel--right {
  left: calc(50% + 360px + var(--kw-gap));
  right: auto;
  transform-origin: top right;
}
.kw-panel.kw-panel--left {
  right: calc(50% + 360px + var(--kw-gap));
  left: auto;
  transform-origin: top left;
}
/* Centre-overlay fallback — used when the viewport is too narrow for
   the side panel to fit (vp < 1380px ish). Panel sits over the hero-
   inner column with stronger blur + drop shadow to hide the headline
   text behind it; the same coalesce+jitter+drift rhythm still plays. */
.kw-panel.kw-panel--centre {
  left: 50%;
  right: auto;
  transform: translate(calc(-50% + var(--kw-jitter-x, 0px)),
                       calc(-50% + var(--kw-jitter-y, 0px)));
  transform-origin: center center;
  background: rgba(25,28,28,0.78);
  backdrop-filter: blur(22px);
  -webkit-backdrop-filter: blur(22px);
  /* Slightly heavier than the side panels since centre-mode sits over
     copy that would otherwise read through, but same feathering profile. */
  box-shadow: 0 8px 26px rgba(0,0,0,0.38);
}
.kw-panel:not([hidden]) {
  /* the [hidden] attribute is removed by JS when opening — at that
     point pointer-events come back and the fade-in plays. */
  pointer-events: auto;
}
.kw-panel.in {
  opacity: 1;
  transform: translate(var(--kw-jitter-x, 0px), calc(-50% - 6px + var(--kw-jitter-y, 0px)));
}
.kw-panel.kw-panel--centre.in {
  transform: translate(calc(-50% + var(--kw-jitter-x, 0px)),
                       calc(-50% - 6px + var(--kw-jitter-y, 0px)));
}

.kw-panel-head {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "eyebrow close"
    "title   title";
  align-items: baseline;
  column-gap: 12px;
  margin-bottom: 14px;
}
.kw-panel-eyebrow {
  grid-area: eyebrow;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
}
/* Active keyword name — set by keywords.js renderPanel(). Sits below
   the mono eyebrow as a brand-serif heading; gives the panel its own
   anchor instead of leaning on the kw underline alone. */
.kw-panel-title {
  grid-area: title;
  display: block;
  margin-top: 4px;
  font-family: 'EB Garamond', 'Newsreader', Georgia, serif;
  font-weight: 500;
  font-size: 22px;
  letter-spacing: 0.02em;
  line-height: 1.1;
  color: var(--text);
}
.kw-panel-title:empty { display: none; }
.kw-panel-close {
  grid-area: close;
  background: transparent;
  border: 0;
  color: var(--muted);
  font-family: var(--serif);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
  transition: color 200ms ease;
}
.kw-panel-close:hover { color: var(--text); }

.kw-panel-list {
  list-style: none;
  margin: 0 0 16px;
  padding: 0;
}
.kw-panel-list li + li {
  border-top: 1px solid rgba(125,129,128,0.10);
}
.kw-panel-list a {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 8px 0;
  text-decoration: none;
  color: var(--text-dim);
  font-size: 15px;
  line-height: 1.35;
  transition: color 180ms ease;
}
.kw-panel-list a:hover,
.kw-panel-list a:focus-visible { color: var(--text); outline: none; }
.kw-panel-list .kw-num {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--muted);
  min-width: 22px;
}

.kw-panel-cta {
  display: inline-block;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-hi);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color 180ms ease, border-color 180ms ease;
}
.kw-panel-cta:hover { color: var(--text); border-bottom-color: var(--accent-hi); }

/* ───────── reticle (site-wide cursor-tracking targeting overlay) ───────── */
.reticle {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 50;
  opacity: 0;
  transition: opacity 600ms ease;
}
.reticle.active { opacity: 1; }

.reticle .frame {
  position: absolute;
  width: 56px; height: 56px;          /* initial; JS sets per frame */
  will-change: transform, width, height;
  transform: translate(-50%, -50%);
  pointer-events: none;
}
.reticle .frame span {
  position: absolute;
  width: 12px; height: 12px;
  border-color: var(--accent-hi);
  border-style: solid;
  border-width: 0;
  opacity: 0.85;
  transition: opacity 200ms ease;
}
.reticle.locked .frame span { opacity: 1; }
.reticle .frame .tl { top: 0; left: 0; border-top-width: 1px; border-left-width: 1px; }
.reticle .frame .tr { top: 0; right: 0; border-top-width: 1px; border-right-width: 1px; }
.reticle .frame .bl { bottom: 0; left: 0; border-bottom-width: 1px; border-left-width: 1px; }
.reticle .frame .br { bottom: 0; right: 0; border-bottom-width: 1px; border-right-width: 1px; }

.reticle .readout {
  position: absolute;
  /* placed adjacent to the frame, offset right & down */
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  color: var(--accent-hi);
  white-space: nowrap;
  text-transform: uppercase;
}
.reticle .readout div + div { margin-top: 3px; }

/* hide the reticle on touch and reduced-motion */
@media (hover: none), (prefers-reduced-motion: reduce) {
  .reticle { display: none; }
}

/* On touch or narrow viewports, suppress the floating hero kw-panel
   entirely. Tapping a keyword routes to the catalogue (see
   js/keywords.js routeToCatalogue) and opens the mapped pillars as
   inline accordions there — no centre-overlay covering the hero. */
@media (hover: none), (max-width: 720px) {
  .kw-panel { display: none !important; }

  /* Tactile down-state for touch: a brief opacity dip on tap so the
     user sees the press register. Skipped on hover-capable desktops
     where the existing :hover treatment already provides feedback. */
  .kw:active,
  .pillar-head:active,
  .cta:active,
  nav a:active {
    opacity: 0.65;
    transition: opacity 80ms ease-out;
  }
}

/* One-shot accent flash on a pillar head when the keyword router
   opens it (mobile kw tap → scroll-to-catalogue → pulse). Subtle
   accent-tint background, fades to transparent. */
@keyframes kw-pulse-flash {
  0%   { background: rgba(56, 61, 60, 0.0); }
  20%  { background: rgba(56, 61, 60, 0.55); }
  100% { background: rgba(56, 61, 60, 0.0); }
}
.pillar-head.kw-pulse { animation: kw-pulse-flash 700ms ease-out; }

/* ───────── proposition ───────── */
/* Phase 6: section-level flex centring (in main > section) handles
   the layout — no per-section padding/flex needed here. */
.proposition p {
  max-width: 860px;
  text-align: center;
  font-family: var(--serif);
  font-size: clamp(22px, 2.4vw, 32px);
  line-height: 1.45;
  letter-spacing: -0.005em;
  color: var(--text);
  font-weight: 400;
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 900ms ease-out, transform 900ms ease-out;
}
.proposition p.in { opacity: 1; transform: translateY(0); }
.proposition em {
  font-style: italic;
  color: var(--text-dim);
}
@media (max-width: 720px) {
  /* Slightly bigger floor than the desktop clamp's 22px so the
     paragraph doesn't feel cramped at 390px width. */
  .proposition p { font-size: clamp(20px, 4.6vw, 28px); }
}

/* ───────── delivery modes ───────── */
/* Phase 6: .modes-inner wraps the section's three content blocks (h2,
   grid, foot) so the section's flex column has a single content
   child beside the ::before eyebrow. max-width + margin auto live
   on the inner now, not the section. */
.modes-inner {
  width: 100%;
  max-width: var(--max-w);
  margin: 0 auto;
}
.modes h2 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(28px, 3.6vw, 48px);
  letter-spacing: -0.015em;
  line-height: 1.1;
  max-width: 22ch;
  margin: 0 auto 64px;
  text-align: center;
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 800ms ease-out, transform 800ms ease-out;
}
.modes h2.in { opacity: 1; transform: translateY(0); }

.modes-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
}
@media (max-width: 760px) {
  .modes-grid { grid-template-columns: 1fr; }
}
@media (max-width: 720px) {
  /* Cards have plenty of internal whitespace at desktop sizes (48/44);
     tighten on phones so the card doesn't dominate the viewport. */
  .mode-card { padding: 32px 28px; }
}

.mode-card {
  position: relative;
  background: var(--panel);
  padding: 48px 44px;
  border: 1px solid rgba(56,61,60,0.5);
  transition: opacity 300ms ease;
  opacity: 0;
  transform: translateY(40px);
}
.mode-card.in {
  opacity: 1; transform: translateY(0);
  transition: opacity 700ms ease-out, transform 700ms ease-out;
}
.mode-card.in:nth-child(2) { transition-delay: 120ms; }

.modes-grid:hover .mode-card { opacity: 0.4; }
.modes-grid:hover .mode-card:hover { opacity: 1; }

.mode-card .label {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  color: var(--muted);
  text-transform: uppercase;
}
.mode-card .tagline {
  font-family: var(--serif);
  font-style: italic;
  font-size: 22px;
  margin-top: 20px;
  color: var(--text);
}
.mode-card .body {
  margin-top: 24px;
  color: var(--text-dim);
  font-size: 17px;
  line-height: 1.6;
}

/* sequential corner-drawing border on hover */
.mode-card .corner {
  position: absolute;
  background: var(--accent-hi);
  transition: all 350ms ease-out;
  opacity: 0;
}
.mode-card .corner.t { top: 0; left: 0; height: 1px; width: 0; }
.mode-card .corner.r { top: 0; right: 0; width: 1px; height: 0; }
.mode-card .corner.b { bottom: 0; right: 0; height: 1px; width: 0; }
.mode-card .corner.l { bottom: 0; left: 0; width: 1px; height: 0; }

.mode-card:hover .corner { opacity: 1; }
.mode-card:hover .corner.t { width: 100%; transition-delay: 0ms; }
.mode-card:hover .corner.r { height: 100%; transition-delay: 150ms; }
.mode-card:hover .corner.b { width: 100%; transition-delay: 300ms; }
.mode-card:hover .corner.l { height: 100%; transition-delay: 450ms; }

.modes-foot {
  margin-top: 36px;
  text-align: center;
  font-style: italic;
  color: var(--text-dim);
  font-size: 14px;
}

/* ───────── capability catalogue ───────── */
.catalogue {
  position: relative;
  padding-left: 0;
  padding-right: 0;
}
.catalogue-inner {
  position: relative;        /* positioning context for .pillar-panel */
  width: 100%;
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.catalogue-header {
  margin: 0 auto 80px;
  max-width: 720px;
  text-align: center;
}
.catalogue-header h2 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(28px, 3.6vw, 48px);
  letter-spacing: -0.015em;
  line-height: 1.1;
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 800ms ease-out, transform 800ms ease-out;
}
.catalogue-header h2.in {
  opacity: 1;
  transform: translateY(0);
}
.catalogue-sub {
  margin-top: 18px;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  font-style: italic;
  opacity: 0;
  transition: opacity 800ms ease-out 100ms;
}
.catalogue-sub.in { opacity: 1; }

.catalogue-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 64px 56px;
}
@media (max-width: 980px) {
  .catalogue-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 600px) {
  .catalogue-grid { grid-template-columns: 1fr; }
}

.family-name {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 12px;
  padding-bottom: 14px;
  border-bottom: 1px solid rgba(125,129,128,0.18);
}

.pillars {
  list-style: none;
  padding: 0;
  margin: 0;
}
.pillar { position: relative; }
.pillar + .pillar { border-top: 1px solid rgba(125,129,128,0.10); }

.pillar-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  width: 100%;
  padding: 18px 4px 18px 0;
  background: transparent;
  border: 0;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: var(--text-dim);
  position: relative;
  transition: color 250ms ease;
}
.pillar-head .num {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--muted);
  flex-shrink: 0;
  width: 22px;
  transition: color 250ms ease;
}
.pillar-head .dash {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--muted);
  flex-shrink: 0;
  transition: color 250ms ease;
}
.pillar-head .name {
  font-family: var(--serif);
  font-size: 18px;
  line-height: 1.3;
  color: var(--text-dim);
  transition: color 250ms ease;
}

/* hover under-line: 1px accent draws left-to-right */
.pillar-head::after {
  content: '';
  position: absolute;
  left: 0; bottom: -1px;
  width: 100%; height: 1px;
  background: var(--accent-hi);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 250ms ease-out;
}
.pillar-head:hover::after,
.pillar-head[aria-expanded="true"]::after {
  transform: scaleX(1);
}
.pillar-head:hover .num,
.pillar-head[aria-expanded="true"] .num { color: var(--text); }
.pillar-head:hover .name,
.pillar-head[aria-expanded="true"] .name { color: var(--text); }

/* expandable body — height set in JS for smooth transition */
.pillar-body {
  overflow: hidden;
  max-height: 0;
  transition: max-height 220ms ease-out;
}
.pillar-body .scope {
  padding: 4px 4px 12px 34px;
  font-size: 15.5px;
  color: var(--text-dim);
  line-height: 1.55;
  opacity: 0;
  transition: opacity 250ms ease-out;
}
.pillar-body .nda {
  display: block;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: right;
  padding: 0 4px 14px;
  opacity: 0;
  transition: opacity 250ms ease-out;
}
.pillar-head[aria-expanded="true"] + .pillar-body .scope {
  opacity: 1;
  transition-delay: 100ms;
}
.pillar-head[aria-expanded="true"] + .pillar-body .nda {
  opacity: 1;
  transition-delay: 200ms;
}

/* ───────── catalogue side-coalesce panel (desktop only) ─────────
   Phase 7 — kw-panel-style anchoring. The panel sits absolutely
   positioned inside .catalogue-inner; --pillar-y is written by JS
   to the active pillar's vertical centre so the panel rides the
   same coordinate space as the pillars (scroll comes for free —
   no scroll listener needed). Side classes pick a column-aware
   horizontal anchor: --right overlays the column to the right of
   the active pillar, --left overlays the column to the left.

   Below 981px the floating panel is fully suppressed (rule at the
   bottom of this block) and the original inline accordion stands.
   Below 981px OR on coarse pointers, hover-intent is also gated
   off in catalogue.js so the panel never floats on touch. */
.pillar-panel {
  position: absolute;
  top: var(--pillar-y, 50%);
  /* Match kw-panel's 240–280 width — both surfaces share the same
     visual rhythm AND the narrower clamp frees up vp gutter space at
     1920+ so the panel can prefer the gutter (see catalogue.js
     isGutterMode + applyAnchor). */
  width: clamp(240px, 18vw, 280px);
  /* Match hero kw-panel styling: borderless, translucent + heavy blur,
     drop shadow. The panel overlays adjacent column content; the blur
     hides the pillars behind it without a hard frame. --pillar-jitter-x/y
     are written by JS on each open so the panel doesn't materialise in
     the exact same spot every time. */
  background: rgba(25,28,28,0.78);
  padding: 22px 24px 24px;
  backdrop-filter: blur(22px);
  -webkit-backdrop-filter: blur(22px);
  /* Match the kw-panel shadow weight — same feathering family. */
  box-shadow: 0 6px 22px rgba(0,0,0,0.32);
  z-index: 40;
  opacity: 0;
  pointer-events: none;
  transform: translate(var(--pillar-jitter-x, 0px),
                       calc(-50% + 8px + var(--pillar-jitter-y, 0px)));
  /* Shared rhythm tokens — see dark.css. Only opacity + transform
     transition; --pillar-y / --pillar-anchor are mutated at opacity 0
     during swap so they jump (not animate) to the new pillar. */
  transition: opacity var(--panel-swap-ms) var(--panel-curve),
              transform var(--panel-swap-ms) var(--panel-curve);
}
.pillar-panel:not([hidden]) { pointer-events: auto; }
.pillar-panel.in {
  opacity: 1;
  transform: translate(var(--pillar-jitter-x, 0px),
                       calc(-50% + var(--pillar-jitter-y, 0px)));
}

/* Side variants — JS writes --pillar-anchor as the x-coordinate of
   the pillar's near edge (right edge for --right side, distance-from-
   inner-right for --left side), measured inside .catalogue-inner.
   Anchoring per-pillar avoids the grid-gap math that 33.333% tricks
   would mis-handle, and lets the panel slide horizontally as well as
   vertically when swapping between pillars in different families. */
.pillar-panel.pillar-panel--right {
  left: calc(var(--pillar-anchor, 50%) + var(--pillar-gap));
  right: auto;
  transform-origin: top left;
}
.pillar-panel.pillar-panel--left {
  right: calc(var(--pillar-anchor, 50%) + var(--pillar-gap));
  left: auto;
  transform-origin: top right;
}

.pillar-panel-head {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "eyebrow close"
    "title   title";
  align-items: baseline;
  column-gap: 12px;
  margin-bottom: 16px;
}
.pillar-panel-eyebrow {
  grid-area: eyebrow;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
}
.pillar-panel-close {
  grid-area: close;
  background: transparent;
  border: 0;
  color: var(--muted);
  font-family: var(--serif);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
  transition: color 200ms ease;
}
.pillar-panel-close:hover { color: var(--text); }
.pillar-panel-title {
  grid-area: title;
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-top: 6px;
  font-family: 'EB Garamond', 'Newsreader', Georgia, serif;
  font-weight: 500;
  font-size: 22px;
  letter-spacing: 0.02em;
  line-height: 1.2;
  color: var(--text);
}
.pillar-panel-num {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--muted);
  flex-shrink: 0;
}
.pillar-panel-scope {
  font-size: 16px;
  line-height: 1.55;
  color: var(--text-dim);
  margin-bottom: 18px;
}
.pillar-panel-nda {
  display: block;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: right;
}

@media (max-width: 980px) {
  .pillar-panel { display: none !important; }
}

/* ───────── why partners choose us ───────── */
/* Phase 6: section-level rule owns padding + centring; max-width
   lives on the inner. */
.why-inner {
  width: 100%;
  max-width: var(--max-w);
  margin: 0 auto;
}
.why h2 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(28px, 3.6vw, 48px);
  letter-spacing: -0.015em;
  line-height: 1.1;
  max-width: 18ch;
  margin: 0 auto 72px;
  text-align: center;
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 800ms ease-out, transform 800ms ease-out;
}
.why h2.in { opacity: 1; transform: translateY(0); }

.why-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 56px 64px;
}
@media (max-width: 760px) {
  .why-grid { grid-template-columns: 1fr; gap: 48px; }
}

.why-cell {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 700ms ease-out, transform 700ms ease-out;
}
.why-cell.in { opacity: 1; transform: translateY(0); }
.why-cell:nth-child(2).in { transition-delay: 100ms; }
.why-cell:nth-child(3).in { transition-delay: 200ms; }
.why-cell:nth-child(4).in { transition-delay: 300ms; }

.why-cell h3 {
  position: relative;
  font-family: var(--serif);
  font-weight: 600;
  font-size: 24px;
  letter-spacing: -0.005em;
  line-height: 1.25;
  padding-bottom: 14px;
  margin-bottom: 16px;
  display: inline-block;
}
.why-cell h3::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: var(--accent-hi);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 250ms ease-out;
}
.why-cell:hover h3::after { transform: scaleX(1); }

.why-cell p {
  color: var(--text-dim);
  font-size: 17.5px;
  line-height: 1.6;
  max-width: 50ch;
}

/* ───────── engagement timeline ───────── */
/* Phase 6: section-level rule owns padding + centring; max-width
   lives on the inner. */
.engagement-inner {
  width: 100%;
  max-width: var(--max-w);
  margin: 0 auto;
}
.engagement h2 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(28px, 3.6vw, 48px);
  letter-spacing: -0.015em;
  line-height: 1.1;
  margin: 0 auto 96px;
  text-align: center;
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 800ms ease-out, transform 800ms ease-out;
}
.engagement h2.in { opacity: 1; transform: translateY(0); }

.timeline { position: relative; }
.timeline-line {
  position: absolute;
  top: 14px;
  left: 0; right: 0;
  height: 1px;
  background: var(--muted);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 1000ms cubic-bezier(0.4, 0, 0.2, 1);
}
.timeline.in .timeline-line { transform: scaleX(1); }

.timeline-steps {
  list-style: none;
  margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 32px;
  position: relative;
}

.step {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding-right: 24px;
}
.step-node {
  position: relative;
  margin-bottom: 32px;
  transform: scale(0.5);
  opacity: 0;
  transition: transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1), opacity 200ms ease-out;
}
.step.in .step-node { transform: scale(1); opacity: 1; }

/* odometer-style number — two digit slots, each is a vertical strip */
.step-num {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 28px;
  line-height: 1;
  color: var(--text);
  background: var(--bg);
  padding: 0 8px 0 0;
  display: inline-flex;
}
.digit-slot {
  display: inline-block;
  height: 1em;
  overflow: hidden;
  vertical-align: bottom;
}
.digit-strip {
  display: flex;
  flex-direction: column;
  transform: translateY(0);
}
.digit-strip i {
  display: block;
  height: 1em;
  line-height: 1;
  font-style: normal;
}

.step-meta {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 500ms ease-out 100ms, transform 500ms ease-out 100ms;
}
.step.in .step-meta { opacity: 1; transform: translateY(0); }

.step-date {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 10px;
}
.step-title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: 22px;
  line-height: 1.25;
  margin-bottom: 12px;
}
.step-body {
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--text-dim);
  max-width: 28ch;
}

/* mobile — vertical timeline */
@media (max-width: 760px) {
  .timeline-line {
    top: 0; bottom: 0;
    left: 13px; right: auto;
    width: 1px; height: auto;
    transform: scaleY(0);
    transform-origin: top;
    transition: transform 1000ms cubic-bezier(0.4, 0, 0.2, 1);
  }
  .timeline.in .timeline-line { transform: scaleY(1); }

  .timeline-steps {
    grid-template-columns: 1fr;
    gap: 56px;
  }
  .step {
    flex-direction: row;
    align-items: flex-start;
    padding-right: 0;
    gap: 28px;
  }
  .step-node {
    margin-bottom: 0;
    flex-shrink: 0;
  }
  .step-meta { flex: 1; }
}

/* ───────── CTA block ───────── */
/* Phase 6: section-level rule already centres vertically + horizontally
   in 100dvh; only text-align here. */
.cta-block { text-align: center; }
.cta-inner {
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
}
.cta-block h2 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(36px, 5vw, 56px);
  letter-spacing: -0.02em;
  line-height: 1.1;
  margin-bottom: 24px;
}
.cta-sub {
  font-style: italic;
  font-size: 17.5px;
  color: var(--text-dim);
  margin-bottom: 48px;
}

/* CTA button hover — accent line extends right of the arrow */
.cta-extended { position: relative; overflow: visible; }
.cta-extended::after {
  content: '';
  position: absolute;
  top: 50%;
  right: -8px;
  width: 0; height: 1px;
  background: var(--accent-hi);
  transform: translateY(-50%);
  transition: width 200ms ease-out, right 200ms ease-out;
  pointer-events: none;
}
.cta-extended:hover::after {
  width: 40px;
  right: -48px;
}

/* ───────── footer ───────── */
footer {
  border-top: 1px solid rgba(125,129,128,0.18);
  padding: 48px var(--gutter) 32px;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  /* slightly above muted so 11px copy clears AA on the dark bg */
  color: var(--text-dim);
  text-transform: uppercase;
  display: flex; flex-wrap: wrap;
  justify-content: space-between; gap: 24px;
  max-width: var(--max-w);
  margin: 0 auto;
}
footer .footer-block.right { text-align: right; }
footer .brand { color: var(--text); letter-spacing: 0.16em; }
footer .strapline {
  margin-top: 8px;
  text-transform: none;
  letter-spacing: 0.06em;
}
footer .footer-contacts { margin-top: 8px; }
footer a { color: var(--text-dim); text-decoration: none; transition: color 200ms ease; }
footer a:hover { color: var(--text); }

@media (max-width: 600px) {
  /* Explicit stack rather than relying on flex-wrap — the right block
     was right-aligning awkwardly when wrap kicked in on narrow phones. */
  footer { flex-direction: column; gap: 28px; }
  footer .footer-block.right { text-align: left; }
}

/* ───────── animations ───────── */
@keyframes fadeUp {
  to { opacity: 1; transform: translateY(0); }
}
@keyframes cursorFade {
  0%, 30% { opacity: 1; }
  100% { opacity: 0; }
}

/* ───────── reduced motion ───────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  #constellation { display: none; }
  .reticle { display: none; }
  /* ensure delayed-fade content is visible without animation */
  .hero .eyebrow,
  .hero .sub,
  .hero .cta-row,
  .scroll-hint,
  .hero-meta { opacity: 1; transform: none; }
  .proposition p,
  .modes h2,
  .mode-card,
  .catalogue-header h2,
  .catalogue-sub,
  .why h2,
  .why-cell,
  .engagement h2,
  .step-node,
  .step-meta { opacity: 1; transform: none; }
  .timeline-line { transform: scaleX(1); }
  /* keyword panel: instant open/close, no slide */
  .kw-panel { transition: none; }
  .kw-panel.in { transform: translateY(-50%); }
  @media (max-width: 760px) { .kw-panel.in { transform: none; } }
  /* catalogue pillar panel: instant open/close, no slide */
  .pillar-panel { transition: none; }
  .pillar-panel.in { transform: translateY(-50%); }
}
