/* ===========================================================================
   BLESSED LOGIC SYSTEMS
   Dark by default, [data-theme="light"] flips it. Structured but not fussy:
   real containers and colour, no gradients, glows or entrance animations.
   =========================================================================== */

:root {
  --bg: #05090f;
  --bg-2: #080e17;
  --panel: #0b1220;
  --panel-2: #101a2a;
  --line: #17222f;
  --line-2: #26333f;

  --fg: #f8f9fa;
  --fg-2: #b9c6d4;
  --fg-3: #7e8fa3;

  --accent: #d4af37;        /* celestial gold */
  --accent-bg: #d4af37;     /* gold fills, identical in both themes */
  --accent-fg: #05090f;     /* text on a gold fill */
  --cyan: #40e0d0;
  --indigo: #071527;        /* ai indigo, the tinted bands */
  --indigo-2: #0c1f38;      /* one step up, borders inside a band */
  --jade: #3aa06d;          /* the only green, status only, kept clear of cyan */
  --danger: #e5675e;
  --ok: var(--jade);


  --sans: 'Plus Jakarta Sans Variable', 'Segoe UI', system-ui, -apple-system, sans-serif;
  --mono: 'JetBrains Mono Variable', ui-monospace, Consolas, monospace;

  --wrap: 1080px;
  --pad: clamp(1rem, 4vw, 2rem);
  --gap: clamp(3.25rem, 7vw, 5.5rem);
  --r: 3px;
  /* about 75 characters in this face, at whatever size the text is set in */
  --measure: 35em;

  /* Five steps, and everything below the headings picks one of them. The site
     used to carry twenty seven separate sizes between 0.7 and 1.25rem. */
  --t-xs: 0.76rem;      /* counts, captions, hints, uppercase labels */
  --t-sm: 0.85rem;      /* secondary interface text */
  --t-ui: 0.92rem;      /* buttons, navigation, fields, dense lists */
  --t-body: 1rem;       /* running text people actually read */
  --t-title: 1.08rem;   /* panel headings */
  --t-card: 1.2rem;     /* the name on a system's card, one size everywhere */
  --t-lead: 1.22rem;    /* the largest step below a section heading */
  --t-figure: clamp(1.7rem, 2.6vw, 2.2rem);  /* a counted number, here or in the panel */

  /* aliases kept for admin.css, which predates the rename */
  --surface: var(--panel);
  --surface-2: var(--panel-2);
  --line-soft: var(--line);
  --line-strong: var(--line-2);
  --text: var(--fg);
  --text-2: var(--fg-2);
  --muted: var(--fg-3);
  --dim: var(--fg-3);
  --accent-soft: #efce6b;
  --accent-deep: #a8842a;
  --glow: var(--cyan);
  --good: var(--ok);
  --bad: var(--danger);
  --gold: var(--accent);
  --on-accent: var(--accent-fg);
  --radius-sm: var(--r);
  --radius: var(--r);
  --radius-lg: var(--r);
  --font-display: var(--sans);
  --font-body: var(--sans);
  --font-mono: var(--mono);
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --shadow: none;
  --shadow-lg: none;
}

[data-theme='light'] {
  --bg: #f8f9fa;
  --bg-2: #eef1f5;
  --panel: #ffffff;
  --panel-2: #f2f5f8;
  --line: #dfe4ea;
  --line-2: #c2ccd8;

  --fg: #0e1b2e;
  --fg-2: #35455a;
  --fg-3: #56667a;

  --accent: #8a6a12;
  --cyan: #0d7490;
  --indigo: #eef2f8;
  --indigo-2: #dfe7f2;
  --jade: #14663f;
  --danger: #b3261e;
  --accent-soft: #6f5410;
  --accent-deep: #574109;

}

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

html {
  background: var(--bg);
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: 5rem;
  /* the parts of the browser we did not draw still belong to the design */
  caret-color: var(--accent);
  scrollbar-color: var(--line-2) var(--bg);
  scrollbar-width: thin;
}

body {
  background: transparent;
  color: var(--fg);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* The ambient pointer field sits between the page background and the content.
   The background lives on <html> and the body is transparent, so the canvas
   can use z-index -1 and still be seen. Do NOT force position on body's
   children to raise them instead: that unsticks the header and gives the
   hidden skip link real height above the navigation. */
.ambient { position: fixed; inset: 0; z-index: -1; pointer-events: none; }

img, svg { display: block; max-width: 100%; }
img { height: auto; }
input, button, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
ul, ol { list-style: none; padding: 0; }
a { color: inherit; text-decoration: none; }
table { border-collapse: collapse; width: 100%; }
::selection { background: var(--accent-bg); color: var(--accent-fg); }
:focus-visible { outline: 2px solid var(--accent-bg); outline-offset: 2px; }

.skip-link { position: absolute; left: -9999px; }
.skip-link:focus { left: 0; top: 0; background: var(--accent-bg); color: var(--accent-fg); padding: 0.6rem 1rem; z-index: 99; }

/* -- type ------------------------------------------------------------------ */
h1, h2, h3 { font-weight: 800; letter-spacing: -0.025em; text-wrap: balance; }
h1 { font-size: clamp(2.35rem, 5.4vw, 3.5rem); line-height: 1.04; }
h2 { font-size: clamp(1.5rem, 2.4vw, 1.9rem); line-height: 1.1; }
h3 { font-size: 1.05rem; line-height: 1.3; letter-spacing: -0.02em; }
p { text-wrap: pretty; }

.lede { font-size: clamp(1.05rem, 1.5vw, 1.18rem); line-height: 1.55; color: var(--fg-2); }
.mono { font-family: var(--mono); }

/* Running text never runs wider than the eye can track back. Anything that is
   a label, a row or a column of its own is excluded on purpose. */
.box p, .box li, .ticks li, .rec__text, .foot__bar,
.cnote p, .status p, .empty p,
.wrap--narrow > p { max-width: var(--measure); }

/* -- layout ---------------------------------------------------------------- */
.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--pad); }
.wrap--narrow { max-width: 760px; }

main { flex: 1; }
.block { padding-block: var(--gap); }
.block + .block { padding-top: 0; }
.block--alt { background: var(--indigo); border-block: 1px solid var(--line); margin-block: var(--gap); }
.block--alt + .block { padding-top: 0; }
/* beats `.block + .block`, which would otherwise flatten the top of the band */
.block.block--alt { padding-block: var(--gap); }
/* the band's own padding is the separation; the preceding block already paid it */
.block + .block--alt { margin-top: 0; }
/* the testimonial belt sits close under the introduction it belongs to */
.intro + .says { margin-top: clamp(1.25rem, 2.5vw, 2rem); }
/* a lead-in section hands over to its content, it does not open a hole */
.intro + .block { padding-top: clamp(2rem, 4vw, 3rem); }

.rule {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}
.rule h2 { position: relative; padding-bottom: 0.62rem; }
.rule h2::after { content: ''; position: absolute; left: 0; bottom: 0; width: 52px; height: 2px; background: var(--accent-bg); }
/* the node that closes the rule, a small kamon diamond */
.rule h2::before { content: ''; position: absolute; left: 58px; bottom: -1px; width: 4px; height: 4px; background: var(--accent-bg); transform: rotate(45deg); }
.rule > a { font-size: var(--t-ui); color: var(--accent); }
.rule > a:hover { text-decoration: underline; }

/* -- header ---------------------------------------------------------------- */
.top { border-bottom: 1px solid var(--line); background: var(--bg); position: sticky; top: 0; z-index: 20; }
.top__in { display: flex; align-items: center; gap: 1.25rem; min-height: 62px; }

.brand { display: flex; align-items: center; gap: 0.6rem; font-weight: 800; letter-spacing: -0.02em; font-size: 1.28rem; white-space: nowrap; }
.brand em { font-style: normal; color: var(--accent); }
.brand svg { width: 30px; height: 30px; flex-shrink: 0; }

.top nav { margin-left: auto; display: flex; align-items: center; gap: 1.4rem; }
.top nav a { font-size: var(--t-ui); color: var(--fg-2); padding-block: 0.35rem; }
.top nav a:hover { color: var(--fg); }
.top nav a[aria-current] { color: var(--fg); font-weight: 600; box-shadow: inset 0 -2px 0 var(--accent-bg); }

.theme-btn {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  color: var(--fg-2);
  font-size: var(--t-sm);
}
.theme-btn:hover { color: var(--accent); border-color: var(--accent-bg); }
.theme-btn .fa-sun { display: none; }
[data-theme='light'] .theme-btn .fa-sun { display: block; }
[data-theme='light'] .theme-btn .fa-moon { display: none; }

.menu-btn { display: none; border: 1px solid var(--line-2); border-radius: var(--r); width: 34px; height: 34px; }

/* the toggle belongs with the navigation, not stranded beside the wordmark */
@media (min-width: 781px) {
  .top nav { order: 2; }
  .theme-btn { order: 3; }
}

/* -- intro ----------------------------------------------------------------- */
.intro { padding-block: clamp(2.25rem, 6vw, 3.5rem) 0; }
.intro__grid { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr); gap: clamp(1.5rem, 4vw, 3rem); align-items: center; }
.intro h1 { margin-bottom: 0.75rem; }
.intro p { color: var(--fg-2); }
/* Closes the page header the way .rule closes a section heading: a hairline
   across, with the accent stub sitting on it. Skipped on the About page, whose
   intro carries the portrait, and on the home page, whose intro already ends
   in the row of figures. */
.intro:not(:has(.meshot)):not(:has(.figures)) { position: relative; padding-bottom: 1.25rem; }
.intro:not(:has(.meshot)):not(:has(.figures))::before,
.intro:not(:has(.meshot)):not(:has(.figures))::after { content: ''; position: absolute; bottom: 0; }
.intro:not(:has(.meshot)):not(:has(.figures))::before { left: 0; right: 0; height: 1px; background: var(--line); }
.intro:not(:has(.meshot)):not(:has(.figures))::after { left: 0; width: 52px; height: 2px; background: var(--accent-bg); }
/* only the home hero keeps a narrow standfirst, it sits in a grid column */
.page-home .intro__grid p { max-width: 60ch; }
.intro__do { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-top: 1.5rem; }

/* stat strip, counts up on scroll */
.figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  border: 1px solid var(--line);
  border-top: 2px solid var(--accent-bg);
  background: var(--indigo);
  margin-top: 2.6rem;
}
.figures > * { padding: 1.05rem 1.2rem; border-right: 1px solid var(--line); }
.figure { position: relative; transition: background 0.2s; }
.figure:hover { background: color-mix(in srgb, var(--accent) 5%, transparent); }
/* the glyph rides beside the number rather than sitting above it, in the same
   gold, sized off the figure so the two scale together */
