/* Layout follows Tommer's Figma ("Cascade break" file): a full-screen portrait
   table on green felt -- menu + round + opponent score on top, the opponent's
   hand peeking in from the top edge, closed pile + open row, the shared table,
   then your hand, undo and your score along the bottom. */
:root {
  --felt-1: #0c6e2b;
  --felt-2: #065022;
  --felt-3: #032d11;
  --card-bg: #fdfdfb;
  --card-border: #1a1a1a;
  --red: #c0392b;
  --black: #1a1a1a;
  --green: #1f7a3d;
  --blue: #1f5fa8;
  --accent: #e8c14c;
  --you: #ffb347; /* who scores a card: you ... */
  --them: #5aa9ff; /* ... or the AI (also the score colours) */
  --brand: #1f08f0;
  --balloon: #5345d0;
  --text: #f7f5ed;
  --muted: #d3e3d9;
  --back-a: #5b4fe0;
  --back-b: #8f86f5;
}

* {
  box-sizing: border-box;
}

/* Structural fix for a bug class hit twice already (#modalRoot, then
   #rearrangeControls): any author rule setting `display` on an element
   beats the browser's default `[hidden]{display:none}` regardless of
   specificity, because author stylesheets always outrank the user-agent
   stylesheet. A single !important rule here guarantees `hidden` actually
   means hidden for every element, current or future, instead of relying
   on remembering a matching `.some-class[hidden]` override each time a
   new dynamically-shown/hidden element reuses a `display`-setting class. */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  min-height: 100dvh;
  font-family:
    Inter,
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    Roboto,
    sans-serif;
  color: var(--text);
  background: var(--felt-3);
  background: radial-gradient(
      ellipse at 50% 38%,
      var(--felt-1) 0%,
      var(--felt-2) 55%,
      var(--felt-3) 100%
    )
    fixed;
}

#app {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: max(env(safe-area-inset-top), 4px) 8px
    max(env(safe-area-inset-bottom), 8px);
}

button {
  background: var(--brand);
  color: #fff;
  border: none;
  border-radius: 8px;
  padding: 0.45rem 0.9rem;
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
}
button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
button:hover:not(:disabled) {
  filter: brightness(1.12);
}
button.secondary {
  background: transparent;
  color: inherit;
  border: 1px solid currentColor;
}

select,
input {
  padding: 0.3rem;
  border-radius: 4px;
  border: none;
}

/* --- Header: menu, round, opponent score ------------------------------- */
.gbar {
  position: relative;
  display: flex;
  align-items: center;
  gap: 4px;
  min-height: 44px;
}
.menu-btn {
  width: 44px;
  height: 44px;
  padding: 0;
  display: grid;
  place-items: center;
  background: transparent;
  color: var(--text);
}
.round-label {
  font-size: 0.8rem;
  font-weight: 600;
}
.score {
  margin-left: auto;
  color: var(--accent);
  font-size: 0.85rem;
  font-weight: 700;
}
.menu-pop {
  position: absolute;
  top: 44px;
  left: 6px;
  z-index: 15;
  display: flex;
  flex-direction: column;
  min-width: 96px;
  background: #fff;
  border: 1px solid #000;
}
.menu-pop button {
  background: transparent;
  color: #000;
  border-radius: 0;
  text-align: left;
  padding: 0.4rem 0.7rem;
  font-weight: 500;
}
.menu-pop button:hover {
  background: #e9e9ff;
  filter: none;
}

/* --- Opponent: card backs peeking in from the top edge ----------------- */
.opp-hand {
  display: flex;
  height: 30px;
  overflow: hidden;
  padding-left: 22px; /* cancels the first card's negative margin */
  margin-top: -6px;
}
.opp-hand .card.back {
  width: 40px;
  height: 58px;
  margin-top: -28px;
  margin-left: -22px;
}

