/* ===== The card rooms: picker, felt frame and the buttons over it =====
   This file owns everything the table layer paints. game.css is the town; the
   casino is a room inside it, so it borrows the town's tokens rather than
   inventing a second palette — a table that does not match the HUD reads as an ad
   pasted over the game.

   The table borrows the town's type too. Every line here is on the body ladder
   (--t-1..--t-6) and the shared control heights, because a card room is the one
   place that speaks in whole sentences — 跟注, 本地牌, 筹码输光了 — and the pixel
   face has no Chinese in it. Where a number is set in Latin digits it gets
   tabular-nums instead: a buy-in that changes width as the slider moves reads as
   a broken machine.

   Two constraints shape the layout:
   - the felt is a 640x400 Phaser canvas that scales itself (Phaser.Scale.FIT), so
     its parent must have a real size before the engine boots. An aspect-ratio box
     gives it one at every width without a resize listener.
   - everything is inside .modal-content, and a seated player cannot be handed a
     scrolling table: the felt sheet sizes the cloth out of whatever height is left
     after the chrome, and the log is the only row allowed to lose ground. See
     "the felt sheet" below the head. */

.tbl-rooms { display: grid; gap: var(--s3); }

.tbl-room {
  background: var(--bg-mid); border: 2px solid;
  border-color: var(--bg-raise) #0a0c14 #0a0c14 var(--bg-raise);
  padding: var(--s3) var(--s4); display: flex; flex-direction: column; gap: var(--s2);
}
.tbl-room.sel { border-color: var(--gold) #6a5a10 #6a5a10 var(--gold); }
.tbl-room.shut { opacity: 0.62; }
.tbl-room-top { display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap; }
.tbl-room-name { font-size: var(--t-2); font-weight: 700; color: var(--text); line-height: 1.3; }
.tbl-room-game { font-size: var(--t-1); color: var(--text-dim); margin-left: auto; white-space: nowrap; }
.tbl-tag {
  font-size: var(--t-1); font-weight: 700; letter-spacing: 0.5px;
  color: var(--bg-void); background: var(--gold);
  padding: 2px 7px; border-width: 2px; border-style: solid;
  border-color: #ffe9a8 #6a5a10 #6a5a10 #ffe9a8;
}

.tbl-buy { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }
.tbl-buy > label { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-1); color: var(--text-2); }
.tbl-amt { font-size: var(--t-4); font-weight: 800; color: var(--gold); min-width: 92px; font-variant-numeric: tabular-nums; }
.tbl-stake { font-size: var(--t-1); color: var(--text-dim); }
.tbl-poor { font-size: var(--t-1); color: var(--danger); }
.tbl-shut { font-size: var(--t-1); color: var(--text-dim); font-style: italic; }
/* a house rule is not an error: the room is open, it simply does not seat you yet */
.tbl-minor { font-size: var(--t-1); color: var(--gold); }
.tbl-note { font-size: var(--t-1); line-height: 1.7; color: var(--text-dim); margin: var(--s4) 0 0; }

/* the felt does not stretch: the canvas is created at the device's own pixel size, so
   the browser has nothing to resample. The column is also a query container, because the
   cloth has to be sized from the width the sheet actually ended up with. */
.tbl { display: flex; flex-direction: column; gap: var(--s3); min-height: 0; container-type: inline-size; }
.tbl-head {
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; flex: 0 0 auto;
  font-size: var(--t-1); color: var(--text-2);
  border-bottom: 1px solid var(--line); padding-bottom: var(--s2);
}
/* the same room, the same name: the head used to set it two points smaller than the
   picker, which reads as a different, less important room */
