/* Puzzle Rush on the web.
 *
 * Colours are the game's own (see js/theme.js, which reads them off the Godot
 * source). Kept in step by hand rather than by a build step - there are six
 * of them, and a build step for six values is worse than the duplication.
 *
 * No inline styles and no inline scripts anywhere, because the CSP the server
 * sends allows neither. That was decided before anything was written rather
 * than bolted on afterwards.
 *
 * The layout's whole job is to give the BOARD the screen. Everything else -
 * logo, meta line, HUD, actions - is chrome, and every pixel of chrome height
 * comes straight off the cell size, because a 6x12 board on a phone is always
 * constrained by height rather than width.
 */

:root {
  --bg: #1c1f29;
  --panel: #2a3346;
  --panel-hi: #38445c;
  --panel-lo: #1e2534;
  --panel-dim: rgba(0, 0, 0, 0.6);
  --accent: #4cd9ff;
  --text: #f2f5ff;
  --muted: #a6a8b3;
  --amber: #f2a03c;
  --green: #3db347;
  --radius: 10px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  /* Both are load-bearing on a phone, not cosmetic. Without them an upward
   * drag triggers pull-to-refresh mid-puzzle and a sideways drag gets claimed
   * by the browser for scrolling - which makes every swipe feel broken on
   * exactly the devices most people will play on. */
  overscroll-behavior: none;
  -webkit-user-select: none;
  user-select: none;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
        Helvetica, Arial, sans-serif;
}

body {
  /* height, NOT min-height. min-height lets the flex column grow past the
   * viewport, and since the board's wrap is given an explicit pixel height
   * from JS it did exactly that - the page scrolled and the board stayed at
   * whatever size it had been measured for on a taller screen instead of
   * shrinking to fit. A fixed height makes the leftover space real, which is
   * what board-area is measuring.
   *
   * dvh, not vh: on mobile Safari vh is the tallest the viewport ever gets,
   * so the bottom of the page would sit under the URL bar. */
  height: 100dvh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: max(6px, env(safe-area-inset-top)) 8px max(6px, env(safe-area-inset-bottom));
  /* Every pixel here comes off the cell size: a 6x12 board on a phone is
   * always height-constrained, never width-constrained. */
  gap: 6px;
}

