/* Grand Master Checklist — visual reproduction of the printed poster.
   Colours are sampled straight from the poster so the page reads as the same artefact. */

:root {
  --page:      #fdfcf8;
  --ink:       #16181d;
  --ink-soft:  #313338;
  /* 4.9:1 on the card, 4.8:1 on the page — it carries the card numbers and
     every meta line, so it has to clear AA rather than merely look quiet */
  --num:       #767060;
  --rule:      #dcd8cc;
  --sym-ring:  rgba(0, 0, 0, .20);
  --card:      #ffffff;

  --std:   #6c6d70;
  --rev:   #1e3f8f;
  --holo:  #cc7a18;
  --ex:    #c8262d;
  --ir:    #2f7a4f;
  --ur:    #6b3f9e;
  --sir:   #1e3f8f;
  --hr:    #b08618;
  --cosmos:#1e3f8f;
  --hd:    #4d9fd8;
  --sparkle:#c8266b;
  --stamp: #c8262d;
  --jumbo: #cc7a18;

  --bar:     #16181d;
  --bar-ink: #ffffff;
  --veil:    255, 255, 255;
  --pattern: url("images/pattern.jpg");
  --pattern-filter: none;
  --invert-ink:  none;
  --invert-snow: invert(1);
  --flip-tone:   none;

  --display: "Archivo Narrow", "Arial Narrow", "Helvetica Neue", Impact, system-ui, sans-serif;
  --body: "Segoe UI", Roboto, -apple-system, Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  padding: 18px;
  background: var(--page);
  color: var(--ink);
  font-family: var(--body);
  font-size: 13px;
  -webkit-text-size-adjust: 100%;
}

/* ---------------------------------------------------------------- banner */

.banner {
  position: relative;
  display: flex;
  align-items: center;
  gap: 26px;
  padding: 18px 26px;
  border: 2px solid var(--ink);
  border-radius: 12px;
  background: var(--card);
  overflow: hidden;
}
/* the confetti runs edge to edge like the poster, on its own layer so the dark
   theme can flip it, under a veil that keeps the text side legible */
.banner::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--pattern, none) repeat;
  filter: var(--pattern-filter);
}
.banner::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
    rgba(var(--veil),.94) 0%, rgba(var(--veil),.92) 46%, rgba(var(--veil),.62) 72%,
    rgba(var(--veil),.86) 88%, rgba(var(--veil),.94) 100%);
}
.banner > * { position: relative; z-index: 1; }

/* 168px of logo and 38px of heading are a poster at 1600 and half a phone
   screen at 390 — the header stood 522px tall there, ahead of the first card
   of 471. Both are held at their poster size down to ~1120, where the banner
   folds anyway, and give way from there. */
.logo {
  flex: 0 0 auto;
  width: clamp(104px, 15vw, 168px);
  height: auto;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.18));
}

.banner-text { flex: 1 1 auto; min-width: 0; }
.eyebrow {
  margin: 0 0 2px;
  font-family: var(--display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ex);
}
.banner h1 {
  margin: 0 0 6px;
  font-family: var(--display);
  font-size: clamp(26px, 3.4vw, 38px);
  line-height: 1;
  font-weight: 900;
  letter-spacing: -.005em;
  text-transform: uppercase;
}

.banner-progress {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
}
.tally {
  font-family: var(--display);
  font-size: 30px;
  font-weight: 900;
  line-height: 1;
  letter-spacing: -.01em;
}
.tally-sep { color: var(--rule); margin: 0 2px; }
#tally-total { color: var(--num); }
.meter {
  width: 150px;
  height: 7px;
  border: 1.5px solid var(--ink);
  border-radius: 99px;
  overflow: hidden;
  background: var(--page);
}
.meter i {
  display: block;
  height: 100%;
  width: 0;
  background: var(--ex);
  transition: width .18s ease;
}
/* Three readings of one number, kept together so the eye runs count → bar →
   share without leaving the block. The share is not decoration: 236 / 467 is
   two numbers to divide, and half the reason the bar is there. */
.gauge { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; }
.meter-row { display: flex; align-items: center; gap: 8px; }
.pct {
  font-family: var(--display);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--num);
  min-width: 32px;
  text-align: right;
}

.modes, .langs, .pickers { display: flex; gap: 4px; }
.themes { display: flex; gap: 4px; }
/* Every chip in the banner answers to the keyboard the same way. Only the four
   tools used to say so, which left the scope, the two languages and the theme
   with whatever ring the browser draws over a dark chip — on the chosen one,
   nothing you could see. */