.figure__num { display: flex; align-items: center; gap: 0.55rem; }
.figure__mark {
  font-size: 0.5em;
  line-height: 1;
  color: var(--accent);
  flex-shrink: 0;
  transition: transform 0.2s var(--ease), opacity 0.2s;
  opacity: 0.85;
}
.figure:hover .figure__mark { opacity: 1; transform: scale(1.12) rotate(-6deg); }
.figures > *:last-child { border-right: 0; }
.figures .figure__num { font-size: var(--t-figure); }
.figures b {
  display: block;
  font-size: inherit;
  font-weight: 800;
  letter-spacing: -0.04em;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.figures span { display: block; font-size: var(--t-sm); line-height: 1.35; color: var(--fg-3); text-wrap: balance; }

/* inline variant, used on system and legal pages */
.facts { display: flex; flex-wrap: wrap; gap: 0.3rem 1.5rem; margin-top: 1.25rem; font-size: var(--t-sm); color: var(--fg-3); }
.facts b { color: var(--fg); font-weight: 700; margin-right: 0.3rem; font-variant-numeric: tabular-nums; }

/* -- buttons --------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.6rem 1.1rem;
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  font-size: var(--t-ui);
  font-weight: 600;
  color: var(--fg);
  background: var(--panel);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.btn:hover { border-color: var(--accent-bg); color: var(--accent); }
.btn:active { transform: translateY(1px); }
.btn[disabled], .btn[aria-disabled='true'] { opacity: 0.5; cursor: not-allowed; }
.btn--gold { background: var(--accent-bg); border-color: var(--accent-bg); color: var(--accent-fg); font-weight: 700; }
.btn--gold:hover { filter: brightness(1.08); color: var(--accent-fg); }
.btn--danger { color: var(--danger); border-color: var(--danger); background: none; }
.btn--danger:hover { background: var(--danger); color: #fff; }
.btn--sm { padding: 0.32rem 0.65rem; font-size: var(--t-sm); }
.btn--block { display: flex; width: 100%; justify-content: center; }

/* a button used as a link must not carry the browser's button padding, which
   is what shifted the Discord handle a character to the right */
.lnk { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; padding: 0; border: 0; background: none; font: inherit; }
.lnk:hover { color: var(--cyan); }

/* -- tags ------------------------------------------------------------------ */
.tags { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.tag {
  display: inline-flex;
  align-items: center;
  line-height: 1;
  font-size: var(--t-xs);
  font-weight: 600;
  padding: 0.3rem 0.5rem;
  border-radius: 2px;
  border: 1px solid color-mix(in srgb, var(--tag, var(--fg-3)) 40%, transparent);
  background: color-mix(in srgb, var(--tag, var(--fg-3)) 12%, transparent);
  color: color-mix(in srgb, var(--tag, var(--fg-3)) 75%, var(--fg));
  white-space: nowrap;
}
a.tag:hover { border-color: var(--tag, var(--accent-bg)); }

/* filters on the left, search held to the right edge, both on one baseline */
.filters {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 1.5rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}
.filters .pillbar { margin-bottom: 0; flex: 1 1 auto; }
.filters__search { position: relative; flex: 0 1 300px; }
.filters__search i { position: absolute; left: 0.7rem; top: 50%; transform: translateY(-50%); color: var(--fg-3); font-size: var(--t-xs); pointer-events: none; }
.filters__search input[type='search'] { padding-left: 2.15rem; }

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.pillbar { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 1.5rem; }
.pillbar a {
  font-size: var(--t-sm);
  padding: 0.35rem 0.8rem;
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  color: var(--fg-2);
  transition: border-color 0.15s, color 0.15s;
}
.pillbar a:hover { color: var(--fg); border-color: var(--accent-bg); }
.pillbar a.on { background: var(--accent-bg); border-color: var(--accent-bg); color: var(--accent-fg); font-weight: 600; }
.pillbar small { color: var(--fg-3); font-weight: 400; }
.pillbar a.on small { color: var(--accent-fg); opacity: 0.7; }

/* -- system cards, laid out in rows ---------------------------------------- *
 * How many go on a row is the row's own --cols, written on it by the template
 * from the count the admin panel asked for. One line sizes every layout, and
 * one line stacks them all on a narrow screen.
 *
 * minmax(0, 1fr) on every track, not a bare 1fr. A bare one floors at the
 * content's own minimum width, and a card four to a row — or one that has
 * stepped back to a sixth under the opening-out layout — is narrower than its
 * own title wants to be. Without it a row refuses to shrink and pushes its last
 * card off the side of the page instead.                                     */
.sysrows { display: grid; gap: 1.1rem; }
.sysrow {
  display: grid;
  gap: 1.1rem;
  grid-template-columns: repeat(var(--cols, 1), minmax(0, 1fr));
  /* a card that has stepped back keeps its own height rather than being
     stretched to the tall one's, which would leave a column of nothing */
  align-items: start;
}
/* only one layout changes its columns, so only that one waits about it */
.sysrows--zoom .sysrow { transition: grid-template-columns 0.45s var(--ease); }

/* How many columns a row is divided into, taken from the layout rather than
   from how many cards happen to be in the row. That matters for the last row:
   four systems three to a row leaves one on its own, and a row that counted its
   own children would draw that one at the full width of the page. It is the
   same system as the three above it and it is drawn the same size.

   Said as a class rather than written onto the element, because an inline style
   outranks the stylesheet and a row carrying its own count could never be told
   to stack. `.sysrow--ofN` is still on the row, but only as the marker the
   opening-out rules hang off.

   The rules that narrow these follow them and match on two classes as well, so
   the later one simply wins. */
.sysrows--cols2 .sysrow { --cols: 2; }
.sysrows--cols3 .sysrow { --cols: 3; }
.sysrows--cols4 .sysrow { --cols: 4; }

/* Four to a row is a laptop's worth of room and no more, so it halves rather
   than going straight to one; below 900px everything is one to a row. */
@media (max-width: 1150px) { .sysrows--cols4 .sysrow { --cols: 2; } }
@media (max-width: 900px) { .sysrows .sysrow { --cols: 1; } }

/* -- opening out ----------------------------------------------------------- *
 * The "zoom" layout and only that one, which is why every rule here is scoped
 * to it: the other two share the row markup without inheriting any of this.
 *
 * At rest the cards on a row are the same width. Point at one and the others
 * each drop to half the width they had; the one being looked at takes
 * everything that frees up. In fr, for three: even columns are two units each,
 * so six across the row. A card stepping back goes to one — half of two — and
 * the two units the pair gave up land on the card in front, taking it from two
 * to four. Two to a row is the same rule and comes out 3fr / 1fr.
 *
 * THE TRACK LISTS ARE WRITTEN OUT IN FULL HERE, AT BOTH ENDS. That is the whole
 * reason these rest states exist when `repeat()` would say the same thing:
 * grid-template-columns only animates between two explicit track lists.
 * `repeat(3, minmax(0, 1fr))` is not interpolable with
 * `minmax(0, 4fr) minmax(0, 1fr) minmax(0, 1fr)` — no transition is created at
 * all and the row snaps instead of opening. Measured, not assumed.
 *
 * The other layouts never animate their columns, so they are welcome to the
 * repeat() above and this only applies here.                                 */
.sysrows--zoom .sysrow--of2 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.sysrows--zoom .sysrow--of3 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); }

@media (min-width: 901px) and (hover: hover) {
  .sysrows--zoom .sysrow--of2:has(> .card:nth-child(1):hover) { grid-template-columns: minmax(0, 3fr) minmax(0, 1fr); }
  .sysrows--zoom .sysrow--of2:has(> .card:nth-child(2):hover) { grid-template-columns: minmax(0, 1fr) minmax(0, 3fr); }
  .sysrows--zoom .sysrow--of3:has(> .card:nth-child(1):hover) { grid-template-columns: minmax(0, 4fr) minmax(0, 1fr) minmax(0, 1fr); }
  .sysrows--zoom .sysrow--of3:has(> .card:nth-child(2):hover) { grid-template-columns: minmax(0, 1fr) minmax(0, 4fr) minmax(0, 1fr); }
  .sysrows--zoom .sysrow--of3:has(> .card:nth-child(3):hover) { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 4fr); }

  /* A gap between two cards belongs to neither of them, so crossing from one to
     the next un-hovers the row for an instant and it starts closing before it
     reopens. Waiting a moment before closing rides straight over that; opening
     never waits. */
  .sysrows--zoom .sysrow { transition-delay: 0.12s; }
  .sysrows--zoom .sysrow:has(> .card:hover) { transition-delay: 0s; }
}

/* nothing to stack out of below 900px, and nothing to animate either */
@media (max-width: 900px) {
  .sysrows--zoom .sysrow--of2,
  .sysrows--zoom .sysrow--of3 { grid-template-columns: minmax(0, 1fr); }
}

/* -- video details --------------------------------------------------------- *
 * Two to a row, and only the system's name and its price show until the card is
 * pointed at: then the summary, the tags and the counts unfold. Where those two
 * stand is a choice in the admin panel — over the foot of the picture, or in a
 * strip underneath it.
 *
 * The folding is the same either way and is written once, at the bottom. What
 * differs is the placement, and that is the two blocks above it.             */

/* -- over the picture ------------------------------------------------------ *
 * No markup of its own. The card already carries all of it in the right order
 * for a card; `order` puts that order on its head — name and price last so they
 * finish at the bottom of the picture, the foot first so it ends up on top.
 *
 * The player's own bar goes to the top of the picture here. It lives at the
 * bottom everywhere else, and here the bottom is spoken for.                 */
.sysrows--over .card {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;   /* everything settles at the foot of the video */
  aspect-ratio: 1920 / 1000;
}
.sysrows--over .card__shot {
  position: absolute;
  inset: 0;
  aspect-ratio: auto;
  border: 0;
}
/* the wash the words are read against, over the video and under the text */
.sysrows--over .card__shot::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  /* impeccable-disable-next-line design-system-color -- a scrim over a video, not a palette colour */
  background: linear-gradient(180deg, transparent 42%, rgba(0, 0, 0, 0.55) 68%, rgba(0, 0, 0, 0.88));
}
.sysrows--over .player__bar {
  top: 0;
  bottom: auto;
  /* impeccable-disable-next-line design-system-color -- the bar's own wash, flipped to match */
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.78), transparent);
}

.sysrows--over .card__body,
.sysrows--over .card__foot {
  position: relative;
  z-index: 2;
  background: none;
  border: 0;
}
/* `flex: 0 0 auto` matters: a card's body normally grows to fill the space under
   the picture, and a body that fills everything leaves nothing for
   justify-content to push to the bottom. Here it is only as tall as it reads. */
.sysrows--over .card__body {
  display: flex;
  flex: 0 0 auto;
  flex-direction: column;
  padding: 0 1.1rem 0.9rem;
  gap: 0;
}
.sysrows--over .card__foot { order: -1; flex: 0 0 auto; padding: 0 1.1rem 0.6rem; }
.sysrows--over .card__d { order: 0; }
.sysrows--over .card__rule { order: 1; margin: 0.55rem 0; }
.sysrows--over .card__head { order: 2; }

/* Read against a video rather than against the page, so these do not follow the
   theme: white on a dark wash is right in both of them. */
