/* Shared design system for every uni-rankings page — the single source of truth for
   design tokens + the base reset/body. Page-specific chrome (.grain, nav, cards, charts)
   stays in each page's inline <style>. This file is linked BEFORE that <style> so the
   tokens resolve. Edit a colour/spacing here and all pages move together. */
:root {
  /* Calibrate — a refined instrument console. Graphite field, amber primary readout +
     steel-teal secondary (the two-needle gauge), mono data. --ink is the *primary text*
     (warm off-white), --accent the action hue (roles, not literal darkness). */
  --bg: #16181C;
  --surface: #1E2126;
  --surface-2: #262A30;
  --ink: #E8E9E4;
  --muted: #9AA1AE;
  --faint: #858D9B;
  --line: #2E333C;
  --line-2: #24282F;
  --accent: #E8A13A;
  --accent-2: #5FA8B5;
  --accent-ink: #F2B75C;
  --accent-wash: #2A200E;
  --accent-line: #5A441F;
  /* rgb channels so glows/washes can derive from the accent instead of hardcoding it */
  --accent-rgb: 232, 161, 58;
  --accent-2-rgb: 95, 168, 181;
  --up: #6EE787;
  --down: #FF6B5E;
  --med: #C98BB0;
  --qs: #4f8ce6;     --qs-d: #7db0f5;
  --the: #e85f51;    --the-d: #f0897e;
  --arwu: #2bbd86;   --arwu-d: #5fd6a8;
  --usnews: #a07ce8; --usnews-d: #c0a6f2;
  --cwur: #d98a3c;   --cwur-d: #edb36e;
  /* field-authority subject sources (subjects page only) */
  --ft: #d6537e;          --ft-d: #e685a5;
  --csrankings: #45c2d4;  --csrankings-d: #7ad8e5;
  --natureindex: #a4c957; --natureindex-d: #c2dd85;
  --repec: #6f7fdd;       --repec-d: #98a5ea;
  --theimpact: #2e9e8f;   --theimpact-d: #5cc0b2;
  --gold: #c69a2b; --silver: #9b9488; --bronze: #b3743c;
  --glow: 0 0 14px;
  --display: "Instrument Sans", system-ui, sans-serif;   /* Archivo retired (v11) — the token survives for legacy literals, resolved to the house sans */
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* shadows carry the ground's blue-black hue (redesign audit: pure-black shadows read generic;
     tinted ones sit IN the environment) — same alphas, hue from #05070b's blue cast */
  --shadow: 0 1px 2px rgba(3,8,20,.55), 0 12px 34px -16px rgba(3,8,20,.8);
  --maxw: 1280px;
  /* Stacking order, named once. These were ad-hoc numbers spread over app.css, skin.css and the
     pages (40/50/60/70), and when skin.css lifted the fixed header to 100 everything below it was
     silently orphaned: the skip link (70) tabbed to behind the header, and the full-screen compare
     modal (50) had the header punched through it. Layers are declared here so a new overlay picks a
     name, not a number. Gaps of 100 leave room to slot something between two layers. */
  --z-sticky: 20;     /* sticky table headers inside a scroll region */
  --z-tray: 40;       /* docked strips: compare tray, back-to-top */
  --z-header: 100;    /* the fixed top bar — everything chrome-like must clear this */
  --z-overlay: 200;   /* full-screen modals and their scrim */
  --z-tooltip: 300;   /* hover readouts, which may float above a modal */
  --z-skip: 400;      /* the skip link outranks all of it, by definition */
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;

  /* ═══════════════════════════════════════════════════════════════════════════════════════════════
     DESIGN SYSTEM v1 — "One dark environment" (the shipped v10 look, named as roles). The tokens
     above are palette primitives; these name how the site actually composes them, so one change here
     moves the whole system. Values match what skin.css hard-codes today (adopting them is a no-op). ═══ */
  /* ground & surfaces */
  --ground: #05070b;                              /* the single flat near-black body ground */
  --glass: rgba(255,255,255,.028);                /* translucent panel fill, floating over the void */
  --glass-line: rgba(255,255,255,.08);            /* 1px panel edge */
  --glass-hover: rgba(255,255,255,.045);          /* panel fill on hover / focus-within */
  --glass-blur: 12px;                             /* panel backdrop blur */
  --elev-panel: 0 24px 60px -40px rgba(3,8,20,.92); /* panel drop shadow — blue-black tinted */
  /* type scale — three voices: serif (--serif, editorial headings) · mono (--mono, data) · sans (body) */
  --h-hero: clamp(46px, 8.4vw, 116px);            /* full-bleed video hero */
  --h-page: clamp(44px, 6.6vw, 84px);             /* inner-page hero */
  --h-section: clamp(30px, 3.8vw, 46px);          /* section title (v11: more editorial punch) */
  --h-sub: 20px;                                  /* panel / column / modal sub-heading */
  /* rhythm & rank accents */
  --space-section: clamp(80px, 12vh, 140px);      /* cathedral gap between sections */
  --medal-1: var(--gold); --medal-2: var(--silver); --medal-3: var(--bronze);
  /* motion (DESIGN.md §5 — two tiers, two curves; springs on affordances only, never data) */
  --dur-1: .15s;                                  /* micro: hover, press, color */
  --dur-2: .3s;                                   /* small movement: chips, in-place reveals */
  --dur-3: .8s;                                   /* entrances: scroll-reveal, chart draw-in */
  --ease-micro: cubic-bezier(.2, 0, 0, 1);        /* crisp decel */
  --ease-out: cubic-bezier(.16, 1, .3, 1);        /* expo-out — entrance family */
  --ease-spring: cubic-bezier(.22, 1.8, .5, .95); /* alive press/hover — affordances only */
}
* { box-sizing: border-box; scrollbar-width: none; -ms-overflow-style: none; }
/* kill the 300ms double-tap delay on every interactive element (mobile responsiveness) */
a, button, input, select, textarea, [role="button"] { touch-action: manipulation; }
*::-webkit-scrollbar { display: none; }
html, body { margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
/* App-feel navigation (DESIGN.md §5; cross-document View Transitions, Chrome 126+ — others simply
   ignore): page content cross-fades on nav while the floating top bar and back-to-top button keep
   their own stable groups, so the chrome appears to persist across pages like an app shell. */
@view-transition { navigation: auto; }
.topbar { view-transition-name: topbar; }
.totop { view-transition-name: totop; }
::view-transition-old(root), ::view-transition-new(root) {
  animation-duration: .22s; animation-timing-function: var(--ease-micro); }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}
