/* ═══════════════════════════════════════════════════════════════════════════
   GeroPGB — Population Genome Browser
   Design system: GeroArk brand green on the GeroBrowser shell
   (dark green-black sidebar + light content). Offline-only: no CDN, no @import.

   ─────────────────────────────────────────────────────────────────────────
   ★ READ THIS BEFORE "FIXING" THE TWO GREENS ★
   ─────────────────────────────────────────────────────────────────────────
   The brand green is Naver green #03C75A = hsl(147 97% 40%). White text on it
   measures only 2.3:1 — below every WCAG threshold, and genuinely hard to read
   on a laptop screen in a lit lab. So this file deliberately carries TWO greens:

     --brand   #03C75A   DECORATIVE ONLY. Splash, hero glows, particle fields,
                         chart fills, PWA theme-color. Never place text on it.
     --primary #049545   hsl(147 95% 30%) — the real hex of GeroArk's --primary.
                         Anything that carries white text: buttons, active nav,
                         badges-on-fill. ~3.9:1 on its own; the --grad it lives
                         in ends at --primary-strong so the run averages ~4.4:1.
     --primary-ink #03813C  hsl(147 95% 26%) — body links and inline text on
                         white. 5.0:1. Text-on-white needs the extra step down.

   Two greens in one palette LOOKS like an inconsistency in a screenshot. It
   isn't. If you flatten them to one value you either lose the brand (too dark
   everywhere) or lose readability (too light everywhere).
   Dark mode inverts the rule: on a near-black surface the dark greens vanish,
   so html.dark lifts --primary to hsl(147 62% 48%) and lets --brand stay put.
   ═══════════════════════════════════════════════════════════════════════════ */

@font-face {
  font-family: "Pretendard Variable";
  font-weight: 45 920;
  font-style: normal;
  font-display: swap;
  src: url("/static/fonts/PretendardVariable.woff2") format("woff2-variations");
}