/* --- Closed pile + open row ------------------------------------------- */
.deck-row {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}
.pile {
  position: relative;
  flex-shrink: 0;
  width: 52px;
  height: 74px;
  padding: 0;
  margin: 0 6px 6px 0;
  background: repeating-linear-gradient(
    45deg,
    var(--back-a) 0 3px,
    var(--back-b) 3px 6px
  );
  border: 3px solid #fff;
  border-radius: 6px;
  box-shadow:
    2px 2px 0 #fff,
    3px 3px 0 var(--back-a),
    5px 5px 0 #fff,
    6px 6px 0 var(--back-a);
}
.pile:disabled {
  opacity: 1; /* it's a table object, not a greyed-out control */
  cursor: default;
}
.pile-count {
  position: absolute;
  left: 50%;
  bottom: -3px;
  transform: translateX(-50%);
  min-width: 22px;
  padding: 0 4px;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.7);
  color: #fff;
  font-size: 0.65rem;
  line-height: 14px;
}

/* --- Shared table ------------------------------------------------------ */
.tableau {
  flex: 1 1 120px;
  min-height: 120px;
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 0.5rem;
  /* Its own bounded, scrollable region (added #40) -- without this the
     tableau just grows the whole page taller as melds accumulate, forcing
     constant scrolling past it to reach the hand/controls below. Also gives
     drag-to-add a concrete scrollable target to autoscroll when a drop
     target has scrolled out of view mid-drag. */
  overflow-y: auto;
}

.rearrange-pool {
  background: rgba(0, 0, 0, 0.25);
  border-radius: 8px;
  padding: 0.25rem 0.6rem 0.5rem;
}

.status-line {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.75rem;
  font-size: 0.7rem;
  color: var(--muted);
}

/* --- Bottom bar: undo, contextual actions, your score ------------------ */
.bottom-bar {
  display: flex;
  align-items: center;
  gap: 8px;
}
.action-bar {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
}

/* --- Debug panel (menu -> Debug) --------------------------------------- */
.debug-panel {
  margin-top: 0.5rem;
  padding: 0.5rem 0.75rem 0.9rem;
  background: rgba(0, 0, 0, 0.35);
  border: 1px dashed var(--accent);
  border-radius: 8px;
}
.debug-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  align-items: center;
  font-size: 0.8rem;
}
.debug-panel .hand .card {
  cursor: default;
}

/* --- Hint balloon (one at a time, dismissible) ------------------------- */
.balloon {
  position: relative;
  align-self: flex-end;
  max-width: 200px;
  padding: 14px 30px 14px 14px;
  background: var(--balloon);
  color: #fff;
  border-radius: 5px;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1.3;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}
.x-btn {
  position: absolute;
  top: 0;
  right: 2px;
  width: 28px;
  height: 28px;
  padding: 0;
  background: transparent;
  color: inherit;
  font-size: 1.4rem;
  line-height: 1;
}

h2 {
  font-size: 0.95rem;
  margin: 0.4rem 0 0.6rem;
  color: var(--muted);
}
.hint {
  font-weight: normal;
  font-size: 0.75rem;
}

.open-row {
  display: flex;
  flex: 1;
  min-width: 0;
  min-height: 80px;
  padding: 0.25rem 0;
  overflow-x: auto;
  touch-action: pan-x;
}

.meld {
  border: 1px dashed var(--muted);
  border-radius: 8px;
  padding: 0.4rem;
  display: flex;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.03);
}
.meld.targeted {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent);
}
.meld.draft-valid {
  border-style: solid;
  border-color: #6fbf73;
}
.meld.draft-invalid {
  border-style: solid;
  border-color: #d9534f;
}
/* "+ New group" target at the end of the draft groups. */
.meld.draft-new {
  align-items: center;
  justify-content: center;
  min-width: 90px;
  min-height: 56px;
  color: var(--muted);
  font-size: 0.9rem;
}
/* The card picked up in a rearrange draft (the hand version of this lives
   under .hand, which draft cards are not inside). Same ring as a selected
   hand card, plus a lift. Unlike the hand (which lifts by margin so the card
   stays tucked under its neighbour), a transform is deliberate here: the
   picked-up card should paint above the overlapping cards of its group so it
   is easy to see. */
