:root {
  color-scheme: dark;
  --ink: #121126;
  --ink-soft: #1b1939;
  --ink-raised: #242149;
  --cream: #f6f0e4;
  --cream-muted: #c6c0ba;
  --gold: #ffc43d;
  --gold-deep: #d98d13;
  --magenta: #ef3d82;
  --cyan: #64d9df;
  --mint: #80d499;
  --lavender: #9a89d2;
  --danger: #ff6b6b;
  --line: rgba(255, 255, 255, 0.1);
  --line-strong: rgba(255, 255, 255, 0.18);
  --shadow: 0 24px 70px rgba(5, 4, 20, 0.35);
  --radius-sm: 12px;
  --radius: 20px;
  --radius-lg: 32px;
  --page: min(1180px, calc(100vw - 40px));
  /* The real card image ratio (744x1039 art), named once so every
     card-shaped slot -- hand cards, table zones, deck-editor slots -- can
     size off the same source of truth instead of repeating the raw ratio. */
  --card-aspect-ratio: 744 / 1039;
  /* Per-seat playmat size: every seat shares one fixed size per breakpoint
     (see .match-mode .mat-seat) instead of animating between an
     active/inactive value. It drives the playmat's WIDTH (the playmat is
     square via aspect-ratio, so width fixes both dimensions), and it
     inherits so .player-info and .battle-player-board -- both children of
     .mat-seat -- agree on one width without each needing their own override. */
  --mat-size: 150px;
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

* {
  box-sizing: border-box;
}

/* Card-flight animations (hand -> field -> discard) use the View Transitions
   API, keyed by a stable per-card name (see card-transition-name() in
   app.js). The default full-page crossfade is disabled so that only the
   named card elements actually visible move themselves animate; everything
   else in the re-rendered table swaps instantly. */
::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
}

::view-transition-group(*) {
  animation-duration: 460ms;
  animation-timing-function: cubic-bezier(0.32, 0.1, 0.2, 1);
}

/* The transition overlay defaults to pointer-events:auto in Chromium, which
   would swallow clicks/drags anywhere on screen for the ~460ms a transition
   is running. Cards fly for looks only - real interaction must always reach
   the live DOM underneath. */
::view-transition,
::view-transition-group(*),
::view-transition-image-pair(*),
::view-transition-old(*),
::view-transition-new(*) {
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*) {
    animation: none !important;
  }
}

html {
  min-width: 320px;
  scroll-behavior: smooth;
  background: var(--ink);
}

body {
  min-width: 320px;
  min-height: 100vh;
  margin: 0;
  color: var(--cream);
  background:
    radial-gradient(circle at 82% 10%, rgba(89, 63, 139, 0.22), transparent 28rem),
    radial-gradient(circle at 0 80%, rgba(18, 115, 124, 0.13), transparent 24rem),
    var(--ink);
  overflow-x: hidden;
}

/* One large constellation field moves as a single plane. The wide tile keeps
   individual shapes varied while preserving a seamless, calm drift. */
body::before {
  position: fixed;
  z-index: 0;
  top: 0;
  left: 0;
  right: -2600px;
  bottom: 0;
  pointer-events: none;
  content: "";
  opacity: 0.32;
  background-image: url("assets/constellation-field.svg");
  background-position: 0 0;
  background-size: 2600px 1600px;
  /* Driven by transform (composited) instead of animating background-position
     (paint-only, forces a full repaint every frame). The element is widened
     by exactly one tile (2600px) so translating it a full tile-width still
     covers the viewport with no seam, then the animation loops seamlessly. */
  will-change: transform;
  animation: constellation-drift 180s linear infinite;
}

#app,
#modal-root {
  position: relative;
  z-index: 1;
}

button,
input,
select {
  font: inherit;
}

button,
a {
  -webkit-tap-highlight-color: transparent;
}

button {
  color: inherit;
}

a {
  color: inherit;
  text-decoration: none;
}

img {
  display: block;
  max-width: 100%;
}

h1,
h2,
h3,
p {
  margin-top: 0;
}

h1,
h2,
h3,
.display {
  font-family: Georgia, "Times New Roman", serif;
  font-weight: 700;
  letter-spacing: -0.035em;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: fixed;
  z-index: 1000;
  top: 10px;
  left: 10px;
  padding: 10px 14px;
  color: var(--ink);
  background: var(--gold);
  border-radius: 10px;
  transform: translateY(-150%);
}

.skip-link:focus {
  transform: none;
}

.btn {
  display: inline-flex;
  min-height: 46px;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 0 19px;
  border: 1px solid transparent;
  border-radius: 999px;
  font-weight: 750;
  line-height: 1;
  cursor: pointer;
  transition: 180ms ease;
}

.btn:hover {
  transform: translateY(-2px);
}

.btn:active {
  transform: translateY(0);
}

.btn:focus-visible,
.icon-btn:focus-visible,
.nav-item:focus-visible,
.deck-card:focus-visible,
.room-card:focus-visible,
.choice-card:focus-visible {
  outline: 3px solid rgba(255, 196, 61, 0.45);
  outline-offset: 3px;
}

.btn-primary {
  color: #191426;
  background: var(--gold);
  box-shadow: 0 10px 32px rgba(255, 196, 61, 0.2);
}

.btn-primary:hover {
  background: #ffd267;
}

.btn-secondary {
  color: var(--cream);
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--line-strong);
  backdrop-filter: blur(12px);
}

.btn-secondary:hover {
  background: rgba(255, 255, 255, 0.13);
}

.btn-ghost {
  min-height: 40px;
  padding-inline: 14px;
  color: var(--cream-muted);
  background: transparent;
}

.btn-ghost:hover {
  color: var(--cream);
  background: rgba(255, 255, 255, 0.06);
}

.btn-danger {
  color: #ffd9d9;
  background: rgba(255, 107, 107, 0.13);
  border-color: rgba(255, 107, 107, 0.28);
}

.btn-sm {
  min-height: 38px;
  padding-inline: 14px;
  font-size: 0.88rem;
}

.btn[disabled],
button[disabled] {
  pointer-events: none;
  opacity: 0.55;
}

.icon {
  display: inline-flex;
  width: 20px;
  height: 20px;
  align-items: center;
  justify-content: center;
  flex: none;
}

.icon svg {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.8;
}

.icon-btn {
  display: inline-grid;
  width: 42px;
  height: 42px;
  place-items: center;
  padding: 0;
  color: var(--cream);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--line);
  border-radius: 50%;
  cursor: pointer;
  transition: 180ms ease;
}

.icon-btn:hover {
  background: rgba(255, 255, 255, 0.12);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 1.55rem;
  font-weight: 700;
  letter-spacing: -0.06em;
}

.brand-mark {
  position: relative;
  display: grid;
  width: 44px;
  height: 52px;
  place-items: center;
  overflow: hidden;
  color: var(--ink);
  background: var(--gold);
  border-radius: 12px 12px 18px 18px;
  box-shadow: inset 0 -8px 18px rgba(175, 104, 0, 0.12);
}

.brand-mark svg {
  width: 39px;
  height: 49px;
  overflow: visible;
  font-family: ui-sans-serif, system-ui, sans-serif;
  font-weight: 950;
}

.brand-mark img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.brand-mark text {
  fill: currentColor;
  text-anchor: middle;
}

.brand-mark-card {
  fill: currentColor;
}

.brand-mark-vs {
  fill: currentColor !important;
  font-size: 12px;
  letter-spacing: -1.4px;
  paint-order: stroke;
  stroke: var(--gold);
  stroke-width: 3px;
  stroke-linejoin: round;
}

.brand-mark-card-text,
.brand-mark-bottom {
  fill: var(--gold) !important;
}

.brand-mark-bottom {
  font-size: 12px;
  letter-spacing: -1.5px;
}

.brand-word span {
  color: var(--gold);
}

.eyebrow {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 14px;
  color: var(--gold);
  font-size: 0.74rem;
  font-weight: 850;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.eyebrow::before {
  width: 26px;
  height: 1px;
  content: "";
  background: currentColor;
}

.muted {
  color: var(--cream-muted);
}

.landing-nav {
  position: absolute;
  z-index: 20;
  top: 0;
  left: 50%;
  display: grid;
  width: var(--page);
  height: 88px;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  transform: translateX(-50%);
}

.landing-links {
  display: flex;
  align-items: center;
  gap: 30px;
  color: rgba(246, 240, 228, 0.78);
  font-size: 0.9rem;
  font-weight: 650;
}

.landing-links a:hover {
  color: var(--cream);
}

.landing-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
}

.language-selector {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.language-selector select {
  min-width: 58px;
  height: 40px;
  padding: 0 22px 0 11px;
  color: var(--cream);
  background:
    linear-gradient(45deg, transparent 50%, currentColor 50%) calc(100% - 12px) 17px / 5px 5px no-repeat,
    linear-gradient(135deg, currentColor 50%, transparent 50%) calc(100% - 8px) 17px / 5px 5px no-repeat,
    rgba(255, 255, 255, 0.07);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 850;
  letter-spacing: 0.08em;
  appearance: none;
  cursor: pointer;
}

.language-selector select:hover {
  background-color: rgba(255, 255, 255, 0.12);
}

.language-selector select:focus-visible {
  outline: 3px solid rgba(255, 196, 61, 0.45);
  outline-offset: 3px;
}

.language-selector option {
  color: #17152e;
  background: var(--cream);
}

.landing-hero {
  position: relative;
  min-height: 760px;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  isolation: isolate;
  background: #17152c;
}

.landing-hero::before {
  position: absolute;
  z-index: -2;
  inset: 0;
  content: "";
  background-image: url("assets/starpath-hero.png");
  background-position: center 37%;
  background-size: cover;
  filter: saturate(0.9) contrast(1.04);
  transform: scale(1.025);
}

.landing-hero::after {
  position: absolute;
  z-index: -1;
  inset: 0;
  content: "";
  background:
    linear-gradient(90deg, rgba(15, 13, 34, 0.97) 0%, rgba(15, 13, 34, 0.76) 38%, rgba(15, 13, 34, 0.08) 74%),
    linear-gradient(0deg, var(--ink) 0%, transparent 27%, rgba(10, 8, 28, 0.16) 65%, rgba(10, 8, 28, 0.5) 100%);
}

.hero-content {
  width: var(--page);
  margin: 0 auto;
  padding: 150px 0 104px;
}

.hero-copy {
  max-width: 620px;
}

.hero-title {
  max-width: 650px;
  margin-bottom: 20px;
  font-size: clamp(3.5rem, 8vw, 7.6rem);
  line-height: 0.83;
  text-wrap: balance;
}

html[lang="pt-BR"] .hero-title {
  font-size: clamp(3.35rem, 7.25vw, 6.8rem);
}

html[lang="de"] .hero-title {
  max-width: 720px;
  font-size: clamp(3.15rem, 6.6vw, 6.15rem);
}

.hero-title .accent {
  display: block;
  color: var(--gold);
}

.hero-lead {
  max-width: 545px;
  margin-bottom: 30px;
  color: rgba(246, 240, 228, 0.84);
  font-size: clamp(1rem, 1.6vw, 1.18rem);
  line-height: 1.68;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.hero-proof {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-top: 35px;
  color: rgba(246, 240, 228, 0.63);
  font-size: 0.82rem;
}

.proof-rule {
  width: 38px;
  height: 1px;
  background: rgba(255, 255, 255, 0.28);
}

.section {
  width: var(--page);
  margin: 0 auto;
  padding: 105px 0;
}

.section-heading {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 32px;
  margin-bottom: 44px;
}

.section-heading h2 {
  max-width: 670px;
  margin-bottom: 0;
  font-size: clamp(2.35rem, 5vw, 4.4rem);
  line-height: 0.98;
  overflow-wrap: normal;
  text-wrap: balance;
}

.section-heading p {
  max-width: 390px;
  margin-bottom: 3px;
  color: var(--cream-muted);
  line-height: 1.65;
}

.feature-strip {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  border-top: 1px solid var(--line-strong);
  border-bottom: 1px solid var(--line-strong);
}

.feature-item {
  position: relative;
  min-height: 250px;
  padding: 38px;
  overflow: hidden;
}

.feature-item + .feature-item {
  border-left: 1px solid var(--line-strong);
}

.feature-number {
  display: block;
  margin-bottom: 52px;
  color: var(--gold);
  font-family: Georgia, "Times New Roman", serif;
  font-size: 0.88rem;
}

.feature-item h3 {
  margin-bottom: 11px;
  font-size: 1.55rem;
}

.feature-item p {
  max-width: 300px;
  margin-bottom: 0;
  color: var(--cream-muted);
  font-size: 0.92rem;
  line-height: 1.65;
}

.feature-glyph {
  position: absolute;
  top: 10px;
  right: 22px;
  color: rgba(255, 255, 255, 0.04);
  font-family: Georgia, serif;
  font-size: 8rem;
  line-height: 1;
}

.paths-section {
  padding-top: 90px;
  background: linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.025), transparent);
}

.path-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 12px;
}

.path-card {
  position: relative;
  min-height: 310px;
  padding: 20px;
  overflow: hidden;
  background:
    radial-gradient(circle at 50% 24%, color-mix(in srgb, var(--path) 68%, transparent), transparent 34%),
    linear-gradient(155deg, color-mix(in srgb, var(--path) 25%, var(--ink-soft)), var(--ink-soft) 70%);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius);
  transition: 220ms ease;
}

.path-card:hover {
  z-index: 2;
  border-color: color-mix(in srgb, var(--path) 58%, white);
  transform: translateY(-7px);
}

.path-symbol {
  display: grid;
  width: 92px;
  height: 92px;
  place-items: center;
  margin: 32px auto 50px;
  color: var(--path-soft, #fff);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 50%;
  box-shadow: inset 0 0 0 8px rgba(255, 255, 255, 0.035), 0 0 45px color-mix(in srgb, var(--path) 38%, transparent);
}

.family-glyph {
  display: block;
  width: 58%;
  height: 58%;
  overflow: visible;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 3;
}

.family-glyph path[fill-rule],
.family-glyph-dot {
  fill: currentColor;
  stroke: none;
}

.path-card h3 {
  margin-bottom: 5px;
  font-size: 1.18rem;
}

.path-card p {
  margin-bottom: 0;
  color: rgba(246, 240, 228, 0.6);
  font-size: 0.78rem;
}

.game-story {
  display: grid;
  min-height: 570px;
  grid-template-columns: 1.08fr 0.92fr;
  overflow: hidden;
  background: var(--cream);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}

.story-image {
  position: relative;
  min-height: 500px;
  background-image: linear-gradient(90deg, transparent 70%, rgba(246, 240, 228, 0.22)), url("assets/table-match.png");
  background-position: center;
  background-size: cover;
}

.story-badge {
  position: absolute;
  right: 28px;
  bottom: 28px;
  display: grid;
  width: 116px;
  height: 116px;
  place-items: center;
  color: var(--ink);
  background: var(--gold);
  border: 8px solid rgba(18, 17, 38, 0.15);
  border-radius: 50%;
  font-family: Georgia, serif;
  font-size: 1.15rem;
  font-weight: 700;
  line-height: 1.05;
  text-align: center;
  transform: rotate(7deg);
}

.story-copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(38px, 6vw, 76px);
  color: var(--ink);
}

.story-copy .eyebrow {
  color: #b16f08;
}

.story-copy h2 {
  margin-bottom: 22px;
  font-size: clamp(2.4rem, 4.8vw, 4.6rem);
  line-height: 0.95;
  overflow-wrap: normal;
  text-wrap: balance;
}

html[lang="pt-BR"] .story-copy h2,
html[lang="de"] .story-copy h2 {
  font-size: clamp(2.15rem, 4.05vw, 3.85rem);
  line-height: 1;
}

.story-copy p {
  color: var(--cream-muted);
  line-height: 1.7;
}

.story-copy .btn {
  align-self: flex-start;
  margin-top: 12px;
}

.landing-cta {
  position: relative;
  width: var(--page);
  min-height: 390px;
  display: grid;
  place-items: center;
  margin: 20px auto 100px;
  padding: 50px 30px;
  overflow: hidden;
  text-align: center;
  background-image: linear-gradient(rgba(18, 17, 38, 0.63), rgba(18, 17, 38, 0.9)), url("assets/card-families.png");
  background-position: center;
  background-size: cover;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
}

.landing-cta::before {
  position: absolute;
  inset: 16px;
  pointer-events: none;
  content: "";
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 22px;
}

.landing-cta > div {
  position: relative;
  max-width: 680px;
}

.landing-cta h2 {
  margin-bottom: 16px;
  font-size: clamp(2.5rem, 6vw, 5.4rem);
  line-height: 0.95;
  text-wrap: balance;
}

.landing-cta p {
  max-width: 520px;
  margin: 0 auto 26px;
  color: rgba(246, 240, 228, 0.74);
  line-height: 1.6;
}

.landing-footer {
  width: var(--page);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  margin: 0 auto;
  padding: 30px 0 42px;
  color: rgba(246, 240, 228, 0.55);
  border-top: 1px solid var(--line);
  font-size: 0.83rem;
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 22px;
}

.footer-credit {
  margin-top: 4px;
  font-size: 0.78rem;
  color: rgba(246, 240, 228, 0.4);
}

/* Auth and shared forms */
.modal-backdrop {
  position: fixed;
  z-index: 200;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 18px;
  overflow-y: auto;
  background: rgba(7, 6, 19, 0.76);
  backdrop-filter: blur(16px);
  animation: fade-in 150ms ease both;
}

.modal {
  position: relative;
  width: min(540px, 100%);
  max-height: calc(100vh - 36px);
  overflow: auto;
  padding: 34px;
  background:
    radial-gradient(circle at 100% 0, rgba(112, 83, 172, 0.2), transparent 260px),
    #1c1938;
  border: 1px solid var(--line-strong);
  border-radius: 26px;
  box-shadow: 0 40px 100px rgba(0, 0, 0, 0.55);
  animation: modal-in 210ms ease both;
}

.modal-lg {
  width: min(880px, 100%);
}

.modal-wide {
  width: min(1040px, 100%);
}

.modal-close {
  position: absolute;
  top: 18px;
  right: 18px;
}

.modal-kicker {
  display: block;
  margin-bottom: 9px;
  color: var(--gold);
  font-size: 0.72rem;
  font-weight: 850;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.modal h2 {
  max-width: calc(100% - 44px);
  margin-bottom: 8px;
  font-size: clamp(2rem, 6vw, 3rem);
  line-height: 1;
}

.modal-intro {
  margin-bottom: 26px;
  color: var(--cream-muted);
  line-height: 1.55;
}

.auth-tabs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  margin: 24px 0;
  padding: 4px;
  background: rgba(255, 255, 255, 0.055);
  border-radius: 999px;
}

.auth-tab {
  min-height: 42px;
  color: var(--cream-muted);
  background: transparent;
  border: 0;
  border-radius: 999px;
  font-weight: 750;
  cursor: pointer;
}

.auth-tab.active {
  color: var(--ink);
  background: var(--cream);
}

.form-grid {
  display: grid;
  gap: 17px;
}

.form-grid.two-col {
  grid-template-columns: 1fr 1fr;
}

.field {
  display: grid;
  gap: 7px;
}

.field > span,
.field label {
  color: rgba(246, 240, 228, 0.78);
  font-size: 0.79rem;
  font-weight: 750;
}

.field input,
.field select,
.search-input {
  width: 100%;
  min-height: 50px;
  padding: 0 15px;
  color: var(--cream);
  background: rgba(8, 7, 24, 0.42);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  outline: 0;
  transition: 160ms ease;
}

.field input::placeholder,
.search-input::placeholder {
  color: rgba(246, 240, 228, 0.34);
}

.field input:focus,
.field select:focus,
.search-input:focus {
  border-color: rgba(255, 196, 61, 0.75);
  box-shadow: 0 0 0 3px rgba(255, 196, 61, 0.1);
}

.field-hint {
  color: rgba(246, 240, 228, 0.42);
  font-size: 0.72rem;
  line-height: 1.45;
}

.checkbox-field {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--cream-muted);
  font-size: 0.86rem;
  cursor: pointer;
}

.checkbox-field input {
  width: 18px;
  height: 18px;
  accent-color: var(--gold);
}

.form-error {
  padding: 11px 13px;
  color: #ffd0d0;
  background: rgba(255, 107, 107, 0.11);
  border: 1px solid rgba(255, 107, 107, 0.25);
  border-radius: 10px;
  font-size: 0.82rem;
}

.form-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 8px;
}

.form-actions .btn-primary:last-child {
  min-width: 145px;
}

.auth-submit {
  width: 100%;
  margin-top: 4px;
}

.auth-note {
  display: flex;
  gap: 9px;
  margin-top: 18px;
  color: rgba(246, 240, 228, 0.5);
  font-size: 0.74rem;
  line-height: 1.5;
}

/* Authenticated application */
.app-shell {
  min-height: 100vh;
  display: grid;
  grid-template-columns: 244px minmax(0, 1fr);
}

