/* Engaging Data, site stylesheet.
   Implements docs/design/website-design-system.md (§7a: liquid glass).
   Layered on brand-tokens.css, which supplies the --edl-* brand values.

   Structural model: a page is a vertical stack of <section class="band">.
   Each band scopes its own surface/text tokens so dark and light sections can
   alternate down one page, see .claude/skills/engaging-data-frontend/references/site.md

   Surface model: a fixed ambient gradient (body::before) sits behind
   everything. Bands are translucent glass sheets over it; cards, stats,
   buttons, chips and inputs are a second, more opaque layer of glass on top
   of that. backdrop-filter is disabled wholesale under @media print and for
   .panel-slide specifically. Chromium's print-to-PDF pipeline cannot
   render it (see build/generate-pdfs.mjs and the case-study print rules
   below); every glass surface therefore ships an opaque print fallback.
*/

/* ─── Fonts ───────────────────────────────────────────────────────── */
/* Geist (Vercel + basement.studio), self-hosted, SIL OFL 1.1.
   See static-assets/fonts/LICENSE-Geist.txt. */

@font-face {
  font-family: "Geist";
  src: url("fonts/Geist-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Geist Mono";
  src: url("fonts/GeistMono-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ─── Tokens ──────────────────────────────────────────────────────── */

:root {
  /* Band-scoped, dark by default. brand-tokens.css supplies --edl-*. */
  --band-bg:          rgba(30, 32, 38, 0.60);
  --band-surface:     rgba(255, 255, 255, 0.06);
  --band-surface-alt: rgba(93, 149, 255, 0.12);
  --band-text:        #F8F7F4;
  --band-heading:     #FFFFFF;
  /* NOT #555568, that is ~1.5:1 on charcoal. See design system §4. */
  --band-muted:       rgba(248, 247, 244, 0.68);
  --band-divider:     rgba(255, 255, 255, 0.14);

  /* Glass mechanics, border/highlight/blur/shadow for every glass surface.
     Print fallbacks (opaque, no blur) live in the solid pairs below. */
  --band-glass-border:    rgba(255, 255, 255, 0.16);
  --band-glass-highlight: rgba(255, 255, 255, 0.30);
  --band-glass-shadow:    0 12px 40px rgba(0, 0, 0, 0.28);
  --band-blur:    26px;
  --band-blur-sm: 16px;

  /* Opaque equivalents, used only under @media print / .panel-slide. */
  --band-bg-solid:          #2A2A2A;
  --band-surface-solid:     #2B2F38;
  --band-surface-alt-solid: #2E3A57;

  --sp-1: 4px;   --sp-2: 8px;   --sp-3: 12px;  --sp-4: 16px;  --sp-5: 24px;
  --sp-6: 32px;  --sp-7: 48px;  --sp-8: 64px;  --sp-9: 96px;  --sp-10: 128px;

  --shell: 1200px;
  --shell-narrow: 760px;
  --gutter: 24px;
  --band-pad: 96px;
  --header-h: 72px;
  --header-gap: 16px;
  --radius: 9px;
  --radius-sm: 6px;
  --radius-btn: 10px;

  --font-head: "Geist", "Helvetica Neue Light", Arial, sans-serif;
  --font-body: "Geist", "Helvetica Neue", Arial, sans-serif;
  --font-mono: "Geist Mono", "SF Mono", Menlo, monospace;
}

.band--light {
  /* Near-opaque: at 0.55 over the dark backdrop the band read as a muddy
     grey-lavender, not glass. At 0.94 it is a clean off-white with just a
     trace of the ambient blue/teal coming through. Cards sit a step brighter
     so they lift off it. */
  --band-bg:          rgba(246, 247, 250, 0.94);
  --band-surface:     rgba(255, 255, 255, 0.96);
  --band-surface-alt: rgba(238, 242, 255, 0.92);
  --band-text:        #2A2A2A;
  --band-heading:     #1E2430;
  --band-muted:       #4B4B5C;
  --band-divider:     rgba(42, 42, 42, 0.12);

  --band-glass-border:    rgba(30, 36, 48, 0.08);
  --band-glass-highlight: rgba(255, 255, 255, 0.9);
  --band-glass-shadow:    0 1px 2px rgba(30, 36, 48, 0.05), 0 12px 32px -10px rgba(30, 36, 48, 0.16);

  --band-bg-solid:          #FFFFFF;
  --band-surface-solid:     #F8F7F4;
  --band-surface-alt-solid: #EEF2FF;
}

/* ─── Reset & base ────────────────────────────────────────────────── */

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

/* The browser's own [hidden] rule is display:none at the lowest precedence, so
   any component that sets display (a.card, .team-member, .step) silently beats
   it and stays visible after `el.hidden = true`. That is how the case-study
   filter came to report "showing 8 of 18" with all 18 tiles still on screen.
   Enforce it once here rather than per component. */
[hidden] { display: none !important; }

html { scroll-behavior: smooth; scroll-padding-top: calc(var(--header-h) + var(--header-gap) + 16px); }

body {
  background: #2A2A2A;
  color: #F8F7F4;
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* Fixed ambient gradient every glass surface blurs. Never scrolls, so the
   glow stays put behind whichever band is in the viewport. Hidden under
   print, position: fixed is unreliable in Chromium's print pagination
   (see build/generate-pdfs.mjs) and every glass surface has an opaque
   print fallback anyway, so there's nothing for it to show through. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(circle at 12% 18%, rgba(57, 103, 217, 0.55), transparent 42%),
    radial-gradient(circle at 88% 12%, rgba(49, 151, 149, 0.45), transparent 46%),
    radial-gradient(circle at 25% 92%, rgba(49, 151, 149, 0.30), transparent 50%),
    radial-gradient(circle at 82% 85%, rgba(57, 103, 217, 0.40), transparent 50%),
    #1E2026;
  pointer-events: none;
}

img, svg, canvas { display: block; max-width: 100%; }
ul { list-style: none; }

h1, h2, h3, h4 {
  font-family: var(--font-head);
  font-weight: 700;
  text-align: left;
  color: var(--band-heading);
  letter-spacing: -0.01em;
}

h1 { font-size: 2.25rem; line-height: 1.08; letter-spacing: -0.02em; max-width: 20ch; }
h2 { font-size: 2.25rem; line-height: 1.15; max-width: 24ch; }
h3 { font-size: 1.25rem; line-height: 1.3; }
h4 { font-size: 1rem; }

p  { max-width: 68ch; }
a  { color: inherit; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

:focus-visible { outline: 2px solid var(--edl-blue); outline-offset: 2px; }
.btn--primary:focus-visible,
.band:not(.band--light) :focus-visible { outline-color: var(--edl-teal); }

.skip-link {
  position: absolute; left: var(--sp-4); top: -100px; z-index: 300;
  background: var(--edl-blue); color: #FFFFFF; padding: var(--sp-3) var(--sp-5);
  border-radius: 2px; text-decoration: none; font-weight: 600;
}
.skip-link:focus { top: var(--sp-4); }

/* ─── Bands ───────────────────────────────────────────────────────── */

.band {
  position: relative;
  background: var(--band-bg);
  backdrop-filter: blur(var(--band-blur)) saturate(150%);
  -webkit-backdrop-filter: blur(var(--band-blur)) saturate(150%);
  color: var(--band-text);
  padding: var(--band-pad) 0;
  box-shadow: inset 0 1px 0 var(--band-glass-highlight);
}
.band__inner { max-width: var(--shell); margin: 0 auto; padding: 0 var(--gutter); }
.band__inner--narrow { max-width: var(--shell-narrow); }

/* Light bands keep the brand's heading colour hierarchy. Dark bands use white
   for every level, blue and teal headings on charcoal read as links. */
.band--light h2 { color: var(--edl-blue); }
.band--light h3 { color: var(--edl-teal); } 

.eyebrow {
  font-size: 1.25rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--edl-teal);
  margin-bottom: var(--sp-3);
}

.lead { font-size: 1.25rem; line-height: 1.5; max-width: 60ch; margin-top: var(--sp-4); }
.muted { color: var(--band-muted); }

.band__head { margin-bottom: var(--sp-7); }
.band__head p { color: var(--band-muted); margin-top: var(--sp-3); }

/* ─── Header ──────────────────────────────────────────────────────── */

.site-header {
  position: fixed; top: var(--header-gap); left: var(--header-gap); right: var(--header-gap);
  z-index: 200;
  max-width: var(--shell); margin: 0 auto;
  height: var(--header-h);
  border-radius: var(--radius);
  background: var(--edl-glass-chrome-bg);
  backdrop-filter: blur(var(--edl-glass-blur)) saturate(160%);
  -webkit-backdrop-filter: blur(var(--edl-glass-blur)) saturate(160%);
  border: 1px solid var(--edl-glass-chrome-border);
  box-shadow: inset 0 1px 0 var(--edl-glass-chrome-highlight), 0 8px 30px rgba(0, 0, 0, 0.22);
}
.site-header__inner {
  height: 100%; padding: 0 var(--gutter);
  display: flex; align-items: center; gap: var(--sp-6);
}
.site-header__logo { display: flex; align-items: center; }
.site-header__logo img { height: 30px; width: auto; }

.site-nav { margin-left: auto; }
.site-nav__list { display: flex; align-items: center; gap: 28px; }
.site-nav__list a {
  color: #F8F7F4; text-decoration: none; font-size: 0.9375rem;
  padding: 4px 0; border-bottom: 2px solid transparent;
  transition: border-color 0.15s, color 0.15s;
}
.site-nav__list a:hover,
.site-nav__list a[aria-current="page"] { border-bottom-color: var(--edl-teal); color: #FFFFFF; }

.site-nav__toggle { display: none; }
.site-nav__cta-mobile { display: none; }

/* ─── Buttons ─────────────────────────────────────────────────────── */

.btn {
  display: inline-block;
  padding: 0.7rem 1.5rem;
  border-radius: var(--radius-btn);
  border: 1px solid transparent;
  font-family: inherit;
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s, box-shadow 0.15s, transform 0.15s;
}
.btn--primary {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.28), rgba(255, 255, 255, 0) 60%), var(--edl-blue);
  color: #FFFFFF;
  border-color: rgba(255, 255, 255, 0.35);
  box-shadow: 0 6px 18px rgba(41, 82, 184, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.4);
}
.btn--primary:hover { background: linear-gradient(180deg, rgba(255, 255, 255, 0.28), rgba(255, 255, 255, 0) 60%), var(--edl-blue-deep); }
.btn--teal {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.28), rgba(255, 255, 255, 0) 60%), var(--edl-teal);
  color: #FFFFFF;
  border-color: rgba(255, 255, 255, 0.35);
  box-shadow: 0 6px 18px rgba(38, 116, 111, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.4);
}
.btn--teal:hover { background: linear-gradient(180deg, rgba(255, 255, 255, 0.28), rgba(255, 255, 255, 0) 60%), #26746F; }
.btn--ghost {
  background: var(--band-surface);
  backdrop-filter: blur(var(--band-blur-sm));
  -webkit-backdrop-filter: blur(var(--band-blur-sm));
  color: var(--band-text);
  border-color: var(--band-glass-border);
  box-shadow: inset 0 1px 0 var(--band-glass-highlight);
}
.btn--ghost:hover { border-color: rgba(255, 255, 255, 0.6); }
.band--light .btn--ghost { color: var(--edl-blue); border-color: var(--edl-blue); }
.band--light .btn--ghost:hover { background: rgba(238, 242, 255, 0.8); }

/* Floating action (the case-study "Download as PDF"). It sits outside every
   band and floats over light and dark ones alike, so it carries the header's
   dark glass rather than band-scoped colours. */
.btn--float {
  position: fixed; right: 24px; bottom: 24px; z-index: 150;
  background: var(--edl-glass-chrome-bg);
  -webkit-backdrop-filter: blur(var(--edl-glass-blur)) saturate(160%);
          backdrop-filter: blur(var(--edl-glass-blur)) saturate(160%);
  color: #FFFFFF; border-color: var(--edl-glass-chrome-border);
  box-shadow: 0 12px 32px -8px rgba(0, 0, 0, 0.4), inset 0 1px 0 var(--edl-glass-chrome-highlight);
}
.btn--float:hover { border-color: rgba(255, 255, 255, 0.4); }
@media (max-width: 640px) { .btn--float { right: 16px; bottom: 16px; } }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-6); }