/* impeccable-disable-next-line design-system-color -- text over a video */
.sysrows--over .card__t a { color: #f8f9fa; }
/* impeccable-disable-next-line design-system-color -- text over a video */
.sysrows--over .card__d { color: rgba(248, 249, 250, 0.82); }
.sysrows--over .card__p { color: var(--accent-bg); }
.sysrows--over .views { color: rgba(248, 249, 250, 0.72); }
.sysrows--over .views i { color: inherit; }

/* -- underneath the picture ------------------------------------------------ *
 * The name and the price sit in a strip below the video instead, and the rest
 * falls down from under them. Nothing is written over the picture, so the
 * player's own bar stays where it is everywhere else — at the foot of the video,
 * which is exactly where the name and the price stand in the other placement.
 *
 * An ordinary card that keeps most of itself folded away, so almost all of what
 * it needs is what a card already does.                                      */
.sysrows--under .card__body { gap: 0; }
.sysrows--under .card__foot { border-top-color: transparent; }
.sysrows--under .card:hover .card__foot,
.sysrows--under .card:focus-within .card__foot { border-top-color: var(--line); }

/* -- the folding, shared by both placements -------------------------------- *
 * Everything but the name and the price is folded away until the card is
 * pointed at. Clipped rather than hidden, so it all stays in the reading order
 * whether or not anyone can see it.                                          */
.sysrows--overlay .card__d,
.sysrows--overlay .card__rule,
.sysrows--overlay .card__foot {
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-height 0.32s var(--ease), opacity 0.24s var(--ease), margin 0.32s var(--ease), padding 0.32s var(--ease);
}
.sysrows--overlay .card__rule { margin: 0; }
.sysrows--overlay .card__foot { padding-block: 0; }
.sysrows--overlay .card:hover .card__d,
.sysrows--overlay .card:focus-within .card__d { max-height: 5rem; opacity: 1; }
.sysrows--overlay .card:hover .card__rule,
.sysrows--overlay .card:focus-within .card__rule { max-height: 1px; opacity: 1; margin: 0.55rem 0; }
.sysrows--overlay .card:hover .card__foot,
.sysrows--overlay .card:focus-within .card__foot { max-height: 4rem; opacity: 1; padding-bottom: 0.6rem; }

/* a card this size has room for the words on the button once it is open */
.sysrows--overlay .card:hover .peek,
.sysrows--overlay .card:focus-within .peek { gap: 0.4rem; }
.sysrows--overlay .card:hover .peek__label,
.sysrows--overlay .card:focus-within .peek__label { max-width: 9rem; }

@media (prefers-reduced-motion: reduce) {
  .sysrows--overlay .card__d,
  .sysrows--overlay .card__rule,
  .sysrows--overlay .card__foot { transition: none; }
}

/* A phone has no pointer to rest on a card, so folding the summary and the tags
   away behind one would put them somewhere nobody can reach. Stacked, both
   placements go back to being an ordinary card with everything showing. */
@media (max-width: 900px) {
  .sysrows--over .card { display: flex; aspect-ratio: auto; justify-content: flex-start; }
  .sysrows--over .card__shot { position: relative; inset: auto; aspect-ratio: 1920 / 1000; border-bottom: 1px solid var(--line); }
  .sysrows--over .card__shot::after { display: none; }
  .sysrows--over .player__bar { top: auto; bottom: 0; background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.78)); }
  .sysrows--over .card__body {
    order: 0;
    flex: 1;
    padding: 0.95rem 1.05rem;
    gap: 0.45rem;
  }
  .sysrows--over .card__foot { order: 0; padding: 0.7rem 1.05rem; background: var(--panel-2); border-top: 1px solid var(--line); }
  .sysrows--over .card__d { order: 0; }
  .sysrows--over .card__rule { order: 0; margin: 0.1rem 0 0.15rem; }
  .sysrows--over .card__head { order: 0; }
  .sysrows--over .card__t a { color: var(--fg); }
  .sysrows--over .card__d { color: var(--fg-3); }
  .sysrows--over .card__p { color: var(--accent); }
  .sysrows--over .views { color: var(--fg-3); }

  .sysrows--under .card__body { gap: 0.45rem; }
  .sysrows--under .card__foot { border-top-color: var(--line); }

  /* `revert` would go back to the browser's own value, not to the card's, so
     the padding these gave up is written back rather than reverted. */
  .sysrows--overlay .card__d,
  .sysrows--overlay .card__rule,
  .sysrows--overlay .card__foot { max-height: none; opacity: 1; overflow: visible; }
  .sysrows--overlay .card__foot { padding-block: 0.7rem; }
  .sysrows--overlay .card__rule { margin-block: 0.1rem 0.15rem; }
}

.card {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--panel);
  overflow: hidden;
  transition: border-color 0.15s, box-shadow 0.3s var(--ease);
}
.card:hover { border-color: var(--accent-bg); }

.card__shot {
  display: block;
  position: relative;
  aspect-ratio: 1920 / 1000;
  background: var(--panel-2);
  border-bottom: 1px solid var(--line);
  overflow: hidden;
}
.card__shot img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 0.45s var(--ease); }
.card:hover .card__shot img { transform: scale(1.035); }

/* -- the player inside a card ---------------------------------------------- *
 * The same player as the system page, fitted to the box the card gives it. It
 * keeps that box rather than taking the shape of the video (see the `fixed`
 * flag in the player partial), because two cards in a row whose videos were
 * recorded at different sizes would otherwise be different heights.
 *
 * The bar is the player's own, so it appears on hover and while paused, and
 * says nothing here about when. What is set here is how much of it fits: a card
 * beside a wide one is a third of the page, and the clock either side of the
 * seek bar and the quality menu are the first things to go. Measured against
 * the card, not the window — the same card is narrow in a row of two and wide
 * on a phone.                                                                */
.card__shot { container-type: inline-size; }

/* The player shows its bar while the pointer is on the player. On a card the
   pointer is often on the card but not on the player — reading the summary,
   reaching for a tag, or anywhere in the details written over the picture — and
   the bar would fade out from under it. The card is the thing being pointed at,
   so the card is what keeps the bar up. */
.card:hover .player__bar,
.card:focus-within .player__bar { opacity: 1; transform: none; }
.card__shot .player {
  margin: 0;
  height: 100%;
  border: 0;
  border-radius: 0;
  aspect-ratio: auto;
}
/* cover, not contain: the box is the shape of the videos this site carries, and
   a card that letterboxed would break the line the row is drawn on */
.card__shot .player__video { object-fit: cover; }
.card__shot .player__bar { padding: 0.45rem 0.5rem; gap: 0.4rem; }
.card__shot .player__big-ring { width: 52px; height: 52px; }

@container (max-width: 470px) {
  .card__shot .player__time { display: none; }
  .card__shot [data-player-quality] { display: none; }
}
@container (max-width: 330px) {
  .card__shot .player__vol { display: none; }
  .card__shot [data-player-speed] { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .sysrow { transition: none; }
}
.card__ref {
  height: 100%;
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: var(--t-sm);
  letter-spacing: 0.08em;
  color: var(--fg-3);
  background-image: repeating-linear-gradient(-45deg, var(--line) 0 1px, transparent 1px 9px);
}
.card__body { padding: 0.95rem 1.05rem; display: grid; gap: 0.45rem; flex: 1; align-content: start; }
.card__rule { height: 1px; border: 0; margin: 0.1rem 0 0.15rem; background: var(--line); }
/* the name and what it costs read as one line, the way a price tag does */
.card__head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; }
.card__t { font-weight: 700; letter-spacing: -0.03em; line-height: 1.25; min-width: 0; }
/* One size wherever a card is printed. The catalogue writes the name as an h2
   so the page outline reads properly and the other pages write an h3, and a
   skin's heading rule outranks a bare class — which made the same card bigger
   in the catalogue than on the home page. Two classes settle it. */
.card .card__t { font-size: var(--t-card); }
.card__t:hover { color: var(--accent); }
/* the whole summary is kept: a long one scrolls inside the card instead of
   being cut short, so the cards in a row stay the same height */
/* Three lines, and a long one ends in an ellipsis rather than a scrollbar.
   The cards in a row still match, and there is nothing to scroll inside a box
   that is itself inside a scrolling page. */
.card__d {
  font-size: var(--t-sm);
  color: var(--fg-3);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}
.card__foot {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 1.05rem;
  border-top: 1px solid var(--line);
  background: var(--panel-2);
}
/* tags read from the left, the count and the way in sit together on the right */
.card__foot .views { margin-left: auto; }
.card__p {
  flex-shrink: 0;
  font-weight: 800;
  font-size: 1.12rem;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  white-space: nowrap;
  /* The price carries itself on weight and colour; the glow is a reaction to
     being pointed at, not a permanent state. No transition on it: the browser
     cannot interpolate between two colour-mixes and blinked it out on the way. */
  font-size: 1.18rem;
}
.card:hover .card__p { text-shadow: 0 0 14px color-mix(in srgb, currentColor 55%, transparent); }
/* On white the accent is a dark gold, and a glow of it reads as a smudge. */
[data-theme='light'] .card__p,
[data-theme='light'] .card:hover .card__p { text-shadow: none; }
.card__p small { font-weight: 400; font-size: var(--t-xs); color: var(--fg-3); margin-left: 0.25rem; }
.card__p--free { color: var(--ok); }
/* One line of tags, whatever else is in the foot. They used to wrap, which
   meant the foot grew a second line the moment the button beside them opened
   out and the card jumped as you pointed at it. The button slides over the last
   tag instead, which is what it looks like it is doing. */
/* Tags stay put. They are clipped by the foot rather than taken out of it, so
   the button beside them slides over the last one as it opens rather than
   pushing them onto a second line. */
.card__foot .tags { display: flex; flex-wrap: nowrap; gap: 0.3rem; min-width: 0; overflow: hidden; }

/* -- the way into a system ------------------------------------------------- *
 * An arrow while the card is at rest; the arrow with its name beside it once
 * the card is pointed at. The words are clipped rather than hidden, so the link
 * is named "View all details" whether or not anyone can see that it says so.  */
.peek {
  display: inline-flex;
  align-items: center;
  gap: 0;
  flex-shrink: 0;
  padding: 0.34rem 0.5rem;
  border: 1px solid var(--accent-bg);
  border-radius: var(--r);
  color: var(--accent);
  font-size: var(--t-xs);
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  transition: gap 0.3s var(--ease), background-color 0.2s, border-color 0.2s, color 0.2s;
}
/* a flex item is as wide as its content until it is told it may be narrower */
.peek__label { min-width: 0; max-width: 0; overflow: hidden; transition: max-width 0.3s var(--ease); }
/* The name appears on the card that has opened out, which is the one there is
   room on. That is the hovered card, so the words arrive as it widens rather
   than on top of a card too narrow to hold them. The button's own focus ring
   opens it too, for anyone arriving by keyboard rather than by pointer. */
.card:hover .peek,
.peek:focus-visible { gap: 0.4rem; background: var(--accent-bg); color: var(--accent-fg); }
.card:hover .peek__label,
.peek:focus-visible .peek__label { max-width: 9rem; }

/* Stacked, nothing opens out: every card is the full width of the page, so
   every card has room to say what its button does. */
@media (max-width: 900px) {
  .card .peek { gap: 0.4rem; background: var(--accent-bg); color: var(--accent-fg); }
  .card .peek__label { max-width: 9rem; }
}
.peek:hover { background: var(--accent-soft); border-color: var(--accent-soft); color: var(--accent-fg); }

@media (prefers-reduced-motion: reduce) {
  .peek, .peek__label { transition: none; }
}
.views { display: inline-flex; align-items: center; gap: 0.3rem; flex-shrink: 0; font-size: var(--t-xs); color: var(--fg-3); font-family: var(--mono); }
.views i { color: var(--fg-3); font-size: 0.9em; }

/* -- terminal -------------------------------------------------------------- */
.term { border: 1px solid var(--line); border-radius: var(--r); background: var(--panel); overflow: hidden; }
.term__bar { display: flex; align-items: center; gap: 0.6rem; padding: 0.5rem 0.8rem; border-bottom: 1px solid var(--line); background: var(--panel-2); }
.term__bar i { width: 9px; height: 9px; border-radius: 50%; background: var(--line-2); }
.term__bar i:first-child { background: var(--danger); }
.term__bar i:nth-child(2) { background: var(--accent-bg); }
.term__bar i:nth-child(3) { background: var(--ok); }
.term__bar span { font-family: var(--mono); font-size: var(--t-xs); color: var(--fg-3); }
.term pre { padding: 0.9rem 1rem; font-family: var(--mono); font-size: var(--t-xs); line-height: 1.8; color: var(--fg-2); overflow-x: auto; }
.term b { color: var(--ok); font-weight: 400; }
.term i { color: var(--fg-3); font-style: normal; }
.term u { color: var(--accent); text-decoration: none; }

