/* ============================================================================
   JOURNEYMRO DESIGN SYSTEM
   ----------------------------------------------------------------------------
   One stylesheet. No build step, no dependencies. Drop it in, link it, and the
   page is already in the system.

       <link rel="stylesheet" href="assets/css/journeymro.css">

   Extracted from journeymro_site (Astro + Tailwind 4, but almost all of the
   styling there is hand-written CSS in src/styles/global.css). Class names are
   kept VERBATIM from that codebase wherever the component already exists there,
   so markup can be moved between the two projects without translation. New
   components — tables, stat rows, chart containers, mobile chrome — follow the
   same naming idiom, taken from the report screens in journeymro_site/public/.

   CONTENTS
     01  Tokens                    --bg … --maxw
     02  Light theme
     03  Fonts
     04  Reset + base
     05  Utilities                 .mono .num .rule .stack
     06  Frame                     .shell .nav-inner .hero-footer .grid-bg
     07  Notches                   .svc7-arrow
     08  Reticles
     09  Type scale                .display .title .heading .lede .prose
     10  Buttons                   .btn .hero-btn .prod-link .msg-send
     11  Nav                       header.nav .wordmark .nav-text .lang-popover
     12  Mobile drawer             .nav-toggle .nav-drawer
     13  Cards                     .card .sv7-card
     14  Tabs, ticks, pills        .sv7-tick .pill .seg
     15  Forms                     .msg-input .msg-textarea .field .switch
     16  Dialogs + overlays        .svc7-overlay .svc7-dialog
     17  Tables                    .table-wrap table .cell-bar
     18  Data display              .stat-row .score-box .meter .kv .tier .badge
     19  Chart containers          .viz-panel .viz .chart-legend
     20  Callouts + status         .callout .empty .skeleton .toast
     21  Lists                     .dash-list .research-bullets .sd-deliver
     22  Icons                     .icon
     23  Screen                    .svc7-head .svc7-tabs .svc7-stage .svc7-card
     24  Motion
     25  Responsive
   ========================================================================== */


/* ══ 01 · TOKENS ══════════════════════════════════════════════════════════ */
/* Every value in the system resolves through one of these. Nothing below
   hardcodes a colour except where a rule needs an alpha over an unknown
   backdrop, and those are written as rgba() of the token's own RGB. */

:root {
  /* surfaces */
  --bg: #020617;                              /* page */
  --panel: #0b1120;                           /* raised surface */
  --panel-2: #0f172a;                         /* raised surface, one step up */
  --panel-fill: rgba(11, 17, 32, 0.4);        /* card at rest */
  --panel-fill-hover: rgba(11, 17, 32, 0.6);  /* card on hover */

  /* lines — there is only one border colour in the system */
  --hairline: #1e293b;
  --hairline-faint: rgba(30, 41, 59, 0.55);   /* grid background only */
  --rule-bright: rgba(148, 163, 184, 0.22);   /* table head underline */
  --row-divider: rgba(148, 163, 184, 0.08);   /* table body dividers */

  /* ink */
  --text: #f8fafc;      /* headings, emphasised text          19.4:1 */
  --body: #cbd5e1;      /* body copy                          11.9:1 */
  --ink: #dde5ef;       /* long-form prose — brighter on purpose */
  --ink-2: #9fb0c4;     /* secondary prose                     7.4:1 */
  --muted: #7c8ba1;     /* labels + chrome — NOT prose         5.8:1 */
  --dim: #64748b;       /* de-emphasised chrome — NOT prose    4.2:1 (below AA) */

  /* accent — one, and only one */
  --cyan: #67c7d4;
  --cyan-bright: #8fdbe6;
  --cyan-rgb: 103, 199, 212;                  /* for rgba() tints */

  /* status — used sparingly, never as decoration */
  --amber: #d9a441;
  --warning: #e6b864;
  --danger: #ef6e6e;
  --ok: #6ee7b7;

  /* type */
  --font-sans: 'Alliance No 2', ui-sans-serif, system-ui, -apple-system, sans-serif;
  --font-heading: 'Alliance No 1', ui-sans-serif, system-ui, sans-serif;
  --font-mono: 'Space Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --num-tabular: "tnum" 1, "lnum" 1, "ss01" 1;

  /* geometry — three numbers define the frame */
  --maxw: 1320px;                             /* rail separation */
  --gutter: clamp(1.25rem, 4vw, 2.8rem);      /* padding inside the rails */
  --band: 64px;                               /* nav height, footer height */
  --chrome: 130px;                            /* 64 + 1 + 1 + 64 — count BOTH hairlines */
  --measure: 44rem;                           /* reading column */
  --radius: 0;                                /* the frame has no radius */
  --radius-notch: 3px;                        /* the one exception */

  /* motion */
  --t-fast: 0.2s;
  --t: 0.25s;
  --t-slow: 0.42s;
  --ease: cubic-bezier(0.22, 0.6, 0.36, 1);

  color-scheme: dark;
}


/* ══ 02 · LIGHT THEME ═════════════════════════════════════════════════════ */
/* Set html[data-theme="light"]. The palette is entirely token-driven, so the
   theme is a token swap and nothing more. Tailwind `stone` throughout — a warm
   neutral, deliberately not the cool slate of the dark theme, which goes muddy
   when inverted. The dark-mode cyan fails contrast on stone, so it steps down
   to cyan-700. */

html[data-theme='light'] {
  --bg: #f5f5f4;                              /* stone-100 */
  --panel: #fafaf9;                           /* stone-50  */
  --panel-2: #e7e5e4;                         /* stone-200 */
  --panel-fill: rgba(255, 255, 255, 0.55);
  --panel-fill-hover: rgba(255, 255, 255, 0.85);

  --hairline: #bfbbb7;                        /* stone-300, darkened so the rail reads */
  --hairline-faint: rgba(87, 83, 78, 0.22);
  --rule-bright: rgba(41, 37, 36, 0.55);
  --row-divider: rgba(41, 37, 36, 0.18);

  --text: #0c0a09;                            /* stone-950 */
  --body: #292524;                            /* stone-800 */
  --ink: #1c1917;
  --ink-2: #44403c;
  --muted: #57534e;                           /* stone-600 */
  --dim: #78716c;                             /* stone-500 */

  --cyan: #0e7490;                            /* cyan-700 */
  --cyan-bright: #155e75;
  --cyan-rgb: 14, 116, 144;

  --amber: #b45309;
  --warning: #854d0e;
  --danger: #991b1b;
  --ok: #047857;

  color-scheme: light;
}


/* ══ 03 · FONTS ═══════════════════════════════════════════════════════════ */
/* Alliance No 1 carries headings, UI labels and every piece of uppercase
   chrome; Alliance No 2 carries body copy. Both are light-weight grotesques
   and read small for their nominal size — size UP rather than down.
   No 2 ships ONE weight (300). Never ask it for 400: the browser will
   synthesise a bold and it looks smeared. Emphasis changes face to No 1.
   Space Mono is data only — code, tokens, indices, figures. It is not a
   chrome face; a mono label beside an Alliance one reads as a mismatch. */

