/* Planar Archive
   ------------------------------------------------------------------------
   One stylesheet for the whole console, served under a name that contains a
   hash of its contents so it is fetched once and then never again.

   The look is a card binder under a desk lamp: a dark room, gold leaf off a
   card frame, and the five colours of mana used as what they actually are -
   data. A pip is not decoration on this site; it is the colour identity of a
   deck or a card, and it is the one thing on a row you can read at a glance.

   The dark theme is the real one. The light theme is the same desk with the
   curtains open - it exists because somebody will open this on a phone in a
   game shop, not because the archive has a daytime.

   Tokens first, components second. Anything a page wants to look different
   about should be a variable here rather than a style attribute there. */

/* ---------------------------------------------------------------- tokens */

:root {
  color-scheme: dark;

  --bg: #0a0910;
  --bg-glow: rgba(217, 164, 65, 0.07);
  --surface: #12101b;
  --raised: #191624;
  --sunken: #0c0a13;

  --text: #ece7f7;
  --muted: #9a92ae;
  --faint: #665e7a;
  --line: rgba(190, 170, 230, 0.13);
  --line-strong: rgba(190, 170, 230, 0.28);

  /* The gold off a card frame. */
  --accent: #d9a441;
  --accent-strong: #f0c877;
  --accent-text: #1a1204;
  --accent-weak: rgba(217, 164, 65, 0.12);
  --accent-glow: 0 0 18px rgba(217, 164, 65, 0.28);

  /* The five, plus colourless. These are data, not decoration. */
  --w: #f6ecc9;
  --u: #57a5e8;
  --b: #9b7fc4;
  --r: #e2604a;
  --g: #56b36e;
  --c: #b3a99b;

  --w-ink: #3a3008;
  --u-ink: #04182c;
  --b-ink: #1a1026;
  --r-ink: #2c0703;
  --g-ink: #04240f;
  --c-ink: #211d18;

  --ok: #56d18d;
  --ok-weak: rgba(86, 209, 141, 0.11);
  --warn: #e8b552;
  --warn-weak: rgba(232, 181, 82, 0.11);
  --danger: #ef6a58;
  --danger-weak: rgba(239, 106, 88, 0.12);

  --font-display: "Cinzel", "Iowan Old Style", Georgia, serif;
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  --r-sm: 4px;
  --r-md: 8px;
  --r-lg: 12px;

  --bar-h: 58px;
}

/* The same desk with the curtains open. The mana colours darken rather than
   lighten here: a pip has to hold its meaning against white as well as
   against black, and the pale ones vanish otherwise. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;

    --bg: #f2eee6;
    --bg-glow: rgba(217, 164, 65, 0.1);
    --surface: #fffdf8;
    --raised: #f8f4ea;
    --sunken: #ebe5d9;

    --text: #221d2e;
    --muted: #5f586c;
    --faint: #8d8699;
    --line: rgba(60, 40, 90, 0.14);
    --line-strong: rgba(60, 40, 90, 0.26);

    --accent: #9a6c12;
    --accent-strong: #7d5608;
    --accent-text: #fffdf8;
    --accent-weak: rgba(154, 108, 18, 0.1);
    --accent-glow: 0 0 14px rgba(154, 108, 18, 0.18);

    --w: #b39a48;
    --u: #1f6bb5;
    --b: #6a4f9c;
    --r: #bf3a24;
    --g: #2c8049;
    --c: #7d7367;

    --w-ink: #fffdf8;
    --u-ink: #ffffff;
    --b-ink: #ffffff;
    --r-ink: #ffffff;
    --g-ink: #ffffff;
    --c-ink: #ffffff;

    --ok: #1e7a4a;
    --ok-weak: rgba(30, 122, 74, 0.1);
    --warn: #8a6208;
    --warn-weak: rgba(138, 98, 8, 0.1);
    --danger: #b33326;
    --danger-weak: rgba(179, 51, 38, 0.1);
  }
}

/* ------------------------------------------------------------------ base */

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  background:
    radial-gradient(900px 500px at 80% -10%, var(--bg-glow), transparent 70%),
    var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