/* Kado: the corner brackets of a lacquer panel, on two opposite corners. */
.box--gold, .box--kado { position: relative; }
.box--gold::before, .box--gold::after,
.box--kado::before, .box--kado::after { content: ''; position: absolute; width: 13px; height: 13px; pointer-events: none; }
.box--gold::before, .box--kado::before { top: -1px; right: -1px; border-top: 2px solid var(--accent-bg); border-right: 2px solid var(--accent-bg); }
.box--gold::after, .box--kado::after { bottom: -1px; left: -1px; border-bottom: 2px solid var(--accent-bg); border-left: 2px solid var(--accent-bg); }

/* -- brand mark ------------------------------------------------------------ */
.mark { flex-shrink: 0; }
.mark--file { display: inline-flex; align-items: center; }
.mark--file img { height: var(--logo-h, 26px); width: auto; max-width: 200px; }
.mark__light { display: none; }
[data-theme='light'] .mark__dark:not(:only-child) { display: none; }
[data-theme='light'] .mark__light { display: block; }
.foot__brand .mark--file img { height: calc(var(--logo-h, 26px) * 0.8); }

/* The built-in mark is a </> in the gold. The two chevrons breathe apart and
   back, and the slash blinks like a caret. */
/* An inline SVG with a viewBox and no size of its own falls back to the
   browser's 300x150 default, which is how the sign in page ended up with a
   mark the size of the box. Every block that places one gives it a size;
   this is the size for any that does not. */
.mark--sigil { width: 30px; height: 30px; color: var(--accent); overflow: visible; }
@keyframes mark-in { from { opacity: 0; transform: translateY(-3px) scale(0.9); } }
@keyframes mark-drift { 50% { transform: translateY(-1.5px); } }
@keyframes tag-open-l { 50% { transform: translateX(-1.1px); } }
@keyframes tag-open-r { 50% { transform: translateX(1.1px); } }
@keyframes tag-slash { 0%, 42% { opacity: 1; } 50%, 92% { opacity: 0.35; } 100% { opacity: 1; } }

.mark--live { animation: mark-in 0.5s var(--ease) both; }
.mark--live img { animation: mark-drift 7s ease-in-out 0.6s infinite; }
.mark--live .mark__lt { animation: tag-open-l 4.5s ease-in-out 0.6s infinite; }
.mark--live .mark__gt { animation: tag-open-r 4.5s ease-in-out 0.6s infinite; }
.mark--live .mark__slash { animation: tag-slash 4.5s ease-in-out 0.6s infinite; }

/* -- boxes ----------------------------------------------------------------- */
.box { border: 1px solid var(--line); border-radius: var(--r); background: var(--panel); padding: 1.25rem 1.4rem; }
/* A gold panel is marked by a gold hairline all round and the corner brackets,
   not by a bar down one edge. The bar is the tell everyone recognises. */
.box--gold { border-color: color-mix(in srgb, var(--accent) 42%, var(--line)); }
.box--gold > h2, .box--gold > h3 { color: var(--accent); }
.box h2, .box h3 { font-size: var(--t-title); letter-spacing: -0.025em; margin-bottom: 0.55rem; }
.box p { color: var(--fg-2); font-size: var(--t-ui); }

.cols { display: grid; gap: 1.1rem; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }

/* -- a system's order panel ------------------------------------------------ */
.sysgrid { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: clamp(1.5rem, 3.5vw, 2.75rem); align-items: start; }
.sysgrid__lead h1 { margin-bottom: 0.7rem; }
.sysgrid__summary { color: var(--fg-2); margin-top: 0.7rem; max-width: var(--measure); }
/* the lead and the description are one article, separated by a rule rather
   than by a hole */
.sysgrid__cut { border: 0; border-top: 1px solid var(--line); margin: 1.5rem 0 1.6rem; max-width: var(--measure); }

.spec {
  border: 1px solid var(--line);
  /* a 2px accent edge and a rounded corner read as a mistake together */
  border-top: 2px solid var(--accent-bg);
  border-radius: 0;
  background: var(--panel);
  padding: 1rem 1.15rem;
  display: grid;
  gap: 0.7rem;
  /* the whole article is the sticky container, so this follows the reader all
     the way down instead of unsticking after the first screen */
  position: sticky;
  top: 5rem;
}
.spec__title { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.09em; color: var(--fg-3); }
.spec__price { display: flex; align-items: baseline; gap: 0.4rem; }
.spec__price b { font-size: 2.15rem; font-weight: 800; letter-spacing: -0.03em; color: var(--accent); font-variant-numeric: tabular-nums; }
.spec__price small { font-size: var(--t-xs); color: var(--fg-3); }
.spec__note { font-size: var(--t-sm); color: var(--fg-2); }
.spec__rows { font-size: var(--t-sm); }
.spec__rows div { display: flex; justify-content: space-between; gap: 1rem; padding: 0.4rem 0; border-bottom: 1px solid var(--line); }
.spec__rows div:last-child { border-bottom: 0; }
.spec__rows dt { color: var(--fg-3); }
.spec__rows dd { color: var(--fg); font-variant-numeric: tabular-nums; }
.spec__hint { font-size: var(--t-xs); color: var(--fg-3); }
.spec__free { display: flex; gap: 0.5rem; font-size: var(--t-xs); color: var(--fg-2); line-height: 1.5; }
.spec__free i { color: var(--ok); margin-top: 0.22rem; }

/* On this page the intro and the closing note run the full width of the cards
   underneath them, because a short column above a wide list looked unfinished.
   It is wider than the usual reading measure on purpose. */
/* the page lede is the same size everywhere; only the denser bits below it
   are stepped down on this page */
.page-recs .intro .lede { max-width: none; }
.page-recs .rec__text,
.page-recs .block > .field__hint { max-width: none; font-size: var(--t-sm); }

/* -- the portrait on the about page ---------------------------------------- *
 * Two columns only for as long as the introduction lasts. Everything after the
 * first heading drops back to the full width of the page.                     */
.meshot { display: grid; grid-template-columns: minmax(0, 1fr) 330px; gap: clamp(1.5rem, 4vw, 3rem); align-items: start; }
.meshot__frame {
  position: relative;
  aspect-ratio: 1;
  border-radius: 50%;
  overflow: hidden;
  border: 2px solid var(--accent-bg);
  padding: 0;
  background: var(--panel);
  /* a warm halo that breathes, and a ring that turns with it */
  box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent) 10%, transparent),
              0 18px 55px -25px color-mix(in srgb, var(--accent) 85%, transparent);
  animation: meshot-glow 6s ease-in-out infinite;
}
/* the frame clips to a circle, so the Discord ring lives outside it */
.meshot__portrait { position: relative; }
/* The picture and the frame are one assembly, driven off a single width so the
   hexagon below keeps lining up with the hole in the frame.

   --frame-w sizes the pair: 115.4% lands the visible gold on the same width as
   the buttons beneath, because the artwork fills 86.8% of the decoration's own
   canvas. The picture is 80% of that, which is wider than the hole, so its
   clipped edge runs *under* the gold rather than stopping short of it. Meeting
   the hole exactly leaves hairline gaps at the six corners: the frame's inner
   corners are slightly rounded and no straight-sided polygon follows them. */
.meshot__portrait { --frame-w: 115.4%; --photo-of-frame: 0.8; }
.meshot__decor {
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--frame-w);
  height: var(--frame-w);
  max-width: none;
  transform: translate(-50%, -50%);
  object-fit: contain;
  pointer-events: none;
}
/* One ring is enough. With a Discord frame present the dashed orbit, the gold
   halo and our own border all step aside: the frame supplies the outline now.

   The picture is clipped to the hexagon cut out of the decoration itself. Those
   numbers are measured, not guessed: decoding the 288x288 frame shows the hole
   running from a point at 15.3% down to 84.7%, with vertical sides between
   34.7% and 65.3% at a width of 18.8%..81.6%. Converted into the picture's own
   box, which is 1/1.39 of the frame, that is the polygon below. */
.meshot__portrait.has-decor .meshot__frame::after { display: none; }
.meshot__portrait.has-decor .meshot__frame {
  width: calc(var(--frame-w) * var(--photo-of-frame));
  margin-inline: auto;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  animation: none;
  /* Built from five numbers in the settings and adjustable on the page itself.
     The default sits a little outside the hole in the frame on every side, so
     the cut edge runs under the gold rather than stopping short of it. */
  clip-path: var(--portrait-clip, polygon(50% 2.3%, 93.9% 30.9%, 93.9% 69.1%, 50% 97.8%, 6.6% 69.1%, 6.6% 30.9%));
}
.meshot__portrait.has-decor .meshot__frame img { border-radius: 0; }


.meshot__frame::after {
  content: '';
  position: absolute;
  inset: -9px;
  border-radius: 50%;
  border: 1px dashed color-mix(in srgb, var(--accent) 45%, transparent);
  animation: meshot-turn 26s linear infinite;
  pointer-events: none;
}
/* Not copy proof, nothing on the web is: this stops the casual right click,
   drag to desktop and long press save. A determined visitor can still read it
   out of the network tab. */
.meshot__frame img {
  transition: transform 0.6s var(--ease), filter 0.6s var(--ease);
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}
.meshot__frame::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
}
.meshot__frame:hover img { transform: scale(1.05); filter: saturate(1.1) contrast(1.05); }

@keyframes meshot-glow {
  0%, 100% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent) 9%, transparent),
                         0 18px 55px -25px color-mix(in srgb, var(--accent) 70%, transparent); }
  50%      { box-shadow: 0 0 0 11px color-mix(in srgb, var(--accent) 14%, transparent),
                         0 18px 60px -22px color-mix(in srgb, var(--accent) 95%, transparent); }
}
@keyframes meshot-turn { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .meshot__frame { animation: none; }
  .meshot__frame::after { animation: none; }
}
.meshot__frame img { width: 100%; height: 100%; object-fit: cover; }
.meshot__side { display: grid; gap: 0.7rem; }
.meshot__who { display: grid; gap: 0.1rem; text-align: center; margin-top: 0.2rem; }
.meshot__who b { font-size: var(--t-lead); letter-spacing: -0.02em; }
.meshot__who span { font-size: var(--t-xs); color: var(--fg-3); }

/* On a phone the two columns become two sections, one under the other: the
   writing, then the portrait card. The card is a fixed 260 wide, so it has to
   be centred in the row rather than left where the column used to start. */
@media (max-width: 780px) {
  .meshot { grid-template-columns: minmax(0, 1fr); }
  .meshot__side { max-width: 260px; margin-inline: auto; }
  .meshot__portrait { margin-inline: auto; }
  .meshot__frame { width: 240px; margin-inline: auto; }
}

/* -- testimonials, a right to left belt ------------------------------------ *
 * The track holds two identical passes and slides exactly half its width, so
 * the loop restarts on a frame that looks the same. No JavaScript, so nothing
 * runs on a browser that would rather not animate: the reduced-motion rule
 * below stops it dead and leaves the cards readable and scrollable by hand.  */
/* the belt tints at the top and fades out into the page, so it hands over to
   the footer instead of ending on a hard edge */
