/* ==========================================================================
   The site's one stylesheet. Public pages and the admin panel are one system:
   they share every token and every component and differ only in chrome.

   One visual world, deliberately: a campaign ledger read by candlelight. The
   site commits to a single dark treatment rather than shipping two themes, so
   every colour below is painted explicitly and nothing is inherited.

   The light source is warm — tallow, not the red pop that dark-fantasy pages
   default to. Oxblood appears only where something is destructive or lost.

   Depth is carried by borders (--rime -> --rime-bright -> --tallow-dim on
   hover) and by background steps (--ink-deep recessed, --iron surface,
   --iron-raised raised). There are no elevation shadows; the only box-shadows
   here are focus rings and one inset hairline. Keep it that way.

   Sections:
     faces · tokens · base · type · layout · chrome · panels · ledger
     status · buttons · forms · tables · feedback · overlays · figures
     home · admin · prose · utilities · motion

   Media queries sit next to the component they modify rather than collected at
   the bottom, so a component is one place to read.

   Every class name used in a template must be defined here.
   TestEveryClassTheTemplatesUseIsDefined enforces it: on a dark ground an
   undefined class renders invisible rather than obviously wrong.
   ========================================================================== */

/* ------------------------------------------------------------------- faces */

/* Latin subset only, self-hosted. Nothing here loads from another origin, which
   is what lets the Content-Security-Policy stay at 'self'. */

@font-face {
  font-family: 'Grenze Gotisch';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/grenze-gotisch-600.woff2") format('woff2');
}

@font-face {
  font-family: 'Spectral';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/spectral-400.woff2") format('woff2');
}

@font-face {
  font-family: 'Spectral';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/spectral-600.woff2") format('woff2');
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-500.woff2") format('woff2');
}

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

:root {
  /* Ground and surfaces: tempered steel, blue-cold, never pure black. */
  --ink:        #0b0e12;
  --ink-deep:   #070a0d;
  --iron:       #161b22;
  --iron-raised:#1c232c;
  --rime:       #2a323c;
  --rime-bright:#3b4553;

  /* The light. Tallow is the accent and the page's only warm hue. */
  --tallow:     #e8c88a;
  --tallow-lit: #f6dda9;
  --tallow-dim: #a8905f;
  --tallow-wash: rgba(232, 200, 138, 0.09);
  --tallow-ring: rgba(232, 200, 138, 0.35);

  /* Meaning, kept separate from the accent. */
  --oxblood:    #8e2436;
  --oxblood-lit:#c2415a;
  --oxblood-wash: rgba(142, 36, 54, 0.09);
  --oxblood-edge: rgba(142, 36, 54, 0.4);
  --verdigris:  #4e8c7d;
  --verdigris-wash: rgba(78, 140, 125, 0.1);
  --verdigris-edge: rgba(78, 140, 125, 0.45);
  --azure:      #4f8fb8;
  --azure-deep: #2f5d7a;

  /* Text, three steps down. */
  --parchment:  #e6decf;
  --parchment-2:#a9a294;
  --parchment-3:#6e6a62;

  /* Hairlines drawn between rows, softer than a border. */
  --hairline: rgba(42, 50, 60, 0.9);

  --display: 'Grenze Gotisch', 'Iowan Old Style', Georgia, serif;
  --body:    'Spectral', Georgia, 'Times New Roman', serif;
  --data:    'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* The A3 mark. One asset, two treatments: crimson as an <img> in the topbar,
     tallow as a mask-image behind the hero and the auth cards. */
  --mark: url("../img/mark.png");

  --r-sm:   2px;
  --r-md:   3px;
  --r-pill: 999px;

  --rail: 2.75rem;
  --measure: 74rem;

  --ease: .18s;
}

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

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  background-color: var(--ink);
  color: var(--parchment);
  font-family: var(--body);
  font-size: 1rem;
  line-height: 1.6;
  font-synthesis-weight: none;
  overflow-x: hidden;
}

/* The forge glow. Two fixed radials, the warm one biased to where the eye
   enters the page, so the ground is lit rather than flat. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(58rem 40rem at 12% -6%, rgba(232, 200, 138, 0.10), transparent 62%),
    radial-gradient(48rem 38rem at 92% 8%, rgba(142, 36, 54, 0.13), transparent 60%);
}

#embers { position: fixed; inset: 0; z-index: 0; pointer-events: none; }

/* Everything above the ambient layers. */
.shell { position: relative; z-index: 1; display: flex; flex-direction: column; min-height: 100vh; }
.shell > main { flex: 1 0 auto; }

img { max-width: 100%; display: block; }

a { color: var(--tallow); text-decoration-color: var(--tallow-ring); text-underline-offset: 0.2em; }
a:hover { color: var(--tallow-lit); }

:focus-visible {
  outline: 2px solid var(--tallow);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

/* Keyboard visitors get past the nav without tabbing all of it. */
.skip {
  position: absolute;
  left: 0.5rem;
  top: -3rem;
  z-index: 50;
  padding: 0.6rem 0.9rem;
  background: var(--iron-raised);
  border: 1px solid var(--tallow-dim);
  border-radius: var(--r-sm);
  color: var(--tallow);
  font-family: var(--data);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  transition: top var(--ease);
}
.skip:focus { top: 0.5rem; }

/* Present to a screen reader, absent to the eye. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

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

h1, h2, h3 { margin: 0; font-weight: 600; text-wrap: balance; line-height: 1.15; }

.wordmark {
  font-family: var(--display);
  font-size: clamp(1.15rem, 1rem + 0.7vw, 1.5rem);
  letter-spacing: 0.08em;
  color: var(--parchment);
}
.wordmark em { font-style: normal; color: var(--tallow); }

.display {
  font-family: var(--display);
  font-size: clamp(2.1rem, 1.4rem + 3.4vw, 4rem);
  letter-spacing: 0.02em;
  color: var(--parchment);
}

.eyebrow {
  font-family: var(--data);
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--tallow-dim);
}

.lede { color: var(--parchment-2); max-width: 42ch; font-size: 1.05rem; }

.num { font-family: var(--data); font-variant-numeric: tabular-nums; }

.muted { color: var(--parchment-3); }
.accent { color: var(--tallow); }

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

.page {
  max-width: var(--measure);
  margin: 0 auto;
  padding: clamp(1.5rem, 4vw, 3.5rem) clamp(1rem, 4vw, 2.5rem) 5rem;
}

/* The left rail carries section marks. It is information: each mark is a
   section of the record, so the rail is a table of contents, not a stripe. */
.railed { display: grid; grid-template-columns: var(--rail) minmax(0, 1fr); }
.railed > .rail { position: relative; }
.railed > .rail::before {
  content: "";
  position: absolute;
  left: 0.75rem;
  top: 0.4rem;
  bottom: 0.4rem;
  width: 1px;
  background: linear-gradient(180deg, var(--rime-bright), transparent);
}
.rail-mark {
  position: absolute;
  left: 0;
  top: 0.15rem;
  width: 1.5rem;
  height: 1.5rem;
  display: grid;
  place-items: center;
  background: var(--ink);
  color: var(--tallow-dim);
  font-family: var(--data);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
}

.section + .section { margin-top: clamp(2.5rem, 6vw, 4.5rem); }

.section-head {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.25rem;
}
.section-head h2 {
  font-family: var(--display);
  font-size: clamp(1.35rem, 1.1rem + 1vw, 1.9rem);
  letter-spacing: 0.02em;
}
.section-head .eyebrow { margin-left: auto; }

@media (max-width: 46rem) {
  .railed { grid-template-columns: 1fr; }
  .railed > .rail { display: none; }
}

.grid { display: grid; gap: 1rem; }

/* One panel standing where a pair used to. Left at full width it reads as a row that
   lost its other half, so it keeps a card's measure and centres under the rail. */
.solo { max-width: 34rem; margin-inline: auto; }
.g-3 { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
.g-2 { grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); }

/* The character sheet: figure on the left, ledger on the right. */
.sheet { display: grid; gap: clamp(1rem, 3vw, 1.75rem); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 60rem) {
  .sheet { grid-template-columns: minmax(20rem, 26rem) minmax(0, 1fr); align-items: start; }
}

/* The auth pages are a single measured card on the lit ground. */
.gate { position: relative; display: grid; place-items: center; padding: clamp(2rem, 8vh, 5rem) 1rem 6rem; }
.gate-card { position: relative; z-index: 1; width: min(30rem, 100%); }
.gate-card .panel { padding: 1.75rem clamp(1.25rem, 4vw, 2rem); }
.gate-head { text-align: center; margin-bottom: 1.6rem; }
.gate-head h1 { font-family: var(--display); font-size: 2rem; margin-bottom: 0.3rem; }
.gate-head p { margin: 0; color: var(--parchment-2); font-size: 0.95rem; }

/* ------------------------------------------------------------------ chrome */

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 1.25rem;
  padding: 0.85rem clamp(1rem, 4vw, 2.5rem);
  background: rgba(11, 14, 18, 0.88);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--rime);
}