.mode:focus-visible, .theme:focus-visible, .lang:focus-visible,
.uilang:focus-visible, .rail-top:focus-visible {
  outline: 2px solid var(--rev);
  outline-offset: 1px;
}
/* the pencil the custom scope wears once it is the scope in force */
.mode-edit { margin-left: 5px; font-size: 10px; letter-spacing: 0; opacity: .8; }
.theme {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: var(--card);
  border: 1.5px solid var(--rule);
  border-radius: 4px;
  padding: 3px 9px;
  cursor: pointer;
}
.theme-glyph { font-size: 13px; line-height: 1; letter-spacing: 0; }
.theme:hover { border-color: var(--ink); color: var(--ink); }
.theme.is-on { background: var(--ink); border-color: var(--ink); color: var(--page); }
.mode {
  font-family: var(--display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: none;
  border: 1.5px solid var(--rule);
  border-radius: 4px;
  padding: 3px 10px;
  cursor: pointer;
}
.mode:hover { border-color: var(--ink); color: var(--ink); }
.mode.is-on { background: var(--ink); border-color: var(--ink); color: var(--card); }

.lang {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .12em;
  color: var(--ink-soft);
  background: var(--card);
  border: 1.5px solid var(--rule);
  border-radius: 4px;
  padding: 3px 9px;
  cursor: pointer;
}
.flag {
  width: 24px;
  height: 16px;
  object-fit: cover;
  border-radius: 2px;
  box-shadow: 0 0 0 1px rgba(0,0,0,.25);
}
.lang:hover { border-color: var(--rev); color: var(--rev); }
.lang.is-on {
  background: var(--rev);
  border-color: var(--rev);
  color: var(--card);
}
/* each figure carries its own reason, so each is its own span */
.missing-bit { cursor: help; }
.missing {
  margin: 0;
  min-height: 14px;
  font-size: 11px;
  font-weight: 600;
  color: var(--num);
}

/* the four tools share one chip, so none of them reads as an afterthought */
.banner-acts { display: flex; flex-wrap: wrap; gap: 5px; }
.banner-acts button {
  font-family: var(--display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: var(--card);
  border: 1.5px solid var(--rule);
  border-radius: 4px;
  padding: 3px 9px;
  cursor: pointer;
  transition: border-color .12s ease, color .12s ease, background-color .12s ease;
}
.banner-acts button:hover { border-color: var(--rev); color: var(--rev); }
.banner-acts button:focus-visible { outline: 2px solid var(--rev); outline-offset: 1px; }
/* the two that change the sheet wholesale carry the warmer edge */
#bulk:hover, #data:hover { border-color: var(--holo); color: var(--holo); }
#reset { background: none; }
#reset:hover { border-color: var(--ex); color: var(--ex); }

/* ------------------------------------------------------------ board grid */

/* the widths come from meta.board, through a custom property rather than an
   inline grid-template — an inline template would outrank the media queries
   below and the board would stop collapsing */
.board-row {
  display: grid;
  gap: 16px;
  margin-top: 16px;
  align-items: start;
  grid-template-columns: var(--cols, 1fr);
}
.stack { display: grid; gap: 16px; align-content: start; }

/* An accent used as text sits on the page rather than on itself, so on a dark
   page it has to be lifted towards the light — in the stylesheet, so it follows
   a theme switch even on a panel that is already open. */
.panel, .modal-card, .give-sec, .bulk-row--head { --accent-text: var(--accent); }
:root[data-theme="dark"] .panel,
:root[data-theme="dark"] .modal-card,
:root[data-theme="dark"] .give-sec,
:root[data-theme="dark"] .bulk-row--head {
  --accent-text: color-mix(in srgb, var(--accent) 52%, #fff);
}

/* --------------------------------------------------------------- panels */

.panel {
  border: 2px solid var(--accent);
  border-radius: 10px;
  background: var(--card);
  overflow: hidden;
  break-inside: avoid;
}
.panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 14px 10px;
  background: var(--accent);
  /* not a theme token: the fill comes from the data and does not flip, so ink
     taken from --card turned every heading near-black on a dark page */
  color: var(--accent-ink, #fff);
}
.panel-head h2 {
  margin: 0;
  min-width: 0;
  font-family: var(--display);
  font-size: 19px;
  line-height: 1.05;
  font-weight: 900;
  letter-spacing: .005em;
  text-transform: uppercase;
}
.head-right {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
}
/* re-orders the panel by where each card came from */
.grouper {
  font-family: var(--display);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--card);
  background: none;
  border: 1.5px solid rgba(255,255,255,.5);
  border-radius: 4px;
  padding: 2px 7px;
  cursor: pointer;
}
.grouper:hover { border-color: var(--card); }
.grouper.is-on { background: var(--card); border-color: var(--card); color: var(--accent-text, var(--accent)); }

.panel-head .count {
  flex: 0 0 auto;
  font-family: var(--display);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .17em;
  text-transform: uppercase;
  white-space: nowrap;
  opacity: .92;
}
.panel-icon {
  height: 20px;
  width: auto;
  margin-left: 8px;
  vertical-align: -4px;
  filter: drop-shadow(0 0 1.5px rgba(255,255,255,.85));
}
.panel-body { padding: 8px 12px 10px; }

/* -------------------------------------------------------- card checklist */

/* the column count comes from the data, through a custom property: an inline
   grid-template would outrank the phone rules below, which is why this one
   used to need !important down there to fight its way back */
.cols { display: grid; gap: 0 18px; grid-template-columns: var(--cols, 1fr); }
.col + .col { border-left: 1px dotted var(--rule); padding-left: 18px; }

.col-head {
  display: grid;
  grid-template-columns: 34px 1fr auto;
  align-items: end;
  gap: 6px;
  padding: 2px 0 4px;
  border-bottom: 2px solid var(--ink);
  font-family: var(--display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.col-head .t-right { text-align: right; }

.row {
  display: grid;
  grid-template-columns: 34px 1fr auto;
  align-items: center;
  gap: 6px;
  padding: 3.5px 0;
  border-bottom: 1px dotted var(--rule);
}

/* name and rarity read as one thing: "Bulbasaur, common" */
.named {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
img.rar {
  flex: 0 0 auto;
  height: 9px;
  width: auto;
  object-fit: contain;
}
.row:last-child { border-bottom: 0; }
.row .no {
  font-family: var(--display);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--num);
}
.row .name {
  font-size: 12.5px;
  line-height: 1.15;
  color: var(--ink);
  /* a long name gives way rather than running into its own boxes:
     four columns of a 165-card panel get tight around 1200px */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.row .variants { display: flex; gap: 5px; justify-content: flex-end; }

/* variant pill = bordered box holding a checkbox + its code */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1.5px 5px 1.5px 4px;
  border: 1.5px solid var(--c);
  border-radius: 4px;
  cursor: pointer;
  user-select: none;
}
.pill span {
  font-family: var(--display);
  font-size: 11px;
  font-weight: 700;
  /* S is a narrower glyph than H — pin both to the wider one so the two
     single-letter pills come out the same size */
  min-width: 9px;
  text-align: center;
  color: var(--c);
}
.pill img { display: block; height: 13px; width: auto; }
.pill--ex img, .pill--rev img { height: 13px; }
.pill--std { --c: var(--std); }
.pill--rev { --c: var(--rev); }
.pill--h   { --c: var(--holo); }
.pill--ex  { --c: var(--ex); }
/* A pill already draws a border round its box and its label, so the checkbox
   drawing its own put two of them 1.5px apart. Transparent rather than removed,
   so the tick keeps the place the border box gave it. */
.pill input[type="checkbox"] {
  border-color: transparent;
  background: transparent;
}
.pill:hover { background: color-mix(in srgb, var(--c) 8%, transparent); }
.pill:has(:checked) { background: color-mix(in srgb, var(--c) 14%, transparent); }

/* -------------------------------------------------------------- checkbox */

input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  width: 13px;
  height: 13px;
  margin: 0;
  border: 1.5px solid var(--c, var(--std));
  border-radius: 2.5px;
  background: var(--card);
  cursor: pointer;
  position: relative;
}
input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  /* the box is 13px over a 1.5px border, so the tick sits in 10px of content —
     two more than it used to, which is one more on each side */
  left: 3.5px;
  top: 0.5px;
  width: 3.5px;
  height: 7.5px;
  border: solid var(--c, var(--ink));
  border-width: 0 2px 2px 0;
  transform: rotate(42deg);
}
input[type="checkbox"]:focus-visible { outline: 2px solid var(--rev); outline-offset: 1px; }

/* --------------------------------------------------------- availability */

/* A card never printed in the selected language stays listed and stays tickable
   — you pick those up while chasing another language's set — but it reads as
   secondary and its ticks land in the "extra" figure, not the set total. */
/* A row the goal does not include recedes, but its text does not go with it.
   Muting the colour and draining the saturation say the same thing that fading
   the whole row said, and stay readable: three of these used to stack — a
   not-printed row inside an out-of-scope panel reached 1.2:1 — and hover was
   the only way back, which is no way at all on a phone.

   The text elements are listed rather than inherited from the row, because
   .row .name and .notes .card set their own colour. */
.is-na:not(:hover) .no, .is-na:not(:hover) .name, .is-na:not(:hover) .text,
.is-na:not(:hover) .code, .is-na:not(:hover) .card,
.is-tbc:not(:hover) .no, .is-tbc:not(:hover) .name, .is-tbc:not(:hover) .text,
.is-tbc:not(:hover) .code, .is-tbc:not(:hover) .card { color: var(--num); }
.is-na:not(:hover) img, .is-na:not(:hover) .key { opacity: .5; }
.is-tbc:not(:hover) img, .is-tbc:not(:hover) .key { opacity: .72; }
.is-na:not(:hover) .pill, .is-tbc:not(:hover) .pill { --c: var(--num); }
.is-na:not(:hover) .badge, .is-tbc:not(:hover) .badge { --b: var(--num); }
.is-na:not(:hover) .dot, .is-tbc:not(:hover) .dot,
.is-na:not(:hover) .type-icon, .is-tbc:not(:hover) .type-icon { filter: grayscale(1); }
.is-na:not(:hover) img, .is-tbc:not(:hover) img { filter: grayscale(1); }
.pill.is-out:not(:hover) img { filter: grayscale(1); }
.is-na:not(:hover) input, .is-tbc:not(:hover) input { --c: var(--num); }

/* same treatment for a box the chosen scope doesn't ask for (a reverse holo
   while collecting a plain set) — greyed, still yours to tick */
.pill.is-out:not(:hover) { --c: var(--num); }
.pill.is-out:not(:hover) img { opacity: .55; }
.pill.is-out:not(:hover):has(:checked) img { opacity: 1; }

/* a whole panel outside the scope recedes rather than disappears — you can
   still read and tick it, and it comes back to full strength on approach */
.panel.is-out {
  filter: grayscale(.85);
  transition: filter .15s ease;
}
/* the panel recedes by losing its colour, not by muting its rows: a row that
   was never printed in this language still has to read differently from one
   that was, and both live inside panels that can be out of scope */
.panel.is-out:not(:hover):not(:focus-within) img,
.panel.is-out:not(:hover):not(:focus-within) .key { opacity: .62; }
.panel.is-out:hover, .panel.is-out:focus-within { filter: none; }

/* owned anyway: bring it back up so it doesn't read as an empty row */
.is-na:not(:hover):has(:checked), .is-tbc:not(:hover):has(:checked) { color: inherit; }
.is-na:not(:hover):has(:checked) img, .is-tbc:not(:hover):has(:checked) img { opacity: 1; }
.is-tbc input[type="checkbox"] { border-style: dashed; }

/* "?" — exists on the poster, availability in this language not settled yet */
.tbc-flag {
  display: inline-block;
  margin-left: 6px;
  vertical-align: 1px;
  font-family: var(--display);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  color: var(--card);
  background: var(--num);
  border-radius: 50%;
  padding: 2px 5px;
}

/* a translation transcribed but not yet checked against a card in hand */
.verify { border-bottom: 1px dotted var(--num); }

/* ----------------------------------------------------------- rarity list */

.row--rarity { grid-template-columns: 14px 34px 1fr auto; }
.col-head--rarity { grid-template-columns: 14px 34px 1fr auto; }

img.badge {
  display: block;
  height: 15px;
  width: auto;
  min-width: 30px;
  object-fit: contain;
  object-position: right center;
}

/* ------------------------------------------------------------ energy list */

/* eight short groups: two columns fill the panel instead of one long list */
/* the type symbol stands in the gutter, the group's rows hang off it */
.dot {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  border: 1.5px solid var(--ink);
  background: var(--d);
}
/* finishes sit inline — five short words don't earn five lines each */
/* the Prize Pack rows are marked by the Play! logo rather than named */
.e-row-logo {
  height: 19px;
  width: auto;
}
.check--inline { align-items: center; gap: 5px; }
.check--inline .text { white-space: nowrap; }

/* --------------------------------------------------------- type symbols */

.type-icon {
  width: 15px;
  height: 15px;
  flex: none;
  vertical-align: -3px;
  border-radius: 50%;
  box-shadow: 0 0 0 1px var(--sym-ring);
}
.type-icon--lg { width: 21px; height: 21px; }
/* inside a sentence: scales with the text it sits in */
.type-icon--text {
  width: 1.15em;
  height: 1.15em;
  vertical-align: -.22em;
  margin: 0 1px;
}
.type-row { display: inline-flex; align-items: center; gap: 3px; }

/* ------------------------------------------------------------- note list */

.notes { margin: 0; padding: 0; list-style: none; }
.notes li { padding: 4.5px 0; border-bottom: 1px dotted var(--rule); }
.notes li:last-child { border-bottom: 0; }
/* the source a run of cards shares, standing in for their meta line */
.notes li.group-head {
  border-bottom: 0;
  padding: 8px 0 2px;
  font-family: var(--display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--accent-text, var(--accent));
}
.notes li:first-child.group-head { padding-top: 2px; }
/* grouped, the rule moves off the rows and onto the seam between sources */
.notes.is-grouped li { border-bottom: 0; }
.notes.is-grouped li.group-head { border-top: 1px dotted var(--rule); }
.notes.is-grouped li.group-head:first-child { border-top: 0; }
.notes .check {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: start;
  gap: 8px;
  width: 100%;
}
.notes.has-code .check { grid-template-columns: auto auto 1fr auto; }
.notes .card { color: var(--ink); }
/* how it was printed, where it came from, what is stamped on it — always in
   that order, so the rows scan down the column even when fields are missing */
.notes .meta { color: var(--num); }
/* the finish reads in its pattern's colour, so "HD holo" and "Cosmos holo"
   are told apart at a glance */
.print--cosmos  { color: var(--cosmos); }
.print--hd      { color: var(--hd); }
.print--sparkle { color: var(--sparkle); }
.notes .meta::before { content: " · "; }
.notes .marks { display: flex; gap: 4px; justify-content: flex-end; }

.mk {
  padding: 0 5px;
  border: 1.5px solid var(--c);
  border-radius: 3px;
  font-family: var(--display);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .06em;
  line-height: 1.5;
  white-space: nowrap;
  color: var(--c);
}
.mk--stamp   { --c: var(--stamp); }
.stamp-art { height: 15px; width: auto; display: block; }
/* the reverse mark sits inside the sentence, so it rides the text baseline */
.rev-art { height: 12px; width: auto; vertical-align: -2px; margin-right: 4px; }
/* the one metal card, set apart from the rest of the line */
.print--metal { color: var(--ink-soft); font-weight: 600; }
/* the flags of the languages a card reached, when it didn't reach them all */
.langs-avail { display: inline-flex; gap: 3px; vertical-align: -1px; }
.flag--sm { width: 15px; height: 10px; }
.notes .code {
  font-family: var(--display);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--num);
  white-space: nowrap;
}
.notes.code-bold .code { color: var(--ink); font-size: 12.5px; }

/* a plain checkbox row (energy / notes) — label wraps the box + text */
.check {
  display: inline-flex;
  align-items: start;
  gap: 7px;
  cursor: pointer;
  line-height: 1.3;
}
.check input { margin-top: 2px; --c: var(--std); }
.check:hover .text { color: var(--ink); }
.check .text { color: var(--ink-soft); font-size: 12.5px; }
li:has(:checked) .text,
li:has(:checked) .code { opacity: .55; text-decoration: line-through; }

/* ---------------------------------------------------------------- legend */

.legend {
  margin-top: 16px;
  border: 2px solid var(--ink);
  border-radius: 10px;
  overflow: hidden;
  background: var(--card);
}
.legend h2 {
  margin: 0;
  padding: 8px;
  text-align: center;
  background: var(--bar);
  color: var(--bar-ink);
  font-family: var(--display);
  font-size: 14px;
  font-weight: 900;
  letter-spacing: .18em;
  text-transform: uppercase;
}
.legend-body {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  padding: 12px 16px;
}
.legend-group { padding: 0 24px; }
.legend-group:first-child { padding-left: 0; }
.legend-group + .legend-group { border-left: 1px solid var(--rule); }
.legend-group h3 {
  margin: 0 0 6px;
  font-family: var(--display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--num);
}
.legend-group ul {
  display: grid;
  grid-template-columns: var(--cols, auto);
  gap: 6px 16px; margin: 0; padding: 0; list-style: none;
}
.legend li { display: flex; align-items: center; gap: 8px; font-size: 12.5px; }
.legend .key {
  flex: 0 0 auto;
  min-width: 34px;
  padding: 1.5px 6px;
  text-align: center;
  border-radius: 3px;
  font-family: var(--display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .1em;
}
/* outlined keys (variant pills) vs filled keys (rarities / tags) */
.key--std { border: 1.5px solid var(--std); color: var(--std); }
.key--rev { border: 1.5px solid var(--rev); color: var(--rev); }
.key--h   { border: 1.5px solid var(--holo); color: var(--holo); }
.key--ex  { border: 1.5px solid var(--ex);  color: var(--ex); }
img.key--art {
  flex: 0 0 auto;
  height: 14px;
  width: auto;
  min-width: 30px;
  object-fit: contain;
  object-position: center;
}

.key--cosmos { border: 1.5px solid var(--cosmos); color: var(--cosmos); }
.key--hd     { border: 1.5px solid var(--hd);     color: var(--hd); }
.key--c   { border: 1.5px solid #b9b5aa;   color: #a8a396; }
.key--u   { border: 1.5px solid var(--std); color: var(--std); }
.key--r   { border: 1.5px solid var(--hr);  color: var(--hr); }
.key--rr  { border: 1.5px solid var(--ex);  color: var(--ex); }
.key--ir  { background: var(--ir);  color: var(--card); }
.key--ur  { background: var(--ur);  color: var(--card); }
.key--sir { background: var(--sir); color: var(--card); }
.key--hr  { background: var(--hr);  color: var(--card); }
.key--sparkle { border: 1.5px solid var(--sparkle); color: var(--sparkle); }
.key--metal   { border: 1.5px solid var(--std); color: var(--std); }
/* a legend chip holding a symbol rather than a letter */
.key:has(img) { display: inline-flex; align-items: center; justify-content: center; padding: 1.5px 5px; }
.key-art { display: block; height: 13px; width: auto; }
.key--stamp { background: var(--stamp); color: var(--card); }
.key--jumbo { background: var(--jumbo); color: var(--card); }

/* ------------------------------------------------------------ responsive */

/* A row of three panels crowds before a row of two does — but folding three
   stacks into a two-column grid puts the third under the first and leaves the
   space beside it empty: 764px of nothing at 1200. Flow the panels themselves
   instead, in the order the board declares them, and let the browser balance
   the columns. */
@media (max-width: 1500px) {
  .board-row--wide {
    display: block;
    column-count: 2;
    column-gap: 16px;
  }
  .board-row--wide > .stack { display: contents; }
  .board-row--wide .panel { break-inside: avoid; margin-bottom: 16px; }
}
@media (max-width: 1100px) {
  .board-row { grid-template-columns: 1fr; }
  .banner { flex-wrap: wrap; }
}
@media (max-width: 860px) {
  .board-row--wide { column-count: 1; }
}

/* 165 rows come in four blocks, and how many sit side by side is a width
   question the data cannot answer. It used to be four at every width above
   760px, which truncated 109 names at 1200 and all 165 at 768.

   The blocks fill DOWN each column, not across: two columns must read 001-042
   then 043-084 on the left, not 001-042 then 085-126, or the numbers stop
   climbing. That is what grid-auto-flow: column buys, and it is why the counts
   below divide four exactly. */
@media (max-width: 1499px) {
  .cols[data-blocks="4"] {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: repeat(2, auto);
    grid-auto-flow: column;
  }
}
/* three blocks fold to two the same way — the odd one sits alone in the second
   column, which is the price of the numbers still climbing down each */
@media (max-width: 1400px) {
  .cols[data-blocks="3"] {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: repeat(2, auto);
    grid-auto-flow: column;
  }
}
/* [data-blocks] and not a bare .cols: the rules above are attribute selectors
   and would outrank it, which left a phone showing two columns and truncating
   164 of the 165 names. */
@media (max-width: 860px) {
  .cols[data-blocks] {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
    grid-auto-flow: row;
  }
  .col + .col { border-left: 0; padding-left: 0; }
}
@media (max-width: 760px) {
  .legend-group ul { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .legend-group { padding: 0; }
  .legend-group + .legend-group { border-left: 0; border-top: 1px solid var(--rule); padding-top: 12px; }
}

@media print {
  body { padding: 0; }
  .banner-progress, .rail { display: none; }
  .panel, .legend, .banner { break-inside: avoid; }
}

/* ------------------------------------------------------------ dark mode --
   Same tokens, restated. An explicit choice wins in either direction; with no
   choice stored the page follows the machine. */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --page: #14161a;  --ink: #e9e7e2;  --ink-soft: #c6c3bc;
    --num: #8a877f;   --rule: #2f333b; --card: #1b1e24;
    --bar: #0e1013;   --bar-ink: #e9e7e2;
    --veil: 20, 22, 26;
    --std: #9aa0a8;  --rev: #6f9ae8;  --holo: #e6a04a; --ex: #ef5b62;
    --ir: #57b07d;   --ur: #a481d8;   --sir: #6f9ae8;  --hr: #d7ab3f;
    --cosmos: #6f9ae8; --hd: #6fbdf0; --stamp: #ef5b62; --jumbo: #e6a04a;
    --sparkle: #ef6a9e;
    --pattern-filter: invert(1) hue-rotate(180deg) brightness(.85);
    --invert-ink: invert(1);
    --invert-snow: none;
    --flip-tone: invert(1) hue-rotate(180deg);
    --sym-ring: rgba(255, 255, 255, .30);
  }
}

:root[data-theme="dark"] {
  --page: #14161a;  --ink: #e9e7e2;  --ink-soft: #c6c3bc;
  --num: #8a877f;   --rule: #2f333b; --card: #1b1e24;
  --bar: #0e1013;   --bar-ink: #e9e7e2;
  --veil: 20, 22, 26;
  --std: #9aa0a8;  --rev: #6f9ae8;  --holo: #e6a04a; --ex: #ef5b62;
  --ir: #57b07d;   --ur: #a481d8;   --sir: #6f9ae8;  --hr: #d7ab3f;
  --cosmos: #6f9ae8; --hd: #6fbdf0; --stamp: #ef5b62; --jumbo: #e6a04a;
    --sparkle: #ef6a9e;
  --pattern-filter: invert(1) hue-rotate(180deg) brightness(.85);
  --invert-ink: invert(1);
  --invert-snow: none;
  --flip-tone: invert(1) hue-rotate(180deg);
  --sym-ring: rgba(255, 255, 255, .30);
}

/* artwork cut for one background gets flipped for the other */
.ink-art  { filter: var(--invert-ink); }
.snow-art { filter: var(--invert-snow); }
.tone-art { filter: var(--flip-tone); }
.is-na:not(:hover) .ink-art, .is-tbc:not(:hover) .ink-art,
.pill.is-out:not(:hover) .ink-art { filter: var(--invert-ink) grayscale(1); }
.is-na:not(:hover) .snow-art, .is-tbc:not(:hover) .snow-art { filter: var(--invert-snow) grayscale(1); }
.is-na:not(:hover) .tone-art, .is-tbc:not(:hover) .tone-art { filter: var(--flip-tone) grayscale(1); }

/* ---------------------------------------------------------- card preview --
   The dataset ships every card as an image; hovering a row shows the one it
   names, so you can check a print without leaving the sheet. */

.preview {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 50;
  width: 248px;
  padding: 8px 8px 6px;
  border: 2px solid var(--ink);
  border-radius: 10px;
  background: var(--card);
  box-shadow: 0 10px 28px rgba(0,0,0,.28);
  pointer-events: none;
}
.preview[hidden] { display: none; }
.preview img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 6px;
}
.preview-cap {
  margin: 6px 2px 0;
  font-size: 11px;
  line-height: 1.3;
  color: var(--ink-soft);
}

/* ------------------------------------------------------------ card detail --
   The number is the handle: hover it for the card, click it for the record. */

.card-key { cursor: zoom-in; border-radius: 3px; }
.card-key:hover, .card-key:focus-visible {
  outline: 1.5px solid var(--accent, var(--ink));
  outline-offset: 1px;
  color: var(--ink);
}
.modal[hidden] { display: none; }
.modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 24px;
}
.modal-back { position: absolute; inset: 0; background: rgba(10,11,14,.6); }
.modal-card {
  position: relative;
  display: grid;
  /* Fluid, not fixed. At 460+400 the card was the same 940px on a 1200 screen
     and on a 2560 one — 1600px of it unused — while a 412px phone got 261px of
     a 380px opening. It scales with the viewport now, leaving room either side
     for the arrows, and stops growing where a card stops being worth enlarging. */
  grid-template-columns: minmax(0, 1.08fr) minmax(340px, 1fr);
  gap: 24px;
  width: min(1160px, 100%);
  max-height: calc(100vh - 48px);
  padding: 20px;
  overflow: auto;
  border: 2px solid var(--ink);
  border-radius: 12px;
  background: var(--card);
  box-shadow: 0 18px 50px rgba(0,0,0,.4);
}
.modal-x {
  position: absolute;
  top: 8px;
  right: 10px;
  font-size: 20px;
  line-height: 1;
  color: var(--num);
  background: none;
  border: 0;
  cursor: pointer;
}
.modal-x:hover { color: var(--ex); }
.modal-pic {
  width: 100%;
  height: auto;
  /* a card is about 1.4 tall for its width, so cap it against the viewport
     rather than letting a tall screenful scroll */
  max-height: calc(100vh - 88px);
  object-fit: contain;
  object-position: top;
  border-radius: 8px;
  align-self: start;
}
.modal-body { min-width: 0; }

