/* PHILKOFA membership portal -- base stylesheet.
 *
 * The surfaces are glass: translucent panels that blur whatever sits behind
 * them, over a slow-moving field of colour painted on the page background.
 * Everything a template can ask for is a token, so a card, a button or a badge
 * looks the same wherever it is used and both colour schemes come out of one
 * set of rules.
 *
 * Contents
 *   1  Tokens              8  Messages           15  Pagination
 *   2  Backdrop            9  Tables             16  Footer
 *   3  Typography         10  Forms              17  Page furniture
 *   4  Layout             11  Portal             18  Motion
 *   5  Header             12  Member bits        19  Responsive
 *   6  Surfaces           13  Proposals          20  Reduced motion & print
 *   7  Buttons & pills    14  Publications
 */

/* ============================================================
   1  Tokens
   ============================================================ */

:root {
  color-scheme: light;

  /* Brand. --blue fills things; --link colours text on a page background,
     which is why the two part company in the dark scheme. */
  --blue: #0b3d8c;
  --blue-dark: #072a61;
  --blue-bright: #2563eb;
  --blue-soft: #eaf0fb;
  --link: #0b3d8c;
  --link-hover: #072a61;
  --gold: #f0a500;
  --gold-bright: #ffc233;
  --red: #c8102e;

  /* Ink */
  --ink: #141b2b;
  --muted: #5b6880;
  --faint: #8a95a8;

  /* Page */
  --bg: #eef2f9;
  --line: rgba(19, 38, 74, .12);
  --line-strong: rgba(19, 38, 74, .2);
  --card: #ffffff;

  /* Glass. --glass-2 is the quieter surface used when glass sits on glass,
     where a second blur would only muddy the first. */
  --glass: linear-gradient(155deg, rgba(255, 255, 255, .82), rgba(255, 255, 255, .58));
  --glass-2: rgba(255, 255, 255, .55);
  --glass-3: rgba(255, 255, 255, .38);
  --glass-border: rgba(255, 255, 255, .75);
  --glass-edge: rgba(255, 255, 255, .95);
  --glass-blur: saturate(165%) blur(18px);
  --glass-blur-sm: saturate(150%) blur(10px);

  /* Status */
  --ok: #136c46;
  --ok-bg: #e2f3ea;
  --warn: #855400;
  --warn-bg: #fdf1d8;
  --err: #98182a;
  --err-bg: #fbe7ea;

  /* Depth */
  --shadow-sm: 0 1px 2px rgba(13, 28, 60, .06), 0 2px 8px -4px rgba(13, 28, 60, .1);
  --shadow: 0 1px 2px rgba(13, 28, 60, .05), 0 14px 32px -18px rgba(13, 28, 60, .35);
  --shadow-lg: 0 2px 4px rgba(13, 28, 60, .06), 0 28px 60px -26px rgba(13, 28, 60, .45);
  --glow: 0 10px 30px -12px rgba(37, 99, 235, .55);
  --glow-gold: 0 10px 28px -12px rgba(240, 165, 0, .6);
  --focus: 0 0 0 3px rgba(37, 99, 235, .32);

  /* Geometry */
  --radius-sm: 9px;
  --radius: 16px;
  --radius-lg: 22px;
  --radius-pill: 999px;

  /* Motion */
  --ease: cubic-bezier(.4, 0, .2, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --spring: cubic-bezier(.34, 1.56, .64, 1);
  --dur-fast: .16s;
  --dur: .28s;
  --dur-slow: .7s;
}

/* The dark scheme is written twice on purpose: once for a visitor who never
   touched the switch (the browser decides), once for one who did. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    color-scheme: dark;
    --blue: #1d4fbb;
    --blue-dark: #12326f;
    --blue-bright: #4f8cff;
    --blue-soft: rgba(79, 140, 255, .16);
    --link: #93b8ff;
    --link-hover: #c3d7ff;
    --ink: #e7edfa;
    --muted: #9aa7c2;
    --faint: #76839c;
    --bg: #070b17;
    --line: rgba(255, 255, 255, .11);
    --line-strong: rgba(255, 255, 255, .2);
    --card: #101728;
    --glass: linear-gradient(155deg, rgba(255, 255, 255, .085), rgba(255, 255, 255, .035));
    --glass-2: rgba(255, 255, 255, .05);
    --glass-3: rgba(255, 255, 255, .035);
    --glass-border: rgba(255, 255, 255, .13);
    --glass-edge: rgba(255, 255, 255, .28);
    --ok: #62d79b;
    --ok-bg: rgba(34, 197, 124, .15);
    --warn: #fbc35b;
    --warn-bg: rgba(234, 179, 8, .15);
    --err: #ff9ea8;
    --err-bg: rgba(239, 68, 68, .16);
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4), 0 2px 10px -4px rgba(0, 0, 0, .5);
    --shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 16px 36px -20px rgba(0, 0, 0, .8);
    --shadow-lg: 0 2px 6px rgba(0, 0, 0, .5), 0 32px 64px -28px rgba(0, 0, 0, .9);
    --glow: 0 12px 34px -14px rgba(79, 140, 255, .7);
    --focus: 0 0 0 3px rgba(122, 165, 255, .4);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --blue: #1d4fbb;
  --blue-dark: #12326f;
  --blue-bright: #4f8cff;
  --blue-soft: rgba(79, 140, 255, .16);
  --link: #93b8ff;
  --link-hover: #c3d7ff;
  --ink: #e7edfa;
  --muted: #9aa7c2;
  --faint: #76839c;
  --bg: #070b17;
  --line: rgba(255, 255, 255, .11);
  --line-strong: rgba(255, 255, 255, .2);
  --card: #101728;
  --glass: linear-gradient(155deg, rgba(255, 255, 255, .085), rgba(255, 255, 255, .035));
  --glass-2: rgba(255, 255, 255, .05);
  --glass-3: rgba(255, 255, 255, .035);
  --glass-border: rgba(255, 255, 255, .13);
  --glass-edge: rgba(255, 255, 255, .28);
  --ok: #62d79b;
  --ok-bg: rgba(34, 197, 124, .15);
  --warn: #fbc35b;
  --warn-bg: rgba(234, 179, 8, .15);
  --err: #ff9ea8;
  --err-bg: rgba(239, 68, 68, .16);
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4), 0 2px 10px -4px rgba(0, 0, 0, .5);
  --shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 16px 36px -20px rgba(0, 0, 0, .8);
  --shadow-lg: 0 2px 6px rgba(0, 0, 0, .5), 0 32px 64px -28px rgba(0, 0, 0, .9);
  --glow: 0 12px 34px -14px rgba(79, 140, 255, .7);
  --focus: 0 0 0 3px rgba(122, 165, 255, .4);
}

/* ============================================================
   2  Backdrop
   ============================================================ */

* { box-sizing: border-box; }

html {
  background: var(--bg);
  scroll-behavior: smooth;
  /* Anything jumped to lands below the sticky header rather than under it. */
  scroll-padding-top: 96px;
  -webkit-text-size-adjust: 100%;
}

/* Three soft lights drifting behind the page. Glass needs something to blur;
   this is it. Both layers are fixed, so they stay put while the page scrolls. */
body::before {
  content: "";
  position: fixed;
  inset: -20vmax;
  z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(38vmax 38vmax at 12% 8%, rgba(37, 99, 235, .28), transparent 62%),
    radial-gradient(34vmax 34vmax at 88% 12%, rgba(240, 165, 0, .22), transparent 60%),
    radial-gradient(42vmax 42vmax at 62% 88%, rgba(11, 61, 140, .24), transparent 62%);
  animation: drift 42s var(--ease) infinite alternate;
}

/* A faint dot grid, which gives the blur something with structure to chew on
   and keeps large empty areas from reading as flat paint. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: .5;
  background-image: radial-gradient(circle at 1px 1px, var(--line) 1px, transparent 0);
  background-size: 30px 30px;
  mask-image: radial-gradient(120vmax 90vmax at 50% 0%, #000 30%, transparent 78%);
}

:root[data-theme="dark"] body::before,
:root:not([data-theme]) body::before {
  opacity: 1;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) body::before {
    background:
      radial-gradient(40vmax 40vmax at 10% 6%, rgba(37, 99, 235, .4), transparent 62%),
      radial-gradient(32vmax 32vmax at 90% 10%, rgba(240, 165, 0, .16), transparent 60%),
      radial-gradient(46vmax 46vmax at 66% 92%, rgba(88, 28, 235, .32), transparent 64%);
  }
}

:root[data-theme="dark"] body::before {
  background:
    radial-gradient(40vmax 40vmax at 10% 6%, rgba(37, 99, 235, .4), transparent 62%),
    radial-gradient(32vmax 32vmax at 90% 10%, rgba(240, 165, 0, .16), transparent 60%),
    radial-gradient(46vmax 46vmax at 66% 92%, rgba(88, 28, 235, .32), transparent 64%);
}

@keyframes drift {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  50%  { transform: translate3d(2vmax, -3vmax, 0) scale(1.08); }
  100% { transform: translate3d(-3vmax, 2vmax, 0) scale(1.03); }
}

/* ============================================================
   3  Typography
   ============================================================ */

body {
  margin: 0;
  min-height: 100vh;
  font-family: "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  font-size: 16px;
  line-height: 1.62;
  color: var(--ink);
  background: transparent;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a {
  color: var(--link);
  text-decoration-color: color-mix(in srgb, var(--link) 35%, transparent);
  text-underline-offset: .18em;
  transition: color var(--dur-fast) var(--ease), text-decoration-color var(--dur-fast) var(--ease);
}
a:hover { color: var(--link-hover); text-decoration-color: currentColor; }

h1, h2, h3, h4 {
  line-height: 1.2;
  margin: 0 0 .5em;
  letter-spacing: -.018em;
  font-weight: 700;
}
h1 { font-size: clamp(1.75rem, 1.35rem + 1.5vw, 2.25rem); }
h2 { font-size: clamp(1.3rem, 1.15rem + .6vw, 1.55rem); }
h3 { font-size: 1.12rem; }
h4 { font-size: 1rem; }
p { margin: 0 0 1em; }

/* A heading that happens to be a link is still a heading: it keeps the page's
   ink and picks the underline up only under the pointer. */
h1 a, h2 a, h3 a, h4 a { color: inherit; text-decoration: none; }
h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover { color: var(--link); text-decoration: underline; }

/* "All activities ->" and its like, sitting opposite a heading. */
.section-head > a {
  text-decoration: none;
  font-weight: 650;
  font-size: .92rem;
  white-space: nowrap;
}
.section-head > a:hover { text-decoration: underline; }

b, strong { font-weight: 700; }
small { font-size: .85em; }

::selection { background: color-mix(in srgb, var(--blue-bright) 30%, transparent); }

/* ============================================================
   4  Layout
   ============================================================ */

.wrap { max-width: 1140px; margin: 0 auto; padding: 0 20px; }
.narrow { max-width: 740px; }

main { padding: 30px 0 72px; position: relative; }

.section { padding: 38px 0; }
.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}
.section-head h1, .section-head h2 { margin: 0; }

.grid { display: grid; gap: 18px; }
.grid.cols-2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid.cols-3 { grid-template-columns: repeat(auto-fit, minmax(258px, 1fr)); }
.grid.cols-4 { grid-template-columns: repeat(auto-fit, minmax(196px, 1fr)); }

.divider { border: 0; border-top: 1px solid var(--line); margin: 30px 0; }
.muted { color: var(--muted); }
.lead { font-size: 1.08rem; color: var(--muted); max-width: 68ch; }

ul.clean { list-style: none; padding: 0; margin: 0; }
ul.checks { padding-left: 1.2em; }
ul.checks li { margin-bottom: .4em; }

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

.site-head {
  position: sticky;
  top: 0;
  z-index: 60;
  background: rgba(7, 42, 97, .9);
  background: color-mix(in srgb, var(--blue-dark) 88%, transparent);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-bottom: 1px solid rgba(255, 255, 255, .1);
  transition: box-shadow var(--dur) var(--ease), background var(--dur) var(--ease);
}
html.is-scrolled .site-head {
  box-shadow: 0 10px 30px -18px rgba(3, 12, 34, .9);
  background: rgba(7, 42, 97, .96);
  background: color-mix(in srgb, var(--blue-dark) 94%, transparent);
}

/* The contact ribbon folds away once the page moves, handing the height back
   to the content. */
.topbar {
  color: #c9d8f2;
  font-size: .8rem;
  border-bottom: 1px solid rgba(255, 255, 255, .09);
  overflow: hidden;
  max-height: 44px;
  opacity: 1;
  transition: max-height var(--dur) var(--ease), opacity var(--dur) var(--ease),
    border-color var(--dur) var(--ease);
}
html.is-scrolled .topbar { max-height: 0; opacity: 0; border-color: transparent; }
.topbar .wrap {
  display: flex;
  gap: 18px;
  justify-content: space-between;
  align-items: center;
  padding-block: 7px;
  flex-wrap: wrap;
}
.topbar a { color: #c9d8f2; text-decoration: none; }
.topbar a:hover { color: #fff; }
.topbar .spot { display: flex; align-items: center; gap: 10px; }

.masthead { color: #fff; }
.masthead .wrap {
  display: flex;
  align-items: center;
  gap: 16px;
  padding-block: 14px;
  transition: padding var(--dur) var(--ease);
}
html.is-scrolled .masthead .wrap { padding-block: 9px; }

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  text-decoration: none;
  color: #fff;
  border-radius: var(--radius-sm);
}
/* The app icon: a navy tile, so a hairline ring keeps it from sinking into the
   masthead blue. */
.brand .mark {
  width: 44px; height: 44px; flex: none;
  display: block;
  border-radius: 11px;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .22), 0 4px 14px rgba(0, 0, 0, .25);
  transition: transform var(--dur) var(--spring), box-shadow var(--dur) var(--ease),
              width var(--dur) var(--ease), height var(--dur) var(--ease);
}
.brand:hover .mark { transform: rotate(-6deg) scale(1.06); }
html.is-scrolled .brand .mark { width: 38px; height: 38px; border-radius: 9px; }
.brand b { display: block; font-size: 1.16rem; letter-spacing: .05em; font-weight: 800; }
.brand > span > span { display: block; font-size: .74rem; color: #bccdea; font-weight: 500; }

nav.main { margin-left: auto; display: flex; gap: 2px; align-items: center; }
nav.main a {
  position: relative;
  isolation: isolate;
  color: #dbe5f7;
  text-decoration: none;
  padding: 8px 13px;
  border-radius: var(--radius-pill);
  font-size: .92rem;
  font-weight: 600;
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
/* The pill grows out of the middle rather than snapping in. */
nav.main a::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: rgba(255, 255, 255, .14);
  transform: scale(.7);
  opacity: 0;
  transition: transform var(--dur) var(--spring), opacity var(--dur) var(--ease);
}
nav.main a:hover { color: #fff; }
nav.main a:hover::before { transform: scale(1); opacity: 1; }
nav.main a.active {
  color: #fff;
  background: rgba(255, 255, 255, .17);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .22);
}
nav.main a.cta {
  background: linear-gradient(135deg, var(--gold-bright), var(--gold));
  color: #452f00;
  font-weight: 700;
  margin-left: 6px;
  box-shadow: var(--glow-gold);
}
nav.main a.cta::before { background: rgba(255, 255, 255, .35); }
nav.main a.cta:hover { color: #3a2800; transform: translateY(-1px); }

.nav-toggle,
.theme-toggle {
  display: grid;
  place-items: center;
  width: 38px; height: 38px;
  flex: none;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, .22);
  border-radius: 12px;
  background: rgba(255, 255, 255, .1);
  color: #fff;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}
.nav-toggle:hover,
.theme-toggle:hover { background: rgba(255, 255, 255, .2); transform: translateY(-1px); }
.nav-toggle:active,
.theme-toggle:active { transform: translateY(0) scale(.95); }
.theme-toggle svg { width: 17px; height: 17px; }
.theme-toggle .moon { display: none; }
:root[data-theme="dark"] .theme-toggle .moon { display: block; }
:root[data-theme="dark"] .theme-toggle .sun { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .theme-toggle .moon { display: block; }
  :root:not([data-theme]) .theme-toggle .sun { display: none; }
}

.nav-toggle { display: none; margin-left: auto; }
.nav-toggle .bars { position: relative; width: 17px; height: 12px; }
.nav-toggle .bars span {
  position: absolute; left: 0;
  width: 100%; height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform var(--dur) var(--ease-out), opacity var(--dur-fast) var(--ease);
}
.nav-toggle .bars span:nth-child(1) { top: 0; }
.nav-toggle .bars span:nth-child(2) { top: 5px; }
.nav-toggle .bars span:nth-child(3) { top: 10px; }
html.nav-open .nav-toggle .bars span:nth-child(1) { transform: translateY(5px) rotate(45deg); }
html.nav-open .nav-toggle .bars span:nth-child(2) { opacity: 0; transform: scaleX(.4); }
html.nav-open .nav-toggle .bars span:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

/* ============================================================
   6  Surfaces
   ============================================================ */

.card {
  position: relative;
  background: var(--glass);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius);
  padding: 22px;
  box-shadow: var(--shadow);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  isolation: isolate;
  transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease),
    border-color var(--dur) var(--ease);
}

/* The lit top edge that sells a pane of glass. */
.card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 1px;
  border-radius: inherit;
  background: linear-gradient(90deg, transparent, var(--glass-edge), transparent);
  opacity: .9;
  pointer-events: none;
}

/* A highlight that follows the pointer. --mx/--my come from js/ui.js; without
   it they stay at the default and the glow simply never shows. */
.card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  opacity: 0;
  pointer-events: none;
  background: radial-gradient(22rem 22rem at var(--mx, 50%) var(--my, 50%),
    color-mix(in srgb, var(--blue-bright) 16%, transparent), transparent 62%);
  transition: opacity var(--dur) var(--ease);
}
.card:hover::after { opacity: 1; }
.card:hover { border-color: color-mix(in srgb, var(--blue-bright) 32%, var(--glass-border)); }

