/* ==========================================================================
   Flourisher design tokens
   Never hard-code a colour, space, or radius below this block — use a token.
   Every colour token is redefined for dark; nothing else changes by theme.
   ========================================================================== */

:root {
  /* Colour — light */
  --bg: #fbfaf7;
  --bg-sunken: #f4f2ec;
  --surface: #ffffff;
  --surface-raised: #ffffff;
  --text: #1d2321;
  --muted: #5f6b66;
  --faint: #78837d;
  --line: #e3e1da;
  --line-strong: #cfccc2;
  --accent: #2f7d5d;
  --accent-hover: #276a4f;
  --accent-soft: #eaf3ee;
  --accent-contrast: #ffffff;
  --warn: #8a5d13;
  --warn-soft: #fdf4e3;
  --danger: #b0432f;
  --hc-bark: #6b4a2c;   /* trunk, branches and roots */

  /* Space — 4px base */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 64px;
  --s-9: 96px;

  /* Type */
  --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Inter, Roboto, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --t-xs: .8125rem;
  --t-sm: .9375rem;
  --t-base: 1rem;
  --t-lg: clamp(1.05rem, 1.6vw, 1.19rem);
  --t-xl: clamp(1.25rem, 2vw, 1.5rem);
  --t-2xl: clamp(1.6rem, 3vw, 2.1rem);
  --t-3xl: clamp(2.1rem, 4.4vw, 3rem);
  --t-4xl: clamp(2.4rem, 5.4vw, 3.7rem);

  /* Shape */
  --radius-sm: 8px;
  --radius: 14px;
  --radius-lg: 20px;
  --radius-pill: 999px;
  --border: 1px solid var(--line);

  /* Layout */
  --maxw: 1120px;
  --maxw-narrow: 760px;
  --maxw-prose: 62ch;
  --gutter: var(--s-5);
  --section-y: clamp(56px, 9vw, 104px);

  /* Motion */
  --ease: cubic-bezier(.2, .6, .3, 1);
  --dur: .18s;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #101413;
    --bg-sunken: #0b0e0d;
    --surface: #171c1a;
    --surface-raised: #1c2220;
    --text: #e8ece9;
    --muted: #a3b0aa;
    --faint: #899892;
    --line: #262d2a;
    --line-strong: #38423e;
    --accent: #6fc79b;
    --accent-hover: #86d4ad;
    --accent-soft: #17251e;
    --accent-contrast: #0d1211;
    --warn: #e0b263;
    --warn-soft: #241d10;
    --danger: #f0846f;
    --hc-bark: #c49a68;   /* trunk, branches and roots */
  }
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

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

/* ==========================================================================
   Base
   ========================================================================== */

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: var(--t-base)/1.65 var(--font);
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
a:hover { color: var(--accent); }

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

h1, h2, h3, h4 { line-height: 1.16; letter-spacing: -.02em; margin: 0 0 .5em; font-weight: 650; }
p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

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

.sr-only {
  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;
  left: var(--s-4);
  top: -100px;
  z-index: 100;
  background: var(--accent);
  color: var(--accent-contrast);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--radius-sm);
  font-weight: 600;
  transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: var(--s-4); color: var(--accent-contrast); }

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
  display: inline-block;
  background: var(--accent);
  color: var(--accent-contrast);
  border: 1px solid var(--accent);
  border-radius: var(--radius-pill);
  padding: 11px 20px;
  font: inherit;
  font-weight: 600;
  font-size: var(--t-sm);
  line-height: 1.3;
  text-align: center;
  cursor: pointer;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.btn:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--accent-contrast); }
.btn:active { transform: translateY(1px); }

.btn-sm { padding: 8px 15px; font-size: var(--t-xs); }

.btn-ghost { background: transparent; color: var(--text); border-color: var(--line-strong); }
.btn-ghost:hover { background: transparent; border-color: var(--accent); color: var(--accent); }

.btn-invert { background: var(--bg); color: var(--text); border-color: var(--bg); }
.btn-invert:hover { background: var(--surface); border-color: var(--surface); color: var(--text); }

/* ==========================================================================
   Header
   ========================================================================== */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--bg);
  border-bottom: var(--border);
}

.header-inner {
  position: relative;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: var(--s-3) var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
}

.brand { display: inline-flex; align-items: center; gap: var(--s-2); font-weight: 650; }
.brand:hover { color: var(--text); }
.brand-mark { width: 26px; height: 26px; flex: none; }
.brand-name { letter-spacing: -.02em; }
.tld { color: var(--muted); font-weight: 400; }

.site-nav {
  display: flex;
  align-items: center;
  gap: var(--s-5);
  font-size: var(--t-sm);
  color: var(--muted);
}
.site-nav a[aria-current="page"] { color: var(--text); font-weight: 600; }