body.centred {
  display: grid;
  place-items: center;
  padding: 24px 16px;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: 0.01em;
  margin: 0;
}

h1 { font-size: 26px; }
h2 { font-size: 19px; }
h3 { font-size: 16px; }

p { margin: 8px 0; }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-strong); text-decoration: underline; }

.mono { font-family: var(--font-mono); font-size: 0.92em; }
.small { font-size: 13px; }
.muted { color: var(--muted); }
.faint { color: var(--faint); }
.right { text-align: right; }
.nowrap { white-space: nowrap; }

.kicker {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 600;
}

.lede { color: var(--muted); }

/* An icon that never arrives - no kit configured, or a blocked cdn - must
   leave no gap. Every control is labelled in words as well. */
.ico { margin-right: 7px; width: 1em; text-align: center; }
.no-icons .ico { display: none; }

/* ------------------------------------------------------------------- bar */

.bar {
  position: sticky;
  top: 0;
  z-index: 40;
  height: var(--bar-h);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}

.bar nav {
  max-width: 1360px;
  margin: 0 auto;
  height: 100%;
  padding: 0 18px;
  display: flex;
  align-items: center;
  gap: 4px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 9px;
  color: var(--text);
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 700;
  margin-right: 14px;
  white-space: nowrap;
}

.brand:hover { text-decoration: none; color: var(--accent-strong); }
.brand .pips { display: flex; gap: 2px; }

.tab {
  padding: 7px 11px;
  border-radius: var(--r-md);
  color: var(--muted);
  font-size: 14px;
  white-space: nowrap;
}

.tab:hover { background: var(--raised); color: var(--text); text-decoration: none; }
.tab.on { background: var(--accent-weak); color: var(--accent-strong); }

.bar .spacer { flex: 1; }

.who {
  font-size: 13px;
  color: var(--muted);
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 7px;
}

.role {
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 2px 6px;
  border-radius: 999px;
  background: var(--accent-weak);
  color: var(--accent);
}

/* ---------------------------------------------------------------- layout */

.page {
  max-width: 1360px;
  margin: 0 auto;
  padding: 26px 18px 80px;
}

.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}

.page-head p { color: var(--muted); max-width: 66ch; }

.head-actions { display: flex; gap: 8px; flex-wrap: wrap; }

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  margin: 26px 0 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line);
}

.grid { display: grid; gap: 14px; }
.grid.two { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid.three { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.grid.four { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }

.scroll { overflow-x: auto; }

/* ----------------------------------------------------------------- panel */

.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 16px 18px;
}

.panel.gold { border-color: color-mix(in srgb, var(--accent) 34%, var(--line)); }
.panel.sunken { background: var(--sunken); }

.gate {
  width: 100%;
  max-width: 420px;
  text-align: center;
  padding: 30px 26px;
}

.gate .pips { justify-content: center; margin-bottom: 14px; }
.gate form { margin-top: 14px; }

/* A figure and its label: the dashboard and every totals strip. */
.fact {
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 12px 14px;
}

.fact .label {
  font-size: 11px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
}

.fact .figure {
  font-family: var(--font-display);
  font-size: 25px;
  font-weight: 700;
  line-height: 1.25;
}

.fact .note { font-size: 12px; color: var(--faint); }
.fact.warn .figure { color: var(--warn); }
.fact.ok .figure { color: var(--ok); }

/* ----------------------------------------------------------------- forms */

.field { text-align: left; margin-bottom: 12px; }

.field label {
  display: block;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 5px;
}

input[type="text"], input[type="email"], input[type="url"], input[type="number"],
input[type="date"], input[type="search"], select, textarea {
  width: 100%;
  padding: 9px 11px;
  background: var(--sunken);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
}

textarea { resize: vertical; min-height: 64px; }

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-weak);
}

input::placeholder { color: var(--faint); }

input[type="checkbox"] {
  width: 17px;
  height: 17px;
  accent-color: var(--accent);
  cursor: pointer;
}

.row-form {
  display: flex;
  gap: 10px;
  align-items: flex-end;
  flex-wrap: wrap;
}