.card h3, .card h2 { margin-top: 0; }
.card .meta { color: var(--muted); font-size: .85rem; }
.card > :last-child { margin-bottom: 0; }

.card img.cover {
  width: calc(100% + 44px);
  margin: -22px -22px 18px;
  border-radius: var(--radius) var(--radius) 0 0;
  object-fit: cover;
  height: 168px;
  display: block;
}

/* Item-like cards lift; container cards stay put so a whole dashboard does not
   bob about under the pointer. */
article.card,
a.card,
.card.stat,
.card.publication { cursor: default; }
article.card:hover,
a.card:hover,
.card.stat:hover,
.card.publication:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
}

/* Glass on glass: the inner pane borrows the blur already done for it. */
.card .card {
  background: var(--glass-2);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none;
  border-color: var(--line);
}
.card .card::before { opacity: .45; }

.stat { text-align: center; padding: 20px 16px; }
.stat b {
  display: block;
  font-size: clamp(1.9rem, 1.5rem + 1.2vw, 2.4rem);
  line-height: 1.05;
  font-weight: 800;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
  background: linear-gradient(140deg, var(--link), var(--blue-bright));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.stat span { color: var(--muted); font-size: .86rem; font-weight: 500; }

fieldset {
  border: 1px solid var(--glass-border);
  border-radius: var(--radius);
  padding: 20px 22px;
  margin: 0 0 22px;
  background: var(--glass);
  box-shadow: var(--shadow-sm);
  backdrop-filter: var(--glass-blur-sm);
  -webkit-backdrop-filter: var(--glass-blur-sm);
}
legend {
  font-weight: 700;
  padding: 2px 12px;
  color: var(--link);
  background: var(--blue-soft);
  border-radius: var(--radius-pill);
  font-size: .85rem;
  letter-spacing: .01em;
}

/* ============================================================
   6b  Hero
   ============================================================ */

.hero {
  position: relative;
  overflow: hidden;
  color: #fff;
  padding: 68px 0 76px;
  background:
    radial-gradient(60% 90% at 78% 6%, rgba(240, 165, 0, .28), transparent 60%),
    radial-gradient(80% 100% at 6% 100%, rgba(79, 140, 255, .45), transparent 62%),
    linear-gradient(140deg, var(--blue) 0%, var(--blue-dark) 62%, #04193f 100%);
}
/* A slow sheen crossing the panel, so the hero is never quite still. */
.hero::after {
  content: "";
  position: absolute;
  inset: -50%;
  pointer-events: none;
  background: conic-gradient(from 0deg at 50% 50%, transparent 0deg,
    rgba(255, 255, 255, .07) 60deg, transparent 140deg, rgba(255, 255, 255, .05) 250deg, transparent 320deg);
  animation: spin 50s linear infinite;
}
.hero .wrap { position: relative; z-index: 1; }
.hero h1 {
  font-size: clamp(2rem, 1.3rem + 3vw, 3.15rem);
  max-width: 19ch;
  letter-spacing: -.03em;
  font-weight: 800;
  margin-bottom: .35em;
}
.hero p { color: #d3e0f5; max-width: 62ch; font-size: 1.06rem; }
.hero .actions { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 26px; }

@keyframes spin { to { transform: rotate(360deg); } }

/* ============================================================
   7  Buttons & pills
   ============================================================ */

.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 11px 20px;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: linear-gradient(135deg, var(--blue-bright), var(--blue));
  color: #fff;
  font: inherit;
  font-size: .94rem;
  font-weight: 650;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  overflow: hidden;
  isolation: isolate;
  box-shadow: var(--shadow-sm), inset 0 1px 0 rgba(255, 255, 255, .22);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur) var(--ease),
    background var(--dur) var(--ease), color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}
/* A light sweeping across the face on hover. */
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(100deg, transparent 20%, rgba(255, 255, 255, .28) 50%, transparent 80%);
  transform: translateX(-120%);
  transition: transform .6s var(--ease-out);
}
.btn:hover::before { transform: translateX(120%); }
.btn:hover { transform: translateY(-2px); box-shadow: var(--glow), inset 0 1px 0 rgba(255, 255, 255, .3); color: #fff; }
.btn:active { transform: translateY(0) scale(.98); box-shadow: var(--shadow-sm); }

.btn.gold {
  background: linear-gradient(135deg, var(--gold-bright), var(--gold));
  color: #452f00;
}
.btn.gold:hover { box-shadow: var(--glow-gold), inset 0 1px 0 rgba(255, 255, 255, .45); color: #3a2800; }

.btn.ghost {
  background: var(--glass-3);
  border-color: var(--line-strong);
  color: var(--link);
  box-shadow: none;
  backdrop-filter: var(--glass-blur-sm);
  -webkit-backdrop-filter: var(--glass-blur-sm);
}
.btn.ghost:hover {
  background: var(--blue-soft);
  border-color: color-mix(in srgb, var(--blue-bright) 45%, transparent);
  color: var(--link-hover);
  box-shadow: var(--shadow-sm);
}
.btn.ghost::before { background: linear-gradient(100deg, transparent 20%, color-mix(in srgb, var(--blue-bright) 14%, transparent) 50%, transparent 80%); }

.btn.light {
  background: rgba(255, 255, 255, .92);
  color: var(--blue-dark);
  border-color: rgba(255, 255, 255, .6);
}
.btn.light:hover { background: #fff; color: var(--blue-dark); }

.btn.danger { background: linear-gradient(135deg, #e0344d, var(--red)); }
.btn.danger:hover { box-shadow: 0 10px 28px -12px rgba(200, 16, 46, .65); }

/* Kept tight: rows of these sit in the action column of dense manage tables,
   where every pixel of width is one the title column does not get. */
.btn.small { padding: 6px 12px; font-size: .82rem; }
.btn[disabled], .btn.disabled { opacity: .5; pointer-events: none; box-shadow: none; }

/* Keyboard focus is always visible, whatever the control. */
a:focus-visible,
.btn:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--blue-bright);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
.site-head a:focus-visible,
.site-head button:focus-visible { outline-color: #fff; }

.pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 11px;
  border-radius: var(--radius-pill);
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .015em;
  background: var(--blue-soft);
  color: var(--link);
  border: 1px solid color-mix(in srgb, var(--link) 18%, transparent);
  vertical-align: middle;
  transition: transform var(--dur-fast) var(--ease-out);
}
.pill:hover { transform: translateY(-1px); }
/* A small dot in the badge's own colour reads faster than the word alone. */
.pill::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
  opacity: .8;
}
.pill.ok { background: var(--ok-bg); color: var(--ok); border-color: color-mix(in srgb, var(--ok) 28%, transparent); }
.pill.warn { background: var(--warn-bg); color: var(--warn); border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
.pill.err { background: var(--err-bg); color: var(--err); border-color: color-mix(in srgb, var(--err) 30%, transparent); }
.pill.muted { background: var(--glass-2); color: var(--muted); border-color: var(--line); }

/* ============================================================
   8  Messages
   ============================================================ */

.messages { list-style: none; margin: 0 0 22px; padding: 0; }
.messages li {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 13px 18px 13px 20px;
  border-radius: var(--radius);
  margin-bottom: 10px;
  background: var(--glass);
  color: var(--ink);
  border: 1px solid var(--glass-border);
  box-shadow: var(--shadow-sm);
  backdrop-filter: var(--glass-blur-sm);
  -webkit-backdrop-filter: var(--glass-blur-sm);
  overflow: hidden;
  animation: slide-in .45s var(--ease-out) both;
}
/* The accent stripe carries the meaning; the text keeps the page's own ink. */
.messages li::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 4px;
  background: var(--link);
}
.messages li.success { background: color-mix(in srgb, var(--ok-bg) 70%, var(--glass-2)); border-color: color-mix(in srgb, var(--ok) 24%, transparent); }
.messages li.success::before { background: var(--ok); }
.messages li.warning { background: color-mix(in srgb, var(--warn-bg) 70%, var(--glass-2)); border-color: color-mix(in srgb, var(--warn) 26%, transparent); }
.messages li.warning::before { background: var(--warn); }
.messages li.error { background: color-mix(in srgb, var(--err-bg) 70%, var(--glass-2)); border-color: color-mix(in srgb, var(--err) 26%, transparent); }
.messages li.error::before { background: var(--err); }

.messages .dismiss {
  margin-left: auto;
  flex: none;
  width: 26px; height: 26px;
  display: grid; place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
    transform var(--dur-fast) var(--ease);
}
.messages .dismiss:hover { background: var(--glass-2); color: var(--ink); transform: rotate(90deg); }
.messages li.leaving { animation: slide-out .3s var(--ease) forwards; }

@keyframes slide-in {
  from { opacity: 0; translate: 0 -10px; }
  to { opacity: 1; translate: 0 0; }
}
@keyframes slide-out {
  to { opacity: 0; translate: 0 -8px; max-height: 0; margin-bottom: 0;
       padding-top: 0; padding-bottom: 0; border-width: 0; }
}

/* ============================================================
   9  Tables
   ============================================================ */

.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--glass-border);
  border-radius: var(--radius);
  background: var(--glass);
  box-shadow: var(--shadow-sm);
  backdrop-filter: var(--glass-blur-sm);
  -webkit-backdrop-filter: var(--glass-blur-sm);
  /* The shadow at the cut edge tells you the table scrolls sideways. */
  background-attachment: local;
}
.card .table-wrap { background: transparent; box-shadow: none; border-color: var(--line); backdrop-filter: none; }

table.data { width: 100%; border-collapse: collapse; background: transparent; }
table.data th, table.data td {
  text-align: left;
  padding: 12px 14px;
  border-bottom: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
}
table.data th {
  font-size: .73rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  font-weight: 700;
  color: var(--muted);
  background: var(--glass-3);
}
table.data tbody tr { transition: background var(--dur-fast) var(--ease); }
table.data tbody tr:hover { background: var(--glass-2); }
table.data tr:last-child td { border-bottom: 0; }

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

form.stack { max-width: 660px; }
form .field { margin-bottom: 18px; }
form label {
  display: block;
  font-weight: 650;
  font-size: .88rem;
  margin-bottom: 6px;
  color: var(--ink);
  transition: color var(--dur-fast) var(--ease);
}
form .field:focus-within > label { color: var(--link); }

form input[type=text], form input[type=email], form input[type=password],
form input[type=number], form input[type=date], form input[type=url],
form input[type=tel], form input[type=search], form input[type=file],
form select, form textarea {
  width: 100%;
  padding: 11px 13px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  font: inherit;
  background: var(--glass-2);
  color: var(--ink);
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease);
}
form textarea { min-height: 120px; resize: vertical; }
form input::placeholder, form textarea::placeholder { color: var(--faint); }

