/* ==========================================================================
   Island Settlers — interface styling (entry point).
   Landscape mobile web. System faces only, no webfonts, no remote assets.

   Split across five files to stay well inside the per-file line budget:
     ui-base.css  design tokens, reset, shared material, buttons
     ui-hud.css   the in-world heads-up display
     ui-boot.css  the boot splash — the cold open, before anything exists
     tutorial.css the rules book and the guided practice run
     ui.css       board overview, modal panels, results, responsive

   Owner: UI agent.
   ========================================================================== */

@import './ui-base.css';
@import './ui-hud.css';
@import './ui-boot.css';
@import './tutorial.css';
@import './rooms.css';

/* ======================================================== opening screen
   The match-flow module sets `mf-introlive` on the interface root while the
   opening screen is up. There is no match yet — an empty resource bar and a
   0-0-0-0 ranking behind the title is noise, and it was reading straight
   through the wordmark. Everything but the flow layer stands down, then
   fades back in as the draft begins. */
#ui.mf-introlive > *:not(.mf-layer){
  opacity:0 !important;
  transition:opacity .3s ease;
}
/* The overview and the panels animate their own opacity — leave them alone. */
#ui > *:not(.mf-layer):not(.ov):not(.panels){transition:opacity .45s ease .12s}

/* ============================================================== overview */
.ov{
  position:absolute;inset:0;
  opacity:0;transform:scale(1.05);
  transition:opacity .25s ease,transform .25s cubic-bezier(.2,.9,.3,1);
}
.ov.on{opacity:1;transform:scale(1)}
.ov-cv{position:absolute;inset:0;width:100%;height:100%;display:block;touch-action:none}

/* `.ov-top` is gone: the title plate that used to run across the top centre of
   the board was standing on the third of the screen a player most wants to
   see. `.ov-title` and `.ov-hint` still exist as the panel's accessible name —
   see the `.ov-say` rule in overview.js's own stylesheet. */

.ov-x{position:absolute;top:var(--gT);left:var(--gL)}

.ov-rail{
  position:absolute;right:var(--gR);top:var(--gT);
  bottom:var(--gB);width:186px;overflow:auto;
  display:flex;flex-direction:column;gap:6px;padding:7px;
}
.rail-head{font:800 9px/1 var(--ff);letter-spacing:.24em;text-transform:uppercase;
  color:rgba(255,231,154,.78);text-align:center;padding:2px 0 4px}

/* One row per settler. The left bar is the player's colour at full strength —
   no tint, no transparency — and the whole row is washed with it, so "which
   one am I" is answered before the name is read. */
.rr{position:relative;padding:6px 7px 6px 12px;border-radius:10px;overflow:hidden;
  background:
    linear-gradient(90deg,var(--ct,rgba(59,127,212,.34)),rgba(255,255,255,.03) 64%),
    linear-gradient(180deg,rgba(255,255,255,.09),rgba(255,255,255,.03));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.16),0 1px 2px rgba(0,0,0,.3)}