.row-form .field { margin-bottom: 0; flex: 1 1 180px; }
.row-form .field.narrow { flex: 0 0 120px; }
.row-form .field.wide { flex: 2 1 280px; }

.check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--muted);
}

/* --------------------------------------------------------------- buttons */

button, .button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 9px 14px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: var(--accent);
  color: var(--accent-text);
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}

button:hover, .button:hover {
  background: var(--accent-strong);
  text-decoration: none;
  color: var(--accent-text);
}

button:disabled { opacity: 0.5; cursor: not-allowed; }

button.ghost, .button.ghost {
  background: transparent;
  border-color: var(--line-strong);
  color: var(--text);
}

button.ghost:hover, .button.ghost:hover { background: var(--raised); color: var(--text); }

button.danger {
  background: transparent;
  border-color: color-mix(in srgb, var(--danger) 45%, transparent);
  color: var(--danger);
}

button.danger:hover { background: var(--danger-weak); color: var(--danger); }

button.block, .button.block { width: 100%; margin-top: 6px; }

button.small, .button.small { padding: 5px 9px; font-size: 13px; }

button.link {
  background: none;
  border: none;
  color: var(--muted);
  font-weight: 500;
  padding: 6px 8px;
}

button.link:hover { background: var(--raised); color: var(--text); }

button.icon {
  padding: 6px 8px;
  background: transparent;
  border-color: var(--line);
  color: var(--muted);
}

button.icon .ico { margin: 0; }
button.icon:hover { background: var(--raised); color: var(--text); }

/* Without a kit the icon-only buttons have to say their words instead, or
   the row becomes a line of empty boxes. */
.no-icons button.icon { font-size: 12px; padding: 5px 8px; }
.no-icons button.icon::after { content: attr(aria-label); }

/* --------------------------------------------------------------- banners */

.banner {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 11px 14px;
  border-radius: var(--r-md);
  background: var(--danger-weak);
  border: 1px solid color-mix(in srgb, var(--danger) 35%, transparent);
  color: var(--text);
  margin-bottom: 16px;
  font-size: 14px;
}

.banner.warn { background: var(--warn-weak); border-color: color-mix(in srgb, var(--warn) 35%, transparent); }
.banner.ok { background: var(--ok-weak); border-color: color-mix(in srgb, var(--ok) 35%, transparent); }
.banner.info { background: var(--accent-weak); border-color: color-mix(in srgb, var(--accent) 32%, transparent); }
.banner .ico { margin-top: 2px; }
.banner a { text-decoration: underline; }

.empty {
  padding: 34px 20px;
  text-align: center;
  color: var(--muted);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-lg);
}

.empty .big { font-size: 28px; color: var(--faint); margin-bottom: 8px; }

/* ------------------------------------------------------------------ pips */

/* A mana pip. The five colours are the one piece of this interface that
   carries meaning by colour alone, so each also has its letter in it - a pip
   read by somebody who cannot tell the red one from the green one still says
   R or G. */
.pip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 17px;
  height: 17px;
  border-radius: 50%;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  border: 1px solid rgba(0, 0, 0, 0.25);
}

.pips { display: inline-flex; gap: 3px; vertical-align: middle; }

.pip.W { background: var(--w); color: var(--w-ink); }
.pip.U { background: var(--u); color: var(--u-ink); }
.pip.B { background: var(--b); color: var(--b-ink); }
.pip.R { background: var(--r); color: var(--r-ink); }
.pip.G { background: var(--g); color: var(--g-ink); }
.pip.C { background: var(--c); color: var(--c-ink); }

.pip.big { width: 22px; height: 22px; font-size: 12px; }

/* ---------------------------------------------------------------- badges */

.badge {
  display: inline-block;
  padding: 2px 7px;
  border-radius: 999px;
  font-size: 11px;
  letter-spacing: 0.06em;
  background: var(--raised);
  border: 1px solid var(--line);
  color: var(--muted);
  white-space: nowrap;
}