form select {
  appearance: none;
  /* The chevron is drawn in CSS so the control matches in both schemes. */
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 19px) calc(50% + 1px), calc(100% - 14px) calc(50% + 1px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 36px;
}

form input:hover, form select:hover, form textarea:hover { border-color: var(--link); }
form input:focus, form select:focus, form textarea:focus {
  outline: none;
  border-color: var(--blue-bright);
  background: color-mix(in srgb, var(--card) 55%, transparent);
  box-shadow: var(--focus);
}
form .helptext { display: block; color: var(--muted); font-size: .82rem; margin-top: 5px; }

form .errorlist {
  list-style: none;
  margin: 7px 0 0;
  padding: 0;
  color: var(--err);
  font-size: .85rem;
  font-weight: 600;
  animation: shake .4s var(--ease);
}
form .field:has(.errorlist) input,
form .field:has(.errorlist) select,
form .field:has(.errorlist) textarea {
  border-color: var(--err);
  background: color-mix(in srgb, var(--err-bg) 60%, transparent);
}
form > .errorlist {
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  background: var(--err-bg);
  border: 1px solid color-mix(in srgb, var(--err) 30%, transparent);
  margin-bottom: 18px;
}

@keyframes shake {
  0%, 100% { translate: 0 0; }
  25% { translate: -4px 0; }
  75% { translate: 4px 0; }
}

