/* hax-haxopoly.css — Haxopoly play surface, mobile-first per HaxopolyDesign/docs/design.md §10.
   Batch F (2026-06-03): live-state styling — owner badges, building markers, tokens, mortgage
   dim, centre HUD with viewer's Credits + piece + state lines, status chrome, action chrome,
   resign-confirm overlay. Static board layout from the thin slice carries through unchanged.

   iOS Safari guardrails per [[reference-ios-safari-css-traps]]:
   • box-sizing: border-box on every cell — padding + the sector band don't eat the grid gap
   • min-width: 0 + min-height: 0 on grid items — webkit otherwise defaults to min:auto and
     children can overflow the row
   • overflow: hidden on the board container so a single glyph mid-render can't burst the loop

   Mobile-first sizing: the board sits inside a container-query context. Cell padding + font
   sizes use `cqi` (container-query inline) units so the whole layout scales smoothly from a
   360px portrait iPhone up through desktop without media queries. */

/* ---------- Board container + grid ---------- */

.haxopoly-play {
    width: 100%;
    max-width: 560px;               /* desktop cap — board was overpowering wide viewports at 720 */
    margin: 0 auto;
    padding: 8px;
    box-sizing: border-box;
}

.haxopoly-board {
    /* The board fills the available width up to ~560px (see .haxopoly-play max-width);
       aspect-ratio: 1 keeps it square.
       Non-uniform 7×7 grid: outer rows/columns (which carry corners + property tiles + Wormhole
       transit + card spaces + Starship Maintenance) get 1.5fr each; inner rows/columns (which
       only contain the centre hub) get 1fr each. Total 8fr per axis, so outer cells are
       1.5/8 = 18.75% of board (≈ 105px on a 560px board) and inner cells are 1/8 = 12.5%
       (≈ 70px). That gives every property tile ≈ 50% more area than a uniform-fr layout — more
       room for the name + price + building markers without growing the board's footprint. The
       centre hub still spans the 5 inner cells. */
    container-type: inline-size;
    aspect-ratio: 1 / 1;
    display: grid;
    grid-template-rows: 1.5fr 1fr 1fr 1fr 1fr 1fr 1.5fr;
    grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr 1fr 1.5fr;
    gap: 2px;
    background: #0B1437;            /* §14.2 deep-space navy frame */
    border: 2px solid #14181F;
    border-radius: 6px;
    overflow: hidden;
    box-sizing: border-box;
}

/* ---------- Cells (every grid item) ---------- */

.haxopoly-cell {
    background: #FCFCFD;            /* §14.2 card cream */
    color: #14181F;                 /* §14.2 ink */
    box-sizing: border-box;
    min-width: 0;                   /* iOS Safari grid-item overflow guard */
    min-height: 0;
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    justify-content: center;
    overflow: hidden;
    font-family: var(--mud-typography-default-family, sans-serif);
}

/* ---------- Corner tiles ---------- */

.haxopoly-cell--corner {
    background: #FAF6EC;            /* slightly warmer than the property cream */
    padding: 4cqi 2cqi;
    align-items: center;
    justify-content: center;
    gap: 1cqi;
}

.haxopoly-corner-icon {
    width: 60%;
    height: auto;
    max-height: 60%;
    object-fit: contain;
}

.haxopoly-corner-label {
    font-size: clamp(0.55rem, 2.4cqi, 0.85rem);
    font-weight: 600;
    text-align: center;
    line-height: 1.05;
    letter-spacing: 0.02em;
}

/* ---------- Edge property + transit + card-space tiles ---------- */

.haxopoly-cell--top,
.haxopoly-cell--right,
.haxopoly-cell--bottom,
.haxopoly-cell--left {
    /* Universal edge-cell padding — keeps the name + price + glyph clear of the cell borders at
       every viewport. Was 1.6cqi / 1.2cqi which resolved to ~6px / ~5px on mobile and looked
       cramped on the live render. Flat 6px is comfortable + the band-side padding overrides
       below carry the band's own clearance. */
    padding: 6px 6px;
    gap: 0.8cqi;
}

/* Sector colour band — a thin colored strip flush against the inside edge of each property
   tile (the side facing the centre hub). All four edge variants use position: absolute so the
   band butts directly against the cell border, no white-space gap between the band and the
   inside edge. The cell adds matching padding on that side so the body content doesn't overlap
   the band.
   12% of cell width/height = a clearly thin strip; body takes the remaining 88%.
   Earlier in-flex-flow approach (top/bottom only) floated the band in the middle of the cell
   because justify-content: center centered the band+body cluster — symptom the operator caught
   in the live render. Absolute positioning is consistent for all four edges and unambiguous. */
.haxopoly-cell--top,
.haxopoly-cell--bottom,
.haxopoly-cell--left,
.haxopoly-cell--right {
    position: relative;
    /* Breathing room between the glyph (Wormhole / Anomaly / Guild / Starship Maintenance) and
       the tile name + price stack below. Overrides the 0.8cqi value on the shared edge-cell
       rule above; 14px ≈ a blank line at the tile-name font size, visually distinct at every
       viewport. */
    gap: 14px;
}

.haxopoly-cell--top {
    padding-bottom: calc(12% + 4px);
}

.haxopoly-cell--bottom {
    padding-top: calc(12% + 4px);
}

.haxopoly-cell--right {
    padding-left: calc(12% + 4px);
}

.haxopoly-cell--left {
    padding-right: calc(12% + 4px);
}

.haxopoly-cell--top .haxopoly-sector-band {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;                      /* inside edge = the side facing the centre */
    height: 12%;
    min-height: 6px;
}

.haxopoly-cell--bottom .haxopoly-sector-band {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;                         /* inside edge for bottom-row tiles */
    height: 12%;
    min-height: 6px;
}

.haxopoly-cell--right .haxopoly-sector-band {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;                        /* inside edge for right-column tiles */
    width: 12%;
    min-width: 6px;
}

.haxopoly-cell--left .haxopoly-sector-band {
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;                       /* inside edge for left-column tiles */
    width: 12%;
    min-width: 6px;
}

/* ---------- Tile body (name + price stack) ---------- */

.haxopoly-tile-body {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    /* Breathing room between the property name and the purchase-price line below it. Bumped
       to 12px ≈ a blank line at the tile-name font size for the side-column tiles which have
       the full cell height available (their sector band runs vertically along the inside
       edge). Top + bottom row tiles override below with a tighter gap because their band
       eats into cell height. */
    gap: 12px;
    min-width: 0;
    min-height: 0;
}

/* Top + bottom row property tiles have ~12% of cell height consumed by the sector band, and
   left + right column tiles use the inner 1fr rows (shorter than the 1.5fr outer rows) — both
   variants are vertically constrained relative to the side-band 1.5fr tiles, so the 12px
   universal body gap pushed the name + price stack over the edge for long names. Tighten the
   gap here so the wrapping case fits without bumping the cell borders. */