.badge.foil {
  /* Foils are the one thing on this site allowed a gradient, because that is
     what a foil looks like and it is instantly readable in a list. */
  background: linear-gradient(110deg, #9b7fc4, #57a5e8, #56b36e, #e8b552, #e2604a);
  color: #100c18;
  border-color: transparent;
  font-weight: 700;
}

.badge.etched { background: linear-gradient(110deg, #b08d4d, #e8d9a8, #b08d4d); color: #2a1f06; border-color: transparent; font-weight: 700; }
.badge.gold { background: var(--accent-weak); border-color: color-mix(in srgb, var(--accent) 40%, transparent); color: var(--accent); }
.badge.ok { background: var(--ok-weak); border-color: color-mix(in srgb, var(--ok) 40%, transparent); color: var(--ok); }
.badge.warn { background: var(--warn-weak); border-color: color-mix(in srgb, var(--warn) 40%, transparent); color: var(--warn); }
.badge.danger { background: var(--danger-weak); border-color: color-mix(in srgb, var(--danger) 40%, transparent); color: var(--danger); }
.badge.self { background: var(--accent-weak); color: var(--accent); border-color: transparent; }

/* ---------------------------------------------------------------- tables */

table { width: 100%; border-collapse: collapse; font-size: 14px; }

thead th {
  text-align: left;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
  padding: 0 10px 8px;
  border-bottom: 1px solid var(--line-strong);
  white-space: nowrap;
}

tbody td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}

tbody tr:hover { background: var(--raised); }
tbody tr.gone { opacity: 0.45; }

tfoot td {
  padding: 10px;
  border-top: 2px solid var(--line-strong);
  font-weight: 600;
}

th.num, td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------ collection */

.cardrow { display: flex; align-items: center; gap: 10px; min-width: 0; }

.cardrow .art {
  width: 34px;
  height: 47px;
  border-radius: 3px;
  object-fit: cover;
  background: var(--sunken);
  border: 1px solid var(--line);
  flex: none;
}

.cardrow .who-what { min-width: 0; }
.cardrow .name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cardrow .sub { font-size: 12px; color: var(--muted); }

/* The full card, shown on hover over a thumbnail. It is fixed rather than
   absolute so a table that scrolls sideways cannot clip it. */
.peek {
  position: fixed;
  z-index: 60;
  width: 244px;
  border-radius: 11px;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.6);
  pointer-events: none;
  display: none;
}

.peek.on { display: block; }

.qty {
  font-family: var(--font-mono);
  font-weight: 700;
  min-width: 2.5em;
  display: inline-block;
}

.where {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--muted);
}

.where .ico { margin: 0; }
.where.none { color: var(--faint); font-style: italic; }

.filters {
  display: flex;
  gap: 10px;
  align-items: flex-end;
  flex-wrap: wrap;
  margin-bottom: 14px;
}

.filters .field { margin-bottom: 0; }
.filters .field.grow { flex: 1 1 220px; }

/* --------------------------------------------------------- the card find */

/* The search box that finds a printing to add. Results drop under it and are
   walked with the arrow keys. */
.finder { position: relative; }

.hits {
  position: absolute;
  z-index: 50;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  /* Twelve results at the old row height came to nearly 700px, which on a
     phone is the whole screen and on the deck page covered the deck. The
     list is now short enough to read past and never takes more than half of
     what is in front of you. */
  max-height: min(276px, 46vh);
  overflow-y: auto;
  background: var(--raised);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.5);
  display: none;
}

.hits.on { display: block; }

/* One result is one line: the name, and the set pushed to the far end. The
   second line it used to take was what made the list twice as tall as it
   needed to be. */
.hit {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 4px 9px;
  cursor: pointer;
  border-bottom: 1px solid var(--line);
}