.brand { display: flex; align-items: center; gap: 0.7rem; text-decoration: none; }

/* The A3 mark, in its own crimson. It is the single red thing in the chrome,
   which is what lets it read as a seal rather than as a warning — oxblood
   elsewhere means danger, and nothing else in the bar competes with it. */
/* Capped by height rather than sized to a square, because this is the configured
   branding.logo_path and its shape is whatever an operator uploaded: the bundled mark is
   a tall seal, somebody else's may be a wide wordmark, and forcing either into 30 square
   squashes one of them. */
.mark { height: 30px; width: auto; max-width: 10rem; flex: none; object-fit: contain; }

/* The same asset, repainted. The file is a flat silhouette on alpha, so its own
   alpha masks a painted box: one image serves both the crimson seal and the
   gold watermark without shipping a second copy. */
.mark-wash {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  display: grid;
  place-items: center;
  overflow: hidden;
}
.mark-wash i {
  width: min(38rem, 90vw);
  aspect-ratio: 737 / 859;
  background: var(--tallow);
  opacity: 0.05;
  -webkit-mask-image: var(--mark);
  mask-image: var(--mark);
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  transform: rotate(-8deg);
}

.topnav { margin-left: auto; display: flex; align-items: center; gap: 0.35rem; }

.navlink {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--data);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--parchment-2);
  text-decoration: none;
  padding: 0.5rem 0.7rem;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  background: none;
  cursor: pointer;
  white-space: nowrap;
}
.navlink svg { width: 0.95rem; height: 0.95rem; flex: none; }
.navlink:hover { color: var(--parchment); background: var(--iron); }
.navlink[aria-current="page"] {
  color: var(--tallow);
  border-color: var(--rime-bright);
  background: var(--tallow-wash);
}

/* Seventeen modules will not fit a bar. The ones a visitor uses live in it and
   the rest fall into this menu, which is also what the drawer is built from. */
.menu { position: relative; }
.menu > summary { list-style: none; cursor: pointer; }
.menu > summary::-webkit-details-marker { display: none; }
.menu > summary svg:last-child { width: 0.7rem; height: 0.7rem; transition: transform var(--ease); }
.menu[open] > summary { color: var(--parchment); background: var(--iron); }
.menu[open] > summary svg:last-child { transform: rotate(180deg); }
.menu-list {
  position: absolute;
  right: 0;
  top: calc(100% + 0.4rem);
  z-index: 30;
  min-width: 12rem;
  display: none;
  flex-direction: column;
  gap: 1px;
  padding: 0.3rem;
  background: var(--iron);
  border: 1px solid var(--rime-bright);
  border-radius: var(--r-sm);
}
.menu[open] .menu-list { display: flex; }
.menu-list .navlink { justify-content: flex-start; letter-spacing: 0.1em; }

/* The drawer is the whole navigation at a width the bar cannot hold. It is a <dialog>,
   so the browser owns the top layer, Esc, and returning focus to the burger on close. */
.burger { display: none; }

.drawer {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 1rem clamp(1rem, 4vw, 2.5rem) 3rem;
  border: 0;
  background: rgba(7, 10, 13, 0.97);
  color: var(--parchment);
  overflow-y: auto;
}
.drawer[open] { display: flex; flex-direction: column; gap: 0.3rem; }
.drawer::backdrop { background: rgba(7, 10, 13, 0.8); }
.drawer .drawer-head { display: flex; align-items: center; margin-bottom: 1rem; }
.drawer .drawer-head .brand { margin-right: auto; }
.drawer .navlink { justify-content: flex-start; font-size: 0.82rem; padding: 0.75rem 0.7rem; }
.drawer .eyebrow { margin-top: 0.9rem; padding: 0 0.7rem; }

@media (max-width: 60rem) {
  .topnav { display: none; }
  .burger { display: inline-flex; margin-left: auto; }
}

.foot {
  border-top: 1px solid var(--rime);
  margin-top: 4rem;
  padding: 1.5rem clamp(1rem, 4vw, 2.5rem);
  color: var(--parchment-3);
  font-size: 0.82rem;
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  justify-content: space-between;
}
.foot a { color: var(--parchment-2); text-decoration: none; }
.foot a:hover { color: var(--tallow); }
.foot .links { display: flex; gap: 1rem; flex-wrap: wrap; }

.crumbs {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
  font-family: var(--data);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--parchment-3);
}
.crumbs a { color: var(--parchment-2); text-decoration: none; }
.crumbs a:hover { color: var(--tallow); }
.crumbs svg { width: 0.7rem; height: 0.7rem; opacity: 0.6; }

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

.panel {
  background:
    linear-gradient(180deg, rgba(255,255,255,0.022), transparent 40%),
    var(--iron);
  border: 1px solid var(--rime);
  border-radius: var(--r-md);
  padding: 1.25rem 1.35rem;
}

/* The head is pulled out to the panel's own edges, so its padding is coupled to
   the panel's. Change one and change the other. */
.panel-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: -1.25rem -1.35rem 1.1rem;
  padding: 0.7rem 1.35rem;
  border-bottom: 1px solid var(--rime);
  background: rgba(0,0,0,0.22);
}
.panel-head h3 {
  font-family: var(--data);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--parchment-2);
}
.panel-head .spacer { margin-left: auto; }

.panel-foot {
  margin: 1.1rem -1.35rem -1.25rem;
  padding: 0.8rem 1.35rem;
  border-top: 1px solid var(--rime);
  background: rgba(0,0,0,0.18);
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}
.panel-foot .spacer { margin-left: auto; }

