/* TeamVanilla: shared stylesheet for teamvanilla.org and teamvanilla.dev.
   The two copies must stay identical. Rules for editing are in STYLE.md. */

/* Two palettes on one axis: spring green in light mode, dark evergreen in dark.
   The page surface is green in both; the header green moves with it. The dark tokens are declared twice on purpose, once for the OS
   preference and once for an explicit choice, because a media query and a plain
   selector cannot share one rule. */

:root {
  --green: #1ed67c;
  --green-hover: #26e086;
  --green-deep: #17b566;
  --green-light: #2aeb8c;
  --on-green: #06281a;
  --on-green-soft: #1d5c3c;
  --nav-link: rgba(6, 40, 26, .78);
  --gh-edge: rgba(6, 40, 26, .35);
  --gh-hover: rgba(6, 40, 26, .1);
  --bg: #dcf9e9;
  --bg-2: #c9f2dc;
  --bg-3: #c2efd6;
  --ink: #06281a;
  --text: #17402e;
  --muted: #3f6c57;
  --rule: #a5dfc2;
  --link: #0a7a45;
  --shade-top: rgba(6, 40, 26, .22);
  --shade-drop: rgba(6, 40, 26, .2);
  --shade-btn: rgba(6, 40, 26, .16);
  --fx-edge: drop-shadow(0 0 1px rgba(6, 40, 26, .55));
  --discord: #5865f2;
  --discord-hover: #4752c4;
  --github: #24292f;
  --github-hover: #32383f;
  --github-edge: #3d444d;
  --sans: "Segoe UI", system-ui, -apple-system, Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "Cascadia Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --green: #0e5a3a;
    --green-hover: #136b46;
    --green-deep: #093f28;
    --green-light: #14764c;
    --on-green: #e6f6ec;
    --on-green-soft: #9ec9b1;
    --nav-link: rgba(230, 246, 236, .82);
    --gh-edge: rgba(230, 246, 236, .35);
    --gh-hover: rgba(230, 246, 236, .12);
    --bg: #0a2419;
    --bg-2: #0f3324;
    --bg-3: #071b13;
    --ink: #e6f6ec;
    --text: #bcdccb;
    --muted: #7fa892;
    --rule: #1b4433;
    --link: #46e08f;
    --shade-top: rgba(0, 0, 0, .45);
    --shade-drop: rgba(0, 0, 0, .42);
    --shade-btn: rgba(0, 0, 0, .32);
    --fx-edge: none;
    color-scheme: dark;
  }
}

:root[data-theme="dark"] {
  --green: #0e5a3a;
  --green-hover: #136b46;
  --green-deep: #093f28;
  --green-light: #14764c;
  --on-green: #e6f6ec;
  --on-green-soft: #9ec9b1;
  --nav-link: rgba(230, 246, 236, .82);
  --gh-edge: rgba(230, 246, 236, .35);
  --gh-hover: rgba(230, 246, 236, .12);
  --bg: #0a2419;
  --bg-2: #0f3324;
  --bg-3: #071b13;
  --ink: #e6f6ec;
  --text: #bcdccb;
  --muted: #7fa892;
  --rule: #1b4433;
  --link: #46e08f;
  --shade-top: rgba(0, 0, 0, .45);
  --shade-drop: rgba(0, 0, 0, .42);
  --shade-btn: rgba(0, 0, 0, .32);
  --fx-edge: none;
  color-scheme: dark;
}

/* Registering the colour tokens makes them animatable. That matters because the
   header is a gradient, and a gradient cannot be transitioned: animating the
   variables instead re-resolves every rule that reads them, so the header, the
   page, the panels and the text all ease together rather than snapping. A
   browser without @property ignores this block and switches instantly. */
