/* Dicebox — line-work dice on a near-white field.
   The palette is almost entirely value; the one hue is reserved for the total. */

:root {
  --paper:   #FCFCFA;
  --face:    #FFFFFF;
  --line:    #1A1A18;
  --muted:   #9A968C;
  --hair:    #E4E2DC;
  --accent:  #3D5A45;
  --danger:  #8C3A2E;

  --tap: 44px;
  --gut: clamp(16px, 5vw, 28px);
}

:root[data-theme="dark"] {
  --paper:  #141413;
  --face:   #1E1E1C;
  --line:   #F2F0EA;
  --muted:  #6E6A62;
  --hair:   #2A2A27;
  --accent: #8FB79A;
  --danger: #D98878;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:  #141413;
    --face:   #1E1E1C;
    --line:   #F2F0EA;
    --muted:  #6E6A62;
    --hair:   #2A2A27;
    --accent: #8FB79A;
    --danger: #D98878;
  }
}

* { box-sizing: border-box; }

/* The `hidden` attribute is only `display: none` in the UA stylesheet, so any
   class that sets display beats it. Without this, .sheet (display:flex) and
   .help render on load despite being marked hidden — the sheet covered the whole
   page in an unclickable blur. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  overscroll-behavior: none;
}

body {
  background: var(--paper);
  color: var(--line);
  font-family: "Inter Tight", system-ui, -apple-system, sans-serif;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  transition: background 240ms ease, color 240ms ease;
}

.app {
  display: flex;
  flex-direction: column;
  height: 100%;
  max-width: 620px;
  margin: 0 auto;
  padding: env(safe-area-inset-top) var(--gut) calc(env(safe-area-inset-bottom) + 12px);
  gap: 10px;
}

/* ---- header ---- */

.bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 14px;
  flex: 0 0 auto;
}

/* Present on arrival, then out of the way. Once you have rolled, the name has
   done its job and the tray should be the only thing asking for attention. */
.wordmark {
  margin: 0;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
  transition: opacity 1200ms ease;
}
.wordmark[data-faded="1"] { opacity: 0.28; }

/* The active system, beside the name. It sits in the accent because the accent
   is the thing that changed, so the badge names the colour the app is wearing.
   `margin-right: auto` keeps it next to the wordmark instead of being pushed to
   the middle by the bar's space-between. */
.system-badge {
  appearance: none;
  margin: 0 0 0 10px;
  margin-right: auto;
  border: 1px solid var(--accent);
  background: none;
  color: var(--accent);
  font: inherit;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 3px 9px;
  border-radius: 999px;
  cursor: pointer;
}

@media (prefers-reduced-motion: reduce) {
  .wordmark { transition-duration: 1ms; }
}

.bar-actions {
  display: flex;
  align-items: center;
  gap: 6px;
}

.ghost {
  appearance: none;
  border: 1px solid var(--hair);
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  min-height: 32px;
  padding: 0 12px;
  border-radius: 999px;
  cursor: pointer;
}

.ghost.icon {
  width: 32px;
  padding: 0;
  font-size: 14px;
  letter-spacing: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* One icon at a time: the sun in light mode, the moon in dark. */
#themeToggle [data-icon] { display: none; }
#themeToggle[data-mode="light"] [data-icon="sun"] { display: block; }
#themeToggle[data-mode="dark"] [data-icon="moon"] { display: block; }

.ghost[aria-expanded="true"] {
  color: var(--accent);
  border-color: var(--accent);
}

/* ---- help ---- */

/* Fills the tray rather than floating over the page: the tray is already a
   bounded rectangle, so nothing below it shifts when the panel opens. */
/* The syntax reference is a corner popover under its ? button, the same shape
   as the mode picker — not a layer over the tray. Tall content scrolls inside. */
.help {
  position: fixed;
  z-index: 40;
  width: min(400px, calc(100vw - 20px));
  max-height: min(74vh, 600px);
  overflow-y: auto;
  scrollbar-width: thin;
  padding: 12px 16px;
  background: var(--face);
  border: 1px solid var(--hair);
  border-radius: 14px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.28);
  animation: help-in 180ms ease both;
}
.help::-webkit-scrollbar { width: 4px; }

/* The room panel: the same corner popover, under the room button. */
.room-pop {
  position: fixed;
  z-index: 40;
  width: min(340px, calc(100vw - 20px));
  max-height: min(78vh, 620px);
  overflow-y: auto;
  scrollbar-width: thin;
  padding: 12px 16px 14px;
  background: var(--face);
  border: 1px solid var(--hair);
  border-radius: 14px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.28);
  animation: help-in 180ms ease both;
}
.room-pop::-webkit-scrollbar { width: 4px; }

/* A popover's title line — the sheet-title look without its close button. */
.pop-title {
  margin: 0 0 8px;
  font-family: "Iosevka Etoile", ui-monospace, monospace;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

@keyframes help-in {
  from { opacity: 0; transform: translateY(-3px); }
  to   { opacity: 1; transform: none; }
}

.syntax {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 1px 14px;
  margin: 0;
  align-items: baseline;
}

/* Each example is tappable, so the reference doubles as a set of presets. */
.syntax dt {
  font-family: "Iosevka Etoile", ui-monospace, "SF Mono", monospace;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--line);
  text-align: right;
  cursor: pointer;
  border-radius: 4px;
  padding: 1px 3px;
  margin: -1px -3px;
  transition: color 140ms ease;
}
.syntax dt:hover,
.syntax dt:focus-visible { color: var(--accent); }

.syntax dd {
  margin: 0;
  font-size: 12.5px;
  color: var(--muted);
}

.help .note {
  margin: 12px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--muted);
  max-width: 44ch;
}

.help .install {
  margin-top: 10px;
}

.install-button {
  appearance: none;
  border: 1px solid var(--accent);
  border-radius: 8px;
  background: none;
  color: var(--accent);
  font: inherit;
  font-size: 12.5px;
  min-height: 30px;
  padding: 0 12px;
  margin-right: 8px;
  cursor: pointer;
}
.install-button:disabled { opacity: 0.5; }

.help .colophon {
  margin: 10px 0 0;
  font-size: 12px;
}

/* Which build is loaded. Quiet and monospaced — it is a serial number, not
   information anyone reads on purpose, and it only earns its place on the days
   when a cached copy is lying about what it is. */
.build {
  display: block;
  margin-top: 6px;
  font-family: "Iosevka Etoile", ui-monospace, monospace;
  font-size: 10.5px;
  letter-spacing: 0.04em;
  color: var(--muted);
  opacity: 0.65;
}

.colophon-sep {
  color: var(--muted);
  margin: 0 6px;
}

/* The fine print: trademark/affiliation notice and the credits the publishers'
   policies ask for. Quiet, small, and last. */
.disclaimer {
  margin: 12px 0 0;
  font-size: 10.5px;
  line-height: 1.5;
  color: var(--muted);
  opacity: 0.85;
}

.help .colophon a {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid currentColor;
  padding-bottom: 1px;
}

/* ---- tray ---- */

.tray {
  position: relative;
  flex: 1 1 auto;
  min-height: 180px;
  touch-action: none;
}

.tray canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* The one-time intro. Lines fade in one after another, then the whole thing
   leaves — it should read like a title card, not a tooltip. */
.intro {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 7px;
  pointer-events: none;
  text-align: center;
  transition: opacity 700ms ease;
}
.intro[data-gone="1"] { opacity: 0; }

.intro p {
  margin: 0;
  font-size: 13px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
  opacity: 0;
  animation: intro-line 620ms ease forwards;
}
.intro p:nth-child(1) { animation-delay: 260ms; }
.intro p:nth-child(2) { animation-delay: 660ms; }
.intro p:nth-child(3) { animation-delay: 1060ms; }

@keyframes intro-line {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 0.8; transform: none; }
}

.intro-last { color: var(--accent) !important; }

@media (prefers-reduced-motion: reduce) {
  .intro p { animation-duration: 1ms; animation-delay: 0ms; opacity: 0.8; }
}