.haxopoly-cell--top .haxopoly-tile-body,
.haxopoly-cell--bottom .haxopoly-tile-body,
.haxopoly-cell--left .haxopoly-tile-body,
.haxopoly-cell--right .haxopoly-tile-body {
    gap: 5px;
}

.haxopoly-tile-name {
    font-size: clamp(0.50rem, 1.85cqi, 0.78rem);
    font-weight: 600;
    line-height: 1.05;
    letter-spacing: 0.01em;
    /* word-break: break-word is equivalent to "break anywhere" — it was wrapping mid-word
       ("Wormhol e Alpha", "Anomal y"). The proper modern pair: word-break: normal honours
       word boundaries; overflow-wrap: break-word only breaks INSIDE a word when the word
       itself can't fit at all, which is the polite-wrap behavior we want. */
    word-break: normal;
    overflow-wrap: break-word;
    hyphens: auto;
}

.haxopoly-tile-price {
    font-size: clamp(0.45rem, 1.6cqi, 0.7rem);
    color: #4B5363;
    font-weight: 500;
    letter-spacing: 0.02em;
}

/* Starship Maintenance tax label — same role as the price label (sub-text under the tile
   name), but coloured warm-red to telegraph "this space costs you" the way the Mtg overlay
   does. The percentage text itself carries the meaning so color is never the sole cue. */
.haxopoly-tile-tax {
    font-size: clamp(0.45rem, 1.6cqi, 0.7rem);
    color: #B91C1C;
    font-weight: 600;
    letter-spacing: 0.02em;
}

/* ---------- Space icons (Wormhole, Anomaly, Guild, Starship Maintenance) ---------- */

.haxopoly-space-icon {
    /* 50% width with height: auto keeps the icon square based on cell width without reserving
       50% of cell HEIGHT in the flex layout — the prior height: 50% rule ate vertical room
       proportional to cell height (74px on outer rows) and pushed the price line off the
       bottom of Wormhole tiles. height: auto lets the icon be just as tall as it is wide
       (square PNGs). */
    width: 50%;
    height: auto;
    object-fit: contain;
    margin: 0.4cqi auto;
    flex: 0 0 auto;
}

/* Left + right column icon-bearing tiles (Anomaly, Maintenance) use the shorter inner 1fr row
   height (~49px on iPhone XR vs ~74px for the 1.5fr outer rows feeding top + bottom icon
   tiles). The 50% icon was bumping the cell border on those sides; drop to 38% so the icon +
   name fit comfortably with the new 6px padding. Also tighten the cell flex gap (icon ↔ body)
   to 6px because the universal 14px gap eats more space than the short cells can spare. */
.haxopoly-cell--left .haxopoly-space-icon,
.haxopoly-cell--right .haxopoly-space-icon {
    width: 38%;
    height: 38%;
}

.haxopoly-cell--left,
.haxopoly-cell--right,
.haxopoly-cell--top,
.haxopoly-cell--bottom {
    /* Cell flex gap between the icon and the body for icon-bearing tiles (Wormhole / Anomaly /
       Guild / Maintenance). 6px works for both axes: top + bottom Wormhole tiles need it
       because they carry icon + 2-line name + price in the inner 1fr column (~50px wide on
       small viewports); the universal 14px above was overflowing the price. Property tiles
       have a single body child, so flex gap doesn't apply — this rule only affects the
       icon-bearing variants. */
    gap: 6px;
}

/* ---------- Sector colour palette (§14.2) ---------- */