.says {
  padding-block: clamp(1.25rem, 2vw, 1.75rem);
  overflow: hidden;
  background: none;
  border: 0;
  margin-bottom: 0;
}
.says__note { margin-top: 1.1rem; font-size: var(--t-sm); color: var(--fg-3); }
.says__viewport {
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  /* A card lifts 3px on hover, and overflow-x:auto forces the vertical axis to
     auto as well, which would slice the top off the raised border. The padding
     gives it room and the negative margin keeps the layout where it was. */
  padding-block: 8px;
  margin-block: -8px;
  /* the belt fades out at both ends rather than being sliced by the edge */
  /* impeccable-disable-next-line design-system-color -- mask alpha, not a palette colour */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  /* impeccable-disable-next-line design-system-color -- mask alpha, not a palette colour */
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.says__viewport::-webkit-scrollbar { display: none; }
/* The track stays unwrapped at its natural width so site.js can measure whether
   it overflows at all; without JS that leaves a belt you can still scroll by
   hand. Only once it is cloned does it move, and if the reviews all fit they
   are centred and left alone. */
.says__track { display: flex; gap: 1rem; width: max-content; }
/* The belt is carried by the viewport's own scroll position, driven from
   site.js, so a finger and the animation are moving the same thing and a drag
   can never run off the end of it. Nothing here animates. */
/* nothing overflows: no belt to fade, so drop the mask and centre the cards */
.says__viewport:has(.is-static) { mask-image: none; -webkit-mask-image: none; }
/* width:auto lets the flex line actually break; max-content would keep the row
   wider than the viewport and quietly clip the last card instead of wrapping */
.says__track.is-static { width: auto; max-width: 100%; margin-inline: auto; flex-wrap: wrap; justify-content: center; }

/* three bands: who said it, what they said, and when. The middle one sits on a
   slightly darker ground so the quote reads as a quote rather than as more of
   the card. Padding lives on the bands, not the card, so the middle one can
   run edge to edge. */
.says__card {
  display: flex;
  flex-direction: column;
  width: 340px;
  flex-shrink: 0;
  padding: 0;
  /* deliberately not overflow:hidden. The header and footer bands are
     transparent so nothing needs clipping at the rounded corners, and clipping
     here would cut the profile popout off at the bottom of the card. */
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--panel);
}
.says__stars { display: inline-flex; gap: 0.12rem; color: var(--accent); font-size: var(--t-xs); margin-left: auto; align-self: flex-start; }
.says__stars .fa-regular { color: var(--fg-3); }
.says__when { font-size: var(--t-xs); color: var(--fg-3); font-variant-numeric: tabular-nums; }
.says__card blockquote {
  flex: 1;
  padding: 1rem 1.25rem;
  font-size: var(--t-sm);
  line-height: 1.65;
  color: var(--fg-2);
  background: color-mix(in srgb, var(--bg) 45%, transparent);
  border-block: 1px solid var(--line);
}
.says__card blockquote::before { content: '“'; color: var(--accent); font-size: 1.3em; line-height: 0; vertical-align: -0.2em; margin-right: 0.15rem; }
.says__card blockquote::after { content: '”'; color: var(--accent); font-size: 1.3em; line-height: 0; vertical-align: -0.35em; margin-left: 0.1rem; }

/* -- who said it: avatar, name, and the profile popout --------------------- */
.says__card { position: relative; }
.says__by { display: flex; align-items: center; gap: 0.65rem; padding: 1.05rem 1.25rem 0.9rem; }
.says__id { display: flex; flex-direction: column; min-width: 0; }
.says__id .says__who { font-weight: 700; font-size: var(--t-sm); line-height: 1.25; }
/* what goes under the name on a review: a role, written by hand. Not a
   username, and never a Discord handle. */
.says__id .says__role { font-size: var(--t-xs); color: var(--fg-3); line-height: 1.3; }

.says__avatar {
  --avatar-size: 40px;
  position: relative;
  display: grid;
  place-items: center;
  width: var(--avatar-size);
  height: var(--avatar-size);
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--panel-2);
  border: 1px solid var(--line);
  padding: 0;
}
.says__avatar img:not(.says__decor) { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 50%; }

/* Discord avatar decorations are 288x288 frames drawn around an avatar that
   fills roughly the middle 72% of them, so the frame has to be rendered about
   1.39x the avatar to line up. Centring is done with a translate rather than
   insets: a percentage inset resolves against the padding box, which the
   avatar's own border shifts off centre. One variable drives both sizes. */
/* Every decoration frames its hole differently, so no single number suits all
   of them perfectly. This one is judged by eye; nudge it here and both the
   card and the popout follow. */
.says__avatar, .says__who-avatar { --decor-scale: 1.28; }
.says__decor, .says__who-decor {
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--avatar-size) * var(--decor-scale));
  height: calc(var(--avatar-size) * var(--decor-scale));
  transform: translate(-50%, -50%);
  max-width: none;
  border-radius: 0;
  object-fit: contain;
  pointer-events: none;
}
.says__initial { font-weight: 700; font-size: var(--t-sm); color: var(--fg-3); text-transform: uppercase; }
.says__avatar--btn { cursor: pointer; transition: border-color 0.18s, transform 0.18s; }
.says__avatar--btn:hover, .says__avatar--btn[aria-expanded='true'] { border-color: var(--accent); transform: scale(1.05); }

/* the popout, shaped after the profile card Discord shows on an avatar click */
/* On open, site.js moves this card to the end of <body> and positions it in
   page coordinates. Left inside the row it was fighting the cascade: a later
   heading painted over it, and the review belt's scrolling viewport clipped
   it. As a direct child of body nothing can clip it and nothing can out-paint
   it, and absolute page coordinates behave the same everywhere, which fixed
   positioning did not. */
.says__who-card {
  position: absolute;
  z-index: 200;
  top: 0;
  left: 0;
  width: 260px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  background: var(--panel-2);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
  overflow: hidden;
  animation: who-in 0.16s var(--ease);
}
.says__who-card[hidden] { display: none; }
@keyframes who-in { from { opacity: 0; transform: translateY(-4px); } }

.says__who-banner {
  height: 64px;
  background-color: var(--who-accent, var(--accent));
  background-size: cover;
  background-position: center;
}
.says__who-body { padding: 0 0.9rem 0.9rem; display: grid; gap: 0.1rem; }
.says__who-avatar {
  --avatar-size: 64px;
  position: relative;
  display: block;
  width: var(--avatar-size);
  height: var(--avatar-size);
  margin-top: -36px;
  border-radius: 50%;
  border: 5px solid var(--panel-2);
  background: var(--panel-2);
}
.says__who-avatar img:not(.says__who-decor) { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: block; }

.says__who-badges { display: flex; flex-wrap: wrap; gap: 0.3rem; margin: 0.5rem 0 0.35rem; }
.says__badge {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: var(--r);
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  color: var(--fg-2);
  font-size: var(--t-xs);
}

.says__who-name { font-weight: 800; font-size: var(--t-lead); letter-spacing: -0.02em; line-height: 1.2; }
.says__who-handle { font-family: var(--mono); font-size: var(--t-xs); color: var(--fg-3); }
.says__who-role { font-size: var(--t-xs); color: var(--fg-3); }

.says__who-block { margin-top: 0.6rem; padding-top: 0.6rem; border-top: 1px solid var(--line); }
.says__who-label {
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-3);
  margin-bottom: 0.2rem;
}
.says__who-value { font-size: var(--t-sm); color: var(--fg-2); display: flex; align-items: center; gap: 0.4rem; }
.says__who-value i { color: var(--accent); }

.says__card figcaption { display: flex; align-items: baseline; gap: 0.55rem; flex-wrap: wrap; padding: 0.8rem 1.25rem; }
/* date on the left, account age pushed to the right edge of the card */
.says__since { font-size: var(--t-xs); color: var(--fg-3); display: inline-flex; align-items: center; gap: 0.3rem; margin-left: auto; }
.says__src { font-size: var(--t-xs); color: var(--accent); }
.says__src:hover { text-decoration: underline; }


/* -- recommendations ------------------------------------------------------- */
.recs { display: grid; gap: 0.7rem; counter-reset: rec; }
.rec {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 210px;
  align-items: start;
  gap: 0.5rem 1.6rem;
  padding: 1.2rem 1.4rem;
  border: 1px solid color-mix(in srgb, var(--who, var(--cyan)) 24%, var(--line));
  border-radius: var(--r);
  background: color-mix(in srgb, var(--who, var(--cyan)) 9%, var(--panel));
  transition: border-color 0.18s, background 0.18s;
}
.rec { border-left: 2px solid color-mix(in srgb, var(--who, var(--cyan)) 60%, transparent); }
.rec:hover {
  border-color: color-mix(in srgb, var(--who, var(--cyan)) 45%, var(--line));
  border-left-color: var(--who, var(--cyan));
  background: color-mix(in srgb, var(--who, var(--cyan)) 15%, var(--panel));
}
.rec__role::before { background: var(--who, var(--cyan)); }
.rec__head { display: flex; align-items: center; gap: 0.7rem; flex-wrap: wrap; }
.rec__head h2 { font-size: var(--t-lead); letter-spacing: -0.03em; }
/* the kamon diamond again, this time separating a name from what they do */
.rec__role { font-size: var(--t-sm); color: var(--fg-2); display: flex; align-items: center; gap: 0.55rem; line-height: 1; }
.rec__role::before { content: ''; width: 4px; height: 4px; background: var(--cyan); transform: rotate(45deg); flex-shrink: 0; }
.rec__text { font-size: var(--t-ui); color: var(--fg-2); }
/* the two ways to reach a person, stacked and kept to the right edge */
.rec__do { display: grid; gap: 0.4rem; justify-items: stretch; }
.rec__do .btn { justify-content: flex-start; }
.rec__soon { font-size: var(--t-sm); color: var(--fg-3); padding: 0.32rem 0; }

.duo { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: clamp(1.5rem, 4vw, 2.5rem); align-items: start; }

.dl { font-size: var(--t-ui); }
.dl div { display: flex; gap: 0.9rem; padding: 0.4rem 0; border-bottom: 1px solid var(--line); }
.dl div:last-child { border-bottom: 0; }
.dl dt { color: var(--fg-3); min-width: 7.5rem; flex-shrink: 0; }
.dl dd { color: var(--fg-2); }

.ticks { display: grid; gap: 0.5rem; font-size: var(--t-ui); color: var(--fg-2); }
.ticks li { display: flex; gap: 0.6rem; }
.ticks li::before { content: '\f00c'; font-family: 'Font Awesome 6 Free'; font-weight: 900; font-size: var(--t-xs); color: var(--accent); margin-top: 0.3rem; }

/* -- faq ------------------------------------------------------------------- */
.faq { display: grid; gap: 0.5rem; }
.faq details { border: 1px solid var(--line); border-radius: var(--r); background: var(--panel); }
.faq summary { padding: 0.8rem 1.05rem; font-weight: 600; font-size: var(--t-ui); cursor: pointer; list-style: none; display: flex; justify-content: space-between; gap: 1rem; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: '+'; color: var(--accent); font-family: var(--mono); }
.faq details[open] summary::after { content: '\2013'; }
.faq p { padding: 0 1.15rem 1.05rem; color: var(--fg-2); font-size: var(--t-body); max-width: none; }

/* -- prose (rendered markdown) --------------------------------------------- */
.prose { color: var(--fg-2); font-size: var(--t-body); line-height: 1.75; max-width: var(--measure); }
/* the about page runs its text across the whole column, no side panel */
.prose--wide { max-width: none; }
.prose > * + * { margin-top: 1rem; }
.prose h2 { color: var(--fg); margin-top: 2.1rem; font-size: var(--t-lead); }
.prose h3 { color: var(--fg); margin-top: 1.5rem; font-size: var(--t-title); }
.prose h2 .anchor, .prose h3 .anchor { display: none; }
.prose strong { color: var(--fg); }
.prose a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.prose ul, .prose ol { display: grid; gap: 0.45rem; padding-left: 1.2rem; }
.prose ul { list-style: square; }
.prose ol { list-style: decimal; }
.prose li::marker { color: var(--accent); }
.prose blockquote { padding-left: 1.15rem; border-left: 1px solid var(--accent-bg); color: var(--fg-2); }
.prose hr { border: 0; border-top: 1px solid var(--line); margin-block: 1.8rem; }
.prose code { font-family: var(--mono); font-size: 0.86em; padding: 0.1em 0.35em; border: 1px solid var(--line); border-radius: 2px; color: var(--accent); }
.prose pre { padding: 1rem 1.1rem; border: 1px solid var(--line); border-radius: var(--r); background: var(--panel); overflow-x: auto; font-size: var(--t-sm); }
.prose pre code { border: 0; padding: 0; color: var(--fg-2); }
/* pictures inside a description. The file's name is a tooltip on the image
   rather than a caption under it, and the placement classes are written by the
   editor. `.pic` deliberately does not reuse `.figure`, which belongs to the
   counted-number panels and paints a gold wash on hover. */
