/* 「붉은 전선 1918–1920」. Shared game chrome is in games.css; this file holds
   the campaign board, the map layer and the battle grid. The map tones repeat
   the event map's (commulingo.css .emap-*), which this page does not load. */
.cw-shell {
    --cw-red: #a21e2a;
    /* The Whites: white with a dark rim, so they read on both themes. */
    --cw-white: #ffffff; --cw-ink: #4a4a46;
    /* The commissar's star is red. Trotsky's train is armour plate, olive
       steel with a pale rim on the dark map (a dark one on the light). */
    --cw-gold: #f2d64b; --cw-gold-rim: #1d1d1b; --cw-star: #e0252f;
    --cw-steel: #5b6b45; --cw-steel-rim: #e4dfcc;
    /* What eases a front (a trait that helps, a pursuit): cream on green,
       which reads on both themes where green text alone does not. */
    --cw-good: #2f7d43; --cw-good-ink: #faf2da;
    /* A loss of the Red side's own (a stat falling, a counter's blow):
       orange, since red is the Red Army's colour. */
    --cw-loss: #d4691c;
    --emap-blue: #2f6fb8; --emap-red: var(--clr-red); --emap-amber: #c7861c;
    --emap-purple: #7b5aa6; --emap-gray: #8d8d86; --emap-green: #3f8a4a; --emap-black: var(--text-primary);
}
[data-theme="light"] .cw-shell { --cw-gold: #e8b10c; --cw-gold-rim: #3a2a00; --cw-steel-rim: #23261d; }
.cw-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.cw-heading { display: flex; align-items: center; justify-content: space-between; gap: 20px; margin: 18px 0 20px; border-bottom: 2px solid var(--border); padding: 0 var(--content-inset) 16px; }
.cw-heading h1 { font-size: clamp(1.35rem, 3.3vw, 2.3rem); letter-spacing: -.045em; margin: 8px 0; }
.games-shell .cw-primary { background: var(--cw-red); color: #fff; border-color: var(--cw-red); font-weight: 700; }
.games-shell .cw-primary:hover:enabled { filter: brightness(1.08); }
.games-shell .cw-primary:disabled { background: var(--cw-ink); border-color: var(--cw-ink); color: #b5b5ad; opacity: 1; }
.cw-shell h2, .cw-shell h3, .cw-shell h4 { margin: 0; }
.cw-shell p { line-height: var(--leading-relaxed); }

/* Intro */
.cw-intro { display: grid; grid-template-columns: 1.4fr 1fr; border: 1px solid var(--border); background: var(--bg-surface); }
.cw-intro-copy { padding: var(--space-6) var(--content-inset); }
.cw-intro-copy h2 { font-size: clamp(1.3rem, 3vw, 1.9rem); line-height: var(--leading-tight); margin: 10px 0 22px; }
.cw-setup-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.cw-setup-actions [hidden] { display: none; }
.cw-steps { list-style: none; counter-reset: step; padding: 0; margin: 0 0 18px; display: grid; gap: 14px; }
.cw-steps li { counter-increment: step; display: grid; grid-template-columns: 30px 1fr; column-gap: 12px; line-height: var(--leading-normal); }
.cw-steps li::before { content: counter(step); grid-row: span 2; display: grid; place-items: center; width: 30px; height: 30px; background: var(--cw-red); color: #fff; font-weight: 800; }
.cw-steps span { color: var(--text-secondary); font-size: .9rem; }
.cw-intro-art { background: var(--cw-red); color: #faf2da; display: flex; flex-direction: column; justify-content: flex-end; padding: 32px; min-height: 380px; background-image: repeating-linear-gradient(-45deg, transparent, transparent 22px, #00000010 22px, #00000010 44px); }
.cw-intro-star { font-size: 5rem; line-height: var(--leading-none); }
.cw-intro-art strong { font-size: clamp(2.2rem, 5vw, 3.4rem); letter-spacing: -.04em; margin: 12px 0; }
.cw-intro-art p { font-weight: 700; border-top: 1px solid #faf2da60; padding-top: 14px; margin: 0; }

/* HUD */
.cw-hud { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; border: 1px solid var(--border); background: var(--bg-surface); padding: 16px var(--content-inset); margin-bottom: 18px; }
.cw-season h2 { font-size: clamp(1.25rem, 3vw, 1.7rem); margin: 4px 0 12px; }
.cw-calendar { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(var(--cw-seasons, 8), minmax(0, 1fr)); gap: 4px; }
.cw-calendar li { font-size: .75rem; color: var(--text-secondary); min-width: 0; }
.cw-calendar .cw-cal-year { display: block; font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--text-primary); }
.cw-calendar .cw-cal-bar { display: block; height: 4px; margin: 2px 0 4px; background: var(--border); }
.cw-calendar li span { display: block; font-weight: 700; font-variant-numeric: tabular-nums; }
.cw-calendar small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cw-calendar .is-past .cw-cal-bar { background: color-mix(in srgb, var(--cw-red) 45%, var(--border)); }
.cw-calendar .is-current .cw-cal-bar { background: var(--cw-red); }
.cw-calendar .is-current { color: var(--text-primary); }
.cw-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 8px; margin: 0; }
.cw-stats dt { font-size: .78rem; color: var(--text-secondary); }
.cw-stats dd { margin: 2px 0 0; }
.cw-stats strong { font-size: 1.35rem; font-variant-numeric: tabular-nums; }
.cw-stats small { display: block; font-size: .75rem; color: var(--text-secondary); }
.cw-stats > div { min-width: 0; }
/* Red is the Red Army's; what needs attention is drawn inverted instead. */
.cw-stats .is-warn strong { color: var(--bg-surface); background: var(--text-primary); padding: 0 5px; }
.cw-stats .is-warn small { color: var(--text-primary); font-weight: 700; }

/* The army panel: the season's troops as pips, then the changes */
.cw-army { border-top: 1px solid var(--border); padding-top: 12px; }
.cw-army h3 { font-size: .95rem; margin-bottom: 8px; }
.cw-troops { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 8px 16px; }
.cw-troops li { display: grid; grid-template-columns: auto 1fr; column-gap: 8px; align-items: baseline; }
.cw-troops strong { font-size: .9rem; }
.cw-troops strong a { color: inherit; }
.cw-troops small { grid-column: 1 / -1; font-size: .78rem; color: var(--text-secondary); line-height: var(--leading-normal); }
.cw-troop-pips { display: inline-flex; flex-wrap: wrap; gap: 2px; max-width: 120px; }
.cw-troop-pips { gap: 3px; }
/* A division here is the army's infantry sign (a crossed box), not the
   red unit that counts strength on the fronts. */
.cw-troop-pips i { width: 13px; height: 9px; box-sizing: border-box; border: 1.5px solid var(--cw-red); background: linear-gradient(to top right, transparent calc(50% - .8px), var(--cw-red) calc(50% - .8px) calc(50% + .8px), transparent calc(50% + .8px)), linear-gradient(to bottom right, transparent calc(50% - .8px), var(--cw-red) calc(50% - .8px) calc(50% + .8px), transparent calc(50% + .8px)); }
.cw-troop-changes { list-style: none; padding: 0; margin: 10px 0 0; display: flex; flex-wrap: wrap; gap: 6px; font-size: .8rem; font-weight: 700; }
.cw-troop-changes li { border: 1px solid var(--border); padding: 3px 8px; }
.cw-troop-changes .is-plus { border-color: var(--btn-primary, #2da44e); }
.cw-troop-changes .is-minus { border-style: dashed; border-color: var(--text-primary); }

/* Briefing: the text-first heart of each season */
.cw-brief { border-left: 4px solid var(--cw-red); padding: 4px var(--content-inset); margin-bottom: 18px; max-width: 46rem; }
.cw-brief p { margin: 0 0 10px; }

/* Board: map and fronts */
.cw-board { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(300px, 1fr); gap: 18px; align-items: start; margin-bottom: 18px; }
.cw-map-panel { border: 1px solid var(--border); background: var(--bg-surface); min-width: 0; }
.cw-map-heading { display: flex; align-items: center; gap: 4px 8px; padding: 2px 3px 2px 12px; border-bottom: 1px solid var(--border); font-size: .82rem; color: var(--text-secondary); }
.cw-map-heading span:first-child { font-weight: 700; color: var(--text-primary); }
.cw-map-frame > svg { display: block; width: 100%; height: auto; font-family: inherit; }
.cw-map-frame { position: relative; }
.cw-map-frame svg.cw-map-over { position: absolute; top: 0; left: 0; will-change: transform; }
/* civil-war.js drawBasemap: the basemap as a CPU-drawn bitmap over its hidden svg. */
.cw-map-frame .cw-map-bitmap { position: absolute; top: 0; left: 0; max-width: none; pointer-events: none; }
.cw-map-frame.has-bitmap svg.emap-svg { visibility: hidden; }
.cw-map-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; padding: 8px 12px; font-size: .78rem; color: var(--text-secondary); }
.cw-map-legend span { display: inline-flex; align-items: center; gap: 6px; }
.cw-map-legend i { display: inline-block; width: 14px; height: 10px; border: 1px solid var(--border); }
.cw-map-legend i.is-red { background: color-mix(in srgb, var(--emap-red) 34%, transparent); border-color: var(--emap-red); }
.cw-map-legend i.is-blue { background: var(--cw-white); border: 1px dashed var(--cw-ink); }
.cw-map-legend i.is-amber { background: color-mix(in srgb, var(--emap-amber) 34%, transparent); border-color: var(--emap-amber); }
.cw-map-legend i.is-gray { background: color-mix(in srgb, var(--emap-gray) 34%, transparent); border-color: var(--emap-gray); }
.cw-map-legend i.is-black { background: color-mix(in srgb, var(--emap-black) 40%, transparent); border-color: var(--emap-black); }
.cw-map-legend i.cw-key-held { width: 10px; border-radius: 50%; background: var(--clr-red); border-color: var(--clr-red); }
.cw-map-legend i.cw-key-lost { width: 10px; border-radius: 50%; border: 1.5px solid var(--cw-ink); }
.cw-map-legend i.cw-key-ring { width: 12px; height: 12px; border-radius: 50%; border: 2px dashed var(--clr-red); background: transparent; }
.cw-map-legend i.cw-key-pip { width: 8px; height: 8px; background: var(--clr-red); }
.cw-map-legend i.k-white { background: var(--cw-white); }
.cw-map-legend i.k-interv { background: var(--emap-blue); }
.cw-map-legend i.k-central { background: var(--emap-amber); }
.cw-map-legend i.k-national { background: var(--emap-gray); }
.cw-map-legend i.cw-key-badge { width: 22px; background: var(--cw-white); border: 1px solid var(--cw-ink); }
.cw-map-heading #cwMapCaption { flex: 1 1 0; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.games-shell .cw-history-toggle { min-height: 26px; margin-left: auto; padding: 1px 8px; font-size: .78rem; }
.games-shell .cw-history-toggle[aria-pressed="true"] { border-color: var(--cw-red); color: var(--text-primary); font-weight: 700; }

/* Basemap (event-map-svg.js classes) */
.cw-map-frame { --cw-land: color-mix(in srgb, #b8a46f 17%, var(--bg-surface)); --cw-sea: color-mix(in srgb, #3a6d96 16%, var(--bg-surface)); }
[data-theme="light"] .cw-map-frame { --cw-land: color-mix(in srgb, #c9b27a 30%, var(--bg-surface)); --cw-sea: color-mix(in srgb, #6f9cc0 30%, var(--bg-surface)); }
.cw-map-frame .emap-sea { fill: var(--cw-sea); }
.cw-map-frame .emap-land { fill: var(--cw-land); stroke: color-mix(in srgb, var(--text-primary) 45%, transparent); stroke-width: .9; }
.cw-map-frame .emap-grid { stroke: color-mix(in srgb, var(--border) 8%, transparent); stroke-width: 1; }
.cw-map-frame .emap-lake { fill: var(--cw-sea); stroke: color-mix(in srgb, var(--text-primary) 35%, transparent); stroke-width: .5; }
.cw-map-frame .emap-river { fill: none; stroke: color-mix(in srgb, #3a78b0 60%, var(--cw-land)); stroke-width: 1.3; stroke-linecap: round; }
.cw-map-frame .emap-border { fill: none; stroke: color-mix(in srgb, var(--border) 55%, transparent); stroke-width: 1; }
.cw-map-frame .emap-control { display: none; }
.cw-map-frame.is-history .emap-control { display: inline; }
.cw-map-frame .emap-phase { display: none; }
.cw-map-frame .emap-phase.is-current { display: inline; }
.cw-map-frame .emap-ctl { stroke: none; fill-opacity: .32; }
.cw-map-frame .emap-ctl-knock { fill: var(--cw-land); stroke: none; }
.cw-map-frame .emap-ctl.is-drawn { stroke-width: .8; stroke-opacity: .7; stroke-linejoin: round; }
/* The Whites in white (the event map draws them blue). */
.cw-map-frame .emap-ctl.is-blue { fill: var(--cw-white); stroke: var(--cw-ink); fill-opacity: .55; stroke-dasharray: 3 2; }
.cw-map-frame .emap-ctl.is-red { fill: var(--emap-red); stroke: var(--emap-red); }
.cw-map-frame .emap-ctl.is-amber { fill: var(--emap-amber); stroke: var(--emap-amber); }
.cw-map-frame .emap-ctl.is-gray { fill: var(--emap-gray); stroke: var(--emap-gray); }
.cw-map-frame .emap-ctl.is-black { fill: var(--emap-black); stroke: var(--emap-black); }
.cw-map-frame .emap-ctl.is-green { fill: var(--emap-green); stroke: var(--emap-green); }
.cw-map-frame .emap-ctl-hatch { stroke-width: 1; stroke-dasharray: 3 2; }
.cw-map-frame .emap-ctl-hatch.is-black { stroke: var(--emap-black); }
.cw-map-frame .emap-hatch-line { stroke-width: 1.6; }
.cw-map-frame .emap-hatch-line.is-black { stroke: var(--emap-black); }

/* The game's layer */
/* A front card under the pointer: the other fronts dim (the map zooms to
   the picked front, so the front itself no longer blinks). */
.cw-map-frame.is-spotlight [data-front]:not(.is-lit) { opacity: .22; }
.cw-chain { fill: none; stroke: color-mix(in srgb, var(--text-secondary) 35%, transparent); stroke-width: 1; stroke-dasharray: 3 3; }
.cw-chain.is-active { stroke: var(--cw-red); stroke-width: 2; stroke-dasharray: none; }
.cw-rail { fill: none; stroke: color-mix(in srgb, var(--cw-red) 80%, transparent); stroke-width: 6; stroke-dasharray: 1.4 4.2; stroke-linejoin: round; }
.cw-chain { stroke-linejoin: round; }
/* The railways of 1918: a rail with its sleepers, in ink on the land. */
.cw-map-frame .cw-railway-bed { fill: none; stroke: color-mix(in srgb, var(--text-primary) 55%, var(--cw-land)); stroke-width: 1.3; stroke-linejoin: round; }
.cw-map-frame .cw-railway { fill: none; stroke: color-mix(in srgb, var(--text-primary) 55%, var(--cw-land)); stroke-width: 4.5; stroke-dasharray: 1 5; stroke-linejoin: round; }
/* Places the words name off the fronts (civil-war.js renderMap). */
.cw-map-place { font-size: 9.5px; fill: var(--text-secondary); paint-order: stroke; stroke: var(--cw-land); stroke-width: 3px; stroke-linejoin: round; pointer-events: none; }
.cw-map-place.is-region { font-size: 11px; font-style: italic; letter-spacing: .12em; opacity: .85; }
.cw-map-place.is-river { font-style: italic; fill: color-mix(in srgb, #3a78b0 70%, var(--text-primary)); }
.cw-map-place.is-beyond { font-weight: 700; fill: var(--text-primary); }
.cw-place-dot { fill: var(--text-secondary); pointer-events: none; }
.cw-railway-label { font-size: 9.5px; font-style: italic; fill: var(--text-secondary); paint-order: stroke; stroke: var(--cw-land); stroke-width: 3px; stroke-linejoin: round; pointer-events: none; }
.cw-chain.is-moving { stroke-dasharray: 4 3; }
.cw-ring-under { fill: none; stroke: var(--cw-ink); stroke-width: 3.6; }
.cw-ring { fill: none; stroke-width: 2; stroke-dasharray: 3 2; }
.cw-ring.is-red { stroke: var(--clr-red); }
.cw-ring.k-white { stroke: var(--cw-white); }
.cw-ring.k-interv { stroke: var(--emap-blue); }
.cw-ring.k-central { stroke: var(--emap-amber); }
.cw-ring.k-national { stroke: var(--emap-gray); }
.cw-pip { fill: var(--clr-red); stroke: var(--bg-surface); stroke-width: .8; }
.cw-pip-star { fill: var(--cw-star); stroke: var(--cw-gold-rim); stroke-width: .9; }
.cw-city { stroke-width: 2; }
.cw-city.is-held { fill: var(--clr-red); stroke: var(--bg-surface); }
.cw-city.is-lost { stroke: var(--cw-ink); stroke-width: 1.5; }
.cw-city.k-white { fill: var(--cw-white); }
.cw-city.k-interv { fill: var(--emap-blue); }
.cw-city.k-central { fill: var(--emap-amber); }
.cw-city.k-national { fill: var(--emap-gray); }
.cw-city-label { font-size: 11px; fill: var(--text-secondary); paint-order: stroke; stroke: var(--bg-surface); stroke-width: 3px; stroke-linejoin: round; }
.cw-city-label.is-active { fill: var(--text-primary); font-weight: 700; }
.cw-city-label.is-capital { font-size: 13px; font-weight: 800; fill: var(--text-primary); }
.cw-moscow { fill: var(--clr-red); stroke: var(--bg-surface); stroke-width: 1.5; }
.cw-moscow-ring { fill: color-mix(in srgb, var(--bg-surface) 70%, transparent); stroke: var(--clr-red); stroke-width: 1.6; }
.cw-foe-shadow { fill: #000; fill-opacity: .45; }
.cw-foe-marker { cursor: pointer; }
.cw-foe-marker:focus-visible { outline: none; }
.cw-foe-marker:focus-visible .cw-foe-name { stroke: var(--text-primary); stroke-width: 3; }
.cw-foe-name { stroke: var(--cw-ink); stroke-width: 1; }
.cw-foe-marker.k-white .cw-foe-name { fill: var(--cw-white); }
.cw-foe-marker.k-interv .cw-foe-name { fill: var(--emap-blue); }
.cw-foe-marker.k-central .cw-foe-name { fill: var(--emap-amber); }
.cw-foe-marker.k-national .cw-foe-name { fill: var(--emap-gray); }
.cw-foe-text { font-size: 11px; font-weight: 800; fill: #1d1d1b; }
.cw-foe-marker.k-interv .cw-foe-text { fill: #fff; }
.cw-foe-numbers { fill: var(--text-primary); }
.cw-foe-numbers-text { font-size: 10.5px; font-weight: 700; fill: var(--bg-surface); }
.cw-foe-wedge { stroke: #14110d; stroke-width: 1.6; stroke-linejoin: round; }
.cw-foe-stop { stroke-width: 4; stroke-linecap: round; paint-order: stroke; }
/* The attacker's arrow in its colour (red is the Red Army's). */
.cw-foe-marker.k-white .cw-foe-stop { stroke: var(--cw-white); }
.cw-foe-marker.k-white .cw-foe-wedge { fill: var(--cw-white); }
.cw-foe-marker.k-interv .cw-foe-stop { stroke: var(--emap-blue); }
.cw-foe-marker.k-interv .cw-foe-wedge { fill: var(--emap-blue); }
.cw-foe-marker.k-central .cw-foe-stop { stroke: var(--emap-amber); }
.cw-foe-marker.k-central .cw-foe-wedge { fill: var(--emap-amber); }
.cw-foe-marker.k-national .cw-foe-stop { stroke: var(--emap-gray); }
.cw-foe-marker.k-national .cw-foe-wedge { fill: var(--emap-gray); }

/* The map's entrance (civil-war.js renderMap): when a season opens the
   attackers come on in their own manner (m-*: infantry in step, cavalry at
   a gallop that overshoots, the legion and Wrangel gliding along the
   railway, the intervention off the sea); in the review the beaten are
   trampled flat and the winners lunge at their city.
   Delays count from when the map came into view (--since), so a re-render
   mid-way carries on; is-waiting holds the first frame until then. */
/* Motion on svg elements (here, the battle board's falls) moves only translate / rotate / scale and fill- / stroke-opacity,
   never transform or opacity. Chrome hands transform and opacity
   animations on svg elements to the compositor: each moving element and
   every label drawn over it became its own GPU layer (21 for one season's
   result, a single city name 442x540), and phones drew the map torn into
   split tiles and stuttered. The individual properties stay on the main
   thread and repaint only the game layer's svg. */
#cwOverlay.is-waiting * { animation-play-state: paused !important; }
.cw-foe-body, .cw-foe-mark, .cw-city.is-flipped { transform-box: fill-box; }
.cw-foe-body { transform-origin: 50% 100%; }
.cw-foe-mark, .cw-city.is-flipped, .cw-spoils-tag { transform-origin: center; }
.cw-spoils-tag { transform-box: fill-box; }
/* The enemy broke: what he left behind, gold under his marker. */
.cw-spoils-tag rect { fill: var(--cw-gold); stroke: var(--cw-gold-rim); stroke-width: 1; }
.cw-spoils-tag text { font-size: 10px; font-weight: 800; fill: #1d1d1b; }
.cw-spoils ul { list-style: none; padding: 0; margin: 4px 0; display: grid; gap: 4px; }
.cw-spoils .cw-face { width: 18px; height: 18px; vertical-align: -4px; margin-right: 5px; }
.cw-foe-marker.is-beaten .cw-foe-body { rotate: -4deg; scale: .97 .8; fill-opacity: .75; stroke-opacity: .75; }
@media (prefers-reduced-motion: no-preference) {
    #cwOverlay.is-plan:is(.is-entering, .is-waiting) .cw-foe-body {
        animation: cw-march 1s steps(5, end) both;
        animation-delay: calc(var(--order) * 220ms - var(--since, 0ms));
    }
    #cwOverlay.is-plan:is(.is-entering, .is-waiting) .m-charge .cw-foe-body { animation: cw-charge .75s cubic-bezier(.2, .7, .3, 1) both; animation-delay: calc(var(--order) * 220ms - var(--since, 0ms)); }
    /* The rail fronts' attackers glide down their railway (civil-war.js glide). */
    #cwOverlay.is-plan:is(.is-entering, .is-waiting) .m-rail .cw-foe-body { animation: none; }
    #cwOverlay.is-plan:is(.is-entering, .is-waiting) .m-sail .cw-foe-body { animation: cw-sail 1.3s ease-out both; animation-delay: calc(var(--order) * 220ms - var(--since, 0ms)); }
    #cwOverlay.is-plan:is(.is-entering, .is-waiting) .cw-foe-wedge {
        animation: cw-op-fade .45s ease-out both;
        animation-delay: calc(var(--order) * 220ms + 900ms - var(--since, 0ms));
    }
    #cwOverlay.is-plan:is(.is-entering, .is-waiting) .cw-foe-mark {
        animation: cw-pop .25s ease-out both;
        animation-delay: calc(var(--order) * 220ms + 800ms + var(--m) * 35ms - var(--since, 0ms));
    }
    #cwOverlay.is-review:is(.is-entering, .is-waiting) .is-beaten .cw-foe-body {
        animation: cw-trample 1.1s ease-in-out both;
        animation-delay: calc(var(--order) * 300ms + 400ms - var(--since, 0ms));
    }
    #cwOverlay.is-review:is(.is-entering, .is-waiting) .is-surging .cw-foe-body {
        animation: cw-surge 1.2s ease-in-out both;
        animation-delay: calc(var(--order) * 300ms + 400ms - var(--since, 0ms));
    }
    #cwOverlay.is-review:is(.is-entering, .is-waiting) .cw-spoils-tag {
        animation: cw-pop .4s ease-out both;
        animation-delay: calc(var(--order) * 300ms + 1500ms - var(--since, 0ms));
    }
    #cwOverlay.is-review:is(.is-entering, .is-waiting) .cw-city.is-flipped {
        animation: cw-flip .6s cubic-bezier(.3, 1.6, .5, 1) both;
        animation-delay: calc(1100ms - var(--since, 0ms));
    }
}
@keyframes cw-march { from { translate: var(--ox) var(--oy); fill-opacity: 0; stroke-opacity: 0; } 20% { fill-opacity: 1; stroke-opacity: 1; } to { translate: 0 0; } }
@keyframes cw-charge {
    from { translate: calc(var(--ox) * 1.6) calc(var(--oy) * 1.6); fill-opacity: 0; stroke-opacity: 0; }
    15% { fill-opacity: 1; stroke-opacity: 1; }
    65% { translate: calc(var(--ox) * -.18) calc(var(--oy) * -.18); }
    85% { translate: calc(var(--ox) * .06) calc(var(--oy) * .06); }
    to { translate: 0 0; }
}
@keyframes cw-sail {
    from { translate: var(--ox) calc(var(--oy) + 4px); rotate: -4deg; fill-opacity: 0; stroke-opacity: 0; }
    35% { translate: calc(var(--ox) * .5) calc(var(--oy) * .5 - 3px); rotate: 3deg; fill-opacity: 1; stroke-opacity: 1; }
    70% { translate: calc(var(--ox) * .15) calc(var(--oy) * .15 + 2px); rotate: -2deg; }
    to { translate: 0 0; rotate: 0deg; }
}
/* An operation told on the map (game.js renderOperation): thick arrows
   in the side's colour over an ink casing, a ring where forces gather, a
   burst where they met. The step being told draws in; the earlier ones
   stay fainter. Only stroke-dashoffset and fill/stroke-opacity move (the
   SVG animation rule above). */
#cwOp { pointer-events: none; --u: calc(1px / var(--opk, 1)); }
@media (min-width: 901px) { .cw-map-frame.is-op-zoom { overflow: hidden; } }
.cw-map-frame.is-op-zoom > svg { max-width: none; }
/* The season's own markers step back while the operation is told, and
   the map's words keep their size on screen however close it goes. */
.cw-map-frame.is-op-zoom #cwOverlay :is([data-front], .cw-troop-pips) { opacity: .15; }
.cw-map-frame.is-op-zoom #cwOverlay :is(.cw-city-label, .cw-map-place, .cw-railway-label) { font-size: calc(13px / var(--opk)); stroke-width: calc(3px / var(--opk)); }
.cw-op-under { fill: none; stroke: var(--cw-ink); stroke-width: calc(var(--u) * 13); stroke-linecap: round; stroke-linejoin: round; }
.cw-op-line { fill: none; stroke: var(--cw-red); stroke-width: calc(var(--u) * 8); stroke-linecap: round; stroke-linejoin: round; }
.cw-op-head { fill: var(--cw-red); stroke: var(--cw-ink); stroke-width: calc(var(--u) * 2); stroke-linejoin: round; }
.cw-op-arrow.k-white .cw-op-line { stroke: var(--cw-white); }
.cw-op-arrow.k-white .cw-op-head { fill: var(--cw-white); }
.cw-op-arrow.k-interv .cw-op-line { stroke: var(--emap-blue); }
.cw-op-arrow.k-interv .cw-op-head { fill: var(--emap-blue); }
.cw-op-arrow.is-next :is(.cw-op-line, .cw-op-under) { stroke-dasharray: calc(var(--u) * 14) calc(var(--u) * 10); }
.cw-op-arrow.is-beaten :is(.cw-op-line, .cw-op-under) { stroke-dasharray: calc(var(--u) * 6) calc(var(--u) * 11); }
.cw-op-arrow.is-beaten .cw-op-line { stroke-width: calc(var(--u) * 5); }
.cw-op-arrow.is-beaten .cw-op-under { stroke-width: calc(var(--u) * 9); }
.cw-op-cross { fill: none; stroke: var(--cw-red); stroke-width: calc(var(--u) * 5); stroke-linecap: round; }
.cw-op-ring { fill: color-mix(in srgb, var(--cw-red) 22%, transparent); stroke: var(--cw-red); stroke-width: calc(var(--u) * 4); stroke-dasharray: calc(var(--u) * 7) calc(var(--u) * 5); }
.cw-op-dot { fill: var(--cw-red); }
.cw-op-burst { fill: #f2c14e; stroke: var(--cw-red); stroke-width: calc(var(--u) * 2.5); stroke-linejoin: round; }
.cw-op-name { font-size: calc(var(--u) * 15); font-weight: 800; fill: var(--text-primary); paint-order: stroke; stroke: var(--bg-surface); stroke-width: calc(var(--u) * 4.5); stroke-linejoin: round; }
.cw-op-step.is-past :is(.cw-op-line, .cw-op-under, .cw-op-head, .cw-op-ring, .cw-op-burst, .cw-op-cross) { stroke-opacity: .55; fill-opacity: .55; }
.cw-op-clash .cw-op-burst { transform-box: fill-box; transform-origin: center; }
@media (prefers-reduced-motion: no-preference) {
    .cw-op-step.is-drawing .cw-op-arrow :is(.cw-op-line, .cw-op-under) {
        stroke-dasharray: var(--len) 999; animation: cw-op-draw 1.1s ease-out both; animation-delay: calc(var(--k) * 500ms + 200ms);
    }
    .cw-op-step.is-drawing .cw-op-arrow.is-next :is(.cw-op-line, .cw-op-under) { stroke-dasharray: calc(var(--u) * 14) calc(var(--u) * 10); animation: cw-op-fade .6s ease-out both; animation-delay: calc(var(--k) * 500ms + 200ms); }
    .cw-op-step.is-drawing .cw-op-arrow.is-beaten :is(.cw-op-line, .cw-op-under) { stroke-dasharray: calc(var(--u) * 6) calc(var(--u) * 11); animation: cw-op-fade .6s ease-out both; animation-delay: calc(var(--k) * 500ms + 200ms); }
    .cw-op-step.is-drawing :is(.cw-op-head, .cw-op-cross, .cw-op-name, .cw-op-ring, .cw-op-dot) { animation: cw-op-fade .35s ease-out both; animation-delay: calc(var(--k) * 500ms + 1.1s); }
    .cw-op-step.is-drawing .cw-op-gather :is(.cw-op-ring, .cw-op-dot, .cw-op-name) { animation-delay: calc(var(--k) * 500ms + 200ms); }
    .cw-op-step.is-drawing .cw-op-clash :is(.cw-op-name) { animation-delay: calc(var(--k) * 500ms + 400ms); }
    .cw-op-step.is-drawing .cw-op-burst { animation: cw-pop .45s ease-out both; animation-delay: calc(var(--k) * 500ms + 200ms); }
    .cw-op-step.is-now .cw-op-ring { animation: cw-op-pulse 1.4s ease-in-out infinite; }
}
@keyframes cw-op-draw { from { stroke-dashoffset: var(--len); stroke-opacity: 0; } 8% { stroke-opacity: 1; } to { stroke-dashoffset: 0; stroke-opacity: 1; } }
@keyframes cw-op-fade { from { fill-opacity: 0; stroke-opacity: 0; } to { fill-opacity: 1; stroke-opacity: 1; } }
@keyframes cw-op-pulse { 50% { stroke-opacity: .35; } }
@keyframes cw-pop { from { scale: 0; } 70% { scale: 1.35; } to { scale: 1; } }
@keyframes cw-trample {
    from { translate: 0 0; rotate: 0deg; scale: 1; fill-opacity: 1; stroke-opacity: 1; }
    30% { translate: 0 -3px; scale: 1.04; }
    42% { translate: 0 0; scale: 1.18 .35; }
    56% { rotate: 2deg; scale: 1.02 .9; }
    68% { rotate: 0deg; scale: 1.12 .45; }
    82% { rotate: -5deg; scale: .97 .86; }
    to { rotate: -4deg; scale: .97 .8; fill-opacity: 0.75; stroke-opacity: 0.75; }
}
@keyframes cw-surge {
    from, 40%, 75%, to { translate: 0 0; scale: 1; }
    22% { translate: var(--lx) var(--ly); scale: 1.06; }
    58% { translate: calc(var(--lx) * 1.5) calc(var(--ly) * 1.5); scale: 1.1; }
}
@keyframes cw-flip { from { scale: 2.4; fill-opacity: 0.2; stroke-opacity: 0.2; } to { scale: 1; fill-opacity: 1; stroke-opacity: 1; } }

/* Fronts */
.cw-fronts { display: grid; gap: 10px; min-width: 0; }
.cw-fronts h3 { font-size: 1.05rem; padding: 0 2px; }
.cw-front { border: 1px solid var(--border); background: var(--bg-surface); padding: 12px 14px; display: grid; gap: 6px; }
.cw-front:hover, .cw-front:focus-within { border-color: var(--text-secondary); }
.cw-front:focus-visible { outline: 3px solid var(--text-primary); outline-offset: 2px; }
/* A front against history (civil-war.js trendMark): ▼ behind, ▲ ahead. */
.cw-trend { margin-left: 5px; font-size: .78em; color: var(--text-primary); }
.cw-front header { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.cw-front header h4 { font-size: 1rem; }
.cw-front header span { font-size: .78rem; color: var(--text-secondary); }
.cw-front p { margin: 0; font-size: .9rem; line-height: var(--leading-normal); }
.cw-front-line { color: var(--text-secondary); }
.cw-front-controls { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.cw-stepper { display: inline-flex; align-items: stretch; }
.games-shell .cw-stepper button { min-width: 44px; padding: 8px 0; font-weight: 800; }
.cw-stepper output { display: grid; place-items: center; min-width: 86px; border-block: 1px solid var(--border); font-variant-numeric: tabular-nums; font-weight: 700; }
.games-shell .cw-toggle { padding: 8px 12px; font-size: .85rem; }
.games-shell .cw-toggle[aria-pressed="true"] { border-color: var(--cw-red); color: var(--text-primary); }
/* In use on another front: pressing it moves it here. */
.games-shell .cw-toggle.is-elsewhere { border-style: dashed; color: var(--text-secondary); }
.games-shell .cw-toggle[aria-disabled="true"] { opacity: .45; cursor: default; }
.cw-front header .cw-front-hint { font-size: .76rem; color: var(--text-primary); }
.cw-front header .cw-front-hint:empty { display: none; }
.cw-meter { display: grid; gap: 4px; margin: 2px 0 10px; }
.cw-meter-top { display: flex; justify-content: space-between; gap: 8px; font-size: .9rem; font-variant-numeric: tabular-nums; }
.cw-meter-top span { color: var(--text-secondary); }
/* Bands coloured by their result against history; the black line is the strength. */
.cw-meter-bar { position: relative; height: 16px; border: 1px solid var(--border); background: var(--bg-canvas); }
.cw-meter-band { position: absolute; top: 0; bottom: 0; }
.cw-meter-band.is-behind { background: repeating-linear-gradient(-45deg, color-mix(in srgb, var(--text-primary) 28%, transparent) 0 3px, transparent 3px 6px); }
.cw-meter-band.is-even { background: color-mix(in srgb, var(--text-secondary) 18%, transparent); }
.cw-meter-band.is-ahead { background: color-mix(in srgb, var(--btn-primary, #2da44e) 35%, transparent); }
.cw-meter-tick { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--border); }
.cw-meter-tick small { position: absolute; top: 100%; left: 0; transform: translateX(-50%); padding-top: 1px; font-size: .7rem; font-variant-numeric: tabular-nums; color: var(--text-secondary); }
.cw-meter-now { position: absolute; top: -4px; bottom: -4px; width: 3px; margin-left: -1.5px; background: var(--text-primary); transition: left .2s ease; }
@media (prefers-reduced-motion: reduce) { .cw-meter-now, .cw-seg { transition: none; } }
.cw-foes { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 6px; }
.cw-foes-label { font-size: .75rem; font-weight: 700; color: var(--text-secondary); padding-top: 5px; }
.cw-foe { flex: 1 1 100%; border: 1px solid var(--border); background: var(--bg-canvas); }
.cw-foe summary { cursor: pointer; display: flex; align-items: center; gap: 7px; min-height: 32px; padding: 3px 8px; font-size: .86rem; font-weight: 700; }
.cw-foe summary small { color: var(--text-secondary); font-weight: 700; }
.cw-foe p { margin: 0; padding: 0 8px 8px; font-size: .82rem !important; color: var(--text-secondary); }
.cw-foe.is-white { border-left: 4px solid var(--cw-white); }
.cw-foe.is-interv { border-left: 4px solid var(--emap-blue); }
.cw-foe.is-central { border-left: 4px solid var(--emap-amber); }
.cw-foe.is-national { border-left: 4px solid var(--emap-gray); }
.cw-flag { width: 18px; height: 12px; flex: 0 0 auto; outline: 1px solid color-mix(in srgb, var(--text-secondary) 50%, transparent); }
.cw-foe-dot { width: 12px; height: 12px; flex: 0 0 auto; border-radius: 50%; border: 1.5px solid var(--cw-ink); background: var(--cw-white); }
.cw-foe.is-national .cw-foe-dot { background: var(--emap-gray); }
.cw-foe.is-interv .cw-foe-dot { background: var(--emap-blue); }
.cw-parts { color: var(--text-secondary); font-size: .8rem !important; }
/* The battle's order of battle from the plan: units that join solid, units that wait dashed. */
.cw-muster { display: grid; gap: 4px; margin-bottom: 8px; }
.cw-muster p { font-size: .82rem !important; color: var(--text-secondary); }
.cw-muster p strong { color: var(--text-primary); }
.cw-muster ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 4px; }
.cw-muster li { display: inline-flex; align-items: baseline; gap: 5px; padding: 3px 7px; font-size: .78rem; border: 1px solid var(--border); }
.cw-muster li.is-in { border-color: var(--cw-red); background: color-mix(in srgb, var(--cw-red) 14%, transparent); font-weight: 700; }
.cw-muster li.is-out { border-style: dashed; color: var(--text-secondary); }
.cw-muster li small { font-size: .72rem; }
.cw-warn { border-left: 4px solid var(--text-primary); padding: 8px 12px; margin: 0; background: var(--bg-surface); font-size: .9rem; }

/* Advisor cards */
.cw-cards, .cw-choice, .cw-review, .cw-outcome { border: 1px solid var(--border); background: var(--bg-surface); padding: 16px var(--content-inset); margin-bottom: 18px; }
.cw-cards h3, .cw-review h3 { font-size: 1.05rem; margin-bottom: 12px; }
.cw-card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 10px; }
.cw-card { border: 1px solid var(--border); padding: 12px; display: flex; flex-direction: column; gap: 6px; background: var(--bg-canvas); }
.cw-card.is-played { border-color: var(--cw-red); }
.cw-card h4 { font-size: .98rem; }
.cw-card p { margin: 0; font-size: .86rem; line-height: var(--leading-normal); }
.cw-advisor { font-size: .8rem !important; }
/* A person's name with their drawn portrait (art.js) on a chip of their
   side's colour (the portrait's disc), the same in light and dark. */
.cw-person { display: inline-flex; align-items: center; gap: 5px; vertical-align: middle; padding: 1px 7px 1px 2px; border: 1px solid #1d1d1b; background: var(--cw-chip, #a21e2a); color: var(--cw-chip-ink, #fff); font-weight: 700; text-decoration: none; }
.cw-person:hover { color: var(--cw-chip-ink, #fff); text-decoration: underline; }
.cw-person .cw-face { width: 1.6em; height: 1.6em; flex: none; }
.cw-person.is-white { --cw-chip: #e8e4da; --cw-chip-ink: #1d1d1b; }
.cw-person.is-national { --cw-chip: #8d8d86; --cw-chip-ink: #1d1d1b; }
.cw-person.is-black { --cw-chip: #2b2b28; --cw-chip-ink: #f1e6cf; border-color: #8d8d86; }
.cw-person.is-interv { --cw-chip: #2f6fb8; --cw-chip-ink: #fff; }
.cw-person.is-central { --cw-chip: #c7861c; --cw-chip-ink: #1d1d1b; }
.cw-note { color: var(--text-secondary); font-size: .8rem !important; }
.cw-effect { font-weight: 700; margin-top: auto !important; }
.cw-card-target { display: flex; flex-wrap: wrap; gap: 6px; }
.games-shell .cw-card button, .games-shell .cw-card select { padding: 8px 10px; font-size: .85rem; }
.cw-card-target select { flex: 1 1 120px; }

/* Dilemma, and the season's incident (a darker rim: something that
   happened, not a policy) */
.cw-choice { border-left: 4px solid var(--cw-red); }
.cw-incident { border-left-color: var(--cw-ink); }
.cw-incident .cw-incident-next { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border); }
.cw-choice h3 { font-size: 1.25rem; margin: 6px 0 8px; }
.cw-talk { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 8px; }
.cw-talk li { display: grid; grid-template-columns: minmax(0, max-content) 1fr; gap: 10px; align-items: start; font-size: .92rem; line-height: var(--leading-normal); }
.cw-talk .cw-person { justify-self: start; }
.cw-talk-as { padding: 2px 7px; border: 1px solid var(--border); font-size: .86rem; color: var(--text-secondary); }
.cw-talk q { quotes: '「' '」'; padding-top: 1px; }
@media (max-width: 600px) { .cw-talk li { grid-template-columns: 1fr; gap: 3px; } }
.cw-options { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 10px; margin-top: 12px; }
.games-shell .cw-option { text-align: left; display: grid; gap: 6px; padding: 14px; }
.cw-option:hover { background: var(--bg-surface-hover); }
.cw-option span, .cw-option small { color: var(--text-secondary); line-height: var(--leading-normal); }
.games-shell .cw-option[aria-pressed="true"] { border: 2px solid var(--cw-red); padding: 13px; }
.cw-option[aria-pressed="true"] strong::before { content: '✓ '; color: var(--cw-red); }

.cw-command { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 12px; margin-bottom: 18px; padding: 0 var(--content-inset); }
.cw-command p { margin: 0; color: var(--text-secondary); }

/* Review */
.cw-notes { padding-left: 20px; margin: 0 0 12px; }
.cw-notes li { line-height: var(--leading-normal); margin-bottom: 4px; }
.cw-history { border-top: 1px solid var(--border); padding-top: 12px; margin: 12px 0; }
.cw-history h4 { font-size: 1.05rem; margin: 4px 0 8px; }
/* The season's account: a gauge per stat, its causes as chips, then what
   the next season gets. A rise is green, a fall orange. */
.cw-ledger { display: grid; gap: 14px; margin: 0 0 14px; padding: 12px 14px 14px; border: 1px solid var(--border); background: color-mix(in srgb, var(--text-primary) 3%, var(--bg-surface)); }
.cw-ledger .games-kicker { margin: 0; }
.cw-ledger-row { display: grid; gap: 6px; }
.cw-ledger-head { display: flex; align-items: baseline; gap: 10px; margin: 0; }
.cw-ledger-head strong { font-size: 1rem; }
.cw-ledger-value { color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.cw-ledger-value b { color: var(--text-primary); font-size: 1.15rem; }
.cw-ledger-delta { margin-left: auto; min-width: 3.2em; padding: 1px 8px; text-align: center; font-weight: 800; font-size: 1.05rem; font-variant-numeric: tabular-nums; color: var(--text-secondary); border: 1px solid var(--border); }
.cw-ledger-row.is-up .cw-ledger-delta { background: var(--cw-good); border-color: var(--cw-good); color: var(--cw-good-ink); }
.cw-ledger-row.is-down .cw-ledger-delta { background: var(--cw-loss); border-color: var(--cw-loss); color: #fff; }
/* The gauge: what stayed, what changed, and the lines below which
   something gives, a line's words above it when it is crossed or holds now,
   below it when it is the next one down. */
.cw-gauge { position: relative; height: 14px; margin: 18px 0 18px; background: color-mix(in srgb, var(--text-primary) 10%, transparent); }
.cw-gauge-keep { position: absolute; inset: 0 auto 0 0; background: color-mix(in srgb, var(--text-primary) 55%, transparent); }
.cw-gauge-change { position: absolute; top: 0; bottom: 0; }
.is-up .cw-gauge-change { background: var(--cw-good); transform-origin: left; }
.is-down .cw-gauge-change { background: repeating-linear-gradient(-45deg, var(--cw-loss) 0 3px, transparent 3px 6px); outline: 1px solid var(--cw-loss); outline-offset: -1px; }
.is-down .cw-gauge-change::after { content: ''; position: absolute; inset: 0; background: var(--cw-loss); transform-origin: left; scale: 0 1; }
.cw-gauge-mark { position: absolute; top: -4px; bottom: -4px; width: 2px; margin-left: -1px; background: var(--text-primary); opacity: .45; }
.cw-gauge-mark span { position: absolute; left: 50%; translate: -50% 0; white-space: nowrap; font-size: .72rem; font-weight: 700; font-style: normal; line-height: 1; }
.cw-gauge-mark:is(.is-fell, .is-rose, .is-in-force, .is-ahead) { opacity: 1; }
.cw-gauge-mark:is(.is-fell, .is-rose, .is-in-force) span { bottom: calc(100% + 3px); }
.cw-gauge-mark.is-ahead { background: var(--text-secondary); }
.cw-gauge-mark.is-ahead span { top: calc(100% + 3px); color: var(--text-secondary); font-weight: 600; }
.cw-gauge-mark.is-fell { background: var(--cw-loss); width: 3px; }
.cw-gauge-mark.is-fell span { color: #fff; background: var(--cw-loss); padding: 2px 5px; }
.cw-gauge-mark.is-rose { background: var(--cw-good); width: 3px; }
.cw-gauge-mark.is-rose span { color: var(--cw-good-ink); background: var(--cw-good); padding: 2px 5px; }
.cw-gauge-mark.is-in-force span { color: var(--cw-loss); }
/* A label near an end keeps inside the gauge. */
.cw-gauge-mark.is-near-start span { left: 0; translate: 0 0; }
.cw-gauge-mark.is-near-end span { left: auto; right: 0; translate: 0 0; }
.cw-ledger-causes { list-style: none; display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; }
.cw-ledger-causes li { display: inline-flex; align-items: baseline; gap: 6px; padding: 3px 8px; font-size: .82rem; border: 1px solid var(--border); background: var(--bg-surface); }
.cw-ledger-causes li b { font-variant-numeric: tabular-nums; }
.cw-ledger-causes li.is-plus { border-color: var(--cw-good); }
.cw-ledger-causes li.is-plus b { color: var(--cw-good); }
.cw-ledger-causes li.is-minus { border-color: var(--cw-loss); }
.cw-ledger-causes li.is-minus b { color: var(--cw-loss); }
.cw-ledger-next .cw-ledger-causes li.is-plus { background: var(--cw-good); color: var(--cw-good-ink); }
.cw-ledger-next .cw-ledger-causes li.is-minus { background: var(--cw-loss); color: #fff; }
.cw-ledger-next { display: grid; gap: 6px; border-top: 1px dashed var(--border); padding-top: 10px; }
/* Fresh, the rows come in one after another (--k), each gauge moving
   from where the stat stood, then the lines it crossed pop. */
@media (prefers-reduced-motion: no-preference) {
    .cw-ledger.is-fresh .cw-ledger-row { animation: cw-ledger-in .35s ease-out both; animation-delay: calc(var(--k) * .3s); }
    .cw-ledger.is-fresh .is-up .cw-gauge-change { animation: cw-gauge-grow .8s ease-out both; animation-delay: calc(var(--k) * .3s + .3s); }
    .cw-ledger.is-fresh .is-down .cw-gauge-change::after { animation: cw-gauge-drain .9s ease-in both; animation-delay: calc(var(--k) * .3s + .3s); }
    .cw-ledger.is-fresh .cw-gauge-mark:is(.is-fell, .is-rose) span { animation: cw-mark-pop .4s ease-out both; animation-delay: calc(var(--k) * .3s + 1.1s); }
}
@keyframes cw-ledger-in { from { opacity: 0; translate: 0 6px; } to { opacity: 1; translate: 0 0; } }
@keyframes cw-gauge-grow { from { scale: 0 1; } to { scale: 1 1; } }
@keyframes cw-gauge-drain { from { scale: 1 1; } to { scale: 0 1; } }
@keyframes cw-mark-pop { from { opacity: 0; scale: .6; } 70% { opacity: 1; scale: 1.12; } to { opacity: 1; scale: 1; } }
.cw-links { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: .9rem; }

/* Battle */
.cw-battle { border: 1px solid var(--border); background: var(--bg-surface); padding: 16px var(--content-inset); margin-bottom: 18px; }
.cw-battle-head h2 { font-size: clamp(1.25rem, 3vw, 1.7rem); margin: 4px 0 10px; }
.cw-battle-brief { margin-bottom: 14px; }
.cw-battle-brief summary { cursor: pointer; font-weight: 700; min-height: 44px; display: flex; align-items: center; }
.cw-battle-brief p { margin: 0 0 8px; max-width: 46rem; }
.cw-battle-layout { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(260px, 1fr); gap: 18px; align-items: start; }
.cw-grid-wrap { min-width: 0; }
.cw-grid-scroll { position: relative; }
.cw-grid { display: block; width: 100%; max-width: 680px; height: auto; overflow: visible; font-family: inherit; outline: 2px solid color-mix(in srgb, var(--text-primary) 65%, transparent); outline-offset: -1px; }
.cw-tile { stroke: color-mix(in srgb, var(--border) 35%, transparent); stroke-width: 1; }
.cw-tile.t-steppe { fill: color-mix(in srgb, #c8b77a 30%, var(--bg-surface)); }
.cw-tile.t-town { fill: color-mix(in srgb, #9d8a62 55%, var(--bg-surface)); }
.cw-tile.t-ford { fill: color-mix(in srgb, #5f93b8 45%, var(--bg-surface)); }
.cw-tile.t-ford.is-flooded { fill: color-mix(in srgb, #2f6fb8 60%, var(--bg-surface)); }
.cw-tile.t-deep { fill: color-mix(in srgb, #1f4f8a 75%, var(--bg-surface)); }
.cw-tile.t-wall { fill: color-mix(in srgb, #b4652a 70%, var(--bg-surface)); }
.cw-tile.t-yushun { fill: color-mix(in srgb, #8c7b55 60%, var(--bg-surface)); }
.cw-tile.t-chongar { fill: color-mix(in srgb, #8c7b55 60%, var(--bg-surface)); }
.cw-tile.t-strait { fill: color-mix(in srgb, #3f6f95 55%, var(--bg-surface)); }
/* Kazan: the Volga, the bridge over it, the wood, the city round its kremlin, the wharves. */
.cw-tile.t-river { fill: color-mix(in srgb, #2f6fb8 60%, var(--bg-surface)); }
.cw-tile.t-bridge { fill: color-mix(in srgb, #3f6f95 55%, var(--bg-surface)); }
.cw-tile.t-forest { fill: color-mix(in srgb, #4f7a3a 50%, var(--bg-surface)); }
.cw-tile.t-city { fill: color-mix(in srgb, #9d8a62 70%, var(--bg-surface)); }
.cw-tile.t-kremlin { fill: color-mix(in srgb, #b4652a 70%, var(--bg-surface)); }
.cw-tile.t-docks { fill: color-mix(in srgb, #7d8a96 55%, var(--bg-surface)); }
/* Ufa: the Belaya's crossing by boat and raft, the sappers' pontoon, the
   blown railway bridge, the east bank's trenches. */
.cw-tile.t-crossing { fill: color-mix(in srgb, #5f93b8 50%, var(--bg-surface)); }
.cw-tile.t-pontoon, .cw-tile.t-blown { fill: color-mix(in srgb, #2f6fb8 60%, var(--bg-surface)); }
.cw-tile.t-trench { fill: color-mix(in srgb, #8c7b55 60%, var(--bg-surface)); }
.cw-tile.t-ravine { fill: color-mix(in srgb, #7a5a2e 50%, var(--bg-surface)); }
.cw-tile.t-works { fill: color-mix(in srgb, #7d7466 60%, var(--bg-surface)); }
/* Simbirsk: the Sviyaga to wade, the Venets heights over the Volga. */
.cw-tile.t-stream { fill: color-mix(in srgb, #5f93b8 38%, var(--bg-surface)); }
.cw-tile.t-heights { fill: color-mix(in srgb, #8a6a3c 55%, var(--bg-surface)); }
.cw-tile.is-goal { stroke: var(--clr-red); stroke-width: 3; }
/* Tile drawings (civil-war.js tileMarks). */
.cw-marks { pointer-events: none; }
.cw-marks path { fill: none; stroke-linecap: round; stroke-linejoin: round; }
.cw-marks .cw-mark-grass { stroke: color-mix(in srgb, #6d7a2e 70%, var(--text-primary)); stroke-opacity: .55; stroke-width: 1.1; }
.cw-marks .cw-mark-fir { fill: color-mix(in srgb, #1f4a1a 80%, var(--bg-surface)); stroke: #0f220c; stroke-width: .8; }
.cw-marks .cw-mark-trunk { stroke: #3a2614; stroke-width: 1.6; }
.cw-marks .cw-mark-house { fill: color-mix(in srgb, #e7dcc3 80%, var(--bg-surface)); stroke: #3a2a18; stroke-width: 1; }
.cw-marks .cw-mark-pier { stroke: #2a2a26; stroke-width: 1.6; }
.cw-marks .cw-mark-fort { fill: color-mix(in srgb, #7a3a16 80%, var(--bg-surface)); stroke: #2a1406; stroke-width: 1.1; }
.cw-marks .cw-mark-brick { stroke: #2a1406; stroke-opacity: .6; stroke-width: .8; }
.cw-marks .cw-mark-trench { stroke: color-mix(in srgb, var(--text-primary) 60%, transparent); stroke-width: 1.8; }
.cw-marks .cw-mark-wire { stroke: color-mix(in srgb, var(--text-primary) 55%, transparent); stroke-width: .8; }
.cw-marks .cw-mark-truss { stroke: #2a2a26; stroke-width: 2; }
.cw-marks .cw-mark-water { stroke: #ffffff; stroke-opacity: .45; stroke-width: 1.3; }
.cw-marks .cw-mark-gully { stroke: #2e2210; stroke-opacity: .85; stroke-width: 3.2; fill: none; }
.cw-marks .cw-mark-bank { stroke: #2e2210; stroke-opacity: .7; stroke-width: 1.2; }
.cw-marks .cw-mark-channel { stroke: #2f6fb8; stroke-opacity: .8; stroke-width: 5; }
.cw-marks .cw-mark-reed { stroke: color-mix(in srgb, #4f6a2a 80%, var(--text-primary)); stroke-width: 1.2; }
.cw-marks .cw-mark-ridge { stroke: #2e2210; stroke-opacity: .85; stroke-width: 2.4; }
.cw-marks .cw-mark-smoke { stroke: color-mix(in srgb, var(--text-primary) 45%, transparent); stroke-width: 1.4; fill: none; }
.cw-wear { font-size: 8px; font-weight: 800; fill: var(--text-primary); paint-order: stroke; stroke: var(--bg-surface); stroke-width: 2px; pointer-events: none; }
.cw-tile-label { font-size: 9px; font-weight: 700; fill: var(--text-primary); paint-order: stroke; stroke: var(--bg-surface); stroke-width: 2.5px; stroke-linejoin: round; pointer-events: none; }
.cw-tile-label.is-goal { fill: var(--clr-red); font-weight: 800; }
/* The post to hold (civil-war.js postMark): a dashed frame, solid blue when an enemy can march in. */
.cw-post { pointer-events: none; }
.cw-post-frame { fill: none; stroke: var(--text-primary); stroke-width: 2.5; stroke-dasharray: 6 3; }
.cw-post.is-open .cw-post-frame { stroke: var(--emap-blue); stroke-width: 3.5; stroke-dasharray: none; }
.cw-post-name { font-size: 9px; font-weight: 800; fill: var(--text-primary); paint-order: stroke; stroke: var(--bg-surface); stroke-width: 3px; stroke-linejoin: round; }
.cw-terrain-key .k-post { border: 2px dashed var(--text-primary); }
.cw-post-alarm { flex-basis: 100%; margin: 0; padding: 6px 10px; border: 2px solid var(--emap-blue); background: color-mix(in srgb, var(--emap-blue) 22%, var(--bg-surface)); font-weight: 800; font-size: .88rem; }
.cw-flank-zone { fill: none; stroke: var(--text-primary); stroke-opacity: .55; stroke-width: 1.5; stroke-dasharray: 6 4; pointer-events: none; }
.cw-danger { fill: var(--emap-blue); fill-opacity: .28; stroke: var(--emap-blue); stroke-width: 1.5; pointer-events: none; }
.cw-hit { fill: var(--text-primary); fill-opacity: .26; stroke: var(--text-primary); stroke-opacity: .65; stroke-width: 1; cursor: pointer; }
.cw-hit:hover { fill-opacity: .4; }
.cw-place { font-size: 10px; font-weight: 800; fill: var(--text-primary); paint-order: stroke; stroke: var(--bg-surface); stroke-width: 3px; stroke-linejoin: round; pointer-events: none; }
.cw-place.is-flank { fill: var(--text-secondary); }
.cw-terrain-key { display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: .78rem; color: var(--text-secondary); margin: 8px 0 0; }
.cw-terrain-key span { display: inline-flex; align-items: center; gap: 5px; }
.cw-terrain-key i { width: 14px; height: 14px; border: 1px solid var(--border); }
.cw-terrain-key .k-steppe { background: color-mix(in srgb, #c8b77a 30%, var(--bg-surface)); }
.cw-terrain-key .k-ford { background: color-mix(in srgb, #5f93b8 45%, var(--bg-surface)); }
.cw-terrain-key .k-wall { background: color-mix(in srgb, #b4652a 70%, var(--bg-surface)); }
.cw-terrain-key .k-yushun { background: color-mix(in srgb, #8c7b55 60%, var(--bg-surface)); }
.cw-terrain-key .k-strait { background: color-mix(in srgb, #3f6f95 55%, var(--bg-surface)); }
.cw-terrain-key .k-town { background: color-mix(in srgb, #9d8a62 55%, var(--bg-surface)); }
.cw-terrain-key .k-river { background: color-mix(in srgb, #2f6fb8 60%, var(--bg-surface)); }
.cw-terrain-key .k-bridge { background: color-mix(in srgb, #3f6f95 55%, var(--bg-surface)); }
.cw-terrain-key .k-forest { background: color-mix(in srgb, #4f7a3a 50%, var(--bg-surface)); }
.cw-terrain-key .k-city { background: color-mix(in srgb, #9d8a62 70%, var(--bg-surface)); }
.cw-terrain-key .k-kremlin { background: color-mix(in srgb, #b4652a 70%, var(--bg-surface)); }
.cw-terrain-key .k-docks { background: color-mix(in srgb, #7d8a96 55%, var(--bg-surface)); }
.cw-terrain-key .k-crossing { background: color-mix(in srgb, #5f93b8 50%, var(--bg-surface)); }
.cw-terrain-key :is(.k-pontoon, .k-blown) { background: color-mix(in srgb, #2f6fb8 60%, var(--bg-surface)); }
.cw-terrain-key .k-trench { background: color-mix(in srgb, #8c7b55 60%, var(--bg-surface)); }
.cw-terrain-key .k-ravine { background: color-mix(in srgb, #7a5a2e 50%, var(--bg-surface)); }
.cw-terrain-key .k-works { background: color-mix(in srgb, #7d7466 60%, var(--bg-surface)); }
.cw-terrain-key .k-stream { background: color-mix(in srgb, #5f93b8 38%, var(--bg-surface)); }
.cw-terrain-key .k-heights { background: color-mix(in srgb, #8a6a3c 55%, var(--bg-surface)); }
.cw-terrain-key .k-move { background: color-mix(in srgb, var(--text-primary) 26%, transparent); border: 1px solid color-mix(in srgb, var(--text-primary) 65%, transparent); }
.cw-terrain-key .k-flank { border: 1.5px dashed color-mix(in srgb, var(--text-primary) 55%, transparent); }
.cw-terrain-key .k-danger { background: color-mix(in srgb, #2f6fb8 35%, transparent); border-color: var(--emap-blue); }
.cw-hit:focus-visible { outline: none; stroke: var(--text-primary); stroke-width: 3; }
.cw-unit { cursor: pointer; }
.cw-unit > rect:first-child { stroke-width: 1.5; }
.cw-unit.is-red > rect:first-child { fill: var(--cw-red); stroke: #4a0b12; }
.cw-unit.is-white > rect:first-child { fill: #f4f1e8; stroke: var(--emap-blue); }
.cw-unit-tag { font-size: 10px; font-weight: 800; letter-spacing: -.03em; pointer-events: none; }
.cw-unit-hp { font-size: 11px; font-weight: 800; font-variant-numeric: tabular-nums; pointer-events: none; }
/* The unit's picture (civil-war.js unitPicture) in its side's ink. */
.cw-pic { pointer-events: none; }
.cw-pic path, .cw-pic circle { stroke-linecap: round; stroke-linejoin: round; }
.cw-pic .cw-pic-line { fill: none; stroke-width: 1.4; }
.cw-pic .cw-pic-barrel { fill: none; stroke-width: 3; }
.cw-pic .cw-pic-wheel { fill: none; stroke-width: 1.6; }
.cw-unit.is-red .cw-unit-tag, .cw-unit.is-red .cw-unit-hp, .cw-unit.is-red .cw-pic-fill { fill: #fff; }
.cw-unit.is-red :is(.cw-pic-line, .cw-pic-barrel, .cw-pic-wheel) { stroke: #fff; }
.cw-unit.is-white .cw-unit-tag, .cw-unit.is-white .cw-unit-hp, .cw-unit.is-white .cw-pic-fill { fill: #1d2a44; }
.cw-unit.is-white :is(.cw-pic-line, .cw-pic-barrel, .cw-pic-wheel) { stroke: #1d2a44; }
/* Headgear: the figure's ink, rimmed in the counter's colour so it parts
   from the head; an officer's shoulder boards in gold. */
.cw-pic .cw-pic-cap { stroke-width: .7; stroke-linejoin: round; }
.cw-unit.is-red .cw-pic-cap { fill: #fff; stroke: var(--cw-red); }
.cw-unit.is-white .cw-pic-cap { fill: #1d2a44; stroke: #f4f1e8; }
.cw-pic .cw-pic-gold { fill: #c9a227; stroke: #1d2a44; stroke-width: .4; }
/* A unit that has just struck lunges at its target once (translate only,
   per the svg animation rule above). */
@media (prefers-reduced-motion: no-preference) {
    .cw-unit.is-lunging { animation: cw-lunge .6s ease-out both; animation-delay: calc(0ms - var(--since, 0ms)); }
}
@keyframes cw-lunge { 0%, 100% { translate: 0 0; } 35% { translate: var(--lx) var(--ly); } }
.cw-hp-track { fill: #00000040; pointer-events: none; }
.cw-hp { fill: #f4f1e8; pointer-events: none; }
.cw-unit.is-white .cw-hp { fill: var(--emap-blue); }
/* The strength a foretold blow takes: the target's in red, the attacker's
   (the counter, while a target is pointed at) in orange, apart from the
   Red Army's own red. */
.cw-hp-loss { fill: var(--cw-red); pointer-events: none; }
.cw-hp-loss.is-counter { fill: #ff8a3d; }
.cw-unit.is-done { opacity: .5; }
/* The unit picked: corner brackets in the page's ink; a target in red. */
.cw-unit-pick { fill: none; stroke: var(--text-primary); stroke-width: 3; stroke-linecap: square; pointer-events: none; }
.cw-unit.is-target .cw-unit-pick { stroke: var(--clr-red); }
.cw-unit:focus, .cw-unit:focus-visible { outline: none; }
.cw-unit:focus-visible > rect:first-child { stroke: var(--text-primary); stroke-width: 2.5; }
.cw-badge { pointer-events: none; }
.cw-grid-scroll { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
.cw-badge text { font-size: 10px; font-weight: 800; fill: #fff; }
.cw-badge.is-preview rect, .cw-badge.is-red rect { fill: var(--cw-red); stroke: #fff; stroke-width: 1; }
/* A foretold loss beside a unit's strength: red on the target, orange on
   the attacker; outlined so it reads over the unit's picture. */
.cw-unit .cw-hp-delta { font-size: 10px; paint-order: stroke; stroke-width: 3px; stroke-linejoin: round; }
/* The number and its loss lie over the picture's edge: a halo of the
   counter's own colour keeps them clear of it. */
.cw-unit .cw-unit-hp { paint-order: stroke; stroke-width: 3px; stroke-linejoin: round; }
.cw-unit.is-red .cw-unit-hp { stroke: var(--cw-red); }
.cw-unit.is-white .cw-unit-hp { stroke: #f4f1e8; }
.cw-unit .cw-hp-delta.is-loss { fill: var(--cw-red); stroke: #fff; }
.cw-unit .cw-hp-delta.is-counter { fill: #ff8a3d; stroke: #1d1606; }
.cw-ground { pointer-events: none; }
.cw-ground.is-cover { fill: #7a3f14; }
.cw-ground.is-exposed { fill: #1f4f8a; }
.cw-ground-text { font-size: 9px; font-weight: 800; fill: #fff; pointer-events: none; }
.cw-badge.is-white rect { fill: var(--emap-blue); stroke: #fff; stroke-width: 1; }
.cw-arrows { pointer-events: none; }
.cw-blow { stroke-width: 3; fill: none; }
.cw-blow.is-white { stroke: var(--emap-blue); }
.cw-blow.is-red { stroke: var(--cw-red); }
.cw-head.is-white { fill: var(--emap-blue); }
.cw-head.is-red { fill: var(--cw-red); }
.cw-trail { stroke: var(--emap-blue); stroke-width: 2; stroke-dasharray: 3 3; fill: none; }
.cw-battle-side { display: grid; gap: 12px; min-width: 0; }
.cw-battle-status { list-style: none; padding: 0; margin: 0; display: grid; gap: 4px; font-size: .9rem; }
.cw-battle-status li:first-child { font-weight: 800; font-size: 1.05rem; }
.cw-unit-info { border: 1px solid var(--border); padding: 10px 12px; background: var(--bg-canvas); }
.cw-unit-info h3 { font-size: 1rem; }
.cw-unit-info small { color: var(--text-secondary); font-weight: 400; }
.cw-unit-info p { margin: 4px 0 0; font-size: .88rem; line-height: var(--leading-normal); }
.cw-info-hp { display: flex; align-items: center; gap: 8px; }
.cw-info-bar { flex: 1 1 auto; max-width: 160px; height: 7px; background: linear-gradient(90deg, var(--bar) var(--hp), color-mix(in srgb, var(--text-primary) 15%, transparent) var(--hp)); }
.cw-info-bar.is-red { --bar: var(--text-primary); }
.cw-info-bar.is-white { --bar: var(--emap-blue); }
.cw-info-targets { list-style: none; padding: 0; margin: 8px 0 0; display: grid; gap: 6px; }
.games-shell .cw-info-targets button { display: grid; gap: 2px; width: 100%; text-align: left; padding: 6px 10px; font-size: .84rem; }
.cw-info-targets button span { color: var(--text-secondary); font-size: .8rem; }
/* Feats (civil-war.js featInfo): a unit's decisive moment, once a battle.
   Gold marks it: the star on the counter (faint while it waits), the
   roster chip's star, the buttons that use it. */
.cw-feat-star { fill: var(--cw-gold); stroke: var(--cw-gold-rim); stroke-width: 1; pointer-events: none; }
.cw-feat-star.is-waiting { fill-opacity: .4; stroke-opacity: .5; }
.cw-roster-feat { color: var(--cw-gold); font-weight: 800; }
.cw-feat { margin-top: 8px; padding-top: 6px; border-top: 1px dashed var(--border); }
.cw-feat.is-used { color: var(--text-secondary); }
.cw-feat details { margin-top: 4px; font-size: .84rem; }
.cw-feat summary { cursor: pointer; color: var(--text-secondary); }
.cw-feat > p { line-height: 1.75; margin: 0 0 4px; }
.cw-feat-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 6px !important; }
/* A feat's name is its button; an aimed one (a blow) is a switch whose
   targets are picked on the board. Not usable now: the name as a dashed tag. */
.games-shell .cw-feat-pill { display: inline-flex; align-items: center; min-height: 28px; padding: 0 10px; margin: 0 2px; font: inherit; font-weight: 700; border: 1px solid var(--cw-gold-rim); background: var(--cw-gold); color: #1d1606; vertical-align: baseline; }
/* A usable feat and its targets are raised keys that press down. */
.games-shell button.cw-feat-pill { box-shadow: 0 3px 0 color-mix(in srgb, var(--cw-gold) 45%, #000); cursor: pointer; transition: transform .06s, box-shadow .06s; }
.games-shell button.cw-feat-pill:hover { filter: brightness(1.08); }
/* An aimed feat switched on stays pressed in, dark with a gold ring. */
.games-shell button.cw-feat-pill.is-on { transform: translateY(2px); box-shadow: 0 0 0 2px var(--cw-gold); background: #1d1606; color: var(--cw-gold); border-color: var(--cw-gold); }
.cw-feat-state { margin-left: 8px; padding-left: 8px; border-left: 1px solid currentColor; font-size: .82em; font-weight: 700; }
.games-shell button.cw-feat-pill:active { transform: translateY(2px); box-shadow: 0 1px 0 color-mix(in srgb, var(--cw-gold) 45%, #000); }
.games-shell span.cw-feat-pill { border-style: dashed; background: transparent; color: var(--text-secondary); }
/* A unit that surrendered keeps the pale band of the side it left. */
/* The banner of a feat or a surrender across the board's top. An html
   element, so its fade is the html layer's; it plays once (--since). */
.cw-moment-row { position: absolute; top: 10px; left: 0; width: 100%; z-index: 3; display: flex; justify-content: center; pointer-events: none; }
.cw-moment {
    max-width: calc(100% - 24px);
    display: grid; gap: 2px; justify-items: center; padding: 8px 18px; pointer-events: none;
    background: var(--cw-red); color: #fff; border: 2px solid var(--cw-gold); box-shadow: 4px 4px 0 rgba(0, 0, 0, .45);
    opacity: 0;
}
.cw-moment strong { font-size: 1.35rem; letter-spacing: -.03em; }
.cw-moment span { font-size: .85rem; }
.cw-moment.is-surrender { background: #f4f1e8; color: #1d2a44; border-color: var(--cw-red); }
@media (prefers-reduced-motion: no-preference) {
    .cw-moment { animation: cw-moment 2.4s ease-out both; animation-delay: calc(0ms - var(--since, 0ms)); }
}
@keyframes cw-moment {
    from { opacity: 0; scale: 1.7; }
    10% { opacity: 1; scale: .96; }
    16% { scale: 1; }
    78% { opacity: 1; }
    to { opacity: 0; scale: 1; }
}
/* An ended battle's verdict, stamped over the middle of the board once the
   last falls have played; it stays until the press that moves on. Won:
   red and gold, the objective bursting and the Red units leaping; lost:
   the enemy's blue. */
.cw-verdict-row { position: absolute; top: 40%; left: 0; width: 100%; z-index: 3; display: flex; justify-content: center; pointer-events: none; }
.cw-verdict {
    display: grid; justify-items: center; gap: 2px; padding: 10px 30px 12px; max-width: calc(100% - 24px);
    background: var(--cw-red); color: #fff; border: 3px solid var(--cw-gold); box-shadow: 6px 6px 0 rgba(0, 0, 0, .5);
    rotate: -3deg;
}
.cw-verdict strong { font-size: clamp(2rem, 6vw, 3rem); font-weight: 900; letter-spacing: .12em; line-height: 1.05; color: var(--cw-gold); text-shadow: 2px 2px 0 rgba(0, 0, 0, .45); }
.cw-verdict span { font-size: .9rem; font-weight: 700; }
.cw-verdict .cw-verdict-title { font-size: .75rem; letter-spacing: .08em; opacity: .85; }
.cw-verdict.is-lost { background: #1d2a44; border-color: var(--emap-blue); rotate: 0deg; }
.cw-verdict.is-lost strong { color: #f4f1e8; }
.cw-burst-rays { fill: none; stroke: var(--cw-gold); stroke-width: 4; stroke-linecap: square; }
.cw-burst-ring { fill: none; stroke: var(--cw-gold); stroke-width: 3; }
.cw-burst { transform-box: fill-box; transform-origin: 50% 50%; pointer-events: none; }
@media (prefers-reduced-motion: no-preference) {
    .cw-verdict { animation: cw-verdict .7s cubic-bezier(.2, 1.4, .4, 1) both; animation-delay: calc(0ms - var(--since, 0ms)); }
    .cw-verdict.is-lost { animation-name: cw-verdict-lost; animation-duration: .9s; animation-timing-function: ease-out; }
    .cw-burst { animation: cw-burst 1.4s ease-out both; animation-delay: calc(0ms - var(--since, 0ms)); }
    .cw-burst-rays { animation: cw-rays 1.2s ease-in-out 3 both; animation-delay: calc(400ms - var(--since, 0ms)); }
    .cw-unit.is-cheering { animation: cw-cheer .5s ease-out 2 both; animation-delay: calc(0ms - var(--since, 0ms)); }
}
@keyframes cw-verdict { from { opacity: 0; scale: 2.6; rotate: -12deg; } 60% { opacity: 1; scale: .94; rotate: -2deg; } to { opacity: 1; scale: 1; rotate: -3deg; } }
@keyframes cw-verdict-lost { from { opacity: 0; translate: 0 -14px; } to { opacity: 1; translate: 0 0; } }
@keyframes cw-burst { from { scale: 0; } 45% { scale: 1.25; } to { scale: 1; } }
@keyframes cw-rays { 0%, 100% { stroke-opacity: 1; } 50% { stroke-opacity: .25; } }
@keyframes cw-cheer { 0%, 100% { translate: 0 0; } 40% { translate: 0 -7px; } }
.cw-battle-status li.is-goal { border-left: 3px solid var(--clr-red); padding-left: 8px; font-weight: 700; }
.cw-goal-flag { fill: var(--clr-red); stroke: #1d1d1b; stroke-width: 1; stroke-linejoin: round; pointer-events: none; }
.cw-battle-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.cw-battle-events .games-kicker { margin: 0 0 4px; }
.cw-battle-log { margin: 0; padding-left: 20px; font-size: .85rem; color: var(--text-secondary); line-height: var(--leading-normal); }

/* Outcome */
.cw-outcome h2 { font-size: clamp(1.4rem, 3.5vw, 2rem); margin: 6px 0 10px; }
.cw-stars { list-style: none; padding: 0; margin: 12px 0; display: grid; gap: 6px; font-size: 1.05rem; color: var(--text-secondary); }
.cw-stars .is-got { color: var(--text-primary); font-weight: 700; }
/* The war looked back on. */
.cw-retro-title { font-size: 1.05rem; margin: 22px 0 6px; }
.cw-seasons { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 2px; }
.cw-seasons summary { cursor: pointer; display: grid; grid-template-columns: minmax(8rem, 13rem) 1fr 5.5rem; gap: 10px; align-items: center; min-height: 40px; padding: 4px 6px; }
.cw-seasons summary:hover { background: var(--bg-canvas); }
.cw-season-name { display: grid; font-size: .86rem; font-weight: 700; }
.cw-season-name small { font-weight: 400; color: var(--text-secondary); font-size: .76rem; }
.cw-season-bar { position: relative; height: 14px; border-left: 0; background: linear-gradient(to right, transparent calc(50% - .5px), var(--border) calc(50% - .5px) calc(50% + .5px), transparent calc(50% + .5px)); }
.cw-season-bar i { position: absolute; top: 1px; bottom: 1px; }
.cw-season-bar i.is-behind { background: repeating-linear-gradient(-45deg, color-mix(in srgb, var(--text-primary) 28%, transparent) 0 3px, transparent 3px 6px); box-shadow: inset 0 0 0 1px var(--text-secondary); }
.cw-season-bar i.is-ahead { background: var(--btn-primary, #2da44e); }
.cw-season-bar i.is-even { background: var(--text-secondary); margin-left: -1px; }
.cw-season-value { font-size: .84rem; font-weight: 700; font-variant-numeric: tabular-nums; text-align: right; color: var(--text-secondary); }
.cw-season-value.is-behind { color: var(--text-primary); }
.cw-season-value.is-behind::before { content: '▼ '; }
.cw-season-value.is-ahead::before { content: '▲ '; }
.cw-season-value.is-ahead { color: var(--text-primary); }
.cw-season-fronts { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 8px; padding: 6px 0 12px; }
.cw-forks-wrap { overflow-x: auto; }
.cw-forks { width: 100%; border-collapse: collapse; font-size: .86rem; }
.cw-forks th, .cw-forks td { text-align: left; vertical-align: top; padding: 8px 8px 8px 0; border-bottom: 1px solid var(--border); }
.cw-forks thead th { font-size: .76rem; color: var(--text-secondary); }
.cw-forks tbody th { font-weight: 700; }
.cw-forks td strong, .cw-forks td small, .cw-forks tbody th .cw-links { display: block; }
.cw-forks td small { color: var(--text-secondary); font-size: .76rem; margin-top: 2px; }
.cw-forks tbody th .cw-links { font-weight: 400; font-size: .76rem; margin-top: 2px; }
.cw-forks tbody th .cw-links a { margin-right: 8px; }
.cw-forks tr:not(.is-same) td:nth-child(3) strong { text-decoration: underline; text-underline-offset: 3px; }
.cw-same { color: var(--text-secondary); }
.cw-forks .cw-fork-kind { display: block; font-weight: 400; font-size: .72rem; color: var(--text-secondary); }
.cw-battle-results { margin: 6px 0 0; padding-left: 1.2rem; font-size: .9rem; display: grid; gap: 4px; }
.cw-support { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; margin-bottom: 18px; }
.cw-support figure { margin: 0; }
.cw-support figcaption { font-size: .86rem; }
.cw-support figcaption small { color: var(--text-secondary); }
.cw-spark { width: 100%; max-width: 300px; height: auto; display: block; }
.cw-spark-grid { stroke: var(--border); stroke-width: 1; }
.cw-spark-mark { stroke: var(--text-secondary); stroke-width: 1; stroke-dasharray: 3 3; }
.cw-spark-axis { fill: var(--text-secondary); font-size: 9px; }
.cw-spark-line { fill: none; stroke: var(--text-primary); stroke-width: 2; stroke-linejoin: round; }
.cw-spark-dot { fill: var(--text-primary); stroke: var(--bg-surface); stroke-width: 2; }
.cw-spark-dot.is-low { fill: var(--bg-surface); stroke: var(--text-primary); }
@media (max-width: 720px) {
    .cw-seasons summary { grid-template-columns: 1fr 4.5rem; }
    .cw-season-bar { grid-column: 1 / -1; grid-row: 2; }
    .cw-forks thead { display: none; }
    .cw-forks, .cw-forks tbody, .cw-forks tr, .cw-forks th, .cw-forks td { display: block; }
    .cw-forks tr { padding: 8px 0; border-bottom: 1px solid var(--border); }
    .cw-forks th, .cw-forks td { border: 0; padding: 2px 0; }
    .cw-forks td::before { content: attr(data-label); display: block; font-size: .72rem; color: var(--text-secondary); }
}

.cw-rules { border: 1px solid var(--border); padding: 0 var(--content-inset); margin: 18px 0; }
.cw-rules summary { cursor: pointer; font-weight: 700; min-height: 44px; display: flex; align-items: center; }
.cw-rules p { font-size: .92rem; }
.cw-reading { padding: 0 var(--content-inset); }
.cw-reading h2 { font-size: 1.1rem; margin-bottom: 10px; }

/* Beside the fronts' cards the map stays in view, so the card under the
   pointer can light its front however far down it is. */
@media (min-width: 901px) {
    .cw-map-panel { position: sticky; top: calc(var(--bar-height, 56px) + 12px); }
}
@media (max-width: 900px) {
    .cw-board, .cw-battle-layout { grid-template-columns: minmax(0, 1fr); }
    /* The picked unit's details sit right under the board, not below the
       status list: the board and what was tapped share one screen. */
    .cw-battle-layout { gap: 10px; }
    .cw-grid-wrap, .cw-battle-side { display: contents; }
    .cw-grid-scroll { order: -2; }
    .cw-unit-info { order: -1; }
    .cw-terrain-key { margin: 0; }
}
@media (max-width: 720px) {
    .cw-intro { grid-template-columns: 1fr; }
    .cw-intro-art { min-height: 200px; }
    .cw-calendar small { display: none; }
    .cw-command { position: sticky; bottom: 0; background: var(--bg-canvas); border-top: 1px solid var(--border); padding-block: 8px; margin-inline: calc(-1 * var(--content-inset) * 0); z-index: 2; }
    /* The board keeps a readable cell and scrolls sideways instead. */
    .cw-grid-scroll { overflow-x: auto; }
    .cw-grid { min-width: 540px; }
    /* The battle's orders stay under the thumb below the grid. */
    .cw-battle-actions { position: sticky; bottom: 0; background: var(--bg-surface); border-top: 1px solid var(--border); padding-block: 8px; z-index: 2; }
    .cw-battle-actions .cw-primary { margin-left: auto; }
}
@media (prefers-reduced-motion: no-preference) {
    .cw-option, .cw-front { transition: background-color .15s ease, border-color .15s ease; }
}

/* The crumb bar pins while a season is planned and carries what is left:
   divisions to send, command, commissars, the grain over what the sent
   divisions eat. The jump to the other game gives up its place. */
.cw-shell .games-crumb.has-status { position: sticky; top: 0; z-index: 50; flex-wrap: nowrap; }
.cw-crumb-status { display: none; margin: 0 0 0 auto; gap: 14px; font-size: .8rem; white-space: nowrap; }
.games-crumb.has-status .cw-crumb-status { display: flex; }
.cw-crumb-status div { display: flex; align-items: baseline; gap: 5px; }
.cw-crumb-status dt { color: var(--text-secondary); }
.cw-crumb-status dd { margin: 0; font-weight: 800; font-variant-numeric: tabular-nums; }
.cw-crumb-status .is-warn dd { color: var(--bg-surface); background: var(--text-primary); padding: 0 4px; }
@media (max-width: 560px) {
    .cw-shell .games-crumb.has-status .games-crumb-item:last-child { display: none; }
    .cw-crumb-status { gap: 10px; font-size: .76rem; }
    .cw-crumb-status div { gap: 3px; }
}

/* Density while playing: the page heading gives way to the crumb bar,
   the HUD runs in two short rows, explanations fold. */
.cw-shell.is-playing .cw-heading { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; margin: 0; padding: 0; border: 0; }
.cw-shell.is-playing .cw-hud { gap: 8px; padding: 10px var(--content-inset); margin: 10px 0 12px; }
.cw-shell.is-playing .cw-season { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: end; column-gap: 16px; }
.cw-shell.is-playing .cw-season .games-kicker { grid-column: 1; margin: 0; }
.cw-shell.is-playing .cw-season h2 { grid-column: 1; font-size: 1.15rem; margin: 0; }
.cw-shell.is-playing .cw-calendar { grid-column: 2; grid-row: 1 / span 2; align-self: center; }
.cw-shell.is-playing .cw-calendar li { font-size: .7rem; }
.cw-shell.is-playing .cw-calendar .cw-cal-bar { height: 3px; margin: 1px 0 3px; }
.cw-shell.is-playing .cw-stats { grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: 4px 10px; }
.cw-shell.is-playing .cw-stats strong { font-size: 1.05rem; }
.cw-shell.is-playing .cw-stats > div { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 6px; }
.cw-shell.is-playing .cw-stats dd { margin: 0; display: contents; }
.cw-shell.is-playing .cw-stats small { flex-basis: 100%; }
.cw-shell.is-playing .cw-stats small { font-size: .7rem; line-height: 1.3; }
/* On a phone the planning counts live in the pinned crumb bar only; the
   stats left run in one row, each label and value on one line. */
@media (max-width: 720px) {
    .cw-shell.is-playing .cw-stats > .is-crumb { display: none; }
    .cw-shell.is-playing .cw-stats:has(> .is-crumb) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .cw-shell.is-playing .cw-stats dt { white-space: nowrap; }
    .cw-shell.is-playing .cw-stats > div { flex-wrap: nowrap; }
    .cw-shell.is-playing .cw-stats > .is-warn { flex-wrap: wrap; }
}
.cw-army { padding-top: 6px; }
.cw-army summary { cursor: pointer; display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; min-height: 32px; font-size: .85rem; }
.cw-army summary strong { font-size: .9rem; }
.cw-army details > summary::before { margin-right: -4px; color: var(--text-primary); }
.cw-army-line { color: var(--text-secondary); font-size: .8rem; }
.cw-army-change { font-size: .75rem; font-weight: 700; border: 1px solid var(--border); padding: 1px 6px; }
.cw-army-change.is-plus { border-color: var(--btn-primary, #2da44e); }
.cw-army-change.is-minus { border-style: dashed; border-color: var(--text-primary); }
.cw-army details[open] .cw-troops { margin-top: 8px; }
.cw-shell.is-playing .cw-brief { margin-bottom: 12px; padding-block: 0; }
.cw-shell.is-playing .cw-brief p { font-size: .95rem; line-height: 1.6; margin-bottom: 6px; }
.cw-shell.is-playing .cw-board { gap: 12px; margin-bottom: 12px; }

/* Front cards: one header row, a row of folded foes, one row of controls */
.cw-fronts { gap: 8px; }
.cw-front { padding: 8px 10px; gap: 5px; align-content: start; }
.cw-front header { justify-content: flex-start; align-items: center; gap: 4px 8px; margin: 0; }
.cw-front header > span { font-size: .75rem; margin-left: auto; color: var(--text-secondary); }
.cw-front .cw-threat { font-size: .84rem; color: var(--text-secondary); }
.cw-foes { gap: 4px; }
.cw-foe { flex: 0 1 auto; }
.cw-foe[open] { flex-basis: 100%; }
.cw-foe summary { min-height: 28px; padding: 2px 8px; font-size: .8rem; gap: 6px; }
.cw-foe summary small { font-size: .7rem; font-weight: 700; color: var(--text-secondary); }
.cw-foe summary small::before { content: '· '; }
.cw-foe .cw-foe-trait { color: var(--text-primary); }
/* The attackers' traits on this front: a bitten rule in red, a blunted one
   plain, one that helps the Reds in green. */
.cw-foe-entry { flex: 1 1 100%; display: grid; gap: 2px; }
.cw-foe-entry .cw-foe { justify-self: start; }
.cw-foe-entry .cw-foe[open] { justify-self: stretch; }
.cw-trait { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 6px; margin: 0; padding: 2px 6px; border-left: 3px solid var(--border); font-size: .76rem !important; color: var(--text-secondary); }
.cw-trait b { margin-left: auto; font-size: .7rem; padding: 0 5px; border: 1px solid currentColor; white-space: nowrap; font-variant-numeric: tabular-nums; }
.cw-trait.is-bites { border-left-color: var(--text-primary); }
.cw-trait.is-bites b { color: var(--bg-surface); background: var(--text-primary); border-color: var(--text-primary); }
.cw-trait.is-blunted b { color: var(--text-secondary); }
.cw-trait.is-works { border-left-color: var(--cw-good); }
.cw-trait.is-works b { color: var(--cw-good-ink); background: var(--cw-good); border-color: var(--cw-good); }
.cw-front-controls { gap: 4px; }
.games-shell .cw-stepper button { min-width: 36px; min-height: 36px; padding: 4px 0; }
.cw-stepper output { min-width: 58px; font-size: .9rem; }
.games-shell .cw-front .cw-toggle { padding: 4px 9px; min-height: 36px; font-size: .8rem; }
.cw-meter { gap: 2px; margin: 2px 0 2px; }
.cw-meter-top { font-size: .84rem; }
.cw-meter-bar { height: 12px; }
.cw-parts { font-size: .74rem !important; }

/* Advisor cards: title, effect and button; the story folds */
.cw-shell.is-playing .cw-cards, .cw-shell.is-playing .cw-choice { padding: 10px var(--content-inset); margin-bottom: 12px; }
.cw-cards h3 { margin-bottom: 8px; }
.cw-card-grid { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 6px; }
.cw-card { padding: 8px 10px; gap: 4px; }
.cw-card h4 { font-size: .9rem; }
.cw-card h4 small { font-weight: 400; font-size: .74rem; color: var(--text-secondary); }
.cw-card .cw-effect { margin-top: 0 !important; font-size: .8rem; }
.cw-card-more summary { cursor: pointer; font-size: .74rem; color: var(--text-secondary); }
.cw-card-more p { font-size: .8rem; margin-top: 4px; }
.games-shell .cw-card button { padding: 5px 8px; font-size: .8rem; margin-top: auto; }

/* The dilemma */
.cw-choice h3 { font-size: 1.05rem; margin: 2px 0 4px; }
.cw-choice > p { font-size: .9rem; margin: 0; }
.cw-options { gap: 6px; margin-top: 8px; }
.games-shell .cw-option { padding: 8px 10px; gap: 3px; font-size: .88rem; }
.games-shell .cw-option[aria-pressed="true"] { padding: 7px 9px; }
.cw-option small { font-size: .74rem; }

/* The season's order stays at the bottom of the screen while planning,
   over the map pinned on a phone when the map reaches the screen's foot. */
.cw-command { position: sticky; bottom: 0; z-index: 5; background: var(--bg-canvas); border-top: 1px solid var(--border); padding-block: 6px; margin-bottom: 12px; }
@media (max-width: 560px) {
    .cw-shell.is-playing .cw-season { grid-template-columns: minmax(0, 1fr); }
    .cw-shell.is-playing .cw-calendar { grid-column: 1; grid-row: auto; margin-top: 4px; }
    .cw-shell.is-playing .cw-stats { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.cw-map-key summary { cursor: pointer; padding: 6px 12px; font-size: .78rem; color: var(--text-secondary); }
.cw-map-key[open] summary { padding-bottom: 0; }
.cw-command { flex-wrap: nowrap; }
.cw-command p { font-size: .82rem; min-width: 0; }
.games-shell .cw-command .cw-primary { flex: 0 0 auto; padding-block: 8px; }
@media (max-width: 560px) {
    .cw-shell.is-playing .cw-stats > div:not(.is-warn) small { display: none; }
    .cw-stepper output { min-width: 50px; }
    .cw-command { justify-content: space-between; }
}

/* A card in play: red rim and a red wash, not just its button's words. */
.cw-card.is-played { border: 2px solid var(--cw-red); padding: 7px 9px; background: color-mix(in srgb, var(--cw-red) 22%, var(--bg-canvas)); }
.games-shell .cw-toggle[aria-pressed="true"] { background: color-mix(in srgb, var(--cw-red) 22%, var(--bg-canvas)); }

/* The cities at stake: a chain of map dots, inner to outer. */
.cw-stake-head { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; font-size: .8rem; }
.cw-stake-verdict { font-weight: 800; padding-left: 6px; border-left: 3px solid var(--text-secondary); }
.cw-stake-verdict.is-behind { border-color: var(--text-primary); }
.cw-stake-verdict.is-behind::before { content: '▼ '; }
.cw-stake-verdict.is-ahead::before { content: '▲ '; }
.cw-stake-verdict.is-ahead { border-color: var(--btn-primary, #2da44e); }
.cw-stake { list-style: none; margin: 2px 0 0; padding: 0; display: flex; flex-wrap: nowrap; align-items: flex-start; row-gap: 4px; }
.cw-stake li { position: relative; display: flex; flex-direction: column; align-items: center; min-width: 0; flex: 1 1 0; max-width: 92px; padding: 0 2px; overflow-wrap: anywhere; font-size: .72rem; color: var(--text-secondary); text-align: center; line-height: 1.2; }
.cw-stake li.is-changing { color: var(--text-primary); font-weight: 700; }
/* the chain between dots */
.cw-stake li:not(.cw-stake-hist)::before { content: ''; position: absolute; top: 12px; left: 0; right: 0; height: 1.6px; background: color-mix(in srgb, var(--cw-red) 60%, transparent); z-index: 0; }
.cw-stake li:first-child::before { left: 50%; }
.cw-stake li:last-child::before { right: 50%; }
.cw-stake-dot { width: 24px; height: 24px; position: relative; z-index: 1; }
/* A city's prize this season (Arkhangelsk's stores and men): outlined
   while the plan falls short, filled green once it takes the city. */
.cw-stake-prize { position: relative; margin-top: 3px; padding: 1px 5px 1px 14px; border: 1px dashed var(--cw-good); color: var(--text-primary); font-size: .66rem; font-weight: 700; line-height: 1.3; text-align: left; }
.cw-stake-prize span { display: block; white-space: nowrap; }
.cw-stake-prize::before { content: '★'; position: absolute; left: 3px; top: 1px; color: var(--cw-gold); }
.cw-stake-prize.is-won { border-style: solid; background: var(--cw-good); color: var(--cw-good-ink); }
.cw-stake-hist { min-width: 0 !important; flex: 0 0 auto !important; padding: 0 !important; align-self: stretch; }
.cw-stake-hist::after { content: ''; position: absolute; top: 0; bottom: 14px; left: 50%; border-left: 1.5px dashed var(--text-primary); }
.cw-stake-hist span { margin-top: auto; font-size: .64rem; font-weight: 800; color: var(--text-primary); background: var(--bg-surface); padding: 0 2px; position: relative; z-index: 1; }

.cw-mk-star { width: 17px; height: 17px; fill: var(--cw-star); stroke: var(--cw-gold-rim); stroke-width: .8; }
.cw-mk-train { width: 22px; height: 18px; }
.cw-train { stroke-linejoin: round; }
.cw-train-body { fill: var(--cw-steel); stroke: var(--cw-steel-rim); stroke-width: .45; }
.cw-train-frame { fill: var(--cw-steel-rim); }
.cw-train-wheel { fill: #2a2d24; stroke: var(--cw-steel-rim); stroke-width: .5; }
.cw-train-star { fill: var(--cw-star); stroke: none; }
.cw-foe-mark { stroke: var(--bg-surface); stroke-width: .6; }
.cw-foe-mark.k-white { fill: var(--cw-white); }
.cw-foe-mark.k-interv { fill: var(--emap-blue); }
.cw-foe-mark.k-central { fill: var(--emap-amber); }
.cw-foe-mark.k-national { fill: var(--emap-gray); }
.cw-foe-marks { fill: #1d1d1b; stroke: var(--cw-ink); stroke-width: 1; }
.cw-map-legend .cw-mk-star, .cw-map-legend .cw-mk-train { vertical-align: middle; }

/* Strength and pressure on one scale: the bar is built of the strength's
   blocks, the attacker's units stand over it. */
.cw-meter-top { display: flex; justify-content: space-between; }
.cw-meter-scale { position: relative; display: grid; gap: 3px; margin-bottom: 16px; }
.cw-meter-scale { --cw-bar-h: 18px; }
/* A band's line runs through the strength's bar only; its word stays
   under the attacker's row. */
.cw-meter-scale > .cw-meter-tick { pointer-events: auto; background: linear-gradient(var(--border), var(--border)) top / 1px var(--cw-bar-h) no-repeat; }
.cw-foe-row { position: relative; min-height: 18px; }
.cw-foe-count { display: flex; flex-wrap: wrap; align-items: baseline; gap: 1px 5px; font-size: .75rem; line-height: 18px; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.cw-next { font-weight: 700; padding: 0 4px; line-height: 1.25; }
.cw-next.is-momentum { color: var(--bg-surface); background: var(--text-primary); }
.cw-next.is-pursuit { color: var(--cw-good-ink); background: var(--cw-good); }
.cw-unit-mark { position: absolute; top: 0; height: 18px; box-sizing: border-box; border: 1px solid var(--bg-surface); }
.cw-unit-mark.k-white { background: var(--cw-white); border-color: var(--cw-ink); }
.cw-unit-mark.k-interv { background: var(--emap-blue); }
.cw-unit-mark.k-central { background: var(--emap-amber); }
.cw-unit-mark.k-national { background: var(--emap-gray); }
.cw-unit-mark.is-momentum { background-image: repeating-linear-gradient(-45deg, transparent 0 2px, var(--cw-ink) 2px 3.5px); }
/* Pressure the pursuit took off: an empty mark, outlined in the ahead colour. */
.cw-unit-mark.is-pursued { background: transparent; border: 1px dashed var(--cw-good); }
.cw-meter-bar { height: var(--cw-bar-h, 18px); }
.cw-seg { position: absolute; top: 0; bottom: 0; box-sizing: border-box; display: grid; place-items: center; overflow: hidden; transition: left .2s ease, width .2s ease; }
.cw-seg.is-div { background: var(--cw-red); border-right: 1.5px solid var(--bg-surface); }
.cw-seg.is-commissar, .cw-seg.is-train { background: color-mix(in srgb, var(--cw-red) 70%, #000); border-right: 1.5px solid var(--bg-surface); }
.cw-seg.is-other { background: repeating-linear-gradient(-45deg, var(--cw-red) 0 4px, color-mix(in srgb, var(--cw-red) 70%, #000) 4px 8px); border-right: 1.5px solid var(--bg-surface); }
.cw-seg small { font-size: .62rem; font-weight: 800; color: #fff; }
.cw-seg.is-minus { background: repeating-linear-gradient(45deg, color-mix(in srgb, var(--text-secondary) 55%, transparent) 0 3px, transparent 3px 6px); }
.cw-seg.is-minus small { color: var(--text-primary); }
.cw-seg .cw-mk-star { width: 14px; height: 14px; fill: #faf2da; stroke: none; }
.cw-seg .cw-mk-train { width: 18px; height: 15px; }
.cw-pip.is-empty { fill: none; stroke: var(--clr-red); stroke-width: .8; }

/* A season just sent: the map alone, its result playing, then the button
   to the review (civil-war.js renderUnveil). */
.cw-campaign.is-unveiling :is(.cw-brief, .cw-fronts, .cw-map-key, .cw-history-toggle) { display: none; }
.cw-campaign.is-unveiling .cw-board { grid-template-columns: minmax(0, 1fr); margin-bottom: 0; }
.cw-campaign.is-unveiling .cw-map-panel { width: 100%; max-width: min(900px, calc((100vh - 190px) * 1.15)); margin-inline: auto; position: static; }
.cw-unveil { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px 16px; min-height: 56px; padding: 8px 0 12px; }
.cw-unveil p { margin: 0; font-size: .92rem; }
.cw-unveil p span { color: var(--text-secondary); }
/* The season's stat changes, filled like the review's next-season chips. */
.cw-unveil-stats li { font-weight: 700; }
.cw-unveil-stats li.is-plus { background: var(--cw-good); color: var(--cw-good-ink); }
.cw-unveil-stats li.is-minus { background: var(--cw-loss); color: #fff; }
.cw-unveil-stats li:is(.is-plus, .is-minus) b { color: inherit; }
@media (prefers-reduced-motion: no-preference) {
    .cw-unveil-stats li { animation: cw-mark-pop .4s ease-out both; }
    .cw-unveil-stats li:nth-child(2) { animation-delay: .12s; }
    .cw-unveil-stats li:nth-child(3) { animation-delay: .24s; }
}
@media (max-width: 720px) {
    .cw-unveil { position: sticky; bottom: 0; background: var(--bg-canvas); border-top: 1px solid var(--border); z-index: 2; }
}

/* Battle: a unit destroyed (civil-war.js wreck). The broken box plays
   once; what stays is the burnt cross on its tile. */
.cw-wreck { pointer-events: none; }
.cw-wreck-scar { stroke: color-mix(in srgb, var(--cw-ink) 75%, transparent); stroke-width: 3; stroke-linecap: round; stroke-opacity: .7; }
.cw-wreck-body, .cw-wreck-slash, .cw-wreck-word { fill-opacity: 0; stroke-opacity: 0; }
.cw-wreck-body { transform-box: fill-box; transform-origin: 50% 100%; }
.cw-wreck.is-red .cw-wreck-body rect { fill: var(--cw-red); stroke: #fff; stroke-width: 2; }
.cw-wreck.is-white .cw-wreck-body rect { fill: #f4f1e8; stroke: var(--emap-blue); stroke-width: 2; }
.cw-wreck.is-red .cw-wreck-body text { fill: #fff; }
.cw-wreck.is-white .cw-wreck-body text { fill: #1d2a44; }
.cw-wreck-slash { fill: none; stroke-width: 3.5; stroke-linecap: round; stroke-dasharray: 52; }
.cw-wreck.is-white .cw-wreck-slash { stroke: var(--clr-red); }
.cw-wreck.is-red .cw-wreck-slash { stroke: var(--emap-blue); }
.cw-wreck-word { font-size: 12px; font-weight: 900; paint-order: stroke; stroke: var(--bg-surface); stroke-width: 3px; }
.cw-wreck.is-white .cw-wreck-word { fill: var(--clr-red); }
.cw-wreck.is-red .cw-wreck-word { fill: var(--emap-blue); }
@media (prefers-reduced-motion: no-preference) {
    .cw-wreck.is-falling .cw-wreck-scar { animation: cw-scar 1.3s ease-out both; animation-delay: calc(0ms - var(--since)); }
    .cw-wreck.is-falling .cw-wreck-body { animation: cw-break 1.3s ease-in both; animation-delay: calc(0ms - var(--since)); }
    .cw-wreck.is-falling .cw-wreck-slash { animation: cw-slash 1.3s ease-out both; animation-delay: calc(0ms - var(--since)); }
    .cw-wreck.is-falling .cw-wreck-slash.is-second { animation-delay: calc(90ms - var(--since)); }
    .cw-wreck.is-falling .cw-wreck-word { animation: cw-word 1.3s ease-out both; animation-delay: calc(0ms - var(--since)); }
}
@keyframes cw-break {
    from { translate: 0 0; rotate: 0deg; scale: 1; fill-opacity: 1; stroke-opacity: 1; }
    8% { translate: -3px 0; }
    16% { translate: 3px 0; }
    24% { translate: -2px 0; }
    45% { translate: 0 0; rotate: -3deg; fill-opacity: 1; stroke-opacity: 1; }
    70% { translate: 0 4px; rotate: 8deg; scale: .9 .55; fill-opacity: 0.8; stroke-opacity: 0.8; }
    to { translate: 0 10px; rotate: 14deg; scale: .7 .2; fill-opacity: 0; stroke-opacity: 0; }
}
@keyframes cw-slash { from, 18% { fill-opacity: 0; stroke-opacity: 0; stroke-dashoffset: 52; } 22% { fill-opacity: 1; stroke-opacity: 1; } 40% { stroke-dashoffset: 0; fill-opacity: 1; stroke-opacity: 1; } 70% { fill-opacity: 1; stroke-opacity: 1; } to { fill-opacity: 0; stroke-opacity: 0; stroke-dashoffset: 0; } }
@keyframes cw-word { from, 20% { fill-opacity: 0; stroke-opacity: 0; translate: 0 0; } 35% { fill-opacity: 1; stroke-opacity: 1; } 80% { fill-opacity: 1; stroke-opacity: 1; } to { fill-opacity: 0; stroke-opacity: 0; translate: 0 -10px; } }
@keyframes cw-scar { from, 55% { fill-opacity: 0; stroke-opacity: 0; } to { fill-opacity: 0.7; stroke-opacity: 0.7; } }

/* A battle just ended: the board stays still under its last blows. */
.cw-grid.is-over .cw-unit { cursor: default; }
.cw-battle-close { align-items: center; }
.cw-battle-close p { margin: 0; }

/* One front card at a time (civil-war.js renderFronts): the row of fronts
   picks it, as do the attackers' markers on the map. */
.cw-front-tabs { display: flex; flex-wrap: wrap; gap: 6px; }
.games-shell .cw-front-tab { display: inline-flex; align-items: center; gap: 6px; min-height: 34px; padding: 4px 10px; line-height: 1.2; font-size: .84rem; font-variant-numeric: tabular-nums; }
.cw-front-tab span { color: var(--text-secondary); font-size: .78rem; }

.cw-front-tab .cw-trend { margin-left: 0; }
.games-shell .cw-front-tab[aria-pressed="true"] { border-color: var(--cw-red); background: color-mix(in srgb, var(--cw-red) 14%, transparent); }
.cw-map-frame.has-pick:not(.is-spotlight) [data-front]:not(.is-picked) { opacity: .5; }
/* Narrow screens: the map pins under the crumb bar while the fronts scroll
   beneath it, so the open card and its front are in sight together. The
   panel lets its parts join the board, which runs through the cards.
   While the season's result plays the map stands alone, unpinned. */
@media (max-width: 900px) {
    .cw-campaign:not(.is-unveiling) .cw-map-panel { display: contents; }
    .cw-campaign:not(.is-unveiling) .cw-map-heading { background: var(--bg-surface); border: 1px solid var(--border); }
    .cw-campaign:not(.is-unveiling) .cw-map-key { background: var(--bg-surface); border: 1px solid var(--border); border-top: 0; }
    .cw-campaign:not(.is-unveiling) .cw-board { gap: 0; }
    .cw-campaign:not(.is-unveiling) .cw-fronts { margin-top: 12px; }
    .cw-campaign:not(.is-unveiling) .cw-map-frame { position: sticky; top: 34px; z-index: 4; background: var(--bg-surface); border: 1px solid var(--border); border-top: 0; }
    /* Outside planning the crumb bar does not pin, so neither gap. */
    .cw-shell:not(:has(.games-crumb.has-status)) .cw-campaign:not(.is-unveiling) .cw-map-frame { top: 0; }
    /* civil-war.js fitMap sets the frame's height and the map's width. */
    .cw-map-frame { overflow: auto; overscroll-behavior: contain; scrollbar-width: none; }
    .cw-map-frame::-webkit-scrollbar { display: none; }
    .cw-map-frame > svg { max-width: none; }
    .cw-front-tabs { flex-wrap: nowrap; overflow-x: auto; }
    .games-shell .cw-front-tab { flex: 0 0 auto; gap: 4px; padding: 4px 8px; font-size: .8rem; }
}

/* Voices: who speaks on the map as a season opens or ends (civil-war.js
   renderVoice). Html over the map svg, one bubble at a time, its tail at
   the speaker; the bubble itself animates (html, not svg elements). */
.cw-voices { position: absolute; inset: 0 auto auto 0; width: 0; height: 0; z-index: 3; }
.cw-voice {
    position: absolute; display: grid; grid-template-columns: 44px minmax(0, 1fr); gap: 0 10px; align-items: start;
    translate: -50% calc(-100% - 14px); padding: 9px 30px 10px 9px; cursor: pointer;
    background: var(--bg-surface); color: var(--text-primary); border: 2px solid var(--cw-white); box-shadow: 4px 4px 0 #0000004d;
    font-size: .86rem; line-height: 1.45; text-align: left;
}
.cw-voice.is-below { translate: -50% 18px; }
.cw-voice::after {
    content: ''; position: absolute; left: calc(var(--tail) - 8px); top: 100%; border: 8px solid transparent; border-top: 12px solid var(--cw-white); border-bottom: 0;
}
.cw-voice.is-below::after { top: auto; bottom: 100%; border-top: 0; border-bottom: 12px solid var(--cw-white); }
.cw-voice.is-red { border-color: var(--cw-red); }
.cw-voice.is-red::after { border-top-color: var(--cw-red); }
.cw-voice.is-red.is-below::after { border-bottom-color: var(--cw-red); }
.cw-voice.k-interv { border-color: var(--emap-blue); }
.cw-voice.k-interv::after { border-top-color: var(--emap-blue); }
.cw-voice.k-interv.is-below::after { border-bottom-color: var(--emap-blue); }
.cw-voice.k-central { border-color: var(--emap-amber); }
.cw-voice.k-central::after { border-top-color: var(--emap-amber); }
.cw-voice.k-central.is-below::after { border-bottom-color: var(--emap-amber); }
.cw-voice.k-national { border-color: var(--emap-gray); }
.cw-voice.k-national::after { border-top-color: var(--emap-gray); }
.cw-voice.k-national.is-below::after { border-bottom-color: var(--emap-gray); }
[data-theme="light"] .cw-voice.k-white { border-color: var(--cw-ink); }
[data-theme="light"] .cw-voice.k-white::after { border-top-color: var(--cw-ink); }
[data-theme="light"] .cw-voice.k-white.is-below::after { border-bottom-color: var(--cw-ink); }
.cw-voice > .cw-face { width: 44px; height: 44px; }
.cw-voice p { margin: 0; line-height: 1.45; }
.cw-voice-who { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 6px; font-size: .78rem; color: var(--text-secondary); }
.cw-voice-who strong { color: var(--text-primary); font-size: .84rem; }
.cw-voice-text { margin-top: 2px !important; font-weight: 600; }
.cw-voice-text::before { content: '「'; }
.cw-voice-text::after { content: '」'; }
.cw-voice-src { font-size: .72rem; }
.cw-voice-src a { color: var(--text-secondary); }
.cw-voice-src.is-made { padding: 0 4px; border: 1px solid var(--border); color: var(--text-secondary); }
.cw-voice-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 3px; }
.games-shell .cw-voice-next { flex: 0 0 auto; min-height: 28px; padding: 2px 9px; font-size: .74rem; font-variant-numeric: tabular-nums; }
.cw-voice-next span { color: var(--text-secondary); }
.games-shell .cw-voice-close { position: absolute; top: 2px; right: 2px; min-height: 0; width: 26px; height: 26px; padding: 0; border: 0; background: none; color: var(--text-secondary); font-size: 1.1rem; line-height: 1; }
.cw-voice:focus-visible { outline: 2px solid var(--text-primary); outline-offset: 2px; }
@media (prefers-reduced-motion: no-preference) {
    .cw-voice.is-new { animation: cw-voice-in .32s cubic-bezier(.2, 1.4, .4, 1) both; }
}
@keyframes cw-voice-in { from { scale: .6; opacity: 0; } to { scale: 1; opacity: 1; } }
/* On a phone the map's frame is short (36vh): the bubble keeps small so the
   place it speaks of still shows. A smaller face and type, the source on one
   line, cut short (its link and full name stay on it). */
@media (max-width: 900px) {
    .cw-voice { grid-template-columns: 30px minmax(0, 1fr); gap: 0 8px; padding: 6px 26px 6px 6px; font-size: .8rem; line-height: 1.36; }
    .cw-voice > .cw-face { width: 30px; height: 30px; }
    .cw-voice p { line-height: 1.36; }
    .cw-voice-who { font-size: .72rem; }
    .cw-voice-who strong { font-size: .78rem; }
    .cw-voice-foot { margin-top: 2px; }
    .cw-voice-foot > p { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
    .cw-voice-src { font-size: .68rem; }
    .games-shell .cw-voice-next { min-height: 24px; padding: 1px 8px; font-size: .7rem; }
}
.cw-voice-log { margin: 0 0 10px; font-size: .9rem; }
.cw-voice-log ul { margin: 8px 0 0; padding-left: 18px; }
.cw-voice-log li { margin-bottom: 6px; line-height: var(--leading-normal); }
.cw-voice-log .cw-voice-src { margin-left: 4px; }
.games-shell .cw-voice-replay { margin-left: auto; }
.games-shell .cw-voice-replay + .cw-history-toggle { margin-left: 0; }

/* What the season still wants, as chips before the send button: an open
   one inverted, a done one plain with its check. */
/* overflow-x clips the y axis too: room above and below, or a chip's rim is
   cut when the pinned bar sits on a fractional pixel (a phone's toolbar). */
.cw-todo { display: flex; gap: 4px; flex: 0 1 auto; min-width: 0; overflow-x: auto; scrollbar-width: none; margin-right: auto; padding-block: 3px; margin-block: -3px; }
.games-shell .cw-todo-item { flex: 0 0 auto; min-height: 30px; padding: 3px 9px; font-size: .78rem; font-variant-numeric: tabular-nums; background: var(--text-primary); color: var(--bg-canvas); border-color: var(--text-primary); }
.games-shell .cw-todo-item.is-done { background: transparent; color: var(--text-secondary); border-color: var(--border); }
.games-shell .cw-todo-item.is-next { background: var(--cw-red); border-color: var(--cw-red); color: #fff; }
/* The first season's next step in words, over the row. */
.cw-command.has-guide { flex-wrap: wrap; row-gap: 6px; }
.games-shell .cw-guide {
    flex: 1 0 100%; display: flex; align-items: baseline; gap: 8px; min-height: 0; padding: 4px 0; text-align: left;
    background: none; border: 0; color: var(--text-primary); font-size: .85rem; font-weight: 700; line-height: 1.35;
}
.cw-guide span { flex: 0 0 auto; padding: 1px 6px; background: var(--cw-red); color: #fff; font-size: .72rem; letter-spacing: .04em; }
@media (max-width: 560px) { .cw-command p { display: none; } }

/* Where a front's divisions come from; the ones moved by rail inverted. */
.cw-rail-row { margin: 4px 0 0; font-size: .78rem; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.cw-rail-row strong { color: var(--text-primary); }
.cw-seg.is-div.is-moved { background: repeating-linear-gradient(-45deg, var(--cw-red) 0 3px, color-mix(in srgb, var(--cw-red) 35%, var(--bg-surface)) 3px 6px); }
/* The steps beside the stepper: to the next result, and back without
   losing it; the divisions short of the next result as plain words. */
.games-shell .cw-front .cw-step { min-height: 36px; padding: 4px 9px; font-size: .8rem; font-variant-numeric: tabular-nums; }
.games-shell .cw-front .cw-step.is-more { border-color: var(--text-primary); font-weight: 700; }
.cw-meter-strength { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px; }
.cw-meter-top .cw-step-short { font-size: .76rem; color: var(--text-secondary); }
/* The enemy, folded under the meter: who and what their traits add. */
.cw-foe-fold { display: grid; gap: 5px; border-top: 1px solid var(--border); padding-top: 4px; }
.cw-foe-fold > summary { display: flex; align-items: center; gap: 6px; min-height: 30px; cursor: pointer; font-size: .82rem; color: var(--text-secondary); }
.cw-foe-fold > summary strong { color: var(--text-primary); }
.cw-foe-fold > summary b { margin-left: auto; font-size: .72rem; padding: 0 5px; border: 1px solid currentColor; white-space: nowrap; font-variant-numeric: tabular-nums; }
.cw-foe-fold > summary b.is-bites { color: var(--bg-surface); background: var(--text-primary); border-color: var(--text-primary); }
.cw-foe-fold > summary b.is-works { color: var(--cw-good-ink); background: var(--cw-good); border-color: var(--cw-good); }
.cw-foe-fold[open] > summary b { display: none; }
.cw-foe-fold > summary::before, .cw-brief-fold > summary::before, .cw-voice-log > summary::before, .cw-army details > summary::before, .cw-battle-brief > summary::before, .cw-rules > summary::before { content: '▸'; font-size: 1em; color: var(--text-secondary); margin-right: 6px; }
.cw-foe-fold[open] > summary::before, .cw-brief-fold[open] > summary::before, .cw-voice-log[open] > summary::before, .cw-army details[open] > summary::before, .cw-battle-brief[open] > summary::before, .cw-rules[open] > summary::before { content: '▾'; }
.cw-foe-fold > summary::-webkit-details-marker, .cw-brief-fold > summary::-webkit-details-marker, .cw-voice-log > summary::-webkit-details-marker, .cw-army details > summary::-webkit-details-marker, .cw-battle-brief > summary::-webkit-details-marker, .cw-rules > summary::-webkit-details-marker { display: none; }
.cw-foe-fold > summary, .cw-brief-fold > summary, .cw-voice-log > summary, .cw-army details > summary { list-style: none; }
.cw-battle-brief > summary::before, .cw-rules > summary::before { content: '▶'; color: var(--text-primary); font-size: .75em; margin-right: 8px; }
.cw-battle-brief[open] > summary::before, .cw-rules[open] > summary::before { content: '▼'; }
.cw-battle-brief > summary:hover, .cw-rules > summary:hover { text-decoration: underline; text-underline-offset: 3px; }
/* The season's story folded on a phone (civil-war.js renderBrief). */
/* The season's story and its voices fold alike, side by side. */
.cw-brief-fold > summary, .cw-voice-log > summary { cursor: pointer; font-weight: 700; font-size: .9rem; min-height: 30px; display: flex; align-items: center; }
.cw-brief-fold[open] > summary { margin-bottom: 4px; }
.cw-brief-fold > summary::before, .cw-voice-log > summary::before { color: var(--text-primary); }
.cw-brief-fold > summary:hover, .cw-voice-log > summary:hover, .cw-army details > summary:hover strong { text-decoration: underline; text-underline-offset: 3px; }

/* The battle: its one-line orders, the Red units over the board, and the
   turn's end armed by a first press. */
.cw-battle-brief summary span { font-weight: 400; color: var(--text-secondary); }
.cw-roster { display: flex; gap: 4px; overflow-x: auto; scrollbar-width: none; padding-bottom: 6px; }
.games-shell .cw-roster-unit { flex: 0 0 auto; display: inline-flex; gap: 4px; align-items: baseline; min-height: 30px; padding: 3px 8px; font-size: .78rem; }
.cw-roster-unit small { font-variant-numeric: tabular-nums; color: var(--text-secondary); }
.games-shell .cw-roster-unit.is-done { border-style: dashed; color: var(--text-secondary); }
.games-shell .cw-roster-unit[aria-pressed="true"] { border-color: var(--cw-red); background: color-mix(in srgb, var(--cw-red) 18%, transparent); }
.games-shell .cw-primary.is-armed { background: var(--text-primary); color: var(--bg-canvas); border-color: var(--text-primary); }
.cw-battle-brief summary { display: block; padding-block: 10px; }
@media (max-width: 900px) {
    /* The roster stays right over the board when the wrap is unboxed. */
    .cw-roster { order: -3; }
}
@media (max-width: 560px) {
    /* Only what is still to do takes room in the narrow command bar. */
    .cw-todo-item.is-done { display: none; }
    .games-shell .cw-battle-actions button { padding: 6px 10px; font-size: .82rem; min-height: 36px; }
}
@media (max-width: 560px) {
    .cw-todo { gap: 3px; }
    .games-shell .cw-todo-item { padding: 3px 6px; font-size: .72rem; }
}

/* The battle board's railway. */
.cw-track-ties { fill: none; stroke: color-mix(in srgb, var(--text-primary) 55%, transparent); stroke-width: 9; stroke-dasharray: 1.6 5.5; }
.cw-track-rail { fill: none; stroke: color-mix(in srgb, var(--text-primary) 70%, transparent); stroke-width: 2; }
.cw-terrain-key .k-track { border: 0; background: linear-gradient(transparent 6px, color-mix(in srgb, var(--text-primary) 70%, transparent) 6px 8px, transparent 8px), repeating-linear-gradient(90deg, color-mix(in srgb, var(--text-primary) 55%, transparent) 0 1.5px, transparent 1.5px 5px); }

/* ── The poster (2026-10-18) ──
   The page is dressed as a constructivist poster of 1919–1920: two inks,
   black and red, on the theme's paper; heavy condensed type; thick rules
   instead of hairlines; the season's title and the open choices on red
   fields; what can be pressed stands off the paper on an offset shadow.
   The intro's art is Lissitzky's red wedge driving into the white circle.
   The map, the battle board and their pieces keep their own colours. */
.cw-shell {
    --cw-rule-w: 2px;
    --cw-paper-ink: #faf2da;           /* type on a red or ink field */
    --cw-lift: var(--text-primary);    /* the offset shadow of what is pressed */
}
[data-theme="dark"] .cw-shell { --cw-lift: var(--cw-red); }
.cw-shell :is(h1, h2, h3, h4) { font-family: var(--font-display); font-weight: 800; letter-spacing: -.015em; }
.cw-shell .games-kicker { letter-spacing: .1em; }

/* The page heading: a heavy title over a red bar and a black one. */
.cw-heading { border-bottom: 0; box-shadow: 0 8px 0 var(--cw-red), 0 11px 0 var(--text-primary); margin-bottom: 30px; }
.cw-heading h1 { font-size: clamp(1.7rem, 4.6vw, 3.2rem); letter-spacing: -.04em; }
.cw-heading .games-kicker { display: inline-block; padding: 3px 8px; background: var(--text-primary); color: var(--bg-canvas); }

/* Intro: the poster's art beside its copy. */
.cw-intro { border: var(--cw-rule-w) solid var(--text-primary); }
.cw-intro-art { position: relative; overflow: hidden; isolation: isolate; background-image: none; }
.cw-intro-art::before { content: ''; position: absolute; z-index: -1; height: 52%; aspect-ratio: 1; top: 6%; right: -8%; border-radius: 50%; background: var(--cw-paper-ink); }
.cw-intro-art::after { content: ''; position: absolute; z-index: -1; inset: 0; background: #14110d; clip-path: polygon(0 12%, 80% 27%, 0 40%); }
.cw-intro-art p { border-top: 3px solid var(--cw-paper-ink); }
/* On a phone the art is a short band: the circle smaller at its edge, the
   wedge longer to reach it. */
@media (max-width: 720px) {
    .cw-intro-art::before { height: 46%; right: -18%; }
    .cw-intro-art::after { clip-path: polygon(0 12%, 88% 27%, 0 40%); }
}
.cw-steps li::before { font-family: var(--font-display); font-size: 1.05rem; box-shadow: 3px 3px 0 var(--text-primary); }
.cw-steps strong { font-family: var(--font-display); font-weight: 800; }

/* Panels: a thick ink rule all round. */
.cw-hud, .cw-cards, .cw-choice, .cw-review, .cw-outcome, .cw-battle, .cw-map-panel, .cw-front, .cw-rules, .cw-card, .cw-ledger {
    border-width: var(--cw-rule-w); border-color: var(--text-primary);
}
.cw-map-heading { border-bottom: var(--cw-rule-w) solid var(--text-primary); }
.cw-front:hover, .cw-front:focus-within { border-color: var(--text-primary); }

/* The HUD: a red bar across its head, the season's title on a red field,
   the calendar's current season in a thick red bar. */
.cw-hud { border-top: 8px solid var(--cw-red); }
.cw-season h2 { justify-self: start; display: inline-block; padding: 2px 10px 3px; background: var(--cw-red); color: var(--cw-paper-ink); }
.cw-shell.is-playing .cw-season h2 { justify-self: start; }
.cw-calendar .is-current .cw-cal-bar, .cw-shell.is-playing .cw-calendar .is-current .cw-cal-bar { height: 6px; margin-block: 0 2px; }
.cw-stats strong { font-family: var(--font-display); font-weight: 800; }

/* The season's story under a thick red bar. */
.cw-brief { border-left-width: 8px; }

/* Choices: the incident on an ink edge, the dilemma on a red one, each
   kicker on its own field. */
.cw-choice { border-left-width: 10px; border-left-color: var(--cw-red); }
.cw-incident { border-left-color: var(--text-primary); }
.cw-choice .games-kicker { display: inline-block; padding: 2px 7px; background: var(--cw-red); color: var(--cw-paper-ink); }
.cw-incident .games-kicker { background: var(--text-primary); color: var(--bg-canvas); }
.cw-cards h3, .cw-review h3 { display: flex; align-items: center; gap: 8px; }
.cw-cards h3::before, .cw-review h3::before { content: ''; flex: none; width: 10px; height: 10px; background: var(--cw-red); }

/* What can be pressed stands off the paper; pressed, it sits down. */
.games-shell .cw-option { border-width: var(--cw-rule-w); border-color: var(--text-primary); box-shadow: 4px 4px 0 var(--cw-lift); }
.games-shell .cw-option[aria-pressed="true"] { border-width: var(--cw-rule-w); border-color: var(--cw-red); padding: 8px 10px; background: color-mix(in srgb, var(--cw-red) 14%, var(--bg-canvas)); box-shadow: 4px 4px 0 var(--cw-red); }
.games-shell .cw-primary { box-shadow: 4px 4px 0 var(--text-primary); }
[data-theme="dark"] .games-shell .cw-primary { box-shadow: 4px 4px 0 var(--cw-paper-ink); }
.games-shell .cw-primary:disabled { box-shadow: none; }
.games-shell :is(.cw-option, .cw-primary):active:enabled { translate: 3px 3px; box-shadow: 1px 1px 0 var(--cw-lift); }
.games-shell .cw-front-tab { border-width: var(--cw-rule-w); border-color: var(--text-primary); font-weight: 700; }
.games-shell .cw-front-tab[aria-pressed="true"] { background: var(--cw-red); border-color: var(--cw-red); color: var(--cw-paper-ink); }
.games-shell .cw-front-tab[aria-pressed="true"] :is(span, .cw-trend) { color: var(--cw-paper-ink); }
.cw-card.is-played { border-color: var(--cw-red); box-shadow: inset 0 6px 0 var(--cw-red); }

/* The order bar at the screen's foot under a thick rule. */
.cw-command { border-top: 3px solid var(--text-primary); }

/* The battle: its title on a red field like the season's. */
.cw-battle { border-top: 8px solid var(--cw-red); }
.cw-battle-head h2 { display: inline-block; padding: 2px 10px 3px; background: var(--cw-red); color: var(--cw-paper-ink); }
.cw-outcome { border-top: 8px solid var(--cw-red); }

/* The map in the poster's inks: the land is the paper, the water one flat
   darker tone, no graticule, the borders faint; the railways of 1918 in
   the atlas's black-and-white rail, the held stretch in thick red; the
   cities ringed in ink, Moscow a red disc with a cream star; each
   fighting front's name in big letters along it; the attackers' wedges. */
.cw-map-frame { --cw-land: color-mix(in srgb, #c9b27a 10%, var(--bg-surface)); --cw-sea: color-mix(in srgb, #24495f 42%, var(--bg-surface)); }
[data-theme="dark"] .cw-map-frame { --cw-land: color-mix(in srgb, #c9b27a 9%, var(--bg-surface)); --cw-sea: color-mix(in srgb, #3a6d96 14%, #050607); }
.cw-map-frame .emap-grid { display: none; }
.cw-map-frame .emap-border { stroke: color-mix(in srgb, var(--text-primary) 22%, transparent); stroke-dasharray: 4 3; }
.cw-map-frame .emap-land { stroke: var(--text-primary); stroke-width: 1.1; }
.cw-map-frame .emap-river { stroke: color-mix(in srgb, #24495f 70%, var(--text-primary)); stroke-width: 1; }
[data-theme="dark"] .cw-map-frame .emap-river { stroke: color-mix(in srgb, #4f8cc0 70%, var(--cw-land)); }
.cw-map-frame .cw-railway-bed { stroke: var(--text-primary); stroke-width: 3.2; }
.cw-map-frame .cw-railway { stroke: var(--cw-land); stroke-width: 1.3; stroke-dasharray: 5 5; }
.cw-chain.is-active { stroke-width: 3.6; stroke-linecap: round; }
.cw-rail { stroke: var(--cw-red); stroke-width: 9; stroke-dasharray: 1.6 4; }
.cw-city { stroke: var(--text-primary); stroke-width: 1.6; }
.cw-city.is-held { stroke: #14110d; }
.cw-city.is-lost { stroke: #14110d; stroke-width: 1.6; }
.cw-moscow-ring { fill: var(--cw-red); stroke: #14110d; stroke-width: 2; }
.cw-moscow { fill: var(--cw-paper-ink); stroke: none; }
.cw-city-label, .cw-city-label.is-capital { font-family: var(--font-display); }
.cw-city-label.is-active { font-weight: 800; fill: var(--text-primary); }
.cw-front-title { font-family: var(--font-display); font-size: 21px; font-weight: 800; letter-spacing: .14em; fill: var(--cw-red); fill-opacity: .55; pointer-events: none; }
.cw-foe-shadow { fill: #14110d; fill-opacity: 1; }
[data-theme="dark"] .cw-foe-shadow { fill: var(--cw-red); }
.cw-foe-name { stroke: #14110d; stroke-width: 1.5; }
[data-theme="dark"] .cw-front-title { fill-opacity: .8; }

/* The map's frame: its heading on an ink field, the legend in chips. */
.cw-shell .cw-map-panel .cw-map-heading { background: var(--text-primary); color: var(--bg-canvas); border-color: var(--text-primary); }
.cw-map-heading span:first-child { color: var(--bg-canvas); letter-spacing: .08em; }
.games-shell .cw-map-heading .cw-history-toggle { background: transparent; color: var(--bg-canvas); border-color: color-mix(in srgb, var(--bg-canvas) 60%, transparent); }
.games-shell .cw-map-heading .cw-history-toggle[aria-pressed="true"] { background: var(--cw-red); border-color: var(--cw-red); color: var(--cw-paper-ink); }
.cw-map-legend span { padding: 2px 7px; border: 1px solid color-mix(in srgb, var(--text-primary) 45%, transparent); background: var(--bg-canvas); color: var(--text-primary); }
/* The Red strength on the map, boxed like the attacker's marker. */
.cw-pips-box { fill: #faf2da; stroke: #14110d; stroke-width: 1.5; }
.cw-pips-shadow { fill: #14110d; }
[data-theme="dark"] .cw-pips-shadow { fill: var(--cw-red); }
.cw-pips-tab { fill: var(--cw-red); stroke: #14110d; stroke-width: 1.5; }
.cw-pips-num { font-family: var(--font-display); font-size: 11.5px; font-weight: 800; fill: #faf2da; font-variant-numeric: tabular-nums; }
.cw-pips .cw-pip { fill: var(--cw-red); stroke: #14110d; stroke-width: .8; }
.cw-pips .cw-pip.is-empty { fill: none; stroke: var(--cw-red); stroke-width: 1; }
.cw-pips .cw-pip-star { fill: var(--cw-red); stroke: #14110d; stroke-width: .8; stroke-linejoin: round; }
.cw-pip-card { fill: #14110d; }
.cw-pip-card-text { font-family: var(--font-display); font-size: 9.5px; font-weight: 800; fill: #faf2da; font-variant-numeric: tabular-nums; }
/* The legend's key to that box, in the same inks. */
.cw-map-legend i.cw-key-pip { width: 9px; height: 9px; background: var(--cw-red); border: 1px solid #14110d; }
.cw-map-legend .cw-mk-star { fill: var(--cw-red); stroke: #14110d; }
.cw-map-legend :is(.cw-key-num, .cw-key-card) { padding: 0 4px; font-family: var(--font-display); font-size: .72rem; font-variant-numeric: tabular-nums; color: #faf2da; }
.cw-map-legend .cw-key-num { background: var(--cw-red); border: 1px solid #14110d; }
.cw-map-legend .cw-key-card { background: #14110d; }
/* A cavalry army on the map: an ink horse in a cream rim, a red rider,
   his sabre in ink (cream on the dark map); in the strength bar, cream. */
.cw-cavalry path, .cw-cavalry circle { stroke-linecap: round; stroke-linejoin: round; }
.cw-cavalry .cw-cav-horse { fill: #14110d; stroke: #faf2da; stroke-width: .6; }
.cw-cavalry .cw-cav-rider-under { fill: none; stroke: #14110d; stroke-width: 3.8; }
.cw-cavalry .cw-cav-rider { fill: none; stroke: var(--cw-red); stroke-width: 2.2; }
[data-theme="dark"] .cw-cavalry .cw-cav-rider-under { stroke: #faf2da; }
.cw-cavalry .cw-cav-head { fill: var(--cw-red); stroke: #14110d; stroke-width: .7; }
[data-theme="dark"] .cw-cavalry :is(.cw-cav-head, .cw-pic-cap) { stroke: #faf2da; }
.cw-cavalry .cw-cav-sabre { fill: none; stroke: #14110d; stroke-width: 1.2; }
[data-theme="dark"] .cw-cavalry .cw-cav-sabre { stroke: #faf2da; }
.cw-cavalry .cw-pic-cap { fill: var(--cw-red); stroke: #14110d; stroke-width: .6; }
.cw-mk-cav { width: 20px; height: 14px; }
.cw-seg .cw-mk-cav :is(.cw-cav-horse, .cw-cav-head, .cw-pic-cap) { fill: #faf2da; stroke: none; }
.cw-seg .cw-mk-cav :is(.cw-cav-rider, .cw-cav-sabre) { stroke: #faf2da; }
.cw-seg .cw-mk-cav .cw-cav-rider-under { stroke: none; }
/* The attacker's pressure in figures, on an ink tab like the Red strength's red one. */
.cw-foe-tab { fill: #14110d; stroke: #14110d; stroke-width: 1; }
.cw-foe-tab-num { font-family: var(--font-display); font-size: 11.5px; font-weight: 800; fill: #faf2da; font-variant-numeric: tabular-nums; }