@font-face {
  font-family: 'Alliance No 1';
  src: url('../fonts/alliance/alliance-no1-light.woff2') format('woff2');
  font-weight: 300;
  font-display: swap;
}
@font-face {
  font-family: 'Alliance No 1';
  src: url('../fonts/alliance/alliance-no1-regular.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Alliance No 2';
  src: url('../fonts/alliance/alliance-no2-light.woff2') format('woff2');
  font-weight: 300;
  font-display: swap;
}

/* Space Mono, self-hosted rather than pulled from Google Fonts. journeymro_site
   links the CDN; here it is local so the guide works offline and behind a
   proxy, and so a new project inherits no external font dependency. */
@font-face {
  font-family: 'Space Mono';
  src: url('../fonts/space-mono/space-mono-400-latin.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Space Mono';
  src: url('../fonts/space-mono/space-mono-400-latin-ext.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Space Mono';
  src: url('../fonts/space-mono/space-mono-700-latin.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
}


/* ══ 04 · RESET + BASE ════════════════════════════════════════════════════ */

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

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
  font-size: 16px;
}

body {
  background: var(--bg);
  color: var(--body);
  font-family: var(--font-sans);
  font-weight: 300;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  font-feature-settings: "ss01" 1;
}

/* Alliance No 2 ships a single weight (300). Asking it for 400 makes the
   browser smear a synthetic bold, which is obvious at reading sizes — so
   emphasis changes face rather than weight. Alliance No 1 has a real Regular
   and is already loaded, so this costs nothing. */
strong, b { font-family: var(--font-heading); font-weight: 400; }

::selection { background: rgba(var(--cyan-rgb), 0.22); color: var(--text); }
a { color: inherit; text-decoration: none; }
img, svg, video, canvas { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
input, textarea, select { font: inherit; color: inherit; }

/* Chrome — tabs, cards, notches — carries its own hover/active state, so the
   browser ring is suppressed there and only there. Never on form controls. */
:focus-visible { outline: 1px solid var(--cyan); outline-offset: 2px; }

/* scrollbars, where the page scrolls internally */
.scroll-y {
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(148, 168, 188, 0.25) transparent;
}
.scroll-y:focus, .scroll-y:focus-visible { outline: none; }
.scroll-y::-webkit-scrollbar { width: 8px; height: 8px; }
.scroll-y::-webkit-scrollbar-thumb { background: rgba(148, 168, 188, 0.22); border-radius: 4px; }
.scroll-y::-webkit-scrollbar-thumb:hover { background: rgba(var(--cyan-rgb), 0.4); }


/* ══ 05 · UTILITIES ═══════════════════════════════════════════════════════ */

/* Monospace, small, tracked, uppercase — for DATA that wants a fixed width:
   part numbers, order references, token names, hex values, indices, figures.
   Chrome labels use Alliance instead (see .eyebrow, .field > label, .badge). */
.mono {
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: 0.96rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
}
.mono-sm { font-size: 0.9rem; letter-spacing: 0.14em; }
.mono-idx { color: var(--cyan); }

/* Tabular figures. Put this on anything a reader will compare down a column. */
.num { font-feature-settings: var(--num-tabular); }

.rule { border-top: 1px solid var(--hairline); }
.rule-v { border-left: 1px solid var(--hairline); }
.hr { border: none; border-top: 1px solid var(--hairline); margin: 0; }

.stack { display: flex; flex-direction: column; }
.row { display: flex; align-items: center; }
.gap-1 { gap: 0.5rem; } .gap-2 { gap: 1rem; } .gap-3 { gap: 1.6rem; }
.grow { flex: 1 1 auto; min-width: 0; min-height: 0; }
.text-right { text-align: right; }
.text-accent { color: var(--cyan); }
.text-danger { color: var(--danger); }
.text-warning { color: var(--warning); }
.text-ok { color: var(--ok); }
.text-dim { color: var(--dim); }
.text-muted { color: var(--muted); }
.measure { max-width: var(--measure); }

.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;
}


/* ══ 06 · FRAME ═══════════════════════════════════════════════════════════ */
/* THE defining structure. Two vertical hairlines run the full height of the
   page, --maxw apart, centred. They are never drawn as one element: every
   horizontal band — nav, main, footer — carries its own border-left and
   border-right at the same max-width, so the lines read as continuous while
   horizontal rules cross them.

   Three things make it read as deliberate rather than as a stray container:
     1. The rails never break. A band that omits them puts a gap in the line.
     2. Everything is 1px. No 2px borders, no shadow standing in for an edge,
        no radius on the frame. The only radius in the system is 3px, on the
        outer edge of the docking notches.
     3. Horizontal rules are full-bleed; content need not be. A rule that
        matches the text column collapses the effect. */

.shell,
.nav-inner,
.hero-footer,
.shell-foot-rule {
  max-width: var(--maxw);
  margin: 0 auto;
  border-left: 1px solid var(--hairline);
  border-right: 1px solid var(--hairline);
}

.shell { position: relative; z-index: 1; }
.page-rule { border-top: 1px solid var(--hairline); position: relative; z-index: 1; }
.section { position: relative; padding: 0 var(--gutter); }

/* Viewport-locked page: the rails and footer stay put, content scrolls inside.
   --chrome is 130px, which counts BOTH hairlines. Subtracting 129 overflows by
   exactly 1px and puts a scrollbar on a page that is supposed to be fixed. */
.page-main {
  min-height: calc(100vh - var(--chrome));
  max-height: calc(100vh - var(--chrome));
  display: flex;
  flex-direction: column;
  padding: clamp(1rem, 2.5vh, 1.8rem) var(--gutter) clamp(1rem, 4vh, 2.5rem);
  overflow: hidden;
}
/* Give .page-scroll tabindex="0" and focus it — the page itself no longer
   scrolls, so arrow keys, Page Up/Down and space would otherwise do nothing. */
.page-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; }

/* A page that scrolls normally instead. Same rails, no lock. */
.page-flow { min-height: calc(100vh - var(--chrome)); padding: 0 var(--gutter); }

.hero-footer {
  height: var(--band);
  padding: 0 var(--gutter);
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
.shell-foot-rule { border-top: 1px solid var(--hairline); height: 3rem; }

/* Grid background — fixed behind everything, masked so it fades below the
   fold. Establishes the 96px module the layout implies. */
.grid-bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(var(--hairline-faint) 1px, transparent 1px),
    linear-gradient(90deg, var(--hairline-faint) 1px, transparent 1px);
  background-size: 96px 96px;
  -webkit-mask-image: radial-gradient(ellipse 120% 75% at 50% 0%, #000 38%, transparent 80%);
          mask-image: radial-gradient(ellipse 120% 75% at 50% 0%, #000 38%, transparent 80%);
  opacity: 0.5;
}


/* ══ 07 · NOTCHES ═════════════════════════════════════════════════════════ */
/* The signature detail: page-turn controls that dock onto the rails from
   OUTSIDE the frame, appearing fused to it. They depend entirely on the rail
   above — with no rail they float in space with a stub border on one side.

   Two declarations carry it. The `- 1px` laps the notch's border ONTO the rail
   instead of leaving a hairline gap; dropping the border on the side that meets
   the rail stops it doubling to 2px. Get either wrong and it reads as a
   floating button. */

.svc7-arrow {
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;              /* tall and narrow — a tab on the frame, not a button */
  height: 120px;
  background: rgba(148, 168, 188, 0.04);
  border: 1px solid var(--hairline);
  color: rgba(120, 145, 165, 0.5);   /* dim at rest; the accent is spent on hover */
  cursor: pointer;
  transition: color var(--t), background var(--t);
}
.svc7-arrow[hidden] { display: none !important; }   /* beats the base display:flex */
.svc7-arrow:hover { color: var(--cyan); background: rgba(148, 168, 188, 0.07); }
.svc7-arrow svg { width: 24px; height: 24px; }
.svc7-arrow:focus, .svc7-arrow:focus-visible { outline: none; box-shadow: none; }

.svc7-arrow-left {
  right: calc(50% + var(--maxw) / 2 - 1px);
  border-right: none;
  border-radius: var(--radius-notch) 0 0 var(--radius-notch);
}
.svc7-arrow-right {
  left: calc(50% + var(--maxw) / 2 - 1px);
  border-left: none;
  border-radius: 0 var(--radius-notch) var(--radius-notch) 0;
}

/* brief flash when the turn is triggered by the left/right arrow keys */
.svc7-arrow.is-key-active {
  color: var(--cyan);
  background: rgba(var(--cyan-rgb), 0.16);
  border-color: rgba(var(--cyan-rgb), 0.5);
  filter: drop-shadow(0 0 8px rgba(var(--cyan-rgb), 0.55));
  transition: color 0.08s, background 0.08s, border-color 0.08s;
}
/* pulse as an invitation on a landing page */
.svc7-arrow-breathe { animation: arrowBreathe 3.2s ease-in-out infinite; }
.svc7-arrow-breathe:hover {
  animation: none;
  color: var(--cyan);
  filter: drop-shadow(0 0 8px rgba(var(--cyan-rgb), 0.55));
}
@keyframes arrowBreathe {
  0%, 100% { color: rgba(120, 145, 165, 0.5); filter: drop-shadow(0 0 0 transparent); }
  50%      { color: var(--cyan); filter: drop-shadow(0 0 10px rgba(var(--cyan-rgb), 0.65)); }
}
/* below the frame width there is no rail to dock to */
@media (max-width: 760px) { .svc7-arrow { display: none; } }


/* ══ 08 · RETICLES ════════════════════════════════════════════════════════ */
/* Crosshair decorators for panel corners. A technical-drawing tick, not a
   decoration — use them on at most one or two elements per screen. */

.reticle { position: absolute; width: 11px; height: 11px; z-index: 3; pointer-events: none; }
.reticle::before, .reticle::after { content: ""; position: absolute; background: var(--cyan); opacity: 0.65; }
.reticle::before { left: 50%; top: 0; width: 1px; height: 100%; transform: translateX(-0.5px); }
.reticle::after  { top: 50%; left: 0; height: 1px; width: 100%; transform: translateY(-0.5px); }
.reticle.tl { left: -6px; top: -6px; }
.reticle.tr { right: -6px; top: -6px; }
.reticle.bl { left: -6px; bottom: -6px; }
.reticle.br { right: -6px; bottom: -6px; }


/* ══ 09 · TYPE SCALE ══════════════════════════════════════════════════════ */
/* Headings are light weight (300) and negatively tracked (-0.02em). Labels are
   uppercase with 0.1–0.22em tracking. Prose is sentence case, 1.6–1.8 leading.
   Every heading size is a clamp() — the frame is fixed at 1320px but the type
   inside it is fluid. */

.display {
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: clamp(2.39rem, 4.46vw, 3.95rem);
  line-height: 1.08;
  letter-spacing: -0.02em;
  color: var(--text);
}
.title {
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: clamp(2.1rem, 4.08vw, 3.27rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--text);
}
.heading {
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: clamp(1.63rem, 3.14vw, 2.2rem);
  line-height: 1.15;
  letter-spacing: -0.015em;
  color: var(--text);
  text-wrap: balance;
}
.subheading {
  font-family: var(--font-heading);
  font-weight: 400;
  font-size: 1.5rem;
  letter-spacing: 0.01em;
  color: var(--text);
}
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 0.95rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cyan);
}
.eyebrow .line { height: 1px; width: 2.5rem; background: var(--cyan); opacity: 0.7; }
.eyebrow .dot { width: 6px; height: 6px; background: var(--cyan); }
.lede {
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: clamp(1.2rem, 1.71vw, 1.37rem);
  line-height: 1.65;
  color: var(--body);
  max-width: 38ch;
}

/* Long-form reading. --ink / --ink-2 are brighter than --muted and --dim on
   purpose: those are chrome colours and fail at length. */
.prose { color: var(--ink); font-size: 1.27rem; max-width: var(--measure); }
.prose h2 { margin-top: 2.6rem; font-family: var(--font-heading); font-weight: 400; font-size: 1.5rem; letter-spacing: 0.01em; color: var(--text); }
.prose h3 { margin-top: 1.8rem; font-family: var(--font-heading); font-weight: 400; font-size: 1.25rem; color: var(--text); }
.prose p { margin-top: 1.15rem; line-height: 1.8; }
.prose strong { color: var(--text); font-weight: 400; }
.prose em { color: var(--text); font-style: italic; }
.prose a { color: var(--cyan); text-decoration: underline; text-decoration-color: rgba(var(--cyan-rgb), 0.45); text-underline-offset: 0.25em; }
.prose a:hover { color: var(--cyan-bright); }
.prose code { font-family: var(--font-mono); font-size: 0.86em; color: var(--cyan); }

.standfirst {
  margin-top: 1.1rem;
  padding-bottom: 1.6rem;
  border-bottom: 1px solid var(--hairline);
  font-size: 1.39rem;
  line-height: 1.65;
  color: var(--ink-2);
}
.pull {
  margin: 2.4rem 0 0;
  padding: 1.4rem 0 1.4rem 1.5rem;
  border-left: 2px solid var(--cyan);
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 1.54rem;
  line-height: 1.55;
  letter-spacing: -0.01em;
  color: var(--text);
}

/* Section head: title left, chrome right, baseline-aligned. */
.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 2.4rem;
}


/* ══ 10 · BUTTONS ═════════════════════════════════════════════════════════ */
/* Three weights and one link form. The solid is the only place the accent
   becomes a fill; everywhere else it is a hairline or a text colour. */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 0.97rem;
  letter-spacing: 0.096em;
  text-transform: uppercase;
  color: var(--bg);
  background: var(--cyan);
  padding: 0.62rem 1.05rem;
  border: 1px solid var(--cyan);
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast);
}
.btn:hover { background: var(--cyan-bright); border-color: var(--cyan-bright); }

/* Ghost — the default for anything that is not the one primary action. */
.btn.ghost { background: transparent; color: var(--text); border-color: var(--hairline); }
.btn.ghost:hover { border-color: var(--cyan); color: var(--cyan); background: transparent; }

/* Quiet — a tinted accent field. Reads as affirmative without shouting. */
.btn.quiet {
  color: var(--cyan-bright);
  background: rgba(var(--cyan-rgb), 0.1);
  border-color: rgba(var(--cyan-rgb), 0.42);
}
.btn.quiet:hover { background: rgba(var(--cyan-rgb), 0.16); border-color: rgba(var(--cyan-rgb), 0.62); }

