:root{
  --bg-0:#08111d;
  --ink:#fff4d0;
  --ink-dim:#d8c39a;
  --ink-faint:#9c8763;
  --gold:#f5d45a;
  --gold-deep:#9b6f17;
  --red:#ef6f57;
  --red-deep:#8e3426;
  --blue:#7cb7ff;
  --green:#a4de63;
  --shadow:0 18px 40px rgba(0,0,0,.42);
  --focus:#ffe487;
  --ui-panel:url("../assets/tiny-swords/UI/Banners/Carved_9Slides.png");
  --ui-red:url("../assets/tiny-swords/UI/Buttons/Button_Red_9Slides.png");
  --ui-red-press:url("../assets/tiny-swords/UI/Buttons/Button_Red_9Slides_Pressed.png");
  --ui-blue:url("../assets/tiny-swords/UI/Buttons/Button_Blue_9Slides.png");
  --ui-blue-press:url("../assets/tiny-swords/UI/Buttons/Button_Blue_9Slides_Pressed.png");
  --ui-disable:url("../assets/tiny-swords/UI/Buttons/Button_Disable_9Slides.png");
}

*{box-sizing:border-box;margin:0;padding:0}

html,body{height:100%}

body{
  background:
    radial-gradient(900px 500px at 50% 10%, rgba(255,180,80,.10), transparent 55%),
    linear-gradient(180deg, rgba(21,36,59,.7), rgba(7,11,18,.92)),
    var(--bg-0);
  color:var(--ink);
  font:14px/1.45 "Trebuchet MS","Segoe UI",system-ui,sans-serif;
  overflow:hidden;
  -webkit-font-smoothing:none;
  text-rendering:optimizeSpeed;
}

body:before{
  content:"";
  position:fixed;
  inset:0;
  background:
    linear-gradient(rgba(255,255,255,.025), rgba(0,0,0,.025)),
    repeating-linear-gradient(0deg, rgba(0,0,0,.08) 0 1px, transparent 1px 3px);
  pointer-events:none;
  opacity:.55;
}

#app{height:100vh;display:flex;flex-direction:column;position:relative;z-index:1}

.hidden{display:none !important}

/* announcements for screen readers only — main.js writes into #bf-live */
.sr-only{
  position:absolute;
  width:1px;
  height:1px;
  margin:-1px;
  padding:0;
  overflow:hidden;
  white-space:nowrap;
  clip:rect(0 0 0 0);
  clip-path:inset(50%);
  border:0;
}

/* index.html ships the skip link as `.skip-link .sr-only` so it never disturbs
   the layout. `.skip-link:focus` (0,2,0) outranks `.sr-only` (0,1,0) whatever
   the source order, so it needs no !important to become visible on tab. */
.skip-link:focus{
  position:fixed;
  left:12px;
  top:12px;
  width:auto;
  height:auto;
  margin:0;
  padding:8px 14px;
  overflow:visible;
  white-space:normal;
  clip:auto;
  clip-path:none;
  z-index:9999;
  background:var(--bg-0);
  color:var(--ink);
  border:2px solid var(--gold);
  border-radius:4px;
  text-decoration:none;
  font-weight:800;
  font-size:12px;
  letter-spacing:.08em;
}

#topbar{
  display:flex;
  align-items:center;
  gap:12px;
  padding:8px 12px;
  background:linear-gradient(180deg, rgba(18,24,36,.92), rgba(9,12,18,.88));
  border-bottom:2px solid rgba(255,216,119,.18);
  box-shadow:0 10px 26px rgba(0,0,0,.25);
}

/* The Groot Games passport. Sits in the topbar rather than in front of the
   game, because signing in buys persistence and not the game itself. It is
   deliberately quiet: a player who never signs in should not be nagged by it
   on every frame. */
.passport{
  display:flex;
  align-items:center;
  gap:8px;
  margin-left:auto;
  font-size:11px;
  letter-spacing:.06em;
}
.passport-who{
  color:var(--ink-dim);
  max-width:22ch;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}
.passport-btn{
  font:inherit;
  letter-spacing:.08em;
  color:var(--ink);
  background:rgba(255,216,119,.10);
  border:1px solid rgba(255,216,119,.34);
  border-radius:3px;
  padding:4px 10px;
  cursor:pointer;
}
.passport-btn:hover{ background:rgba(255,216,119,.20); }
.passport-btn:focus-visible{ outline:2px solid var(--gold); outline-offset:2px; }
.passport-gold{ color:var(--gold); letter-spacing:.08em; }
.vigil{ display:flex; align-items:center; gap:8px; }
.vigil-boon{ color:var(--ink); max-width:18ch; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.vigil-note{ color:var(--ink-faint); }
.vigil [hidden]{ display:none; }
#btn-vigil:disabled{ opacity:.5; cursor:default; }

/* The Passport panel. Dense and quiet: it sits beside a board somebody is
   playing on, so it reads as a sheet of standings rather than as a shop. */
.acct-block{ padding:14px 0; border-top:1px solid rgba(255,216,119,.14); }
.acct-block:first-child{ border-top:0; }
.acct-block h3{ font-size:12px; letter-spacing:.22em; color:var(--gold); margin-bottom:8px; }
.acct-lead{ color:var(--ink); margin-bottom:6px; }
.acct-note{ color:var(--ink-faint); font-size:12px; line-height:1.5; margin-bottom:8px; }
.acct-figure{ display:flex; align-items:baseline; gap:10px; margin-bottom:6px; }
.acct-figure b{ font-size:30px; color:var(--ink); font-variant-numeric:tabular-nums; }
.acct-figure span{ font-size:11px; letter-spacing:.2em; color:var(--ink-dim); text-transform:uppercase; }
.acct-list{ list-style:none; display:flex; flex-direction:column; gap:2px; }
.acct-row{ display:flex; align-items:center; gap:10px; padding:5px 0; }
.acct-row .nm{ color:var(--ink); flex:1; }
.acct-row .tier{ font-size:10px; letter-spacing:.16em; color:var(--ink-faint); text-transform:uppercase; }
.acct-row button{ font:inherit; font-size:11px; letter-spacing:.08em; padding:3px 9px;
  color:var(--ink); background:rgba(255,216,119,.10);
  border:1px solid rgba(255,216,119,.30); border-radius:3px; cursor:pointer; }
.acct-row button:hover{ background:rgba(255,216,119,.20); }
.acct-row button:disabled{ opacity:.45; cursor:default; }
.acct-link{ color:var(--gold); font-size:12px; text-decoration:none; }
.acct-link:hover{ text-decoration:underline; }
.passport-gold b{ color:var(--ink); font-weight:700; }
.passport-link{ color:var(--ink-faint); text-decoration:none; }
.passport-link:hover{ color:var(--gold); text-decoration:underline; }
.passport [hidden]{ display:none; }

.brand,.clock,.score,.treasury,.tool,.panel,.con-block,.ch-entry,#view-toggle,#feed,#warnbar,.sheet,.rule,.go-in,.ban-in,#rail,#canvas-wrap{
  border:16px solid transparent;
  border-image-source:var(--ui-panel);
  border-image-slice:64;
  border-image-width:14px;
  border-image-repeat:stretch;
}

.brand{
  display:flex;
  align-items:center;
  gap:12px;
  min-width:240px;
  padding:7px 12px;
  background:rgba(28,19,12,.78);
}

.brand-mark{
  width:38px;
  height:38px;
  background:url("../assets/tiny-swords/UI/Icons/Regular_10.png") center/contain no-repeat;
  filter:drop-shadow(0 0 10px rgba(255,205,83,.42));
  image-rendering:pixelated;
}

.brand-text h1{
  font-size:17px;
  letter-spacing:.18em;
  color:#fff7dc;
  text-shadow:2px 2px 0 rgba(74,44,10,.9);
}

.brand-text span{
  display:block;
  font-size:10px;
  color:var(--ink-dim);
  letter-spacing:.1em;
}

.clock{
  padding:7px 12px;
  text-align:center;
  background:rgba(28,19,12,.78);
}