.nav-toggle {
  display: none;
  width: 40px; height: 40px;
  align-items: center; justify-content: center;
  background: transparent;
  border: var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--text);
}
.nav-toggle-bars, .nav-toggle-bars::before, .nav-toggle-bars::after {
  display: block; width: 16px; height: 1.5px;
  background: currentColor; border-radius: 2px;
  transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.nav-toggle-bars { position: relative; }
.nav-toggle-bars::before, .nav-toggle-bars::after { content: ""; position: absolute; left: 0; }
.nav-toggle-bars::before { top: -5px; }
.nav-toggle-bars::after { top: 5px; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::before { transform: translateY(5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::after { transform: translateY(-5px) rotate(-45deg); }

@media (max-width: 880px) {
  .nav-toggle { display: inline-flex; }
  .site-nav {
    position: absolute;
    left: 0; right: 0; top: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: var(--s-2) var(--gutter) var(--s-5);
    background: var(--bg);
    border-bottom: var(--border);
    font-size: var(--t-base);
  }
  .site-nav a { padding: var(--s-3) 0; border-bottom: var(--border); }
  .site-nav .btn { margin-top: var(--s-4); border-bottom: 0; }
  /* Hidden only once JS has taken control — the menu is open by default without JS */
  .site-nav[hidden] { display: none; }
}

/* ==========================================================================
   Layout primitives
   ========================================================================== */

.section {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: var(--section-y) var(--gutter);
}

.section-sunken {
  max-width: none;
  background: var(--bg-sunken);
  border-block: var(--border);
}
.section-sunken > * { max-width: var(--maxw); margin-inline: auto; }

.section-head { max-width: var(--maxw-narrow); margin-bottom: var(--s-7); }
.section-title { font-size: var(--t-2xl); margin-bottom: var(--s-3); }
.section-sub { color: var(--muted); font-size: var(--t-lg); max-width: var(--maxw-prose); }
.section-foot {
  margin-top: var(--s-6);
  color: var(--muted);
  font-size: var(--t-sm);
  max-width: var(--maxw-prose);
}

.eyebrow {
  display: inline-block;
  margin: 0 0 var(--s-3);
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  background: var(--accent-soft);
  color: var(--accent);
  font-size: var(--t-xs);
  font-weight: 600;
}

.grid { display: grid; gap: var(--s-4); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(268px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(224px, 1fr)); }

/* ==========================================================================
   Cards
   ========================================================================== */

.card {
  background: var(--surface);
  border: var(--border);
  border-radius: var(--radius);
  padding: var(--s-5);
  display: flex;
  flex-direction: column;
}
.card h3 { font-size: var(--t-base); margin-bottom: var(--s-2); }
.card p { color: var(--muted); font-size: var(--t-sm); }
.card-quiet { background: transparent; border-style: dashed; }

/* Icons are masked, not <img>-loaded: currentColor and CSS custom properties do
   not cross the <img> boundary, so an <img> would render the wrong accent in one
   of the two themes. The mask preserves each asset's internal opacity. */
.icon {
  display: block;
  width: 24px; height: 24px;
  background-color: var(--accent);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain; mask-size: contain;
}
.card .icon { margin-bottom: var(--s-4); }

.icon-ai         { -webkit-mask-image: url(../img/icon-ai.svg);         mask-image: url(../img/icon-ai.svg); }
.icon-advisory   { -webkit-mask-image: url(../img/icon-advisory.svg);   mask-image: url(../img/icon-advisory.svg); }
.icon-platform   { -webkit-mask-image: url(../img/icon-platform.svg);   mask-image: url(../img/icon-platform.svg); }
.icon-mobile     { -webkit-mask-image: url(../img/icon-mobile.svg);     mask-image: url(../img/icon-mobile.svg); }
.icon-data       { -webkit-mask-image: url(../img/icon-data.svg);       mask-image: url(../img/icon-data.svg); }
.icon-enablement { -webkit-mask-image: url(../img/icon-enablement.svg); mask-image: url(../img/icon-enablement.svg); }

.card-link {
  margin-top: auto;
  padding-top: var(--s-4);
  color: var(--accent);
  font-size: var(--t-sm);
  font-weight: 600;
}
.card-link::after { content: " \2192"; }

.tag-list {
  list-style: none;
  margin: 0 0 var(--s-2);
  padding: 0;
  color: var(--muted);
  font-size: var(--t-sm);
}
.tag-list li { padding: 2px 0 2px var(--s-4); position: relative; }
.tag-list li::before {
  content: "";
  position: absolute; left: 0; top: .8em;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--accent);
  opacity: .6;
}

/* ==========================================================================
   Hero
   ========================================================================== */

.hero {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: clamp(40px, 7vw, 84px) var(--gutter) clamp(32px, 5vw, 56px);
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  align-items: center;
  gap: var(--s-7);
}
.hero h1 { font-size: var(--t-4xl); font-weight: 700; margin-bottom: var(--s-4); }
.hero .lede {
  color: var(--muted);
  font-size: var(--t-lg);
  max-width: 34rem;
  margin-bottom: var(--s-6);
}
/* Inlined rather than <img>-loaded so its two-tone palette follows the theme. */
.hero-art { justify-self: end; color: var(--text); width: 100%; }
.hero-svg, .hero-fallback { display: block; width: 100%; height: auto; }

/* The hero art is clickable, and on touch that produced a blue tap-highlight
   rectangle around the whole SVG box. Suppressing it is safe HERE and only
   here: the element is decorative and aria-hidden, so it is never keyboard
   focusable and no real focus indicator is being removed. `touch-action`
   drops the double-tap-zoom wait so the shake fires immediately. */
#hero-constellation {
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  outline: none;
  -webkit-user-select: none;
  user-select: none;
}
#hero-constellation:focus,
#hero-constellation:focus-visible { outline: none; }

.cta-row { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.cta-note { margin-top: var(--s-4); color: var(--faint); font-size: var(--t-xs); }

/* The constellation is the brand mark of the page — it must survive to phone
   size rather than be hidden. Below the two-column breakpoint it moves under
   the copy, height-capped so it never shrinks into a smudge. */
@media (max-width: 820px) {
  .hero { grid-template-columns: 1fr; gap: var(--s-5); padding-bottom: var(--s-6); }
  .hero-copy { order: 1; }
  .hero-art  { order: 2; justify-self: center; width: 100%; max-width: 460px; }
  .hero-svg, .hero-fallback { max-height: 52vh; }
}

@media (max-width: 520px) {
  .hero-art { max-width: 100%; }
  .hero-svg, .hero-fallback { max-height: 44vh; }
}

/* ==========================================================================
   Site-wide starfield

   Pure CSS. The stars are two static, tileable vector masks — no DOM nodes and
   no JavaScript. An earlier version built 269 SVG circles and animated twelve
   groups; this does the same job with zero elements and two animations.

   Masks rather than background images, so the stars take their colour from a
   token and theme correctly.

   Shooting stars are HTML elements using translate3d, which reliably gets its
   own compositor layer. As SVG <g> transforms they ran on the main thread and
   visibly stuck whenever anything else was busy.
   ========================================================================== */

:root { --star: rgba(29, 35, 33, .30); }
@media (prefers-color-scheme: dark) {
  :root { --star: rgba(232, 236, 233, .95); }
}

.site-stars {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}

/* Two offset star fields cross-fading = twinkle, for two animations total. */
.site-stars::before,
.site-stars::after {
  content: "";
  position: absolute;
  inset: -10%;
  background-color: var(--star);
  -webkit-mask-repeat: repeat;
          mask-repeat: repeat;
}
.site-stars::before {
  -webkit-mask-image: url(../img/starfield-a.svg);
          mask-image: url(../img/starfield-a.svg);
  -webkit-mask-size: 420px 420px;
          mask-size: 420px 420px;
  opacity: .85;
}
.site-stars::after {
  -webkit-mask-image: url(../img/starfield-b.svg);
          mask-image: url(../img/starfield-b.svg);
  -webkit-mask-size: 305px 305px;
          mask-size: 305px 305px;
  opacity: .7;
}

/* A single star flaring. The cycle is long and the visible window is short,
   so each star is dark most of the time; with staggered delays across a dozen
   of them, one or two are flickering at any moment and never the same pair. */
@keyframes star-flicker {
  0%,  100% { opacity: 0;   transform: scale(.55); }
  2%        { opacity: 1;   transform: scale(1.25); }
  3.5%      { opacity: .35; transform: scale(.9); }
  5%        { opacity: .95; transform: scale(1.1); }
  7%        { opacity: .25; }
  9%        { opacity: 0;   transform: scale(.55); }
}

.twinkle-star {
  position: absolute;
  top: var(--y);
  left: var(--x);
  width: var(--size, 3px);
  height: var(--size, 3px);
  border-radius: 50%;
  background: var(--star);
  box-shadow: 0 0 6px 1px var(--star);
  opacity: 0;
  will-change: opacity, transform;
  animation: star-flicker var(--dur) ease-in-out var(--delay) infinite;
}

/* Shooting stars — GPU-composited, so they cannot stall mid-flight. */
.shooting-star {
  position: absolute;
  top: var(--y);
  left: var(--x);
  width: var(--len, 60px);
  height: 1.4px;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--star));
  opacity: 0;
  transform: rotate(var(--ang)) translate3d(0, 0, 0);
  will-change: transform, opacity;
  animation: star-shoot var(--dur) linear var(--delay) infinite;
}

@keyframes star-shoot {
  0%   { opacity: 0; transform: rotate(var(--ang)) translate3d(0, 0, 0); }
  1.5% { opacity: .95; }
  8%   { opacity: 0; transform: rotate(var(--ang)) translate3d(var(--dist), 0, 0); }
  100% { opacity: 0; transform: rotate(var(--ang)) translate3d(var(--dist), 0, 0); }
}

/* Content sits above the stars. The header is deliberately NOT in this list:
   it needs its own sticky positioning and a much higher stacking order, and
   folding it in here silently demoted it to z-index 1 — the same as <main>,
   which paints later and so covered the open mobile menu. */
main, .site-footer { position: relative; z-index: 1; }

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;      /* above <main>, so the mobile menu opens over the page */
}