/* Inline text link with the teal underline treatment */
.link {
  color: var(--edl-blue); font-weight: 600; text-decoration: none;
  border-bottom: 2px solid transparent; transition: border-color 0.15s;
}
.band:not(.band--light) .link { color: var(--edl-text, #F8F7F4); }
.link:hover { border-bottom-color: var(--edl-teal); }
.link::after { content: " \2192"; }

/* ─── Hero ────────────────────────────────────────────────────────── */

.hero { position: relative; overflow: hidden; padding-top: calc(var(--band-pad) + var(--header-h) + var(--header-gap)); }
.hero__rain {
  position: absolute; top: 0; right: 0; width: 45%; height: 100%;
  opacity: 0.18; pointer-events: none; z-index: 0;
}
.hero .band__inner { position: relative; z-index: 1; }
/* Homepage mesh-gradient background (hero-shader.js, Paper Shaders). Sits
   behind the copy, and the hero text carries a soft shadow to keep contrast
   against it. Opacity well under 1 is deliberate: it dilutes the whole
   canvas, accents included, toward the band's own dark background so the
   colour reads as a restrained glow rather than a saturated gradient. */
.hero__shader {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 0; opacity: .5;
}
.hero:has(.hero__shader) .band__inner { text-shadow: 0 2px 24px rgba(8, 9, 12, 0.9), 0 1px 2px rgba(8, 9, 12, 0.6); }
@media (max-width: 900px) { .hero__shader { opacity: .35; } }
.hero__sharp { display: inline-block; }
.hero__words {
  font-size: 2rem; line-height: 1.25;
  display: grid; align-content: start; overflow: hidden; height: 1.25em;
}
/* One phrase at a time: the next rises in from below as the current one
   lifts out (site.js). */
.hero__words li {
  grid-area: 1 / 1; color: var(--edl-teal);
  transform: translateY(100%); opacity: 0;
  transition: transform .7s cubic-bezier(.2,.7,.2,1), opacity .7s cubic-bezier(.2,.7,.2,1);
}
.hero__words li > span { display: inline-block; }
.hero__words li.is-active { transform: translateY(0); opacity: 1; }
.hero__words li.is-leaving { transform: translateY(-100%); opacity: 0; }
.hero--compact { padding-top: calc(var(--sp-8) + var(--header-h) + var(--header-gap)); padding-bottom: var(--sp-8); }
.hero--compact h1 { font-size: 2.6rem; max-width: none; }

/* Home hero: headline and lead run the full shell width rather than the
   global ch caps, so the strapline sits on one line on desktop. */
.hero:not(.hero--compact) h1 { max-width: none; font-size: 3.25rem; letter-spacing: -0.025em; }
/* The lead is kept short of the blade so the two never overlap. */
.hero:not(.hero--compact) .lead { max-width: 50ch; margin-top: var(--sp-5); color: rgba(255,255,255,.8); text-wrap: pretty; }
/* Room for the shader background to breathe at a generous size (desktop;
   the tablet breakpoint below lets it collapse to its content). */
@media (min-width: 901px) { .hero:not(.hero--compact) { min-height: 680px; } }
.hero__words-label {
  margin-top: var(--sp-7); font-size: 1.125rem; font-weight: 400;
  letter-spacing: .02em; color: rgba(255,255,255,.65);
}

/* ─── Breadcrumb ──────────────────────────────────────────────────── */

.breadcrumb { font-size: 0.8125rem; color: var(--band-muted); margin-bottom: var(--sp-4); }
.breadcrumb ol { display: flex; flex-wrap: wrap; gap: var(--sp-2); list-style: none; }
.breadcrumb li + li::before { content: "/"; margin-right: var(--sp-2); opacity: 0.5; }
.breadcrumb a { color: inherit; text-decoration: none; }
.breadcrumb a:hover { color: var(--band-heading); }

/* ─── Cards ───────────────────────────────────────────────────────── */
/* Glass panel. See design system §7a. */

.card {
  position: relative;
  background: var(--band-surface);
  backdrop-filter: blur(var(--band-blur-sm)) saturate(150%);
  -webkit-backdrop-filter: blur(var(--band-blur-sm)) saturate(150%);
  border: 1px solid var(--band-glass-border);
  border-radius: var(--radius);
  box-shadow: var(--band-glass-shadow), inset 0 1px 0 var(--band-glass-highlight);
  padding: var(--sp-5) var(--sp-5) var(--sp-6);
  transition: box-shadow 0.15s, transform 0.15s;
}
.card--alt  { background: var(--band-surface-alt); }
.card h3 { color: var(--band-heading); margin-bottom: var(--sp-2); }
.card p  { color: var(--band-muted); font-size: 0.9375rem; }
a.card { display: block; text-decoration: none; color: inherit; }
a.card:hover {
  /* Pulled in (negative spread) so the lift reads without the shadow
     reaching far enough to be clipped by the rail's scroller. */
  box-shadow: var(--band-glass-shadow), inset 0 1px 0 var(--band-glass-highlight), 0 20px 40px -16px rgba(0, 0, 0, 0.28);
  transform: translateY(-2px);
}

.card__meta {
  font-size: 0.6875rem; letter-spacing: 0.12em; text-transform: uppercase;
  font-weight: 600; color: var(--edl-teal); margin-bottom: var(--sp-3);
}

/* Headline figure on a case-study card. Colour follows the card variant, the
   same blue/teal alternation the stat tiles use. */
.card__stat {
  font-family: var(--font-head); font-size: 1.75rem; font-weight: 700;
  line-height: 1.1; color: var(--edl-blue); margin-bottom: var(--sp-3);
}
.card--teal .card__stat { color: var(--edl-teal); }
.card__more { display: block; margin-top: auto; padding-top: var(--sp-4); font-size: 0.875rem; font-weight: 600; color: var(--edl-teal); }
/* Column layout pins every card's link cue to the same baseline across the rail. */
.rail > .card { display: flex; flex-direction: column; }

/* These five were added with the case-study template and then lost when the
   dev branch's stylesheet was taken wholesale during a merge. The markup kept
   using them, so the Journey bullets, case-study quotes and h1 second lines
   rendered unstyled. See the class-coverage check in build/build.mjs. */

/* Journey phases and service outcomes. Needs the list marker restored
   explicitly because the base reset sets `ul { list-style: none }`. */
.card__list { margin: var(--sp-4) 0; padding-left: 1.1rem; }
.card__list li {
  list-style: disc;
  color: var(--band-muted);
  font-size: 0.9375rem;
  margin-top: var(--sp-2);
}
.card__list li::marker { color: var(--edl-teal); }

/* Service subtitle. Qualified with p so it outranks `.card p` on colour
   rather than resorting to !important. */
p.card__sub {
  color: var(--edl-teal);
  font-weight: 600;
  font-size: 0.9375rem;
  margin-bottom: var(--sp-3);
}
.band--light p.card__sub { color: var(--edl-blue); }

/* Client quote on a case study. */
.pull-quote {
  margin-top: var(--sp-6);
  padding: var(--sp-5) var(--sp-6);
  background: var(--band-surface);
  border-left: 3px solid var(--edl-teal);
  border-radius: var(--radius-sm);
  font-style: italic;
  color: var(--band-text);
  max-width: 68ch;
}
.pull-quote cite {
  display: block; margin-top: var(--sp-4);
  font-style: normal; font-size: 0.8125rem; color: var(--band-muted);
}

/* Second line of a case-study h1. */
.h1-sub { color: var(--edl-teal); }

.card__logo {
  display: block;
  height: 2.5rem;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  margin-bottom: var(--sp-3);
}

/* Card whose logo fills the whole tile, swapping to its meta/h3/p blurb on hover. */
.card--logo-fill {
  position: relative;
  overflow: hidden;
  padding: 0;
  min-height: 12rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.card--logo-fill .card__logo {
  position: absolute;
  inset: 0;
  height: 100%;
  width: 100%;
  margin: 0;
  padding: var(--sp-5);
  transition: opacity 0.15s;
}
.card--logo-fill::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--band-surface);
  opacity: 0;
  transition: opacity 0.15s;
}
.card--logo-fill .card__meta,
.card--logo-fill h3,
.card--logo-fill p {
  position: relative;
  z-index: 1;
  opacity: 0;
  margin: 0;
  padding: 0 var(--sp-5);
  transition: opacity 0.15s;
}
.card--logo-fill h3 { margin-top: var(--sp-2); }
.card--logo-fill p { margin-top: var(--sp-2); }
.card--logo-fill:hover .card__logo { opacity: 0; }
.card--logo-fill:hover::after,
.card--logo-fill:hover .card__meta,
.card--logo-fill:hover h3,
.card--logo-fill:hover p {
  opacity: 1;
}

/* Team member card: headshot to the left of name/title. */
.team-member { display: flex; align-items: center; gap: var(--sp-4); }
.team-member__photo {
  flex-shrink: 0; width: 112px; height: 112px; border-radius: 50%;
  object-fit: cover;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.45);
}
.team-member h3 { margin-bottom: 2px; }