.prose .pic { margin-block: 1.4rem; }
/* No width of its own: the picture is as big as it was made, and the width
   written by the editor rides on the img itself. Never wider than the column. */
.prose .pic img { display: block; max-width: 100%; height: auto; border: 1px solid var(--line); border-radius: var(--r); }
.prose .pic--full img { width: 100%; }
.prose .pic--center { width: fit-content; margin-inline: auto; }
.prose .pic--left { float: left; margin: 0.35rem 1.4rem 1rem 0; }
.prose .pic--right { float: right; margin: 0.35rem 0 1rem 1.4rem; }
/* a floated picture must not hang out of the bottom of the description */
.prose::after { content: ''; display: block; clear: both; }
@media (max-width: 560px) {
  .prose .pic--left, .prose .pic--right { float: none; margin-inline: auto; }
}

/* The whole callout carries its type, tinted from the hue it means. */
.callout {
  border: 1px solid color-mix(in srgb, var(--accent) 32%, var(--line));
  border-radius: var(--r);
  padding: 0.9rem 1.15rem;
  background: color-mix(in srgb, var(--accent) 5%, var(--panel));
}
.callout__head { font-weight: 700; color: var(--accent); font-size: var(--t-ui); letter-spacing: -0.01em; margin-bottom: 0.25rem; }
.callout__head i { margin-right: 0.45rem; }
.callout--tip, .callout--note, .callout--info {
  border-color: color-mix(in srgb, var(--cyan) 32%, var(--line));
  background: color-mix(in srgb, var(--cyan) 5%, var(--panel));
}
.callout--tip .callout__head, .callout--note .callout__head, .callout--info .callout__head { color: var(--cyan); }
.callout--danger, .callout--warn, .callout--warning {
  border-color: color-mix(in srgb, var(--danger) 32%, var(--line));
  background: color-mix(in srgb, var(--danger) 5%, var(--panel));
}
.callout--danger .callout__head, .callout--warn .callout__head, .callout--warning .callout__head { color: var(--danger); }

.table-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r); }
.table-wrap table { font-size: var(--t-sm); min-width: 420px; }
.table-wrap th, .table-wrap td { padding: 0.55rem 0.85rem; text-align: left; border-bottom: 1px solid var(--line); vertical-align: top; }
.table-wrap thead th { background: var(--panel-2); color: var(--fg); font-size: var(--t-xs); }
.table-wrap tbody th { color: var(--fg-3); font-weight: 400; white-space: nowrap; }
.table-wrap tr:last-child td, .table-wrap tr:last-child th { border-bottom: 0; }

/* -- video (loads only on click) ------------------------------------------- */
.video { position: relative; aspect-ratio: 16 / 9; border: 1px solid var(--line); border-radius: var(--r); background: var(--panel); margin-block: 1.4rem; overflow: hidden; }
.video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.video__play { position: absolute; inset: 0; display: grid; place-content: center; justify-items: center; gap: 0.45rem; text-align: center; padding: 1rem; }
.video__icon { display: grid; place-items: center; width: 50px; height: 50px; border-radius: 50%; background: var(--accent-bg); color: var(--accent-fg); }
.video__label { font-weight: 700; font-size: var(--t-ui); color: var(--fg); }
.video__hint { font-size: var(--t-xs); color: var(--fg-3); }

.shots { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 0.6rem; margin-top: 1.4rem; }
.shots a { border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.shots a:hover { border-color: var(--accent-bg); }
.shots img { width: 100%; aspect-ratio: 16/10; object-fit: cover; }

/* -- forms ----------------------------------------------------------------- */
.field { display: grid; gap: 0.35rem; margin-bottom: 1rem; }
.field-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 1rem; }
.field label, .field > span { font-size: var(--t-sm); font-weight: 600; color: var(--fg-2); }
.req { color: var(--accent); }
.opt { font-weight: 400; color: var(--fg-3); font-size: var(--t-xs); }
.field__hint { font-size: var(--t-xs); color: var(--fg-3); }

input[type='text'], input[type='email'], input[type='password'], input[type='search'], input[type='url'],
input[type='number'], input[type='color'], input[type='file'], select, textarea {
  width: 100%;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  background: var(--bg);
  color: var(--fg);
  font-size: var(--t-ui);
}
input::placeholder, textarea::placeholder { color: var(--fg-3); }
input:focus, select:focus, textarea:focus { outline: 0; border-color: var(--accent-bg); }
textarea { resize: vertical; min-height: 120px; }
select { cursor: pointer; }
select option { background: var(--panel); color: var(--fg); }

.field-icon { position: relative; }
.field-icon > i { position: absolute; left: 0.65rem; top: 50%; transform: translateY(-50%); color: var(--fg-3); font-size: var(--t-sm); }
.field-icon input, .field-icon select { padding-left: 2.05rem; }

.consent { display: flex; gap: 0.55rem; align-items: flex-start; font-size: var(--t-sm); color: var(--fg-2); margin-block: 1rem; cursor: pointer; }
.consent input { position: absolute; opacity: 0; width: 0; height: 0; }
.check__box { display: grid; place-items: center; width: 17px; height: 17px; border: 1px solid var(--line-2); border-radius: 2px; flex-shrink: 0; margin-top: 0.15rem; }
/* impeccable-disable-next-line design-system-font-size -- glyph sized to a 17px box, not text */
.check__box i { font-size: 0.58rem; opacity: 0; color: var(--accent-fg); }
.consent input:checked + .check__box { background: var(--accent-bg); border-color: var(--accent-bg); }
.consent input:checked + .check__box i { opacity: 1; }
.consent a { color: var(--accent); text-decoration: underline; }

.form-foot { font-size: var(--t-xs); color: var(--fg-3); margin-top: 0.7rem; }
.hp { position: absolute; left: -9999px; }

/* -- notices --------------------------------------------------------------- */
.notice, .flash {
  border: 1px solid color-mix(in srgb, var(--accent) 32%, var(--line-2));
  border-radius: var(--r);
  padding: 0.75rem 1rem;
  font-size: var(--t-ui);
  margin-bottom: 1rem;
  background: color-mix(in srgb, var(--accent) 5%, var(--panel));
}
.flash { margin-top: 1.25rem; display: flex; gap: 0.6rem; align-items: center; }
.flash span { flex: 1; }
.flash--success, .notice--warn {
  border-color: color-mix(in srgb, var(--ok) 32%, var(--line-2));
  background: color-mix(in srgb, var(--ok) 5%, var(--panel));
}
.flash--error, .notice--error {
  border-color: color-mix(in srgb, var(--danger) 32%, var(--line-2));
  background: color-mix(in srgb, var(--danger) 5%, var(--panel));
}
.notice ul { list-style: disc; padding-left: 1.3rem; margin-top: 0.35rem; }

.empty { padding: 3rem 1rem; text-align: center; color: var(--fg-3); border: 1px dashed var(--line-2); border-radius: var(--r); }
.empty h2 { color: var(--fg); margin-bottom: 0.4rem; }

/* -- footer ---------------------------------------------------------------- */
.foot { border-top: 1px solid var(--line); background: var(--indigo); padding-block: 1.9rem 1.4rem; margin-top: var(--gap); font-size: var(--t-sm); color: var(--fg-3); }
.foot__grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1.25rem 2rem; padding-bottom: 1.25rem; }
/* the halves mirror each other: two columns reading out from the left, two
   reading back from the right */
.foot__grid > *:nth-child(3),
.foot__grid > *:nth-child(4) { text-align: right; }
.foot__company p { line-height: 1.7; }
.foot__company b { color: var(--fg-2); font-weight: 600; }
.foot__brand { display: flex; align-items: center; gap: 0.5rem; font-weight: 800; color: var(--fg); margin-bottom: 0.5rem; }
.foot__brand span { color: var(--accent); }
.foot h2 { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.09em; color: var(--fg-3); font-weight: 600; margin-bottom: 0.6rem; }
.foot ul { display: grid; gap: 0.35rem; }
.foot a:hover { color: var(--accent); }
.foot__bar {
  border-top: 1px solid var(--line);
  padding-top: 0.9rem;
  font-size: var(--t-sm);
  line-height: 1.6;
  max-width: none;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem 2rem;
  flex-wrap: wrap;
}
.foot__bar b { color: var(--fg-2); font-weight: 600; }

/* -- cookie notice --------------------------------------------------------- */
.cnote {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 40;
  background: var(--panel);
  border-top: 1px solid var(--accent-bg);
}
.cnote[hidden] { display: none; }
.cnote__in { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; padding-block: 0.8rem; }
.cnote p { font-size: var(--t-sm); color: var(--fg-2); margin: 0; }

/* -- legal ----------------------------------------------------------------- */
.legal h2 { margin-top: 2rem; font-size: var(--t-title); color: var(--fg); }
.legal p, .legal li { color: var(--fg-2); font-size: var(--t-body); }
.legal > * + * { margin-top: 0.9rem; }
.legal ul, .legal ol { display: grid; gap: 0.4rem; padding-left: 1.3rem; }
.legal ul { list-style: square; }
.legal ol { list-style: decimal; }
.legal li::marker { color: var(--accent); }
.legal a { color: var(--accent); text-decoration: underline; }
.legal strong { color: var(--fg); }
.legal hr { border: 0; border-top: 1px solid var(--line); margin-block: 1.8rem; }

/* -- status pages ---------------------------------------------------------- */
.status { padding-block: clamp(3rem, 10vw, 5.5rem); }
/* impeccable-disable-next-line design-system-font-size -- the status number is a graphic, not a heading */
.status h1 { font-size: clamp(3rem, 10vw, 4.5rem); color: var(--accent); margin-bottom: 0.5rem; }
.status p { color: var(--fg-2); }
.status pre { margin-top: 1rem; padding: 1rem; border: 1px solid var(--line); border-radius: var(--r); background: var(--panel); font-size: 0.76rem; overflow: auto; max-height: 240px; }

/* -- sign in --------------------------------------------------------------- */
.signin { min-height: 100vh; display: grid; place-items: center; padding: 2rem 1rem; }
.signin__box { width: min(380px, 100%); border: 1px solid var(--line); border-radius: var(--r); background: var(--panel); padding: 1.9rem; }
.signin__brand { display: flex; align-items: center; gap: 0.5rem; font-weight: 800; margin-bottom: 1.4rem; }
.signin__brand span { color: var(--accent); }
.signin h1 { font-size: var(--t-lead); margin-bottom: 1.4rem; }
.signin__foot { margin-top: 1.4rem; font-size: var(--t-sm); color: var(--fg-3); }

/* -- responsive ------------------------------------------------------------ */
/* On a phone the mirrored halves stop making sense: everything reads left, the
   link lists become two side by side columns so the footer stays short, and the
   company block sits under them as the signature. */