@media (prefers-reduced-motion: reduce) {
  .site-stars::before, .site-stars::after { opacity: .7; }
  .twinkle-star { display: none; }
  .shooting-star { display: none; }
}

/* ---- Hero constellation (built by hero-constellation.js) ---------------- */

@keyframes hc-tw    { 0%, 100% { opacity: .62; } 50% { opacity: 1; } }
/* Breeze. Amplitude is per-group via --hc-amp; groups are nested (trunk ->
   limb -> foliage) so their rotations compose the way a real tree flexes. */
@keyframes hc-breeze {
  0%, 100% { transform: rotate(calc(var(--hc-amp, 1deg) * -0.55)); }
  28%      { transform: rotate(var(--hc-amp, 1deg)); }              /* gust */
  46%      { transform: rotate(calc(var(--hc-amp, 1deg) * 0.25)); } /* settle */
  68%      { transform: rotate(calc(var(--hc-amp, 1deg) * 0.62)); } /* second push */
}
@keyframes hc-pulse { 0%, 100% { opacity: .5; } 50% { opacity: 1; } }
@keyframes hc-rise {
  0%   { transform: translateY(0);      opacity: 0; }
  15%  { opacity: .5; }
  85%  { opacity: .18; }
  100% { transform: translateY(-120px); opacity: 0; }
}
@keyframes hc-sap {
  0%   { stroke-dashoffset: 140; opacity: 0; }
  12%  { opacity: .85; }
  88%  { opacity: .5; }
  100% { stroke-dashoffset: -80; opacity: 0; }
}