.btn.danger { background: transparent; color: var(--danger); border-color: rgba(239, 110, 110, 0.4); }
.btn.danger:hover { background: rgba(239, 110, 110, 0.1); border-color: var(--danger); }

.btn.sm { font-size: 0.91rem; padding: 0.42rem 0.7rem; letter-spacing: 0.14em; }
.btn.lg { font-size: 1.04rem; padding: 0.8rem 1.5rem; }
.btn.block { width: 100%; }
.btn[disabled], .btn:disabled { opacity: 0.35; cursor: not-allowed; pointer-events: none; }
.btn svg { width: 0.95em; height: 0.95em; flex-shrink: 0; }

/* Nav / hero button — heading face rather than mono, for running text contexts. */
.hero-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 1.02rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bg);
  background: var(--cyan);
  padding: 0.45rem 0.9rem;
  border: 1px solid var(--cyan);
  white-space: nowrap;
  transition: background var(--t-fast), border-color var(--t-fast);
}
.hero-btn:hover { background: var(--cyan-bright); border-color: var(--cyan-bright); }

/* Link with a travelling arrow — the gap grows on hover rather than the text
   moving, so the label stays where the eye left it. */
.prod-link {
  display: inline-flex;
  gap: 0.5rem;
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 0.95rem;
  letter-spacing: 0.084em;
  text-transform: uppercase;
  color: var(--cyan);
  transition: gap var(--t-fast), color var(--t-fast);
}
.prod-link:hover { gap: 0.85rem; color: var(--cyan-bright); }

/* Icon button — 44px hit area, no chrome until hover. */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.2rem;
  height: 2.2rem;
  color: var(--muted);
  border: 1px solid transparent;
  transition: color var(--t-fast), border-color var(--t-fast), background var(--t-fast);
}
.icon-btn:hover { color: var(--text); border-color: var(--hairline); background: var(--panel-fill); }
.icon-btn svg { width: 1.05rem; height: 1.05rem; }


/* ══ 11 · NAV ═════════════════════════════════════════════════════════════ */
/* Sticky, translucent, 64px, with the rails on the inner element. The nav is a
   band of the frame, so .nav-inner repeats the same max-width and side borders
   as .shell — see section 06. */

header.nav {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--hairline);
}
.nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--gutter);
  height: var(--band);
}

/* Wordmark. Baseline-aligned, not centred: "MRO" is a heavier span with no
   descenders, so centring each flex item by its own box height lifts it off
   the line. The J is clipped 8% at the foot so its tail does not drop the
   whole lockup. */
.wordmark {
  display: flex;
  align-items: baseline;
  gap: 0;
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 1.54rem;
  letter-spacing: -0.03em;
  color: var(--text);
}
.wordmark .mark-j { display: inline-block; clip-path: inset(0 0 8% 0); }
.wordmark .mark-name { font-weight: 400; color: var(--cyan); letter-spacing: -0.01em; }
.wordmark .mark-desc {
  align-self: center;
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 0.97rem;
  letter-spacing: 0.084em;
  text-transform: uppercase;
  color: var(--dim);
  padding-left: 0.7rem;
  margin-left: 0.6rem;
  border-left: 1px solid var(--hairline);
}
.wordmark.sm { font-size: 1.24rem; }
.wordmark.lg { font-size: 2.68rem; }

.nav-right { display: flex; align-items: center; gap: 1.6rem; }
.nav-links { display: flex; align-items: center; gap: 1.8rem; }
.nav-text {
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 0.95rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  transition: color var(--t-fast);
}
.nav-text:hover { color: var(--text); }
.nav-text.active { color: var(--cyan); }
.nav-cta {
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 0.95rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bg);
  background: var(--cyan);
  padding: 0.45rem 0.9rem;
  border: 1px solid var(--cyan);
  white-space: nowrap;
  transition: background var(--t-fast), border-color var(--t-fast);
}
.nav-cta:hover { background: var(--cyan-bright); border-color: var(--cyan-bright); }

/* Hover-driven popover menu. The trigger fills the full 64px band so the
   pointer never leaves the hover region on the way down, and ::after bridges
   the gap between trigger and panel. */
.lang-select {
  position: relative;
  display: flex;
  align-items: center;
  height: var(--band);
  padding: 0 0.45rem;
  margin: 0 -0.45rem;
}
.lang-select::after { content: ""; position: absolute; top: 100%; right: 0; width: 100%; height: 0.4rem; }
.lang-select-trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 0.95rem;
  letter-spacing: 0.1em;
  color: var(--muted);
  padding: 0.35rem 0.15rem;
  transition: color var(--t-fast);
}
.lang-select-trigger:hover,
.lang-select-trigger[aria-expanded="true"] { color: var(--text); }
.nav-menu-trigger { font-size: 0.95rem; letter-spacing: 0.12em; text-transform: uppercase; }
.nav-menu-trigger.active { color: var(--cyan); }
/* Hairline caret, drawn rather than typed. U+25BC is not in Alliance, so it
   rendered from a fallback font whose weight and seat in the em box were not
   ours to set — hence a filled triangle sitting a shade low. As an SVG the box
   IS the ink, so it aligns predictably. translateY lifts it the 0.7px from the
   line-box centre that flex uses onto the label's cap-height centre. */
.lang-caret {
  display: flex;
  color: var(--muted);
  transform: translateY(-0.7px);
  transition: transform var(--t-fast);
}
.lang-caret svg {
  width: 11px; height: 9px;
  fill: none; stroke: currentColor; stroke-width: 0.9;
  stroke-linecap: round; stroke-linejoin: round;
}
.lang-select:hover .lang-caret,
.lang-select:focus-within .lang-caret { transform: translateY(-0.7px) rotate(180deg); }

.lang-popover {
  position: absolute;
  top: calc(100% + 0.2rem);
  right: 0;
  min-width: 13.25rem;
  border: 1px solid var(--hairline);
  background: color-mix(in srgb, var(--panel) 96%, transparent);
  backdrop-filter: blur(10px);
  padding: 0.42rem;
  display: none;
  flex-direction: column;
  gap: 0.15rem;
  z-index: 60;
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.35);
}
.lang-select:hover .lang-popover,
.lang-select:focus-within .lang-popover { display: flex; }
.nav-menu-popover { right: auto; left: 0; min-width: 14.5rem; }

/* Menu row. The hover cue is a cyan hairline UNDER the label, not a tinted
   band — the site's own idiom. The rule sits on .lang-label so the numeric or
   icon prefix is not dragged into the underline. */
.lang-option {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  text-align: left;
  padding: 0.62rem 0.72rem;
  font-family: var(--font-heading);
  font-size: 1.15rem;
  letter-spacing: 0.02em;
  color: var(--body);
  transition: color var(--t-fast);
}
.lang-option:hover, .lang-option.active { color: var(--text); }
.lang-option:hover .lang-label,
.lang-option.active .lang-label {
  text-decoration: underline;
  text-decoration-color: rgba(var(--cyan-rgb), 0.7);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.3em;
}
.lang-label { white-space: nowrap; }
.nav-popover-divider { display: block; height: 1px; background: var(--hairline); margin: 0.25rem 0.5rem; }
.nav-popover-prefix {
  flex: 0 0 1.75rem;
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 1.02rem;
  letter-spacing: 0.08em;
  color: var(--muted);
  line-height: 1;
}
.nav-popover-prefix-icon { display: inline-flex; align-items: center; width: 1.75rem; height: 1.1rem; color: var(--dim); }
.nav-popover-prefix-icon svg { width: 1.05rem; height: 1.05rem; }
.lang-option:hover .nav-popover-prefix,
.lang-option:hover .nav-popover-prefix-icon { color: var(--body); }

/* Theme toggle */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.9rem; height: 1.9rem; margin-left: 0.55rem;
  color: var(--muted); transition: color var(--t-fast);
}
.theme-toggle:hover { color: var(--text); }
.theme-icon { display: block; }
/* Filled, not hairline. At 12.8px the 1.4 stroke .icon supplies all but
   disappears against the band. The moon is a closed crescent so it drops its
   stroke entirely; the sun keeps one for the rays, which have no area. */
.theme-toggle .theme-icon {
  width: 0.8rem; height: 0.8rem;
  fill: currentColor;
  transform: translateY(-0.7px);
}
.theme-toggle .theme-icon-sun path { stroke-width: 1.8; }
.theme-toggle .theme-icon-moon path { stroke: none; }
.theme-icon-moon { display: none; }
html[data-theme='light'] .theme-icon-sun { display: none; }
html[data-theme='light'] .theme-icon-moon { display: block; }


/* ══ 12 · MOBILE DRAWER ═══════════════════════════════════════════════════ */
/* Below 820px the desktop popovers are unusable — they are hover-driven and
   absolutely positioned, so on a touch device they either never open or open
   and trap. The drawer replaces them wholesale with a second screen that
   slides in under the fixed header.

   Two panes ride one track: the root section list, and whichever section has
   been opened. Choosing a section slides the track left rather than opening a
   nested popover, and the breadcrumb at the top names where you are.

   Keep the drawer a SIBLING of <header>, never nested inside it: header.nav
   has backdrop-filter, which makes it a containing block for position:fixed
   descendants, so a fixed drawer nested inside resolves top/bottom against the
   64px header box and collapses to zero height. */

.nav-toggle, .nav-drawer { display: none; }