.app-shell.match-mode {
  display: block;
}

.match-mode .sidebar,
.match-mode .app-topbar,
.match-mode .mobile-nav {
  display: none;
}

.match-mode .app-main {
  width: 100%;
  padding: 0;
}

.match-mode .match-table-page {
  width: 100%;
  max-width: none;
}

.match-mode .match-table-shell {
  min-height: 100vh;
  margin: 0;
  border: 0;
  border-radius: 0;
}

.sidebar {
  position: fixed;
  z-index: 30;
  top: 0;
  bottom: 0;
  left: 0;
  width: 244px;
  display: flex;
  flex-direction: column;
  padding: 24px 18px;
  background: rgba(15, 14, 33, 0.91);
  border-right: 1px solid var(--line);
  backdrop-filter: blur(22px);
}

.sidebar .brand {
  margin: 4px 10px 38px;
}

.side-nav {
  display: grid;
  gap: 5px;
}

.nav-item {
  display: flex;
  min-height: 47px;
  align-items: center;
  gap: 12px;
  padding: 0 14px;
  color: rgba(246, 240, 228, 0.56);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 13px;
  font-size: 0.88rem;
  font-weight: 700;
  cursor: pointer;
  transition: 160ms ease;
}

.nav-item:hover {
  color: var(--cream);
  background: rgba(255, 255, 255, 0.045);
}

.nav-item.active {
  color: var(--ink);
  background: var(--gold);
  box-shadow: 0 8px 24px rgba(255, 196, 61, 0.14);
}

.nav-badge {
  min-width: 21px;
  padding: 2px 6px;
  margin-left: auto;
  color: inherit;
  background: rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  font-size: 0.66rem;
  text-align: center;
}

.sidebar-user {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 12px 8px 0;
  margin-top: auto;
  border-top: 1px solid var(--line);
}

.avatar {
  display: grid;
  width: 39px;
  height: 39px;
  flex: none;
  place-items: center;
  color: var(--ink);
  background: var(--gold);
  border-radius: 50%;
  font-family: Georgia, serif;
  font-weight: 800;
}

.user-meta {
  min-width: 0;
  flex: 1;
}

.user-meta strong,
.user-meta span {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.user-meta strong {
  font-size: 0.82rem;
}

.user-meta span {
  margin-top: 2px;
  color: rgba(246, 240, 228, 0.45);
  font-size: 0.68rem;
}

.app-main {
  min-width: 0;
  grid-column: 2;
  padding: 0 34px 70px;
}

.app-topbar {
  position: sticky;
  z-index: 25;
  top: 0;
  display: flex;
  min-height: 82px;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  margin: 0 -6px;
  background: linear-gradient(var(--ink) 68%, transparent);
}

.app-topbar .brand,
.mobile-menu-btn {
  display: none;
}

.topbar-title {
  min-width: 0;
}

.topbar-title span {
  display: block;
  margin-bottom: 3px;
  color: rgba(246, 240, 228, 0.47);
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.topbar-title strong {
  display: block;
  overflow: hidden;
  font-family: Georgia, serif;
  font-size: 1.25rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

.mobile-logout {
  display: none;
}

.page {
  width: min(1180px, 100%);
  margin: 0 auto;
  animation: page-in 240ms ease both;
}

.page-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 30px;
  margin: 24px 0 28px;
}

.page-head h1 {
  margin-bottom: 8px;
  font-size: clamp(2.3rem, 5vw, 4.1rem);
  line-height: 0.98;
}

.page-head p {
  max-width: 620px;
  margin-bottom: 0;
  color: var(--cream-muted);
  line-height: 1.55;
}

.page-actions {
  display: flex;
  flex: none;
  gap: 10px;
}

.dashboard-hero {
  position: relative;
  min-height: 350px;
  display: flex;
  align-items: flex-end;
  margin-top: 18px;
  padding: clamp(28px, 5vw, 52px);
  overflow: hidden;
  background: #211d43;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}

.dashboard-hero::before {
  position: absolute;
  inset: 0;
  content: "";
  background-image: url("assets/card-families.png");
  background-position: center 48%;
  background-size: cover;
  opacity: 0.68;
}

.dashboard-hero::after {
  position: absolute;
  inset: 0;
  content: "";
  background: linear-gradient(90deg, rgba(17, 15, 39, 0.98) 0, rgba(17, 15, 39, 0.8) 48%, rgba(17, 15, 39, 0.22)), linear-gradient(0deg, rgba(17, 15, 39, 0.88), transparent 50%);
}

.dashboard-hero-content {
  position: relative;
  z-index: 1;
  max-width: 560px;
}

.dashboard-hero h1 {
  margin-bottom: 13px;
  font-size: clamp(2.35rem, 5vw, 4.6rem);
  line-height: 0.95;
}

.dashboard-hero p {
  max-width: 480px;
  margin-bottom: 23px;
  color: rgba(246, 240, 228, 0.72);
  line-height: 1.58;
}

.dashboard-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(280px, 0.8fr);
  gap: 20px;
  margin-top: 20px;
}

.panel {
  padding: 24px;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 19px;
}

.panel-head h2,
.panel-head h3 {
  margin-bottom: 0;
  font-size: 1.3rem;
}

.panel-link {
  color: var(--gold);
  background: transparent;
  border: 0;
  font-size: 0.78rem;
  font-weight: 750;
  cursor: pointer;
}

.stat-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.stat {
  padding: 17px;
  background: rgba(255, 255, 255, 0.038);
  border: 1px solid var(--line);
  border-radius: 14px;
}

.stat strong {
  display: block;
  margin-bottom: 6px;
  color: var(--gold);
  font-family: Georgia, serif;
  font-size: 1.85rem;
}

.stat span {
  color: var(--cream-muted);
  font-size: 0.72rem;
}

.quick-decks {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.mini-deck {
  position: relative;
  min-height: 150px;
  padding: 16px;
  overflow: hidden;
  text-align: left;
  background: linear-gradient(145deg, color-mix(in srgb, var(--family) 35%, var(--ink-raised)), var(--ink-soft));
  border: 1px solid color-mix(in srgb, var(--family) 36%, transparent);
  border-radius: 15px;
  cursor: pointer;
}

.mini-deck::after {
  position: absolute;
  right: -12px;
  bottom: -30px;
  color: color-mix(in srgb, var(--family) 40%, white);
  content: attr(data-symbol);
  font-family: Georgia, serif;
  font-size: 7rem;
  opacity: 0.22;
}

.mini-deck strong,
.mini-deck span {
  position: relative;
  z-index: 1;
  display: block;
}

.mini-deck strong {
  margin-top: 54px;
  font-family: Georgia, serif;
  font-size: 1rem;
}

.mini-deck span {
  margin-top: 4px;
  color: rgba(246, 240, 228, 0.58);
  font-size: 0.7rem;
}

.activity-list,
.room-list-compact {
  display: grid;
  gap: 10px;
}

.activity-item,
.compact-room {
  display: flex;
  min-height: 58px;
  align-items: center;
  gap: 12px;
  padding: 10px;
  background: rgba(255, 255, 255, 0.025);
  border: 1px solid var(--line);
  border-radius: 12px;
}

.activity-dot {
  width: 9px;
  height: 9px;
  flex: none;
  background: var(--mint);
  border-radius: 50%;
  box-shadow: 0 0 15px var(--mint);
}

.activity-item > div,
.compact-room > div {
  min-width: 0;
  flex: 1;
}

.activity-item strong,
.activity-item span,
.compact-room strong,
.compact-room span {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.activity-item strong,
.compact-room strong {
  font-size: 0.79rem;
}

.activity-item span,
.compact-room span {
  margin-top: 3px;
  color: rgba(246, 240, 228, 0.42);
  font-size: 0.68rem;
}

/* Decks */
.deck-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 18px;
}

.deck-card {
  position: relative;
  min-height: 315px;
  padding: 0;
  overflow: hidden;
  color: var(--cream);
  text-align: left;
  background: linear-gradient(145deg, color-mix(in srgb, var(--family) 38%, #27234c), #18162f 74%);
  border: 1px solid color-mix(in srgb, var(--family) 38%, rgba(255, 255, 255, 0.14));
  border-radius: var(--radius);
  cursor: pointer;
  transition: 210ms ease;
}

.deck-card:hover {
  border-color: color-mix(in srgb, var(--family) 62%, white);
  box-shadow: 0 22px 55px color-mix(in srgb, var(--family) 13%, transparent);
  transform: translateY(-5px);
}

.deck-cover {
  position: relative;
  height: 205px;
  overflow: hidden;
}

.deck-cover::before {
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(170%, 560px);
  height: auto;
  aspect-ratio: 1;
  z-index: 0;
  content: "";
  background:
    radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--family) 85%, white), transparent 19%),
    conic-gradient(from 20deg, transparent, color-mix(in srgb, var(--family) 45%, transparent), transparent 32%, color-mix(in srgb, var(--family) 25%, transparent), transparent 62%);
  filter: blur(2px);
  border-radius: 50%;
  animation: deck-glow-spin 22s linear infinite;
}

.deck-orbit {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1;
  display: grid;
  width: 116px;
  height: 116px;
  place-items: center;
  color: color-mix(in srgb, var(--family) 55%, white);
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(18, 17, 38, 0.82) 0 26%, rgba(18, 17, 38, 0.52) 48%, rgba(18, 17, 38, 0.12) 70%, transparent 76%);
  box-shadow: inset 0 0 0 10px rgba(255, 255, 255, 0.035), 0 0 55px color-mix(in srgb, var(--family) 30%, transparent);
  transform: translate(-50%, -50%);
}

.deck-orbit::before,
.deck-orbit::after {
  position: absolute;
  width: 155px;
  height: 68px;
  content: "";
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 50%;
  transform: rotate(23deg);
}

.deck-orbit::after {
  transform: rotate(-44deg);
}

.deck-orbit .family-glyph {
  position: relative;
  z-index: 2;
  filter: drop-shadow(0 0 9px currentColor) drop-shadow(0 2px 2px rgba(0, 0, 0, 0.5));
}

.deck-body {
  position: relative;
  padding: 19px 20px 20px;
  background: linear-gradient(transparent, rgba(9, 8, 25, 0.32));
}

.deck-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.deck-label h3 {
  margin-bottom: 0;
  overflow: hidden;
  font-size: 1.35rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.deck-tag {
  flex: none;
  padding: 5px 8px;
  color: color-mix(in srgb, var(--family) 66%, white);
  background: color-mix(in srgb, var(--family) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--family) 30%, transparent);
  border-radius: 999px;
  font-size: 0.62rem;
  font-weight: 800;
  text-transform: uppercase;
}

.deck-body p {
  margin: 8px 0 0;
  color: rgba(246, 240, 228, 0.52);
  font-size: 0.75rem;
}

.deck-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 23px;
}

.deck-toolbar .search-wrap {
  width: min(360px, 100%);
}

.search-wrap {
  position: relative;
}

.search-wrap .icon {
  position: absolute;
  z-index: 1;
  top: 50%;
  left: 14px;
  width: 17px;
  color: rgba(246, 240, 228, 0.38);
  transform: translateY(-50%);
}

.search-wrap .search-input {
  padding-left: 42px;
}

.deck-editor-head {
  display: flex;
  align-items: center;
  gap: 16px;
  margin: 20px 0 26px;
}

.deck-editor-head .back-btn {
  flex: none;
}

.deck-title-wrap {
  min-width: 0;
  flex: 1;
}

.deck-title-input {
  width: 100%;
  padding: 3px 0 6px;
  color: var(--cream);
  background: transparent;
  border: 0;
  border-bottom: 1px solid transparent;
  outline: 0;
  font-family: Georgia, serif;
  font-size: clamp(1.8rem, 4vw, 3.2rem);
  font-weight: 700;
  letter-spacing: -0.035em;
}

.deck-title-input:focus {
  border-color: var(--gold);
}

.deck-title-wrap span {
  display: block;
  color: var(--cream-muted);
  font-size: 0.78rem;
}

.deck-editor-actions {
  display: flex;
  gap: 10px;
}

.deck-rule {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  padding: 14px 16px;
  margin-bottom: 20px;
  color: rgba(246, 240, 228, 0.68);
  background: rgba(255, 196, 61, 0.07);
  border: 1px solid rgba(255, 196, 61, 0.18);
  border-radius: 13px;
  font-size: 0.8rem;
  line-height: 1.5;
}

.deck-rule .icon {
  color: var(--gold);
}

.card-slots {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 12px;
}

.game-card {
  position: relative;
  min-height: 245px;
  padding: 15px;
  overflow: hidden;
  color: var(--cream);
  text-align: left;
  background:
    radial-gradient(circle at 60% 28%, color-mix(in srgb, var(--card) 72%, white), transparent 9%),
    radial-gradient(circle at 52% 33%, color-mix(in srgb, var(--card) 45%, transparent), transparent 34%),
    linear-gradient(155deg, color-mix(in srgb, var(--card) 42%, #302953), #17152e 74%);
  border: 1px solid color-mix(in srgb, var(--card) 45%, rgba(255, 255, 255, 0.2));
  border-radius: 16px;
  box-shadow: 0 15px 30px rgba(7, 6, 20, 0.18);
  cursor: pointer;
  transition: 190ms ease;
}

.game-card-shell,
.choice-card-wrap {
  position: relative;
  min-width: 0;
}

.game-card-shell .game-card {
  width: 100%;
  height: auto;
  aspect-ratio: var(--card-aspect-ratio);
}

.game-card:hover {
  border-color: color-mix(in srgb, var(--card) 68%, white);
  transform: translateY(-5px) rotate(-0.5deg);
}

.game-card::before {
  position: absolute;
  top: 36px;
  left: 50%;
  width: 92px;
  height: 92px;
  content: "";
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 50%;
  box-shadow: inset 0 0 0 7px rgba(255, 255, 255, 0.03), 0 0 34px color-mix(in srgb, var(--card) 24%, transparent);
  transform: translateX(-50%);
}

.game-card.has-art {
  background-color: #17152e;
  background-image: var(--card-art);
  background-repeat: no-repeat;
  background-position: center top;
  background-size: 100% auto;
}

.game-card.has-art::before {
  display: none;
}

.game-card.has-art::after {
  position: absolute;
  z-index: 1;
  inset: 42% 0 0;
  pointer-events: none;
  content: "";
  background: linear-gradient(transparent, rgba(12, 10, 29, 0.88) 60%, rgba(12, 10, 29, 0.98));
}

.game-card.has-art .card-symbol {
  display: none;
}

.game-card.has-art .card-number {
  opacity: 0;
}

.card-number {
  position: relative;
  z-index: 2;
  display: grid;
  width: 30px;
  height: 30px;
  place-items: center;
  color: var(--ink);
  background: var(--cream);
  border-radius: 50%;
  font-family: Georgia, serif;
  font-size: 0.9rem;
  font-weight: 800;
}

.card-symbol {
  position: absolute;
  z-index: 1;
  top: 55px;
  left: 50%;
  color: color-mix(in srgb, var(--card) 48%, white);
  font-family: Georgia, serif;
  font-size: 4rem;
  line-height: 1;
  text-shadow: 0 0 25px color-mix(in srgb, var(--card) 68%, transparent);
  transform: translateX(-50%);
}

.card-copy {
  position: absolute;
  z-index: 2;
  right: 14px;
  bottom: 15px;
  left: 14px;
}

.card-copy > strong,
.card-copy > em,
.card-copy > span {
  display: block;
}

.card-copy > strong {
  overflow: hidden;
  font-family: Georgia, serif;
  font-size: 1rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.card-copy > em {
  overflow: hidden;
  margin-top: 2px;
  color: rgba(246, 240, 228, 0.76);
  font-family: Georgia, serif;
  font-size: 0.62rem;
  font-style: italic;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.card-copy > span {
  display: -webkit-box;
  margin-top: 5px;
  overflow: hidden;
  color: rgba(246, 240, 228, 0.55);
  font-size: 0.66rem;
  line-height: 1.35;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.card-keyword {
  color: color-mix(in srgb, var(--card, var(--gold)) 48%, white);
  font-weight: 850;
}

.card-preview-figure .card-keyword {
  color: var(--gold);
}

.swap-hint {
  position: absolute;
  z-index: 3;
  top: 13px;
  right: 49px;
  display: grid;
  width: 28px;
  height: 28px;
  place-items: center;
  color: var(--cream);
  background: rgba(8, 7, 24, 0.62);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 50%;
  opacity: 0;
  transition: 170ms ease;
}

.game-card-shell:hover .swap-hint,
.game-card:focus-visible .swap-hint {
  opacity: 1;
}

.card-preview-trigger {
  position: absolute;
  z-index: 4;
  top: 13px;
  right: 13px;
  display: grid;
  width: 30px;
  height: 30px;
  place-items: center;
  padding: 0;
  color: var(--cream);
  background: rgba(8, 7, 24, 0.82);
  border: 1px solid rgba(255, 255, 255, 0.24);
  border-radius: 50%;
  box-shadow: 0 7px 20px rgba(0, 0, 0, 0.28);
  cursor: zoom-in;
  opacity: 0.82;
  transition: 160ms ease;
}

.card-preview-trigger:hover,
.card-preview-trigger:focus-visible {
  color: var(--ink);
  background: var(--gold);
  border-color: var(--gold);
  opacity: 1;
  transform: scale(1.08);
}

.card-preview-trigger .icon {
  width: 15px;
  height: 15px;
}

.card-choice-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
}

.choice-card {
  display: block;
  width: 100%;
  min-width: 0;
  padding: 0;
  color: inherit;
  background: transparent;
  border: 2px solid transparent;
  border-radius: 17px;
  cursor: pointer;
}

.choice-card .game-card {
  display: block;
  box-sizing: border-box;
  width: 100%;
  min-height: 0;
  aspect-ratio: var(--card-aspect-ratio);
  pointer-events: none;
}

.choice-card.selected {
  border-color: var(--gold);
  box-shadow: 0 0 0 3px rgba(255, 196, 61, 0.12);
}

.card-preview-modal {
  width: min(920px, 100%);
  padding: 24px;
}

.discard-pile-modal h2 {
  margin: 0 0 4px;
  padding-right: 32px;
  font-family: Georgia, serif;
  font-size: 1.3rem;
}

.discard-pile-hint {
  margin: 0 0 18px;
  color: var(--cream-muted);
  font-size: 0.78rem;
}

.discard-pile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
  gap: 12px;
}

.discard-pile-card {
  position: relative;
  aspect-ratio: var(--card-aspect-ratio);
  padding: 0;
  overflow: hidden;
  background: #17142d;
  border: 1px solid var(--line);
  border-radius: 9px;
  cursor: zoom-in;
  transition: 160ms ease;
}

.discard-pile-card:hover,
.discard-pile-card:focus-visible {
  border-color: var(--gold);
  transform: translateY(-4px);
}

.discard-pile-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.discard-pile-top-badge {
  position: absolute;
  top: 6px;
  left: 6px;
  padding: 2px 7px;
  color: #21170a;
  background: var(--gold);
  border-radius: 999px;
  font-size: 0.55rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.empty-zone-trigger {
  display: grid;
  width: 100%;
  height: 100%;
  place-items: center;
  padding: 0;
  color: inherit;
  background: transparent;
  border: 0;
  cursor: pointer;
}

.card-preview-figure {
  display: grid;
  grid-template-columns: minmax(260px, 430px) minmax(220px, 1fr);
  align-items: center;
  gap: clamp(24px, 5vw, 54px);
  margin: 0;
}

.card-preview-figure img {
  width: 100%;
  max-height: calc(100vh - 84px);
  object-fit: contain;
  border: 1px solid var(--line-strong);
  border-radius: 20px;
  box-shadow: 0 28px 65px rgba(0, 0, 0, 0.42);
}

.card-preview-figure figcaption {
  padding-right: 14px;
}

.card-preview-figure h2 {
  margin-bottom: 8px;
}

.card-preview-figure em {
  display: block;
  color: var(--cream-muted);
  font-family: Georgia, serif;
}

.card-preview-figure p {
  margin: 22px 0 0;
  color: var(--cream-muted);
  line-height: 1.65;
}

/* Rooms */
.rooms-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 24px;
}

.rooms-toolbar .search-wrap {
  min-width: 240px;
  flex: 1;
}

.filter-chips {
  display: flex;
  gap: 7px;
}

.filter-chip {
  min-height: 40px;
  padding: 0 14px;
  color: var(--cream-muted);
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 0.77rem;
  font-weight: 700;
  cursor: pointer;
}

.filter-chip.active {
  color: var(--ink);
  background: var(--cream);
}

.room-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}

.room-card {
  display: flex;
  min-height: 220px;
  flex-direction: column;
  padding: 20px;
  color: var(--cream);
  text-align: left;
  background: linear-gradient(150deg, rgba(255, 255, 255, 0.055), rgba(255, 255, 255, 0.02));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  transition: 190ms ease;
}

.room-card:hover {
  background: rgba(255, 255, 255, 0.06);
  border-color: var(--line-strong);
  transform: translateY(-4px);
}

.room-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.room-status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--mint);
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.room-status::before {
  width: 7px;
  height: 7px;
  content: "";
  background: currentColor;
  border-radius: 50%;
  box-shadow: 0 0 10px currentColor;
}

.room-status.full {
  color: var(--gold);
}

.room-lock {
  color: rgba(246, 240, 228, 0.5);
}

.room-card h3 {
  margin: 25px 0 7px;
  overflow: hidden;
  font-size: 1.35rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.room-host {
  color: rgba(246, 240, 228, 0.52);
  font-size: 0.75rem;
}

.room-bottom {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 15px;
  margin-top: auto;
}

.player-stack {
  display: flex;
  padding-left: 6px;
}

.player-dot {
  display: grid;
  width: 32px;
  height: 32px;
  place-items: center;
  margin-left: -6px;
  color: var(--ink);
  background: var(--cream);
  border: 3px solid #1c1938;
  border-radius: 50%;
  font-family: Georgia, serif;
  font-size: 0.72rem;
  font-weight: 800;
}

.player-dot.empty {
  color: rgba(246, 240, 228, 0.3);
  background: transparent;
  border-color: rgba(255, 255, 255, 0.14);
  border-style: dashed;
}

.room-action {
  color: var(--gold);
  font-size: 0.72rem;
  font-weight: 800;
}

.empty-state {
  grid-column: 1 / -1;
  display: grid;
  min-height: 310px;
  place-items: center;
  padding: 35px;
  text-align: center;
  background: rgba(255, 255, 255, 0.025);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius);
}

.empty-state-symbol {
  margin-bottom: 12px;
  color: var(--gold);
  font-family: Georgia, serif;
  font-size: 3.7rem;
}

.empty-state h3 {
  margin-bottom: 8px;
  font-size: 1.5rem;
}

.empty-state p {
  max-width: 380px;
  margin: 0 auto 18px;
  color: var(--cream-muted);
  font-size: 0.85rem;
  line-height: 1.55;
}

.lobby {
  position: relative;
  min-height: 560px;
  padding: clamp(24px, 5vw, 48px);
  overflow: hidden;
  background-image: linear-gradient(rgba(19, 17, 42, 0.82), rgba(19, 17, 42, 0.94)), url("assets/player-board.png");
  background-position: center;
  background-size: cover;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
}

.lobby-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 55px;
}