/* Destructive work is fenced off and stated plainly, never mixed into a form. */
.danger {
  border-color: rgba(142, 36, 54, 0.45);
  background:
    linear-gradient(180deg, rgba(142, 36, 54, 0.07), transparent 45%),
    var(--iron);
}
.danger .panel-head { border-bottom-color: rgba(142, 36, 54, 0.35); }
.danger .panel-head h3 { color: var(--oxblood-lit); }
.danger-row {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding: 0.8rem 0;
  border-bottom: 1px dashed var(--hairline);
}
.danger-row:last-child { border-bottom: 0; }
.danger-row .what { min-width: 0; }
.danger-row .what b { display: block; color: var(--parchment); font-weight: 600; }
.danger-row .what span { font-size: 0.86rem; color: var(--parchment-3); }
.danger-row .btn, .danger-row form { margin-left: auto; }

/* ------------------------------------------------------------------ ledger */

/* Label left, figure right, hairline between. Digits are tabular so the
   column reads down. */
.ledger { display: grid; gap: 0; margin: 0; }
.ledger div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.5rem 0;
  border-bottom: 1px dashed var(--hairline);
}
.ledger div:last-child { border-bottom: 0; }
.ledger dt, .ledger .k {
  color: var(--parchment-3);
  font-size: 0.86rem;
  letter-spacing: 0.02em;
}
.ledger dd, .ledger .v {
  margin: 0;
  font-family: var(--data);
  font-variant-numeric: tabular-nums;
  font-size: 0.95rem;
  color: var(--parchment);
  text-align: right;
}
.ledger dd.accent, .ledger .v.accent { color: var(--tallow); }

/* ------------------------------------------------------------------ status */

.state {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--data);
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 0.25rem 0.6rem;
  border: 1px solid var(--rime-bright);
  border-radius: var(--r-pill);
  color: var(--parchment-2);
  white-space: nowrap;
}
.state::before {
  content: "";
  width: 0.42rem; height: 0.42rem;
  border-radius: 50%;
  background: var(--parchment-3);
}
.state.live { color: var(--verdigris); border-color: var(--verdigris-edge); }
.state.live::before { background: var(--verdigris); box-shadow: 0 0 0 3px rgba(78,140,125,0.18); }
.state.gone::before { background: var(--oxblood-lit); }
.state.warn { color: var(--tallow-dim); border-color: var(--tallow-dim); }
.state.warn::before { background: var(--tallow-dim); }

/* A category, not a severity. */
.chip {
  display: inline-block;
  font-family: var(--data);
  font-size: 0.6rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.2rem 0.45rem;
  border: 1px solid var(--rime-bright);
  border-radius: var(--r-sm);
  color: var(--parchment-3);
  white-space: nowrap;
}
.chip.warn { color: var(--tallow-dim); border-color: var(--tallow-dim); }
.chip.danger { color: var(--oxblood-lit); border-color: var(--oxblood-edge); }
.chip.good { color: var(--verdigris); border-color: var(--verdigris-edge); }

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

.btn {
  --btn-bg: transparent;
  --btn-fg: var(--parchment);
  --btn-edge: var(--rime-bright);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-family: var(--data);
  font-size: 0.74rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 0.7rem 1.15rem;
  border: 1px solid var(--btn-edge);
  border-radius: var(--r-sm);
  background: var(--btn-bg);
  color: var(--btn-fg);
  cursor: pointer;
  text-decoration: none;
  transition: border-color var(--ease), background-color var(--ease), color var(--ease);
}
.btn svg { width: 0.95rem; height: 0.95rem; flex: none; }
.btn:hover { --btn-edge: var(--tallow-dim); --btn-bg: var(--tallow-wash); color: var(--tallow); }
.btn.primary {
  --btn-bg: var(--tallow);
  --btn-fg: #14100a;
  --btn-edge: var(--tallow);
  font-weight: 600;
}
.btn.primary:hover { --btn-bg: var(--tallow-lit); --btn-edge: var(--tallow-lit); color: #14100a; }
.btn.wide { width: 100%; }
.btn.danger { --btn-edge: rgba(142,36,54,0.7); color: var(--oxblood-lit); }
.btn.danger:hover { --btn-bg: rgba(142,36,54,0.16); --btn-edge: var(--oxblood-lit); color: var(--oxblood-lit); }
.btn.tiny { padding: 0.34rem 0.6rem; font-size: 0.64rem; letter-spacing: 0.1em; }
.btn.quiet { --btn-edge: transparent; color: var(--parchment-2); }
.btn.quiet:hover { --btn-edge: var(--rime-bright); }
.btn:disabled, .btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

.btn-row { display: flex; gap: 0.6rem; flex-wrap: wrap; align-items: center; }
.btn-row .spacer { margin-left: auto; }

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

.field { display: grid; gap: 0.4rem; }
.field + .field { margin-top: 1rem; }

.field > label, .lab {
  font-family: var(--data);
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--parchment-2);
}
.field .req { color: var(--oxblood-lit); margin-left: 0.2rem; }

.field input, .field select, .field textarea, .input {
  width: 100%;
  font-family: var(--body);
  font-size: 1rem;
  color: var(--parchment);
  background: var(--ink-deep);
  border: 1px solid var(--rime);
  border-radius: var(--r-sm);
  padding: 0.72rem 0.85rem;
  transition: border-color var(--ease), box-shadow var(--ease);
}
.field textarea { min-height: 7rem; resize: vertical; line-height: 1.5; }
.field input::placeholder, .field textarea::placeholder { color: var(--parchment-3); }
.field input:focus, .field select:focus, .field textarea:focus, .input:focus {
  outline: none;
  border-color: var(--tallow-dim);
  box-shadow: 0 0 0 3px var(--tallow-wash);
}
.field input:disabled, .field textarea:disabled, .field select:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.field .hint { font-size: 0.8rem; color: var(--parchment-3); }

/* An invalid field states the problem under itself; the border alone is not a
   message, and colour alone is not one either. */
.field.bad input, .field.bad select, .field.bad textarea {
  border-color: var(--oxblood-lit);
}
.field.bad input:focus, .field.bad select:focus, .field.bad textarea:focus {
  box-shadow: 0 0 0 3px var(--oxblood-wash);
}
.field .bad-note {
  display: flex;
  align-items: flex-start;
  gap: 0.35rem;
  font-size: 0.82rem;
  color: var(--oxblood-lit);
}
.field .bad-note svg { width: 0.9rem; height: 0.9rem; flex: none; margin-top: 0.2rem; }

.check { display: flex; align-items: center; gap: 0.55rem; font-size: 0.9rem; color: var(--parchment-2); }
.check input { accent-color: var(--tallow); width: 1rem; height: 1rem; flex: none; }

/* The captcha widget's own class, which its script finds and replaces with an iframe of
   a size we do not control. The slot is reserved at that height so the button below it
   does not jump down the moment the script lands -- which is exactly when somebody is
   reaching for it. */
.cf-turnstile { min-height: 65px; }

.form-foot {
  margin-top: 1.35rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  font-size: 0.9rem;
  color: var(--parchment-2);
}
.form-foot.right { justify-content: flex-end; }
.form-foot.center { justify-content: center; }