@media (max-width: 820px) {
  .nav-right { display: none; }

  .nav-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    /* 44px square: the touch-target floor, and it keeps the icon optically
       centred against the wordmark on the other rail */
    width: 44px; height: 44px; margin-right: -0.5rem;
    color: var(--text);
    -webkit-tap-highlight-color: transparent;
  }
  .nav-toggle svg { width: 22px; height: 22px; }
  .nav-toggle .nav-toggle-x { display: none; }
  .nav-toggle.open .nav-toggle-bars { display: none; }
  .nav-toggle.open .nav-toggle-x { display: block; }

  body.nav-drawer-open { overflow: hidden; }

  .nav-drawer {
    display: flex; flex-direction: column;
    position: fixed; top: var(--band); left: 0; right: 0; bottom: 0;
    z-index: 60;
    background: var(--bg);
    border-top: 1px solid var(--hairline);
  }
  .nav-drawer[hidden] { display: none; }

  .nav-drawer-crumb {
    display: flex; align-items: center; gap: 0.15rem; flex: 0 0 auto;
    padding: 0.9rem var(--gutter);
    border-bottom: 1px solid var(--hairline);
  }
  .nav-drawer-back {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; margin-left: -0.75rem;
    color: var(--muted);
  }
  .nav-drawer-back[hidden] { display: none; }
  .nav-drawer-back svg { width: 20px; height: 20px; }
  .nav-drawer-trail {
    display: flex; align-items: center; gap: 0.4rem;
    font-family: var(--font-heading);
  font-weight: 300; font-size: 0.95rem;
    letter-spacing: 0.096em; text-transform: uppercase;
    color: var(--muted);
  }
  .nav-drawer-crumb-sep { color: var(--dim); }
  .nav-drawer-crumb-leaf { color: var(--text); }

  .nav-drawer-track {
    display: flex; width: 200%; flex: 1 1 auto; min-height: 0;
    transition: transform var(--t-slow) var(--ease);
  }
  .nav-drawer.at-sub .nav-drawer-track { transform: translateX(-50%); }
  .nav-drawer-page {
    width: 50%; flex: 0 0 50%; min-height: 0;
    overflow-y: auto; -webkit-overflow-scrolling: touch;
    padding: 1.4rem var(--gutter) 3rem;
  }

  /* A drawer row is a full-width 56px target with the label left and the
     value/chevron right — the phone idiom, not a shrunken desktop menu. */
  .nav-drawer-row {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    width: 100%; min-height: 56px; padding: 0.9rem 0;
    border-bottom: 1px solid var(--hairline);
    text-align: left;
    -webkit-tap-highlight-color: transparent;
  }
  .nav-drawer-row:active { background: var(--panel-fill); }
  .nav-drawer-row-label {
    font-family: var(--font-heading); font-weight: 300; font-size: 1.33rem;
    letter-spacing: -0.01em; color: var(--text);
  }
  .nav-drawer-row-value { font-family: var(--font-mono); font-size: 0.96rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }
  .nav-drawer-chev { width: 18px; height: 18px; color: var(--dim); flex-shrink: 0; }

  .nav-drawer-group + .nav-drawer-group { margin-top: 1.6rem; }
  .nav-drawer-group-head {
    display: flex; align-items: baseline; gap: 0.6rem;
    padding-bottom: 0.6rem; border-bottom: 1px solid var(--hairline);
    font-family: var(--font-heading);
  font-weight: 300; font-size: 0.94rem;
    letter-spacing: 0.108em; text-transform: uppercase; color: var(--muted);
  }
  .nav-drawer-group-idx { color: var(--dim); }
  .nav-drawer-item {
    display: block; min-height: 52px; padding: 0.85rem 0;
    border-bottom: 1px solid var(--hairline);
    font-family: var(--font-heading); font-weight: 300; font-size: 1.24rem;
    color: var(--body);
  }
  .nav-drawer-item:active { color: var(--text); }
}


/* ══ 13 · CARDS ═══════════════════════════════════════════════════════════ */
/* Same 1px hairline as the frame. An empty media area gets a diagonal hatch
   rather than a flat fill — it reads as "space reserved" instead of
   "nothing here". */

.card {
  border: 1px solid var(--hairline);
  background: var(--panel-fill);
  overflow: hidden;
  transition: border-color 0.3s, background 0.3s;
}
.card.interactive { cursor: pointer; text-align: left; }
.card.interactive:hover,
.card.is-active {
  border-color: rgba(var(--cyan-rgb), 0.45);
  background: var(--panel-fill-hover);
}
/* transient cue when a card is reached from a menu — softer than hover, so it
   reads as a hint rather than a selection */
.card.is-nav-target {
  border-color: rgba(var(--cyan-rgb), 0.45);
  box-shadow: 0 0 0 1px rgba(var(--cyan-rgb), 0.16), 0 0 22px rgba(var(--cyan-rgb), 0.07);
  transition: border-color 0.45s ease, box-shadow 0.45s ease;
}
.card-media {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  padding: 0 1rem;
  text-align: center;
  border-bottom: 1px solid var(--hairline);
  background: repeating-linear-gradient(-45deg, var(--hairline-faint) 0 1px, transparent 1px 12px);
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 0.89rem;
  letter-spacing: 0.108em;
  text-transform: uppercase;
  color: var(--dim);
}
.card-body { display: flex; flex-direction: column; gap: 0.55rem; padding: clamp(0.9rem, 2vh, 1.3rem) clamp(0.9rem, 1.6vw, 1.3rem); min-height: 0; }
.card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.8rem; }
.card-name {
  display: block;
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: clamp(1.24rem, 1.68vw, 1.45rem);
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--text);
  text-wrap: balance;
}
.card-tag {
  display: block;
  flex-shrink: 0;
  white-space: nowrap;
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 0.91rem;
  letter-spacing: 0.06em;
  color: var(--cyan);
  opacity: 0.75;
}
.card-blurb { display: block; font-size: 1.08rem; line-height: 1.55; color: var(--ink-2); }
.card-foot { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.8rem clamp(0.9rem, 1.6vw, 1.3rem); border-top: 1px solid var(--hairline); }

/* Card grid — one rule, three columns down to two down to one. */
.card-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(1rem, 2vw, 1.8rem); }
.card-grid.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.card-grid.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* Master–detail list. The classic two-pane pattern in the frame's idiom:
   one outer border, one vertical rule, hairlines between rows. */
.svc-grid { display: grid; grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr); border: 1px solid var(--hairline); }
.svc-list { border-right: 1px solid var(--hairline); }
.svc-item {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  width: 100%; padding: 1.15rem 1.4rem;
  border-bottom: 1px solid var(--hairline);
  text-align: left; cursor: pointer;
  transition: background var(--t-fast);
}
.svc-item:last-child { border-bottom: none; }
.svc-item:hover, .svc-item.active { background: var(--panel); }
.si-left { display: flex; align-items: baseline; gap: 0.9rem; min-width: 0; }
.si-idx { flex-shrink: 0; font-family: var(--font-mono); font-size: 0.9rem; letter-spacing: 0.12em; color: var(--dim); }
.si-name { font-family: var(--font-heading); font-weight: 300; font-size: 1.21rem; color: var(--body); }
.svc-item.active .si-name { color: var(--text); }
.si-arrow { flex-shrink: 0; font-family: var(--font-mono); color: var(--dim); opacity: 0; transition: opacity var(--t-fast), color var(--t-fast); }
.svc-item:hover .si-arrow, .svc-item.active .si-arrow { opacity: 1; color: var(--cyan); }
.svc-detail { padding: 1.9rem; min-width: 0; }

/* Key/value rows — rules BETWEEN rows, never a box around them. */
.sd-rows { border-top: 1px solid var(--hairline); }
.sd-row { display: grid; grid-template-columns: 7rem minmax(0, 1fr); gap: 1rem; padding: 0.9rem 0; border-bottom: 1px solid var(--hairline); }
.sd-row .k { padding-top: 0.2rem; font-family: var(--font-heading);
  font-weight: 300; font-size: 0.9rem; letter-spacing: 0.084em; text-transform: uppercase; color: var(--dim); }
.sd-row .v { color: var(--body); font-size: 1.13rem; line-height: 1.55; }
.sd-row.notfor .k { color: var(--amber); }
.sd-row.notfor .v { color: var(--muted); }


/* ══ 14 · TABS, TICKS, PILLS ══════════════════════════════════════════════ */

/* Tick tabs — the site's own: a 3px bar, no label, no box. Used where the
   sections are already titled elsewhere on the screen. */
.sv7-ticks { display: flex; gap: 0.55rem; padding-top: 0.4rem; }
.sv7-tick { display: inline-flex; align-items: center; padding: 0.5rem 0; width: clamp(2.6rem, 4vw, 3.8rem); cursor: pointer; }
.sv7-tick::before { content: ""; width: 100%; height: 3px; background: var(--hairline); transition: background 0.3s; }
.sv7-tick.active::before { background: var(--cyan); }
.sv7-tick:hover::before { background: var(--muted); }
.sv7-tick.active:hover::before { background: var(--cyan); }

/* Labelled tabs — an underline on the active tab, hairline beneath the set. */
/* Tabs wrap rather than clip: at the larger label size a four-tab row no
   longer fits a phone on one line. */
.tabs { display: flex; flex-wrap: wrap; gap: 0 1.6rem; border-bottom: 1px solid var(--hairline); }
.tab {
  position: relative;
  padding: 0.7rem 0;
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 1.02rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  transition: color var(--t-fast);
}
.tab:hover { color: var(--text); }
.tab.active { color: var(--cyan); }
.tab.active::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px; background: var(--cyan); }

/* Segmented control — one hairline box, dividers between, no radius. */
.seg { display: inline-flex; border: 1px solid var(--hairline); }
.seg > * {
  padding: 0.45rem 0.85rem;
  border-left: 1px solid var(--hairline);
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 0.91rem;
  letter-spacing: 0.084em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  transition: color var(--t-fast), background var(--t-fast);
}
.seg > *:first-child { border-left: none; }
.seg > *:hover { color: var(--text); background: var(--panel-fill); }
.seg > .active { color: var(--bg); background: var(--cyan); }
.seg > .active:hover { background: var(--cyan-bright); color: var(--bg); }

/* Pill — a filter chip. Hairline at rest, tinted when on. */
.pill {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.34rem 0.72rem;
  border: 1px solid var(--hairline);
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 0.91rem;
  letter-spacing: 0.072em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  transition: color var(--t-fast), border-color var(--t-fast), background var(--t-fast);
}
.pill:hover { color: var(--text); border-color: var(--muted); }
.pill.active { color: var(--cyan); border-color: rgba(var(--cyan-rgb), 0.5); background: rgba(var(--cyan-rgb), 0.08); }
.pill .x { color: var(--dim); }
.pill:hover .x { color: var(--danger); }


/* ══ 15 · FORMS ═══════════════════════════════════════════════════════════ */
/* Inputs are a recessed field with a hairline border. Focus does NOT draw a
   browser ring — it lifts the border to the accent and the caret is already
   cyan, which is enough and stays inside the 1px rule. */

.field { display: flex; flex-direction: column; gap: 0.4rem; min-width: 0; }
.field > label {
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 0.9rem;
  letter-spacing: 0.096em;
  text-transform: uppercase;
  color: var(--muted);
}
.field-hint { font-size: 1.02rem; color: var(--dim); line-height: 1.5; }
.field-error { font-size: 1.02rem; color: var(--danger); line-height: 1.5; }