form .checkbox { display: flex; align-items: center; gap: 10px; }
form .checkbox label { margin: 0; font-weight: 400; cursor: pointer; }
form input[type=checkbox], form input[type=radio] {
  width: 18px; height: 18px;
  flex: none;
  accent-color: var(--blue-bright);
  cursor: pointer;
}

form .actions { display: flex; gap: 12px; align-items: center; margin-top: 24px; flex-wrap: wrap; }

.filters {
  display: flex;
  gap: 12px;
  align-items: flex-end;
  flex-wrap: wrap;
  margin-bottom: 22px;
  padding: 16px 18px;
  border-radius: var(--radius);
  background: var(--glass);
  border: 1px solid var(--glass-border);
  box-shadow: var(--shadow-sm);
  backdrop-filter: var(--glass-blur-sm);
  -webkit-backdrop-filter: var(--glass-blur-sm);
}
/* The fields share the row evenly and wrap together, so a search box never
   ends up half the width of the select beside it. */
.filters .field { margin: 0; flex: 1 1 190px; min-width: 0; }
.filters label { font-size: .78rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.filters .btn { flex: none; }

/* ============================================================
   11  Portal
   ============================================================ */

.portal { display: grid; grid-template-columns: 234px 1fr; gap: 28px; align-items: start; }

.sidenav {
  position: sticky;
  top: 96px;
  padding: 14px;
  border-radius: var(--radius);
  background: var(--glass);
  border: 1px solid var(--glass-border);
  box-shadow: var(--shadow);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
}
.sidenav a {
  position: relative;
  display: block;
  padding: 9px 13px;
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: var(--ink);
  font-size: .92rem;
  font-weight: 550;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
    padding-left var(--dur) var(--ease-out);
}
/* The marker slides out from the left edge on hover and stays for the page
   you are on. */
.sidenav a::before {
  content: "";
  position: absolute;
  left: 4px; top: 50%;
  width: 3px; height: 0;
  border-radius: 3px;
  background: var(--blue-bright);
  transform: translateY(-50%);
  transition: height var(--dur) var(--ease-out);
}
.sidenav a:hover { background: var(--glass-2); color: var(--link); padding-left: 17px; }
.sidenav a:hover::before { height: 16px; }
.sidenav a.active {
  background: linear-gradient(135deg, var(--blue-bright), var(--blue));
  color: #fff;
  font-weight: 700;
  box-shadow: var(--glow);
}
.sidenav a.active::before { height: 0; }
.sidenav .label {
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .09em;
  color: var(--faint);
  font-weight: 700;
  margin: 16px 0 6px;
  padding-left: 13px;
}
.sidenav .label:first-child { margin-top: 4px; }
.sidenav form { margin-top: 10px; padding: 0 4px; }
.sidenav form .btn { width: 100%; }

/* ============================================================
   12  Member bits
   ============================================================ */

/* The circle draws the member's initials, and a picture -- their upload, or
   their Gravatar -- is laid over the top of them. A member with no Gravatar
   gets a 404 by design (accounts/gravatar.py), and an image that 404s or
   otherwise fails to load simply leaves the initials showing, with no script
   involved. The initials size themselves off the circle, so one rule covers
   the 48px directory thumbnails and the 120px one on a profile. */
.avatar {
  position: relative;
  display: inline-grid;
  place-items: center;
  width: 64px; height: 64px;
  flex-shrink: 0;
  border-radius: 50%;
  overflow: hidden;
  object-fit: cover;
  background: linear-gradient(140deg, var(--blue-soft), color-mix(in srgb, var(--gold) 18%, transparent));
  box-shadow: 0 0 0 1px var(--glass-border), var(--shadow-sm);
  transition: transform var(--dur) var(--spring), box-shadow var(--dur) var(--ease);
}
.avatar:hover { transform: scale(1.06); box-shadow: 0 0 0 2px color-mix(in srgb, var(--blue-bright) 45%, transparent), var(--shadow); }
.avatar.lg { width: 120px; height: 120px; }
.avatar .avatar-initials { font-size: .38em; line-height: 1; font-weight: 800; color: var(--link); }
.avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

.kv { display: grid; grid-template-columns: 180px 1fr; gap: 10px 18px; }
.kv dt { color: var(--muted); font-size: .86rem; font-weight: 600; }
.kv dd { margin: 0; }

.timeline { position: relative; border-left: 2px solid var(--line); padding-left: 20px; }
.timeline .item { position: relative; margin-bottom: 20px; }
.timeline .item::before {
  content: "";
  position: absolute;
  left: -27px; top: 8px;
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--blue-bright);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--blue-bright) 18%, transparent);
}
.timeline .item .date { font-size: .8rem; color: var(--muted); }

