/* conquest — landscape first, because forty-two countries on a phone is only
 * possible sideways.
 *
 * TWO THEMES, AND LIGHT IS THE BASE, matching the rest of the house. The
 * palette below is the light one on bare :root; dark is a token swap in two
 * guarded blocks — the media query for "system", the attribute for a choice
 * the user made. Nothing outside those blocks names a literal colour, so a
 * rule written once is right in both. The duplication between the two dark
 * blocks is unavoidable: a media query cannot be OR-ed with an attribute
 * selector.
 *
 * THE PLAYER COLOURS ARE THE EXCEPTION, exactly as wizard's card faces and
 * gems' jewels are. Blue is blue in both themes, because a player's colour is
 * the one thing on this screen that must never look different on two phones
 * at the same table. They live in map.js and are painted as inline styles;
 * the OCEAN and the parchment change with the theme, the armies do not.
 *
 * Insets go on PADDING, never on layout, and every one carries a 0px
 * fallback — `calc(6px + env(...))` with no fallback is an invalid length.
 */

:root {
  /* THE OCEAN IS THE GROUND OF THE WHOLE APP, not only the board. The lobby
     and the menu were the house's flat greys with white cards, and the
     board was a dark sea with glass floating over it; the person this was
     built for liked the second and asked for it everywhere. So --bg IS the
     ocean, and every card, sheet, prompt and button is the same glass as
     the floating controls. */
  --bg:       #cfe0ea;
  --panel:    #ffffff;
  --panel-2:  #f6f6f9;
  --glass:    rgba(255, 255, 255, .78);
  --glass-2:  rgba(255, 255, 255, .55);
  --line:     rgba(60, 60, 67, .16);
  --line-2:   rgba(60, 60, 67, .08);
  --ink:      #000000;
  --ink-2:    #3c3c43;
  --dim:      #8a8a8e;
  --dim-2:    #b0b0b6;
  --pill:     #efeff0;
  --good:     #1a9e4b;
  --warn:     #b8730a;
  --bad:      #e5322b;
  --link:     #007aff;
  --shadow:   0 1px 2px rgba(0,0,0,.04), 0 6px 20px rgba(0,0,0,.05);

  /* ── THE SEA AND THE LAND'S EDGE ───────────────────────────────────────
   * The map's colours that are NOT a player's. A pale sea in the light theme,
   * a deep one in the dark; the sea links are ink on it. A country's fill is
   * always its owner's colour, so these two are the whole of the map's
   * theme-dependence. */
  --ocean:     #cfe0ea;
  --ocean-ink: rgba(20, 40, 60, .45);
  --land-edge: rgba(0, 0, 0, .55);
  --badge-ink: #ffffff;

  /* ── GOLD MEANS YOU ─────────────────────────────────────────────────────
   * wizard's rule, kept: one accent, reserved absolutely for the current
   * user. Here it rings your selected country and the countries it may act
   * on, and it is the colour of the action button when the table is waiting
   * for you. It appears nowhere else. */
  --gold:      #d9a520;
  --gold-lit:  #ffd45c;

  --r-card: 18px;
  --r-mid:  14px;
  --r-sm:   10px;

  /* THE INSETS, AS VARIABLES, because when the app is drawn sideways on an
     upright phone the phone's TOP inset is the app's LEFT. See the
     orientation block at the end of this file. */
  --sat: env(safe-area-inset-top, 0px);
  --sab: env(safe-area-inset-bottom, 0px);
  --sal: env(safe-area-inset-left, 0px);
  --sar: env(safe-area-inset-right, 0px);

  --font: -apple-system, BlinkMacSystemFont, system-ui, "SF Pro Text",
          "Helvetica Neue", sans-serif;
  --font-num: ui-rounded, -apple-system, "SF Pro Rounded", system-ui, sans-serif;

}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:       #10202c;
    --panel:    #1c1c1e;
    --panel-2:  #2c2c2e;
    --glass:    rgba(28, 28, 30, .86);
    --glass-2:  rgba(44, 44, 46, .75);
    --line:     rgba(84, 84, 88, .5);
    --line-2:   rgba(84, 84, 88, .28);
    --ink:      #ffffff;
    --ink-2:    #ebebf5;
    --dim:      #8e8e93;
    --dim-2:    #636366;
    --pill:     #2c2c2e;
    --good:     #30d158;
    --warn:     #ffd60a;
    --bad:      #ff453a;
    --link:     #0a84ff;
    --shadow:   0 1px 2px rgba(0,0,0,.5), 0 6px 20px rgba(0,0,0,.4);
    --ocean:     #10202c;
    --ocean-ink: rgba(170, 200, 225, .45);
    --land-edge: rgba(0, 0, 0, .7);
    --gold:      #e8c05a;
    --gold-lit:  #ffd77a;
  }
}
:root[data-theme="dark"] {
  --bg:       #10202c;
  --panel:    #1c1c1e;
  --panel-2:  #2c2c2e;
  --glass:    rgba(28, 28, 30, .86);
  --glass-2:  rgba(44, 44, 46, .75);
  --line:     rgba(84, 84, 88, .5);
  --line-2:   rgba(84, 84, 88, .28);
  --ink:      #ffffff;
  --ink-2:    #ebebf5;
  --dim:      #8e8e93;
  --dim-2:    #636366;
  --pill:     #2c2c2e;
  --good:     #30d158;
  --warn:     #ffd60a;
  --bad:      #ff453a;
  --link:     #0a84ff;
  --shadow:   0 1px 2px rgba(0,0,0,.5), 0 6px 20px rgba(0,0,0,.4);
  --ocean:     #10202c;
  --ocean-ink: rgba(170, 200, 225, .45);
  --land-edge: rgba(0, 0, 0, .7);
  --gold:      #e8c05a;
  --gold-lit:  #ffd77a;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
/* A GAME, NOT A PAGE: no double-tap zoom, no text selection, no long-press
   callout. `touch-action: manipulation` is what actually stops the double-tap
   zoom on iOS (the viewport meta alone does not); the inputs keep selection
   so a table name can still be edited. */
html { touch-action: manipulation; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
input, textarea { -webkit-user-select: text; user-select: text; }

html, body {
  margin: 0; padding: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}
body { min-height: 100dvh; }

.screen { display: flex; flex-direction: column; min-height: 100dvh; position: relative; }
.backdrop { position: fixed; inset: 0; z-index: 0; opacity: .22; pointer-events: none; display: flex; align-items: center; justify-content: center; }
.backdrop .world { width: 100%; height: 100%; }
.backdrop .country .shape { fill: #6b7a86; }
.backdrop .borders .edge { stroke: rgba(0,0,0,.3); stroke-width: 2.5; }
.backdrop .borders .line { stroke: rgba(255,255,255,.4) !important; stroke-width: 1.2; }
.backdrop .sea line { stroke: rgba(255,255,255,.35); }
#screen-lobby > .bar, #screen-lobby > .wrap { position: relative; z-index: 1; }
[hidden] { display: none !important; }

/* ── the bar (lobby only) ─────────────────────────────────────────────────
 * iOS 26 draws a blur behind the status bar that reaches PAST the safe-area
 * inset; ~20px more clears it. */
.bar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; gap: 10px;
  padding: calc(12px + var(--sat)) calc(14px + var(--sar)) 10px calc(14px + var(--sal));
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom: .5px solid var(--line-2);
}
.bar h1 { font-size: 20px; font-weight: 700; margin: 0; letter-spacing: -.02em; }
.bar-title { flex: 1; min-width: 0; }
.bar-title h1 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 18px; margin: 0; }
.bar-title .sub, .bar .sub { display: block; font-size: 12px; color: var(--dim); }
.bar > h1 { flex: 1; }

.icon {
  appearance: none; background: var(--glass); color: var(--ink); backdrop-filter: blur(10px);
  border: .5px solid var(--line); box-shadow: var(--shadow);
  width: 40px; height: 40px; border-radius: 20px; font-size: 17px;
  display: grid; place-items: center; cursor: pointer; flex: none; padding: 0;
}
.icon .glyph { line-height: 0; }
.icon .glyph svg { width: 22px; height: 22px; display: block; }

.wrap {
  padding: 14px calc(14px + var(--sar)) calc(24px + var(--sab)) calc(14px + var(--sal));
  max-width: 620px; width: 100%; margin: 0 auto;
}

/* ── cards, panels, forms ─────────────────────────────────────────────── */
/* THE GLASS. One recipe for every panel in the app, the same as the
   controls that float over the board: translucent, blurred, a hairline
   edge, a soft shadow. */
.card, .room, .overlay .panel, .prompt, .seat, .pcard, .talk, .status, .score, .banner, .signin-card {
  background: var(--glass); backdrop-filter: blur(12px) saturate(140%);
  border: .5px solid var(--line); box-shadow: var(--shadow);
}
.card {
  border-radius: var(--r-card);
  padding: 16px; margin: 0 0 14px;
}
.card h2, .card h3 { margin: 0 0 12px; font-size: 16px; font-weight: 650; }
.section { font-size: 13px; text-transform: uppercase; letter-spacing: .06em;
           color: var(--dim); margin: 22px 4px 10px; font-weight: 600; }

.field { display: block; margin: 0 0 12px; }
.field > span { display: block; font-size: 13px; color: var(--dim); margin-bottom: 6px; }
/* The hold's beats, as sliders (the settings sheet). */
input[type=range] { width: 100%; accent-color: var(--gold); margin: 4px 0 0; }
.field .v { font-family: var(--font-num); font-weight: 800; color: var(--ink); }
input[type=text], select {
  width: 100%; font: inherit; color: var(--ink);
  background: var(--panel-2); border: .5px solid var(--line);
  border-radius: var(--r-mid); padding: 11px 12px;
}
.code-input { text-transform: uppercase; letter-spacing: .32em; font-weight: 700;
              font-family: var(--font-num); text-align: center; }
.check { display: flex; align-items: flex-start; gap: 10px; margin: 12px 0;
         font-size: 14px; color: var(--ink-2); }
.check input { margin-top: 2px; }
.check.grow { flex: 1; margin: 8px 0; }
button.small { padding: 7px 14px; font-size: 13px; }

button {
  appearance: none; font: inherit; font-weight: 600; cursor: pointer;
  border: .5px solid var(--line); background: var(--glass-2); color: var(--ink);
  backdrop-filter: blur(10px); box-shadow: var(--shadow);
  border-radius: var(--r-mid); padding: 11px 16px;
}
/* GOLD MEANS YOU, and the one thing to do next is yours: the primary button
   is the action button's gold. */
button.primary { background: var(--gold); color: #1a1200; border-color: transparent; }
button.big { width: 100%; padding: 14px; font-size: 17px; }
button.quiet { background: transparent; border-color: transparent; box-shadow: none; backdrop-filter: none;
               color: var(--link); padding: 10px 0; }
button:disabled { opacity: .4; cursor: default; }
/* A CONTROL THAT HAS BEEN TAPPED SAYS SO BEFORE THE SERVER DOES. wizard's
   press() and the bug that made it, carried verbatim. */
button.working { opacity: .55; cursor: default; position: relative; overflow: hidden; }
button.working::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: currentColor; opacity: .5;
  animation: work-slide 900ms linear infinite;
}
@keyframes work-slide { 0% { transform: translate3d(-100%,0,0); } 100% { transform: translate3d(100%,0,0); } }
button.danger { color: var(--bad); }

.row { display: flex; gap: 10px; align-items: center; }
.row > input { flex: 1; }
.wrap-row { flex-wrap: wrap; }
.wrap-row > button { flex: 1 1 auto; }
.fine { font-size: 12.5px; color: var(--dim); line-height: 1.45; margin: 8px 0 0; }
.fine a { color: var(--link); }
.fine.version { text-align: center; margin-top: 18px; opacity: .7; font-family: var(--font-num); }

/* ── the lobby list ──────────────────────────────────────────────────── */
.list { display: flex; flex-direction: column; gap: 10px; }
.list .empty { color: var(--dim); font-size: 14px; padding: 18px 4px; text-align: center; }
.room {
  display: flex; align-items: center; gap: 12px;
  border-radius: var(--r-mid); padding: 12px 14px; cursor: pointer;
}
.room .who { flex: 1; min-width: 0; }
.room .rname { font-weight: 650; }
.room .rsub { font-size: 12.5px; color: var(--dim); margin-top: 2px; }
.room .code { font-family: var(--font-num); font-weight: 700; letter-spacing: .14em;
              color: var(--dim); }
.list.small .room { padding: 9px 12px; cursor: default; }

/* ── sign in ─────────────────────────────────────────────────────────── */
#gate { align-items: center; justify-content: center; padding: 24px; }
.signin-card { text-align: center; max-width: 340px; width: 100%; }
.signin-card { padding: 22px; border-radius: var(--r-card); }
.signin-card h1 { font-size: 30px; margin: 10px 0 6px; letter-spacing: -.03em; }
.signin-card .lede { color: var(--ink-2); margin: 0 0 22px; line-height: 1.45; }
.brand { width: 96px; height: 96px; margin: 0 auto; }
.brand svg { width: 100%; height: 100%; }

/* ── THE BOARD ────────────────────────────────────────────────────────── */
.board {
  position: fixed; inset: 0; min-height: 0;
  flex-direction: row;
  background: var(--ocean);
  /* The notch sits on one side or the other sideways; keep the land out
     from under it on both. */
  padding-left: var(--sal); padding-right: var(--sar);
}
.mapwrap {
  flex: 1; min-width: 0; min-height: 0; position: relative;
  display: flex; align-items: center; justify-content: center;
  padding: 2px;
}
.world { width: 100%; height: 100%; display: block; touch-action: manipulation; }
.ocean { fill: var(--ocean); }
.sea line { stroke: var(--ocean-ink); stroke-width: 2; stroke-dasharray: 5 4; }
.sea line.wrap { stroke-dasharray: 5 4; }

/* A COUNTRY IS ITS OWNER'S COLOUR. The fill is set inline from map.js's
   PLAYER_COLOURS. The borders are a layer of their own (map.js): a dark edge
   with the continent's colour on top, so the colour reads on any fill,
   including its own. */
.country .shape {
  fill: #9a9a9a; stroke: none;
  transition: fill 260ms ease, opacity 200ms ease;
  cursor: pointer;
}
.borders path { fill: none; pointer-events: none; stroke-linejoin: round; stroke-linecap: round; }
.borders .edge { stroke: rgba(0, 0, 0, .75); stroke-width: 4.2; }
.borders .line { stroke-width: 2; }
.badge .halo { display: none; fill: var(--gold); opacity: .38; }
.badge .k { transition: transform 180ms ease; }
.badge .ring { fill: none; stroke: transparent; stroke-width: 3; }
.badge .disc { fill: #333; stroke: rgba(255,255,255,.55); stroke-width: 1.5; transition: stroke 180ms ease; }
.badge .count {
  font-family: var(--font-num); font-weight: 700; font-size: 16px;
  fill: var(--badge-ink); pointer-events: none; user-select: none;
}
.badge .name { font-size: 10px; fill: rgba(255,255,255,.85); pointer-events: none;
               font-weight: 600; paint-order: stroke fill; stroke: rgba(0,0,0,.45); stroke-width: 2px;
               display: none; }
.world.named .badge .name { display: block; }
/* THE BADGE IS A TAP TARGET, not just a label. Iceland, Japan and Madagascar
   are smaller than the disc that names them; a tap on the disc must land on
   the country, not on the sea behind it. And the badges are a layer ABOVE
   every country (map.js), so a count is never under a neighbour's border. */
.badge { pointer-events: all; cursor: pointer; }
.world.nogame .badges { display: none; }
/* NO SPARE ARMIES, NO BADGE — AND NO HALO EITHER. The garrison — the one
   army that can never leave — is not drawn; the country's colour says it is
   held. The group stays in the DOM (it is still the tap target that names
   the country, and the haptic switch sits on it), only the disc and count
   go. The first version kept the gold ring and halo on an empty badge that
   could be tapped, so the target stayed visible; on a board at the top of a
   turn that was a scatter of gold rings round nothing (2026-09-13), and the
   country itself already stands forward — full-strength fill and outline
   among dimmed neighbours — so the ring said nothing the shape did not. */
.badge.empty .disc, .badge.empty .ring, .badge.empty .count, .badge.empty .halo { display: none !important; }

/* What you may tap, and what you have tapped. The ring is gold because it is
   yours; a country you cannot act on right now is drawn a step quieter so the
   ones you can stand forward — wizard's "playable cards stand up" rule, on a
   map.

   THREE THINGS STAND A TAPPABLE COUNTRY FORWARD, because one was not enough
   (2026-09-12, from a screenshot of a place phase): Egypt's badge, gold ring
   and all, was one disc among nine in the densest corner of the board, and
   its outline was the same continent colour as its neighbours'. So: the
   badge grows a gold halo and a size; everything else drops further back —
   the badges to half, and the BORDER LINES too, which were full-strength on
   every country and made the map equally loud everywhere; and a tappable
   country's own outline stays at full strength, so it is the one drawn in
   colour among quieter neighbours. The dark edges are left alone: they are
   what keeps a dimmed country from bleeding into the next. Enemy counts are
   still legible at half, which matters in the attack phase where the ones
   you are weighing up are not yet the ones you may tap. */
.world.acting .country:not(.can):not(.sel) .shape { opacity: .55; }
.world.acting .badge:not(.can):not(.sel) { opacity: .5; }
.world.acting .lines .line:not(.can):not(.sel) { opacity: .35; }
.world.acting .lines .line.yours:not(.can):not(.sel) { opacity: .6; }
.badge.can .halo, .badge.sel .halo { display: block; }
.badge.can .k { transform: scale(1.12); }
.badge.can .ring { stroke: var(--gold); }
.badge.can .disc { stroke: rgba(255,255,255,.9); }
.country.can .shape { filter: saturate(1.15); }
.badge.sel .k { transform: scale(1.18); }
.badge.sel .ring { stroke: var(--gold-lit); stroke-width: 4; }
.badge.sel .halo { opacity: .5; fill: var(--gold-lit); }
.borders .line.sel { stroke: var(--gold-lit) !important; stroke-width: 3.2; }
/* THE FLASHES ANIMATE THE FILL, NOT A FILTER. A CSS `filter` animation on an
   SVG path was the first version, and Safari drew nothing for it while
   Chrome drew it fine — so the continent flash "did not exist" on the phone
   this is for. A fill animation outranks the inline fill (animations beat
   inline styles in the cascade) and every browser honours it. */
.country.hit .shape { animation: hit 520ms ease-out; }
.country.flash .shape { animation: flash 900ms ease-out; }
/* No 100% keyframe on purpose: the end of the animation is the element's
   own computed fill — its owner's colour — whatever that is at the time. */
@keyframes flash { 0%, 35% { fill: #fff; } }
@keyframes hit { 0% { fill: #fff; } }
.badge.bumped .disc { animation: disc-bump 380ms cubic-bezier(.22,.92,.28,1.02); }
@keyframes disc-bump { 0% { transform: scale(1); } 40% { transform: scale(1.35); } 100% { transform: scale(1); } }
.badge.turnowner .disc { stroke: rgba(255,255,255,.9); stroke-width: 2.5; }

/* ── THE FLOATING CONTROLS ────────────────────────────────────────────── */
.fab {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  background: var(--glass); backdrop-filter: blur(12px) saturate(140%);
  border: .5px solid var(--line); border-radius: 16px; color: var(--ink); padding: 6px 4px;
  box-shadow: var(--shadow); min-width: 64px; min-height: 52px;
}
.fab .glyph { line-height: 0; display: block; }
.fab .glyph svg { width: 24px; height: 24px; display: block; }
.fab .lbl { font-size: 10px; color: var(--dim); font-weight: 600; white-space: nowrap; }
.fab.corner {
  position: absolute; z-index: 4; min-width: 44px; min-height: 44px; border-radius: 22px; padding: 0;
  /* Flush with the notch-side inset (a hair into it, which is still screen),
     in tandem with the scoreboard below it. 8px on a phone with no notch. */
  left: max(8px, calc(var(--sal) - 2px)); top: calc(8px + var(--sat));
}
/* The column in the eastern Pacific: the action button over the cards, in the
   ocean east of Japan and Australia, below Kamchatka's tail. */
.pacific {
  position: absolute; z-index: 4; display: flex; flex-direction: column; gap: 8px; align-items: stretch;
  right: calc(8px + var(--sar)); top: 36%;
  width: 76px;
}
.fab.action { min-height: 66px; }
.fab.action .big-num { font-family: var(--font-num); font-weight: 800; font-size: 22px; line-height: 1; }
.fab.action .big-num:empty { display: none; }
.fab.action .lbl { color: var(--ink-2); }
.fab.action.mine { background: var(--gold); color: #1a1200; border-color: transparent; }
.fab.action.mine .lbl { color: #1a1200; }
.fab.action.mine:not(:disabled) { animation: breathe 2.6s ease-in-out infinite; }
@keyframes breathe { 0%, 100% { box-shadow: 0 0 0 0 rgba(217,165,32,.0); } 50% { box-shadow: 0 0 0 6px rgba(217,165,32,.3); } }
.fab.action:disabled { opacity: .85; }
/* The count sits as a small badge at the corner of the hand of cards, not on
   top of it — over the glyph it was unreadable against the drawing. */
.cards-glyph { position: relative; width: 26px; height: 26px; }
.cards-glyph b {
  position: absolute; right: -9px; top: -6px; min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 9px; background: var(--gold); color: #1a1200; display: grid; place-items: center;
  font-family: var(--font-num); font-size: 11px; font-weight: 800; line-height: 1; pointer-events: none;
  box-shadow: 0 1px 2px rgba(0,0,0,.35);
}
.cards-glyph b:empty, .cards-glyph b.zero { display: none; }
/* A set in hand: the badge stays gold and BREATHES; the pulse is the whole
   signal. Must trade: the label says so. And the count pops when it changes,
   as every other number on the board does. */
.fab.has-set .cards-glyph b { animation: badge-breathe 1.8s ease-in-out infinite; }
.fab.has-set .lbl { font-weight: 700; }
.fab.must .lbl { color: var(--bad); font-weight: 700; }
.cards-glyph b.bumped { animation: disc-bump 380ms cubic-bezier(.22,.92,.28,1.02); }
.fab.has-set .cards-glyph b.bumped { animation: disc-bump 380ms cubic-bezier(.22,.92,.28,1.02), badge-breathe 1.8s ease-in-out 380ms infinite; }
@keyframes badge-breathe { 0%, 100% { box-shadow: 0 0 0 0 rgba(217,165,32,.0); } 50% { box-shadow: 0 0 0 5px rgba(217,165,32,.4); } }

/* ── OVERLAYS ON THE MAP ──────────────────────────────────────────────── */
.score {
  /* FLUSH WITH THE MENU BUTTON'S LEFT EDGE — the same 8px past the inset —
     so the corner reads as one column. It went a third of the way into the
     inset for a version and looked adrift. */
  /* UP OFF THE BOTTOM LINE, into the ocean west of Central America: the
     status line — and the dice, which take its place — is wide, and at the
     bottom the two overlapped. Sixty-odd pixels up is clear water between
     Central America's tip and Peru's coast, and the same idea as the action
     column sitting in the ocean on the right. */
  /* ANCHORED AT THE TOP, NOT THE BOTTOM. Anchored at the bottom it grew
     UPWARD with the table, and six rows put its top over Mexico. Anchored
     at 60% it grows down into the open Pacific west of Peru, and six rows
     still clear the home-indicator inset on a 390px-tall phone. */
  position: absolute; left: max(8px, calc(var(--sal) - 2px)); top: 60%;
  border-radius: 14px;
  font-family: var(--font-num); font-size: 12px; font-weight: 600;
  padding: 5px 7px; cursor: pointer; z-index: 3;
  max-width: 46%;
}
.score table { border-collapse: collapse; }
.score th { font-size: 10px; color: var(--dim); font-weight: 600; padding: 0 5px 2px; text-align: right; }
.score th:first-child { text-align: left; }
.score td { padding: 1px 5px; text-align: right; white-space: nowrap; }   /* colour comes from the row: the player's */
.score td.nm { text-align: left; max-width: 88px; overflow: hidden; text-overflow: ellipsis; font-family: var(--font); }
.score tr.dead td { opacity: .35; text-decoration: line-through; }
/* WHOSE TURN: the row is lit and carries a pointer, in both states. */
.score tr.turn td { background: var(--turn-tint, color-mix(in srgb, var(--ink) 12%, transparent)); }
.score tr.turn td.nm { font-weight: 800; }
.score tr.turn td:first-child { border-radius: 5px 0 0 5px; }
.score td.nm .sw { margin-right: 5px; }
.score tr.turn td:last-child { border-radius: 0 5px 5px 0; }
.score th.ico svg { width: 13px; height: 13px; display: inline-block; vertical-align: -2px; opacity: .8; }
.score:not(.folded) td { padding: 1px 4px; }
.score .sw { display: inline-block; width: 10px; height: 10px; border-radius: 3px; vertical-align: -1px; margin-right: 5px; }
.score.folded table { display: none; }
.score.folded .swatches { display: flex; gap: 7px; align-items: center; }
.score:not(.folded) .swatches { display: none; }
.score .mini { display: inline-flex; align-items: center; font-size: 12px; }
.score .mini .sw { margin-right: 3px; }
.score .mini.turn { font-weight: 800; background: color-mix(in srgb, var(--ink) 12%, transparent); border-radius: 5px; padding: 1px 4px; margin: -1px -2px; }
.score .mini.dead { opacity: .3; }

.status {
  /* Dead centre. It sat a touch right to clear the tip of South America, and
     read as off; the tip can carry it. */
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: calc(6px + var(--sab));
  color: var(--ink);
  padding: 7px 14px; border-radius: 999px; font-size: 13px; font-weight: 600;
  white-space: nowrap; max-width: 60%; overflow: hidden; text-overflow: ellipsis;
  z-index: 3; cursor: pointer;
}
.status.mine { border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold) inset; }

/* THE TABLE TALK, top centre. */
.talk {
  position: absolute; top: calc(6px + var(--sat)); left: 50%; transform: translateX(-50%);
  border-radius: 999px; padding: 6px 14px 6px 10px;
  display: flex; align-items: center; gap: 8px; z-index: 4;
  /* As wide as the top edge allows once the menu button has its corner:
     a line was being cut at 62% with a third of the screen still free. */
  max-width: calc(100% - 140px);
  font-size: 13px; font-style: italic; font-weight: 500; color: var(--ink); text-align: left;
  animation: talk-in 220ms ease-out;
}
.talk .sw { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.talk > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.talk b { font-style: normal; font-weight: 800; margin-right: 6px; }
@keyframes talk-in { from { opacity: 0; transform: translate(-50%, -6px); } to { opacity: 1; transform: translate(-50%, 0); } }

/* THE DICE. The last roll, INSIDE THE STATUS PILL at the bottom, gone in a
   moment. Not in the glass list above: the pill is the glass, and a second
   hairline inside it read as a box within a box (seen 2026-09-14). It is
   the table telling you what happened, which is what that
   line is for; the words step aside while the roll is up, in the one pill,
   so nothing jumps. Who rolled on whom, then the dice, nothing else: the
   winners pop, the count on the country is the arithmetic. Real dice: the
   attacker's red with white pips, the defender's white with black — the
   glyphs are game-icons' die faces, whose pips are cut out, on a coloured
   tile. */
.status.rolling { padding-top: 4px; padding-bottom: 4px; max-width: 80%; }
.status.rolling #status-text { display: none; }
.dice {
  display: inline-flex; align-items: center; gap: 12px; vertical-align: middle;
  white-space: nowrap; animation: dice-in 180ms ease-out;
}
@keyframes dice-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
.dice .who { font-size: 13px; font-weight: 700; white-space: nowrap; }
.dice .row { display: flex; gap: 4px; align-items: center; }
.die {
  width: 28px; height: 28px; border-radius: 6px; display: grid; place-items: center;
  background: #111; color: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.4);
}
.die svg { width: 28px; height: 28px; display: block; }
.die.att { background: #fff; color: #c8102e; }
.die.won { animation: die-pop 480ms cubic-bezier(.22,.92,.28,1.02) 120ms both; }
@keyframes die-pop { 0% { transform: scale(1); } 45% { transform: scale(1.35); } 100% { transform: scale(1); } }
.dice .who span { font-weight: 800; }
.dice .vs { color: var(--dim); font-size: 11px; }
.dice .res { font-size: 12px; font-weight: 700; white-space: nowrap; }
.dice .res.took { color: var(--good); }
.dice .res.beaten { color: var(--bad); }

/* THE PROMPT: move-in, fortify count, forced trade — IN THE MIDDLE of the
   board, where a thumb is going to work a slider. */
.prompt {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  border-radius: var(--r-mid);
  padding: 10px 12px; z-index: 5; min-width: 250px; max-width: 70%;
}
.prompt h4 { margin: 0 0 6px; font-size: 13px; font-weight: 700; }
.prompt .slide { display: flex; align-items: center; gap: 8px; }
.prompt input[type=range] { flex: 1; min-width: 0; }
.prompt .n { font-family: var(--font-num); font-weight: 800; font-size: 18px; min-width: 34px; text-align: center; }
.prompt .actions { display: flex; gap: 8px; margin-top: 8px; }
.prompt .actions button { flex: 1; padding: 9px 10px; font-size: 14px; }
.prompt .stepper { width: 34px; height: 34px; padding: 0; border-radius: 17px; font-size: 18px; }
.prompt .quick { height: 34px; padding: 0 10px; border-radius: 17px; font-family: var(--font-num); font-size: 12px; font-weight: 800; min-width: 52px; }

/* Pregame and final, centred over the map. */
.overlay {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,.28); z-index: 6; padding: 10px;
}
.overlay .panel {
  border-radius: var(--r-card); padding: 14px 16px;
  width: min(440px, 100%); max-height: 100%; overflow: auto;
}
.panel-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.seatlist { display: flex; flex-direction: column; gap: 6px; margin: 6px 0 8px; }
.seat { display: flex; align-items: center; gap: 10px; padding: 7px 10px; border-radius: var(--r-sm);
        font-size: 14px; }
.seat .sw { width: 14px; height: 14px; border-radius: 4px; flex: none; }
.seat .nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.seat .tag { font-size: 10px; font-weight: 700; letter-spacing: .04em; color: var(--dim); text-transform: uppercase; }
.seat.you .nm::after { content: " · you"; color: var(--gold); font-weight: 700; }
.seat.away { opacity: .55; }
.seat.provisional { border-style: dashed; opacity: .6; }
.seat button { padding: 5px 9px; font-size: 12px; }
.final h2 { margin: 0 0 6px; font-size: 22px; }
.final .places { display: flex; flex-direction: column; gap: 5px; margin: 8px 0 12px; }
.final .places div { display: flex; align-items: center; gap: 8px; font-size: 14px; }
.final .places .sw { width: 12px; height: 12px; border-radius: 3px; }
.final .places .pl { font-family: var(--font-num); font-weight: 800; width: 22px; color: var(--dim); }

/* ── sheets ──────────────────────────────────────────────────────────── */
.sheet {
  position: fixed; inset: 0; z-index: 30; background: var(--bg);
  display: flex; flex-direction: column;
}
.sheet-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: calc(12px + var(--sat)) calc(16px + var(--sar)) 10px calc(16px + var(--sal));
  border-bottom: .5px solid var(--line-2);
  background: color-mix(in srgb, var(--bg) 70%, transparent); backdrop-filter: blur(20px);
}
.sheet-head h2 { margin: 0; font-size: 18px; }
.sheet-body { flex: 1; overflow: auto; padding: 14px calc(14px + var(--sar)) calc(24px + var(--sab)) calc(14px + var(--sal));
              max-width: 620px; width: 100%; margin: 0 auto; }
.kv { display: grid; grid-template-columns: max-content 1fr; gap: 6px 12px; font-size: 13px; }
.kv dt { color: var(--dim); }
.kv dd { margin: 0; overflow-wrap: anywhere; }
.kv dd.yes { color: var(--good); } .kv dd.no { color: var(--bad); } .kv dd.unknown { color: var(--warn); }
.segmented { display: flex; background: var(--glass-2); border-radius: var(--r-sm); padding: 3px; gap: 3px; border: .5px solid var(--line-2); }
.segmented button { flex: 1; border: 0; background: transparent; padding: 8px 6px; border-radius: 8px; box-shadow: none; backdrop-filter: none; font-size: 14px; }
.segmented button[aria-pressed="true"] { background: var(--gold); color: #1a1200; box-shadow: var(--shadow); }
.log { display: flex; flex-direction: column; gap: 4px; font-size: 13px; max-height: 40vh; overflow: auto; }
.log .ev { display: flex; gap: 8px; align-items: baseline; }
.log .ev .sw { width: 8px; height: 8px; border-radius: 2px; flex: none; }
.log .ev.banter { font-style: italic; }
.log .ev.out, .log .ev.win { font-weight: 700; }
/* A PLAYER'S NAME IS WRITTEN IN THEIR COLOUR wherever it appears — the log,
   the status line, the talk, the dice, the lists (app.js nameEl / withNames). */
.pn { font-weight: 650; }
/* A SUPPORTER'S STAR, by the name in every list — never in running text,
   where a star after every mention would be noise. */
.star { color: var(--gold); font-size: .85em; margin-left: 4px; }
/* THE COLOUR PICKER on a supporter's own row in the lobby: the colours at
   this table, the current one ringed. Tapping one swaps seats with whoever
   holds it (Rooms::swapSeat). */
.pick { display: flex; gap: 6px; flex: none; }
.pick button { width: 22px; height: 22px; padding: 0; border-radius: 6px; border: 2px solid transparent; min-height: 0; }
.pick button[aria-pressed="true"] { border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold); }
.log .empty { color: var(--dim); }
.plist { display: flex; flex-direction: column; gap: 6px; }

/* The leaderboard: a grid, not a table, so it wraps nothing and the numbers
   line up. Gold means you, as everywhere. */
.ladder { display: grid; grid-template-columns: max-content 1fr max-content max-content max-content; gap: 6px 12px; margin-top: 12px;
         font-size: 13px; align-items: baseline; }
.ladder .h { color: var(--dim); font-size: 11px; text-transform: uppercase; letter-spacing: .06em; }
.ladder .rank { color: var(--dim); font-family: var(--font-num); }
.ladder .num { font-family: var(--font-num); text-align: right; font-variant-numeric: tabular-nums; }
.ladder .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ladder .name .tag { color: var(--dim); font-size: 11px; margin-left: 6px; }
.ladder .you { color: var(--gold); font-weight: 650; }
.ladder .empty { grid-column: 1 / -1; color: var(--dim); }

/* Your cards. */
.hand { display: flex; flex-wrap: wrap; gap: 8px; }
.pcard {
  width: 96px; height: 128px; border-radius: 10px; position: relative; overflow: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  gap: 2px; padding: 6px; text-align: center;
}
/* The territory, filling the card behind everything, dimmed so the unit and
   the name read over it. */
.pcard .art { position: absolute; inset: 6px 6px 34px; opacity: .55; }
.pcard .art svg { width: 100%; height: 100%; display: block; }
.pcard.wild .art { color: var(--dim); opacity: .35; }
.pcard .ty { position: absolute; top: 8px; right: 8px; line-height: 0;
             background: var(--glass); border-radius: 50%; padding: 4px; border: .5px solid var(--line); }
.pcard .ty svg { width: 22px; height: 22px; display: block; }
.pcard .cn, .pcard .ct { position: relative; }
.pcard .cn { font-size: 11px; font-weight: 600; line-height: 1.2; }
.pcard .ct { font-size: 10px; color: var(--dim); text-transform: uppercase; letter-spacing: .04em; }
.pcard.wild { border-style: dashed; }
.pcard.owned { border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold) inset; }
.sets { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.sets button { text-align: left; }
.sets button .v { font-family: var(--font-num); font-weight: 800; }
.sets button.primary .fine { color: #4a3a08; display: inline; margin: 0 0 0 6px; }

/* ── toast and banner ────────────────────────────────────────────────── */
.toast {
  position: fixed; left: 50%; bottom: calc(70px + var(--sab)); transform: translateX(-50%);
  background: var(--ink); color: var(--bg); padding: 10px 16px; border-radius: 999px;
  font-size: 14px; font-weight: 600; z-index: 50; max-width: 90%; text-align: center;
}
.banner {
  position: fixed; left: calc(8px + var(--sal)); right: calc(8px + var(--sar)); top: calc(8px + var(--sat));
  z-index: 40; border-radius: var(--r-mid);
  padding: 10px 12px; display: flex; align-items: center; gap: 10px; font-size: 13px;
}
.banner[data-tone="warn"] { border-color: var(--warn); }
.banner span { flex: 1; }
.banner button { padding: 6px 10px; font-size: 13px; }
.banner #banner-x { background: transparent; border: 0; font-size: 18px; padding: 0 4px; }
.update-note { color: var(--ink-2); }

/* ── motion ─────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

@media (max-height: 420px) {
  .score { font-size: 11px; }
}

.country:focus, .country:focus-visible { outline: none; }

/* ── SIDEWAYS ON AN UPRIGHT PHONE ────────────────────────────────────────
 *
 * This is a landscape game and a native one would simply draw itself
 * sideways; the person picks the phone up, sees that, and turns it. The web
 * cannot ask for that: iOS ignores the manifest's `orientation` and has no
 * screen.orientation.lock() for a page. So while the phone is upright the
 * whole app is ROTATED with a transform — body becomes 100vh wide and 100vw
 * tall, turned a quarter turn — and the moment the phone is turned the media
 * query stops matching and the ordinary rendering takes over. The first
 * version showed a "turn your phone" card only once a game began, which was
 * the disorienting order: the lobby was upright and the board was not.
 *
 * Two things the rotation has to carry with it: the INSETS — the phone's top
 * (the notch) is the app's left while it is turned, its bottom the app's
 * right — and the keyboard, which will still rise from the phone's bottom
 * edge, i.e. from the app's right; nothing to be done about that, and it
 * only matters while typing a table name before turning the phone.
 */
@media (orientation: portrait) and (max-width: 700px) {
  html { overflow: hidden; }
  body {
    position: fixed; top: 0; left: 0;
    width: 100vh; height: 100vw;
    width: 100dvh; height: 100dvw;
    min-height: 0;                  /* body's min-height: 100dvh would win otherwise */
    transform-origin: top left;
    transform: rotate(90deg) translateY(-100%);
    overflow: hidden;
    --sat: env(safe-area-inset-right, 0px);
    --sab: env(safe-area-inset-left, 0px);
    --sal: env(safe-area-inset-top, 0px);
    --sar: env(safe-area-inset-bottom, 0px);
  }
  .screen { min-height: 100%; height: 100%; overflow: auto; }
  .board { position: absolute; }
  .sheet, .toast, .banner, .backdrop { position: absolute; }
  .toast { bottom: calc(70px + var(--sab)); }
}