#tableau .card.selected,
#rearrangeHandPool .card.selected {
  box-shadow:
    inset 0 -5px 0 var(--owner, transparent),
    0 0 0 3px var(--accent);
  transform: translateY(-6px);
}
/* Compact melds: the shared table is where cards pile up fastest, so meld
   cards are smaller than hand cards and overlap each other, like the other
   rummy/gin apps do for laid-down sets. Rank/suit sit in the top-left
   corner index, so each card still shows ~24px of readable strip. */
.meld {
  padding: 0.3rem;
}
.meld .meld-cards {
  display: flex;
  /* cancels the first card's negative margin below; --meld-overlap is only
     set (by fitMeldOverlaps) on a meld too long to fit at the default */
  padding-left: var(--meld-overlap, 16px);
}
.meld .card {
  width: 40px;
  height: 58px;
  margin-left: calc(-1 * var(--meld-overlap, 16px));
  padding: 2px 0 0 4px;
  font-size: 0.85rem;
  border-radius: 5px;
}

.card {
  width: 52px;
  height: 74px;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: 6px;
  display: flex;
  flex-direction: column;
  /* Rank/suit pinned to the top-left corner (like a real card's index),
     not centered -- the open row overlaps each card's right side with
     the next one (see .open-row .card below), so only the left strip of
     a buried card stays visible. Centered text would land under the
     card on top; top-left text stays in that visible strip. */
  justify-content: flex-start;
  align-items: flex-start;
  padding: 3px 0 0 5px;
  font-weight: 700;
  font-size: 1rem;
  color: var(--black);
  flex-shrink: 0;
  /* The bar along the bottom edge shows who scores the card (--owner is set per
     card below); it stays visible even where cards overlap sideways. */
  box-shadow:
    inset 0 -5px 0 var(--owner, transparent),
    1px 1px 3px rgba(0, 0, 0, 0.4);
  user-select: none;
  position: relative;
}
/* Four-color deck: spades black (default), clubs green, hearts red,
   diamonds blue -- easier to tell apart at a glance than the standard
   two-color deck, especially spades vs. clubs. */
.card.suit-s {
  color: var(--black);
}
.card.suit-c {
  color: var(--green);
}
.card.suit-h {
  color: var(--red);
}
.card.suit-d {
  color: var(--blue);
}
.card.joker {
  background: #e9dfc4;
}
/* Corner index: bold and sized up so rank + suit stay readable when cards
   overlap. All four suit colors measure >= 5.2:1 against --card-bg (WCAG
   asks 4.5:1 for anything read quickly). */
.card .rank {
  font-size: 1.05em;
  font-weight: 800;
  line-height: 1.1;
}
.card .suit {
  font-size: 1.4em;
  line-height: 1;
}
/* A joker standing in for another card: shows that card's index, marked as
   a joker by the dashed border and the badge. */
.card.wild {
  border-style: dashed;
  border-width: 2px;
  padding: 1px 0 0 3px;
}
.card.wild::after {
  content: "J";
  position: absolute;
  right: 2px;
  bottom: 8px; /* clear of the owner bar */
  width: 12px;
  height: 12px;
  border-radius: 3px;
  background: var(--black);
  color: #fff;
  font-size: 9px;
  line-height: 12px;
  text-align: center;
}
.card.owner-1,
#hand .card {
  --owner: var(--you);
}
.card.owner-2 {
  --owner: var(--them);
}

/* Only the open row gets the overlapping "cascade" look — it's a visual
   metaphor for that specific mechanic, not a general card style. */
.open-row .card {
  margin-left: -26px;
  cursor: default;
}
.open-row .card:first-child {
  margin-left: 0;
}
.open-row .card.pickable {
  cursor: pointer;
}
.open-row .card.pickable:hover {
  transform: translateY(-6px);
  z-index: 1;
  position: relative;
}

.obligation {
  color: #ffb74d;
  font-size: 0.85rem;
  font-weight: 600;
}
.selection-count {
  color: var(--muted);
  font-size: 0.85rem;
}