/* Stop paying for the animation once the hero scrolls out of view. */
.hc-paused g, .hc-paused path { animation-play-state: paused !important; }

}

/* ==========================================================================
   Motion
   Every animation in this block is opt-out: the prefers-reduced-motion rule
   near the top of this file zeroes all durations, and the reveal utility
   below falls back to fully visible so nothing depends on motion to be read.
   ========================================================================== */

/* Hero starfield ------------------------------------------------------- */

.starfield .star {
  opacity: .18;
  animation: twinkle var(--t, 4s) var(--d, 0s) infinite ease-in-out;
}
.motes .mote {
  opacity: .3;
  animation: drift var(--t, 10s) var(--d, 0s) infinite ease-in-out;
}

@keyframes twinkle {
  0%, 100% { opacity: .10; }
  50%      { opacity: .55; }
}

@keyframes drift {
  0%, 100% { transform: translate(0, 0);      opacity: .28; }
  33%      { transform: translate(7px, -9px); opacity: .62; }
  66%      { transform: translate(-6px, 5px); opacity: .38; }
}

/* The lattice breathes very slowly, so the hero is never quite static. */
.hero-fallback > g > g:nth-of-type(1) { animation: breathe 19s infinite ease-in-out; }
@keyframes breathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: .62; }
}

/* Scroll reveal -------------------------------------------------------- */

/* Applied by main.js only when motion is allowed. Without JS, or with
   reduced motion, elements are never given .reveal and stay visible. */
.reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .55s var(--ease), transform .55s var(--ease);
}
.reveal.is-in { opacity: 1; transform: none; }

/* Hover lift ----------------------------------------------------------- */

.card {
  transition: transform var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.practice:hover, .case:hover {
  transform: translateY(-3px);
  border-color: var(--line-strong);
}

@media (prefers-reduced-motion: reduce) {
  .starfield .star, .motes .mote, .hero-fallback > g > g:nth-of-type(1) { animation: none; }
  .starfield .star { opacity: .22; }
  .motes .mote { opacity: .45; }
  .reveal { opacity: 1; transform: none; }
  .practice:hover, .case:hover { transform: none; }
}

/* ==========================================================================
   Field band
   ========================================================================== */

.field-band {
  color: var(--text);
  background: var(--bg);
  line-height: 0;
  margin-bottom: -1px; /* seal the seam against the CTA band */
}
.field-svg { display: block; width: 100%; height: clamp(96px, 10vw, 150px); opacity: .95; }

/* ==========================================================================
   Logo wall + proof strip
   ========================================================================== */

.logo-wall {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: var(--s-6) var(--gutter);
  text-align: center;
}
.logo-wall-title {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: var(--s-5);
}
.logo-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap;
  justify-content: center;
  gap: var(--s-4) var(--s-7);
  color: var(--muted);
  font-size: var(--t-base);
  font-weight: 600;
  letter-spacing: -.01em;
}