.clock-turn{display:flex;align-items:baseline;gap:8px;justify-content:center}
.clock-turn em{font-style:normal;font-size:22px;font-weight:800;color:#fff4d0}
.clock-turn span{font-size:11px;color:var(--ink-dim)}
.clock-season{margin-top:3px;font-size:10px;letter-spacing:.22em;color:var(--gold)}
.clock-season b{color:#fff8cb}

/* The seed line carries the map's name beside it. NOWRAP is load-bearing:
   #topbar is a flex row whose height decides where the board begins, so a name
   long enough to wrap would add a line to .clock, and that could push the board
   down under a cursor already aimed at a tile. Wrapping is forbidden; getting
   wider is harmless, because #warnbar beside it is flex:1 with min-width:0. */
.clock-seed{white-space:nowrap}
.clock-shape{font-style:normal;color:#e8c98a}

.scores{flex:1;display:flex;gap:14px;justify-content:flex-end}

.score{
  width:238px;
  padding:7px 10px;
  background:rgba(28,19,12,.78);
}

.score-head{
  display:flex;
  align-items:center;
  gap:8px;
  font-size:11px;
  letter-spacing:.15em;
  color:var(--ink-dim);
  margin-bottom:8px;
}

.score-head i{
  width:18px;
  height:18px;
  background:center/contain no-repeat;
  image-rendering:pixelated;
}

.side-a .score-head i{background-image:url("../assets/tiny-swords/UI/Icons/Regular_02.png")}
.side-b .score-head i{background-image:url("../assets/tiny-swords/UI/Icons/Regular_03.png")}
.score-head b{margin-left:auto;font-size:17px;color:#fff7dc}
.score-head small{font-size:9.5px;color:var(--ink-faint)}

/* the 9-slice frame lives in an 8px transparent border, and box-sizing is
   border-box everywhere, so the height has to pay for both borders before it
   buys any content box. 30px - 16px = a 14px channel, which is exactly what
   the fill and the "N / 10" label need. */
.bp-track{
  position:relative;
  height:30px;
  border:8px solid transparent;
  border-image-source:var(--ui-blue);
  border-image-slice:64 fill;
  border-image-width:8px;
  background:rgba(0,0,0,.35);
  overflow:hidden;
}

.bp-fill{
  height:100%;
  width:0;
  transition:width .45s ease;
  background:linear-gradient(90deg, rgba(255,214,90,.78), rgba(255,247,181,.92));
}

.bp-label{
  position:absolute;
  right:7px;
  top:0;
  height:14px;
  line-height:14px;
  font-size:10px;
  letter-spacing:.08em;
  color:#392913;
  font-weight:800;
  text-shadow:0 1px 0 rgba(255,247,205,.55);
}

#stage{flex:1;display:flex;min-height:0;gap:10px;padding:10px}
#board-col{flex:1;display:flex;flex-direction:column;min-width:0;gap:10px}

/* Lives in the top bar's middle gap. It must never change #topbar's HEIGHT,
   or the board below resizes and the player's aim moves mid-plan: the height
   is owned by .score (238px wide, taller than this), and max-height keeps it
   that way even with a long warning. Overflow is clipped rather than wrapped
   for the same reason. */
#warnbar{
  flex:1;
  min-width:0;
  max-height:62px;
  overflow:hidden;
  display:flex;
  align-items:center;
  gap:10px;
  padding:6px 12px;
  /* Sits beside the two score panels, so it takes their weight: a bright fill
     here read as louder than the Banner Points it was next to. The alarm is
     carried by the red frame, the icon and the title ink instead, which costs
     no area and does not compete with the standings. */
  background:rgba(41,21,14,.80);
  border-image-source:var(--ui-red);
}

#warnbar .warn-body{min-width:0}
#warnbar .warn-title{color:var(--red)}
#warnbar .warn-sub{color:var(--ink-dim)}
#warnbar .warn-count{
  background:rgba(239,111,87,.16);
  color:var(--ink-dim);
}
#warnbar .warn-title,
#warnbar .warn-sub{
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}

.warn-icon{
  width:32px;
  height:32px;
  background:url("../assets/tiny-swords/UI/Icons/Pressed_10.png") center/contain no-repeat;
  image-rendering:pixelated;
}

.warn-body{flex:1}
.warn-title{font-size:12px;font-weight:800;letter-spacing:.18em;color:#fff4d0}
.warn-sub{font-size:11px;color:#f6d6b7;margin-top:2px}
.warn-count{
  padding:6px 10px;
  background:rgba(255,245,214,.18);
  color:#fff4d0;
  font-size:10px;
  letter-spacing:.14em;
}

#canvas-wrap{
  flex:1;
  position:relative;
  min-height:0;
  overflow:hidden;
  padding:3px;
  background:
    linear-gradient(180deg, rgba(33,44,67,.92), rgba(11,17,27,.96)),
    url("../assets/tiny-swords/Terrain/Water/Water.png");
  box-shadow:var(--shadow);
}

#map{
  display:block;
  width:100%;
  height:100%;
  image-rendering:pixelated;
  border-radius:8px;
}

/* the 3d board occupies exactly the same box as #map — #canvas-wrap is the
   containing block and pads its content by 3px, so a 3px offset on every side
   matches the content box #map lays out into.
   `inset:3px` alone is not enough: <canvas> is a replaced element with an
   intrinsic 300x150, so with width/height auto the browser keeps that intrinsic
   size and simply ignores `right`/`bottom`. The size has to be stated. 100% here
   is #canvas-wrap's padding box, hence the 6px it gives back. */
#map3d{
  display:block;
  position:absolute;
  left:3px;
  top:3px;
  width:calc(100% - 6px);
  height:calc(100% - 6px);
  border-radius:8px;
  touch-action:none;
}

/* ---------------------------------------------------------- 2d / 3d view */
#view-toggle{
  display:inline-flex;
  align-items:center;
  gap:6px;
  align-self:flex-start;
  padding:4px 6px;
  background:rgba(28,19,12,.86);
  border-width:12px;
  border-image-width:11px;
}

.vbtn{
  font:inherit;
  cursor:pointer;
  min-width:46px;
  padding:5px 8px;
  border:12px solid transparent;
  border-image-source:var(--ui-blue);
  border-image-slice:64 fill;
  border-image-width:10px;
  background:transparent;
  color:#351f11;
  font-size:10px;
  font-weight:800;
  letter-spacing:.14em;
  opacity:.82;
}

.vbtn.on{
  border-image-source:var(--ui-red);
  color:#fff4d0;
  text-shadow:1px 1px 0 rgba(54,31,10,.9);
  opacity:1;
  transform:translateY(1px);
}

.vbtn:disabled{
  border-image-source:var(--ui-disable);
  color:rgba(255,244,208,.5);
  text-shadow:none;
  opacity:.65;
  cursor:not-allowed;
  filter:none;
  transform:none;
}

/* The panel explains the board, so it must never be wider than the board or
   cover more of it than it has to. Without a max-width the raid hint — one
   long sentence naming both candidate attackers and their numbers — stretched
   it past the right edge, and the very values the player is being asked to
   judge were the ones clipped off. js/main.js narrows this further on a small
   canvas; the hint wraps instead of growing. */
#tooltip{
  position:absolute;
  pointer-events:none;
  z-index:6;
  min-width:132px;
  max-width:250px;
  padding:9px 10px;
  border:14px solid transparent;
  border-image-source:var(--ui-panel);
  border-image-slice:64;
  border-image-width:12px;
  background:rgba(37,25,16,.94);
  font-size:11px;
  line-height:1.32;
  overflow-wrap:anywhere;
}

#tooltip h4{font-size:11px;letter-spacing:.15em;margin-bottom:4px;color:#fff4d0}
#tooltip .tt-row{display:flex;justify-content:space-between;gap:10px;color:var(--ink-dim)}
#tooltip .tt-row b{color:#fff7dc;white-space:nowrap}
#tooltip .tt-hint{margin-top:6px;padding-top:5px;border-top:1px solid rgba(255,232,179,.16);color:var(--gold)}

#toast,#gameover{
  position:absolute;
  inset:0;
  display:flex;
  align-items:center;
  justify-content:center;
  z-index:7;
}

#toast{pointer-events:none}

.toast-in,.go-in{
  min-width:min(680px,86%);
  text-align:center;
  padding:28px 36px;
  background:rgba(30,21,14,.88);
  box-shadow:var(--shadow);
}

/* main.js restarts this animation on every toast() by clearing style.animation */
.toast-in{animation:toast-in .34s ease-out both}

@keyframes toast-in{
  from{opacity:0;transform:translateY(-16px) scale(.97)}
  to{opacity:1;transform:none}
}

#toast h2,.go-in h2{
  font-size:34px;
  letter-spacing:.18em;
  color:#fff7d0;
  text-shadow:3px 3px 0 rgba(91,51,10,.9);
}

#toast p,.go-in p{
  margin-top:9px;
  color:var(--ink-dim);
  font-size:13px;
}

#gameover{
  background:rgba(5,8,12,.72);
  backdrop-filter:blur(4px);
}

#feed{
  flex:0 0 132px;
  overflow-y:auto;
  padding:7px 10px;
  background:rgba(28,19,12,.78);
}

#feed .fe{display:flex;gap:10px;padding:4px 0}
#feed .fe-t{flex:0 0 44px;color:var(--ink-faint);font-size:10px;letter-spacing:.1em}
#feed .fe-x{flex:1;color:var(--ink-dim)}
#feed .fe.a .fe-x{color:#efe27a}
#feed .fe.b .fe-x{color:#ffb296}
#feed .fe.world .fe-x{color:#ffe0a4;font-weight:700}
#feed .fe.banner .fe-x{color:#fff08c}

#rail{
  flex:0 0 326px;
  display:flex;
  flex-direction:column;
  min-height:0;
  padding:1px;
  background:rgba(28,19,12,.84);
}

.tabs{
  display:flex;
  gap:6px;
  padding:6px 6px 0;
}

.tab,.btn,.mini,.tool{
  font:inherit;
  cursor:pointer;
  color:#fff4d0;
  text-shadow:1px 1px 0 rgba(54,31,10,.9);
}

.tab,.btn,.mini{
  border:12px solid transparent;
  border-image-slice:64 fill;
  border-image-width:10px;
  background:transparent;
}

.tab{
  flex:1;
  padding:7px 4px;
  border-image-source:var(--ui-blue);
  color:#351f11;
  text-shadow:none;
  font-weight:800;
  font-size:10px;
  letter-spacing:.14em;
  opacity:.82;
}

.tab.active{
  border-image-source:var(--ui-red);
  color:#fff4d0;
  opacity:1;
  transform:translateY(1px);
}

.panel{
  display:none;
  flex:1;
  min-height:0;
  overflow-y:auto;
  padding:10px;
  background:rgba(29,20,13,.76);
}

.panel.active{display:block}

