/* Screens, the playing HUD, and board interaction. */

/* .row and friends set display:flex, which would otherwise defeat [hidden]. */
[hidden] { display: none !important; }

/* ------------------------------------------------------------------- screens */

.screen { display: none; flex-direction: column; gap: 10px; flex: 1 1 auto; min-height: 0; }
.screen.is-active { display: flex; }

/* ========================================================================== */
/*  Playing: the course is the whole screen                                   */
/* ========================================================================== */

/* While a hole is in play nothing is left of the page but the course. The
   sheet loses its card, the app loses its gutters, and every control becomes
   an overlay — which also means no control can ever change the board's size. */
body.is-playing { height: 100dvh; overflow: hidden; }
body.is-playing .app { height: 100%; padding: 0; }
body.is-playing .sheet {
  height: 100%;
  border-radius: 0;
  padding: 0;
  gap: 0;
  box-shadow: none;
  background: var(--paper);
}

.stage {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  /* The HUD is absolutely positioned against this box, so its bands are carved
     out as padding rather than overlapped. The course is then laid out in what
     is left, and no chip can ever cover a target, the flag or the ball. */
  padding: calc(58px + env(safe-area-inset-top)) 10px calc(88px + env(safe-area-inset-bottom));
  padding-left: max(10px, env(safe-area-inset-left));
  padding-right: max(10px, env(safe-area-inset-right));
}

.board {
  flex: 1 1 auto;
  width: 100%;
  height: 100%;
  min-height: 0;
  display: block;
  touch-action: manipulation; /* suppress double-tap zoom on the board */
  user-select: none;
  -webkit-user-select: none;
}

/* ------------------------------------------------------------------- the HUD */

.hud {
  position: absolute;
  left: 0;
  right: 0;
  z-index: 5;
  display: flex;
  pointer-events: none;      /* the course underneath keeps every stray tap */
}
.hud > * { pointer-events: auto; }

.hud--top {
  top: 0;
  padding: calc(10px + env(safe-area-inset-top)) 12px 0;
  padding-left: max(12px, env(safe-area-inset-left));
  padding-right: max(12px, env(safe-area-inset-right));
  align-items: flex-start;
  gap: 8px;
}