@property --green { syntax: "<color>"; inherits: true; initial-value: #1ed67c; }
@property --green-hover { syntax: "<color>"; inherits: true; initial-value: #26e086; }
@property --green-deep { syntax: "<color>"; inherits: true; initial-value: #17b566; }
@property --green-light { syntax: "<color>"; inherits: true; initial-value: #2aeb8c; }
@property --on-green { syntax: "<color>"; inherits: true; initial-value: #06281a; }
@property --on-green-soft { syntax: "<color>"; inherits: true; initial-value: #1d5c3c; }
@property --nav-link { syntax: "<color>"; inherits: true; initial-value: rgba(6, 40, 26, .78); }
@property --gh-edge { syntax: "<color>"; inherits: true; initial-value: rgba(6, 40, 26, .35); }
@property --gh-hover { syntax: "<color>"; inherits: true; initial-value: rgba(6, 40, 26, .1); }
@property --bg { syntax: "<color>"; inherits: true; initial-value: #dcf9e9; }
@property --bg-2 { syntax: "<color>"; inherits: true; initial-value: #c9f2dc; }
@property --bg-3 { syntax: "<color>"; inherits: true; initial-value: #c2efd6; }
@property --ink { syntax: "<color>"; inherits: true; initial-value: #06281a; }
@property --text { syntax: "<color>"; inherits: true; initial-value: #17402e; }
@property --muted { syntax: "<color>"; inherits: true; initial-value: #3f6c57; }
@property --rule { syntax: "<color>"; inherits: true; initial-value: #a5dfc2; }
@property --link { syntax: "<color>"; inherits: true; initial-value: #0a7a45; }
@property --shade-top { syntax: "<color>"; inherits: true; initial-value: rgba(6, 40, 26, .22); }
@property --shade-drop { syntax: "<color>"; inherits: true; initial-value: rgba(6, 40, 26, .2); }
@property --shade-btn { syntax: "<color>"; inherits: true; initial-value: rgba(6, 40, 26, .16); }

@media (prefers-reduced-motion: no-preference) {
  :root {
    transition:
      --green .45s ease-in-out,
      --green-hover .45s ease-in-out,
      --green-deep .45s ease-in-out,
      --green-light .45s ease-in-out,
      --on-green .45s ease-in-out,
      --on-green-soft .45s ease-in-out,
      --nav-link .45s ease-in-out,
      --gh-edge .45s ease-in-out,
      --gh-hover .45s ease-in-out,
      --bg .45s ease-in-out,
      --bg-2 .45s ease-in-out,
      --bg-3 .45s ease-in-out,
      --ink .45s ease-in-out,
      --text .45s ease-in-out,
      --muted .45s ease-in-out,
      --rule .45s ease-in-out,
      --link .45s ease-in-out,
      --shade-top .45s ease-in-out,
      --shade-drop .45s ease-in-out,
      --shade-btn .45s ease-in-out;
  }
}

* { box-sizing: border-box; }

/* our own pointer, everywhere: MWTools' mouse.png, 32x32 with the tip at 1,1.
   It overrides the hand on links too, so hover states carry the affordance:
   links thicken their underline and buttons change colour. */
html, body { cursor: url("/assets/cursor.png") 1 1, default; }
/* links and buttons need it spelled out: the browser's own a:any-link rule
   sets the hand, and that beats an inherited value. */
a[href], button, summary, label, [role="button"] { cursor: url("/assets/cursor.png") 1 1, default; }

html { background: var(--bg); -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  color: var(--text);
  font: 1rem/1.65 var(--sans);
}

a { color: var(--link); text-underline-offset: .18em; text-decoration-thickness: 1px; }
a:hover { text-decoration-thickness: 2px; }

.skip { position: absolute; left: -999px; }
.skip:focus { left: 1rem; top: 1rem; z-index: 30; background: var(--bg); color: var(--ink); padding: .4rem .7rem; }

/* green header: the nav strip only */

.top {
  position: relative;
  color: var(--on-green);
  background:
    radial-gradient(900px 300px at 30% 0%, rgba(255, 255, 255, .16), transparent 70%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='62.35' viewBox='0 0 36 62.35'%3E%3Cpath d='M18 0L36 10.39V31.18L18 41.57L0 31.18V10.39Z M18 41.57V62.35' fill='none' stroke='%23122419' stroke-opacity='.09'/%3E%3C/svg%3E") 0 0 / 36px 62.35px,
    linear-gradient(160deg, var(--green-light) 0%, var(--green) 45%, var(--green-deep) 100%);
}

/* the soft shadow the header casts onto the page */
.top::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  height: 26px;
  background: linear-gradient(var(--shade-top), transparent);
  pointer-events: none;
}

.navstrip {
  position: relative;
  z-index: 10;
  background: rgba(8, 40, 22, .2);
  border-bottom: 1px solid rgba(255, 255, 255, .14);
  box-shadow: 0 1px 0 rgba(8, 40, 22, .18);
}

.bar {
  max-width: 64rem;
  margin: 0 auto;
  padding: .8rem 1.25rem;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: .6rem 1.15rem;
}

.brand {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  color: var(--on-green);
  font-weight: 700;
  font-size: 1.05rem;
  text-decoration: none;
}
.brand img { display: block; width: 58px; height: 34px; }
.brand span { font-weight: 400; color: var(--on-green-soft); }

/* one line, always: the .dev nav carries six items, five of them menus, so
   the row is wrapped only by the breakpoint below, never by running out of room. */
.bar nav { flex: 1; min-width: 0; display: flex; flex-wrap: nowrap; align-items: center; gap: .2rem .95rem; font-size: .95rem; }
.bar nav a { color: var(--nav-link); text-decoration: none; }
.bar nav > a, .menu > a { white-space: nowrap; }
.bar nav a:hover, .bar nav a[aria-current] { color: var(--on-green); text-decoration: underline; }

/* hover menus under Libraries, Tools, Games, Encyclopedias and Web.
   CSS only, because the Content-Security-Policy allows no scripts. */
.menu { position: relative; }
/* the padding is the hover bridge down to .drop; the negative margin keeps it
   from making the strip taller than it is on teamvanilla.org. */
.menu > a { display: block; padding: .35rem 0; margin: -.35rem 0; }
.drop {
  display: none;
  position: absolute;
  top: 100%;
  left: -.75rem;
  z-index: 20;
  min-width: 11rem;
  padding: .4rem;
  background: var(--bg-2);
  border: 1px solid var(--rule);
  border-radius: 12px;
  box-shadow: 0 10px 28px var(--shade-drop);
}
@media (hover: hover) { .menu:hover .drop { display: block; } }
.menu:focus-within .drop { display: block; }
.bar nav .drop a {
  display: block;
  padding: .45rem .75rem;
  border-radius: 8px;
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
}
.bar nav .drop a:hover, .bar nav .drop a:focus { background: var(--bg-3); color: var(--ink); }
/* "in development" after a menu entry that leads to a hub row, not a page */
.bar nav .drop .drop-note { margin-left: .4rem; color: var(--muted); font-size: .8rem; }

/* margin-left: auto keeps the pills against the right edge on the pages that
   carry no nav at all — teamvanilla.org and both 404s — where there is no
   flex: 1 element in the bar to push them over. */
.links { display: flex; align-items: center; gap: .5rem; margin-left: auto; }
.links svg { width: 18px; height: 18px; fill: currentColor; flex: none; }

/* header pills: outlined in the header's dark green. The brand colours are for buttons in the page. */

.gh {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  padding: .3rem .75rem;
  border: 1px solid var(--gh-edge);
  border-radius: 999px;
  color: var(--on-green);
  font-size: .9rem;
  font-weight: 600;
  text-decoration: none;
}
.gh:hover { background: var(--gh-hover); border-color: var(--on-green); }

/* theme switch: the same outlined pill as the header's other links, but a
   <button>, so it is reachable by keyboard and announces its state. Which icon
   shows is decided in CSS, for all three states — OS preference, and either
   explicit choice — so the button is never wrong before the script runs. */

.theme-toggle {
  background: none;
  font-family: inherit;
  line-height: 1.2;
  padding: .3rem .55rem;      /* icon only: the label used to change width and
                                 reflowed the header on every switch */
  cursor: url("/assets/cursor.png") 1 1, default;
}
.theme-toggle svg { fill: currentColor; }

/* default (light): offer the moon */
.theme-toggle .i-sun { display: none; }
.theme-toggle .i-moon { display: block; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .i-moon { display: none; }
  :root:not([data-theme="light"]) .theme-toggle .i-sun { display: block; }
}

:root[data-theme="dark"] .theme-toggle .i-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .i-sun { display: block; }
:root[data-theme="light"] .theme-toggle .i-moon { display: block; }
:root[data-theme="light"] .theme-toggle .i-sun { display: none; }

/* product banner: the artwork centred on the page background, the name under it.
   It sits below the header, not inside it, so the green stops at the nav strip. */

.hero {
  max-width: 64rem;
  margin: 0 auto;
  padding: 2.6rem 1.25rem 0;
  text-align: center;
}

/* the artwork. Every logo in assets/art/ carries its own transparency, so it
   sits straight on the page: no card, no frame. */
.cover { width: fit-content; max-width: 100%; margin: 0 auto 1.6rem; }
.cover img { display: block; width: auto; height: auto; max-width: 100%; max-height: 11rem; }

/* Three logos are drawn in white on transparency and would disappear on the
   light page, so they ship a light-theme twin with the white strokes inked in.
   No plate behind the art in either theme. */
.cover .art-dark { display: none; }
.cover .art-light { display: block; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .cover .art-light { display: none; }
  :root:not([data-theme="light"]) .cover .art-dark { display: block; }
}

:root[data-theme="dark"] .cover .art-light { display: none; }
:root[data-theme="dark"] .cover .art-dark { display: block; }
:root[data-theme="light"] .cover .art-light { display: block; }
:root[data-theme="light"] .cover .art-dark { display: none; }
/* the MWSDK banner is a photograph, not a logotype: it keeps its own edges,
   rounded so the rectangle reads as deliberate. */
.cover.photo img { border-radius: 14px; }

.hero h1 {
  margin: 0;
  font-size: 2.4rem;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -.01em;
  color: var(--ink);
}

/* a headline that is a sentence rather than a product name takes a smaller size,
   so it holds one line across the layout's full width. */
.hero h1.long { font-size: 2.2rem; }

.hero .sub { margin: .8rem auto 0; max-width: 42rem; font-size: 1.08rem; color: var(--muted); }

/* body */

.page { max-width: 64rem; margin: 0 auto; padding: 2.4rem 1.25rem 1rem; }
.page main { max-width: 46rem; min-width: 0; }
.page.solo main { margin: 0 auto; }

.page.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 15rem;
  gap: 3rem;
  align-items: start;
}

aside { position: sticky; top: 1.5rem; min-width: 0; }
.aside-box { background: var(--bg-2); border-radius: 14px; padding: 1.1rem 1.1rem 1.2rem; }
.aside-box .btn { display: flex; width: 100%; margin-bottom: .6rem; }
.aside-box dl { margin: 1rem 0 0; font-size: .9rem; }
.aside-box dt { color: var(--muted); font-size: .8rem; margin-top: .6rem; }
.aside-box dd { margin: 0; color: var(--text); }

@media (max-width: 860px) {
  .page.split { grid-template-columns: minmax(0, 1fr); gap: 1.5rem; }
  aside { position: static; }
}

h2, h3 { color: var(--ink); line-height: 1.25; }

h2 {
  font-size: 1.35rem;
  font-weight: 600;
  margin: 2.6rem 0 .8rem;
  padding-top: 1.3rem;
  border-top: 1px solid var(--rule);
}
main > h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }

h3 { font-size: 1.08rem; font-weight: 600; margin: 1.9rem 0 .4rem; }

p { margin: 0 0 1rem; }
ul, ol { padding-left: 1.25rem; margin: 0 0 1.1rem; }
li { margin: .3rem 0; }
strong { color: var(--ink); }

.note { color: var(--muted); font-size: .94rem; }

/* buttons: rounded, with the brand's own colour for Discord and GitHub */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  padding: .5rem 1.1rem;
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: .95rem;
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  box-shadow: 0 2px 6px var(--shade-btn);
}
.btn svg { width: 17px; height: 17px; fill: currentColor; flex: none; }

.btn-green { background: var(--green); color: var(--on-green); }
.btn-green:hover { background: var(--green-hover); }
.btn-dark { background: var(--bg-2); color: var(--text); border-color: var(--rule); font-weight: 600; }
.btn-dark:hover { color: var(--ink); border-color: var(--muted); }
.btn-discord { background: var(--discord); color: #fff; }
.btn-discord:hover { background: var(--discord-hover); }
.btn-github { background: var(--github); color: #fff; border-color: var(--github-edge); }
.btn-github:hover { background: var(--github-hover); }

.buttons { display: flex; flex-wrap: wrap; gap: .7rem; margin: 1.4rem 0 1.6rem; }

/* code */

code, pre { font-family: var(--mono); font-size: .86em; }
code { background: var(--bg-3); color: var(--ink); padding: .08em .32em; border-radius: 4px; overflow-wrap: anywhere; }

pre {
  background: var(--bg-3);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: .9rem 1rem;
  margin: 0 0 1.2rem;
  overflow-x: auto;
  line-height: 1.5;
}
pre code { background: none; padding: 0; font-size: inherit; color: var(--text); overflow-wrap: normal; }
pre .c { color: var(--muted); }

/* tables and facts */

.scroll { overflow-x: auto; margin: 0 0 1.2rem; }
table { border-collapse: collapse; width: 100%; font-size: .94rem; }
th, td { text-align: left; vertical-align: top; padding: .55rem .9rem .55rem 0; border-bottom: 1px solid var(--rule); }
th { color: var(--ink); font-weight: 600; border-bottom: 2px solid var(--rule); }
th:last-child, td:last-child { padding-right: 0; }
table.mono td { font-family: var(--mono); font-size: .86rem; }
/* the hub row a menu link landed on (work in development has a row, not a page) */
tr:target td { background: var(--bg-2); }
tr:target td:first-child { padding-left: .7rem; box-shadow: inset 3px 0 var(--link); }
.nowrap { white-space: nowrap; }

dl.facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: .3rem 1.2rem;
  margin: 0 0 1.2rem;
}
dl.facts dt { color: var(--muted); }
dl.facts dd { margin: 0; }

/* About page: our building at night as isometric pixel art (tools/hq.py), and
   nothing else (user, 2026-10-10). Clicking anyone shows their profile card:
   everyone at work on three glass-fronted floors, a lit sign on the roof,
   traffic going by on the road out front, a park across it, and two cats who
   don't want to be found. It is the one playful corner of the site, at the
   user's request. The layers are PNGs stacked at their native size in a box
   that is zoomed up with crisp pixels; where each one sits, and every walk and
   drive, is in the generated assets/hq.css. All the motion stops for anyone
   who asks for less. Who is who is in a hidden list for screen readers. Each
   person is the label of a radio, so the cards need no script. */
.story { min-width: 0; }
.story > h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.lead { font-size: 1.1rem; color: var(--ink); }
.roster > h2 { margin-top: 0; }
.roster > p { color: var(--muted); margin-bottom: 1.4rem; }
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.hq { display: flex; justify-content: center; }
/* one scene per building (tools/buildings/); the built default shows until the admin page picks another */
.hq.hq-bld { display: none; }
.hq.hq-bld.hq-bld-default { display: flex; }

/* the scene: 656 x 808 (drawn at half that and doubled with Scale2x), zoomed
   to about 525 wide, by whole device pixels where it can be */
.hq-scene {
  --z: .8;
  position: relative;
  zoom: var(--z);
  width: 656px;
  height: 808px;
  overflow: hidden;
  background-color: #070a16;
  background-size: 656px 808px;
  image-rendering: pixelated;
  border-radius: 8px;
  box-shadow: 0 0 0 2px var(--rule), 0 12px 36px var(--shade-drop);
}
/* the whole page on a big screen: two device pixels to a picture pixel */
@media (min-width: 1000px) and (min-height: 880px) { .hq-scene { --z: 1; } }
@media (min-resolution: 1.45dppx) and (max-resolution: 1.55dppx) { .hq-scene { --z: .6667; } }
@media (min-resolution: 1.95dppx) { .hq-scene { --z: .75; } }
.hq-scene > * { position: absolute; }
.hq-scene img, .hq-scene span { image-rendering: pixelated; }
.hq-layer, .hq-hook { left: 0; top: 0; width: 656px; height: 808px; max-width: none; pointer-events: none;
  background-repeat: no-repeat; background-size: 656px 808px; }
/* a season's overlays, sky sprites, people in the street and weather: hidden until
   assets/live/season.css fills them */
.hq-hook { display: none; }
.hq-person { width: 42px; height: 68px; background-repeat: no-repeat; }
.hq-car { background-repeat: no-repeat; pointer-events: none; }
.hq-cloud { left: 0; background-repeat: no-repeat; pointer-events: none; }
.hq-cloud-1 { top: 40px; width: 180px; height: 44px; transform: translateX(300px); }
.hq-cloud-2 { top: 116px; width: 140px; height: 36px; transform: translateX(20px); }

/* a name over a head, while the person is hovered */
.hq-tag, .hq-bubble {
  z-index: 3;
  margin: 4px 0 0 21px;
  transform: translate(-50%, -100%);
  font: 700 16px/1 var(--sans);
  white-space: nowrap;
  pointer-events: none;
}
.hq-tag {
  padding: 4px 6px 5px;
  color: #fff;
  background: rgba(8, 10, 14, .86);
  border-radius: 4px;
  opacity: 0;
  transition: opacity .15s;
}
/* speech bubbles, white with a dark edge as the hotel games drew them */
.hq-bubble {
  margin-top: -6px;
  padding: 4px 6px;
  color: #15181b;
  background: #fff;
  border: 2px solid #15181b;
  border-radius: 6px;
  opacity: 0;
}
.hq-bubble::after {
  content: "";
  position: absolute;
  left: calc(50% - 4px);
  top: 100%;
  border: 4px solid transparent;
  border-top-color: #15181b;
}
.hq-bubble-wide { font-size: 14px; }

/* who someone is: a click shows their card in the corner of the sky and lights
   them up; clicking the card closes it. The radios sit fixed at the top of the
   window, so focusing one never scrolls the page. */
.hq-scene > .hq-pick { position: fixed; top: 0; left: 0; width: 1px; height: 1px; margin: 0; opacity: 0; pointer-events: none; }
.hq-card {
  display: none; left: 14px; top: 14px; z-index: 6; width: 360px;
  --card-accent: #1ed67c; --card-on-accent: #06281a; --card-bg: rgba(8, 10, 14, .93);
  --card-ink: #e9ecef; --card-strong: #fff; --card-muted: #9fb3aa;
}
.hq-pick:checked:not(.hq-pick-none) ~ .hq-card { display: block; }
.hq-card-in {
  position: relative;
  display: flex;
  gap: 14px;
  padding: 12px 30px 12px 12px;
  color: var(--card-ink);
  background: var(--card-bg);
  border: 2px solid var(--card-accent);
  border-radius: 8px;
  box-shadow: 0 5px 0 rgba(0, 0, 0, .35);
  font: 15px/1.35 var(--sans);
  white-space: normal;
}
.hq-card-in::after { content: "\00d7"; position: absolute; right: 9px; top: 3px; color: var(--card-muted); font-size: 22px; line-height: 1; }
.hq-card-in:hover::after { color: var(--card-strong); }
.hq-card-pic {
  flex: none;
  width: 84px;
  height: 136px;
  background-color: #1b1e21;
  background-repeat: no-repeat;
  background-size: 1344px auto;
  border-radius: 4px;
}
.hq-card-text { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.hq-card-group { color: var(--card-accent); font-size: 12px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; }
.hq-card-name { color: var(--card-strong); font-size: 25px; font-weight: 700; line-height: 1.15; }
.hq-card-handle { color: var(--card-muted); font-size: 14px; }
.hq-card-badge { display: none; align-self: flex-start; margin-top: 5px; padding: 1px 9px 2px; color: var(--card-on-accent); background: var(--card-accent); border-radius: 999px; font-size: 12px; font-weight: 700; }
.hq-card-role { margin-top: 6px; }
.hq-card-bio { margin-top: 4px; font-size: 14px; font-style: italic; }
.hq-card-where { margin-top: auto; padding-top: 8px; color: var(--card-muted); font-size: 13px; }

/* the cats: a click or a tap stops them and they say so */
/* no z-index: the cats are indoors, behind the desks and the glass */
.hq-cat { width: 36px; height: 26px; margin: 0; }
.hq-cat summary { display: block; width: 36px; height: 26px; list-style: none; }
.hq-cat summary::-webkit-details-marker { display: none; }
.hq-cat summary:focus-visible { outline: 2px solid #1ed67c; }
.hq-cat-sprite { display: block; width: 36px; height: 26px; background-repeat: no-repeat; }
.hq-cat[open], .hq-cat[open] .hq-cat-sprite { animation-play-state: paused !important; }
.hq-cat .hq-meow { position: absolute; left: 50%; top: 0; margin: -4px 0 0; opacity: 1; animation: none; }

@media (prefers-reduced-motion: no-preference) {
  .hq-cloud-1 { animation: hq-drift 150s linear -60s infinite; }
  .hq-cloud-2 { animation: hq-drift 100s linear -20s infinite; }
  .hq-glow { animation: hq-flicker 9s linear infinite; }
  .hq-lamplight { animation: hq-lamp 7s step-end infinite; }
  .hq-card-pic { animation: hq-card-frames 2.24s steps(16) infinite; }
}
/* 16 frames a row: 42 pixels wide for a person, 36 for a cat */
@keyframes hq-frames { from { background-position-x: 0; } to { background-position-x: -672px; } }
@keyframes hq-cat-frames { from { background-position-x: 0; } to { background-position-x: -576px; } }
@keyframes hq-card-frames { from { background-position-x: 0; } to { background-position-x: -1344px; } }
@keyframes hq-drift { from { transform: translateX(-200px); } to { transform: translateX(680px); } }
@keyframes hq-flicker {
  0%, 41%, 43%, 70%, 71.5%, 100% { opacity: 1; }
  42% { opacity: .35; }
  70.7% { opacity: .55; }
}
/* a bubble shows for a few seconds now and then: the last eighth of its loop */
@keyframes hq-say { 0%, 84% { opacity: 0; } 86%, 96% { opacity: 1; } 98%, 100% { opacity: 0; } }
/* the street lamp across the road has a tired bulb */
@keyframes hq-lamp {
  0%, 34%, 74%, 78%, 100% { opacity: 1; }
  30% { opacity: .15; }
  31% { opacity: .9; }
  32% { opacity: .1; }
  33% { opacity: .7; }
  72% { opacity: .35; }
  76% { opacity: .2; }
}

/* phones: one device-independent pixel to a pixel, which is 2 or 3 real ones */
@media (max-width: 560px) {
  .hq-scene { --z: .5; }
}

/* Seasons: a theme, a time of day and a weather picked on the NiTools admin page
   (tools/seasons.py lists them; tools/hqseason.py draws them). The backend writes
   assets/live/season.css, which imports the stylesheets for the three choices; TV
   Standard is an empty file. Every page carries the hooks below, empty and hidden
   until one of those fills them: particles behind the page, a string of lights
   along the nav strip, a hat on the logo. Nothing moves for anyone who asks for less. */
.season-fx { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
.season-fx > span {
  display: none;
  position: absolute;
  inset: 0;
  background-repeat: repeat;
  image-rendering: pixelated;
  opacity: .55;
  filter: var(--fx-edge);
}
.season-strip {
  display: none;
  position: absolute;
  left: 0;
  right: 0;
  bottom: -7px;
  height: 12px;
  background-repeat: repeat-x;
  background-size: 96px 24px;
  image-rendering: pixelated;
  pointer-events: none;
}
.season-logo {
  display: none;
  position: absolute;
  left: 15px;
  top: -13px;
  width: 28px;
  height: 28px;
  background-repeat: no-repeat;
  background-size: 28px 28px;
  image-rendering: pixelated;
  pointer-events: none;
}
/* shared by the seasons' stylesheets */
@keyframes season-frames16 { from { background-position-x: 0; } to { background-position-x: -1280px; } }
@keyframes season-frames2 { from { background-position-x: 0; } to { background-position-x: -36px; } }
@keyframes season-twinkle { 0%, 49.9% { opacity: 1; } 50%, 100% { opacity: .2; } }
@keyframes season-blink { 0%, 100% { opacity: .15; } 50% { opacity: 1; } }
@keyframes season-strip { 0%, 49.9% { background-position-y: 0; } 50%, 100% { background-position-y: -12px; } }
@keyframes season-rise {
  0% { opacity: 0; transform: translateY(0); }
  12%, 78% { opacity: 1; }
  100% { opacity: 0; transform: translateY(-120px); }
}
@keyframes season-ascend {
  0% { opacity: 0; transform: translate(0, 0); }
  8%, 88% { opacity: 1; }
  100% { opacity: 0; transform: translate(30px, -340px); }
}
@keyframes season-fog { from { background-position-x: 0; } to { background-position-x: 512px; } }
@keyframes season-deck { from { background-position-x: 0; } to { background-position-x: 656px; } }
@keyframes season-flash {
  0%, 90%, 91.4%, 92.6%, 100% { opacity: 0; }
  90.6% { opacity: .55; }
  92% { opacity: .3; }
}

/* footer */

.site-foot {
  max-width: 64rem;
  margin: 3rem auto 0;
  padding: 1.2rem 1.25rem 2.5rem;
  border-top: 1px solid var(--rule);
  color: var(--muted);
  font-size: .9rem;
}
.site-foot p { margin: .3rem 0; }
.site-foot a { color: var(--text); }

/* below the bar's own 64rem the nav takes a row of its own, rather than
   wrapping raggedly against the GitHub and Discord pills. */
@media (max-width: 1023px) {
  .bar { flex-wrap: wrap; justify-content: space-between; }
  .bar nav { order: 3; flex-basis: 100%; flex-wrap: wrap; }
}

@media (max-width: 640px) {
  .hero { padding: 2rem 1.25rem 0; }
  .hero h1 { font-size: 1.8rem; }
  .hero h1.long { font-size: 1.55rem; }
  .cover img { max-height: 8.5rem; }
  dl.facts { grid-template-columns: 1fr; gap: 0; }
  dl.facts dd { margin-bottom: .6rem; }
}