.msg-input, .msg-textarea, .msg-select {
  width: 100%;
  min-width: 0;
  font-family: var(--font-heading);
  font-size: 1.16rem;
  line-height: 1.5;
  color: var(--text);
  background: color-mix(in srgb, var(--bg) 35%, transparent);
  border: 1px solid var(--hairline);
  padding: 0.55rem 0.8rem;
  caret-color: var(--cyan);
  transition: border-color var(--t-fast);
}
.msg-textarea { min-height: 8rem; resize: vertical; line-height: 1.55; }
.msg-input::placeholder, .msg-textarea::placeholder { color: var(--muted); }
.msg-input:focus, .msg-textarea:focus, .msg-select:focus {
  outline: none;
  border-color: rgba(var(--cyan-rgb), 0.45);
}
.msg-input[aria-invalid="true"], .msg-textarea[aria-invalid="true"] { border-color: rgba(239, 110, 110, 0.6); }
.msg-input:disabled, .msg-textarea:disabled, .msg-select:disabled { opacity: 0.45; cursor: not-allowed; }
/* autofill repaints the field white in webkit — pin it back to the token */
.msg-input:-webkit-autofill,
.msg-input:-webkit-autofill:hover,
.msg-input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--text);
  -webkit-box-shadow: 0 0 0 1000px var(--bg) inset;
  border-color: rgba(var(--cyan-rgb), 0.45) !important;
  transition: background-color 9999s ease-out 0s;
}

.msg-select {
  appearance: none;
  padding-right: 2rem;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 1.05rem) 55%, calc(100% - 0.75rem) 55%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  color: var(--body);
}

/* Checkbox and radio — square, hairline, cyan when on. No native control. */
.check { display: inline-flex; align-items: flex-start; gap: 0.6rem; cursor: pointer; font-size: 1.11rem; color: var(--body); }
.check input { position: absolute; opacity: 0; width: 0; height: 0; }
.check .box {
  flex-shrink: 0;
  width: 15px; height: 15px; margin-top: 0.22rem;
  border: 1px solid var(--hairline);
  display: inline-flex; align-items: center; justify-content: center;
  transition: border-color var(--t-fast), background var(--t-fast);
}
.check .box.round { border-radius: 50%; }
.check:hover .box { border-color: var(--muted); }
.check input:checked + .box { border-color: var(--cyan); background: rgba(var(--cyan-rgb), 0.14); }
.check input:checked + .box::after {
  content: ""; width: 7px; height: 7px; background: var(--cyan);
}
.check .box.round::after { border-radius: 50%; }
.check input:focus-visible + .box { outline: 1px solid var(--cyan); outline-offset: 2px; }

/* Switch — a 1px track, a square knob. Nothing in the system is a capsule. */
.switch { display: inline-flex; align-items: center; gap: 0.6rem; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch .track {
  position: relative; width: 34px; height: 16px;
  border: 1px solid var(--hairline);
  transition: border-color var(--t-fast), background var(--t-fast);
}
.switch .track::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 10px; height: 10px; background: var(--muted);
  transition: transform var(--t-fast), background var(--t-fast);
}
.switch input:checked + .track { border-color: rgba(var(--cyan-rgb), 0.5); background: rgba(var(--cyan-rgb), 0.1); }
.switch input:checked + .track::after { transform: translateX(16px); background: var(--cyan); }
.switch input:focus-visible + .track { outline: 1px solid var(--cyan); outline-offset: 2px; }

/* Range — hairline track, square thumb. */
.range { -webkit-appearance: none; appearance: none; width: 100%; height: 16px; background: transparent; }
.range::-webkit-slider-runnable-track { height: 1px; background: var(--hairline); }
.range::-moz-range-track { height: 1px; background: var(--hairline); }
.range::-webkit-slider-thumb { -webkit-appearance: none; width: 10px; height: 10px; margin-top: -4.5px; background: var(--cyan); border: none; }
.range::-moz-range-thumb { width: 10px; height: 10px; background: var(--cyan); border: none; border-radius: 0; }

/* Toolbar — the strip above a table or chart. Full-bleed hairline, controls
   sitting on it rather than in a box of their own. */
.toolbar {
  display: flex; align-items: center; gap: 0.8rem; flex-wrap: wrap;
  padding: 0.7rem 0;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}
.toolbar .sep { width: 1px; align-self: stretch; background: var(--hairline); }
.search {
  display: flex; align-items: center; gap: 0.5rem;
  border: 1px solid var(--hairline);
  padding: 0.35rem 0.6rem;
  min-width: 12rem;
  transition: border-color var(--t-fast);
}
.search:focus-within { border-color: rgba(var(--cyan-rgb), 0.45); }
.search svg { width: 0.9rem; height: 0.9rem; color: var(--dim); flex-shrink: 0; }
.search input { flex: 1; min-width: 0; border: none; background: none; font-size: 1.08rem; color: var(--text); }
.search input:focus { outline: none; }
.search input::placeholder { color: var(--dim); }


/* ══ 16 · DIALOGS + OVERLAYS ══════════════════════════════════════════════ */
/* A dialog is a panel of the frame that happens to float: same hairline, same
   no-radius, rules BETWEEN its sections rather than boxes around them. */

.svc7-overlay { position: fixed; inset: 0; z-index: 80; display: flex; align-items: center; justify-content: center; padding: clamp(1rem, 4vh, 3rem) clamp(1rem, 4vw, 3rem); }
.svc7-overlay[hidden] { display: none; }
.svc7-overlay-dim { position: absolute; inset: 0; background: rgba(2, 6, 23, 0.72); animation: dimIn var(--t) ease both; }
html[data-theme='light'] .svc7-overlay-dim { background: rgba(28, 25, 23, 0.45); }
.svc7-dialog {
  position: relative;
  width: min(56rem, 95vw);
  max-height: 92vh;
  display: flex; flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--hairline);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
  animation: dlgIn 0.32s var(--ease) both;
}
.svc7-dialog.sm { width: min(30rem, 95vw); }
.svc7-dialog.lg { width: min(72rem, 95vw); }
.svc7-dialog-head {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  flex: 0 0 auto;
  padding: 1.1rem 1.4rem;
  border-bottom: 1px solid var(--hairline);
}
.svc7-dialog-title { font-family: var(--font-heading); font-weight: 300; font-size: 1.41rem; letter-spacing: -0.01em; color: var(--text); }
.svc7-dialog-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 1.4rem; }
.svc7-dialog-foot { flex: 0 0 auto; display: flex; align-items: center; justify-content: flex-end; gap: 0.6rem; padding: 1rem 1.4rem; border-top: 1px solid var(--hairline); }
.svc7-dialog-close { display: inline-flex; align-items: center; justify-content: center; width: 2rem; height: 2rem; color: var(--muted); transition: color var(--t-fast); }
.svc7-dialog-close:hover { color: var(--text); }
.svc7-dialog-close svg { width: 1.05rem; height: 1.05rem; }

/* Spec block — the six-panel layout the site uses for a service detail. Rules
   run between the cells, never around them; the dialog supplies the outer edge. */
.svc7-spec-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); border-top: 1px solid var(--hairline); }
.svc7-spec-block { padding: 1.1rem 1.7rem 1.15rem 0; }
.svc7-spec-row > .svc7-spec-block + .svc7-spec-block { border-left: 1px solid var(--hairline); padding-left: 1.9rem; }
.svc7-spec-wide { grid-column: 1 / -1; padding-right: 0; }
.svc7-spec-block h4 {
  margin-bottom: 0.6rem;
  font-family: var(--font-heading); font-weight: 400; font-size: 0.94rem;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--muted);
}
.svc7-spec-block p { font-size: 1.18rem; line-height: 1.55; color: var(--body); }


/* ══ 17 · TABLES ══════════════════════════════════════════════════════════ */
/* The frame's rules applied to data. No zebra striping, no vertical rules, no
   box around the table — a bright hairline under the head, a faint one under
   each row, and that is the whole grid. Everything numeric is tabular and
   right-aligned so the eye can compare down a column.

   ALWAYS wrap a table in .table-wrap. It is the only thing standing between a
   wide table and a horizontally scrolling page. */

.table-wrap { overflow-x: auto; margin: 1rem 0; -webkit-overflow-scrolling: touch; }
.table-wrap > table { margin: 0; }
/* a fade at the right edge cues that there is more table off-screen */
.table-wrap.fade { position: relative; }

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 1.07rem;
  margin: 1rem 0;
  font-feature-settings: var(--num-tabular);
}
thead th {
  padding: 0.7rem 0.6rem;
  text-align: left;
  font-family: var(--font-sans);
  font-weight: 300;
  font-size: 0.93rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--rule-bright);
  white-space: nowrap;
  background: var(--bg);
}
/* sticky head — needs a solid background, which is why th is not transparent */
.table-sticky thead th { position: sticky; top: 0; z-index: 2; }
tbody td {
  padding: 0.55rem 0.6rem;
  border-bottom: 1px solid var(--row-divider);
  color: var(--body);
  vertical-align: top;
}
tbody td strong { color: var(--text); font-weight: 400; }
tbody tr:hover td { background: rgba(var(--cyan-rgb), 0.04); }
tbody tr.is-selected td { background: rgba(var(--cyan-rgb), 0.08); }
tfoot td {
  padding: 0.7rem 0.6rem;
  border-top: 1px solid var(--rule-bright);
  color: var(--text);
  font-family: var(--font-heading);
  font-weight: 400;
}
table .text-right, table th.text-right { text-align: right; }
td.mono, th.mono { font-family: var(--font-mono); font-size: 0.97rem; letter-spacing: 0.06em; color: var(--dim); text-transform: none; }
/* a compact variant for dense operational grids */
.table-dense thead th { padding: 0.5rem 0.5rem; font-size: 0.9rem; }
.table-dense tbody td { padding: 0.34rem 0.5rem; font-size: 1.04rem; }

/* sortable head */
th.sortable { cursor: pointer; user-select: none; transition: color var(--t-fast); }
th.sortable:hover { color: var(--text); }
th.sortable::after { content: "↕"; margin-left: 0.4rem; opacity: 0.35; font-size: 0.92em; }
th.sortable.asc::after { content: "↑"; opacity: 1; color: var(--cyan); }
th.sortable.desc::after { content: "↓"; opacity: 1; color: var(--cyan); }

/* In-cell bar — a magnitude read at a glance without leaving the row. Keep it
   to one column; two competing bars in one table is a chart pretending to be
   a table. */
.cell-bar { display: block; position: relative; height: 4px; min-width: 70px; background: var(--panel-2); }
.cell-bar > span { position: absolute; inset: 0 auto 0 0; background: var(--cyan); }
.cell-bar.danger > span { background: var(--danger); }
.cell-bar.warning > span { background: var(--warning); }

/* Diverging bar — variance either side of a centre axis (actual vs. plan,
   lead-time drift). Under and over get different colours, not different sides
   of one colour. */