.grid { display: grid; gap: var(--sp-4); }
.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }
.grid--4 { grid-template-columns: repeat(4, 1fr); }

/* ─── Pillars ─────────────────────────────────────────────────────── */

.pillar__num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 50%;
  background: var(--edl-blue); color: #FFFFFF;
  font-family: var(--font-head); font-weight: 700; font-size: 1.1rem;
  margin-bottom: var(--sp-4);
  box-shadow: 0 6px 16px rgba(41, 82, 184, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.45);
}
.card--teal .pillar__num { background: var(--edl-teal); }
.pillar__links { margin-top: var(--sp-4); border-top: 1px solid var(--band-divider); padding-top: var(--sp-3); }
.pillar__links li { margin-top: var(--sp-2); }
.pillar__links a {
  color: var(--band-text); text-decoration: none; font-size: 0.9375rem;
  border-bottom: 1px solid transparent;
}
.pillar__links a:hover { border-bottom-color: var(--edl-teal); }

/* ─── Steps ───────────────────────────────────────────────────────── */

.step {
  display: flex; align-items: flex-start; gap: var(--sp-5);
  padding: var(--sp-5) 0; border-bottom: 1px solid var(--band-divider);
}
.step:last-child { border-bottom: none; }
.step__circle {
  flex-shrink: 0; width: 44px; height: 44px; border-radius: 50%;
  background: var(--edl-blue); color: #FFFFFF;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-head); font-weight: 700; font-size: 1.1rem;
  box-shadow: 0 6px 16px rgba(41, 82, 184, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.45);
}
.step__circle--teal { background: var(--edl-teal); }
.step h4 { color: var(--band-heading); margin-bottom: var(--sp-1); }
.step p  { color: var(--band-muted); font-size: 0.9375rem; }