/* ============================================================
   13  Proposals
   ============================================================ */

.proposal-head { display: flex; gap: 18px; align-items: flex-start; }

.tally { text-align: center; min-width: 58px; flex-shrink: 0; }
.tally b {
  display: block;
  font-size: 1.55rem;
  line-height: 1.1;
  font-weight: 800;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.tally b.up { color: var(--ok); }
.tally b.down { color: var(--err); }
.tally span { color: var(--faint); font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; font-weight: 700; }
.tally.big b { font-size: 2.5rem; }

.votes { display: flex; gap: 8px; margin: 14px 0; flex-wrap: wrap; }

/* ============================================================
   14  Publications
   ============================================================ */

.card.publication { display: flex; gap: 18px; align-items: flex-start; }

/* A title page is portrait; the thumbnail keeps that shape so a row of cards
   lines up whatever was uploaded. */
.cover-thumb {
  width: 84px; height: 112px; flex-shrink: 0;
  object-fit: cover; object-position: top;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--blue-soft);
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease);
}
.card.publication:hover .cover-thumb { transform: translateY(-2px) rotate(-1.5deg); box-shadow: var(--shadow); }

/* ============================================================
   15  Pagination
   ============================================================ */

.pagination { display: flex; gap: 8px; justify-content: center; margin-top: 32px; align-items: center; flex-wrap: wrap; }
.pagination a, .pagination span {
  padding: 8px 15px;
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-pill);
  text-decoration: none;
  background: var(--glass);
  font-size: .88rem;
  font-weight: 600;
  backdrop-filter: var(--glass-blur-sm);
  -webkit-backdrop-filter: var(--glass-blur-sm);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur) var(--ease),
    border-color var(--dur-fast) var(--ease);
}
.pagination a:hover { transform: translateY(-2px); box-shadow: var(--shadow); border-color: color-mix(in srgb, var(--blue-bright) 40%, transparent); }
.pagination .current {
  background: linear-gradient(135deg, var(--blue-bright), var(--blue));
  color: #fff;
  border-color: transparent;
  box-shadow: var(--glow);
}

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