.proof-strip {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: var(--s-6) var(--gutter) var(--s-7);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--s-5);
  border-bottom: var(--border);
}
.stat { display: flex; flex-direction: column; gap: 2px; }
.stat-n { font-size: var(--t-xl); font-weight: 700; letter-spacing: -.02em; }
.stat-l { color: var(--muted); font-size: var(--t-sm); }

/* ==========================================================================
   Case studies
   ========================================================================== */

.case-client {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--s-2);
}
.case h3 { font-size: var(--t-base); margin-bottom: var(--s-4); }
.case-dl { margin: 0; font-size: var(--t-sm); }
.case-dl dt {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--faint);
  margin-top: var(--s-3);
}
.case-dl dt:first-child { margin-top: 0; }
.case-dl dd { margin: 2px 0 0; color: var(--muted); }

/* ==========================================================================
   Steps
   ========================================================================== */

.steps {
  list-style: none;
  counter-reset: step;
  margin: 0 auto;          /* auto, or it loses the centring from .section-sunken > * */
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(224px, 1fr));
  gap: var(--s-5);
}
.steps li { counter-increment: step; position: relative; padding-top: var(--s-6); }
.steps li::before {
  content: counter(step, decimal-leading-zero);
  position: absolute; top: 0; left: 0;
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.steps li::after {
  content: "";
  position: absolute; top: 10px; left: 30px; right: 0;
  height: 1px; background: var(--line);
}
.steps h3 { font-size: var(--t-base); margin-bottom: var(--s-2); }
.steps p { color: var(--muted); font-size: var(--t-sm); }

/* ==========================================================================
   Quotes + rating
   ========================================================================== */

.quote { margin: 0; }
.quote blockquote { margin: 0 0 var(--s-5); }
.quote blockquote p {
  color: var(--text);
  font-size: var(--t-sm);
  line-height: 1.6;
}
.quote blockquote p::before { content: "\201C"; }
.quote blockquote p::after { content: "\201D"; }
.quote-by {
  margin-top: auto;
  display: flex; align-items: center; gap: var(--s-3);
  font-size: var(--t-xs);
  color: var(--muted);
  line-height: 1.45;
}
.quote-by strong { color: var(--text); font-size: var(--t-sm); }
.avatar {
  width: 40px; height: 40px; flex: none;
  background-color: var(--accent);
  -webkit-mask: url(../img/avatar-placeholder.svg) center/contain no-repeat;
  mask: url(../img/avatar-placeholder.svg) center/contain no-repeat;
}

.rating {
  margin-top: var(--s-6);
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: center;
  gap: var(--s-2) var(--s-3);
  font-size: var(--t-sm);
  color: var(--muted);
}
.rating-stars { color: var(--accent); letter-spacing: .1em; font-size: var(--t-base); }
.rating-score strong { font-size: var(--t-xl); color: var(--text); }

/* ==========================================================================
   Placeholder markers — see ADR 0002
   ========================================================================== */

.placeholder-flag {
  margin-top: var(--s-5);
  font-size: var(--t-xs);
  color: var(--warn);
  text-align: center;
}
.placeholder-flag::before { content: "\26A0  "; }

.pending {
  color: var(--faint);
  font-size: var(--t-sm);
  font-style: italic;
  margin-bottom: var(--s-2);
}

.site-notice {
  max-width: var(--maxw);
  margin: 0 auto var(--s-6);
  padding: var(--s-4) var(--s-5);
  background: var(--warn-soft);
  border: 1px solid var(--warn);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: var(--t-xs);
  line-height: 1.65;
}
.site-notice code {
  font-family: var(--font-mono);
  font-size: .95em;
}

/* ==========================================================================
   Forms
   ========================================================================== */

.inline-form {
  display: flex;
  gap: var(--s-2);
  flex-wrap: wrap;
  margin-top: var(--s-5);
}
.inline-form input {
  flex: 1 1 250px;
  min-width: 0;
  padding: 11px 16px;
  border: var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: var(--t-sm);
}
.inline-form input::placeholder { color: var(--faint); }
.inline-form input:focus-visible { border-color: var(--accent); outline-offset: 1px; }

.field { display: flex; flex-direction: column; gap: var(--s-2); margin-bottom: var(--s-4); }
.field label { font-size: var(--t-sm); font-weight: 600; }
.field input, .field textarea, .field select {
  padding: 11px 14px;
  border: var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: var(--t-sm);
  width: 100%;
}
.field textarea { min-height: 130px; resize: vertical; }
.field-hint { font-size: var(--t-xs); color: var(--faint); }

.form-note {
  min-height: 1.4em;
  margin: var(--s-3) 0 0;
  font-size: var(--t-sm);
  color: var(--accent);
}
.form-note.error { color: var(--danger); }

.form-micro { margin-top: var(--s-3); font-size: var(--t-xs); color: var(--faint); }

/* ==========================================================================
   Signal + CTA band
   ========================================================================== */

.signal-inner {
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  border-radius: var(--radius-lg);
  padding: clamp(28px, 5vw, 56px);
  max-width: var(--maxw-narrow);
  margin: 0 auto;
  text-align: center;
}
.signal-inner .eyebrow { background: transparent; padding-inline: 0; }
.signal-inner .section-sub { margin-inline: auto; }
.signal-inner .inline-form { justify-content: center; max-width: 460px; margin-inline: auto; }

/* Light: a dark slab, for emphasis at the close.
   Dark: inverting would put a pale slab at the foot of a dark page, which
   reads as a mistake — so it becomes a raised surface instead. */
.cta-band {
  background: var(--text);
  color: var(--bg);
  text-align: center;
  padding: var(--section-y) var(--gutter);
}
.cta-band h2 { font-size: var(--t-2xl); margin-bottom: var(--s-3); }
.cta-band p { opacity: .78; max-width: 44ch; margin: 0 auto var(--s-6); }

@media (prefers-color-scheme: dark) {
  .cta-band {
    background: var(--surface-raised);
    color: var(--text);
    border-top: 1px solid var(--line-strong);
  }
  .cta-band .btn-invert {
    background: var(--accent);
    color: var(--accent-contrast);
    border-color: var(--accent);
  }
  .cta-band .btn-invert:hover {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
    color: var(--accent-contrast);
  }
}

/* ==========================================================================
   Footer
   ========================================================================== */

.site-footer {
  border-top: var(--border);
  padding: var(--s-8) var(--gutter) var(--s-6);
}
.footer-grid {
  max-width: var(--maxw);
  margin: 0 auto var(--s-8);
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) repeat(3, minmax(0, 1fr));
  gap: var(--s-6) var(--s-5);
}
.footer-blurb {
  margin-top: var(--s-3);
  color: var(--muted);
  font-size: var(--t-sm);
  max-width: 30ch;
}
.footer-col { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-2); }
.footer-col h2 {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: var(--s-1);
}
.footer-col a { color: var(--muted); font-size: var(--t-sm); }
.footer-col a:hover { color: var(--accent); }
.footer-col .btn { margin-top: var(--s-2); }

