body,
html {
  margin: 0;
  background-color: #000;
  color: #444;
  width: 100%;
  height: 100%;
  border: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

main {
  display: contents;
}

::selection {
  color: white;
}

div {
  color: antiquewhite;
  touch-action: none;
}

h1,
p {
  margin: 0 0 1em;
}

#snookerScore {
  font-family: var(--font-family-primary);
  font-size: 32px;
  color: yellow;
  text-align: center;
  filter: drop-shadow(0.08em 0.08em black);
  display: flex;
  flex-flow: row wrap;
  justify-content: center;
  align-items: center;
  gap: 12px;
}

#snookerScoreOverlay {
  position: absolute;
  bottom: 10px;
  right: 80px;
}

.p1Score,
.p2Score,
.hudMiddle {
  font-size: 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.p1Score.is-active,
.p2Score.is-active {
  font-weight: 800;
  filter: brightness(1.2);
}

.breakScore {
  font-size: 32px;
  font-style: italic;
  color: yellow;
  text-align: center;
  margin-top: 0.2em;
}

.view3d {
  width: 100%;
  flex: 1;
  min-height: 0;
  cursor: none;
  caret-color: transparent;
  -webkit-user-drag: none;
  user-select: none;
  position: relative;
}

.view3d:focus {
  outline: none;
}

canvas {
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  border-radius: 5px;
  -webkit-user-drag: none;
  user-select: none;
}

.panel {
  width: 100%;
  height: 70px;
  display: flex;
  flex-direction: row;
  align-items: flex-end;
  gap: 0;
  padding: 0;
  background: linear-gradient(rgb(0 64 94), #000214);
  border-radius: 0;
  border-top: 1px solid #2d1e16;
  box-sizing: border-box;
  position: relative;
}

.aimControlColumn {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-end;
  gap: 4px;
  position: relative;
}

.toggleButton {
  height: 100%;
  width: 20%;
  margin-right: 4px;
  aspect-ratio: 0.4;
  border-radius: 10px;
  border-width: 4px;
  padding: 0;
  max-width: 80px;
}

.toggleButton:active,
.menuButton:active {
  transform: scale(0.97);
}

a.pill {
  outline: none;
  background: rgb(0 0 0 / 40%);
  border-radius: 8px;
  text-decoration: none;
  border: 1px solid rgb(255 255 255 / 15%);
  margin: 0;
  padding: 0 8px;
  font-size: 12px;
  height: 30px;
  display: flex;
  align-items: center;
  box-sizing: border-box;
}

a {
  color: lightblue;
}

.constants {
  background: linear-gradient(#495155, #0b0b0e);
  border-radius: 5px;
  position: fixed;
  right: 0;
  top: 0;
  visibility: hidden;
  padding: 5px;
  border: double;
  max-width: 50%;
  font-size: 12px;
}

label {
  display: inline-block;
  width: 9ch;
}

/* Bot Debug Overlay */
#botDebugOverlay {
  position: absolute;
  top: 10px;
  left: 10px;
  width: 320px;
  min-height: 30px;
  max-height: 400px;
  background: rgb(0 0 0 / 40%);
  border: 1px solid rgb(255 255 255 / 15%);
  border-radius: 8px;
  padding: 0 8px;
  font-family: var(--font-family-mono);
  font-size: 16px;
  color: #ccc;
  z-index: 2000;
  overflow: hidden;
  display: none;
  box-sizing: border-box;
  align-items: center;
  flex-direction: column;
}

#botDebugOverlay header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 0;
  padding-bottom: 0;
  border-bottom: none;
  min-height: 30px;
}

#botDebugOverlay header button {
  background: transparent;
  border: 1px solid #666;
  color: #aaa;
  cursor: pointer;
  padding: 2px 6px;
  font-size: 12px;
  border-radius: 4px;
  height: 22px;
}

#analysisPanel button:disabled {
  opacity: 0.5;
  cursor: default;
}

#botDebugOverlay header button:hover {
  background: #333;
  color: #fff;
}

#botDebugOverlay header div {
  display: flex;
  align-items: center;
  gap: 8px;
}

#botDebugLog {
  margin: 4px 0 0;
  padding: 0;
  max-height: 340px;
  overflow-y: auto;
  white-space: pre-wrap;
  word-break: break-all;
}

#botDebugLog::-webkit-scrollbar {
  width: 8px;
}