.lobby-head h1 {
  margin: 6px 0;
  font-size: clamp(2.2rem, 5vw, 4rem);
}

.lobby-code {
  color: var(--cream-muted);
  font-size: 0.78rem;
}

.versus-grid {
  display: grid;
  max-width: 760px;
  grid-template-columns: 1fr 70px 1fr;
  align-items: center;
  gap: 20px;
  margin: 0 auto;
}

.player-seat {
  min-height: 210px;
  display: grid;
  place-items: center;
  padding: 24px;
  text-align: center;
  background: rgba(13, 11, 33, 0.66);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  backdrop-filter: blur(10px);
}

.player-seat.empty {
  border-style: dashed;
}

.seat-avatar {
  display: grid;
  width: 66px;
  height: 66px;
  place-items: center;
  margin: 0 auto 14px;
  color: var(--ink);
  background: var(--gold);
  border-radius: 50%;
  font-family: Georgia, serif;
  font-size: 1.5rem;
  font-weight: 800;
}

.player-seat.empty .seat-avatar {
  color: rgba(246, 240, 228, 0.36);
  background: rgba(255, 255, 255, 0.055);
  border: 1px dashed var(--line-strong);
}

.player-seat strong,
.player-seat span {
  display: block;
}

.player-seat .seat-avatar {
  display: grid;
  width: 66px;
  height: 66px;
  place-items: center;
  margin: 0 auto 14px;
  color: var(--ink);
  font-size: 1.5rem;
}

.player-seat.empty .seat-avatar {
  color: rgba(246, 240, 228, 0.52);
  font-size: 1rem;
}

.player-seat .seat-avatar .icon {
  width: 27px;
  height: 27px;
}

.player-seat strong {
  font-family: Georgia, serif;
  font-size: 1.22rem;
}

.player-seat span {
  margin-top: 5px;
  color: rgba(246, 240, 228, 0.5);
  font-size: 0.73rem;
}

.versus {
  color: var(--gold);
  font-family: Georgia, serif;
  font-size: 1.7rem;
  font-style: italic;
  text-align: center;
}

.lobby-actions {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-top: 42px;
}

.lobby-player-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}

.lobby-deck-picker {
  display: grid;
  width: min(420px, 100%);
  gap: 8px;
  margin: 24px auto 0;
  color: var(--cream-muted);
  font-size: 0.78rem;
  font-weight: 750;
}

.lobby-deck-picker select {
  width: 100%;
  min-height: 48px;
  padding: 0 14px;
  color: var(--cream);
  background: rgba(8, 7, 24, 0.72);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
}

.match-setup {
  max-width: 920px;
  padding: clamp(28px, 5vw, 58px);
  margin: 28px auto 0;
  background: radial-gradient(circle at 50% 0, rgba(255, 196, 61, 0.13), transparent 38%), rgba(22, 19, 49, 0.88);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  text-align: center;
}

.match-setup > p {
  max-width: 620px;
  margin: 0 auto 30px;
  color: var(--cream-muted);
  line-height: 1.65;
}

.turn-order {
  display: grid;
  gap: 10px;
  text-align: left;
}

.turn-player {
  display: grid;
  grid-template-columns: 34px 48px minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  padding: 12px 15px;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--line);
  border-radius: 14px;
}

.turn-player.starter {
  background: rgba(255, 196, 61, 0.1);
  border-color: rgba(255, 196, 61, 0.42);
}

.turn-player .seat-avatar {
  width: 42px;
  height: 42px;
  margin: 0;
  font-size: 1rem;
}

.turn-player strong,
.turn-player div span {
  display: block;
}

.turn-player div span {
  margin-top: 3px;
  color: var(--cream-muted);
  font-size: 0.72rem;
}

.turn-position {
  color: var(--gold);
  font-family: Georgia, serif;
  font-size: 1.35rem;
  font-weight: 800;
}

.starter-badge {
  padding: 6px 9px;
  color: var(--ink);
  background: var(--gold);
  border-radius: 999px;
  font-size: 0.65rem;
  font-weight: 850;
  text-transform: uppercase;
}

.match-placeholder {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 16px;
  margin-top: 24px;
  text-align: left;
  background: rgba(100, 217, 223, 0.07);
  border: 1px solid rgba(100, 217, 223, 0.18);
  border-radius: 14px;
}

.match-placeholder strong,
.match-placeholder span {
  display: block;
}

.match-status-actions {
  display: flex;
  justify-content: center;
  margin: 18px 0 26px;
}

.match-close {
  margin-top: 0;
}

.match-placeholder span {
  margin-top: 4px;
  color: var(--cream-muted);
  font-size: 0.76rem;
}

/* Match table */
.match-table-page {
  width: min(1400px, 100%);
}

.match-table-shell {
  position: relative;
  margin-top: 18px;
  overflow: hidden;
  background: rgba(13, 11, 32, 0.9);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  box-shadow: 0 32px 90px rgba(4, 3, 18, 0.46);
  /* A card table shouldn't let a fast drag gesture turn into a text
     selection - that reads as visual glitching mid-drag. */
  user-select: none;
  -webkit-user-select: none;
}

.match-side-rail {
  position: absolute;
  z-index: 90;
  top: 16px;
  right: 16px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  /* Each .rail-pill's layout box is always full-width now (clip-path does
     the collapsing, see below), so the nav's own box is too - without this
     it would silently block clicks on the table over its whole hover-reveal
     footprint, not just the visible circles. */
  pointer-events: none;
}

.rail-pill {
  position: relative;
  display: inline-flex;
  flex-direction: row-reverse;
  pointer-events: auto;
  align-items: center;
  gap: 8px;
  height: 40px;
  width: min(260px, 62vw);
  padding: 0 11px;
  overflow: hidden;
  color: var(--cream);
  font-family: inherit;
  text-decoration: none;
  white-space: nowrap;
  background: rgba(15, 13, 37, 0.92);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  cursor: pointer;
  backdrop-filter: blur(10px);
  /* Reveal-on-hover is done with clip-path (composited) instead of
     animating width/max-width, which forces a layout reflow every frame
     and was the main source of choppy-feeling rail buttons. The icon is
     flipped to the visual right (row-reverse) so it stays pinned at the
     rail's fixed edge while the label reveals to its left. */
  clip-path: inset(0 0 0 calc(100% - 40px) round 999px);
  transition: clip-path 260ms cubic-bezier(0.32, 0.1, 0.2, 1), border-color 160ms ease, background 160ms ease, color 160ms ease;
  will-change: clip-path;
}

.rail-pill:hover,
.rail-pill:focus-within,
.rail-pill:focus-visible {
  clip-path: inset(0 0 0 0 round 999px);
  border-color: rgba(255, 196, 61, 0.4);
  outline: none;
}

.rail-icon {
  display: grid;
  flex: none;
  width: 18px;
  height: 18px;
  place-items: center;
}

.rail-icon .icon {
  width: 100%;
  height: 100%;
}

.rail-icon-text {
  font-size: 0.62rem;
  font-weight: 900;
  letter-spacing: 0.02em;
}

.rail-label {
  font-size: 0.74rem;
  font-weight: 650;
  opacity: 0;
  transition: opacity 140ms ease;
}

.rail-pill:hover .rail-label,
.rail-pill:focus-within .rail-label,
.rail-pill:focus-visible .rail-label {
  opacity: 1;
}

.rail-danger {
  color: #ffc4c4;
}

.rail-danger:hover,
.rail-danger:focus-within {
  background: rgba(255, 108, 108, 0.14);
  border-color: rgba(255, 108, 108, 0.42);
}

.rail-lang select {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  opacity: 0;
  cursor: pointer;
  appearance: none;
  border: 0;
}

@media (max-width: 680px) {
  /* Desktop's rail is a vertical, absolutely-positioned stack pinned to the
     top-right corner -- fine there, since desktop mats never get close
     enough to that corner to be affected. Mobile's 2-column player grid
     (see the other max-width:680px block) uses nearly the full viewport
     width, so that same corner stack forced the top row's right-hand mat to
     render partly underneath it (confirmed via bounding-box overlap in
     Playwright), which was fixed once by narrowing just that one seat --
     but that made it visibly smaller than its siblings for no reason a
     player would understand. This turns the rail into a normal, in-flow
     horizontal toolbar above the grid instead: position:static removes it
     from the corner entirely, so every seat can go back to being the same
     size, and nothing needs to dodge it any more since it no longer
     overlaps anything -- it just takes its own row at the top, same as any
     other block-level content in a column flex layout with .stellar-table
     below it (see .match-mode .match-table-shell). This is also why it
     doesn't use position:fixed/sticky: an in-flow block can never overlap
     the mats or the hand dock by construction, which a fixed one would
     have to be actively kept clear of. */
  .match-side-rail {
    position: static;
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 6px 10px;
  }

  /* Was a wide (min(260px,62vw)) box clipped down to a circle via clip-path,
     revealing its label on hover -- a pattern built for a VERTICAL stack,
     where the label unclips sideways into empty space next to that one
     pill. In a horizontal row every pill would still occupy its full
     unclipped width for layout purposes (clip-path only changes what's
     painted, not the box's contribution to flex sizing), so seven ~260px
     boxes would blow the row width out to ~1800px even though only small
     circles are visible -- confirmed this is exactly what happened before
     sizing these explicitly. A real fixed-size square sidesteps that
     entirely, and touch devices have no hover to reveal a label with
     anyway (the current desktop behavior is already effectively
     icon-only on a touchscreen). 38px keeps every icon within a few px of
     the 40-44px touch-target guideline while still comfortably fitting all
     seven possible pills (back, event log, language, sound, fullscreen,
     concede, leave) on one row at 360px wide: 7*38 + 6*6(gap) + 2*10
     (this rule's own padding) = 322px, well under 360. */
  .rail-pill {
    width: 38px;
    height: 38px;
    padding: 0;
    justify-content: center;
    clip-path: none;
    /* The label text still exists (for screen readers) but is no longer
       meant to ever be visually revealed on a touch device -- overflow:
       hidden on this same box (see the base .rail-pill rule) already
       clips it at this width, this just stops the hover/focus transition
       from trying to animate a clip-path that no longer applies. */
    transition: border-color 160ms ease, background 160ms ease, color 160ms ease;
  }

  .match-event-log[open] .rail-pill {
    clip-path: none;
  }

  /* Same reasoning as .rail-pill's clip-path: the reveal-on-hover opacity
     transition is for the vertical rail's pointer/keyboard users, not a
     touch toolbar where there's no hover to reveal it with. Leaving the
     transition harmless rather than deleting it outright -- a keyboard
     user tabbing through can still :focus-within their way to a visible
     label if their device does support hover/pointer, per the media query
     this is nested in matching width, not input type. */
  .rail-label {
    max-width: 0;
  }

  /* No override needed for .match-event-log ol here: this same
     max-width:680px condition already turns it into a full-width bottom
     sheet further down the file (position:fixed, left/right:0), which
     already doesn't care where the summary button sits in the row -- it
     was written for exactly this "button can be anywhere" case. Adding a
     second, redundant override here caused more harm than good: even
     touching only non-conflicting properties like transform, a
     translateX(-50%) left stacked on top of that rule's later left:0,
     shifting the already-correct sheet off-screen by half its own (now
     100vw) width -- confirmed via getBoundingClientRect while this was
     still here. */
}

