/* Nine Ball -- the page around the table.
 *
 * Everything here is sized in one fluid unit so a 320px phone and a 27 inch
 * monitor both get a legible, reachable layout. Nothing asks anyone to turn
 * their device: the table is framed by the camera, and the chrome moves to
 * whichever edge has room.
 */

:root {
  --ink: #f2ede4;
  --dim: #9aa3ac;
  --gold: #f0b73e;
  --cloth: #0f6b45;
  --panel: rgba(12, 15, 18, 0.82);
  --edge: rgba(255, 255, 255, 0.10);
  --u: clamp(11px, 1.7vmin + 6px, 17px);
  --tap: clamp(44px, 8.2vmin, 58px);
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0; padding: 0; height: 100%; overflow: hidden;
  background: #07090b; color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: var(--u);
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
  touch-action: manipulation;
}

#app { position: fixed; inset: 0; }

#gl {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
  touch-action: none;
  cursor: crosshair;
}

/* ------------------------------------------------------------------ chrome */

#hud {
  position: absolute; inset: 0;
  pointer-events: none;
  display: flex; flex-direction: column;
  justify-content: space-between;
  padding: max(8px, env(safe-area-inset-top)) max(10px, env(safe-area-inset-right))
           max(8px, env(safe-area-inset-bottom)) max(10px, env(safe-area-inset-left));
}

#topbar {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 8px; flex-wrap: wrap;
}

#players { display: flex; gap: 6px; flex-wrap: wrap; }

.chip {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 12px;
  padding: 5px 10px 6px;
  min-width: 76px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: border-color .18s, background .18s;
}
.chip.active {
  border-color: var(--gold);
  box-shadow: 0 0 0 1px rgba(240,183,62,.35), 0 6px 22px -10px rgba(240,183,62,.7);
}
.chip .who { display: block; font-size: .74em; letter-spacing: .09em; text-transform: uppercase; color: var(--dim); }
.chip.active .who { color: var(--gold); }
.chip .pts { font-size: 1.5em; font-weight: 650; line-height: 1.05; font-variant-numeric: tabular-nums; }
.chip .sub { font-size: .68em; color: var(--dim); letter-spacing: .06em; text-transform: uppercase; margin-left: 4px; }
.chip .foul { display: block; font-size: .66em; color: #ff8a7a; letter-spacing: .04em; margin-top: 1px; }

#turn {
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 11px;
  padding: 7px 12px;
  font-size: .84em;
  font-weight: 550;
  letter-spacing: .02em;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  white-space: nowrap;
}

/* The mode chip states which game is on the table and, against the computer,
   the level it is playing at. */
.chip.mode { display: flex; flex-direction: column; justify-content: center; }
.chip.mode .mval {
  font-size: 1.05em; font-weight: 650; color: var(--gold);
  line-height: 1.1; letter-spacing: .03em;
}

#msg {
  position: absolute;
  left: 50%; transform: translateX(-50%);
  top: calc(max(8px, env(safe-area-inset-top)) + 58px);
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 999px;
  padding: 6px 15px;
  font-size: .8em;
  /* Without an explicit width the shrink-to-fit box is capped at the space
     right of the 50% mark, so a long line broke into three on a phone. */
  width: max-content;
  max-width: min(92vw, 460px);
  text-align: center;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: background .15s, border-color .15s, transform .15s;
  opacity: .96;
}
#msg.flash {
  border-color: #ff8a7a;
  background: rgba(60, 16, 12, .9);
  transform: translateX(-50%) scale(1.04);
}

#bottom {
  pointer-events: none;
  display: flex; flex-direction: column; gap: 7px;
  align-items: stretch;
}

/* Power is its own control and always in the same place: a slider here, and a
   Shoot button next to it. Aiming is done on the cloth and never has to touch
   the cue ball, so loading a shot is one hand and lining it up is the other. */
#powerwrap {
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 12px;
  padding: 5px 10px 8px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  opacity: .9;
  transition: opacity .15s, border-color .15s;
}
#powerwrap.live { opacity: 1; border-color: rgba(240,183,62,.5); }
/* While the computer is at the table the shot controls belong to it, so the
   panel dims and stops taking a hand. */
#powerwrap.waiting { opacity: .5; }
#powerwrap.waiting #powerslider,
#powerwrap.waiting #spinball { pointer-events: none; }