.hud--bottom {
  bottom: 0;
  padding: 0 12px calc(12px + env(safe-area-inset-bottom));
  padding-left: max(12px, env(safe-area-inset-left));
  padding-right: max(12px, env(safe-area-inset-right));
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

/* Every HUD chip carries its own paper behind it, because it may sit over the
   cream margin or over the green, and it has to stay readable on both. */
.chip {
  background: rgba(246, 244, 236, .93);
  border: 1.5px solid rgba(34, 48, 31, .18);
  border-radius: 999px;
  padding: 7px 14px;
  font-size: .9rem;
  line-height: 1.25;
  backdrop-filter: blur(3px);
  max-width: 100%;
}

/* ------------------------------------------------------------ hamburger */

.icon-btn {
  flex: 0 0 auto;
  width: 44px; height: 44px;
  display: grid;
  place-content: center;
  gap: 5px;
  padding: 0;
  border: 1.5px solid rgba(34, 48, 31, .2);
  border-radius: 50%;
  background: rgba(246, 244, 236, .93);
  cursor: pointer;
  backdrop-filter: blur(3px);
}
.icon-btn span {
  display: block;
  width: 19px; height: 2.4px;
  border-radius: 2px;
  background: var(--ink);
}
.icon-btn:active { transform: translateY(1px); }
.icon-btn:focus-visible { outline: 2.5px solid var(--ink); outline-offset: 2px; }

/* --------------------------------------------------------------- turn pill */

.turn-pill {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  margin-top: 1px;
}
.turn-pill.is-you { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.turn-who { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.turn-clock { font-variant-numeric: tabular-nums; opacity: .7; flex: 0 0 auto; }
.turn-pill.is-urgent { border-color: var(--flag); }
.turn-pill.is-urgent .turn-clock { opacity: 1; color: var(--flag); }
.turn-pill.is-you.is-urgent .turn-clock { color: #ffd9d5; }

/* ---------------------------------------------------------------------- die */

/* The die lives docked in the top-right. On a roll it leaps to the middle of
   the course, tumbles there where it cannot be missed, then travels back to
   its corner carrying the result. */
.die-dock {
  flex: 0 0 auto;
  margin-left: auto;        /* the die keeps the right-hand corner to itself */
  width: 44px; height: 44px;
  display: grid;
  place-items: center;
}

.die {
  width: 44px; height: 44px;
  /* The die is a read-out, never a target, and it spends part of every turn
     sitting over the middle of the course at more than twice its size. It also
     keeps travelling for half a second after `is-waiting-dice` has handed the
     rings back, so without this it silently eats real taps on whatever it is
     passing over — a gap no test that calls element.click() can see. */
  pointer-events: none;
  border: 2.5px solid var(--ink);
  border-radius: 10px;
  background: var(--paper);
  display: grid;
  grid-template: repeat(3, 1fr) / repeat(3, 1fr);
  padding: 6px; gap: 2px;
  box-shadow: 0 2px 5px rgba(40, 50, 35, .2);
  transform-origin: center;
  transition: transform .52s cubic-bezier(.34, .9, .36, 1);
  will-change: transform;
}
.die i {
  border-radius: 50%;
  background: var(--ink);
  align-self: center; justify-self: center;
  width: 8px; height: 8px;
}

/* Thrown: sitting over the middle of the course at playing size. */
.die.is-airborne {
  transition: none;
  transform: translate(var(--die-dx, 0), var(--die-dy, 0)) scale(2.1);
  box-shadow: 0 10px 20px rgba(30, 44, 26, .3);
  z-index: 20;
}
.die.is-tumbling { animation: tumble .26s linear infinite; }
@keyframes tumble {
  0%   { rotate: 0deg; }
  25%  { rotate: -13deg; }
  50%  { rotate: 4deg; }
  75%  { rotate: 12deg; }
  100% { rotate: 0deg; }
}
/* The old shake, still used when a roll happens with no room to throw. */
.die.is-rolling { animation: shake .42s ease; }
@keyframes shake {
  0%, 100% { transform: rotate(0) }
  25% { transform: rotate(-7deg) translateY(-2px) }
  75% { transform: rotate(7deg) translateY(1px) }
}
@media (prefers-reduced-motion: reduce) {
  .die { transition: none; }
  .die.is-airborne { transform: none; }
  .die.is-rolling, .die.is-tumbling { animation: none; }
}

/* --------------------------------------------------------------- bottom bar */

/* One permanently reserved line for the lie readout. It never collapses and
   never wraps, so nothing below it shifts as targets are highlighted. */
.hud-line {
  margin: 0;
  min-height: 1.55rem;
  display: flex;
  align-items: center;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hud-line:empty { visibility: hidden; }

.hud-stats {
  display: flex;
  gap: 4px 16px;
  font-size: .95rem;
  letter-spacing: .02em;
  white-space: nowrap;
}
.hud-stats .val { min-width: 1.2em; display: inline-block; }

.target-readout .sep, .hud-stats .sep { color: var(--ink-faint); margin: 0 .4em; }
.target-readout span { white-space: nowrap; }
.target-readout .good { border-bottom: 1.5px solid var(--ink); }
.target-readout .weak { color: var(--ink-soft); }

.message { font-size: .88rem; color: var(--ink-soft); }
.message.is-alert { color: var(--ink); }

/* ------------------------------------------------------------ board layers */

/* Only targets and Bigfoot are interactive. Without this the aim line, which is
   drawn on top and ends exactly on the hovered target, swallows its click. */
.pg-grid, .pg-trees, .pg-slopes, .pg-markers, .pg-trail,
.pg-trail-marks, .pg-aim, .pg-ball, .pg-terrain, .pg-debug { pointer-events: none; }

.pg-target { cursor: pointer; }
/* Hover emphasis is pure CSS so that moving the pointer never re-renders the
   target nodes; rebuilding them mid-gesture would swap out the element the
   click is about to land on. */
.pg-target .pg-target-ring { transition: stroke-width .12s ease, opacity .12s ease; }
.pg-target:hover .pg-target-ring {
  opacity: 1;
  stroke-width: 7;
  stroke-dasharray: none;
}
.pg-target:hover .pg-target-num { opacity: .95; }
.pg-target:focus { outline: none; }

.pg-ball circle { transition: none; }
.pg-targets { transition: opacity .18s ease; }

/* Targets are hidden while the ball is travelling, so the board reads cleanly. */
.board.is-animating .pg-targets,
.board.is-animating .pg-aim { opacity: 0; transition: opacity .12s ease; }

/* And held back until the die has landed, so the roll is read before the
   options appear rather than alongside them. pointer-events go too, or a tap
   during the tumble would pick a spot the player has not seen yet. */
.board.is-waiting-dice .pg-targets,
.board.is-waiting-dice .pg-aim { opacity: 0; pointer-events: none; }
.pg-bigfoot { cursor: pointer; }
.pg-bigfoot:hover path { opacity: .65 !important; }
.pg-bigfoot.is-spotted { cursor: default; }

.pg-target-num, .pg-player-num { font-family: 'Excalifont', cursive; pointer-events: none; }
.pg-player-ball { pointer-events: none; }
.pg-player-ball.is-turn circle:first-child { animation: turnPulse 1.8s ease-in-out infinite; }
@keyframes turnPulse {
  0%, 100% { opacity: .3; }
  50% { opacity: .8; }
}

@media (hover: none) {
  /* No pointer to hover with, so nothing should depend on it. */
  .pg-target:hover .pg-target-ring { stroke-width: inherit; }
  .btn:hover:not(:disabled) { background: var(--paper); }
  .course-card:hover { background: var(--paper); }
}
@media (prefers-reduced-motion: reduce) {
  .pg-target .pg-target-ring { transition: none; }
  .pg-player-ball.is-turn circle:first-child { animation: none; }
}

/* ========================================================================== */
/*  The menu                                                                  */
/* ========================================================================== */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(24, 34, 20, .42);
  animation: fadeIn .16s ease;
}
.overlay__panel {
  width: 100%;
  max-width: var(--page-max);
  background: var(--paper);
  border-radius: 16px 16px 0 0;
  padding: 14px 16px calc(18px + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  gap: 8px;
  animation: slideUp .2s cubic-bezier(.3, .9, .4, 1);
}
.overlay__title { font-size: 1.05rem; color: var(--ink-soft); margin: 2px 0 6px; text-align: center; }
@keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } }
@keyframes slideUp { from { transform: translateY(24px) } to { transform: none } }
@media (prefers-reduced-motion: reduce) {
  .overlay, .overlay__panel { animation: none; }
}

/* ========================================================================== */
/*  Buttons, fields, tables                                                   */
/* ========================================================================== */

.row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.row--split { justify-content: space-between; }

.btn {
  font: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: var(--paper);
  border: 1.5px solid var(--ink);
  border-radius: 8px;
  padding: 10px 16px;
  min-height: 44px;
  cursor: pointer;
  letter-spacing: .01em;
  transition: transform .06s ease, background .12s ease;
}
.btn:hover:not(:disabled) { background: #eceadf; }
.btn:active:not(:disabled) { transform: translateY(1px); background: #e4e1d3; }
.btn:focus-visible { outline: 2.5px solid var(--ink); outline-offset: 2px; }
.btn:disabled { opacity: .35; cursor: default; }

.btn--primary { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.btn--primary:hover:not(:disabled) { background: #16210f; }
.btn--ghost { border-color: var(--ink-faint); color: var(--ink-soft); }
.btn--small { padding: 7px 11px; min-height: 38px; font-size: .9rem; }
.btn--wide { flex: 1 1 auto; }

.menu-list { display: flex; flex-direction: column; gap: 9px; margin: 14px 0; }

.course-card {
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  border: 1.5px solid var(--ink); border-radius: 8px;
  padding: 13px 15px; background: var(--paper);
  font: inherit; color: inherit; cursor: pointer; text-align: left; width: 100%;
}
.course-card:hover { background: #eceadf; }
.course-card:focus-visible { outline: 2.5px solid var(--ink); outline-offset: 2px; }
.course-card .meta { font-size: .85rem; color: var(--ink-soft); }
.course-card .cname { font-size: .95rem; }
.course-card[disabled] { opacity: .4; cursor: default; }

.scorecard { width: 100%; border-collapse: collapse; font-size: .82rem; margin: 6px 0 2px; }
.scorecard th, .scorecard td { border: 1px solid var(--rule); padding: 3px 1px; text-align: center; width: 5.2%; }
.scorecard th { color: var(--ink-soft); font-weight: 400; }
.scorecard td.under { background: #dcecc4; }
.scorecard td.over { background: #f3e2dd; }
.scorecard td.now { outline: 2px solid var(--ink); outline-offset: -2px; }

.result { text-align: center; padding: 8px 0 2px; }
.result .headline { font-size: 2.1rem; line-height: 1.1; }
.result .detail { color: var(--ink-soft); margin-top: 4px; }

.seed-line { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; font-size: .85rem; color: var(--ink-soft); }
.seed-line code { font-family: inherit; letter-spacing: .12em; color: var(--ink); }

.field, .namerow__field {
  font: inherit; font-size: 1rem; padding: 9px 11px; min-height: 44px;
  border: 1.5px solid var(--ink-faint); border-radius: 8px;
  background: #fffefa; color: var(--ink);
  flex: 1 1 10ch; min-width: 0;
  letter-spacing: .1em; text-transform: uppercase;
}
.field:focus-visible,
.namerow__field:focus-visible { outline: 2.5px solid var(--ink); outline-offset: 1px; border-color: var(--ink); }

.legend { display: grid; grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); gap: 9px 14px; margin: 8px 0 4px; }
.legend div { display: flex; align-items: center; gap: 8px; font-size: .86rem; }
.legend .swatch { width: 26px; height: 26px; flex: 0 0 auto; border-radius: 7px; border: 1px solid rgba(34,48,31,.15); }

.rules-body { overflow-y: auto; max-height: 58vh; padding-right: 4px; }
.rules-body h3 { margin: 15px 0 5px; }
.rules-body ul { margin: 0 0 .7em; padding-left: 1.15em; line-height: 1.55; }
.rules-body li { margin-bottom: .25em; }

.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;
}

.debug-panel {
  position: absolute; left: 8px; bottom: 96px; z-index: 6;
  border: 1px dashed #c33; padding: 8px 10px; font-size: .74rem;
  color: #a22; background: rgba(253, 246, 245, .95); border-radius: 4px;
  white-space: pre-wrap; word-break: break-word; max-width: 60%;
}

/* ------------------------------------------------------------- multiplayer */

.code-box {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  border: 1.5px dashed var(--ink);
  border-radius: 10px;
  padding: 12px 14px;
  margin: 4px 0 2px;
}
.code-box code {
  font-family: inherit;
  font-size: 1.9rem;
  letter-spacing: .22em;
  line-height: 1;
  flex: 1 1 auto;
}

.player-list { list-style: none; margin: 4px 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.player-list li {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 4px;
  border-bottom: 1px solid var(--rule);
  font-size: .98rem;
}
.player-list li:last-child { border-bottom: 0; }
/* The pip is the player's ball: same colour, same number, same order. */
.player-list .pip {
  width: 28px; height: 28px; flex: 0 0 auto;
  border: 3px solid var(--seat, var(--ink));
  color: var(--seat, var(--ink));
  background: #fff;
  border-radius: 50%;
  display: grid; place-items: center;
  font-size: .8rem;
}
.player-list .tag { margin-left: auto; font-size: .8rem; color: var(--ink-soft); }
.player-list li.is-out { opacity: .45; }

.mp-table { width: 100%; border-collapse: collapse; font-size: .9rem; margin: 6px 0; }
.mp-table th, .mp-table td { border-bottom: 1px solid var(--rule); padding: 7px 5px; text-align: center; }
.mp-table th { color: var(--ink-soft); font-weight: 400; font-size: .8rem; }
.mp-table td.name, .mp-table th.name { text-align: left; }
.mp-table td.name { border-left: 5px solid var(--seat, transparent); padding-left: 8px; }
.mp-table td.total { font-size: 1.05rem; }
.mp-table tr.is-you td { background: #eceadf; }
.mp-table tr.is-out td { opacity: .45; }
.mp-table .rank { color: var(--ink-soft); width: 2ch; }

.conn-warning {
  border: 1.5px solid var(--ink);
  border-radius: 8px;
  padding: 8px 11px;
  font-size: .88rem;
  background: #f1eee0;
}

/* ---------------------------------------------------------------- responsive */

@media (min-width: 720px) {
  .app { padding: 26px 16px 34px; }
  .sheet { padding: 26px 22px 18px; }
  /* Playing stays phone-shaped on a big screen: a course is a tall thing. */
  body.is-playing .app { padding: 0; max-width: 620px; }
}

@media (max-width: 380px) {
  .sheet { padding: 18px 13px 12px; }
  .btn { padding: 9px 12px; font-size: .94rem; }
  h1 { font-size: 1.7rem; }
  .hud-stats { font-size: .88rem; gap: 4px 11px; }
  .chip { padding: 6px 11px; font-size: .84rem; }
}

@media (max-height: 680px) {
  .hud--top { padding-top: calc(7px + env(safe-area-inset-top)); }
  .hud--bottom { padding-bottom: calc(8px + env(safe-area-inset-bottom)); }
  .hud-line { min-height: 1.4rem; font-size: .84rem; }
  /* Short screens cannot spare as much, and the HUD is smaller here too. */
  .stage { padding-top: calc(52px + env(safe-area-inset-top));
           padding-bottom: calc(76px + env(safe-area-inset-bottom)); }
}

/* ------------------------------------------------------- touch target floor */

/* A finger still needs 44px whatever the height-based rules above decide. */
@media (pointer: coarse) {
  .btn, .btn--small, .course-card, .field { min-height: 44px; }
  .btn--small { padding: 8px 12px; }
  /* The club chips need the same floor and get it beside their own rule, which
     comes later in this file and would otherwise win on source order. */
}

/* ========================================================================== */
/*  Home, and the things a game keeps between visits                          */
/* ========================================================================== */

.screen--centred { justify-content: center; }

.brand { display: flex; align-items: center; gap: 14px; margin: 6px 0 2px; }
.brand__mark { width: 62px; height: 62px; flex: 0 0 auto; filter: drop-shadow(0 3px 5px rgba(40,50,35,.22)); }
.brand__tag { margin: 2px 0 0; font-size: .86rem; color: var(--ink-soft); }

/* A mode is a big, obvious, thumb-sized target — the first thing on the screen
   and the only thing that needs to be understood before playing. */
.mode-list { display: flex; flex-direction: column; gap: 12px; margin: 16px 0 8px; }

.mode-card {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 16px 16px;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  background: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 16px;
  /* A hard offset shadow rather than a blur: it reads as a physical key you can
     press, and it survives being drawn over a coloured background. */
  box-shadow: 0 4px 0 var(--ink);
  transition: transform .08s ease, box-shadow .08s ease, background .12s ease;
}
.mode-card:hover { background: #fffdf5; }
/* Closed to a suspended account: still legible, plainly not a door. */
.mode-card:disabled { opacity: .45; cursor: not-allowed; box-shadow: 0 2px 0 var(--ink-faint); }
.mode-card:disabled:hover { background: var(--paper); }
.mode-card:active { transform: translateY(4px); box-shadow: 0 0 0 var(--ink); }
.mode-card:focus-visible { outline: 3px solid var(--flag); outline-offset: 3px; }

.mode-card__icon {
  flex: 0 0 auto;
  width: 48px; height: 48px;
  display: grid; place-items: center;
  font-size: 1.6rem; line-height: 1;
  background: var(--fairway);
  border: 2px solid var(--ink);
  border-radius: 13px;
}
.mode-card__body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.mode-card__title { font-size: 1.22rem; line-height: 1.15; }
.mode-card__sub { font-size: .84rem; color: var(--ink-soft); }
.mode-card__go { flex: 0 0 auto; font-size: 1.3rem; color: var(--ink-faint); }

/* The running total of everything the player has ever done, one line, tappable. */
.career-strip {
  display: flex; align-items: center; gap: 4px 16px; flex-wrap: wrap;
  width: 100%; margin: 4px 0 0;
  padding: 11px 14px;
  font: inherit; font-size: .88rem; color: var(--ink-soft);
  text-align: left; cursor: pointer;
  background: rgba(255, 255, 255, .5);
  border: 1.5px dashed var(--ink-faint);
  border-radius: 12px;
}
.career-strip b { color: var(--ink); font-weight: 400; }
.career-strip__go, .levelbar__go { margin-left: auto; font-size: 1.1rem; }

.home-footer {
  margin-top: auto; padding-top: 12px;
  display: flex; flex-wrap: wrap; gap: 8px;
}
.home-footer .btn { flex: 1 1 30%; }

/* The lead mode is the one the game wants you in: same card, more ink. */
.mode-card--lead { border-width: 2.5px; box-shadow: 0 5px 0 var(--ink); }
.mode-card--lead .mode-card__icon { background: var(--rough); }
.mode-card--lead .mode-card__title { font-size: 1.32rem; }

/* ------------------------------------------------------------ today's hole */

/* Not a mode card on purpose: a fixture rather than a choice. One line, always
   in the same place, saying whether today has been played. */
.daily-strip {
  display: flex; align-items: center; gap: 12px;
  width: 100%; margin: 10px 0 2px;
  padding: 12px 14px;
  font: inherit; color: inherit; text-align: left; cursor: pointer;
  background: linear-gradient(100deg, #eef4e2, var(--paper) 70%);
  border: 2px dashed var(--rough-edge);
  border-radius: 14px;
}
.daily-strip:hover { background: linear-gradient(100deg, #e8f1d8, #fffdf5 70%); }
.daily-strip:focus-visible { outline: 3px solid var(--flag); outline-offset: 3px; }
.daily-strip.is-done { border-style: solid; border-color: var(--rule); background: var(--paper); }

.daily-strip__mark {
  flex: 0 0 auto; width: 38px; height: 38px;
  display: grid; place-items: center;
  border-radius: 11px; border: 2px solid var(--ink); background: #fff;
}
.daily-strip__mark .ico { width: 21px; height: 21px; }
.daily-strip__body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.daily-strip__title { font-size: 1.05rem; }
.daily-strip__sub { font-size: .78rem; color: var(--ink-soft); }
.daily-strip__streak {
  flex: 0 0 auto; display: flex; align-items: center; gap: 3px;
  font-size: .92rem; color: var(--flag);
}
.daily-strip__streak .ico { width: 16px; height: 16px; }

/* One square per shot: how well each was read, left to right. */
.bandrow { display: flex; flex-wrap: wrap; gap: 5px; margin: 10px 0; justify-content: center; }
.bandrow i {
  width: 22px; height: 22px;
  border: 1.5px solid var(--ink); border-radius: 5px;
  background: var(--rough);
}
.bandrow i.b2 { background: var(--sand); }
.bandrow i.b3 { background: var(--paper); }
.bandrow i.unknown { border-style: dashed; background: none; }

/* ------------------------------------------------- luck against judgement */

/* The one number no other dice game can print, so it is given room: two bars
   from a common zero, one for the dice and one for the reading. */
.split { display: flex; flex-direction: column; gap: 7px; margin: 12px 0; }
.split__row { display: flex; align-items: center; gap: 9px; font-size: .84rem; }
.split__label { flex: 0 0 5.2em; color: var(--ink-soft); }
.split__track {
  flex: 1 1 auto; height: 15px; position: relative;
  background: rgba(34, 48, 31, .06);
  border-radius: 999px;
}
.split__fill {
  position: absolute; top: 0; bottom: 0;
  border-radius: 999px;
  transition: width .5s cubic-bezier(.3,.9,.4,1), left .5s cubic-bezier(.3,.9,.4,1);
}
.split__fill.is-good { background: var(--rough); }
.split__fill.is-bad { background: var(--flag); opacity: .78; }
.split__value { flex: 0 0 4.4em; text-align: right; }
.split__note { font-size: .76rem; color: var(--ink-soft); margin: 2px 0 0; }
@media (prefers-reduced-motion: reduce) { .split__fill { transition: none; } }

/* ------------------------------------------------------------------- run */

.budget { margin: 10px 0; }
.budget__bar {
  height: 20px; border: 2px solid var(--ink); border-radius: 999px;
  background: #fff; overflow: hidden;
}
.budget__fill {
  display: block; height: 100%;
  background: var(--rough);
  transition: width .45s cubic-bezier(.3,.9,.4,1);
}
.budget__fill.is-tight { background: var(--sand-line); }
.budget__fill.is-critical { background: var(--flag); }
.budget__legend { display: flex; justify-content: space-between; font-size: .78rem; color: var(--ink-soft); margin-top: 4px; }
@media (prefers-reduced-motion: reduce) { .budget__fill { transition: none; } }

.club-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 9px; margin: 8px 0; }

.club {
  display: flex; flex-direction: column; gap: 5px;
  padding: 11px 12px;
  background: var(--paper);
  border: 1.5px solid var(--rule);
  border-radius: 13px;
  text-align: left;
  font: inherit; color: inherit;
}
.club.is-spent { opacity: .45; }
.club__name { font-size: 1rem; }
.club__blurb { font-size: .76rem; color: var(--ink-soft); }
/* Charges as pips rather than "x2": you can see what is left without reading. */
.club__charges { display: flex; gap: 4px; margin-top: 2px; }
.club__charges i {
  width: 9px; height: 9px; border-radius: 50%;
  border: 1.5px solid var(--ink); background: var(--gold);
}
.club__charges i.is-used { background: none; opacity: .4; }

/* The draft: three cards, one tap, no going back. */
.draft { display: flex; flex-direction: column; gap: 11px; margin: 14px 0; }
.draft .club {
  cursor: pointer;
  border: 2px solid var(--ink);
  box-shadow: 0 4px 0 var(--ink);
  padding: 15px;
}
.draft .club:active { transform: translateY(4px); box-shadow: 0 0 0 var(--ink); }
.draft .club:focus-visible { outline: 3px solid var(--flag); outline-offset: 3px; }
.draft .club__name { font-size: 1.18rem; }

/* In play: a row of chips above the message line, within thumb reach. */
.club-tray { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; margin-bottom: 4px; }
.club-chip {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 11px; min-height: 38px;
  font: inherit; font-size: .84rem; color: var(--ink);
  background: rgba(255, 255, 255, .92);
  border: 1.5px solid var(--ink);
  border-radius: 999px;
  cursor: pointer;
}
/* The chips were left out of the 44px floor above, and they are the one control
   a thumb reaches for mid-shot: at 38px tall and 6px apart a miss lands on the
   tray and does nothing, and spending a charge is too consequential to be a
   target the finger has to aim at. */
@media (pointer: coarse) {
  .club-chip { min-height: 44px; }
  .club-tray { gap: 8px; }
}

.club-chip:disabled { opacity: .4; cursor: not-allowed; }
.club-chip.is-armed { background: var(--ink); color: var(--paper); }
.club-chip__n { font-size: .74rem; opacity: .75; }

/* A ghost's line is drawn by the renderer; this only keeps it out of the way
   of taps aimed at the board underneath. */
.pg-ghost, .pg-ghost-trail { pointer-events: none; }

/* ------------------------------------------------------------------- tiles */

.tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 8px;
  margin: 10px 0;
}
.tile {
  background: var(--paper);
  border: 1.5px solid var(--rule);
  border-radius: 12px;
  padding: 10px 8px;
  text-align: center;
}
.tile b { display: block; font-size: 1.5rem; line-height: 1.1; font-weight: 400; }
.tile span { font-size: .74rem; color: var(--ink-soft); }
.tile.is-good b { color: var(--tree); }
.tile.is-bad b { color: var(--flag); }

/* --------------------------------------------------------- hole celebration */

/* The one moment the game gets to be loud. */
.result--big { padding: 18px 0 6px; }
.scorepop {
  /* "Triple bogey" is the longest thing this ever says; it has to fit at 360px. */
  font-size: clamp(2rem, 8.5vw, 3rem);
  line-height: 1.05;
  animation: pop .42s cubic-bezier(.2, 1.5, .5, 1);
}
.scorepop.is-great { color: var(--tree); }
.scorepop.is-ace { color: var(--gold); }
.scorepop.is-over { color: var(--ink-soft); }
@keyframes pop {
  0% { transform: scale(.4); opacity: 0 }
  60% { transform: scale(1.08); opacity: 1 }
  100% { transform: scale(1) }
}

.streak {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 10px; padding: 6px 13px;
  font-size: .92rem;
  background: #fdf2dc;
  border: 1.5px solid var(--gold);
  border-radius: 999px;
}

@media (prefers-reduced-motion: reduce) {
  .scorepop { animation: none; }
}

/* ------------------------------------------------------------------ badges */

.badge-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: 8px; margin: 8px 0; }
.badge {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 10px 6px;
  border: 1.5px solid var(--rule);
  border-radius: 12px;
  background: var(--paper);
  text-align: center;
}
.badge__pip {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--fairway);
  border: 2px solid var(--ink);
  font-size: 1rem;
}
.badge__name { font-size: .74rem; line-height: 1.2; }
.badge.is-locked { opacity: .42; }
.badge.is-locked .badge__pip { background: transparent; border-style: dashed; border-color: var(--ink-faint); }
.badge.is-new { border-color: var(--gold); background: #fdf7ea; }

.badge-toasts { display: flex; flex-direction: column; gap: 6px; margin: 4px 0; }
.badge-toasts:empty { display: none; }
.badge-line {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 12px;
  border: 1.5px solid var(--gold);
  border-radius: 999px;
  background: #fdf7ea;
  font-size: .88rem;
}
.badge-line b { font-weight: 400; }

/* Floating unlock notice, used when the player is not on a results screen. */
.toast-stack {
  position: fixed;
  left: 50%; bottom: calc(18px + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  z-index: 60;
  display: flex; flex-direction: column; gap: 8px;
  width: min(420px, calc(100vw - 24px));
  pointer-events: none;
}
.toast {
  padding: 10px 15px;
  border: 2px solid var(--ink);
  border-radius: 999px;
  background: var(--paper);
  box-shadow: 0 4px 0 var(--ink);
  font-size: .92rem;
  text-align: center;
  animation: toastIn .28s cubic-bezier(.2, 1.3, .4, 1);
}
.toast.is-going { opacity: 0; transform: translateY(8px); transition: opacity .3s ease, transform .3s ease; }
@keyframes toastIn { from { opacity: 0; transform: translateY(14px) } to { opacity: 1; transform: none } }
@media (prefers-reduced-motion: reduce) { .toast { animation: none; } }

/* -------------------------------------------------------------------- log */

.log-list { list-style: none; margin: 6px 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
/* The card is the <li> and the button inside it is bare, so an opened entry
   reads as one card that has grown rather than a card with a panel hung off it.
   The padding moved to .log__row for that, and the selector became a child one
   so the standings rows inside a detail are not cards too. */
.log-list > li {
  background: var(--paper);
  border: 1.5px solid var(--rule);
  border-radius: 12px;
  font-size: .88rem;
  overflow: hidden;
}
.log__row {
  display: flex; align-items: center; gap: 10px;
  width: 100%;
  padding: 10px 12px;
  font: inherit; color: inherit; text-align: left;
  background: none; border: 0;
  cursor: pointer;
}
/* An entry with nothing more to say is drawn the same and opens nothing. */
.log__row:disabled { cursor: default; }
/* Drawn inside the button: the card clips anything outside its corners. */
.log__row:focus-visible { outline: 2.5px solid var(--ink); outline-offset: -3px; }
/* Open: filled, with a rule under it drawn as a shadow so the row does not grow
   by a pixel and shunt the detail down as it opens. */
.log__row[aria-expanded="true"] {
  background: #eceadf;
  box-shadow: inset 0 -1px 0 var(--rule);
}
.log__icon {
  flex: 0 0 auto; width: 30px; height: 30px;
  display: grid; place-items: center;
  border-radius: 9px; background: var(--fairway); border: 1.5px solid var(--ink);
  font-size: .95rem;
}
.log__body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.log__line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.log__when { font-size: .74rem; color: var(--ink-soft); }
.log__score { flex: 0 0 auto; font-size: 1.05rem; }
.log__score.is-good { color: var(--tree); }
.log__score.is-bad { color: var(--ink-soft); }

.log__detail { padding: 9px 12px 11px; }

/* Label over value, in columns, so the numbers line up across the entry however
   many facts it has. */
.log__facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(74px, 1fr));
  gap: 7px 12px;
  margin: 0;
}
.log__facts > div { min-width: 0; }
.log__facts dt { font-size: .72rem; color: var(--ink-soft); }
.log__facts dd { margin: 0; font-size: .96rem; }

.log__table {
  list-style: none;
  margin: 9px 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 1px;
}
.log__table li {
  display: flex; align-items: center; gap: 9px;
  padding: 4px 7px;
  border-radius: 7px;
}
.log__pos { flex: 0 0 auto; min-width: 1.6em; text-align: right; color: var(--ink-soft); }
/* A name can outrun the row — a bot's arrives with "(bot)" on the end — and the
   total is the point of the row, so the name is what gives way. */
.log__who { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* What that player spent, under their name: a second line rather than a third
   column, because it is absent for most players in most matches and an empty
   column would still have to be paid for in width. */
.log__who small { display: block; font-size: .74rem; color: var(--ink-soft); }
.log__total { flex: 0 0 auto; font-variant-numeric: tabular-nums; }
/* The same fill the match table and the leaderboard use for your own row. */
.log__table li.is-you { background: #eceadf; }

/* ---------------------------------------------------------------- name row */

/* One line: what you are called, and a pencil to change it. The row is held at
   the height of the input whether or not the input is showing, so starting to
   edit does not shove the rest of the screen down. */
.namerow {
  display: flex; align-items: center; gap: 10px;
  min-height: 44px;
}
.namerow__label { flex: 0 0 auto; font-size: .82rem; color: var(--ink-soft); }
.namerow__value {
  flex: 1 1 auto; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* .field is sized to share a .row with a button and set for codes. A name is not
   a code, so it keeps its own case and spacing, and it takes the rest of the row
   rather than a fixed width — fourteen characters have to fit at 390px, and the
   label and the pencil are all that is bidding against it. */
.namerow__field {
  display: none;
  flex: 1 1 15ch;
  letter-spacing: normal;
  text-transform: none;
}
.namerow.is-editing .namerow__value { display: none; }
.namerow.is-editing .namerow__field { display: block; }

.namerow__edit {
  flex: 0 0 auto;
  margin-left: auto;
  width: 32px; height: 32px;
  display: grid; place-items: center;
  padding: 0;
  font: inherit; color: var(--ink-soft);
  background: none; border: 0;
  border-radius: 8px;
  cursor: pointer;
}
.namerow__edit .ico { width: 19px; height: 19px; }
.namerow__edit:focus-visible { outline: 2.5px solid var(--ink); outline-offset: 2px; }
/* Saving is the one thing on this row worth full ink. */
.namerow.is-editing .namerow__edit { color: var(--ink); }

/* The switch's trick: the pencil stays a pencil and an invisible box over the
   top makes up the rest of the finger. 36 + 4 + 4 = 44, and 4px is all it can
   take without reaching into the field beside it. */
@media (pointer: coarse) {
  .namerow__edit { position: relative; width: 36px; height: 36px; }
  .namerow__edit::before {
    content: '';
    position: absolute;
    inset: -4px;
  }
}

/* ------------------------------------------------------------------- bots */

.bot-bar {
  margin: 8px 0;
  padding: 12px;
  border: 1.5px dashed var(--ink-faint);
  border-radius: 14px;
  display: flex; flex-direction: column; gap: 9px;
}
.bot-bar__label { font-size: .86rem; color: var(--ink-soft); }
.bot-bar .tiny { margin: 0; }

/* A segmented control, the way a phone game picks between three of something. */
.seg { display: flex; border: 1.5px solid var(--ink); border-radius: 10px; overflow: hidden; }
.seg__btn {
  flex: 1 1 0;
  font: inherit; font-size: .9rem;
  padding: 9px 4px; min-height: 42px;
  border: 0; border-right: 1.5px solid var(--ink);
  background: var(--paper); color: var(--ink);
  cursor: pointer;
}
.seg__btn:last-child { border-right: 0; }
.seg__btn.is-on { background: var(--ink); color: var(--paper); }
.seg__btn:focus-visible { outline: 2.5px solid var(--flag); outline-offset: -3px; }

.player-list .pip.is-bot { border-style: dashed; }
.player-list .who { display: flex; flex-direction: column; min-width: 0; }
.player-list .who small { font-size: .72rem; color: var(--ink-soft); }

/* Drawn icons, sized to whatever line of text or pip holds them. */
.ico { width: 1.15em; height: 1.15em; display: block; flex: 0 0 auto; }
.mode-card__icon .ico { width: 27px; height: 27px; }
.badge__pip .ico { width: 19px; height: 19px; }
.log__icon .ico { width: 17px; height: 17px; }
.badge-line .ico, .toast .ico { width: 18px; height: 18px; color: var(--gold); }
.toast .ico { display: inline-block; vertical-align: -3px; margin-right: 4px; }
.streak .ico { color: var(--flag); }
.hud-stats .ico { display: inline-block; vertical-align: -2px; width: 14px; height: 14px; color: var(--flag); margin-right: 2px; }
.badge.is-locked .badge__pip .ico { color: var(--ink-faint); }

/* The modes take the middle of the home screen; the brand stays at the top and
   the secondary actions stay at the bottom, whatever the phone's height. */
.mode-wrap { flex: 1 1 auto; display: flex; flex-direction: column; justify-content: center; min-height: 0; }

/* ========================================================================== */
/*  Progression, the wallet and the shop                                      */
/* ========================================================================== */

.levelbar {
  display: flex; align-items: center; gap: 11px;
  width: 100%; margin: 10px 0 2px;
  padding: 9px 12px;
  font: inherit; text-align: left; cursor: pointer;
  background: var(--paper);
  border: 1.5px solid var(--rule);
  border-radius: 14px;
  transition: transform .06s ease, background .12s ease;
}
.levelbar:focus-visible { outline: 2.5px solid var(--ink); outline-offset: 2px; }
/* The bar is a door into the record, so a tap has to be answered. It carries no
   key shadow for the .mode-card press to work against, so it answers like a
   .btn instead. */
.levelbar:active { transform: translateY(1px); background: #eceadf; }
/* The career strip reads its chevron in --ink-soft because the whole strip is
   soft; here the surrounding text is full ink, so the chevron says so itself. */
.levelbar__go { color: var(--ink-soft); }
.levelbar__badge {
  flex: 0 0 auto;
  width: 36px; height: 36px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--fairway);
  border: 2px solid var(--ink);
  font-size: 1rem;
}
.levelbar__body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.levelbar__track {
  height: 8px; border-radius: 999px;
  background: #e2e4d6;
  overflow: hidden;
}
.levelbar__fill {
  display: block; height: 100%;
  background: var(--rough);
  border-radius: 999px;
  transition: width .45s cubic-bezier(.3,.9,.4,1);
}
.levelbar__sub { font-size: .76rem; color: var(--ink-soft); }
@media (prefers-reduced-motion: reduce) { .levelbar__fill { transition: none; } }

/* Coins and trophies, always shown together so their relative rarity is obvious. */
.wallet { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; font-size: .9rem; }
.wallet__coins::before,
.wallet__trophies::before {
  content: '';
  display: inline-block;
  width: 13px; height: 13px;
  margin-right: 4px;
  vertical-align: -2px;
  border-radius: 50%;
}
.wallet__coins::before { background: var(--gold); border: 1.5px solid #a97812; }
.wallet__trophies::before {
  background: #cfd6dd; border: 1.5px solid #8b97a3;
  border-radius: 3px 3px 50% 50%;
}
.wallet--big { font-size: 1.05rem; }

/* ------------------------------------------------------------ leaderboard */

/* A rank is read down the left edge, so the place sits in a fixed column and the
   rest of the row flows past it. Three medal colours and nothing else: a hundred
   rows all shouting would say as little as a hundred rows all whispering. */
.rank-list {
  list-style: none;
  margin: 2px 0; padding: 0;
  display: flex; flex-direction: column; gap: 6px;
}
.rank {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 11px;
  background: var(--paper);
  border: 1.5px solid var(--rule);
  border-radius: 12px;
  font-size: .9rem;
}
.rank__place {
  flex: 0 0 auto; min-width: 2.4em;
  font-size: 1rem; color: var(--ink-soft);
  text-align: right;
}
.rank--1 .rank__place, .rank--2 .rank__place, .rank--3 .rank__place { color: var(--ink); }
.rank--1 { border-color: var(--gold); background: #fdf9ef; }
.rank--2 { border-color: #a9b2bb; }
.rank--3 { border-color: #c69a6d; }

.rank__who { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.rank__name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rank__title { font-size: .72rem; color: var(--ink-soft); }
.rank__level {
  flex: 0 0 auto;
  min-width: 2.1em; padding: 2px 6px;
  font-size: .82rem; text-align: center;
  background: var(--fairway);
  border: 1.5px solid var(--ink);
  border-radius: 8px;
}
.rank__trophies { flex: 0 0 auto; font-size: .85rem; min-width: 3.4em; }
/* The column being sorted on is the one that decides the order, so it is the
   one that reads as a number rather than a footnote. */
.rank__level.is-key, .rank__trophies.is-key { color: var(--ink); font-size: .95rem; }
.rank__level:not(.is-key) { background: none; border-color: var(--rule); color: var(--ink-soft); }
.rank__trophies:not(.is-key) { color: var(--ink-soft); }

.rank.is-you { border-color: var(--ink); background: #eceadf; }

/* The asker's own row, pinned below the list — the hundredth place is somebody
   else's news, and this is theirs. */
.rank-mine { margin-top: 4px; }
.rank-mine .rank { border-style: dashed; border-color: var(--ink); background: #eceadf; }
.rank-mine__label { font-size: .74rem; color: var(--ink-soft); margin: 0 0 4px; }

/* ------------------------------------------------------------------- shop */

.shop-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.shop-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(142px, 1fr)); gap: 10px; margin: 10px 0; }

.shop-item {
  display: flex; flex-direction: column; gap: 7px;
  padding: 11px;
  background: var(--paper);
  border: 1.5px solid var(--rule);
  border-radius: 14px;
  text-align: left;
}
.shop-item.is-offer { border-color: var(--gold); background: #fdf9ef; }
.shop-item.is-owned { border-color: var(--rough); }
.shop-item.is-locked { opacity: .55; }

.shop-item__swatch {
  height: 52px;
  border-radius: 10px;
  background: var(--rough);
  display: grid; place-items: center;
  overflow: hidden;
}
.shop-item__name { font-size: .95rem; line-height: 1.15; }
.shop-item__meta { font-size: .74rem; color: var(--ink-soft); }
.shop-item__meta .was { text-decoration: line-through; opacity: .65; margin-right: 5px; }
.shop-item .btn { width: 100%; }

.shop-tier { font-size: .68rem; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-faint); }
.shop-tier.is-rare { color: #2b5fc4; }
.shop-tier.is-epic { color: #7b3fbf; }

.recovery-code {
  font-family: inherit; font-size: 1.15rem; letter-spacing: .18em;
  padding: 8px 12px;
  border: 1.5px dashed var(--ink); border-radius: 8px;
}
/* Beside the name it shares that row's sizing, and it is quiet while masked —
   there is nothing to read yet, and a dashed box round four bullets reads as an
   empty field somebody forgot to fill in. */
#code-row .recovery-code {
  flex: 0 1 auto; min-width: 0;
  font-size: 1rem; padding: 2px 8px;
  border-color: var(--rule); color: var(--ink-soft);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#code-row.is-shown .recovery-code { border-color: var(--ink); color: var(--ink); }
/* Copied. Brief, and colour is not the only signal — the button's aria-label
   changes with it, so it is announced as well as shown. */
.namerow__edit.is-done { color: var(--tree); }

/* What the hole just paid, under the score. */
.earned { display: flex; justify-content: center; gap: 14px; margin: 2px 0 6px; font-size: .95rem; }
.earned:empty { display: none; }
.earned span { display: inline-flex; align-items: center; }

/* A level-up is worth more than a line of text. */
.levelup {
  display: flex; align-items: center; gap: 12px;
  margin: 8px 0;
  padding: 12px 14px;
  border: 2px solid var(--gold);
  border-radius: 14px;
  background: #fdf7ea;
  animation: pop .4s cubic-bezier(.2, 1.5, .5, 1);
}
.levelup__num {
  width: 42px; height: 42px; flex: 0 0 auto;
  display: grid; place-items: center;
  border-radius: 50%; background: var(--gold); color: #fff;
  font-size: 1.15rem;
}
.levelup__body { display: flex; flex-direction: column; }
.levelup__body b { font-weight: 400; }
.levelup__body small { font-size: .78rem; color: var(--ink-soft); }
@media (prefers-reduced-motion: reduce) { .levelup { animation: none; } }

/* A boost in hand, shown beside the hole and stroke count. */
.hud-boost {
  padding: 1px 7px;
  border: 1.5px solid #2b5fc4;
  border-radius: 999px;
  color: #2b5fc4;
  font-size: .82rem;
}

/* Pickups are objects to collect, so they fade rather than vanish when taken. */
/* --------------------------------------------------------- a living board
   Idle motion only: nothing here says anything about the state of the game, so
   it must never compete with the rings, the ball or the die for attention. All
   of it is off under prefers-reduced-motion, at the foot of this block. */

/* Two rows of ripple, opposite ways at different speeds. Each slides exactly one
   pattern tile, so the loop is seamless and the tile can stay small. */
@keyframes pg-drift-a { from { transform: translateX(0); }   to { transform: translateX(-60px); } }
@keyframes pg-drift-b { from { transform: translateX(-60px); } to { transform: translateX(0); } }
.pg-wave--a { animation: pg-drift-a 11s linear infinite; }
.pg-wave--b { animation: pg-drift-b 17s linear infinite; }

/* Trees lean about their own base rather than their centre, or they skate
   sideways instead of swaying. `fill-box` makes that origin the glyph's own
   bounding box, whatever cell it was drawn into. */
@keyframes pg-sway {
  0%, 100% { transform: rotate(-3deg); }
  50%      { transform: rotate(3deg); }
}
.pg-tree {
  transform-box: fill-box; transform-origin: 50% 92%;
  animation-name: pg-sway; animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  /* duration and delay come from the element, so a wood does not move as one */
}

/* The pennant lifts and falls from the pole, which is its left edge. */
/* Rotation as well as shear: the pennant is about fifty units long and a tenth
   of the board wide, so shear alone moves its tip by under a pixel on a phone.
   Swinging it about the pole is what makes the flag read as catching the wind. */
@keyframes pg-flutter {
  0%, 100% { transform: rotate(-3deg) skewY(-5deg) scaleX(1); }
  30%      { transform: rotate(3.5deg) skewY(5deg) scaleX(0.87); }
  62%      { transform: rotate(-1deg) skewY(-2deg) scaleX(1.05); }
}
.pg-pennant {
  transform-box: fill-box; transform-origin: 0% 50%;
  animation: pg-flutter 3.4s ease-in-out infinite;
}

/* The ball's shadow. At rest it is a hint; the shot animator scales and fades it
   as the ball climbs, which is what makes a struck shot read as leaving the
   paper at all. */
.pg-ball-shadow { fill: var(--ink); opacity: .16; pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  .pg-wave--a, .pg-wave--b, .pg-tree, .pg-pennant { animation: none; }
}

.pg-pickups { pointer-events: none; }
.pg-pickup { transition: opacity .3s ease; }
@media (prefers-reduced-motion: reduce) { .pg-pickup { transition: none; } }

/* A labelled switch, for a setting that changes how the game plays. */
.menu-toggle {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 14px;
  border: 1.5px solid var(--rule);
  border-radius: 12px;
  text-align: left;
}
.menu-toggle span { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; }
.menu-toggle b { font-weight: 400; }
.menu-toggle small { font-size: .76rem; color: var(--ink-soft); line-height: 1.3; }

.switch {
  flex: 0 0 auto;
  width: 52px; height: 30px;
  padding: 3px;
  border: 2px solid var(--ink);
  border-radius: 999px;
  background: var(--paper);
  cursor: pointer;
}
/* The track stays 30px because that is what reads as a switch, but a thumb needs
   more than that to land on. The difference is made up by an invisible box over
   the top rather than by growing the switch, so the graphic is untouched. */
@media (pointer: coarse) {
  /* Mostly a taller track, topped up by a small invisible box. The box is kept
     to 4px because the menu stacks controls 8px apart, and a hit area that
     reached further would start answering for the button above it. */
  .switch { position: relative; height: 36px; padding: 6px; }
  .switch::before {
    content: '';
    position: absolute;
    inset: -4px 0;          /* 36 + 4 + 4 = 44 */
  }
}

.switch span {
  display: block;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--ink);
  transition: transform .16s ease;
}
.switch[aria-checked="true"] { background: var(--rough); }
.switch[aria-checked="true"] span { transform: translateX(22px); }
.switch:focus-visible { outline: 2.5px solid var(--flag); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .switch span { transition: none; } }

/* Aiming lines sit under the ball but must stay tappable. */
.pg-dirs { cursor: crosshair; }
.pg-line { pointer-events: none; }
.pg-dir .pg-target-num { pointer-events: none; }

/* ========================================================================== */
/*  The illustrated guide                                                     */
/* ========================================================================== */

.rules-body { overflow-y: auto; max-height: 68vh; padding-right: 4px; }
.rules-body p { line-height: 1.55; }

/* Jump links, so a long guide is still navigable on a phone. */
/* One scrolling row rather than a wrapping block: wrapped, it grew to three rows
   on a phone, ate a third of the screen and let the guide show through behind it. */
.dg-contents-wrap {
  position: sticky; top: 0; z-index: 2;
  margin: 0 0 12px;
  background: var(--paper);
  box-shadow: 0 6px 8px -6px rgba(40, 50, 35, .25);
}
.dg-contents {
  display: flex; gap: 6px;
  padding: 8px 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.dg-contents::-webkit-scrollbar { display: none; }
/* A fade on the trailing edge, so it is obvious the row scrolls. */
.dg-contents-wrap::after {
  content: '';
  position: absolute; top: 0; right: 0; bottom: 0;
  width: 30px;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--paper));
}
.dg-jump {
  flex: 0 0 auto;
  font-size: .76rem;
  padding: 5px 11px;
  border: 1.5px solid var(--rule);
  border-radius: 999px;
  color: var(--ink-soft);
  text-decoration: none;
  white-space: nowrap;
}
.dg-jump:hover { border-color: var(--ink); color: var(--ink); }

.dg-section { scroll-margin-top: 44px; padding-bottom: 6px; }
.dg-section + .dg-section { border-top: 1px solid var(--rule); padding-top: 12px; margin-top: 12px; }
.dg-section h3 { margin: 4px 0 8px; }

/* Diagrams. Every one is drawn from the board's own palette and glyphs. */
.dg { display: block; width: 100%; height: auto; }
.dg-wide { margin: 10px 0; }

.dg-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
  gap: 9px;
  margin: 10px 0 12px;
}
/* These two have a fixed number of things in them and should not reflow into an
   orphan on a narrow phone. */
.dg-row--pars { grid-template-columns: repeat(4, 1fr); align-items: end; }
.dg-row--ground { grid-template-columns: repeat(5, 1fr); gap: 6px; }
.dg-row--ground figcaption { font-size: .68rem; }
.dg-row--finds { grid-template-columns: repeat(4, 1fr); gap: 7px; }

.dg-cell { margin: 0; text-align: center; }
.dg-cell figcaption { font-size: .74rem; color: var(--ink-soft); margin-top: 5px; line-height: 1.25; }
.dg-cell figcaption b { display: block; color: var(--ink); font-weight: 400; font-size: .8rem; }
.dg-cell figcaption small { display: block; font-size: .7rem; }

.dg-legend { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 7px; text-align: center; }
.dg-legend span { display: flex; flex-direction: column; font-size: .74rem; color: var(--ink-soft); }
.dg-legend b { color: var(--ink); font-weight: 400; font-size: .78rem; }

.dg-seats { display: flex; gap: 8px; margin: 10px 0; flex-wrap: wrap; }
.dg-seat {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border: 3px solid var(--seat); color: var(--seat);
  background: #fff; border-radius: 50%;
  font-size: .8rem;
}

.dg-bots { margin: 2px 0 10px; }

/* ---------------------------------------------------------- the animations */

/* A ball carried one space at a time down a run of arrows. */
@keyframes dgSlide {
  0%, 8%    { transform: translate(0, 0); }
  22%, 30%  { transform: translate(105px, 0); }
  44%, 52%  { transform: translate(210px, 0); }
  66%, 100% { transform: translate(315px, 0); }
}
.dg-slope .dg-slide { animation: dgSlide 4s cubic-bezier(.4, 0, .3, 1) infinite; }

/* A struck shot: out, up, and down onto the green. */
@keyframes dgFly {
  0%, 6%   { transform: translate(0, 0); }
  27%      { transform: translate(78px, -52px); }
  46%      { transform: translate(155px, -74px); }
  65%      { transform: translate(232px, -52px); }
  84%, 100%{ transform: translate(310px, 0); }
}
.dg-flight .dg-fly { animation: dgFly 3.4s ease-in-out infinite; }

/* The boost, drawing attention to itself. */
@keyframes dgPulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.08); }
}
.dg-pulse { transform-origin: 50% 50%; animation: dgPulse 1.9s ease-in-out infinite; }

@media (prefers-reduced-motion: reduce) {
  .dg-slope .dg-slide,
  .dg-flight .dg-fly,
  .dg-pulse { animation: none; }
  /* Without movement the ball should still sit where the diagram means it to. */
  .dg-slope .dg-slide { transform: translate(315px, 0); }
  .dg-flight .dg-fly { transform: translate(310px, 0); }
}
.dg-row--lies { grid-template-columns: repeat(3, 1fr); }

/* ========================================================================== */
/*  Looking for players                                                       */
/* ========================================================================== */

.queue { text-align: center; padding: 10px 0 4px; }
.queue h2 { margin-top: 14px; }
.queue__line { color: var(--ink-soft); font-size: .9rem; margin: 6px 0 0; min-height: 1.3em; }

/* Rings going out from a ball, which is what looking for someone feels like. */
.queue__radar {
  position: relative;
  width: 132px; height: 132px;
  margin: 0 auto;
  display: grid;
  place-items: center;
}
.queue__radar span {
  position: absolute;
  inset: 0;
  border: 2.5px solid var(--rough);
  border-radius: 50%;
  opacity: 0;
  animation: queuePing 2.4s cubic-bezier(.2, .6, .4, 1) infinite;
}
.queue__radar span:nth-child(2) { animation-delay: .8s; }
.queue__radar span:nth-child(3) { animation-delay: 1.6s; }
.queue__ball { width: 46px; height: 46px; position: relative; }

@keyframes queuePing {
  0%   { transform: scale(.34); opacity: 0; }
  18%  { opacity: .9; }
  100% { transform: scale(1); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .queue__radar span { animation: none; opacity: .35; transform: scale(1); }
  .queue__radar span:nth-child(2) { transform: scale(.72); }
  .queue__radar span:nth-child(3) { transform: scale(.48); }
}

/* Held to the bottom so the buttons do not jump up when they appear. */
.queue__actions { margin-top: auto; padding-top: 12px; display: grid; gap: 8px; }

/* How long this has been going on. Tabular figures because the seconds tick and
   a clock whose width changes every digit reads as a flicker. */
.queue__clock {
  margin: 0 0 2px;
  text-align: center;
  font-size: 1.5rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
  transition: color .25s ease;
}
/* Once the room has a start time it is counting down to something real, so it
   stops being a tally of how long you have put up with this. */
.queue__clock.is-starting { color: var(--ink); }

/* A countdown once enough players are in. */
.queue__line b { color: var(--ink); font-weight: 400; }