.panel-note{
  font-size:11px;
  color:var(--ink-dim);
  line-height:1.55;
  margin-bottom:12px;
}

/* WHICH GROUND THIS IS. The shape the seed chose, and the one line that says
   what it changes. It sits at the top of the rail's ORDERS panel, which is a
   scrolling flex column BESIDE #board-col — nothing here can ever change where
   the board starts or how tall it is, however long a blurb gets.
   Quieter than .treasury on purpose: it is the frame the match is played in,
   read once at the start and glanced at afterwards, not a number that moves. */
.ground{
  padding:6px 10px 7px;
  margin-bottom:10px;
  background:rgba(0,0,0,.22);
  border-left:2px solid rgba(255,216,119,.34);
}

.ground-head{
  display:flex;
  align-items:baseline;
  gap:8px;
  justify-content:space-between;
}

.ground-head span{
  font-size:9px;
  letter-spacing:.2em;
  color:var(--ink-faint);
}

.ground-head b{
  font-size:12px;
  letter-spacing:.11em;
  color:#fff7d0;
  text-align:right;
}

.ground-blurb{
  margin-top:3px;
  font-size:10.5px;
  line-height:1.5;
  color:var(--ink-dim);
}

.treasury{
  padding:7px 10px;
  margin-bottom:12px;
  background:rgba(0,0,0,.22);
}

.tre-row{
  display:flex;
  justify-content:space-between;
  align-items:baseline;
  font-size:10.5px;
  letter-spacing:.14em;
  color:var(--ink-faint);
}

.tre-row b{color:#fff7d0;font-size:18px}
.tre-row.small{margin-top:8px;font-size:10px;letter-spacing:.06em}
.tre-row.small b{font-size:12px}
.tre-note{font-style:normal;font-size:9px;letter-spacing:.06em;color:var(--ink-faint);text-align:right}

/* THE COMMAND BUDGET — one pip per Command, and nothing in the panel is
   louder. A ready pip is a lit ember slot; main.js adds .spent as each Command
   is given, which drops it to a cold disabled slab.
   Three pips are the markup's, because three is what a turn is GIVEN. The row
   grows past them: main.js appends one .cmd.banked for every Command carried
   over (up to five, so eight in all) and sets grid-template-columns itself
   once the row is wider than four, which is why the three here is a starting
   point rather than the shape. */
#hud-commands{
  display:grid;
  grid-template-columns:repeat(3,1fr);
  gap:7px;
  margin-top:8px;
}

.cmd{
  position:relative;
  display:block;
  height:38px;
  border:10px solid transparent;
  border-image-source:var(--ui-red);
  border-image-slice:64 fill;
  border-image-width:10px;
  filter:drop-shadow(0 0 8px rgba(255,142,62,.42));
  transition:filter .18s, opacity .18s;
}

/* the lit ember in a ready slot */
.cmd:after{
  content:"";
  position:absolute;
  left:50%;
  top:50%;
  width:13px;
  height:13px;
  margin:-7px 0 0 -7px;
  background:linear-gradient(180deg,#fff6d8,#ffcf72);
  box-shadow:0 0 9px rgba(255,226,150,.9);
  transform:rotate(45deg);
}

.cmd.spent{
  border-image-source:var(--ui-disable);
  filter:grayscale(.75) brightness(.58);
  opacity:.72;
}

/* spent reads as a struck-out slot, not a dimmer ember */
.cmd.spent:after{
  width:16px;
  height:3px;
  margin:-2px 0 0 -8px;
  background:rgba(255,244,208,.34);
  box-shadow:none;
  transform:none;
}

/* a hairline under the pips: it carries the over-budget warning the pips
   themselves cannot show (a fourth order queued against a budget of three). */
.spend-track{
  height:6px;
  margin-top:7px;
  border:1px solid rgba(255,216,119,.16);
  background:rgba(0,0,0,.36);
  overflow:hidden;
}

.spend-fill{
  height:100%;
  width:0;
  background:linear-gradient(90deg, rgba(255,168,80,.85), rgba(255,236,143,.95));
  transition:width .25s;
}

.spend-fill.over{background:linear-gradient(90deg, rgba(239,111,87,.9), rgba(255,203,131,.95))}

/* ONE STRIKE A TURN — armed, then unmistakably spent. */
#hud-strike{
  display:flex;
  align-items:center;
  gap:8px;
  margin-top:8px;
  padding:6px 9px;
  background:rgba(239,111,87,.16);
  border-left:3px solid var(--red);
  font-size:10px;
  letter-spacing:.14em;
  color:#ffd9c6;
}

#hud-strike:before{
  content:"\2694";
  font-size:14px;
  line-height:1;
  color:#ffb896;
  text-shadow:0 0 8px rgba(239,111,87,.7);
}

#hud-strike b{font-size:11px;letter-spacing:.16em;color:#fff0e2}
#hud-strike em{
  font-style:normal;
  margin-left:auto;
  font-size:9px;
  letter-spacing:.06em;
  color:var(--ink-faint);
}

#hud-strike.used{
  background:rgba(255,244,208,.05);
  border-left-color:rgba(255,244,208,.2);
  color:var(--ink-faint);
}

#hud-strike.used:before{color:rgba(216,195,154,.4);text-shadow:none}
#hud-strike.used b{
  color:var(--ink-faint);
  text-decoration:line-through;
  text-decoration-color:rgba(239,111,87,.85);
}

/* LEVY, BOTH SIDES SIDE BY SIDE — the comparison is the strategic read, so
   the two numbers sit in one row in each side's own colour. */
.levy-head{margin-top:10px}

.levies{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:7px;
  margin-top:5px;
}

.levy{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:6px;
  padding:5px 9px;
  background:rgba(0,0,0,.28);
  border:1px solid rgba(255,216,119,.16);
}

.levy span{font-size:9.5px;letter-spacing:.1em;line-height:1.2}
.levy b{font-size:24px;line-height:1;text-shadow:2px 2px 0 rgba(54,31,10,.9)}