.stellar-table {
  position: relative;
  isolation: isolate;
  min-height: 720px;
  padding: clamp(16px, 2.5vw, 32px);
  overflow: hidden;
  background:
    radial-gradient(circle at 50% 42%, rgba(114, 91, 192, 0.24), transparent 31%),
    radial-gradient(circle at 12% 8%, rgba(83, 194, 207, 0.1), transparent 26%),
    radial-gradient(circle at 88% 16%, rgba(255, 196, 61, 0.08), transparent 25%),
    linear-gradient(145deg, #171430 0%, #100e27 52%, #171432 100%);
}

.stellar-table::before {
  position: absolute;
  z-index: -2;
  inset: 0;
  content: "";
  opacity: 0.42;
  background-image:
    radial-gradient(circle at 7% 12%, rgba(255,255,255,.9) 0 1px, transparent 1.5px),
    radial-gradient(circle at 23% 31%, rgba(255,255,255,.55) 0 1px, transparent 1.6px),
    radial-gradient(circle at 48% 11%, rgba(255,196,61,.72) 0 1.2px, transparent 1.8px),
    radial-gradient(circle at 68% 37%, rgba(255,255,255,.6) 0 1px, transparent 1.6px),
    radial-gradient(circle at 92% 18%, rgba(100,217,223,.75) 0 1.2px, transparent 1.8px);
  background-size: 290px 220px, 410px 310px, 520px 360px, 670px 470px, 790px 520px;
  /* Static, not animated: five differently-timed background-position
     animations forced a full repaint of this layer on every frame, which
     was the single biggest cost behind choppy drag/hover animations during
     a match (measured via a Chrome trace - see the drag-fps investigation).
     A still starfield reads almost identically at this size/opacity. */
}

.stellar-table::after {
  position: absolute;
  z-index: -1;
  inset: 0;
  content: "";
  opacity: 0.12;
  background-image:
    repeating-linear-gradient(48deg, transparent 0 68px, rgba(187, 174, 235, 0.28) 69px 70px, transparent 71px 138px),
    repeating-linear-gradient(-48deg, transparent 0 68px, rgba(187, 174, 235, 0.22) 69px 70px, transparent 71px 138px);
  mask-image: radial-gradient(ellipse at center, black, transparent 88%);
}

/* A stable multiplayer table: every seat -- opponents and the local player
   alike -- sits in one grid, in the same cell for the whole match (DOM order
   follows room.game.turnOrder, which is fixed once the game starts, with the
   local seat always last). Nobody grows, shrinks or swaps position when the
   active player changes; see .battle-player-board.active-turn for the only
   thing that marks whose turn it is. Column count is chosen per player count
   so the shape stays readable at 2-6 players instead of one generic wrap. */
/* Columns size to the mats themselves (`auto`, not `1fr`) and the whole
   track set centres as one packed group (`justify-content:center`) -- `1fr`
   columns stretch to fill the row and just push the (much narrower, fixed
   --mat-size) mats apart, leaving a dead gap between them instead of using
   that width to make anything bigger. */
.game-players {
  display: grid;
  gap: clamp(14px, 2.2vw, 40px);
  justify-content: center;
  justify-items: center;
  align-items: center;
  padding-block: 18px;
}

.game-players[data-count="2"] { grid-template-columns: auto; }
.game-players[data-count="3"] { grid-template-columns: repeat(3, auto); }
.game-players[data-count="4"] { grid-template-columns: repeat(2, auto); }
.game-players[data-count="5"],
.game-players[data-count="6"] { grid-template-columns: repeat(3, auto); }

/* Seat wrapper -------------------------------------------------------------
   The mat itself must keep overflow:hidden so the playmat art stays inside its
   rounded border. That also means nothing can hang off a mat's edge, so the
   opponent's hand fan (which DOES overhang) lives on the seat, not the mat.
   The seat is also the container-query root everything below scales against:
   `cqw` units on player-info/zone text are percentages of THIS box's own
   width, so they track the mat's (fixed, per-breakpoint) size instead of
   needing a fixed override per breakpoint. */
.mat-seat {
  position: relative;
  width: var(--mat-size, auto);
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(2px, 1.4cqw, 6px);
  container-type: inline-size;
  container-name: mat;
}

.mat-seat > .battle-player-board {
  min-height: 0;
}

/* Name + hand size, stacked above the mat as plain text -- see renderPlayerInfo
   in app.js. Percent/clamp sizing (not a fixed-px override per state) is the
   whole point: it scales with the mat instead of breaking when it shrinks. */
.player-info {
  display: flex;
  flex: none;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  column-gap: clamp(4px, 2.5cqw, 10px);
  row-gap: 0;
  width: var(--mat-size, auto);
  min-width: 0;
  padding-inline: 4px;
}

/* The only label marking whose turn it is -- mats no longer resize, so this
   plus the glow on .battle-player-board.active-turn is the whole signal. */
.player-info-turn {
  flex: 0 0 100%;
  overflow: hidden;
  color: var(--gold);
  font-size: clamp(0.42rem, 6cqw, 0.6rem);
  font-weight: 850;
  letter-spacing: 0.1em;
  text-align: center;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
  animation: turn-label-in 220ms ease both;
}

@keyframes turn-label-in {
  from { opacity: 0; transform: translateY(-2px); }
  to { opacity: 1; transform: translateY(0); }
}

.player-info-name {
  overflow: hidden;
  color: var(--cream);
  font-family: Georgia, serif;
  font-size: clamp(0.62rem, 10cqw, 1.05rem);
  font-weight: 700;
  text-align: center;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.player-info-count {
  flex: none;
  overflow: hidden;
  color: var(--cream-muted);
  font-size: clamp(0.48rem, 6.5cqw, 0.72rem);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.player-info .nameplate-flag {
  flex: none;
  font-size: clamp(0.42rem, 5.5cqw, 0.6rem);
}

.player-info .room-left-badge {
  width: clamp(16px, 12cqw, 30px);
  height: clamp(16px, 12cqw, 30px);
}

.player-info .room-left-badge .icon {
  width: 55%;
  height: 55%;
}

/* .nameplate-avatar is still used directly by the mat close-up header
   (renderExpandedMat in app.js); .player-nameplate/.nameplate-name were the
   old pill that wrapped it inline over the mat and are gone now that the
   avatar there just sits next to plain text. */
.nameplate-avatar {
  display: grid;
  width: 20px;
  height: 20px;
  flex: none;
  place-items: center;
  color: #141127;
  background: color-mix(in srgb, var(--mat-line, var(--lavender)) 70%, white);
  border-radius: 50%;
  font-family: Georgia, serif;
  font-size: 0.55rem;
  font-weight: 850;
}

.nameplate-flag {
  flex: none;
  color: var(--danger);
  font-size: 0.5rem;
  font-weight: 900;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

/* Small/discreet by design (see renderStatusBadges) -- a quiet reminder of
   who a lasting targeted effect is affecting, not a new alert of its own. */
.status-badge {
  flex: none;
  padding: 1px 6px;
  color: var(--gold);
  background: rgba(255, 196, 61, 0.12);
  border: 1px solid rgba(255, 196, 61, 0.35);
  border-radius: 999px;
  font-size: clamp(0.4rem, 5cqw, 0.56rem);
  white-space: nowrap;
}

/* The coin ------------------------------------------------------------------
   Two faces on one disc: light = this player holds the coin and acts first,
   dark = they do not. Both faces carry the spirit score, so the number never
   disappears mid-flip. Each playmat gets its own metal via the inherited
   --mat-* custom properties rather than six more selector blocks. */
.player-coin {
  display: block;
  aspect-ratio: 1;
  perspective: 420px;
}

/* On the table, the coin lives INSIDE the playmat (a child of
   .battle-player-board, its `position:relative` containing block) and is
   sized/placed with plain percentages -- not a fixed px override per
   spotlight/dimmed state. Percent top/right/width on an absolutely positioned
   element are already relative to the containing block by spec, so this
   needs no container-query unit. The mat close-up header uses the same
   .player-coin markup but as a normal inline flow element (its own rule,
   near .mat-closeup-head), so the absolute positioning is scoped here and
   does not leak into that context. */
.battle-player-board > .player-coin {
  position: absolute;
  z-index: 3;
  top: 4%;
  right: 4%;
  width: 16%;
}

.coin-inner {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transform: rotateY(180deg);
  transition: transform 620ms cubic-bezier(0.5, 0.05, 0.2, 1);
}

.player-coin.has-coin .coin-inner {
  transform: rotateY(360deg);
}

.coin-face {
  position: absolute;
  display: grid;
  inset: 0;
  place-items: center;
  border-radius: 50%;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

.coin-face b {
  font-family: Georgia, serif;
  font-size: 1.02rem;
  font-weight: 850;
  line-height: 1;
}

/* Light face: the coin holder. */
.coin-front {
  color: #1b1733;
  background:
    radial-gradient(circle at 34% 26%, #fffdf5 6%, #ece3cd 42%, #b8a97f 74%, #8a7c52 100%);
  border: 2px solid color-mix(in srgb, var(--mat-line, #d9cfae) 60%, white);
  box-shadow: 0 0 18px color-mix(in srgb, var(--mat-glow, rgba(255, 255, 255, 0.3)) 90%, transparent),
    inset 0 -3px 7px rgba(84, 71, 33, 0.35);
}

/* Dark face: everyone else. */
.coin-back {
  color: color-mix(in srgb, var(--mat-line, #9586d3) 70%, white);
  background:
    radial-gradient(circle at 34% 26%, #3b3556 4%, #221d3d 46%, #14112a 100%);
  border: 2px solid color-mix(in srgb, var(--mat-line, #9586d3) 38%, transparent);
  box-shadow: inset 0 -3px 7px rgba(0, 0, 0, 0.45);
  transform: rotateY(180deg);
}

/* Fired once per round by syncCoins(): every coin spins, each landing on its
   own correct face. A keyframe, not a transition, because the element is
   recreated on every render and a transition would have nothing to start from. */
.player-coin.spinning .coin-inner {
  animation: coin-spin 900ms cubic-bezier(0.42, 0.02, 0.18, 1) both;
}

@keyframes coin-spin {
  from { transform: rotateY(0deg); }
  to { transform: rotateY(720deg); }
}

.player-coin.has-coin.spinning .coin-inner {
  animation-name: coin-spin-to-light;
}

@keyframes coin-spin-to-light {
  from { transform: rotateY(180deg); }
  to { transform: rotateY(720deg); }
}

/* Full-bleed hit target for "click a mat to see it up close". It sits under the
   zones (which are z-index:1) so the card buttons still win the click. The
   zone scaffolding itself is made transparent to the pointer, otherwise it
   would blanket the mat and swallow every click that isn't exactly on a card.
   Drop targets keep pointer-events because the drag system hit-tests them with
   elementFromPoint(), which honours pointer-events: none. */
.battle-zones,
.battle-zone {
  pointer-events: none;
}

.battle-zone.drop-target,
.battle-zone button {
  pointer-events: auto;
}

.mat-expand {
  position: absolute;
  z-index: 0;
  inset: 0;
  padding: 0;
  background: none;
  border: 0;
  border-radius: inherit;
  cursor: zoom-in;
}

.mat-expand:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: -3px;
}

@media (prefers-reduced-motion: reduce) {
  .coin-inner { transition: none; }
  .player-coin.spinning .coin-inner { animation: none; }
}

/* Mat close-up. Lives inside the table (not the modal root) so it keeps
   updating while the match runs and never blocks play underneath. */
.mat-closeup-layer {
  position: absolute;
  z-index: 95;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 16px;
  background: rgba(6, 5, 20, 0.72);
  backdrop-filter: blur(6px);
  animation: fade-in 160ms ease both;
}

/* "Sua vez!" -- a brief cinematic flash, not a modal: pointer-events:none so
   it never blocks a click on the mats/hand underneath while it's on screen.
   z-index sits between the mat close-up (95) and the effect-choice-drawer
   (120) on purpose -- above the table, but a real choice prompt still wins.
   The animation is a single one-shot keyframe (not a CSS transition) because
   the node is freshly inserted by showTurnAlert() each time; a transition on
   a brand-new element has no previous value to animate from, same reasoning
   as the coin-spin/active-playmat-glow keyframes elsewhere on this page. */
.turn-alert {
  position: fixed;
  z-index: 100;
  top: 40%;
  left: 50%;
  pointer-events: none;
  transform: translate(-50%, -50%);
  animation: turn-alert-pop 1.7s cubic-bezier(0.22, 0.8, 0.24, 1) both;
}

@keyframes turn-alert-pop {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.7); }
  14% { opacity: 1; transform: translate(-50%, -50%) scale(1.08); }
  22% { transform: translate(-50%, -50%) scale(1); }
  80% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(0.94); }
}

.turn-alert span {
  display: block;
  padding: 16px 38px;
  color: var(--ink);
  background: linear-gradient(150deg, #fff6d2, var(--gold) 55%, var(--gold-deep));
  border: 2px solid rgba(255, 255, 255, 0.65);
  border-radius: 999px;
  box-shadow: 0 22px 60px rgba(255, 196, 61, 0.4), 0 0 0 7px rgba(255, 196, 61, 0.12);
  font-family: Georgia, serif;
  font-size: clamp(1.5rem, 4.6vw, 2.5rem);
  font-weight: 900;
  letter-spacing: 0.03em;
  text-align: center;
  text-shadow: 0 2px 0 rgba(255, 255, 255, 0.35);
  text-transform: uppercase;
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  .turn-alert {
    animation: none;
    opacity: 1;
    transform: translate(-50%, -50%);
    transition: opacity 250ms ease 1.2s;
  }
}

@media (max-width: 430px) {
  .turn-alert span {
    padding: 13px 26px;
    font-size: clamp(1.15rem, 8vw, 1.7rem);
  }
}

/* Public card reveal (Navius/Ceres/Deneb-style "reveal a card" effects, see
   queueCardReveal): same z-index tier as .turn-alert (above the table, below
   the choice drawer) and the same "not a modal" shape -- the dimmed backdrop
   is pointer-events:none so it never blocks the rest of the match, only the
   card panel itself is clickable (to dismiss early; it also auto-dismisses
   via a fixed JS timeout either way, so this never gets stuck on screen). */
.card-reveal {
  position: fixed;
  z-index: 100;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  background: radial-gradient(circle at 50% 45%, rgba(6, 5, 20, 0.55), rgba(6, 5, 20, 0.15) 70%);
  /* Duration set inline per node (--card-reveal-duration) -- a grouped
     reveal with extra lines (Protubera: discarded + power + returned to
     deck) stays up longer than a bare card name, and the JS removal timer
     is set to match; without the shared variable the animation would finish
     (fully transparent) well before the node is actually removed. */
  animation: card-reveal-fade var(--card-reveal-duration, 2.6s) ease both;
}

.card-reveal-panel {
  pointer-events: auto;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 18px 22px 20px;
  background: linear-gradient(165deg, var(--ink-soft), var(--ink));
  border: 2px solid rgba(255, 196, 61, 0.55);
  border-radius: 18px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.5), 0 0 0 6px rgba(255, 196, 61, 0.1);
  animation: card-reveal-pop var(--card-reveal-duration, 2.6s) cubic-bezier(0.22, 0.8, 0.24, 1) both;
}

/* Extra description lines for a grouped reveal (see handleEventGroup) --
   e.g. "Descartada" / "+3 de Poder" / "Retornou ao Baralho" under the card
   name. Small and left-aligned so it reads as a short summary, not another
   headline competing with the card name above it. */
.card-reveal-lines {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
  text-align: center;
}

.card-reveal-lines li {
  color: var(--cream-muted, #cbb);
  font-size: 0.76rem;
}

.card-reveal-panel strong {
  font-family: Georgia, serif;
  font-size: clamp(0.95rem, 2.4vw, 1.2rem);
  color: var(--gold);
  text-align: center;
}

.card-reveal-art {
  width: clamp(150px, 26vw, 240px);
  aspect-ratio: var(--card-aspect-ratio);
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
}

.card-reveal-art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.card-reveal-panel span {
  font-weight: 700;
  color: var(--paper, #fff);
  text-align: center;
}

@keyframes card-reveal-fade {
  0% { opacity: 0; }
  10% { opacity: 1; }
  85% { opacity: 1; }
  100% { opacity: 0; }
}

@keyframes card-reveal-pop {
  0% { transform: scale(0.82); }
  14% { transform: scale(1.04); }
  22% { transform: scale(1); }
  100% { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .card-reveal, .card-reveal-panel {
    animation: none;
    opacity: 1;
    transform: none;
    transition: opacity 250ms ease calc(var(--card-reveal-duration, 2.6s) - 400ms);
  }
}

@media (max-width: 430px) {
  .card-reveal-art {
    width: clamp(120px, 42vw, 190px);
  }
  .card-reveal-panel strong {
    font-size: 0.86rem;
  }
}

.mat-closeup {
  width: min(660px, 100%);
  max-height: 100%;
  padding: 18px 20px 20px;
  overflow-y: auto;
  overscroll-behavior: contain;
  background-color: var(--mat-bg-2, var(--ink-soft));
  background-image:
    var(--mat-art, none),
    radial-gradient(circle at 18% 4%, var(--mat-glow, transparent), transparent 46%),
    linear-gradient(150deg, var(--mat-bg-1, var(--ink-raised)), var(--mat-bg-2, var(--ink)));
  background-size: cover;
  border: 1px solid color-mix(in srgb, var(--mat-line, var(--line-strong)) 55%, transparent);
  border-radius: 20px;
  box-shadow: 0 30px 80px rgba(3, 2, 16, 0.6);
}

.mat-closeup-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 11px;
  margin-bottom: 16px;
}

.mat-closeup-head .nameplate-avatar {
  width: 38px;
  height: 38px;
  font-size: 0.95rem;
}

.mat-closeup-head .player-coin {
  width: 42px;
}

.mat-closeup-head strong {
  display: block;
  font-family: Georgia, serif;
  font-size: 1.1rem;
}

.mat-closeup-head > div > span {
  color: var(--cream-muted);
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

/* Needs to outrank the base .battle-zones grid, which is defined later in the
   file and otherwise wins on source order. */
.mat-closeup .mat-closeup-zones {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-template-areas: "discard fighter supporter draw";
  gap: 6px 12px;
  margin-bottom: 18px;
}

.mat-closeup .mat-closeup-zones .zone-card {
  width: min(100%, 116px);
  margin-inline: auto;
}

.mat-closeup-subtitle {
  margin: 0 0 10px;
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.mat-closeup-discard {
  max-height: 260px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

@media (max-height: 760px) {
  .mat-closeup { padding: 12px 14px 14px; }
  .mat-closeup-head { margin-bottom: 10px; }
  .mat-closeup .mat-closeup-zones .zone-card { width: min(100%, 74px); }
  .mat-closeup-discard { max-height: 140px; }
}

.battle-player-board {
  position: relative;
  --mat-bg-1: #211b46;
  --mat-bg-2: #100e29;
  --mat-line: #9586d3;
  --mat-glow: rgba(149, 134, 211, 0.22);
  --mat-art: none;
  width: var(--mat-size, 100%);
  min-width: 0;
  flex: none;
  aspect-ratio: 1;
  padding: 15px;
  overflow: hidden;
  background-color: var(--mat-bg-2);
  background-image:
    var(--mat-art),
    radial-gradient(circle at 18% 4%, var(--mat-glow), transparent 43%),
    linear-gradient(145deg, var(--mat-bg-1), var(--mat-bg-2));
  background-position: center;
  background-size: cover;
  border: 1px solid color-mix(in srgb, var(--mat-line) 55%, rgba(255, 255, 255, 0.1));
  border-radius: 19px;
  box-shadow: inset 0 0 55px rgba(4, 3, 18, 0.2), 0 18px 40px rgba(3, 2, 16, 0.25);
  backdrop-filter: blur(12px);
}

[data-playmat="lunar"] {
  --mat-bg-1: #282052;
  --mat-bg-2: #12102f;
  --mat-line: #a99ae5;
  --mat-glow: rgba(167, 146, 235, 0.25);
  --mat-art: url("data:image/svg+xml,%3Csvg viewBox='0 0 520 340' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='%23ddd4ff' stroke-opacity='.2' stroke-width='1.2'%3E%3Cpath d='M42 70c38-44 92 8 58 43 47-12 68 45 24 68M392 43c58 24 53 102-8 117 36-30 31-83 8-117ZM35 278c72-62 130-65 198-22s131 38 239-20'/%3E%3Cellipse cx='320' cy='177' rx='86' ry='29' transform='rotate(-18 320 177)'/%3E%3Ccircle cx='320' cy='177' r='43'/%3E%3C/g%3E%3C/svg%3E");
}

[data-playmat="solar"] {
  --mat-bg-1: #573019;
  --mat-bg-2: #241126;
  --mat-line: #f2ae3d;
  --mat-glow: rgba(255, 157, 39, 0.27);
  --mat-art: url("data:image/svg+xml,%3Csvg viewBox='0 0 520 340' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='%23ffd56a' stroke-opacity='.19' stroke-width='1.3'%3E%3Ccircle cx='85' cy='72' r='39'/%3E%3Cpath d='M85 10v25M85 109v25M23 72h25M122 72h25M41 28l18 18M111 98l18 18M129 28l-18 18M59 98l-18 18M310 54l18 61 61 18-61 18-18 61-18-61-61-18 61-18 18-61ZM388 252c38-49 83-49 111 2-41-24-77-22-111-2Z'/%3E%3C/g%3E%3C/svg%3E");
}

[data-playmat="orbit"] {
  --mat-bg-1: #343754;
  --mat-bg-2: #17162d;
  --mat-line: #d9cc74;
  --mat-glow: rgba(214, 201, 90, 0.22);
  --mat-art: url("data:image/svg+xml,%3Csvg viewBox='0 0 520 340' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='%23eee6a5' stroke-opacity='.2' stroke-width='1.2'%3E%3Cellipse cx='263' cy='166' rx='198' ry='63' transform='rotate(-12 263 166)'/%3E%3Cellipse cx='263' cy='166' rx='128' ry='42' transform='rotate(32 263 166)'/%3E%3Ccircle cx='263' cy='166' r='34'/%3E%3Ccircle cx='77' cy='215' r='13'/%3E%3Ccircle cx='436' cy='96' r='18'/%3E%3Cpath d='M32 54c86 43 111 84 121 155M484 288c-74-25-119-64-144-118'/%3E%3C/g%3E%3C/svg%3E");
}

[data-playmat="forge"] {
  --mat-bg-1: #442c2a;
  --mat-bg-2: #1c1421;
  --mat-line: #c48267;
  --mat-glow: rgba(200, 113, 76, 0.23);
  --mat-art: url("data:image/svg+xml,%3Csvg viewBox='0 0 520 340' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='%23e4aa8b' stroke-opacity='.2' stroke-width='1.25'%3E%3Cpath d='m260 23 28 105 104-43-73 81 91 57-111-18-6 112-39-105-91 65 60-94-108-39 111-4 34-117ZM32 46l85 67-42 58 73 54-42 84M482 31l-79 79 45 55-77 70 39 76'/%3E%3C/g%3E%3C/svg%3E");
}

[data-playmat="stellar"] {
  --mat-bg-1: #1e315c;
  --mat-bg-2: #11162f;
  --mat-line: #7f9de9;
  --mat-glow: rgba(88, 126, 224, 0.25);
  --mat-art: url("data:image/svg+xml,%3Csvg viewBox='0 0 520 340' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='%23b7c8ff' stroke-opacity='.21' stroke-width='1.15'%3E%3Cpath d='M32 90 116 43l78 69 92-55 75 80 114-67M65 282l91-76 96 48 91-89 113 79M116 43l40 163M286 57l57 108'/%3E%3Cpath d='m257 83 17 44 47 3-37 29 12 46-39-26-40 26 13-46-37-29 46-3 18-44Z'/%3E%3C/g%3E%3Cg fill='%23dce5ff' fill-opacity='.36'%3E%3Ccircle cx='32' cy='90' r='3'/%3E%3Ccircle cx='116' cy='43' r='2'/%3E%3Ccircle cx='194' cy='112' r='3'/%3E%3Ccircle cx='361' cy='137' r='2.5'/%3E%3Ccircle cx='475' cy='70' r='3'/%3E%3C/g%3E%3C/svg%3E");
}

[data-playmat="nebula"] {
  --mat-bg-1: #442044;
  --mat-bg-2: #170e29;
  --mat-line: #df4e8f;
  --mat-glow: rgba(218, 43, 119, 0.24);
  --mat-art: url("data:image/svg+xml,%3Csvg viewBox='0 0 520 340' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='%23ff8abb' stroke-opacity='.19' stroke-width='1.25'%3E%3Cpath d='M-18 112c97-91 174-76 225 0s121 88 186 17 123-45 161 13M-24 226c82-72 161-70 218 8s133 65 190 2 119-61 165 7M85 23c-34 83 5 127 65 154s73 83 29 141M405 16c39 79 4 130-58 158s-72 85-20 149'/%3E%3Ccircle cx='263' cy='170' r='47'/%3E%3Cpath d='M263 107c17 29 17 53 0 72-17-19-17-43 0-72ZM326 170c-29 17-53 17-72 0 19-17 43-17 72 0ZM263 233c-17-29-17-53 0-72 17 19 17 43 0 72ZM200 170c29-17 53-17 72 0-19 17-43 17-72 0Z'/%3E%3C/g%3E%3C/svg%3E");
}

.battle-player-board::before {
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  content: "";
  background: linear-gradient(90deg, transparent, var(--mat-line), transparent);
  opacity: 0.75;
}

.battle-player-board.starter {
  border-color: color-mix(in srgb, var(--gold) 58%, var(--player-color));
  box-shadow: 0 0 0 1px rgba(255, 196, 61, 0.08), 0 20px 48px rgba(3, 2, 16, 0.32);
}

.battle-player-board.active-turn {
  border-color: var(--gold);
  animation: active-playmat-glow 2.4s ease-in-out infinite;
}

@keyframes active-playmat-glow {
  0%, 100% {
    box-shadow: 0 0 0 1px rgba(255, 196, 61, 0.18), 0 0 20px rgba(255, 196, 61, 0.16), 0 18px 40px rgba(3, 2, 16, 0.28);
  }
  50% {
    box-shadow: 0 0 0 2px rgba(255, 224, 132, 0.5), 0 0 40px rgba(255, 196, 61, 0.38), 0 18px 48px rgba(3, 2, 16, 0.34);
  }
}

/* Same size and layout as every other seat now -- border-width is the only
   thing marking "this is you", a purely visual difference on the shared
   component rather than a distinct bigger layout. */
.battle-player-board.local {
  border-width: 2px;
}

.battle-avatar {
  display: grid;
  width: 42px;
  height: 42px;
  place-items: center;
  color: #141127;
  background: color-mix(in srgb, var(--player-color, var(--lavender)) 65%, white);
  border: 2px solid rgba(255, 255, 255, 0.3);
  border-radius: 50%;
  font-family: Georgia, serif;
  font-size: 0.92rem;
  font-weight: 850;
  box-shadow: 0 0 24px color-mix(in srgb, var(--player-color, var(--lavender)) 25%, transparent);
}

.room-left-badge {
  display: grid;
  width: 30px;
  height: 30px;
  place-items: center;
  color: var(--danger);
  background: rgba(255, 107, 107, 0.14);
  border: 1px solid rgba(255, 107, 107, 0.4);
  border-radius: 50%;
}

.room-left-badge .icon {
  width: 16px;
  height: 16px;
}

.battle-player-board.room-left {
  opacity: 0.72;
}

.battle-zones {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 0.84fr) minmax(0, 1fr) minmax(0, 0.84fr);
  grid-template-areas:
    "discard fighter draw"
    "discard supporter draw";
  align-items: center;
  gap: 9px 11px;
}

.battle-zone.discard { grid-area: discard; }
.battle-zone.fighter { grid-area: fighter; }
.battle-zone.supporter { grid-area: supporter; }
.battle-zone.draw { grid-area: draw; }

.battle-zone.discard,
.battle-zone.draw {
  align-self: center;
}

.battle-zone {
  min-width: 0;
}

.zone-label {
  display: block;
  margin-bottom: 5px;
  overflow: hidden;
  color: rgba(246, 240, 228, 0.55);
  font-size: 0.55rem;
  font-weight: 850;
  letter-spacing: 0.07em;
  text-align: center;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
}

.zone-card {
  position: relative;
  width: min(100%, 90px);
  min-height: 0;
  aspect-ratio: var(--card-aspect-ratio);
  display: grid;
  place-items: center;
  margin-inline: auto;
  overflow: hidden;
  background: rgba(8, 7, 24, 0.38);
  border: 1px dashed rgba(246, 240, 228, 0.16);
  border-radius: 10px;
}

.fighter .zone-card,
.supporter .zone-card {
  border-color: color-mix(in srgb, var(--player-color) 28%, rgba(255, 255, 255, 0.12));
}

.empty-zone-glyph {
  color: color-mix(in srgb, var(--player-color) 38%, transparent);
  font-family: Georgia, serif;
  font-size: clamp(1.25rem, 2.8vw, 2.3rem);
}

.zone-card > small {
  position: absolute;
  right: 5px;
  bottom: 7px;
  left: 5px;
  color: rgba(246, 240, 228, 0.32);
  font-size: 0.48rem;
  font-weight: 750;
  text-align: center;
}

.table-card-stack {
  position: relative;
  width: 78%;
  aspect-ratio: 372 / 520;
}

.table-card-stack::before,
.table-card-stack::after {
  position: absolute;
  inset: 0;
  content: "";
  background: #25213f;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 7px;
}

.table-card-stack::before {
  transform: translate(5px, -5px) rotate(2deg);
}

.table-card-stack::after {
  transform: translate(2px, -2px) rotate(1deg);
}

.table-card-stack img {
  position: relative;
  z-index: 2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border: 1px solid rgba(255, 255, 255, 0.24);
  border-radius: 7px;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.35);
}

.zone-count {
  position: absolute;
  z-index: 3;
  right: 7px;
  bottom: 7px;
  display: grid;
  min-width: 25px;
  height: 25px;
  place-items: center;
  padding-inline: 5px;
  color: #181328;
  background: var(--gold);
  border: 2px solid rgba(255, 255, 255, 0.56);
  pointer-events: none;
  border-radius: 999px;
  font-size: 0.67rem;
  font-weight: 900;
}

/* A near-full-slot fill (not a small px-capped fraction) -- the slot itself
   (.zone-card) is already sized to real card proportions via aspect-ratio,
   so the card just needs a small margin inside it, not to stay tiny inside
   a now much bigger slot. */
.table-face-card {
  width: 92%;
  padding: 0;
  overflow: hidden;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 8px;
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.34);
  cursor: zoom-in;
  transition: 160ms ease;
}

.table-face-card:hover,
.table-face-card:focus-visible {
  border-color: var(--gold);
  transform: translateY(-3px);
}

.table-face-card img {
  width: 100%;
  aspect-ratio: var(--card-aspect-ratio);
  object-fit: cover;
}

.match-hand {
  position: relative;
  z-index: 1;
}

/* Round + phase moved into .action-card beside the hand (see renderActionCard
   in app.js) -- the round marker that used to sit between the opponent row
   and the local mat is gone along with that three-row layout. */

/* Floating hand dock ---------------------------------------------------------
   Pinned to the viewport's own bottom edge (position:fixed - NOT a grid row),
   so it never reserves a big empty strip in the table layout and the freed
   height goes back to the player grid instead. It sits above the table
   (z-index) but below the two things meant to eclipse it: the mat close-up
   overlay (.mat-closeup-layer, z-index:95) and the pending-effect-choice
   drawer (.effect-choice-drawer, z-index:120) -- both already covered the
   hand before this change too, just via normal stacking inside the grid.

   The action card sits beside the hand (not stacked above it) so the hand
   keeps its fan centred and the freed vertical space goes to the player grid.
   Everything here is pointer-events:none by default and only the actual
   interactive pieces (the action card, each hand card) opt back in --
   otherwise the dock's wide, mostly-empty bounding box would sit on top of
   the table and silently swallow clicks meant for whatever is behind it. */
.floating-hand-dock {
  position: fixed;
  z-index: 70;
  left: 50%;
  bottom: 0;
  /* Symmetric 3-column grid -- action card, hand, and an equal-width
     invisible spacer -- instead of a 2-slot flex row. With only two slots,
     the hand's own "centering" was relative to the leftover space after the
     action card, not the screen: the whole dock is centered as a unit, but
     the action card eating real width on the left meant the hand visibly
     read as shifted toward it. The third (empty) column balances that out
     so the middle column -- the hand -- lands exactly on the dock's, and so
     the viewport's, true center. */
  display: grid;
  grid-template-columns: var(--dock-action-w) minmax(0, 1fr) var(--dock-action-w);
  align-items: end;
  /* Single source of truth for the quantities the row is built from -- the
     action card's own width, the breathing room around it, and the dock's
     own total width -- consumed below by .action-card, .floating-hand, AND
     (inherited down through the DOM, see .match-hand-card-wrap's
     --fan-budget) the fan-sizing math itself, so none of them can drift out
     of sync with each other again. */
  --dock-w: min(1200px, 98vw);
  --dock-action-w: min(220px, 30vw);
  --dock-gap: clamp(8px, 1.5vw, 20px);
  /* Total width the hand's own column yields to on BOTH sides (the real
     action card on the left, its mirror spacer on the right) -- read by
     --fan-budget below so the fan's own spacing math stays in sync with
     this column's actual rendered width without a second number to keep in
     sync. Zeroed out in the stacked mobile layout, where the hand no longer
     shares a row with anything -- see that breakpoint's own override. */
  --dock-reserve-w: calc((var(--dock-action-w) + var(--dock-gap)) * 2);
  column-gap: var(--dock-gap);
  width: var(--dock-w);
  padding-bottom: clamp(4px, 1.2vh, 12px);
  transform: translateX(-50%);
  pointer-events: none;
}

.floating-hand-dock > .action-card {
  grid-column: 1;
  pointer-events: auto;
}

.floating-hand {
  grid-column: 2;
  display: flex;
  width: 100%;
  max-width: min(900px, 70vw);
  min-width: 0;
  justify-content: center;
  margin-inline: auto;
  pointer-events: none;
  --card-total: 1;
}

/* Narrow viewports don't have room for the card beside the hand -- stack it
   above instead, same as the old full-width bar used to. */
@media (max-width: 680px) {
  .floating-hand-dock {
    grid-template-columns: none;
    justify-items: center;
    row-gap: var(--dock-gap);
    /* Stacked now, not side-by-side -- .floating-hand no longer shares its
       axis with the action card, so it shouldn't yield any width to it. */
    --dock-reserve-w: 0px;
  }

  .floating-hand-dock > .action-card {
    grid-column: 1;
    width: min(420px, 92vw);
  }

  .floating-hand {
    grid-column: 1;
  }
}

/* Desktop/notebook only: a visibly clearer gap between the action card and
   the hand than the base clamp gives (max 20px, easy to misread as the two
   being one crowded block) -- widened here rather than by raising the base
   clamp's own ceiling so mobile (where 1.5vw is already small) is
   unaffected either way. --dock-reserve-w picks this up automatically
   (see .floating-hand-dock), so the hand's own width/fan budget stays
   consistent with the actual gap without a second number to keep in sync. */
@media (min-width: 901px) {
  .floating-hand-dock {
    --dock-gap: clamp(28px, 2.4vw, 56px);
  }
}

/* Cards pinned to the viewport edge, as if the player is holding them: shift
   the whole fanned row down so a small slice of the cards' bottom edge sits
   past the viewport, exactly like a hand held up from below frame. Hovering a
   card lifts it back out of that sunken position (see .match-hand-card-wrap
   below), which reads as picking it up to look at it. overflow stays visible
   (not auto/scroll) because a raised, scaled hover card must be free to paint
   outside this row's own box. */
.match-hand-cards {
  display: flex;
  justify-content: center;
  gap: clamp(7px, 1.2vw, 14px);
  transform: translateY(clamp(6px, 1.6vh, 14px));
  pointer-events: none;
}

/* Desktop's .floating-hand-dock is now a symmetric 3-column grid (action
   card | hand | equal-width spacer, see .floating-hand-dock) instead of a
   2-slot flex row, so .floating-hand's own column is already centered on
   the viewport -- unlike before, hugging the action-card edge is no longer
   needed to read as "near" it with a consistent gap; centering within that
   column now reads as centered on screen, which is the actual goal. Both
   .match-hand-cards and .floating-hand keep their unconditional
   justify-content:center from their base rules above -- no width-specific
   override needed here anymore. The fan itself rotates symmetrically around
   its own middle card (see --fan-offset in .match-hand-card-wrap), so it
   doesn't need the old asymmetric edge-clearance padding either now that
   nothing is hugging one edge. */

.match-hand-card {
  position: relative;
  min-width: 92px;
  max-width: 150px;
  flex: 1 1 130px;
  aspect-ratio: var(--card-aspect-ratio);
  padding: 0;
  overflow: hidden;
  background: #17142d;
  border: 1px solid color-mix(in srgb, var(--hand-color) 52%, rgba(255, 255, 255, 0.2));
  border-radius: 11px;
  box-shadow: 0 15px 26px rgba(3, 2, 16, 0.35);
  cursor: zoom-in;
  scroll-snap-align: center;
  transition: 180ms ease;
}

.match-hand-card:hover,
.match-hand-card:focus-visible {
  z-index: 2;
  border-color: color-mix(in srgb, var(--hand-color) 65%, white);
  box-shadow: 0 20px 35px rgba(3, 2, 16, 0.48), 0 0 24px color-mix(in srgb, var(--hand-color) 20%, transparent);
  /* The rise/scale/de-rotate live on .match-hand-card-wrap (the fan item),
     not here -- putting it on both would compound into a much bigger jump
     than intended, since this button sits nested inside that wrap. */
}

.match-hand-card > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  -webkit-user-drag: none;
  user-select: none;
}

.match-hand-card > span {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  display: grid;
  grid-template-columns: 25px minmax(0, 1fr) 18px;
  align-items: center;
  gap: 5px;
  padding: 28px 7px 7px;
  color: var(--cream);
  background: linear-gradient(transparent, rgba(9, 7, 26, 0.95) 45%);
}

.match-hand-card b {
  display: grid;
  width: 24px;
  height: 24px;
  place-items: center;
  color: #171227;
  background: var(--cream);
  border-radius: 50%;
  font-size: 0.68rem;
}

.match-hand-card strong {
  overflow: hidden;
  font-family: Georgia, serif;
  font-size: 0.67rem;
  text-align: left;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.match-hand-card .icon {
  width: 15px;
  height: 15px;
}

.match-table-notice {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 15px 17px;
  margin-top: 17px;
  color: #b7ebee;
  background: rgba(100, 217, 223, 0.07);
  border: 1px solid rgba(100, 217, 223, 0.18);
  border-radius: 14px;
}

.match-table-notice .icon {
  margin-top: 1px;
}

.match-table-notice strong,
.match-table-notice span {
  display: block;
}

.match-table-notice strong {
  color: var(--cream);
  font-family: Georgia, serif;
}

.match-table-notice span {
  margin-top: 4px;
  color: var(--cream-muted);
  font-size: 0.72rem;
  line-height: 1.5;
}

/* Rules */
.rules-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 310px;
  align-items: start;
  gap: 24px;
}

.rules-content {
  display: grid;
  gap: 16px;
}

.rule-block {
  padding: clamp(22px, 4vw, 34px);
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.rule-block h2 {
  margin-bottom: 12px;
  font-size: 1.65rem;
}

.rule-block p,
.rule-block li {
  color: var(--cream-muted);
  font-size: 0.9rem;
  line-height: 1.7;
}

.rule-block ol,
.rule-block ul {
  padding-left: 20px;
  margin-bottom: 0;
}

.turn-steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-top: 20px;
}

.turn-step {
  padding: 16px;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--line);
  border-radius: 13px;
}

.turn-step b {
  display: grid;
  width: 28px;
  height: 28px;
  place-items: center;
  margin-bottom: 13px;
  color: var(--ink);
  background: var(--gold);
  border-radius: 50%;
  font-family: Georgia, serif;
}

.turn-step strong {
  display: block;
  margin-bottom: 5px;
  font-family: Georgia, serif;
}

.turn-step span {
  color: var(--cream-muted);
  font-size: 0.75rem;
  line-height: 1.48;
}

.rules-aside {
  position: sticky;
  top: 100px;
}

.manual-card {
  padding: 22px;
  background: var(--cream);
  border-radius: var(--radius);
  color: var(--ink);
}

.manual-cover {
  min-height: 260px;
  display: grid;
  place-items: center;
  margin-bottom: 20px;
  overflow: hidden;
  background-image: linear-gradient(rgba(16, 14, 38, 0.14), rgba(16, 14, 38, 0.3)), url("assets/starpath-hero.png");
  background-position: center;
  background-size: cover;
  border-radius: 13px;
}

.manual-cover .brand-mark {
  width: 66px;
  height: 78px;
  flex: none;
  border-radius: 17px 17px 23px 23px;
  box-shadow: 0 16px 40px rgba(12, 10, 30, 0.34);
}

.manual-card h3 {
  margin-bottom: 6px;
  font-size: 1.3rem;
}

.manual-card p {
  color: #6c6670;
  font-size: 0.78rem;
  line-height: 1.5;
}

.manual-card .btn {
  width: 100%;
  color: var(--ink);
  border-color: rgba(18, 17, 38, 0.16);
}

.manual-credit {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid rgba(18, 17, 38, 0.1);
  font-size: 0.72rem;
}

/* Toasts, loader */
.toast-stack {
  position: fixed;
  z-index: 500;
  right: 20px;
  bottom: 20px;
  top: auto;
  display: flex;
  flex-direction: column-reverse;
  width: min(380px, calc(100vw - 40px));
  gap: 10px;
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  padding: 14px 16px;
  color: var(--cream);
  background: #29254e;
  border: 1px solid var(--line-strong);
  border-radius: 13px;
  box-shadow: 0 15px 40px rgba(0, 0, 0, 0.35);
  backdrop-filter: blur(18px);
  animation: toast-in 220ms ease both;
}

.toast.success .toast-dot {
  background: var(--mint);
}

.toast.error .toast-dot {
  background: var(--danger);
}

.toast-dot {
  width: 8px;
  height: 8px;
  flex: none;
  margin-top: 6px;
  background: var(--gold);
  border-radius: 50%;
  box-shadow: 0 0 12px currentColor;
}

.toast p {
  margin: 0;
  font-size: 0.8rem;
  line-height: 1.5;
}

#round-result-root {
  position: fixed;
  z-index: 95;
  top: 74px;
  left: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  transform: translateX(-50%);
  pointer-events: none;
}

.round-result-toast {
  position: relative;
  width: min(360px, calc(100vw - 32px));
  padding: 14px 18px 16px;
  overflow: hidden;
  color: var(--cream);
  background: linear-gradient(160deg, rgba(31, 27, 65, 0.97), rgba(16, 14, 39, 0.97));
  border: 1px solid rgba(255, 196, 61, 0.4);
  border-radius: 16px;
  box-shadow: 0 24px 60px rgba(3, 2, 16, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.03);
  animation: round-result-in 380ms cubic-bezier(0.22, 1.4, 0.36, 1) both;
}

.round-result-toast.leaving {
  animation: round-result-out 420ms ease both;
}

.round-result-glow {
  position: absolute;
  z-index: -1;
  inset: -40% -20%;
  background: radial-gradient(ellipse 60% 60% at 50% 0%, rgba(255, 196, 61, 0.22), transparent 70%);
  pointer-events: none;
}

.round-result-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}

.round-result-icon {
  display: grid;
  width: 30px;
  height: 30px;
  flex: none;
  place-items: center;
  color: #21170a;
  background: radial-gradient(circle at 36% 30%, #fff3b1, #ffc43d 58%, #9c6712);
  border-radius: 50%;
  font-size: 0.95rem;
}

.round-result-head strong {
  display: block;
  font-family: Georgia, serif;
  font-size: 1rem;
  line-height: 1.25;
}

.round-result-head span {
  color: var(--cream-muted);
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.round-result-list {
  display: grid;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.round-result-list li {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 9px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--line);
  border-radius: 10px;
}

.round-result-list li .battle-avatar {
  width: 26px;
  height: 26px;
  font-size: 0.6rem;
}

.round-result-name {
  flex: 1;
  overflow: hidden;
  font-size: 0.76rem;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.round-result-tag {
  color: var(--cream-muted);
  font-size: 0.58rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.round-result-list li.win .round-result-tag { color: #aaf1d9; }
.round-result-list li.loss .round-result-tag { color: #ffb3b3; }
.round-result-list li.tie .round-result-tag { color: #d8d0ff; }

.round-result-list li.win {
  background: rgba(89, 221, 172, 0.08);
  border-color: rgba(89, 221, 172, 0.35);
}

.round-result-spirit {
  color: var(--gold);
  font-size: 0.7rem;
  font-weight: 800;
  white-space: nowrap;
}

@keyframes round-result-in {
  from { opacity: 0; transform: translateY(-14px) scale(0.92); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes round-result-out {
  from { opacity: 1; transform: translateY(0) scale(1); }
  to { opacity: 0; transform: translateY(-10px) scale(0.96); }
}

@media (prefers-reduced-motion: reduce) {
  .round-result-toast,
  .round-result-toast.leaving {
    animation: none;
  }
}

/* Battle-reveal overlay: fighters/Supporters/power breakdown/winner, shown
   for the ~3s round freeze window. Staged purely with animation-delay (one
   value per element, computed once in JS) rather than a chain of timers. */
.battle-reveal {
  position: fixed;
  z-index: 150;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(6, 5, 20, 0.6);
  backdrop-filter: blur(3px);
  cursor: pointer;
  pointer-events: auto;
  animation: battle-reveal-backdrop-in 220ms ease both;
}

.battle-reveal.leaving {
  animation: battle-reveal-backdrop-out 260ms ease both;
}

.battle-reveal-card {
  position: relative;
  width: 720px;
  max-width: calc(100vw - 40px);
  max-height: min(80vh, 640px);
  display: flex;
  flex-direction: column;
  /* Was overflow-y:auto directly on this box, with .battle-reveal-glow (see
     below) as a plain child -- but that glow deliberately bleeds past this
     box's own edges (inset uses negative percentages, for a soft halo behind
     the header) and overflow-y:auto here made that decorative bleed part of
     this box's real scrollable area (Chromium computes overflow-x as auto
     too whenever only overflow-y is set to something other than visible),
     so EVERY battle reveal got both scrollbars, at every player count and
     viewport, from a purely cosmetic element that holds no content --
     confirmed via computed scrollWidth/scrollHeight vs clientWidth/
     clientHeight in Playwright, matching almost exactly the glow's own
     inset math, even at 2 players. Fix: keep the glow bleed but stop it
     from counting as overflow by clipping THIS box (overflow:hidden -- safe
     here, there's no real content in the glow to lose) and moving the
     actual scrolling to .battle-reveal-body, a plain sibling of the glow
     that holds everything else. */
  overflow: hidden;
  padding: 22px 24px 18px;
  color: var(--cream);
  cursor: default;
  background: linear-gradient(165deg, rgba(31, 27, 65, 0.98), rgba(14, 12, 34, 0.98));
  border: 1px solid rgba(255, 196, 61, 0.35);
  border-radius: 20px;
  box-shadow: 0 30px 80px rgba(3, 2, 16, 0.55);
  animation: battle-reveal-card-in 320ms cubic-bezier(0.22, 1.3, 0.36, 1) both;
}

.battle-reveal-glow {
  position: absolute;
  z-index: -1;
  inset: -30% -10%;
  background: radial-gradient(ellipse 60% 50% at 50% 0%, rgba(255, 196, 61, 0.2), transparent 70%);
  pointer-events: none;
}

.battle-reveal-body {
  /* min-height:0 is load-bearing: a flex item's default min-height is auto
     (its own content size), which would stop this from ever shrinking
     below its content and defeat the scroll this exists for -- the exact
     flex/min-size gotcha .battle-reveal-card itself used to run into via
     .battle-reveal-grid before this split. */
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}

.battle-reveal-head {
  margin-bottom: 16px;
  text-align: center;
}

.battle-reveal-head .eyebrow {
  display: block;
  margin-bottom: 4px;
}

.battle-reveal-head h3 {
  margin: 0;
  font-family: Georgia, serif;
  font-size: 1.5rem;
  letter-spacing: 0.02em;
}

.battle-reveal-grid {
  display: grid;
  /* min(120px, 100%) instead of a bare 120px: a plain minmax(120px, 1fr)
     floor doesn't shrink below 120px per column even when the container
     can't fit that many -- auto-fit only grows tracks into free space, it
     doesn't let min demands go negative, so 6 unshrinkable 120px+gap columns
     (needs ~792px) inside this card's ~672px content width would overflow
     it outright. Wrapping the floor in min(...,100%) lets a single track
     shrink to fill 100% of the row when there isn't room for a neighbour,
     so auto-fit wraps to more rows instead of overflowing -- this is what
     actually keeps 5-6 players (with Supporters, at any viewport) inside
     the card without cropping anyone. */
  grid-template-columns: repeat(auto-fit, minmax(min(120px, 100%), 1fr));
  gap: 12px;
  margin-bottom: 16px;
}

.battle-reveal-fighter {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 12px 8px 14px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--line);
  border-radius: 14px;
  opacity: 0;
  animation: battle-reveal-rise-in 420ms cubic-bezier(0.22, 1.2, 0.36, 1) both;
  animation-delay: var(--stage-delay, 0ms);
}

.battle-reveal-fighter.win {
  background: rgba(255, 196, 61, 0.08);
  border-color: rgba(255, 196, 61, 0.55);
  box-shadow: 0 0 0 1px rgba(255, 196, 61, 0.2), 0 14px 30px rgba(255, 196, 61, 0.08);
}

.battle-reveal-name {
  display: flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  font-size: 0.76rem;
  font-weight: 700;
}

.battle-reveal-name span:last-child {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.battle-reveal-name .battle-avatar {
  flex: none;
  width: 22px;
  height: 22px;
  font-size: 0.55rem;
}

.battle-reveal-cards {
  display: flex;
  align-items: flex-end;
  gap: 6px;
}

.battle-reveal-art {
  width: 54px;
  border-radius: 8px;
  box-shadow: 0 8px 18px rgba(3, 2, 16, 0.4);
}

.battle-reveal-art.supporter {
  width: 36px;
  opacity: 0.92;
  transform: translateY(6px);
}

.battle-reveal-empty {
  display: grid;
  width: 36px;
  height: 50px;
  place-items: center;
  padding: 2px;
  color: var(--cream-muted);
  background: rgba(255, 255, 255, 0.03);
  border: 1px dashed var(--line);
  border-radius: 8px;
  font-size: 0.48rem;
  line-height: 1.2;
  text-align: center;
}

.battle-reveal-power {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-height: 30px;
}

.battle-reveal-power .fighter-power-badge {
  position: static;
}

.battle-reveal-power .power-modifiers {
  position: static;
  flex-wrap: wrap;
  transform: none;
}

.battle-reveal-outcome {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
  text-align: center;
  opacity: 0;
  animation: battle-reveal-rise-in 420ms cubic-bezier(0.22, 1.2, 0.36, 1) both;
  animation-delay: var(--stage-delay, 0ms);
}

.battle-reveal-outcome strong {
  font-family: Georgia, serif;
  font-size: 1.1rem;
}

.battle-reveal-skip {
  display: block;
  margin-top: 12px;
  color: var(--cream-muted);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-align: center;
  text-transform: uppercase;
  opacity: 0.55;
}

@keyframes battle-reveal-backdrop-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes battle-reveal-backdrop-out {
  from { opacity: 1; }
  to { opacity: 0; }
}

@keyframes battle-reveal-card-in {
  from { opacity: 0; transform: translateY(16px) scale(0.94); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes battle-reveal-rise-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

@media (max-width: 680px) {
  .battle-reveal-card { padding: 18px 14px 16px; border-radius: 16px; }
  .battle-reveal-head h3 { font-size: 1.25rem; }
  .battle-reveal-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .battle-reveal-art { width: 46px; }
  .battle-reveal-art.supporter { width: 30px; }
}

@media (prefers-reduced-motion: reduce) {
  .battle-reveal,
  .battle-reveal.leaving,
  .battle-reveal-card,
  .battle-reveal-fighter,
  .battle-reveal-outcome {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}

.loading-screen {
  min-height: 100vh;
  display: grid;
  place-items: center;
}

.loader {
  position: relative;
  width: 68px;
  height: 68px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 50%;
  animation: slow-spin 1.4s linear infinite;
}

.loader::before {
  position: absolute;
  top: -4px;
  left: 28px;
  width: 10px;
  height: 10px;
  content: "";
  background: var(--gold);
  border-radius: 50%;
  box-shadow: 0 0 18px var(--gold);
}

.skeleton {
  min-height: 180px;
  background: linear-gradient(100deg, rgba(255, 255, 255, 0.025) 25%, rgba(255, 255, 255, 0.07) 45%, rgba(255, 255, 255, 0.025) 65%);
  background-size: 250% 100%;
  border-radius: var(--radius);
  animation: shimmer 1.6s infinite linear;
}

@keyframes fade-in {
  from { opacity: 0; }
}

@keyframes modal-in {
  from { opacity: 0; transform: translateY(12px) scale(0.985); }
}

@keyframes page-in {
  from { opacity: 0; transform: translateY(7px); }
}

@keyframes toast-in {
  from { opacity: 0; transform: translateX(12px); }
}

@keyframes slow-spin {
  to { transform: rotate(360deg); }
}

@keyframes deck-glow-spin {
  from { transform: translate(-50%, -50%) rotate(0deg); }
  to { transform: translate(-50%, -50%) rotate(360deg); }
}

@keyframes constellation-drift {
  to { transform: translate3d(-2600px, 0, 0); }
}

@keyframes shimmer {
  to { background-position: -150% 0; }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

@media (max-width: 1100px) {
  .path-grid {
    grid-template-columns: repeat(3, 1fr);
  }

  .path-card {
    min-height: 260px;
  }

  .path-symbol {
    margin: 20px auto 35px;
  }

  .card-slots {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  .card-choice-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (max-width: 900px) {
  :root {
    --page: min(100% - 30px, 760px);
  }

  .landing-nav {
    grid-template-columns: 1fr auto;
  }

  .landing-links {
    display: none;
  }

  .landing-hero {
    min-height: 700px;
  }

  .landing-hero::after {
    background: linear-gradient(90deg, rgba(15, 13, 34, 0.96), rgba(15, 13, 34, 0.58) 72%, rgba(15, 13, 34, 0.25)), linear-gradient(0deg, var(--ink), transparent 34%, rgba(10, 8, 28, 0.4));
  }

  .feature-strip {
    grid-template-columns: 1fr;
  }

  .feature-item {
    min-height: 210px;
  }

  .feature-item + .feature-item {
    border-top: 1px solid var(--line-strong);
    border-left: 0;
  }

  .feature-number {
    margin-bottom: 34px;
  }

  .game-story {
    grid-template-columns: 1fr;
  }

  .story-image {
    min-height: 390px;
  }

  .app-shell {
    display: block;
  }

  .sidebar {
    display: none;
  }

  .app-main {
    padding: 0 20px 100px;
  }

  .app-topbar {
    min-height: 74px;
    margin: 0 -2px;
  }

  .app-topbar .brand {
    display: inline-flex;
  }

  .app-topbar .brand-word {
    display: none;
  }

  .app-topbar .brand-mark {
    width: 39px;
    height: 39px;
  }

  .topbar-title {
    flex: 1;
  }

  .mobile-menu-btn {
    display: inline-grid;
  }

  .mobile-logout {
    display: inline-grid;
  }

  .mobile-nav {
    position: fixed;
    z-index: 80;
    right: 12px;
    bottom: 12px;
    left: 12px;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    padding: 7px;
    background: rgba(24, 22, 49, 0.92);
    border: 1px solid var(--line-strong);
    border-radius: 20px;
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.42);
    backdrop-filter: blur(20px);
  }

  .toast-stack {
    top: auto;
    right: 12px;
    /* Clears the fixed mobile bottom nav bar (hidden only in match-mode). */
    bottom: 92px;
    width: min(380px, calc(100vw - 24px));
  }

  .mobile-nav .nav-item {
    min-height: 52px;
    flex-direction: column;
    justify-content: center;
    gap: 3px;
    padding: 4px;
    border-radius: 14px;
    font-size: 0.61rem;
  }

  .mobile-nav .nav-item .icon {
    width: 18px;
    height: 18px;
  }

  .nav-badge {
    display: none;
  }

  .dashboard-grid {
    grid-template-columns: 1fr;
  }

  .deck-grid,
  .room-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .rules-layout {
    grid-template-columns: 1fr;
  }

  .rules-aside {
    position: static;
  }

  .manual-card {
    display: grid;
    grid-template-columns: 180px 1fr;
    gap: 20px;
  }

  .manual-cover {
    min-height: 230px;
    margin-bottom: 0;
  }
}

@media (max-width: 680px) {
  :root {
    --page: calc(100% - 24px);
    --radius-lg: 24px;
  }

  .landing-nav {
    display: flex;
    min-width: 0;
    align-items: center;
    justify-content: space-between;
    height: 74px;
  }

  .landing-actions {
    flex: 0 0 auto;
    margin-left: auto;
  }

  .language-selector-landing {
    position: absolute;
    top: 17px;
    right: 0;
    z-index: 2;
    display: inline-flex;
  }

  .landing-nav .brand-word {
    font-size: 1.3rem;
  }

  .landing-actions .btn-ghost {
    display: none;
  }

  .landing-actions .btn-primary {
    display: none;
  }

  .landing-actions .btn {
    min-height: 40px;
    padding-inline: 14px;
    font-size: 0.78rem;
  }

  .landing-hero {
    min-height: 710px;
    align-items: flex-end;
  }

  .landing-hero::before {
    background-position: 61% center;
  }

  .landing-hero::after {
    background: linear-gradient(0deg, var(--ink) 0%, rgba(15, 13, 34, 0.87) 48%, rgba(15, 13, 34, 0.18) 85%, rgba(10, 8, 28, 0.5));
  }

  .hero-content {
    padding: 130px 0 72px;
  }

  .hero-copy {
    width: calc(100vw - 40px);
    max-width: 100%;
  }

  .hero-title {
    max-width: 100%;
    font-size: clamp(3.05rem, 15vw, 4.5rem);
    line-height: 0.88;
    overflow-wrap: anywhere;
    hyphens: auto;
    text-wrap: wrap;
  }

  html[lang="pt-BR"] .hero-title {
    font-size: clamp(2.65rem, 12vw, 3.8rem);
  }

  html[lang="de"] .hero-title {
    font-size: clamp(2.3rem, 10vw, 3.15rem);
  }

  .hero-lead {
    font-size: 0.96rem;
    line-height: 1.58;
    overflow-wrap: anywhere;
  }

  .hero-actions .btn {
    width: 100%;
    flex: none;
    padding-inline: 14px;
  }

  .hero-actions {
    display: grid;
    grid-template-columns: 1fr;
    width: 100%;
  }

  .hero-proof {
    align-items: flex-start;
    flex-wrap: wrap;
    gap: 9px 13px;
    font-size: 0.72rem;
  }

  .hero-proof .proof-rule {
    width: 22px;
    margin-top: 7px;
  }

  .section {
    padding: 75px 0;
  }

  .section-heading {
    display: block;
    margin-bottom: 30px;
  }

  .section-heading h2 {
    margin-bottom: 16px;
  }

  .feature-item {
    padding: 28px 22px;
  }

  .path-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 9px;
  }

  .path-card {
    min-height: 220px;
    padding: 15px;
  }

  .path-symbol {
    width: 74px;
    height: 74px;
    margin: 15px auto 29px;
    font-size: 2.8rem;
  }

  .story-image {
    min-height: 320px;
  }

  .story-badge {
    width: 90px;
    height: 90px;
    font-size: 0.9rem;
  }

  .landing-cta {
    min-height: 390px;
    margin-bottom: 65px;
  }

  .landing-footer {
    align-items: flex-start;
    flex-direction: column;
  }

  .modal {
    padding: 28px 20px 22px;
    border-radius: 22px;
  }

  .form-grid.two-col {
    grid-template-columns: 1fr;
  }

  .app-main {
    padding-inline: 13px;
  }

  .topbar-title strong {
    font-size: 1.02rem;
  }

  .topbar-actions .btn {
    min-width: 42px;
    padding: 0;
  }

  .topbar-actions .btn-label {
    display: none;
  }

  .page-head {
    align-items: flex-start;
    flex-direction: column;
    margin-top: 15px;
  }

  .page-actions {
    width: 100%;
  }

  .page-actions .btn {
    flex: 1;
  }

  .dashboard-hero {
    min-height: 460px;
    padding: 25px 21px;
  }

  .dashboard-hero::before {
    background-position: 58% center;
  }

  .dashboard-hero::after {
    background: linear-gradient(0deg, rgba(17, 15, 39, 0.98), rgba(17, 15, 39, 0.63) 72%, rgba(17, 15, 39, 0.25));
  }

  .dashboard-hero .hero-actions {
    display: grid;
  }

  .stat-row,
  .quick-decks {
    grid-template-columns: 1fr;
  }

  .stat {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .stat strong {
    margin-bottom: 0;
  }

  .quick-decks {
    grid-template-columns: repeat(3, 1fr);
  }

  .mini-deck {
    min-height: 130px;
    padding: 12px;
  }

  .mini-deck strong {
    margin-top: 48px;
    font-size: 0.83rem;
  }

  .mini-deck span {
    display: none;
  }

  .deck-grid,
  .room-grid {
    grid-template-columns: 1fr;
  }

  .deck-card {
    min-height: 290px;
  }

  .deck-cover {
    height: 185px;
  }

  .deck-toolbar,
  .rooms-toolbar {
    align-items: stretch;
    flex-direction: column;
  }

  .filter-chips {
    overflow-x: auto;
    padding-bottom: 3px;
  }

  .deck-editor-head {
    align-items: flex-start;
    flex-wrap: wrap;
  }

  .deck-title-wrap {
    width: calc(100% - 60px);
  }

  .deck-editor-actions {
    width: 100%;
  }

  .deck-editor-actions .btn {
    flex: 1;
  }

  .card-slots {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
  }

  .game-card {
    min-height: 235px;
  }

  .card-choice-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .choice-card .game-card {
    min-height: 0;
  }

  .card-preview-modal {
    padding: 54px 18px 20px;
  }

  .card-preview-figure {
    display: block;
  }

  .card-preview-figure img {
    width: min(100%, 390px);
    max-height: 62vh;
    margin: 0 auto;
  }

  .card-preview-figure figcaption {
    padding: 20px 2px 0;
  }

  .turn-steps {
    grid-template-columns: 1fr;
  }

  .manual-card {
    display: block;
  }

  .manual-cover {
    margin-bottom: 20px;
  }

  .versus-grid {
    grid-template-columns: 1fr;
  }

  .versus {
    transform: rotate(90deg);
  }

  .lobby-head {
    margin-bottom: 30px;
  }

  .lobby-actions {
    flex-direction: column-reverse;
  }

  .lobby-actions .btn {
    width: 100%;
  }

  .lobby-player-grid {
    grid-template-columns: 1fr 1fr;
  }

  .turn-player {
    grid-template-columns: 26px 42px minmax(0, 1fr);
  }

  .starter-badge {
    grid-column: 2 / -1;
    justify-self: start;
  }
}

@media (max-width: 380px) {
  .path-grid {
    grid-template-columns: 1fr;
  }

  .path-card {
    min-height: 200px;
  }

  .path-symbol {
    margin-bottom: 20px;
  }

  .card-slots,
  .card-choice-grid {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 680px) {
  .match-table-shell {
    border-radius: 20px;
  }

  .stellar-table {
    min-height: 600px;
    padding: 13px;
  }

  /* Replaces the old horizontal-scroll strip: a flat 2-column grid for every
     seat count from 3 up (2p keeps its own single-column stack below,
     unchanged). Rows wrap naturally instead of scrolling sideways to find a
     seat -- the [data-count] attribute selector matches the same specificity
     as the .game-players[data-count="N"] column-count rules above (an
     attribute selector, regardless of its value), so this actually overrides
     them instead of losing to them despite coming later in the file -- a
     plain .game-players class selector here would NOT win against those. */
  .game-players[data-count] {
    /* Referenced by the odd-count centring rule below via calc(), so both
       rules move together if this ever changes instead of drifting apart. */
    --mobile-grid-gap: clamp(8px, 2.5vw, 14px);
    grid-auto-flow: row;
    grid-template-columns: repeat(2, 1fr);
    grid-auto-rows: max-content;
    justify-content: center;
    justify-items: stretch;
    gap: var(--mobile-grid-gap);
    padding: 6px 10px 10px;
    overflow-x: hidden;
    /* .stellar-table/.game-players keep their existing overflow-y:auto
       (see the universal match-mode tier below) as the fallback for however
       many rows 3-6 players need -- deliberately internal/table scroll, not
       a page-level one, same mechanism the old strip and the 2p stack both
       already relied on. */
  }

  /* align-content on its own: .match-mode .game-players (the universal tier,
     further down the file) sets align-content:center at equal specificity
     but wins by source order regardless of media query -- ".match-mode"
     prefixed here on top of the same [data-count] selector adds the one
     point needed to actually override it for this tier specifically. Rows
     that wrap (3-6p) should stack from the top down, not centre as a block
     -- centring here would push a 2-row grid oddly low and, worse, "safe
     centring" behaviour for overflowing content isn't guaranteed identically
     across browsers, so this doesn't rely on it. */
  .match-mode .game-players[data-count] {
    align-content: start;
  }

  /* Odd seat counts leave one seat alone in the final row -- span it across
     both columns and centre it there, matching how the desktop 3+2/5-count
     tiers already centre a lone trailing seat instead of pinning it to a
     column. nth-child(N) targets exactly the Nth (and only the Nth) seat for
     that specific count, which is always the trailing one -- a seat-
     count-agnostic :last-child rule would incorrectly also match the last
     seat of an EVEN count, which should stay in its own column. The local
     player is always rendered last (see renderMatchSetup), so for 3p/5p this
     is also always the local player -- centred in the bottom row, per the
     local-seat placement rule documented in app.js's isBottomTableRow. */
  .match-mode .game-players[data-count="3"] .mat-seat.mat-seat:nth-child(3),
  .match-mode .game-players[data-count="5"] .mat-seat.mat-seat:nth-child(5) {
    grid-column: 1 / -1;
    justify-self: center;
    /* Spans both columns, so a bare 100% (see the general rule just below)
       would stretch it to double width -- this recovers the same width a
       normal single-column seat gets by subtracting the one gap the spanned
       cell doesn't have to leave room for. */
    width: calc((100% - var(--mobile-grid-gap)) / 2);
  }

  /* [data-count] again (not a plain descendant selector) to match the
     specificity of the desktop tier's [data-count="N"] .mat-seat rules --
     but this block comes BEFORE that tier in the file (it's part of the
     680px block near the top; the per-count --mat-size overrides are much
     further down), so equal specificity would still lose to source order.
     ".mat-seat.mat-seat" repeats the class on purpose to add the one extra
     point of specificity needed to actually win here. Without it --mat-size
     stayed at its desktop value (150-210px) even though the seat itself was
     correctly narrowed to ~96-150px, and .battle-player-board (which reads
     --mat-size for its own width) blew out past its seat and bled into the
     next mat in the grid -- verified via computed-style diff in Playwright,
     not just visually. Setting it to 100% makes every --mat-size consumer
     size off its actual rendered parent (its own grid cell) instead of a
     stale fixed value -- so it's already exactly half the available width,
     no vw/vh math to duplicate here. max-width is just a ceiling for the
     wide end of this tier (a 680px-wide phone/small tablet), not something
     that's expected to bind on an actual phone. */
  .match-mode .game-players[data-count] .mat-seat.mat-seat {
    --mat-size: 100%;
    width: 100%;
    max-width: 300px;
  }

  /* 2 players specifically: stack top-over-bottom instead of the horizontal
     scroll strip above. A phone in portrait almost always has more free
     height than width -- two mats side by side just made both of them
     smaller than they needed to be, for no real benefit, and the strip's
     horizontal scroll wasn't needed anyway with only two seats to show. Both
     [data-count="2"] selectors below are exactly as specific as the
     [data-count] ones just above (an attribute selector either way) and
     come later in source order, so they win for this count without needing
     the extra specificity trick. */
  .game-players[data-count="2"] {
    grid-auto-flow: row;
    grid-template-columns: none;
    grid-auto-rows: minmax(0, 1fr);
    justify-content: center;
    justify-items: center;
    overflow-x: visible;
    overflow-y: auto;
  }

  .match-mode .game-players[data-count="2"] .mat-seat.mat-seat {
    --mat-size: min(48vw, 42vh, 230px);
    width: var(--mat-size);
    max-width: none;
    scroll-snap-align: none;
  }

  .battle-player-board.compact {
    padding: 12px;
  }

  .battle-player-board.compact .zone-card {
    width: min(100%, 82px);
    min-height: 0;
  }

  .match-table-notice {
    padding: 13px;
  }

  /* The dock stacks (action card above hand, see its own 680px rule) at this
     width, measured ~246-250px tall regardless of viewport height -- mostly
     fixed padding/card minimums, not something that scales with vh. The
     desktop tier's clamp(200px,20vh,230px) floor undershoots that on short
     phones, which meant the hand dock could actually overlap the bottom mat
     row (verified via bounding-box diff in Playwright, not just eyeballed).
     260px clears it with a small margin at the shortest tested height.
     ".stellar-table.stellar-table": the always-on base tier sets padding on
     this same selector at equal specificity but appears LATER in the file,
     so it would otherwise win over this 680px override regardless of media
     query -- same doubled-selector trick as .mat-seat.mat-seat above. */
  .match-mode .stellar-table.stellar-table {
    padding: 6px 10px clamp(260px, 30vh, 300px);
  }

  .match-mode .game-players {
    padding-block: 6px;
  }
}

@media (max-width: 430px) {
  .player-coin { width: 34px; height: 34px; }
  .coin-face b { font-size: 0.86rem; }

  .battle-zones {
    gap: 5px;
  }

  .zone-label {
    font-size: 0.48rem;
    letter-spacing: 0.035em;
  }

  .battle-player-board.compact .zone-card {
    width: min(100%, 72px);
    min-height: 0;
  }

  .table-card-stack {
    width: min(58%, 64px);
  }

  .zone-card > small {
    font-size: 0.43rem;
  }

  .match-table-notice span {
    font-size: 0.66rem;
  }
}

/* Battle view: keep the complete table and the local hand within one
   viewport at any size, no page scroll, while preserving the physical
   playmat hierarchy. Narrower/shorter viewports get further compaction
   below via dedicated breakpoints. */
  .app-shell.match-mode {
    height: 100dvh;
    overflow: hidden;
  }

  .match-mode .match-table-page {
    animation: none;
  }

  .match-mode .match-table-shell {
    display: flex;
    height: 100dvh;
    flex-direction: column;
  }

  /* Every seat gets one fixed size for the whole match -- no more grid rows
     or --mat-size values keyed to who is currently acting. The hand is a
     position:fixed dock outside this flow (see .floating-hand-dock), so
     bottom padding (not a row) reserves it its own clearance, leaving the
     rest of .stellar-table's height budget for the player grid to centre in. */
  .match-mode .stellar-table {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: 0;
    flex: 1;
    padding: 10px 18px clamp(200px, 20vh, 230px);
    overflow: hidden;
  }

  .match-mode .game-players {
    flex: 1;
    min-height: 0;
    align-content: center;
    overflow-y: auto;
  }

  /* --mat-size drives the playmat's WIDTH (it is square via aspect-ratio on
     the base .battle-player-board rule); .mat-seat/.player-info just inherit
     the same value so all three agree on one width with no separate override
     needed anywhere -- and, unlike the old spotlight system, no transition is
     needed since this value no longer changes while a match is running.

     Graduated by player count (not one flat size for everyone) so a 2-player
     table actually uses the room a 6-player table doesn't have: fewer seats
     means each one can claim more of the grid. The 2/4/5/6-count values use
     min(vw, vh) because those layouts stack 2 rows, so height is as much of
     a constraint as width; 3-count is a single row, so width alone bounds it.

     For realistic desktop aspect ratios vw is essentially always the larger
     of the two, so vh is what actually binds inside min() here -- which
     means the whole board's size tracks *content* viewport height, i.e.
     whatever a given browser's own chrome (tab bar, address bar, bookmark
     bar) leaves after being subtracted from the window. That's what made the
     table look meaningfully smaller in a browser with taller chrome (e.g.
     Opera) than in one with less, at the same physical window size, and only
     "right" once F11 removed the chrome difference entirely -- verified
     empirically (900px vs 768px content height for the same window produced
     a 261px vs 223px mat here).

     Fix: raise the floor and trim the vh coefficient together so the floor
     -- not the vh term -- is what actually governs through the realistic
     "windowed browser, no F11" range (roughly 761-800px content height:
     28vh doesn't clear a 224px floor until h~800). Scaling the coefficient
     alone (tried first) does NOT help here: for a pure `coef * h` term,
     size(900)/size(768) always equals 900/768 no matter what coef is, so
     every browser-chrome height still produces a proportionally different
     board. A floor is the only thing that can make two different heights
     resolve to the *same* size -- confirmed empirically: 761/768/800px all
     now render the identical 224px 2-count mat, where the old 29vh formula
     gave three visibly different sizes (221/223/232px) for those same three
     heights.

     The floor can't be pushed much higher than this without overflowing
     .game-players' own vertical budget (contentHeight minus .stellar-table's
     own padding, including the flat 200px floor it reserves below for the
     hand dock, see clamp(200px,20vh,230px) below) -- confirmed by measuring
     .game-players' scrollHeight vs its clientHeight at each height while
     tuning these values; a higher floor was overflowing that budget at the
     760px handoff. Freeing more headroom would mean shrinking the hand-dock
     reservation itself, which was already tuned once before specifically to
     avoid a hand/mat overlap bug (see the 680px breakpoint's own comment on
     that) -- left alone here rather than risk reintroducing it. So above
     ~800-850px (still well short of needing fullscreen) it resumes scaling
     with vh up to the ceiling. */
  .match-mode .game-players[data-count="2"] .mat-seat { --mat-size: clamp(224px, min(29vw, 28vh), 340px); }
  .match-mode .game-players[data-count="3"] .mat-seat { --mat-size: clamp(210px, 25vw, 380px); }
  .match-mode .game-players[data-count="4"] .mat-seat { --mat-size: clamp(196px, min(21vw, 26vh), 280px); }
  .match-mode .game-players[data-count="5"] .mat-seat { --mat-size: clamp(172px, min(17vw, 23vh), 240px); }
  .match-mode .game-players[data-count="6"] .mat-seat { --mat-size: clamp(161px, min(15vw, 21vh), 210px); }

  .match-mode .battle-player-board.compact {
    display: flex;
    flex-direction: column;
    padding: 8px;
    border-radius: 14px;
  }

  /* Cross layout, not a single row: fighter/supporter stacked in the centre
     column, discard/draw as tall flanks either side -- this is what makes a
     square mat read as a real playmat instead of a stretched toolbar. Every
     seat uses it now, local player included, since every mat is this size.
     Explicit equal row heights (not auto) so fighter and supporter each get
     exactly half the mat's height to work with -- auto rows just centred the
     (undersized) card in whatever leftover space existed, which is what was
     reading as "space between things", not the cards being wrong. The centre
     column is wider than before: Combatente/Suporte no longer reserve a
     caption line above the card (see renderBattleZone's showLabel), so that
     freed width goes to making those two cards themselves bigger instead of
     sitting on unused headroom. */
  .match-mode .compact .battle-zones {
    min-height: 0;
    flex: 1;
    grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.3fr) minmax(0, 0.82fr);
    grid-template-rows: 1fr 1fr;
    align-items: center;
    gap: 4px 6px;
  }

  .match-mode .compact .zone-label {
    margin-bottom: 2px;
    font-size: clamp(0.4rem, 3.4cqw, 0.6rem);
    letter-spacing: 0.025em;
  }

  /* The slot itself fills its grid column (not a value derived from the
     whole seat's width via cqw, which had no relationship to how wide that
     column actually was -- that mismatch was the root cause of both
     overflowing and undersized-looking slots). aspect-ratio does the rest:
     height always follows from the column's own width, so every slot keeps
     real card proportions at any mat size. */
  .match-mode .compact .zone-card { width: 100%; border-radius: 6px; }
  .match-mode .compact .zone-card > small { display: none; }
  .match-mode .compact .empty-zone-glyph { font-size: clamp(1rem, 7cqw, 1.8rem); }
  .match-mode .compact .zone-count { width: clamp(18px, 6cqw, 27px); height: clamp(18px, 6cqw, 27px); font-size: clamp(0.58rem, 2.2cqw, 0.76rem); }

  /* backdrop-filter is dropped on the whole table during a match: up to six
     blurring boxes each animating size was the dominant cost behind choppy
     drag/hover (measured via a Chrome trace). Mats sit on an opaque
     starfield, so the blur was buying almost nothing. */
  .match-mode .battle-player-board {
    backdrop-filter: none;
  }

  .match-mode .match-hand-cards { gap: 8px; padding: 4px 4px 2px; }
  .match-mode .match-hand-card { min-width: 78px; max-width: 108px; flex: 0 1 108px; border-radius: 9px; }
  .match-mode .match-hand-card > span { grid-template-columns: 19px minmax(0, 1fr) 14px; padding: 18px 5px 4px; }
  .match-mode .match-hand-card b { width: 18px; height: 18px; font-size: 0.55rem; }
  .match-mode .match-hand-card strong { font-size: 0.58rem; }
  .match-mode .match-hand-card .icon { width: 12px; height: 12px; }
  .match-mode .match-table-notice { display: none; }

/* Short viewports (landscape phones, resized short windows): the header and
   mat size have to shrink hard so the player grid keeps its full budget. */
@media (max-height: 760px) {
  .match-mode .eyebrow {
    display: none;
  }

  .match-side-rail .rail-pill {
    height: 30px;
    padding: 0 8px;
    font-size: 0.6rem;
    clip-path: inset(0 0 0 calc(100% - 30px) round 999px);
  }

  .match-side-rail .rail-icon {
    width: 16px;
    height: 16px;
  }

  .match-mode .stellar-table {
    padding: 4px 10px clamp(130px, 20vh, 155px);
  }

  .match-mode .floating-hand-dock {
    gap: 2px;
    padding: 2px 10px 0;
  }

  .match-mode .game-players {
    padding-block: 2px;
  }

  /* Same graduated-by-count idea as the base tier, just conservative --
     there's very little vertical budget in a short viewport to spare.
     [data-count] (any value) is a catch-all for 5/6 -- it matches the SAME
     specificity as the base tier's [data-count="5"]/[data-count="6"] rules,
     but comes later in the file, so it wins over them here; the explicit
     count="2"/"3"/"4" rules below it are equally specific and come later
     still, so THEY win over this catch-all for their own counts. Without
     this, 5/6-player mats fell through to the much bigger base-tier size
     and overflowed the short viewport (verified via Playwright). */
  .match-mode .mat-seat { --mat-size: clamp(70px, 11vh, 100px); }
  .match-mode .game-players[data-count] .mat-seat { --mat-size: clamp(66px, 10vh, 96px); }
  .match-mode .game-players[data-count="2"] .mat-seat { --mat-size: clamp(90px, 16vh, 150px); }
  .match-mode .game-players[data-count="3"] .mat-seat { --mat-size: clamp(80px, 13vh, 120px); }
  .match-mode .game-players[data-count="4"] .mat-seat { --mat-size: clamp(76px, 12vh, 110px); }

  .match-mode .compact .zone-card { width: 100%; }

  /* Too small a square for a legible caption above each of its four zones --
     that's exactly what tapping the mat to open the close-up view is for. */
  .match-mode .compact .zone-label {
    display: none;
  }

  .match-mode .battle-player-board.local,
  .match-mode .battle-player-board.compact {
    padding: 6px;
  }

  .match-mode .action-card {
    padding: 4px 8px;
  }

  .match-mode .turn-action-copy strong {
    font-size: 0.66rem;
  }

  .match-mode .turn-action-copy span {
    display: none;
  }

  /* Short viewports (landscape phones) can't fit a full 44px comfortably,
     but 24px was well below any reasonable touch target -- 38px is the best
     compromise against the available vertical budget here. */
  .match-mode .turn-action-buttons .btn {
    min-height: 38px;
    padding: 6px 12px;
    font-size: 0.62rem;
  }

  .match-mode .match-hand-card-wrap {
    min-width: 44px;
    max-width: 64px;
    flex: 0 1 64px;
  }

  .match-mode .match-hand-card > span {
    padding: 10px 4px 3px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .battle-player-board.active-turn { animation: none; }
  .player-info-turn { animation: none; }
}

/* Interactive match controls ------------------------------------------------ */
/* Zone LAYOUT (not card rotation) is what says "facing the opponent": Fighter
   always sits in the row closer to the table centre. .row-top/.row-bottom
   (set in renderBattlePlayer from the seat's actual grid row -- see
   isBottomTableRow in app.js, which mirrors the .game-players[data-count]
   column counts below) drive this, NOT "is this the local player" -- a
   bottom-row seat is always closer to the centre on its TOP edge (nothing
   below it), a top-row seat is closer to the centre on its BOTTOM edge
   (nothing above it), regardless of who ends up sitting there. Card art
   itself always stays upright; only which zone sits where changes. */
.battle-player-board.compact.row-bottom .battle-zones {
  grid-template-areas:
    "discard fighter draw"
    "discard supporter draw";
}

.battle-player-board.compact.row-top .battle-zones {
  grid-template-areas:
    "discard supporter draw"
    "discard fighter draw";
}

/* The "action card" beside the hand: round + phase up top, then whatever the
   player needs to see/do right now (see renderActionCard in app.js, which
   reuses the same phase/pending-choice/available-actions branching the old
   full-width turn-action-bar had -- only this wrapper and its layout are
   new). Sized/proportioned closer to a playing card than a toolbar. */
.action-card {
  position: relative;
  z-index: 6;
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 8px;
  padding: 10px 12px;
  color: var(--cream);
  background:
    radial-gradient(circle at 12% 0, rgba(255, 196, 61, 0.13), transparent 42%),
    rgba(9, 8, 27, 0.82);
  border: 1px solid rgba(255, 196, 61, 0.22);
  border-radius: 13px;
  box-shadow: inset 0 1px rgba(255, 255, 255, 0.045), 0 9px 24px rgba(3, 2, 16, 0.2);
  backdrop-filter: blur(14px);
}

.action-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  padding-bottom: 7px;
  border-bottom: 1px solid rgba(255, 196, 61, 0.18);
}

.action-card-head span {
  overflow: hidden;
  color: var(--cream-muted);
  font-size: 0.56rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
}

.action-card-head strong {
  overflow: hidden;
  color: var(--gold);
  font-family: Georgia, serif;
  font-size: 0.78rem;
  text-align: right;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.turn-action-copy {
  display: grid;
  min-width: 0;
  gap: 2px;
}

.turn-action-copy strong,
.turn-action-copy span {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
}

.turn-action-copy strong {
  color: var(--cream);
  font-family: Georgia, serif;
  font-size: 0.84rem;
  white-space: nowrap;
}

.turn-action-copy span {
  color: var(--cream-muted);
  font-size: 0.68rem;
  line-height: 1.35;
  white-space: normal;
}

.turn-action-buttons {
  display: flex;
  flex: none;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: 7px;
}

/* >=44px min-height/touch-target on purpose (Passar/Faísca are the two most-
   clicked buttons in the whole match). btn-primary/btn-secondary (see
   renderActionCard) already carries the primary/secondary visual weight; this
   only controls size, common to both. */
.turn-action-buttons .btn {
  min-height: 44px;
  padding: 10px 16px;
  font-size: 0.78rem;
}

/* Fan geometry scales with hand size (--card-total, set inline per card by
   renderFloatingHand) so a crowded hand compacts instead of running off the
   viewport. Card width and overlap are solved together from one target: N
   cards of width --fan-w, each overlapping the last by a fixed 66% of that
   width, should span roughly --fan-budget (a proxy for the space actually
   available -- the same min(900px,70vw) shape .floating-hand's own
   max-width uses). Solving width + (n-1)*width*0.34 = budget for width
   gives --fan-tight-w below; margin-left is then just -width*0.66 (the
   overlap that produces that same 0.34 "visible sliver" pitch).
   margin-left/width both take min(that, the OLD flat per-viewport value) so
   a small hand (<=4 cards, where --fan-tight-w comes out larger than the
   flat value since little compaction is actually needed) renders exactly
   as it did before -- open and unhurried -- and only a genuinely crowded
   hand pulls tighter. This replaces flat, count-blind constants everywhere
   below, INCLUDING the 680px-mobile and 901px-narrow-desktop tiers, which
   used to hardcode a flex-shrink:0 fixed width with no per-count
   compaction at all -- that was the actual overflow bug (confirmed
   empirically: an 8+ card hand pushed cards over 400px past the edge on a
   390px-wide viewport, margin capped at -34px, nowhere near enough).
   Coefficients were tuned against that same measurement (Playwright: no
   card's bounding box may leave the viewport, no page horizontal overflow,
   and no more than one card fully hidden behind its neighbor) at
   320-1920px width with hands from 1-12 cards. */
.match-hand-card-wrap {
  --fan-n: max(var(--card-total, 1), 1);
  --fan-mid: calc((var(--fan-n) - 1) / 2);
  --fan-offset: calc(var(--card-index, 0) - var(--fan-mid));
  /* Mirrors .floating-hand's own real width formula exactly (both read the
     same --dock-w/--dock-reserve-w set on .floating-hand-dock -- see that
     rule) instead of an independent guess at how much room the action card
     beside it leaves. The two drifting apart is exactly what caused the
     hand to overlap the action card panel before: this file previously
     hardcoded "-280px" as a rough estimate of the action card's width plus
     gap, which was close enough to avoid the worst crowded-hand cases but
     still let a plain 3-card hand's cards run a good ~20px under "Fase de
     Combatente" (caught by eye in a screenshot, not by any automated
     check). Deriving it from the same inherited variables the layout
     itself uses means they can't go out of sync again. */
  --fan-budget: min(900px, 70vw, calc(var(--dock-w, 1200px) - var(--dock-reserve-w, 0px)));
  --fan-tight-w: clamp(70px, calc(var(--fan-budget) / (1 + (var(--fan-n) - 1) * 0.34)), 150px);
  --fan-w: min(130px, var(--fan-tight-w));
  /* Margin-left (the overlap) is the tighter of two candidates: the old flat
     per-viewport value, and a "pitch" computed so --fan-n cards of the
     ACTUAL rendered width (--fan-w, already capped per tier below) span
     --fan-budget exactly: pitch = (budget - w) / (n - 1), margin = pitch - w.
     At low card counts that pitch comes out larger than the card itself
     (cards would need to spread apart, not overlap, to fill the budget) --
     margin is then a big POSITIVE number, and min() correctly falls back to
     the old flat value, so a small hand looks exactly as it did before.
     Only once real crowding brings the pitch below the flat value does this
     kick in. (A flat --fan-w * 0.66 -- tried first -- ignored whether
     compaction was actually needed and was tighter than the flat value even
     at 2 cards, visibly overlapping the action card beside the hand: caught
     by eye in a screenshot, not by the automated checks, which is why this
     comment says to actually look at hand screenshots and not just trust
     "no overflow".) */
  --fan-pitch: calc((var(--fan-budget) - var(--fan-w)) / max(var(--fan-n) - 1, 1));
  --fan-tight-margin: calc(var(--fan-pitch) - var(--fan-w));
  position: relative;
  z-index: calc(var(--card-index, 0) + 1);
  /* flex:1 1 (not 0 1 or 0 0) matches the old rule's grow/shrink factors --
     max-width below still caps the actual rendered size at --fan-w either
     way, so this doesn't change final card size, but flex-grow:0 here
     (tried first) made this element's own flex CONTAINER (.match-hand-cards,
     nested one level up) collapse to a few px regardless of min-width, an
     odd but very reproducible behavior traced by bisecting every property on
     this rule one at a time in a live page -- grow:0 was the one and only
     thing that triggered it, unrelated to min-width, aspect-ratio, or view
     transitions (all independently ruled out). Keeping grow:1 sidesteps it
     entirely. min-width is a low floor (not --fan-w) so the dock's real flex
     negotiation with the action-card alongside it (see its own comment)
     still has room to shrink cards further than --fan-w when needed. */
  min-width: 46px;
  max-width: var(--fan-w);
  flex: 1 1 var(--fan-w);
  aspect-ratio: var(--card-aspect-ratio);
  margin-left: min(clamp(-34px, -4vw, -16px), var(--fan-tight-margin));
  scroll-snap-align: center;
  transform: rotate(calc(var(--fan-offset) * clamp(1.5deg, calc(96deg / var(--fan-n)), 6deg)));
  transform-origin: bottom center;
  transition: transform 180ms ease;
  pointer-events: auto;
}

.match-hand-card-wrap:first-child {
  margin-left: 0;
}

.match-hand-card-wrap:hover,
.match-hand-card-wrap:focus-within {
  z-index: 20;
  transform: translateY(-32px) scale(1.1) rotate(0deg);
}

.match-hand-card-wrap .match-hand-card {
  width: 100%;
  min-width: 0;
  max-width: none;
  height: 100%;
  flex: none;
}

.match-hand-card.can-play {
  border-color: color-mix(in srgb, var(--hand-color) 74%, white);
  box-shadow:
    0 14px 27px rgba(3, 2, 16, 0.38),
    0 0 0 1px color-mix(in srgb, var(--hand-color) 36%, transparent),
    0 0 20px color-mix(in srgb, var(--hand-color) 18%, transparent);
}

.match-hand-card.can-play::after {
  position: absolute;
  z-index: 2;
  inset: 0;
  content: "";
  pointer-events: none;
  border: 1px solid color-mix(in srgb, var(--hand-color) 58%, transparent);
  border-radius: inherit;
  box-shadow: inset 0 0 18px color-mix(in srgb, var(--hand-color) 12%, transparent);
}

.match-card-play-button {
  width: 100%;
  height: 100%;
  padding: 0;
  overflow: hidden;
  color: inherit;
  background: transparent;
  border: 0;
  border-radius: inherit;
  cursor: pointer;
}

.match-card-play-button:disabled {
  cursor: not-allowed;
  filter: saturate(0.42) brightness(0.68);
}

.match-card-play-button > img,
.match-card-play-button img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.match-card-play-button:focus-visible,
.match-card-preview-button:focus-visible,
.effect-choice-card:focus-visible,
.effect-choice-player:focus-visible {
  outline: 3px solid rgba(255, 196, 61, 0.82);
  outline-offset: 2px;
}

.match-card-preview-button {
  position: absolute;
  z-index: 5;
  top: 6px;
  right: 6px;
  display: grid;
  width: 28px;
  height: 28px;
  place-items: center;
  padding: 0;
  color: var(--cream);
  background: rgba(10, 8, 27, 0.82);
  border: 1px solid rgba(255, 255, 255, 0.24);
  border-radius: 50%;
  box-shadow: 0 5px 15px rgba(3, 2, 16, 0.35);
  cursor: zoom-in;
  backdrop-filter: blur(7px);
  transition: 160ms ease;
}

.match-card-preview-button:hover {
  color: #21180a;
  background: var(--gold);
  border-color: var(--gold);
  transform: scale(1.08);
}

.match-card-preview-button .icon {
  width: 14px;
  height: 14px;
}

.match-hand-card[data-drag-card="true"] {
  touch-action: none;
  cursor: grab;
}

.match-hand-card.dragging-source {
  opacity: 0.32;
}

.drag-ghost {
  position: fixed;
  z-index: 900;
  pointer-events: none;
  will-change: transform;
  box-shadow: 0 18px 38px rgba(3, 2, 16, 0.5);
  opacity: 0.96;
  transition: none !important;
  animation: none !important;
}

body.card-dragging {
  cursor: grabbing;
  user-select: none;
}

.battle-zone.drop-target .zone-card {
  outline: 2px dashed color-mix(in srgb, var(--gold) 55%, transparent);
  outline-offset: 3px;
  border-radius: inherit;
  transition: outline-color 140ms ease, background-color 140ms ease;
}

.battle-zone.drop-target.drag-over .zone-card {
  outline-color: var(--gold);
  outline-style: solid;
  background: rgba(255, 196, 61, 0.14);
}

.card-preview-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 14px;
}

.card-preview-drag-hint {
  font-size: 0.68rem;
  color: var(--cream-muted);
}

.battle-zone.fighter {
  position: relative;
}

.fighter-power-badge {
  position: absolute;
  z-index: 7;
  top: 20px;
  right: max(3px, calc(50% - 54px));
  display: grid;
  min-width: 31px;
  height: 31px;
  place-items: center;
  padding-inline: 6px;
  color: #171126;
  background: var(--cream);
  border: 2px solid var(--gold);
  border-radius: 999px;
  box-shadow: 0 7px 18px rgba(3, 2, 16, 0.38), 0 0 17px rgba(255, 196, 61, 0.2);
  font-family: Georgia, serif;
  font-size: 0.83rem;
  font-weight: 900;
  line-height: 1;
}

.power-modifiers {
  position: absolute;
  z-index: 7;
  right: 50%;
  bottom: 4px;
  display: flex;
  max-width: 100%;
  align-items: center;
  justify-content: center;
  gap: 3px;
  transform: translateX(50%);
}

.power-modifier {
  padding: 2px 5px;
  color: var(--cream);
  background: rgba(12, 10, 31, 0.9);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 0.46rem;
  font-weight: 850;
  line-height: 1.15;
  white-space: nowrap;
}

.power-modifier.positive {
  color: #aaf1d9;
  border-color: rgba(89, 221, 172, 0.45);
}

.power-modifier.negative {
  color: #ffaaaa;
  border-color: rgba(255, 107, 107, 0.48);
}

.power-modifier.set {
  color: #d8d0ff;
  border-color: rgba(171, 150, 240, 0.52);
}

.face-down-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--cream-muted);
  font-size: 0.58rem;
  white-space: nowrap;
}

.face-down-toggle input {
  accent-color: var(--gold);
}

/* Centred over the whole table (not pinned to the bottom, where it used to
   get partially covered by the hand) so it reads as a modal the player must
   deal with. Ancestor check: neither .stellar-table nor .match-table-shell
   sets transform/filter/perspective/contain/will-change, so nothing between
   this and the viewport creates a containing block that would break
   position:fixed centring. The huge box-shadow spread below was already
   acting as a full-viewport backdrop before this change (it darkens
   everything outside the drawer's own rounded rect); it still does, now
   centred -- no separate backdrop element needed. */
.effect-choice-drawer {
  position: fixed;
  z-index: 120;
  top: 50%;
  left: 50%;
  /* Was a flat min(980px, ...) regardless of content -- a 2-field form
     (opponent_and_number) or a 1-2 card choice (banish_discard) stretched
     its <select>s/cards to fill that same width via the 1fr/auto-fit tracks
     below, which is what actually made those look like giant near-empty
     panels. A content-heavy grid (many cards) still wraps into extra rows
     under this cap and scrolls via max-height/overflow -- it doesn't need
     980px either, just room for ~4 cards per row. */
  width: min(640px, calc(100vw - 24px));
  max-height: min(68dvh, 620px);
  padding: 16px;
  overflow: auto;
  color: var(--cream);
  background:
    radial-gradient(circle at 16% 0, rgba(124, 100, 211, 0.2), transparent 37%),
    linear-gradient(145deg, rgba(31, 27, 65, 0.98), rgba(13, 11, 34, 0.99));
  border: 1px solid rgba(207, 196, 245, 0.3);
  border-radius: 20px;
  box-shadow: 0 25px 90px rgba(3, 2, 16, 0.7), 0 0 0 999vmax rgba(5, 4, 17, 0.4);
  transform: translate(-50%, -50%);
  overscroll-behavior: contain;
}

.effect-choice-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px;
  padding-bottom: 11px;
  border-bottom: 1px solid var(--line);
}

.effect-choice-head > div {
  flex: 1;
  min-width: 0;
}

.choice-source-symbol {
  display: grid;
  width: 44px;
  height: 44px;
  flex: 0 0 44px;
  place-items: center;
  color: var(--gold);
  background: rgba(255, 196, 61, 0.08);
  border: 1px solid rgba(255, 196, 61, 0.24);
  border-radius: 50%;
  font-size: 1.35rem;
}

.effect-choice-head strong,
.effect-choice-head h2 {
  display: block;
  margin: 0;
  font-family: Georgia, serif;
  font-size: clamp(1.05rem, 2.3vw, 1.45rem);
  line-height: 1.1;
}

.effect-choice-head p,
.effect-choice-head span {
  display: block;
  margin: 4px 0 0;
  color: var(--cream-muted);
  font-size: 0.68rem;
  line-height: 1.45;
}

.effect-choice-options {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(105px, 1fr));
  gap: 10px;
  padding: 14px 2px;
}

/* Card-thumbnail choices (banish_discard/Ultima Quarta, tutor_card,
   choose_discard_card, break_support, etc.) capped per-item instead of
   1fr -- with few cards, 1fr let each button stretch to fill the whole
   drawer width (a single card blown up to ~600px), which is what actually
   made those look like giant near-empty panels rather than a normal card
   picker. justify-content centers a sparse row instead of stretching or
   left-hugging it. */
.effect-choice-options.cards {
  grid-template-columns: repeat(auto-fit, minmax(105px, 140px));
  justify-content: center;
}

.effect-choice-card,
.effect-choice-player {
  min-width: 0;
  color: var(--cream);
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid var(--line);
  cursor: pointer;
  transition: 160ms ease;
}

.effect-choice-card {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  gap: 7px;
  padding: 7px;
  border-radius: 12px;
}

.effect-choice-card img {
  width: 100%;
  aspect-ratio: var(--card-aspect-ratio);
  object-fit: cover;
  border-radius: 8px;
}

.effect-choice-card span,
.effect-choice-card strong {
  display: -webkit-box;
  overflow: hidden;
  font-size: 0.62rem;
  line-height: 1.28;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.effect-choice-player {
  display: grid;
  min-height: 64px;
  grid-template-columns: 38px minmax(0, 1fr) auto;
  align-items: center;
  gap: 9px;
  padding: 9px 11px;
  border-radius: 13px;
  text-align: left;
}

.effect-choice-player:hover,
.effect-choice-card:hover,
.effect-choice-player.selected,
.effect-choice-card.selected,
.effect-choice-player[aria-pressed="true"],
.effect-choice-card[aria-pressed="true"] {
  background: rgba(255, 196, 61, 0.1);
  border-color: var(--gold);
  box-shadow: 0 0 22px rgba(255, 196, 61, 0.14);
  transform: translateY(-2px);
}

.effect-choice-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 9px;
  padding-top: 11px;
  border-top: 1px solid var(--line);
}

.effect-choice-card-wrap {
  position: relative;
  min-width: 0;
}

.effect-choice-card-wrap .effect-choice-card {
  width: 100%;
  height: 100%;
}

.effect-choice-form {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr)) auto;
  align-items: end;
  gap: 10px;
  padding-top: 14px;
}

.effect-choice-form > label {
  display: grid;
  gap: 5px;
  color: var(--cream-muted);
  font-size: 0.61rem;
  font-weight: 750;
}

.effect-choice-form select {
  width: 100%;
  min-height: 39px;
  padding: 7px 30px 7px 9px;
  color: var(--cream);
  background: #17142f;
  border: 1px solid var(--line);
  border-radius: 9px;
}

.effect-choice-checks,
.effect-reorder-list {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: repeat(auto-fit, minmax(105px, 1fr));
  gap: 9px;
}

.effect-choice-checks label {
  position: relative;
  display: grid;
  grid-template-columns: 22px 46px minmax(0, 1fr);
  align-items: center;
  gap: 7px;
  padding: 7px;
  color: var(--cream);
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid var(--line);
  border-radius: 10px;
  font-size: 0.62rem;
  cursor: pointer;
}

.effect-choice-checks input { accent-color: var(--gold); }
.effect-choice-checks img {
  width: 46px;
  aspect-ratio: var(--card-aspect-ratio);
  object-fit: cover;
  border-radius: 5px;
}

.effect-choice-count {
  min-height: 46px;
  color: #21170a;
  background: var(--gold);
  border: 1px solid #ffe69a;
  border-radius: 11px;
  font-family: Georgia, serif;
  font-size: 1.1rem;
  font-weight: 900;
  cursor: pointer;
}

.effect-choice-form.reorder {
  grid-template-columns: 1fr auto;
}

.effect-choice-form .no-discard {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
}

.effect-reorder-row {
  display: grid;
  grid-template-columns: 47px minmax(0, 1fr);
  align-items: center;
  gap: 7px;
  padding: 7px;
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid var(--line);
  border-radius: 11px;
}

.effect-reorder-row > img {
  width: 47px;
  grid-row: span 3;
  aspect-ratio: var(--card-aspect-ratio);
  object-fit: cover;
  border-radius: 6px;
}

.effect-reorder-row strong {
  overflow: hidden;
  font-size: 0.66rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.effect-reorder-row label {
  display: flex;
  align-items: center;
  gap: 5px;
  color: var(--cream-muted);
  font-size: 0.55rem;
}

.effect-reorder-row select {
  min-height: 28px;
  padding-block: 3px;
}

.choice-skip {
  margin-top: 9px;
}

.match-event-log summary {
  cursor: pointer;
  list-style: none;
}

.match-event-log summary::-webkit-details-marker { display: none; }

.match-event-log[open] .rail-pill {
  clip-path: inset(0 0 0 0 round 999px);
  color: #21170a;
  background: var(--gold);
  border-color: var(--gold);
}

.match-event-log ol {
  position: absolute;
  z-index: 81;
  top: 0;
  pointer-events: auto;
  right: calc(100% + 10px);
  display: grid;
  width: min(340px, calc(100vw - 24px));
  max-height: min(60vh, 480px);
  gap: 5px;
  padding: 14px 14px 14px 30px;
  margin: 0;
  overflow: auto;
  color: var(--cream);
  background: rgba(15, 13, 37, 0.97);
  border: 1px solid rgba(207, 196, 245, 0.24);
  border-radius: 14px;
  box-shadow: 0 20px 65px rgba(3, 2, 16, 0.58);
  backdrop-filter: blur(16px);
  overscroll-behavior: contain;
}

.match-winner-banner small,
.match-winner-banner strong {
  display: block;
}

.match-winner-banner small {
  margin-bottom: 9px;
  font-size: 0.68rem;
  font-weight: 900;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.match-event-log p,
.match-event-log li {
  color: var(--cream-muted);
  font-size: 0.64rem;
  line-height: 1.45;
}

.match-winner-banner {
  position: fixed;
  z-index: 110;
  top: 50%;
  left: 50%;
  width: min(620px, calc(100vw - 28px));
  /* This box has no ancestor to clip it -- position:fixed puts it directly
     against the viewport, escaping .match-table-shell's overflow:hidden. With
     no height cap, a long winner list/name on a short viewport could render
     taller than the viewport, and a position:fixed box that exceeds the
     viewport does grow the page's own scrollable area (unlike normal-flow
     overflow, body's overflow-x:hidden doesn't catch this). dvh over vh for
     the same browser-chrome-inconsistency reason as everywhere else in this
     file; overflow-y:auto is the fallback for whatever's still too tall to
     fit even after capping. */
  max-height: calc(100dvh - 32px);
  overflow-y: auto;
  padding: clamp(24px, 5vw, 48px);
  color: #21170a;
  background:
    radial-gradient(circle at 50% 0, #fff3b5, transparent 44%),
    linear-gradient(145deg, #ffd96c, #f0a91f);
  border: 3px solid rgba(255, 250, 215, 0.78);
  border-radius: 27px;
  box-shadow: 0 32px 100px rgba(3, 2, 16, 0.74), 0 0 0 999vmax rgba(5, 4, 17, 0.6);
  text-align: center;
  transform: translate(-50%, -50%);
}

/* The match table underneath keeps rendering once the game is finished
   (renderMatchSetup doesn't remove it, only adds the banner on top of it) --
   on narrow viewports .game-players is a horizontal-scroll strip (see the
   max-width:680px block), and that strip's own scrollbar stayed fully live
   and visible behind/around the banner, which is what actually read as "two
   scrollbars" on the victory screen. The sibling combinator only matches
   while .match-winner-banner is actually present (game finished), so normal
   mid-match scrolling is untouched. */
.match-winner-banner ~ .stellar-table .game-players {
  overflow: hidden;
  pointer-events: none;
}

.match-winner-banner h2,
.match-winner-banner strong {
  margin: 0;
  font-family: Georgia, serif;
  font-size: clamp(1.9rem, 6vw, 3.8rem);
  line-height: 0.98;
}

.match-winner-banner p {
  margin: 10px auto 0;
  font-size: 0.8rem;
  line-height: 1.5;
}

.match-postgame-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 22px;
}

.match-postgame-waiting {
  font-size: 0.72rem;
  font-weight: 700;
  color: #21170a;
  opacity: 0.75;
}

.battle-player-board.eliminated {
  opacity: 0.48;
  filter: grayscale(0.8) saturate(0.35);
}

.battle-player-board.eliminated::after {
  position: absolute;
  z-index: 8;
  inset: 0;
  display: grid;
  place-items: center;
  content: "×";
  color: rgba(255, 115, 115, 0.72);
  background: rgba(8, 7, 24, 0.26);
  font-family: Georgia, serif;
  font-size: clamp(2.4rem, 7vw, 5rem);
  pointer-events: none;
}

.face-down-card {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #24203e url("assets/card-back.jpg") center / cover no-repeat;
  border: 1px solid rgba(255, 255, 255, 0.24);
  border-radius: inherit;
  box-shadow: 0 9px 22px rgba(3, 2, 16, 0.38);
}

.face-down-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Keep the added controls inside the existing 1366x768 battle composition. */
@media (min-width: 901px) and (min-height: 700px) {
  .match-mode .action-card {
    padding: 8px 10px;
    border-radius: 10px;
  }

  .match-mode .turn-action-copy strong { font-size: 0.7rem; }
  .match-mode .turn-action-copy span { font-size: 0.5rem; white-space: nowrap; }
  .match-mode .turn-action-buttons { gap: 6px; }

  .match-mode .match-hand-card-wrap {
    --fan-w: min(92px, var(--fan-tight-w));
    min-width: 44px;
    max-width: var(--fan-w);
    flex: 1 1 var(--fan-w);
    margin-left: min(clamp(-34px, -4vw, -16px), var(--fan-tight-margin));
  }

  .match-mode .match-card-preview-button {
    top: 4px;
    right: 4px;
    width: 22px;
    height: 22px;
  }

  .match-mode .match-card-preview-button .icon { width: 11px; height: 11px; }
  .match-mode .compact .fighter-power-badge { top: 12px; right: max(0px, calc(50% - 29px)); min-width: 20px; height: 20px; padding-inline: 3px; border-width: 1px; font-size: 0.57rem; }
  .match-mode .compact .power-modifiers { display: none; }
}

@media (max-width: 680px) {
  .action-card {
    padding: 10px;
  }

  .turn-action-copy strong,
  .turn-action-copy span {
    white-space: normal;
  }

  .turn-action-buttons {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .turn-action-buttons .btn:last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }

  /* Same --fan-w solve as the base rule (see its comment), just with this
     tier's own 104px cap -- flex:0 0 104px here previously meant NO
     shrinking was possible at all regardless of hand size, so margin-overlap
     (capped at -34px) was the only compaction lever on phones, nowhere near
     enough for a crowded hand. */
  .match-hand-card-wrap {
    /* .floating-hand-dock stacks (column) below this width -- see its own
       680px rule -- so unlike the base rule's --fan-budget, nothing else
       shares this row and the full dock width is fair game. */
    --fan-budget: min(1200px, 96vw);
    --fan-w: min(104px, var(--fan-tight-w));
    min-width: 44px;
    max-width: var(--fan-w);
    flex: 1 1 var(--fan-w);
    margin-left: min(clamp(-34px, -4vw, -16px), var(--fan-tight-margin));
  }

  /* Still centred (see the base rule) -- just sized to fit a phone viewport
     instead of the bottom-sheet treatment this used to have. */
  .effect-choice-drawer {
    width: calc(100% - 16px);
    max-height: min(78dvh, 680px);
    padding: 14px 12px max(14px, env(safe-area-inset-bottom));
  }

  .effect-choice-head {
    gap: 10px;
  }

  .effect-choice-options {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
  }

  /* Restated here because .cards is more specific than the bare rule just
     above -- without this, that extra specificity would win even inside
     this media query and skip the intended 2-column phone layout. */
  .effect-choice-options.cards {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    justify-content: initial;
  }

  .effect-choice-form,
  .effect-choice-form.reorder {
    grid-template-columns: 1fr;
  }

  .effect-choice-form > .btn,
  .effect-choice-form.reorder > .btn,
  .effect-choice-checks,
  .effect-reorder-list,
  .effect-choice-form .no-discard {
    grid-column: 1;
  }

  .effect-choice-checks,
  .effect-reorder-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .effect-choice-actions {
    position: sticky;
    bottom: calc(-1 * max(14px, env(safe-area-inset-bottom)));
    padding: 10px 0 max(6px, env(safe-area-inset-bottom));
    background: linear-gradient(transparent, rgba(13, 11, 34, 0.99) 22%);
  }

  .effect-choice-actions .btn {
    min-width: 0;
    flex: 1;
  }

  .match-event-log ol {
    position: fixed;
    top: auto;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    max-height: 66dvh;
    padding-bottom: max(14px, env(safe-area-inset-bottom));
    border-right: 0;
    border-bottom: 0;
    border-left: 0;
    border-radius: 21px 21px 0 0;
  }

  .fighter-power-badge {
    top: 17px;
    right: max(1px, calc(50% - 43px));
    min-width: 27px;
    height: 27px;
    font-size: 0.72rem;
  }
}

@media (max-width: 380px) {
  .effect-choice-options,
  .effect-choice-options.cards {
    grid-template-columns: 1fr;
  }

  .effect-choice-checks,
  .effect-reorder-list {
    grid-template-columns: 1fr;
  }

  .turn-action-buttons {
    grid-template-columns: 1fr;
  }

  .turn-action-buttons .btn {
    grid-column: auto !important;
  }
}

@media (prefers-reduced-motion: reduce) {
  .match-hand-card,
  .match-card-preview-button,
  .effect-choice-card,
  .effect-choice-player {
    transition: none;
  }
}