.modal-head h2 {
  margin: 0;
  font-family: var(--display);
  font-size: 24px;
  font-weight: 900;
  line-height: 1.05;
}
.modal-sub {
  margin: 3px 0 0;
  font-family: var(--display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--num);
}
.modal-row { margin: 8px 0 0; font-size: 12.5px; color: var(--ink-soft); }
/* the flags double as a language switch for this card only: the one in force
   sits on a solid accent chip, the rest recede */
/* the languages this card was printed in — pick one to see that print */
.modal-flags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 10px 0 0;
}
.lang-pick {
  position: relative;
  display: block;
  padding: 4px;
  line-height: 0;
  background: none;
  border: 1.5px solid transparent;
  border-radius: 7px;
  cursor: pointer;
  transition: border-color .14s ease, background-color .14s ease, transform .14s ease;
}
/* twice the size of the inline availability flags: this one is a control */
.lang-pick .flag {
  width: 30px;
  height: 20px;
  border-radius: 3px;
  opacity: .58;
  filter: grayscale(.5);
  transition: opacity .14s ease, filter .14s ease;
}
.lang-pick:hover { border-color: var(--rule); }
.lang-pick:hover .flag { opacity: 1; filter: none; }
.lang-pick:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* the print on show: accent ring, full colour, lifted off the others */
.lang-pick.is-on {
  border-color: var(--accent);
  background: rgba(128, 128, 128, .10);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  transform: translateY(-1px);
}
.lang-pick.is-on .flag { opacity: 1; filter: none; }
/* no picture of this print in that language: struck through, and inert */
.lang-pick.is-off { cursor: default; }
.lang-pick.is-off:hover { border-color: transparent; }
.lang-pick.is-off .flag,
.lang-pick.is-off:hover .flag { opacity: .22; filter: grayscale(1); }
.lang-pick.is-off::after {
  content: "";
  position: absolute;
  inset: 4px;
  background: linear-gradient(to top right, transparent calc(50% - 1px),
    var(--num) calc(50% - 1px), var(--num) calc(50% + 1px), transparent calc(50% + 1px));
}
/* the card back is the same in every language, so none of them applies */
.modal-flags.is-idle { opacity: .45; }