.levy.mine{background:rgba(243,213,84,.12);border-color:rgba(243,213,84,.45)}
.levy.mine span{color:#e6d5a1}
.levy.mine b{color:#ffe487}

.levy.foe{background:rgba(239,111,87,.12);border-color:rgba(239,111,87,.42)}
.levy.foe span{color:#e5b5a7}
.levy.foe b{color:#ffa78d}

.ai-intent{
  padding:7px 10px;
  margin-bottom:10px;
  background:rgba(255,245,214,.08);
  color:#ffe7a5;
  font-size:10.5px;
  letter-spacing:.12em;
}

.tools{display:flex;flex-direction:column;gap:7px;margin-bottom:12px}

.tool{
  position:relative;
  padding:7px 9px 7px 36px;
  text-align:left;
  border-image-source:var(--ui-blue);
  border-image-slice:64 fill;
  border-image-width:10px;
  background:transparent;
}

.tool.active{
  border-image-source:var(--ui-red);
}

.tool kbd{
  position:absolute;
  left:9px;
  top:50%;
  transform:translateY(-50%);
  width:18px;
  height:18px;
  display:grid;
  place-items:center;
  background:rgba(32,22,14,.72);
  color:#fff4d0;
  font-size:10px;
}

.tname{font-size:11px;font-weight:800;letter-spacing:.12em}
.tcost{float:right;font-size:10px;color:#ffe68a;font-weight:800}
.tool em{display:block;font-style:normal;font-size:10px;color:var(--ink-dim);margin-top:1px;line-height:1.3}
.tcost.raised{color:#ffb8a8}

.orders-head{
  display:flex;
  justify-content:space-between;
  align-items:center;
  font-size:10px;
  letter-spacing:.18em;
  color:var(--ink-faint);
  margin-bottom:6px;
}

#orderlist{list-style:none;min-height:26px;margin-bottom:12px}

#orderlist li{
  display:flex;
  align-items:center;
  gap:8px;
  padding:6px 8px;
  margin-bottom:5px;
  background:rgba(255,245,214,.08);
  font-size:11.5px;
  cursor:pointer;
}

#orderlist li:hover{background:rgba(255,245,214,.15)}
#orderlist li .oc{margin-left:auto;color:#ffe68a;font-size:10.5px}
#orderlist .empty{color:var(--ink-faint);background:none;cursor:default}

.mods{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:14px}
.mod{
  padding:5px 9px;
  background:rgba(255,214,90,.12);
  color:#ffe490;
  font-size:10px;
  letter-spacing:.08em;
}

.btn{
  padding:7px 12px;
  border-image-source:var(--ui-blue);
  font-size:10.5px;
  letter-spacing:.14em;
  font-weight:800;
  color:#352011;
  text-shadow:none;
}

.btn:hover:not(:disabled),.mini:hover:not(:disabled),.tab:hover,.tool:hover,.vbtn:hover:not(:disabled){filter:brightness(1.07)}

/* a control that cannot be pressed has to look like it. CREATE MATCH stays on
   screen for the whole match so that it is obvious what created the one you
   are in, but it must not read as an invitation to press it again. */
.btn:disabled,.mini:disabled{filter:grayscale(.7) brightness(.72);cursor:default}

/* pressed 9-slice frames, so the skin reads as a real button under the mouse */
.tab:active,.btn:active,.mini:active,.tool:active,.vbtn:active:not(:disabled){
  border-image-source:var(--ui-blue-press);
  transform:translateY(2px);
}

.tab.active:active,.btn.primary:active,.mini.solid:active,.mini.primary:active,.tool.active:active,.vbtn.on:active{
  border-image-source:var(--ui-red-press);
}

.btn.primary{border-image-source:var(--ui-red);color:#fff4d0;text-shadow:1px 1px 0 rgba(54,31,10,.9)}
.btn.big{width:100%;padding:10px 14px}
.btn kbd,.mini kbd{font-family:inherit;font-size:9px;opacity:.8;margin-left:6px}

.mini{
  padding:5px 7px;
  border-image-source:var(--ui-blue);
  font-size:10px;
  letter-spacing:.1em;
  font-weight:800;
  color:#352011;
  text-shadow:none;
}

.mini.solid,.mini.primary{border-image-source:var(--ui-red);color:#fff4d0;text-shadow:1px 1px 0 rgba(54,31,10,.9)}

/* ---------------------------------------------------------- chronicle */
.ch-entry{
  padding:9px 11px 10px;
  margin-bottom:10px;
  background:rgba(255,245,214,.05);
}

/* progressive enhancement: tint the whole card by how the prediction scored */
.ch-entry:has(.tag.hit){background:rgba(164,222,99,.09)}
.ch-entry:has(.tag.miss){background:rgba(239,111,87,.10)}

.ch-top{
  display:flex;
  flex-wrap:wrap;
  align-items:baseline;
  gap:4px 8px;
  padding-bottom:6px;
  margin-bottom:7px;
  border-bottom:1px solid rgba(255,232,179,.16);
}

.ch-name{
  order:-1;
  flex:0 0 100%;
  font-size:12px;
  font-weight:800;
  letter-spacing:.14em;
  color:#fff7dc;
  text-shadow:1px 1px 0 rgba(54,31,10,.7);
}

.ch-season{
  flex:1;
  min-width:0;
  font-size:9.5px;
  letter-spacing:.1em;
  color:var(--ink-faint);
}

.ch-int{
  font-size:11px;
  font-weight:800;
  letter-spacing:.28em;
  color:var(--gold);
}

.ch-msg{
  color:#ffe7a5;
  font-size:11.5px;
  line-height:1.5;
}

.ch-why{
  margin-top:6px;
  padding-left:8px;
  border-left:2px solid rgba(255,232,179,.22);
  color:var(--ink-dim);
  font:10.5px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;
}

.ch-pred{
  display:flex;
  flex-wrap:wrap;
  align-items:baseline;
  gap:5px 7px;
  margin-top:8px;
  padding-top:7px;
  border-top:1px solid rgba(255,232,179,.14);
}

.pred-text{
  flex:1 1 100%;
  color:var(--ink-dim);
  font-size:10.5px;
  line-height:1.45;
}

/* prediction verdicts — the artefact judges read */
.tag{
  padding:3px 7px;
  font-size:9px;
  font-weight:800;
  letter-spacing:.14em;
  color:#231508;
  background:var(--ink-faint);
  box-shadow:inset 0 -2px 0 rgba(0,0,0,.22);
}

.tag.hit{background:var(--green)}
.tag.miss{background:var(--red);color:#2a1008}
.tag.wait{background:var(--gold-deep);color:#fff1c4;box-shadow:inset 0 0 0 1px rgba(255,232,179,.22)}

/* provenance badges — where the decision actually came from */
.src,.ch-src{
  display:inline-block;
  padding:2px 6px;
  font-size:9px;
  font-weight:800;
  letter-spacing:.14em;
  color:#231508;
  background:var(--ink-faint);
}

.src.llm,.ch-src.llm{background:var(--blue)}
.src.fallback,.ch-src.fallback{background:var(--red);color:#2a1008}
.src.pending,.ch-src.pending{background:rgba(255,245,214,.22);color:#ffe7a5}

.src.override,.ch-src.override{
  background:var(--gold);
  color:#3a2708;
  box-shadow:0 0 0 1px #2a1a0d, 0 0 0 3px var(--gold-deep);
}

.src.override:before,.ch-src.override:before{content:"✋ "}

.con-block{
  padding:10px 12px;
  margin-bottom:12px;
  background:rgba(255,245,214,.05);
}

.con-title{
  margin-bottom:8px;
  color:#ffe59b;
  font-size:10px;
  letter-spacing:.18em;
  font-weight:800;
}

.con-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}

.ai-status,.reason,.pending,.memory,pre{
  white-space:pre-wrap;
  word-break:break-word;
  color:var(--ink-dim);
  font:11px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;
}

/* the AI runtime is degraded — this must never be mistakable for the LLM path */
.fallback{
  padding:8px 10px;
  color:#ffd7c4;
  background:
    repeating-linear-gradient(135deg, rgba(239,111,87,.14) 0 8px, rgba(239,111,87,.05) 8px 16px),
    rgba(142,52,38,.34);
  box-shadow:inset 4px 0 0 var(--red), inset 0 0 0 1px var(--red-deep);
}

.fallback:before{
  content:"⚠ FALLBACK — DETERMINISTIC CODE IS DRIVING";
  display:block;
  margin-bottom:6px;
  color:#ffb9a2;
  font:800 9px/1.4 "Trebuchet MS","Segoe UI",system-ui,sans-serif;
  letter-spacing:.16em;
}

/* -------------------------------------------------- template memory bars */
.memory .mem-row{
  display:grid;
  grid-template-columns:minmax(0,1.15fr) minmax(52px,1fr) auto;
  align-items:center;
  gap:8px;
  padding:4px 0;
  font-size:10px;
  letter-spacing:.06em;
}

.mem-row + .mem-row{border-top:1px solid rgba(255,232,179,.1)}

.mem-row > span:first-child{
  overflow:hidden;
  white-space:nowrap;
  text-overflow:ellipsis;
  color:var(--ink-dim);
}

.mbar{
  position:relative;
  display:block;
  height:9px;
  background:rgba(0,0,0,.36);
  box-shadow:inset 0 0 0 1px rgba(255,232,179,.18);
  overflow:hidden;
}

/* 50% reference tick — the bar is a prediction hit-rate, not a quantity */
.mbar:after{
  content:"";
  position:absolute;
  left:50%;
  top:0;
  bottom:0;
  width:1px;
  background:rgba(255,232,179,.24);
}

.mbar i{
  display:block;
  height:100%;
  background:linear-gradient(90deg, rgba(164,222,99,.85), rgba(255,236,143,.95));
}

.mem-row b{
  color:#ffe68a;
  font-size:10px;
  font-weight:800;
  text-align:right;
}

select,input[type="checkbox"]{accent-color:#d14b38}

.chk{
  display:block;
  margin:6px 0;
  color:var(--ink-dim);
  font-size:11px;
}

#intro{
  position:fixed;
  inset:0;
  z-index:25;
  background:rgba(7,12,20,.82);
  backdrop-filter:blur(4px);
}

#intro.leaving{opacity:0;transition:opacity .55s ease}

#introcanvas{
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  image-rendering:pixelated;
}

.skip{
  position:absolute;
  top:18px;
  right:18px;
  z-index:3;
  padding:8px 12px;
  background:rgba(24,16,10,.72);
  color:#fff4d0;
  border:1px solid rgba(255,225,150,.28);
}

/* #intro is fixed and body cannot scroll, so the stage itself must never
   overflow: the active slide is the scroll container and shrinks to whatever
   room is left, while the nav (dots + the "TAKE THE RING" call to action)
   keeps its size and stays on screen at every viewport height. */
.intro-stage{
  position:relative;
  z-index:2;
  display:flex;
  height:100%;
  flex-direction:column;
  justify-content:center;
  align-items:center;
  padding:32px 20px;
}

.slide{
  display:none;
  width:min(1040px,92vw);
}

.slide.active{
  display:block;
  min-height:0;
  overflow-y:auto;
  overscroll-behavior:contain;
  scrollbar-width:thin;
  scrollbar-color:rgba(245,212,90,.55) transparent;
}

.slide.active::-webkit-scrollbar{width:8px}
.slide.active::-webkit-scrollbar-thumb{background:rgba(245,212,90,.55)}
.slide.active::-webkit-scrollbar-track{background:rgba(0,0,0,.25)}

.hero,.sheet{
  padding:24px 28px;
  background:rgba(28,19,12,.84);
  box-shadow:var(--shadow);
}

.hero{
  text-align:center;
  max-width:900px;
  margin:0 auto;
  background:rgba(23,16,11,.88);
}
.hero-kicker,.sheet-kicker{font-size:11px;letter-spacing:.25em;color:#ffe59b;margin-bottom:10px}
.hero-title{
  font-size:44px;
  line-height:1.02;
  letter-spacing:.12em;
  color:#fff7d3;
  text-shadow:4px 4px 0 rgba(91,51,10,.9);
}
.hero-rule{width:160px;height:3px;background:#f2ce5e;margin:12px auto}
.hero-line{font-size:16px;color:#fff2c4}
.hero-sub{
  max-width:760px;
  margin:10px auto 0;
  color:var(--ink-dim);
  line-height:1.6;
}
.hero-legend{display:flex;gap:16px;justify-content:center;flex-wrap:wrap;margin-top:16px;color:#ffe8b2}

.dot{display:inline-block;width:11px;height:11px;margin-right:6px}
.dot.ash{background:#f3d554}
.dot.molten{background:#ef6f57}
.dot.fire{background:#ff9d58}

.sheet h2{font-size:28px;letter-spacing:.08em;margin-bottom:14px;color:#fff5d0}
.cols{display:grid;grid-template-columns:1fr 1fr;gap:20px}
.cols p,.rule p{color:var(--ink-dim)}
.pull{color:#fff1ba}
.banner-note{
  display:flex;
  gap:14px;
  margin-top:18px;
  padding:14px;
  background:rgba(255,245,214,.08);
}

.bn-star{font-size:28px;color:#ffe487}
.c-jade{color:#f3d554}
.c-molten{color:#ef947f}

.rulegrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
.rule{
  padding:18px 20px;
  background:rgba(255,245,214,.06);
}

.rule.accent{background:rgba(239,111,87,.12)}
.rule.win{background:rgba(245,212,90,.12)}
.rule-h{display:flex;align-items:center;gap:10px;margin-bottom:10px;font-weight:800;letter-spacing:.12em;color:#fff0ba}
.rn{
  width:26px;
  height:26px;
  display:grid;
  place-items:center;
  background:rgba(255,214,90,.18);
  color:#fff6d6;
}

.orders{list-style:none;margin-top:10px}
.orders li{
  display:grid;
  grid-template-columns:auto 1fr auto;
  gap:10px;
  padding:8px 0;
  color:var(--ink-dim);
}

.orders li b{color:#fff6d6}
.orders li span{color:#ffe487;font-weight:800}
/* The shape list has a name and a line, and no cost column to hold open. */
#intro-ground-list li{grid-template-columns:auto 1fr;padding:6px 0}
#intro-ground-list li b{white-space:nowrap}
.eq{display:grid;gap:8px;margin:12px 0}
.lbl{display:block;font-size:10px;letter-spacing:.18em;color:#ffe59b;margin-bottom:4px}
.quiet{font-size:11px;color:var(--ink-faint)}

.intro-nav{
  flex:none;
  width:min(1040px,92vw);
  display:flex;
  align-items:center;
  justify-content:space-between;
  margin-top:12px;
}

.dots{display:flex;gap:8px}

.dot-nav{
  width:16px;
  height:16px;
  background:url("../assets/tiny-swords/UI/Icons/Disable_01.png") center/contain no-repeat;
  image-rendering:pixelated;
  opacity:.75;
  cursor:pointer;
}

.dot-nav.active{
  background-image:url("../assets/tiny-swords/UI/Icons/Pressed_01.png");
  opacity:1;
}

/* ================================================ legal-move feedback ==== */
/* the one-line answer to "where can I actually use this?", written from the
   same E.legalTargets set the board highlights, so the two cannot disagree. */
.legal-note{
  margin:-6px 0 12px;
  padding:7px 9px;
  font-size:10.5px;
  letter-spacing:.05em;
  color:var(--ink-dim);
  background:rgba(255,245,214,.05);
  box-shadow:inset 0 0 0 1px rgba(255,232,179,.14);
}

.legal-note b{color:#ffe487}
.legal-note.none{color:#ffb8a8;background:rgba(239,111,87,.12);box-shadow:inset 0 0 0 1px rgba(239,111,87,.35)}

/* ==================================================== raid source pick === */
/* Anchored over the target square. It only ever appears when there really is
   a choice, so it costs the player exactly one click and never a needless one. */
#raidpick{
  position:absolute;
  z-index:9;
  width:260px;
  padding:10px 11px 9px;
  background:linear-gradient(180deg, rgba(28,17,10,.98), rgba(16,10,6,.98));
  box-shadow:var(--shadow), inset 0 0 0 1px rgba(255,120,90,.55);
  font-size:11px;
}

.rp-head{margin-bottom:7px;line-height:1.5}
.rp-head b{display:block;font-size:10px;letter-spacing:.16em;color:#ffb8a8}
.rp-head em{display:block;font-style:normal;font-size:10px;color:var(--ink-faint)}
.rp-list{display:flex;flex-direction:column;gap:5px;margin-bottom:8px}

.rp-opt{
  display:grid;
  grid-template-columns:auto 1fr auto;
  align-items:center;
  gap:9px;
  width:100%;
  padding:7px 9px;
  font:inherit;
  text-align:left;
  color:var(--ink);
  cursor:pointer;
  background:rgba(255,245,214,.07);
  border:0;
  box-shadow:inset 0 0 0 1px rgba(255,232,179,.18);
}

.rp-opt:hover,.rp-opt.active{background:rgba(255,245,214,.16)}
.rp-opt .rp-sq{font-weight:800;letter-spacing:.08em;color:#fff7dc}
.rp-opt .rp-num{color:var(--ink-dim)}
.rp-opt .rp-num i{font-style:normal;color:#fff7dc;font-weight:800}
.rp-opt .rp-verdict{font-size:9.5px;letter-spacing:.1em;font-weight:800}
.rp-opt.win{box-shadow:inset 0 0 0 1px rgba(164,222,99,.55)}
.rp-opt.win .rp-verdict{color:var(--green)}
.rp-opt.lose .rp-verdict{color:var(--red)}
/* the strongest attacker, which is also the square the engine would have
   picked on its own. Marked inline on the coordinate so it cannot wrap onto a
   row of its own the way a grid child does. */
.rp-opt.best .rp-sq:after{content:" ◆";color:#ffe487;font-size:9px}

/* ============================================================== versus === */
#pvpbar{
  flex:none;
  display:flex;
  align-items:center;
  gap:14px;
  padding:8px 12px;
  background:linear-gradient(90deg, rgba(124,183,255,.16), rgba(255,245,214,.05));
  box-shadow:inset 0 0 0 1px rgba(124,183,255,.4);
}

.pvp-cell{display:flex;flex-direction:column;gap:1px;min-width:0}
.pvp-cell span{font-size:8.5px;letter-spacing:.2em;color:var(--ink-faint)}
.pvp-cell b{font-size:12px;letter-spacing:.08em;color:#fff4d0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pvp-cell.pvp-phase{flex:1}
.pvp-cell.pvp-phase b{color:#cfe4ff}
#pvpbar.waiting .pvp-cell.pvp-phase b{color:#ffe487}
#pvpbar.desync{background:linear-gradient(90deg, rgba(239,111,87,.28), rgba(255,245,214,.05));box-shadow:inset 0 0 0 1px rgba(239,111,87,.7)}
.pvp-sync b{font-family:"Consolas","Courier New",monospace;font-size:11px;color:#a4de63}
.pvp-sync.bad b{color:#ff9d84}

.pvp-clock{
  flex:none;
  min-width:66px;
  padding:4px 9px;
  text-align:center;
  font-size:16px;
  font-weight:800;
  letter-spacing:.06em;
  color:#fff4d0;
  background:rgba(0,0,0,.35);
  box-shadow:inset 0 0 0 1px rgba(255,232,179,.28);
}

.pvp-clock.low{color:#ffb8a8;box-shadow:inset 0 0 0 1px rgba(239,111,87,.7)}

.pvp-clock-big{
  padding:10px;
  text-align:center;
  font-size:26px;
  font-weight:800;
  letter-spacing:.08em;
  color:#fff4d0;
  background:rgba(0,0,0,.32);
  box-shadow:inset 0 0 0 1px rgba(255,232,179,.22);
}

.pvp-clock-big.low{color:#ffb8a8}

.pvp-input{
  flex:1;
  min-width:0;
  padding:6px 8px;
  font:inherit;
  font-size:11px;
  letter-spacing:.1em;
  color:#fff4d0;
  background:rgba(0,0,0,.35);
  border:0;
  box-shadow:inset 0 0 0 1px rgba(255,232,179,.28);
}

.pvp-input::placeholder{color:var(--ink-faint);letter-spacing:.06em}
.pvp-note{margin-top:6px;font-size:10px;color:var(--ink-faint);line-height:1.4}
#pvp-state.live{color:#cfe4ff}
#pvp-state.waiting{color:#ffe487}
#pvp-state.bad{color:#ffb8a8}

/* ====================================================== accessibility ==== */
/* every interactive control gets the same unmistakable keyboard ring. the
   9-slice buttons carry their art inside a transparent border, so the ring is
   drawn on the border box and offset outward to clear the frame. */
.tab:focus-visible,
.btn:focus-visible,
.mini:focus-visible,
.tool:focus-visible,
.vbtn:focus-visible,
.skip:focus-visible,
.dot-nav:focus-visible,
.chk:focus-within,
#orderlist li:focus-visible,
.rp-opt:focus-visible,
.ban-card:focus-visible,
.pvp-input:focus-visible,
.replay-box:focus-visible,
.pb-scrub:focus-visible,
.pb-jump:focus-visible,
select:focus-visible,
input[type="checkbox"]:focus-visible,
#map:focus-visible,
#map3d:focus-visible,
#chronicle .ch-entry:focus-visible{
  outline:3px solid var(--focus);
  outline-offset:2px;
}

.tab:focus-visible,.btn:focus-visible,.mini:focus-visible,.tool:focus-visible,.vbtn:focus-visible{
  filter:brightness(1.07);
}

/* the board itself: focusing it arms the keyboard cursor, so say so loudly */
#map:focus-visible,#map3d:focus-visible{
  outline-offset:-3px;
  border-radius:8px;
}

#canvas-wrap:focus-within{box-shadow:var(--shadow), inset 0 0 0 2px rgba(255,228,135,.5)}

/* fall back to :focus where :focus-visible is unsupported */
@supports not selector(:focus-visible){
  .tab:focus,.btn:focus,.mini:focus,.tool:focus,.vbtn:focus,.skip:focus,
  .rp-opt:focus,.ban-card:focus,.pvp-input:focus,.replay-box:focus,.pb-scrub:focus,
  .pb-jump:focus,
  select:focus,input[type="checkbox"]:focus,#map:focus,#map3d:focus{
    outline:3px solid var(--focus);
    outline-offset:2px;
  }
}

@media (max-width: 1100px){
  #topbar{flex-wrap:wrap}
  .scores{width:100%;justify-content:stretch}
  .score{flex:1;min-width:240px}
  #rail{flex-basis:320px}
  .cols,.rulegrid{grid-template-columns:1fr}
  .hero-title{font-size:38px}
}

@media (max-width: 820px){
  body{overflow:auto}
  #app{height:auto;min-height:100vh}
  #stage{flex-direction:column}
  #rail{flex:1 1 auto}
  .hero,.sheet{padding:18px 20px}
  .hero-title{font-size:32px;letter-spacing:.08em}
  .hero-sub{font-size:13px}
  .intro-nav{flex-direction:column;gap:10px;align-items:stretch}
  .intro-nav .btn.big{width:100%}
}

/* laptop-height screens (1366x768, 1024x700): tighten the intro so as much of
   a slide as possible is on screen before the reader has to scroll it. */
@media (max-height: 860px){
  .intro-stage{padding:18px 18px}
  .hero,.sheet{padding:16px 20px}
  .hero-title{font-size:36px}
  .hero-sub{margin-top:8px}
  .hero-legend{margin-top:12px}
  .sheet h2{font-size:22px;margin-bottom:10px}
  .rulegrid{gap:10px}
  .rule{padding:12px 14px}
  .rule-h{margin-bottom:7px}
  .banner-note{margin-top:12px;padding:11px}
  .intro-nav{margin-top:10px}
}

/* ================================================== the turn clock ====== */
/* Visible in every mode, in the same place, reading the same way. It sits
   under the turn and season in the top bar so a countdown never lands on the
   board or moves it, and it is a small block rather than a loud one on
   purpose: a clock that shouts for the ninety seconds it is not urgent has
   nothing left to say in the ten seconds it is. The urgency is carried by the
   drain bar and by one colour change at the ten-second mark. */
.clock-timer{
  position:relative;
  margin-top:5px;
  padding:3px 8px 5px;
  background:rgba(0,0,0,.35);
  box-shadow:inset 0 0 0 1px rgba(255,232,179,.22);
  line-height:1.05;
}

.clock-timer b{
  font-size:17px;
  font-weight:800;
  letter-spacing:.06em;
  color:#fff4d0;
  font-variant-numeric:tabular-nums;
}

.clock-timer span{
  display:block;
  font-size:8px;
  letter-spacing:.2em;
  color:var(--ink-faint);
}

/* the drain: the honest reading of "how much of the turn is left" */
.clock-fill{
  position:absolute;
  left:0;
  bottom:0;
  height:2px;
  width:0;
  background:linear-gradient(90deg, rgba(255,168,80,.8), rgba(255,236,143,.95));
  transition:width .25s linear;
}

.clock-timer.low{box-shadow:inset 0 0 0 1px rgba(239,111,87,.75)}
.clock-timer.low b{color:#ffb8a8}
.clock-timer.low .clock-fill{background:linear-gradient(90deg, rgba(239,111,87,.95), rgba(255,203,131,.95))}
.clock-timer.low b{animation:clock-tick 1s steps(2,end) infinite}

.clock-timer.off b{color:var(--ink-faint)}
.clock-timer.off .clock-fill{width:0}

@keyframes clock-tick{
  0%,60%{opacity:1}
  61%,100%{opacity:.55}
}

.clock-pick{
  flex:1;
  font-size:9.5px;
  letter-spacing:.14em;
  color:var(--ink-faint);
  align-self:center;
}

/* ================================================== banked commands ===== */
/* A banked pip is a Command you kept, so it is drawn as the same slot in the
   other frame: gold rather than red, and holding a ring instead of an ember.
   It still spends left to right with the rest of the row, because to the turn
   they are all just Commands. */
.cmd.banked{border-image-source:var(--ui-blue);filter:drop-shadow(0 0 9px rgba(245,212,90,.5))}

.cmd.banked:after{
  width:11px;
  height:11px;
  margin:-6px 0 0 -6px;
  background:none;
  border:3px solid #ffe487;
  box-shadow:0 0 9px rgba(245,212,90,.85);
  transform:rotate(45deg);
}

.cmd.banked.spent{border-image-source:var(--ui-disable)}
.cmd.banked.spent:after{border:0;background:rgba(255,244,208,.34);box-shadow:none}

/* AN OVERSIZED TURN HAS TO LOOK LIKE AN EVENT. Six Commands is the payoff for
   three turns of restraint and the most dangerous turn on the board; the panel
   itself lights up so it is not something you have to count to notice. */
.treasury.surge{
  background:rgba(78,53,14,.66);
  box-shadow:inset 0 0 0 2px rgba(245,212,90,.55), 0 0 22px rgba(245,212,90,.18);
}

.treasury.surge #hud-cmd-note{
  font-size:10px;
  font-weight:800;
  color:#ffe487;
  letter-spacing:.1em;
  text-shadow:0 0 10px rgba(245,212,90,.6);
}

.treasury.surge .spend-track{border-color:rgba(245,212,90,.4)}

.bank-head{margin-top:8px}

.banks{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:7px;
  margin-top:4px;
}

/* deliberately shorter than the Levy chips beside them: the bank is a number
   you watch, the Levy is a number you multiply by, and the rail has no height
   to spare for a second row that shouts as loudly as the first */
.bankchip{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:6px;
  padding:2px 9px;
  background:rgba(0,0,0,.28);
  border:1px solid rgba(255,216,119,.16);
}

.bankchip span{font-size:9.5px;letter-spacing:.1em;line-height:1.2;color:var(--ink-faint)}
.bankchip b{font-size:17px;line-height:1;color:var(--ink-dim);text-shadow:2px 2px 0 rgba(54,31,10,.9)}
.bankchip.mine.full{border-color:rgba(243,213,84,.55);background:rgba(243,213,84,.12)}
.bankchip.mine.full b{color:#ffe487}

/* THE OPPONENT'S BANK IS THE READ. It is deliberately the loudest thing in
   this row once it is filling: a bank you cannot see coming is just a number
   that ambushes you, and the whole rule exists so it can be answered. */
.bankchip.foe.filling{
  border-color:rgba(239,111,87,.5);
  background:rgba(239,111,87,.14);
}
.bankchip.foe.filling span{color:#e5b5a7}
.bankchip.foe.filling b{color:#ffa78d}
.bankchip.foe.full{
  border-color:rgba(239,111,87,.85);
  box-shadow:0 0 14px rgba(239,111,87,.28);
}

/* ================================================== bracing ============= */
/* The order list is where a queued Fortify says what it is already doing. */
.obrace{
  margin-left:7px;
  padding:1px 5px;
  font-size:9px;
  letter-spacing:.08em;
  color:#cfe4ff;
  background:rgba(124,183,255,.16);
  box-shadow:inset 0 0 0 1px rgba(124,183,255,.4);
  white-space:nowrap;
}

/* ================================================== modes and rank ====== */
.modes{display:flex;flex-direction:column;gap:6px}

.modebtn{
  display:block;
  width:100%;
  padding:6px 10px;
  text-align:left;
  font:inherit;
  font-size:11px;
  font-weight:700;
  letter-spacing:.16em;
  color:var(--ink-dim);
  background:rgba(0,0,0,.3);
  border:0;
  box-shadow:inset 0 0 0 1px rgba(255,232,179,.18);
  cursor:pointer;
}

.modebtn em{
  display:block;
  margin-top:2px;
  font-style:normal;
  font-size:9.5px;
  font-weight:400;
  letter-spacing:.04em;
  color:var(--ink-faint);
}

.modebtn:hover:not(:disabled){filter:brightness(1.12)}
.modebtn:disabled{cursor:default;opacity:.55}

/* .on is the mode being played right now; .armed is the one CREATE or JOIN
   will open. They must not look the same — one is a fact, one is a plan. */
.modebtn.on{
  color:#fff7dc;
  background:rgba(124,183,255,.16);
  box-shadow:inset 0 0 0 1px rgba(124,183,255,.6);
}

.modebtn.armed{
  color:#fff7dc;
  background:rgba(243,213,84,.12);
  box-shadow:inset 0 0 0 2px rgba(243,213,84,.6);
}

.modebtn.armed:after{
  content:" — NEXT MATCH";
  font-size:9px;
  letter-spacing:.1em;
  color:var(--gold);
}

#mode-ranked.on,#mode-ranked.armed{box-shadow:inset 0 0 0 2px rgba(239,111,87,.65)}
.pvp-cell.ranked b{color:#ffb8a8}

.rankrow{display:flex;align-items:center;gap:12px}

.rank-badge{
  flex:none;
  padding:7px 11px;
  font-size:12px;
  font-weight:800;
  letter-spacing:.16em;
  color:#2a1c08;
  background:linear-gradient(180deg,#d8c39a,#9c8763);
  text-shadow:0 1px 0 rgba(255,247,205,.4);
}

/* The five tiers, as a fire getting hotter rather than as five metals. The
   metals were renamed in phase 3 because the third of them was called Gold,
   which is also the currency: "3,400 Gold, Gold IV" in one interface reads as
   a bug in the game rather than a bug in its vocabulary. */
.rank-badge.tier-ember{background:linear-gradient(180deg,#c96a3a,#7a3a18)}
.rank-badge.tier-flame{background:linear-gradient(180deg,#f09a4a,#c25a1e)}
.rank-badge.tier-blaze{background:linear-gradient(180deg,#ffc35a,#d97a1c)}
.rank-badge.tier-molten{background:linear-gradient(180deg,#ffe487,#e0561f)}
.rank-badge.tier-obsidian{
  background:linear-gradient(180deg,#5b4a72,#221a2e);
  box-shadow:0 0 16px rgba(142,59,191,.45);
}

.rank-nums{display:flex;gap:16px}
.rank-nums div{display:flex;flex-direction:column}
.rank-nums b{font-size:20px;line-height:1;color:#fff7dc;font-variant-numeric:tabular-nums}
.rank-nums span{font-size:8.5px;letter-spacing:.16em;color:var(--ink-faint)}

.rank-delta{
  margin-top:8px;
  padding:5px 9px;
  font-size:10.5px;
  letter-spacing:.06em;
  color:var(--ink-dim);
  background:rgba(0,0,0,.3);
  box-shadow:inset 0 0 0 1px rgba(255,232,179,.2);
}

.rank-delta.up{color:#cdeaa2;box-shadow:inset 0 0 0 1px rgba(164,222,99,.5)}
.rank-delta.down{color:#ffb8a8;box-shadow:inset 0 0 0 1px rgba(239,111,87,.5)}

/* The honest line. It is not a footnote and it is not styled like one: a
   player must not be able to read the badge above without reading this. */
.pvp-note.honest{
  margin-top:9px;
  padding:7px 9px;
  color:var(--ink-dim);
  background:rgba(239,111,87,.1);
  box-shadow:inset 0 0 0 1px rgba(239,111,87,.35);
}

/* The result, then the ground it happened on, on its own line. js/main.js
   writes both into one textContent, so the break has to survive. */
#go-sub{white-space:pre-line}

.go-rank{
  margin:12px auto 0;
  max-width:520px;
  padding:9px 12px;
  white-space:pre-line;
  font-size:12px;
  line-height:1.5;
  color:var(--ink-dim);
  background:rgba(0,0,0,.3);
  box-shadow:inset 0 0 0 1px rgba(255,232,179,.2);
}

.go-rank.up{color:#cdeaa2;box-shadow:inset 0 0 0 1px rgba(164,222,99,.5)}
.go-rank.down{color:#ffb8a8;box-shadow:inset 0 0 0 1px rgba(239,111,87,.5)}

/* ================================================== the ban step ======== */
/* Ten cards, each carrying what the template actually DOES. It is a full
   overlay rather than a rail panel for one reason: striking a template out is
   a decision about the whole match, taken once, before a single order exists,
   and it deserves the room to be read rather than a dropdown to be guessed at.
   The cards are the widest thing on the page on purpose — the blurb is the
   information, the name is only the label. */
#banstep{
  position:fixed;
  inset:0;
  z-index:24;
  display:flex;
  align-items:center;
  justify-content:center;
  padding:20px;
  overflow:auto;
  background:rgba(7,12,20,.86);
  backdrop-filter:blur(4px);
}

.ban-in{
  width:min(1000px,100%);
  max-height:calc(100vh - 40px);
  display:flex;
  flex-direction:column;
  padding:18px 22px 16px;
  background:rgba(30,21,14,.94);
  box-shadow:var(--shadow);
}

.ban-kicker{font-size:9.5px;letter-spacing:.24em;color:var(--gold-deep);font-weight:800}

.ban-head h2{
  margin-top:4px;
  font-size:26px;
  letter-spacing:.16em;
  color:#fff7d0;
  text-shadow:3px 3px 0 rgba(91,51,10,.9);
}

.ban-head p{margin-top:6px;font-size:12px;color:var(--ink-dim)}

/* what the match is waiting on: this seat, the other seat, or nothing */
.ban-state{
  margin:10px 0 12px;
  padding:7px 10px;
  font-size:11px;
  letter-spacing:.05em;
  line-height:1.5;
  color:var(--ink-dim);
  background:rgba(0,0,0,.32);
  box-shadow:inset 0 0 0 1px rgba(255,232,179,.2);
}

.ban-state.waiting{color:#ffe487;box-shadow:inset 0 0 0 1px rgba(245,212,90,.5)}
.ban-state.bad{color:#ffb8a8;box-shadow:inset 0 0 0 1px rgba(239,111,87,.5)}
.ban-state.done{color:#cdeaa2;box-shadow:inset 0 0 0 1px rgba(164,222,99,.5)}

.ban-list{
  flex:1 1 auto;
  min-height:0;
  overflow-y:auto;
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(288px,1fr));
  gap:8px;
  padding-right:4px;
}

.ban-card{
  display:grid;
  grid-template-columns:1fr auto;
  gap:2px 10px;
  padding:9px 11px;
  font:inherit;
  text-align:left;
  color:var(--ink);
  cursor:pointer;
  background:rgba(255,245,214,.06);
  border:0;
  box-shadow:inset 0 0 0 1px rgba(255,232,179,.18);
}

.ban-card:hover:not(:disabled){background:rgba(255,245,214,.15)}
.ban-card .bc-name{font-size:11px;letter-spacing:.14em;font-weight:800;color:#fff7dc}
.ban-card .bc-blurb{grid-column:1/-1;font-size:10.5px;line-height:1.45;color:var(--ink-dim)}

.ban-card .bc-tag{
  align-self:start;
  padding:2px 6px;
  font-size:8.5px;
  letter-spacing:.14em;
  font-weight:800;
  color:#231508;
  background:var(--ink-faint);
}

/* a struck template is not a disabled control, it is a fact about the match */
.ban-card.struck{cursor:default;background:rgba(239,111,87,.1);box-shadow:inset 0 0 0 1px rgba(239,111,87,.5)}
.ban-card.struck .bc-name{color:#ffb8a8;text-decoration:line-through}
.ban-card.struck .bc-tag{background:var(--red);color:#2a1008}
.ban-card.mine.struck .bc-tag{background:var(--gold);color:#3a2708}
.ban-card:disabled{filter:none}

.ban-foot{
  display:flex;
  align-items:center;
  gap:14px;
  flex-wrap:wrap;
  margin-top:12px;
  padding-top:12px;
  border-top:1px solid rgba(255,232,179,.18);
}

.ban-note{flex:1;min-width:220px;font-size:10.5px;line-height:1.5;color:var(--ink-faint)}
.ban-actions{display:flex;gap:10px;flex-wrap:wrap}

/* ---- what is struck, wherever the player happens to be looking ---------- */
/* The Arbiter panel keeps the list for the whole match, and the modifier row
   carries it beside ASHFALL and STORM — a ban is a standing fact about the
   world, so it belongs where the other standing facts are. */
.banlist-live{
  margin:0 0 8px;
  padding:7px 9px;
  font-size:10.5px;
  line-height:1.5;
  letter-spacing:.04em;
  white-space:pre-wrap;
  word-break:break-word;
  color:var(--ink-dim);
  background:rgba(255,245,214,.05);
  box-shadow:inset 0 0 0 1px rgba(255,232,179,.14);
}

.banlist-live.bad{color:#ffb8a8;background:rgba(239,111,87,.1);box-shadow:inset 0 0 0 1px rgba(239,111,87,.4)}
.banlist-live.done{color:#cdeaa2;box-shadow:inset 0 0 0 1px rgba(164,222,99,.45)}

.banchip{
  display:inline-block;
  margin:3px 5px 0 0;
  padding:3px 7px;
  font-size:9.5px;
  letter-spacing:.12em;
  font-weight:800;
  color:#2a1008;
  background:var(--red);
}

.banchip em{font-style:normal;font-weight:400;opacity:.8}

.mod.struck{background:rgba(239,111,87,.16);color:#ffb8a8}

/* ================================================== the replay ========== */
.replay-label{
  display:block;
  margin:8px 0 4px;
  font-size:8.5px;
  letter-spacing:.16em;
  color:var(--ink-faint);
}

.replay-box{
  display:block;
  width:100%;
  height:66px;
  padding:6px 8px;
  resize:vertical;
  font-family:"Consolas","Courier New",monospace;
  font-size:10px;
  line-height:1.4;
  word-break:break-all;
  color:#cfe4ff;
  background:rgba(0,0,0,.42);
  border:0;
  box-shadow:inset 0 0 0 1px rgba(255,232,179,.24);
}

.replay-box::placeholder{color:var(--ink-faint);font-family:inherit}

/* ================================================== playback ============ */
/* A replay on the board has to be unmistakable from the board itself, because
   the one thing a player must never wonder is whether the click they just made
   counted. Three signals, none of them subtle: this bar above the board, a
   blue ring around the canvas, and the whole order side of the rail dimmed and
   made inert. Blue throughout — the same ink the VERSUS bar uses for "this is
   not the solo game you were in" — and never the gold or red the live match
   spends on its own state. */
/* HEIGHT IS THE CONSTRAINT HERE, not width: this sits directly above the board
   in a column that is already full, so every pixel it takes is a pixel off the
   thing the player came to watch. Hence one control row and a two-column body
   rather than five stacked blocks. */
#playback{
  flex:none;
  padding:7px 10px 8px;
  background:linear-gradient(90deg, rgba(124,183,255,.20), rgba(255,245,214,.05));
  box-shadow:inset 0 0 0 2px rgba(124,183,255,.75);
}

.pb-bar{
  display:flex;
  align-items:center;
  gap:8px;
  flex-wrap:wrap;
}

.pb-kicker{
  font-size:10px;
  letter-spacing:.2em;
  color:#cfe4ff;
}

.pb-body{
  display:flex;
  align-items:flex-start;
  gap:14px;
  margin-top:7px;
}

.pb-side{
  flex:none;
  width:290px;
  max-width:38%;
}

#pb-source{
  font-size:10px;
  letter-spacing:.06em;
  color:var(--ink-faint);
  overflow:hidden;
  white-space:nowrap;
  text-overflow:ellipsis;
}

.mini.pb-step{min-width:34px;text-align:center}

/* the scrubber is the whole point of playback, so it gets the room */
.pb-track{
  position:relative;
  flex:1;
  min-width:0;
  display:flex;
  align-items:center;
}

.pb-scrub{
  flex:1;
  min-width:0;
  height:22px;
  accent-color:#7cb7ff;
  cursor:pointer;
}

/* WHERE THE MATCH TURNED, drawn ON the scrubber. The alternative was a second
   timeline under it, which costs a row of height for information the scrubber
   is already the map of. These sit inside the track, take no height of their
   own and never take a click: the buttons in the row below do the jumping, so
   a 3px tick never has to be a hit target. aria-hidden for the same reason —
   the marks are orientation for the eye, and the same turns are named in words
   on the buttons, which is what a screen reader gets to use. */
.pb-marks{
  position:absolute;
  left:0; right:0; bottom:0;
  height:6px;
  pointer-events:none;
}

/* A range thumb travels between half a thumb's width from each end, so a tick
   at 0% would sit left of the position it names. The 9px/18px inset is that
   half-thumb, and it keeps a mark under the thumb that lands on it. */
.pb-mark{
  position:absolute;
  bottom:0;
  width:3px;
  height:6px;
  margin-left:-1.5px;
  border-radius:1px;
  background:var(--ink-faint);
}

.pb-mark.swing{background:#ffd27c}
.pb-mark.lead{background:#9ee7ff}
.pb-mark.decided{background:var(--red);width:4px;height:8px;margin-left:-2px}

/* ---- the reading -------------------------------------------------------
   ONE row, and a row that cannot change height. Everything in it is a single
   line that clips rather than wraps, because this rewrites on every step of
   the scrubber: a reading two lines long on turn 9 and three long on turn 10
   would move the board down and up under a player who is aiming at it, which
   is the reflow bug that once sent most of a turn's clicks to the wrong tile.
   Long readings lose their tail to an ellipsis; the numbers behind them are in
   the report below, and every one of them is checkable against the board. */
.pb-read{
  display:flex;
  align-items:center;
  gap:8px;
  margin-top:6px;
  height:20px;
  overflow:hidden;
}

.pb-read-tag{
  flex:none;
  font-size:10px;
  letter-spacing:.12em;
  color:#cfe4ff;
  white-space:nowrap;
}

/* the same three inks as the ticks on the scrubber, so a mark and the words
   beside it are visibly the same claim */
.pb-read-tag.swing{color:#ffd27c}
.pb-read-tag.lead{color:#9ee7ff}
.pb-read-tag.decided{color:var(--red)}

.pb-read-line{
  flex:1;
  min-width:0;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
  font-size:10.5px;
  line-height:1.4;
  color:var(--ink-dim);
}

/* The way out of scrubbing blind: the turns worth stopping on, named, in a
   fixed order, one press away. They never shrink — the reading beside them
   gives up its tail first, because a chip that has to be read to be pressed is
   worth more than the last clause of a sentence. */
.pb-jumps{
  flex:none;
  display:flex;
  align-items:center;
  gap:4px;
}

.pb-jump{
  flex:none;
  font-family:inherit;
  font-size:9px;
  letter-spacing:.08em;
  line-height:1;
  padding:4px 6px;
  color:#dfeaff;
  background:rgba(124,183,255,.14);
  border:1px solid rgba(124,183,255,.45);
  border-radius:2px;
  cursor:pointer;
  white-space:nowrap;
  font-variant-numeric:tabular-nums;
}

.pb-jump:hover{background:rgba(124,183,255,.28)}
.pb-jump.on{background:rgba(124,183,255,.42);color:#fff}
.pb-jump.swing{border-color:rgba(255,210,124,.55);color:#ffe6b4}
.pb-jump.lead{border-color:rgba(158,231,255,.6);color:#cdf2ff}
.pb-jump.decided{border-color:rgba(214,106,79,.75);color:#ffc9b6}

.pb-where{
  flex:none;
  min-width:104px;
  text-align:right;
  font-size:11px;
  letter-spacing:.1em;
  color:#fff4d0;
  font-variant-numeric:tabular-nums;
}

/* The turn's own report. `columns` with a width AND a count is what keeps it
   short without a media query: two columns while there is room for two, one
   when the board column narrows, and never three. */
.pb-frame{
  flex:1;
  min-width:0;
  padding:6px 9px;
  columns:250px 2;
  column-gap:16px;
  word-break:break-word;
  font-size:10.5px;
  line-height:1.5;
  color:var(--ink-dim);
  background:rgba(0,0,0,.32);
  box-shadow:inset 0 0 0 1px rgba(124,183,255,.35);
}

.pb-line{break-inside:avoid;margin-bottom:1px}

/* the honest line, and it is not a footnote: that the boards are recomputed
   rather than stored is the reason this scrubber can be exact at all */
.pb-honest{
  margin-top:5px;
  font-size:9.5px;
  line-height:1.45;
  color:var(--ink-faint);
}

/* WHILE WATCHING. The board keeps working — hover, tooltips, 2D and 3D all
   read the frame — but nothing that gives an order does, so the controls that
   give orders stop looking available. pointer-events is the honest half: a
   dimmed button that still depressed would be worse than one that never
   answered. */
#app.watching #canvas-wrap{
  box-shadow:var(--shadow), inset 0 0 0 3px rgba(124,183,255,.8);
}

#app.watching .tools,
#app.watching .treasury,
#app.watching #legal-note,
#app.watching #orderlist,
#app.watching #btn-end{
  opacity:.42;
  filter:grayscale(.6);
  pointer-events:none;
}

/* The board is what is being watched, and js/render.js will not draw a square
   under 24px: below about 284px of canvas the ring starts being cut off rather
   than shrinking. So while playback is up the feed gives back half its height
   — it still carries that turn's narration and it still scrolls — because a
   clipped board is worth less than two extra lines of text. */
#app.watching #feed{flex:0 0 84px}

/* And on a short window there is not enough height for both the turn's report
   and a ring you can see. The board wins: the report collapses and the bar
   above it keeps the scrubber, the turn and the way out. Nothing is lost that
   is not somewhere else — the standings are in the top bar, the Arbiter's call
   is in the feed, the line about the boards being recomputed rather than stored
   is in the replay panel that offered the playback, and the reading row below
   still says what this turn did.

   THE BREAKPOINT USED TO BE 820px AND THAT WAS TOO LATE. Measured in Edge at
   1500 wide, watching, with the report shown: the canvas comes out at roughly
   the viewport height minus 695px, and the ring needs 240 of it (ten rows at
   the 24px floor js/render.js will not go under). That puts the real limit at
   about 935px of viewport — so every window between 821 and 935, which is most
   of a maximised browser on a 1080p screen, was quietly cutting the bottom off
   the ring rather than collapsing the report the rule was written to collapse.
   960px is that limit with room for the control bar to wrap on a narrow window.
   The reading row this pass added made it worse by its own twenty-six pixels;
   it did not cause it, and the number above is what the browser actually
   measured rather than what the old comment assumed.

   The reading row STAYS through all of it. It is twenty pixels, and unlike
   everything in the body it is the one thing that exists nowhere else: the
   standings are in the top bar, but which turns the match hinged on are only
   ever said here. A short window is a reason to drop a restatement, not the
   answer to the question the player opened the replay to ask. */
@media (max-height:960px){
  #playback .pb-body{display:none}
}

/* Shorter still, and the feed gives up another two lines. Even with the report
   collapsed, a viewport under about 730px left the ring short of its ten rows —
   it did before this pass too — and between a narration that still scrolls and
   a board that does not fit, the board wins again: the same trade as the rule
   above it, one size down. Measured in Edge: the ring is now whole from 720px
   of viewport up, and at every height below that it is cut LESS than the build
   before this pass cut it. */
@media (max-height:820px){
  #app.watching #feed{flex:0 0 44px}
}

/* ================================================== reduced motion ====== */
/* last in the file so it wins. kills the intro fade-out, the toast entrance
   and every bar/press transition. the canvas fx pulse and the 3D board's idle
   life are handled by matchMedia inside js/render.js and js/render3d.js,
   which a media query cannot reach. */
@media (prefers-reduced-motion: reduce){
  *,*:before,*:after{
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    animation-delay:0ms !important;
    transition-duration:.001ms !important;
    transition-delay:0ms !important;
    scroll-behavior:auto !important;
  }

  #intro.leaving{transition:none;opacity:0}
  .toast-in{animation:none;opacity:1;transform:none}
  .tab:active,.btn:active,.mini:active,.tool:active,.vbtn:active:not(:disabled){transform:none}
  .bp-fill,.spend-fill,.clock-fill{transition:none}
  /* the ten-second blink is the one piece of urgency that is pure motion, so
     it goes; the colour change beside it carries the same warning */
  .clock-timer.low b{animation:none;opacity:1}
}