#powerrow {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: .68em; letter-spacing: .14em; color: var(--dim);
  text-transform: uppercase;
}
#powerval { font-variant-numeric: tabular-nums; color: var(--ink); letter-spacing: .04em; }

#powerline { display: flex; align-items: center; gap: 8px; margin-top: 2px; }

/* The power slider and its speed scale, as in the reference layout: the slider
   is the control and Slow / Medium / Fast name what its two ends mean. */
#powercol {
  flex: 1 1 auto;
  min-width: 0;
  display: flex; flex-direction: column; justify-content: center;
}
#pscale {
  display: flex; justify-content: space-between;
  padding: 0 3px;
  font-size: .5em; line-height: 1.1;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--dim);
}

/* The cue ball control. A disc the size of the other tap targets, with the
   contact dot inside it. It sits beside the power slider because setting where
   the tip lands and how hard it strikes are the same decision, made in the
   same place, on every screen. */
#spinwrap {
  pointer-events: auto;
  flex: 0 0 auto;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
}
#spinball {
  position: relative;
  width: var(--tap); height: var(--tap);
  border-radius: 50%;
  background: radial-gradient(circle at 36% 30%, #fffdf8, #e6e1d5 55%, #a8a296 100%);
  border: 1px solid rgba(0,0,0,.45);
  box-shadow: inset 0 -3px 8px rgba(0,0,0,.25), 0 2px 7px rgba(0,0,0,.5);
  cursor: pointer;
  touch-action: none;
  outline: none;
}
#spinball:focus-visible { box-shadow: 0 0 0 2px rgba(240,183,62,.7), inset 0 -3px 8px rgba(0,0,0,.25); }
#spinball .spinline { position: absolute; background: rgba(40,40,40,.18); }
#spinball .spinline.h { left: 9%; right: 9%; top: 50%; height: 1px; }
#spinball .spinline.v { top: 9%; bottom: 9%; left: 50%; width: 1px; }
#spindot {
  position: absolute; left: 50%; top: 50%;
  width: 30%; height: 30%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #ff9a86, #c8202a 72%);
  border: 1px solid rgba(0,0,0,.45);
  box-shadow: 0 1px 3px rgba(0,0,0,.55);
  pointer-events: none;
}
#spinread {
  font-size: .54em; line-height: 1;
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--dim);
  max-width: calc(var(--tap) + 10px);
  text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
#powerwrap.live #spinread { color: var(--gold); }

/* The whole pad is the control, not just the thin bar in the middle of it:
   a slider you can only grab by the pixel is a slider a thumb cannot use. */
#powerslider {
  pointer-events: auto;
  width: 100%;
  flex: 1 1 auto;
  min-width: 0;
  height: clamp(32px, 9vw, var(--tap));
  display: flex; align-items: center;
  cursor: pointer;
  touch-action: none;
  outline: none;
}
#powerslider:focus-visible #powerbar { box-shadow: 0 0 0 2px rgba(240,183,62,.7); }

#powerbar {
  position: relative;
  width: 100%;
  height: 12px; border-radius: 6px;
  background: rgba(255,255,255,.12);
  box-shadow: inset 0 1px 2px rgba(0,0,0,.45);
}
#powerfill {
  height: 100%; width: 0%;
  border-radius: 6px;
  background: linear-gradient(90deg, #3fbf7a, #cfe06a);
  transition: width .05s linear, background .2s;
}
#powerfill.warm { background: linear-gradient(90deg, #3fbf7a, #f0b73e); }
#powerfill.hot  { background: linear-gradient(90deg, #f0b73e, #f0503e); }
#powerthumb {
  position: absolute; top: 50%; left: 0%;
  width: 22px; height: 22px; margin: -11px 0 0 -11px;
  border-radius: 50%;
  background: #f7f3ea;
  border: 2px solid rgba(0,0,0,.35);
  box-shadow: 0 2px 6px rgba(0,0,0,.5);
  transition: left .05s linear;
}
#powerwrap.live #powerthumb { background: var(--gold); }

.shoot {
  pointer-events: auto;
  flex: 0 0 auto;
  min-width: clamp(66px, 19vw, 104px);
  min-height: var(--tap);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px;
  background: linear-gradient(180deg, rgba(240,183,62,.28), rgba(240,183,62,.12));
  border: 1px solid rgba(240,183,62,.55);
  border-radius: 12px;
  color: var(--ink);
  font-family: inherit;
  font-size: .66em;
  letter-spacing: .08em;
  text-transform: uppercase;
  cursor: pointer;
  padding: 4px 8px;
}
.shoot .ic { font-size: 1.05em; line-height: 1; color: var(--gold); }
.shoot:active { background: rgba(240,183,62,.42); }
.shoot[disabled] { opacity: .45; }