.modal-facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px 12px;
  margin: 12px 0 0;
  font-size: 12.5px;
}
.modal-facts dt {
  font-family: var(--display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--num);
  align-self: center;
}
.modal-facts dd { margin: 0; color: var(--ink); }

.modal-h3 {
  margin: 14px 0 0;
  font-family: var(--display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--num);
}
.move {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px dotted var(--rule);
}
.move-head { display: flex; align-items: center; gap: 8px; margin: 0; }
.move-cost { gap: 2px; }
.move-cost .type-icon { width: 16px; height: 16px; }
.move-name { font-weight: 700; font-size: 13px; }
.move-dmg {
  margin-left: auto;
  font-family: var(--display);
  font-size: 15px;
  font-weight: 900;
}
.move-text { margin: 3px 0 0; font-size: 12px; line-height: 1.4; color: var(--ink-soft); }
.move--rule_box .move-name { color: var(--ex); }
/* the small print under a Trainer's effect — headless on the card, and
   quieter here too */
.move--reminder { font-size: .92em; }
.move--reminder .move-text { color: var(--num); }
/* A grid, not a wrapping row: a flex line takes the height of its tallest
   figure, and captions here run from one word to two lines. */
.modal-strip {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
  align-items: start;
  gap: 12px 9px;
  margin-top: 8px;
}
.modal-strip figure { margin: 0; cursor: zoom-in; }
.modal-strip figure:hover > img { outline: 2px solid var(--accent); outline-offset: 1px; }
.modal-strip figure.is-on > img { outline: 2px solid var(--accent); outline-offset: 1px; }
.modal-strip figure.is-on figcaption { color: var(--ink); font-weight: 700; }
/* the figure's own thumbnail, and not a symbol inside a caption — as a bare
   .modal-strip img this stretched a 13px reverse-holo mark to 126px and turned
   a two-line caption into twelve */