/* ─── Stats ───────────────────────────────────────────────────────── */

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--sp-4); }
.stat {
  background: var(--band-surface);
  backdrop-filter: blur(var(--band-blur-sm)) saturate(150%);
  -webkit-backdrop-filter: blur(var(--band-blur-sm)) saturate(150%);
  border: 1px solid var(--band-glass-border);
  border-left: 3px solid var(--edl-blue);
  border-radius: var(--radius);
  box-shadow: var(--band-glass-shadow), inset 0 1px 0 var(--band-glass-highlight);
  padding: var(--sp-5);
  /* Column + margin-top:auto on the label keeps every label on the same
     baseline across a row, so a value that needs two lines ("Self-Sufficient"
     breaking on its hyphen) doesn't shunt its own label out of line with its
     neighbours'. */
  display: flex;
  flex-direction: column;
}
.stat--teal { border-left-color: var(--edl-teal); }
/* 2rem, not 2.75rem: these hold short labels as well as figures ("8 Months",
   "Oracle + WhereScape"), and at 2.75rem the multi-word ones wrapped while
   their single-token neighbours did not, so those tiles read as broken. 2rem
   fits the longest value on one line in both the three-up and five-up rows. */
.stat__num {
  overflow-wrap: break-word;
  font-family: var(--font-head); font-size: 2rem; font-weight: 700;
  line-height: 1; color: var(--band-heading);
}
.stat__desc { font-size: 0.8125rem; color: var(--band-muted); margin-top: auto; padding-top: var(--sp-2); }