.footer-base {
  max-width: var(--maxw);
  margin: 0 auto;
  padding-top: var(--s-5);
  border-top: var(--border);
  display: flex; flex-wrap: wrap; gap: var(--s-3);
  justify-content: space-between; align-items: center;
  color: var(--faint);
  font-size: var(--t-xs);
}
.footer-base nav { display: flex; gap: var(--s-4); }

@media (max-width: 760px) {
  .footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .footer-brand { grid-column: 1 / -1; }
}
@media (max-width: 420px) {
  .footer-grid { grid-template-columns: 1fr; }
}

/* ==========================================================================
   Interior pages — page head, prose, service list, tables
   ========================================================================== */

.page-head {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: clamp(40px, 6vw, 72px) var(--gutter) var(--s-5);
}
.page-head h1 { font-size: var(--t-3xl); margin-bottom: var(--s-4); }
.page-head .lede { color: var(--muted); font-size: var(--t-lg); max-width: var(--maxw-prose); }

.prose { max-width: var(--maxw-prose); }
.prose h3 { font-size: var(--t-base); margin-top: var(--s-6); }
.prose p, .prose li { color: var(--muted); font-size: var(--t-sm); }
.prose strong { color: var(--text); }

.service-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-4); }
.service {
  border: var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: var(--s-5);
}
.service h3 { font-size: var(--t-base); margin-bottom: var(--s-2); }
.service > p { color: var(--muted); font-size: var(--t-sm); }
.service-deliverables {
  margin: var(--s-3) 0 0;
  padding-left: var(--s-4);
  color: var(--faint);
  font-size: var(--t-xs);
}
.service-deliverables li { margin-bottom: 2px; }