/* branded text selection — warm amber wash, readable ink */
::selection { background: rgba(var(--accent-rgb), .28); color: var(--ink); }
body {
  /* v11: Instrument Sans — same family voice as the Instrument Serif display face, one designer,
     one brand register. Inter stays in the stack purely as a metric-compatible system fallback. */
  font-family: "Instrument Sans", "Inter", ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-feature-settings: "cv05" 1, "ss01" 1;
  color: var(--ink); font-size: 14px; line-height: 1.5; color-scheme: dark;
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
  background-color: var(--bg);
  background-image:
    radial-gradient(1100px 520px at 18% -240px, rgba(var(--accent-rgb), .05), transparent 60%),
    radial-gradient(900px 480px at 88% -180px, rgba(var(--accent-2-rgb), .04), transparent 64%);
  background-attachment: fixed;
}
@media (prefers-reduced-motion: reduce) {
  /* pseudo-elements are matched explicitly: `*` does not cover ::before/::after, and several pages
     animate them (the eyebrow rule, the rail ticks). Every page used to repeat its own weaker copy
     of this block; they are deleted in favour of this one. */
  *, *::before, *::after {
      animation-duration: .001ms !important; animation-iteration-count: 1 !important;
      transition-duration: .001ms !important; scroll-behavior: auto !important; }
  .seg button:active, .totop:active { transform: none; }   /* press scale is motion too */
}