/* ═══ Tokens — light ═══════════════════════════════════════════════════════ */
:root {
  /* content surfaces — very faintly green-tinted greys so the white cards read
     as "on brand" without any actual colour cast on data */
  --bg: #f7fcf9;
  --bg-2: #f1f8f4;
  --surface: #ffffff;
  --surface-2: #f8fcfa;
  --surface-3: #f3faf6;

  /* ink */
  --ink: #16241c;
  --ink-soft: #55685d;
  --muted: #93a59a;

  /* borders */
  --border: #dfeae3;
  --border-soft: #edf4ef;

  /* ── brand greens — see the header block above before editing ── */
  --brand: #03c75a;            /* decorative only, never under text */
  --brand-glow: rgba(3, 199, 90, .30);
  --primary: #049545;          /* hsl(147 95% 30%) — white text sits here */
  --primary-strong: #026f37;   /* gradient tail / hover */
  --primary-ink: #03813c;      /* hsl(147 95% 26%) — links & text on white */
  --primary-tint: rgba(3, 199, 90, .10);
  --primary-tint-2: rgba(3, 199, 90, .20);

  /* secondary: mint/teal. Carries the 2nd chart series and the far end of every
     gradient — green→mint reads as a spectrum, green→green reads as a smudge. */
  --mint: #16e0c0;
  --mint-deep: #0fa892;
  --teal: #20acb6;             /* hsl(184 70% 42%) */
  --teal-bright: #2ec6d1;
  --teal-deep: #17838b;
  --teal-dark: #0f6068;

  --grad: linear-gradient(135deg, #049545 0%, #026f37 100%);
  --grad-bright: linear-gradient(135deg, #03c75a 0%, #06a04c 100%); /* no text */
  --grad-mint: linear-gradient(90deg, #049545 0%, #16e0c0 100%);
  --grad-soft: linear-gradient(135deg, #e6f8ed 0%, #e2f6f1 100%);

  /* dark sidebar — green-black, not neutral black: keeps the brand present in
     peripheral vision without competing with the data pane */
  --side-bg: #ffffff;
  --side-bg-2: #f2faf5;
  --side-ink: #41564a;
  --side-ink-dim: #7b9285;
  --side-active: var(--grad);
  --side-hover: rgba(4, 149, 69, .09);
  --side-brd: #e6f0ea;
  /* Emphasised sidebar text. Split from --on-dark because the sidebar is light
     here while the hero and .btn.dark it also served are still dark. */
  --side-strong: #0f2a1c;
  --side-inset: rgba(4, 149, 69, .07);
  --side-danger: #c62828;
  --on-dark: #eafcf1;

  /* dark hero — GeroArk's exact 4-stop ramp */
  --hero-bg: linear-gradient(120deg, #06170f 0%, #0b2419 40%, #08241f 72%, #05170f 100%);

  /* semantic */
  --green: #2bab5a;            /* hsl(142 60% 42%) */
  --green-bg: #dcf4e5;
  --green-ink: #17773e;
  --amber: #f59e0b;            /* hsl(38 92% 50%) */
  --ok: #f59e0b;
  --ok-bg: #fdf0d5;
  --amber-ink: #96650a;
  --orange: #ea7317;
  --orange-bg: #fdeada;
  --red: #de3a3a;              /* hsl(0 72% 55%) */
  --red-bg: #fce4e4;
  --red-ink: #b62525;
  --accent: #3583e9;           /* hsl(214 80% 56%) */
  --accent-bg: #e3edfd;
  --gold: #b8860b;

  /* chart series — c1/c2 are the brand pair; the rest are picked for
     deuteranopia separation, which matters for K=6+ admixture plots */
  --c1: #03c75a; --c2: #16e0c0; --c3: #3583e9; --c4: #b8860b;
  --c5: #a855f7; --c6: #f59e0b; --c7: #ef4444; --c8: #64748b;

  /* neutral fills that must flip in dark mode */
  --track: #dde8e0;
  --chip-bg: #edf2ee;
  --code-bg: #07201a;
  --code-ink: #d3f5e2;
  --tip-bg: #07201a;
  --scrim-bg: rgba(6, 23, 15, .46);

  /* shadows — green-black tint so cards sit on the page instead of floating */
  --shadow-sm: 0 1px 2px rgba(9, 35, 22, .05), 0 2px 8px rgba(9, 35, 22, .05);
  --shadow: 0 2px 6px rgba(9, 35, 22, .06), 0 12px 28px rgba(9, 35, 22, .09);
  --shadow-pop: 0 10px 30px rgba(9, 35, 22, .16), 0 30px 70px rgba(9, 35, 22, .18);
  --glow: 0 6px 20px rgba(3, 199, 90, .30);
  --glow-strong: 0 8px 28px rgba(3, 199, 90, .44);

  --radius: 16px;
  --radius-sm: 11px;
  --radius-lg: 22px;
  --sidebar-w: 250px;
  --sidebar-collapsed: 76px;
  --ease: cubic-bezier(.16, 1, .3, 1);
  --font: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont,
          "Segoe UI", Roboto, "Apple SD Gothic Neo", "Noto Sans KR", sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  /* safe-area shorthands so every fixed/edge element can opt in with one token */
  --sat: env(safe-area-inset-top, 0px);
  --sar: env(safe-area-inset-right, 0px);
  --sab: env(safe-area-inset-bottom, 0px);
  --sal: env(safe-area-inset-left, 0px);
}

/* ═══ Tokens — dark (html.dark) ════════════════════════════════════════════ */
/* Same variable names, so every component above inherits dark mode for free.
   Only the tokens move; no component rule is duplicated. */
html.dark {
  --bg: #0d1713;
  --bg-2: #101d17;
  --surface: #14211b;
  --surface-2: #1a2922;
  --surface-3: #203128;

  --ink: #e2eee7;
  --ink-soft: #a1b5aa;
  --muted: #74897d;

  --border: #294036;
  --border-soft: #223429;

  /* On a dark surface the WCAG maths flips: the light-mode greens drop to ~1.9:1
     against #14211b. Lift primary and let the ink token ride the same value —
     there is no "text on white" case in dark mode. */
  --primary: #2fc673;          /* hsl(147 62% 48%) */
  --primary-strong: #23a75e;
  --primary-ink: #4fd98c;      /* links on dark: 8.3:1 */
  --primary-tint: rgba(3, 199, 90, .14);
  --primary-tint-2: rgba(3, 199, 90, .26);

  --mint: #4ceccf;
  --mint-deep: #16e0c0;
  --teal: #2ec6d1;             /* hsl(184 64% 50%) */
  --teal-bright: #62dbe3;
  --teal-deep: #20acb6;

  --grad: linear-gradient(135deg, #2fc673 0%, #17944f 100%);
  --grad-bright: linear-gradient(135deg, #03c75a 0%, #16e0c0 100%);
  --grad-soft: linear-gradient(135deg, rgba(3, 199, 90, .14) 0%, rgba(22, 224, 192, .12) 100%);

  --side-bg: #081812;
  --side-bg-2: #04100b;
  --side-strong: #eafcf1;
  --side-inset: rgba(255, 255, 255, .05);
  --side-danger: #ffb3b3;
  --side-ink: #9db5a7;
  --side-ink-dim: #667f70;
  --side-brd: rgba(255, 255, 255, .06);

  --green: #3fce77; --green-bg: rgba(63, 206, 119, .15); --green-ink: #6ee0a0;
  --amber: #fbbf24; --ok: #fbbf24; --ok-bg: rgba(251, 191, 36, .15); --amber-ink: #fcd34d;
  --orange: #fb923c; --orange-bg: rgba(251, 146, 60, .15);
  --red: #f26a6a; --red-bg: rgba(242, 106, 106, .15); --red-ink: #ff9c9c;
  --accent: #5b9df0; --accent-bg: rgba(91, 157, 240, .15);

  --track: #24382e;
  --chip-bg: #1c2b23;
  --code-bg: #06140f;
  --code-ink: #cdeedb;
  --tip-bg: #06140f;
  --scrim-bg: rgba(0, 0, 0, .62);

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .35), 0 2px 8px rgba(0, 0, 0, .25);
  --shadow: 0 2px 6px rgba(0, 0, 0, .4), 0 12px 28px rgba(0, 0, 0, .35);
  --shadow-pop: 0 10px 30px rgba(0, 0, 0, .5), 0 30px 70px rgba(0, 0, 0, .45);
  --glow: 0 6px 20px rgba(3, 199, 90, .26);
  --glow-strong: 0 8px 28px rgba(3, 199, 90, .38);

  color-scheme: dark;
}

/* ═══ Base ════════════════════════════════════════════════════════════════ */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html {
  scroll-behavior: smooth;
  /* 106.25% of a 16px default = 17px root. GeroArk moved the whole scale up
     (their xs is 13.5px, not Tailwind's 12px) because this is a tool people
     read tables in for hours — 12px micro-labels were being squinted at.
     Using % rather than px keeps the user's own browser font-size preference
     working as a multiplier. Every rem below inherits this bump. */
  font-size: 106.25%;
  -webkit-text-size-adjust: 100%;
}
body {
  font-family: var(--font);
  background: var(--bg);
  background-image:
    radial-gradient(900px 520px at 8% -6%, rgba(3, 199, 90, .07) 0%, transparent 55%),
    radial-gradient(820px 480px at 104% 6%, rgba(22, 224, 192, .07) 0%, transparent 52%);
  background-attachment: fixed;
  color: var(--ink);
  font-size: 1rem;          /* 17px */
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Korean does not break inside an 어절. */
  word-break: keep-all;
  /* MUST be `break-word`, not `anywhere`. Both stop a long token from
     overflowing, but `anywhere` folds its break opportunities into the
     min-content width — so the element's minimum width becomes ONE character.
     Inside flex that lets text crush to 0 and stack vertically ("진돗 / 개"),
     and flex-wrap never fires because nothing ever overflows. `break-word` is
     excluded from min-content, so the minimum stays the longest word and the
     layout behaves. Do not "simplify" this to anywhere. */
  overflow-wrap: break-word;
  -webkit-tap-highlight-color: transparent;
}
h1, h2, h3, h4 { font-weight: 750; line-height: 1.28; color: var(--ink); margin: 0; letter-spacing: -0.02em; word-break: keep-all; }
h1 { font-size: 1.6rem; }
h2 { font-size: 1.2rem; }
h3 { font-size: 1.04rem; }
a { color: var(--primary-ink); text-decoration: none; transition: color .18s; }
a:hover { color: var(--primary); }
button, input, select, textarea { font-family: inherit; font-size: inherit; color: inherit; }
.num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }
::selection { background: rgba(3, 199, 90, .22); }

* { scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb { background: var(--border); border-radius: 999px; border: 2px solid transparent; background-clip: content-box; }
*::-webkit-scrollbar-thumb:hover { background: var(--muted); background-clip: content-box; }
*::-webkit-scrollbar-track { background: transparent; }

.grad-text { background: var(--grad); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: transparent; }

/* ═══ Motion — GeroArk keyframes, gero- → pgb- ════════════════════════════ */
@keyframes pgb-fade-up { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: translateY(0); } }
@keyframes pgb-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes pgb-drift { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(12px, -9px); } }
@keyframes pgb-pulse-glow { 0%, 100% { opacity: .35; } 50% { opacity: .85; } }
@keyframes pgb-gradient { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } }
@keyframes pgb-spin-slow { to { transform: rotate(360deg); } }
@keyframes pgb-dash { to { stroke-dashoffset: 0; } }
@keyframes pgb-shimmer { 0% { background-position: -600px 0; } 100% { background-position: 600px 0; } }
@keyframes pgb-indeterminate { 0% { left: -40%; width: 40%; } 50% { left: 25%; width: 55%; } 100% { left: 100%; width: 40%; } }
@keyframes pgb-stripe { to { background-position: 32px 0; } }
@keyframes pgb-pop { 0% { transform: scale(.94); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }

.pgb-anim { opacity: 0; animation: pgb-fade-up .55s ease forwards; }
.pgb-fade { opacity: 0; animation: pgb-fade-in .5s ease forwards; }
.pgb-pop { animation: pgb-pop .28s cubic-bezier(.2, .9, .3, 1.2) forwards; }
.pgb-node { animation: pgb-drift 9s ease-in-out infinite; }
.pgb-glow { animation: pgb-pulse-glow 4s ease-in-out infinite; }
.pgb-count { font-variant-numeric: tabular-nums; }

/* The banner is static on purpose. A 200% background sweeping every 14s makes
   green slosh across the whole viewport and the banner out-competes the KPIs
   below it — wrong priority for a working screen. */
.pgb-hero {
  position: relative; overflow: hidden; border-radius: 18px;
  background: var(--hero-bg);
}
.pgb-hero::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  /* pure #03C75A is safe here: it is a 17%-opacity glow with no text on it */
  background:
    radial-gradient(ellipse at 18% 20%, rgba(3, 199, 90, .17), transparent 48%),
    radial-gradient(ellipse at 82% 75%, rgba(22, 169, 155, .14), transparent 48%);
}

/* Indeterminate — a task is running and we cannot say how far along it is. */
.pgb-progress { position: relative; overflow: hidden; border-radius: 9999px; background: var(--track); height: 6px; }
.pgb-progress > span {
  position: absolute; top: 0; bottom: 0; border-radius: 9999px;
  background: linear-gradient(90deg, var(--primary), var(--mint));
  animation: pgb-indeterminate 1.15s ease-in-out infinite;
}
/* Value gauge — a measured quantity, NOT a running task. Static on purpose:
   moving stripes here read as "loading" and made users think a job was live. */
.pgb-bar { position: relative; overflow: hidden; border-radius: 9999px; background: var(--track); height: 8px; }
.pgb-bar > i {
  display: block; height: 100%; border-radius: 9999px;
  background: linear-gradient(90deg, var(--primary), var(--mint));
  transition: width .45s cubic-bezier(.25, .9, .35, 1);
}
/* Opt-in animation, reserved for work that is actually in progress. */
.pgb-bar.is-busy > i {
  background-image: linear-gradient(90deg, var(--primary), var(--mint)),
    repeating-linear-gradient(45deg, rgba(255, 255, 255, .22) 0 8px, transparent 8px 16px);
  background-blend-mode: overlay;
  background-size: 100% 100%, 32px 32px;
  animation: pgb-stripe .9s linear infinite;
}

.pgb-skeleton {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--border-soft) 37%, var(--surface-2) 63%);
  background-size: 600px 100%;
  animation: pgb-shimmer 1.3s linear infinite;
  border-radius: 6px;
}

/* Tap affordance — pairs with the haptics helper in app.js. Applied to anything
   a thumb lands on so the press is confirmed visually as well as physically. */
.pgb-tap { transition: transform .1s ease, filter .1s ease; -webkit-tap-highlight-color: transparent; }
.pgb-tap:active { transform: scale(.97); filter: brightness(.97); }

/* SVG chart primitives (shared by PCA, pedigree, sparklines) */
.pgb-axis { stroke: var(--border); stroke-width: 1; }
.pgb-grid { stroke: var(--border); stroke-width: 1; stroke-dasharray: 3 4; opacity: .7; }
/* Axis ticks — below 12px these stopped being readable on a laptop. Changing
   this forces a matching change to cell/lane heights and label gutters, which
   are sized from an estimate of this glyph width. */
.pgb-tick { fill: var(--muted); font-size: 12px; }
.pgb-line { fill: none; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round;
            stroke-dasharray: 2200; stroke-dashoffset: 2200; animation: pgb-dash 1.4s ease forwards; }

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

/* ═══ App shell ═══════════════════════════════════════════════════════════ */
.app { display: flex; min-height: 100vh; min-height: 100dvh; }
.sidebar {
  width: var(--sidebar-w); flex: 0 0 var(--sidebar-w);
  background: linear-gradient(180deg, var(--side-bg) 0%, var(--side-bg-2) 100%);
  border-right: 1px solid var(--side-brd);
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh; height: 100dvh;
  /* notch on the left in landscape, home indicator at the bottom */
  padding-left: var(--sal); padding-bottom: var(--sab);
  transition: width .3s var(--ease), flex-basis .3s var(--ease);
  z-index: 40;
}
.app.collapsed .sidebar { width: var(--sidebar-collapsed); flex-basis: var(--sidebar-collapsed); }
.brand {
  display: flex; align-items: center; gap: 12px;
  padding: calc(20px + var(--sat)) 18px 18px; cursor: pointer; min-height: 66px; user-select: none;
  border-bottom: 1px solid var(--side-brd); -webkit-tap-highlight-color: transparent;
}
.brand .logo { width: 54px; height: 54px; flex: 0 0 54px; filter: drop-shadow(0 4px 16px rgba(199, 138, 32, .55)); transition: transform .7s var(--ease); }
.brand:hover .logo, .brand:focus .logo { transform: rotate(360deg) scale(1.08); }
.brand .brand-name { font-weight: 830; font-size: 1.28rem; letter-spacing: -0.03em; color: var(--side-strong); line-height: 1.15;
  overflow-wrap: break-word; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.brand .brand-sub { font-size: .78rem; color: var(--side-ink-dim); font-weight: 600; letter-spacing: .02em; margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.brand .brand-txt { min-width: 0; }
.app.collapsed .brand-name, .app.collapsed .brand-sub { opacity: 0; width: 0; overflow: hidden; }
.app.collapsed .brand { justify-content: center; padding-left: 8px; padding-right: 8px; gap: 0; }
.app.collapsed .brand-txt { width: 0; overflow: hidden; }

.nav { flex: 1; padding: 12px; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; }
.nav-section { font-size: .78rem; font-weight: 750; letter-spacing: .08em; text-transform: uppercase; color: var(--side-ink-dim); padding: 6px 13px; margin: 12px 0 3px; white-space: nowrap; }
.app.collapsed .nav-section { opacity: 0; height: 6px; margin: 6px 0; padding: 0; overflow: hidden; }
.nav a {
  position: relative; display: flex; align-items: center; gap: 13px;
  padding: 11px 13px; margin: 2px 0; border-radius: var(--radius-sm);
  color: var(--side-ink); font-weight: 620; white-space: nowrap; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color .2s var(--ease), background .2s var(--ease), transform .2s var(--ease);
}
.nav a .ic { flex: 0 0 22px; width: 22px; height: 22px; display: grid; place-items: center; transition: transform .25s var(--ease); }
.nav a:hover { background: var(--side-hover); color: var(--side-strong); transform: translateX(2px); }
.nav a:hover .ic { transform: scale(1.12); color: var(--brand); }
.nav a:active { transform: translateX(2px) scale(.98); }
.nav a.active { color: #fff; background: var(--side-active); box-shadow: var(--glow); }
.nav a.active .ic { color: #fff; }
.nav .nav-label { transition: opacity .2s; }
.app.collapsed .nav .nav-label { opacity: 0; width: 0; overflow: hidden; }
.app.collapsed .nav a { justify-content: center; }
.nav .nav-sep { height: 1px; background: var(--side-brd); margin: 10px 8px; }

.sidebar-foot { padding: 12px; border-top: 1px solid var(--side-brd); display: flex; flex-direction: column; gap: 8px; }
.side-lang-label { font-size: .78rem; font-weight: 750; letter-spacing: .08em; text-transform: uppercase; color: var(--side-ink-dim); padding: 0 4px; }
.app.collapsed .side-lang-label { display: none; }
.side-lang { display: flex; background: var(--side-inset); border: 1px solid var(--side-brd); border-radius: 10px; padding: 3px; gap: 3px; }
.side-lang button { flex: 1; border: none; background: transparent; padding: 7px 0; border-radius: 7px; cursor: pointer; font-weight: 680; color: var(--side-ink-dim); transition: all .2s var(--ease); font-size: .86rem; -webkit-tap-highlight-color: transparent; }
.side-lang button:hover { color: var(--side-strong); }
.side-lang button:active { transform: scale(.96); }
.side-lang button.on { background: var(--side-active); color: #fff; box-shadow: 0 3px 10px rgba(3, 199, 90, .3); }
.app.collapsed .side-lang { flex-direction: column; }
.nav-logout { display: flex; align-items: center; gap: 12px; padding: 10px 13px; border-radius: var(--radius-sm); color: var(--side-ink) !important; font-weight: 620; transition: background .2s, color .2s; white-space: nowrap; }
.nav-logout:hover { background: rgba(222, 58, 58, .12); color: var(--side-danger) !important; text-decoration: none; }
.nav-logout .ic { flex: 0 0 22px; display: grid; place-items: center; }
.app.collapsed .nav-logout { justify-content: center; }
.app.collapsed .nav-logout .nav-label { opacity: 0; width: 0; overflow: hidden; }
.app.collapsed .sidebar-foot { padding: 10px 8px; align-items: center; }
.app.collapsed .side-lang { flex-direction: column; width: 100%; }
.app.collapsed .side-lang button { padding: 6px 0; font-size: .8rem; }
.app.collapsed .collapse-row { justify-content: center; padding: 9px 0; }
.collapse-row { display: flex; align-items: center; gap: 10px; padding: 9px 13px; border-radius: var(--radius-sm); color: var(--side-ink-dim); font-weight: 620; cursor: pointer; font-size: .86rem; transition: background .2s, color .2s; white-space: nowrap; }
.collapse-row:hover { background: var(--side-hover); color: var(--side-strong); }
.collapse-row .ic { transition: transform .3s var(--ease); }
.app.collapsed .collapse-row .ic { transform: rotate(180deg); }
.app.collapsed .collapse-row .nav-label { opacity: 0; width: 0; overflow: hidden; }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.topbar {
  display: flex; align-items: center; gap: 12px;
  padding: 15px 26px; padding-top: calc(15px + var(--sat)); padding-right: calc(26px + var(--sar));
  background: color-mix(in srgb, var(--surface) 80%, transparent);
  backdrop-filter: blur(16px) saturate(150%); -webkit-backdrop-filter: blur(16px) saturate(150%);
  border-bottom: 1px solid var(--border-soft); position: sticky; top: 0; z-index: 30;
}
.topbar .crumb { font-weight: 700; color: var(--ink-soft); display: flex; align-items: center; gap: 8px; min-width: 0; }
.topbar .crumb .home { color: var(--primary-ink); }
.topbar .crumb .sep { color: var(--muted); }
.topbar .spacer { flex: 1; }
.model-badge { display: inline-flex; align-items: center; gap: 7px; padding: 7px 13px; border-radius: 999px; background: var(--grad-soft); border: 1px solid var(--primary-tint-2); color: var(--primary-ink); font-weight: 700; font-size: .84rem; }
.model-badge .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--green); box-shadow: 0 0 0 3px rgba(43, 171, 90, .2); }
.icon-btn { width: 40px; height: 40px; border-radius: var(--radius-sm); border: 1px solid var(--border-soft); background: var(--surface); display: grid; place-items: center; cursor: pointer; color: var(--ink-soft); -webkit-tap-highlight-color: transparent; transition: background .2s var(--ease), color .2s, border-color .2s, transform .12s, box-shadow .2s; }
.icon-btn:hover { background: var(--surface-2); color: var(--primary-ink); border-color: var(--primary); box-shadow: var(--shadow-sm); transform: translateY(-1px); }
.icon-btn:active { transform: translateY(0) scale(.94); }
.hamburger { display: none; }
.content {
  padding: 26px; max-width: 1240px; width: 100%; margin: 0 auto;
  padding-left: max(26px, var(--sal)); padding-right: max(26px, var(--sar));
  padding-bottom: calc(26px + var(--sab));
}
.page-head { display: flex; align-items: center; gap: 14px; margin-bottom: 22px; flex-wrap: wrap; }
.page-head .spacer { flex: 1; }

/* ═══ Buttons ═════════════════════════════════════════════════════════════ */
.btn {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; gap: 8px; justify-content: center;
  padding: 10px 18px; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--surface); color: var(--ink); font-weight: 660; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform .14s var(--ease), background .2s, border-color .2s, box-shadow .25s, color .2s;
  text-decoration: none; white-space: nowrap;
}
.btn::after { content: ""; position: absolute; top: 0; left: -80%; width: 60%; height: 100%; background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .5), transparent); transform: skewX(-18deg); transition: left .6s var(--ease); pointer-events: none; }
.btn:hover { transform: translateY(-2px); box-shadow: var(--shadow-sm); border-color: var(--primary); color: var(--primary-ink); }
.btn:hover::after { left: 130%; }
.btn:active { transform: translateY(0) scale(.97); }
.btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.btn.primary { background: var(--grad); border-color: transparent; color: #fff; box-shadow: var(--glow); }
.btn.primary:hover { color: #fff; box-shadow: var(--glow-strong); }
.btn.ghost { border-color: transparent; background: transparent; box-shadow: none; }
.btn.ghost:hover { background: var(--primary-tint); box-shadow: none; }
.btn.danger { color: var(--red); border-color: color-mix(in srgb, var(--red) 35%, transparent); background: var(--red-bg); }
.btn.danger:hover { background: var(--red); color: #fff; border-color: transparent; box-shadow: 0 6px 20px rgba(222, 58, 58, .4); }
.btn.dark { background: rgba(255, 255, 255, .1); border-color: rgba(255, 255, 255, .18); color: var(--on-dark); backdrop-filter: blur(6px); }
.btn.dark:hover { background: rgba(255, 255, 255, .18); color: #fff; border-color: rgba(255, 255, 255, .3); }
.btn.sm { padding: 7px 12px; font-size: .88rem; border-radius: 9px; }
.btn:disabled { opacity: .5; cursor: not-allowed; transform: none !important; box-shadow: none; }
.btn .ic { display: grid; place-items: center; }

/* ═══ Cards & grids ═══════════════════════════════════════════════════════ */
.card { position: relative; background: var(--surface); border: 1px solid var(--border-soft); border-radius: var(--radius); box-shadow: var(--shadow-sm); padding: 20px; transition: transform .3s var(--ease), box-shadow .3s var(--ease), border-color .3s; }
.card:hover { box-shadow: var(--shadow); }
.card.pad-lg { padding: 26px; }
.grid { display: grid; gap: 18px; min-width: 0; }
/* Grid/flex items default to min-width:auto, which lets wide content (nowrap
   tables, fixed-width bar rows) inflate the track past the viewport and get
   clipped. Allow items to shrink so inner scroll/ellipsis handles overflow. */
.grid > * { min-width: 0; }
.kpi-grid { grid-template-columns: repeat(4, 1fr); }
.cols-2 { grid-template-columns: 1.6fr 1fr; }

.kpi { position: relative; overflow: hidden; display: flex; align-items: center; gap: 16px; background: var(--surface); border: 1px solid var(--border-soft); border-radius: var(--radius); box-shadow: var(--shadow-sm); padding: 20px; transition: transform .3s var(--ease), box-shadow .3s var(--ease); }
.kpi::before { content: ""; position: absolute; inset: 0; opacity: 0; background: var(--grad-soft); transition: opacity .3s; z-index: 0; }
/* KPI cards can be links/buttons — normalize and signal interactivity */
a.kpi, button.kpi { text-decoration: none; color: inherit; font: inherit; text-align: left; width: 100%; cursor: pointer; -webkit-tap-highlight-color: transparent; }
a.kpi:focus-visible, button.kpi:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
a.kpi:active, button.kpi:active { transform: scale(.985); }
.kpi:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
.kpi:hover::before { opacity: .6; }
.kpi > * { position: relative; z-index: 1; }
.kpi .gauge { width: 62px; height: 62px; flex: 0 0 62px; border-radius: 50%; display: grid; place-items: center; position: relative; transition: transform .4s var(--ease); }
.kpi:hover .gauge { transform: rotate(6deg) scale(1.05); }
.kpi .gauge::before { content: ""; position: absolute; inset: 6px; background: var(--surface); border-radius: 50%; box-shadow: inset 0 1px 3px rgba(9, 35, 22, .08); }
.kpi .gauge .g-ic { position: relative; z-index: 1; color: var(--primary-ink); }
.kpi .kpi-val { font-size: 2.05rem; font-weight: 850; letter-spacing: -0.035em; line-height: 1.05;
  background: var(--grad); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
  font-variant-numeric: tabular-nums; transform-origin: left center;
  filter: drop-shadow(0 3px 10px rgba(3, 199, 90, .2)); }
.kpi .kpi-label { color: var(--ink-soft); font-size: .88rem; font-weight: 640; }
@keyframes numPop { 0% { transform: scale(1); } 45% { transform: scale(1.14); } 100% { transform: scale(1); } }
.num-pop { animation: numPop .55s var(--ease); }

/* ═══ Section titles ══════════════════════════════════════════════════════ */
.section-title { display: flex; align-items: center; gap: 10px; margin: 2px 0 14px; }
.section-title h2 { font-size: 1.08rem; }
.section-title h2::before { content: ""; display: inline-block; width: 4px; height: 15px; border-radius: 999px; background: var(--grad); margin-right: 9px; vertical-align: -2px; }
.section-title .spacer { flex: 1; }

/* ═══ List ════════════════════════════════════════════════════════════════ */
.list { display: flex; flex-direction: column; gap: 9px; }
.list-item { position: relative; overflow: hidden; display: flex; align-items: center; gap: 14px; width: 100%; padding: 14px 16px; border: 1px solid var(--border-soft); border-radius: var(--radius-sm); background: var(--surface); cursor: pointer; text-align: left; -webkit-tap-highlight-color: transparent; transition: transform .22s var(--ease), border-color .22s, box-shadow .22s, background .22s; }
.list-item::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--grad); transform: scaleY(0); transform-origin: bottom; transition: transform .28s var(--ease); }
.list-item:hover { border-color: transparent; background: var(--surface-2); box-shadow: var(--shadow); transform: translateX(4px); text-decoration: none; }
.list-item:hover::before { transform: scaleY(1); }
.list-item:active { transform: translateX(4px) scale(.99); }
.list-item .li-avatar { width: 42px; height: 42px; flex: 0 0 42px; border-radius: 13px; background: var(--grad-soft); display: grid; place-items: center; color: var(--primary-ink); font-weight: 820; border: 1px solid var(--border-soft); transition: transform .28s var(--ease); }
.list-item:hover .li-avatar { transform: scale(1.08) rotate(-3deg); }
.list-item .li-main { flex: 1; min-width: 0; }
.list-item .li-title { font-weight: 720; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.list-item .li-sub { color: var(--ink-soft); font-size: .88rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.list-item .li-meta { color: var(--muted); font-size: .82rem; white-space: nowrap; flex: 0 0 auto; }

/* ═══ Badges ══════════════════════════════════════════════════════════════ */
.badge { display: inline-flex; align-items: center; gap: 6px; padding: 4px 11px; border-radius: 999px; font-size: .8rem; font-weight: 700; white-space: nowrap; background: var(--grad-soft); color: var(--primary-ink); border: 1px solid var(--primary-tint-2); }
.badge.ok { background: var(--ok-bg); color: var(--amber-ink); border-color: rgba(245, 158, 11, .28); }
.badge.green { background: var(--green-bg); color: var(--green-ink); border-color: rgba(43, 171, 90, .28); }
.badge.red { background: var(--red-bg); color: var(--red-ink); border-color: rgba(222, 58, 58, .28); }
.badge.muted { background: var(--chip-bg); color: var(--ink-soft); border-color: var(--border-soft); }
.badge .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; box-shadow: 0 0 0 3px rgba(43, 171, 90, .18); animation: pulseDot 1.8s ease-in-out infinite; }
@keyframes pulseDot { 0%, 100% { box-shadow: 0 0 0 2px rgba(43, 171, 90, .16); } 50% { box-shadow: 0 0 0 5px rgba(43, 171, 90, 0); } }
.check-circle { width: 20px; height: 20px; border-radius: 50%; background: var(--grad); display: grid; place-items: center; color: #fff; flex: 0 0 20px; }

/* ═══ Tables (legacy .data) ═══════════════════════════════════════════════ */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; border: 1px solid var(--border-soft); border-radius: var(--radius); background: var(--surface); box-shadow: var(--shadow-sm); max-width: 100%; }
table.data { width: 100%; border-collapse: collapse; min-width: 560px; }
table.data th, table.data td { padding: 13px 15px; text-align: left; border-bottom: 1px solid var(--border-soft); white-space: nowrap; }
table.data th { font-size: .8rem; color: var(--ink-soft); font-weight: 720; text-transform: uppercase; letter-spacing: .04em; background: var(--surface-2); position: sticky; top: 0; }
table.data tr:last-child td { border-bottom: none; }
table.data tbody tr { cursor: pointer; transition: background .16s; }
table.data tbody tr:hover { background: var(--grad-soft); }
.ellip { max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ═══ Forms ═══════════════════════════════════════════════════════════════ */
.field { margin-bottom: 15px; }
.field label { display: block; font-weight: 660; font-size: .9rem; margin-bottom: 7px; color: var(--ink-soft); }
.input, .select, textarea.input { width: 100%; padding: 11px 14px; border: 1.5px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); outline: none; transition: border-color .2s, box-shadow .25s, background .2s; }
.input:hover, .select:hover, textarea.input:hover { border-color: var(--muted); }
.input:focus, .select:focus, textarea.input:focus { border-color: var(--primary); box-shadow: 0 0 0 4px var(--primary-tint); }
textarea.input { resize: vertical; min-height: 92px; line-height: 1.6; }
.hint { color: var(--muted); font-size: .86rem; margin-top: 7px; }
.seg { display: inline-flex; background: var(--surface-2); border: 1px solid var(--border-soft); border-radius: var(--radius-sm); padding: 4px; gap: 3px; }
.seg button { border: none; background: transparent; padding: 7px 14px; border-radius: 8px; cursor: pointer; font-weight: 660; color: var(--ink-soft); -webkit-tap-highlight-color: transparent; transition: all .22s var(--ease); white-space: nowrap; }
#pp-style, #pp-color { width: 100%; }
#pp-style button, #pp-color button { flex: 1; padding: 7px 6px; font-size: .84rem; }
.seg button:hover { color: var(--primary-ink); }
.seg button:active { transform: scale(.96); }
.seg button.on { background: var(--surface); color: var(--primary-ink); box-shadow: var(--shadow-sm); }

/* ═══ Progress (legacy) ═══════════════════════════════════════════════════ */
.progress { height: 9px; border-radius: 999px; background: var(--track); overflow: hidden; position: relative; }
.progress > i { display: block; height: 100%; background: var(--grad); border-radius: 999px; transition: width .5s var(--ease); position: relative; box-shadow: var(--glow); }
.progress > i::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .5), transparent); background-size: 200% 100%; animation: shimmer 1.5s linear infinite; }
.progress.indeterminate > i { width: 35% !important; animation: indet 1.2s var(--ease) infinite; }
@keyframes indet { 0% { margin-left: -35%; } 100% { margin-left: 100%; } }
@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

/* ═══ Toast + pill ════════════════════════════════════════════════════════ */
.toast-wrap { position: fixed; top: calc(20px + var(--sat)); right: calc(20px + var(--sar)); z-index: 200; display: flex; flex-direction: column; gap: 10px; max-width: calc(100vw - 32px); }
.toast { background: color-mix(in srgb, var(--surface) 86%, transparent); backdrop-filter: blur(14px) saturate(160%); -webkit-backdrop-filter: blur(14px) saturate(160%); border: 1px solid var(--border-soft); border-left: 4px solid var(--primary); border-radius: var(--radius-sm); box-shadow: var(--shadow-pop); padding: 13px 17px; min-width: 250px; max-width: 380px; display: flex; align-items: center; gap: 11px; animation: toastIn .45s var(--ease); }
.toast .ic { color: var(--primary); display: grid; place-items: center; }
.toast.ok { border-left-color: var(--green); } .toast.ok .ic { color: var(--green); }
.toast.err { border-left-color: var(--red); } .toast.err .ic { color: var(--red); }
@keyframes toastIn { from { opacity: 0; transform: translateX(30px) scale(.9); } to { opacity: 1; transform: none; } }

#pill-tray { position: fixed; left: 50%; transform: translateX(-50%); bottom: calc(22px + var(--sab)); z-index: 150; display: flex; flex-direction: column; gap: 9px; align-items: center; width: min(92vw, 470px); }
.pill { display: flex; align-items: center; gap: 13px; width: 100%; background: color-mix(in srgb, var(--surface) 88%, transparent); backdrop-filter: blur(16px) saturate(160%); -webkit-backdrop-filter: blur(16px) saturate(160%); border: 1px solid var(--border-soft); border-radius: 999px; box-shadow: var(--shadow-pop); padding: 10px 10px 10px 18px; cursor: pointer; animation: pillIn .5s var(--ease); transition: transform .2s var(--ease); }
.pill:hover { transform: translateY(-3px); }
.pill .pill-spin { width: 22px; height: 22px; border-radius: 50%; border: 2.5px solid var(--primary-tint-2); border-top-color: var(--primary); animation: spin .8s linear infinite; flex: 0 0 22px; }
.pill .pill-main { flex: 1; min-width: 0; }
.pill .pill-title { font-weight: 720; font-size: .9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pill .pill-bar { height: 6px; border-radius: 999px; background: var(--track); overflow: hidden; margin-top: 5px; }
.pill .pill-bar > i { display: block; height: 100%; background: var(--grad); border-radius: 999px; transition: width .5s var(--ease); }
.pill .pill-pct { font-size: .84rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; flex: 0 0 auto; font-weight: 700; }
.pill.done .pill-spin { border: none; background: var(--grad); position: relative; animation: popIn .4s var(--ease); }
.pill.err .pill-spin { border: none; background: var(--red); animation: shake .4s; }
@keyframes pillIn { from { opacity: 0; transform: translateY(20px) scale(.9); } to { opacity: 1; transform: none; } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes popIn { 0% { transform: scale(0); } 60% { transform: scale(1.2); } 100% { transform: scale(1); } }
@keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-3px); } 75% { transform: translateX(3px); } }

