/* ============================================================
   Meridian 9 — public site
   Two static pages. Layout only; every colour comes from
   tokens.css (the approved palette) plus the site-local surface
   tokens below. No raw hex outside this :root block — one
   palette, one place to change it.
   ============================================================ */

:root {
  /* Site-local additions, layered over the copied palette.
     --surface-bright: the app palette tops out at #F2F0EE; the
     site brief asks for more white, so pages use true white for
     raised surfaces and keep the tinted wash behind them. */
  --surface-bright: #FFFFFF;
  --topbar-bg: rgba(255, 255, 255, 0.86);
  /* One soft veil over the page wash. Lifting the wash toward
     white RAISES every text-on-wash contrast ratio (the darkest
     documented wash region only gets lighter), so no measured
     pair below its floor moves the wrong way. */
  --wash-veil: linear-gradient(rgba(255, 255, 255, 0.52), rgba(255, 255, 255, 0.52));
}

* { box-sizing: border-box; }

html {
  font-size: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--wash-veil), var(--wash-page) var(--page);
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 16.5px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;   /* the drifting blobs extend past the edges by design */
}

/* ---------------------------------------------------------------
   Background interest — two large soft colour fields drifting very
   slowly behind everything. Pure CSS, palette colours only, and
   switched off entirely under prefers-reduced-motion.
   --------------------------------------------------------------- */
body::before,
body::after {
  content: "";
  position: fixed;
  z-index: -1;
  border-radius: 50%;
  filter: blur(90px);
  pointer-events: none;
}
body::before {
  width: 55vw; height: 55vw;
  left: -18vw; top: -22vw;
  background: radial-gradient(circle,
    rgba(216, 166, 210, 0.42) 0%, rgba(216, 166, 210, 0) 70%);
  animation: drift-a 46s ease-in-out infinite alternate;
}
body::after {
  width: 60vw; height: 60vw;
  right: -22vw; bottom: -26vw;
  background: radial-gradient(circle,
    rgba(136, 188, 210, 0.38) 0%, rgba(136, 188, 210, 0) 70%);
  animation: drift-b 58s ease-in-out infinite alternate;
}
@keyframes drift-a {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(9vw, 7vh) scale(1.12); }
}
@keyframes drift-b {
  from { transform: translate(0, 0) scale(1.08); }
  to   { transform: translate(-8vw, -6vh) scale(1); }
}

/* ---------------------------------------------------------------
   Network globe — fixed to the lower-right corner, roughly one
   third on screen, behind everything. assets/globe.svg is generated
   procedurally (tools note in README) in the palette's bluegrey
   family. It spins with scroll via a CSS scroll-driven animation:
   no JavaScript, and browsers without support simply show it still.
   --------------------------------------------------------------- */
.globe-wrap {
  position: fixed;
  right: 0;
  bottom: 0;
  z-index: -1;
  width: min(120vmin, 1050px);
  aspect-ratio: 1;
  /* two thirds pushed off the corner; the slight tilt angles the spin
     axis like the reference art */
  transform: translate(33%, 33%) rotate(-8deg);
  pointer-events: none;
  overflow: hidden;   /* the wrapper is the sprite's viewing window */
}
/* assets/globe.svg is a 48-frame sprite strip — the same globe at 7.5°
   longitude steps. The classic background-sprite technique steps the
   strip through this fixed window, so the globe revolves IN PLACE: the
   window never moves, only the frame behind it changes, and background-
   position percentages register each frame exactly (0% = first frame,
   100% = last — no partial-frame bleed possible). */
.globe-spin {
  width: 100%;
  height: 100%;
  background-image: url("globe.svg");
  background-size: 4800% 100%;      /* 48 frames side by side */
  background-repeat: no-repeat;
  background-position: 0 0;
}
/* The halo is a separate static layer over the sprite window: dots and
   lines around the sphere hold still while the globe turns beneath. */
.globe-halo {
  position: absolute;
  inset: 0;
  background: url("globe-halo.svg") center / 100% 100% no-repeat;
}
@supports (animation-timeline: scroll()) {
  .globe-spin {
    animation: globe-spin both;
    /* steps(48, jump-none) => 48 output levels at k/47 of the range, and
       with background-size 4800% that maps to EXACTLY -k frame-widths:
       every stop is a whole frame, so the globe revolves in place.
       ⚠️ steps(47, jump-none) was shipped first, on the reasoning
       "47 intervals = 48 stops". Wrong: jump-none yields N stops spaced
       at 1/(N-1), so every stop missed the frame grid by ~2% and the
       error accumulated — the globe slid left as it spun, showing two
       partial frames by mid-scroll. The stop COUNT must equal the frame
       count; verified with paused-animation renders at 25/50/75%. */
    animation-timing-function: steps(48, jump-none);
    animation-timeline: scroll(root);
  }
}
@keyframes globe-spin {
  from { background-position-x: 0%; }
  to   { background-position-x: 100%; }
}