.lt-bar { display: inline-block; position: relative; height: 14px; min-width: 110px; }
.lt-bar .axis { position: absolute; left: 50%; top: 0; bottom: 0; width: 1px; background: var(--rule-bright); }
.lt-bar .bar { position: absolute; top: 3px; height: 8px; opacity: 0.9; }
.lt-bar .bar.under { right: 50%; background: var(--danger); }
.lt-bar .bar.over { left: 50%; background: var(--cyan); }

/* Sparkline in a cell — 1px stroke, no axis, no label. */
.spark { display: block; width: 84px; height: 20px; overflow: visible; }
.spark path { fill: none; stroke: var(--cyan); stroke-width: 1; vector-effect: non-scaling-stroke; }
.spark path.area { fill: rgba(var(--cyan-rgb), 0.12); stroke: none; }

/* Pagination */
.pager { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.6rem 1rem; padding: 0.8rem 0; border-top: 1px solid var(--hairline); }
.pager-count { font-family: var(--font-heading);
  font-weight: 300; font-size: 0.94rem; letter-spacing: 0.084em; text-transform: uppercase; color: var(--dim); }
.pager-nav { display: flex; gap: 0.35rem; }
.pager-nav button {
  min-width: 2rem; height: 2rem; padding: 0 0.5rem;
  border: 1px solid var(--hairline);
  font-family: var(--font-mono); font-size: 0.95rem; color: var(--muted);
  transition: color var(--t-fast), border-color var(--t-fast);
}
.pager-nav button:hover:not(:disabled) { color: var(--text); border-color: var(--muted); }
.pager-nav button.active { color: var(--cyan); border-color: rgba(var(--cyan-rgb), 0.5); background: rgba(var(--cyan-rgb), 0.08); }
.pager-nav button:disabled { opacity: 0.3; cursor: not-allowed; }


/* ══ 18 · DATA DISPLAY ════════════════════════════════════════════════════ */

/* Stat row — figures in a hairline grid. The grid comes from borders on the
   cells, not from a wrapper, so it reflows to any column count without the
   rules going wrong. auto-fit + minmax means it drops from four across to two
   to one by itself. */
.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  margin: 1.25rem 0;
  border-top: 1px solid var(--hairline);
}
.stat-box {
  padding: 1rem 1.25rem;
  border-right: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  text-align: left;
}
.stat-box:last-child { border-right: none; }
.stat-box .number {
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 1.59rem;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--text);
  font-feature-settings: var(--num-tabular);
}
.stat-box .number.danger { color: var(--danger); }
.stat-box .number.warning { color: var(--warning); }
.stat-box .number.ok { color: var(--ok); }
.stat-box .number .unit { font-size: 0.54em; color: var(--dim); margin-left: 0.3em; letter-spacing: 0.16em; text-transform: uppercase; }
.stat-box .desc {
  margin-top: 0.55rem;
  font-size: 0.93rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
}
.stat-box .delta { margin-top: 0.4rem; font-family: var(--font-mono); font-size: 0.94rem; letter-spacing: 0.08em; }
.stat-box .delta.up { color: var(--ok); }
.stat-box .delta.down { color: var(--danger); }

/* Score box — a number with a 3px track under it. The track is the whole
   chart; it needs no axis because the label says what full means. */
.score-box { padding: 1.1rem 1.25rem; border-right: 1px solid var(--hairline); }
.score-box:last-child { border-right: none; }
.score-num { display: block; font-family: var(--font-heading); font-weight: 300; font-size: 2.1rem; line-height: 1; letter-spacing: -0.02em; color: var(--text); font-feature-settings: var(--num-tabular); }
.score-label { display: block; margin-top: 0.5rem; font-size: 0.93rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--dim); }
.score-track { display: block; position: relative; height: 3px; margin-top: 0.9rem; background: var(--panel-2); }
.score-track > span { position: absolute; inset: 0 auto 0 0; background: var(--cyan); }
.score-box.concern .score-num { color: var(--danger); }
.score-box.concern .score-track > span { background: var(--danger); }
.score-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); border-top: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline); }

/* Meter — a labelled horizontal bar in a list. Value right-aligned on the
   same line as the label; the bar sits under both. */
.meter { display: block; padding: 0.7rem 0; border-bottom: 1px solid var(--row-divider); }
.meter-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.meter-name { display: block; font-size: 1.1rem; color: var(--body); }
.meter-value { display: block; font-family: var(--font-mono); font-size: 1rem; color: var(--text); font-feature-settings: var(--num-tabular); }
.meter-track { display: block; position: relative; height: 4px; margin-top: 0.5rem; background: var(--panel-2); }
.meter-track > span { position: absolute; inset: 0 auto 0 0; background: var(--cyan); transition: width var(--t-slow) var(--ease); }
.meter.danger .meter-track > span { background: var(--danger); }
.meter.warning .meter-track > span { background: var(--warning); }

/* Key/value definition grid — for a "specification" block. */
.kv { border-top: 1px solid var(--hairline); }
.kv-row { display: grid; grid-template-columns: 9rem minmax(0, 1fr); gap: 1.25rem; padding: 0.85rem 0; border-bottom: 1px solid var(--hairline); }
.kv-row dt { font-family: var(--font-heading);
  font-weight: 300; font-size: 0.94rem; letter-spacing: 0.084em; text-transform: uppercase; color: var(--cyan); padding-top: 0.15rem; }
.kv-row dd { margin: 0; line-height: 1.7; color: var(--body); }

/* Badge — status, one word, no fill unless it must be seen from across a room. */
.badge {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.16rem 0.5rem;
  border: 1px solid currentColor;
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 0.89rem;
  letter-spacing: 0.084em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}
.badge.accent { color: var(--cyan); }
.badge.ok { color: var(--ok); }
.badge.warning { color: var(--warning); }
.badge.danger { color: var(--danger); }
.badge.solid { color: var(--bg); background: var(--cyan); border-color: var(--cyan); }
.badge .dot { width: 5px; height: 5px; background: currentColor; }

/* Tier chip — borderless, colour-coded, for a dense table column. */
.tier { font-size: 0.93rem; letter-spacing: 0.12em; text-transform: uppercase; white-space: nowrap; }
.tier.a { color: var(--cyan); }
.tier.b { color: var(--body); }
.tier.c { color: var(--warning); }

/* Legend — reuse it for charts, maps, anything keyed by colour. */
.chart-legend { display: flex; flex-wrap: wrap; gap: 1.2rem; }
.chart-legend span {
  display: inline-flex; align-items: center; gap: 0.45rem;
  font-family: var(--font-heading);
  font-weight: 300; font-size: 0.92rem;
  letter-spacing: 0.072em; text-transform: uppercase; color: var(--muted);
}
.chart-legend .key { width: 14px; height: 0; border-top: 1.5px solid var(--cyan); }
.chart-legend .key.ghost { border-top: 1px dashed var(--dim); }
.chart-legend .key.swatch { width: 10px; height: 10px; border: none; background: var(--cyan); }


/* ══ 19 · CHART CONTAINERS ════════════════════════════════════════════════ */
/* The container is the design; the plot inside it is left alone.
   Rules that keep a chart in the system:
     · The panel is a 1px hairline with a TRANSPARENT fill — the page grid
       shows through, which is what makes a chart read as part of the drawing
       rather than as a screenshot pasted onto it.
     · The title is mono chrome, not a heading. The chart is not a section.
     · One accent. A second series is white/body at reduced opacity, a third is
       amber. Never a rainbow — see --series-* below.
     · Gridlines are --hairline-faint at most, and horizontal only.
     · Axis text is --muted at 0.64rem, tracked. Same as every other label.
     · Give the plot a fixed aspect-ratio box so it cannot reflow the page
       while a library measures it. */

.viz-panel {
  border: 1px solid var(--hairline);
  background: transparent;
  padding: clamp(1rem, 2vw, 1.5rem);
  margin: 1.5rem 0;
}
.viz-panel.filled { background: var(--panel-fill); }
.viz-title {
  display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; flex-wrap: wrap;
  margin-bottom: 1.25rem;
  font-family: var(--font-sans);
  font-weight: 300;
  font-size: 0.93rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
}
.viz-title .tag { color: var(--cyan); letter-spacing: 0.1em; }
.viz { width: 100%; position: relative; }
.viz svg, .viz canvas { display: block; width: 100%; }
/* Reserve the height before the plot exists. 16/9 for a wide time series,
   4/3 for a distribution, 1/1 for a matrix. The plot is then absolutely
   positioned to fill that box, so a library that measures its container gets
   a stable number on the first pass instead of measuring a zero-height div.
   Draw the SVG's own viewBox at the SAME ratio — stretching it to fit
   distorts the stroke weights, which is the one thing the frame cannot
   tolerate. */
.viz.ratio-wide { aspect-ratio: 16 / 9; }
.viz.ratio-std  { aspect-ratio: 4 / 3; }
.viz.ratio-sq   { aspect-ratio: 1 / 1; }
.viz.ratio-wide > svg,
.viz.ratio-std > svg,
.viz.ratio-sq > svg,
.viz.ratio-wide > canvas,
.viz.ratio-std > canvas,
.viz.ratio-sq > canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.viz-note { margin-top: 1rem; font-size: 1rem; line-height: 1.5; color: var(--dim); }
.viz-foot { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-top: 1rem; padding-top: 0.8rem; border-top: 1px solid var(--hairline); }

/* Series palette. Ordered — take them in sequence, do not pick by taste.
   Beyond four series a line chart is the wrong chart. */
:root {
  --series-1: #67c7d4;   /* the accent */
  --series-2: #cbd5e1;   /* body — reads as "the other one", not a rival hue */
  --series-3: #d9a441;   /* amber */
  --series-4: #7c8ba1;   /* muted */
  --series-ghost: #64748b;  /* forecast / comparison, always dashed */
  --grid-line: var(--hairline-faint);
  --axis-line: var(--hairline);
  --axis-text: var(--muted);
}
html[data-theme='light'] {
  --series-1: #0e7490;
  --series-2: #44403c;
  --series-3: #b45309;
  --series-4: #78716c;
  --series-ghost: #a8a29e;
}

/* Axis + gridline classes for hand-drawn SVG. A charting library should be
   configured to the same values — read them off :root at runtime. */
.viz .grid line { stroke: var(--grid-line); stroke-width: 1; shape-rendering: crispEdges; }
.viz .axis line, .viz .axis path { stroke: var(--axis-line); stroke-width: 1; fill: none; shape-rendering: crispEdges; }
.viz .axis text, .viz text.tick {
  font-family: var(--font-mono);
  font-size: 0.92rem;
  letter-spacing: 0.1em;
  fill: var(--axis-text);
  text-transform: uppercase;
}
.viz .series-1 { stroke: var(--series-1); fill: none; stroke-width: 1.5; }
.viz .series-2 { stroke: var(--series-2); fill: none; stroke-width: 1.5; }
.viz .series-3 { stroke: var(--series-3); fill: none; stroke-width: 1.5; }
.viz .series-4 { stroke: var(--series-4); fill: none; stroke-width: 1.5; }
.viz .ghost { stroke: var(--series-ghost); fill: none; stroke-width: 1; stroke-dasharray: 3 3; }
.viz .fill-1 { fill: rgba(var(--cyan-rgb), 0.12); stroke: none; }
.viz .bar-1 { fill: var(--series-1); }
.viz .bar-2 { fill: var(--series-2); }
.viz .bar-danger { fill: var(--danger); }
.viz .marker { fill: var(--series-1); }