.select {
  font-family: var(--data);
  font-size: 0.74rem;
  letter-spacing: 0.06em;
  color: var(--parchment);
  background: var(--ink-deep);
  border: 1px solid var(--rime);
  border-radius: var(--r-sm);
  padding: 0.55rem 0.6rem;
}
.select:focus { outline: none; border-color: var(--tallow-dim); }

.search { position: relative; flex: 1 1 16rem; min-width: 0; }
.search svg {
  position: absolute; left: 0.65rem; top: 50%; transform: translateY(-50%);
  width: 0.95rem; height: 0.95rem; color: var(--parchment-3); pointer-events: none;
}
.search input {
  width: 100%;
  font-family: var(--body);
  font-size: 0.92rem;
  color: var(--parchment);
  background: var(--ink-deep);
  border: 1px solid var(--rime);
  border-radius: var(--r-sm);
  padding: 0.55rem 0.7rem 0.55rem 2.1rem;
}
.search input:focus { outline: none; border-color: var(--tallow-dim); box-shadow: 0 0 0 3px var(--tallow-wash); }

/* A switch reads as a state at a glance where a checkbox reads as a question.
   The markup must be label.switch > input + span.switch-track (+ span.lab),
   because the state is carried by adjacent-sibling selectors. */
.switch { display: inline-flex; align-items: center; gap: 0.6rem; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
  width: 2.4rem; height: 1.25rem;
  flex: none;
  border-radius: var(--r-pill);
  background: var(--ink-deep);
  border: 1px solid var(--rime-bright);
  position: relative;
  transition: background-color var(--ease), border-color var(--ease);
}
.switch-track::after {
  content: "";
  position: absolute;
  top: 50%; left: 0.2rem;
  transform: translateY(-50%);
  width: 0.8rem; height: 0.8rem;
  border-radius: 50%;
  background: var(--parchment-3);
  transition: transform var(--ease), background-color var(--ease);
}
.switch input:checked + .switch-track { background: var(--tallow-wash); border-color: var(--tallow-dim); }
.switch input:checked + .switch-track::after { transform: translateY(-50%) translateX(1.1rem); background: var(--tallow); }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--tallow); outline-offset: 2px; }
.switch input:disabled + .switch-track { opacity: 0.45; }
.switch .lab { text-transform: none; letter-spacing: 0.08em; font-size: 0.72rem; }

/* The settings form is generated from a typed tree, so each field shows the
   json path it writes — an operator reading the file and one reading the form
   see one name. */
.setting { display: grid; gap: 0.35rem; }
.setting .path {
  font-family: var(--data);
  font-size: 0.64rem;
  color: var(--parchment-3);
  word-break: break-all;
}
.setting input, .setting select {
  width: 100%;
  font-family: var(--body);
  font-size: 1rem;
  color: var(--parchment);
  background: var(--ink-deep);
  border: 1px solid var(--rime);
  border-radius: var(--r-sm);
  padding: 0.6rem 0.7rem;
}
.setting input.mono { font-family: var(--data); font-size: 0.9rem; }
.setting textarea {
  width: 100%;
  min-height: 6rem;
  font-family: var(--data);
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--parchment);
  background: var(--ink-deep);
  border: 1px solid var(--rime);
  border-radius: var(--r-sm);
  padding: 0.6rem 0.7rem;
  resize: vertical;
  white-space: pre;
  overflow-x: auto;
}
.setting input:focus, .setting select:focus, .setting textarea:focus {
  outline: none;
  border-color: var(--tallow-dim);
  box-shadow: 0 0 0 3px var(--tallow-wash);
}
.setting.bad input, .setting.bad select, .setting.bad textarea { border-color: var(--oxblood-lit); }
.setting .bad-note { font-size: 0.8rem; color: var(--oxblood-lit); }
.setting.full { grid-column: 1 / -1; }

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

/* Header cells carry a real button because they sort; the direction is drawn
   rather than described, and only the active column carries it. */
.toolbar { display: flex; gap: 0.6rem; flex-wrap: wrap; align-items: center; margin-bottom: 0.9rem; }
.toolbar .spacer { margin-left: auto; }

.table-wrap { overflow-x: auto; }
table.data { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
table.data th, table.data td { padding: 0.62rem 0.7rem; text-align: left; white-space: nowrap; }
table.data thead th {
  position: sticky; top: 0;
  background: var(--iron-raised);
  border-bottom: 1px solid var(--rime-bright);
  font-family: var(--data);
  font-size: 0.64rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--parchment-2);
}
table.data thead th.num { text-align: right; }
table.data thead th .sort {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-decoration: none;
}
table.data thead th .sort:hover { color: var(--tallow); }
table.data thead th .dir { opacity: 0; }
table.data thead th[aria-sort] { color: var(--tallow); }
table.data thead th[aria-sort] .dir { opacity: 1; }
table.data thead th .dir svg { width: 0.7rem; height: 0.7rem; }
table.data tbody tr { border-bottom: 1px solid rgba(42, 50, 60, 0.7); }
table.data tbody tr:hover { background: rgba(232, 200, 138, 0.035); }
table.data tbody tr:last-child { border-bottom: 0; }
table.data td.num { font-family: var(--data); font-variant-numeric: tabular-nums; text-align: right; }
table.data td.who { color: var(--parchment); }
table.data td.act { text-align: right; }
table.data td.act .btn + .btn, table.data td.act form { margin-left: 0.3rem; }
table.data td.wrap { white-space: normal; min-width: 16rem; }

.empty-row { padding: 2rem 0; text-align: center; color: var(--parchment-3); }

.pager {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  margin-top: 0.9rem;
  font-family: var(--data);
  font-size: 0.72rem;
  color: var(--parchment-3);
}
.pager .pages { display: flex; gap: 0.3rem; flex-wrap: wrap; }
.pager .pages .btn.on { --btn-edge: var(--tallow-dim); color: var(--tallow); background: var(--tallow-wash); }
.pager .gap { padding: 0.34rem 0.2rem; }

/* An empty state outside a table says what would be here and how to make one. */
.empty {
  display: grid;
  justify-items: center;
  gap: 0.6rem;
  padding: 3rem 1rem;
  text-align: center;
  color: var(--parchment-3);
}
.empty svg { width: 2rem; height: 2rem; color: var(--rime-bright); }
.empty b { color: var(--parchment-2); font-weight: 600; }
.empty p { margin: 0; max-width: 34ch; font-size: 0.9rem; }

/* ---------------------------------------------------------------- feedback */

/* The design carries one alert, in oxblood. A site needs four, so the other
   three are built from the same shape with their own semantic edge. Each keeps
   an icon, because colour alone does not carry a meaning. */
.notice {
  display: flex;
  gap: 0.85rem;
  padding: 0.9rem 1rem;
  background: var(--oxblood-wash);
  border: 1px solid var(--oxblood-edge);
  border-left-width: 2px;
  border-radius: var(--r-sm);
  font-size: 0.92rem;
  color: var(--parchment-2);
}
.notice svg { width: 1.15rem; height: 1.15rem; flex: none; color: var(--oxblood-lit); margin-top: 0.15rem; }
.notice b { color: var(--parchment); font-weight: 600; }
.notice p { margin: 0; }
.notice p + p { margin-top: 0.4rem; }