#buttons {
  pointer-events: auto;
  display: flex; gap: 6px;
  justify-content: center;
  flex-wrap: wrap;
}

.btn {
  flex: 1 1 0;
  min-width: 0;
  min-height: var(--tap);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 12px;
  color: var(--ink);
  font-family: inherit;
  font-size: .66em;
  letter-spacing: .07em;
  text-transform: uppercase;
  cursor: pointer;
  padding: 5px 4px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: background .15s, border-color .15s, color .15s;
}
.btn .ic { font-size: 1.5em; line-height: 1; }
.btn:active { background: rgba(255,255,255,.13); }
.btn.off { color: var(--dim); }
.btn.off .ic { opacity: .45; }

/* The two aim nudge buttons are the same tap target as the rest, marked with
   the shot's gold so the pair reads as one control for aiming rather than two
   more utility buttons lost in the row. */
.btn.nudge { border-color: rgba(240,183,62,.42); }
.btn.nudge .ic { color: var(--gold); }
.btn.nudge:active { background: rgba(240,183,62,.2); }

/* ----------------------------------------------------------------- overlays */

.overlay {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: max(14px, env(safe-area-inset-top)) max(14px, env(safe-area-inset-right))
           max(14px, env(safe-area-inset-bottom)) max(14px, env(safe-area-inset-left));
  background: radial-gradient(120% 90% at 50% 0%, rgba(10,14,16,.72), rgba(4,6,7,.94));
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  z-index: 10;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.overlay.hidden { display: none; }
/* Used by the reset-view button, which is only on the row once the camera has
   actually been swung somewhere. */
.gone { display: none !important; }

.card {
  width: min(560px, 100%);
  max-height: 100%;
  background: linear-gradient(180deg, rgba(20,25,29,.96), rgba(11,14,17,.97));
  border: 1px solid var(--edge);
  border-radius: 18px;
  padding: clamp(16px, 3.4vmin, 28px);
  box-shadow: 0 30px 70px -30px #000, inset 0 1px 0 rgba(255,255,255,.05);
  margin: auto;
}

.brand h1 {
  margin: 0 0 6px;
  font-size: clamp(26px, 8.2vmin, 46px);
  letter-spacing: .17em;
  font-weight: 700;
  background: linear-gradient(180deg, #fff8e6, #d8a63a 65%, #8a6420);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.brand .tag { color: var(--dim); font-size: .86em; line-height: 1.45; }
.greet { color: var(--gold); font-size: .74em; letter-spacing: .1em; margin-top: 5px; height: 1em; }

h2 {
  margin: 0 0 10px;
  font-size: 1em; letter-spacing: .12em; text-transform: uppercase;
  color: var(--gold); font-weight: 600;
}
.card h2 + .rows + h2 { margin-top: 18px; }

.modes { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }

.mode {
  display: flex; flex-direction: column; gap: 3px;
  text-align: left;
  min-height: var(--tap);
  padding: 11px 14px;
  border-radius: 13px;
  border: 1px solid var(--edge);
  background: rgba(255,255,255,.045);
  color: var(--ink);
  font-family: inherit; font-size: .92em;
  cursor: pointer;
  transition: background .15s, border-color .15s, transform .06s;
}
.mode:hover { background: rgba(255,255,255,.08); }
.mode:active { transform: scale(.985); }
.mode.primary {
  border-color: rgba(240,183,62,.55);
  background: linear-gradient(180deg, rgba(240,183,62,.20), rgba(240,183,62,.08));
}
.mtitle { font-weight: 600; letter-spacing: .04em; }
.msub { font-size: .78em; color: var(--dim); line-height: 1.4; }

/* The setup on the start card: a name each, and the computer's level. It sits
   above the mode buttons because it belongs to all of them, and it wraps to one
   column on a narrow screen rather than squeezing the fields. */
.setup {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin-top: 14px;
}
.field {
  flex: 1 1 140px;
  display: flex; flex-direction: column; gap: 4px;
  min-width: 0;
}
.flabel {
  font-size: .66em; letter-spacing: .12em; text-transform: uppercase;
  color: var(--dim);
}
.ninput {
  width: 100%;
  min-height: clamp(38px, 7vmin, 48px);
  padding: 7px 11px;
  border-radius: 11px;
  border: 1px solid var(--edge);
  background: rgba(255,255,255,.05);
  color: var(--ink);
  font-family: inherit; font-size: .92em;
  outline: none;
}
.ninput::placeholder { color: rgba(154,163,172,.7); }
.ninput:focus-visible { border-color: rgba(240,183,62,.65); box-shadow: 0 0 0 2px rgba(240,183,62,.25); }

.diffwrap {
  margin-top: 12px;
  padding: 9px 12px 7px;
  border-radius: 12px;
  border: 1px solid var(--edge);
  background: rgba(255,255,255,.03);
}
.diffrow {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 8px;
}
#diffval {
  font-size: .78em; font-weight: 650; color: var(--gold);
  letter-spacing: .04em;
}
.dinput {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: var(--tap);
  margin: 2px 0 0;
  background: transparent;
  cursor: pointer;
  touch-action: none;
}
.dinput::-webkit-slider-runnable-track {
  height: 8px; border-radius: 4px;
  background: rgba(255,255,255,.14);
  box-shadow: inset 0 1px 2px rgba(0,0,0,.45);
}
.dinput::-moz-range-track {
  height: 8px; border-radius: 4px;
  background: rgba(255,255,255,.14);
}
.dinput::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 22px; height: 22px; margin-top: -7px;
  border-radius: 50%;
  background: var(--gold);
  border: 2px solid rgba(0,0,0,.35);
  box-shadow: 0 2px 6px rgba(0,0,0,.5);
}
.dinput::-moz-range-thumb {
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--gold);
  border: 2px solid rgba(0,0,0,.35);
}
.dinput:focus-visible { outline: none; box-shadow: 0 0 0 2px rgba(240,183,62,.4); border-radius: 8px; }
.diffmarks {
  display: flex; justify-content: space-between;
  font-size: .6em; letter-spacing: .1em; text-transform: uppercase;
  color: var(--dim);
}