/* ---------------------------------------------------------------
   Skip link — hidden until keyboard focus.
   --------------------------------------------------------------- */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 10px 16px;
  background: var(--surface-bright);
  color: var(--text-primary);
  border-radius: 0 0 var(--r-chip) 0;
  box-shadow: var(--shadow-pop);
}
.skip:focus {
  left: 0;
}

/* ---------------------------------------------------------------
   Shell
   --------------------------------------------------------------- */
.wrap {
  max-width: 1000px;
  margin: 0 auto;
  padding: 0 clamp(20px, 4vw, 36px);
}

/* The top bar is a real header band: white, sticky, hairline below.
   It is the "more white" anchor the rest of the page hangs from. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--topbar-bg);
  backdrop-filter: blur(14px) saturate(1.1);
  border-bottom: 1px solid var(--border-soft);
}

.site-head {
  display: flex;
  /* Brand on the RIGHT of the bar, nav on the left. row-reverse rather
     than reordered markup, so the DOM (and tab order) still leads with
     the brand link. */
  flex-direction: row-reverse;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
  padding: 14px 0;
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  text-decoration: none;
  color: var(--text-primary);
}
.brand-mark {
  height: 36px;
  width: auto;
  display: block;
}
.brand-word {
  font-size: 15px;
  font-weight: 650;
  letter-spacing: 0.22em;
  color: var(--text-primary);
}

.site-nav {
  display: flex;
  align-items: center;
  gap: clamp(16px, 3vw, 28px);
}
.site-nav a {
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 14.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 6px 2px;
  border-bottom: 2px solid transparent;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.site-nav a:hover {
  color: var(--text-primary);
}
.site-nav a[aria-current="page"] {
  color: var(--text-primary);
  border-bottom-color: var(--accent);
}

/* ---------------------------------------------------------------
   Hero
   --------------------------------------------------------------- */
.hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(24px, 5vw, 64px);
  padding: clamp(40px, 8vw, 88px) 0 clamp(36px, 6vw, 64px);
}
.hero h1 {
  margin: 0 0 var(--sp-5);
  font-size: clamp(34px, 5.2vw, var(--fs-hero));
  line-height: 1.12;
  letter-spacing: -0.015em;
  font-weight: 680;
  max-width: 15ch;
}
.lede {
  margin: 0 0 var(--sp-7);
  max-width: 54ch;
  font-size: clamp(17px, 2vw, 19px);
  color: var(--text-secondary);
}
.hero-logo {
  height: clamp(180px, 24vw, 300px);
  width: auto;
  /* rgba(25,29,35,…) is ink — the same base every shadow token in
     tokens.css uses. A shadow is not a palette colour; documented
     rather than minted as a one-off token in the copied file. */
  filter: drop-shadow(0 14px 30px rgba(25, 29, 35, 0.18));
  animation: hero-float 7s ease-in-out infinite;
}
@keyframes hero-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-8px); }
}

/* ---------------------------------------------------------------
   Sections
   --------------------------------------------------------------- */
main {
  padding-top: clamp(4px, 1vw, 12px);
}
section {
  margin: 0 0 clamp(40px, 6vw, 72px);
}

.kicker {
  margin: 0 0 var(--sp-2);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.kicker::after {
  content: "";
  display: block;
  width: 44px;
  height: 3px;
  margin-top: 8px;
  border-radius: var(--r-bar);
  background: var(--wash-accent-bar);
}

section h2 {
  margin: var(--sp-4) 0 var(--sp-4);
  font-size: clamp(24px, 3.2vw, 31px);
  line-height: 1.2;
  letter-spacing: -0.01em;
  font-weight: 660;
  max-width: 24ch;
}

section > p, .band p, .tour-slot p, .diagram-caption {
  max-width: 62ch;
}

p { margin: 0 0 var(--sp-4); }
p:last-child { margin-bottom: 0; }

/* A band is a raised panel — surface + hairline + shadow, because the
   tokens file records that fill difference alone (1.20:1) is invisible.
   Bright white, per the design brief; the hairline and shadow carry the
   edge against the lightened wash. */
.band {
  background: var(--surface-bright);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-panel);
  box-shadow: var(--shadow-panel);
  padding: clamp(24px, 4vw, 44px);
}