@media (max-width: 680px) {
  .foot__grid { grid-template-columns: 1fr 1fr; gap: 1.4rem 1.5rem; }
  .foot__grid > *:nth-child(3),
  .foot__grid > *:nth-child(4) { text-align: left; }
  /* site across the top, the two short legal lists side by side under it, the
     company block as the signature at the bottom */
  .foot__grid > *:nth-child(2) { grid-column: 1 / -1; order: 1; }
  .foot__grid > *:nth-child(3) { grid-column: 1; order: 2; }
  .foot__grid > *:nth-child(4) { grid-column: 2; order: 2; }
  .foot__company { grid-column: 1 / -1; order: 3; padding-top: 1.1rem; border-top: 1px solid var(--line); }
  .foot__grid > *:nth-child(2) ul { display: grid; grid-template-columns: 1fr 1fr; gap: 0.35rem 1rem; }
  .foot__bar { flex-direction: column; gap: 0.35rem; }
}

@media (max-width: 900px) {
  .intro__grid { grid-template-columns: minmax(0, 1fr); }
  .duo { grid-template-columns: minmax(0, 1fr); }
  .foot__grid { grid-template-columns: 1fr 1fr; }
  .sysgrid { grid-template-columns: minmax(0, 1fr); }
  .spec { position: static; }
}

@media (max-width: 620px) {
  .rec { grid-template-columns: minmax(0, 1fr); }
  /* the search drops under the filters rather than squeezing beside them */
  .filters__search { flex: 1 1 100%; }
}

/* five nav items stop fitting next to the brand well before the phone width */
@media (max-width: 780px) {
  .menu-btn { display: grid; place-items: center; margin-left: auto; }
  .top nav { display: none; margin-left: 0; width: 100%; flex-direction: column; align-items: flex-start; gap: 0; padding-bottom: 0.75rem; }
  .top nav.open { display: flex; }
  .top nav a { padding: 0.6rem 0; width: 100%; border-bottom: 1px solid var(--line); }
  .top__in { flex-wrap: wrap; }
}

@media (max-width: 480px) {
  /* keep the bar on one row: brand, menu, theme */
  .top__in { gap: 0.7rem; }
  .brand { font-size: 1.05rem; gap: 0.45rem; }
  .brand svg { width: 24px; height: 24px; }
}

@media (max-width: 680px) {
  /* a 2 by 2 ledger reads better than four stacked rows */
  .figures { grid-template-columns: 1fr 1fr; }
  .figures > * { border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); }
  .figures > *:nth-child(2n) { border-right: 0; }
  .figures > *:nth-last-child(-n + 2) { border-bottom: 0; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

@media print {
  .top, .foot, .btn, .pillbar { display: none !important; }
  /* impeccable-disable-next-line design-system-color -- ink on paper, not the screen palette */
  body { background: #fff; color: #000; }
}

/* footer brand mark sizing */
.foot__brand svg { width: 22px; height: 22px; }
.foot__brand span { color: var(--accent); }

/* -- the review belt, shared by several pages ------------------------------ */
.says__heading { font-size: var(--t-lead); letter-spacing: -0.03em; margin-bottom: 0.9rem; }
.says__all { color: var(--accent); text-decoration: underline; text-underline-offset: 0.2em; white-space: nowrap; }
.says__all:hover { color: var(--accent-soft); }

/* the same lift and glow a catalogue card gets, so hovering feels consistent */
.says__card {
  transition: transform 0.18s var(--ease), border-color 0.18s, box-shadow 0.18s;
}
.says__viewport:hover .says__card:hover,
.revwall .says__card:hover {
  transform: translateY(-3px) scale(1.012);
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
  box-shadow: 0 12px 30px -20px color-mix(in srgb, var(--accent) 90%, transparent);
}
@media (prefers-reduced-motion: reduce) {
  .says__card { transition: border-color 0.18s, box-shadow 0.18s; }
  .says__viewport:hover .says__card:hover,
  .revwall .says__card:hover { transform: none; }
}

/* every review, on its own page, as a wall rather than a belt */
/* Cards in the wall stretch to a common height per row rather than each
   ending where its quote does, so the grid lines up. The quote band absorbs
   the difference, which keeps the header and the footer on the same lines
   across a row. */
.revwall { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1rem; align-items: stretch; }
.revwall .says__card { width: auto; height: 100%; }

/* -- a recommended person's picture ---------------------------------------- */
/* The picture is its own column and spans both rows, so it stands beside the
   name and the description together rather than just the name. */
.rec__main { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.5rem 1rem; align-items: start; }
.rec__said { display: grid; gap: 0.5rem; min-width: 0; }
.rec__face {
  --avatar-size: 56px;
  position: relative;
  display: grid;
  place-items: center;
  width: var(--avatar-size);
  height: var(--avatar-size);
  flex-shrink: 0;
  border-radius: 50%;
  background: color-mix(in srgb, var(--who, var(--cyan)) 18%, var(--panel-2));
  border: 1px solid color-mix(in srgb, var(--who, var(--cyan)) 40%, transparent);
}
.rec__face img:not(.rec__decor) { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: block; }
.rec__initial { font-weight: 800; font-size: var(--t-lead); color: var(--who, var(--cyan)); text-transform: uppercase; }
.rec__decor {
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--avatar-size) * 1.28);
  height: calc(var(--avatar-size) * 1.28);
  max-width: none;
  transform: translate(-50%, -50%);
  object-fit: contain;
  pointer-events: none;
}
.rec__more { font-size: var(--t-sm); color: var(--fg-3); line-height: 1.6; }

/* an avatar that opens a profile card, same behaviour as on a review */
.rec { position: relative; }
.rec__face--btn { cursor: pointer; padding: 0; transition: border-color 0.18s, transform 0.18s; }
.rec__face--btn:hover, .rec__face--btn[aria-expanded='true'] {
  border-color: var(--who, var(--cyan));
  transform: scale(1.06);
}

@media (max-width: 720px) {
  .rec__main { grid-template-columns: auto minmax(0, 1fr); }
  .rec__face { --avatar-size: 44px; }
}

/* -- what I work in -------------------------------------------------------- *
 * A tile and a name across the top, the line of work under it, and the bar and
 * the figure sharing the foot so the number sits bottom right where the eye
 * lands last. The percentage is carried on the row as --pct, so the markup
 * states it once and the bar, the label and the count all read from it.       */
.skills {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 0.9rem;
  margin-top: 1.1rem;
}
.skill {
  position: relative;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: 0.7rem;
  padding: 1.15rem 1.2rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--panel);
  overflow: hidden;
  transition: border-color 0.18s, transform 0.18s var(--ease), box-shadow 0.18s;
}
.skill:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
  box-shadow: 0 12px 30px -20px color-mix(in srgb, var(--accent) 90%, transparent);
}

.skill__top { display: flex; align-items: center; gap: 0.7rem; }
.skill__tile {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  border-radius: var(--r);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
  color: var(--accent);
  font-size: var(--t-ui);
  transition: background 0.25s, border-color 0.25s, transform 0.25s var(--ease);
}
.skill:hover .skill__tile {
  background: color-mix(in srgb, var(--accent) 20%, transparent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  transform: rotate(-6deg) scale(1.06);
}
.skill__name { font-weight: 700; font-size: var(--t-title); letter-spacing: -0.02em; }
.skill__note { font-size: var(--t-xs); color: var(--fg-3); line-height: 1.55; }

/* the bar takes the room that is left, the figure sits at the end of it */
.skill__foot { display: flex; align-items: flex-end; gap: 0.8rem; }
.skill__bar {
  position: relative;
  flex: 1;
  height: 6px;
  margin-bottom: 0.35rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--fg) 10%, transparent);
  overflow: hidden;
}
.skill__fill {
  display: block;
  width: 0;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, color-mix(in srgb, var(--accent) 55%, transparent), var(--accent));
  transition: width 1.2s var(--ease);
}
.skill.is-on .skill__fill { width: var(--pct); }

/* a sheen that crosses the filled part once it has arrived */
.skill.is-on .skill__fill::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--fg) 28%, transparent), transparent);
  animation: skill-sheen 2.6s 1.1s ease-in-out infinite;
}
@keyframes skill-sheen { from { transform: translateX(-100%); } to { transform: translateX(100%); } }

.skill__pct {
  font-family: var(--mono);
  font-size: var(--t-lead);
  font-weight: 800;
  line-height: 1;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.03em;
}
.skill__pct small { font-size: 0.6em; margin-left: 0.06em; color: var(--fg-3); font-weight: 400; }

@media (prefers-reduced-motion: reduce) {
  .skill, .skill__tile { transition: none; }
  .skill:hover { transform: none; }
  .skill:hover .skill__tile { transform: none; }
  .skill__fill { transition: none; }
  .skill.is-on .skill__fill::after { animation: none; }
}

/* -- come and join the server ---------------------------------------------- *
 * Deliberately not Discord's own widget iframe: that would hand every visitor
 * to discord.com and mean opening frame-src in the content security policy.
 * The name, icon and counts are read server side and the icon is served from
 * this domain like everything else.                                          */
.joinup {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.4rem 1.15rem;
  margin-top: 1.75rem;
  padding: 1.35rem 1.5rem;
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
  border-radius: var(--r);
  background: var(--panel);
  overflow: hidden;
  transition: border-color 0.2s, transform 0.2s var(--ease), box-shadow 0.2s;
}
.joinup:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--accent) 55%, var(--line));
  box-shadow: 0 16px 40px -24px color-mix(in srgb, var(--accent) 95%, transparent);
}

/* a slow warm sweep behind the card, so it reads as an invitation */
.joinup__glow {
  position: absolute;
  /* Wider than the card it sits in, and still. It used to drift sideways, which
     walked its own edge into view every few seconds — a hard line through the
     warmth, right beside the badge. The card clips it, so the extra width costs
     nothing and there is no edge left to see. */
  inset: -40% -25%;
  background: radial-gradient(45% 60% at 32% 50%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 70%);
  pointer-events: none;
}

.joinup__badge {
  position: relative;
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  flex-shrink: 0;
  border-radius: var(--r);
  overflow: hidden;
  background: color-mix(in srgb, var(--accent) 14%, var(--panel-2));
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
}
.joinup__badge img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* impeccable-disable-next-line design-system-font-size -- the fallback mark, sized to its tile */
.joinup__badge i { font-size: 1.9rem; color: var(--accent); }

.joinup__say { position: relative; display: grid; gap: 0.3rem; }
.joinup__say h2 { font-size: var(--t-title); letter-spacing: -0.02em; }
.joinup__say p { font-size: var(--t-sm); color: var(--fg-2); }
.joinup__count { display: flex; align-items: center; gap: 0.45rem; font-size: var(--t-xs); color: var(--fg-3); }
.joinup__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--good, #2f9c7c);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--good, #2f9c7c) 70%, transparent);
  animation: joinup-pulse 2.4s ease-out infinite;
}
@keyframes joinup-pulse {
  70% { box-shadow: 0 0 0 7px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
.joinup__sep { color: var(--line-2, var(--line)); }
.joinup__go { position: relative; white-space: nowrap; }

@media (max-width: 620px) {
  .joinup { grid-template-columns: auto minmax(0, 1fr); }
  .joinup__go { grid-column: 1 / -1; justify-content: center; margin-top: 0.4rem; }
}
@media (prefers-reduced-motion: reduce) {
  .joinup, .joinup__dot { animation: none; transition: none; }
  .joinup:hover { transform: none; }
}

/* -- paging the wall of reviews -------------------------------------------- */
.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin-top: 1.25rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line);
}
.pager__where { font-size: var(--t-sm); color: var(--fg-3); font-variant-numeric: tabular-nums; }

/* -- framing the portrait -------------------------------------------------- *
 * The picture is zoomed and shifted inside the hexagon with object-fit and
 * object-position, so nothing is re-encoded and the original file is never
 * touched: three numbers are saved and applied on the way out.               */