.table-wrap { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; font-size: var(--t-sm); min-width: 520px; }
th, td { text-align: left; padding: var(--s-3) var(--s-4); border-bottom: var(--border); vertical-align: top; }
th {
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .06em;
  color: var(--faint); font-weight: 600;
}
td { color: var(--muted); }
td:first-child { color: var(--text); font-weight: 600; }

/* ==========================================================================
   Liquid glass surfaces

   What makes this read as glass rather than as tinted paint over a blur:
     1. saturate() alongside blur — colour behind the pane blooms instead of
        going grey. This is the single biggest tell.
     2. A specular rim: a bright hairline on the TOP inner edge and a dark one
        on the bottom, so the pane has thickness and catches light.
     3. A soft lift shadow, so it floats above the background rather than
        being cut into it.
     4. A restrained tint. The material is mostly clear; the colour is a hint.

   This layer sits last on purpose: it re-skins the solid surfaces defined
   above rather than duplicating them, so the base design remains intact and
   the glass can be removed in one block if it ever needs to go.
   ========================================================================== */

:root {
  --glass:        rgba(255, 255, 255, .58);
  --glass-tint:   rgba(47, 125, 93, .10);
  --glass-line:   rgba(255, 255, 255, .70);
  --glass-rim:    rgba(255, 255, 255, .90);
  --glass-under:  rgba(29, 35, 33, .07);
  --glass-shadow: 0 8px 32px rgba(29, 35, 33, .10), 0 2px 8px rgba(29, 35, 33, .06);
  --glass-fx:     saturate(185%) blur(26px);
}

@media (prefers-color-scheme: dark) {
  :root {
    --glass:        rgba(30, 42, 37, .52);
    --glass-tint:   rgba(111, 199, 155, .10);
    --glass-line:   rgba(111, 199, 155, .16);
    --glass-rim:    rgba(190, 235, 213, .28);
    --glass-under:  rgba(0, 0, 0, .28);
    --glass-shadow: 0 8px 32px rgba(0, 0, 0, .40), 0 2px 8px rgba(0, 0, 0, .28);
    --glass-fx:     saturate(180%) blur(26px);
  }
}

/* The page stops being opaque so the starfield shows through everywhere. */
body { background: transparent; }
html { background: var(--bg); }

.card,
.service,
.signal-inner,
.site-notice,
.section-sunken,
.cta-band,
.site-header,
.site-footer,
.btn-ghost,
.field input, .field textarea, .field select,
.inline-form input {
  background-color: var(--glass);
  background-image: linear-gradient(var(--glass-tint), var(--glass-tint));
  backdrop-filter: var(--glass-fx);
  -webkit-backdrop-filter: var(--glass-fx);
  border: 1px solid var(--glass-line);
  box-shadow:
    inset 0 1px 0 var(--glass-rim),
    inset 0 -1px 0 var(--glass-under),
    var(--glass-shadow);
}

.card, .service, .signal-inner, .site-notice { border-radius: var(--radius-lg); }
.card-quiet { background-image: none; background-color: transparent; box-shadow: none; }

/* Full-bleed bands: rims on the horizontal edges only. */
.section-sunken, .cta-band, .site-header, .site-footer {
  border-inline: 0;
  border-radius: 0;
}
.site-header { border-top: 0; }
.site-footer { border-bottom: 0; }

.cta-band { color: var(--text); }
.cta-band p { opacity: .78; }
.cta-band .btn-invert {
  background: var(--accent); color: var(--accent-contrast); border-color: var(--accent);
  box-shadow: var(--glass-shadow);
}

/* Inputs read as recessed glass, not raised. */
.field input, .field textarea, .field select, .inline-form input {
  box-shadow: inset 0 1px 3px var(--glass-under), inset 0 -1px 0 var(--glass-rim);
}

/* The primary action stays solid — the one element that must never lose contrast. */
.btn { box-shadow: var(--glass-shadow); }

/* Without backdrop-filter, translucency over a moving starfield is unreadable.
   Fall back to the solid surfaces rather than shipping illegible text. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header, .site-footer { background-color: var(--bg); background-image: none; }
  .card, .service, .signal-inner, .section-sunken, .cta-band,
  .field input, .field textarea, .inline-form input {
    background-color: var(--surface); background-image: none; box-shadow: none;
  }
}

/* ---- Hero constellation: click interaction -----------------------------
   Four shake variants so repeated clicks never repeat exactly. The jerk sits
   on its own group wrapped around the breeze groups — a single element cannot
   animate `transform` twice, and nesting lets the two compose. */