/* ---- readout ---- */

.readout {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  flex: 0 0 auto;
  /* So a headline made of words has an edge to wrap against rather than the
     width of its own longest line. */
  width: 100%;
  min-width: 0;
}

.total {
  font-family: "Iosevka Etoile", ui-monospace, "SF Mono", monospace;
  font-size: clamp(56px, 19vw, 104px);
  font-weight: 600;
  line-height: 0.92;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  transition: opacity 160ms ease;
}

/* Some systems answer with a phrase rather than a number, and a phrase at the
   numeral's size runs past both edges of a phone and then wraps — onto a second
   line set at 0.92 line-height, which is what put "Bestial Failure" on top of
   itself. Words get their own size and their own leading. */
.total[data-kind="text"] {
  font-family: inherit;
  font-size: clamp(28px, 9vw, 46px);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.01em;
  text-align: center;
  max-width: 100%;
  text-wrap: balance;
}
.total[data-idle="1"] { color: var(--muted); }
.total[data-rolling="1"] { opacity: 0.35; }

.breakdown {
  font-size: 13px;
  color: var(--muted);
  text-align: center;
  min-height: 1.3em;
  font-variant-numeric: tabular-nums;
  word-break: break-word;
}

/* ---- dice picker ---- */

.picker {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}

/* The mode button shows only the active system's mark. */
.mode-ico { display: none; }
#modeToggle[data-system="numeric"] .mode-ico[data-for="numeric"] { display: block; }
#modeToggle[data-system="v5"] .mode-ico[data-for="v5"] { display: block; }
#modeToggle[data-system="fate"] .mode-ico[data-for="fate"] { display: block; }
#modeToggle[data-system="tarot"] .mode-ico[data-for="tarot"] { display: block; }
#modeToggle[data-system="napoletane"] .mode-ico[data-for="napoletane"] { display: block; }
#modeToggle[data-system="hanafuda"] .mode-ico[data-for="hanafuda"] { display: block; }
#modeToggle[data-system="utagaruta"] .mode-ico[data-for="utagaruta"] { display: block; }
#modeToggle[data-system="genesys"] .mode-ico[data-for="genesys"] { display: block; }
#modeToggle[data-system="daggerheart"] .mode-ico[data-for="daggerheart"] { display: block; }
#modeToggle[data-system="cthulhutech"] .mode-ico[data-for="cthulhutech"] { display: block; }
#modeToggle[data-system="starwars"] .mode-ico[data-for="starwars"] { display: block; }
#modeToggle[data-system="onering"] .mode-ico[data-for="onering"] { display: block; }
#modeToggle[data-system="pbta"] .mode-ico[data-for="pbta"] { display: block; }
#modeToggle[data-system="mist"] .mode-ico[data-for="mist"] { display: block; }
#modeToggle[data-system="mothership"] .mode-ico[data-for="mothership"] { display: block; }
#modeToggle[data-system="cards"] .mode-ico[data-for="cards"] { display: block; }
/* A named system's mark carries its own accent so the header reads as that mode
   at a glance; the numeric default stays with the quiet chrome colour. */
#modeToggle[data-system="v5"],
#modeToggle[data-system="fate"],
#modeToggle[data-system="genesys"],
#modeToggle[data-system="daggerheart"],
#modeToggle[data-system="cthulhutech"],
#modeToggle[data-system="starwars"],
#modeToggle[data-system="onering"],
#modeToggle[data-system="pbta"],
#modeToggle[data-system="mist"],
#modeToggle[data-system="mothership"],
#modeToggle[data-system="cards"] { color: var(--accent); }

/* ---- Vampire dice tray ---- */

/* The dice to tap, then a Difficulty chip beneath. */
.v5-picker {
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.v5-dice-row {
  display: flex;
  gap: 10px;
  width: 100%;
}

/* Each die type is a tap-to-add button carrying the ankh — regular in the ink
   colour, Hunger in blood red — with a count badge and a loaded state. */
.v5-die-btn {
  --dc: var(--line);
  flex: 1 1 0;
  min-width: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 12px 8px 9px;
  border: 1px solid var(--hair);
  border-radius: 12px;
  background: none;
  color: var(--dc);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: border-color 140ms ease, background 140ms ease, transform 120ms ease;
}
.v5-die-hunger { --dc: var(--accent); }
.v5-die-name {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.v5-die-btn[data-count] { border-color: var(--dc); background: color-mix(in srgb, var(--dc) 12%, transparent); }
.v5-die-btn[data-count] .v5-die-name { color: var(--line); }
.v5-die-btn[data-count]::after {
  content: attr(data-count);
  position: absolute;
  top: -6px;
  right: -4px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--dc);
  color: var(--paper);
  font: 700 10px system-ui;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: center;
  justify-content: center;
}
.v5-die-btn:active { transform: scale(0.95); }

/* ---- Fate dice ---- */

/* Fate is fixed at 4dF, so the picker is just the modifier chip, centred. */
.fate-picker { justify-content: center; }

/* ---- Genesys dice ---- */

.genesys-picker { padding: 0; }
/* One row of buttons shaped like the real dice — tap to add, hold to remove. */
.gen-chips {
  display: flex;
  gap: 6px;
  width: 100%;
}
.gen-chip {
  --dc: var(--muted);
  flex: 1 1 0;
  min-width: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 9px 2px 6px;
  border: 1px solid var(--hair);
  border-radius: 12px;
  background: none;
  color: var(--dc);
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: border-color 140ms ease, background 140ms ease, transform 120ms ease;
}
/* The die silhouette: a faint fill of its colour under a solid outline. */
.die-ico { width: 27px; height: 27px; display: block; }
.die-ico path, .die-ico rect {
  fill: currentColor;
  fill-opacity: 0.16;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linejoin: round;
}
.gen-chip-label {
  white-space: nowrap;
  font-size: 10.5px;
  color: var(--muted);
  letter-spacing: 0.01em;
}
/* A loaded chip lights up in its die's colour, its silhouette fills, and a count
   badge rides the corner. */
.gen-chip[data-count] { border-color: var(--dc); background: color-mix(in srgb, var(--dc) 12%, transparent); }
.gen-chip[data-count] .gen-chip-label { color: var(--line); }
.gen-chip[data-count] .die-ico path, .gen-chip[data-count] .die-ico rect { fill-opacity: 0.34; }
.gen-chip[data-count]::after {
  content: attr(data-count);
  position: absolute;
  top: -6px;
  right: -4px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--dc);
  color: var(--paper);
  font: 700 10px system-ui;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: center;
  justify-content: center;
}
.gen-chip:active { transform: scale(0.94); }

/* The die-type hues, constant across light and dark (matched to the on-tray
   dice in app.js GEN_COLORS). */