/* ═══ Modal ═══════════════════════════════════════════════════════════════ */
.modal-backdrop { position: fixed; inset: 0; background: var(--scrim-bg); backdrop-filter: blur(5px); z-index: 300; display: grid; place-items: center; padding: max(16px, var(--sat)) max(16px, var(--sar)) max(16px, var(--sab)) max(16px, var(--sal)); animation: fade .25s var(--ease); }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
.modal { background: var(--surface); border-radius: var(--radius-lg); box-shadow: var(--shadow-pop); width: min(640px, 96vw); max-height: 88vh; max-height: 88dvh; display: flex; flex-direction: column; overflow: hidden; animation: modalIn .4s var(--ease); border: 1px solid var(--border-soft); }
.modal.wide { width: min(880px, 96vw); }
@keyframes modalIn { from { opacity: 0; transform: translateY(26px) scale(.96); } to { opacity: 1; transform: none; } }
.modal-head { display: flex; align-items: center; gap: 12px; padding: 18px 22px; border-bottom: 1px solid var(--border-soft); }
.modal-head h3 { flex: 1; }
.modal-head h3::before { content: ""; display: inline-block; width: 4px; height: 16px; border-radius: 999px; background: var(--grad); margin-right: 10px; vertical-align: -2px; }
.modal-body { padding: 20px 22px; overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
.modal-foot { padding: 15px 22px; border-top: 1px solid var(--border-soft); display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap; background: var(--surface-2); }

/* ═══ Login (split hero + card) ═══════════════════════════════════════════ */
.login-split { min-height: 100vh; min-height: 100dvh; display: flex; background: var(--surface); }
.login-hero { flex: 1 1 50%; position: relative; overflow: hidden; display: flex; align-items: center; background: #05170f; }
.login-hero::before { content: ""; position: absolute; inset: 0; background: url("/static/img/banner.jpg") center center / cover no-repeat; }
.login-hero::after { content: ""; position: absolute; inset: 0; background: linear-gradient(120deg, rgba(3, 20, 12, .93) 0%, rgba(8, 34, 22, .78) 55%, rgba(7, 38, 30, .52) 100%); }
.login-hero-inner { position: relative; z-index: 1; padding: 0 9%; max-width: 600px; color: #fff; }
.login-hero-logo { width: 66px; height: 66px; margin-bottom: 22px; filter: drop-shadow(0 8px 30px rgba(199, 138, 32, .6)); animation: floatLogo 4s ease-in-out infinite; }
@keyframes floatLogo { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-8px); } }
.login-hero .eyebrow { color: var(--brand); font-weight: 800; letter-spacing: .18em; font-size: .8rem; margin-bottom: 14px; }
.login-hero h1 { font-size: 2.4rem; line-height: 1.22; color: #fff; letter-spacing: -.02em; }
.login-hero .sources { color: rgba(255, 255, 255, .72); margin-top: 18px; font-size: 1rem; line-height: 1.7; }
.login-main { flex: 1 1 50%; display: flex; align-items: center; justify-content: center; padding: 24px; }
.login-card2 { width: min(400px, 100%); background: var(--surface); border: 1px solid var(--border-soft); border-radius: 20px; box-shadow: var(--shadow); padding: 34px 32px; animation: modalIn .5s var(--ease); }
.login-card2 .lc-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 22px; }
.login-card2 .lc-brand { font-weight: 850; font-size: 1.3rem; background: var(--grad); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; }
.login-card2 .lc-lang button { padding: 5px 11px; font-size: .8rem; }
.login-card2 h2 { font-size: 1.3rem; }
.login-card2 .lc-sub { color: var(--ink-soft); margin: 6px 0 22px; font-size: .94rem; }
.login-card2 .input { padding: 14px 16px; border-radius: 12px; }
.login-card2 .btn.primary { width: 100%; padding: 14px; font-size: 1.02rem; margin-top: 6px; }
.login-card2 .login-err { background: var(--red-bg); color: var(--red-ink); border: 1px solid rgba(222, 58, 58, .3); border-radius: 12px; padding: 11px 13px; font-size: .9rem; margin-bottom: 15px; font-weight: 600; }
@media (max-width: 860px) {
  html, body.login-split { overflow-x: hidden; max-width: 100%; }
  .login-split { display: block; min-height: 100dvh; }
  .login-hero { display: block; width: 100%; min-height: 190px; padding: calc(90px + var(--sat)) 22px 22px; box-sizing: border-box; }
  .login-hero::before, .login-hero::after { position: absolute; }
  .login-hero-inner { display: block; position: relative; width: 100%; max-width: 100%; padding: 0; box-sizing: border-box; }
  .login-hero-logo { width: 44px; height: 44px; margin-bottom: 10px; }
  .login-hero .eyebrow { font-size: .78rem; margin-bottom: 8px; }
  .login-hero h1 { font-size: 1.35rem; }
  .login-hero .sources { font-size: .84rem; margin-top: 9px; }
  .login-main { display: block; width: 100%; max-width: 100%; padding: 20px 18px calc(44px + var(--sab)); box-sizing: border-box; }
  .login-card2 { display: block; width: 100%; max-width: 100%; margin: 0; border: none; box-shadow: none; padding: 18px 0 0; box-sizing: border-box; }
  .login-card2 .input { width: 100%; max-width: 100%; box-sizing: border-box; }
  .login-card2 .lc-head { gap: 10px; }
}

/* ═══ Error page ══════════════════════════════════════════════════════════ */
.error-page { min-height: 100vh; min-height: 100dvh; display: grid; place-items: center; }
.error-box { text-align: center; padding: 40px; }
.error-box .error-code { font-size: 4.5rem; font-weight: 850; background: var(--grad); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; }

/* ═══ Hero banner ═════════════════════════════════════════════════════════ */
.hero { position: relative; overflow: hidden; border-radius: var(--radius-lg); padding: 40px 36px; background: var(--hero-bg); color: #fff; box-shadow: var(--shadow); }
.hero::before { content: ""; position: absolute; inset: 0; background: url("/static/img/banner.jpg") center center / cover no-repeat; z-index: 0; transform: scale(1.02); }
.hero::after { content: ""; position: absolute; inset: 0; z-index: 0;
  background: linear-gradient(95deg, rgba(3, 20, 12, .93) 0%, rgba(5, 30, 20, .80) 46%, rgba(7, 38, 26, .48) 100%); }
.hero > * { position: relative; z-index: 1; }
.hero h1 { color: #fff; font-size: 2rem; }
.hero .sci { font-style: italic; font-size: 2.1rem; font-weight: 850; letter-spacing: -0.03em; color: #fff; }
.hero .common { color: rgba(255, 255, 255, .78); font-size: 1.1rem; margin-top: 5px; }
.hero p { color: rgba(255, 255, 255, .82); max-width: 62ch; }
.hero .badge { background: rgba(255, 255, 255, .12); color: var(--on-dark); border-color: rgba(255, 255, 255, .2); }
.hero .badge.muted { background: rgba(255, 255, 255, .07); color: rgba(255, 255, 255, .75); }

.stat-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 16px; }
.stat-tile { position: relative; overflow: hidden; background: var(--surface); border: 1px solid var(--border-soft); border-radius: var(--radius); padding: 22px 20px; box-shadow: var(--shadow-sm); transition: transform .3s var(--ease), box-shadow .3s var(--ease); }
.stat-tile::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: var(--grad); transform: scaleX(0); transform-origin: left; transition: transform .4s var(--ease); }
.stat-tile:hover { transform: translateY(-5px); box-shadow: var(--shadow); }
.stat-tile:hover::before { transform: scaleX(1); }
.stat-tile .st-val { font-size: 2.3rem; font-weight: 860; letter-spacing: -0.04em; line-height: 1.05;
  background: var(--grad); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
  font-variant-numeric: tabular-nums; transform-origin: left center;
  filter: drop-shadow(0 3px 12px rgba(3, 199, 90, .24)); }
.stat-tile .st-label { color: var(--ink-soft); font-size: .84rem; font-weight: 660; margin-top: 4px; text-transform: uppercase; letter-spacing: .04em; }
.bar-row { display: flex; align-items: center; gap: 11px; margin: 9px 0; min-width: 0; }
.bar-row .bl { width: 132px; flex: 0 0 132px; font-size: .88rem; color: var(--ink-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 600; }
.bar-row .bt { flex: 1; height: 13px; background: var(--track); border-radius: 999px; overflow: hidden; }
.bar-row .bt > i { display: block; height: 100%; background: var(--grad); border-radius: 999px; box-shadow: var(--glow); animation: growBar 1s var(--ease) both; transform-origin: left; }
@keyframes growBar { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.bar-row .bv { width: 68px; text-align: right; font-variant-numeric: tabular-nums; font-weight: 750; font-size: .88rem; }

/* ═══ Genome browser — canvas (UCSC-style, legacy .gb-*) ══════════════════ */
.gb-toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.gb-toolbar .btn.sm { padding: 7px 10px; }
.gb-region { font-family: var(--mono); font-size: .84rem; font-weight: 700; color: var(--primary-ink); background: var(--grad-soft); border: 1px solid var(--primary-tint-2); padding: 6px 11px; border-radius: 8px; white-space: nowrap; }
.gb-count { font-size: .84rem; color: var(--ink-soft); font-weight: 600; }
.gb-overview { position: relative; border: 1px solid var(--border-soft); border-radius: 12px; background: var(--surface); overflow: hidden; margin-bottom: 10px; box-shadow: var(--shadow-sm); }
.gb-overview canvas { display: block; width: 100%; height: 54px; cursor: crosshair; }
.gb-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 4px 2px 12px; }
.gb-legend .lg { display: inline-flex; align-items: center; gap: 6px; font-size: .8rem; color: var(--ink-soft); font-weight: 600; cursor: pointer; opacity: .95; transition: opacity .15s; }
.gb-legend .lg.off { opacity: .35; text-decoration: line-through; }
.gb-legend .lg .sw { width: 15px; height: 11px; border-radius: 3px; }
.gb-canvas { position: relative; border: 1px solid var(--border-soft); border-radius: var(--radius); background: var(--surface); overflow: hidden; box-shadow: var(--shadow-sm); }
#gb-main { display: block; width: 100%; cursor: grab; touch-action: none; }
#gb-main.drag { cursor: grabbing; }
.gb-tip { position: fixed; z-index: 200; pointer-events: none; background: var(--tip-bg); color: var(--code-ink); border: 1px solid rgba(255, 255, 255, .14); border-radius: 10px; padding: 9px 12px; font-size: .82rem; box-shadow: var(--shadow-pop); max-width: 300px; opacity: 0; transition: opacity .12s; line-height: 1.5; }
.gb-tip b { color: var(--mint); }
.gb-tip .mono { color: #bfeed6; }
.gb-hint { font-size: .8rem; color: var(--muted); margin: 8px 2px 0; }

.suggest { display: flex; flex-wrap: wrap; gap: 8px; }
.suggest .chip { padding: 9px 14px; border-radius: 999px; border: 1px solid var(--border); background: var(--surface); color: var(--ink-soft); font-weight: 620; font-size: .88rem; cursor: pointer; text-align: left; -webkit-tap-highlight-color: transparent; transition: all .2s var(--ease); }
.suggest .chip:hover { border-color: transparent; color: var(--primary-ink); background: var(--grad-soft); transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.suggest .chip:active { transform: scale(.97); }

.gb-slider { -webkit-appearance: none; appearance: none; width: 100%; height: 8px; border-radius: 999px;
  background: linear-gradient(90deg, var(--primary) 0%, var(--primary) var(--v, 0%), var(--track) var(--v, 0%), var(--track) 100%);
  outline: none; cursor: pointer; margin: 0; }
.gb-slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 20px; height: 20px; border-radius: 50%;
  background: var(--surface); border: 3px solid var(--primary); box-shadow: var(--glow); cursor: grab; transition: transform .12s; }
.gb-slider::-webkit-slider-thumb:active { transform: scale(1.2); cursor: grabbing; }
.gb-slider::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: var(--surface); border: 3px solid var(--primary); box-shadow: var(--glow); cursor: grab; }
.gb-pos-wrap { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.gb-pos-wrap .lbl { font-size: .8rem; color: var(--muted); font-weight: 600; white-space: nowrap; }
.gene-model { display: flex; align-items: center; gap: 2px; flex-wrap: wrap; margin: 8px 0; }
.gene-model .ex { height: 14px; background: var(--grad); border-radius: 3px; }
.gene-model .in { height: 2px; background: var(--muted); flex: 0 0 auto; }

/* ═══ Genome-browser custom track toggles ═════════════════════════════════ */
.gb-tracks { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 8px 0 4px; }
.gb-tracks-lbl { font-size: .78rem; font-weight: 750; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.gb-track-chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 11px; border: 1px solid var(--border);
  background: var(--surface); border-radius: 999px; cursor: pointer; font-size: .82rem; font-weight: 620; color: var(--ink); transition: opacity .15s; -webkit-tap-highlight-color: transparent; }
.gb-track-chip i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.gb-track-chip.off { opacity: .4; text-decoration: line-through; }
.gb-track-chip:active { transform: scale(.96); }

/* ═══ Misc utilities ══════════════════════════════════════════════════════ */
.empty { text-align: center; color: var(--muted); padding: 44px 20px; }
.empty .big-ic { opacity: .45; margin-bottom: 12px; color: var(--primary); }
.spinner-sm { width: 16px; height: 16px; border: 2px solid var(--primary-tint-2); border-top-color: var(--primary); border-radius: 50%; animation: spin .8s linear infinite; display: inline-block; vertical-align: -2px; }
.chat-msg { padding: 13px 16px; border-radius: var(--radius); margin-bottom: 11px; max-width: 85%; animation: msgIn .4s var(--ease) both; line-height: 1.65; }
@keyframes msgIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.chat-msg.me { background: var(--grad); color: #fff; margin-left: auto; box-shadow: var(--glow); }
.chat-msg.ai { background: var(--surface); border: 1px solid var(--border-soft); box-shadow: var(--shadow-sm); }
.chat-msg strong { color: var(--primary-ink); font-weight: 720; }
.chat-msg.me strong { color: #fff; }
.mono { font-family: var(--mono); }
.flex { display: flex; align-items: center; gap: 10px; }
.wrap { flex-wrap: wrap; }
.grow { flex: 1; min-width: 0; }
.mt-8 { margin-top: 8px; } .mt-16 { margin-top: 16px; } .mt-24 { margin-top: 24px; }
.muted-txt { color: var(--ink-soft); }
.scrim { display: none; }

/* ═══ Entrance reveal ═════════════════════════════════════════════════════ */
@keyframes revealUp { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
.content > *, .site-wrap > * { animation: revealUp .5s var(--ease) both; }
.content > *:nth-child(1) { animation-delay: .02s; } .content > *:nth-child(2) { animation-delay: .08s; }
.content > *:nth-child(3) { animation-delay: .14s; } .content > *:nth-child(4) { animation-delay: .2s; }
.content > *:nth-child(5) { animation-delay: .26s; }
.site-wrap > *:nth-child(1) { animation-delay: .02s; } .site-wrap > *:nth-child(2) { animation-delay: .1s; }
.site-wrap > *:nth-child(3) { animation-delay: .18s; } .site-wrap > *:nth-child(4) { animation-delay: .26s; }
.kpi-grid .kpi { animation: revealUp .55s var(--ease) both; }
.kpi-grid .kpi:nth-child(1) { animation-delay: .05s; } .kpi-grid .kpi:nth-child(2) { animation-delay: .12s; }
.kpi-grid .kpi:nth-child(3) { animation-delay: .19s; } .kpi-grid .kpi:nth-child(4) { animation-delay: .26s; }
.stat-tiles .stat-tile { animation: revealUp .55s var(--ease) both; }
.stat-tiles .stat-tile:nth-child(1) { animation-delay: .05s; } .stat-tiles .stat-tile:nth-child(2) { animation-delay: .12s; }
.stat-tiles .stat-tile:nth-child(3) { animation-delay: .19s; } .stat-tiles .stat-tile:nth-child(4) { animation-delay: .26s; }
.list-item { animation: revealUp .45s var(--ease) both; }
.list-item:nth-child(1) { animation-delay: .03s; } .list-item:nth-child(2) { animation-delay: .07s; }
.list-item:nth-child(3) { animation-delay: .11s; } .list-item:nth-child(4) { animation-delay: .15s; }
.list-item:nth-child(5) { animation-delay: .19s; } .list-item:nth-child(6) { animation-delay: .23s; }

/* ═══ Markdown rendering (AI answers) ═════════════════════════════════════ */
.md-body { line-height: 1.68; }
.md-body > *:first-child { margin-top: 0; }
.md-body > *:last-child { margin-bottom: 0; }
.md-body p { margin: 0 0 10px; }
.md-body h1, .md-body h2, .md-body h3, .md-body h4 { margin: 14px 0 8px; line-height: 1.3; }
.md-body h1 { font-size: 1.25rem; } .md-body h2 { font-size: 1.13rem; } .md-body h3 { font-size: 1.02rem; } .md-body h4 { font-size: .97rem; }
.md-body ul, .md-body ol { margin: 6px 0 12px; padding-left: 22px; }
.md-body li { margin: 3px 0; }
.md-body strong { font-weight: 720; color: var(--primary-ink); }
.md-body.on-teal strong, .md-body.on-brand strong { color: var(--on-dark); }
.md-body em { font-style: italic; }
.md-body code { font-family: var(--mono); background: var(--primary-tint); color: var(--primary-ink); padding: 1px 6px; border-radius: 6px; font-size: .88em; }
.md-body pre.md-pre { background: var(--code-bg); color: var(--code-ink); border-radius: 10px; padding: 12px 14px; overflow-x: auto; margin: 10px 0; }
.md-body pre.md-pre code { background: none; color: inherit; padding: 0; }
.md-body blockquote { border-left: 3px solid var(--primary); margin: 10px 0; padding: 4px 0 4px 14px; color: var(--ink-soft); }
.md-body a { color: var(--primary-ink); text-decoration: underline; }
.md-body hr { border: none; border-top: 1px solid var(--border); margin: 14px 0; }
.md-body table { border-collapse: collapse; margin: 10px 0; font-size: .9em; width: auto; min-width: 60%; }
.md-body th, .md-body td { border: 1px solid var(--border); padding: 6px 10px; }
.md-body th { background: var(--surface-2); }

/* consult page chrome */
.consult-msgs { display: flex; flex-direction: column; }
.chat-msg.ai { max-width: 100%; }
.chat-msg.me { max-width: 85%; }
.consult-head { display: flex; align-items: center; gap: 10px; }
.suggest-wrap { margin-top: 14px; }
.md-tablewrap { overflow-x: auto; margin: 10px 0; -webkit-overflow-scrolling: touch; }
.msg-actions { display: flex; gap: 6px; margin-top: 10px; opacity: .7; transition: opacity .2s; }
.chat-msg:hover .msg-actions { opacity: 1; }
.msg-actions .btn.sm { padding: 4px 10px; font-size: .8rem; }

/* ═══ Command palette ═════════════════════════════════════════════════════ */
.gpal-backdrop { position: fixed; inset: 0; background: var(--scrim-bg); backdrop-filter: blur(4px); z-index: 400; display: none; align-items: flex-start; justify-content: center; padding-top: 12vh; }
.gpal-backdrop.on { display: flex; animation: fade .18s var(--ease); }
.gpal { width: min(620px, 94vw); background: var(--surface); border: 1px solid var(--border-soft); border-radius: 16px; box-shadow: var(--shadow-pop); overflow: hidden; animation: modalIn .25s var(--ease); }
.gpal-in { display: flex; align-items: center; gap: 10px; padding: 14px 18px; border-bottom: 1px solid var(--border-soft); }
.gpal-ic { color: var(--muted); display: grid; place-items: center; }
.gpal-in input { flex: 1; border: none; outline: none; font-size: 1.07rem; background: transparent; color: var(--ink); }
.gpal-list { max-height: 52vh; overflow-y: auto; }
.gpal-item { display: flex; align-items: center; gap: 10px; padding: 11px 18px; cursor: pointer; }
.gpal-item.active { background: var(--grad-soft); }
.gpal-sym { font-weight: 750; }
.gpal-name { flex: 1; color: var(--ink-soft); font-size: .88rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gpal-empty { padding: 26px; text-align: center; color: var(--muted); }
.gpal-foot { padding: 9px 18px; border-top: 1px solid var(--border-soft); font-size: .78rem; color: var(--muted); background: var(--surface-2); }

/* ═══ Splash screen ═══════════════════════════════════════════════════════ */
/* Splash is the one place pure #03C75A belongs: full-strength brand, no text
   sitting on the coloured pixels (the title is on the near-black backdrop). */
#pgb-splash { position: fixed; inset: 0; z-index: 9999; display: grid; place-items: center;
  background: linear-gradient(120deg, #05170f, #0a2a1c 35%, #072a26 70%, #041a10);
  background-size: 200% 200%; animation: pgb-gradient 12s ease infinite;
  transition: opacity .55s ease, visibility .55s; }
#pgb-splash::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(ellipse at 50% 42%, rgba(3, 199, 90, .30), transparent 55%),
    radial-gradient(ellipse at 20% 80%, rgba(22, 169, 155, .22), transparent 50%); }
#pgb-splash.hide { opacity: 0; visibility: hidden; }
#pgb-splash .sp-in { text-align: center; position: relative; z-index: 1; }
#pgb-splash img { width: 96px; height: 96px; filter: drop-shadow(0 10px 38px rgba(199, 138, 32, .65)); animation: splashPulse 1.6s ease-in-out infinite; }
#pgb-splash .sp-name { color: #fff; font-weight: 850; font-size: 1.5rem; margin-top: 16px; letter-spacing: -.02em; text-shadow: 0 2px 26px rgba(3, 150, 70, .65); }
#pgb-splash .sp-sub { color: rgba(255, 255, 255, .62); font-size: .88rem; margin-top: 6px; }
#pgb-splash .sp-bar { width: 150px; height: 4px; border-radius: 999px; background: rgba(255, 255, 255, .14); margin: 18px auto 0; overflow: hidden; }
#pgb-splash .sp-bar i { display: block; height: 100%; width: 40%; border-radius: 999px;
  background: linear-gradient(90deg, transparent, var(--mint), var(--brand), transparent);
  animation: splashBar 1.1s ease-in-out infinite; }
#pgb-splash .sp-foot { position: absolute; bottom: calc(28px + var(--sab)); left: 0; right: 0; text-align: center; font-size: .78rem; letter-spacing: .2em; color: rgba(255, 255, 255, .32); }
#pgb-splash .sp-ring { position: absolute; inset: 6px; border-radius: 9999px; border: 1.5px solid transparent;
  border-top-color: rgba(184, 134, 11, .9); border-right-color: rgba(22, 224, 192, .55); animation: splashRing 2.6s linear infinite; }
#pgb-splash .sp-ring-2 { inset: -10px; border-top-color: rgba(3, 199, 90, .7); border-right-color: transparent;
  border-bottom-color: rgba(16, 185, 129, .45); animation: splashRingRev 4.2s linear infinite; }
@keyframes splashPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.08); } }
@keyframes splashBar { 0% { margin-left: -40%; } 100% { margin-left: 110%; } }
@keyframes splashRing { to { transform: rotate(360deg); } }
@keyframes splashRingRev { to { transform: rotate(-360deg); } }
@media (prefers-reduced-motion: reduce) {
  #pgb-splash .sp-ring, #pgb-splash .sp-bar i { animation: none; }
  #pgb-splash .sp-ring { border-color: rgba(184, 134, 11, .5); }
}

/* ═══ Region-analysis / docking result extras ═════════════════════════════ */
.ra-kvs { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
.ra-kv { display: flex; justify-content: space-between; gap: 8px; padding: 8px 12px;
  background: var(--surface-2); border: 1px solid var(--border-soft); border-radius: var(--radius-sm); }
.ra-k { font-size: .8rem; color: var(--ink-soft); font-weight: 600; }
.ra-v { font-weight: 750; }
.ra-cached { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px;
  padding: 8px 12px; background: var(--ok-bg); border-radius: var(--radius-sm); font-size: .86rem; color: var(--ink-soft); }
.dock-preset { cursor: pointer; }

/* ═══ Species-site home search box ════════════════════════════════════════ */
.home-search .hs-wrap { display: flex; align-items: center; gap: 10px; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-sm);
  padding: 8px 10px 8px 16px; }
.home-search .hs-wrap:focus-within { border-color: var(--primary); box-shadow: var(--glow); }
.home-search .hs-ic { color: var(--muted); display: grid; place-items: center; flex: 0 0 auto; }
.home-search #home-q { flex: 1; min-width: 0; border: none; box-shadow: none; padding: 8px 0; font-size: 1.02rem; background: transparent; }
.home-search #home-go { flex: 0 0 auto; }

/* ═══ Private-site password gate ══════════════════════════════════════════ */
.lock-body { min-height: 100vh; min-height: 100dvh; display: grid; place-items: center; padding: 24px;
  background: radial-gradient(circle at 50% 34%, #0b2419, #041a10 78%); }
.lock-card { width: min(400px, 94vw); text-align: center; background: rgba(255, 255, 255, .04);
  border: 1px solid rgba(255, 255, 255, .09); border-radius: var(--radius-lg);
  padding: 40px 34px; box-shadow: var(--shadow-pop); backdrop-filter: blur(8px); }
.lock-logo { width: 76px; height: 76px; filter: drop-shadow(0 8px 26px rgba(3, 199, 90, .6)); }
.lock-title { color: var(--on-dark); font-size: 1.5rem; font-style: italic; margin: 14px 0 6px; }
.lock-sub { color: #9fc2ae; font-size: .94rem; margin: 0 0 18px; }
.lock-err { color: #ff9c9c; font-size: .88rem; margin: 0 0 12px; }
.lock-form { display: flex; flex-direction: column; gap: 10px; }
.lock-form .input { text-align: center; background: rgba(255, 255, 255, .9); color: #16241c; }
.lock-form .btn { width: 100%; padding: 13px; }

/* ═══ Scientific-name autocomplete ════════════════════════════════════════ */
/* dropdown is fixed-positioned on <body> (above the modal, z 300) so the
   modal-body overflow never clips it; JS sets left/top/width from the input */
.ac-list { position: fixed; z-index: 400; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-pop); max-height: 300px; overflow-y: auto; padding: 5px; }
.ac-item { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; padding: 8px 11px; border-radius: 8px; cursor: pointer; line-height: 1.35; }
.ac-item.active { background: var(--grad-soft); }
.ac-sci { font-weight: 680; font-style: italic; color: var(--ink); }
.ac-common { font-size: .84rem; color: var(--ink-soft); }
.ac-group { font-size: .8rem; color: var(--muted); margin-left: auto; white-space: nowrap; }

/* Stat numbers stay on one line; app.js fitNumber() shrinks the font to fit. */
.kpi .kpi-val, .stat-tile .st-val { white-space: nowrap; }

/* ═══ Protein structure page ══════════════════════════════════════════════ */
.pp-sec { margin: 0 0 14px; }
.pp-sec:last-child { margin-bottom: 0; }
.pp-h { font-size: .78rem; font-weight: 750; letter-spacing: .05em; text-transform: uppercase; color: var(--ink-soft); margin-bottom: 5px; }
.pp-sec .badge { margin: 0 4px 4px 0; }
.pp-go { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px; margin-bottom: 6px; }
.pp-go-lab { font-size: .78rem; font-weight: 700; color: var(--muted); min-width: 84px; }
.pp-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: 10px; }
.pp-tile { background: var(--surface-2); border: 1px solid var(--border-soft); border-radius: var(--radius-sm); padding: 10px 12px; text-align: center; }
.pp-tile .pp-tv { font-weight: 820; font-size: 1.15rem; color: var(--primary-ink); letter-spacing: -.02em; white-space: nowrap; }
.pp-tile .pp-tl { font-size: .78rem; color: var(--ink-soft); margin-top: 2px; text-transform: uppercase; letter-spacing: .03em; }
.pp-comp { display: flex; align-items: flex-end; gap: 3px; height: 72px; padding-top: 6px; overflow-x: auto; }
.pp-cc { flex: 1 0 16px; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; gap: 3px; }
.pp-cc > i { display: block; width: 100%; min-height: 2px; background: var(--grad); border-radius: 3px 3px 0 0; }
.pp-cc > span { font-size: .68rem; color: var(--muted); font-variant-numeric: tabular-nums; }
/* Sequences are the one place break-all is right: an amino-acid string has no
   words, so keeping "the longest word" intact would overflow every container. */
.pp-seq { margin: 12px 0 0; padding: 12px 14px; background: var(--code-bg); color: var(--code-ink); border-radius: var(--radius-sm); font-size: .76rem; line-height: 1.7; letter-spacing: .06em; max-height: 210px; overflow: auto; white-space: pre-wrap; word-break: break-all; font-family: var(--mono); }

/* ═══ PWA install button (floating, bottom-right) ═════════════════════════ */
.gb-install {
  position: fixed;
  right: calc(20px + var(--sar));
  bottom: calc(20px + var(--sab));
  z-index: 150;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 11px 17px;
  font: inherit; font-weight: 650; font-size: .94rem;
  color: #fff; background: var(--grad); border: none;
  border-radius: 999px; cursor: pointer;
  box-shadow: var(--glow), var(--shadow);
  opacity: 0; transform: translateY(16px) scale(.96);
  transition: opacity .3s var(--ease), transform .3s var(--ease), box-shadow .2s;
  -webkit-tap-highlight-color: transparent;
}
.gb-install.show { opacity: 1; transform: translateY(0) scale(1); }
.gb-install:hover { box-shadow: var(--glow-strong), var(--shadow-pop); transform: translateY(-1px) scale(1.02); }
.gb-install:active { transform: translateY(0) scale(.97); }
.gb-install .ic-svg { display: block; }
.pwa-ios { line-height: 1.7; color: var(--ink-soft); }
.pwa-ios-glyph { display: inline-flex; vertical-align: middle; color: var(--primary-ink); }
@media (prefers-reduced-motion: reduce) {
  .gb-install { transition: opacity .2s; transform: none; }
  .gb-install.show { transform: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   1 · KINSHIP HEATMAP
   ---------------------------------------------------------------------------
   .kin              outer block (card-like, owns the CSS vars)
   .kin-scroll       the ONLY horizontally scrolling element
   .kin-frame        grid: corner | x-axis / y-axis | matrix
   .kin-corner       top-left dead cell
   .kin-axis-x  .kin-axis-y      label strips
   .kin-lab          one axis label (.is-hl when its row/col is hovered)
   .kin-grid         the matrix itself (CSS grid, --kin-n columns)
   .kin-cell         one pairwise cell — JS sets inline `background`
   .kin-cell.is-diag   self-comparison (φ = 0.5), drawn hollow
   .kin-cell.is-hl     row/col crosshair (JS toggles)
   .kin-cell.is-sel    clicked pair, persists
   .kin-cell.is-na     no call / missing data
   .kin-legend  .kin-ramp  .kin-ticks  .kin-tick
   .kin-tip          hover tooltip (position:fixed, JS sets left/top)
   .kin-tip-pair  .kin-tip-row  .kin-tip-k  .kin-tip-v
   .kin-empty
   ═══════════════════════════════════════════════════════════════════════════ */
.kin {
  /* Cell size is a variable so the same markup serves a 40×40 preview and a
     600×600 cohort matrix — JS only has to set --kin-cell and --kin-n. */
  --kin-cell: 26px;
  --kin-gut: 132px;          /* y-axis label gutter */
  --kin-n: 20;
  position: relative;
}
.kin-scroll {
  overflow: auto; -webkit-overflow-scrolling: touch;
  /* contain so a fast horizontal flick over the matrix never chains into the
     page and triggers back-navigation on iOS */
  overscroll-behavior: contain;
  max-width: 100%; max-height: 72vh;
  border: 1px solid var(--border-soft); border-radius: var(--radius);
  background: var(--surface); box-shadow: var(--shadow-sm);
  padding: 0;
}
.kin-frame {
  display: grid;
  grid-template-columns: var(--kin-gut) max-content;
  grid-template-rows: auto max-content;
  width: max-content; min-width: 100%;
}
.kin-corner { position: sticky; left: 0; top: 0; z-index: 3; background: var(--surface-2); border-right: 1px solid var(--border-soft); border-bottom: 1px solid var(--border-soft); }
.kin-axis-x {
  grid-column: 2; grid-row: 1;
  position: sticky; top: 0; z-index: 2;
  display: grid; grid-template-columns: repeat(var(--kin-n), var(--kin-cell));
  background: var(--surface-2); border-bottom: 1px solid var(--border-soft);
  /* Vertical labels: sample IDs are 8-16 chars and would need ~110px each if
     laid flat, which makes the matrix unreadably wide. Rotating trades width
     for a fixed 108px header band. */
  height: 108px;
}
.kin-axis-x .kin-lab {
  writing-mode: vertical-rl; transform: rotate(180deg);
  align-items: flex-end; justify-content: flex-start;
  padding: 0 0 6px; text-align: left;
}
.kin-axis-y {
  grid-column: 1; grid-row: 2;
  position: sticky; left: 0; z-index: 2;
  display: grid; grid-auto-rows: var(--kin-cell);
  background: var(--surface-2); border-right: 1px solid var(--border-soft);
}
.kin-axis-y .kin-lab { justify-content: flex-end; padding: 0 8px 0 6px; }
.kin-lab {
  display: flex; align-items: center;
  font-size: .78rem; font-weight: 620; color: var(--ink-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  line-height: var(--kin-cell); cursor: default; transition: color .12s, background .12s;
}
.kin-lab.is-hl { color: var(--primary-ink); background: var(--primary-tint); font-weight: 780; }
.kin-grid {
  grid-column: 2; grid-row: 2;
  display: grid; grid-template-columns: repeat(var(--kin-n), var(--kin-cell));
  grid-auto-rows: var(--kin-cell);
  /* 1px collapsed gaps read as a grid without costing a border on every cell —
     at 600 samples that is 360 000 borders and it visibly janks the paint. */
  gap: 0; background: var(--border-soft);
  touch-action: pan-x pan-y;
}
.kin-cell {
  width: var(--kin-cell); height: var(--kin-cell);
  outline: .5px solid var(--surface); outline-offset: -.5px;
  /* background comes from JS (inline style) — the ramp is data, not design */
  cursor: crosshair; position: relative;
  transition: outline-color .1s, transform .1s;
  -webkit-tap-highlight-color: transparent;
}
.kin-cell.is-diag { background: repeating-linear-gradient(45deg, var(--border) 0 3px, var(--surface-2) 3px 6px) !important; cursor: default; }
.kin-cell.is-na { background: var(--surface-2) !important; }
.kin-cell.is-na::after { content: ""; position: absolute; inset: 40%; border-radius: 50%; background: var(--muted); opacity: .4; }
.kin-cell.is-hl { outline: 1.5px solid var(--primary-ink); outline-offset: -1.5px; z-index: 1; }
.kin-cell.is-sel { outline: 2.5px solid var(--ink); outline-offset: -2.5px; z-index: 2; }
.kin-cell:hover { transform: scale(1.35); z-index: 4; box-shadow: var(--shadow); }

.kin-legend { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 12px; font-size: .8rem; color: var(--ink-soft); }
.kin-ramp { flex: 1 1 200px; min-width: 140px; height: 12px; border-radius: 999px; border: 1px solid var(--border-soft);
  /* default ramp: unrelated → duplicate. JS may override for other metrics. */
  background: linear-gradient(90deg, var(--surface-2) 0%, #d7f0e0 22%, #7fd6a2 45%, #16a05a 68%, #05532c 100%); }
.kin-ticks { display: flex; justify-content: space-between; flex: 1 1 200px; min-width: 140px; font-variant-numeric: tabular-nums; }
.kin-tick { font-size: .78rem; color: var(--muted); }

.kin-tip {
  position: fixed; z-index: 220; pointer-events: none; opacity: 0;
  background: var(--tip-bg); color: var(--code-ink);
  border: 1px solid rgba(255, 255, 255, .14); border-radius: 10px;
  padding: 9px 12px; font-size: .82rem; line-height: 1.5; max-width: 260px;
  box-shadow: var(--shadow-pop); transition: opacity .12s;
}
.kin-tip.on { opacity: 1; }
.kin-tip-pair { font-weight: 780; color: #fff; margin-bottom: 4px; word-break: break-all; }
.kin-tip-row { display: flex; gap: 10px; justify-content: space-between; }
.kin-tip-k { color: rgba(255, 255, 255, .6); }
.kin-tip-v { font-variant-numeric: tabular-nums; color: var(--mint); font-weight: 700; }
.kin-empty { padding: 44px 20px; text-align: center; color: var(--muted); }

/* ═══════════════════════════════════════════════════════════════════════════
   2 · RELATIONSHIP BADGES
   ---------------------------------------------------------------------------
   .rel              base pill  (+ .rel-sm  .rel-lg  .rel-solid)
   .rel-dup   duplicate / MZ twin      φ ≈ 0.5
   .rel-po    parent–offspring         φ ≈ 0.25, IBD0 ≈ 0
   .rel-fs    full sibling             φ ≈ 0.25, IBD0 ≈ 0.25
   .rel-deg2  2nd degree (half-sib, grandparent, avuncular)
   .rel-deg3  3rd degree (first cousin)
   .rel-deg4  4th degree / distant
   .rel-un    unrelated
   .rel-dot   leading colour dot
   .rel-phi   trailing φ value (tabular)
   .rel-key   legend row wrapper
   ---------------------------------------------------------------------------
   Intensity decreases monotonically DUP→UN so a column of these reads as a
   gradient even before you parse the abbreviations. Hue also shifts green→grey
   rather than green→red: distance is not an error state.
   ═══════════════════════════════════════════════════════════════════════════ */
.rel {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 10px; border-radius: 999px;
  font-size: .78rem; font-weight: 760; letter-spacing: .01em;
  white-space: nowrap; border: 1px solid transparent;
  font-variant-numeric: tabular-nums;
}
.rel-sm { padding: 1px 7px; font-size: .72rem; }
.rel-lg { padding: 6px 14px; font-size: .9rem; }
.rel-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: 0 0 7px; }
.rel-phi { opacity: .72; font-weight: 640; }

.rel-dup  { background: #04532c; color: #eafcf1; border-color: #04532c; }
.rel-po   { background: #067a3e; color: #f2fff7; border-color: #067a3e; }
.rel-fs   { background: #0ba055; color: #ffffff; border-color: #0ba055; }
.rel-deg2 { background: #cdf0dc; color: #05633a; border-color: #a8e2c4; }
.rel-deg3 { background: #e2f6ea; color: #10794b; border-color: #cbecd9; }
.rel-deg4 { background: #eef4f0; color: #4b6558; border-color: #dfe9e3; }
.rel-un   { background: var(--chip-bg); color: var(--muted); border-color: var(--border-soft); }
/* Solid variant for the two closest classes when they appear ON a heatmap cell,
   where the light fills of deg3/deg4 would disappear into the ramp. */
.rel-solid { background: var(--ink); color: #fff; border-color: transparent; }

html.dark .rel-dup  { background: #0a7a44; color: #eafcf1; border-color: #0a7a44; }
html.dark .rel-po   { background: #0d9a55; color: #fff; border-color: #0d9a55; }
html.dark .rel-fs   { background: #17b46a; color: #06210f; border-color: #17b46a; }
html.dark .rel-deg2 { background: rgba(23, 180, 106, .26); color: #7fe6ad; border-color: rgba(23, 180, 106, .4); }
html.dark .rel-deg3 { background: rgba(23, 180, 106, .15); color: #63d69b; border-color: rgba(23, 180, 106, .26); }
html.dark .rel-deg4 { background: rgba(255, 255, 255, .06); color: #9db5a7; border-color: rgba(255, 255, 255, .1); }

.rel-key { display: flex; flex-wrap: wrap; gap: 6px 8px; align-items: center; }

/* ═══════════════════════════════════════════════════════════════════════════
   3 · RISK BANDS
   ---------------------------------------------------------------------------
   .risk             inline chip (+ .risk-sm .risk-lg)
   .risk-excellent  .risk-good  .risk-caution  .risk-high  .risk-critical
                     — the band modifier; also valid on .risk-card
   .risk-dot         leading dot
   .risk-card        breeding-recommendation card (left rail is band-coloured)
   .risk-card-head  .risk-card-title  .risk-card-sub  .risk-card-body
   .risk-score       big number
   .risk-score-unit
   .risk-meter  .risk-meter > i     filled gauge in the band colour
   .risk-scale  .risk-scale-seg  .risk-scale-mark   5-segment reference strip
   .risk-note        one-line rationale
   ---------------------------------------------------------------------------
   Colour is never the only channel: every band also carries a word, and the
   card gets a 4px rail whose position/thickness is constant so the colour is
   compared against a fixed reference rather than judged in isolation.
   ═══════════════════════════════════════════════════════════════════════════ */
.risk {
  --risk-c: var(--green); --risk-bg: var(--green-bg); --risk-ink: var(--green-ink);
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 11px; border-radius: 999px;
  background: var(--risk-bg); color: var(--risk-ink);
  border: 1px solid color-mix(in srgb, var(--risk-c) 34%, transparent);
  font-size: .8rem; font-weight: 750; white-space: nowrap;
}
.risk-sm { padding: 1px 8px; font-size: .74rem; }
.risk-lg { padding: 6px 15px; font-size: .94rem; }
.risk-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--risk-c); flex: 0 0 8px; }

.risk-excellent { --risk-c: #0a9d52; --risk-bg: #dcf4e5; --risk-ink: #076436; }
.risk-good      { --risk-c: #6cbf3f; --risk-bg: #e9f6dd; --risk-ink: #47771f; }
.risk-caution   { --risk-c: #f59e0b; --risk-bg: #fdf0d5; --risk-ink: #96650a; }
.risk-high      { --risk-c: #ea7317; --risk-bg: #fdeada; --risk-ink: #a04b06; }
.risk-critical  { --risk-c: #de3a3a; --risk-bg: #fce4e4; --risk-ink: #a72222; }

html.dark .risk-excellent { --risk-c: #3fce77; --risk-bg: rgba(63, 206, 119, .16); --risk-ink: #7fe6ad; }
html.dark .risk-good      { --risk-c: #8fd85f; --risk-bg: rgba(143, 216, 95, .16); --risk-ink: #b6e894; }
html.dark .risk-caution   { --risk-c: #fbbf24; --risk-bg: rgba(251, 191, 36, .16); --risk-ink: #fcd34d; }
html.dark .risk-high      { --risk-c: #fb923c; --risk-bg: rgba(251, 146, 60, .16); --risk-ink: #fdba74; }
html.dark .risk-critical  { --risk-c: #f26a6a; --risk-bg: rgba(242, 106, 106, .16); --risk-ink: #ff9c9c; }

.risk-card {
  position: relative; overflow: hidden;
  background: var(--surface); border: 1px solid var(--border-soft);
  border-radius: var(--radius); box-shadow: var(--shadow-sm);
  padding: 18px 20px 18px 24px;
  transition: transform .25s var(--ease), box-shadow .25s var(--ease);
}
.risk-card::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; background: var(--risk-c); }
.risk-card:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
.risk-card-head { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.risk-card-title { font-weight: 780; font-size: 1.02rem; min-width: 0; flex: 1; }
.risk-card-sub { color: var(--ink-soft); font-size: .86rem; }
.risk-card-body { margin-top: 12px; }
.risk-score { font-size: 2rem; font-weight: 860; letter-spacing: -.035em; line-height: 1.05; color: var(--risk-c); font-variant-numeric: tabular-nums; }
.risk-score-unit { font-size: .86rem; font-weight: 660; color: var(--ink-soft); margin-left: 4px; }
.risk-meter { height: 9px; border-radius: 999px; background: var(--track); overflow: hidden; margin-top: 10px; }
.risk-meter > i { display: block; height: 100%; border-radius: 999px; background: var(--risk-c); transition: width .45s var(--ease); }
/* The 5-segment strip is the fixed reference the card's colour is read against.
   Without it "orange" is meaningless; with it you can see it is band 4 of 5. */
.risk-scale { display: flex; gap: 2px; margin-top: 10px; }
.risk-scale-seg { flex: 1; height: 6px; border-radius: 3px; background: var(--track); transition: background .25s; }
.risk-scale-seg:nth-child(1) { background: #0a9d52; } .risk-scale-seg:nth-child(2) { background: #6cbf3f; }
.risk-scale-seg:nth-child(3) { background: #f59e0b; } .risk-scale-seg:nth-child(4) { background: #ea7317; }
.risk-scale-seg:nth-child(5) { background: #de3a3a; }
.risk-scale-seg:not(.is-on) { background: var(--track); }
.risk-scale-mark { position: relative; }
.risk-scale-mark::after { content: ""; position: absolute; top: -4px; left: 50%; width: 2px; height: 14px; background: var(--ink); border-radius: 1px; }
.risk-note { margin-top: 10px; font-size: .86rem; color: var(--ink-soft); line-height: 1.6; }

/* ═══════════════════════════════════════════════════════════════════════════
   4 · GENOME BROWSER — DOM tracks  (prefix .gbx-, distinct from the legacy
       canvas browser above, which keeps the .gb- prefix)
   ---------------------------------------------------------------------------
   .gbx                       root; owns --gbx-gutter / --gbx-lane-h
   .gbx-head  .gbx-locus  .gbx-tools
   .gbx-ideogram              chromosome strip
   .gbx-ideo-chr  .gbx-ideo-band  .gbx-ideo-band.is-cen (centromere)
   .gbx-ideo-view             translucent box marking the visible window
   .gbx-ruler  .gbx-ruler-tick  .gbx-ruler-lab  .gbx-scalebar
   .gbx-body                  clips the lanes; owns the drag cursor
   .gbx-lane  .gbx-lane-label  .gbx-lane-sub  .gbx-lane-track
   .gbx-lane.is-dense         collapsed multi-row features
   .gbx-feat                  a feature block (JS sets left/width inline)
   .gbx-feat-exon  .gbx-feat-intron  .gbx-feat-label
   .gbx-feat.is-fwd / .is-rev strand arrowheads
   .gbx-var                   a variant tick
   .gbx-var.is-hom / .is-het / .is-ref
   .gbx-viewport              the pan/zoom capture surface
   .gbx-sel                   drag-selection box
   .gbx-cursor                vertical hairline follows the pointer
   .gbx-zoom  .gbx-zoom-btn
   .gbx-legend  .gbx-legend-item  .gbx-sw
   .gbx-empty
   ═══════════════════════════════════════════════════════════════════════════ */
.gbx {
  /* Lane labels live in a fixed left gutter so the tracks all start at the
     same x — otherwise every zoom re-aligns and the eye loses the position. */
  --gbx-gutter: 148px;
  --gbx-lane-h: 34px;
  position: relative;
  border: 1px solid var(--border-soft); border-radius: var(--radius);
  background: var(--surface); box-shadow: var(--shadow-sm); overflow: hidden;
}
.gbx-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 12px 14px; border-bottom: 1px solid var(--border-soft); background: var(--surface-2); }
.gbx-locus { font-family: var(--mono); font-size: .84rem; font-weight: 700; color: var(--primary-ink); background: var(--primary-tint); border: 1px solid var(--primary-tint-2); padding: 5px 10px; border-radius: 8px; white-space: nowrap; }
.gbx-tools { display: flex; align-items: center; gap: 6px; margin-left: auto; flex-wrap: wrap; }

.gbx-ideogram { position: relative; padding: 12px var(--gbx-pad, 14px) 10px calc(var(--gbx-gutter) + 14px); background: var(--surface); }
.gbx-ideo-chr { position: relative; height: 14px; border-radius: 7px; background: var(--surface-2); border: 1px solid var(--border); overflow: hidden; }
/* Giemsa bands are data — JS sets the greyscale inline. Only geometry here. */
.gbx-ideo-band { position: absolute; top: 0; bottom: 0; }
.gbx-ideo-band.is-cen { background: repeating-linear-gradient(45deg, var(--muted) 0 2px, transparent 2px 4px); }
.gbx-ideo-view {
  position: absolute; top: -3px; bottom: -3px; min-width: 3px;
  border: 2px solid var(--primary-ink); border-radius: 5px;
  background: var(--primary-tint); pointer-events: none;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .7);
  transition: left .18s var(--ease), width .18s var(--ease);
}

.gbx-ruler { position: relative; height: 26px; margin-left: var(--gbx-gutter); border-bottom: 1px solid var(--border-soft); background: var(--surface); user-select: none; }
.gbx-ruler-tick { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--border); }
.gbx-ruler-tick.is-major { background: var(--muted); }
.gbx-ruler-lab { position: absolute; top: 4px; font-size: .72rem; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; transform: translateX(4px); }
.gbx-scalebar { position: absolute; right: 10px; bottom: 4px; display: flex; align-items: center; gap: 6px; font-size: .72rem; color: var(--muted); }
.gbx-scalebar::before { content: ""; width: var(--gbx-scale-w, 60px); height: 6px; border-left: 1px solid var(--ink-soft); border-right: 1px solid var(--ink-soft); border-bottom: 1px solid var(--ink-soft); }

.gbx-body { position: relative; overflow: hidden; }
.gbx-lane { position: relative; display: flex; align-items: stretch; min-height: var(--gbx-lane-h); border-bottom: 1px solid var(--border-soft); }
.gbx-lane:last-child { border-bottom: none; }
.gbx-lane:nth-child(even) { background: var(--surface-2); }
.gbx-lane-label {
  position: sticky; left: 0; z-index: 2;
  flex: 0 0 var(--gbx-gutter); width: var(--gbx-gutter);
  display: flex; flex-direction: column; justify-content: center;
  padding: 4px 10px; background: inherit;
  border-right: 1px solid var(--border-soft);
  font-size: .8rem; font-weight: 700; color: var(--ink);
  overflow: hidden;
}
.gbx-lane-label > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gbx-lane-sub { font-size: .72rem; font-weight: 600; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gbx-lane-track { position: relative; flex: 1; min-width: 0; }
.gbx-lane.is-dense { --gbx-lane-h: 20px; }

.gbx-feat { position: absolute; top: 50%; transform: translateY(-50%); height: 13px; border-radius: 3px; background: var(--primary); min-width: 2px; }
.gbx-feat-exon { position: absolute; top: 50%; transform: translateY(-50%); height: 13px; border-radius: 2px; background: var(--primary); }
.gbx-feat-intron { position: absolute; top: 50%; height: 2px; transform: translateY(-50%); background: var(--muted); }
.gbx-feat.is-fwd::after, .gbx-feat.is-rev::after {
  content: ""; position: absolute; top: 50%; width: 0; height: 0;
  border-top: 5px solid transparent; border-bottom: 5px solid transparent; transform: translateY(-50%);
}
.gbx-feat.is-fwd::after { right: -6px; border-left: 6px solid var(--primary); }
.gbx-feat.is-rev::after { left: -6px; border-right: 6px solid var(--primary); }
.gbx-feat-label { position: absolute; top: 50%; transform: translateY(-50%); left: 100%; margin-left: 8px; font-size: .74rem; font-weight: 660; color: var(--ink-soft); white-space: nowrap; pointer-events: none; }
.gbx-var { position: absolute; top: 4px; bottom: 4px; width: 3px; border-radius: 2px; background: var(--accent); }
.gbx-var.is-hom { background: #04532c; }
.gbx-var.is-het { background: var(--brand); }
.gbx-var.is-ref { background: var(--border); }

.gbx-viewport {
  position: absolute; inset: 0; left: var(--gbx-gutter); z-index: 3;
  cursor: grab;
  /* pan-y lets the page still scroll vertically under a finger; horizontal
     movement is ours, which is what makes this usable one-handed on a phone. */
  touch-action: pan-y;
  -webkit-tap-highlight-color: transparent;
}
.gbx-viewport.is-drag { cursor: grabbing; }
.gbx-viewport.is-select { cursor: crosshair; }
.gbx-sel { position: absolute; top: 0; bottom: 0; background: var(--primary-tint-2); border-left: 1.5px solid var(--primary-ink); border-right: 1.5px solid var(--primary-ink); pointer-events: none; }
.gbx-cursor { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--primary-ink); opacity: .55; pointer-events: none; }

.gbx-zoom { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; background: var(--surface); }
.gbx-zoom-btn { border: none; background: transparent; padding: 7px 11px; cursor: pointer; color: var(--ink-soft); font-weight: 700; -webkit-tap-highlight-color: transparent; transition: background .15s, color .15s; }
.gbx-zoom-btn + .gbx-zoom-btn { border-left: 1px solid var(--border); }
.gbx-zoom-btn:hover { background: var(--primary-tint); color: var(--primary-ink); }
.gbx-zoom-btn:active { transform: scale(.95); }

.gbx-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; padding: 10px 14px; border-top: 1px solid var(--border-soft); background: var(--surface-2); }
.gbx-legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: .78rem; color: var(--ink-soft); font-weight: 620; cursor: pointer; transition: opacity .15s; }
.gbx-legend-item.is-off { opacity: .35; text-decoration: line-through; }
.gbx-sw { width: 14px; height: 10px; border-radius: 3px; flex: 0 0 14px; }
.gbx-empty { padding: 40px 20px; text-align: center; color: var(--muted); }

/* ═══════════════════════════════════════════════════════════════════════════
   5 · ADMIXTURE BAR PLOT
   ---------------------------------------------------------------------------
   .adx                root (owns --adx-h, --adx-min-w)
   .adx-scroll         horizontal scroller (only when bars hit their min width)
   .adx-plot           flex row of bars, packed edge to edge — NO gap
   .adx-bar            one sample: a vertical stack
   .adx-bar.is-sel     clicked sample
   .adx-seg            one ancestry component (JS sets height + background)
   .adx-sep            population/group divider drawn between bars
   .adx-groups  .adx-group  .adx-group-lab     population strip under the plot
   .adx-ids  .adx-id       optional per-sample id strip (rotated)
   .adx-legend  .adx-legend-item  .adx-k       K-component legend
   .adx-tip
   ---------------------------------------------------------------------------
   Bars carry NO gap and NO border: an admixture plot is read as a continuous
   field of ancestry proportion, and a 1px gutter at n=500 eats a third of the
   width and invents structure that is not in the data.
   ═══════════════════════════════════════════════════════════════════════════ */
.adx { --adx-h: 180px; --adx-min-w: 3px; position: relative; }
.adx-scroll { overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; max-width: 100%; }
.adx-plot {
  display: flex; align-items: stretch; gap: 0;
  height: var(--adx-h); width: 100%; min-width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--border-soft); border-radius: 8px; overflow: hidden;
}
.adx-bar { flex: 1 1 0; min-width: var(--adx-min-w); display: flex; flex-direction: column-reverse; position: relative; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.adx-bar:hover { filter: brightness(1.12) saturate(1.1); }
.adx-bar.is-sel { outline: 2px solid var(--ink); outline-offset: -1px; z-index: 2; }
.adx-seg { width: 100%; flex: 0 0 auto; }
.adx-sep { flex: 0 0 2px; min-width: 2px; background: var(--surface); align-self: stretch; }

.adx-groups { display: flex; gap: 0; margin-top: 4px; }
.adx-group { flex: 1 1 0; min-width: 0; text-align: center; border-top: 2px solid var(--border); padding-top: 4px; }
.adx-group-lab { font-size: .74rem; font-weight: 700; color: var(--ink-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.adx-ids { display: flex; gap: 0; height: 84px; }
.adx-id { flex: 1 1 0; min-width: var(--adx-min-w); writing-mode: vertical-rl; transform: rotate(180deg); font-size: .68rem; color: var(--muted); white-space: nowrap; overflow: hidden; }

.adx-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 10px; }
.adx-legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: .78rem; color: var(--ink-soft); font-weight: 620; }
.adx-k { width: 12px; height: 12px; border-radius: 3px; flex: 0 0 12px; }
.adx-tip { position: fixed; z-index: 220; pointer-events: none; opacity: 0; background: var(--tip-bg); color: var(--code-ink); border-radius: 10px; padding: 8px 11px; font-size: .8rem; line-height: 1.5; box-shadow: var(--shadow-pop); transition: opacity .12s; max-width: 240px; }
.adx-tip.on { opacity: 1; }

/* ═══════════════════════════════════════════════════════════════════════════
   6 · PCA / MDS SCATTER
   ---------------------------------------------------------------------------
   .pca                root
   .pca-frame          square-ish plot frame with grid background
   .pca-svg            the <svg> that fills the frame
   .pca-pt             a point (JS sets fill); .is-sel .is-dim .is-focal
   .pca-hull           population convex hull / ellipse
   .pca-axis-x  .pca-axis-y   axis titles ("PC1 — 12.4% var")
   .pca-var            the variance-explained fraction inside the axis title
   .pca-legend  .pca-legend-item  .pca-sw   wrapping legend
   .pca-tip
   ═══════════════════════════════════════════════════════════════════════════ */
.pca { position: relative; }
.pca-frame {
  position: relative; width: 100%;
  /* Square by default: a PCA read on a non-1:1 frame silently distorts the
     apparent distance between clusters, which is the entire point of the plot.
     Override aspect-ratio only when the two PCs are explicitly rescaled. */
  aspect-ratio: 1 / 1; max-height: 62vh;
  border: 1px solid var(--border-soft); border-radius: var(--radius);
  background: var(--surface);
  background-image:
    linear-gradient(to right, var(--border-soft) 1px, transparent 1px),
    linear-gradient(to bottom, var(--border-soft) 1px, transparent 1px);
  background-size: 12.5% 12.5%;
  overflow: hidden;
}
.pca-svg { display: block; width: 100%; height: 100%; touch-action: none; }
.pca-pt { cursor: pointer; transition: opacity .18s, r .12s, stroke-width .12s; stroke: var(--surface); stroke-width: 1; }
.pca-pt:hover { stroke: var(--ink); stroke-width: 1.6; }
.pca-pt.is-dim { opacity: .18; }
.pca-pt.is-sel { stroke: var(--ink); stroke-width: 2.2; }
.pca-pt.is-focal { stroke: var(--primary-ink); stroke-width: 3; }
.pca-hull { fill: currentColor; fill-opacity: .09; stroke: currentColor; stroke-width: 1.2; stroke-dasharray: 4 4; }
.pca-axis-x { text-align: center; font-size: .82rem; font-weight: 660; color: var(--ink-soft); margin-top: 8px; }
.pca-axis-y { position: absolute; left: 0; top: 50%; transform: translate(-50%, -50%) rotate(-90deg); transform-origin: center; font-size: .82rem; font-weight: 660; color: var(--ink-soft); white-space: nowrap; }
.pca-var { color: var(--muted); font-weight: 600; font-variant-numeric: tabular-nums; }
.pca-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 12px; align-items: center; }
.pca-legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: .8rem; font-weight: 620; color: var(--ink-soft); cursor: pointer; transition: opacity .15s; -webkit-tap-highlight-color: transparent; }
.pca-legend-item.is-off { opacity: .35; text-decoration: line-through; }
.pca-legend-item:active { transform: scale(.97); }
.pca-sw { width: 12px; height: 12px; border-radius: 50%; flex: 0 0 12px; border: 1px solid rgba(0, 0, 0, .12); }
.pca-tip { position: fixed; z-index: 220; pointer-events: none; opacity: 0; background: var(--tip-bg); color: var(--code-ink); border-radius: 10px; padding: 8px 11px; font-size: .8rem; box-shadow: var(--shadow-pop); transition: opacity .12s; }
.pca-tip.on { opacity: 1; }

/* ═══════════════════════════════════════════════════════════════════════════
   7 · PEDIGREE / FAMILY NETWORK  (SVG)
   ---------------------------------------------------------------------------
   .ped                root      .ped-scroll   pan container    .ped-svg
   .ped-node           <g> wrapper for one individual
   .ped-male           <rect>    square
   .ped-female         <circle>  circle
   .ped-unknown        <rect transform="rotate(45)"> diamond
   .ped-node.is-focal      the proband / selected animal
   .ped-node.is-affected   carrier / affected — filled, not just outlined
   .ped-node.is-deceased   adds the diagonal strike via .ped-strike
   .ped-strike
   .ped-label  .ped-sublabel
   .ped-edge           base line   (+ .ped-edge-po  .ped-edge-sib  .ped-edge-mate)
   .ped-sibship        the horizontal sibship bar
   .ped-legend  .ped-legend-item  .ped-legend-sym
   ---------------------------------------------------------------------------
   Shape (not colour) encodes sex — this is the standard pedigree convention and
   it survives print, greyscale and colour-blindness. Colour is reserved for
   affection status, which is app data.
   ═══════════════════════════════════════════════════════════════════════════ */
.ped { position: relative; }
.ped-scroll { overflow: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; max-width: 100%; max-height: 70vh;
  border: 1px solid var(--border-soft); border-radius: var(--radius); background: var(--surface); box-shadow: var(--shadow-sm); }
.ped-svg { display: block; touch-action: pan-x pan-y; }

.ped-node { cursor: pointer; }
.ped-male, .ped-female, .ped-unknown {
  fill: var(--surface); stroke: var(--ink-soft); stroke-width: 1.8;
  transition: fill .18s, stroke .18s, stroke-width .18s;
}
.ped-node:hover .ped-male, .ped-node:hover .ped-female, .ped-node:hover .ped-unknown { stroke: var(--primary-ink); stroke-width: 2.4; }
.ped-node.is-focal .ped-male, .ped-node.is-focal .ped-female, .ped-node.is-focal .ped-unknown { stroke: var(--primary-ink); stroke-width: 3; fill: var(--primary-tint); }
.ped-node.is-affected .ped-male, .ped-node.is-affected .ped-female, .ped-node.is-affected .ped-unknown { fill: var(--ink); stroke: var(--ink); }
.ped-node.is-carrier .ped-male, .ped-node.is-carrier .ped-female, .ped-node.is-carrier .ped-unknown { fill: url(#pedHalf); }
.ped-strike { stroke: var(--ink); stroke-width: 2; display: none; }
.ped-node.is-deceased .ped-strike { display: inline; }
.ped-label { fill: var(--ink); font-size: 12px; font-weight: 700; text-anchor: middle; pointer-events: none; }
.ped-sublabel { fill: var(--muted); font-size: 11px; text-anchor: middle; pointer-events: none; }

.ped-edge { fill: none; stroke: var(--muted); stroke-width: 1.6; }
/* Parent→offspring is the structural backbone: solid and darker.
   Sibling links are inferred, not observed, so they are dashed. */
.ped-edge-po { stroke: var(--ink-soft); stroke-width: 1.8; }
.ped-edge-sib { stroke: var(--muted); stroke-width: 1.4; stroke-dasharray: 5 4; }
.ped-edge-mate { stroke: var(--ink-soft); stroke-width: 1.8; }
.ped-edge.is-hl, .ped-edge-po.is-hl, .ped-edge-sib.is-hl { stroke: var(--primary-ink); stroke-width: 2.6; }
.ped-sibship { stroke: var(--ink-soft); stroke-width: 1.8; }

.ped-legend { display: flex; flex-wrap: wrap; gap: 8px 16px; margin-top: 12px; }
.ped-legend-item { display: inline-flex; align-items: center; gap: 7px; font-size: .8rem; color: var(--ink-soft); font-weight: 620; }
.ped-legend-sym { width: 14px; height: 14px; flex: 0 0 14px; border: 1.8px solid var(--ink-soft); background: var(--surface); }
.ped-legend-sym.is-female { border-radius: 50%; }
.ped-legend-sym.is-unknown { transform: rotate(45deg); width: 11px; height: 11px; flex-basis: 11px; }
.ped-legend-sym.is-affected { background: var(--ink); }

/* ═══════════════════════════════════════════════════════════════════════════
   8 · DENSE DATA TABLE
   ---------------------------------------------------------------------------
   .dt-wrap            the ONLY horizontal scroller — the page never scrolls
   .dt-scroll          inner element that actually overflows (max-height rows)
   table.dt            the table  (+ .dt-zebra  .dt-compact  .dt-bordered)
   .dt th              sticky header
   .dt-sort            sortable header button-ish th
   .dt-sort.is-asc / .is-desc / .is-idle    arrow state
   .dt-sort-ic         the caret
   .dt-num             right-aligned tabular column (th AND td)
   .dt-mono            monospace column (loci, alleles, IDs)
   .dt-pin             first column pinned while scrolling sideways
   .dt-row.is-sel  .dt-row.is-muted
   .dt-bar             in-cell proportion bar
   .dt-foot            summary / pagination row
   .dt-count  .dt-pager  .dt-empty
   ---------------------------------------------------------------------------
   The header is sticky INSIDE .dt-scroll rather than to the viewport, because a
   viewport-sticky header collides with the app topbar on mobile.
   ═══════════════════════════════════════════════════════════════════════════ */
.dt-wrap { border: 1px solid var(--border-soft); border-radius: var(--radius); background: var(--surface); box-shadow: var(--shadow-sm); overflow: hidden; max-width: 100%; }
.dt-scroll { overflow: auto; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; max-height: var(--dt-max-h, 68vh); max-width: 100%; }
table.dt { width: 100%; border-collapse: separate; border-spacing: 0; min-width: 520px; font-size: .88rem; }
table.dt th, table.dt td { padding: 9px 13px; text-align: left; border-bottom: 1px solid var(--border-soft); white-space: nowrap; vertical-align: middle; }
table.dt.dt-compact th, table.dt.dt-compact td { padding: 6px 10px; font-size: .84rem; }
table.dt th {
  position: sticky; top: 0; z-index: 2;
  background: var(--surface-2); color: var(--ink-soft);
  font-size: .76rem; font-weight: 760; text-transform: uppercase; letter-spacing: .045em;
  border-bottom: 1px solid var(--border);
  /* the header is translucent-free on purpose: rows sliding under a blurred
     header at 60fps was the single biggest scroll-jank source on low-end phones */
}
table.dt tbody tr:last-child td { border-bottom: none; }
table.dt.dt-zebra tbody tr:nth-child(even) td { background: var(--surface-2); }
table.dt.dt-bordered th, table.dt.dt-bordered td { border-right: 1px solid var(--border-soft); }
table.dt.dt-bordered th:last-child, table.dt.dt-bordered td:last-child { border-right: none; }
.dt-row { transition: background .14s; }
tbody .dt-row:hover td { background: var(--primary-tint); }
.dt-row.is-sel td { background: var(--grad-soft); box-shadow: inset 3px 0 0 var(--primary); }
.dt-row.is-muted td { color: var(--muted); }

.dt-sort { cursor: pointer; user-select: none; -webkit-tap-highlight-color: transparent; padding-right: 26px !important; position: relative; transition: color .15s, background .15s; }
.dt-sort:hover { color: var(--primary-ink); background: var(--primary-tint); }
.dt-sort-ic { position: absolute; right: 9px; top: 50%; transform: translateY(-50%); width: 8px; height: 12px; opacity: .35; transition: opacity .15s; }
/* Idle carets stay visible at low opacity — a sort affordance that only appears
   on hover is invisible on touch, where there is no hover at all. */
.dt-sort.is-idle .dt-sort-ic { opacity: .3; }
.dt-sort.is-asc, .dt-sort.is-desc { color: var(--primary-ink); }
.dt-sort.is-asc .dt-sort-ic, .dt-sort.is-desc .dt-sort-ic { opacity: 1; color: var(--primary-ink); }
.dt-sort.is-desc .dt-sort-ic { transform: translateY(-50%) rotate(180deg); }

/* Numeric columns right-align so the decimal points stack; tabular-nums keeps
   the digit advance constant so they actually line up. */
th.dt-num, td.dt-num { text-align: right; font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }
th.dt-mono, td.dt-mono { font-family: var(--mono); font-size: .84em; letter-spacing: -.01em; }
th.dt-pin, td.dt-pin {
  position: sticky; left: 0; z-index: 1;
  background: var(--surface); border-right: 1px solid var(--border);
}
table.dt.dt-zebra tbody tr:nth-child(even) td.dt-pin { background: var(--surface-2); }
th.dt-pin { z-index: 3; background: var(--surface-2); }
.dt-bar { display: block; height: 6px; border-radius: 999px; background: var(--track); overflow: hidden; min-width: 54px; }
.dt-bar > i { display: block; height: 100%; border-radius: 999px; background: var(--grad); }
.dt-foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 13px; border-top: 1px solid var(--border-soft); background: var(--surface-2); font-size: .82rem; color: var(--ink-soft); }
.dt-count { font-variant-numeric: tabular-nums; }
.dt-pager { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.dt-empty { padding: 40px 20px; text-align: center; color: var(--muted); }

/* ═══════════════════════════════════════════════════════════════════════════
   9 · STAT / METRIC TILE
   ---------------------------------------------------------------------------
   .metric-grid        auto-fit grid of tiles
   .metric             the tile  (+ .metric-flat for in-card use, no shadow)
   .metric-label       small caps label
   .metric-val         the big number
   .metric-unit        trailing unit, de-emphasised
   .metric-delta       change indicator  (+ .is-up .is-down .is-flat)
   .metric-delta-ic
   .metric-spark       slot for an inline sparkline <svg>/<canvas>
   .metric-foot        optional caption under the sparkline
   ---------------------------------------------------------------------------
   .is-up is NOT hard-coded to green: in this app "up" can be inbreeding
   coefficient, which is bad. Direction gets the arrow; sentiment is opt-in via
   .is-good / .is-bad on the same element.
   ═══════════════════════════════════════════════════════════════════════════ */
.metric-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px; }
.metric-grid > * { min-width: 0; }
.metric {
  position: relative; overflow: hidden;
  background: var(--surface); border: 1px solid var(--border-soft);
  border-radius: var(--radius); padding: 16px 18px; box-shadow: var(--shadow-sm);
  display: flex; flex-direction: column; gap: 2px;
  transition: transform .25s var(--ease), box-shadow .25s var(--ease);
}
.metric:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
.metric-flat { box-shadow: none; border-color: var(--border-soft); background: var(--surface-2); }
.metric-flat:hover { transform: none; box-shadow: none; }
.metric-label { font-size: .76rem; font-weight: 720; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.metric-val { font-size: 1.85rem; font-weight: 860; letter-spacing: -.035em; line-height: 1.1; color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.metric-unit { font-size: .84rem; font-weight: 660; color: var(--muted); margin-left: 3px; letter-spacing: 0; }
.metric-delta { display: inline-flex; align-items: center; gap: 4px; font-size: .8rem; font-weight: 720; font-variant-numeric: tabular-nums; color: var(--muted); margin-top: 2px; }
.metric-delta-ic { display: inline-grid; place-items: center; }
.metric-delta.is-up .metric-delta-ic { transform: rotate(0deg); }
.metric-delta.is-down .metric-delta-ic { transform: rotate(180deg); }
.metric-delta.is-flat .metric-delta-ic { opacity: .5; }
.metric-delta.is-good { color: var(--green-ink); }
.metric-delta.is-bad { color: var(--red-ink); }
.metric-spark { display: block; width: 100%; height: 34px; margin-top: 8px; overflow: hidden; }
.metric-spark svg, .metric-spark canvas { display: block; width: 100%; height: 100%; }
.metric-foot { font-size: .76rem; color: var(--muted); margin-top: 4px; }

/* ═══════════════════════════════════════════════════════════════════════════
   10 · CHAT / COPILOT
   ---------------------------------------------------------------------------
   .cop                page/panel root
   .cop-thread         scrolling message column
   .cop-turn           one exchange wrapper (+ .is-me / .is-ai)
   .cop-avatar
   .cop-msg            the bubble  (.cop-msg.is-me / .is-ai / .is-err)
   .cop-meta           timestamp / model line
   .cop-typing  .cop-typing-dot        streaming indicator
   .cop-trace          <details> disclosure: which computation actually ran
   .cop-trace-head     <summary>
   .cop-trace-body
   .cop-trace-step     one tool/kernel invocation (+ .is-ok .is-run .is-fail)
   .cop-trace-name  .cop-trace-args  .cop-trace-time
   .cop-chips  .cop-chip             suggestion chips
   .cop-composer  .cop-composer-in  .cop-send
   ---------------------------------------------------------------------------
   The trace strip is collapsed by default but always PRESENT — a population
   genetics answer is only trustworthy if you can see it came from a real KING /
   ADMIXTURE run and not from the model's prior.
   ═══════════════════════════════════════════════════════════════════════════ */
.cop { display: flex; flex-direction: column; min-height: 0; }
.cop-thread { display: flex; flex-direction: column; gap: 16px; overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; padding: 4px 2px; min-height: 0; }
.cop-turn { display: flex; gap: 10px; align-items: flex-start; animation: msgIn .4s var(--ease) both; }
.cop-turn.is-me { flex-direction: row-reverse; }
.cop-avatar { width: 30px; height: 30px; flex: 0 0 30px; border-radius: 9px; display: grid; place-items: center; background: var(--grad-soft); color: var(--primary-ink); font-weight: 800; font-size: .8rem; border: 1px solid var(--border-soft); }
.cop-turn.is-me .cop-avatar { background: var(--grad); color: #fff; border-color: transparent; }
.cop-msg {
  max-width: min(72ch, 86%); min-width: 0;
  padding: 12px 15px; border-radius: 14px; line-height: 1.68;
  background: var(--surface); border: 1px solid var(--border-soft); box-shadow: var(--shadow-sm);
}
.cop-msg.is-me { background: var(--grad); color: #fff; border-color: transparent; box-shadow: var(--glow); border-bottom-right-radius: 5px; }
.cop-msg.is-ai { border-bottom-left-radius: 5px; }
.cop-msg.is-err { border-color: color-mix(in srgb, var(--red) 40%, transparent); background: var(--red-bg); color: var(--red-ink); }
.cop-msg.is-me a, .cop-msg.is-me strong { color: #fff; }
.cop-msg > *:first-child { margin-top: 0; }
.cop-msg > *:last-child { margin-bottom: 0; }
.cop-meta { font-size: .74rem; color: var(--muted); margin-top: 5px; font-variant-numeric: tabular-nums; }

.cop-typing { display: inline-flex; gap: 4px; align-items: center; padding: 4px 2px; }
.cop-typing-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--muted); animation: copDot 1.1s ease-in-out infinite; }
.cop-typing-dot:nth-child(2) { animation-delay: .16s; }
.cop-typing-dot:nth-child(3) { animation-delay: .32s; }
@keyframes copDot { 0%, 60%, 100% { opacity: .3; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }

.cop-trace { margin-top: 10px; border: 1px solid var(--border-soft); border-radius: 10px; background: var(--surface-2); overflow: hidden; }
.cop-trace-head {
  display: flex; align-items: center; gap: 8px; cursor: pointer; list-style: none;
  padding: 7px 11px; font-size: .78rem; font-weight: 700; color: var(--ink-soft);
  -webkit-tap-highlight-color: transparent; transition: background .15s, color .15s;
}
.cop-trace-head::-webkit-details-marker { display: none; }
.cop-trace-head::after { content: "▾"; margin-left: auto; font-size: .7rem; opacity: .6; transition: transform .2s var(--ease); }
.cop-trace[open] .cop-trace-head::after { transform: rotate(180deg); }
.cop-trace-head:hover { background: var(--primary-tint); color: var(--primary-ink); }
.cop-trace-body { padding: 4px 11px 10px; display: flex; flex-direction: column; gap: 5px; border-top: 1px solid var(--border-soft); }
.cop-trace-step { display: flex; align-items: baseline; gap: 8px; font-size: .78rem; padding: 4px 0; flex-wrap: wrap; }
.cop-trace-step::before { content: ""; width: 7px; height: 7px; border-radius: 50%; flex: 0 0 7px; background: var(--muted); align-self: center; }
.cop-trace-step.is-ok::before { background: var(--green); }
.cop-trace-step.is-run::before { background: var(--amber); animation: pulseDot 1.4s ease-in-out infinite; }
.cop-trace-step.is-fail::before { background: var(--red); }
.cop-trace-name { font-family: var(--mono); font-weight: 700; color: var(--ink); }
.cop-trace-args { color: var(--muted); font-family: var(--mono); font-size: .92em; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.cop-trace-time { margin-left: auto; color: var(--muted); font-variant-numeric: tabular-nums; }

.cop-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.cop-chip {
  padding: 8px 14px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); color: var(--ink-soft); font-weight: 640; font-size: .84rem;
  cursor: pointer; text-align: left; -webkit-tap-highlight-color: transparent;
  transition: transform .12s var(--ease), background .2s, color .2s, border-color .2s, box-shadow .2s;
}
.cop-chip:hover { border-color: transparent; color: var(--primary-ink); background: var(--grad-soft); transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.cop-chip:active { transform: scale(.96); }

.cop-composer { display: flex; align-items: flex-end; gap: 8px; margin-top: 14px; padding-bottom: var(--sab); }
.cop-composer-in { flex: 1; min-width: 0; resize: none; min-height: 46px; max-height: 30vh; padding: 12px 14px; border-radius: 14px; border: 1.5px solid var(--border); background: var(--surface); line-height: 1.5; outline: none; transition: border-color .2s, box-shadow .25s; }
.cop-composer-in:focus { border-color: var(--primary); box-shadow: 0 0 0 4px var(--primary-tint); }
.cop-send { flex: 0 0 auto; width: 46px; height: 46px; border-radius: 14px; border: none; background: var(--grad); color: #fff; display: grid; place-items: center; cursor: pointer; box-shadow: var(--glow); -webkit-tap-highlight-color: transparent; transition: transform .12s, box-shadow .2s; }
.cop-send:hover { box-shadow: var(--glow-strong); }
.cop-send:active { transform: scale(.94); }
.cop-send:disabled { opacity: .45; box-shadow: none; cursor: not-allowed; }

/* ═══════════════════════════════════════════════════════════════════════════
   11 · REPORT  (screen + print)
   ---------------------------------------------------------------------------
   .rep                document root
   .rep-cover  .rep-cover-mark  .rep-cover-title  .rep-cover-sub  .rep-cover-meta
   .rep-meta-row  .rep-meta-k  .rep-meta-v
   .rep-sec            a numbered section   .rep-sec-h   .rep-sec-num
   .rep-lead           opening paragraph, larger
   .rep-callout        infographic box  (+ .is-info .is-good .is-warn .is-risk)
   .rep-callout-h  .rep-callout-body  .rep-callout-num
   .rep-figure  .rep-fig-body  .rep-fig-cap
   .rep-kvs  .rep-kv  .rep-kv-k  .rep-kv-v
   .rep-sig            sign-off block
   .rep-break          forces a page break before this element when printing
   .rep-avoid-break    keeps a figure/table from being split
   .no-print  .print-only
   ═══════════════════════════════════════════════════════════════════════════ */
.rep { max-width: 78ch; margin: 0 auto; }
.rep-cover { position: relative; overflow: hidden; border-radius: var(--radius-lg); padding: 44px 38px; background: var(--hero-bg); color: #fff; margin-bottom: 28px; }
.rep-cover::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse at 12% 15%, rgba(3, 199, 90, .20), transparent 52%),
              radial-gradient(ellipse at 88% 82%, rgba(22, 224, 192, .15), transparent 50%); }
.rep-cover > * { position: relative; z-index: 1; }
.rep-cover-mark { width: 52px; height: 52px; margin-bottom: 18px; filter: drop-shadow(0 6px 22px rgba(3, 199, 90, .5)); }
.rep-cover-title { font-size: 2rem; font-weight: 850; letter-spacing: -.03em; color: #fff; line-height: 1.2; }
.rep-cover-sub { color: rgba(255, 255, 255, .74); margin-top: 8px; font-size: 1.02rem; }
.rep-cover-meta { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px 20px; margin-top: 24px; padding-top: 18px; border-top: 1px solid rgba(255, 255, 255, .16); }
.rep-meta-row { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rep-meta-k { font-size: .74rem; text-transform: uppercase; letter-spacing: .07em; color: rgba(255, 255, 255, .5); font-weight: 700; }
.rep-meta-v { font-weight: 700; color: #fff; font-variant-numeric: tabular-nums; overflow-wrap: break-word; }

.rep-sec { margin: 0 0 30px; }
.rep-sec-h { display: flex; align-items: baseline; gap: 10px; font-size: 1.2rem; font-weight: 800; letter-spacing: -.02em; margin-bottom: 12px; padding-bottom: 8px; border-bottom: 2px solid var(--border-soft); }
.rep-sec-num { flex: 0 0 auto; font-size: .84rem; font-weight: 800; color: var(--primary-ink); background: var(--primary-tint); border-radius: 6px; padding: 2px 8px; font-variant-numeric: tabular-nums; }
.rep-lead { font-size: 1.06rem; line-height: 1.85; color: var(--ink-soft); }
.rep p { line-height: 1.85; }

.rep-callout {
  --rep-c: var(--primary);
  display: flex; gap: 14px; align-items: flex-start;
  border: 1px solid color-mix(in srgb, var(--rep-c) 26%, transparent);
  border-left: 4px solid var(--rep-c);
  background: color-mix(in srgb, var(--rep-c) 7%, var(--surface));
  border-radius: 12px; padding: 15px 18px; margin: 16px 0;
}
.rep-callout.is-info { --rep-c: var(--accent); }
.rep-callout.is-good { --rep-c: var(--green); }
.rep-callout.is-warn { --rep-c: var(--amber); }
.rep-callout.is-risk { --rep-c: var(--red); }
/* The big number carries the finding; the prose beside it carries the caveat.
   Sized so a reader skimming only the numerals still gets the report. */
.rep-callout-num { flex: 0 0 auto; font-size: 1.9rem; font-weight: 870; line-height: 1; letter-spacing: -.04em; color: var(--rep-c); font-variant-numeric: tabular-nums; }
.rep-callout-h { font-weight: 780; margin-bottom: 3px; }
.rep-callout-body { min-width: 0; font-size: .94rem; color: var(--ink-soft); line-height: 1.7; }

.rep-figure { margin: 20px 0; }
.rep-fig-body { border: 1px solid var(--border-soft); border-radius: 12px; background: var(--surface); padding: 14px; overflow: hidden; }
.rep-fig-cap { margin-top: 8px; font-size: .82rem; color: var(--muted); line-height: 1.6; }
.rep-fig-cap b { color: var(--ink-soft); font-weight: 720; }

.rep-kvs { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 8px; margin: 14px 0; }
.rep-kv { display: flex; justify-content: space-between; gap: 10px; padding: 9px 13px; background: var(--surface-2); border: 1px solid var(--border-soft); border-radius: 10px; }
.rep-kv-k { font-size: .8rem; color: var(--ink-soft); font-weight: 620; }
.rep-kv-v { font-weight: 760; font-variant-numeric: tabular-nums; }
.rep-sig { margin-top: 34px; padding-top: 16px; border-top: 1px solid var(--border); font-size: .84rem; color: var(--muted); line-height: 1.8; }

.print-only { display: none; }

/* ── Print ─────────────────────────────────────────────────────────────────
   Goal: an A4 PDF a vet or breeding manager can staple to a file. Everything
   that is app chrome or depends on hover/scroll is removed, and colour is kept
   only where it IS the information (risk bands, admixture, heatmap ramps). */
@media print {
  @page { size: A4; margin: 16mm 14mm; }
  html { font-size: 10.5pt; }
  body { background: #fff !important; background-image: none !important; color: #000; padding: 0; }
  html.dark { color-scheme: light; }

  .sidebar, .topbar, .scrim, .hamburger, .gb-install, .toast-wrap, #pill-tray,
  .gpal-backdrop, #pgb-splash, .cop-composer, .cop-chips, .suggest, .msg-actions,
  .gbx-tools, .gbx-zoom, .dt-pager, .no-print { display: none !important; }

  .app { display: block; }
  .main { display: block; }
  .content { max-width: none; padding: 0; margin: 0; }
  .print-only { display: block; }

  /* Animations never finish before the print snapshot, so anything mid-reveal
     prints blank. Force every animated element to its final state. */
  *, *::before, *::after { animation: none !important; transition: none !important; opacity: 1 !important; transform: none !important; }
  .pgb-anim, .pgb-fade, .content > *, .site-wrap > *, .list-item { opacity: 1 !important; }

  .card, .kpi, .stat-tile, .metric, .risk-card, .dt-wrap, .rep-fig-body, .gbx {
    box-shadow: none !important; border: 1px solid #ccc !important; break-inside: avoid;
  }
  .card:hover, .kpi:hover, .metric:hover, .risk-card:hover { transform: none !important; }

  a { color: #000; text-decoration: none; }
  .rep a[href^="http"]::after { content: " (" attr(href) ")"; font-size: .82em; color: #555; word-break: break-all; }

  .rep-cover { background: #fff !important; color: #000; border: 2px solid #05170f; border-radius: 0; padding: 22mm 0 10mm; margin-bottom: 12mm; break-after: avoid; }
  .rep-cover::after { display: none; }
  .rep-cover-title, .rep-meta-v { color: #000; }
  .rep-cover-sub, .rep-meta-k { color: #444; }
  .rep-cover-meta { border-top-color: #999; }
  .rep-sec { break-inside: auto; }
  .rep-sec-h { break-after: avoid; }
  .rep-figure, .rep-callout, .rep-kv, .metric, .risk-card { break-inside: avoid; }
  .rep-break { break-before: page; }
  .rep-avoid-break { break-inside: avoid; }

  /* Colour IS the data in these three — keep the ink. */
  .kin-cell, .adx-seg, .risk-card::before, .risk-scale-seg, .rel, .risk, .pca-pt,
  .kin-ramp, .gbx-ideo-band, .gbx-feat, .gbx-var {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  .kin-scroll, .adx-scroll, .dt-scroll, .ped-scroll, .table-wrap {
    overflow: visible !important; max-height: none !important; max-width: none !important;
  }
  table.dt th, table.data th { position: static; background: #f0f0f0 !important; color: #000; }
  thead { display: table-header-group; }
  tr { break-inside: avoid; }
  .hero, .pgb-hero { background: #f4f4f4 !important; color: #000; box-shadow: none; }
  .hero::before, .hero::after, .pgb-hero::after { display: none !important; }
  .hero h1, .hero .sci, .hero .common, .hero p { color: #000 !important; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   RESPONSIVE — mobile-first in behaviour even though the rules read desktop-down
   Checkpoints exercised: 360 / 420 / 768 / 900 / 1024 / 1440.
     1440  4-up KPI, 1.6fr/1fr split, full sidebar
     1024  3-up KPI, split collapses to a single column
      900  sidebar becomes an off-canvas drawer  ← the structural break
      768  tablet portrait: 2-up KPI, tighter gutters
      640  phone landscape / large phone: single-column heroes, 44px targets
      420  small phone: 1-up KPI, label text drops out of icon buttons
      360  narrowest supported: heatmap/browser shrink to stay usable
   Nothing below ever makes <body> scroll sideways — every wide component owns
   its own scroller (.table-wrap .dt-scroll .kin-scroll .adx-scroll .ped-scroll).
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 1024px) {
  .kpi-grid { grid-template-columns: repeat(3, 1fr); }
  .cols-2 { grid-template-columns: 1fr; }
  .content { padding: 22px; }
}

/* ── < 900px: sidebar becomes a drawer ─────────────────────────────────── */
@media (max-width: 900px) {
  .hamburger { display: grid; }
  .sidebar {
    position: fixed; left: 0; top: 0; height: 100vh; height: 100dvh;
    transform: translateX(-100%); transition: transform .32s var(--ease);
    box-shadow: var(--shadow-pop);
    width: min(var(--sidebar-w), 84vw); flex-basis: min(var(--sidebar-w), 84vw);
  }
  .app.drawer-open .sidebar { transform: none; }
  /* The collapse toggle is meaningless in drawer mode — the drawer is either
     open at full width or gone. Undo every .collapsed side-effect. */
  .app.collapsed .sidebar { width: min(var(--sidebar-w), 84vw); flex-basis: min(var(--sidebar-w), 84vw); }
  .app.collapsed .brand-name, .app.collapsed .brand-sub,
  .app.collapsed .nav .nav-label, .app.collapsed .nav-section,
  .app.collapsed .nav-logout .nav-label, .app.collapsed .collapse-row .nav-label,
  .app.collapsed .side-lang-label { opacity: 1; width: auto; height: auto; display: block; overflow: visible; }
  .app.collapsed .brand, .app.collapsed .nav a, .app.collapsed .nav-logout,
  .app.collapsed .collapse-row { justify-content: flex-start; gap: 13px; padding-left: 13px; padding-right: 13px; }
  .app.collapsed .brand { padding: calc(20px + var(--sat)) 18px 18px; gap: 12px; }
  .app.collapsed .side-lang { flex-direction: row; }
  .collapse-row { display: none; }
  .app.drawer-open .scrim { display: block; position: fixed; inset: 0; background: var(--scrim-bg); backdrop-filter: blur(3px); z-index: 39; animation: fade .3s var(--ease); }
  .kpi-grid { grid-template-columns: repeat(2, 1fr); }
  .content { padding: 18px; padding-left: max(18px, var(--sal)); padding-right: max(18px, var(--sar)); }
  .rep { max-width: none; }
}

/* ── Tablet portrait ───────────────────────────────────────────────────── */
@media (max-width: 768px) {
  #pp-grid { grid-template-columns: 1fr !important; }
  .content { max-width: 100%; padding: 16px; padding-left: max(16px, var(--sal)); padding-right: max(16px, var(--sar)); }
  .card { padding: 17px; }
  .card.pad-lg { padding: 20px; }
  .kpi { padding: 15px; gap: 12px; }
  .kpi .gauge { width: 52px; height: 52px; flex-basis: 52px; }
  .kpi .kpi-val { font-size: 1.5rem; }
  .site-wrap { padding: 16px 14px 54px; }
  .hero { padding: 26px 20px; }
  .hero h1 { font-size: 1.5rem; }
  .hero .sci { font-size: 1.5rem; }
  .modal { width: 96vw; max-height: 92dvh; }
  .list-item .li-sub { white-space: normal; }
  .metric-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 11px; }
  .gbx { --gbx-gutter: 108px; }
  .kin { --kin-cell: 22px; --kin-gut: 104px; }
  .kin-axis-x { height: 86px; }
  .cop-msg { max-width: 92%; }
  .rep-cover { padding: 32px 24px; }
  .rep-cover-title { font-size: 1.6rem; }
}

/* ── Phone ─────────────────────────────────────────────────────────────── */
@media (max-width: 640px) {
  /* Belt and braces: if any component ever leaks, clip it rather than let the
     whole document pan — a sideways-scrolling app feels broken on a phone. */
  html, body { overflow-x: hidden; }
  .topbar { padding: 10px 14px; padding-top: calc(10px + var(--sat)); gap: 8px; }
  .topbar .crumb { font-size: .9rem; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; min-width: 0; }
  .topbar .crumb .home { max-width: 34vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: bottom; }
  .model-badge { font-size: .74rem; padding: 5px 9px; }
  .content { padding: 14px; padding-left: max(14px, var(--sal)); padding-right: max(14px, var(--sar)); padding-bottom: calc(20px + var(--sab)); }
  .page-head { gap: 8px; margin-bottom: 16px; }
  .page-head h1 { font-size: 1.3rem; }
  .card { padding: 16px; border-radius: 14px; }
  .card.pad-lg { padding: 18px; }
  .kpi-grid { grid-template-columns: 1fr 1fr; gap: 11px; }
  .kpi .kpi-val { font-size: 1.45rem; }
  .stat-tiles { grid-template-columns: 1fr 1fr; gap: 12px; }
  .stat-tile .st-val { font-size: 1.7rem; }
  .hero { padding: 22px 18px; border-radius: 18px; }
  .hero h1 { font-size: 1.4rem; }
  .hero .sci { font-size: 1.45rem; }
  .hero .common { font-size: .96rem; }
  .gb-toolbar { gap: 6px; }
  .gb-toolbar .input, .gb-toolbar .select { max-width: none !important; flex: 1 1 130px; min-width: 0; }
  .gb-legend { gap: 5px 10px; }
  .seg { width: 100%; overflow-x: auto; }
  .seg button { padding: 6px 11px; }
  .modal-foot { flex-direction: column-reverse; align-items: stretch; }
  .modal-foot .btn { width: 100%; }
  .modal-foot label { margin-right: 0 !important; justify-content: center; }
  table.data th, table.data td { padding: 10px 12px; }
  table.dt th, table.dt td { padding: 8px 10px; }
  #viewer { height: 380px !important; }
  .list-item { padding: 12px 13px; gap: 11px; }
  .list-item .li-avatar { width: 38px; height: 38px; flex-basis: 38px; }
  .site-wrap { padding: 14px 12px 48px; }
  .toast-wrap { left: 12px; right: calc(12px + var(--sar)); top: calc(12px + var(--sat)); }
  .toast { min-width: 0; max-width: none; }
  .gb-install { min-height: 44px; right: calc(14px + var(--sar)); bottom: calc(14px + var(--sab)); }

  /* Touch targets: 44px is the smallest reliably hittable size with a thumb.
     Anything below this got mis-taps in the field on gloved hands. */
  .btn, .icon-btn, .seg button, .chip, .cop-chip, .gb-track-chip,
  .gbx-zoom-btn, .dt-sort, .side-lang button { min-height: 44px; }
  .icon-btn { width: 44px; }
  .field .input, .select, .input { min-height: 46px; }

  /* Component-level phone tuning */
  .kin { --kin-cell: 20px; --kin-gut: 84px; }
  .kin-axis-x { height: 72px; }
  .kin-lab { font-size: .72rem; }
  .kin-scroll { max-height: 60vh; }
  /* No :hover on touch — the scale-up would stick after a tap and cover the
     neighbouring cells. Selection (is-sel) carries the feedback instead. */
  .kin-cell:hover { transform: none; box-shadow: none; }
  .gbx { --gbx-gutter: 82px; --gbx-lane-h: 30px; }
  .gbx-lane-label { font-size: .74rem; padding: 4px 7px; }
  .gbx-lane-sub { display: none; }
  .gbx-ruler-lab { font-size: .68rem; }
  .gbx-head { padding: 10px; }
  .gbx-tools { margin-left: 0; width: 100%; }
  .adx { --adx-h: 130px; --adx-min-w: 2px; }
  .adx-ids { display: none; }
  .pca-frame { aspect-ratio: 4 / 3; }
  .pca-axis-y { display: none; }
  .metric-grid { grid-template-columns: 1fr 1fr; gap: 10px; }
  .metric { padding: 13px 14px; }
  .metric-val { font-size: 1.5rem; }
  .cop-msg { max-width: 100%; }
  .cop-turn { gap: 8px; }
  .cop-avatar { width: 26px; height: 26px; flex-basis: 26px; }
  .cop-trace-args { display: none; }
  .rep-callout { flex-direction: column; gap: 8px; padding: 13px 15px; }
  .rep-callout-num { font-size: 1.6rem; }
  .rep-sec-h { font-size: 1.08rem; }
  .risk-card { padding: 15px 16px 15px 20px; }
  .risk-score { font-size: 1.7rem; }
}

/* ── Small phone (≤ 420px) ─────────────────────────────────────────────── */
@media (max-width: 420px) {
  .kpi-grid { grid-template-columns: 1fr; }
  .stat-tiles { grid-template-columns: 1fr; }
  .metric-grid { grid-template-columns: 1fr; }
  .hero h1 { font-size: 1.26rem; }
  .page-head h1 { font-size: 1.2rem; }
  /* Icon buttons shed their text label; the icon plus an aria-label carries it. */
  .btn.icon-only .nav-label, .page-head .btn.icon-only span:not(.ic) { display: none; }
  .rep-cover { padding: 26px 18px; }
  .rep-cover-title { font-size: 1.4rem; }
  .dt-foot { font-size: .78rem; }
}

/* ── Narrowest supported (≤ 360px) ─────────────────────────────────────── */
@media (max-width: 360px) {
  .content { padding: 11px; padding-left: max(11px, var(--sal)); padding-right: max(11px, var(--sar)); }
  .card { padding: 13px; }
  /* Below this the matrix stops being legible as a matrix; 16px cells still
     resolve a colour and the tooltip carries the exact φ. */
  .kin { --kin-cell: 16px; --kin-gut: 64px; }
  .kin-axis-x { height: 60px; }
  .kin-lab { font-size: .68rem; }
  .gbx { --gbx-gutter: 68px; }
  .gbx-locus { font-size: .76rem; }
  .adx { --adx-h: 110px; }
  .badge, .rel, .risk { font-size: .74rem; padding-left: 8px; padding-right: 8px; }
  .brand .logo { width: 46px; height: 46px; flex-basis: 46px; }
  .brand .brand-name { font-size: 1.14rem; }
}

/* ── Collapsed sidebar, desktop only (≥ 901px) ─────────────────────────── */
@media (min-width: 901px) {
  .app.collapsed .brand-txt,
  .app.collapsed .nav .nav-label,
  .app.collapsed .nav-section,
  .app.collapsed .nav-logout .nav-label,
  .app.collapsed .collapse-row .nav-label,
  .app.collapsed .side-lang-label { display: none; }
  .app.collapsed .brand { justify-content: center; gap: 0; padding: calc(18px + var(--sat)) 8px 18px; }
  .app.collapsed .nav { padding: 12px 10px; }
  .app.collapsed .nav a,
  .app.collapsed .nav-logout,
  .app.collapsed .collapse-row { justify-content: center; gap: 0; padding-left: 0; padding-right: 0; }
  .app.collapsed .nav a .ic,
  .app.collapsed .nav-logout .ic,
  .app.collapsed .collapse-row .ic { flex: 0 0 24px; }
  .app.collapsed .nav-section { height: 0; margin: 8px 0; padding: 0; border-top: 1px solid var(--side-brd); }
  .app.collapsed .sidebar-foot { padding: 10px 8px; gap: 6px; }
  .app.collapsed .side-lang { flex-direction: column; gap: 4px; width: 100%; }
  .app.collapsed .side-lang button { padding: 7px 0; font-size: .78rem; }
}

/* ── Coarse pointer: hover states are lies on touch ─────────────────────── */
@media (hover: none) {
  .btn:hover, .card:hover, .kpi:hover, .stat-tile:hover, .metric:hover,
  .risk-card:hover, .list-item:hover, .pill:hover, .cop-chip:hover,
  .suggest .chip:hover { transform: none; }
  .list-item:hover { transform: none; }
  .kin-cell:hover { transform: none; box-shadow: none; }
  .msg-actions { opacity: 1; }
}

/* ── PWA standalone: the browser chrome is gone, so we own the status bar ── */
@media (display-mode: standalone) {
  /* The topbar backdrop must be opaque under the status bar, otherwise the
     page content shows through the notch area while scrolling. */
  .topbar { background: var(--surface); }
  .gb-install { display: none; }
}

/* ═══ Dark-mode touch-ups ═════════════════════════════════════════════════
   Everything above runs on tokens, so these are only the handful of places
   where a value was baked in for a reason and has to be re-baked for dark. */
html.dark body {
  background-image:
    radial-gradient(900px 520px at 8% -6%, rgba(3, 199, 90, .06) 0%, transparent 55%),
    radial-gradient(820px 480px at 104% 6%, rgba(22, 224, 192, .05) 0%, transparent 52%);
}
html.dark .btn::after { background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .12), transparent); }
html.dark .kpi .gauge::before { box-shadow: inset 0 1px 3px rgba(0, 0, 0, .4); }
html.dark .gb-tip, html.dark .kin-tip, html.dark .adx-tip, html.dark .pca-tip { border: 1px solid var(--border); }
html.dark .gbx-ideo-view { box-shadow: 0 0 0 1px rgba(0, 0, 0, .5); }
html.dark .kin-cell { outline-color: var(--surface); }
html.dark .kin-ramp { background: linear-gradient(90deg, var(--surface-2) 0%, #14402a 22%, #17804a 45%, #2fc673 68%, #9df5c2 100%); }
html.dark .pca-sw { border-color: rgba(255, 255, 255, .18); }
html.dark .lock-form .input { background: rgba(255, 255, 255, .92); color: #16241c; }
html.dark .risk-scale-seg:nth-child(1) { background: #2fa864; } html.dark .risk-scale-seg:nth-child(2) { background: #79c14e; }
html.dark .risk-scale-seg:nth-child(3) { background: #d9920f; } html.dark .risk-scale-seg:nth-child(4) { background: #d1691a; }
html.dark .risk-scale-seg:nth-child(5) { background: #c94242; }
html.dark .risk-scale-seg:not(.is-on) { background: var(--track); }
html.dark .risk-scale-mark::after { background: var(--ink); }
html.dark .gbx-var.is-hom { background: #9df5c2; }
html.dark .ped-node.is-affected .ped-male,
html.dark .ped-node.is-affected .ped-female,
html.dark .ped-node.is-affected .ped-unknown { fill: var(--ink); stroke: var(--ink); }
html.dark .ped-legend-sym.is-affected { background: var(--ink); }
html.dark .adx-sep { background: var(--bg); }