.hand {
  display: flex;
  min-height: 90px;
  padding: 0.5rem 0 1rem;
  overflow-x: auto;
  touch-action: pan-x;
}
.hand .card {
  cursor: pointer;
}
.hand .card.selected {
  /* Lift via margin, not transform (#40, second follow-up): removing
     z-index wasn't enough on its own. ANY transform -- even with no
     z-index set -- implicitly promotes an element above its plain,
     untransformed siblings in paint order, regardless of DOM order (per
     the CSS stacking spec, a transformed static element paints as if it
     were positioned with z-index:0, a layer above ordinary in-flow static
     content). So `translateY` alone still put the selected card on top of
     a later, unselected, overlapping neighbor. A margin-top shift moves the
     box the same way without creating a stacking context, so the selected
     card stays in the same paint layer as everything else -- it can still
     end up tucked under a later unselected neighbor, same as any other
     overlap in the fan, which is the point: it should look raised, not
     jump in front. */
  margin-top: -12px;
  box-shadow:
    inset 0 -5px 0 var(--owner, transparent),
    0 0 0 3px var(--accent);
}
/* Bigger + overlapping, scoped to the real (human) hand only -- #aiHand
   shares the .hand class for its debug-only row, which doesn't need either
   the larger touch targets or the overlap (added #40; an earlier version
   enlarged .card globally, which also ballooned every meld in the tableau). */
/* Hand: each card keeps >= 44px of its width visible (the minimum tappable
   strip), and a hand that doesn't fit wraps onto a second row rather than
   overlapping tighter or scrolling sideways. Every card has the same
   negative margin; the container's left padding cancels it, so the first
   card of EVERY row lines up. 56px cards with a 12px overlap give the 44px
   strip, and 7 cards then need 6*44 + 56 = 320px -- just inside the ~322px a
   360px-wide phone leaves for the hand (tests/layout_large_hand.js checks
   it). */
#hand {
  min-height: 96px;
  flex-wrap: wrap;
  overflow-x: visible;
  padding-left: 12px;
  row-gap: 14px; /* room for a selected card's 12px lift */
}
#hand .card {
  width: 56px;
  height: 80px;
  margin-left: -12px;
}

/* Undo: round icon button, first in the bottom button row (bottom-left). */
.icon-btn {
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: 50%;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  background: rgba(255, 255, 255, 0.12);
  color: var(--text);
  border: 1px solid var(--muted);
}

.card.dragging {
  opacity: 0.35;
}
.drag-ghost {
  position: fixed;
  pointer-events: none;
  z-index: 50;
  opacity: 0.9;
  transform: scale(1.06);
  box-shadow: 2px 4px 10px rgba(0, 0, 0, 0.5);
}
/* Strong, unambiguous drop highlight -- especially important on the open row,
   since dragging a hand card there ends the turn (discard) in one continuous
   motion, unlike today's two deliberate steps (select, then press Discard).
   This is the pre-release "this will happen" signal that stands in for a
   confirmation dialog without slowing the gesture down. */
.meld.drag-over,
#openRow.drag-over,
#hand.drag-over,
#rearrangeHandPoolWrap.drag-over {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

.controls {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  align-items: center;
}

#log {
  max-height: 160px; /* about 8 lines */
  overflow-y: auto;
  font-size: 0.8rem;
  color: var(--muted);
}
#log div {
  padding: 0.15rem 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.modal-root {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
}
.modal-root.dialog-root {
  z-index: 20;
}
.modal-box {
  position: relative;
  background: #fff;
  color: #000;
  border: 2px solid var(--brand);
  border-radius: 5px;
  padding: 1.25rem 1.5rem 1.1rem;
  max-width: 480px;
  width: 90%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  text-align: center;
}
/* Round/game-end notification content (#modalRoot was reserved but unused
   until now -- see the postmortem above about its [hidden] handling). Fixed
   to the viewport center regardless of scroll position, unlike the old
   top-of-page #banner, which was easy to scroll past and mistake for the
   app being stuck. */
/* Every popup (error, confirm, round end, game end) uses the same
   title / body / button-row layout. */