/* ---------------------------------------------------------------
   System map — the information-flow diagram, inline SVG.
   Ported from the roadmap's Diagram 03 with its original geometry
   (the routing there was drawn specifically so no line crosses a
   node). Pulses are SMIL and hidden under reduced motion.
   --------------------------------------------------------------- */
.diagram-panel {
  background: var(--surface-bright);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-panel);
  box-shadow: var(--shadow-panel);
  padding: clamp(16px, 3vw, 32px);
  overflow-x: auto;   /* the map scrolls inside its own panel on phones */
}
/* The map earns more width than the text column: break out of .wrap,
   centred, capped, never wider than the viewport. */
.diagram-breakout {
  width: min(1440px, 100vw - 24px);
  position: relative;
  left: 50%;
  transform: translateX(-50%);
}
.diagram-panel svg {
  display: block;
  width: 100%;
  min-width: 1000px;  /* below this the labels stop being readable; scroll instead */
  height: auto;
  font-family: var(--font-sans);
}
.diagram-caption {
  margin-top: var(--sp-5);
  font-size: 15px;
  color: var(--text-secondary);
  margin-left: auto;
  margin-right: auto;
}

/* Legend chips, matching the destination-coded line colours. */
.map-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;   /* matches the centred tabs above it */
  gap: 6px 18px;
  margin: var(--sp-4) 0 var(--sp-2);
  font-size: 13px;
  color: var(--text-secondary);
}
.map-legend span { display: inline-flex; align-items: center; gap: 7px; }
.map-legend i {
  display: inline-block;
  width: 22px; height: 0;
  border-top: 3px solid currentColor;
  border-radius: 2px;
}
.leg-flow i     { color: var(--accent); }
.leg-inv i      { color: var(--status-healthy-text); }
.leg-ship i     { color: var(--status-critical-text); }
.leg-cust i     { color: var(--status-warning-text); }
.leg-cross i    { border-top-style: dotted; color: var(--text-secondary); }

/* ---------------------------------------------------------------
   Isolation control — CSS only, no JavaScript. Three visually-
   hidden radios precede the map; :checked + sibling selectors dim
   whichever module groups are not selected. The radios stay
   focusable, so the control works from the keyboard.
   --------------------------------------------------------------- */
.iso-radio {
  position: absolute;
  width: 1px; height: 1px;
  clip-path: inset(50%);
  overflow: hidden;
}
.iso-tabs {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin: var(--sp-5) 0 var(--sp-4);
}
.iso-tabs label {
  padding: 8px 18px;
  border: 1px solid var(--border-soft);
  border-radius: var(--r-chip);
  background: var(--surface-bright);
  color: var(--text-secondary);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--shadow-panel);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.iso-tabs label:hover { color: var(--text-primary); }

#iso-all:checked  ~ .iso-tabs label[for="iso-all"],
#iso-inv:checked  ~ .iso-tabs label[for="iso-inv"],
#iso-cust:checked ~ .iso-tabs label[for="iso-cust"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--c-panel);
}
#iso-all:focus-visible  ~ .iso-tabs label[for="iso-all"],
#iso-inv:focus-visible  ~ .iso-tabs label[for="iso-inv"],
#iso-cust:focus-visible ~ .iso-tabs label[for="iso-cust"] {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Text inside the map. Sizes are in viewBox units (2280 wide), so at
   the 1440px breakout cap they render at ~63% of these figures. */
.flow-svg .band-label {
  font-size: 24px;
  font-weight: 700;
  letter-spacing: 4px;
  fill: var(--text-secondary);
}
.flow-svg .node-label { font-size: 24px; font-weight: 600; fill: var(--text-primary); }
.flow-svg .hub-label  { font-size: 27px; font-weight: 650; fill: var(--c-panel); }
.flow-svg .dash-label { font-size: 28px; font-weight: 700; letter-spacing: 1px; fill: var(--text-primary); }

/* Ghost, don't hide: the de-selected module stays as faint context,
   so the isolated view still shows where its edges lead. Cross-module
   lines and the hubs (.m-x) ghost in BOTH isolated views — they belong
   to neither module alone. */
.flow-svg .m-inv, .flow-svg .m-cust, .flow-svg .m-x {
  transition: opacity 300ms var(--ease);
}
#iso-inv:checked  ~ .diagram-breakout .flow-svg :is(.m-cust, .m-x) { opacity: 0.13; }
#iso-cust:checked ~ .diagram-breakout .flow-svg :is(.m-inv, .m-x) { opacity: 0.13; }