.modal-strip figure > img { display: block; width: 100%; height: auto; border-radius: 4px; }
.modal-strip figcaption .meta { display: block; color: var(--num); }
.modal-strip figcaption {
  margin-top: 3px;
  font-size: 10px;
  line-height: 1.25;
  color: var(--num);
}
.modal-flavor {
  margin: 12px 0 0;
  font-size: 12px;
  font-style: italic;
  line-height: 1.45;
  color: var(--num);
}

/* Two columns need room for both. Below this the body's 340px floor was
   squeezing the picture to 194px, which is smaller than the thumbnails under
   it. One column instead, and the arrows come off the flex line: in flow they
   are two 34px buttons plus their gaps, which is 103px — a fifth of a tablet
   and two fifths of a phone, taken from the card and given to nothing. */
@media (max-width: 820px) {
  .modal-card {
    grid-template-columns: minmax(0, 1fr);
    width: min(620px, 100%);
    gap: 16px;
  }
}

/* The arrows in flow are two 34px buttons and their gaps — 103px, taken from
   the card and given to nothing. That is a tenth of a laptop and nearly a
   third of a phone, so below a point they come off the line and sit over the
   card instead. .modal > , because the base rule is further down the sheet and
   a bare .modal-nav would lose to it on source order alone. */
@media (max-width: 1000px) {
  .modal > .modal-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    opacity: .82;
  }
  .modal > #modal-prev { left: 4px; }
  .modal > #modal-next { right: 4px; }
}