main {
  /* min-height: 0 lets the board area actually shrink inside the flex column.
   * Without it a flex child refuses to go below its content size and the
   * board overflows the screen instead of fitting it. */
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

/* --- masthead ----------------------------------------------------------- */

.masthead { width: 100%; text-align: center; flex: 0 0 auto; }

.logo {
  width: min(170px, 42vw);
  height: auto;
  display: block;
  margin: 0 auto;
}

/* --- meta --------------------------------------------------------------- */

.meta {
  margin: 0;
  display: flex;
  gap: 8px;
  align-items: baseline;
  justify-content: center;
  flex-wrap: wrap;
  font-size: 14px;
  line-height: 1.2;
}

.meta-number { color: var(--accent); font-weight: 700; letter-spacing: 0.04em; }
.meta-date { color: var(--muted); }
.meta-name { font-weight: 600; }
.meta-credit { color: var(--muted); }
.meta-credit:not(:empty)::before { content: "· "; }

/* --- hud ---------------------------------------------------------------- */

.hud {
  display: flex;
  align-items: center;
  gap: 16px;
  justify-content: center;
  flex: 0 0 auto;
}

.stat { display: flex; align-items: baseline; gap: 6px; }
.stat-label {
  color: var(--muted);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.stat-value { font-variant-numeric: tabular-nums; font-weight: 600; }

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

/* Measured, never painted. This is the space the board is allowed to use. */
.board-area {
  /* basis 0, not auto: the board is sized FROM this box, so if this box were
   * sized from its content the two would chase each other and the
   * ResizeObserver driving it would loop. */
  flex: 1 1 0;
  min-height: 0;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  /* A wrap still carrying the previous screen's size for one frame must not
   * be able to push the rest of the layout around before the observer
   * corrects it. */
  overflow: hidden;
}

/* Sized from JS to exactly the canvas, so an overlay at inset: 0 covers the
 * board and nothing else. */
.board-wrap {
  position: relative;
  line-height: 0;
}

#board {
  /* Without this the browser claims the drag for scrolling and the board
   * never sees a swipe. */
  touch-action: none;
  display: block;
}

.overlay {
  position: absolute;
  inset: 0;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  text-align: center;
  padding: 16px;
  line-height: 1.4;
  background: rgba(14, 16, 22, 0.9);
}

.overlay-title { margin: 0; font-size: 22px; font-weight: 700; }
.overlay-note { margin: 0; color: var(--muted); font-size: 13px; }
.overlay-note:empty { display: none; }
.overlay-stats { margin: 0; display: flex; gap: 24px; }
.overlay-stats > span { display: block; font-size: 20px; font-weight: 600; }
.overlay-stats .stat-label { display: block; margin-bottom: 2px; font-weight: 400; }

/* --- buttons ------------------------------------------------------------ */

.btn {
  appearance: none;
  /* No accent outline. The game's own buttons carry one, but there it sits
   * over a moving 3D starfield and needs the separation; on a flat dark page
   * a ring of cyan around every control is all you see. The chamfer is what
   * carries the look, so that stays. */
  border: none;
  background: var(--panel);
  /* A one-pixel top highlight instead - the same upper-left light source the
   * blocks are bevelled for, at a fraction of the weight. */
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14);
  color: var(--text);
  font: inherit;
  font-weight: 600;
  padding: 11px 20px;
  cursor: pointer;
  /* Rounded, not the app's chamfered octagon. That shape works over a moving
   * 3D starfield where a button needs a hard silhouette to read as a control;
   * on a flat page the cut corners just look sharp. */
  border-radius: 10px;
}

.btn:hover:not(:disabled) { background: var(--panel-hi); }
.btn:active:not(:disabled) { background: var(--panel-lo); }
.btn:disabled { opacity: 0.35; cursor: default; }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.btn-small { padding: 7px 14px; font-size: 14px; font-weight: 500; border-radius: 8px; }
.btn-primary { background: #2f6c86; padding: 13px 30px; border-radius: 12px; }
.btn-primary:hover:not(:disabled) { background: #3a86a5; }

/* Past puzzles on the left, How to play on the right - they are different
 * kinds of thing and should not read as a pair of equals in the middle. */
.actions {
  display: flex;
  gap: 8px;
  flex: 0 0 auto;
  width: 100%;
  max-width: 420px;
  justify-content: space-between;
  align-items: center;
}

.actions-left { display: flex; gap: 8px; }

/* --- the tutorial ------------------------------------------------------- */

/* .modal-card.tutorial-card, not .tutorial-card: this block sits ABOVE the
 * calendar's .modal-card rule in the file, so at equal specificity the plain
 * class would lose on source order and silently keep the calendar's narrower
 * width and larger padding. Two classes wins regardless of where it sits. */
.modal-card.tutorial-card {
  /* Wider than the calendar card, and less padded, because the tutorial board
   * is limited by WIDTH - it is six columns across and only six rows tall
   * once cropped, so height is never what binds. Every pixel between the
   * viewport edge and the canvas comes straight off the blocks. */
  width: min(460px, 100%);
  padding: 12px;
  max-height: 96dvh;
  overflow-y: auto;
  text-align: center;
}

.tutorial-title { margin: 2px 0 8px; font-size: 19px; clear: both; }

.tutorial-board { display: flex; justify-content: center; }
.tutorial-board { overflow: hidden; }
#tutorialBoard { display: block; }

.tutorial-caption {
  /* A fixed height so the board does not jump every time the line changes
   * length - the caption changes six times and the board is what you are
   * meant to be watching. */
  min-height: 2.8em;
  margin: 10px 0 4px;
  font-size: 14px;
  color: var(--text);
  display: flex;
  align-items: center;
  justify-content: center;
}

.tutorial-actions { display: flex; justify-content: center; gap: 8px; }

/* --- which overlay is showing ------------------------------------------- */

body[data-phase="ready"] #startOverlay,
body[data-phase="won"] #resultOverlay,
body[data-phase="lost"] #resultOverlay,
body[data-phase="loading"] #statusOverlay,
body[data-phase="error"] #statusOverlay { display: flex; }

/* Sharing a loss would be sharing a puzzle you have not solved. */
body[data-phase="lost"] #share { display: none; }

body[data-phase="loading"] .hud,
body[data-phase="error"] .hud,
body[data-phase="loading"] .actions,
body[data-phase="error"] .actions { visibility: hidden; }

/* The Today button is only meaningful once you have wandered off it. */
#today { display: none; }
body.is-past #today { display: inline-block; }

/* --- calendar ----------------------------------------------------------- */

.modal {
  position: fixed;
  inset: 0;
  background: rgba(8, 9, 13, 0.85);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 10px;
  z-index: 10;
}

.modal[hidden] { display: none; }

.modal-card {
  background: #232a38;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--radius);
  padding: 16px;
  width: min(360px, 100%);
}

.modal-close { float: right; margin: -4px -4px 4px 4px; }

.cal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
  clear: both;
}

.cal-title { font-weight: 700; }

.cal-nav {
  appearance: none;
  background: none;
  border: none;
  color: var(--text);
  font-size: 24px;
  line-height: 1;
  padding: 4px 12px;
  cursor: pointer;
}
.cal-nav:disabled { color: #4a4f5c; cursor: default; }

.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }

.cal-dow {
  text-align: center;
  font-size: 11px;
  color: var(--muted);
  padding-bottom: 4px;
}

.cal-day {
  appearance: none;
  aspect-ratio: 1;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.05);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}