.hit:last-child { border-bottom: none; }
.hit:hover, .hit.on { background: var(--accent-weak); }
.hit img, .hit .hit-noart {
  width: 22px;
  height: 30px;
  border-radius: 3px;
  object-fit: cover;
  flex: none;
  background: var(--sunken);
}
.hit > div {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex: 1 1 auto;
  min-width: 0;
}
.hit .name {
  font-weight: 600;
  font-size: 13.5px;
  flex: 0 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hit .sub {
  font-size: 11px;
  color: var(--faint);
  margin-left: auto;
  flex: none;
  white-space: nowrap;
}

.printing {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  margin-bottom: 6px;
  cursor: pointer;
}

/* A ruling is a paragraph to be read, not a row to be scanned, so it is set
   as one: full measure, a little room around it, and the date underneath
   where it does not interrupt the sentence. */
.ruling {
  padding: 9px 12px;
  border-left: 2px solid var(--line-strong);
  margin-bottom: 8px;
}
.ruling:last-child { margin-bottom: 0; }
.ruling .small { line-height: 1.55; }
.ruling .faint { margin-top: 4px; font-size: 11px; }

/* The pane that opens under a card in a list to change its printing and its
   finish. It sits inside whatever is listing the card - a table row on the
   collection, a face-up card on a deck page - so it claims no width of its
   own and its list of versions is capped rather than pushing the page about. */
.cardpane { margin: 8px 0 4px; }
.cardpane .field { margin-bottom: 8px; }
.cardpane input[type="search"] { width: 100%; }

.pane-printings {
  max-height: 240px;
  overflow-y: auto;
  margin-bottom: 10px;
}

.pane-foot {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  flex-wrap: wrap;
}

.pane-foot .pane-finish {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-right: auto;
}

.printing:hover { border-color: var(--accent); background: var(--accent-weak); }
.printing.on { border-color: var(--accent); background: var(--accent-weak); }
.printing img { width: 32px; height: 44px; border-radius: 3px; object-fit: cover; flex: none; }

/* ----------------------------------------------------------------- decks */

.deck {
  display: block;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 15px 16px;
  color: var(--text);
}

.deck:hover { border-color: var(--accent); text-decoration: none; color: var(--text); }
.deck.retired { opacity: 0.55; }

.deck .top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.deck .name { font-family: var(--font-display); font-size: 17px; font-weight: 700; }
.deck .meta { font-size: 13px; color: var(--muted); margin-top: 3px; }
.deck .numbers { display: flex; gap: 14px; margin-top: 11px; font-size: 13px; }
.deck .numbers b { font-family: var(--font-mono); color: var(--text); }

/* ----------------------------------------------------------- tournaments */

.record {
  font-family: var(--font-mono);
  font-weight: 700;
  white-space: nowrap;
}

.record .w { color: var(--ok); }
.record .l { color: var(--danger); }
.record .d { color: var(--muted); }

/* A win rate as a bar behind the number. It is the column the eye goes down,
   and a bar makes a run of good and bad evenings visible without reading a
   single figure. */
.rate {
  position: relative;
  display: block;
  min-width: 74px;
  padding: 2px 6px;
  border-radius: var(--r-sm);
  background: var(--sunken);
  font-variant-numeric: tabular-nums;
  text-align: right;
  overflow: hidden;
}

.rate .fill {
  position: absolute;
  inset: 0 auto 0 0;
  background: color-mix(in srgb, var(--ok) 28%, transparent);
}

.rate.poor .fill { background: color-mix(in srgb, var(--danger) 26%, transparent); }
.rate.even .fill { background: color-mix(in srgb, var(--warn) 26%, transparent); }
.rate span { position: relative; }

/* --------------------------------------------------------------- selling */

.ladder td { padding: 6px 10px; }
.ladder input[type="number"] { width: 100%; max-width: 110px; text-align: right; }
.ladder tr.curated td:first-child { border-left: 3px solid var(--accent); }
.ladder tr.standard td:first-child { border-left: 3px solid var(--line-strong); }
.ladder .per { font-family: var(--font-mono); font-size: 12px; color: var(--muted); }

.gap.over { color: var(--ok); }
.gap.under { color: var(--danger); }

/* ----------------------------------------------------------------- toast */

.toasts {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 80;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: min(380px, calc(100vw - 32px));
}

.toast {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 11px 14px;
  border-radius: var(--r-md);
  background: var(--raised);
  border: 1px solid var(--line-strong);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45);
  cursor: pointer;
  animation: rise 0.16s ease-out;
}

@keyframes rise { from { transform: translateY(8px); opacity: 0; } }