.modal-box .dialog-title {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 600;
  color: #000;
}
.modal-box .dialog-body {
  margin: 0;
  font-size: 0.8rem;
  font-weight: 600;
  white-space: pre-wrap; /* keeps newlines (rearrange-commit problems) and spacing */
  line-height: 1.4;
}
.modal-box .modal-actions {
  display: flex;
  gap: 0.5rem;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 0.5rem;
}
.modal-box button.secondary {
  background: #fff;
  color: var(--brand);
  border: 1px solid var(--brand);
}
.modal-box .x-btn {
  top: 2px;
  right: 4px;
  color: #000;
}

/* Card back (opponent's hand). */
.card.back {
  padding: 0;
  background: repeating-linear-gradient(
    45deg,
    var(--back-a) 0 3px,
    var(--back-b) 3px 6px
  );
  border: 3px solid #fff;
}

/* Turn-0 prompt: same look as the dialogs, but inline so your hand stays
   visible while you answer. */
.banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  padding: 0.6rem 0.8rem;
  background: #fff;
  color: #000;
  border: 2px solid var(--brand);
  border-radius: 5px;
  font-size: 0.8rem;
  font-weight: 600;
}

/* Opponent's come-out progress ("35/40" or "out") beside the round label. */
.opp-status {
  margin-left: auto;
  margin-right: 8px;
  padding: 1px 8px;
  border: 1px solid var(--muted);
  border-radius: 10px;
  color: var(--muted);
  font-size: 0.7rem;
}
/* Keep the AI's total in the corner whether or not the badge is showing: the
   badge only takes over the auto margin while it is visible. */
.opp-status:not([hidden]) + .score {
  margin-left: 0;
}

/* The one action that's easy to miss and blocks everything else: after taking
   from the open row you must press it before you can meld. */
.action-bar .cta {
  flex: 1 1 100%;
  background: var(--accent);
  color: #1a1a1a;
  font-size: 0.95rem;
  padding: 0.65rem 1rem;
}

/* --- Startup splash (2 seconds, then fades; app.ts also removes it) ------ */
.splash {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  background: #08235b; /* the splash image's own navy */
  color: var(--text);
  animation: splash-out 0.4s ease 2s forwards;
}
.splash-img {
  width: min(62vw, 300px);
  height: auto;
}
.splash-title {
  font-size: 1.3rem;
  font-weight: 700;
}
.splash-version {
  min-height: 1em;
  color: var(--muted);
  font-size: 0.85rem;
}
@keyframes splash-out {
  to {
    opacity: 0;
    visibility: hidden;
  }
}
.menu-version {
  padding: 0.35rem 0.7rem;
  border-top: 1px solid #ddd;
  color: #666;
  font-size: 0.65rem;
}

/* --- Movable popups ---------------------------------------------------- */
/* Drag a popup by its top handle (or any empty part of it); app.ts moves it
   with a transform and keeps it on screen. */
.modal-box {
  cursor: grab;
  touch-action: none;
}
.modal-box button {
  cursor: pointer;
}
.modal-box::before {
  content: "";
  align-self: center;
  width: 40px;
  height: 4px;
  margin: -0.6rem 0 0.2rem;
  border-radius: 2px;
  background: #c5c5f5;
}
/* The round/game-end popup (not the confirmations) must not hide the table it
   reports on: no dimming, and taps/scrolls fall through to the page; only the
   popup box itself takes input. */
#modalRoot {
  background: transparent;
  pointer-events: none;
}
#modalRoot .modal-box {
  pointer-events: auto;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.5);
}

/* Score colours match the owner bars on the cards. */
#scoreP1,
#scoreP2 {
  font-weight: 700;
}
#scoreP1 {
  color: var(--you);
}
#scoreP2 {
  color: var(--them);
}
#scoreP1::before,
#scoreP2::before {
  font-size: 0.65rem;
  font-weight: 600;
  margin-right: 0.3rem;
  opacity: 0.85;
}
#scoreP1::before {
  content: "You";
}
#scoreP2::before {
  content: "AI";
}