/* HUD overlay — figure number, legend, timestamp floating over a full-bleed
   plot. Pointer-events off so it never eats a hover on the chart. */
.hud { position: absolute; z-index: 3; pointer-events: none; }
.hud .mono { font-size: 1rem; letter-spacing: 0.16em; color: var(--muted); }
.hud-fig { top: 1rem; right: 1rem; display: flex; flex-direction: column; align-items: flex-end; gap: 0.55rem; text-align: right; }
.hud-foot { left: 0; right: 0; bottom: 0.6rem; display: flex; justify-content: flex-end; align-items: center; }
.hud-foot .mono { font-size: 0.89rem; letter-spacing: 0.12em; color: var(--dim); }
.hud-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--cyan); box-shadow: 0 0 0 3px rgba(var(--cyan-rgb), 0.14); }
/* scrim for a chart bleeding behind text — fades the plot out under the copy */
.chart-scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(90deg, var(--bg) 0%, color-mix(in srgb, var(--bg) 55%, transparent) 34%, transparent 64%);
}

/* Tooltip — same hairline panel as everything else, mono figures. */
.viz-tip {
  position: absolute; z-index: 5; pointer-events: none;
  min-width: 9rem; padding: 0.55rem 0.7rem;
  background: var(--panel); border: 1px solid var(--hairline);
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.35);
}
.viz-tip .t-label { font-family: var(--font-heading);
  font-weight: 300; font-size: 0.89rem; letter-spacing: 0.096em; text-transform: uppercase; color: var(--dim); }
.viz-tip .t-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-top: 0.35rem; font-size: 1.05rem; color: var(--body); }
.viz-tip .t-row b { color: var(--text); font-weight: 400; font-feature-settings: var(--num-tabular); }

/* Chart grid — several panels sharing one hairline lattice. */
.viz-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 0; border-top: 1px solid var(--hairline); border-left: 1px solid var(--hairline); }
.viz-grid > .viz-panel { margin: 0; border-top: none; border-left: none; }


/* ══ 20 · CALLOUTS + STATUS ═══════════════════════════════════════════════ */

/* Callout — a 2px left rule and nothing else. No tinted box: the colour of the
   rule is the whole signal. */
.callout {
  margin: 1.25rem 0;
  padding: 0.5rem 0 0.5rem 1.25rem;
  border-left: 2px solid var(--rule-bright);
  font-size: 1.16rem;
  line-height: 1.55;
  color: var(--body);
}
.callout strong { color: var(--text); font-weight: 400; }
.callout.info { border-left-color: var(--cyan); }
.callout.warning { border-left-color: var(--warning); }
.callout.danger { border-left-color: var(--danger); }
.callout.ok { border-left-color: var(--ok); }

/* Empty state — hatched, like an empty card media area. Says "reserved",
   not "broken". */
.empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.7rem;
  padding: clamp(2rem, 8vh, 4rem) 1.5rem;
  border: 1px solid var(--hairline);
  background: repeating-linear-gradient(-45deg, var(--hairline-faint) 0 1px, transparent 1px 12px);
  text-align: center;
}
.empty .empty-title { display: block; font-family: var(--font-heading); font-weight: 300; font-size: 1.33rem; color: var(--text); }
.empty p { font-size: 1.1rem; color: var(--muted); max-width: 36ch; }

/* Skeleton — a hairline block with a sweep. Never a rounded grey pill. */
.skeleton { position: relative; overflow: hidden; background: var(--panel-2); height: 1rem; }
.skeleton::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(var(--cyan-rgb), 0.08), transparent);
  animation: sweep 1.4s linear infinite;
}
@keyframes sweep { from { transform: translateX(-100%); } to { transform: translateX(100%); } }

/* Inline loading tick */
.loading { display: inline-flex; align-items: center; gap: 0.5rem; font-family: var(--font-heading);
  font-weight: 300; font-size: 0.92rem; letter-spacing: 0.096em; text-transform: uppercase; color: var(--muted); }
.loading .tick { width: 6px; height: 6px; background: var(--cyan); animation: pulse 1.1s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: 0.25; } 50% { opacity: 1; } }

/* Toast — bottom-right, docked to the frame rail on wide screens. */
.toast-stack { position: fixed; right: max(var(--gutter), calc(50vw - var(--maxw) / 2 + var(--gutter))); bottom: calc(var(--band) + 1rem); z-index: 90; display: flex; flex-direction: column; gap: 0.5rem; }
.toast {
  display: flex; align-items: flex-start; gap: 0.7rem;
  min-width: 16rem; max-width: 24rem;
  padding: 0.8rem 1rem;
  background: var(--panel); border: 1px solid var(--hairline);
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.35);
  animation: toastIn var(--t) var(--ease) both;
}
.toast .bar { width: 2px; align-self: stretch; background: var(--cyan); flex-shrink: 0; }
.toast.danger .bar { background: var(--danger); }
.toast.ok .bar { background: var(--ok); }
.toast-title { display: block; font-size: 1.1rem; color: var(--text); }
.toast-body { display: block; margin-top: 0.2rem; font-size: 1.04rem; color: var(--muted); line-height: 1.5; }

/* Progress bar for a running job */
.progress { height: 2px; background: var(--panel-2); overflow: hidden; }
.progress > span { display: block; height: 100%; background: var(--cyan); transition: width var(--t-slow) var(--ease); }
.progress.indeterminate > span { width: 40%; animation: indet 1.2s ease-in-out infinite; }
@keyframes indet { 0% { transform: translateX(-100%); } 100% { transform: translateX(250%); } }


/* ══ 21 · LISTS ═══════════════════════════════════════════════════════════ */
/* The system's bullet is a 1px dash, not a disc. Cyan when the list is a set
   of deliverables; muted when it is neutral detail. */

.dash-list { list-style: none; display: flex; flex-direction: column; gap: 0.6rem; }
.dash-list li { position: relative; padding-left: 1.5rem; font-size: 1.13rem; line-height: 1.55; color: var(--body); }
.dash-list li::before { content: ""; position: absolute; left: 0; top: 0.7em; width: 0.75rem; height: 1px; background: var(--dim); }
.dash-list li strong { color: var(--text); font-weight: 400; }
.dash-list.accent li::before { background: var(--cyan); opacity: 0.8; }

/* Numbered steps — mono index in a fixed gutter, hairline between. */
.step-list { list-style: none; border-top: 1px solid var(--hairline); }
.step-list li { display: grid; grid-template-columns: 3rem minmax(0, 1fr); gap: 1.25rem; padding: 1rem 0; border-bottom: 1px solid var(--hairline); }
.step-list li::before { content: counter(step, decimal-leading-zero); counter-increment: step; font-family: var(--font-mono); font-size: 0.96rem; letter-spacing: 0.12em; color: var(--cyan); padding-top: 0.25rem; }
.step-list { counter-reset: step; }


/* ══ 22 · ICONS ═══════════════════════════════════════════════════════════ */
/* One family only: 24×24 viewBox, no fill, currentColor stroke, stroke-width
   1 to 1.5, round caps and joins. The weight is the point — the icons are the
   same hairline as the frame. Never a filled or duotone icon; never a third-
   party set with a heavier stroke mixed in. */

.icon {
  width: 1.05rem; height: 1.05rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex-shrink: 0;
}
.icon.sm { width: 0.85rem; height: 0.85rem; stroke-width: 1.5; }
.icon.lg { width: 1.5rem; height: 1.5rem; stroke-width: 1.2; }
.icon.xl { width: 2.2rem; height: 2.2rem; stroke-width: 1; }
/* the chevrons on the notches are hairline-weight to match the frame */
.icon.hair { stroke-width: 1; }


/* ══ 23 · SCREEN ══════════════════════════════════════════════════════════ */
/* The desktop screen: a viewport-locked page whose content is a row of tiles,
   led by a tab row and paged by the notches. This is the primary desktop
   layout of journeymro_site — /services is built from it — and the alternative
   to a scrolling document. Section 06 supplies the lock (.page-main); this
   section supplies what sits inside it, and section 07 the controls that page
   between screens.

   Ported from journeymro_site with two deliberate departures:
     · colour resolves through tokens instead of the literals the site hard-codes,
       so the light theme needs no second set of rules;
     · type is lifted onto this system's 13.6px floor. The site sets its tile
       blurb at 0.8rem and its labels at 0.58–0.62rem, which is 9–13px and below
       what anything here is allowed to be.
   Class names are otherwise verbatim, so markup moves between the two projects
   without translation. */

/* Head — tabs left, position counter right, baselines aligned. */
.svc7-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
}
.svc7-head-left { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }

/* The one place a figure is set this large. It reads as pagination rather than
   data, which is why it is a dimmed accent and not --text. */
.svc7-counter {
  font-family: var(--font-heading);
  font-weight: 200;
  font-size: clamp(1.5rem, 2.84vw, 2.1rem);
  line-height: 1;
  letter-spacing: -0.02em;
  color: rgba(var(--cyan-rgb), 0.2);
}
.svc7-counter .n { color: rgba(var(--cyan-rgb), 0.45); }

/* NOT .tab. That one is uppercase mono-idiom chrome for switching panels
   within a page; these name the screens themselves, so they are sentence case
   in the heading face at subheading size. Two tab idioms is deliberate. */
.svc7-tabs { display: flex; align-items: center; gap: 1.4rem; min-width: 0; }
.svc7-tab {
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: clamp(1.16rem, 1.86vw, 1.41rem);
  letter-spacing: -0.01em;
  background: none;
  border: none;
  /* reserves the underline so nothing shifts when a tab goes active */
  border-bottom: 2px solid transparent;
  color: var(--muted);
  opacity: 0.5;
  cursor: pointer;
  padding: 0.2rem 0 0.3rem;
  white-space: nowrap;
  transition: opacity var(--t-fast), color var(--t-fast), border-color var(--t-fast);
}
.svc7-tab:hover { opacity: 0.75; }
.svc7-tab.active { opacity: 1; color: var(--cyan); border-bottom-color: var(--cyan); }
.svc7-tab:focus, .svc7-tab:focus-visible { outline: none; }
.svc7-tab-divider { width: 1px; height: 1rem; background: var(--hairline); margin: 0 0.2rem; }