/* ─── Rail ────────────────────────────────────────────────────────── */
/* Native overflow + scroll-snap. Nothing auto-advances. */

.rail {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(290px, 340px);
  gap: var(--sp-4); overflow-x: auto; scroll-snap-type: x mandatory;
  /* The scroller clips whatever overflows it, which cut the cards' shadows
     into a hard grey line and the hover lift off the top. Pad room for both,
     and pull the margins back so the layout doesn't move. */
  padding: var(--sp-3) 0 var(--sp-8);
  margin: calc(-1 * var(--sp-3)) 0 calc(var(--sp-3) - var(--sp-8));
  /* No visible scrollbar: the arrow buttons and auto-rotation are the
     affordance now (see .rail-wrap and site.js). Scroll, swipe, trackpad and
     keyboard still work, and the rail keeps its tabindex/role, so hiding the
     bar costs no functionality. */
  scrollbar-width: none;
}
.rail > * { scroll-snap-align: start; }
.rail::-webkit-scrollbar { display: none; }

/* Arrow buttons are enhancement over the rail's native scroll/swipe/keyboard
   behaviour, see site.js. They sit as a pair above the rail's right end, level
   with the band head's last line, so they never cover card text. Hidden under
   900px, touch drag covers it there. */
.rail-wrap { position: relative; }
.rail__btn {
  position: absolute; top: calc(-1 * var(--sp-7) - 44px);
  width: 44px; height: 44px; border-radius: 50%; z-index: 2;
  display: flex; align-items: center; justify-content: center;
  background: transparent; color: var(--band-heading);
  border: 1px solid var(--band-divider); cursor: pointer;
  transition: background-color .2s, border-color .2s, color .2s, opacity .2s;
}
.rail__btn:hover:not(:disabled) { background: var(--edl-blue); border-color: var(--edl-blue); color: #FFFFFF; }
.rail__btn:disabled { opacity: 0.35; cursor: default; }
.rail__btn--prev { right: 56px; }
.rail__btn--next { right: 0; }
/* The card cut off at the right edge fades out instead of being chopped
   mid-word; the fade lifts once the rail reaches its end (site.js). */
.rail-wrap:not(.is-at-end) .rail {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 96px), transparent);
          mask-image: linear-gradient(to right, #000 calc(100% - 96px), transparent);
}
@media (max-width: 900px) { .rail__btn { display: none; } }