#botDebugLog::-webkit-scrollbar-track {
  background: #222;
}

#botDebugLog::-webkit-scrollbar-thumb {
  background: #555;
  border-radius: 4px;
}

/* Compact mode */
#botDebugOverlay.compact {
  width: 50px;
  height: 30px;
  padding: 0 8px;
  display: flex;
  align-items: center;
  box-sizing: border-box;
}

#botDebugOverlay.compact #botDebugLog {
  max-height: 0;
  overflow: hidden;
}

#botDebugOverlay.compact #botDebugClear {
  display: none;
}

/* Lobster emoji in header. Addressed by class rather than by the #botDebugToggle
   id so this early rule stays low in the cascade order. */
.bot-debug-emoji {
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  display: flex;
  align-items: center;
  position: relative;
  top: -2px;
}

.lobby-btn {
  padding: 6px;
  background: rgb(255 255 255 / 10%);
  color: white;
  border: 1px solid rgb(255 255 255 / 20%);
  border-radius: 6px;
  cursor: pointer;
  font-size: 13px;
  transition: all 0.2s ease;
}

.lobby-btn:hover {
  background: rgb(255 255 255 / 20%);
  border-color: rgb(255 255 255 / 40%);
}

#botDebugLog::-webkit-scrollbar-thumb:hover {
  background: #777;
}

.drill-panel {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 1000;
  pointer-events: auto;
}

.drill-panel-top {
  top: 10px;
  transform: none;
}

.drill-panel-right {
  inset: auto 10px 49px auto;

  /* .outerMenu (hamburger/camera buttons) overflows 39px above #panel's top
     edge, i.e. above #viewP1's bottom edge; clear that plus a small gap. */
  transform: none;
}

/* When the elevation panel is open the Hit button shifts up; the drill buttons
   are lifted above it. DrillPanel sets `bottom` inline at runtime by measuring
   the Hit button's actual top; here we just switch to bottom-anchoring. */
.drill-panel.elevation-open {
  top: auto;
  transform: none;
}

/* The 44px spacer aligns Preview with the Hit button when centered; it is not
   needed once the stack is lifted above the elevation panel. */
.drill-gap {
  height: 44px;
}

.drill-panel.elevation-open .drill-gap {
  height: 0;
}

.drill-btn {
  width: 90px;
  height: 44px;
  border-radius: 8px;
  border: 2px solid rgb(255 255 255 / 25%);
  background: rgb(0 30 60 / 75%);
  color: antiquewhite;
  font-size: 13px;
  font-family: var(--font-family-primary, sans-serif);
  cursor: pointer;
  user-select: none;
  transition:
    opacity 0.15s,
    background 0.15s;
}

.drill-btn:disabled {
  opacity: 0.3;
  cursor: default;
}

.drill-btn:not(:disabled):hover {
  background: rgb(0 60 110 / 85%);
  border-color: rgb(255 255 255 / 50%);
}

.drill-btn:not(:disabled):active {
  transform: scale(0.96);
}

/* ===========================================================================
   Shot analysis split-screen mode (?ruletype=threecushion&practice&analysis)
   Left: live 3D table (#viewP1). Right: results panel (#analysisPanel). The
   table pane is responsively sized so CameraTop orients it (upright in a
   narrow pane, horizontal in a wide one) automatically.
   =========================================================================== */

/* Grid: table top-left, the game's control panel bottom-left, analysis panel
   down the right. Using grid (not flex) keeps #panel's own internal layout
   (cue-ball spin widget, hit button, power slider) intact — we just hand it the
   bottom-left cell and hide the menu buttons. */
body.analysis-mode {
  display: grid;
  grid-template: "table panel" 1fr "controls panel" auto / 72% 28%;
}

body.analysis-mode #viewP1 {
  grid-area: table;
  width: auto;
  height: 100%;

  /* Let the table cell shrink: the WebGL canvas carries an explicit pixel size,
     which would otherwise become the cell's min-content size and overflow. */
  min-width: 0;
  min-height: 0;
}

/* The renderer sets an explicit pixel size on its canvas; cap it to the cell. */
.analysis-content canvas {
  background: #0d0d10;
  border-radius: 6px;
  display: block;
  max-width: 100%;
  height: auto;
  image-rendering: auto;
}

body.analysis-mode #viewP1 > canvas {
  max-width: 100%;
}