/* ---------------------------------------------------------------
   Buttons
   --------------------------------------------------------------- */
.button {
  display: inline-block;
  padding: 12px 22px;
  border-radius: var(--r-chip);
  background: var(--accent);
  color: var(--c-panel);
  font-size: 15px;
  font-weight: 650;
  letter-spacing: 0.015em;
  text-decoration: none;
  border: 1px solid transparent;
  box-shadow: var(--shadow-panel);
  overflow-wrap: anywhere;   /* the contact address is long; never overflow */
  transition: filter var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
.button:hover {
  filter: brightness(0.92);
  box-shadow: var(--shadow-panel-hover);
  transform: translateY(-1px);
}

.button-quiet {
  background: transparent;
  color: var(--text-secondary);
  border-color: var(--accent);
  box-shadow: none;
}
.button-quiet:hover {
  filter: none;
  color: var(--text-primary);
  box-shadow: var(--shadow-panel);
}

/* ---------------------------------------------------------------
   Tour slot — the reserved place screenshots drop into later.

   Border is --accent, not --border, and the elevation is an INSET
   shadow, not the panel shadow. Measured, both directions: the
   sunken fill #E9E6E3 is 1.001:1 against the wash's light end
   (invisible) and the mauve border peaked at 1.51:1 — the exact
   band-vs-surface failure the tokens file documents. --accent
   measures 4.22:1 against the fill and 3.10:1 against the wash's
   darkest region, clearing 3:1 wherever the slot lands. A raised
   drop shadow would be wrong here: the slot is deliberately sunken.
   --------------------------------------------------------------- */
.tour-slot {
  border: 2px dashed var(--accent);
  border-radius: var(--r-panel);
  background: var(--surface-sunken);
  box-shadow: inset 0 1px 3px rgba(25, 29, 35, 0.08);
  padding: clamp(28px, 5vw, 52px);
  text-align: center;
}
.tour-slot p {
  margin-left: auto;
  margin-right: auto;
  color: var(--text-secondary);
}

/* ---------------------------------------------------------------
   Contact
   --------------------------------------------------------------- */
.contact { text-align: center; }
.contact h2,
.contact p { margin-left: auto; margin-right: auto; }
.contact .kicker::after { margin-left: auto; margin-right: auto; }
.contact-line { margin-top: var(--sp-6); }
.contact-phone {
  margin-top: var(--sp-3);
  font-size: 16px;
  font-weight: 600;
}
.contact-phone a {
  color: var(--text-secondary);
  letter-spacing: 0.04em;
}
.contact-meta {
  font-size: var(--fs-sm);
  color: var(--text-secondary);
}

/* ---------------------------------------------------------------
   Prose links (about page)
   --------------------------------------------------------------- */
main a:not(.button):not(.brand) {
  color: var(--text-secondary);
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-underline-offset: 3px;
  transition: color var(--dur) var(--ease);
}
main a:not(.button):not(.brand):hover {
  color: var(--text-primary);
}

/* ---------------------------------------------------------------
   Footer
   --------------------------------------------------------------- */
.site-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
  padding: var(--sp-7) 0 var(--sp-8);
  border-top: 1px solid var(--border-soft);
  font-size: var(--fs-sm);
  color: var(--text-secondary);
}
.site-foot p { margin: 0; }
.site-foot a {
  color: var(--text-secondary);
  text-decoration: none;
  font-weight: 600;
}
.site-foot a:hover { color: var(--text-primary); }

/* ---------------------------------------------------------------
   Focus + small screens + reduced motion
   --------------------------------------------------------------- */
a:focus-visible, .button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-chip);
}

@media (max-width: 860px) {
  .hero { grid-template-columns: 1fr; }
  .hero-logo { display: none; }
}

/* Brand mark alone below 480px. Measured, not guessed: at 375px the full
   header is 385px of min-content — mark + wordmark + three nav links — which
   is 10px of genuine horizontal overflow. The wordmark is the reclaimable
   piece; the mark still identifies the brand, as it does in the favicon. */
@media (max-width: 480px) {
  .brand-word { display: none; }
  .site-nav { gap: 14px; }
}

@media (prefers-reduced-motion: reduce) {
  body::before, body::after { animation: none; }
  .hero-logo { animation: none; }
  .globe-spin { animation: none; }
  html { scroll-behavior: auto; }
  /* SMIL pulses in the system map: SMIL ignores this media query on its
     own, so the moving dots are hidden outright. */
  .m9-pulse { display: none; }
}