/* The Menu button sits with the turn label, out of the table and away from the
   aim controls. It is the one control for the game options after the break. */
.topright { display: flex; align-items: stretch; gap: 6px; }
.menu-btn {
  pointer-events: auto;
  display: flex; align-items: center; gap: 5px;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 11px;
  padding: 7px 11px;
  color: var(--ink);
  text-decoration: none;
  font-family: inherit;
  font-size: .76em;
  letter-spacing: .06em;
  text-transform: uppercase;
  cursor: pointer;
  user-select: none;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.menu-btn .ic { font-size: 1.05em; line-height: 1; }
.menu-btn:active { background: rgba(255,255,255,.13); }

/* The view and control switches and the way back appear only when the setup
   card is opened mid-rack as the game options. On the opening screen they are
   the live controls, so they are already under the thumb, and the Back button
   is not needed. */
#session-settings, #start-back { display: none; }
#overlay-start.in-game #session-settings { display: block; }
#overlay-start.in-game #start-back { display: block; }
#overlay-start.in-game .quick { display: none; }
#overlay-start.in-game .brand .tag { display: none; }

/* A close that is always on screen, even when the card is taller than a short
   landscape window and has to scroll. It is fixed to the viewport rather than
   the scrolling overlay, so it never scrolls out of reach. */
#overlay-start:not(.in-game) .menu-close { display: none; }
.menu-close {
  position: fixed;
  top: max(10px, env(safe-area-inset-top));
  right: max(12px, env(safe-area-inset-right));
  z-index: 20;
  pointer-events: auto;
  min-height: 40px;
  padding: 8px 14px;
  border-radius: 999px;
  border: 1px solid rgba(240,183,62,.6);
  background: rgba(20,25,29,.95);
  color: var(--gold);
  font-family: inherit; font-size: .8em; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase;
  cursor: pointer;
  box-shadow: 0 8px 24px -12px #000;
}
.menu-close:active { background: rgba(240,183,62,.2); }

.session { margin-top: 16px; padding-top: 13px; border-top: 1px solid var(--edge); }
.switches { display: flex; flex-wrap: wrap; gap: 8px; }
.switch {
  flex: 1 1 130px;
  min-height: var(--tap);
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px;
  border-radius: 12px;
  border: 1px solid var(--edge);
  background: rgba(255,255,255,.045);
  color: var(--dim);
  font-family: inherit; font-size: .82em; letter-spacing: .04em;
  cursor: pointer; text-align: left;
}
.switch .sdot {
  width: 12px; height: 12px; border-radius: 50%;
  background: rgba(255,255,255,.18);
  box-shadow: inset 0 1px 2px rgba(0,0,0,.5);
  flex: 0 0 auto;
  transition: background .15s, box-shadow .15s;
}
.switch.on { color: var(--ink); border-color: rgba(240,183,62,.5); }
.switch.on .sdot { background: var(--gold); box-shadow: 0 0 8px rgba(240,183,62,.7); }
.switch:active { background: rgba(255,255,255,.1); }

.quick {
  margin-top: 16px; padding-top: 13px;
  border-top: 1px solid var(--edge);
  color: var(--dim); font-size: .78em; line-height: 1.6;
}
.quick b { color: var(--ink); font-weight: 600; }

.rows { display: flex; flex-direction: column; gap: 8px; }
.row { display: flex; gap: 12px; align-items: baseline; font-size: .84em; line-height: 1.5; }
.row .k {
  flex: 0 0 clamp(62px, 19vw, 92px);
  color: var(--gold); font-weight: 600; letter-spacing: .03em;
}
.row .v { color: #d5d9dd; }

.wide {
  width: 100%; margin-top: 18px;
  min-height: var(--tap);
  border-radius: 13px;
  border: 1px solid var(--edge);
  background: rgba(255,255,255,.07);
  color: var(--ink); font-family: inherit; font-size: .9em; font-weight: 600;
  letter-spacing: .06em; cursor: pointer;
}
.wide:active { background: rgba(255,255,255,.13); }

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

.board { margin-top: 16px; }
.board .btitle {
  font-size: .7em; letter-spacing: .14em; text-transform: uppercase;
  color: var(--dim); margin-bottom: 7px;
}
.board .brow {
  display: grid; grid-template-columns: 22px 1fr auto;
  gap: 10px; align-items: baseline;
  padding: 5px 9px; border-radius: 9px; font-size: .84em;
  font-variant-numeric: tabular-nums;
}
.board .brow:nth-child(odd) { background: rgba(255,255,255,.035); }
.board .brow.mine { background: rgba(240,183,62,.16); color: #ffe6a8; }
.board .bn { color: var(--dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board .brow.mine .bn { color: inherit; }
.board .bv { font-weight: 650; }
.bload { font-size: .76em; color: var(--dim); }

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

@media (min-aspect-ratio: 1/1) {
  #msg { top: max(10px, env(safe-area-inset-top)); }
  /* The bottom bar wraps rather than squeezes. On a narrow landscape screen the
     power panel and five buttons do not fit on one line, and without wrap the
     flex row paid for it out of the slider: at 480x320 the slider measured
     zero pixels wide, so the control the whole shot rests on was not there at
     all. A second row costs some table and keeps every control its own size. */
  #bottom { flex-direction: row; align-items: flex-end; gap: 10px; flex-wrap: wrap; }
  /* The power panel is the whole left side of the bottom bar on a wide screen:
     slider, and the Shoot button beside it, both in one place every time. The
     floor is what stops it being eaten: it may not shrink past a slider a
     thumb can drag. */
  #powerwrap { flex: 0 1 clamp(200px, 30vw, 360px); min-width: min(190px, 100%); }
  #buttons { flex: 0 0 auto; }
  .btn { flex: 0 0 auto; min-width: 68px; }
  .card { width: min(560px, 74vw); }
  .modes { flex-direction: row; }
  .mode { flex: 1 1 0; }
}

/* A phone on its side has the least vertical room of any screen this page
   meets, and the message line is where a foul is explained. It used to be
   hidden here, which meant the one orientation with no space for it was also
   the one where a player could foul and never be told why. It shrinks
   instead: smaller type, tighter pill, and the turn chip it would have
   collided with drops to a single line. */
@media (max-height: 460px) {
  /* max(), not the em alone: --u tracks vmin, and on the shortest screen it
     lands at 12.6px, which makes .72em a nine pixel sentence explaining a
     foul. Eleven is the floor of what is worth reading. */
  #msg { top: calc(max(6px, env(safe-area-inset-top)) + 44px);
         font-size: max(11px, .82em); padding: 4px 12px; }
  #hud { padding-top: max(6px, env(safe-area-inset-top)); }
  .chip { padding: 4px 9px 5px; min-width: 68px; }
  .chip .pts { font-size: 1.25em; }
  #turn { padding: 6px 10px; font-size: .78em; }
  .quick { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}