/* Keep the control panel, but show only the aim controls the analysis view
   wants (cue-ball spin + elevation, hit button, speed slider). Hide the game
   menu/share/concede/comment buttons; the camera toggle is the floating button. */
body.analysis-mode #panel {
  grid-area: controls;
  background: transparent;
  border-top: 0;
}

body.analysis-mode .outerMenu,
body.analysis-mode #commentMenu {
  display: none;
}

#analysisPanel {
  grid-area: panel;
  min-width: 0;
  height: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  background: #15151a;
  border-left: 1px solid #2a2a30;
  color: #eee;
  font-family: system-ui, sans-serif;
  overflow-y: auto;
}

.analysis-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  border-bottom: 1px solid #2a2a30;
}

.analysis-title {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.3px;
}

.analysis-content {
  padding: 14px;
}

/* Floating camera-toggle, lower-right of the table pane (only active control). */
.analysis-camera-btn {
  position: absolute;
  right: 12px;
  bottom: 12px;
  width: 48px;
  height: 48px;
  border-radius: 8px;
  border: 1px solid rgb(255 255 255 / 25%);
  background: rgb(0 30 60 / 75%);
  color: antiquewhite;
  font-size: 22px;
  cursor: pointer;
  z-index: 5;
}

.analysis-camera-btn:hover {
  background: rgb(0 60 110 / 85%);
}

/* Camera state classes set dynamically via JS */
.menuButton.aim {
  /* default aim view - no extra styling needed */
}

.menuButton.aimz {
  /* zoomed-out aim view - slightly larger emoji */
  font-size: 28px;
}

.menuButton.topview {
  /* top-down view */
}

/* Stack table / controls / analysis vertically on portrait/narrow screens. */
@media (orientation: portrait) {
  body.analysis-mode {
    grid-template: "table" 1fr "controls" auto "panel" 45% / 100%;
  }

  #analysisPanel {
    border-left: 0;
    border-top: 1px solid #2a2a30;
  }
}

/* Chromeless mode (L key toggles body.chromeless): collapse the lower control
   panel while keeping the right-hand menu column (hamburger/submenu + camera
   button) visible. .outerMenu is taller than #panel and overflows above its
   top edge, so overflow must stay visible; the panel's visibility:hidden and
   pointer-events:none are re-asserted on .outerMenu to bring it back. */
body.chromeless #panel {
  height: 0;
  min-height: 0;
  padding: 0;
  border: 0;
  overflow: visible;
  visibility: hidden;
  pointer-events: none;
}

body.chromeless #panel .outerMenu {
  visibility: visible;
  pointer-events: auto;
}

/* AimInputs sets an inline visibility:visible on the spin-area overlap ball,
   which would override #panel's inherited visibility:hidden and leak the spin
   widget back onto the table. display:none hides it (and the hit button,
   tilt/elevation and power slider) regardless of inline styles. */
body.chromeless #panel .aimControlColumn,
body.chromeless #panel .powerSliderContainer {
  display: none;
}

/* --- analysis results (ported from the former standalone page) --- */
.analysis-content h2 {
  font-size: 14px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: #9aa0a6;
  margin: 16px 0 8px;
}

.analysis-status {
  color: #9aa0a6;
  font-size: 13px;
  margin-bottom: 8px;
}

.analysis-results .legend {
  display: flex;
  gap: 14px;
  font-size: 13px;
  color: #9aa0a6;
  margin: 8px 0;
}

.analysis-results .legend span::before {
  content: "";
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  margin-right: 5px;
  vertical-align: middle;
}

.analysis-results .legend .ok::before {
  background: #2ec27e;
}

.analysis-results .legend .fail::before {
  background: #e0556b;
}

.analysis-results .legend .original::before {
  background: transparent;
  border: 2px solid #fff;
}

.plot-wrap {
  position: relative;
  display: inline-block;
  max-width: 100%;
}

.bars {
  margin-top: 14px;
}

.bar-row {
  margin: 16px 0;
}

.bar-label {
  font-size: 14px;
  color: #9aa0a6;
  margin-bottom: 2px;
}

/* Header row of a 1-D bar: the on-demand "Show" button left of the label. */
.bar-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 2px;
}

.bar-head .bar-label {
  margin-bottom: 0;
}

.bar-show {
  flex: none;
  padding: 2px 10px;
  font-size: 12px;
  color: #fff;
  border: 0;
  border-radius: 5px;
  cursor: pointer;
  background: #2f7d6e;
}

