/* Waaagh! War — Grimdark Future tracker.
   Own palette on purpose: per-app identity is deliberately NOT unified across
   the suite. Hive is dark green and rust; this is cold steel and amber.

   suite.css is loaded FIRST and owns html{font-size}, --font-head/--font-body
   and .btn sizing. Do not declare those here — they sit at equal specificity,
   so load order would not win; the local declaration has to be absent. Only
   colour modifiers below. */

:root {
  --gw-bg:        #14181c;
  --gw-panel:     #1b2127;
  --gw-panel-2:   #222a31;
  --gw-line:      #2e3942;
  --gw-ink:       #d7dee4;
  --gw-dim:       #8b98a3;
  --gw-amber:     #d9a441;
  --gw-amber-dk:  #8a6a24;
  --gw-danger:    #b9503f;
  --gw-good:      #6f9f5e;
  --gw-shaken:    #c08a2e;
  /* The two recessed shades. These were hardcoded at six call sites, which is
     how the hero box and the rule chips ended up dark-on-dark under e-ink: the
     flatten below only ever reached values that went through a token. */
  --gw-sunk:      #101418;
  --gw-inset:     #1a2026;
  /* "has acted this round" - deliberately a cool slate, not a warning colour:
     it is bookkeeping, not a problem with the unit. */
  --gw-done:      #4a5a66;
}

* { box-sizing: border-box; }
html, body { margin: 0; }
body { background: var(--gw-bg); color: var(--gw-ink); }

/* The `hidden` attribute is only a UA-stylesheet `display: none`, so ANY author
   `display:` rule silently beats it. `.gw-modal { display: flex }` did exactly
   that and shipped both modals open on the lobby. Global and !important on
   purpose: every element toggled by .hidden here (modals, rule popup, toast)
   would otherwise need remembering one at a time. */
[hidden] { display: none !important; }

a { color: var(--gw-amber); }

/* ── Header ─────────────────────────────────────────────────────────────── */
.gw-top { position: sticky; top: 0; z-index: 40; background: var(--gw-sunk); border-bottom: 2px solid var(--gw-line); }
.gw-top-inner { max-width: 1400px; margin: 0 auto; padding: 10px 16px; display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.gw-brand { font-family: var(--font-head); font-size: 1.25rem; letter-spacing: .06em; text-decoration: none; color: var(--gw-ink); display: inline-flex; align-items: center; gap: 10px; }
.gw-brand-hi { color: var(--gw-amber); }
/* Sized in the markup too, so the row does not jump while the JS swaps the
   real src in - every other app's header logo has the same shape. */
.gw-logo { height: 34px; width: 34px; display: block; flex: none; }
.gw-sub { font-size: .62rem; letter-spacing: .18em; text-transform: uppercase; color: var(--gw-dim); }
.gw-spacer { flex: 1 1 auto; }
.gw-eink, .gw-waaagh, .gw-auth, .gw-dice, .gw-theme {
  font-family: var(--font-head); font-size: .66rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--gw-dim); background: none; border: 1px solid var(--gw-line);
  padding: 8px 14px; min-height: 40px; border-radius: 3px; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px; text-decoration: none;
}
.gw-eink:hover, .gw-waaagh:hover, .gw-auth:hover, .gw-dice:hover { color: var(--gw-amber); border-color: var(--gw-amber-dk); }
.gw-eink input { accent-color: var(--gw-amber); margin: 0; }

.gw-wrap { max-width: 1400px; margin: 0 auto; padding: 20px 16px 60px; }

/* ── Panels ─────────────────────────────────────────────────────────────── */
.gw-panel { background: var(--gw-panel); border: 1px solid var(--gw-line); border-radius: 6px; padding: 18px; margin-bottom: 18px; }
.gw-panel h1, .gw-panel h2 { font-family: var(--font-head); margin: 0 0 10px; letter-spacing: .04em; }
.gw-panel h1 { font-size: 1.4rem; }
.gw-panel h2 { font-size: 1rem; color: var(--gw-amber); }
.gw-note { font-size: .82rem; color: var(--gw-dim); line-height: 1.55; margin: 0 0 12px; }
.gw-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 10px; }
.gw-msg { font-size: .82rem; color: var(--gw-danger); }
.gw-msg.ok { color: var(--gw-good); }

/* type="search" was missing here, so the glossary box fell back to the
   browser's own control - white, system font - on a dark panel. */
textarea, input[type="text"], input[type="password"], input[type="search"] {
  width: 100%; background: var(--gw-sunk); color: var(--gw-ink);
  border: 1px solid var(--gw-line); border-radius: 4px; padding: 10px 12px;
  font: inherit; font-size: .86rem;
}
textarea { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: .76rem; line-height: 1.5; resize: vertical; }