.toast .title { font-weight: 600; font-size: 14px; }
.toast .body { font-size: 13px; color: var(--muted); }
.toast.ok { border-color: color-mix(in srgb, var(--ok) 45%, transparent); }
.toast.error { border-color: color-mix(in srgb, var(--danger) 45%, transparent); }
.toast.busy { cursor: default; }

.spin {
  width: 14px;
  height: 14px;
  margin-top: 3px;
  border-radius: 50%;
  border: 2px solid var(--line-strong);
  border-top-color: var(--accent);
  animation: turn 0.7s linear infinite;
  flex: none;
}

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

@media (prefers-reduced-motion: reduce) {
  .toast, .spin { animation: none; }
}

/* ----------------------------------------------------------------- users */

table.people td:first-child { font-family: var(--font-mono); }
table.people select { max-width: 240px; }

/* -------------------------------------------------------------- the sync */

.sync-state { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

.dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--faint);
  flex: none;
}

.dot.ready { background: var(--ok); box-shadow: 0 0 8px var(--ok); }
.dot.loading { background: var(--warn); }
.dot.missing { background: var(--danger); }

/* ------------------------------------------------------------ the dialog */

.sheet {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: none;
  place-items: center;
  padding: 18px;
  background: rgba(4, 3, 8, 0.66);
}

.sheet.on { display: grid; }

.sheet .panel {
  width: 100%;
  max-width: 560px;
  max-height: calc(100vh - 36px);
  overflow-y: auto;
  background: var(--surface);
}

/* The add form is the one sheet somebody sits in rather than passes through:
   a card, a version chosen out of ninety, a count, a price, a place. At the
   ordinary width the printings are a keyhole to read them through, so this
   one gets the room. It is still capped, because a form as wide as a monitor
   is a form whose labels are nowhere near its boxes. */
.sheet.wide .panel { max-width: 720px; }

.sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}

.sheet-foot {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 16px;
  flex-wrap: wrap;
}

.sheet-foot .grow { margin-right: auto; }

/* ----------------------------------------------------------- deck view */

/* A deck is read as pictures, because that is how it is played. The grid
   below is the whole of the difference between this page and a spreadsheet,
   and it is built for a phone first: no fixed widths, no sideways scroll,
   and controls big enough to hit with a thumb.

   Note the curve's columns are .col and not .bar - .bar is already the
   navigation strip at the top of every page. */

.shape { display: grid; gap: 14px; margin-bottom: 20px; }
.shape .grid.four { margin: 0; }
.shape .panel { padding: 14px 16px 12px; }

.section-head.tight { margin: 0 0 10px; padding: 0; border: 0; }
.section-head.tight h2 { font-size: 14px; }

/* The curve. Heights arrive as a --h custom property set by the page's own
   script, so the markup carries no inline style and this file still owns
   what a column looks like. */
.bars { display: flex; align-items: flex-end; gap: 6px; height: 108px; }
.bars .col {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  height: 100%;
  min-width: 0;
}
.bars .n { font-size: 11px; color: var(--muted); font-family: var(--font-mono); line-height: 1; height: 12px; }
.bars .fill {
  width: 100%;
  height: var(--h, 0%);
  min-height: 2px;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  background: linear-gradient(180deg, var(--accent-strong), var(--accent));
  transition: height 180ms ease;
}
.bars .mv { font-size: 11px; color: var(--faint); font-family: var(--font-mono); line-height: 1; }

/* Colour demand: one row per colour, the bar drawn in that colour. */
.demands { display: grid; gap: 7px; }
.demand-row { display: grid; grid-template-columns: 18px 1fr auto; align-items: center; gap: 9px; }
.demand-row .track { height: 8px; border-radius: 99px; background: var(--sunken); overflow: hidden; }
.demand-row .track .fill { display: block; height: 100%; width: var(--h, 0%); border-radius: 99px; transition: width 180ms ease; }
.demand-row .fill.W { background: var(--w); }
.demand-row .fill.U { background: var(--u); }
.demand-row .fill.B { background: var(--b); }
.demand-row .fill.R { background: var(--r); }
.demand-row .fill.G { background: var(--g); }