.bar-show:disabled {
  opacity: 0.5;
  cursor: default;
}

.bar-show:hover:not(:disabled) {
  background: #3a988e;
}

/* The bar whose 1-D sweep is currently shown (blue = the live/active accent). */
.bar-row.active .bar-show {
  background: #4aa3ff;
}

.bar-row.active .bar-label {
  color: #cfd3d8;
}

.bar-shiftrow {
  position: relative;
  height: 16px;
  font-size: 13px;
  color: #9aa0a6;
  margin-bottom: 2px;
}

.bar-shift-label {
  position: absolute;
  top: 0;
  white-space: nowrap;
}

.bar-shift-label--left {
  padding-right: calc(6px + 3ch);
}

.bar-shift-label--right {
  padding-left: calc(6px + 3ch);
}

/* Row containing the track+ruler column flanked by the two step buttons.
   flex-start (not center) — .bar-trackcol holds the track AND the taller
   ruler below it, so centering against the whole column would push the
   buttons below the track; flex-start aligns them with the track itself,
   the column's first/top child. */
.bar-trackrow {
  display: flex;
  align-items: flex-start;
  gap: 4px;
}

.bar-trackcol {
  flex: 1;
  min-width: 0;
}

.bar-track {
  position: relative;
  display: flex;
  height: 16px;
  border-radius: 3px;
  overflow: hidden;
  background: #0d0d10;
}

.bar-cell {
  flex: 1;
}

/* A small colored patch under the marker, showing the live point's result
   (red/green, matching the scatter dot colors) even when the bar hasn't been
   Shown — also doubles as a visual cue for the size of one grid step.
   Positioned/sized by positionMarkers; colored via .scored/.missed by
   updateBarLiveCellFill, always on, independent of which bar is "active". */
.bar-live-cell {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 1;
  background: transparent;
  pointer-events: none;
}

.bar-live-cell.scored {
  background: #2ec27e;
}

.bar-live-cell.missed {
  background: #e0556b;
}

/* The bowtie marker = the current shot's value on that bar (two triangles
   meeting at the marker's position). Always plain white — coloring it
   directly used to camouflage it against an identically-colored cell once
   the bar was Shown (the live-cell patch above now carries the color
   instead). Positioned by AnalysisPanel via left %. */
.bar-marker {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 12px;
  margin-left: -6px;
  pointer-events: none;
  z-index: 2;
}

.bar-marker::before,
.bar-marker::after {
  content: "";
  position: absolute;
  top: 0;
  width: 0;
  height: 0;
  border-style: solid;
}

.bar-marker::before {
  left: 0;
  border-width: 8px 6px 8px 0;
  border-color: transparent #fff transparent transparent;
}

.bar-marker::after {
  right: 0;
  border-width: 8px 0 8px 6px;
  border-color: transparent transparent transparent #fff;
}

/* Step buttons: move the marker exactly one grid step left/right. Solid CSS
   triangles (same construction as the marker) so the two read as one visual
   language; muted grey by default, white on hover, dimmed once the studied
   window's edge is reached (no further step possible). */
.bar-step {
  flex: none;
  width: 14px;
  height: 16px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  position: relative;
}

.bar-step::before {
  content: "";
  position: absolute;
  top: 0;
  width: 0;
  height: 0;
  border-style: solid;
}

.bar-step--left::before {
  right: 1px;
  border-width: 6px 7px 6px 0;
  border-color: transparent #cdd1d6 transparent transparent;
}

.bar-step--right::before {
  left: 1px;
  border-width: 6px 0 6px 7px;
  border-color: transparent transparent transparent #cdd1d6;
}

.bar-step:disabled {
  cursor: default;
}

.bar-step--left:disabled::before {
  border-right-color: #3a3a40;
}

.bar-step--right:disabled::before {
  border-left-color: #3a3a40;
}

.bar-step--left:hover:not(:disabled)::before {
  border-right-color: #fff;
}

.bar-step--right:hover:not(:disabled)::before {
  border-left-color: #fff;
}

/* The single white ring = the current shot's spin on the scatter. Positioned by
   AnalysisPanel via left/top %. */
.spin-marker {
  position: absolute;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid #fff;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 3;
  box-shadow: 0 0 0 1px rgb(0 0 0 / 50%);
}

/* Filled with the live point's simulated result (green/red, matching the
   scatter dot colors) once known; hollow/white while pending or while a 1-D
   bar is the active display. */