footer.site {
  position: relative;
  margin-top: 40px;
  background: linear-gradient(180deg, rgba(7, 42, 97, .94), #04173a);
  background: linear-gradient(180deg, color-mix(in srgb, var(--blue-dark) 92%, transparent), #04173a);
  color: #b7c6e2;
  padding: 48px 0 30px;
  font-size: .9rem;
  border-top: 1px solid rgba(255, 255, 255, .1);
  backdrop-filter: var(--glass-blur-sm);
  -webkit-backdrop-filter: var(--glass-blur-sm);
}
footer.site a { color: #d9e3f6; text-decoration: none; }
footer.site a:hover { color: #fff; text-decoration: underline; }
footer.site h4 {
  color: #fff;
  font-size: .78rem;
  text-transform: uppercase;
  letter-spacing: .09em;
  margin-bottom: .9em;
}
footer.site .cols { display: grid; gap: 28px; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
footer.site ul.clean li { margin-bottom: 7px; }
footer.site ul.clean a { position: relative; display: inline-block; transition: transform var(--dur-fast) var(--ease-out); }
footer.site ul.clean a:hover { transform: translateX(4px); }
footer.site .legal {
  border-top: 1px solid rgba(255, 255, 255, .13);
  margin-top: 32px;
  padding-top: 18px;
  font-size: .82rem;
  color: #91a3c4;
}

/* ============================================================
   17  Page furniture
   ============================================================ */

/* Jumps a keyboard visitor past the navigation. Off-screen until focused. */
.skip-link {
  position: fixed;
  top: 8px; left: 8px;
  z-index: 200;
  padding: 10px 18px;
  border-radius: var(--radius-pill);
  background: var(--card);
  color: var(--link);
  font-weight: 700;
  box-shadow: var(--shadow-lg);
  transform: translateY(-160%);
  transition: transform var(--dur) var(--ease-out);
}
.skip-link:focus { transform: translateY(0); }

/* How far down the page you are. The width comes from js/ui.js. */
.scroll-progress {
  position: fixed;
  top: 0; left: 0;
  z-index: 90;
  height: 3px;
  width: 100%;
  transform: scaleX(var(--progress, 0));
  transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--blue-bright), var(--gold));
  pointer-events: none;
  will-change: transform;
}

.to-top {
  position: fixed;
  right: 22px; bottom: 22px;
  z-index: 80;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border: 1px solid var(--glass-border);
  border-radius: 50%;
  background: var(--glass);
  color: var(--link);
  cursor: pointer;
  box-shadow: var(--shadow-lg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  opacity: 0;
  visibility: hidden;
  transform: translateY(14px) scale(.85);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--spring),
    visibility var(--dur), background var(--dur-fast) var(--ease);
}
html.is-deep .to-top { opacity: 1; visibility: visible; transform: none; }
.to-top:hover { background: var(--blue-soft); transform: translateY(-3px); }
.to-top:active { transform: scale(.92); }
.to-top svg { width: 18px; height: 18px; }

/* ============================================================
   18  Motion
   ============================================================ */

/* Panels arrive from just below. `translate` is used rather than `transform`
   so a hover lift, which is a transform, still works once the animation has
   finished and its fill is holding the end frame. */
@keyframes rise {
  from { opacity: 0; translate: 0 18px; }
  to { opacity: 1; translate: 0 0; }
}

.card,
.section-head,
.table-wrap,
.filters,
.hero .wrap > *,
.timeline .item,
fieldset {
  animation: rise var(--dur-slow) var(--ease-out) both;
}

/* With scripting on, a panel waits until it is scrolled into view: the
   animation is held at its first frame and js/ui.js lets it run. If the script
   never arrives, the safety class in the head releases everything. */
html.js :is(.card, .section-head, .table-wrap, .filters, .timeline .item, fieldset) {
  animation-play-state: paused;
}
html.js :is(.card, .section-head, .table-wrap, .filters, .timeline .item, fieldset).in-view,
html.reveal-all :is(.card, .section-head, .table-wrap, .filters, .timeline .item, fieldset) {
  animation-play-state: running;
}
/* The hero is above the fold by definition, so it never waits. */
html.js .hero .wrap > * { animation-play-state: running; }

.hero .wrap > *:nth-child(1) { animation-delay: .05s; }
.hero .wrap > *:nth-child(2) { animation-delay: .14s; }
.hero .wrap > *:nth-child(3) { animation-delay: .23s; }

/* A row of cards comes in one after another rather than all at once. */
.grid > *:nth-child(2) { animation-delay: .06s; }
.grid > *:nth-child(3) { animation-delay: .12s; }
.grid > *:nth-child(4) { animation-delay: .18s; }
.grid > *:nth-child(5) { animation-delay: .24s; }
.grid > *:nth-child(6) { animation-delay: .3s; }

/* The mobile menu panel. */
@keyframes drop-in {
  from { opacity: 0; translate: 0 -12px; scale: .98; }
  to { opacity: 1; translate: 0 0; scale: 1; }
}

/* ============================================================
   19  Responsive
   ============================================================ */

@media (max-width: 1000px) {
  nav.main { gap: 0; }
  nav.main a { padding: 8px 10px; font-size: .88rem; }
}

/* Four across leaves an orphan on a tablet, so they pair up instead. */
@media (max-width: 880px) {
  .grid.cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
  .nav-toggle { display: grid; }
  .masthead .wrap { gap: 10px; }

  nav.main {
    position: absolute;
    top: calc(100% + 8px);
    right: 16px;
    left: 16px;
    margin-left: 0;
    display: none;
    flex-direction: column;
    align-items: stretch;
    gap: 3px;
    padding: 12px;
    border-radius: var(--radius);
    /* Opaque, not glass: the panel sits inside the header, which has already
       taken the blur, so anything translucent here would simply show the page
       through sharp and cost legibility for nothing. */
    background: linear-gradient(170deg, var(--blue-dark), #04193f);
    border: 1px solid rgba(255, 255, 255, .16);
    box-shadow: var(--shadow-lg);
  }
  html.nav-open nav.main { display: flex; animation: drop-in var(--dur) var(--ease-out) both; }
  nav.main a { padding: 11px 14px; border-radius: var(--radius-sm); font-size: .95rem; }
  nav.main a.cta { margin: 6px 0 0; text-align: center; }
  nav.main .theme-toggle { align-self: center; margin-top: 10px; }
  .masthead { position: relative; }
}

/* Same point as the main menu: below it the portal menu gives its column back,
   which is what the wide manage tables need most. */
@media (max-width: 900px) {
  .portal { grid-template-columns: 1fr; }
  .sidenav { position: static; }
  /* On a narrow screen the portal menu reads better as a row of chips. */
  .sidenav { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
  .sidenav a { padding: 8px 13px; border-radius: var(--radius-pill); }
  .sidenav a:hover { padding-left: 13px; }
  .sidenav a::before { display: none; }
  .sidenav .label { width: 100%; margin: 8px 0 0; padding-left: 4px; }
  .sidenav .label:first-child { margin-top: 0; }
  .sidenav form { width: 100%; margin-top: 8px; }
  .kv { grid-template-columns: 1fr; gap: 2px 0; }
  .kv dd { margin-bottom: 12px; }
}

@media (max-width: 560px) {
  .wrap { padding: 0 16px; }
  main { padding: 22px 0 56px; }
  .card { padding: 18px; }
  .card img.cover { width: calc(100% + 36px); margin: -18px -18px 16px; }
  .hero { padding: 48px 0 54px; }
  .card.publication { flex-direction: column; }
  .to-top { right: 14px; bottom: 14px; }
  .topbar .wrap { justify-content: center; gap: 8px; font-size: .74rem; }
}

/* ============================================================
   20  Reduced motion & print
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  html.js :is(.card, .section-head, .table-wrap, .filters, .timeline .item, fieldset) {
    animation-play-state: running;
  }
  body::before { animation: none; }
}

@media print {
  body::before, body::after, .site-head, footer.site, .to-top, .scroll-progress, .skip-link { display: none !important; }
  .card, .table-wrap, fieldset { box-shadow: none; border: 1px solid #ccc; background: #fff; backdrop-filter: none; }
  * { animation: none !important; }
}

/* ============================================================
   21  Charts
   ============================================================

   The roster breakdowns on /manage/statistics/. Which chart a breakdown gets
   is decided in backoffice/statistics.py, where the numbers are counted and
   their job is known; this only draws what it is told.

   Three palettes, three jobs:

     * A ranking or a year series is ONE series, so every bar is one colour.
       Shading them light-to-dark by size would spend the only free channel
       restating what the bar lengths already say.
     * A part-to-whole names its parts, so each part takes the next slot of a
       categorical palette whose order is what keeps neighbouring parts apart
       for a colour-blind reader (validated: worst adjacent CVD dE 9.1 light,
       8.4 dark, against these surfaces).
     * Three light slots sit under 3:1 against the surface. That is allowed
       only with relief, which here is both: every part is named and counted in
       the legend, and every chart keeps its table a fold away.

   No text ever wears a series colour -- labels and values stay in the page's
   own ink, and the swatch beside them carries the identity. */

.chart {
  --track: color-mix(in srgb, var(--ink) 8%, transparent);
  --chart-surface: #f6f8fc;
  --series-1: #2a78d6;  --on-1: #ffffff;
  --series-2: #eb6834;  --on-2: #101828;
  --series-3: #1baf7a;  --on-3: #101828;
  --series-4: #eda100;  --on-4: #101828;
  --series-5: #e87ba4;  --on-5: #101828;
  --series-6: #008300;  --on-6: #ffffff;
  margin: 4px 0 0;
}

/* The dark steps are the same six hues restepped for the dark surface, not an
   automatic flip of the light ones. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .chart {
    --chart-surface: #141a2a;
    --series-1: #3987e5;  --on-1: #101828;
    --series-2: #d95926;  --on-2: #101828;
    --series-3: #199e70;  --on-3: #101828;
    --series-4: #c98500;  --on-4: #101828;
    --series-5: #d55181;  --on-5: #101828;
    --series-6: #008300;  --on-6: #ffffff;
  }
}
:root[data-theme="dark"] .chart {
  --chart-surface: #141a2a;
  --series-1: #3987e5;  --on-1: #101828;
  --series-2: #d95926;  --on-2: #101828;
  --series-3: #199e70;  --on-3: #101828;
  --series-4: #c98500;  --on-4: #101828;
  --series-5: #d55181;  --on-5: #101828;
  --series-6: #008300;  --on-6: #ffffff;
}

.series-1 { background: var(--series-1); color: var(--on-1); }
.series-2 { background: var(--series-2); color: var(--on-2); }
.series-3 { background: var(--series-3); color: var(--on-3); }
.series-4 { background: var(--series-4); color: var(--on-4); }
.series-5 { background: var(--series-5); color: var(--on-5); }
.series-6 { background: var(--series-6); color: var(--on-6); }

.chart-title {
  font-size: .76rem;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--muted);
  margin: 20px 0 8px;
}
.chart-title:first-of-type { margin-top: 10px; }

/* ---------- Ranking: bars down the page ---------- */

.chart-rank .bars { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
.chart-rank li {
  display: grid;
  grid-template-columns: minmax(0, 9rem) minmax(60px, 1fr) auto;
  gap: 12px;
  align-items: center;
  border-radius: var(--radius-sm);
  transition: background var(--dur-fast) var(--ease);
}
.chart-rank li:hover { background: var(--glass-2); }

.bar-label { font-size: .87rem; overflow-wrap: anywhere; }
.bar-track {
  height: 14px;
  background: var(--track);
  /* Square where it starts, rounded where the data ends. */
  border-radius: 0 4px 4px 0;
  border-left: 1px solid var(--line-strong);
}
.bar-fill {
  display: block;
  height: 100%;
  min-width: 3px;
  border-radius: 0 4px 4px 0;
  background: var(--series-1);
}
.bar-value { font-size: .9rem; font-weight: 700; font-variant-numeric: tabular-nums; }

/* ---------- Part-to-whole: one stacked bar ---------- */

/* The 2px gaps are the separator -- never an outline drawn round a segment.
   They over-subscribe the row, so the segments shrink in proportion to their
   own width, which leaves every ratio exactly as it was. */
.chart-share .stack {
  display: flex;
  gap: 2px;
  height: 22px;
  border-radius: 4px;
  overflow: visible;
}
.chart-share .seg {
  flex: 0 1 auto;
  min-width: 3px;
  display: grid;
  place-items: center;
  container-type: inline-size;
  transition: filter var(--dur-fast) var(--ease);
}
.chart-share .seg:first-child { border-radius: 4px 0 0 4px; }
.chart-share .seg:last-child { border-radius: 0 4px 4px 0; }
.chart-share .seg:only-child { border-radius: 4px; }
.chart-share .seg:hover { filter: brightness(1.08); }

/* Drawn only where it fits. A figure cropped by its own segment reads worse
   than no figure at all, and the legend below names every part regardless. */
.chart-share .seg-value { display: none; font-size: .7rem; font-weight: 700; }
@container (min-width: 46px) {
  .chart-share .seg-value { display: block; }
}

.chart-share .legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  margin-top: 14px;
}
.chart-share .key { display: flex; align-items: center; gap: 7px; font-size: .84rem; }
.chart-share .swatch {
  width: 10px; height: 10px;
  flex: none;
  border-radius: 3px;
}
.chart-share .key-label { color: var(--muted); }
.chart-share .key b { font-variant-numeric: tabular-nums; }
.chart-share .key-share { color: var(--faint); font-size: .78rem; }

/* ---------- Series: columns along a year axis ---------- */

.chart-series .columns-scroll { overflow-x: auto; padding-bottom: 2px; }
.chart-series .columns {
  display: flex;
  align-items: stretch;
  gap: 2px;
  height: 190px;
  border-bottom: 1px solid var(--line-strong);
}
.chart-series .column {
  flex: 1 0 30px;
  display: grid;
  grid-template-rows: 17px 1fr 20px;
  justify-items: center;
  align-items: end;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  transition: background var(--dur-fast) var(--ease);
}
.chart-series .column:hover { background: var(--glass-2); }
.chart-series .col-value {
  font-size: .72rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.chart-series .col-fill {
  width: 100%;
  max-width: 22px;
  min-height: 2px;
  background: var(--series-1);
  border-radius: 4px 4px 0 0;
  align-self: end;
}
/* A year nobody finished in is drawn as the gap it is, not skipped: a stub in
   the track's own grey, so it reads as "nothing here" and not as "one". */
.chart-series .is-empty .col-fill { background: var(--track); min-height: 2px; }
.chart-series .col-label {
  font-size: .68rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  padding-top: 5px;
  align-self: start;
}
.chart-series .axis-note { font-size: .78rem; color: var(--faint); margin-top: 10px; }

/* ---------- The numbers, a fold away ---------- */

.table-view { margin-top: 14px; }
.table-view summary {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  cursor: pointer;
  font-size: .82rem;
  font-weight: 650;
  color: var(--link);
  padding: 4px 2px;
  list-style: none;
}
.table-view summary::-webkit-details-marker { display: none; }
.table-view summary::before {
  content: "";
  width: 6px; height: 6px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform var(--dur) var(--ease-out);
}
.table-view[open] summary::before { transform: rotate(45deg); }
.table-view summary:hover { color: var(--link-hover); }
.table-view .table-wrap { margin-top: 10px; animation: rise .4s var(--ease-out) both; }
.table-view td:nth-child(2), .table-view th:nth-child(2),
.table-view td:nth-child(3), .table-view th:nth-child(3) { text-align: right; width: 5.5rem; }

/* A year axis wants the full width of the page, not half of it. */
.grid.cols-2 > .span-2 { grid-column: 1 / -1; }

/* Bars and columns wipe in with the card that holds them, and hold still
   until it is scrolled to -- the same mechanism as section 18. */
@keyframes wipe-right { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
@keyframes wipe-up { from { clip-path: inset(100% 0 0 0); } to { clip-path: inset(0 0 0 0); } }

.bar-fill, .seg { animation: wipe-right .75s var(--ease-out) both; }
.col-fill { animation: wipe-up .6s var(--ease-out) both; }

/* The two selectors are deliberately the same weight, so the later one -- the
   safety net for a page whose scripts never arrived -- is the one that wins.
   Written with `.chart-share .seg` the pause would outweigh the release, and a
   chart would sit at zero width for good. */
html.js .card:not(.in-view) :is(.bar-fill, .seg, .col-fill) { animation-play-state: paused; }
html.reveal-all .card :is(.bar-fill, .seg, .col-fill) { animation-play-state: running; }

.bars li:nth-child(2) .bar-fill { animation-delay: .05s; }
.bars li:nth-child(3) .bar-fill { animation-delay: .1s; }
.bars li:nth-child(4) .bar-fill { animation-delay: .15s; }
.bars li:nth-child(5) .bar-fill { animation-delay: .2s; }
.bars li:nth-child(6) .bar-fill { animation-delay: .25s; }
.bars li:nth-child(n+7) .bar-fill { animation-delay: .3s; }

@media (max-width: 560px) {
  .chart-rank li { grid-template-columns: minmax(0, 7rem) minmax(40px, 1fr) auto; gap: 8px; }
  .chart-series .columns { height: 150px; }
}

@media (prefers-reduced-motion: reduce) {
  html.js .card:not(.in-view) :is(.bar-fill, .chart-share .seg, .col-fill) {
    animation-play-state: running;
  }
}

@media print {
  .table-view[open] .table-wrap, .table-view .table-wrap { animation: none; }
}

/* ---------------------------------------------------------------------------
   Privacy Policy and Terms of Use (/privacy/, /terms/).
   A reading page: one comfortable column, a contents card, numbered sections
   that can be linked to, and tables that scroll on their own when narrow.
   ------------------------------------------------------------------------- */
.legal { max-width: 780px; }
.legal .meta { color: var(--muted); font-size: .92rem; margin-top: 4px; }
.legal-draft {
  margin: 18px 0 6px;
  padding: 14px 18px;
  border-radius: var(--radius-sm);
  background: var(--warn-bg);
  color: var(--warn);
  border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent);
  font-size: .95rem;
}
.legal-draft strong { display: block; margin-bottom: 2px; }
.legal-toc { margin: 22px 0 8px; }
.legal-toc h2 { font-size: .8rem; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); margin: 0 0 8px; }
.legal-toc ol { margin: 0; padding-left: 1.3em; columns: 2 16rem; column-gap: 28px; }
.legal-toc li { margin: 0 0 4px; break-inside: avoid; }
.legal section { scroll-margin-top: 96px; }
.legal section h2 { margin-top: 40px; }
.legal p, .legal li { line-height: 1.7; }
.legal li { margin-bottom: 6px; }
.legal .table-wrap { margin: 10px 0 18px; }
.legal table.data td:first-child { font-weight: 600; width: 30%; }
.legal .tag-sensitive {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 5px;
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--err);
  background: var(--err-bg);
  vertical-align: 1px;
}
.legal mark.todo {
  background: var(--warn-bg);
  color: var(--warn);
  padding: 0 5px;
  border-radius: 4px;
}
.legal .basis { color: var(--muted); font-size: .9rem; }
.legal address { font-style: normal; }