.gen-chip[data-type="ability"]     { --dc: #4E9E60; }
.gen-chip[data-type="proficiency"] { --dc: #C39A2E; }
.gen-chip[data-type="boost"]       { --dc: #4C86C6; }
.gen-chip[data-type="difficulty"]  { --dc: #8A5CC0; }
.gen-chip[data-type="challenge"]   { --dc: #C24046; }
.gen-chip[data-type="setback"]     { --dc: #7C828A; }
.gen-chip[data-type="force"]       { --dc: #AEB4BC; }

/* The Force chip belongs to Star Wars only — hidden until that mode adds it. */
.genesys-picker:not(.with-force) .gen-chip-force { display: none; }

/* A standalone die-add button — the Genesys rack's look, sized to sit inline
   rather than fill a row. Used where a pool is one die type (CthulhuTech d10,
   Fate dF, the Vampire pair, a One Ring Success die). */
.die-btn {
  --dc: var(--muted);
  position: relative;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 10px 18px 7px;
  border: 1px solid var(--hair);
  border-radius: 12px;
  background: none;
  color: var(--dc);
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: border-color 140ms ease, background 140ms ease, transform 120ms ease;
}
.die-btn .die-ico { width: 30px; height: 30px; }
.die-btn-label { font-size: 11px; color: var(--muted); white-space: nowrap; }
.die-btn[data-count] { border-color: var(--dc); background: color-mix(in srgb, var(--dc) 12%, transparent); }
.die-btn[data-count] .die-btn-label { color: var(--line); }
.die-btn[data-count] .die-ico path, .die-btn[data-count] .die-ico rect { fill-opacity: 0.34; }
.die-btn[data-count]::after {
  content: attr(data-count);
  position: absolute;
  top: -6px;
  right: -4px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--dc);
  color: var(--paper);
  font: 700 10px system-ui;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: center;
  justify-content: center;
}
.die-btn:active { transform: scale(0.94); }

/* Tap-to-cycle chip for a single number (Difficulty, Power, Target): tap to
   raise, hold to lower — the die buttons' gesture, for the non-dice controls. */
.spin-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  padding: 10px 16px;
  border: 1px solid var(--hair);
  border-radius: 999px;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: border-color 140ms ease, transform 120ms ease;
}
.spin-chip-label { color: var(--muted); letter-spacing: 0.01em; }
.spin-chip-val { color: var(--line); font-size: 16px; font-variant-numeric: tabular-nums; }
.spin-chip-val[data-unset="1"] { color: var(--muted); font-size: 12.5px; }
.spin-chip.is-set { border-color: color-mix(in srgb, var(--accent) 55%, var(--hair)); }
.spin-chip:active { transform: scale(0.96); }

/* CthulhuTech: one d10 to build the pool, Difficulty as a subordinate chip. */
.ct-picker { display: flex; gap: 14px; justify-content: center; align-items: center; }
.ct-picker .die-btn { --dc: #57B591; }

/* PbtA / Mist: nothing to build (the 2d6 sit in the tray), so just the one chip. */
.twod6-picker { display: flex; justify-content: center; }

/* ---- Daggerheart ---- */

.dh-picker {
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
}
/* Three column buttons: the Advantage and Disadvantage d6 adders flank the
   Duality roll — a split gold/violet d12, accent-filled so it reads as the
   action rather than another adder. */
.dh-adv-row { display: flex; align-items: stretch; gap: 8px; }
.dh-adv-btn, .dh-roll {
  flex: 1 1 0;
  min-width: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 9px 4px 6px;
  border: 1px solid var(--hair);
  border-radius: 12px;
  background: none;
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: border-color 140ms ease, background 140ms ease, transform 120ms ease;
}
.dh-adv-btn { --dc: var(--muted); color: var(--dc); }
.dh-adv-btn[data-adv="adv"] { --dc: #4E9E60; }
.dh-adv-btn[data-adv="dis"] { --dc: #C24046; }
.dh-adv-label, .dh-roll-label { font-size: 10.5px; color: var(--muted); white-space: nowrap; }
/* The adder die with its direction chevrons — up adds a d6, down subtracts. */
.dh-adv-ico { width: 30px; height: 30px; display: block; }
.dh-adv-ico .die { fill: currentColor; fill-opacity: 0.16; stroke: currentColor; stroke-width: 2; stroke-linejoin: round; }
.dh-adv-ico .chev { fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.dh-adv-btn[data-count] { border-color: var(--dc); background: color-mix(in srgb, var(--dc) 12%, transparent); }
.dh-adv-btn[data-count] .dh-adv-label { color: var(--line); }
.dh-adv-btn[data-count] .dh-adv-ico .die { fill-opacity: 0.34; }
.dh-adv-btn[data-count]::after {
  content: attr(data-count);
  position: absolute;
  top: -6px;
  right: -4px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--dc);
  color: var(--paper);
  font: 700 10px system-ui;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* The Duality roll is the primary action, so it wears the accent. */
.dh-roll { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.dh-roll .dh-roll-label { color: var(--accent); }
.dh-adv-btn:active, .dh-roll:active { transform: scale(0.95); }

/* The modifier and difficulty chips, centred beneath the dice. */
.dh-fields-row { display: flex; justify-content: center; gap: 12px; }

/* Daggerheart's strip carries only the standard dice — hide the DCC oddities,
   keep the custom-die picker and any dice the player made themselves. */
.dice-buttons.standard-only .dbtn:not([data-std]):not(.custom):not([data-custom]) { display: none; }
/* The owned PSG/WOM use only d5, d10, d20 and d100. Keep d? and a custom die
   created through it, but hide Dicebox's broader DCC/novelty catalog here. */
.dice-buttons.mothership-only .dbtn:not(.custom):not([data-sides="5"]):not([data-sides="10"]):not([data-sides="20"]):not([data-sides="100"]):not([data-custom]) { display: none; }

/* The total tinted by the duality tone. */
.total[data-variant="hope"] { color: #C9A227; }
.total[data-variant="fear"] { color: #8577C0; }
.total[data-variant="critical"] { color: #E7C24C; }

/* CthulhuTech: the hit count glows green when it meets the Difficulty, greys
   out when it falls short. */
.total[data-variant="ct-success"] { color: #57B591; }
.total[data-variant="ct-failure"] { color: var(--muted); }

/* CthulhuTech's difficulty can be unset — just count the hits. */
.ct-picker .count[data-unset="1"] { color: var(--muted); }

/* The One Ring: gold for a Gandalf auto-success, green for a success, muted for
   a failure. */
.total[data-variant="tor-gandalf"] { color: #E8C24E; }
.total[data-variant="tor-success"] { color: #8FB77A; }
.total[data-variant="tor-failure"] { color: var(--muted); }

/* PbtA / Mist Engine share the Daggerheart picker chrome; their lone modifier
   stepper just sits centred. */
.twod6-picker { justify-content: center; }

/* PbtA / Mist Engine 2d6 outcome: a hit reads green, a partial amber, a miss
   muted — the same bands the dice take. */
.total[data-variant="band-hit"] { color: #57B591; }
.total[data-variant="band-partial"] { color: #C99A3C; }
.total[data-variant="band-miss"] { color: var(--muted); }

/* ---- The One Ring picker ---- */
/* One Ring: a Success-d6 button + Target chip, then the condition toggles. */
.tor-picker { flex-direction: column; align-items: center; gap: 10px; }
.tor-controls-row { display: flex; gap: 12px; align-items: center; }
.tor-success-btn { --dc: #B5893C; }
.tor-flags-row { display: flex; gap: 8px; width: 100%; }
/* Each condition is an icon toggle: favoured/ill show the two Feat dice with the
   kept one on top (high for favoured, low for ill); Weary a struck Success die. */
.tor-flag {
  flex: 1 1 0;
  min-width: 0;
  appearance: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px 4px 7px;
  border: 1px solid var(--hair);
  border-radius: 12px;
  background: none;
  color: var(--muted);
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: border-color 140ms ease, color 140ms ease, background 140ms ease, transform 120ms ease;
}
.tor-flag-ico { width: 30px; height: 30px; display: block; }
.tor-flag-label { font-size: 10.5px; letter-spacing: 0.01em; white-space: nowrap; }
/* Two Feat dice: the dropped one outlined grey, the kept one bronze and opaque so
   it sits clearly on top; chevrons point to which was kept. */
.tor-flag-ico .drop { fill: none; stroke: #6f6857; stroke-width: 2; stroke-linejoin: round; }
.tor-flag-ico .keep { fill: color-mix(in srgb, #B5893C 18%, var(--paper)); stroke: #B5893C; stroke-width: 2.3; stroke-linejoin: round; }
.tor-flag-ico .chev { fill: none; stroke: #B5893C; stroke-width: 2.1; stroke-linecap: round; stroke-linejoin: round; }
/* Weary: a Success die with its low faces struck through. */
.tor-flag-ico .weary-die { fill: none; stroke: var(--muted); stroke-width: 2.2; }
.tor-flag-ico .weary-pip { fill: var(--muted); }
.tor-flag-ico .weary-strike { fill: none; stroke: var(--danger); stroke-width: 2.4; stroke-linecap: round; }
.tor-flag[aria-pressed="true"] {
  color: var(--accent);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.tor-flag[aria-pressed="true"] .tor-flag-label { color: var(--line); }
.tor-flag:active { transform: scale(0.96); }

/* ---- Mothership ---- */
/* Signature dice are direct roll actions; modifiers flank them in one tactile row. */
.ms-picker { flex-direction: column; align-items: stretch; gap: 6px; }
.ms-dice-row, .ms-fields-row, .ms-action-row { display: flex; align-items: stretch; gap: 6px; }
/* The second and final visual row is a single horizontal rail: character setup,
   dice-per-tap, then the five rules-grounded dice. It scrolls instead of wrapping. */
.picker.mothership-rail {
  width: 100%;
  max-width: 100%;
  min-width: 0;
  overflow-x: auto;
  flex-wrap: nowrap;
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--accent) 65%, transparent) transparent;
  -webkit-overflow-scrolling: touch;
}
.picker.mothership-rail::-webkit-scrollbar { height: 4px; }
.picker.mothership-rail::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--accent) 65%, transparent); border-radius: 999px; }
.mothership-rail .ms-fields-row, .mothership-rail .counter, .mothership-rail .dice-scroll { flex: 0 0 auto; }
.mothership-rail .dice-scroll { min-width: max-content; }
.mothership-rail .dice-scroll { order: 2; }
.mothership-rail .counter { order: 3; }
.mothership-rail .dice-buttons { overflow: visible; }
.mothership-rail .nudge { display: none; }
.mothership-rail .counter .step, .mothership-rail .count, .mothership-rail .dbtn { min-height: 44px; }
.mothership-rail .ms-direct-field {
  width: 72px;
  box-sizing: border-box;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
  padding: 0 4px;
}
.mothership-rail .ms-dial-field { position: relative; padding: 0; overflow: hidden; }
.mothership-rail .ms-dial-field > span {
  position: absolute;
  z-index: 1;
  top: 5px;
  left: 0;
  width: 100%;
  text-align: center;
  pointer-events: none;
}
.mothership-rail .ms-direct-field span { font-size: 9px; line-height: 1; }
.mothership-rail .ms-skill-field { width: 82px; position: relative; padding: 0; overflow: hidden; }
.mothership-rail .ms-skill-field > span {
  position: absolute;
  z-index: 1;
  top: 5px;
  left: 0;
  width: 100%;
  text-align: center;
  pointer-events: none;
}
.mothership-rail .counter .step { width: 22px; }
.mothership-rail .counter .count { width: 28px; }
.mothership-rail .dice-buttons { gap: 2px; }
.mothership-rail .dbtn { min-width: 38px; padding-inline: 3px; }
/* Square SVG viewports prevent the percentile pair and d20 from inheriting the
   old wide-card distortion that made one look thin and the other look fat. */
.ms-role-roll {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  padding: 4px;
  border: 1px solid var(--hair);
  border-radius: 12px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: border-color 140ms ease, color 140ms ease, background 140ms ease, transform 120ms ease;
}
.ms-role-ico { width: 34px; height: 34px; flex: 0 0 auto; display: block; }
.ms-percentile-ico { width: 42px; }
.ms-panic-ico { overflow: visible; }
.ms-role-ico polygon { fill: color-mix(in srgb, currentColor 10%, transparent); stroke: currentColor; stroke-width: 1.3; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.ms-percentile-ico .rear { opacity: 0.58; }
.ms-role-label { color: var(--line); font-size: 10.5px; font-weight: 650; white-space: nowrap; }
.ms-role-die { color: var(--muted); font-size: 9px; letter-spacing: 0.08em; text-transform: uppercase; }
.ms-role-roll:active { transform: scale(0.97); }
/* Target and Stress launch the tactile number wheel; Skill remains a direct
   select. The small ↕ says “flickable dial”, not one-point spinner arrows. */
.ms-fields-row { justify-content: center; align-items: center; }
.ms-direct-field {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px;
  border: 1px solid var(--hair);
  border-radius: 999px;
  color: var(--muted);
  font-size: 11px;
  white-space: nowrap;
}
.ms-direct-field input, .ms-direct-field select {
  min-width: 0;
  border: 0;
  outline: 0;
  background: none;
  color: var(--line);
  font: 600 15px/1 system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
}
.ms-direct-field input { width: 42px; text-align: center; }
.ms-direct-field select { width: 94px; cursor: pointer; }
.ms-value-dial {
  width: 100%;
  min-width: 42px;
  min-height: 44px;
  padding: 10px 4px 0;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--line);
  font: 600 15px/1 system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.ms-value-dial::after { content: "↕"; margin-left: 4px; color: var(--accent); font-size: 10px; }
.ms-value-dial:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Skill is a four-tier cycle (None → Trained → Expert → Master), not a number
   wheel, so it is a plain tap-to-raise / hold-to-lower button with no ↕ marker. */
.ms-skill-dial {
  width: 100%;
  min-height: 44px;
  padding: 12px 4px 0;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--line);
  font: 600 12px/1 system-ui, sans-serif;
  cursor: pointer;
}
.ms-skill-dial:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.ms-direct-field:focus-within { border-color: var(--accent); }
/* Advantage and Disadvantage borrow One Ring's visual explanation: the outlined
   die is discarded and the accent die is kept. This remains accurate because
   Mothership compares resolved outcomes, not merely high or low numbers. */
.ms-adv, .ms-role-roll {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 5px 4px 4px;
  border: 1px solid var(--hair);
  border-radius: 12px;
  background: none;
  color: var(--muted);
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: border-color 140ms ease, color 140ms ease, background 140ms ease, transform 120ms ease;
}
/* Only the active roll type carries the accent, so one tile reads as primary at a
   time — matching the staged dice in the tray. The inactive tile stays neutral
   like the Advantage/Disadvantage buttons beside it. Cascade order is deliberate:
   this sits after the shared modifier/action geometry above. */
.ms-role-roll[aria-pressed="true"] {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
}
.ms-keep-dice { width: 34px; height: 34px; display: block; }
.ms-keep-dice .drop { fill: none; stroke: var(--muted); stroke-width: 1.8; stroke-linejoin: round; }
.ms-keep-dice .keep { fill: color-mix(in srgb, var(--accent) 18%, var(--paper)); stroke: var(--accent); stroke-width: 2.1; stroke-linejoin: round; }
.ms-keep-dice .chev { fill: none; stroke: var(--accent); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.ms-adv-label { font-size: 10px; letter-spacing: 0.01em; white-space: nowrap; }
.ms-adv[aria-pressed="true"] { color: var(--accent); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.ms-adv[aria-pressed="true"] .ms-adv-label { color: var(--line); }
.ms-adv:active, .ms-role-roll:active { transform: scale(0.96); }

/* ---- mode sheet ---- */

/* The mode sheet is a touch narrower than the modifier sheet: two cards want a
   compact shelf, not the full width. */
/* ---- Cards: the deck rail ---- */
/* Compact: count chip, Shuffle, the two icon toggles, and the live count. The
   draw itself is the entry button (relabelled Draw) and the deck on the tray. */
.cards-picker { display: flex; gap: 8px; justify-content: center; align-items: stretch; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: thin; }
.deck-shuffle {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-width: 64px;
  padding: 5px 10px 4px;
  border: 1px solid var(--hair);
  border-radius: 12px;
  background: none;
  color: var(--muted);
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: border-color 140ms ease, background 140ms ease, transform 120ms ease;
}
.deck-shuffle svg { width: 26px; height: 26px; }
.deck-shuffle svg path { fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.deck-shuffle-label { font-size: 10px; color: var(--muted); white-space: nowrap; }
.deck-shuffle:active { transform: scale(0.94); }
/* The toggles wear their meaning: a card with a joker star, a card with a
   return arrow — pressed takes the accent, like every flag in the app. */
.deck-flag {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  min-width: 58px;
  padding: 4px 8px 4px;
  border: 1px solid var(--hair);
  border-radius: 12px;
  background: none;
  color: var(--muted);
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: border-color 140ms ease, color 140ms ease, background 140ms ease, transform 120ms ease;
}
.deck-flag-ico { width: 26px; height: 26px; display: block; }
.deck-flag-ico .crd { fill: none; stroke: currentColor; stroke-width: 1.7; }
.deck-flag-ico .mark { fill: currentColor; }
.deck-flag-ico .mark2 { fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.deck-flag-name { font-size: 10px; letter-spacing: 0.01em; white-space: nowrap; }
.deck-flag[aria-pressed="true"] { color: var(--accent); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.deck-flag[aria-pressed="true"] .deck-flag-name { color: var(--line); }
.deck-flag:active { transform: scale(0.94); }
.deck-remain {
  align-self: center;
  font: 600 12px ui-monospace, monospace;
  color: var(--muted);
  padding: 0 4px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* A single red card's label in the big readout keeps its suit's colour. */
#total[data-variant="card-red"] { color: #C74B3F; }

/* ---- discard browser ---- */
/* The pile fanned open: same frost as the close-up, one layer beneath it, so
   a card tapped here rises above the spread it came from. */
#discardPanel {
  position: fixed;
  z-index: 29;
  left: 0; right: 0; bottom: 0;
  /* top is set from the real bar height when it opens */
  overflow-y: auto;
  background: color-mix(in srgb, var(--paper) 82%, transparent);
  backdrop-filter: blur(7px);
  -webkit-backdrop-filter: blur(7px);
  cursor: zoom-out;
  padding: 10px 14px 20px;
}
.discard-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 4px 4px 10px;
}
.discard-title { font: 650 14px/1.2 inherit; letter-spacing: 0.02em; color: var(--line); }
.discard-hint { font-size: 11px; color: var(--muted); }
.discard-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
}
.discard-card {
  appearance: none;
  border: 0;
  padding: 0;
  background: none;
  width: clamp(84px, 17vw, 132px);
  cursor: zoom-in;
  -webkit-tap-highlight-color: transparent;
  transition: transform 120ms ease;
}
.discard-card:active { transform: scale(0.96); }
.discard-card svg,
.discard-card img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 8px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.3);
}
/* Explicit origin: SVG elements can default transform-origin to their top-left
   corner, which spins the card clean out of its cell and leaves it blank. */
.discard-card.rev svg { transform: rotate(180deg); transform-origin: 50% 50%; transform-box: fill-box; }

/* ---- card close-up ---- */
/* Everything below the top bar frosts over and the held card rises into it.
   The bar stays clear (and above, z-wise), so the corner menus keep working
   while a card is up. */
#cardFocus {
  position: fixed;
  z-index: 30;
  left: 0; right: 0; bottom: 0;
  /* top is set from the real bar height when it opens */
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--paper) 82%, transparent);
  backdrop-filter: blur(7px);
  -webkit-backdrop-filter: blur(7px);
  cursor: zoom-out;
}
.card-focus-art {
  transition: transform 280ms cubic-bezier(0.2, 0.8, 0.25, 1), opacity 220ms ease;
  /* No will-change: pinning this as a permanent GPU layer makes the browser
     rasterise the vector card once at a low resolution and scale the texture,
     which fills a high-DPI screen with a blurry blow-up. The transform
     transition still composites on its own for the 280ms it runs; letting the
     layer retire afterwards re-rasterises the SVG crisp at device resolution. */
}
.card-focus-art svg,
.card-focus-art img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 12px;
  box-shadow: 0 20px 52px rgba(0, 0, 0, 0.45);
}
/* The two-faced close-up: decks with a reading side (uta-garuta) flip over.
   The front is the traced art, the back a typeset reading of the poem. */
.card-focus-art { perspective: 1400px; }
.cf-flip {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 460ms cubic-bezier(0.3, 0.7, 0.3, 1);
}
.cf-flip.flipped { transform: rotateY(180deg); }
.cf-face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
.cf-back { transform: rotateY(180deg); }
.uta-read {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  background: var(--face);
  border: 1.5px solid var(--hair);
  border-radius: 12px;
  box-shadow: 0 20px 52px rgba(0, 0, 0, 0.45);
  color: var(--line);
  padding: 0.9em 0.9em 0.8em;
  overflow: hidden;
}
.uta-read::before {
  content: '';
  position: absolute;
  inset: 0.55em;
  border: 1px solid var(--accent);
  opacity: 0.5;
  border-radius: 8px;
  pointer-events: none;
}
.uta-read-head { text-align: center; line-height: 1.35; margin-top: 0.35em; }
.uta-read-n {
  font-family: Iosevka Etoile, Georgia, serif;
  color: var(--accent);
  font-size: 0.72em;
  letter-spacing: 0.08em;
  display: block;
}
.uta-read-ja { font-size: 1.05em; letter-spacing: 0.12em; display: block; }
.uta-read-cat { font-size: 0.62em; color: var(--muted); letter-spacing: 0.2em; display: block; }
.uta-read-en {
  font-family: Iosevka Etoile, Georgia, serif;
  font-size: 0.62em;
  color: var(--muted);
  letter-spacing: 0.04em;
  display: block;
}
/* The poem proper: five ku, five vertical columns, right to left. Each ku is
   its own column (row-reverse puts the first on the right), so a column can
   never break in the middle of a verse. */
.uta-read-poem {
  flex: 1;
  display: flex;
  flex-direction: row-reverse;
  align-items: center;
  justify-content: center;
  gap: 0.55em;
  margin: 0.4em 0;
  font-family: "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif JP", "Noto Serif CJK JP", serif;
  font-size: 0.92em;
  line-height: 1.7;
  letter-spacing: 0.1em;
}
.uta-read-poem > div { writing-mode: vertical-rl; }
.uta-read-solo {
  writing-mode: horizontal-tb;
  font-family: Iosevka Etoile, Georgia, serif;
  font-style: italic;
  font-size: 0.8em;
  line-height: 1.6;
  text-align: center;
  letter-spacing: 0.01em;
  padding: 0 0.6em;
}
.uta-read-kana {
  text-align: center;
  color: var(--muted);
  font-size: 0.56em;
  line-height: 1.5;
  margin-bottom: 0.5em;
}
.uta-read-trans {
  text-align: center;
  font-family: Iosevka Etoile, Georgia, serif;
  font-style: italic;
  font-size: 0.66em;
  line-height: 1.5;
  opacity: 0.92;
  margin-bottom: 0.6em;
}
.card-focus-hint {
  position: absolute;
  left: 0;
  right: 0;
  bottom: max(14px, env(safe-area-inset-bottom));
  margin: 0;
  text-align: center;
  color: var(--muted);
  font-size: 12.5px;
  letter-spacing: 0.06em;
  pointer-events: none;
}
.uta-lang { min-width: 64px; }
@media (prefers-reduced-motion: reduce) {
  .cf-flip { transition: none; }
}

/* A reversed tarot card reads in the mode's violet rather than an alarm red —
   reversal is a meaning, not a failure. */
#total[data-variant="tarot-rev"] { color: var(--accent); }

/* The dial's optional second action: releases the value back to "unset"
   (the Mothership target's table-sets-it state). A quiet ghost next to the
   primary commit. */
.dial-clear {
  appearance: none;
  border: 1px solid var(--hair);
  background: none;
  color: var(--muted);
  border-radius: 10px;
  padding: 10px 14px;
  font: inherit;
  font-size: 14px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.dial-clear:hover { color: var(--line); border-color: var(--muted); }

/* ---- system picker popover ---- */
/* A corner menu anchored under the header's mode button (openMode positions it
   from the button's rect). One-line rows: mark, shorthand, dice. */
.mode-pop {
  position: fixed;
  z-index: 40;
  width: 252px;
  /* Tall enough for all thirteen rows on a phone; small screens still scroll. */
  max-height: min(84vh, 640px);
  overflow-y: auto;
  background: var(--face);
  border: 1px solid var(--hair);
  border-radius: 14px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.28);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.mode-row {
  appearance: none;
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 7px 9px;
  border: 0;
  border-radius: 9px;
  background: none;
  color: var(--line);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background 120ms ease;
}
.mode-row:hover { background: color-mix(in srgb, var(--line) 7%, transparent); }
.mode-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* The active system holds its own colour, matching the tray it recolours. */
.mode-row[aria-pressed="true"] {
  background: color-mix(in srgb, var(--sys, var(--accent)) 13%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sys, var(--accent)) 50%, transparent);
}
/* Card decks sit below the dice systems, past their own rule. */
.mode-row-break {
  border-top: 1px solid var(--hair);
  border-radius: 0 0 9px 9px;
  margin-top: 3px;
  padding-top: 9px;
}
/* Dicebox itself sits pinned above the alphabetical systems. */
.mode-row-pinned {
  border-bottom: 1px solid var(--hair);
  border-radius: 9px 9px 0 0;
  margin-bottom: 3px;
  padding-bottom: 9px;
}
.mode-row-ico { width: 20px; height: 20px; flex: 0 0 auto; color: var(--sys, var(--muted)); }
.mode-row-name { font-size: 13px; font-weight: 650; letter-spacing: 0.01em; white-space: nowrap; }
.mode-row-sub {
  margin-left: auto;
  font-size: 10.5px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Each system's row wears the colour its mode paints the app with. */
.mode-cards { --sys: #2F8B57; }
.mode-tarot { --sys: #8B76D6; }
.mode-napoletane { --sys: #12A0D7; }
.mode-hanafuda { --sys: #E8483B; }
.mode-utagaruta { --sys: #C9A227; }
.mode-v5 { --sys: #C3212E; }
.mode-fate { --sys: #6E96BE; }
.mode-genesys { --sys: #BFA766; }
.mode-daggerheart { --sys: #C9A227; }
.mode-cthulhutech { --sys: #57B591; }
.mode-starwars { --sys: #5B9BD5; }
.mode-onering { --sys: #B5893C; }
.mode-pbta { --sys: #D97A3C; }
.mode-mist { --sys: #3F9FA0; }
.mode-mothership { --sys: #AEC93C; }

/* How many dice each tap adds. Typing a number here and tapping d6 is how you
   get 100d6 without tapping a hundred times. */
.counter {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  border: 1px solid var(--hair);
  border-radius: 8px;
  overflow: hidden;
}

.counter .step {
  border: 0;
  border-radius: 0;
  width: 30px;
  height: 34px;
}

.count {
  width: 34px;
  height: 34px;
  border: 0;
  border-left: 1px solid var(--hair);
  border-right: 1px solid var(--hair);
  background: none;
  color: var(--line);
  font: inherit;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  text-align: center;
  padding: 0;
  -moz-appearance: textfield;
}
.count::-webkit-outer-spin-button,
.count::-webkit-inner-spin-button { appearance: none; margin: 0; }

.step {
  appearance: none;
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
  border: 1px solid var(--hair);
  border-radius: 8px;
  background: none;
  color: var(--line);
  font: inherit;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}

/* The row holds more dice than fit, so it scrolls. Arrows appear at whichever
   end has more to show — a scrollable strip with no visible edge reads as the
   whole set, and everything past the eighth die goes unnoticed. */
.dice-scroll {
  position: relative;
  display: flex;
  align-items: center;
  gap: 2px;
  flex: 1 1 auto;
  min-width: 0;
}

.dice-buttons {
  display: flex;
  gap: 4px;
  overflow-x: auto;
  scrollbar-width: none;
  flex: 1 1 auto;
  scroll-behavior: smooth;
  /* Momentum scrolling on iOS. */
  -webkit-overflow-scrolling: touch;
}
.dice-buttons::-webkit-scrollbar { display: none; }

.nudge {
  appearance: none;
  flex: 0 0 auto;
  width: 22px;
  min-height: 34px;
  border: 0;
  background: none;
  color: var(--muted);
  font-size: 19px;
  line-height: 1;
  padding: 0;
  cursor: pointer;
}
.nudge:hover { color: var(--accent); }

.dbtn {
  appearance: none;
  flex: 0 0 auto;
  min-width: 46px;
  min-height: 34px;
  border: 1px solid var(--hair);
  border-radius: 8px;
  background: none;
  color: var(--line);
  font: inherit;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: border-color 160ms ease, color 160ms ease;
}
.dbtn:active { border-color: var(--line); }

/* A die in the tray reads as selected, with its count on the button — the pool
   is visible in the row you are already looking at, not only in the field. */
.dbtn[aria-pressed="true"] {
  color: var(--accent);
  border-color: var(--accent);
}

/* Kept inside the button's padding: the hold-fill needs overflow:hidden, so
   anything poking past the border would be clipped. */
.dbtn[data-count]::after {
  content: attr(data-count) "\00d7";
  position: absolute;
  top: 2px;
  right: 4px;
  font-size: 9.5px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  color: var(--accent);
  pointer-events: none;
}

/* Make room for the badge so it never sits on the label. */
.dbtn[data-count] {
  padding-right: 14px;
}

/* The modifier's own glyph, bottom-left so it never collides with the count
   badge top-right. A shared underline said "modified" but not which. */
.dbtn[data-mod]::before {
  content: attr(data-mod);
  position: absolute;
  bottom: 1px;
  left: 4px;
  z-index: 1;
  font-size: 9px;
  line-height: 1;
  letter-spacing: 0;
  color: var(--accent);
  pointer-events: none;
}

.dbtn[data-mod] {
  padding-left: 13px;
}

.sheet-option[aria-pressed="true"] .sheet-option-name {
  color: var(--accent);
}

/* ---- custom die wheel ---- */

.dial .sheet-panel {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* Scroll-snap does the physics: flick and a number settles under the marker,
   the way a phone's timer picker behaves. */
.wheel {
  flex: 1 1 auto;
  min-height: 96px;
  max-height: 150px;
  overflow-y: auto;
  scroll-snap-type: y mandatory;
  scrollbar-width: none;
  position: relative;
  /* Fades the ends so the list reads as a wheel rather than a cut-off column. */
  mask-image: linear-gradient(transparent, #000 22%, #000 78%, transparent);
  -webkit-mask-image: linear-gradient(transparent, #000 22%, #000 78%, transparent);
  /* Half the visible height, so the first and last items can reach the middle. */
  padding: 56px 0;
}
.wheel::-webkit-scrollbar { display: none; }

.wheel-item {
  scroll-snap-align: center;
  height: 30px;
  line-height: 30px;
  text-align: center;
  font-family: "Iosevka Etoile", ui-monospace, monospace;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  opacity: 0.45;
  cursor: pointer;
  transition: color 120ms ease, opacity 120ms ease;
}
.wheel-item[aria-selected="true"] {
  color: var(--accent);
  opacity: 1;
  font-size: 19px;
}

/* The marker: two hairlines the selected number sits between. */
.dial .sheet-panel {
  position: relative;
}
.wheel::before,
.wheel::after {
  content: "";
  position: sticky;
  left: 0;
  display: block;
  height: 1px;
  background: var(--hair);
  z-index: 1;
}
.wheel::before { top: calc(50% - 16px); }
.wheel::after  { bottom: calc(50% - 16px); }

.dial-actions {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 8px 2px 2px;
}

.dial-entry {
  display: flex;
  align-items: center;
  gap: 2px;
  border: 1px solid var(--hair);
  border-radius: 10px;
  padding: 0 10px;
  min-height: var(--tap);
  font-family: "Iosevka Etoile", ui-monospace, monospace;
  font-size: 15px;
  color: var(--muted);
}

.dial-entry input {
  width: 3.6em;
  border: 0;
  background: none;
  color: var(--line);
  font: inherit;
  font-variant-numeric: tabular-nums;
  padding: 0;
}
.dial-entry input:focus { outline: none; }

.dial-add {
  flex: 1 1 auto;
}

/* The custom-die button reads as an action rather than a die. */
.dbtn.custom {
  color: var(--muted);
  font-family: "Iosevka Etoile", ui-monospace, monospace;
}

/* A die added through the picker. Dashed to show it is not part of the standard
   set, but otherwise a full die: tappable, countable, holdable for modifiers. */
.dbtn[data-custom] {
  border-style: dashed;
}


/* Holding a die fills it, which both signals that a long press does something
   and shows how much longer to hold. The fill is its own element rather than a
   pseudo-element: ::before carries the modifier glyph and ::after the count, and
   a third use would collide with one of them. */
.dbtn {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  /* Left to the browser, so a sideways drag scrolls the row. Claiming the
     gesture for scrubbing instead made the row unscrollable on a phone, which
     put every die past the eighth out of reach — a far worse trade than losing
     drag-to-scrub. Scrubbing now lives behind the long press, where it does not
     compete with panning. */
  touch-action: manipulation;
}

.dbtn-fill {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  transform: scaleX(0);
  transform-origin: left;
  pointer-events: none;
}
.dbtn[data-holding="1"] .dbtn-fill {
  transform: scaleX(1);
  transition: transform 450ms linear;
}

@media (prefers-reduced-motion: reduce) {
  .dbtn[data-holding="1"] .dbtn-fill { transition-duration: 1ms; }
}

/* ---- modifier sheet ---- */

/* Fills the tray, like the help panel. A floating card over a dimmed page is a
   different design language than the rest of this app — the tray is the surface
   everything else happens on, so options belong there too. */
.sheet {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  overflow-y: auto;
  scrollbar-width: none;
  background: var(--paper);
  animation: help-in 160ms ease both;
}
.sheet::-webkit-scrollbar { display: none; }

.sheet-panel {
  width: 100%;
  padding: 2px;
  /* Sits toward the top of a tall tray rather than floating in the middle of a
     large empty field, which reads as stranded on a desktop window. */
  margin-top: clamp(0px, 4vh, 28px);
  margin-bottom: auto;
}

.sheet-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 0 0 8px;
  padding: 0 10px;
  font-family: "Iosevka Etoile", ui-monospace, monospace;
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

.sheet-close {
  appearance: none;
  border: 0;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  line-height: 1;
  padding: 6px;
  margin: -6px;
  cursor: pointer;
}

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

.sheet-option {
  appearance: none;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0 12px;
  width: 100%;
  text-align: left;
  border: 0;
  border-radius: 8px;
  background: none;
  padding: 9px 10px;
  font: inherit;
  cursor: pointer;
  transition: background 140ms ease;
}
.sheet-option:hover,
.sheet-option:focus-visible {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

/* How many of this die are staged. Dragging the button sideways is quicker, but
   only a typed number lands on an exact count. */
.sheet-count {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 4px 10px 10px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--hair);
}

.sheet-count-label {
  font-size: 14px;
  color: var(--line);
}

.sheet-count-field {
  width: 46px;
}

/* Dragging a die button to dial its count. */
.dbtn[data-scrubbing] {
  border-color: var(--accent);
  color: var(--accent);
}

/* Another modifier already answers this question, so it cannot also apply. */
.sheet-option[data-blocked] {
  opacity: 0.34;
  cursor: default;
}
.sheet-option[data-blocked]:hover { background: none; }

.sheet-option-name {
  font-size: 14px;
  color: var(--line);
}

/* The same mark the die button shows, so the row's glyphs teach themselves. */
.sheet-option-mark {
  display: inline-block;
  width: 1.3em;
  color: var(--accent);
  font-size: 12px;
}

.sheet-option-notation {
  font-family: "Iosevka Etoile", ui-monospace, monospace;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  text-align: right;
}

.sheet-option-hint {
  grid-column: 1 / -1;
  font-size: 12px;
  color: var(--muted);
  margin-top: 1px;
}

.clear {
  appearance: none;
  flex: 0 0 auto;
  width: var(--tap);
  min-height: var(--tap);
  border: 1px solid var(--hair);
  border-radius: 10px;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

/* ---- notation entry ---- */

.entry {
  display: flex;
  gap: 8px;
  flex: 0 0 auto;
}

.entry input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: var(--tap);
  padding: 0 14px;
  border: 1px solid var(--hair);
  border-radius: 10px;
  background: var(--face);
  color: var(--line);
  font: inherit;
  font-size: 16px; /* 16px keeps iOS from zooming on focus */
}
.entry input::placeholder { color: var(--muted); }
.entry input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: transparent;
}

.roll {
  appearance: none;
  min-height: var(--tap);
  padding: 0 22px;
  border: 0;
  border-radius: 10px;
  background: var(--line);
  color: var(--paper);
  font: inherit;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.04em;
  cursor: pointer;
}
.roll:active { opacity: 0.8; }

.error {
  margin: 0;
  font-size: 13px;
  color: var(--danger);
  text-align: center;
  flex: 0 0 auto;
}

/* ---- history ---- */

/* The recent strip stays short; the full session lives behind its own panel.
   Only the list scrolls, so the "full history" button is always reachable. */
.log {
  flex: 0 0 auto;
}

.log ul {
  max-height: 92px;
  overflow-y: auto;
  scrollbar-width: none;
}
.log ul::-webkit-scrollbar { display: none; }
.log::-webkit-scrollbar { display: none; }

.log ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.log li {
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  padding: 3px 0;
  border-top: 1px solid var(--hair);
}

/* Notation on the left, total on the right. */
.log-line {
  display: flex;
  justify-content: space-between;
  gap: 12px;
}
.log li b {
  color: var(--line);
  font-weight: 600;
}

.log-more {
  margin: 4px 0 0;
  text-align: center;
}

.log-more-button {
  appearance: none;
  border: 0;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 3px 6px;
  cursor: pointer;
}
.log-more-button:hover { color: var(--accent); }

.log-count {
  font-variant-numeric: tabular-nums;
  text-transform: none;
  letter-spacing: 0;
  opacity: 0.8;
}

/* ---- full history ---- */

/* The one sheet that outgrows the tray.
   Every other sheet is a control acting on the dice underneath it, so covering
   only the tray is right for those. This one is a reading view — the question
   it answers is "what did I roll twenty minutes ago" — and confining it to the
   tray meant scrolling a session through a window a third of a phone tall.
   So it covers the app instead, and the list inside it does the scrolling. */
#historyPanel {
  position: fixed;
  inset: 0;
  z-index: 6;
  overflow: hidden;
  padding: calc(env(safe-area-inset-top) + 10px) var(--gut)
           calc(env(safe-area-inset-bottom) + 10px);
}

/* Overrides .sheet-panel's top margin and auto bottom margin, which exist to
   stop a short panel floating in the middle of a tall tray. This one is not
   short — it fills what it is given. */
#historyPanel .sheet-panel {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  max-width: 620px;
  margin: 0 auto;
}

#historyPanel .sheet-title,
#historyPanel .history-actions { flex: 0 0 auto; }

.history-full {
  list-style: none;
  margin: 0;
  padding: 0 2px;
  overflow-y: auto;
  scrollbar-width: none;
  /* Reading size, adjustable from the panel's own control and remembered
     between sessions. Rows below scale off this, so one property moves the
     whole list. */
  font-size: var(--history-text, 13px);
}
#historyPanel .history-full {
  flex: 1 1 auto;
  min-height: 0;
}
.history-full::-webkit-scrollbar { display: none; }

.history-full li {
  font-size: 1em;
  line-height: 1.4;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  padding: 6px 8px;
  border-bottom: 1px solid var(--hair);
}
.history-full li:last-child { border-bottom: 0; }
.history-full li b { color: var(--line); font-weight: 600; }

.history-time {
  font-family: "Iosevka Etoile", ui-monospace, monospace;
  font-size: 0.9em;
  opacity: 0.75;
  min-width: 7em;
}

/* Scaled with the list rather than pinned at 11px: this is the line that says
   what each die landed on, which is the whole reason to open the panel, and it
   was the least readable thing in it. */
#historyPanel .log-detail {
  font-size: 0.92em;
  opacity: 0.85;
}

.history-size {
  display: flex;
  gap: 2px;
  margin-left: auto;
  margin-right: 6px;
}

.history-size-step {
  appearance: none;
  border: 1px solid var(--hair);
  border-radius: 6px;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 11px;
  line-height: 1;
  /* A comfortable tap target without making the title row tall. */
  min-width: 30px;
  padding: 6px 4px;
  cursor: pointer;
}
.history-size-step:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); }
.history-size-step:disabled { opacity: 0.35; cursor: default; }

.history-notation {
  flex: 1 1 auto;
  text-align: left;
  padding-left: 10px;
}

.history-empty {
  text-align: center;
  padding: 18px 0 !important;
  opacity: 0.7;
}

.history-actions {
  display: flex;
  gap: 6px;
  padding: 10px 8px 2px;
  border-top: 1px solid var(--hair);
  margin-top: 6px;
}

.history-actions .ghost {
  flex: 1 1 auto;
  min-height: 34px;
  padding: 0 8px;
}

.history-clear {
  flex: 0 0 auto !important;
  color: var(--danger) !important;
}

/* What each die landed on, under its own roll. Quieter than the total, since
   the total is what you look for first and this is what you check afterwards. */
.log-detail {
  display: block;
  font-family: "Iosevka Etoile", ui-monospace, monospace;
  font-size: 11px;
  line-height: 1.35;
  color: var(--muted);
  opacity: 0.75;
  word-break: break-word;
}

/* ---- rooms ---- */

/* The standing answer to "are my rolls leaving this device?". It sits in the
   header rather than inside the panel, because the panel is shut almost all of
   the time and the question is always live. */
.share-flag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  margin-right: 2px;
}

.share-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  animation: share-pulse 2.4s ease-in-out infinite;
}

/* Slow enough to read as "on" rather than as an alert. A steady dot was too
   easy to stop seeing; anything faster looked like something was wrong. */
@keyframes share-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.35; }
}

@media (prefers-reduced-motion: reduce) {
  .share-dot { animation: none; }
}

/* On a narrow phone the word would push the buttons off the row; the dot alone
   still carries it, and the panel spells it out. */
@media (max-width: 380px) {
  .share-flag-text { display: none; }
  .wordmark { font-size: 13px; letter-spacing: 0.15em; }
  .system-badge { margin-left: 6px; padding: 3px 6px; font-size: 9px; letter-spacing: 0.1em; }
}

#roomToggle[data-live] {
  color: var(--accent);
  border-color: var(--accent);
}

/* The before-joining half of the panel. Toggled wholesale against .room-live,
   so it needs a display of its own for [hidden] to have something to beat. */
.room-setup {
  display: block;
}

.room-note,
.room-hint {
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--muted);
  max-width: 46ch;
}

.room-note { padding: 0 10px 12px; }

/* The link sits directly under the note it belongs to, so the note above it
   drops its own bottom padding rather than the pair reading as two blocks. */
.room-note:has(+ .room-note-more) { padding-bottom: 6px; }

.room-block {
  padding: 12px 10px;
  border-top: 1px solid var(--hair);
}

/* The first thing in the panel needs no rule above it — the title is already
   the boundary, and a hairline directly under it reads as a stray line. */
.room-block-lead {
  border-top: 0;
  padding-top: 4px;
}

/* The privacy note, now at the foot of the panel rather than in front of it.
   Set apart and quieter than the controls: it is there to be found, not to be
   read before you are allowed to roll. */
.room-privacy {
  margin-top: 4px;
  padding-top: 10px;
  border-top: 1px solid var(--hair);
}

.room-create { width: 100%; }

.room-block .room-hint { margin-top: 8px; }

.room-label {
  display: block;
  margin: 0 0 6px;
  font-family: "Iosevka Etoile", ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.room-made {
  padding: 12px 10px;
  border-top: 1px solid var(--hair);
}

/* Shown plainly and large enough to read across a table, since reading it out
   loud is how it usually travels. Selectable, because copying by hand is the
   fallback when the clipboard API is unavailable. */
.room-phrase {
  margin: 0 0 10px;
  font-family: "Iosevka Etoile", ui-monospace, monospace;
  font-size: 15px;
  line-height: 1.5;
  color: var(--accent);
  word-spacing: 0.3em;
  word-break: break-word;
  cursor: pointer;
  /* `all` rather than `text`: one tap takes the whole phrase, which is what
     anyone highlighting it wants, and a partial passphrase is never useful. */
  user-select: all;
  -webkit-user-select: all;
}
.room-phrase:hover { opacity: 0.8; }

/* The confirmation. A brief lift and a fade rather than a colour change: the
   words are already the accent colour, so there is nothing for a colour change
   to say. */
.room-phrase[data-copied] { animation: phrase-copied 700ms ease; }

@keyframes phrase-copied {
  0%   { opacity: 1;    transform: none; }
  14%  { opacity: 0.35; transform: scale(1.04); }
  46%  { opacity: 1;    transform: scale(1.01); }
  100% { opacity: 1;    transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .room-phrase[data-copied] { animation-duration: 1ms; }
}

.room-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.room-actions .ghost {
  flex: 1 1 auto;
  min-height: 34px;
  padding: 0 8px;
}

.room-leave {
  flex: 0 0 auto !important;
  color: var(--danger) !important;
}

.room-join input,
#roomName {
  width: 100%;
  min-height: var(--tap);
  padding: 0 12px;
  margin-bottom: 8px;
  border: 1px solid var(--hair);
  border-radius: 10px;
  background: var(--face);
  color: var(--line);
  font: inherit;
  font-size: 16px; /* 16px keeps iOS from zooming on focus */
}
.room-join input::placeholder { color: var(--muted); }

.room-join-button { width: 100%; }

.room-live {
  padding: 12px 10px;
  border-top: 1px solid var(--hair);
}

.room-roster-label { margin-top: 14px; }

.room-roster {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 26vh;
  overflow-y: auto;
  scrollbar-width: none;
}
.room-roster::-webkit-scrollbar { display: none; }

.room-roster li {
  font-size: 13.5px;
  color: var(--line);
  padding: 5px 2px;
  border-bottom: 1px solid var(--hair);
}
.room-roster li:last-child { border-bottom: 0; }

.room-roster-empty {
  color: var(--muted) !important;
  font-size: 12.5px !important;
}

.room-state {
  margin: 0;
  padding: 12px 10px 2px;
  border-top: 1px solid var(--hair);
  font-size: 12px;
  line-height: 1.5;
  color: var(--muted);
  min-height: 1.5em;
}

/* Someone else's roll. Marked quietly — a name in the accent colour and a rule
   down the left — because the log is read at a glance and a loud treatment for
   half the entries would make the whole strip harder to scan, not easier. */
.log-who {
  color: var(--accent);
  margin-right: 6px;
}

/* Your own name, once a room makes it worth showing. Deliberately not the
   accent colour: every line in the log carries a name now, so if yours were
   painted like a peer's there would be no difference left for the accent to
   carry and the strip would read as one undifferentiated block. Quiet enough
   to be a label, loud enough to answer "which of these are mine". */
.log-who[data-self] {
  color: var(--muted);
  opacity: 0.75;
}

.log li[data-remote] {
  border-left: 2px solid var(--accent);
  padding-left: 6px;
  margin-left: -8px;
}

/* Same rule in the full history, but paid for out of the existing padding
   rather than a negative margin: these rows are inside a panel with an edge, so
   pulling them left would hang them off it. 6px + a 2px border lands the text
   exactly where an unmarked row's text sits. */
.history-full li[data-remote] {
  border-left: 2px solid var(--accent);
  padding-left: 6px;
}

:where(button, input):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 1ms !important; }
}