.spin-marker.scored {
  background: #2ec27e;
}

.spin-marker.missed {
  background: #e0556b;
}

.bar-ruler {
  position: relative;
  height: 22px;
  margin-top: 2px;
}

.bar-tick {
  position: absolute;
  top: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  transform: translateX(-50%);
}

.bar-tick--start {
  align-items: flex-start;
  transform: none;
}

.bar-tick--end {
  align-items: flex-end;
  transform: translateX(-100%);
}

.bar-tick-mark {
  width: 1px;
  height: 4px;
  background: #51515a;
}

.bar-tick-label {
  font-size: 12px;
  color: #9aa0a6;
  margin-top: 2px;
  white-space: nowrap;
}

.analysis-results .error {
  color: #e0556b;
  font-size: 15px;
}

.spin-show {
  position: absolute;
  left: 10px;
  bottom: 14px;
  width: auto;
  padding: 8px 14px;
  font-size: 14px;
  color: #fff;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  background: #2f7d6e;
  box-shadow: 0 1px 4px rgb(0 0 0 / 40%);
}

.spin-show:hover:not(:disabled) {
  background: #3a988e;
}

/* The spin scatter is currently the live display (blue = the live/active
 * accent, matching .bar-row.active .bar-show). Needs an explicit :hover
 * override too — otherwise .spin-show:hover (2 selectors) outranks
 * .spin-show.active (also 2) on hover and the green flashes back. */
.spin-show.active,
.spin-show.active:hover {
  background: #4aa3ff;
}

.analysis-toolbar {
  display: none;
}

.analysis-toolbar button.secondary {
  background: #34343c;
  color: #fff;
  border: 0;
  border-radius: 6px;
  padding: 7px 12px;
  font-size: 13px;
  cursor: pointer;
}

/* ===========================================================================
   About Panel Overlay Styles
   =========================================================================== */

.about-more {
  position: fixed;
  inset: 0;
  z-index: 3000;
}

/* `display` is only set in the open state: the closed state relies on the
   browser's own rule for the `hidden` attribute, which for hidden="until-found"
   is content-visibility rather than display. Setting `display` here would
   override it and break find-in-page reveal and indexing. */
.about-more:not([hidden]) {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The blur cannot live on .about-more: while closed the element keeps its box
   (content-visibility skips its contents, not its own paint). A pseudo-element
   is content, so it is skipped too. */
.about-more::before {
  content: "";
  position: absolute;
  inset: 0;
  backdrop-filter: blur(3px);
}

.about-more[hidden] {
  pointer-events: none;
}

.about-more .about-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: min(38rem, calc(100vw - 5rem));
  max-height: 80dvh;
  overflow: hidden;
  background: linear-gradient(
    180deg,
    rgb(43 43 43) 0%,
    rgb(29 29 29) 55%,
    rgb(17 17 17) 100%
  );
  border: 1px solid rgb(255 255 255 / 15%);
  border-radius: 8px;
  box-shadow: 0 4px 16px rgb(0 0 0 / 30%);
  color: #e0e0e0;
  font-family: Exo, sans-serif;
  font-weight: 200;
}

/* Scrolling sits on an inner element so the close button can stay pinned
   to the panel corner instead of scrolling away with the content. */
.about-scroll {
  overflow-y: auto;
  scrollbar-width: none;
  touch-action: pan-y;
  min-height: 0;

  /* index.css sets `div { color: antiquewhite }`; keep the panel's text
     colour instead of inheriting that one. */
  color: inherit;

  /* No top padding: the first section's 2px top margin provides it, so
     the gap above Play matches the 2px between the panels. */
  padding: 0 2px 2px;
}

.about-panel a {
  color: #8ab6f8;
}

.about-close {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 1;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;

  /* The × ink in Exo sits above the line box centre; flex centring then
     leaves it looking high. Padding the top pushes the centred line down by
     half the padding, so 2px nudges the glyph down 1px. */
  padding: 2px 0 0;
  background: linear-gradient(
    180deg,
    rgb(255 255 255 / 16%) 0%,
    rgb(255 255 255 / 6%) 100%
  );
  border: 1px solid rgb(255 255 255 / 25%);
  border-radius: 6px;
  box-shadow:
    0 1px 2px rgb(0 0 0 / 40%),
    inset 0 1px 0 rgb(255 255 255 / 20%);
  color: #eee;
  font-family: inherit;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  transition:
    background 0.15s,
    transform 0.1s,
    box-shadow 0.15s;
}