.tbl-hand { color: var(--text-dim); }
.tbl-stack { color: var(--gold); font-size: var(--t-2); font-weight: 700; font-variant-numeric: tabular-nums; }
.tbl-delta { margin-left: auto; font-size: var(--t-2); font-weight: 700; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.tbl-delta.good { color: var(--success); }
.tbl-delta.bad { color: var(--danger); }
/* ▸▸ is not in the pixel face either, so the pace key takes the town's button skin
   at full size rather than a shrunk one that a thumb cannot find */
.tbl-pace { min-width: var(--ctl-md); padding: 0 var(--s2); letter-spacing: -1px; }
.tbl-leave { padding: 0 var(--s3); }

/* ===== the felt sheet =====
   A card room is the one panel you play inside rather than read and close, so the sheet
   has to hold the whole table at once. Under the shared 82dvh modal at 1440x900 the body
   was 554px against a table that wanted 688, and the 134px that fell off the bottom was
   the hand and 跟注 — the two things a seated player cannot do without, and exactly what
   the note at the top of this file promised would never happen.

   The felt gets its own sheet: 94dvh, wider than the reading panels, and a cloth that is
   sized rather than squeezed. The cloth row takes the smaller of "the height this width
   implies" and "what the sheet has left after the chrome", and the host then takes the
   smaller of "the row's width" and "the width this height implies". Both branches land on
   the same box, so the parent always has the canvas's own shape and Phaser.Scale.FIT
   never has to leave a band of dead background beside the felt.

   The ratio is not written here: sit() puts --ar on the modal box from the felt module,
   because a betting table is 8:5 and a trick table carries fifty more pixels of cloth for
   the hand you have to tap. --felt-chrome is what the head, the bar, the log, the gaps and
   the sheet's own padding cost, measured on a live seat at this resolution; the log is the
   fuse if a state ever asks for more than that, and the sheet scrolls if a landscape phone
   asks for more than the screen has. */
.modal-content.felt {
  max-height: 94dvh;
  --felt-chrome: 316px;
  /* the sheet's own padding is a variable because on a phone it is taken straight out of
     the cloth: the felt there is width-bound, so every pixel of the 48 the reading panels
     reserve is a pixel the hand does not get. */
  --pad-x: var(--s5);
  --pad-y: var(--s5);
  padding: var(--pad-y) var(--pad-x);
  /* and the panel hugs the cloth instead of framing it: the sheet is only ever as wide as
     the height it was given can carry at the felt's own ratio. Without this a 1440x900
     window showed a 1180px panel around a 722px table — two thirds of the room was the
     wall. --felt-chrome is only the seed: the bar is the table's own voice and does not
     cost the same in every state, so fitFelt() (entry.js) measures the real chrome off the
     DOM on every repaint and writes it back here. */
  width: max(300px, min(1180px, 100%, calc((94dvh - var(--felt-chrome)) * var(--ar, 1.6) + var(--pad-x) * 2)));
}
.modal-content.felt .modal-body {
  overflow-y: auto; scrollbar-gutter: stable;
}
/* and the shared 关闭 strip goes: on a felt it is a second exit next to 离桌 that does
   not say which of the two cashes you out, and it costs the table 44 pixels of cloth */
.modal-content.felt .modal-foot { display: none; }
.tbl-felt {
  container-type: size; flex: none; display: grid; place-items: center;
  height: min(calc(100cqw / var(--ar, 1.6)), calc(94dvh - var(--felt-chrome, 316px)));
}
.tbl-stage {
  position: relative; width: min(100cqw, calc(100cqh * var(--ar, 1.6)));
  aspect-ratio: var(--ar, 1.6);
  background: #0d1017; border: 3px solid;
  border-color: #6b452a #241509 #241509 #6b452a;
  overflow: hidden;
}
.tbl-stage canvas { display: block; }

/* a phone held upright has no height problem and a width one: the cloth is as tall as the
   sheet's own allowance can carry, so the only thing that limits it is how much of the
   screen the panel spends on its own margins. There the sheet gives them back — and the
   scrollbar gutter goes too, since a felt that fits never scrolls. */
@media (max-width: 560px) {
  .modal-content.felt { --pad-x: var(--s2); }
  .modal-content.felt .modal-body { padding-right: 0; scrollbar-gutter: auto; }
  /* the head is one line of facts about the seat, and on a phone it was two: the gap
     between those facts is what made the second row appear. */
  .modal-content.felt .tbl-head { gap: var(--s1) var(--s2); }
  /* and the room's own name is the fact the head can drop: the sheet is titled 德州扑克 ·
     外滩德州俱乐部, so the head was saying the room twice and paying 52px of panel for it
     on the one screen that has no height to spare. */
  .modal-content.felt .tbl-head .tbl-room-name { display: none; }
  /* a betting drawer that breaks its own line is a price the player cannot read before
     the tap, so on a phone the drawer is the full width of the sheet and the track gives up
     its minimum to keep 加注到 100 beside the number it just set. */
  .tbl-bar > .tbl-row:not(.cards) { width: 100%; padding: var(--s1) var(--s2); }
  .tbl-row:not(.cards) .tbl-slider input[type=range] { min-width: 0; }
  .tbl-row:not(.cards) .tbl-slider .tbl-val { min-width: 0; }
  .tbl-row:not(.cards) .tbl-go { padding: 0 var(--s3); }
}

/* a phone on its side has neither the height for a cloth under a betting bar nor any use
   for a panel that hugs. There the chrome moves beside the felt: the cloth keeps the whole
   height the screen has and the head, the bar and the log stand in a column at the right of
   it. A wrapped flex row cannot put three items in the same column, so the sheet goes back
   to flow and the rows float around the cloth. */
@media (orientation: landscape) and (max-height: 620px) {
  .modal-content.felt {
    --pad-x: var(--s3);
    --pad-y: var(--s2);
    --col: 340px;
    width: min(1180px, 100%);
  }
  .modal-content.felt .modal-body { margin-bottom: 0; }
  .modal-content.felt .tbl {
    display: block; overflow: hidden;
    /* the cloth is as tall as the screen allows and no wider than what the column leaves
       it; the ratio then fixes the other edge, so there is never a band of dead felt */
    --cloth: min(calc((94dvh - var(--felt-chrome, 316px)) * var(--ar, 1.6)), calc(100% - var(--col)));
  }
  .modal-content.felt .tbl-felt {
    float: left; height: auto; aspect-ratio: var(--ar, 1.6); width: var(--cloth);
  }
  .modal-content.felt .tbl-head { float: right; width: calc(100% - var(--cloth) - var(--s3)); }
  /* clear, not beside: a row that stands between the cloth and the head gets the eighty
     pixels left over and turns a betting bar into a stack of single-character buttons */
  .modal-content.felt .tbl-bar { clear: right; margin-top: var(--s3); width: calc(100% - var(--cloth) - var(--s3)); }
  /* the column is a phone's width standing on its side, so the drawer pays for it the
     same way: the number gives up its reserved column and the button its padding, and
     加注到 keeps the slider company instead of dropping under it. */
  .modal-content.felt .tbl-bar > .tbl-row:not(.cards) { padding: var(--s1) var(--s2); }
  .modal-content.felt .tbl-row:not(.cards) .tbl-slider .tbl-val { min-width: 0; }
  .modal-content.felt .tbl-row:not(.cards) .tbl-go { padding: 0 var(--s3); }
  /* the history stays in the column too: below the bar there is a hundred and ninety pixels
     of bare panel, and whose turn it is, at what price, is the one thing a player looks for
     the moment a button they expected is not there. Two whole lines, because that is all the
     column has and a third would be drawn in halves. */
  .modal-content.felt .tbl-log {
    clear: right; margin-top: var(--s3); width: calc(100% - var(--cloth) - var(--s3));
    max-height: calc(2 * 1.5em + 2px);
  }
}

/* the bar is the table's voice: what the engine offered, at the price it offered it.
   It is never the thing that shrinks — a row that cannot reach 跟注 is not a row. */
.tbl-bar { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; flex: 0 0 auto; min-height: var(--ctl-md); }
.tbl-bar:empty { display: none; }
/* the plain buttons first, the priced drawer last. The engine offers 弃牌、跟注、加注、全下 in
   that order, and only the drawer is too wide to share a line — left in DOM order, 全下 falls
   *under* it and the bar costs three rows on a phone where it needs two. The card-picking row
   is a different animal and stays where the engine put it: 出牌 is the primary action there,
   not a drawer that follows the pass. */
.tbl-bar > .tbl-row:not(.cards) { order: 1; }
.tbl-wait, .tbl-oops { font-size: var(--t-1); color: var(--text-dim); }
.tbl-oops { color: var(--danger); line-height: 1.5; }

.tbl-row {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  background: var(--bg-mid); border: 2px solid var(--line); padding: var(--s2) var(--s3);
}
.tbl-mk { display: flex; gap: 4px; flex-wrap: wrap; }
.tbl-chip {
  font: inherit; font-size: var(--t-2); color: var(--text-2); background: var(--bg-light);
  border: 2px solid; border-color: var(--bg-raise) #0a0c14 #0a0c14 var(--bg-raise);
  min-height: var(--ctl-sm); padding: 0 var(--s3); cursor: pointer;
  font-variant-numeric: tabular-nums;
}
.tbl-chip i { font-style: normal; color: var(--gold); margin-left: 4px; font-size: var(--t-1); }
.tbl-chip.on { color: var(--bg-void); background: var(--gold); border-color: #ffe9a8 #6a5a10 #6a5a10 #ffe9a8; }
.tbl-chip.on i { color: #4a3608; }
/* the slider is the one part of a betting drawer that has no fixed job: the price and the
   button both have to be read, the track only has to be pushed. So it takes what the row
   gives it and gives way first when the row is a phone or a landscape column. */
.tbl-slider { display: flex; align-items: center; gap: var(--s2); flex: 1 1 auto; min-width: 0; }
.tbl-slider input[type=range] { width: auto; flex: 1 1 120px; min-width: 96px; }
.tbl-slider .tbl-val { font-size: var(--t-2); font-weight: 700; color: var(--gold); min-width: 82px; font-variant-numeric: tabular-nums; }
/* a dashed row is one whose price is not a number: the cards come off the felt,
   so the control that spends them has to look different from a betting bar */
.tbl-row.cards { border-style: dashed; }
.tbl-go { padding: 0 var(--s4); }
/* the shared sheet rule makes the last primary button of a modal body fill the panel —
   right for a 确认 under a reading panel, wrong for a betting drawer, where it drops
   加注到 off the slider that sets its own price and costs the bar a row at every size. */
.modal-content.felt .tbl-bar .tbl-row:not(.cards) .tbl-go { width: auto; }
/* a row the hand cannot complete still answers a tap — with the reason. A button
   that only looks dead is how a player starts blaming the table */
.tbl-go.off { filter: saturate(.28) brightness(.72); }

input[type=range].tbl-range, .tbl-slider input[type=range] {
  -webkit-appearance: none; appearance: none; height: 10px;
  background: linear-gradient(180deg, #0a0c14, #1b1e38); border: 2px solid var(--line); cursor: pointer;
}
/* the door's buy-in track is a fixed column in a row of facts; the drawer's is the part
   of the row that is left over, so only the first one carries a width */
input[type=range].tbl-range { width: 140px; }
input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 12px; height: 18px;
  background: var(--gold); border: 2px solid; border-color: #ffe9a8 #6a5a10 #6a5a10 #ffe9a8;
}
input[type=range]::-moz-range-thumb {
  width: 12px; height: 18px; border-radius: 0; background: var(--gold); border: 2px solid #6a5a10;
}

/* one screen of what the table actually said, newest last — the felt caption is
   decorative at phone size, so the same lines have to exist as text. The engine
   hands over four lines, so the band is exactly four lines tall. It is also the one
   row the sheet is allowed to shorten: if a state costs more than --felt-chrome, a
   dropped line of commentary beats a cut-off betting bar. */
.tbl-log {
  display: flex; flex-direction: column; gap: 2px; flex: 0 1 auto; min-height: 0;
  font-size: var(--t-1); line-height: 1.5;
  color: var(--text-dim); overflow: hidden;
  /* a whole number of lines, counted with the gap between them: an em-rounded max-height
     ended 48px into a 58px stack and the third line was sawn through its own characters.
     Dropping a line reads as history; slicing one reads as a broken panel. */
  max-height: calc(3 * 1.5em + 2 * 2px);
}
.tbl-line.good { color: var(--success); }
.tbl-line.bad { color: var(--danger); }
.tbl-line.warn { color: var(--gold); }
.tbl-line.event { color: var(--accent-2); }

.tbl-result { display: flex; flex-direction: column; gap: var(--s2); width: 100%; }
.tbl-result-head { font-size: var(--t-3); font-weight: 700; color: var(--text); line-height: 1.3; }
.tbl-result-btns, .tbl-bust + * { display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; }
.tbl-bust { font-size: var(--t-1); color: var(--danger); }
.tbl-result .good { color: var(--success); }
.tbl-result .bad { color: var(--danger); }

/* a thumb is not a mouse: under the desktop skin these sit at 40px, which is the
   smallest key that still reads 跟注; a phone pays for the finger instead */
@media (max-width: 640px) {
  .tbl-room-game { margin-left: 0; width: 100%; }
  .tbl-buy { gap: var(--s2); }
  .tbl-amt { min-width: 0; }
  input[type=range].tbl-range { width: 110px; }
  .tbl-act, .tbl-go, .tbl-hint { padding: 0 var(--s4); min-height: 44px; }
  .tbl-chip { min-height: var(--ctl-md); padding: 0 var(--s4); }
  .tbl-row { padding: var(--s2); gap: var(--s2); }
  .tbl-delta { margin-left: 0; }
}