.btn-primary { background: var(--gw-amber); border-color: var(--gw-amber); color: var(--gw-bg); font-weight: 700; }
.btn-primary:hover { background: #e8b95a; }
.btn-danger { border-color: var(--gw-danger); color: var(--gw-danger); background: none; }
.btn-danger:hover { background: var(--gw-danger); color: #fff; }

/* ── Saved lists ────────────────────────────────────────────────────────── */
.gw-lib { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.gw-lib li { display: flex; gap: 8px; align-items: stretch; }
.gw-lib-load {
  flex: 1; text-align: left; background: var(--gw-panel-2); border: 1px solid var(--gw-line);
  border-radius: 4px; padding: 10px 12px; cursor: pointer; color: var(--gw-ink); font: inherit;
}
.gw-lib-load:hover { border-color: var(--gw-amber-dk); }
.gw-lib-load strong { display: block; font-size: .9rem; }
.gw-lib-load span { font-size: .74rem; color: var(--gw-dim); }
.gw-del {
  background: none; border: 1px solid var(--gw-line); color: var(--gw-dim);
  border-radius: 4px; min-width: 40px; min-height: 40px; cursor: pointer; font-size: 1.1rem;
}
.gw-del:hover { color: var(--gw-danger); border-color: var(--gw-danger); }

/* ── Result screen and battle history (2026-09-28) ─────────────────────────
   Tokens only, so e-ink and the Marty theme reach them with no rules of their own. */
.gw-result { max-width: 640px; margin-inline: auto; }
.gw-res-head { font-size: 1.1rem; color: var(--gw-amber); margin: 0 0 2px; }
.gw-res-sub { margin: 0 0 10px; }
.gw-res-table { width: 100%; border-collapse: collapse; margin: 6px 0 12px; font-size: .82rem; }
.gw-res-table th, .gw-res-table td { padding: 6px 8px; border-bottom: 1px solid var(--gw-line); text-align: left; }
.gw-res-table thead th { color: var(--gw-ink); font-size: .78rem; }
.gw-res-table tbody th { color: var(--gw-dim); font-weight: normal; white-space: nowrap; }
.gw-hist { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.gw-hist details { background: var(--gw-panel-2); border: 1px solid var(--gw-line); border-radius: 4px; padding: 8px 12px; }
.gw-hist details[open] { border-color: var(--gw-amber-dk); }
.gw-hist summary { cursor: pointer; display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: baseline; font-size: .84rem; }
.gw-hist-date { color: var(--gw-dim); font-size: .74rem; min-width: 70px; }
.gw-hist-match { flex: 1; }
.gw-hist-won { color: var(--gw-amber); font-size: .74rem; }
.gw-hist details .gw-res-head { margin-top: 10px; font-size: .95rem; }

/* ── Battle bar ─────────────────────────────────────────────────────────── */
.gw-battlebar {
  background: var(--gw-panel); border: 1px solid var(--gw-line); border-radius: 6px;
  padding: 14px 18px; margin-bottom: 12px;
  display: flex; gap: 22px; align-items: center; flex-wrap: wrap;
}
.gw-bb-force strong { display: block; font-family: var(--font-head); font-size: 1.05rem; }
.gw-bb-force span { font-size: .74rem; color: var(--gw-dim); }
.gw-bb-round, .gw-bb-score { display: flex; align-items: center; gap: 8px; }
.gw-bb-label { font-size: .6rem; letter-spacing: .16em; color: var(--gw-dim); }
.gw-bb-num { font-family: var(--font-head); font-size: 1.5rem; color: var(--gw-amber); min-width: 1.6em; text-align: center; }
.gw-bb-actions { margin-left: auto; display: flex; gap: 8px; }

.gw-step {
  background: var(--gw-panel-2); border: 1px solid var(--gw-line); color: var(--gw-ink);
  border-radius: 4px; min-width: 44px; min-height: 44px; font-size: 1.2rem; cursor: pointer; line-height: 1;
}
.gw-step:hover { border-color: var(--gw-amber-dk); color: var(--gw-amber); }

/* ── Army strength ──────────────────────────────────────────────────────── */
.gw-strength { margin-bottom: 16px; }
.gw-strength-bar { height: 6px; background: var(--gw-line); border-radius: 3px; overflow: hidden; margin-bottom: 6px; }
.gw-strength-bar span { display: block; height: 100%; background: var(--gw-amber); transition: width .25s ease; }

/* ── Objectives ─────────────────────────────────────────────────────────── */
.gw-objectives { background: var(--gw-panel); border: 1px solid var(--gw-line); border-radius: 6px; padding: 14px 18px; margin-bottom: 18px; }
.gw-objectives h2 { font-family: var(--font-head); font-size: .8rem; letter-spacing: .12em; text-transform: uppercase; color: var(--gw-dim); margin: 0 0 10px; }
.gw-obj-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.gw-obj { display: flex; }
.gw-obj > button:first-child {
  background: var(--gw-panel-2); border: 1px solid var(--gw-line); color: var(--gw-ink);
  border-radius: 4px 0 0 4px; padding: 8px 12px; min-height: 40px; cursor: pointer; font: inherit; font-size: .8rem;
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
}
.gw-obj > button span { font-size: .6rem; letter-spacing: .1em; color: var(--gw-dim); }
.gw-obj .gw-del { border-radius: 0 4px 4px 0; border-left: none; min-width: 34px; }
/* Keyed to the viewer, not to host/guest. The marker the guest is holding has
   to read as theirs on their own screen; with positional classes it was green
   for one player and red for the other, for the same marker. */
.gw-obj-mine > button:first-child { border-color: var(--gw-good); }
.gw-obj-mine > button span { color: var(--gw-good); }
.gw-obj-theirs > button:first-child { border-color: var(--gw-danger); }
.gw-obj-theirs > button span { color: var(--gw-danger); }

/* ── Unit cards ─────────────────────────────────────────────────────────── */
.gw-units { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); }
.gw-unit { background: var(--gw-panel); border: 1px solid var(--gw-line); border-left: 4px solid var(--gw-line); border-radius: 6px; padding: 14px; }
.gw-unit.is-shaken { border-left-color: var(--gw-shaken); }
/* Activated = "has acted this round", not "is finished with". It can still be
   shot at and still takes wounds, so the card must stay fully legible - this
   used to drop the whole thing to 62% opacity, weapon table included. Marked
   the same way shaken is (left border + the lit ACTIVATED pill) rather than
   inventing a second mechanism for the same kind of state. */
.gw-unit.is-done { border-left-color: var(--gw-done); }
.gw-unit.is-done .gw-u-head h3 { color: var(--gw-dim); }
/* Destroyed reads as shape and text as well as colour — under e-ink every one
   of these borders is black, so colour alone would say nothing. */
.gw-unit.is-dead { border-left-color: var(--gw-danger); opacity: .5; }
.gw-unit.is-dead .gw-u-head h3 { text-decoration: line-through; }

.gw-u-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.gw-u-head h3 { font-family: var(--font-head); font-size: 1rem; margin: 0; }
.gw-u-stats { font-size: .7rem; color: var(--gw-dim); white-space: nowrap; }
/* A joined hero. Nested inside its squad because they are one unit on the
   table, but with its own wound track, weapons and rules - it keeps a separate
   Tough pool and is removed last. */
.gw-hero { background: var(--gw-inset); border: 1px dashed var(--gw-amber-dk); border-radius: 4px; padding: 10px; margin-top: 10px; }
.gw-hero-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.gw-hero-head strong { color: var(--gw-amber); font-size: .84rem; }
.gw-hero-tag { font-family: var(--font-head); font-size: .56rem; letter-spacing: .12em; color: var(--gw-bg); background: var(--gw-amber-dk); border-radius: 3px; padding: 2px 5px; }
.gw-hero-stats { color: var(--gw-dim); font-size: .7rem; margin-left: auto; }
.gw-hero-note { font-size: .64rem; color: var(--gw-dim); margin: 0 0 8px; line-height: 1.4; }
.gw-hero.is-dead { opacity: .5; }
.gw-hero.is-dead .gw-hero-head strong { text-decoration: line-through; }
/* Smaller readout than a squad's - a hero is one model, so the big model
   counter would be shouting "1/1" at the same size as a 10-model squad. */
.gw-u-wounds-sm .gw-u-models { font-size: 1.1rem; }

.gw-u-wounds { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.gw-u-readout { flex: 1; text-align: center; }
.gw-u-models { display: block; font-family: var(--font-head); font-size: 1.6rem; line-height: 1; }
.gw-u-models i { font-style: normal; font-size: .9rem; color: var(--gw-dim); }
.gw-u-sub { display: block; font-size: .66rem; color: var(--gw-dim); margin-top: 3px; }

.gw-u-pips { display: flex; flex-wrap: wrap; gap: 3px; margin-bottom: 10px; }
.gw-pip { width: 14px; height: 14px; border-radius: 50%; background: var(--gw-amber); display: inline-flex; align-items: center; justify-content: center; font-size: .6rem; color: var(--gw-bg); }
.gw-pip.lost { background: none; border: 1px solid var(--gw-line); color: var(--gw-dim); }

.gw-u-toggles { display: flex; gap: 8px; margin-bottom: 10px; }
.gw-tog {
  flex: 1; background: none; border: 1px solid var(--gw-line); color: var(--gw-dim);
  border-radius: 4px; padding: 8px; min-height: 40px; cursor: pointer;
  font-family: var(--font-head); font-size: .66rem; letter-spacing: .1em;
}
.gw-tog.on { background: var(--gw-shaken); border-color: var(--gw-shaken); color: var(--gw-bg); }
.gw-tog[data-field="activated"].on { background: var(--gw-dim); border-color: var(--gw-dim); }

.gw-weap { width: 100%; border-collapse: collapse; font-size: .7rem; margin-bottom: 8px; }
.gw-weap th { text-align: left; color: var(--gw-dim); font-weight: 400; font-size: .58rem; letter-spacing: .1em; text-transform: uppercase; border-bottom: 1px solid var(--gw-line); padding: 3px 4px; }
.gw-weap td { padding: 3px 4px; border-bottom: 1px solid #222a31; vertical-align: top; }

.gw-u-rules { display: flex; flex-wrap: wrap; gap: 4px; }
.gw-chip, .gw-chip-none {
  font-size: .66rem; padding: 2px 7px; border-radius: 10px;
  background: var(--gw-inset); border: 1px solid var(--gw-line); color: var(--gw-dim);
  display: inline-block; font-family: inherit;
}
button.gw-chip.known { cursor: pointer; color: var(--gw-ink); border-color: var(--gw-amber-dk); }
button.gw-chip.known:hover { background: var(--gw-amber-dk); color: var(--gw-bg); }

/* ── Rule popup, toast, modals ──────────────────────────────────────────── */
.gw-rulepop {
  position: fixed; z-index: 60; max-width: 340px;
  background: var(--gw-sunk); border: 1px solid var(--gw-amber-dk); border-radius: 6px;
  padding: 12px 14px; font-size: .8rem; line-height: 1.5; box-shadow: 0 10px 30px rgba(0,0,0,.5);
}
.gw-rulepop strong { display: block; color: var(--gw-amber); margin-bottom: 4px; font-family: var(--font-head); }

.gw-toast {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); z-index: 70;
  background: var(--gw-sunk); border: 1px solid var(--gw-line); border-radius: 6px;
  padding: 10px 18px; font-size: .84rem; box-shadow: 0 8px 24px rgba(0,0,0,.5);
}
.gw-toast.bad { border-color: var(--gw-danger); color: #e8a294; }

.gw-modal { position: fixed; inset: 0; z-index: 80; background: rgba(8,10,12,.8); display: flex; align-items: center; justify-content: center; padding: 20px; }
.gw-modal-box { background: var(--gw-panel); border: 1px solid var(--gw-line); border-radius: 6px; padding: 20px; width: min(420px, 100%); }
/* Two columns on a laptop, one on a phone - a reference you scan, not read
   top to bottom. */
.gw-core-box { width: min(880px, 100%); max-height: 88vh; display: flex; flex-direction: column; gap: 10px; }
.gw-core-body { overflow-y: auto; flex: 1; min-height: 0; columns: 2; column-gap: 26px;
                border-top: 1px solid var(--gw-line); padding-top: 10px; font-size: .78rem; line-height: 1.55; }
.gw-core-body section { break-inside: avoid; margin: 0 0 14px; }
.gw-core-body h3 { font-family: var(--font-head); font-size: .72rem; letter-spacing: .12em;
                   text-transform: uppercase; color: var(--gw-amber); margin: 0 0 5px;
                   border-bottom: 1px solid var(--gw-line); padding-bottom: 3px; }
.gw-core-body p { margin: 0 0 7px; }
.gw-core-body b { color: var(--gw-ink); }
/* The two places this app's own model depends on the rule being read right. */
.gw-core-flag { border-left: 2px solid var(--gw-amber-dk); padding-left: 9px; color: var(--gw-dim); }
.gw-core-table { width: 100%; border-collapse: collapse; margin: 0 0 8px; font-size: .74rem; }
.gw-core-table th { text-align: left; color: var(--gw-dim); font-weight: 400; font-size: .58rem;
                    letter-spacing: .1em; text-transform: uppercase; border-bottom: 1px solid var(--gw-line); padding: 3px 6px 3px 0; }
.gw-core-table td { padding: 3px 6px 3px 0; border-bottom: 1px solid #222a31; }
.gw-core-table td:first-child { color: var(--gw-amber); }
@media (max-width: 720px) { .gw-core-body { columns: 1; } }

.gw-rules-box { width: min(680px, 100%); max-height: 86vh; display: flex; flex-direction: column; gap: 10px; }
.gw-rules-head { display: flex; align-items: center; gap: 10px; }
.gw-rules-head h2 { margin: 0; flex: none; }
.gw-rules-head .gw-note { margin-left: auto; }
/* Kill the UA search decorations that come with type=search, so it matches
   every other field rather than carrying WebKit's inset shadow and its own
   cancel button. */
input[type="search"] { -webkit-appearance: none; appearance: none; }
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; }
input[type="search"]:focus { outline: none; border-color: var(--gw-amber-dk); }
#gw-rules-q, #gw-core-q { width: 100%; }
/* The list scrolls, not the modal - the header and search box stay put while
   371 rules move under them. */
.gw-rules-list { overflow-y: auto; flex: 1; min-height: 0; border-top: 1px solid var(--gw-line); padding-top: 8px; }
.gw-rule-row { padding: 8px 0; border-bottom: 1px solid var(--gw-line); font-size: .78rem; line-height: 1.5; }
.gw-rule-row:last-child { border-bottom: 0; }
.gw-rule-row strong { display: block; color: var(--gw-amber); font-family: var(--font-head); letter-spacing: .04em; }
.gw-rules-foot { flex: none; margin: 0; }

.gw-modal-box h2 { font-family: var(--font-head); margin: 0 0 10px; font-size: 1.05rem; }

.gw-foot { max-width: 1400px; margin: 0 auto; padding: 24px 16px 40px; font-size: .7rem; color: var(--gw-dim); line-height: 1.6; border-top: 1px solid var(--gw-line); }
.gw-foot p { margin: 0 0 6px; }

/* ── Joining ────────────────────────────────────────────────────────────── */
/* input.gw-code-in, not .gw-code-in. The shared rule above is
   `input[type="text"] { width: 100% }` at (0,1,1), and a bare class is
   (0,1,0) — so it loses on specificity no matter how far down the file it
   sits, and the code box stretched the full panel and shoved JOIN BATTLE
   onto its own line. Adding the element name makes it (0,1,1) and later.
   Same trap as the suite.css note at the top, one rung further in: it is
   not always EQUAL specificity that beats you. */
input.gw-code-in {
  font-family: var(--font-head); font-size: 1.4rem; letter-spacing: .3em;
  text-transform: uppercase; text-align: center;
  width: 6.5em; flex: none;
  background: var(--gw-sunk); border: 1px solid var(--gw-line); color: var(--gw-amber);
  border-radius: 4px; padding: 8px 10px; min-height: 44px;
}
input.gw-code-in:focus { outline: none; border-color: var(--gw-amber-dk); }

/* ── The share strip ────────────────────────────────────────────────────── */
.gw-share {
  background: var(--gw-panel); border: 1px solid var(--gw-amber-dk); border-radius: 6px;
  padding: 12px 18px; margin-bottom: 12px;
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
}
.gw-share.is-joined { border-color: var(--gw-line); }
.gw-share-label { font-size: .6rem; letter-spacing: .16em; color: var(--gw-dim); }
.gw-share-code { font-family: var(--font-head); letter-spacing: .2em; color: var(--gw-amber); }
.gw-share-big { font-size: 2rem; }
.gw-share .gw-note { flex: 1 1 260px; margin: 0; }
/* A filled dot, not a pulsing one: the poll already tells you the board is
   live by changing, and a permanently animating element on an e-ink screen is
   a full-page repaint every second. */
.gw-share-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--gw-good); flex: none; }

/* ── The table strip ────────────────────────────────────────────────────── */
.gw-table { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); margin-bottom: 18px; }
.gw-tcard {
  background: var(--gw-panel); border: 1px solid var(--gw-line); border-radius: 6px;
  padding: 12px 14px; display: flex; flex-direction: column; gap: 10px;
}
.gw-tcard header { display: flex; align-items: center; gap: 8px; }
.gw-tcard h3 { font-family: var(--font-head); font-size: .8rem; letter-spacing: .12em; text-transform: uppercase; margin: 0; color: var(--gw-ink); }
/* Provenance, and it is not decoration: this app's claim is that rule text
   comes from OPR rather than being guessed at, so the two tables we wrote
   have to say which is which at the point of use. */
.gw-tsrc { margin-left: auto; font-size: .55rem; letter-spacing: .14em; padding: 3px 6px; border-radius: 3px; border: 1px solid; }
.gw-tsrc-opr   { color: var(--gw-good);  border-color: var(--gw-good); }
.gw-tsrc-house { color: var(--gw-dim);   border-color: var(--gw-line); }
.gw-tbody { flex: 1; min-height: 3.4em; }
/* The coin sits beside its result rather than above it: the card is one of
   three in a row and a stacked 64px image made this one twice the height of
   its neighbours. */
.gw-tbody-coin { display: flex; align-items: center; gap: 12px; }
.gw-tbody-coin > div { min-width: 0; }
.gw-coin { width: 64px; height: 64px; flex: none; object-fit: contain; display: block; }
.gw-coin-heads { color: var(--gw-amber); }
.gw-coin-tails { color: var(--gw-good); }
.gw-tbody p { margin: 0 0 4px; }
.gw-tdice { font-family: var(--font-head); font-size: 1.3rem; color: var(--gw-ink); }
.gw-tdice b { color: var(--gw-amber); }
.gw-tdice i { font-size: .65rem; color: var(--gw-dim); font-style: normal; letter-spacing: .08em; }
.gw-tname { font-family: var(--font-head); font-size: .92rem; color: var(--gw-amber); }
.gw-tname b { color: var(--gw-dim); font-size: .75rem; margin-right: 4px; }
.gw-tname i { color: var(--gw-dim); font-size: .62rem; font-style: normal; letter-spacing: .1em; }
.gw-ttext { font-size: .78rem; line-height: 1.5; color: var(--gw-ink); }
.gw-tnone { font-size: .74rem; line-height: 1.5; color: var(--gw-dim); }

/* ── Round / first-activation / scores ──────────────────────────────────── */
.gw-bb-of { font-size: .62rem; letter-spacing: .1em; color: var(--gw-dim); }
.gw-bb-first { display: flex; flex-direction: column; gap: 2px; }
.gw-bb-first strong { font-family: var(--font-head); font-size: .95rem; color: var(--gw-dim); }
.gw-bb-first[data-me="1"] strong { color: var(--gw-good); }
.gw-bb-score-them .gw-bb-num { color: var(--gw-danger); }

/* ── The two boards ─────────────────────────────────────────────────────── */
.gw-switch { display: flex; gap: 0; margin-bottom: 14px; }
.gw-sw {
  flex: 1; background: var(--gw-panel); border: 1px solid var(--gw-line); color: var(--gw-dim);
  font: inherit; font-family: var(--font-head); font-size: .74rem; letter-spacing: .12em;
  padding: 12px 10px; min-height: 44px; cursor: pointer;
}
.gw-sw:first-child { border-radius: 4px 0 0 4px; }
.gw-sw:last-child  { border-radius: 0 4px 4px 0; border-left: none; }
.gw-sw.on { background: var(--gw-panel-2); color: var(--gw-amber); border-color: var(--gw-amber-dk); }

.gw-boards { display: block; }
.gw-board { margin-bottom: 18px; }
/* Text inputs default to width:100%, which on a flex row shoves every button
   onto its own line. Inside a row they flex instead; the code box opts back
   out because it is exactly four characters wide and should look it. Both
   selectors are (0,2,1) so the later one wins, which is the code box. */
.gw-row input[type="text"] { flex: 1 1 200px; width: auto; }
.gw-row input.gw-code-in   { flex: none; width: 6.5em; }
.gw-board-head h2 { font-family: var(--font-head); font-size: .9rem; letter-spacing: .08em; margin: 0 0 8px; color: var(--gw-ink); }
.gw-board-them .gw-strength-bar span { background: var(--gw-danger); }
/* One board at a time BELOW the two-column breakpoint, and the media query is
   load-bearing rather than tidy. The first cut put these rules unconditionally
   and re-showed both inside @media (min-width:1200px) — but
   `.gw-boards[data-showing="mine"] .gw-board[data-board="theirs"]` is (0,4,0)
   and the re-show was (0,3,0), so the hide won inside the media query too and
   the opponent's column rendered empty on a laptop. A media query that simply
   does not apply cannot be out-specified. */
@media (max-width: 1199px) {
  /* Both boards stay in the DOM at every width so the poll can write into the
     hidden one and it is right the moment it is shown — hiding is a display
     decision, not a data one. */
  .gw-boards[data-showing="mine"]   .gw-board[data-board="theirs"] { display: none; }
  .gw-boards[data-showing="theirs"] .gw-board[data-board="mine"]   { display: none; }
}

/* Read-only: the opponent's board. No controls drawn at all rather than
   disabled ones - see gwUnitCard(). */
.gw-u-wounds-ro { justify-content: flex-start; }
/* `.gw-tog` is flex:1 so two buttons split the row. A lone read-only flag
   inherited that and stretched the full card width, reading as a button you
   could press. It is a badge: it takes the space its own word needs. */
.gw-tog.is-ro { flex: none; cursor: default; min-height: 0; padding: 5px 10px; }

@media (min-width: 1200px) {
  /* Both armies at once, and the switcher has nothing left to do. */
  .gw-switch { display: none; }
  .gw-boards { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
  .gw-boards.is-solo { display: block; }
  /* One card per column here: two boards side by side already uses the width,
     and the auto-fill default would put two narrow cards in each. */
  .gw-units { grid-template-columns: 1fr; }
  /* Sticky heads, so you can still tell whose board you are scrolled into
     once both columns are taller than the screen. */
  .gw-board-head { position: sticky; top: 0; background: var(--gw-bg); padding: 6px 0 4px; z-index: 2; }
}

@media (max-width: 600px) {
  .gw-battlebar { gap: 12px; }
  .gw-bb-actions { margin-left: 0; width: 100%; }
  .gw-units { grid-template-columns: 1fr; }
  .gw-share-big { font-size: 1.6rem; }
  .gw-table { grid-template-columns: 1fr; }
}

/* ── "Marty" ────────────────────────────────────────────────────────────────
   A pride theme that behaves like heraldry rather than a sticker.

   The flag does NOT drive the semantic colours, and that was a measured
   decision rather than a stylistic one. Mapping the six stripes onto
   danger/good/shaken/done put red against orange at OKLab dE 5.0 under
   deuteranopia - a shaken unit and a destroyed one reading the same on a
   tabletop, in an app whose whole job is telling you which is which. So the
   readout keeps colours that stay apart, and the flag lives where it carries no
   meaning: the banner under the header, the brand, the rules.

   Which is also the more Warhammer answer. A regiment's colours go on the
   standard; they do not go on the casualty markers.

   Measured against the default dark theme, every semantic pair here is equal or
   better, and one is dramatically better: shaken against amber was dE 8.0 and is
   now 21.5, because the accent moved off gold and on to violet. Status is never
   carried by colour alone either way - SHAKEN and ACTIVATED are literal words in
   the markup.

   Muted on purpose. Full-saturation flag colours on a grimdark ground read as a
   vinyl overlay; these are pitched like pigment on a painted banner, which is
   what the rest of this suite looks like. */
:root[data-theme="marty"] {
  --gw-bg:        #120e18;
  --gw-panel:     #1a1520;
  --gw-panel-2:   #221b29;
  --gw-line:      #342b3d;
  --gw-ink:       #e6e1ea;
  --gw-dim:       #9d92a9;
  /* The theme's voice. Everything the default paints gold goes violet. */
  --gw-amber:     #a888cf;
  --gw-amber-dk:  #6b5288;
  --gw-danger:    #c05545;
  --gw-good:      #6f9f5e;
  --gw-shaken:    #c9973a;
  --gw-sunk:      #0d0a12;
  --gw-inset:     #221b29;
  --gw-done:      #5c6b8a;

  /* The six, individually, for everything that cycles through them. */
  --gw-f1: #c85a4c;  --gw-f2: #d48846;  --gw-f3: #dcb457;
  --gw-f4: #74a862;  --gw-f5: #5e93c0;  --gw-f6: #9e7cc6;

  /* The banner. Progress order: the chevron's five run first and short, then
     the six across the rest, all hard stops so it reads as sewn panels rather
     than an airbrush. */
  --gw-flag: linear-gradient(90deg,
    #2b2430 0 3.6%, #8d6a51 3.6% 7.2%, #7fb4d6 7.2% 10.8%, #d59ab3 10.8% 14.4%,
    #e9e4ec 14.4% 18%,
    #c85a4c 18% 31.7%, #d48846 31.7% 45.4%, #dcb457 45.4% 59%,
    #74a862 59% 72.7%, #5e93c0 72.7% 86.4%, #9e7cc6 86.4% 100%);
}

/* The banner itself, and the only place the full flag appears. Drawn as a
   pseudo-element on the sticky header so it cannot disturb the layout or the
   existing border, and gated on the theme so the other two are untouched. */
:root[data-theme="marty"] .gw-top { border-bottom-color: transparent; }
:root[data-theme="marty"] .gw-top::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 4px;
  background: var(--gw-flag);
}


/* A solo board has no second column, so the grid would otherwise hand half the
   screen to nothing. Scoped to the modifier rather than to "no opponent yet",
   which is a different state that still wants the empty column and its code. */
.gw-boards.is-solo { display: block; }
.gw-boards.is-solo .gw-board[data-board="theirs"] { display: none; }

/* ── Marty, beyond the banner ────────────────────────────────────────────────
   One stripe under the header is a garnish, not a theme. Everything below is
   somewhere colour carries NO meaning, which is the same line the palette draws:
   headings, the brand, and pips whose meaning is filled-versus-hollow rather
   than hue. Nothing here touches danger, good, shaken or done. */

/* The wordmark. Gradient text, with a plain colour declared first so a browser
   that does not do background-clip still gets a readable brand rather than a
   transparent one. */
:root[data-theme="marty"] .gw-brand-hi {
  color: var(--gw-f6);
  background: var(--gw-flag);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* The three table cards, and the panel headings, each taking the next colour.
   They are labels for sections, not states, so this is free. */
:root[data-theme="marty"] .gw-tcard:nth-of-type(3n+1) h3 { color: var(--gw-f1); }
:root[data-theme="marty"] .gw-tcard:nth-of-type(3n+2) h3 { color: var(--gw-f4); }
:root[data-theme="marty"] .gw-tcard:nth-of-type(3n+3) h3 { color: var(--gw-f6); }
:root[data-theme="marty"] .gw-tcard:nth-of-type(3n+1) { border-top: 2px solid var(--gw-f1); }
:root[data-theme="marty"] .gw-tcard:nth-of-type(3n+2) { border-top: 2px solid var(--gw-f4); }
:root[data-theme="marty"] .gw-tcard:nth-of-type(3n+3) { border-top: 2px solid var(--gw-f6); }

:root[data-theme="marty"] .gw-panel:nth-of-type(6n+1) h1,
:root[data-theme="marty"] .gw-panel:nth-of-type(6n+1) h2 { color: var(--gw-f1); }
:root[data-theme="marty"] .gw-panel:nth-of-type(6n+2) h1,
:root[data-theme="marty"] .gw-panel:nth-of-type(6n+2) h2 { color: var(--gw-f2); }
:root[data-theme="marty"] .gw-panel:nth-of-type(6n+3) h1,
:root[data-theme="marty"] .gw-panel:nth-of-type(6n+3) h2 { color: var(--gw-f3); }
:root[data-theme="marty"] .gw-panel:nth-of-type(6n+4) h1,
:root[data-theme="marty"] .gw-panel:nth-of-type(6n+4) h2 { color: var(--gw-f4); }
:root[data-theme="marty"] .gw-panel:nth-of-type(6n+5) h1,
:root[data-theme="marty"] .gw-panel:nth-of-type(6n+5) h2 { color: var(--gw-f5); }
:root[data-theme="marty"] .gw-panel:nth-of-type(6n+6) h1,
:root[data-theme="marty"] .gw-panel:nth-of-type(6n+6) h2 { color: var(--gw-f6); }

/* YOUR army's strength bar flies the flag; the opponent's stays danger-red, so
   the one thing the pair has to say - which board is which - is untouched. As it
   shortens you keep the left of the banner, which is the right way round for a
   standard being cut down. */
:root[data-theme="marty"] .gw-board-me .gw-strength-bar span,
:root[data-theme="marty"] .gw-strength-bar span { background: var(--gw-flag); }
:root[data-theme="marty"] .gw-board-them .gw-strength-bar span { background: var(--gw-danger); }

/* Wound pips. Safe because a lost pip is an OUTLINE and a remaining one is a
   FILL - the distinction is shape, so the fill colour is decoration. */
:root[data-theme="marty"] .gw-pip:nth-child(6n+1) { background: var(--gw-f1); }
:root[data-theme="marty"] .gw-pip:nth-child(6n+2) { background: var(--gw-f2); }
:root[data-theme="marty"] .gw-pip:nth-child(6n+3) { background: var(--gw-f3); }
:root[data-theme="marty"] .gw-pip:nth-child(6n+4) { background: var(--gw-f4); }
:root[data-theme="marty"] .gw-pip:nth-child(6n+5) { background: var(--gw-f5); }
:root[data-theme="marty"] .gw-pip:nth-child(6n+6) { background: var(--gw-f6); }
:root[data-theme="marty"] .gw-pip.lost { background: none; }

/* ── E-ink ──────────────────────────────────────────────────────────────── */
/* Every colour redefined, not a targeted few — the lesson from the suite's
   earlier passes is that a partial sweep always leaves something invisible.
   suite.css kills animations and shadows globally; what matters here is that
   nothing depends on colour ALONE: destroyed units keep their strike-through,
   lost pips keep their hollow ring and x, toggles keep their border. */
:root[data-eink] {
  --gw-bg: #fff; --gw-panel: #fff; --gw-panel-2: #fff;
  --gw-line: #000; --gw-ink: #000; --gw-dim: #333;
  --gw-amber: #000; --gw-amber-dk: #000;
  --gw-danger: #000; --gw-good: #000; --gw-shaken: #000; --gw-done: #000;
  --gw-sunk: #fff; --gw-inset: #fff;
}

/* And the banner, which the block above CANNOT reach: it is a background-image on
   an element, not a value behind a --gw-* token, so the flatten goes straight past
   it exactly like the six hardcoded backgrounds this app shipped with. On a Boox it
   becomes a plain black rule. */
/* Everything Marty adds outside the token set, flattened. These have to be listed
   one by one and at matching specificity: the block above only rewrites --gw-*
   values, and each rule below is either a gradient IMAGE or a selector heavier
   than the plain element rules the e-ink sweep uses. A :root[data-theme] rule
   scores (0,2,0) against the sweep's (0,1,0), so it would simply win. */
:root[data-eink][data-theme="marty"] .gw-top::after { background: #000; }
:root[data-eink][data-theme="marty"] .gw-top { border-bottom-color: #000; }
:root[data-eink][data-theme="marty"] .gw-brand-hi {
  background: none; -webkit-text-fill-color: #000; color: #000;
}
:root[data-eink][data-theme="marty"] .gw-tcard[class] { border-top-color: #000; }
:root[data-eink][data-theme="marty"] .gw-tcard h3,
:root[data-eink][data-theme="marty"] .gw-panel h1,
:root[data-eink][data-theme="marty"] .gw-panel h2 { color: #000; }
:root[data-eink][data-theme="marty"] .gw-strength-bar span,
:root[data-eink][data-theme="marty"] .gw-board-them .gw-strength-bar span { background: #000; }
:root[data-eink][data-theme="marty"] .gw-pip { background: #000; }
:root[data-eink][data-theme="marty"] .gw-pip.lost { background: #fff; }
/* Hovers that assumed a dark page: each would land light-on-white or
   black-on-black once everything flattens. */
[data-eink] .btn-primary:hover { background: #000; color: #fff; }
[data-eink] .btn-danger:hover  { background: #000; color: #fff; }
[data-eink] .gw-toast.bad      { color: #000; }
[data-eink] .gw-brand-hi { color: #000; }
[data-eink] .btn-primary { background: #000; color: #fff; }
[data-eink] .gw-pip { background: #000; color: #fff; }
[data-eink] .gw-pip.lost { background: #fff; color: #000; border-width: 2px; }
[data-eink] .gw-tog.on { background: #000; color: #fff; }
[data-eink] .gw-unit.is-dead { opacity: 1; }
[data-eink] .gw-unit.is-done .gw-u-head h3::after { content: ' (activated)'; font-size: .7rem; }
[data-eink] .gw-modal { background: #fff; }
[data-eink] * { border-color: #000 !important; }
/* Hairlines vanish on e-ink; the suite convention is 2px. Animation and
   transition go for the same reason they do in every other app here. */
[data-eink] * { animation: none !important; transition: none !important; box-shadow: none !important; }
[data-eink] .gw-unit, [data-eink] .gw-hero, [data-eink] .gw-chip,
[data-eink] .gw-tog, [data-eink] .gw-step, [data-eink] .gw-weap td,
[data-eink] .gw-weap th, [data-eink] .gw-tcard, [data-eink] .gw-sw,
[data-eink] .gw-tsrc { border-width: 2px !important; }
/* Everything above flattens to black on white, so the three things that were
   carrying meaning in colour alone need it back in another form. */
[data-eink] .gw-sw.on { background: #000; color: #fff; }
[data-eink] .gw-share-dot { background: #000; }
[data-eink] .gw-bb-first[data-me="1"] strong::after { content: ' \2190'; }
/* Heads and tails are the same gold coin in silhouette once colour is gone,
   and the two words are the only thing telling them apart - so the word is
   what has to survive, not the tint. */
[data-eink] .gw-coin-heads, [data-eink] .gw-coin-tails { color: #000; }
[data-eink] .gw-obj-mine   > button span::before { content: '\2713 '; }
[data-eink] .gw-obj-theirs > button span::before { content: '\2717 '; }
[data-eink] .gw-tsrc-opr::after { content: ''; }

/* The saved-list picker inside the Play Both Sides dialog. Capped and scrolled:
   a library of thirty armies would otherwise push the textarea off the screen. */
.gw-lib-inline { max-height: 170px; overflow-y: auto; margin: 0 0 6px; }
.gw-both-or { margin: 8px 0 4px; }