.notice.good { background: var(--verdigris-wash); border-color: var(--verdigris-edge); }
.notice.good svg { color: var(--verdigris); }
.notice.warn { background: var(--tallow-wash); border-color: var(--tallow-dim); }
.notice.warn svg { color: var(--tallow-dim); }
.notice.info { background: rgba(79, 143, 184, 0.08); border-color: rgba(79, 143, 184, 0.4); }
.notice.info svg { color: var(--azure); }

.notice + .notice, .notice + .panel, .panel + .notice { margin-top: 1rem; }

/* Transient feedback, raised by an HX-Trigger rather than by a page load. */
.toasts {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  max-width: min(24rem, calc(100vw - 2rem));
}
.toast {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.75rem 0.9rem;
  background: var(--iron-raised);
  border: 1px solid var(--rime-bright);
  border-left-width: 2px;
  border-radius: var(--r-sm);
  font-size: 0.9rem;
  color: var(--parchment);
  animation: rise .24s cubic-bezier(.2,.7,.3,1) both;
}
.toast svg { width: 1rem; height: 1rem; flex: none; margin-top: 0.2rem; }
.toast.good { border-left-color: var(--verdigris); }
.toast.good svg { color: var(--verdigris); }
.toast.bad { border-left-color: var(--oxblood-lit); }
.toast.bad svg { color: var(--oxblood-lit); }
.toast.info svg { color: var(--tallow-dim); }
.toast.leaving { opacity: 0; transition: opacity .3s; }

/* A tooltip on anything, with no JavaScript: the text is an attribute, so it
   cannot go stale against the element it describes. */
[data-tip] { position: relative; }
[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + 0.35rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: 15;
  min-width: max-content;
  max-width: 16rem;
  padding: 0.4rem 0.55rem;
  background: var(--ink-deep);
  border: 1px solid var(--rime-bright);
  border-radius: var(--r-sm);
  font-family: var(--body);
  font-size: 0.78rem;
  line-height: 1.35;
  letter-spacing: normal;
  text-transform: none;
  white-space: normal;
  color: var(--parchment);
  opacity: 0;
  visibility: hidden;
  transition: opacity .14s;
  pointer-events: none;
}
[data-tip]:hover::after, [data-tip]:focus-visible::after { opacity: 1; visibility: visible; }

/* ---------------------------------------------------------------- overlays */

/* One dialog element, so the browser owns the top layer, the backdrop, Esc and
   the focus trap. app.js only opens and closes it. */
.modal {
  width: min(32rem, calc(100vw - 2rem));
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--parchment);
}
.modal::backdrop { background: rgba(7, 10, 13, 0.72); backdrop-filter: blur(2px); }
.modal .panel { margin: 0; }
.modal-head { display: flex; align-items: center; gap: 0.75rem; }
.modal-head h3 { font-family: var(--display); font-size: 1.25rem; letter-spacing: 0.02em; color: var(--parchment); }
.modal-head .close {
  margin-left: auto;
  display: grid;
  place-items: center;
  width: 1.75rem; height: 1.75rem;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  color: var(--parchment-3);
  cursor: pointer;
}
.modal-head .close:hover { color: var(--tallow); border-color: var(--rime-bright); }
.modal-head .close svg { width: 0.9rem; height: 0.9rem; }
.modal-body { color: var(--parchment-2); font-size: 0.94rem; }
.modal-body p { margin: 0 0 0.6rem; }
.modal-body p:last-child { margin-bottom: 0; }
.modal-foot { margin-top: 1.35rem; display: flex; gap: 0.6rem; justify-content: flex-end; flex-wrap: wrap; }

/* Tabs are links to real routes, so a tab is shareable and survives a reload. */
.tabs {
  display: flex;
  gap: 0.3rem;
  flex-wrap: wrap;
  margin-bottom: 1.25rem;
  border-bottom: 1px solid var(--rime);
}
.tabs a {
  padding: 0.6rem 0.9rem;
  font-family: var(--data);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--parchment-2);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.tabs a:hover { color: var(--parchment); }
.tabs a[aria-current="page"] { color: var(--tallow); border-bottom-color: var(--tallow); }

/* Native <details>, so disclosure works with no script at all. */
.fold + .fold { border-top: 1px solid var(--hairline); }
.fold > summary {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.85rem 0;
  cursor: pointer;
  list-style: none;
  font-size: 1rem;
  color: var(--parchment);
}
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary:hover { color: var(--tallow); }
.fold > summary svg { width: 0.8rem; height: 0.8rem; margin-left: auto; transition: transform var(--ease); }
.fold[open] > summary { color: var(--tallow); }
.fold[open] > summary svg { transform: rotate(180deg); }
.fold .fold-body { padding: 0 0 1rem; color: var(--parchment-2); font-size: 0.94rem; }

/* ----------------------------------------------------------------- figures */

.roster { display: grid; gap: 0.75rem; }

.card-link {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.9rem 1rem;
  background: var(--iron);
  border: 1px solid var(--rime);
  border-left: 2px solid var(--rime-bright);
  border-radius: var(--r-md);
  text-decoration: none;
  color: inherit;
  transition: border-color var(--ease), background-color var(--ease), transform var(--ease);
}
.card-link:hover {
  background: var(--iron-raised);
  border-left-color: var(--tallow);
  transform: translateX(2px);
}

.crest {
  width: 3rem; height: 3rem; flex: none;
  display: grid; place-items: center;
  border: 1px solid var(--rime);
  border-radius: var(--r-sm);
  background: var(--ink-deep);
  color: var(--tallow-dim);
}
.crest svg { width: 1.5rem; height: 1.5rem; }

.card-link .who { min-width: 0; }
.card-link .name { display: block; font-family: var(--display); font-size: 1.2rem; letter-spacing: 0.02em; }
.card-link .meta { display: block; font-size: 0.85rem; color: var(--parchment-3); }
.card-link .tail { margin-left: auto; text-align: right; }

.hero-name {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  margin-bottom: 1.5rem;
}
.hero-name .crest { width: 3.75rem; height: 3.75rem; }
.hero-name .crest svg { width: 2rem; height: 2rem; }
.hero-name h1 { font-family: var(--display); font-size: clamp(1.9rem, 1.4rem + 2vw, 2.9rem); }
.hero-name .meta { font-family: var(--data); font-size: 0.78rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--parchment-3); }
.hero-name .tail { margin-left: auto; }

/* Equipment figure. Nine named positions, drawn whether or not filled: on a
   figure the gaps are information. */
.doll { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; }

.slot {
  position: relative;
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  background:
    radial-gradient(circle at 50% 30%, rgba(232,200,138,0.05), transparent 70%),
    var(--ink-deep);
  border: 1px solid var(--rime);
  border-radius: var(--r-sm);
  color: var(--parchment-3);
  cursor: default;
  transition: border-color var(--ease), background-color var(--ease);
}
.slot svg { width: 46%; height: 46%; }
.slot.empty svg { opacity: 0.28; }
.slot.filled { color: var(--tallow); border-color: var(--rime-bright); }
.slot.filled::after {
  content: attr(data-lv);
  position: absolute;
  right: 0.28rem; bottom: 0.16rem;
  font-family: var(--data);
  font-size: 0.62rem;
  color: var(--parchment-2);
}
.slot:hover { border-color: var(--tallow-dim); background-color: var(--iron); }
/* Blessed reads as an inner gold hairline rather than another colour. */
.slot.blessed { box-shadow: inset 0 0 0 1px var(--tallow-ring); }