/* ─── Value grid ──────────────────────────────────────────────────── */

.value-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-7) var(--sp-6); }
.value-grid h3 { color: var(--band-heading); font-size: 1.0625rem; margin-bottom: var(--sp-2); }
.value-grid p  { color: var(--band-muted); font-size: 0.9375rem; }
.value-grid > div { border-top: 2px solid var(--edl-teal); padding-top: var(--sp-4); }

/* ─── Logo wall ───────────────────────────────────────────────────── */

.logo-wall {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: var(--sp-6); align-items: center;
}
.logo-wall img {
  height: 38px; width: auto; margin: 0 auto;
  filter: grayscale(1); opacity: 0.55;
  transition: filter 0.15s, opacity 0.15s;
}
.logo-wall a:hover img, .logo-wall a:focus-visible img { filter: none; opacity: 1; }

/* ─── CTA band ────────────────────────────────────────────────────── */

.cta-band h2 { margin-bottom: var(--sp-3); }
.cta-band p  { color: var(--band-muted); }


/* ─── Prose (article body) ────────────────────────────────────────── */

.prose { max-width: var(--shell-narrow); }
.prose > * + * { margin-top: var(--sp-5); }
.prose h2 { font-size: 1.75rem; margin-top: var(--sp-8); }
.prose h3 { font-size: 1.25rem; margin-top: var(--sp-6); }
.prose ul, .prose ol { padding-left: var(--sp-5); }
.prose ul li { list-style: disc; margin-top: var(--sp-2); }
.prose ol li { list-style: decimal; margin-top: var(--sp-2); }
.prose blockquote {
  border-left: 3px solid var(--edl-teal); padding-left: var(--sp-5);
  font-size: 1.125rem; color: var(--band-text);
}
.prose a { color: var(--edl-blue); font-weight: 600; }

/* ─── Tag chips ───────────────────────────────────────────────────── */

/* Case-study listing filters: the "Who We Help" / "How We Help" groups. */
.filters { display: flex; flex-wrap: wrap; gap: var(--sp-6); margin-bottom: var(--sp-6); }
.filters__group { flex: 1 1 320px; }
.filters__group .eyebrow { margin-bottom: var(--sp-3); }

.tags { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.tag {
  font-size: 0.75rem; font-weight: 600; letter-spacing: 0.04em;
  padding: 4px 12px; border-radius: var(--radius-btn);
  background: var(--band-surface);
  backdrop-filter: blur(var(--band-blur-sm));
  -webkit-backdrop-filter: blur(var(--band-blur-sm));
  border: 1px solid var(--band-glass-border); color: var(--band-muted);
  text-decoration: none;
}
.tag:hover { border-color: var(--edl-teal); color: var(--band-heading); }

/* ─── Forms ───────────────────────────────────────────────────────── */

.field { display: flex; flex-direction: column; gap: var(--sp-1); margin-bottom: var(--sp-4); }
.field label {
  font-size: 0.75rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--band-muted);
}
.field input, .field textarea, .field select {
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(var(--band-blur-sm));
  -webkit-backdrop-filter: blur(var(--band-blur-sm));
  border: 1px solid var(--band-glass-border); border-radius: var(--radius-sm);
  padding: 0.65rem 0.85rem; color: #2A2A2A;
  font-family: inherit; font-size: 0.9375rem; width: 100%;
}
.field input:focus, .field textarea:focus, .field select:focus {
  outline: 2px solid var(--edl-blue); outline-offset: 0; border-color: var(--edl-blue);
}

/* ─── Footer ──────────────────────────────────────────────────────── */