.rr::before{content:'';position:absolute;left:0;top:0;bottom:0;width:6px;
  background:linear-gradient(180deg,var(--cl,#93cbff),var(--c,#2f8ffb) 55%);
  box-shadow:inset -1px 0 0 rgba(0,0,0,.45)}
.rr.me{padding-top:8px;
  background:
    linear-gradient(90deg,var(--ct2,rgba(59,127,212,.62)),rgba(59,127,212,.16) 72%),
    linear-gradient(180deg,rgba(255,255,255,.12),rgba(255,255,255,.04));
  box-shadow:inset 0 0 0 2px var(--gold),0 0 14px rgba(255,201,60,.34),
             inset 0 1px 0 rgba(255,255,255,.2)}
.rr.me::before{width:7px;box-shadow:inset -1px 0 0 rgba(0,0,0,.4),0 0 10px rgba(255,201,60,.6)}
.rr-you{position:absolute;top:0;right:0;padding:2px 7px 3px;border-radius:0 10px 0 9px;
  background:linear-gradient(180deg,var(--gold-l),var(--gold));
  font:800 8px/1 var(--ff);letter-spacing:.16em;text-transform:uppercase;color:#3a2208;
  box-shadow:0 1px 3px rgba(0,0,0,.5)}
.rr-top{display:flex;align-items:center;gap:7px}
.rr-av{line-height:0;flex:0 0 auto}
.rr-id{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.rr-name{min-width:0;font:800 12.5px/1 var(--ff);color:#fff;letter-spacing:.05em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  text-shadow:0 1px 2px rgba(0,0,0,.6)}
.rr-col{font:800 7.5px/1 var(--ff);letter-spacing:.2em;text-transform:uppercase;
  color:var(--cl,#93cbff);text-shadow:0 1px 2px rgba(0,0,0,.7)}
.rr.me .rr-col{color:var(--gold-l)}
.rr-vp{font:800 20px/1 var(--ff);color:var(--gold);
  -webkit-text-stroke:1.6px rgba(10,18,30,.8);paint-order:stroke fill}
.rr-stats{display:flex;flex-wrap:wrap;gap:4px;margin-top:5px}
.rr-stats i{display:inline-flex;align-items:center;gap:3px;font-style:normal;
  padding:2px 5px;border-radius:7px;background:rgba(0,0,0,.30);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.12)}
.rr-stats i em{font:800 13px/1 var(--ff);font-style:normal;color:#eaf2fb}
.rr-stats i.won{background:rgba(255,201,60,.26);box-shadow:inset 0 0 0 1px rgba(255,201,60,.6)}
.rr-stats i.won em{color:var(--gold-l)}

.ov-bar{
  position:absolute;left:50%;transform:translateX(-50%);
  bottom:var(--gB);
  display:flex;align-items:center;gap:11px;padding:7px 11px;
  border-radius:17px;
}
.ov-sel{font:800 10px/1.2 var(--ff);letter-spacing:.13em;text-transform:uppercase;
  color:rgba(226,238,250,.9);max-width:34vw;text-align:center}
/* A spot is chosen: the label goes gold, which is the standing cue that tapping
   that same spot again will place the piece without a trip to Confirm. */
.ov-sel.armed{color:var(--gold-l,#ffe79a);text-shadow:0 0 10px rgba(255,201,60,.5)}

/* ================================================================ panels */
.panels{position:absolute;inset:0;opacity:0;transition:opacity .22s ease}
.panels.on{opacity:1}
.scrim{position:absolute;inset:0;background:rgba(4,14,28,.62);
  backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px);pointer-events:auto}
.scrim.hard{background:rgba(4,14,28,.9)}

.sheet{
  position:absolute;left:50%;top:50%;transform:translate(-50%,-50%) scale(.97);
  width:min(680px,calc(100vw - 28px - var(--sal) - var(--sar)));
  max-height:calc(100vh - 20px - var(--sat) - var(--sab));
  display:flex;flex-direction:column;gap:8px;padding:11px 13px 12px;
  background:linear-gradient(180deg,#fdf5e2 0%,var(--cream) 34%,var(--cream-2) 100%);
  border:3px solid var(--brown);border-radius:20px;
  box-shadow:0 10px 0 rgba(90,58,30,.5),0 22px 46px rgba(0,0,0,.6),
             inset 0 3px 0 rgba(255,255,255,.9);
  transition:transform .22s cubic-bezier(.2,1.2,.35,1);
  pointer-events:auto;overflow:hidden;
}
.panels.on .sheet{transform:translate(-50%,-50%) scale(1)}
.sheet-head{display:flex;align-items:center;justify-content:space-between;gap:10px}
.sheet-title{font:800 16px/1 var(--ff);letter-spacing:.2em;text-transform:uppercase;
  color:var(--brown);text-shadow:0 1px 0 rgba(255,255,255,.85)}
.sheet h4{margin:0 0 5px;font:800 9.5px/1 var(--ff);letter-spacing:.2em;
  text-transform:uppercase;color:var(--brown-2);text-align:center}
.sheet-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding-top:3px;border-top:2px solid rgba(90,58,30,.18)}
.why,.vp-note{font:700 10.5px/1.25 var(--ff);letter-spacing:.06em;color:#6b4a26;
  text-transform:uppercase;flex:1 1 auto}

/* --- trade ----------------------------------------------------------------
   ONE ROW OF FIVE CARDS. Above each card the arrow that lifts it up into YOU
   RECEIVE, below it the arrow that hands it down into YOU GIVE, and on the card
   the pile itself — what you hold, and what this deal would leave you holding.

   THE LANES ARE THE OTHER WAY UP THAN THEY WERE.

     "I think the 'you give' and 'you receive' should swap sides, with the 'you
      receive' on the top. Since basically how many resources I have is in the
      middle, so if I click the up arrow, that should mean that I'm adding that
      many resources to my stockpile."

   Green band on top, brown band underneath. The COLOURS did not swap with the
   words — green has meant "arrives in your pack" and brown/gold "leaves it"
   since long before this, so keeping them pinned to their meaning is what lets
   a player who already knew the sheet read the new one without being taught it
   twice.

   FOUR COLOURS AND NO MORE. Green for receive, gold-and-brown for give, mint
   for the pile you have asked for, and a flat sand grey for a pile that cannot
   pay. That is the entire palette above the resource art, and it is a rule
   rather than a preference: the first version of this sheet reached eight hues,
   two rings, three badges and two slabs inside a 570px row, and a reviewer
   counted them one by one. Anything new here has to be one of those four or it
   does not go on.

   Deliberately flat. No inner boxes, no rails, no plates inside plates — the
   cream sheet is the only frame and everything else is drawn with a hairline
   and a shadow. See src/ui/trade.js for the staging model. */

.sheet.trade{width:min(600px,calc(100vw - 28px - var(--sal) - var(--sar)))}
.sheet.trade .sheet-head{justify-content:flex-start;gap:7px}
.tr-where{
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font:800 9px/1 var(--ff);letter-spacing:.16em;text-transform:uppercase;
  color:var(--brown-2);opacity:.8;padding-top:2px;
}
/* THE RATE, ONCE, BESIDE THE NAME OF THE POST. It used to be stamped in the
   corner of all five cards, which at the Great Market is the same three
   characters printed five times down one row. It is a property of the place,
   so it is quoted where the place is named, and a card only carries its own
   rate when that rate DISAGREES with this one — which is precisely the 2:1
   dock somebody crossed the island to reach. */
.tr-headrate{
  flex:0 0 auto;padding:3px 7px 4px;border-radius:7px;
  font:800 10px/1 var(--ff);letter-spacing:.06em;
  background:rgba(90,58,30,.12);color:var(--brown);
  box-shadow:inset 0 0 0 1.2px rgba(90,58,30,.22);
}
.sheet.trade .tr-paused{margin-left:auto}
/* THE CLOSE BUTTON'S HIT AREA IS 44 EVEN WHERE ITS CIRCLE IS 34.
 *
 *   "The close X is ~32px at 640x320, still under 44. Raise the hit area even
 *    if the drawn circle stays small."
 *
 * The circle is the first thing that gives up height on a 320px screen, and it
 * should be — Escape, the scrim and the back gesture all close this sheet too,
 * so the X is the least load-bearing control on it. What it must not be is a
 * target a thumb misses, and a miss here lands on the sheet's own head and does
 * nothing at all. So the drawn circle keeps shrinking and the LIVE box does
 * not: a 44px pad in absolute pixels, so it is 44 whatever the breakpoint has
 * done to the button. It inherits the button's own `pointer-events:auto` (see
 * the `[data-ui]` rule in ui-base.css), and it grows into the sheet's padding
 * and the dead space beside the "Match paused" chip, which is not a control and
 * cannot lose a tap to it.
 *
 * IT LEANS DOWNWARDS, AND IT IS 46 TALL RATHER THAN 44, because the sheet clips
 * its own overflow: a pad centred on a 34px circle whose top edge lands on the
 * sheet's inner rim has that top row cut off by the clip and measures 43 —
 * exactly the sort of one-pixel lie this pass exists to stop. The spare room is
 * underneath, where the receive band is a caption rather than a control and
 * cannot lose a tap to anything. */
.sheet.trade .sheet-head .cbtn.small::after{
  content:'';position:absolute;left:50%;top:50%;
  width:44px;height:46px;margin:-21px 0 0 -22px;border-radius:50%;
}
/* AND IT IS THE SHEET'S OWN CHROME, NOT A BLUE BUTTON ON A BROWN PANEL.
 *
 *   "The close button is the only blue on the sheet (hue 213 against everything
 *    else at 28-43 and 127-130) and its highest-contrast object, pulling the eye
 *    to the corner away from the deal. Tone it toward the panel's brown chrome."
 *
 * `.cbtn.ghost` is the HUD's dark glass disc, and on the HUD it is right — it
 * sits over the island. On a cream sheet it was a navy hole with a gold rim
 * competing with the Trade button for first look. It wears the arrows' own face
 * now: cream disc, brown hairline, brown lip, the dark keyline X that every
 * chrome glyph in this game is drawn with. Still obviously a button, still the
 * same 44px live pad, and no longer the loudest thing in the room. */
.sheet.trade .sheet-head .cbtn.small.x{
  --f1:#fffaee;--f2:#f2e3c4;--f3:#e0cba3;--lip:rgba(90,58,30,.3);
  border-color:rgba(90,58,30,.45);color:#6b4a26;
  box-shadow:0 3px 0 rgba(90,58,30,.28),0 5px 9px rgba(90,58,30,.18),
             inset 0 2px 0 rgba(255,255,255,.85);
}
.sheet.trade .sheet-head .cbtn.small.x:active{
  box-shadow:0 1px 0 rgba(90,58,30,.28),inset 0 2px 0 rgba(255,255,255,.7);
}
/* "Nothing is happening out there while you do this." Two dots and two words,
   in the sheet's own cream-and-brown, sitting between the place name and the
   close button. The match really is held (see the frame loop in main.js), and a
   player who cannot tell is a player still trading against a clock. */
.tr-paused{
  flex:0 0 auto;display:inline-flex;align-items:center;gap:5px;
  padding:3px 8px 4px;border-radius:7px;
  background:rgba(63,154,82,.16);box-shadow:inset 0 0 0 1.4px rgba(47,212,95,.42);
  font:800 8.5px/1 var(--ff);letter-spacing:.14em;text-transform:uppercase;
  color:#2c6027;white-space:nowrap;
}
.tr-paused i{
  width:7px;height:7px;border-radius:2px;flex:0 0 auto;
  background:linear-gradient(180deg,#8bf5a8,#2fd45f);
  box-shadow:0 0 6px rgba(47,212,95,.7);
}
@media (max-height:500px),(max-width:1023px){
  .tr-paused{padding:2px 6px 3px;font-size:7.5px;letter-spacing:.11em;gap:4px}
  .tr-paused i{width:6px;height:6px}
  .tr-headrate{padding:2px 6px 3px;font-size:9px}
}

.trade-body{display:flex;flex-direction:column;align-items:stretch;gap:6px;min-height:0}

/* THE TWO LANES — the whole explanation of the row, said out loud.
 *
 *   "Can you make the YOU GIVE and YOU RECEIVE sections larger as well and more
 *    clearly marked — maybe with larger text and colored areas above and below."
 *
 * They were 8.5px of grey at 72% opacity: footnote weight for the one fact the
 * sheet turns on. Bands now, full width, each sitting on the side of the row it
 * describes, with the chevron on the outside edge pointing the way the goods
 * travel — up out of the row into the green band, down out of it into the brown
 * one.
 *
 * They are LEANER than they were, and that height went straight into the
 * arrows. A band is worth its 20px because it names the lane; it is not worth
 * 30, because past a certain size it is only decoration and the thumb targets
 * either side of it were the things actually running short.
 *
 * `.tc-live` is the one live message on the sheet, and it is PARKED at the
 * right-hand end rather than sitting in the flow between the title and its
 * chevron — where it used to shove the title sideways every time it changed.
 * It is drawn as a tip (a pale wash of the band's own colour) and not as a
 * stamp: the black-at-26% version read as the disabled badge on a broken
 * control, which is the opposite of "here is what to do next". */
/* A BAND IS A CAPTION, NOT A CONTROL, so it does not answer to a pointer.
   It never had a handler on it, but `#ui [data-ui] *` hands pointer-events back
   to every descendant of the panel wrapper, so the band was still the topmost
   hit-testable thing over its own 16px strip — which is what was eating the
   bottom two pixels of the close button's 44px pad one row above it, and what
   would eat the top of an arrow's reach if that ever grew towards it. Two
   classes deep so it outranks that rule rather than tying with it. Taps land on
   the sheet underneath, exactly as they do on any other piece of cream. */
#ui .tr-cap.get,#ui .tr-cap.give{pointer-events:none}
.tr-cap{
  position:relative;
  display:flex;align-items:center;justify-content:center;gap:9px;
  padding:4px 12px 5px;border-radius:9px;
  font:800 12px/1 var(--ff);letter-spacing:.2em;text-transform:uppercase;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.22),0 2px 0 rgba(90,58,30,.22);
}
.tr-cap b{font:inherit}
.tr-cap.give{background:linear-gradient(180deg,#8a5f2e,#5a3a1e);color:#ffeec4;
  text-shadow:0 1px 0 rgba(0,0,0,.35)}
.tr-cap.get{background:linear-gradient(180deg,#3f9a52,#25692f);color:#eafce9;
  text-shadow:0 1px 0 rgba(0,0,0,.3)}
.tr-cap::before,.tr-cap::after{font-size:13px;line-height:1;opacity:.95}
.tr-cap.get::before{content:'\25B2'}
.tr-cap.get::after{content:'\25B2'}
.tr-cap.give::before{content:'\25BC'}
.tr-cap.give::after{content:'\25BC'}
.tc-live,.tc-cost{
  display:none;position:absolute;top:50%;transform:translateY(-50%);
  font:800 9.5px/1 var(--ff);font-style:normal;letter-spacing:.1em;
  padding:3px 8px 4px;border-radius:7px;color:inherit;
  background:rgba(255,255,255,.2);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.34);
}
/* THE TIP IS THE QUIETER OF THE TWO. It is advice; the thing at the other end
   of the band is the price of the deal, and they were shipping as the same pill
   at the same size in the same colour:
     "The cost readout and the instructional hint are the same pill, same size,
      same colour, at opposite ends of the same bar, so the number reads as
      advice."
   So the tip gives up two points of type and most of its wash, and the total
   takes them. Nothing new is introduced to tell them apart — it is the same
   white-on-the-band's-own-colour they always were, just no longer at equal
   volume. */
.tc-live{right:9px;font-size:9px;background:rgba(255,255,255,.13);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.22)}
.tr-cap.say .tc-live{display:inline-flex}

/* WHAT THE DEAL COSTS, AS A NUMBER, AT THE OTHER END OF THE SAME BAND.
 *
 *   "The price of the deal is currently only ever inferable: in the ask frame
 *    you read it off the PAY plates, in the mixed frame you must mentally add
 *    CLEAR 8 + CLEAR 4, and in trade-960x444-ask3.png the number 12 appears
 *    NOWHERE on the sheet."
 *
 * NEEDS 12, then 8 OF 12, then 12 OF 12. It is parked absolutely exactly as the
 * tip is, at the left end rather than the right, so the two never touch and the
 * band's own title never moves when either of them changes width. It is the
 * same chip in the same wash as the tip because it is the same kind of thing —
 * the band talking — and a second treatment here would be a fifth colour on a
 * sheet that is allowed four.
 *
 * The number itself is a size up and rides in the band's own ink, because it is
 * the one figure on this sheet a player is doing arithmetic with. */
.tc-cost{
  left:9px;font-size:12px;letter-spacing:.08em;padding:3px 9px 4px;
  background:rgba(255,255,255,.3);
  box-shadow:inset 0 0 0 1.4px rgba(255,255,255,.46),0 1px 0 rgba(0,0,0,.16);
}
.tr-cap.cost .tc-cost{display:inline-flex}
/* Covered. Not a new colour — the same white wash, turned up, on the beat the
   Trade button lights. */
.tc-cost.full{background:rgba(255,255,255,.44);
  box-shadow:inset 0 0 0 1.6px rgba(255,255,255,.6),0 1px 0 rgba(0,0,0,.16)}

/* Nothing sits in the foot but the deal itself — and it is spaced so that the
   room above the button and the room below it are the SAME room.
 *
 *   "3px of clearance below the primary call to action against ~10px above it,
 *    with only 10px of screen below the sheet. Give it bottom padding matching
 *    the top."
 *
 * The gap above the Trade button is the sheet's own column gap plus this rule
 * and this padding; the gap below it is the sheet's padding-bottom. Taking a
 * pixel off the padding here is what makes the two match at every breakpoint
 * without moving the sheet's outer padding, which is shared with two other
 * panels that were not complaining. */
.trade .sheet-foot{justify-content:center;padding-top:2px}

/* THE GAP BETWEEN AN ARROW AND ITS CARD IS A SAFETY MARGIN, NOT A MARGIN.
 *
 *   "The gap between the down-arrow and the card above it is ~6px — the two
 *    most different actions in the sheet (spend one step / spend everything)
 *    are 6px apart."
 *
 * Ten now. It is the only separation between "one lot" and "all of it", and a
 * thumb that lands three pixels low should not have spent sixteen wool. */
.tr-row{display:flex;gap:8px;align-items:stretch}
.tr-col{
  flex:1 1 0;min-width:0;display:flex;flex-direction:column;align-items:stretch;gap:10px;
}

/* --- the arrows.
 *
 *   "Can you make the up and down arrows for the trading post larger and easier
 *    to press? Right now it's hard to press consistently."
 *
 * Two boxes, and they had drifted apart. The plate a thumb AIMS at was 25px
 * tall on a phone; the box that answered a press was 47, because a transparent
 * ::after reached out into the lane caption. So the target was mostly in the
 * direction nobody aims, and a press three pixels below the plate hit the CARD,
 * which changes the focus and stages nothing.
 *
 * The plate went to 40, and then to 46 (44 on a phone), because 38 is still
 * under the thumb minimum and this is the one control on the sheet a player
 * hits four times in a row. The height came out of the two lane bands, which
 * were 30px of colour carrying two words each.
 *
 * The ::after still reaches out into the caption and now stops 5px short of the
 * card, which is HALF the 10px gap: the arrow's live box and the card's live
 * box no longer touch, and there is a real no-man's-land between "spend one
 * lot" and "spend all of it". */
.tr-arr{
  position:relative;height:46px;padding:0;border-radius:10px;cursor:pointer;
  background:linear-gradient(180deg,#fffaee,#ead9b3);
  border:1.5px solid rgba(90,58,30,.42);
  box-shadow:0 2px 0 rgba(90,58,30,.26),inset 0 1px 0 rgba(255,255,255,.85);
  display:flex;align-items:center;justify-content:center;
  touch-action:none;
  /* Nor is an arrow's, for the same reason the card's is not: a control caught
     half-way between live and dead is a control the player has to look at
     twice. See the note on `.tr-card` and the muted-gold ring that was neither
     armed nor neutral. */
}
.tr-arr::after{content:'';position:absolute;left:0;right:0}
.tr-arr.up::after{top:-13px;bottom:-5px}
.tr-arr.dn::after{top:-5px;bottom:-13px}
.tr-arr::before{content:'';width:0;height:0;
  border-left:11px solid transparent;border-right:11px solid transparent}
/* THE GLYPHS ARE PAINTED IN THE COLOUR OF THE BAND THEY POINT AT. Both were
   brown when both lanes were brown-above-green-below by accident of order; now
   that the lanes have swapped, an arrow whose colour disagreed with the band
   two pixels away from it would be the single most confusing thing on the
   sheet. Up is green because up is into your pack. The plate keeps its cream —
   ten coloured plates would be a row of traffic lights — and takes the lane's
   colour only in its hairline. */
.tr-arr.up::before{border-bottom:13px solid var(--green-d)}
.tr-arr.dn::before{border-top:13px solid #7a5228}
.tr-arr.up{border-color:rgba(37,105,47,.4)}
.tr-arr.dn{border-color:rgba(90,58,30,.42)}
.tr-arr:active{transform:translateY(2px);box-shadow:0 0 0 rgba(90,58,30,.3)}

/* THE ARROW OVER A PAYING PILE IS THE ONE-LOT UNDO, and it takes a wash of the
   lane it feeds so that it looks like one.
 *
 *   "Sheep is 4 of 8 spent but its down arrow is dimmed, so the only adjustment
 *    is CLEAR 4 — all or nothing."
 *
 * It never was all or nothing — this arrow hands one lot back per tap and is
 * live in exactly that state — but nothing on the card said so, and a green
 * glyph labelled "Receive Sheep" over a card that is spending sheep does not
 * read as the undo it is. A faint mint on the plate, which is the receive
 * lane's own colour and already on the sheet, and the aria-label says the rest.
 * No new hue: taking a lot back out of the give lane really does put those
 * cards back in your pack, which is what green has always meant here.
 *
 * `:not(.off)` because a dead arrow must look dead whatever else is true of it,
 * and two classes would otherwise outrank the disabled rule below. */
.tr-arr.up.back:not(.off){
  background:linear-gradient(180deg,#fbfff6,#dcecd4);
  border-color:rgba(37,105,47,.55);
}

/* DISABLED IS THE POINT. Nothing to pay for yet means no give arrow can move,
   and the player reads that before they read anything else on the sheet.
   The floor sits at .42 rather than the .3 it used to: a dead arrow used to be
   one or two plates in a row of live ones, and the sheet now OPENS with all
   five give arrows dead, which at .3 read as a lane with no controls in it
   rather than a lane waiting its turn. */
.tr-arr.off{cursor:default}
.tr-arr.off::before{border-bottom-color:#b3a084;border-top-color:#b3a084}
.tr-arr.off:active{transform:none}
.tr-arr.off{opacity:.42;box-shadow:none;background:rgba(90,58,30,.05);
  border-color:rgba(90,58,30,.2)}

/* --- the card.
 *
 * Icon and count side by side across the top, and a reserved plate underneath
 * for the action. The plate's slot is held open whether or not it has anything
 * in it, so a card arming, or a payment landing, never moves a single pixel of
 * anything else on the card — the first version had a price badge that jumped
 * forty pixels across the card between two adjacent states. */
.tr-card{
  position:relative;flex:1 1 auto;min-height:74px;padding:5px 5px 4px;
  display:flex;flex-direction:column;align-items:stretch;justify-content:center;gap:4px;
  border-radius:12px;cursor:pointer;touch-action:none;
  background:linear-gradient(180deg,#fffcf3,#f0e2c2);
  border:2px solid rgba(90,58,30,.34);
  box-shadow:0 2px 0 rgba(90,58,30,.2);
  /* A CARD'S STATE IS NOT ANIMATED, AND THE REASON IS A REVIEW.
   *
   *   "960 paid: wood gold, brick green, and sheep/wheat/ore carry a THIRD
   *    muted-gold ring, RGB (196,153,65), while doing nothing. Identically
   *    roled cards render two different ring colours in adjacent frames."
   *
   * That third ring was never a state: 196,153,65 is almost exactly four tenths
   * of the way from the armed gold (201,143,20) to the neutral tan (189,169,139)
   * on all three channels. Those three cards HAD been armed one tap earlier —
   * every pile of eight could settle a two-brick ask alone — and the frame was
   * caught part-way through a 140ms border-colour fade back to neutral. It only
   * showed at 960 because that is the size where the renderer is slowest and a
   * fade lasts longest in painted frames.
   *
   * The fade could be shortened, but a card would still be able to paint a
   * colour that means nothing, and this row is read at a glance by a player in
   * a hurry. A pile is either paying, or asked for, or neither, and it says so
   * on the frame the tap lands. Only the press keeps its motion. */
  transition:transform .1s ease;
}
.tr-face{display:flex;align-items:center;justify-content:center;gap:5px;min-width:0}
.tr-ico{line-height:0;flex:0 0 auto}

/* THE TWO NUMBERS THAT DEFINE THE TRADE, IN THE BIGGEST TYPE ON THE SHEET.
 *
 *   "The two numbers that define the trade are the SMALLEST type on the sheet,
 *    smaller than five inert 8s. Show the ask in the card's primary numeral as
 *    a delta (0 -> 4) and the cost likewise (40 -> 24)."
 *
 * They were 11px badges riding the arrows. They are the pile count now, and the
 * pile count ANSWERS: press up four times on brick and brick reads 0 -> 4, tap
 * wood to pay for it and wood reads 40 -> 24. Which is the owner's own sentence
 * about the up arrow — "that should mean that I'm adding that many resources to
 * my stockpile" — finally drawn instead of described.
 *
 * `.tr-was` only exists while something is staged, so an untouched row is five
 * plain counts and nothing else. */
.tr-count{display:flex;align-items:baseline;gap:3px;min-width:0}
/* THE "FROM" HALF OF THE DELTA, AND IT HAS TO BE READABLE.
 *
 *   "The 'from' value (the '8 ->' before the delta) is 10px at 2.9:1 contrast,
 *    (160,138,104) on mint — the least readable element on the tightest
 *    screen."
 *
 * It was #a08a68, which measures 2.6:1 against the dark end of the mint fill of
 * an asked-for card and worse still against the gold of a paying one — under
 * the 3:1 floor on the two backgrounds it is guaranteed to appear on, since it
 * only ever exists while a card is staged. #7a6242 is the same brown four steps
 * down: 4.5:1 on the mint, 5:1 on the gold, and still visibly the quieter half
 * of the pair beside a 25px numeral. The arrow between them came up with it,
 * for the same reason. */
.tr-was{
  display:none;font:800 12px/1 var(--ff);font-style:normal;color:#7a6242;
}
.tr-count.get .tr-was,.tr-count.give .tr-was{display:inline-flex;align-items:center}
.tr-was::after{content:'\2192';margin-left:2px;opacity:.9}
.tr-now{
  font:800 25px/1 var(--ff);font-style:normal;color:#3f2a12;
  text-shadow:0 1px 0 rgba(255,255,255,.75);
}
.tr-count.get .tr-now{color:#1f6a2c}
.tr-count.give .tr-now{color:#8a5a12}

/* the rate this post charges for THIS resource — drawn ONLY when it disagrees
   with the headline rate in the title bar. At the Great Market that is never;
   at a 2:1 wool dock it is the one card worth walking to. */
.tr-rate{
  position:absolute;top:3px;left:5px;display:none;
  padding:1px 4px 2px;border-radius:6px;
  font:800 9px/1 var(--ff);letter-spacing:.02em;
  background:var(--gold);color:#3a2208;box-shadow:0 1px 0 rgba(90,58,30,.3);
}
.tr-rate.on{display:inline-flex}

/* THE ACTION PLATE — the one-tap payer, drawn as a thing you press.
 *
 *   "This was the owner's marquee request and it currently ships as an easter
 *    egg: the card is a passive cream tile with an icon and a numeral, no verb,
 *    no press affordance. Render the chip as an action — an amber pressable
 *    pill reading e.g. PAY 16 — visible the moment the card becomes armed."
 *
 * A gold button face with a lip under it, the same language every other button
 * in this game uses, carrying the verb and the exact price. CLEAR takes the
 * same slot once the pile is committed, so the plate never moves and the undo
 * is never hidden — and because the whole card is the button, the target for
 * that destructive tap is the full 100x74 card and not a 14px corner badge. */
.tr-act{
  height:19px;flex:0 0 auto;display:flex;align-items:center;justify-content:center;
  border-radius:7px;overflow:hidden;
}
.tr-act u{display:none;text-decoration:none;
  font:800 10px/1 var(--ff);letter-spacing:.08em;text-transform:uppercase;
  white-space:nowrap}
.tr-act.on{
  background:linear-gradient(180deg,var(--gold-l),var(--gold) 58%,var(--gold-d));
  box-shadow:0 2px 0 #a8741a,inset 0 1px 0 rgba(255,255,255,.6);
  color:#3a2208;
}
.tr-act.on u{display:inline}
.tr-act.clear{
  background:linear-gradient(180deg,#f6e6c4,#dfc794);
  box-shadow:0 2px 0 rgba(90,58,30,.34),inset 0 1px 0 rgba(255,255,255,.6);
  color:#5a3a1e;
}
.tr-col.armed .tr-card:active .tr-act.on,
.tr-col.giving .tr-card:active .tr-act.on{box-shadow:0 0 0 #a8741a}

/* A PILE THAT CANNOT PAY FOR ANYTHING HERE — and that is the only thing that
   dims a card.
 *
 *   "He wrote 'trade 8 wood and 8 sheep for 4 brick' — a MIXED payment from two
 *    piles, neither of which covers the full cost. Your rule makes that exact
 *    trade look forbidden. Light any resource the player holds at least one lot
 *    of; reserve dimming for piles that genuinely cannot contribute."
 *
 * So it is `held < ratio`, full stop: a fact about the pack that does not move
 * as the ask grows, and one that always agrees with the give arrow underneath.
 * Soft — the art fades and the count goes pale — because it is a "not this
 * one", not an error. */
.tr-col.dim .tr-now{color:#a08a68}
.tr-col.dim .tr-ico{opacity:.42;filter:grayscale(.8)}
.tr-col.dim .tr-card{background:linear-gradient(180deg,#f8f3e6,#e8dfca);
  border-color:rgba(90,58,30,.2);box-shadow:0 2px 0 rgba(90,58,30,.12)}

/* ONE RING, ONE MEANING: GOLD MEANS THIS CARD IS PAYING — AND ITS WEIGHT SAYS
 * WHETHER IT IS OFFERING OR COMMITTED.
 *
 * It is on the card that could settle the deal in one tap and on the card that
 * already is. The first version had gold and cobalt trading places between the
 * two states of the same card, which meant the ring signified nothing at all,
 * and the second drew both states at identical weight:
 *
 *   "The ask frame fires 4 gold rings plus 4 identical PAY plates — eight
 *    equal-weight gold calls to action at once — while the card the player
 *    actually acted on is the quietest thing on the sheet. Lighten the offer
 *    rings so the staged card stays loudest."
 *
 * Which is what a pack of eights asking for two brick looks like: four piles
 * can each settle it alone, so four piles offer, and the sheet was shouting
 * four times over the one card the player had actually touched. An OFFER keeps
 * the gold hairline and its gold plate — it is still unmistakably the pressable
 * one — and the 2px collar is now reserved for a pile that is really handing
 * cards over. Same hue, same meaning, weight tracking commitment. */
.tr-col.armed .tr-card,
.tr-col.giving .tr-card{border-color:var(--gold-d)}
.tr-col.armed .tr-card{background:linear-gradient(180deg,#fffdf2,#f7ebcd);
  box-shadow:0 2px 0 rgba(168,116,26,.5)}
.tr-col.giving .tr-card{background:linear-gradient(180deg,#fff3d0,#f6dfa4);
  box-shadow:0 0 0 2px rgba(201,143,20,.9),0 3px 0 rgba(90,58,30,.24)}
.tr-col.armed .tr-card:active{transform:translateY(2px);
  box-shadow:0 0 0 rgba(168,116,26,.5)}
.tr-col.giving .tr-card:active{
  transform:translateY(2px);
  box-shadow:0 0 0 2px rgba(201,143,20,.9),0 0 0 rgba(90,58,30,.24);
}
/* THE PILE YOU HAVE ASKED FOR: mint fill, green hairline, and a green lip under
   it — no collar, because it is not paying, but no longer the flattest card in
   the row either. It is the one card on the sheet the player put there. */
.tr-col.getting .tr-card{border-color:#4f9a5c;
  background:linear-gradient(180deg,#eefae9,#d3ecd0);
  box-shadow:0 2px 0 rgba(37,105,47,.38)}

/* THE CURSOR, and it is deliberately not a state. There is no mouse pointer to
   fall back on, so the card the keyboard is pointing at takes a dotted outline
   — a shape no other thing on this sheet uses, in a colour already on it. It
   used to be a 3px cobalt collar, which was heavier than the gold ring it sat
   next to and read as a second, competing meaning; on a touch layout it sat on
   whichever card was last tapped, for a reason a thumb player cannot infer. */
.tr-col.cur .tr-card{
  outline:2px dotted rgba(90,58,30,.6);outline-offset:2px;
}

/* a refused key press: one short shake, nothing else */
.tr-row.nope{animation:trNope .22s ease}
@keyframes trNope{
  0%,100%{transform:translateX(0)}25%{transform:translateX(-4px)}
  75%{transform:translateX(4px)}
}
.sheet.trade.done .tr-row{animation:trDone .45s cubic-bezier(.2,1.4,.4,1)}
@keyframes trDone{0%{transform:scale(1)}34%{transform:scale(1.035)}100%{transform:scale(1)}}
@media (prefers-reduced-motion:reduce){
  .tr-row.nope,.sheet.trade.done .tr-row{animation:none}
}

/* the foot: one line of reason, one line of keys, one button */
/* --- cards --------------------------------------------------------------- */
/* The buy button carries its own cost row: it must not inherit the build
   card's full-bleed gold band, which overflows a 48px-tall button. */
.buy-cost{display:inline-flex;align-items:center;gap:7px;margin-left:3px;
  padding-left:9px;border-left:1.5px solid rgba(90,58,30,.30)}
.buy-cost .cc{display:inline-flex;align-items:center;gap:2px;font-style:normal}
.buy-cost .cc em{font:800 16px/1 var(--ff);font-style:normal;color:#3f2a12;
  text-shadow:0 1px 0 rgba(255,255,255,.55)}
.hand{display:flex;gap:9px;justify-content:center;align-items:flex-start;
  min-height:150px;padding:8px 4px 4px;overflow-x:auto;overflow-y:hidden}
.dcard{
  position:relative;flex:0 0 122px;width:122px;min-height:150px;
  display:flex;flex-direction:column;align-items:center;gap:4px;padding:9px 8px 10px;
  background:linear-gradient(180deg,#fffaea,#f0dfb6 62%,#e0cb9c);
  border:2.5px solid var(--brown);border-radius:12px;
  box-shadow:0 6px 0 rgba(90,58,30,.42),0 12px 22px rgba(0,0,0,.4),
             inset 0 2px 0 rgba(255,255,255,.9);
  transform-origin:50% 120%;cursor:pointer;touch-action:none;
  transition:transform .16s cubic-bezier(.2,1.2,.35,1),box-shadow .16s ease;
}
.dcard:active{box-shadow:0 2px 0 rgba(90,58,30,.42),0 6px 12px rgba(0,0,0,.4)}
.dcard::after{content:'';position:absolute;inset:4px;border-radius:8px;
  border:1.5px dashed rgba(90,58,30,.28);pointer-events:none}
.dc-art{line-height:0;padding:7px;border-radius:50%;
  background:radial-gradient(circle at 40% 32%,#fff,rgba(255,201,60,.35) 70%,rgba(255,201,60,0));
  box-shadow:inset 0 0 0 2px rgba(90,58,30,.22)}
.dc-name{font:800 11px/1 var(--ff);letter-spacing:.11em;text-transform:uppercase;
  color:var(--brown);text-align:center;text-shadow:0 1px 0 rgba(255,255,255,.8)}
.dc-text{font:400 9.5px/1.28 var(--ff);color:#6b4a26;text-align:center;flex:1 1 auto}
.dc-play{font:800 8px/1 var(--ff);letter-spacing:.16em;text-transform:uppercase;
  color:#fff;background:linear-gradient(#48a355,#25692f);padding:4px 9px;border-radius:8px;
  box-shadow:0 2px 0 #17471f}
.c-knight .dc-art{background:radial-gradient(circle at 40% 32%,#fff,rgba(141,151,166,.4) 70%,transparent)}
.c-victoryPoint .dc-art{background:radial-gradient(circle at 40% 32%,#fff,rgba(255,201,60,.5) 70%,transparent)}
.hand-empty{display:flex;flex-direction:column;align-items:center;gap:8px;
  padding:22px 20px;opacity:.62}
.hand-empty p{margin:0;max-width:280px;text-align:center;
  font:700 11px/1.35 var(--ff);color:var(--brown-2)}

/* --- results -------------------------------------------------------------- */
.results{
  position:absolute;inset:0;display:flex;flex-direction:column;
  padding:var(--gT) var(--gR) var(--gB) var(--gL);
  gap:7px;pointer-events:auto;
  background:radial-gradient(ellipse at 50% 22%,rgba(255,201,60,.20),transparent 62%),
             linear-gradient(180deg,#0b2444,#061529);
}
/* ---------------------------------------------------------- the medallion
   Two states, because a rosette handed to the loser is a picture of somebody
   else being congratulated.

   WIN: a gold plinth holding the cup. Gold regardless of your own colour —
   gold is the thing that means "this one won", and tinting the plinth blue
   because you happen to be blue takes that away.
   LOSE: the winner's colour, holding the winner's portrait. Still a proper
   plate, still centred, still dropped in — losing is a result, not a fault —
   but nothing on it is being given to you.

   The keyline is a warm deep brown at partial opacity rather than near-black.
   At 3px against gold, black reads as a sticker cut out and pasted on. */
/* THE CROWN ROW — medallion, words, medallion, all on one line.
   The single plate hung off the top edge and the title had to clear it by 44px,
   so the head of the sheet spent ~75px saying two things. A pair costs the
   height of one medallion, reads symmetrically, and hands the difference to the
   rows underneath. */
/*   "Make the trophies and the text between them all in a single box, instead
 *    of the two trophies in their own boxes."
 *
 * Right — three plates in a row is three objects, and it is one announcement.
 * The plate moved OUT to the crown and the medallions kept only their glyph, so
 * the row now reads as one thing with a trophy at each end of it. */
.rs-crown{
  align-self:center;display:flex;align-items:center;justify-content:center;
  gap:18px;padding:7px 22px 8px;border-radius:20px;
  background:
    radial-gradient(70% 120% at 50% 0%,rgba(255,201,60,.24),rgba(255,201,60,0) 72%),
    linear-gradient(180deg,#2a1c0c 0%,#160e06 58%,#0b0703 100%);
  border:2.5px solid var(--gold,#ffc93c);
  box-shadow:0 8px 20px rgba(0,0,0,.6),0 0 30px rgba(255,201,60,.32),
             inset 0 -8px 16px rgba(0,0,0,.5),
             inset 0 0 0 1px rgba(255,231,154,.28);
  animation:bannerDrop .5s cubic-bezier(.2,1.3,.35,1);
}
/* Losing: the same one box, wearing the winner's colour instead of gold. */
.results.lost .rs-crown{
  background:linear-gradient(180deg,#1b2635 0%,#0d141d 58%,#070b10 100%);
  border-color:var(--wc,#2f8ffb);
  box-shadow:0 8px 20px rgba(0,0,0,.6),inset 0 -8px 16px rgba(0,0,0,.5),
             inset 0 0 0 1px rgba(255,255,255,.16);
}
.rs-banner{
  flex:0 0 auto;line-height:0;padding:0;border:0;border-radius:0;
  background:none;box-shadow:none;animation:none;
  /* The dark plate and the gold rim are the CROWN's now — see above. What is
     left here is the cup itself, lit against that plate. */
}
.rs-banner svg{display:block}
@keyframes bannerDrop{from{transform:translateY(-46px);opacity:0}to{transform:none;opacity:1}}
.rs-head{text-align:center}
.rs-title{margin:0;font:800 34px/1 var(--ff);letter-spacing:.14em;text-transform:uppercase;
  color:var(--gold);-webkit-text-stroke:2px rgba(12,20,34,.85);
  text-shadow:0 4px 0 rgba(8,14,26,.5),0 0 34px rgba(255,201,60,.55);
  animation:titlePop .5s .12s cubic-bezier(.2,1.5,.4,1) both}
.results.lost .rs-title{color:#e3ecf6;text-shadow:0 4px 0 rgba(8,14,26,.5)}
@keyframes titlePop{from{opacity:0;transform:scale(.7)}to{opacity:1;transform:scale(1)}}
.rs-sub{margin:3px 0 0;font:700 11px/1.2 var(--ff);letter-spacing:.14em;text-transform:uppercase;
  color:rgba(200,222,244,.86)}

/* The tab strip. Hidden wherever both panes fit side by side — see the compact
   block at the foot of this file, which is the only place it is shown. */
.rs-tabs{display:none;gap:6px;justify-content:center;margin-top:5px}
.btn.rs-tab{
  min-height:34px;padding:0 14px;border-radius:11px;border-width:2px;
  font-size:10px;letter-spacing:.14em;
  --f1:#dbe6f4;--f2:#b4c4da;--f3:#96a9c3;--lip:#5d6d84;--fg:#1d2b3d;
}
.btn.rs-tab.on{--f1:#ffe79a;--f2:#ffc93c;--f3:#eaad20;--lip:#a8741a;--fg:#3a2208}

.rs-body{flex:1 1 auto;display:flex;gap:11px;min-height:0;margin-top:6px}
.rs-list{flex:1 1 auto;display:flex;flex-direction:column;gap:6px;overflow:auto;min-width:0}
.rs-row{
  display:flex;align-items:center;gap:9px;padding:6px 10px;border-radius:12px;
  border-left:5px solid var(--c,#2f8ffb);
  opacity:0;transform:translateX(34px);
  transition:opacity .34s ease,transform .34s cubic-bezier(.2,1,.3,1);
}
.rs-row.in{opacity:1;transform:none}
.rs-row.win{box-shadow:0 8px 8px -2px rgba(0,0,0,.4),0 0 22px rgba(255,201,60,.34),
  inset 0 2px 0 rgba(255,255,255,.22),inset 0 0 0 1.5px rgba(255,201,60,.6)}
.rs-av{line-height:0;flex:0 0 auto}
.rs-pos{font:800 17px/1 var(--ff);color:rgba(255,255,255,.5);width:16px;text-align:center}
.rs-row.win .rs-pos{color:var(--gold)}
/* NAME AND BADGES ON ONE LINE, WITH A RULE BETWEEN THEM.
 *
 *   "Can you put the name on one line, and badges on the same line to its right
 *    with a small vertical separator line. That way the text and icons can be
 *    larger and more legible."
 *
 * Exactly the trade the row wanted: stacking the name above the chips spent a
 * whole line on two words and left the chips paying for it in point size. Side
 * by side, the row is one line tall and everything in it can grow. `.rs-bd`
 * keeps its wrap as a safety net — a player holding five different scoring
 * things on a narrow screen takes a second line rather than being cut off. */
.rs-mid{flex:1 1 auto;min-width:0;display:flex;flex-direction:row;
  align-items:center;gap:9px}
.rs-name{flex:0 0 auto;white-space:nowrap;
  font:800 14px/1 var(--ff);letter-spacing:.1em;text-transform:uppercase;color:#fff}
/* Each chip reads left to right as a sentence: what it was, then what it paid.
   The payment used to be a bare "+4" in front of the label, which is the one
   number on this screen with no unit on it — sitting beside a settlement count,
   a city count and a card count that were all bare numbers too, so all four
   looked like the same kind of figure and only one of them was. */
.rs-bd{flex:1 1 auto;min-width:0;display:flex;flex-wrap:wrap;gap:5px;
  padding-left:9px;border-left:2px solid rgba(255,255,255,.17)}
.rs-bd i{display:inline-flex;align-items:center;gap:5px;font-style:normal;
  padding:2px 7px 3px;border-radius:7px;background:rgba(0,0,0,.30);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.12)}
/* The score on a chip: a number and a cup, never a word. See `points()` in
   panels.js for why the word went. */
.rs-bd em.pt{display:inline-flex;align-items:center;gap:2px}
.rs-bd em.pt svg{display:block;margin-top:-1px}
.rs-bd em{font:800 9.5px/1 var(--ff);font-style:normal;letter-spacing:.05em;
  color:var(--gold-l);white-space:nowrap}
.rs-bd u{font:700 9px/1 var(--ff);text-decoration:none;letter-spacing:.08em;
  text-transform:uppercase;color:rgba(214,232,248,.82);white-space:nowrap}
/* Two labels per chip, one shown — see `breakdown()` in panels.js. Wide screens
   get the full name; the compact block below swaps in the short one. */
.rs-bd u.s{display:none}

/* Longest road and knights played, for EVERY player — the numbers the
   breakdown above can only ever show on the one player who scored from them.
   The holder's chip goes gold and names the award; everyone else's is a plain
   reading of how close they came.
   It sits on the scoring end of the row, immediately left of the trophy, and
   it never wraps: two small figures beside the total they did not quite earn,
   rather than a third line under a name. */
.rs-tally{display:flex;flex-wrap:nowrap;gap:5px;flex:0 0 auto}
.rs-tally i{display:inline-flex;align-items:center;gap:5px;font-style:normal;
  padding:2px 7px 3px;border-radius:7px;background:rgba(255,255,255,.06);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.10)}
.rs-tally em{font:800 13px/1 var(--ff);font-style:normal;color:#eaf2fb}
.rs-tally u{font:700 8.5px/1 var(--ff);text-decoration:none;letter-spacing:.1em;
  text-transform:uppercase;color:rgba(206,226,246,.62)}
.rs-tally i.won{background:rgba(255,201,60,.20);
  box-shadow:inset 0 0 0 1.4px rgba(255,201,60,.62)}
.rs-tally i.won em{color:var(--gold-l)}
.rs-tally i.won u{color:var(--gold-l);opacity:.92}
@media (max-height:500px),(max-width:1023px){
  /* ICON AND NUMBER, NOTHING ELSE. The label is what made this two lines, and
     it is the least of the three things in the chip: the road glyph and the
     knight glyph are already the words, and the gold still says who holds the
     award. What is left is the same shape as the trophy chip beside it. */
  .rs-tally{gap:4px}
  .rs-tally i{padding:2px 6px 3px;gap:3px}
  .rs-tally u{display:none}
}
.rs-vp{display:flex;align-items:center;gap:4px}
.rs-vp b{font:800 22px/1 var(--ff);color:var(--gold);
  -webkit-text-stroke:1.8px rgba(10,18,30,.8);paint-order:stroke fill}
.rs-vp i{line-height:0}

.rs-side{flex:0 0 210px;display:flex;flex-direction:column;gap:4px;padding:8px 9px;
  overflow:auto}
.rs-side h4{margin:0 0 3px;font:800 9px/1 var(--ff);letter-spacing:.22em;text-transform:uppercase;
  color:rgba(255,231,154,.78);text-align:center}
.rs-stat{display:flex;align-items:center;gap:7px;padding:3px 5px;border-radius:8px}
.rs-stat:nth-child(even){background:rgba(255,255,255,.06)}
.rs-stat span{flex:1 1 auto;font:700 9.5px/1.15 var(--ff);letter-spacing:.06em;
  text-transform:uppercase;color:rgba(206,226,246,.85)}
.rs-stat b{font:800 13px/1 var(--ff);color:#fff}
.rs-foot{display:flex;justify-content:center;gap:10px;padding-top:2px}
/* The scoreboard is a stop, not a wall: both of these put the island back. */
.rs-x{position:absolute;top:var(--gT);right:var(--gR);z-index:3}

/* ============================================== end of match, over the board
   The announcement and the paper play while the match is still on screen —
   pointer-events:none throughout, and nothing over the middle of the frame. */
.endwin{position:absolute;inset:0;pointer-events:none;overflow:hidden;z-index:6}
.endwin.hid{display:none}
.ew-plate{
  position:absolute;left:50%;top:calc(var(--gT) + 4px);transform:translateX(-50%);
  display:flex;align-items:center;gap:11px;padding:9px 20px 11px;border-radius:16px;
  background:linear-gradient(180deg,rgba(12,32,58,.94),rgba(6,20,40,.94));
  border:2.5px solid var(--wc,#ffc93c);
  box-shadow:0 10px 26px rgba(0,0,0,.6),inset 0 1px 0 rgba(255,255,255,.18),
             0 0 34px rgba(255,201,60,.34);
  white-space:nowrap;
}
.endwin.in .ew-plate{animation:ewDrop .6s cubic-bezier(.2,1.35,.35,1) both}
@keyframes ewDrop{from{opacity:0;transform:translateX(-50%) translateY(-64px)}
  to{opacity:1;transform:translateX(-50%) translateY(0)}}
.ew-ico{line-height:0;color:var(--wl,#ffe79a)}
.ew-words{display:flex;flex-direction:column;gap:2px}
.ew-title{margin:0;font:800 23px/1 var(--ff);letter-spacing:.16em;text-transform:uppercase;
  color:var(--gold);-webkit-text-stroke:1.6px rgba(8,14,26,.7);paint-order:stroke fill;
  text-shadow:0 0 22px rgba(255,201,60,.5)}
.endwin.lost .ew-title{color:#e3ecf6}
.ew-sub{margin:0;font:700 9.5px/1 var(--ff);letter-spacing:.13em;text-transform:uppercase;
  color:rgba(198,220,242,.82)}

/* The shower. Three shapes so it does not read as one repeated sprite: the
   winner's own colour, a cream disc and a turquoise ribbon, each swaying on its
   own offset. All CSS, no canvas, no pointer events. */
.ew-paper{position:absolute;inset:0}
.ew-paper i{
  position:absolute;left:var(--x);top:-26px;width:10px;height:15px;border-radius:2px;
  background:linear-gradient(180deg,var(--wl,#ffe79a),var(--wc,#ffc93c));
  box-shadow:0 1px 3px rgba(0,0,0,.4);opacity:0;
}
.ew-paper i.p1{width:9px;height:9px;border-radius:50%;
  background:linear-gradient(180deg,#fffaf0,#f6e7c6)}
.ew-paper i.p2{width:6px;height:17px;border-radius:3px;
  background:linear-gradient(180deg,#9ef0ff,#3fc4d8)}
.endwin.in .ew-paper i{animation:ewFall var(--t,3s) linear var(--d,0ms) 1 both}
@keyframes ewFall{
  0%{opacity:0;transform:translate3d(0,0,0) rotate(var(--r)) scale(var(--s))}
  8%{opacity:1}
  50%{transform:translate3d(var(--sw,0px),56vh,0)
      rotate(calc(var(--r) + 280deg)) scale(var(--s))}
  85%{opacity:1}
  100%{opacity:0;transform:translate3d(0,114vh,0)
       rotate(calc(var(--r) + 560deg)) scale(var(--s))}
}
@media (prefers-reduced-motion:reduce){.endwin.in .ew-paper i{animation-duration:6s}}

/* --------------------------------------------------- the end-of-match bar */
.endbar{
  position:absolute;left:50%;bottom:var(--gB);transform:translateX(-50%);
  display:flex;align-items:center;gap:9px;padding:7px 11px;border-radius:17px;
  background:linear-gradient(180deg,rgba(12,32,58,.92),rgba(6,20,40,.92));
  border:2px solid rgba(255,201,60,.5);
  box-shadow:0 8px 22px rgba(0,0,0,.55),inset 0 1px 0 rgba(255,255,255,.16);
  pointer-events:auto;z-index:7;
  animation:ebIn .3s cubic-bezier(.2,1.3,.35,1) both;
}
.endbar.hid{display:none}
@keyframes ebIn{from{opacity:0;transform:translateX(-50%) translateY(18px)}
  to{opacity:1;transform:translateX(-50%) translateY(0)}}
.eb-tag{font:800 9px/1.1 var(--ff);letter-spacing:.18em;text-transform:uppercase;
  color:rgba(255,231,154,.8);max-width:56px}
.eb-hint{display:flex;align-items:center;gap:6px;
  font:700 8.5px/1.2 var(--ff);letter-spacing:.1em;text-transform:uppercase;
  color:rgba(198,220,242,.72);max-width:104px}
.eb-hint i{font-style:normal}
.endbar .btn{min-height:46px}

/* With the bar up, the build cards have nothing left to say — the match is
   over. Their space becomes board. */
#ui.endgame .hud-bc,#ui.endgame .hud-br{
  opacity:0;pointer-events:none;transition:opacity .2s ease}

/* The boot splash lives in ui-boot.css. */

/* ============================================================ rotate gate */
#rotate-gate{
  position:absolute;inset:0;z-index:40;display:none;
  align-items:center;justify-content:center;
  background:linear-gradient(180deg,#0b2444,#061529);
}
#rotate-gate.show{display:flex}
.rg-inner{display:flex;flex-direction:column;align-items:center;gap:16px;text-align:center;
  padding:22px}
.rg-inner p{margin:0;font:800 20px/1 var(--ff);letter-spacing:.16em;text-transform:uppercase;
  color:var(--gold-l);text-shadow:0 2px 0 #6b4405}
.rg-inner span{font:700 11px/1.3 var(--ff);letter-spacing:.12em;text-transform:uppercase;
  color:rgba(198,220,242,.75)}
.rg-phone{
  width:58px;height:100px;border-radius:12px;
  background:linear-gradient(160deg,#1e4470,#0d2340);
  border:3px solid rgba(255,201,60,.75);
  box-shadow:0 10px 26px rgba(0,0,0,.55),inset 0 2px 0 rgba(255,255,255,.16);
  position:relative;animation:rgSpin 2.4s ease-in-out infinite;
}
.rg-phone::before{content:'';position:absolute;left:50%;top:7px;transform:translateX(-50%);
  width:20px;height:3px;border-radius:2px;background:rgba(255,201,60,.55)}
.rg-phone::after{content:'';position:absolute;left:50%;bottom:6px;transform:translateX(-50%);
  width:20px;height:20px;border-radius:50%;border:2px solid rgba(255,201,60,.45)}
@keyframes rgSpin{0%,22%{transform:rotate(0)}52%,78%{transform:rotate(-90deg)}100%{transform:rotate(0)}}

/* =========================================================== compact phone
   Verified at 667x375. The rule here is simple: gutters, gaps and text may
   shrink — a resource icon never does. Everything the thumb touches keeps a
   48px box, and the four bottom clusters are sized so they cannot meet. */
@media (max-height:500px),(max-width:1023px){
  :root{--edge:9px}

  /* The gear and the clock stay side by side — together they are narrower than
     the scoreboard under them, so there is nothing to stack out of the way. */
  .tl-row{flex-direction:row;align-items:center;gap:8px}
  /* Pushed down to clear the scoreboard — so its height has to come off the
     same 104px, or the last button in it sits below the fold on a 375-tall
     phone with no scrollbar to say so. See the .pop note in ui-hud.css. */
  .pop{top:calc(var(--gT) + 104px);
    max-height:calc(100dvh - var(--gT) - var(--gB) - 112px)}

  .resbar{height:42px;padding:0 3px}
  .res{padding:0 5px;gap:4px}
  .res b{font-size:19px;min-width:12px}

  .hud-tr{width:158px;padding:4px 5px 6px}
  .rk{height:34px;gap:5px;padding:0 6px 0 5px}
  .rk-av svg{width:28px;height:28px}
  .rk-pos{display:none}
  .rk-name{font-size:11.5px}
  .rk-vp b{font-size:17px;min-width:12px}
  .rk-head{font-size:8px;padding:2px 0 4px}

  .bcard{width:70px;height:96px;padding:5px 3px 0}
  .build-row{gap:5px}
  .bc-name{font-size:12px}
  .bcard[data-kind="settlement"] .bc-name{font-size:10.5px}
  .bc-ico svg{width:26px;height:26px}
  /* Two cost chips must share a 70px card: the 20px object is fixed, so the
     padding and the inter-chip gap are what give. */
  .bc-cost{gap:1px 3px;padding:2px 0 3px}
  .bc-cost .cc{gap:1px;padding:0}
  .bc-cost .cc em{font-size:14px}

  .cbtn{width:50px;height:50px}
  .cbtn .cb-lab{font-size:8px}
  .cbtn.small{width:48px;height:48px}
  .hud-br{gap:6px}


  .ann-txt{font-size:22px}
  /* Every pixel the title plate and the confirm bar give back is board: the
     map is scaled to the space between them, and at 375px tall that is the
     difference between a readable island and a crowded one. */
  .ov-title{font-size:12.5px;letter-spacing:.13em}
  .ov-hint{display:none}
  .ov-bar{padding:5px 9px;gap:8px;border-radius:15px}
  .ov-sel{font-size:9px;max-width:30vw}
  .ov-rail{width:158px;padding:6px}
  .rr{padding:5px 6px 5px 11px}
  .rr-col{font-size:7px;letter-spacing:.16em}
  .rr-you{font-size:7px;padding:2px 5px 3px}
  .rr-av svg{width:26px;height:26px}
  .rr-name{font-size:11.5px}
  .rr-vp{font-size:17px}
  .rr-stats{margin-top:4px;gap:3px}
  .rr-stats i{padding:1px 4px}
  /* Four 20px chips will not sit on one 144px line, so the ICONS come down
     rather than the chips coming out. They used to be hidden until somebody
     held the award, which is exactly backwards on the screen a player opens to
     work out how far off holding it they are. */
  .rr-stats i svg{width:16px;height:16px}
  .rr-stats i em{font-size:12px}
  /* ================================================ the results, on a phone
   *
   *   "Can you make the victory section on the top middle to be smaller, it's
   *    taking up too much space. Also make there be two separate sections so
   *    you can tab between the match report in full size and the points
   *    breakdown... We can also make the play again and see the board buttons
   *    smaller."
   *
   * The header was a 64px medallion floating above a 34px headline and a
   * subtitle — about a third of a 375px-tall screen spent saying a thing the
   * cup on the winner's row already says. Halved, and the medallion tucked up
   * so the sheet starts where the standings do.
   *
   * And the two panes stop competing: the tab strip appears, each pane gets the
   * whole width, and only one is up at a time. That is what stops a player's
   * chips wrapping to a second line — which was the real reason the fourth
   * player fell off the bottom. */
  /* The plate is sized by its padding and the glyph inside it, not by a box —
     so shrink those two rather than pinning a width and fighting it. */
  .rs-crown{gap:12px;padding:4px 14px 5px;border-radius:16px;border-width:2px}
  .rs-banner svg{width:32px;height:32px}
  .rs-title{font-size:21px;letter-spacing:.1em}
  .rs-sub{font-size:8.5px;margin-top:2px}

  .rs-tabs{display:flex;margin-top:4px}
  .btn.rs-tab{min-height:28px;padding:0 12px;font-size:9px}
  .rs-body{margin-top:4px}
  .rs-list{gap:4px}
  /* One pane at a time, each the full width of the sheet. */
  .rs-side{flex:1 1 auto;min-width:0}
  .results .rs-side{display:none}
  .results.tab-report .rs-list{display:none}
  .results.tab-report .rs-side{display:flex}
  .rs-stat{padding:4px 7px}
  .rs-stat span{font-size:11px}
  .rs-stat b{font-size:15px}

  /* One line per player, so every figure on it can afford to be legible. The
     row used to be a name stacked over a wrapped strip of 8px chips; the height
     that stack cost is what pays for the type size here. */
  /*   "Make the text for __ settlements, __ cities, __ victory cards a bit
   *    larger. I'd also like for the player lines to be slightly taller so they
   *    have a bit more space."
   *
   * Both, and they have to be paid for: four rows fitting the list without a
   * scrollbar is a property worth keeping. The height comes from the crown (a
   * single plate is shallower than a plate holding a plate), from the tab strip
   * and body margins, and from the gap between rows — none of which anybody
   * reads — and it goes into the row padding and the chip type, which everybody
   * does. */
  .rs-row{padding:6px 8px;gap:7px}
  .rs-mid{gap:8px}
  .rs-name{font-size:13.5px}
  .rs-bd{gap:4px;padding-left:8px}
  .rs-bd i{padding:2px 7px 3px;gap:5px}
  .rs-bd i svg{width:17px;height:17px}
  .rs-bd em,.rs-bd u{font-size:10.5px}
  .rs-bd u:not(.s){display:none}
  .rs-bd u.s{display:inline}
  .rs-tally i svg{width:17px;height:17px}
  .rs-tally em{font-size:13px}
  .rs-vp b{font-size:20px}
  .rs-vp i svg{width:21px;height:21px}

  /* Two buttons that were sized for a title screen, on a results sheet. */
  .rs-foot .btn{min-height:42px;padding:0 16px;font-size:12px;gap:6px}
  .rs-foot .btn .sb-ico svg{width:18px;height:18px}
  .dcard{flex-basis:108px;width:108px;min-height:140px}
  .hand{min-height:140px}
  /* EVERY PIXEL HERE IS SPENT ON THE ARROWS, and the order of who gives it up
     is now settled:
       the two lane BANDS give first — they were 30px slabs of colour carrying
         two words each, which is the cheapest decoration on the sheet;
       the CLOSE BUTTON gives next — 48px of circle for an action the Escape
         key, the scrim and the back gesture all also perform;
       the FOOT button gives a little;
       the CARD gives last, and no resource icon goes under 26.
     The ARROWS DO NOT GIVE AT ALL. 38px was under the thumb minimum on the one
     control a player hits four times in a row — "35px is under the 44px thumb
     minimum, and these are tapped four times in a row" — so they hold 44 here
     and the 10px gap between an arrow and its card holds too, because that gap
     is the entire separation between spending one lot and spending sixteen. */
  .sheet{gap:5px;padding:8px 11px 9px}
  .sheet.trade .sheet-head .cbtn.small{width:40px;height:40px;min-width:40px;min-height:40px}
  .trade .sheet-foot .btn.big{min-height:44px;font-size:13px}
  .trade-body{gap:5px}
  .tr-row{gap:6px}
  .tr-col{gap:10px}
  .tr-card{min-height:60px;padding:4px 4px 3px;gap:3px}
  .tr-ico svg{width:26px;height:26px}
  .tr-now{font-size:22px}
  .tr-was{font-size:11px}
  .tr-act{height:18px}
  .tr-act u{font-size:9.5px;letter-spacing:.05em}
  .tr-arr{height:44px}
  .tr-arr.up::after{top:-9px}
  .tr-arr.dn::after{bottom:-9px}
  .tr-cap{font-size:11px;padding:3px 10px 4px;gap:7px;letter-spacing:.16em}
  .tr-cap::before,.tr-cap::after{font-size:11px}
  .tc-live{font-size:8.5px;padding:2px 6px 3px;right:7px}
  .tc-cost{font-size:11px;padding:2px 7px 3px;left:7px}
  .rs-foot{gap:8px}
  .ew-plate{padding:7px 15px 9px;gap:9px;border-radius:14px}
  .ew-title{font-size:19px}
  .ew-sub{font-size:8.5px}
  .endbar{gap:7px;padding:6px 8px}
  .eb-tag{display:none}
  .eb-hint{max-width:92px;font-size:8px}
}

/* Very short viewports: reclaim vertical space without shrinking any icon. */
@media (max-height:380px){
  .bcard{height:92px}
  .bc-ico{margin:0}
  .bc-ico svg{width:24px;height:24px}
}

/* THE TRADE SHEET'S OWN FLOOR, AND IT IS LOWER THAN 380.
 *
 * These rules used to ride in the block above, which starts at 380px tall — and
 * 375 is the height of a phone held sideways, the single most common way this
 * game is played. So the 8px column gap meant for a 320px screen was being
 * applied on the one screen where a reviewer measured it and said no: "the two
 * most different actions in the sheet are 6px apart". 344 is under every phone
 * and over every one of them turned on its side. */
@media (max-height:344px){
  /* 320px tall is the floor this game is built to, and the trade sheet is the
     tallest thing in it. The arrows STILL do not move — they are the reason the
     rest of this block exists — so the last of the slack comes out of the two
     bands, the head and the foot, in that order. The card keeps a 26px icon and
     a 20px numeral, which is the smallest either has ever been allowed to be.

     AND IT HAS TO ADD UP TO LESS THAN THE BOX, WHICH IT DID NOT.

       "The Trade button is bottomed out at 640x320. Button occupies y 263-304,
        the sheet's inner bottom rim is ~306: 3px of clearance below the primary
        call to action against ~10px above it."

     Measured cold, the sheet wanted 309px inside a box that `max-height` clamps
     to 300, and a flex column that overflows does not distribute the shortfall
     politely: the whole 9px came out of `.trade-body`, whose own contents then
     spilled straight through the bottom of it — which is why the brown band was
     sitting ON the foot's rule and the button was pressed against the rim with
     the sheet's own padding-bottom nowhere to be seen. Nothing was "clipped",
     so every fit check the harness had passed while it happened.

     So this block now budgets. Every trim below is one or two pixels, taken in
     the order the note above sets out — bands, foot, card plate, sheet gap —
     and they add up to eleven, which pays back the nine and buys the button the
     8px of padding underneath that makes it sit in the sheet rather than on the
     edge of it. Arrows, the 10px arrow-to-card gap, the 26px icon, the 20px
     numeral and the 40px button are all still untouchable. */
  .sheet.trade{padding:5px 10px 8px;gap:4px}
  .sheet.trade .sheet-head{gap:5px}
  .sheet.trade .sheet-title{font-size:13px;letter-spacing:.14em}
  .sheet.trade .sheet-head .cbtn.small{width:34px;height:34px;min-width:34px;min-height:34px}
  .sheet.trade .cbtn.small .cb-ico svg{width:15px;height:15px}
  .trade .sheet-foot{padding-top:1px;border-top-width:1.5px}
  .trade .sheet-foot .btn.big{min-height:40px;font-size:12px;border-radius:13px}
  .trade-body{gap:2px}
  .tr-cap{font-size:10px;padding:3px 9px 3px;gap:6px}
  .tr-cap::before,.tr-cap::after{font-size:10px}
  .tr-card{min-height:50px;padding:3px 4px 2px;gap:2px}
  .tr-now{font-size:20px}
  .tr-act{height:14px}
  .tc-cost{font-size:10.5px;padding:2px 6px 3px;left:6px}
  .tc-live{font-size:8px;padding:2px 5px 3px;right:6px}
  /* THE GAP DOES NOT SHRINK EITHER. It went to 8 here for four pixels of
     height, and four pixels is not worth narrowing the only thing standing
     between "spend one lot" and "spend all of it" — they came out of the
     sheet's own padding and the body's gap instead. */
  .tr-arr.up::after{top:-7px}
  .tr-arr.dn::after{bottom:-7px}
}

@media (prefers-reduced-motion:reduce){
  *{animation-duration:.001s !important;transition-duration:.06s !important}
}

/* The pause screen has to keep the standings on ANY screen, so the rail stops
   disappearing below 560px and goes to a 126px column instead. Everything in
   it gives a little; nothing in it is dropped. */
@media (max-width:560px){
  .ov-rail{width:126px;padding:5px;gap:4px}
  .rr{padding:4px 5px 4px 10px}
  .rr-av svg{width:22px;height:22px}
  .rr-name{font-size:10.5px}
  .rr-col{display:none}
  .rr-vp{font-size:15px}
  .rr-stats{margin-top:3px;gap:2px}
  .rr-stats i{padding:1px 3px;gap:2px}
  .rr-stats i svg{width:14px;height:14px}
  .rr-stats i em{font-size:11px}
}