/* The slot's own label, revealed on hover — the figure stays quiet until read. */
.slot .tip {
  position: absolute;
  bottom: calc(100% + 0.35rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: 5;
  min-width: max-content;
  max-width: 14rem;
  padding: 0.45rem 0.6rem;
  background: var(--ink-deep);
  border: 1px solid var(--rime-bright);
  border-radius: var(--r-sm);
  font-size: 0.78rem;
  line-height: 1.35;
  color: var(--parchment);
  opacity: 0;
  visibility: hidden;
  transition: opacity .14s;
  pointer-events: none;
}
.slot .tip b { display: block; color: var(--tallow); font-weight: 600; }
.slot .tip span { color: var(--parchment-3); font-family: var(--data); font-size: 0.7rem; }
.slot:hover .tip, .slot:focus-visible .tip { opacity: 1; visibility: visible; }

.bars { display: grid; gap: 0.65rem; }
.bar-row { display: grid; gap: 0.3rem; }
.bar-row .top { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; }
.bar-row .k { font-size: 0.8rem; color: var(--parchment-3); }
.bar-row .v { font-family: var(--data); font-variant-numeric: tabular-nums; font-size: 0.82rem; }
/* Named apart from the switch's track: both were ".track" in the study and only
   the parent kept them apart. */
.bar-track { height: 5px; background: var(--ink-deep); border: 1px solid var(--rime); border-radius: var(--r-pill); overflow: hidden; }
.fill { height: 100%; border-radius: var(--r-pill); }
.fill.vit { background: linear-gradient(90deg, var(--oxblood), var(--oxblood-lit)); }
.fill.man { background: linear-gradient(90deg, var(--azure-deep), var(--azure)); }
.fill.exp { background: linear-gradient(90deg, var(--tallow-dim), var(--tallow)); }

.wallet { display: grid; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr)); gap: 0.6rem; }
.coin {
  padding: 0.75rem 0.85rem;
  background: var(--ink-deep);
  border: 1px solid var(--rime);
  border-radius: var(--r-sm);
}
.coin .k { display: block; font-family: var(--data); font-size: 0.62rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--parchment-3); }
.coin .v { display: block; font-family: var(--data); font-variant-numeric: tabular-nums; font-size: 1.15rem; color: var(--tallow); margin-top: 0.15rem; }

/* -------------------------------------------------------------------- home */

/* The hero opens on the mark itself: the crest washed huge behind the name, the
   thesis under it in one sentence. Full-bleed rather than measured, so the
   ground's forge glow has somewhere to land. */
.hero {
  position: relative;
  overflow: hidden;
  padding: clamp(3.5rem, 12vh, 7rem) clamp(1rem, 4vw, 2.5rem) clamp(2.5rem, 7vh, 4.5rem);
  border-bottom: 1px solid var(--rime);
  text-align: center;
}
.hero .mark-wash i { width: min(46rem, 108vw); opacity: 0.06; }

.hero-inner { position: relative; z-index: 1; max-width: 52rem; margin: 0 auto; }

.hero h1 {
  font-family: var(--display);
  font-size: clamp(2.6rem, 1.6rem + 5.4vw, 5.5rem);
  letter-spacing: 0.015em;
  line-height: 1.02;
  margin: 0.6rem 0 0;
}
.hero h1 em { font-style: normal; color: var(--tallow); }

.thesis {
  margin: 1.1rem auto 0;
  max-width: 44ch;
  font-size: clamp(1.02rem, 0.98rem + 0.3vw, 1.2rem);
  color: var(--parchment-2);
}

.hero-actions {
  margin-top: 2rem;
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
}

/* The live strip: what a returning player checks before anything else. */
.strip {
  margin-top: 2.25rem;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem clamp(1.5rem, 5vw, 3.5rem);
}
.strip div { padding: 0.35rem 0; }
.strip .k {
  display: block;
  font-family: var(--data);
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--parchment-3);
  margin-bottom: 0.3rem;
}
.strip .v {
  font-family: var(--data);
  font-variant-numeric: tabular-nums;
  font-size: 1.15rem;
  color: var(--parchment);
}
.strip .v.accent { color: var(--tallow); }

/* Three steps, numbered because they genuinely are a sequence: you cannot pick
   a nation before you have a client, or download before you have an account. */
.steps { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
.step { position: relative; padding-top: 2.4rem; }
.step .ord {
  position: absolute;
  top: 0.9rem;
  left: 1.35rem;
  font-family: var(--data);
  font-size: 0.68rem;
  letter-spacing: 0.2em;
  color: var(--tallow-dim);
}
.step h3 { font-family: var(--display); font-size: 1.3rem; margin-bottom: 0.4rem; }
.step p { margin: 0; color: var(--parchment-2); font-size: 0.94rem; }

/* One dispatch. The kind is a chip because it is a category, not a severity. */
.dispatch {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: 0.8rem 0;
  border-bottom: 1px dashed var(--hairline);
  text-decoration: none;
  color: inherit;
}
.dispatch:last-child { border-bottom: 0; }
.dispatch .title { color: var(--parchment); }
.dispatch:hover .title { color: var(--tallow); }
.dispatch .when {
  margin-left: auto;
  font-family: var(--data);
  font-variant-numeric: tabular-nums;
  font-size: 0.78rem;
  color: var(--parchment-3);
  white-space: nowrap;
}

@media (max-width: 34rem) {
  .dispatch { flex-wrap: wrap; gap: 0.5rem; }
  .dispatch .when { margin-left: 0; width: 100%; }
}

/* ------------------------------------------------------------------- admin */

html:has(.admin-page) { scrollbar-gutter: stable; }
.admin-page { max-width: none; width: 100%; padding-inline: clamp(1rem, 2.5vw, 2.5rem); }
.admin > .grid { min-width: 0; align-content: start; }

/* The panel is denser than the site: an operator scans it rather than reading
   it, so the type drops a step and the rhythm tightens. */
.admin { display: grid; grid-template-columns: 13rem minmax(0, 1fr); gap: clamp(1rem, 3vw, 2rem); }
@media (max-width: 60rem) { .admin { grid-template-columns: 1fr; } }

.sidenav { display: flex; flex-direction: column; gap: 1px; align-self: start; position: sticky; top: 5rem; }
@media (max-width: 60rem) {
  .sidenav { position: static; flex-direction: row; flex-wrap: wrap; gap: 0.35rem; }
}
.sidenav a {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 0.75rem;
  font-family: var(--data);
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--parchment-2);
  text-decoration: none;
  border-left: 2px solid transparent;
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
@media (max-width: 60rem) {
  .sidenav a { border-left: 0; border: 1px solid var(--rime); border-radius: var(--r-sm); }
}
.sidenav a:hover { background: var(--iron); color: var(--parchment); }
.sidenav a.on { color: var(--tallow); background: var(--tallow-wash); border-left-color: var(--tallow); }
.sidenav a svg { width: 0.9rem; height: 0.9rem; flex: none; }
.sidenav-group { padding-block: 0.35rem; }
.sidenav-label { display: flex; align-items: center; gap: 0.6rem; padding: 0.55rem 0.85rem; color: var(--parchment-2); font-family: var(--data); font-size: 0.74rem; letter-spacing: 0.08em; text-transform: uppercase; }
.sidenav-label svg { width: 0.9rem; height: 0.9rem; }
.sidenav-submenu { margin-left: 1.25rem; border-left: 1px solid var(--rime); }
.sidenav-submenu a { font-size: 0.72rem; letter-spacing: 0; text-transform: none; }
@media (max-width: 60rem) {
  .sidenav-group { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem; flex-basis: 100%; }
  .sidenav-submenu { display: flex; gap: 0.35rem; margin: 0; border: 0; }
}
.sidenav .count {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  font-size: 0.68rem;
  color: var(--parchment-3);
}

/* Stat tiles. The number is the point, so the sparkline sits behind it rather
   than beside it, and the delta carries its own semantic colour. */
.tiles { display: grid; gap: 0.75rem; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }
.tile { position: relative; overflow: hidden; padding: 0.9rem 1rem 1rem; }
.tile .k {
  font-family: var(--data);
  font-size: 0.6rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--parchment-3);
}
.tile .v {
  font-family: var(--data);
  font-variant-numeric: tabular-nums;
  font-size: 1.7rem;
  line-height: 1.15;
  color: var(--parchment);
  margin-top: 0.15rem;
}
.tile .d { font-family: var(--data); font-size: 0.68rem; }
.tile .d.up { color: var(--verdigris); }
.tile .d.down { color: var(--oxblood-lit); }
.tile .d.flat { color: var(--parchment-3); }
.tile svg.spark { position: absolute; right: 0; bottom: 0; width: 62%; height: 42%; opacity: 0.5; }