@keyframes hc-jerk-a {
  0%   { transform: rotate(0); }
  10%  { transform: rotate(2.4deg); }
  28%  { transform: rotate(-1.5deg); }
  46%  { transform: rotate(.9deg); }
  66%  { transform: rotate(-.45deg); }
  84%  { transform: rotate(.18deg); }
  100% { transform: rotate(0); }
}
@keyframes hc-jerk-b {
  0%   { transform: rotate(0); }
  9%   { transform: rotate(-2.9deg); }
  26%  { transform: rotate(1.7deg); }
  44%  { transform: rotate(-1deg); }
  64%  { transform: rotate(.5deg); }
  100% { transform: rotate(0); }
}
/* A double knock. */
@keyframes hc-jerk-c {
  0%   { transform: rotate(0); }
  7%   { transform: rotate(1.8deg); }
  18%  { transform: rotate(-.6deg); }
  30%  { transform: rotate(2.6deg); }
  48%  { transform: rotate(-1.3deg); }
  70%  { transform: rotate(.6deg); }
  100% { transform: rotate(0); }
}
/* A deeper, slower lean. */
@keyframes hc-jerk-d {
  0%   { transform: rotate(0); }
  16%  { transform: rotate(-3.4deg); }
  40%  { transform: rotate(2deg); }
  62%  { transform: rotate(-1.1deg); }
  82%  { transform: rotate(.4deg); }
  100% { transform: rotate(0); }
}

.hc-jerk-a { animation: hc-jerk-a 1s cubic-bezier(.2,.7,.3,1) both; }
.hc-jerk-b { animation: hc-jerk-b 1s cubic-bezier(.2,.7,.3,1) both; }
.hc-jerk-c { animation: hc-jerk-c 1.05s cubic-bezier(.25,.6,.3,1) both; }
.hc-jerk-d { animation: hc-jerk-d 1.1s cubic-bezier(.3,.6,.3,1) both; }

/* Shed leaves: fall, drift sideways, spin, fade. */
@keyframes hc-fall {
  0%   { opacity: .95; transform: translate(0, 0) rotate(0); }
  /* Knocked loose — moves immediately, so it overlaps the tree's jerk rather
     than waiting for it to finish. */
  9%   { transform: translate(calc(var(--hc-drift, 20px) * .55),
                              calc(var(--hc-fall, 200px) * .09))
                    rotate(calc(var(--hc-spin, 360deg) * .14)); }
  30%  { opacity: .9;
         transform: translate(calc(var(--hc-drift, 20px) * .8),
                              calc(var(--hc-fall, 200px) * .34))
                    rotate(calc(var(--hc-spin, 360deg) * .35)); }
  65%  { transform: translate(calc(var(--hc-drift, 20px) * -.4),
                              calc(var(--hc-fall, 200px) * .72))
                    rotate(calc(var(--hc-spin, 360deg) * .72)); }
  88%  { opacity: .75;
         transform: translate(var(--hc-drift, 20px), var(--hc-fall, 200px))
                    rotate(var(--hc-spin, 360deg)); }
  100% { opacity: 0;                       /* settle on the ground, then fade */
         transform: translate(var(--hc-drift, 20px), var(--hc-fall, 200px))
                    rotate(var(--hc-spin, 360deg)); }
}

@media (prefers-reduced-motion: reduce) {
  .hc-jerk-a, .hc-jerk-b, .hc-jerk-c, .hc-jerk-d { animation: none; }
  .hc-leaf { display: none; }
}

/* Per-clump knock. Direction and amount come from --hc-jolt, set per clump on
   each click, so the crown scatters rather than snapping as one. */
@keyframes hc-jolt {
  0%   { transform: rotate(0); }
  14%  { transform: rotate(var(--hc-jolt, 4deg)); }
  38%  { transform: rotate(calc(var(--hc-jolt, 4deg) * -0.55)); }
  62%  { transform: rotate(calc(var(--hc-jolt, 4deg) * 0.28)); }
  82%  { transform: rotate(calc(var(--hc-jolt, 4deg) * -0.1)); }
  100% { transform: rotate(0); }
}

/* The headline swaps when the tree is shaken. A short fade so the words change
   rather than snap, and a fixed min-height so the two lines cannot reflow the
   hero as they swap. */
.hero-copy h1 {
  transition: opacity .18s var(--ease);
}
.hero-copy h1.is-swapping { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .hero-copy h1 { transition: none; }
}

/* Wandering constellations: a small group of stars joins up somewhere in the
   sky, holds, and fades. Drawn by main.js into a fixed layer; nothing animates
   between appearances, so the cost is a single opacity transition. */
.constellation {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  color: var(--star);
}

/* The fade belongs to each FIGURE, not the layer. Several run at once on
   independent cycles, so a single opacity on the container would tie them
   together — and while the container held the fade it stayed at 0 and nothing
   was ever visible. */
.constellation-figure {
  opacity: 0;
  transition: opacity 1.6s var(--ease);
}
.constellation-figure.is-on { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .constellation { display: none; }
}