.about-close:hover {
  background: linear-gradient(
    180deg,
    rgb(255 255 255 / 24%) 0%,
    rgb(255 255 255 / 12%) 100%
  );
  border-color: rgb(255 255 255 / 40%);
  box-shadow:
    0 2px 4px rgb(0 0 0 / 50%),
    inset 0 1px 0 rgb(255 255 255 / 30%);
}

.about-close:active {
  transform: translateY(1px);
  box-shadow:
    0 1px 1px rgb(0 0 0 / 60%),
    inset 0 1px 2px rgb(0 0 0 / 40%);
}

.about-panel section,
.about-section {
  margin-top: 2px;
  padding: 2px 10px;
  background: linear-gradient(
    180deg,
    rgb(255 255 255 / 4%) 0%,
    rgb(255 255 255 / 1%) 100%
  );
  border: 1px solid rgb(255 255 255 / 12%);
  border-radius: 6px;
}

.about-panel summary {
  cursor: pointer;
  font-weight: 400;
  font-size: 0.95rem;
  margin: 10px 0 4px;
  text-align: center;
}

.about-panel h2 {
  font-size: 0.95rem;
  font-weight: 700;
}

.about-panel h3 {
  font-size: 0.85rem;
  font-weight: 700;
  margin: 10px 0 4px;
}

/* The keyword list is a footnote, not reading copy. */
.about-panel h2.keywords {
  font-size: 0.8rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  margin: 12px 0 4px;
}

.about-panel p.keywords {
  font-size: 0.75rem;
  font-weight: 400;
  line-height: 1.5;
  color: #9a9a9a;
}

.about-panel .help-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  margin-bottom: 2px;
  font-size: 0.75rem;
  color: #eee;
}

.about-panel .phone {
  position: relative;
  width: 100%;
  max-width: 360px;
  aspect-ratio: 16 / 9;
  margin: 6px auto 10px;
  border: 2px solid #666;
  border-radius: 12px;
  overflow: hidden;
  background: linear-gradient(to bottom, #1b2d44 0%, #13212f 50%, #1a1611 100%);
}

.about-panel .top,
.about-panel .bottom {
  position: absolute;
  left: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.about-panel .top {
  top: 0;
  height: 42%;
}

.about-panel .bottom {
  bottom: 0;
  height: 42%;
}

.about-panel .divider {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  border-top: 2px dashed rgb(255 255 255 / 25%);
  transform: translateY(-50%);
}

.about-panel .zoom {
  position: absolute;
  right: 18px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  opacity: 0.9;
}

.about-panel .zoom-arrow {
  width: 2px;
  height: 90px;
  background: white;
  border-radius: 999px;
  position: relative;
}

.about-panel .zoom-arrow::before,
.about-panel .zoom-arrow::after {
  content: "";
  position: absolute;
  left: 50%;
  width: 10px;
  height: 10px;
  border-top: 3px solid white;
  border-right: 3px solid white;
}

.about-panel .zoom-arrow::before {
  top: -2px;
  transform: translateX(-50%) rotate(-45deg);
}

.about-panel .zoom-arrow::after {
  bottom: -2px;
  transform: translateX(-50%) rotate(135deg);
}

.about-panel .zoom-label {
  font-size: 0.72rem;
  writing-mode: vertical-rl;
  text-orientation: mixed;
  letter-spacing: 0.08em;
  opacity: 0.7;
}

.about-panel .balls {
  display: flex;
  gap: 8px;
  margin-bottom: 10px;
}

.about-panel .ball {
  width: 1px;
  height: 1px;
  border-radius: 50%;
  background: white;
  opacity: 0.9;
}

.about-panel .arrow {
  position: relative;
  height: 2px;
  background: white;
  border-radius: 999px;
}

.about-panel .arrow::before,
.about-panel .arrow::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 10px;
  height: 10px;
  border-top: 3px solid white;
  border-right: 3px solid white;
}

.about-panel .arrow::before {
  left: -2px;
  transform: translateY(-50%) rotate(-135deg);
}

.about-panel .arrow::after {
  right: -2px;
  transform: translateY(-50%) rotate(45deg);
}

.about-panel .fine {
  width: 110px;
}

.about-panel .coarse {
  width: 240px;
}

.about-panel .label {
  margin-top: 8px;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.03em;
}

.about-panel .sub {
  margin-top: 4px;
  font-size: 0.78rem;
  opacity: 0.7;
}

.about-panel .input-grid {
  display: grid;
  grid-template-columns: repeat(
    2,
    minmax(0, max-content) minmax(0, max-content)
  );
  justify-content: center;
  align-items: center;
  gap: 3px 6px;
  margin: 0 auto 10px;
  width: 100%;
  box-sizing: border-box;
}

.about-panel .input-grid .help-row {
  display: contents;
}

.about-panel kbd {
  background: #333;
  color: #fff;
  padding: 1px 4px;
  border-radius: 4px;
  font-family: Exo, monospace;
  border-bottom: 2px solid #555;
  font-size: 0.72rem;
  text-align: center;
  display: inline-block;
  justify-self: start;
  white-space: nowrap;
}

.about-panel .input-grid span {
  font-size: 0.75rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Fixed-size buttons, at most four per row. The max-width is four buttons
   (4 x 5rem, below) plus their three gaps, so a fifth can never fit on a
   wide panel; narrower panels wrap to fewer per row and every row is centred. */
.mode-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px;
  max-width: calc(20rem + 12px);
  margin: 4px auto;
}

/* Play and Controls are UI labels, not content headings: keeping them
   out of the heading outline leaves the About h1 as the first heading
   on the page. */
.play-section .panel-title,
#controls .panel-title {
  margin: 0 0 6px;
  font-size: 15px;
  font-weight: 700;
  text-align: center;
}