/* A payment code, drawn by this server and inlined as a data URI. It keeps a white ground
   of its own: a scanner needs the contrast, and the page's is the wrong way round. */
.qr {
  width: min(20rem, 100%);
  margin: 0 auto 1rem;
  padding: 0.75rem;
  background: #fff;
  border-radius: var(--r-sm);
}

/* ------------------------------------------------------------------ thread */

/* A ticket and its replies. What somebody typed is rendered as text and never as markup,
   so the line breaks they used are all the structure there is -- pre-wrap is what keeps
   them without interpreting anything. */
.thread { display: grid; gap: 0.75rem; }

.post .byline {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-bottom: 0.6rem;
  font-family: var(--data);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--parchment-3);
}
.post .byline .who { color: var(--parchment-2); }
.post .byline .spacer { margin-left: auto; }

/* A reply from the desk, so a player can tell an answer from their own message at a
   glance rather than by reading the name. */
.post.staff { border-left: 2px solid var(--tallow-dim); }
.post.staff .byline .who { color: var(--tallow); }

.message {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  color: var(--parchment-2);
  font-size: 0.95rem;
}

/* ------------------------------------------------------------------- prose */

/* News articles and the standing content pages are the only long-form text on
   the site, and the study had none. Measured, quiet, one accent.

   Centred inside its panel rather than left in it. The measure is 68ch and the
   panel is as wide as the page, so ranged left the text stops around halfway and
   the rest of the panel reads as a second, empty column. */
.prose { max-width: 68ch; margin-inline: auto; color: var(--parchment-2); }
.prose > :first-child { margin-top: 0; }
.prose > :last-child { margin-bottom: 0; }
.prose p { margin: 0 0 1.1rem; }
.prose h2 {
  font-family: var(--display);
  font-size: 1.5rem;
  letter-spacing: 0.02em;
  color: var(--parchment);
  margin: 2rem 0 0.75rem;
}
.prose h3 {
  font-family: var(--data);
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--tallow-dim);
  margin: 1.75rem 0 0.6rem;
}
.prose h4 { font-family: var(--body); font-size: 1.05rem; color: var(--parchment); margin: 1.5rem 0 0.5rem; }
.prose ul, .prose ol { margin: 0 0 1.1rem; padding-left: 1.35rem; }
.prose li { margin-bottom: 0.35rem; }
.prose li::marker { color: var(--tallow-dim); }
.prose blockquote {
  margin: 0 0 1.1rem;
  padding: 0.2rem 0 0.2rem 1rem;
  border-left: 2px solid var(--rime-bright);
  color: var(--parchment-3);
  font-style: italic;
}
.prose hr { border: 0; border-top: 1px solid var(--rime); margin: 2rem 0; }
.prose code {
  font-family: var(--data);
  font-size: 0.86em;
  padding: 0.1rem 0.3rem;
  background: var(--ink-deep);
  border: 1px solid var(--rime);
  border-radius: var(--r-sm);
  color: var(--tallow);
}
.prose pre {
  margin: 0 0 1.1rem;
  padding: 0.9rem 1rem;
  background: var(--ink-deep);
  border: 1px solid var(--rime);
  border-radius: var(--r-sm);
  overflow-x: auto;
  font-family: var(--data);
  font-size: 0.84rem;
  line-height: 1.55;
  color: var(--parchment);
}
.prose pre code { padding: 0; background: none; border: 0; color: inherit; }
.prose table { width: 100%; border-collapse: collapse; margin: 0 0 1.1rem; font-size: 0.92rem; }
.prose th, .prose td { padding: 0.5rem 0.6rem; text-align: left; border-bottom: 1px solid var(--hairline); }
.prose th { color: var(--parchment); font-family: var(--data); font-size: 0.68rem; letter-spacing: 0.12em; text-transform: uppercase; }
.prose img { margin: 0 0 1.1rem; border: 1px solid var(--rime); border-radius: var(--r-sm); }
.prose strong { color: var(--parchment); }

/* --------------------------------------------------------------- utilities */

/* Deliberately few. A style attribute is blocked by the policy, so these exist
   to carry the handful of one-off nudges that would otherwise want one. */
.spacer { margin-left: auto; }
.stack { display: grid; gap: 1rem; }
.stack-tight { display: grid; gap: 0.5rem; }
.right { text-align: right; }
.center { text-align: center; }
.nowrap { white-space: nowrap; }
.full { grid-column: 1 / -1; }

/* A bar's width is the one dimension that comes from data. Inline widths are
   blocked, so barClass snaps a percentage to a 5% step and names a class. */
.bar-0   { width: 0; }
.bar-5   { width: 5%; }
.bar-10  { width: 10%; }
.bar-15  { width: 15%; }
.bar-20  { width: 20%; }
.bar-25  { width: 25%; }
.bar-30  { width: 30%; }
.bar-35  { width: 35%; }
.bar-40  { width: 40%; }
.bar-45  { width: 45%; }
.bar-50  { width: 50%; }
.bar-55  { width: 55%; }
.bar-60  { width: 60%; }
.bar-65  { width: 65%; }
.bar-70  { width: 70%; }
.bar-75  { width: 75%; }
.bar-80  { width: 80%; }
.bar-85  { width: 85%; }
.bar-90  { width: 90%; }
.bar-95  { width: 95%; }
.bar-100 { width: 100%; }

/* ------------------------------------------------------------------ motion */

/* htmx swap states. The indicator is hidden until its request is in flight. */
.htmx-indicator { opacity: 0; transition: opacity var(--ease); }
.htmx-request .htmx-indicator, .htmx-request.htmx-indicator { opacity: 1; }
.htmx-swapping { opacity: 0.4; transition: opacity .12s; }