.site-footer {
  position: relative;
  background: var(--edl-glass-chrome-bg);
  backdrop-filter: blur(var(--edl-glass-blur)) saturate(150%);
  -webkit-backdrop-filter: blur(var(--edl-glass-blur)) saturate(150%);
  border-top: 1px solid var(--edl-glass-chrome-border);
  box-shadow: inset 0 1px 0 var(--edl-glass-chrome-highlight);
  color: #F8F7F4; padding: var(--sp-8) 0 var(--sp-6);
}
.site-footer__inner { max-width: var(--shell); margin: 0 auto; padding: 0 var(--gutter); }
.site-footer__cols { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-6); }
.site-footer h4 {
  font-size: 0.6875rem; letter-spacing: 0.15em; text-transform: uppercase;
  color: var(--edl-teal); margin-bottom: var(--sp-3);
}
.site-footer li { margin-top: var(--sp-2); }
.site-footer a { color: rgba(248, 247, 244, 0.72); text-decoration: none; font-size: 0.9375rem; }
.site-footer a:hover { color: #FFFFFF; }
.site-footer__pitch { font-size: 0.9375rem; color: rgba(248, 247, 244, 0.62); margin-bottom: var(--sp-4); }
.site-footer .btn--primary { color: #FFFFFF; }
.site-footer__badges { display: flex; gap: var(--sp-3); margin-bottom: var(--sp-3); }
.site-footer__badges img { width: 88px; height: auto; }
.site-footer__legal {
  margin-top: var(--sp-7); padding-top: var(--sp-5);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  display: flex; flex-wrap: wrap; gap: var(--sp-5);
  font-size: 0.8125rem; color: rgba(248, 247, 244, 0.62);
}
.site-footer__legal span { font-size: 0.9375rem; color: rgba(248, 247, 244, 0.72); }

/* ─── Responsive ──────────────────────────────────────────────────── */

@media (max-width: 900px) {
  :root { --band-pad: 64px; --header-h: 60px; }

  h1 { font-size: 2.4rem; }
  h2 { font-size: 1.85rem; }
  .hero:not(.hero--compact) h1 { font-size: 2.6rem; }
  .hero__words { font-size: 1.6rem; }

  .site-header__cta { display: none; }
  .site-nav__cta-mobile { display: block; margin-top: var(--sp-5); }

  .site-nav__toggle {
    display: block; margin-left: auto; width: 44px; height: 44px;
    background: transparent; border: none; cursor: pointer; position: relative;
  }
  .site-nav__bars,
  .site-nav__bars::before,
  .site-nav__bars::after {
    position: absolute; left: 11px; width: 22px; height: 2px;
    background: #F8F7F4; transition: transform 0.2s, opacity 0.2s;
  }
  .site-nav__bars { top: 21px; }
  .site-nav__bars::before { content: ""; top: -7px; left: 0; }
  .site-nav__bars::after  { content: ""; top: 7px; left: 0; }
  .site-header--menu-open .site-nav__bars { background: transparent; }
  .site-header--menu-open .site-nav__bars::before { transform: translateY(7px) rotate(45deg); }
  .site-header--menu-open .site-nav__bars::after  { transform: translateY(-7px) rotate(-45deg); }

  .site-nav { margin-left: 0; }
  .site-nav__list {
    position: fixed; inset: calc(var(--header-h) + var(--header-gap) * 2) 0 0 0;
    flex-direction: column; align-items: flex-start; gap: var(--sp-5);
    background: var(--edl-glass-chrome-bg);
    backdrop-filter: blur(var(--edl-glass-blur)) saturate(150%);
    -webkit-backdrop-filter: blur(var(--edl-glass-blur)) saturate(150%);
    padding: var(--sp-7) var(--gutter);
    transform: translateX(100%); transition: transform 0.2s ease-out;
    overflow-y: auto;
  }
  .site-header--menu-open .site-nav__list { transform: none; }
  .site-nav__list a { font-size: 1.25rem; }

  .grid--3, .grid--4, .value-grid { grid-template-columns: repeat(2, 1fr); }
  .site-footer__cols { grid-template-columns: repeat(2, 1fr); }
  .hero__rain { width: 70%; opacity: 0.12; }
}

@media (max-width: 640px) {
  :root { --band-pad: 56px; --gutter: 16px; }

  h1 { font-size: 2.1rem; }
  .hero:not(.hero--compact) h1 { font-size: 2.1rem; }
  h2 { font-size: 1.6rem; }
  body { font-size: 1rem; }
  .lead { font-size: 1.125rem; }
  .stat__num { font-size: 2.25rem; }

  .grid--2, .grid--3, .grid--4, .value-grid { grid-template-columns: 1fr; }
  /* Two-up leaves ~123px of content width, which an unbreakable word like
     "WhereScape" overruns by 60px at this size. Full width fits it. */
  .stats { grid-template-columns: 1fr; }
  .site-footer__cols { grid-template-columns: 1fr; }
  .btn { width: 100%; text-align: center; }
  .btn-row { flex-direction: column; }
  .site-nav__list a { width: 100%; }
}

/* ─── Page transitions ────────────────────────────────────────────── */

/* Cross-document view transitions: the browser snapshots the outgoing page,
   loads the next one and cross-fades between them. No JavaScript, no click
   interception, back/forward unaffected. Chrome, Edge and Safari honour it;
   Firefox ignores the at-rule and navigates normally, so this is purely an
   enhancement. Kept short deliberately: a page transition is charming once
   and tiresome by the twentieth click. */
@view-transition { navigation: auto; }

/* Cross-fading the whole page was imperceptible: every page shares the same
   header, footer and fixed ambient gradient, so those pixels are identical in
   both snapshots and nothing appears to move. Naming the content area lets it
   animate on its own while the chrome stays put, and a small rise gives the
   eye something to catch. 24px over an even curve: an aggressive ease-out was
   measurably 99% complete by 240ms of a nominal 350ms, so the motion was real
   but imperceptible. Keep the move modest, this fires on every internal link. */
main { view-transition-name: edl-content; }

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

::view-transition-old(edl-content) {
  animation: 260ms ease both edl-content-out;
}
::view-transition-new(edl-content) {
  animation: 900ms cubic-bezier(0.4, 0, 0.2, 1) both edl-content-in;
}

@keyframes edl-content-out {
  to { opacity: 0; transform: translateY(-10px); }
}
@keyframes edl-content-in {
  from { opacity: 0; transform: translateY(24px); }
}

/* Full-page transitions are a common motion-sensitivity trigger, so opt out
   entirely rather than just shortening them. */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}

/* ─── Motion ──────────────────────────────────────────────────────── */

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

/* ─── Print ───────────────────────────────────────────────────────── */

/* Case-study panels opt out via .panel-slide, they print as full-bleed A4
   landscape pages with their own colours (see build/generate-pdfs.mjs, which
   requires one PDF page per panel). Everything else prints as a plain document. */
@media print {
  /* backdrop-filter cannot render in Chromium's print-to-PDF pipeline (see
     build/generate-pdfs.mjs), every glass surface gets an opaque fallback
     here, and the fixed ambient gradient is removed outright since
     position: fixed is unreliable across paginated print output. */
  body::before { display: none !important; }
  .site-header, .site-footer, .skip-link, .hero__rain, .print-hide { display: none !important; }

  .band:not(.panel-slide) {
    padding: 24px 0; background: #FFFFFF !important; color: #000000 !important;
    backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
    box-shadow: none !important;
  }
  .band:not(.panel-slide) h1,
  .band:not(.panel-slide) h2,
  .band:not(.panel-slide) h3,
  .band:not(.panel-slide) h4 { color: #000000 !important; }
  .band:not(.panel-slide) .card {
    border: 1px solid #D9D9D9; border-top: 3px solid #3967D9; border-radius: 2px;
    background: #FFFFFF !important; backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
    box-shadow: none !important;
  }

  /* .panel-slide (case-study PDFs) prints with its own solid colours
     instead, same intent as the glass version, none of the mechanism. */
  .panel-slide {
    background: var(--band-bg-solid) !important;
    backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
    box-shadow: none !important;
  }
  .panel-slide .card, .panel-slide .stat { background: var(--band-surface-solid) !important; }
  .panel-slide .card--alt { background: var(--band-surface-alt-solid) !important; }
  .panel-slide .card,
  .panel-slide .stat,
  .panel-slide .btn,
  .panel-slide .tag,
  .panel-slide .field input,
  .panel-slide .field textarea,
  .panel-slide .field select {
    backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
    box-shadow: none !important;
  }
}

/* Required by the shared contact-panel.js booking component (footer partial). */
@keyframes cpSlideIn { from { transform: translateX(100%); } to { transform: translateX(0); } }
@keyframes cpSlideOut { from { transform: translateX(0); } to { transform: translateX(100%); } }
@keyframes cpFadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes cpFadeOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes cpFieldAppear { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
@keyframes cpCheckDraw { from { stroke-dashoffset: 24; } to { stroke-dashoffset: 0; } }
@keyframes cpScaleIn { from { transform: scale(0.85); opacity: 0; } to { transform: scale(1); opacity: 1; } }

/* Static placeholder for #contact-booking-root, visible immediately on the
   contact page while React/Babel/contact-panel.js (~3MB from CDN) load in the
   background. ReactDOM.createRoot(...).render() replaces this element's
   children wholesale once the real panel mounts, so no cleanup JS is needed. */
.cp-skeleton {
  position: relative;
  background-color: #2a2a2a;
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 20px 60px rgba(0,0,0,0.18);
  padding: 28px 32px 32px;
}
.cp-skeleton::before {
  content: '';
  position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, #3967D9, #5B85E6, #3967D9);
}
.cp-skeleton__eyebrow { font-size: 12px; font-weight: 500; color: #3967D9; text-transform: uppercase; letter-spacing: 3px; margin-bottom: 4px; }
.cp-skeleton__sub { font-size: 15px; color: rgba(255,255,255,0.5); margin-bottom: 28px; }
.cp-skeleton__nav { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.cp-skeleton__navdot { width: 32px; height: 32px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.12); }
.cp-skeleton__month { width: 120px; height: 14px; border-radius: 4px; background: rgba(255,255,255,0.12); }
.cp-skeleton__calendar,
.cp-skeleton__month {
  background: linear-gradient(90deg, rgba(255,255,255,0.06) 25%, rgba(255,255,255,0.14) 37%, rgba(255,255,255,0.06) 63%);
  background-size: 400% 100%;
  animation: cpSkeletonPulse 1.6s ease infinite;
}
.cp-skeleton__calendar { height: 230px; border-radius: 8px; }
@keyframes cpSkeletonPulse { 0% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } }
@media (prefers-reduced-motion: reduce) {
  .cp-skeleton__calendar, .cp-skeleton__month { animation: none; }
}