.mode-nav a {
  position: relative;
  box-sizing: border-box;
  width: 5rem;
  height: 2rem;
  padding: 0 2px;

  /* One line centred in the fixed height (the 2px accounts for the top and
     bottom border), so the label stays a block box and can still ellipsis. */
  line-height: calc(2rem - 2px);
  text-align: center;
  background: linear-gradient(
    180deg,
    rgb(255 255 255 / 15%) 0%,
    rgb(255 255 255 / 5%) 100%
  );
  border: 1px solid rgb(255 255 255 / 20%);
  border-bottom-color: rgb(0 0 0 / 40%);
  border-radius: 5px;
  color: white;
  text-decoration: none;
  font-size: 12px;
  font-weight: 400;
  box-shadow:
    0 1px 2px rgb(0 0 0 / 30%),
    inset 0 1px 0 rgb(255 255 255 / 15%);
  transition:
    background 0.15s,
    border-color 0.15s,
    transform 0.1s,
    box-shadow 0.15s;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mode-nav a:hover {
  background: linear-gradient(
    180deg,
    rgb(255 255 255 / 25%) 0%,
    rgb(255 255 255 / 10%) 100%
  );
  border-color: rgb(255 255 255 / 40%);
  border-bottom-color: rgb(0 0 0 / 50%);
  box-shadow:
    0 2px 4px rgb(0 0 0 / 40%),
    inset 0 1px 0 rgb(255 255 255 / 25%);
}

.mode-nav a:active {
  transform: translateY(1px);
  box-shadow:
    0 1px 1px rgb(0 0 0 / 50%),
    inset 0 1px 2px rgb(0 0 0 / 40%);
}

/* The rim glint of an incoming challenge (.challenge-pill::after in
   lobby.css), swept along the border of the panel's buttons on hover.
   A plain hover background alone would not read as the same effect, so
   the ring is painted by a pseudo-element and masked to its 1px border
   (mask-composite hides the content box). The element is pulled out by
   1px because an absolutely positioned box is laid out against the
   padding box. Applying the animation only while hovering restarts it
   on every fresh hover; the keyframes are lobby.css's, so both glints
   stay identical. */
.about-close::after,
.mode-nav a::after {
  content: "";
  position: absolute;
  inset: -1px;
  padding: 1px;
  border-radius: inherit;
  background: linear-gradient(
    135deg,
    rgb(255 255 255 / 0%) 46%,
    rgb(255 255 255 / 90%) 50%,
    rgb(255 255 255 / 0%) 54%
  );
  background-size: 200% 200%;
  background-position: 100% 100%;
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
  opacity: 0;
  pointer-events: none;
}

.about-close:hover::after,
.mode-nav a:hover::after {
  animation: challenge-glint 0.5s ease-out 1;
}

@media (prefers-reduced-motion: reduce) {
  .about-close:hover::after,
  .mode-nav a:hover::after {
    animation: none;
  }
}