/* ---------------------------------------------------------- modal extras --
   The panel a card belongs to, and stepping through the sheet without
   leaving the card. */

.modal-panel {
  margin: 0 0 2px;
  font-family: var(--display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--accent-text, var(--accent));
}
.modal-nav {
  position: relative;
  flex: 0 0 auto;
  width: 34px;
  height: 48px;
  font-size: 26px;
  line-height: 1;
  color: var(--num);
  background: var(--card);
  border: 1.5px solid var(--rule);
  border-radius: 6px;
  cursor: pointer;
}
.modal-nav:hover { color: var(--ink); border-color: var(--ink); }
.give .modal-nav { display: none; }

/* ------------------------------------------------------------ contribute --
   What is still missing, and the filename that would fill it. */

.give-card {
  position: relative;
  width: min(760px, 100%);
  max-height: calc(100vh - 48px);
  padding: 20px 22px;
  overflow: auto;
  border: 2px solid var(--ink);
  border-radius: 12px;
  background: var(--card);
  box-shadow: 0 18px 50px rgba(0,0,0,.4);
}
.give-card h2 {
  margin: 0 0 4px;
  font-family: var(--display);
  font-size: 22px;
  font-weight: 900;
  text-transform: uppercase;
}
.give-n {
  margin-left: 8px;
  font-size: 13px;
  color: var(--num);
}
.give-intro { margin: 0 0 6px; font-size: 12px; line-height: 1.5; color: var(--num); }
.give-intro .mail { text-decoration: none; }
.give-sec {
  margin: 16px 0 4px;
  padding-bottom: 3px;
  border-bottom: 1.5px solid var(--accent);
  font-family: var(--display);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--accent-text, var(--accent));
}
.give-row {
  display: grid;
  grid-template-columns: 56px 1fr auto;
  align-items: center;
  gap: 8px;
  padding: 3px 0;
  border-bottom: 1px dotted var(--rule);
  font-size: 12.5px;
}
.give-row:last-child { border-bottom: 0; }
.give-code {
  font-family: var(--display);
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--num);
}
/* the print details read as the checklist's own meta line */
.give-name .card { color: var(--ink); }
.give-name .meta { color: var(--num); }
.give-name .meta::before { content: " · "; }
.give-flags { display: flex; gap: 4px; }
.give-miss { display: block; cursor: help; }

/* ---------------------------------------------------- sheets and questions --
   One panel shell for content, one for decisions. Both reuse the contribute
   card so every dialog in the app reads the same. */

.sheet-acts {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--rule);
}
.act {
  font-family: var(--display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: var(--card);
  border: 1.5px solid var(--rule);
  border-radius: 4px;
  padding: 5px 12px;
  cursor: pointer;
}
.act:hover { border-color: var(--ink); color: var(--ink); }
.act--go { border-color: var(--rev); color: var(--rev); }
.act--go:hover { background: var(--rev); border-color: var(--rev); color: var(--card); }
.act--danger { border-color: var(--rule); color: var(--ink-soft); }
.act--danger:hover { background: var(--ex); border-color: var(--ex); color: var(--card); }
.act--link {
  margin-right: auto;
  border-color: transparent;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.act--tiny { font-size: 9.5px; padding: 2px 8px; letter-spacing: .1em; }
.act:disabled {
  opacity: .35;
  cursor: default;
  border-color: var(--rule);
  color: var(--ink-soft);
  background: var(--card);
}

.ask-card {
  position: relative;
  width: min(420px, calc(100vw - 32px));
  padding: 20px;
  border: 2px solid var(--ink);
  border-radius: 12px;
  background: var(--card);
  box-shadow: 0 18px 50px rgba(0,0,0,.4);
}
.ask-card h2 {
  margin: 0;
  font-family: var(--display);
  font-size: 17px;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: .01em;
}
.ask-text { margin: 8px 0 0; font-size: 13px; line-height: 1.45; color: var(--ink-soft); }
/* a question sits above the panel that asked it — .modal is 100 */
.ask { z-index: 120; }

/* --------------------------------------------------------- mass toggle rows */

.bulk-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: 10px;
  padding: 3px 0 3px 18px;
  font-size: 12.5px;
  border-bottom: 1px dotted var(--rule);
}
/* the panel itself leads its run of variants */
.bulk-row--head {
  padding-left: 0;
  margin-top: 8px;
  border-bottom: 1.5px solid var(--accent);
  font-family: var(--display);
  font-size: 13px;
  font-weight: 900;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.bulk-row--head .bulk-name { color: var(--accent-text, var(--accent)); }
.bulk-name { display: flex; align-items: center; gap: 8px; min-width: 0; }
.bulk-count {
  font-family: var(--display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
  color: var(--num);
  white-space: nowrap;
}
.bulk-acts { display: flex; gap: 5px; }
/* A row whose first cell is a checkbox reads left to right from it: the scope
   sheet put the name in the middle column, which right-aligned every label
   against the count and left the box it belongs to an inch of empty row away. */
.bulk-row--pick { grid-template-columns: auto 1fr auto; }
/* the sheet's own head: what the scope adds up to, and the two clicks that
   build one from either end */
.bulk-row--sum {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-left: 0;
  border-bottom: 1.5px solid var(--rule);
}

/* ------------------------------------------------------------ export/import */

.data-sum { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; }
.data-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 7px;
  border: 1.5px solid var(--rule);
  border-radius: 4px;
  font-family: var(--display);
  font-size: 11px;
  font-weight: 700;
  color: var(--num);
}
.data-chip .flag { opacity: .5; filter: grayscale(.7); }
.data-chip.is-on { border-color: var(--rev); color: var(--rev); }
.data-chip.is-on .flag { opacity: 1; filter: none; }
.data-box {
  display: block;
  width: 100%;
  height: 108px;
  margin: 0;
  padding: 8px 10px;
  border: 1.5px solid var(--rule);
  border-radius: 6px;
  background: var(--page);
  color: var(--ink-soft);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  line-height: 1.5;
  resize: vertical;
}
.data-box:focus { outline: 2px solid var(--rev); outline-offset: 1px; }
/* an export arrives as a file, so the box that takes one says so while it is
   over the drop */
.data-box--drop.is-drop {
  border-color: var(--rev);
  border-style: dashed;
  background: var(--card);
}
/* the file picker was the one control in the app wearing the operating
   system's own button */
.data-file::file-selector-button {
  margin-right: 10px;
  font-family: var(--display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: var(--card);
  border: 1.5px solid var(--rule);
  border-radius: 4px;
  padding: 5px 12px;
  cursor: pointer;
}
.data-file::file-selector-button:hover { border-color: var(--ink); color: var(--ink); }
.data-label { display: block; margin: 6px 0 3px; font-size: 11.5px; color: var(--num); }
.data-file { display: block; margin: 0 0 8px; font-size: 12px; color: var(--ink-soft); }
.data-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 8px 0 4px; }
.data-note { margin: 6px 0 0; min-height: 15px; font-size: 11.5px; color: var(--num); }
.data-note.is-ok { color: var(--ir); font-weight: 600; }
.data-note.is-bad { color: var(--ex); font-weight: 600; }

/* ------------------------------------------------------- format reference -- */

.doc-p { margin: 6px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--ink-soft); }
.doc-code {
  margin: 8px 0 0;
  padding: 10px 12px;
  overflow-x: auto;
  border: 1.5px solid var(--rule);
  border-radius: 6px;
  background: var(--page);
  color: var(--ink);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  line-height: 1.55;
  white-space: pre;
}

/* ------------------------------------------------------------------ contact */

.legend-contact {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
  margin: 0;
  padding: 9px 16px 11px;
  border-top: 1px solid var(--rule);
  font-size: 12px;
  color: var(--num);
}
.legend-contact a, .give-intro .mail {
  color: var(--rev);
  font-weight: 700;
  text-decoration: none;
  border-bottom: 1px dotted currentColor;
}
.legend-contact a:hover, .give-intro .mail:hover { border-bottom-style: solid; }

/* ------------------------------------------------------------- energies

   Eight types across, six printings down. It used to be eight cards of three
   lines each, which spelled every printing's name eight times and ran to
   thirty-two lines. As a grid the names are said once, and availability — a
   property of the printing, not of the type — is one row with one strip of
   flags rather than eight greyed cells that explain nothing. */

.energy-grid {
  display: grid;
  /* the type columns share the width rather than huddling left: parked at
     min-content they left 518px of nothing between the last box and the flags
     on a 2560 screen */
  grid-template-columns:
    auto repeat(var(--cols, 8), minmax(min-content, 1fr)) auto;
  align-items: center;
  /* no gaps: eight columns is a long way for an eye to travel, and the row
     tint that makes that possible has to be continuous to read as one row */
  gap: 0;
  padding: 9px 13px 12px;
}
.e-line:hover > * { background: color-mix(in srgb, var(--ink) 7%, transparent); }
.e-line > * { padding: 4px 0; }
.e-line-label { padding-right: 12px; }
.e-line .e-avail { padding-left: 12px; }
.e-col { margin: 0 2px 4px; }
/* the wrappers exist so the markup still reads as rows; the cells are what
   the grid places */
.e-head, .e-line { display: contents; }

.e-col {
  display: inline-flex;
  justify-content: center;
  padding: 3px 2px;
  border-radius: 4px;
}
.e-col:hover, .e-col:focus-visible {
  background: color-mix(in srgb, var(--ink) 10%, transparent);
}

/* a printing names itself once, at the head of its own block */
.e-group {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 7px;
  font-family: var(--display);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.e-group::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--rule);
}
.e-row-logo { height: 17px; width: auto; }