@keyframes rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

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

/* PK announcements: compose beside a readable sample of the actual shout. */
#pk-editor { min-width: 0; }
#pk-editor .lede { margin-top: 0; }
.pk-enable { padding-block: 1rem 1.5rem; border-bottom: 1px solid var(--rime); }
.pk-enable p, .pk-help, .pk-reference, #pk-editor .hint { color: var(--parchment-2); font-size: 0.9rem; }
.pk-enable p { max-width: 75ch; margin-bottom: 0; }
.pk-compose { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); gap: clamp(1.5rem, 3vw, 3rem); margin-block: 2rem; align-items: start; }
.pk-compose > * { min-width: 0; }
#pk-template { font-family: var(--data); font-size: 0.9rem; }
.pk-fillers { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.85rem; }
.pk-fillers .btn { text-transform: none; letter-spacing: 0; font-size: 0.75rem; min-height: 2.5rem; gap: 0; }
.pk-filters { min-width: 0; margin: 1.75rem 0 0; padding: 0; border: 0; }
.pk-filters legend { color: var(--parchment); font-weight: 600; padding: 0; }
.pk-levels { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; margin-bottom: 1rem; }
.pk-levels .field { margin: 0; }
.boss-monsters { position: relative; border: 1px solid var(--rime); border-radius: var(--r-md); background: var(--iron); }
.boss-monsters summary { padding: 0.75rem 1rem; cursor: pointer; }
.boss-monster-menu { padding: 1rem; border-top: 1px solid var(--rime); }
.boss-monster-options { max-height: 18rem; overflow-y: auto; }
.boss-monster-option { display: flex; align-items: center; gap: 0.75rem; padding: 0.5rem 0; cursor: pointer; }
.boss-monster-option[hidden] { display: none; }
.boss-monster-option input { width: auto; flex: none; }
.boss-monster-option small, .boss-monsters-selected small { color: var(--parchment-2); }
.boss-monsters-selected { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0; padding: 0; list-style: none; }
.boss-monsters-selected[hidden] { display: none; }
.boss-monsters-selected li { min-width: 0; max-width: 100%; padding: 0.4rem 0.65rem; border: 1px solid var(--rime); border-radius: var(--r-md); background: var(--iron); overflow-wrap: anywhere; }
.pk-preview { padding: 1.5rem; background: var(--iron); border: 1px solid var(--rime); border-radius: var(--r-md); }
.pk-preview h3 { font-family: var(--body); font-size: 1.25rem; letter-spacing: 0; }
.pk-chat { margin-block: 1.5rem 0.75rem; padding: 1rem; min-height: 7rem; background: var(--ink-deep); font-family: var(--data); font-size: 0.95rem; color: var(--parchment); overflow-wrap: anywhere; }
#pk-preview-announcer { color: var(--tallow); }
.pk-reference { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.75rem 1rem; margin-top: 1.5rem; border-top: 1px solid var(--rime); padding-top: 1.25rem; }
.pk-reference dt { color: var(--parchment); }
.pk-reference dd { margin: 0; }
.pk-feedback { min-height: 1.6em; color: var(--parchment-2); font-size: 0.9rem; overflow-wrap: anywhere; }
.pk-feedback[data-error="true"] { color: #f2a2af; }
#pk-editor [aria-invalid="true"] { border-color: var(--oxblood-lit); }
.pk-save { display: flex; gap: 1.5rem; align-items: end; border-top: 1px solid var(--rime); padding-top: 1.5rem; }
.pk-save .field { flex: 1; }
.pk-save input::placeholder { color: var(--parchment-2); }
#pk-editor .fold { margin-top: 1rem; }
#pk-editor .notice { overflow-wrap: anywhere; }
@media (max-width: 75rem) { .pk-compose { grid-template-columns: 1fr; } }
@media (max-width: 40rem) {
  .pk-levels { grid-template-columns: 1fr; }
  .pk-save { flex-direction: column; align-items: stretch; }
  .pk-preview { padding: 1rem; }
  .pk-reference { grid-template-columns: 1fr; gap: 0.25rem; }
  .pk-reference dd { margin-bottom: 0.75rem; }
}

/* Gameplay starter editor. */
/* Starter controls reuse the website's field, check, switch and button styles. */
#gameplay-form > .panel + .panel { margin-top: 1.25rem; }
.gameplay-fields, .gameplay-actions, .starter-options {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: end;
  margin-block: 1rem;
}
#gameplay-editor .field { min-width: 0; }
#gameplay-editor .bad-note:empty { display: none; }
#gameplay-editor .check.bad { flex-wrap: wrap; }
#gameplay-editor .check .bad-note { flex-basis: 100%; color: var(--oxblood-lit); font-size: 0.8rem; }
#gameplay-editor .field + .field { margin-top: 0; }
.gameplay-fields > .field, .starter-options > .field { flex: 1 1 12rem; }
#gameplay-editor .field input, #gameplay-editor .field select { min-width: 0; max-width: 100%; }
#gameplay-editor input, #gameplay-editor select { color-scheme: dark; }
#gameplay-editor .check { min-height: 2.75rem; }
#gameplay-editor .gameplay-help, #starter-fields p {
  color: var(--parchment-2);
  font-size: 0.9rem;
  max-width: 75ch;
}
.starter-section { margin-top: 2rem; }
.starter-heading {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  border-bottom: 1px solid var(--rime);
  padding-bottom: 0.6rem;
}
.starter-heading h3 { margin: 0; }
#gameplay-editor fieldset {
  min-width: 0;
  border: 0;
  padding: 0;
  margin: 0;
}
#gameplay-editor fieldset:disabled {
  padding-inline: 1rem;
  background: var(--ink-deep);
  border-inline-start: 2px solid var(--rime);
}
#gameplay-editor .starter-row { border-bottom: 1px solid var(--hairline); padding-block: 1rem; }
#gameplay-editor .starter-row h4 { margin: 0; color: var(--parchment); }
.starter-option { display: flex; gap: 0.5rem; align-items: end; }
.starter-option > .field { flex: 1; }
#gameplay-editor .starter-option .btn { margin-bottom: 0.25rem; }
#gameplay-editor [data-colour="red"] .lab { color: #eda6a0; }
#gameplay-editor [data-colour="blue"] .lab { color: #a7c9f2; }
#gameplay-editor [data-colour="grey"] .lab { color: #c4c5c9; }
#starter-fields p[data-error="true"] { color: #eda6a0; }
#gameplay-editor fieldset > .btn { margin-top: 1rem; }
#gameplay-editor details { border-top: 1px solid var(--hairline); padding-block: 1rem; }
#gameplay-editor summary { cursor: pointer; color: var(--parchment-2); }
#gameplay-editor pre {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-height: 30rem;
  overflow: auto;
  background: var(--ink-deep);
  border: 1px solid var(--rime);
  padding: 1rem;
  font-size: 0.8rem;
}
#gameplay-editor pre:empty { display: none; }
#gameplay-status { color: var(--parchment-2); font-size: 0.9rem; }
#gameplay-status[data-error=true] { color: var(--oxblood-lit); }
@media (max-width: 640px) {
  .gameplay-fields > .field, .starter-options > .field { flex-basis: 100%; }
  .gameplay-actions > .btn { width: 100%; }
}