/* The bracket panel. It arrives after the page, so everything in here is
   built by the deck page's script rather than rendered by the template. */
.insight .sub-head {
  margin: 16px 0 7px;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The estimate itself: the number, large, and what it means beside it. */
.rung { display: grid; grid-template-columns: auto 1fr; align-items: center; gap: 14px; }
.rung .n {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: var(--r-md);
  background: var(--accent-weak);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  color: var(--accent-strong);
  font-family: var(--font-display);
  font-size: 24px;
}
.rung .name { font-family: var(--font-display); font-size: 17px; }

/* Why it came out there: one row per rule, with the cards that broke it. */
.why { display: grid; gap: 7px; }
.reason {
  padding: 8px 10px;
  border-radius: var(--r-sm);
  background: var(--sunken);
  border-left: 2px solid color-mix(in srgb, var(--warn) 55%, transparent);
}
.reason .what { font-size: 13px; margin-bottom: 2px; }

/* EDHREC's spread of other people's decks. */
.spread { display: grid; gap: 5px; }
.rung-bar { display: grid; grid-template-columns: 14px 1fr 38px; align-items: center; gap: 9px; }
.rung-bar .track { height: 7px; border-radius: 99px; background: var(--sunken); overflow: hidden; }
.rung-bar .fill { display: block; height: 100%; border-radius: 99px; background: var(--faint); }
/* The rung this deck was estimated at, picked out of the others. */
.rung-bar.on .fill { background: var(--accent); }
.rung-bar.on .small { color: var(--accent-strong); }

.suggest { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  padding: 4px 9px;
  border-radius: 99px;
  background: var(--raised);
  border: 1px solid var(--line);
  font-size: 12px;
}

/* Picture or list. */
.modes { display: inline-flex; border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
.mode {
  border: 0;
  background: transparent;
  color: var(--muted);
  padding: 6px 11px;
  cursor: pointer;
  font-size: 13px;
}
.mode:hover { color: var(--text); background: var(--raised); }
.mode.on { background: var(--accent-weak); color: var(--accent-strong); }

/* ----------------------------------------------------- adding to a deck */

/* The add box is a box, not a bar. Left at full width its dropdown became a
   page-wide slab that covered the deck the moment anybody typed, which is
   the opposite of what a box you are meant to use over and over should do. */
.addbar { margin: 0 0 18px; }
.addbar .finder { max-width: 380px; }
.addbar .finder input { width: 100%; }
.addbar .addhint { margin: 6px 0 0; max-width: 56ch; }
.addbar kbd {
  font-family: var(--font-mono);
  font-size: 11px;
  padding: 1px 5px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--raised);
  color: var(--muted);
}

/* ------------------------------------------------------------ the stacks */

/* A deck is shown the way a deck is laid out on a table: one pile per kind
   of card, each pile fanned so that every card shows its name and the one at
   the bottom is face up. Opening a card turns it face up in place. */
.deckview {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(226px, 1fr));
  gap: 18px;
  align-items: start;
}

.stack { min-width: 0; }

.stack-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 7px;
  padding-bottom: 5px;
  border-bottom: 1px solid var(--line);
}
.stack-head h3 {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.stack-head h3 .ico { margin-right: 6px; color: var(--faint); }
.stack-head .tally { color: var(--text); }
.stack-head .worth { font-size: 11px; }

.pile { display: flex; flex-direction: column; gap: 2px; }

.slab { min-width: 0; }
.slab.busy { opacity: 0.55; pointer-events: none; }

/* The name strip. It is a button because opening a card is an action, and
   because that is what makes the pile walkable with a keyboard. */
.strip {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 0 6px;
  height: 30px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--tint, var(--c));
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--tint, var(--c)) 13%, var(--raised));
  color: var(--text);
  font: inherit;
  font-size: 12.5px;
  text-align: left;
  cursor: pointer;
}
.strip:hover { border-color: var(--line-strong); background: color-mix(in srgb, var(--tint, var(--c)) 24%, var(--raised)); }
.slab.open > .strip { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }

.strip .name {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.strip .qty { flex: none; font-size: 11px; color: var(--muted); min-width: 12px; }
.strip .pips { flex: none; }
.strip .pip { width: 14px; height: 14px; font-size: 8px; }

/* Foil, etched and proxy marks ride in the strip rather than on the art, so
   they are visible on a card that is face down. */
.strip .mark { flex: none; font-size: 10px; line-height: 1; }
.strip .mark.foil { color: var(--accent-strong); }
.strip .mark.etched { color: var(--b); }
.strip .mark.proxy { font-family: var(--font-mono); font-size: 10px; color: var(--muted); }

/* The tints. Colour identity, which is what a card is in a deck. */
.strip.tW { --tint: var(--w); }
.strip.tU { --tint: var(--u); }
.strip.tB { --tint: var(--b); }
.strip.tR { --tint: var(--r); }
.strip.tG { --tint: var(--g); }
.strip.tC { --tint: var(--c); }
.strip.tM { --tint: var(--accent); }

/* The face-up half. Hidden until a card is opened, and always open for the
   card at the bottom of the pile. */
.slab .body {
  display: none;
  padding: 7px 6px 8px;
  border: 1px solid var(--line);
  border-top: 0;
  border-radius: 0 0 var(--r-sm) var(--r-sm);
  background: var(--raised);
}
.slab.open .body, .pile .slab:last-child .body { display: block; }

.slab .face { position: relative; aspect-ratio: 488 / 680; }
.slab .face img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 4.75% / 3.5%;
  display: block;
  background: var(--sunken);
}
/* A card with no scan still has to be a card-shaped thing, or the pile goes
   ragged the moment the index is cold. */
.slab .noart {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  padding: 10px;
  border: 1px dashed var(--line-strong);
  border-radius: 4.75% / 3.5%;
  background: var(--sunken);
  font-size: 12px;
  color: var(--muted);
  text-align: center;
}
.slab .body .sub { margin-top: 5px; font-size: 10px; }

.steps { display: flex; align-items: center; gap: 2px; margin-top: 6px; }
.step {
  flex: 1 1 auto;
  min-width: 0;
  height: 30px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--muted);
  border-radius: var(--r-sm);
  cursor: pointer;
  font-size: 11px;
}
.step:hover { color: var(--text); border-color: var(--line-strong); }
.step.out:hover { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, transparent); }
.steps .n {
  flex: 0 0 26px;
  text-align: center;
  font-size: 12px;
  color: var(--text);
}

/* The list cut of the same markup: one column, every card face down, so a
   long deck is a thing you can scan in one screen. The toggle is a change of
   density rather than a change of information - every card is still named,
   counted and costed. */
.deckview.list { grid-template-columns: 1fr; gap: 16px; }
.deckview.list .pile .slab:last-child .body { display: none; }
.deckview.list .slab.open .body { display: block; max-width: 226px; }

/* ------------------------------------------------------- small screens */

@media (max-width: 760px) {
  .page { padding: 18px 14px 70px; }

  .bar nav { padding: 0 12px; gap: 2px; overflow-x: auto; }
  .brand .word { display: none; }
  .who { display: none; }
  .tab { padding: 7px 9px; font-size: 13px; }

  h1 { font-size: 22px; }

  .grid.four, .grid.three { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }

  .fact .figure { font-size: 21px; }

  /* A table of a collection cannot usefully shrink, so it scrolls instead of
     wrapping into something unreadable. */
  .scroll table { min-width: 620px; }

  .peek { display: none !important; }

  .toasts { left: 12px; right: 12px; max-width: none; }

  /* The piles stack one under the next rather than scrolling sideways. A
     deck is read top to bottom on a phone, which is where most of them get
     built. */
  .deckview { grid-template-columns: 1fr; gap: 16px; }
  .slab .body, .deckview.list .slab.open .body { max-width: 240px; }

  /* Sized for a thumb here, not a cursor. */
  .strip { height: 36px; font-size: 13.5px; }
  .step { height: 34px; }
  .steps .n { flex-basis: 22px; }

  .addbar .finder { max-width: none; }

  .bars { height: 86px; }
  .shape { gap: 12px; }
}