.haxopoly-sector--darkpurple { background: #6B3FA0; }
.haxopoly-sector--lightblue  { background: #8FD4F0; }
.haxopoly-sector--pink       { background: #E66FA0; }
.haxopoly-sector--orange     { background: #F0962D; }
.haxopoly-sector--red        { background: #D8352A; }
.haxopoly-sector--yellow     { background: #F4C430; }
.haxopoly-sector--green      { background: #2F9E52; }
.haxopoly-sector--darkblue   { background: #1F3A93; }

/* ---------- Centre HUD (§10.2 lean centre) ---------- */

.haxopoly-centre {
    background: #0B1437;
    color: #FCFCFD;
    box-sizing: border-box;
    min-width: 0;
    min-height: 0;
    /* flex column with the stack growing to fill (its own content vertically centred via
       its own justify-content) and the recap docking at the bottom — keeps the player info
       on the eye line while the "what just happened" banner is anchored at the inner edge
       of the bottom row. */
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 4cqi 3cqi;
    /* position: relative lets the in-hub Roll button render in the centre flex flow while the
       Resign button absolutely-positions into the top-right corner (2026-06-04 — moved into
       the hub so both action affordances are visible without scrolling on default viewports). */
    position: relative;
}

.haxopoly-centre-stack {
    /* 2-column layout (2026-06-04): piece glyph LEFT, credits + roll/waiting + kept cards
       RIGHT. Frees vertical room above the docked recap banner. */
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    flex: 1 1 auto;
    gap: 4cqi;
    text-align: center;
}

.haxopoly-centre-info {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2cqi;
    min-width: 0;
}

.haxopoly-roll-hub {
    width: 18cqi;
    height: 18cqi;
    min-width: 64px;
    min-height: 64px;
    border-radius: 50%;
    background: linear-gradient(135deg, #A3E635 0%, #6FB91D 100%);
    color: #14181F;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
    border: 3px solid #FCFCFD;
}

.haxopoly-roll-hub-glyph {
    font-weight: 800;
    font-size: clamp(0.85rem, 3.8cqi, 1.4rem);
    line-height: 1;
}

.haxopoly-roll-hub-label {
    font-weight: 700;
    font-size: clamp(0.55rem, 2.2cqi, 0.9rem);
    margin-top: 2cqi;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.haxopoly-centre-note {
    font-size: clamp(0.60rem, 2.0cqi, 0.85rem);
    color: #C7CFDC;
    max-width: 26ch;
    line-height: 1.3;
}

/* ---------- Owner badges (corner piece-glyph + accent ring per §10.2) ---------- */

.haxopoly-owner-badge {
    position: absolute;
    top: 0.4cqi;
    right: 0.4cqi;
    /* Ownership "tag" — visually distinct from a player token. Token is 6cqi (min 20px);
       badge is 4cqi (min 13px) so the eye reads them as marker vs piece, not as two pieces
       on the same tile. Tightened 2026-06-04 per operator. */
    width: 4cqi;
    height: 4cqi;
    min-width: 13px;
    min-height: 13px;
    border-radius: 50%;
    background: #FCFCFD;
    border: 1px solid #14181F;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
    z-index: 2;
}

.haxopoly-owner-badge--p1 {
    /* Challenger accent ring — the player's profile colour (set on the board root as --hax-p1),
       falling back to §14.2 challenger lime when unset or on a same-colour collision. */
    box-shadow: 0 0 0 1px var(--hax-p1, #A3E635), 0 1px 2px rgba(0, 0, 0, 0.18);
}

.haxopoly-owner-badge--p2 {
    /* Challengee accent ring — profile colour (--hax-p2), fallback §14.2 challengee coral. */
    box-shadow: 0 0 0 1px var(--hax-p2, #F4845F), 0 1px 2px rgba(0, 0, 0, 0.18);
}

.haxopoly-owner-badge-img {
    /* Tighter than the token's 78% so the smaller badge doesn't crowd its ring. */
    width: 85%;
    height: 85%;
    object-fit: contain;
}

/* Owner badge MUST sit on the OUTER perimeter of the tile (the edge facing AWAY from the
   centre), NEVER on the sector colour band — the band holds the §14.4 building markers and a
   badge over it would cover the colonies / starport. The base rule pins badges at
   top: right (correct for top-row + right-column tiles since their outer is the top/right
   corner). Bottom + left edges need an explicit override. 2026-06-04 operator request. */
.haxopoly-cell--bottom .haxopoly-owner-badge {
    top: auto;
    bottom: 0.4cqi;
    /* right: 0.4cqi from base — outer bottom-right corner */
}

.haxopoly-cell--left .haxopoly-owner-badge {
    right: auto;
    left: 0.4cqi;
    /* top: 0.4cqi from base — outer top-left corner */
}

/* ---------- Building markers — sit ON the sector band per §14.4 ---------- */

/* The band hosts the marker row. Top + bottom row bands lay out horizontally; left + right
   column bands lay out vertically. Markers shrink to fit the band's thin dimension. */
.haxopoly-sector-band {
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

.haxopoly-cell--top .haxopoly-sector-band,
.haxopoly-cell--bottom .haxopoly-sector-band {
    flex-direction: row;
    gap: 4px;
}

.haxopoly-cell--left .haxopoly-sector-band,
.haxopoly-cell--right .haxopoly-sector-band {
    flex-direction: column;
    gap: 4px;
}

.haxopoly-building-markers {
    display: contents;            /* let the marker children flex inside the band directly */
}

.haxopoly-building-marker {
    background: #FFF8E1;          /* cream fill */
    border: 1px solid #14181F;    /* ink outline */
    flex: 0 0 auto;
}

.haxopoly-building-marker--colony {
    /* Bumped 5 → 7px on 2026-06-04 — colonies were hard to spot on the band at default
       viewport. Still fits the band's 12% thickness on outer-row + inner-row tiles on
       desktop; mild overflow on the narrowest mobile inner-column cells but the shape
       reads clearly as a square. */
    width: 7px;
    height: 7px;
}

.haxopoly-building-marker--starport {
    /* Bumped 7 → 9px on 2026-06-04 — starport is a single marker per band so it has room
       to be a touch bolder than a colony. */
    width: 9px;
    height: 9px;
    border-radius: 50%;
}

/* ---------- Player tokens (current position on the loop) ---------- */

.haxopoly-token {
    position: absolute;
    bottom: 0.6cqi;
    width: 6cqi;
    height: 6cqi;
    min-width: 20px;
    min-height: 20px;
    border-radius: 50%;
    /* Solid player-colour fill per 2026-06-04 operator visual brief — the cream-fill +
       coloured-border combo read identically to the owner-tag badges + made it hard to
       tell "this is the player's CURRENT position" vs "this property is owned". The
       solid colour fill makes position-tokens pop. */
    border: 2px solid #14181F;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
    z-index: 3;
}

/* Two tokens on the same cell — separate left/right so they don't overlap. */
.haxopoly-token--p1 {
    left: 0.6cqi;
    background: var(--hax-p1, #A3E635);   /* profile colour; fallback §14.2 challenger lime */
}

.haxopoly-token--p2 {
    right: 0.6cqi;
    background: var(--hax-p2, #F4845F);   /* profile colour; fallback §14.2 challengee coral */
}

.haxopoly-token-img {
    width: 78%;
    height: 78%;
    object-fit: contain;
    /* The piece PNG is a dark silhouette on transparent — invert it so the glyph reads
       white on the solid coloured background. Same trick the Wormhole medallion uses
       (no new assets needed). */
    filter: brightness(0) invert(1);
}

/* ---------- Mortgaged plot dim ---------- */

.haxopoly-cell.is-mortgaged {
    filter: grayscale(0.55) brightness(0.78);
}

.haxopoly-cell.is-mortgaged::after {
    content: "Mtg";
    position: absolute;
    bottom: 0.4cqi;
    right: 0.4cqi;
    font-size: clamp(0.45rem, 1.4cqi, 0.65rem);
    color: #B91C1C;
    font-weight: 700;
    letter-spacing: 0.03em;
    background: #FCFCFD;
    padding: 0.2cqi 0.6cqi;
    border-radius: 2px;
    z-index: 1;
}

/* ---------- Centre HUD additions (Credits chip + piece label + state line) ---------- */

.haxopoly-centre-piece {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.8cqi;
}

.haxopoly-centre-piece-disc {
    /* Solid player-colour disc + dark ring + white silhouette glyph — same visual as the
       board tokens so the centre glyph reads as "your piece" on the loop. Background
       colour set by the --p1 / --p2 variants below. */
    width: 14cqi;
    height: 14cqi;
    min-width: 44px;
    min-height: 44px;
    border-radius: 50%;
    border: 2px solid #14181F;
    background: #A3E635;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}

.haxopoly-centre-piece-disc--p1 {
    background: var(--hax-p1, #A3E635);   /* profile colour; fallback §14.2 challenger lime */
}

.haxopoly-centre-piece-disc--p2 {
    background: var(--hax-p2, #F4845F);   /* profile colour; fallback §14.2 challengee coral */
}

.haxopoly-centre-piece-img {
    width: 78%;
    height: 78%;
    object-fit: contain;
    /* Dark silhouette PNG → white glyph on the coloured disc; same trick the board token
       + wormhole medallion use. */
    filter: brightness(0) invert(1);
    /* The centre player-piece glyph is a pure display element — no click handler. Harden it so
       it's fully inert: on mobile a bare <img> otherwise invites the long-press image callout
       ("Save Image") + drag-to-save gesture, which is undesirable on a game glyph. (It was also a
       suspect in a board-wedge a tester reported 2026-06-08, but that turned out non-reproducible
       — likely a transient session; this is defensive hardening, not a confirmed-bug fix.)
       Making a no-op glyph inert can't regress anything; the rest of the board is untouched.
         - pointer-events:none        → the glyph receives no taps (it does nothing anyway)
         - -webkit-touch-callout:none → suppress the iOS long-press image callout
         - -webkit-user-drag:none     → suppress the native image-drag */
    pointer-events: none;
    -webkit-touch-callout: none;
    -webkit-user-drag: none;
}

.haxopoly-centre-piece-label {
    font-size: clamp(0.55rem, 2.0cqi, 0.85rem);
    font-weight: 600;
    color: #C7CFDC;
    letter-spacing: 0.02em;
}

/* ---------- Resolved-state centre takeover (2026-06-06 operator request) ----------
   Replaces the single-viewer piece/credits HUD with stacked stats blocks — one per
   player — so each player can see who won and by how much in §8 net-worth terms. */

.haxopoly-resolved-stats {
    /* flex: 1 1 auto + justify-content: center mirrors .haxopoly-centre-stack — grows to fill
       the navy hub so the recap banner docks at the BOTTOM of the centre area instead of
       pinning directly under the stats. */
    display: flex;
    flex-direction: column;
    justify-content: center;
    flex: 1 1 auto;
    gap: 2.4cqi;
    width: 100%;
    padding: 1cqi 1cqi;
    box-sizing: border-box;
}

.haxopoly-resolved-card {
    display: flex;
    align-items: center;
    gap: 2.4cqi;
    padding: 1.4cqi 2cqi;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.12);
}

.haxopoly-resolved-card.is-winner {
    background: rgba(163, 230, 53, 0.10);          /* §14.2 lime tint */
    border-color: rgba(163, 230, 53, 0.45);
}

/* Shrink the piece disc inside the resolved card vs the live-play centre HUD — two cards
   stacked in the cramped mobile centre need a smaller glyph so the recap stays docked at
   the bottom instead of getting pushed into the property row below. */
.haxopoly-resolved-card .haxopoly-centre-piece-disc {
    width: 11cqi;
    height: 11cqi;
    min-width: 38px;
    min-height: 38px;
    flex-shrink: 0;
}

.haxopoly-resolved-card-info {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: 0.6cqi;
    min-width: 0;                                  /* allow text shrink on narrow centre */
}

.haxopoly-resolved-card-name {
    display: flex;
    align-items: center;
    gap: 1.2cqi;
    font-size: clamp(0.55rem, 2.0cqi, 0.85rem);
    font-weight: 700;
    color: #FCFCFD;
    letter-spacing: 0.02em;
}

.haxopoly-resolved-card-winner-pill {
    font-size: clamp(0.40rem, 1.4cqi, 0.65rem);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #14181F;
    background: #A3E635;
    padding: 0.2cqi 1cqi;
    border-radius: 999px;
}

.haxopoly-resolved-card-networth {
    display: flex;
    align-items: baseline;
    gap: 1.6cqi;
    flex-wrap: wrap;
}

.haxopoly-resolved-card-networth-label {
    font-size: clamp(0.48rem, 1.8cqi, 0.85rem);
    color: #A3E635;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.haxopoly-resolved-card-networth-value {
    /* Lower min so mobile shrinks more (the 7,696 Cr was floored too large on a 430px viewport
       and stretched the two cards past the navy hub's vertical budget, pushing the recap into
       the property row below). Desktop unchanged at the 2rem max. */
    font-size: clamp(0.90rem, 4.4cqi, 2rem);
    font-weight: 800;
    color: #FCFCFD;
    font-variant-numeric: tabular-nums;
    line-height: 1;
}

.haxopoly-resolved-card-breakdown {
    font-size: clamp(0.52rem, 1.9cqi, 0.8rem);
    color: #C7CFDC;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.01em;
    line-height: 1.3;
}

.haxopoly-centre-credits {
    display: flex;
    align-items: baseline;
    gap: 1cqi;
    padding: 1.6cqi 3cqi;
    background: rgba(255, 255, 255, 0.08);
    border-radius: 6px;
    border: 1px solid rgba(255, 255, 255, 0.18);
}

.haxopoly-centre-credits-glyph {
    font-size: clamp(0.60rem, 2.2cqi, 0.95rem);
    color: #A3E635;
    font-weight: 700;
    letter-spacing: 0.04em;
}

.haxopoly-centre-credits-value {
    font-size: clamp(0.95rem, 4cqi, 1.6rem);
    font-weight: 800;
    color: #FCFCFD;
    font-variant-numeric: tabular-nums;
    line-height: 1;
}

.haxopoly-centre-state {
    /* Top-left pin (2026-06-04) — mirrors the Resign button's top-right corner. Detention /
       skip-flag is a passive status indicator, not an action; corner placement keeps it
       visible without competing with the centred piece + credits stack. */
    position: absolute;
    top: 6px;
    left: 6px;
    z-index: 5;
    font-size: clamp(0.55rem, 1.7cqi, 0.75rem);
    color: #F4845F;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    text-align: left;
    padding: 4px 8px;
    background: rgba(244, 132, 95, 0.12);
    border: 1px solid rgba(244, 132, 95, 0.45);
    border-radius: 4px;
}

/* ---------- Header + status badges + recap line + action chrome ---------- */

.haxopoly-play-game {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.haxopoly-play-header {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.haxopoly-play-status {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.haxopoly-play-status-badge {
    font-weight: 700;
    font-size: 0.95rem;
    padding: 4px 12px;
    border-radius: 4px;
    letter-spacing: 0.02em;
}

.haxopoly-play-status-badge.turn-you {
    background: #A3E635;
    color: #14181F;
}

.haxopoly-play-status-badge.turn-opp {
    background: #2A3142;
    color: #FCFCFD;
}

.haxopoly-play-status-badge.win {
    background: #A3E635;
    color: #14181F;
}

.haxopoly-play-status-badge.loss {
    background: #B91C1C;
    color: #FCFCFD;
}

.haxopoly-play-meta {
    font-size: 0.85rem;
    color: #6B6B6B;
    font-variant-numeric: tabular-nums;
}

.haxopoly-play-recap {
    /* In-hub recap docked at the BOTTOM of the centre navy area (2026-06-04 operator
       request, re-tightened after a too-big first cut). Same shape as the original
       below-board recap (ink-on-cream with the lime left-accent), just relocated. Sibling
       of .haxopoly-centre-stack so the flex layout pins it to the bottom while the stack
       grows to fill above. Font scales down on small viewports via clamp + cqi so mobile
       doesn't crowd the surrounding tiles. */
    flex: 0 0 auto;
    margin: 0;
    padding: 6px 10px;
    background: #FAF6EC;
    border-left: 3px solid #A3E635;
    border-radius: 0 4px 4px 0;
    font-size: clamp(0.65rem, 2.4cqi, 0.9rem);
    line-height: 1.25;
    color: #14181F;
    text-align: left;
    font-weight: 500;
    width: 100%;
    box-sizing: border-box;
}

.haxopoly-play-recap--empty {
    /* "Fresh deck. Fresh dice. Fresh empire." flavour text — softer + italic so it doesn't
       compete with the live recap once the first roll lands. */
    background: transparent;
    border-left-color: #C7CFDC;
    color: #C7CFDC;
    font-style: italic;
    font-weight: 400;
}

.haxopoly-play-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    padding-top: 4px;
}

.haxopoly-play-roll {
    /* Make the primary action visually distinct from secondary outlined controls. */
    min-width: 180px;
}

.haxopoly-play-submit-error {
    width: 100%;
}

.haxopoly-play-pregame,
.haxopoly-play-terminal {
    padding: 16px 8px;
    display: flex;
    flex-direction: column;
}

/* Pre-accept event banner (operator request 2026-06-08) — discipline glyph + title/subtitle,
   mirroring the cross-module banner pattern (ClueMaster / Chess / Bowling / Cubing) so the
   awaiting surface makes the event unmistakable. */
.haxopoly-event-banner {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--mud-palette-text-primary);
    font-weight: 600;
    margin-bottom: 0.75rem;
}

.haxopoly-event-banner::before {
    content: '';
    flex-shrink: 0;
    width: 52px;
    height: 52px;
    background-image: url('/icons/disciplines/icon-haxopoly-default.png');
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
}

.haxopoly-event-banner-text {
    display: flex;
    flex-direction: column;
    line-height: 1.2;
    min-width: 0;
}

.haxopoly-event-banner-title {
    font-size: 1.25rem;
    font-weight: 600;
}

.haxopoly-event-banner-subtitle {
    font-size: 0.9rem;
    font-weight: 500;
    color: var(--mud-palette-text-secondary);
}

.haxopoly-play-pregame-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

/* ---------- Resign-confirm overlay ---------- */

.haxopoly-overlay {
    position: fixed;
    inset: 0;
    background: rgba(11, 20, 55, 0.65);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    z-index: 1000;
}

.haxopoly-overlay-card {
    background: #FCFCFD;            /* §14.2 card cream */
    color: #14181F;                 /* §14.2 ink — forces dark text against the surrounding
                                       dark-theme play surface that otherwise leaks its light
                                       text colour into the popup. */
    border-radius: 8px;
    padding: 20px;
    max-width: 360px;
    width: 100%;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
    display: flex;
    flex-direction: column;
    gap: 12px;
    /* position: relative establishes the positioning context for the absolute-positioned
       .haxopoly-overlay-close X button (added 2026-06-04 on informational popups). The
       resign-confirm + landing-card overlays don't render the X — both require an explicit
       decision — but the base-class context is set here once. */
    position: relative;
}

.haxopoly-overlay-close {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 10;
    appearance: none;
    background: transparent;
    border: none;
    color: #14181F;
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1;
    padding: 2px 10px;
    cursor: pointer;
    border-radius: 4px;
    font-family: inherit;
    transition: background 120ms;
}

.haxopoly-overlay-close:hover {
    background: rgba(20, 24, 31, 0.08);
}

.haxopoly-overlay-close:focus-visible {
    outline: 2px solid #A3E635;
    outline-offset: 2px;
}

/* MudText components have their own colour rules tied to mud-palette CSS variables; inside
   the popup we want them to inherit the card's ink colour rather than the dark-theme's light
   foreground. Same shape Lexigon's overlay uses (via mud-palette vars) — this is just the
   inheritance variant since we hard-code the cream brand background. */
.haxopoly-overlay-card .mud-typography {
    color: inherit;
}

.haxopoly-overlay-hint {
    color: #6B6B6B;
}

.haxopoly-overlay-actions {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
    margin-top: 4px;
}

/* ---------- Kept cards display in centre HUD ---------- */

.haxopoly-centre-kept {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    justify-content: center;
    align-items: center;
    margin-top: 2cqi;
    max-width: 32ch;
}

.haxopoly-centre-kept-label {
    font-size: clamp(0.50rem, 1.6cqi, 0.70rem);
    color: #C7CFDC;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    margin-right: 4px;
}

.haxopoly-centre-kept-pill {
    background: #1F3A93;
    color: #FCFCFD;
    font-size: clamp(0.45rem, 1.5cqi, 0.65rem);
    padding: 2px 8px;
    border-radius: 12px;
    font-weight: 600;
    letter-spacing: 0.01em;
    border: 1px solid #FCFCFD;
}

/* ---------- In-hub action buttons (2026-06-04) ----------
   Operator request: Roll + Resign were below the board, off-screen on the default viewport on
   both mobile + desktop. Both moved into the navy centre hub so they're always visible without
   scrolling. Roll sits under Credits in the centre stack; Resign tucks into the top-right corner
   of the hub via absolute positioning. */

.haxopoly-centre-roll {
    appearance: none;
    background: #A3E635;            /* lime — §14.2 primary / challenger accent */
    color: #14181F;
    border: none;
    border-radius: 8px;
    padding: 8px 14px;
    font-family: inherit;
    font-weight: 800;
    /* Floor dropped 2026-06-04 so the button text shrinks on mobile viewports — was
       clamping at 0.7rem which read big inside the narrow centre hub on iPhone XR. */
    font-size: clamp(0.55rem, 2cqi, 0.95rem);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    cursor: pointer;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
    transition: background 120ms, transform 80ms;
    margin-top: 0.4cqi;
    white-space: nowrap;
}

.haxopoly-centre-roll:hover:not(:disabled) {
    background: #B5F050;
}

.haxopoly-centre-roll:active:not(:disabled) {
    transform: translateY(1px);
}

.haxopoly-centre-roll:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Immunity escape button — shown when detained and holding a kept immunity card. Shares the
   same shape/size as the Roll button but uses a golden accent to distinguish the special action. */
.haxopoly-centre-immunity {
    appearance: none;
    background: #F59E0B;            /* amber — "special power" accent */
    color: #14181F;
    border: none;
    border-radius: 8px;
    padding: 8px 14px;
    font-family: inherit;
    font-weight: 800;
    font-size: clamp(0.55rem, 2.2cqi, 0.8rem);
    letter-spacing: 0.03em;
    text-transform: uppercase;
    cursor: pointer;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
    transition: background 120ms, transform 80ms;
    margin-top: 0.4cqi;
    white-space: nowrap;
}

.haxopoly-centre-immunity:hover:not(:disabled) {
    background: #FBBF24;
}

.haxopoly-centre-immunity:active:not(:disabled) {
    transform: translateY(1px);
}

.haxopoly-centre-immunity:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.haxopoly-centre-waiting {
    /* Renders in the same vertical slot the Roll button would occupy on the active player's
       view — keeps the hub from looking empty when it's the opponent's turn and gives the
       viewer a clear "this isn't my turn" cue beyond just the top status badge. */
    font-size: clamp(0.6rem, 2cqi, 0.85rem);
    color: #C7CFDC;                 /* muted on-navy text — same palette as kept-card labels */
    text-align: center;
    font-style: italic;
    letter-spacing: 0.02em;
    margin-top: 0.4cqi;
    padding: 8px 12px;
    max-width: 22ch;
    line-height: 1.3;
}

.haxopoly-centre-resign {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 5;
    appearance: none;
    background: transparent;
    color: #F4845F;                 /* coral — §14.2 challengee / "destructive" accent */
    border: 1px solid rgba(244, 132, 95, 0.55);
    border-radius: 4px;
    font-family: inherit;
    font-size: clamp(0.55rem, 1.7cqi, 0.75rem);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: 4px 10px;
    cursor: pointer;
    transition: background 120ms, border-color 120ms;
}

.haxopoly-centre-resign:hover:not(:disabled) {
    background: rgba(244, 132, 95, 0.12);
    border-color: #F4845F;
}

.haxopoly-centre-resign:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/* ---------- Resolved-view decisive callout ---------- */

.haxopoly-play-decisive {
    margin: 0;
    padding: 8px 12px;
    background: #FFF8E1;
    border-left: 3px solid #F4C430;
    font-size: 0.95rem;
    font-weight: 500;
    color: #14181F;
    border-radius: 0 4px 4px 0;
}

/* ---------- Clickable tile affordance ---------- */

.haxopoly-cell.is-clickable {
    cursor: pointer;
}

.haxopoly-cell.is-clickable:hover {
    filter: brightness(1.03);
}

/* ---------- Title-deed popup (§10.1 standardized single-surface card) ---------- */

.haxopoly-titledeed {
    max-width: 420px;
    padding: 0;
    overflow: hidden;
}

.haxopoly-titledeed-header {
    display: flex;
    align-items: stretch;
    background: #FAF6EC;
    border-bottom: 1px solid #E6E0CC;
}

.haxopoly-titledeed-sector {
    flex: 0 0 12px;
}

.haxopoly-titledeed-title {
    padding: 12px 16px;
    flex: 1;
    min-width: 0;
}

/* Viewer's cash-on-hand, right-aligned in the header (player request 2026-07-10). Padding-right
   clears the absolutely-positioned .haxopoly-overlay-close X. Distinct label + darker-lime "Cr"
   echo the centre-HUD Credits readout while staying legible on the cream card. */
.haxopoly-titledeed-wallet {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: center;
    flex: 0 0 auto;
    /* Right padding clears the .haxopoly-overlay-close X (occupies the rightmost ~42px: right:8 +
       ~34px glyph box) with a comfortable gap so the credits don't crowd the button. */
    padding: 12px 58px 12px 8px;
    text-align: right;
    white-space: nowrap;
}

.haxopoly-titledeed-wallet-label {
    font-size: 0.62rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: #8A8266;
    line-height: 1;
}

.haxopoly-titledeed-wallet-value {
    margin-top: 2px;
    font-size: 1.05rem;
    font-weight: 800;
    color: #14181F;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

.haxopoly-titledeed-wallet-cr {
    margin-left: 3px;
    font-size: 0.72rem;
    font-weight: 700;
    color: #5F8C0B;
    letter-spacing: 0.02em;
}

.haxopoly-titledeed-body {
    padding: 16px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.haxopoly-titledeed-rent {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.85rem;
}

.haxopoly-titledeed-rent td {
    padding: 4px 8px;
    border-bottom: 1px solid #F0EBDF;
}

.haxopoly-titledeed-rent td:last-child {
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.haxopoly-titledeed-rent tr.is-current {
    background: #FFF8E1;
    border-left: 3px solid #A3E635;
}

.haxopoly-titledeed-rent tr.is-current td {
    font-weight: 700;
}

.haxopoly-titledeed-row {
    display: flex;
    justify-content: space-between;
    font-size: 0.9rem;
    padding: 4px 0;
    border-bottom: 1px solid #F0EBDF;
}

.haxopoly-titledeed-row span:last-child {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.haxopoly-titledeed-status {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 4px;
}

.haxopoly-titledeed-status-pill {
    font-size: 0.75rem;
    padding: 3px 8px;
    border-radius: 12px;
    font-weight: 600;
    letter-spacing: 0.02em;
}

.haxopoly-titledeed-status-pill.is-unowned {
    background: #E6E0CC;
    color: #14181F;
}

.haxopoly-titledeed-status-pill.is-yours {
    /* Viewer's own profile colour (--hax-yours), fallback §14.2 lime. Paired with the "Yours"
       text label, so colour is never the sole ownership signal. */
    background: var(--hax-yours, #A3E635);
    color: #14181F;
}

.haxopoly-titledeed-status-pill.is-opp {
    background: var(--hax-opp, #F4845F);
    color: #14181F;
}

.haxopoly-titledeed-status-pill.is-mortgaged {
    background: #B91C1C;
    color: #FCFCFD;
}

.haxopoly-titledeed-status-pill.is-developed {
    background: #1F3A93;
    color: #FCFCFD;
}

.haxopoly-titledeed-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: 0 16px 8px;
}

/* ---------- Staged-actions chip ---------- */

.haxopoly-staged-chip {
    display: flex;
    align-items: center;
    gap: 8px;
    background: #FFF8E1;
    border: 1px solid #A3E635;
    border-radius: 4px;
    padding: 4px 10px;
    font-size: 0.85rem;
    color: #14181F;
    width: 100%;
}

.haxopoly-staged-chip-count {
    background: #A3E635;
    color: #14181F;
    font-weight: 700;
    padding: 1px 8px;
    border-radius: 12px;
    font-variant-numeric: tabular-nums;
}

/* ---------- §14.1 Force End testing panel (Dev only) ---------- */

.haxopoly-force-end-panel {
    margin-top: 16px;
    padding: 12px 14px;
    border: 1px dashed #F4845F;          /* coral-dashed = visibly experimental */
    border-radius: 6px;
    background: rgba(244, 132, 95, 0.06);
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.haxopoly-force-end-header {
    display: flex;
    align-items: center;
    gap: 8px;
}

.haxopoly-force-end-pill {
    background: #F4845F;
    color: #FCFCFD;
    font-size: 0.65rem;
    padding: 2px 8px;
    border-radius: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.haxopoly-force-end-title {
    font-weight: 700;
    font-size: 0.9rem;
    color: #14181F;
}

.haxopoly-force-end-hint {
    margin: 0;
    font-size: 0.8rem;
    color: #4B5363;
    line-height: 1.4;
}

.haxopoly-force-end-section {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.haxopoly-force-end-section-label {
    font-size: 0.75rem;
    font-weight: 600;
    color: #14181F;
    letter-spacing: 0.02em;
}

.haxopoly-force-end-buttons {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.haxopoly-force-end-followup {
    margin: 4px 0 0 0;
    font-size: 0.72rem;
    color: #6B6B6B;
    line-height: 1.4;
    font-style: italic;
}

/* ---------- Stats cards CSS — used by HaxopolyStatsComponent (design.md §12) ---------- */

.haxopoly-stats {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.haxopoly-stats-grid {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.haxopoly-stats-event-title {
    margin: 0 0 8px 0;
    font-size: 1.05rem;
    font-weight: 600;
}

.haxopoly-stats-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: 8px;
    margin: 0 0 12px 0;
    padding: 0;
}

.haxopoly-stats-stats > div {
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-divider);
    border-radius: 6px;
    padding: 8px 12px;
}

.haxopoly-stats-stats dt {
    margin: 0;
    font-size: 0.75rem;
    color: var(--mud-palette-text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.haxopoly-stats-stats dd {
    margin: 2px 0 0 0;
    font-size: 1.3rem;
    font-weight: 700;
}

/* Headline four read as the at-a-glance identity — accent the values. */
.haxopoly-stats-headline dd {
    color: var(--mud-palette-primary);
}

/* Secondary qualifier (e.g. the property a highest-rent record was set on). */
.haxopoly-stats-sub {
    display: block;
    margin-top: 2px;
    font-size: 0.72rem;
    font-weight: 400;
    color: var(--mud-palette-text-secondary);
}

/* Native <details> disclosure for the deeper grids — no JS interop. */
.haxopoly-stats-details > summary {
    cursor: pointer;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--mud-palette-text-secondary);
    margin-bottom: 8px;
    user-select: none;
}

.haxopoly-stats-group-title {
    margin: 4px 0 6px 0;
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--mud-palette-text-secondary);
}

.haxopoly-stats-empty {
    color: var(--mud-palette-text-secondary, #6b6b6b);
    margin: 12px 0;
    font-style: italic;
    text-align: center;
}

.haxopoly-event-card-description {
    margin: 0;
}

/* ---------- Accept-time piece picker (challengee side) ---------- */

.haxopoly-pregame-yourpiece {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 8px;
}

.haxopoly-pregame-yourpiece-img {
    width: 36px;
    height: 36px;
    object-fit: contain;
    background: #FCFCFD;
    border-radius: 50%;
    padding: 4px;
    border: 2px solid #A3E635;
}

.haxopoly-accept-pieces-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 8px;
    max-width: 480px;
}

@media (max-width: 420px) {
    .haxopoly-accept-pieces-grid {
        grid-template-columns: repeat(3, 1fr);
    }
}

.haxopoly-accept-piece {
    appearance: none;
    background: #FCFCFD;
    border: 2px solid #E6E0CC;
    border-radius: 8px;
    padding: 8px 4px 6px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    cursor: pointer;
    font-family: inherit;
    position: relative;
    transition: border-color 120ms, background 120ms, opacity 120ms;
}

.haxopoly-accept-piece:hover:not(:disabled) {
    border-color: #A3E635;
    background: #FAFFE7;
}

.haxopoly-accept-piece.is-selected {
    border-color: #A3E635;
    background: #FFF8E1;
    box-shadow: 0 0 0 2px #A3E635 inset;
}

.haxopoly-accept-piece.is-taken,
.haxopoly-accept-piece:disabled {
    cursor: not-allowed;
    opacity: 0.4;
    filter: grayscale(0.6);
}

.haxopoly-accept-piece-img {
    width: 44px;
    height: 44px;
    object-fit: contain;
}

.haxopoly-accept-piece-name {
    font-size: 0.72rem;
    font-weight: 600;
    text-align: center;
    line-height: 1.1;
    color: #14181F;
    word-break: keep-all;
}

.haxopoly-accept-piece-taken-pill {
    position: absolute;
    top: 4px;
    right: 4px;
    background: #B91C1C;
    color: #FCFCFD;
    font-size: 0.55rem;
    font-weight: 700;
    padding: 1px 5px;
    border-radius: 6px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

/* ---------- Throwdown options — piece picker (§17 8-piece roster) ---------- */

.haxopoly-throwdown-options {
    margin-top: 12px;
}

.haxopoly-throwdown-options-title {
    margin-bottom: 4px;
}

.haxopoly-throwdown-options-hint {
    display: block;
    margin-bottom: 10px;
    color: var(--mud-palette-text-secondary, #6b6b6b);
}

.haxopoly-throwdown-options-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 8px;
}

@media (max-width: 420px) {
    .haxopoly-throwdown-options-grid {
        grid-template-columns: repeat(3, 1fr);
    }
}

.haxopoly-throwdown-options-piece {
    appearance: none;
    background: #FCFCFD;
    border: 2px solid #E6E0CC;
    border-radius: 8px;
    padding: 8px 4px 6px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    cursor: pointer;
    font-family: inherit;
    transition: border-color 120ms, background 120ms, transform 120ms;
}

.haxopoly-throwdown-options-piece:hover {
    border-color: #A3E635;
    background: #FAFFE7;
}

.haxopoly-throwdown-options-piece.is-selected {
    border-color: #A3E635;
    background: #FFF8E1;
    box-shadow: 0 0 0 2px #A3E635 inset;
}

.haxopoly-throwdown-options-piece-img {
    width: 48px;
    height: 48px;
    object-fit: contain;
}

.haxopoly-throwdown-options-piece-name {
    font-size: 0.75rem;
    font-weight: 600;
    text-align: center;
    line-height: 1.1;
    color: #14181F;
    word-break: keep-all;
}

/* ---------- F.3.b §10.1 standardized landing card ---------- */
/* Reuses .haxopoly-overlay + .haxopoly-overlay-card from the resign overlay for the dark
   backdrop + cream card + colour inheritance. Adds the §10.1-spec horizontal header (medallion
   left, TITLE-DEED chip + property name stacked right), a body with the rent ladder + price,
   and the fixed bottom BUY/DECLINE row. */

.haxopoly-landing-card {
    max-width: 420px;
    padding: 0;
    overflow: hidden;
}

.haxopoly-landing-header {
    display: flex;
    align-items: stretch;
    background: #FAF6EC;
    border-bottom: 1px solid #E6E0CC;
}

.haxopoly-landing-medallion {
    flex: 0 0 64px;
    min-height: 64px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-right: 1px solid #E6E0CC;
}

.haxopoly-landing-medallion--wormhole {
    background: #0B1437;            /* deep-space navy frame for Wormholes */
}

.haxopoly-landing-medallion--wormhole img {
    width: 60%;
    height: auto;
    object-fit: contain;
    filter: invert(1);              /* white-on-navy */
}

.haxopoly-landing-title {
    padding: 10px 14px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 2px;
    flex: 1;
    min-width: 0;
}

.haxopoly-landing-chip {
    font-size: 0.65rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    color: #6B6B6B;
    text-transform: uppercase;
}

/* Cash-on-hand on the landing (buy/pass) card. Same readout as the title-deed inspector — reuses
   its -label/-value/-cr text styling — but this card has no close X, so it right-aligns flush with
   the header padding. */
.haxopoly-landing-wallet {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: center;
    flex: 0 0 auto;
    padding: 10px 16px 10px 8px;
    text-align: right;
    white-space: nowrap;
}

.haxopoly-landing-name {
    font-size: 1.15rem;
    font-weight: 700;
    color: #14181F;
    line-height: 1.15;
    word-break: normal;
    overflow-wrap: break-word;
}

.haxopoly-landing-sub {
    font-size: 0.75rem;
    color: #6B6B6B;
    line-height: 1.2;
}

.haxopoly-landing-body {
    padding: 14px 16px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.haxopoly-landing-lead {
    margin: 0;
    font-size: 0.9rem;
    color: #14181F;
    line-height: 1.35;
}

.haxopoly-landing-price-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 8px 0;
    border-top: 1px solid #F0EBDF;
    border-bottom: 1px solid #F0EBDF;
}

.haxopoly-landing-price-label {
    font-size: 0.85rem;
    color: #4B5363;
    font-weight: 600;
}

.haxopoly-landing-price-value {
    font-size: 1.25rem;
    font-weight: 800;
    color: #14181F;
    font-variant-numeric: tabular-nums;
}

.haxopoly-landing-cant-afford {
    font-size: 0.8rem;
    color: #B91C1C;
    background: rgba(185, 28, 28, 0.08);
    border-left: 3px solid #B91C1C;
    padding: 6px 10px;
    border-radius: 0 4px 4px 0;
    font-weight: 500;
}

.haxopoly-landing-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    padding: 12px 16px 14px;
    border-top: 1px solid #E6E0CC;
    background: #FAF6EC;
}

.haxopoly-landing-buy,
.haxopoly-landing-decline {
    /* Plain HTML buttons instead of MudButton — MudBlazor's variant/color theming wasn't
       inheriting the cream-card context, which made the disabled BUY illegible and DECLINE
       nearly invisible (operator caught 2026-06-04). Same approach as the centre Roll +
       Resign buttons. */
    appearance: none;
    font-family: inherit;
    font-weight: 700;
    font-size: 0.85rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 8px 16px;
    border-radius: 6px;
    cursor: pointer;
    transition: background 120ms, border-color 120ms, opacity 120ms, transform 80ms;
}

.haxopoly-landing-buy {
    background: #A3E635;            /* lime — primary action */
    color: #14181F;
    border: none;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.18);
}

.haxopoly-landing-buy:hover:not(:disabled) {
    background: #B5F050;
}

.haxopoly-landing-buy:active:not(:disabled) {
    transform: translateY(1px);
}

.haxopoly-landing-buy:disabled {
    background: #E6E0CC;            /* warm gray on the cream card — clearly inert */
    color: #9A9385;
    box-shadow: none;
    cursor: not-allowed;
}

.haxopoly-landing-decline {
    background: transparent;
    color: #14181F;
    border: 1.5px solid #14181F;
}

.haxopoly-landing-decline:hover:not(:disabled) {
    background: rgba(20, 24, 31, 0.06);
}

.haxopoly-landing-decline:disabled {
    color: #9A9385;
    border-color: #C7C0A8;
    cursor: not-allowed;
}

/* ---------- Card inspector overlay (2026-06-05) ----------
   Operator request: recap line's drawn-card name + each tucked-card pill open a popup that
   shows the full card (deck label + name + flavor + effect). Reuses .haxopoly-overlay +
   .haxopoly-overlay-card shell from the title-deed popup for consistent chrome; the
   .haxopoly-card-overlay class layers card-specific layout on top, and the .is-anomaly /
   .is-guild variants tint the header rail per deck (anomaly = hazard orange, guild = reward
   teal). */

/* Inline card-name button inside .haxopoly-play-recap — looks like a link sitting in the
   cream recap strip, not a button, so the recap reads as prose. Borderless, no background,
   inherits font size from the parent recap clamp so it scales with viewport. */
.haxopoly-recap-card-link {
    appearance: none;
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    font: inherit;
    line-height: inherit;            /* button defaults to its own line-height; inherit so the
                                        link sits on the recap's text baseline, not its own. */
    vertical-align: baseline;        /* button default is middle/baseline-ish per UA — explicit
                                        baseline keeps "Space-Time Fracture" level with the
                                        surrounding "drew ... , then rolled" prose. */
    color: #1F3A93;                  /* deep blue — readable on the cream recap background */
    font-weight: 700;
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
    cursor: pointer;
}

.haxopoly-recap-card-link:hover,
.haxopoly-recap-card-link:focus-visible {
    color: #0B1437;
    text-decoration-thickness: 2px;
    outline: none;
}

/* Tucked-card pill (the "Tucked: Foo Bar" row in the centre hub) was a span before card click
   landed; promoting to a button means we reset the default button styling, keep the existing
   visual (navy fill, white border + text, small chip), and add a clear hover/focus cue. */
.haxopoly-centre-kept-pill {
    appearance: none;
    cursor: pointer;
    font-family: inherit;
    line-height: 1.2;
    transition: background-color 120ms ease, transform 120ms ease;
}

.haxopoly-centre-kept-pill:hover,
.haxopoly-centre-kept-pill:focus-visible {
    background: #2A4FB7;             /* one shade lighter than the resting navy */
    outline: none;
    transform: translateY(-1px);
}

/* Overlay card body — wider than the title-deed (more flavor text to breathe) but still
   capped on desktop so the line length stays readable. */
.haxopoly-card-overlay {
    width: min(420px, 92vw);
    padding: 22px 22px 18px;
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.haxopoly-card-overlay-header {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding-bottom: 12px;
    border-bottom: 1px solid rgba(20, 24, 31, 0.12);
}

.haxopoly-card-overlay-deck {
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: #6B6B6B;
}

/* Deck tint — anomaly leans hazard (the F0962D used on the board's anomaly-icon orange);
   guild leans reward (the lime accent that runs through the rest of the cream surfaces). */
.haxopoly-card-overlay.is-anomaly .haxopoly-card-overlay-deck {
    color: #C25600;
}

.haxopoly-card-overlay.is-guild .haxopoly-card-overlay-deck {
    color: #2F7F3D;
}

.haxopoly-card-overlay-name {
    font-weight: 700;
    color: #14181F;
}

.haxopoly-card-overlay-body {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.haxopoly-card-overlay-flavor {
    margin: 0;
    font-style: italic;
    color: #4A4A4A;
    line-height: 1.4;
    font-size: 0.95rem;
}

.haxopoly-card-overlay-effect {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 10px 12px;
    background: #F4EFE2;             /* one shade darker than the cream surface — softly recessed */
    border-radius: 6px;
}

.haxopoly-card-overlay-effect-label {
    font-size: 0.65rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: #6B6B6B;
}

.haxopoly-card-overlay-effect-text {
    color: #14181F;
    font-weight: 600;
}

/* F.3.c card-resolution variant — the §10.1a card-acknowledgment popup. Reuses the deck-tinted
   card-overlay shell + the haxopoly-landing-actions action row. Parent is a flex container
   (justify-content: flex-end by default); override so the single contextual button stretches
   to span the row (no DECLINE partner to share with). */
.haxopoly-card-resolution-actions {
    justify-content: stretch;
}

.haxopoly-card-resolution-actions .haxopoly-landing-buy {
    flex: 1 1 auto;
}

/* F.3.e opponent-recap variant — the §10.3 turn-start auto-pop. When the opponent's last turn
   had no card draw, the body hosts a plain recap line in place of the deck flavor + effect
   block. Reuses the card-overlay shell + the DONE button from .haxopoly-card-resolution-actions. */
.haxopoly-opponent-recap-line {
    margin: 0;
    color: #14181F;
    font-weight: 500;
    line-height: 1.4;
    font-size: 1rem;
}