/* Back-to-top — element injected by app.js on every page, hidden until scrolled one
   viewport. Right edge sits flush with the content column: every page wraps in
   .wrap { max-width: calc(var(--maxw)+48px); padding: 0 24px }, so the column edge lands at
   50vw - maxw/2 from the viewport's right; clamped to a 16px gutter once the column fills the
   screen. Styling mirrors .btn (accent-wash hover) + the topbar glass so it reads as native
   chrome. Returns to top via the global html{scroll-behavior:smooth} (reduced-motion honored). */
.totop {
  /* 24px floor = the .wrap gutter: the button's right edge sits ON the content cap at narrow
     widths instead of 8px past it; the calc branch keeps it flush with the cap on wide screens. */
  position: fixed; bottom: 24px; right: max(24px, calc(50vw - var(--maxw) / 2)); z-index: var(--z-tray);
  width: 44px; height: 44px; display: grid; place-items: center; padding: 0;
  border: 1px solid var(--line); border-radius: 999px;
  background: color-mix(in srgb, var(--surface) 80%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.3); backdrop-filter: blur(14px) saturate(1.3);
  color: var(--muted); font: 600 18px/1 var(--mono); cursor: pointer; box-shadow: var(--shadow);
  opacity: 0; transform: translateY(8px); pointer-events: none;
  /* transform rides the spring (DESIGN.md §5): the button pops in with a hint of overshoot and
     presses like a real key; colors stay on the crisp micro tier */
  transition: opacity var(--dur-2) var(--ease-micro), transform var(--dur-2) var(--ease-spring),
              border-color var(--dur-1) var(--ease-micro), background var(--dur-1) var(--ease-micro), color var(--dur-1) var(--ease-micro);
}
.totop.show { opacity: 1; transform: none; pointer-events: auto; }
.totop:hover { border-color: var(--accent-line); background: var(--accent-wash); color: var(--accent-ink); }
.totop:active { transform: scale(.92); transition-duration: .08s; }   /* snap in, spring out */
/* One focus ring for the whole site. This exact declaration was repeated 19 times across the pages,
   which meant any control nobody remembered to style had no keyboard indicator at all. Element-level
   specificity (0,1,0) so a page rule that needs a different offset — an inset ring on a row that
   clips, the rail's -2px — still wins by class specificity without restating the colour or width. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── Shared page chrome (hoisted 2026-07-10 from eight near-identical per-page copies) ──
   Skip link, content column, floating glass top bar, brand, nav links, segmented control, footer.
   A page can still override any of it locally — its inline <style> loads after this file. */
.skip { position: fixed; top: 10px; left: 10px; z-index: var(--z-skip); transform: translateY(-160%);
  background: var(--accent); color: var(--bg); padding: 9px 15px; border-radius: 9px; font-size: 13px;
  font-weight: 600; text-decoration: none; box-shadow: var(--shadow); transition: transform var(--dur-2) var(--ease-out); }
.skip:focus { transform: none; outline: 2px solid var(--accent-2); outline-offset: 2px; }
.wrap { max-width: calc(var(--maxw) + 48px); margin: 0 auto; padding: 0 24px; position: relative; z-index: 1; }
.topbar { position: sticky; top: 0; z-index: var(--z-header); padding: 12px 24px 0; }
/* scoped to .topbar: pages reuse the bare class name .bar for data elements (uni.html's indicator
   tracks) — an unscoped .bar leaks nav-pill flex/padding onto them and collapses their fills. */
.topbar .bar { max-width: var(--maxw); margin: 0 auto; display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
       background: color-mix(in srgb, var(--surface) 80%, transparent);
       -webkit-backdrop-filter: blur(14px) saturate(1.3); backdrop-filter: blur(14px) saturate(1.3);
       border: 1px solid var(--line); border-radius: 14px; padding: 8px 12px; box-shadow: var(--shadow); }
.brand { display: inline-flex; align-items: center; gap: 10px; font-family: var(--display); font-weight: 700;
         font-size: 15px; letter-spacing: -.01em; color: var(--ink); text-transform: uppercase; text-decoration: none; }
