:root {
  --bg: #070b12; --panel: #0d1420; --line: #1c3a4a; --line2: #2a5a70; --text: #d7e3ea; --dim: #7f95a3;
  --yellow: #f4e01f; --cyan: #58e0ff; --green: #8ff26a; --red: #ff5c6c; --blue: #6aa8ff;
  /* Cards are as big as the window allows rather than a fixed size, because the point of the board
     is to read the cards. A real card is 1.41 times as tall as it is wide, so height is the free
     variable and width follows from it.

     Two things bind, so both are in the min(): the field must fit four cards across the centre
     column, and two field rows plus the two Legend bands must fit the board's height. The centre
     column is the viewport less the two side columns and the gaps, hence the 100vw term. */
  /* The two side columns, named here so the grid and the card size cannot drift apart: the card
     size is derived from what is left over after them, and the grid is laid out from the same
     two values. */
  --side-l: clamp(300px, 26vw, 500px);
  --side-r: clamp(280px, 24vw, 470px);
  --strip: 74px;                          /* the deck/trash strip down the right edge of the fields */
  --head: 40px;                           /* the app's own header, the only chrome above the mat */
  --centre: calc(100vw - var(--side-l) - var(--side-r) - 32px);   /* 32px: the gaps and the padding */
  /* ONE size for every card on the board — hand, field, Legend slot, Eddies pile, yours or the
     rival's, face up or face down. They are the same object on a table and looking like four
     different objects is what made this board read wrong.

     Two things bind. Across: four cards and the deck/trash strip have to fit the centre column.
     Down: the centre stacks a Legend band, two field rows and a Legend band. The field rows are
     the taller pair, because a Unit with Gear equipped under it is taller than a card, so the card
     is sized off the *short* rows and the fields keep the slack. */
  --fieldrow: 1.24fr;
  --card-h: min(calc((100vh - var(--head) - 27px) / 4.48 - 13px),
                calc((var(--centre) - var(--strip) - 36px) / 4 * 1.41), 340px);
  --card-w: calc(var(--card-h) / 1.41);
  --die: clamp(34px, calc(var(--side-l) / 8.4), 58px);
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--text); font: 14px/1.35 "Segoe UI", Roboto, system-ui, sans-serif; }
button { background: var(--panel); color: var(--text); border: 1px solid var(--line2); border-radius: 6px; padding: 7px 12px; cursor: pointer; font-weight: 600; letter-spacing: .04em; }
button:hover { border-color: var(--cyan); color: var(--cyan); }
button.primary { background: #123; border-color: var(--green); color: var(--green); }
button:disabled { opacity: .4; cursor: default; }
select, input { background: #0b1520; color: var(--text); border: 1px solid var(--line2); border-radius: 6px; padding: 6px 8px; }
.hidden { display: none !important; }
.top { display: flex; align-items: center; justify-content: space-between; padding: 4px 18px; height: var(--head); border-bottom: 1px solid var(--line); background: #050810; }
.brand { color: var(--yellow); font-weight: 800; letter-spacing: .12em; }
.brand span { color: var(--dim); font-weight: 400; letter-spacing: .05em; margin-left: 8px; }
nav button { margin-left: 6px; }
nav button.active { color: var(--yellow); border-color: var(--yellow); }
.setup { max-width: 1100px; margin: 20px auto; padding: 16px 20px; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; }
.setup h2 { margin: 0 0 12px; color: var(--cyan); letter-spacing: .08em; font-size: 15px; }
.row { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; margin: 8px 0; }
.row label { display: flex; gap: 6px; align-items: center; color: var(--dim); }
.hint { color: var(--dim); font-size: 12px; }

/* ---------------- board ---------------- */
/* The reference client's playmat, area for area. Left column, top to bottom: the rival's hand,
   their fixer, their Gig area, the turn box, then mine mirrored back out to my hand, with a name
   plate outside each end. Centre: a Legend+Eddies band, the two fields, another band, and the
   deck/trash strip down the right-hand edge of the fields. Right: the room, the log, the prompt
   and whichever card is selected. */
.board { display: grid; grid-template-columns: var(--side-l) minmax(0, 1fr) var(--side-r); gap: 8px; padding: 6px 8px; height: calc(100vh - var(--head)); }
.col { display: flex; flex-direction: column; gap: 5px; min-height: 0; }
.panel { background: var(--panel); border: 1px solid var(--line); border-radius: 8px; padding: 4px 8px 6px; position: relative; }
.panel .lbl { display: block; font-size: 10px; color: var(--dim); letter-spacing: .1em; margin: 0 0 4px; }
/* On the board there are no area labels at all: the name plates say whose half is whose and the
   shape of each area says what it is, which is how the cards sit on a real playmat. */
.board .panel { padding: 5px 6px; }
.nameplate { flex: 0 0 auto; text-align: center; font-size: 10px; letter-spacing: .22em; color: var(--dim); padding: 1px 0; }
/* The left column is fixed panels with the hands taking up whatever is left, so the turn box stays
   on the middle line of the board however many cards are held. */
.col.left > .panel.hand { flex: 1 1 0; min-height: calc(var(--card-h) + 12px); }
.col.left > .panel { flex: 0 0 auto; }
/* One row, never wrapped: six cards read as a hand, a 2x3 block reads as a grid. The hand fans —
   cards overlap by --overlap, which app.js measures from how many there are and how much room. */
.hand { display: flex; gap: 4px; overflow: hidden; min-height: 0; align-items: center; flex-wrap: nowrap; }
/* Any row of cards that does not fit overlaps instead of being cut off: hands, the Eddies
   pile, a Legend band on a narrow screen, a full field. `fanHand` sets --overlap to 0 when
   the row fits, so this is inert everywhere it is not needed. */
.hand .card + .card, .eddies .list .card + .card,
.legends .card + .card, .field .card + .card,
.field .slot + .slot { margin-left: calc(-1 * var(--overlap, 0px)); }
.hand .card { transition: transform .13s ease; flex: 0 0 auto; }
/* Only the cards drawn on top of the hovered one have to move out of its way. */
.hand.mine .card:hover { transform: translateY(-14px); z-index: 6; }
.hand.mine .card:hover ~ .card { transform: translateX(var(--overlap, 0px)); }
.hand.mine .card { font-size: 10px; }
.hand.mine .card .nm { font-size: 12px; } .hand.mine .card .txt { font-size: 9.5px; max-height: 92px; }
.hand.mine .card .sub { font-size: 10px; } .hand.mine .card .tags { font-size: 9px; }
.hand.mine .card .cost { font-size: 16px; } .hand.mine .card .pw { font-size: 17px; }

/* ---- dice. Outline solids, sized off the row they sit in. */
.dice, .gigs .list { display: flex; gap: 6px; justify-content: center; align-items: center; flex-wrap: wrap; }
.die { position: relative; width: var(--die); height: var(--die); flex: 0 0 auto; display: grid; place-items: center; }
.die svg { position: absolute; inset: 0; width: 100%; height: 100%; fill: none; stroke: var(--line2); stroke-width: 2.2; stroke-linejoin: round; }
.die svg polygon:not(:first-child), .die svg line, .die svg path:not(:first-child) { stroke-width: 1.5; opacity: .75; }
.die .face { position: relative; font-weight: 800; font-size: calc(var(--die) * .46); color: #fff; text-shadow: 0 0 4px #000, 0 0 4px #000; }
.die.fixer .face { font-size: calc(var(--die) * .28); color: var(--dim); }
/* a tetrahedron is bottom-heavy, so its face sits where the solid actually is */
.die[data-kind="4"] .face { transform: translateY(18%); }
.die.own svg { stroke: var(--green); }
.die.rival svg { stroke: var(--cyan); }
.die.pick { cursor: pointer; }
.die.pick svg { stroke: var(--green); filter: drop-shadow(0 0 5px var(--green)); }
.die.pick:hover { transform: translateY(-2px); }
.gigs { display: flex; align-items: center; gap: 8px; min-height: calc(var(--die) + 10px); }
.gigs .list { flex: 1 1 auto; justify-content: flex-start; }
/* Gigs held out of seven, over Street Cred: the two numbers the game is won on, in the one box the
   reference client keeps them in. */
.score { flex: 0 0 auto; border: 1px solid var(--line2); border-radius: 10px; padding: 3px 9px; text-align: center; line-height: 1.25; }
.score .n { font-size: 14px; font-weight: 800; color: var(--dim); border-bottom: 1px solid var(--line); padding-bottom: 1px; }
.score .n.hot { color: var(--green); }
.score .c { font-size: 15px; font-weight: 800; color: var(--cyan); }
.score .c.own { color: var(--green); }

/* ---- the turn box */
.phase { text-align: center; padding: 8px 10px 10px; border-color: var(--line); }
.phase .title { position: relative; color: var(--green); font-weight: 800; letter-spacing: .12em; border: 1px solid var(--green); border-radius: 22px; padding: 7px 10px 7px 26px; font-size: 17px; background: #08131a; }
/* the little bar at the left of the pill, which is the reference client's "it is this side's move" */
.phase .title::before { content: ""; position: absolute; left: 9px; top: 50%; transform: translateY(-50%); width: 7px; height: 60%; border-radius: 4px; background: var(--green); }
.phase .sub { color: var(--yellow); font-size: 12px; margin-top: 9px; letter-spacing: .1em; font-weight: 700; }
.phase .endturn { margin-top: 9px; border-color: var(--green); color: var(--green); letter-spacing: .12em; font-size: 13px; padding: 8px 18px; border-radius: 8px; }
/* waiting is not an instruction, so it does not get the green that means "you can act" */
.phase.waiting .title { color: var(--dim); border-color: var(--line2); }
.phase.waiting .title::before { background: var(--line2); }
.phase.done .title { color: var(--yellow); border-color: var(--yellow); }
.phase.done .title::before { background: var(--yellow); }

/* ---- the centre. Four rows; the fields get the extra height, because a Unit with Gear under it
   is taller than a card. The two bands span the whole width and the deck/trash strip fills the
   right-hand edge of the field rows, exactly as on the playmat. */
.center { display: grid; grid-template-columns: minmax(0, 1fr) var(--strip); grid-template-rows: 1fr var(--fieldrow) var(--fieldrow) 1fr; gap: 5px 6px; overflow: visible; }
.center > * { min-height: 0; min-width: 0; }
.center > .legendrow { grid-column: 1 / -1; }
.center > .field { grid-column: 1; }
.center > .stacks { grid-column: 2; }
.center > .stacks.opp { grid-row: 2; } .center > .stacks.mine { grid-row: 3; }
.legendrow { display: flex; gap: 6px; min-height: 0; align-items: stretch; }
.legendrow > * { min-width: 0; }
.legendrow .legends { flex: 0 0 auto; }
.legendrow .eddies { flex: 1 1 auto; width: auto; }
.field { display: flex; gap: 6px; flex-wrap: nowrap; overflow: hidden; align-items: flex-start; }
.panel.legends { overflow: hidden; }
.legends { display: flex; gap: 6px; align-items: flex-start; flex-wrap: nowrap; }
.eddies { display: flex; flex-direction: column; min-width: 120px; }
.eddies .list { display: flex; flex-wrap: nowrap; overflow: hidden; gap: 4px; align-items: flex-start; flex: 1; min-height: 0; }
.eddies .list .card { cursor: default; }
.eddies .list .card.spent { transform: rotate(90deg); filter: brightness(.45); margin: 0 calc((var(--card-h) - var(--card-w)) / 2); }
/* the count pill sits in the bottom-right corner of the area, over its own edge */
.board .eddies .badge { position: absolute; right: 8px; bottom: 4px; padding: 1px 8px; font-size: 11px; border-radius: 10px; background: #0a1622; color: var(--cyan); }
.board .eddies .badge b { display: inline; font-size: 11px; margin-left: 5px; color: var(--cyan); }
/* ---- deck and trash, mirrored across the middle: trash outside, deck inside. */
.stacks { display: grid; grid-template-rows: repeat(2, 1fr); gap: 5px; }
/* the count is the phone's version of the rival's hand; a wide screen draws the cards instead */
.handcount { display: none; }
.stackbox { border: 1px solid var(--line); border-radius: 8px; background: var(--panel); position: relative; display: grid; place-items: center; overflow: hidden; }
.stackbox .n { font-size: 12px; color: var(--dim); font-weight: 700; }
.stackbox .n.big { font-size: 20px; color: var(--green); }
.stackbox .card.thumb { width: 84%; height: auto; aspect-ratio: 1 / 1.41; border-width: 1px; }
.stackbox.trash .n { position: absolute; right: 5px; bottom: 3px; }

.logwrap { overflow-y: auto; font-size: 12.5px; }
.col > .panel.logwrap { flex: 1 1 0; min-height: 74px; }
/* folded away: the header stays, so the way back is where the way out was */
.col > .panel.logwrap.min { flex: 0 0 auto; min-height: 0; height: 26px; overflow: hidden; }
.loghead { display: flex; align-items: center; justify-content: space-between; gap: 6px; position: sticky; top: 0; background: var(--panel); padding-bottom: 2px; }
.loghead .lbl { margin: 0; }
.foldbtn { padding: 0 7px; font-size: 13px; line-height: 1.2; border-radius: 5px; }
/* The right column is a fixed-height flex column. Everything but the log holds its size and
   the log takes what is left, so the prompt is always where it was a moment ago. */
.col > .panel { flex: 0 0 auto; }
/* The column scrolls; it does not clip. Hiding the overflow stopped the panels being drawn over
   each other but also stopped a short window from reaching the prompt at the bottom. */
.col { overflow-y: auto; overflow-x: hidden; scrollbar-width: thin; scrollbar-color: var(--line2) #0a1018; }
.col.center, .col.left { overflow: visible; }
.col::-webkit-scrollbar { width: 10px; }
.col::-webkit-scrollbar-track { background: #0a1018; border-radius: 6px; }
.col::-webkit-scrollbar-thumb { background: var(--line2); border-radius: 6px; }
.logwrap p { margin: 3px 0; padding-left: 10px; text-indent: -10px; color: var(--text); }
/* A turn header is a divider across the panel, the way the reference client draws it. */
.logwrap p.turn { color: var(--cyan); font-weight: 800; margin: 10px 0 5px; text-align: center; text-indent: 0; padding: 0; letter-spacing: .08em; font-size: 11.5px; }
.logwrap p.end { color: var(--yellow); font-weight: 700; }
/* The prompt is the one panel that can grow without limit — a main phase can offer twenty moves —
   so it, not the column, is what scrolls. The room controls at the top of the column stay put. */
.prompt { min-height: 120px; }
.col > .panel.prompt { flex: 0 1 auto; overflow-y: auto; }
.prompt .q { color: var(--yellow); font-weight: 800; letter-spacing: .08em; margin-bottom: 6px; }
.prompt .desc { color: var(--dim); font-size: 12px; margin-bottom: 8px; }
.opts { display: flex; flex-wrap: wrap; gap: 6px; max-height: 260px; overflow-y: auto; }
.opts button { font-size: 12px; }
.opts button.end { border-color: var(--yellow); color: var(--yellow); }
.controls { display: flex; gap: 6px; flex-wrap: wrap; }   /* four buttons are wider than the column: wrap rather than push the page sideways */
.controls .desc { width: 100%; color: var(--dim); font-size: 11.5px; margin-bottom: 2px; }
.waiting { color: var(--dim); font-style: italic; }

/* ---------------- cards ---------------- */
.card { width: var(--card-w); height: var(--card-h); border-radius: 8px; border: 2px solid var(--line2); background: #0a1018; position: relative; overflow: hidden; flex: 0 0 auto; font-size: 10px; transition: transform .08s; }
/* `sm` marks a card the board draws as a back or a token — a rival hand card, a Legend slot,
   a sold Eddie. On the board they are the same object as every other card and are drawn at
   the same size; `sm` still means small in the deck builder and the card browser. */
.card.sm { width: calc(var(--card-w) * .78); height: calc(var(--card-h) * .78); }
.board .card.sm { width: var(--card-w); height: var(--card-h); }
.card img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card .hd { display: flex; justify-content: space-between; padding: 3px 5px; background: #111c2a; }
.card .cost { font-weight: 900; color: #fff; font-size: 13px; }
.card .type { color: var(--dim); font-size: 9px; letter-spacing: .08em; }
.card .nm { padding: 3px 5px 0; font-weight: 800; font-size: 10.5px; line-height: 1.1; text-transform: uppercase; }
.card .sub { padding: 0 5px; color: var(--dim); font-size: 9px; }
.card .tags { padding: 2px 5px; color: var(--cyan); font-size: 8px; }
.card .txt { padding: 2px 5px; color: #c8d4dc; font-size: 8.5px; line-height: 1.2; max-height: 62px; overflow: hidden; }
.card .pw { position: absolute; right: 4px; bottom: 3px; font-weight: 900; font-size: 14px; color: #fff; background: #1a2635; padding: 0 6px; border-radius: 5px; }
.card .sell { position: absolute; left: 4px; bottom: 4px; color: var(--yellow); font-size: 9px; font-weight: 800; }
.card.Red { border-color: #c73a49; } .card.Green { border-color: #4fb35a; } .card.Blue { border-color: #4f86d6; } .card.Yellow { border-color: #d9c419; }
.card.back { background: repeating-linear-gradient(135deg, #ffe600 0 12px, #111 12px 24px); border-color: #665f00; }
.card.back img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.card.back:has(img)::after { content: none; }
.card.back::after { content: "CYBERPUNK"; position: absolute; inset: 0; display: grid; place-items: center; color: #000; font-weight: 900; letter-spacing: .1em; font-size: 9px; transform: rotate(-90deg); }
/* Orientation carries the state, the way it does on a table: a spent card lies perpendicular, a
   card that is not ready yet is set askew. Both are dimmed, each by the shade it already had. The
   rotation leaves the layout box untouched, so the side margins make room for the long edge. */
.card.spent { transform: rotate(90deg); filter: brightness(.7); margin: 0 calc((var(--card-h) - var(--card-w)) / 2); }
.card.lag { filter: brightness(.52) saturate(.55); }
.card.faceup-legend .nm { color: var(--yellow); }
/* The live power sits where the card prints its own: bottom right. It supersedes the printed
   number, so on a text face the printed one gives way rather than sitting beside it. */
/* Sized and placed off the card itself, so it lands on the same spot of the artwork whatever the
   window is doing: a disc a fifth of the card wide, sitting on the card's own bottom-right corner
   rather than hanging off it. */
.card .pnow { position: absolute; right: 4%; bottom: 1.5%; width: 21%; aspect-ratio: 1 / 1;
  display: grid; place-items: center; color: var(--green); font-weight: 900;
  font-size: calc(var(--card-w) * .125); line-height: 1; background: #0b1620e8;
  border: 2px solid var(--green); border-radius: 50%; z-index: 4; }
.card .pnow.down { color: var(--red); border-color: var(--red); }
.card:has(.pnow) .pw { display: none; }
/* The Gear hangs below the Unit and the area clips it, so what shows is the top strip of each
   piece — the name and what it adds — which is all the reference client shows too. */
/* The stack's box is exactly the host card's box, which is what lets a spent host counter-rotate
   it below: same box, same centre, so `rotate(-90deg)` cancels the host's `rotate(90deg)` on the
   nose and the Gear stays lying flat under a card that is lying on its side. */
.gearstack { position: absolute; inset: 0; z-index: -1; pointer-events: none; }
.card.spent .gearstack { transform: rotate(-90deg); }
/* wins over `.board .card.sm`, which sets every other card on the board to one size */
.board .card .gearcard { position: absolute; top: auto; bottom: calc(-0.22 * var(--card-h));
  left: calc(3% + var(--i) * 27%);
  width: calc(var(--card-w) * .74); height: calc(var(--card-h) * .74); pointer-events: auto; }
/* The Unit no longer clips its own box, or the Gear under it would be cut off at its bottom edge,
   and it becomes its own stacking context so the Gear can sit *behind the Unit's face* without
   falling behind the area's background as well. */
.card:has(.gearstack) { overflow: visible; isolation: isolate; }
.card img { border-radius: 6px; }
.card.can { cursor: pointer; box-shadow: 0 0 0 2px var(--green), 0 0 14px var(--green); }
.card.can:hover { transform: translateY(-3px); }
.card.can.spent:hover { transform: rotate(90deg) translateY(-3px); }
.card.can.lag:hover { transform: translateY(-3px); }
.card.target { box-shadow: 0 0 0 2px var(--red), 0 0 14px var(--red); cursor: pointer; }
.card.attacking { box-shadow: 0 0 0 2px var(--yellow), 0 0 16px var(--yellow); }
.slot { width: var(--card-w); height: var(--card-h); border: 1px dashed var(--line); border-radius: 8px; flex: 0 0 auto; }
.legend-slot { width: var(--card-w); height: var(--card-h); }
.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
.card-grid .card { width: 150px; height: 212px; font-size: 12px; }
.card-grid .card .txt { font-size: 10px; max-height: 90px; }
table.rep { border-collapse: collapse; width: 100%; margin: 10px 0; font-size: 13px; }
table.rep th, table.rep td { border: 1px solid var(--line); padding: 5px 8px; text-align: left; }
table.rep th { color: var(--cyan); }
.over { position: absolute; inset: 0; display: grid; place-items: center; background: #000a; z-index: 20; border-radius: 8px; }
.over div { background: var(--panel); border: 1px solid var(--yellow); padding: 20px 30px; border-radius: 10px; text-align: center; }
.over h3 { color: var(--yellow); margin: 0 0 8px; letter-spacing: .1em; }
@media (max-width: 1100px) and (min-width: 701px) { .board { grid-template-columns: 1fr; height: auto; } }

/* ---------------------------------------------------------------- deck builder */
.builder { display: grid; grid-template-columns: 1fr 400px; gap: 8px; padding: 8px; height: calc(100vh - 52px); }
.builder .panel { overflow: auto; }
.builder .library .card-grid { grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: 6px; }
.builder .library .card { width: 118px; height: 166px; font-size: 10px; cursor: pointer; }
.builder .library .card .txt { font-size: 8.5px; max-height: 70px; }
.builder .library .card.illegal { opacity: .35; filter: grayscale(.6); }
.builder .library .card.maxed { outline: 2px solid var(--yellow); }
.builder .library .card .have { position: absolute; top: 4px; right: 4px; background: var(--yellow); color: #000; font-weight: 800; border-radius: 10px; padding: 0 6px; font-size: 10px; }
.builder .library .card.legend-pick { outline: 2px solid var(--cyan); }
.decksheet input, .decksheet select { min-width: 0; }
#bName { flex: 1; }
.legend-area { margin: 8px 0; padding: 8px; border: 1px solid var(--line); border-radius: 8px; }
.legend-slots { display: flex; gap: 6px; margin: 6px 0; }
.legend-slots .card, .legend-slots .slot { width: calc(var(--card-w) * .78); height: calc(var(--card-h) * .78); cursor: pointer; }
.ram { display: flex; gap: 6px; flex-wrap: wrap; }
.ram span { padding: 2px 8px; border-radius: 10px; border: 1px solid var(--line2); font-size: 11px; }
.ram span b { margin-left: 4px; }
.ram .Red { color: #ff5d5d; } .ram .Green { color: #4ef08a; } .ram .Blue { color: #5aa8ff; } .ram .Yellow { color: #ffe600; } .ram .Purple { color: #c98bff; } .ram .Grey { color: #b8c0cc; }
.curve-row { align-items: flex-end; }
.curve { display: flex; gap: 3px; align-items: flex-end; height: 48px; }
.curve div { width: 22px; background: var(--cyan); position: relative; min-height: 1px; }
.curve div::after { content: attr(data-n); position: absolute; top: -14px; left: 0; right: 0; text-align: center; font-size: 10px; color: var(--dim); }
.curve div::before { content: attr(data-c); position: absolute; bottom: -14px; left: 0; right: 0; text-align: center; font-size: 10px; color: var(--dim); }
.size { margin-left: auto; font-weight: 800; color: var(--yellow); font-size: 16px; }
.size.bad { color: #ff5d5d; }
.status { font-size: 12px; margin: 14px 0 6px; }
.status .err { color: #ff5d5d; } .status .warn { color: var(--dim); } .status .ok { color: var(--green); }
.decklist h4 { margin: 10px 0 4px; color: var(--cyan); font-size: 11px; letter-spacing: .08em; }
.decklist .line { display: grid; grid-template-columns: 24px 1fr 28px 52px; gap: 6px; align-items: center; padding: 2px 0; border-bottom: 1px solid #0f1620; font-size: 12px; }
.decklist .line .n { color: var(--yellow); font-weight: 800; }
.decklist .line .cost { color: var(--dim); text-align: right; }
.decklist .line button { padding: 0 6px; font-size: 11px; line-height: 16px; }
.decklist .line.bad { color: #ff5d5d; }
@media (max-width: 1100px) { .builder { grid-template-columns: 1fr; height: auto; } }
.dim.small { font-size: 11px; flex-basis: 100%; }
.builder .library .card .have { top: 18px; }

/* ---------------------------------------------------------------- lab */
.lab { display: grid; grid-template-columns: 520px minmax(0, 1fr); gap: 8px; padding: 8px; align-items: start; }
.lab .labcol { min-width: 0; }
.lab .setup { max-width: none; margin: 0 0 8px; }
.hint { color: var(--dim); font-size: 12px; margin: 0 0 8px; }
.checklist { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 2px 10px; margin: 6px 0; max-height: 220px; overflow: auto; }
.checklist label { display: flex; gap: 6px; align-items: flex-start; font-size: 12px; color: var(--fg); cursor: pointer; min-width: 0; }
.checklist label input { flex: 0 0 auto; margin-top: 2px; }
.checklist label small { color: var(--dim); }
.checklist label .txt { min-width: 0; overflow-wrap: anywhere; }
.checklist label .txt b { color: var(--text); font-weight: 600; }
.checklist label .txt .rec { color: var(--dim); }
.checklist label .txt small { display: block; white-space: normal; line-height: 1.3; }
.checklist .hint { grid-column: 1 / -1; white-space: normal; margin: 4px 0; }
.jobs .job { border: 1px solid var(--line); border-radius: 8px; padding: 6px 8px; margin: 6px 0; font-size: 12px; }
.jobs .job .head { display: flex; gap: 10px; align-items: center; }
.jobs .job .head b { color: var(--cyan); }
.jobs .job .st { margin-left: auto; padding: 1px 8px; border-radius: 10px; border: 1px solid var(--line2); }
.jobs .job .st.running { color: var(--yellow); } .jobs .job .st.done { color: var(--green); } .jobs .job .st.failed { color: #ff5d5d; }
.jobs .job pre { margin: 6px 0 0; max-height: 140px; overflow: auto; font-size: 11px; color: var(--dim); white-space: pre-wrap; }
.jobs .job .reps a { margin-right: 8px; color: var(--cyan); cursor: pointer; text-decoration: underline; }
#report pre.md { white-space: pre-wrap; font-size: 11px; color: var(--dim); max-height: 60vh; overflow: auto; }
@media (max-width: 1100px) { .lab { grid-template-columns: minmax(0, 1fr); } .labcol { min-width: 0; } .how { max-width: 100%; box-sizing: border-box; } }

/* ---------------------------------------------------------------- drag to play */
.card.draggable { cursor: grab; touch-action: none; }
.card, .card img { -webkit-user-drag: none; user-select: none; -webkit-user-select: none; }
.card.dragging { opacity: .28; }
.dragghost { position: fixed; z-index: 60; pointer-events: none; margin: 0 !important;
  transform: rotate(-5deg) scale(1.06); box-shadow: 0 18px 44px #000d; opacity: .96; }
/* A zone this card could be dropped on. The label is the zone's own, so the board says what the
   drop would do rather than leaving it to be guessed. */
.droptarget { outline: 2px dashed var(--green); outline-offset: -4px; position: relative; }
.droptarget::after { content: attr(data-drop); position: absolute; inset: auto 0 6px 0; text-align: center;
  color: var(--green); font-weight: 900; font-size: 11px; letter-spacing: .16em; pointer-events: none;
  text-shadow: 0 0 6px #000, 0 0 12px #000; }
.dropover { outline-style: solid; background: #0f2a1a; box-shadow: inset 0 0 30px #8ff26a33; }
.card.droptarget::after { inset: auto 0 2px 0; font-size: 8px; letter-spacing: .06em; }

/* ---------------------------------------------------------------- arrivals */
/* The card that just hit the board, held full size for a beat so a rival's turn can be read. */
.spotlight { position: fixed; left: 50%; top: 50%; z-index: 55; width: 300px; height: 418px;
  border-radius: 14px; overflow: hidden; border: 2px solid var(--yellow);
  box-shadow: 0 0 60px #f4e01f55, 0 18px 60px #000d; pointer-events: none; opacity: 0;
  transform: translate(-50%, -50%) scale(.8); }
.spotlight img { width: 100%; height: 100%; object-fit: cover; display: block; }
.spotlight.show { animation: spot 1.1s ease-out forwards; }
@keyframes spot {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(.72); }
  14%  { opacity: 1; transform: translate(-50%, -50%) scale(1.04); }
  22%  { transform: translate(-50%, -50%) scale(1); }
  78%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -52%) scale(.94); }
}

/* A card that was not on the board a moment ago. */
.card.fresh { animation: arrive .9s ease-out; z-index: 2; }
@keyframes arrive {
  0%   { box-shadow: 0 0 0 3px var(--yellow), 0 0 26px var(--yellow); filter: brightness(1.5); }
  60%  { box-shadow: 0 0 0 2px var(--yellow), 0 0 16px var(--yellow); filter: brightness(1.1); }
  100% { box-shadow: none; filter: none; }
}
@media (prefers-reduced-motion: reduce) {
  .spotlight.show { animation: none; opacity: 1; transform: translate(-50%, -50%) scale(1); }
  .card.fresh { animation: none; }
}

/* ---------------------------------------------------------------- log */
.logwrap b.who { color: var(--text); font-weight: 800; }
.logwrap b.cardname { color: var(--yellow); font-weight: 600; }
.logwrap .turn b { color: inherit; }      /* a turn header is already the loudest line there is */

/* ---------------------------------------------------------------- selected card */
.cardinfo .q { color: var(--yellow); font-weight: 800; font-size: 13px; line-height: 1.25; }
.cardinfo .ctext { color: #c8d4dc; font-size: 11px; line-height: 1.45; margin: 6px 0; }
.cardinfo .desc { color: var(--dim); font-size: 11px; margin-top: 6px; }
.cardinfo .opts { display: flex; flex-direction: column; gap: 5px; margin-top: 8px; }
.cardinfo .opts button { font-size: 11px; text-align: left; }
.card.picked { outline: 2px solid var(--cyan); outline-offset: 1px; }

/* ---------------------------------------------------------------- payment picker */
/* The sources are cards on the table, so the choice is made on the table. The panel keeps the
   count and the way out; the cards keep the clicking. */
.paypanel { border-color: var(--yellow); }
.paypanel .q { color: var(--yellow); font-weight: 800; font-size: 15px; letter-spacing: .03em; }
.paypanel .count { color: var(--green); font-weight: 800; font-size: 13px; margin: 6px 0 4px; letter-spacing: .06em; }
.paypanel .desc { color: var(--dim); font-size: 11px; line-height: 1.4; }
.paypanel .opts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.paypanel .opts button { font-size: 11px; }
.card.payable { cursor: pointer; box-shadow: 0 0 0 2px var(--yellow), 0 0 14px var(--yellow); }
.card.payable:hover { transform: translateY(-3px); }
.card.paypicked { box-shadow: 0 0 0 3px var(--green), 0 0 18px var(--green); filter: brightness(1.2); }

/* ---------------------------------------------------------------- hover hints
   A `title` attribute is the browser's white tooltip box, which does not belong over the cards.
   Anything that wants to name itself carries `data-hint` and the board draws it in its own
   colours, above the thing, and never in the way of a click. */
[data-hint] { position: relative; }
[data-hint]:hover::after {
  content: attr(data-hint); position: absolute; left: 50%; bottom: calc(100% + 6px);
  transform: translateX(-50%); white-space: nowrap; pointer-events: none; z-index: 30;
  background: #0a1620; border: 1px solid var(--line2); border-radius: 6px; padding: 3px 8px;
  color: var(--cyan); font-size: 11px; font-weight: 600; letter-spacing: .04em;
  box-shadow: 0 4px 14px #000a;
}
/* the rival's row has nothing above it, so its hints hang below instead */
.p-opp-fixer [data-hint]:hover::after, .p-opp-gig [data-hint]:hover::after,
.stacks > .stackbox:first-child[data-hint]:hover::after {
  bottom: auto; top: calc(100% + 6px);
}

/* ---------------------------------------------------------------- looking through a pile */
.pileview { position: fixed; inset: 0; z-index: 60; background: #030608d8; display: none; place-items: center; padding: 24px; }
.pileview.show { display: grid; }
.pileview .sheet { background: var(--panel); border: 1px solid var(--line2); border-radius: 14px; padding: 16px 18px; max-width: min(1500px, 94vw); max-height: 88vh; overflow-y: auto; box-shadow: 0 18px 60px #000c; }
.pileview .head { display: flex; align-items: center; gap: 14px; margin-bottom: 14px; }
.pileview .head .t { color: var(--cyan); font-size: 21px; font-weight: 800; letter-spacing: .1em; }
.pileview .head .c { color: var(--dim); font-size: 12px; letter-spacing: .1em; margin-right: auto; }
.pileview .pilegrid { display: flex; flex-wrap: wrap; gap: 10px; }
.pileview .pilegrid .card { width: 168px; height: 237px; font-size: 11px; }
.stackbox.open { cursor: pointer; }
.stackbox.open:hover { border-color: var(--cyan); }

/* ---------------------------------------------------------------- hover preview */
.preview { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 50; width: 423px; height: 590px; border-radius: 14px; overflow: hidden; border: 2px solid var(--line2); box-shadow: 0 12px 40px #000c; background: #000; display: none; pointer-events: none; }
.preview.show { display: block; }
.preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (max-height: 700px) { .preview { width: 300px; height: 418px; } }

.logbtn { display: none; }

/* ---------------------------------------------------------------- phone: the playmat, folded
   The board is a real playmat folded about its centre line. Top to bottom: the rival's legends and
   Eddies, their field, the centre line with both Gig trays sitting on it, my field, my legends and
   Eddies, my hand, and the prompt.

   What makes it fit is the flanks. A fixer is six dice and a deck is a number; stood up in a 34px
   column beside the field, neither costs a row, and each sits at the same latitude as its own
   player's field. Everything that was a row and did not need to be — the deck/trash strip, the
   fixer block wrapped over two lines, the selected-card panel, the rival's hand drawn as a row of
   identical backs — is gone, and the height goes to the cards instead.

   No labels. The desktop board dropped them and this one has even less room for them; the previous
   rules pinned a label into a gutter that `.board .panel` had already overridden away, so every
   one of them was drawn on top of its own panel's text. */
@media (max-width: 700px) {
  .top { padding: 3px 6px; height: 36px; box-sizing: border-box; flex-wrap: nowrap; }
  .brand { font-size: 10px; white-space: nowrap; letter-spacing: 0; } .brand span:not(.build) { display: none; }
  /* the nav scrolls rather than clipping its last button off the edge of the screen */
  nav { display: flex; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  nav::-webkit-scrollbar { display: none; }
  nav button { padding: 3px 4px; font-size: 8.5px; margin-left: 2px; letter-spacing: 0; flex: 0 0 auto; }

  .board {
    /* Five rows hold a card: the two Legend bands, the two fields and my hand. Everything else is
       measured chrome, so the card height is what is left over divided by five — derived, not the
       fixed 46x64 this layout used to hard-code. */
    --chrome: 276px;
    --card-h: max(54px, calc((100dvh - var(--chrome)) / 5));
    --card-w: calc(var(--card-h) / 1.41);
    display: grid; gap: 3px; padding: 3px; height: calc(100dvh - 36px);
    overflow: hidden; box-sizing: border-box;
    grid-template-columns: 34px minmax(0, 1fr) minmax(0, 1fr) 34px;
    grid-template-rows: auto auto minmax(0, 1fr) auto minmax(0, 1fr) auto auto auto;
    grid-template-areas:
      "ctl   ctl    ctl   ohand"
      "oleg  oleg   oleg  oleg"
      "ofix  ofield ofield ostk"
      "ogig  ogig   mgig  mgig"
      "mfix  mfield mfield mstk"
      "mleg  mleg   mleg  mleg"
      "mhand mhand  mhand mhand"
      "prmpt prmpt  prmpt prmpt";
  }
  .board > .col { display: contents; }
  .p-controls { grid-area: ctl; display: flex; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
                gap: 3px; padding: 0; border: 0; background: none; align-items: center; }
  .p-controls::-webkit-scrollbar { display: none; }
  .p-controls button { padding: 3px 6px; font-size: 9px; flex: 0 0 auto; }
  .p-controls .desc { display: none; }
  /* The rival's hand is a count here. A row of identical backs is a count wearing a costume, and
     this layout has no room for the costume. */
  .p-opp-hand { grid-area: ohand; padding: 0; border: 0; background: none; overflow: visible;
                display: flex; align-items: center; justify-content: center; min-height: 0; }
  .p-opp-hand .card { display: none; }
  .p-opp-hand .handcount { display: block; font-size: 8px; line-height: 1.1; text-align: center;
                           color: var(--cyan); letter-spacing: .04em; }

  /* `.board ` on every one of these on purpose. The wide layout places the centre column's
     children with `.center > .field`, `.center > .legendrow` and friends, and those are two
     classes deep; a bare `.p-opp-field` is one and loses, which put the field in the 34px fixer
     column. Same weight, later in the file, so these win. */
  .board .p-opp-legends { grid-area: oleg; } .board .p-my-legends { grid-area: mleg; }
  .board .p-opp-field { grid-area: ofield; } .board .p-my-field { grid-area: mfield; }
  .board .p-opp-fixer { grid-area: ofix; } .board .p-my-fixer { grid-area: mfix; }
  .board .p-opp-gig { grid-area: ogig; } .board .p-my-gig { grid-area: mgig; }
  .board .stacks.opp { grid-area: ostk; } .board .stacks.mine { grid-area: mstk; }
  .board .p-my-hand { grid-area: mhand; } .board .p-prompt { grid-area: prmpt; }
  .board .p-controls { grid-area: ctl; overflow-x: auto; overflow-y: hidden; }
  .board .p-opp-hand { grid-area: ohand; }

  /* the flanks: stood up, so neither costs a row */
  /* Six dice down a 34px column would be 166px against a field row of about 116, so they go two
     across: three rows of two, which is shorter than the field beside it at any card size. */
  .dice { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; padding: 1px; align-content: start; }
  .board .panel.dice { padding: 2px 1px; }
  .die { --die: 15px; }
  .die svg { stroke-width: 4; }
  .die.fixer .face { font-size: 6px; }
  .stacks { grid-template-rows: repeat(2, 1fr); gap: 3px; }
  .stackbox .n.big { font-size: 13px; }
  .stackbox .card.thumb { display: none; }

  /* the centre line: the two Gig trays meeting at it, rival above-left, mine above-right */
  /* Both trays take the same top border, so the two of them draw one unbroken rule across the
     width of the board with a Gig area hanging below each end of it — the line in the sketch. */
  .p-opp-gig, .p-my-gig { min-height: 0; border-top: 2px solid var(--cyan); border-radius: 0 0 6px 6px; }
  .gigs { min-height: 0; gap: 3px; padding: 2px 4px; } .gigs .list { gap: 2px; }
  .gigs .waiting { font-size: 9px; }
  .score { padding: 1px 5px; } .score .n { font-size: 10px; } .score .c { font-size: 11px; }

  /* the banner has nothing to say here that the prompt does not: END TURN is in the prompt's own
     list of every legal action, so only the turn and phase survive, folded into the controls row */
  .p-banner { display: none; }
  /* likewise the selected-card panel: the prompt lists every action the card could take, and a
     long press already shows the card's face full size */
  .p-cardinfo, .nameplate { display: none; }

  .board .panel .lbl { display: none; }
  .panel { padding: 3px 4px; border-radius: 6px; min-height: 0; overflow: hidden; }
  .legendrow { gap: 3px; min-height: 0; }
  .legendrow .legends { flex: 1 1 auto; min-width: 0; }
  .legendrow .eddies { flex: 0 1 40%; min-width: 0; }
  .legends, .field, .hand { gap: 3px; flex-wrap: nowrap; overflow: hidden; align-items: flex-start; }
  /* The field rows are the two the grid gives its leftovers to, so they are the two that run a few
     pixels short. Trimming their own padding is worth more than shrinking every card on the board
     by the same few pixels to pay for it. */
  .board .panel.field { padding: 1px 3px; }
  /* Gear hangs a fifth of a card below its host, and no row on a phone has a fifth of a card to
     spare — it was pushing a Legend band 21px past its own edge, where it was clipped and invisible
     anyway. The power chip already says the host is modified, which is the part that changes a
     decision; the long-press preview still shows the card itself. */
  .gearstack { display: none; }
  .eddies { width: auto; min-width: 0; }
  .eddies .list { min-height: 0; gap: 2px; flex-wrap: nowrap; overflow: hidden; }
  .eddies .waiting { font-size: 9px; }
  .board .eddies .badge { right: 3px; bottom: 2px; padding: 0 5px; font-size: 9px; }
  .card { border-width: 1px; border-radius: 4px; font-size: 6px; }
  .board .card, .board .card.sm { width: var(--card-w); height: var(--card-h); }
  .hand.mine .card { width: var(--card-w); height: var(--card-h); }
  /* The wide layout reserves a card's height for each hand with
     `.col.left > .panel.hand { min-height: calc(var(--card-h) + 12px) }`, which is four classes
     deep and beat every phone rule that tried to undo it. The rival's hand is a count here, so
     that reservation was silently making the top row as tall as a card row and taking 105px
     straight off the two fields. Same weight, later in the file. */
  .col.left > .panel.hand { flex: none; min-height: 0; }
  .col > .panel.hand.mine { min-height: 0; flex: 0 0 auto; }
  .card.spent { margin: 0 calc((var(--card-h) - var(--card-w)) / 2); }
  .slot { width: var(--card-w); height: var(--card-h); border-radius: 4px; }
  .card .pnow { border-width: 1px; font-size: calc(var(--card-w) * .18); }

  /* The payment picker is created mid-turn and inserted into the board, so on a grid it has no
     area of its own and auto-places into a brand new row — which collapsed every other row to a
     couple of pixels. It is a question that has to be answered before anything else happens, so a
     sheet over the bottom of the screen is what it should have been here anyway. */
  .paypanel { position: fixed; left: 4px; right: 4px; bottom: 4px; z-index: 30;
              max-height: 52vh; overflow-y: auto; box-shadow: 0 -8px 30px #000c; }
  .logbtn { display: inline-block; }
  .p-log { display: none; position: fixed; inset: 36px 0 0 0; z-index: 40; background: #050810f2; padding: 12px; overflow-y: auto; font-size: 13px; }
  .p-log.open { display: block; }
  .col > .panel.logwrap { flex: none; }

  /* A fixed ceiling, not a share of the screen. At 26vh the prompt was taking 219px of an 844px
     phone and the two field rows were splitting what was left, which is how a field ended up 78px
     tall holding a 111px card. It scrolls inside itself instead. */
  .prompt { min-height: 0; max-height: 128px; overflow-y: auto; }
  .prompt .q { font-size: 12px; margin-bottom: 2px; } .prompt .desc { font-size: 10px; margin-bottom: 4px; }
  .prompt .opts { gap: 4px; max-height: none; } .prompt .opts button { padding: 5px 8px; font-size: 11px; }
  .pileview .pilegrid .card { width: 104px; height: 147px; }
}
/* touch preview: centred, any size screen */
.preview.touch { width: min(84vw, 423px); height: calc(min(84vw, 423px) * 1.395); pointer-events: auto; box-shadow: 0 0 0 100vmax #000a, 0 12px 40px #000c; }

/* ---------------------------------------------------------------- guide + explainers */
.how { margin: 0 0 14px; padding: 8px 12px; border: 1px solid var(--line); border-radius: 8px; background: #0a1018; font-size: 13px; line-height: 1.45; max-width: 80ch; }
.how.wide { margin: 8px; max-width: none; }
.how summary { cursor: pointer; color: var(--cyan); font-weight: 700; letter-spacing: .04em; }
.how p, .how li { color: var(--text); } .how p { margin: 8px 0; }
.how ol, .how ul { padding-left: 22px; margin: 6px 0; } .how li { margin: 4px 0; }
.how code, .guide code { background: #121a26; padding: 1px 5px; border-radius: 4px; color: var(--yellow); font-size: 12px; }
.how b, .guide b { color: #fff; }
.guide { max-width: 90ch; font-size: 14px; line-height: 1.5; }
.guide .lede { font-size: 15px; color: #fff; }
.guide h3 { color: var(--cyan); letter-spacing: .06em; font-size: 13px; margin: 22px 0 6px; text-transform: uppercase; }
.guide ol, .guide ul { padding-left: 22px; } .guide li { margin: 5px 0; }
.guide table.rep td { vertical-align: top; }
@media (max-width: 700px) { .how { font-size: 12px; } .how.wide { margin: 4px; } }

/* ---------------------------------------------------------------- static build: engine loading */
#engineLoading { position: fixed; inset: 0; z-index: 100; background: #050810; display: grid; place-items: center; color: var(--text); }
#engineLoading .box { width: min(90vw, 460px); text-align: center; }
#engineLoading .t { color: var(--yellow); font-weight: 800; letter-spacing: .12em; margin-bottom: 8px; }
#engineLoading .s { color: var(--cyan); margin-bottom: 12px; }
#engineLoading .bar { height: 4px; background: #121a26; border-radius: 2px; overflow: hidden; }
#engineLoading .bar i { display: block; height: 100%; width: 40%; background: var(--cyan); animation: slide 1.2s infinite; }
@keyframes slide { from { transform: translateX(-100%); } to { transform: translateX(250%); } }
#engineLoading .n { color: var(--dim); font-size: 12px; margin-top: 12px; }

@media (max-width: 700px) and (max-height: 720px) {
  .board { --card-w: 40px; --card-h: 56px; gap: 2px; padding: 2px; }
  .card.sm { width: 34px; height: 48px; }
  .hand.mine .card { width: 50px; height: 70px; }
  .hand.opp .card { width: 28px; height: 39px; } .hand.opp .card.back + .card.back { margin-left: -19px; }
  .die { width: 22px; height: 22px; font-size: 7px; } .die.gig { font-size: 9px; }
  .badge { padding: 0 3px; } .badge b { font-size: 9px; }
  .phase .title { font-size: 10px; padding: 1px; } .phase .sub { font-size: 8px; }
  .prompt { max-height: 22vh; } .prompt .desc { display: none; }
  .p-controls button { padding: 2px 5px; font-size: 8px; }
}
.brand .build { color: var(--dim); font-size: 10px; margin-left: 8px; letter-spacing: 0; font-weight: 400; }

/* ---------------------------------------------------------------- lab: estimates, cancel, phone fit */
.estimate { margin-top: 6px; font-size: 12px; color: var(--cyan); }
.estimate.warn { color: var(--yellow); }
.jobs .job .head { flex-wrap: wrap; }
.jobs .job button.cancel { padding: 1px 8px; font-size: 10px; border-color: #ff5d5d; color: #ff5d5d; }
.jobs .job .st.cancelled { color: var(--dim); }
@media (max-width: 700px) {
  .checklist { grid-template-columns: minmax(0, 1fr); max-height: 240px; }
  .jobs .job pre { max-height: 90px; }
  .lab .row label { flex-wrap: wrap; }
}
.toast { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%) translateY(20px); background: #101a28; color: var(--green); border: 1px solid var(--green); border-radius: 8px; padding: 8px 14px; font-size: 13px; opacity: 0; pointer-events: none; transition: opacity .2s, transform .2s; z-index: 60; max-width: 90vw; }
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
@media (max-width: 700px) {
  .setup select, .setup input[type=text], .setup input:not([type]) { max-width: 100%; min-width: 0; }
  .setup .row label { max-width: 100%; min-width: 0; }
  .setup .row select { flex: 1 1 120px; }
}

/* ---------------------------------------------------------------- lab: job progress */
.jobs .job .prog-text { margin-top: 4px; line-height: 1.4; }
.jobs .job .prog { height: 3px; background: #121a26; border-radius: 2px; overflow: hidden; margin: 5px 0 2px; }
.jobs .job .prog i { display: block; height: 100%; width: 0; background: var(--cyan); transition: width .5s ease; }

/* ---------------------------------------------------------------- lab: the report page
   Everything sits inside the right lab column; tables that can be wider than a phone live in a
   .scroll-x wrapper so the page itself never scrolls sideways. */
.report { min-width: 0; max-width: 100%; overflow-wrap: anywhere; }
.report .rhead h2 { margin: 12px 0 4px; color: var(--yellow); font-size: 17px; letter-spacing: .04em; }
.report .facts { margin: 0 0 6px; color: var(--text); font-size: 13px; }
.report .file { font-size: 11px; margin: 0; }
.report .lede { color: var(--dim); font-size: 12.5px; margin: 2px 0 8px; line-height: 1.4; }
.report .rsec { margin: 18px 0 0; padding-top: 10px; border-top: 1px solid var(--line); }
.report .rsec h3 { margin: 0 0 4px; color: var(--cyan); font-size: 14px; letter-spacing: .06em; text-transform: uppercase; }
/* The disclosure of who played the games: readable body text, not a footnote, marked with the
   yellow rule so it is not skimmed past on the way to the standings. */
.report .disc { margin: 4px 0 0; padding: 8px 12px; border-left: 3px solid var(--yellow); background: #10161f;
  border-radius: 0 6px 6px 0; font-size: 13px; line-height: 1.5; color: var(--text); }
.report h4 { margin: 14px 0 4px; color: var(--text); font-size: 13px; }
.report h5 { margin: 8px 0 4px; color: var(--cyan); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; }
.report .words { margin: 4px 0; padding-left: 20px; font-size: 14px; line-height: 1.5; color: #fff; }
.report .words li { margin: 4px 0; }
/* A wide table scrolls sideways inside its wrapper; the shadow at the edge is the cue that there
   is more to the right (or left), drawn with the scrolling-background trick, no script needed. */
.report .scroll-x { overflow-x: auto; max-width: 100%; -webkit-overflow-scrolling: touch;
  background: linear-gradient(90deg, var(--panel) 30%, rgba(13, 20, 32, 0)), linear-gradient(90deg, rgba(13, 20, 32, 0), var(--panel) 70%) 100% 0,
    radial-gradient(farthest-side at 0 50%, rgba(88, 224, 255, .35), rgba(88, 224, 255, 0)), radial-gradient(farthest-side at 100% 50%, rgba(88, 224, 255, .35), rgba(88, 224, 255, 0)) 100% 0;
  background-repeat: no-repeat; background-size: 40px 100%, 40px 100%, 14px 100%, 14px 100%; background-attachment: local, local, scroll, scroll; }
.report .tapnote { margin: 6px 0 0; padding: 6px 10px; border: 1px solid var(--line2); border-radius: 6px; background: #0a1018; color: var(--text); font-size: 12.5px; }
.report .tap { cursor: pointer; }
.report .fresh { color: var(--cyan); font-size: 12.5px; margin: 4px 0; }
.report table { border-collapse: collapse; font-size: 13px; }
.report table.rep.small { font-size: 12px; margin: 6px 0; }
.report table.rep.small td { white-space: nowrap; }
/* a long column name takes a second line instead of pushing the table off the right of the page */
.report table.rep.small th { white-space: normal; vertical-align: bottom; min-width: 4.5em; }
.report table th { overflow-wrap: normal; }              /* a header breaks between words, never inside one */
/* a cell breaks a word only when the word cannot fit at all: "builder3" stays one word */
.report table.rep td { overflow-wrap: break-word; }
.report table.rep .short { display: none; }              /* the phone rules swap in the short header */
.report table.helped .thinflag { display: none; }        /* the word "thin" says it in the Diff. column */
/* no line of the report is smaller than 10.5 px: below that a phone screen stops being readable */
.report small { font-size: max(10.5px, 0.85em); }
.report .kind { display: inline-block; padding: 1px 8px; border-radius: 10px; border: 1px solid var(--cyan); color: var(--cyan); font-size: 11px; font-weight: 600; white-space: nowrap; vertical-align: middle; }
.report .kind.exploring { border-color: var(--dim); color: var(--dim); }
.report .cardchip { color: var(--text); cursor: default; }
.report .cardchip.pic { display: inline-flex; flex-direction: column; align-items: center; width: 64px; text-align: center; font-size: 10.5px; line-height: 1.15; color: var(--dim); overflow-wrap: normal; hyphens: auto; }
.report .cardchip.pic img { width: 60px; height: 84px; object-fit: cover; border-radius: 4px; border: 1px solid var(--line2); display: block; margin-bottom: 2px; }
.report .legs { display: flex; gap: 6px; flex-wrap: wrap; }
.report .legs.big .cardchip.pic { width: 92px; font-size: 11px; }
.report .legs.big .cardchip.pic img { width: 88px; height: 123px; }
.report .acts { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 4px; }
.report .acts button, .report .textver button { padding: 3px 9px; font-size: 11px; }
/* standings */
.report table.stand { width: 100%; }
.report table.stand th, .report table.stand td { border-bottom: 1px solid var(--line); padding: 6px 6px; text-align: left; vertical-align: middle; }
.report table.stand th { color: var(--cyan); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; white-space: nowrap; }
.report table.stand td.rank { color: var(--yellow); font-weight: 800; font-size: 15px; width: 28px; }
.report table.stand td.deck b { color: #fff; }
.report table.stand td.wr { min-width: 150px; }
.report table.stand td.legends { min-width: 216px; } .report table.stand td.legends .legs { flex-wrap: nowrap; }
.report table.stand td.bt small, .report table.stand td.nash small { color: var(--dim); }
.report .barwrap { display: flex; flex-direction: column; gap: 3px; }
.report .bar { position: relative; height: 12px; background: #121a26; border-radius: 6px; overflow: hidden; }
.report .bar s { position: absolute; left: 50%; top: 0; bottom: 0; width: 1px; background: var(--line2); text-decoration: none; }
.report .bar .ci { position: absolute; top: 0; bottom: 0; background: rgba(88, 224, 255, .28); }
.report .bar b { position: absolute; top: 2px; bottom: 2px; width: 3px; margin-left: -1.5px; background: var(--cyan); border-radius: 2px; }
.report .bartext { font-size: 12px; white-space: nowrap; } .report .bartext small { color: var(--dim); }
.report .kinds { margin-top: 10px; }
/* head-to-head */
.report table.matrix th, .report table.matrix td { border: 1px solid var(--line); padding: 5px 7px; text-align: center; font-size: 12px; white-space: nowrap; }
.report table.matrix th { color: var(--cyan); font-weight: 600; }
.report table.matrix tr th:first-child { text-align: left; }
.report table.matrix td .r { font-weight: 700; color: #fff; }
.report table.matrix td .sig { color: var(--yellow); font-weight: 900; margin-left: 2px; }
.report table.matrix td small { color: var(--dim); }
.report table.matrix td.self { color: var(--dim); }
.report table.matrix .short { display: none; }
.report .legend { color: var(--dim); font-size: 11.5px; margin: 6px 0 0; }
/* the decks */
.report details.deck { border: 1px solid var(--line); border-radius: 8px; padding: 6px 10px; margin: 6px 0; background: #0a1018; }
.report details.deck summary { cursor: pointer; font-size: 13px; }
.report details.deck summary b { color: #fff; }
.report details.deck .legnames { font-size: 12px; }
.report details.deck .body { padding: 6px 0 2px; }
.report .shape { font-size: 12.5px; margin: 4px 0 8px; color: var(--text); } .report .shape b { color: var(--cyan); }
.report details.deck .row { align-items: flex-start; }
.report details.deck .lbl { font-size: 10.5px; color: var(--dim); letter-spacing: .1em; flex-basis: 100%; }
.report .deckgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 4px 14px; margin: 6px 0; }
.report .deckgrid .line { font-size: 12px; padding: 1px 0; border-bottom: 1px solid #0f1620; }
.report .deckgrid .line .n { color: var(--yellow); font-weight: 800; margin-right: 5px; }
.report .curve.mini { height: 44px; margin: 14px 0 12px; } .report .curve.mini div { width: 18px; }
.report .changes { margin: 8px 0; font-size: 12.5px; } .report .changes ul { margin: 2px 0; padding-left: 20px; } .report .changes p { margin: 4px 0; }
.report .replaced { color: var(--yellow); font-size: 12.5px; }
/* cards that helped and hurt */
.report table.helped td.pos { color: var(--green); font-weight: 700; } .report table.helped td.neg { color: var(--red); font-weight: 700; }
.report table.helped td.thin { opacity: .65; font-weight: 400; } .report table.helped td.thin small { color: var(--dim); }
.report table.helped tr.gap td { color: var(--dim); }
/* league evolution */
.report svg.evo { width: 100%; height: auto; display: block; margin: 6px 0; }
.report svg.evo .grid { stroke: var(--line); stroke-width: 1; } .report svg.evo .mid { stroke: var(--line2); stroke-dasharray: 4 4; }
.report svg.evo .seg { stroke-width: 2; opacity: .8; } .report svg.evo .seg.newdeck { stroke-dasharray: 3 4; opacity: .5; }
.report svg.evo .tick { fill: var(--dim); font-size: 11px; } .report svg.evo .name { font-size: 11px; font-weight: 700; }
.report svg.evo .cross { fill: var(--red); font-size: 11px; font-weight: 900; }
.report .evolegend { display: flex; gap: 6px 14px; flex-wrap: wrap; font-size: 11.5px; color: var(--text); margin: 4px 0; }
.report .evolegend i { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 5px; vertical-align: -1px; }
/* glossary + text version */
.report .rglossary { margin-top: 18px; max-width: none; }
.report .rglossary dl { margin: 6px 0; } .report .rglossary dt { color: #fff; font-weight: 700; margin-top: 8px; } .report .rglossary dd { margin: 2px 0 0; color: var(--text); }
.report .textver { margin-top: 12px; font-size: 12px; color: var(--dim); } .report .textver summary { cursor: pointer; }
@media (max-width: 700px) {
  .report .lede { font-size: 12px; }
  /* standings become cards: one block per deck, each cell labelled */
  .report table.stand, .report table.stand tbody, .report table.stand tr, .report table.stand td { display: block; width: 100%; }
  .report table.stand thead { display: none; }
  .report table.stand tr { border: 1px solid var(--line); border-radius: 8px; padding: 6px 8px; margin: 6px 0; background: #0a1018; }
  .report table.stand td { border: 0; padding: 3px 0; }
  .report table.stand td[data-l]:not(.rank):not(.deck):not(.act)::before { content: attr(data-l); display: block; font-size: 10.5px; color: var(--dim); letter-spacing: .08em; text-transform: uppercase; }
  .report table.stand td.rank { float: left; width: auto; margin-right: 8px; font-size: 18px; }
  .report table.stand td.deck { font-size: 14px; }
  .report table.stand td.bt, .report table.stand td.nash { display: inline-block; width: 48%; }
  .report .cardchip.pic { width: 52px; } .report .cardchip.pic img { width: 50px; height: 70px; }
  .report table.matrix .full { display: none; } .report table.matrix .short { display: inline; }
  /* four to six columns fit a phone when the games count drops to a second line */
  .report table.matrix th, .report table.matrix td { padding: 4px 3px; font-size: 11px; }
  .report table.matrix td small { display: block; font-size: 10.5px; }
  /* the small tables swap in the short header report.js gives every column and wrap what is left,
     so the whole table fits; a long archetype label wraps inside its badge rather than squeezing
     the number columns to one letter */
  .report table.rep.small th, .report table.rep.small td { white-space: normal; padding: 4px 5px; font-size: 11px; }
  .report table.rep.small th { min-width: 0; }
  .report table.rep .full { display: none; } .report table.rep .short { display: inline; }
  .report table.helped .thinword { display: none; }      /* too small to read; the ⚠ carries it */
  .report table.helped .thinflag { display: inline; color: var(--yellow); }
  .report table.rep.small td:first-child { min-width: 98px; }    /* the small tables fit a 390 px screen whole */
  .report .kind { white-space: normal; line-height: 1.25; }
  .report table.rep.small td .cardchip { white-space: normal; }
  .report .deckgrid { grid-template-columns: 1fr; }
  .report .rsec h3 { font-size: 13px; }
}


/* ---------------------------------------------------------------- card guide
   The CARDS page opens a card's own page: its face, its text, and every card in the pool it
   connects to. Modelled on .pileview — same dim backdrop, same escape-to-close — because it is the
   same gesture, reading something at a size you can actually read it at. */
.guideview { position: fixed; inset: 0; background: rgba(4, 6, 10, .82); z-index: 60;
  display: none; align-items: flex-start; justify-content: center; padding: 3vh 2vw; overflow: auto; }
.guideview.show { display: flex; }
.guideview > .sheet { background: var(--panel, #0d1117); border: 1px solid var(--cyan, #2de2e6);
  border-radius: 6px; max-width: 1000px; width: 100%; max-height: 94vh; overflow: auto; }
.guideview .head { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 10px 14px; border-bottom: 1px solid rgba(45, 226, 230, .3); position: sticky; top: 0;
  background: var(--panel, #0d1117); z-index: 1; }
.guideview .head h3 { margin: 0; font-size: 1.05rem; }
.guideview .x { background: none; border: 0; color: inherit; font-size: 1.1rem; cursor: pointer; }
.guideview .gbody { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: 16px; padding: 14px; }
.guideview .gleft img { width: 100%; border-radius: 6px; display: block; }
.guideview .gstats { display: flex; flex-wrap: wrap; gap: 6px 10px; margin-top: 8px; font-size: .78rem; }
.guideview .gtags { margin-top: 6px; font-size: .72rem; opacity: .7; letter-spacing: .04em; }
.guideview .gtext { white-space: pre-wrap; font: inherit; font-size: .86rem; margin: 0 0 10px;
  padding: 10px; background: rgba(255,255,255,.04); border-left: 2px solid var(--cyan, #2de2e6); border-radius: 3px; }
.guideview h4 { margin: 14px 0 6px; font-size: .74rem; letter-spacing: .1em; opacity: .85; }
.guideview .garch { margin: 4px 0 2px; }
.guideview .garch .tag { display: inline-block; padding: 1px 7px; margin-right: 5px; font-size: .72rem;
  border: 1px solid rgba(45,226,230,.45); border-radius: 10px; }
.guideview .tips { margin: 6px 0 0 16px; padding: 0; font-size: .84rem; }
.guideview .tips li { margin: 2px 0; }
.guideview .lgroup { margin-bottom: 8px; }
.guideview .why { font-size: .76rem; opacity: .8; margin-bottom: 3px; }
.guideview .links .row { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
.guideview .chip { background: rgba(45,226,230,.12); border: 1px solid rgba(45,226,230,.4);
  color: inherit; border-radius: 3px; padding: 2px 7px; font-size: .76rem; cursor: pointer; }
.guideview .chip:hover { background: rgba(45,226,230,.3); }
.guideview .chip.soft { border-style: dashed; opacity: .75; }
.guideview .warn { font-size: .8rem; padding: 6px 8px; border-left: 2px solid #f5a623; margin-bottom: 8px; }
@media (max-width: 700px) {
  .guideview { padding: 0; }
  .guideview > .sheet { max-height: 100vh; border-radius: 0; border: 0; }
  .guideview .gbody { grid-template-columns: 1fr; }
  .guideview .gleft img { max-width: 220px; margin: 0 auto; }
}
.guideview .chip .sub { opacity: .6; font-style: italic; }
.guideview .chip .sub::before { content: " · "; }