/* Stage — takes what the head and foot leave, and clips the paging slide. */
.svc7-stage {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  margin-top: clamp(1rem, 2.4vh, 1.6rem);
}
/* Panes stack absolutely so the outgoing one stays painted while it slides
   out; display:grid !important on the out-classes is what keeps it visible
   after .active is removed. */
.svc7-grid {
  display: none;
  position: absolute;
  inset: 0;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: 1fr;
  gap: clamp(1rem, 2vw, 1.6rem);
}
.svc7-grid.active { display: grid; }
.svc7-grid-1 { grid-template-columns: minmax(0, 1fr); }
.svc7-grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.svc7-grid.slide-out-left  { display: grid !important; animation: slideOutLeft 0.42s var(--ease) forwards; }
.svc7-grid.slide-out-right { display: grid !important; animation: slideOutRight 0.42s var(--ease) forwards; }
.svc7-grid.slide-in-right  { animation: slideInRight 0.42s var(--ease) both; }
.svc7-grid.slide-in-left   { animation: slideInLeft 0.42s var(--ease) both; }

/* Tile — a visual that takes the free height, a fixed-height caption below. */
.svc7-card {
  border: 1px solid var(--hairline);
  background: color-mix(in srgb, var(--panel) 40%, transparent);
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  cursor: pointer;
  text-align: left;
  padding: 0;
  font-family: var(--font-sans);
  transition: border-color 0.3s, background 0.3s;
}
.svc7-card:hover {
  border-color: rgba(var(--cyan-rgb), 0.45);
  background: color-mix(in srgb, var(--panel) 60%, transparent);
}
.svc7-card:focus, .svc7-card:focus-visible { outline: none; }

/* The hatch is painted in every state. A flat fill reads as an opaque panel;
   the hatch reads as space reserved for something. */
.svc7-card-visual {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  border-bottom: 1px solid var(--hairline);
  background: repeating-linear-gradient(-45deg, var(--hairline-faint) 0 1px, transparent 1px 12px);
  flex: 1 1 0;
  min-height: 0;
}
.svc7-card-visual-label {
  font-family: var(--font-mono);
  font-size: 0.89rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--dim);
  padding: 0 1rem;
  text-align: center;
}
/* open-in-full mark, top right of the visual */
.svc7-card-indicator {
  position: absolute;
  top: 0.6rem;
  right: 0.65rem;
  width: 1.15rem;
  height: 1.15rem;
  color: var(--cyan);
  opacity: 0.7;
  pointer-events: none;
  transition: opacity var(--t-fast);
}
.svc7-card:hover .svc7-card-indicator { opacity: 1; }
.svc7-card-body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.35rem;
  padding: 0.85rem clamp(0.8rem, 1.4vw, 1.1rem);
  flex: 0 0 auto;
  height: 5.8rem;
  overflow: hidden;
}
.svc7-card-name {
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: clamp(1.2rem, 1.68vw, 1.37rem);
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--text);
  text-wrap: balance;
}
/* One line. A blurb that wraps changes the tile's height and breaks the row. */
.svc7-card-blurb {
  font-size: 1.04rem;
  line-height: 1.35;
  color: var(--ink-2);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* Foot — what powers the screen. */
.svc7-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: clamp(1rem, 3vh, 1.8rem);
}
.svc7-powered { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.svc7-powered-label {
  font-family: var(--font-heading);
  font-weight: 400;
  font-size: 0.91rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--dim);
}
.svc7-engine-badge {
  font-family: var(--font-heading);
  font-size: 1.16rem;
  letter-spacing: -0.025em;
  color: var(--text);
  background: none;
  border: 1px solid rgba(var(--cyan-rgb), 0.35);
  padding: 0.3rem 0.65rem;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast);
}
.svc7-engine-badge[hidden] { display: none; }
.svc7-engine-badge:hover { background: rgba(var(--cyan-rgb), 0.1); border-color: var(--cyan); }
.svc7-engine-wordmark { font-weight: 300; display: inline-flex; align-items: baseline; }
.svc7-engine-wordmark-accent { font-weight: 400; color: var(--cyan); letter-spacing: -0.01em; }

/* Below the frame width the lock is released (section 25) and the stage has no
   fixed height to fill, so the panes come out of their absolute stack and the
   tiles go one across. The tabs keep working — only one pane is ever shown —
   so the screen model survives; it just scrolls. */
@media (max-width: 820px) {
  .svc7-stage { position: static; overflow: visible; }
  .svc7-grid { position: static; grid-template-columns: minmax(0, 1fr); }
  /* The blurb is clipped to one line so neighbouring tiles in a row keep the
     same height. One across, there is no neighbour — so let it wrap and give
     the caption the room to do it. */
  .svc7-card-body { height: auto; min-height: 5.8rem; }
  .svc7-card-blurb { white-space: normal; }
  .svc7-card-visual { min-height: 12rem; }
  .svc7-tabs { overflow-x: auto; scrollbar-width: none; }
  .svc7-tabs::-webkit-scrollbar { display: none; }
  .svc7-head { align-items: center; }
}


/* ══ 24 · MOTION ══════════════════════════════════════════════════════════ */
/* Short, eased, and always in service of a change of state. The house curve is
   cubic-bezier(0.22, 0.6, 0.36, 1) — a quick start with a long settle. */

@keyframes fadeUp   { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: translateY(0); } }
@keyframes popIn    { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: translateY(0); } }
@keyframes dlgIn    { from { opacity: 0; transform: translateY(16px) scale(0.985); } to { opacity: 1; transform: translateY(0) scale(1); } }
@keyframes dimIn    { from { opacity: 0; } to { opacity: 1; } }
@keyframes toastIn  { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
@keyframes slideInRight  { from { transform: translateX(100%); }  to { transform: translateX(0); } }
@keyframes slideOutLeft  { from { transform: translateX(0); }     to { transform: translateX(-100%); } }
@keyframes slideInLeft   { from { transform: translateX(-100%); } to { transform: translateX(0); } }
@keyframes slideOutRight { from { transform: translateX(0); }     to { transform: translateX(100%); } }

.anim-fade-up { animation: fadeUp 0.55s var(--ease) both; }
.anim-pop-in  { animation: popIn var(--t) var(--ease) both; }
.anim-delay-1 { animation-delay: 0.08s; }
.anim-delay-2 { animation-delay: 0.16s; }
.anim-delay-3 { animation-delay: 0.24s; }

/* Headline sweep — one accent gradient wiped across the text once on load.
   The only decorative animation in the system; use it on a hero and nowhere
   else. */
.sl-glow {
  background: linear-gradient(90deg, var(--text) 0%, var(--cyan-bright) 18%, var(--cyan) 32%, var(--text) 52%, var(--text) 100%);
  background-size: 250% 100%;
  background-position: 100% 0;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: glowSweep 2s var(--ease) 0.05s forwards;
}
@keyframes glowSweep {
  0%   { background-position: 100% 0; text-shadow: none; }
  40%  { text-shadow: 0 0 18px rgba(var(--cyan-rgb), 0.3); }
  100% { background-position: -20% 0; text-shadow: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .sl-glow { background: none; -webkit-text-fill-color: var(--text); }
}


/* ══ 25 · RESPONSIVE ══════════════════════════════════════════════════════ */
/* Four breakpoints, and each one exists for a structural reason rather than a
   device:
     1320px  the rails stop having margins — the frame fills the viewport
      920px  two-column layouts become one
      820px  the desktop nav is replaced by the drawer (see section 12)
      760px  the notches lose their rail; master–detail unstacks
      560px  small-phone: gutters tighten, stat grids go single-column */

@media (max-width: 920px) {
  .card-grid, .card-grid.cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .contact-grid, .svc7-spec-row { grid-template-columns: minmax(0, 1fr); }
  .svc7-spec-row > .svc7-spec-block + .svc7-spec-block { border-left: none; padding-left: 0; border-top: 1px solid var(--hairline); }
}

@media (max-width: 760px) {
  .card-grid, .card-grid.cols-2, .card-grid.cols-4 { grid-template-columns: minmax(0, 1fr); }
  .svc-grid { grid-template-columns: 1fr; }
  .svc-list { border-right: none; border-bottom: 1px solid var(--hairline); }
  .svc-detail { padding: 1.4rem; }
  .kv-row, .sd-row { grid-template-columns: minmax(0, 1fr); gap: 0.35rem; }
  .wordmark .mark-desc { display: none; }

  /* A viewport-locked page is a desktop idea. On a phone the address bar
     resizes the viewport mid-scroll and 100vh is a lie, so let the page
     scroll normally and drop the lock. */
  .page-main { min-height: 0; max-height: none; overflow: visible; }
  .page-scroll { overflow: visible; }

  /* Tables: keep the horizontal scroll rather than reflowing to cards. A table
     that becomes a stack of labelled pairs stops being comparable, which is
     the only reason it was a table. The wrapper is what makes this safe. */
  .table-wrap { margin-left: calc(var(--gutter) * -1); margin-right: calc(var(--gutter) * -1); padding: 0 var(--gutter); }

  .toast-stack { right: var(--gutter); left: var(--gutter); bottom: 1rem; }
  .toast { max-width: none; }
}

@media (max-width: 560px) {
  :root { --gutter: 1.1rem; }
  .stat-row { grid-template-columns: minmax(0, 1fr); }
  .stat-box { border-right: none; }
  .score-strip { grid-template-columns: minmax(0, 1fr); }
  .score-box { border-right: none; border-bottom: 1px solid var(--hairline); }
  .viz-panel { padding: 0.9rem; }
  .svc7-dialog { width: 100%; max-height: 100%; height: 100%; }
  .svc7-overlay { padding: 0; }
}

/* Touch targets. Anything a finger has to hit is 44px, which is the floor, not
   a target. Pointer-coarse rather than a width query — a small window on a
   desktop is still a mouse. */
@media (pointer: coarse) {
  .btn, .nav-cta, .hero-btn { min-height: 44px; }
  .icon-btn { width: 44px; height: 44px; }
  .tab { padding: 0.9rem 0; }
  .lang-option { padding: 0.85rem 0.72rem; }
  a, button { -webkit-tap-highlight-color: transparent; }
}

/* Print — drop the chrome, keep the rules. */
@media print {
  .grid-bg, .svc7-arrow, header.nav, .nav-drawer, .toast-stack { display: none !important; }
  :root { --bg: #fff; --text: #000; --body: #222; --hairline: #999; }
  body { background: #fff; color: #000; }
  .shell { border: none; max-width: none; }
  .page-main { min-height: 0; max-height: none; overflow: visible; }
}