.navlinks { display: flex; gap: 3px; align-items: center; margin-right: auto; flex-wrap: wrap; }
.navlinks a { color: var(--muted); text-decoration: none; font-size: 13px; font-weight: 500; padding: 6px 11px; border-radius: 8px;
              transition: color var(--dur-1) var(--ease-micro), background var(--dur-1) var(--ease-micro); }
.navlinks a:hover { color: var(--ink); background: var(--surface-2); }
.navlinks a.on { color: var(--accent-ink); background: var(--surface); box-shadow: var(--shadow); }
.seg { display: inline-flex; border: 1px solid var(--line); border-radius: 9px; overflow: hidden; background: var(--surface-2); padding: 2px; gap: 2px; }
.seg button { border: 0; background: transparent; padding: 6px 12px; font-size: 12.5px; cursor: pointer; color: var(--muted);
              font-weight: 500; border-radius: 7px; white-space: nowrap;
              transition: background var(--dur-1) var(--ease-micro), color var(--dur-1) var(--ease-micro), transform var(--dur-2) var(--ease-spring); }
.seg button:active { transform: scale(.94); transition-duration: .08s; }   /* press language (DESIGN.md §5) */
.seg button:hover { color: var(--ink); }
.seg button.on { background: var(--surface); color: var(--accent-ink); }
.seg button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
footer { color: var(--faint); font-size: 12px; padding: 30px 0 40px; text-align: center; line-height: 1.7; margin-top: 30px; }
footer a { color: var(--muted); text-decoration: none; }
footer a:hover { color: var(--accent-ink); }

/* Loading skeleton — shared shimmer placeholder shown while data.json loads (cold networks can
   exceed the ~300ms where a spinner should become a skeleton). A muted surface bar with a light
   sweep; the sweep is animation-only, so prefers-reduced-motion (handled globally above) leaves a
   calm static bar. Any page drops a <span class="sk"> and sizes it inline; real content replaces it. */
.sk { position: relative; overflow: hidden; display: inline-block;
      background: var(--surface-2); border-radius: var(--radius-sm); }
.sk::after {
  content: ""; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb), .08) 50%, transparent);
  animation: sk-sweep 1.3s ease-in-out infinite;
}
@keyframes sk-sweep { to { transform: translateX(100%); } }

/* shared ranking-system logo chip (rlogos.js hydrates .rlogo[data-rlogo]) — hue-masked silhouette
   sized to the current text: height ~1em, width by the logo's aspect ratio (--lw). */
.rlogo { display: inline-block; height: calc(var(--rlh, 1.35em) * var(--rls, 1)); width: calc(var(--rlh, 1.35em) * var(--rls, 1) * var(--lw, 3)); vertical-align: -0.16em;
  background: var(--sc, var(--accent)); flex: none;
  -webkit-mask: var(--logo) left center / auto 100% no-repeat;
          mask: var(--logo) left center / auto 100% no-repeat; }
/* Optical size correction (--rls), applied on top of whatever height the context sets. Every chip is
   cropped to its ink bounds and set to a common height, which is right for wide wordmarks but makes a
   short, dense one look oversized next to them: "nature" is six lowercase letters with almost no
   ascender, so it fills its box far more heavily than "FINANCIAL TIMES". Trim it so a row of marks
   reads evenly. Scale, not a fixed size, so it composes with every --rlh context below. */
.rlogo[data-rlogo="natureindex"] { --rls: .82; }
.rlogo:not([data-rl]) { width: 0; }   /* before hydration: take no space (avoid a flash of empty box) */
/* explicit sizes for chips living in tiny-font contexts (toggles, legends, desks) — match the
   homepage sources cards' prominence instead of shrinking to a 9-11px host font. */
button[data-sys] .rlogo { --rlh: 18px; }
/* subjects source toggle: even 2×2 cells — logo fills the button, masked contain + centered,
   so the four marks read the same size regardless of each logo's aspect ratio */
.sb .rlogo { --rlh: 26px; width: 100%;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain; mask-size: contain; }
.railkey .dots .rlogo, .cl .rlogo { --rlh: 15px; }
.sysrow .sname .rlogo { --rlh: 20px; }
.fader .fname .rlogo { --rlh: 30px; }