.e-line-label {
  font-size: 12.5px;
  color: var(--ink-soft);
  white-space: nowrap;
}
.e-line:hover .e-line-label { color: var(--ink); }

/* a printing this language never got. The flags say where it does exist; the
   row has to say plainly that here it does not, and the cells cannot — the
   text that used to carry the greying left them for the header row. */
.e-line.is-off .e-line-label { color: var(--num); }
.e-line.is-off .check:not(:hover) input { --c: var(--num); opacity: .5; }

/* no text in the cell, so the box is the whole control and wants the room */
.check--cell { justify-content: center; }
.check--cell input { margin-top: 0; }

/* the flags are the one part that can give way: eight columns of boxes and
   their labels are the panel, and a phone has no room for both */
.e-avail {
  justify-self: end;
  padding-left: 4px;
  flex-wrap: wrap;
  justify-content: flex-end;
}
.e-corner { min-width: 62px; }

@media (max-width: 620px) {
  .energy-grid { padding: 8px 7px 10px; }
  .e-line-label { padding-right: 5px; }
  .e-line .e-avail { padding-left: 5px; }
  .e-corner { min-width: 0; }
  .e-line-label { font-size: 11.5px; }
  .e-col .type-icon--lg { width: 20px; height: 20px; }
  .energy-grid .flag--sm { width: 13px; height: 9px; }
}
@media (max-width: 430px) {
  .e-col { margin: 0 1px 3px; }
  .e-col .type-icon--lg { width: 17px; height: 17px; }
  .energy-grid .flag--sm { width: 11px; height: 8px; }
  .e-line-label { font-size: 11px; padding-right: 4px; }
  .e-line .e-avail { padding-left: 4px; }
}

/* --------------------------------------------- the same card, elsewhere

   Siblings share the print strip with the row's own printings, so they take
   its figure shape: picture, then what it is underneath. What sets them apart
   is the panel they name — in that panel's colour — because clicking one goes
   there rather than swapping this pane. */
.modal-strip figure.kin { --accent-text: var(--accent); cursor: pointer; }
:root[data-theme="dark"] .modal-strip figure.kin {
  --accent-text: color-mix(in srgb, var(--accent) 52%, #fff);
}
.modal-strip figure.kin > img {
  outline: 1px solid var(--rule);
  outline-offset: 1px;
}
.modal-strip figure.kin:hover > img,
.modal-strip figure.kin:focus-visible > img {
  outline: 2px solid var(--accent-text);
}
.kin-where {
  display: block;
  font-family: var(--display);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--accent-text);
  margin-bottom: 1px;
}
.kin-have {
  display: inline-block;
  margin-top: 2px;
  font-family: var(--display);
  font-size: 10px;
  font-weight: 700;
  color: var(--num);
}
.kin-have.is-all { color: var(--accent-text); }

/* ------------------------------------------------------- language switches

   Two chips side by side: flags for the cards, 文 for the words. Collapsed
   they show only the option in force — nine flags and nine codes would be most
   of the header, and a master set only ever gains languages. The open one gets
   a hairline frame so it reads as a strip of choices rather than a row of
   buttons that suddenly appeared. */

.switch { display: flex; gap: 4px; }

/* [hidden] is a UA rule at specificity zero, and .lang/.uilang set display —
   so a collapsed option stays on screen unless the attribute is spelled into
   the selector. This is the whole reason the switch looked uncollapsible. */
.lang[hidden], .uilang[hidden] { display: none; }

.switch[data-open="true"] {
  border: 1.5px solid var(--rule);
  border-radius: 6px;
  padding: 2px;
  gap: 2px;
}
.switch[data-open="true"] .lang,
.switch[data-open="true"] .uilang { border-color: transparent; }

.uilang {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .12em;
  color: var(--ink-soft);
  background: var(--card);
  border: 1.5px solid var(--rule);
  border-radius: 4px;
  padding: 3px 9px;
  cursor: pointer;
}
/* Every option says what it switches. That is what lets both strips wear
   flags: the word tells them apart, so the flags do not have to — and an
   option still reads on its own once the other strip is off screen. */