.cal-day:disabled { opacity: 0.22; cursor: default; border-style: dashed; }
.cal-day.is-won { background: rgba(61, 179, 71, 0.32); border-color: var(--green); }
.cal-day.is-lost { background: rgba(242, 160, 60, 0.2); border-color: var(--amber); }
.cal-day.is-today { outline: 2px solid var(--accent); }

.cal-key, .cal-status {
  margin: 10px 0 0;
  font-size: 12px;
  color: var(--muted);
  text-align: center;
}

/* --- footer ------------------------------------------------------------- */

.footnote {
  color: var(--muted);
  font-size: 12px;
  text-align: center;
  flex: 0 0 auto;
}
.footnote p { margin: 0; }

/* --- making room for the board ------------------------------------------ */

/* On a short screen every one of these is height the board could have had.
 * A phone in portrait is the case this page exists for, so the chrome gives
 * way rather than the play area. */
@media (max-height: 780px) {
  .footnote { display: none; }
  .logo { width: min(160px, 40vw); }
}

@media (max-height: 700px) {
  .logo { width: min(120px, 32vw); }
  body, main { gap: 4px; }
  .meta { font-size: 13px; }
  .btn-small { padding: 5px 11px; font-size: 13px; }
  .hud { gap: 12px; }
}

@media (max-height: 600px) {
  .masthead { display: none; }
}

/* Landscape on a phone: the board is limited by height alone, so the chrome
 * moves out of its way entirely rather than stacking above it. */
@media (orientation: landscape) and (max-height: 520px) {
  .masthead, .footnote { display: none; }
  body { gap: 4px; padding-top: 4px; }
}

/* --- ?debug=1 ------------------------------------------------------------ */

/* Shown only when the page is loaded with ?debug=1 - see main.js. Fixed and
 * pointer-events: none so it can never itself be what swallowed the touch it
 * is there to diagnose. */
.debug-box {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: 6px 8px;
  max-height: 40vh;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.85);
  color: #7dffa8;
  font: 11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: pre-wrap;
  pointer-events: none;
  z-index: 20;
}