/* the outline of the cut, shown only while it is being adjusted, so the shape
   can be lined up against the frame without guessing where the edge falls */
.clipguide {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: none;
  clip-path: var(--portrait-clip);
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  pointer-events: none;
}
.meshot__portrait.is-framing .clipguide { display: block; }

/* the control only ever renders for a signed-in administrator */
.framer__open {
  position: absolute;
  top: 4%;
  right: 4%;
  z-index: 3;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  color: var(--accent);
  cursor: pointer;
  backdrop-filter: blur(4px);
  transition: background 0.18s, transform 0.18s var(--ease), border-color 0.18s;
}
.framer__open:hover, .framer__open[aria-expanded='true'] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-fg, #1a1205);
  transform: scale(1.06);
}

/* In normal flow beneath the portrait rather than floating over it: sitting on
   the picture hid the very thing being adjusted, and put the buttons under the
   decoration layer where they could not be clicked. */
.framer {
  width: 100%;
  margin-top: 0.9rem;
  padding: 0.9rem 1rem 1rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  background: var(--panel-2);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.5);
  text-align: left;
  animation: who-in 0.16s var(--ease);
}
.framer[hidden] { display: none; }
.framer__title { font-weight: 700; font-size: var(--t-ui); display: flex; align-items: center; gap: 0.5rem; }
.framer__title i { color: var(--accent); }
.framer__hint { font-size: var(--t-xs); color: var(--fg-3); margin: 0.25rem 0 0.8rem; line-height: 1.45; }
.framer__row { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 0.15rem 0.5rem; margin-bottom: 0.6rem; }
.framer__row > span { font-size: var(--t-xs); color: var(--fg-2); }
.framer__row output { font-family: var(--mono); font-size: var(--t-xs); color: var(--accent); font-variant-numeric: tabular-nums; }
.framer__row input[type='range'] { grid-column: 1 / -1; width: 100%; accent-color: var(--accent); }
.framer__do { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.9rem; }
.framer__said { font-size: var(--t-xs); color: var(--good, #2f9c7c); margin-top: 0.6rem; min-height: 1em; }
.framer__said[data-bad] { color: var(--bad, #e5675e); }

/* while framing, the picture answers the mouse */
/* the picture is dimmed while the cut is being placed, so the outline reads */
.meshot__portrait.is-framing .meshot__frame img { opacity: 0.72; }

@media (prefers-reduced-motion: reduce) {
  .framer__open { transition: none; }
  .framer__open:hover { transform: none; }
  .framer { animation: none; }
}

/* -- the site's own video player -------------------------------------------- *
 * Self hosted and self drawn. Nothing is embedded, so no third party learns
 * who watched what, and the controls match the rest of the site rather than
 * whatever the browser ships. The <video> keeps its `controls` attribute as a
 * fallback; the script removes it once it has taken over.                     */
.player {
  position: relative;
  margin-block: 1.4rem;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: #000;
  overflow: hidden;
  /* The shape of the videos this site carries, used until the video itself
     says otherwise — site.js sets the real ratio from the file the moment its
     dimensions are known, so a player never letterboxes whatever it is given. */
  aspect-ratio: 1920 / 1000;
}
.player__video { display: block; width: 100%; height: 100%; object-fit: contain; background: #000; }

/* The browser's own furniture, turned off. Chrome draws a pill of its own
   buttons over a playing video — picture in picture, skip forward — and it does
   it whether or not the element is showing controls, so removing the `controls`
   attribute is not enough on its own. The attributes on the element handle the
   buttons themselves; these handle the enclosures they sit in, which is what
   was landing in the top corner of a card. */
.player__video::-webkit-media-controls,
.player__video::-webkit-media-controls-enclosure,
.player__video::-webkit-media-controls-overlay-enclosure,
.player__video::-webkit-media-controls-panel,
.player__video::-webkit-media-controls-start-playback-button,
.player__video::-webkit-media-controls-overlay-play-button,
.player__video::-internal-media-controls-overlay-cast-button { display: none !important; }

/* Full screen is the one place the frame around the player costs something: a
   1px border makes the picture box 1918 wide on a 1920 screen, so every frame
   is resampled to fit and the whole thing goes soft. Nothing to draw, nothing
   to round, and the video lands on the pixels it was made for. */
.player:fullscreen { border: 0; border-radius: 0; margin: 0; }

/* Full screen is the one place a card's video must not be cropped. A card fills
   its box with the picture — `cover` — because the box is cut to the shape of
   the videos this site carries. A screen is not: a 1920x1000 recording scaled
   to cover a 1920x1080 screen runs off both sides, and the sides are where the
   interface being demonstrated lives. Fit it and take the two black bands; the
   whole point of going full screen is to see the thing whole.

   Written as separate rules on purpose. One unknown selector invalidates a
   whole selector list, and not every browser knows every spelling of this. */
.player:fullscreen .player__video { object-fit: contain; }
.player:-webkit-full-screen .player__video { object-fit: contain; }

/* the big button over an unplayed video */
.player__big {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.35));
  cursor: pointer;
  transition: opacity 0.2s;
}
/* Invisible once it is playing, but still there and still taking the pointer.
   That is deliberate: it is the only reliable way to stop the browser drawing
   its own pill of buttons over a playing video. Chrome puts one up — picture in
   picture, skip forward — whenever the pointer is over the <video> itself, and
   it does that whether or not the element is showing controls, so attributes
   and ::-webkit- rules only reach some of it. A sheet over the video means the
   pointer is never on the video, so there is nothing to draw a pill for.

   It stays under the control bar, which is written after it and so wins the hit
   test, and clicking the sheet is a play or a pause exactly as clicking the
   picture was. */
.player.is-playing .player__big { opacity: 0; }
.player__big-ring {
  display: grid;
  place-items: center;
  width: 68px;
  height: 68px;
  border-radius: 50%;
  background: var(--accent-bg);
  color: var(--accent-fg);
  /* impeccable-disable-next-line design-system-font-size -- play glyph sized to its disc */
  font-size: 1.4rem;
  padding-left: 4px;
  box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.8);
  transition: transform 0.18s var(--ease);
}
.player__big:hover .player__big-ring { transform: scale(1.07); }

/* the control bar, which fades out while a video plays untouched */
.player__bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.6rem 0.7rem;
  background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.78));
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.2s, transform 0.2s;
}
.player:hover .player__bar,
.player:focus-within .player__bar,
.player:not(.is-playing) .player__bar { opacity: 1; transform: none; }

.player__btn {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  border-radius: var(--radius-sm, 4px);
  color: #fff;
  font-size: var(--t-sm);
  cursor: pointer;
  transition: color 0.15s, background 0.15s;
}
.player__btn:hover { color: var(--accent); background: rgba(255, 255, 255, 0.12); }

.player__time {
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: #d8dee6;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}

/* the seek bar: buffered underneath, played on top, a knob on the end */
.player__seek {
  position: relative;
  flex: 1;
  height: 16px;
  display: flex;
  align-items: center;
  cursor: pointer;
  touch-action: none;
}
.player__seek::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  height: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.22);
}
.player__buffered, .player__played {
  position: absolute;
  left: 0;
  height: 4px;
  border-radius: 999px;
  pointer-events: none;
}
.player__buffered { width: 0; background: rgba(255, 255, 255, 0.32); }
.player__played { width: 0; background: var(--accent); }
.player__knob {
  position: absolute;
  left: 0;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--accent);
  transform: translateX(-50%) scale(0);
  transition: transform 0.15s;
  pointer-events: none;
}
.player__seek:hover .player__knob,
.player__seek:focus-visible .player__knob { transform: translateX(-50%) scale(1); }

.player__vol { display: flex; align-items: center; gap: 0.25rem; }
.player__volrange {
  width: 0;
  opacity: 0;
  accent-color: var(--accent);
  transition: width 0.2s, opacity 0.2s;
}
.player__vol:hover .player__volrange,
.player__vol:focus-within .player__volrange { width: 68px; opacity: 1; }

/* the quality menu, drawn only when the video exists in more than one size */
.player__quality { position: relative; flex-shrink: 0; }
.player__btn--wide { width: auto; padding-inline: 0.4rem; gap: 0.3rem; }
.player__qnow {
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
}
.player__menu {
  position: absolute;
  right: 0;
  bottom: calc(100% + 0.45rem);
  z-index: 2;
  min-width: 112px;
  padding: 0.25rem;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--radius-sm, 4px);
  background: rgba(11, 13, 17, 0.95);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
}
.player__menuitem {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  width: 100%;
  padding: 0.34rem 0.5rem;
  border-radius: var(--radius-sm, 4px);
  color: #c9d1da;
  font-size: var(--t-xs);
  text-align: left;
  transition: color 0.15s, background 0.15s;
}
.player__menuitem:hover { color: #fff; background: rgba(255, 255, 255, 0.1); }
.player__menuitem .fa-check { font-size: 0.68em; opacity: 0; }
.player__menuitem[aria-checked="true"] { color: #fff; }
.player__menuitem[aria-checked="true"] .fa-check { opacity: 1; color: var(--accent); }

@media (max-width: 560px) {
  .player__vol .player__volrange { display: none; }
  .player__bar { gap: 0.4rem; padding: 0.5rem; }
}
@media (prefers-reduced-motion: reduce) {
  .player__big, .player__big-ring, .player__bar, .player__knob, .player__volrange { transition: none; }
  .player__menuitem { transition: none; }
  .player__big:hover .player__big-ring { transform: none; }
}

/* -- header files under a description --------------------------------------- *
 * CommonDefines.h and Locale_Inc.h, printed as read-only code. The comments
 * are the point: they are where each switch is explained, so they are marked
 * rather than dimmed away.                                                    */
.heads { margin-top: 2.6rem; }
.heads__note { font-size: var(--t-sm); color: var(--fg-3); margin-bottom: 1rem; }

.code { border: 1px solid var(--line); border-radius: var(--r); background: var(--panel); overflow: hidden; }
.code + .code { margin-top: 0.9rem; }
.code__head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 0.95rem;
  font-family: var(--mono);
  font-size: var(--t-sm);
  color: var(--fg-2);
  cursor: pointer;
  list-style: none;
  border-bottom: 1px solid transparent;
}
.code__head::-webkit-details-marker { display: none; }
.code[open] .code__head { border-bottom-color: var(--line); }
.code__head .fa-file-code { color: var(--accent); }
.code__caret { font-size: 0.7em; color: var(--fg-3); transition: transform 0.15s; }
.code[open] .code__caret { transform: rotate(90deg); }
.code__count { margin-left: auto; font-size: var(--t-xs); color: var(--fg-3); }

.code__body {
  margin: 0;
  padding: 0.75rem 0;
  max-height: 460px;
  overflow: auto;
  font-size: var(--t-sm);
  line-height: 1.5;
  counter-reset: line;
}
/* four, the width a header file is usually written to */
.code__body code { display: block; font-family: var(--mono); tab-size: 4; }
.code__line { display: block; padding: 0 1rem 0 0.9rem; white-space: pre; }
.code__line::before {
  counter-increment: line;
  content: counter(line);
  display: inline-block;
  width: 2.4em;
  margin-right: 1.1rem;
  text-align: right;
  color: var(--fg-3);
  opacity: 0.5;
  user-select: none;
}
.code__line:hover { background: color-mix(in srgb, var(--fg) 4%, transparent); }

.tok--comment { color: var(--fg-3); font-style: italic; }
.tok--pre { color: var(--accent); }
.tok--name { color: var(--fg); font-weight: 600; }
.tok--str { color: var(--ok); }
.tok--num { color: var(--accent-soft); }

@media (prefers-reduced-motion: reduce) {
  .code__caret { transition: none; }
}