.switch-label {
  font-family: var(--display);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  opacity: .6;
  white-space: nowrap;
}
/* Heading an open strip rather than sitting inside a chip, so it needs its own
   vertical alignment and a little air before the first flag. */
.switch-label--strip {
  align-self: center;
  color: var(--ink-soft);
  opacity: .75;
  padding: 0 3px 0 1px;
}
/* Nine options each carrying the word is wider than the banner has to spare,
   so an open strip wraps rather than running off the edge. Wrapping alone is
   not enough: .banner clips what overflows it and .banner-progress was set
   never to shrink, so the strip sized itself to its content and the last
   languages were painted outside the banner where no one could click them.
   It has to be allowed to give way before its children will wrap at all. */
.banner-progress { flex: 0 1 auto; min-width: 0; }
.pickers { flex-wrap: wrap; justify-content: flex-end; }
.switch[data-open="true"] { flex-wrap: wrap; justify-content: flex-end; }
.uilang-link {
  font-size: 12px;
  line-height: 1;
  letter-spacing: 0;
  opacity: .6;
}
.uilang:hover { border-color: var(--rev); color: var(--rev); }
.uilang.is-on { background: var(--rev); border-color: var(--rev); color: var(--card); }
/* Collapsed, a chip is a value and not a pressed button: the only option on
   screen is the one already in force, so painting it "selected" says nothing
   that the absence of alternatives has not already said. It takes the quiet
   chrome and a caret, and keeps the strong state for when the strip is open
   and the highlight is telling you which of nine you are on. */
.switch[data-open="false"] .lang.is-on,
.switch[data-open="false"] .uilang.is-on {
  background: var(--card);
  border-color: var(--rule);
  color: var(--ink-soft);
}
.switch[data-open="false"] .lang.is-on:hover,
.switch[data-open="false"] .uilang.is-on:hover {
  border-color: var(--rev);
  color: var(--rev);
}
.switch[data-open="false"] .lang.is-on::after,
.switch[data-open="false"] .uilang.is-on::after {
  content: "";
  width: 0;
  height: 0;
  border-left: 3.5px solid transparent;
  border-right: 3.5px solid transparent;
  border-top: 4px solid currentColor;
  opacity: .55;
}
/* Collapsed, AUTO is showing a flag it inherited rather than one you chose,
   and a dashed edge is the quietest way to say so without a second word. */
.switch[data-open="false"] .uilang--auto.is-on { border-style: dashed; }

/* ------------------------------------------------------------------ rail --

   The legend's black band, kept in view. It appears only once the banner has
   scrolled out of the page entirely, so nothing on it is ever on screen twice,
   and it carries the same three readings plus the way back up. */

/* `[hidden]` is a UA rule at specificity zero and this sets display, so the
   attribute has to be spelled into a selector of its own — the same reason the
   language switch once looked uncollapsible. */
.rail[hidden] { display: none; }
.rail {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 5px 14px;
  background: var(--bar);
  color: var(--bar-ink);
  border-bottom: 2px solid var(--ink);
  box-shadow: 0 2px 12px rgba(0, 0, 0, .25);
}
.rail-top {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 2px;
  border: 0;
  background: none;
  color: inherit;
  font-family: var(--display);
  font-size: 12px;
  font-weight: 900;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
}
.rail-top:hover .rail-up { transform: translateY(-2px); }
.rail-up { font-size: 14px; line-height: 1; letter-spacing: 0; transition: transform .12s ease; }
.rail-read {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
  min-width: 0;
}
/* the band is one line and never a scrollbar: the name gives way first, the
   three figures are the point of it */
.rail-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-top { min-width: 0; }
.rail-scope {
  font-family: var(--display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  opacity: .72;
  white-space: nowrap;
}
.rail-tally {
  font-family: var(--display);
  font-size: 16px;
  font-weight: 900;
  line-height: 1;
  letter-spacing: -.01em;
  white-space: nowrap;
}
/* --rule is a hairline on paper and all but invisible on the band */
.rail-tally .tally-sep, .rail-tally #rail-total { color: inherit; opacity: .55; }
.meter--rail { width: 110px; height: 6px; border-color: var(--bar-ink); background: transparent; }
.pct--rail { color: inherit; opacity: .8; min-width: 30px; }

/* ------------------------------------------------- the banner, as it folds

   Written after the rules they answer to rather than beside them: a media
   query changes no specificity, so `.banner-progress` up in the switch block
   would have won over the same selector inside one, whatever the width. */

/* Three words for the theme is 234px of a header whose scope and languages
   want the same line. At 1280 they were what pushed the pickers onto a second
   row and the heading onto a third, and the two glyphs are the least ambiguous
   thing in the banner. The name stays on the chip for anyone reading it out. */
@media (max-width: 1400px) {
  .theme-word { display: none; }
  .theme { padding: 3px 8px; }
  .theme-glyph { font-size: 14px; }
}

@media (max-width: 1100px) {
  /* Folded, the control block took its content's width and sat at the left of
     its own line with 400px of empty banner beside it, right-aligned inside a
     box the eye could not see. It is the whole line now — and a line, not a
     column: stacked, its four blocks made the banner 349px tall at 1100, its
     worst width anywhere. */
  .banner-progress {
    flex: 1 0 100%;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 10px 14px;
  }
  .banner-acts { justify-content: flex-end; }
}

@media (max-width: 760px) {
  .banner { gap: 14px; padding: 14px 16px; }
  /* right-aligned chips under a left-aligned heading read as two headers; on a
     phone every row starts where the title starts, and there is no width left
     for two blocks to share a line */
  .banner-progress { flex-direction: column; align-items: stretch; gap: 8px; }
  /* 36 letter-spaced characters of eyebrow are three lines on a 360px screen */
  .eyebrow { font-size: 10px; letter-spacing: .14em; }
  .modes, .pickers, .banner-acts, .switch[data-open="true"] { justify-content: flex-start; }
  .modes, .banner-acts { flex-wrap: wrap; }
  /* The gauge lies down: the count leads, the bar takes the rest of the line,
     the aside sits under both. A grid and not a wrapping flex — a flex line
     sized itself from the bar's flex-basis rather than from anything in it,
     which cost 21px of nothing at all on the shortest screen there is. */
  .gauge {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: 3px 10px;
  }
  .tally { font-size: 26px; }
  .meter-row { min-width: 0; }
  .meter { width: auto; flex: 1 1 auto; }
  .missing { grid-column: 1 / -1; }
  /* two words for a three-way switch is most of a phone line, and the glyph
     already carries it — the name stays on the chip for anyone reading it out */
  .mode { font-size: 10px; padding: 3px 8px; }
  .banner-acts button { font-size: 9.5px; padding: 3px 8px; }
  .rail { gap: 8px; padding: 4px 10px; }
  .rail-top { font-size: 11px; letter-spacing: .1em; }
  .meter--rail { width: 64px; }
  /* the logo and the heading share the phone's first line rather than taking
     one each: a flex basis of auto wraps on content width, whatever min-width
     allows afterwards, and that cost 120px of a 390px screen */
  .banner-text { flex: 1 1 0; }
}

/* three figures, a scope and a name do not fit a 390px band: the scope is the
   one of them the heading behind it repeats */
@media (max-width: 520px) {
  .rail-scope { display: none; }
}
