/* SKYHOOK - layout only. All game visuals are drawn on the canvas. */

:root {
  --bg: #060713;
  --bg-2: #0a0c1e;
  --cyan: #35e6ff;
  --ad-h: 54px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: #cfe9ff;
  font-family: "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
  overflow: hidden;
  overscroll-behavior: none;
  -webkit-text-size-adjust: 100%;
}

body {
  /* Kill mobile scroll / pull-to-refresh / double-tap zoom over the game. */
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

#app {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  /* Respect iOS notches / home indicator. */
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
  background:
    radial-gradient(120% 80% at 50% 0%, #131a3a 0%, rgba(19,26,58,0) 60%),
    linear-gradient(180deg, var(--bg-2) 0%, var(--bg) 100%);
}

#stage {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

#game {
  display: block;
  touch-action: none;
  border-radius: 10px;
  /* Neon frame so the portrait column reads as a device screen on desktop. */
  box-shadow:
    0 0 0 1px rgba(53, 230, 255, 0.18),
    0 0 34px rgba(53, 230, 255, 0.10),
    0 18px 60px rgba(0, 0, 0, 0.65);
  image-rendering: auto;
}

.noscript {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  text-align: center;
  padding: 24px;
  font-size: 16px;
}

/* ===== Ad slot =====
   INACTIVE. No ad unit ships with the game, so the slot is hidden and the
   game gets the full viewport. To activate a banner: delete the
   `display: none` below and put the ad markup inside #ad-slot-inner. The
   reserved fixed height then prevents any layout shift (CLS). */
#ad-slot {
  flex: 0 0 auto;
  height: var(--ad-h);
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.015);
  border-top: 1px solid rgba(53, 230, 255, 0.10);
}

/* Slot is inactive - delete this one rule to switch the banner area on. */
#ad-slot { display: none; }

#ad-slot-inner {
  width: 320px;
  max-width: 100%;
  height: 50px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* Wider viewports can host a leaderboard unit. */
@media (min-width: 760px) and (min-height: 620px) {
  :root { --ad-h: 96px; }
  #ad-slot-inner { width: 728px; height: 90px; }
}

/* Very short screens: give every pixel to the game. */
@media (max-height: 460px) {
  #ad-slot { display: none !important; }
}

/* ===== Account / leaderboard overlay =====
   Hidden until js/ui_online.js finds a configured backend. Everything here is
   scoped under .ol so a build with no config renders not one extra pixel.
   The palette is the canvas palette (--cyan on --bg) so the panel reads as
   part of the same machine rather than a browser dialog bolted on. */

.ol[hidden],
.ol [hidden] { display: none !important; }

.ol {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(3, 4, 12, 0.82);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  /* The overlay is the one place in the game where text is meant to be
     selected and typed into. */
  -webkit-user-select: text;
  user-select: text;
  touch-action: auto;
}

.ol-panel {
  position: relative;
  width: 100%;
  max-width: 420px;
  max-height: 100%;
  overflow-y: auto;
  padding: 22px 20px 20px;
  border-radius: 14px;
  background: linear-gradient(180deg, #0c1030 0%, #070a1c 100%);
  box-shadow:
    0 0 0 1px rgba(53, 230, 255, 0.22),
    0 0 44px rgba(53, 230, 255, 0.12),
    0 18px 60px rgba(0, 0, 0, 0.7);
  overscroll-behavior: contain;
}

.ol-title {
  margin: 0 0 14px;
  font-size: 17px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-align: center;
  color: #fff;
  text-shadow: 0 0 16px rgba(53, 230, 255, 0.75);
}

.ol-close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 40px;
  height: 40px;
  font-size: 20px;
  line-height: 1;
  color: #9fc4e0;
  background: none;
  border: 0;
  border-radius: 10px;
  cursor: pointer;
}
.ol-close:hover { color: #fff; background: rgba(53, 230, 255, 0.10); }

.ol-account {
  margin: 0 0 12px;
  font-size: 12px;
  text-align: center;
  color: rgba(150, 190, 220, 0.85);
}

/* ---- the board ---- */
.ol-list {
  list-style: none;
  margin: 0 0 10px;
  padding: 0;
  max-height: 46vh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.ol-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 7px 10px;
  border-radius: 8px;
  font-variant-numeric: tabular-nums;
}
.ol-row + .ol-row { margin-top: 2px; }
.ol-row:nth-child(odd) { background: rgba(255, 255, 255, 0.022); }

/* Your own line, so you can find yourself without reading every row. */
.ol-row.is-me {
  background: rgba(53, 230, 255, 0.12);
  box-shadow: inset 0 0 0 1px rgba(53, 230, 255, 0.30);
}

.ol-rank {
  flex: 0 0 42px;
  font-size: 12px;
  font-weight: 700;
  color: rgba(150, 190, 220, 0.7);
}
.ol-row.is-top .ol-rank { color: #ffd75e; }

.ol-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 14px;
  color: #dff0ff;
}

.ol-score {
  flex: 0 0 auto;
  font-size: 15px;
  font-weight: 800;
  color: #fff;
}

/* The line every answer in this panel is delivered on: "a reset link is on its
   way", "Link confirmed. Choose a new password", "that link has expired". It
   used to be 12px at 85% opacity, which is fine for "earlier run uploaded" and
   not fine for the only instruction on screen - the player who reads this one
   is, by definition, already confused and often on a phone. Bigger, brighter,
   and left with room for two lines, because the reset confirmation IS two
   lines and a fixed 17px box made the second one shift the layout as it
   arrived. Contrast against the panel gradient is 12.6:1 - WCAG AAA with room,
   and measured in the browser, not estimated. */
.ol-msg {
  margin: 6px 0;
  min-height: 19px;
  font-size: 13px;
  line-height: 1.45;
  text-align: center;
  color: rgba(198, 224, 240, 0.95);
}
.ol-msg.is-error { color: #ff8ca3; }

.ol-myrank {
  margin: 0 0 12px;
  font-size: 13px;
  font-weight: 700;
  text-align: center;
  color: #ffd75e;
}

/* ---- buttons and form ---- */
.ol-actions { display: flex; flex-direction: column; gap: 8px; }

.ol-btn {
  display: block;
  width: 100%;
  padding: 13px 14px;
  font: inherit;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: #dff0ff;
  background: rgba(53, 230, 255, 0.07);
  border: 1px solid rgba(53, 230, 255, 0.34);
  border-radius: 10px;
  cursor: pointer;
}
.ol-btn:hover:not(:disabled) { background: rgba(53, 230, 255, 0.14); }
.ol-btn:disabled { opacity: 0.5; cursor: default; }

.ol-btn-primary {
  color: #04121a;
  background: linear-gradient(180deg, #6ff0ff 0%, #35e6ff 100%);
  border-color: transparent;
}
.ol-btn-primary:hover:not(:disabled) { background: #8af4ff; }

.ol-btn-google {
  color: #1f1f1f;
  background: #fff;
  border-color: rgba(255, 255, 255, 0.6);
}
.ol-btn-google:hover:not(:disabled) { background: #eceff3; }

.ol-or {
  position: relative;
  margin: 14px 0;
  text-align: center;
  font-size: 11px;
  letter-spacing: 0.18em;
  color: rgba(150, 190, 220, 0.55);
}
.ol-or::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 1px;
  background: rgba(53, 230, 255, 0.16);
}
.ol-or span { position: relative; padding: 0 10px; background: #080b1e; }

.ol-field { margin-bottom: 10px; }
.ol-field label {
  display: block;
  margin-bottom: 4px;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(150, 190, 220, 0.75);
}
.ol-field input {
  width: 100%;
  padding: 11px 12px;
  font: inherit;
  font-size: 15px;           /* >=16px-ish keeps iOS from zooming the page */
  color: #eaf6ff;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(53, 230, 255, 0.22);
  border-radius: 9px;
}
.ol-field input::placeholder { color: rgba(150, 190, 220, 0.38); }
/* The line that tells the player which of the two names this box wants. Quiet
   on purpose - it must be readable without competing with the label above it
   or the error line below the button. Not uppercase: unlike a label it is a
   sentence, and letter-spaced caps are slower to read. */
.ol-field .ol-hint {
  margin: 4px 0 0;
  /* 11px at 60% opacity is decoration, and on RESET PASSWORD this line is not
     decoration: it is the sentence that tells somebody who has muddled their
     login with their leaderboard name which of the two the single box wants.
     12px at 78% is still visibly subordinate to the label above it and is
     actually readable on a phone. */
  font-size: 12px;
  line-height: 1.4;
  color: rgba(163, 199, 226, 0.78);
}
.ol-field input:focus {
  outline: none;
  border-color: rgba(53, 230, 255, 0.75);
  box-shadow: 0 0 0 3px rgba(53, 230, 255, 0.14);
}

.ol-links {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  margin-top: 8px;
  /* Three links now ("Create an account", "Forgot password?", "Back to
     leaderboard") where there were two. On a 390px phone they do not fit on
     one line, and without this a flex row does not wrap - it squeezes, and the
     way a <button> squeezes is by clipping its own label. "Forgot password?"
     is the one route back into a locked-out account, so it is the last label
     in the game that may render as "Forgot passw...". Let the row wrap. */
  flex-wrap: wrap;
}
.ol-link {
  /* 44px of height is the tap target every platform's own guidance asks for,
     and the row these sit in is the one place in the game where a mis-tap
     costs a player their way back into their account. The padding does it
     without a fixed height, so the label still wraps rather than clips. */
  padding: 11px 2px;
  min-height: 44px;
  font: inherit;
  font-size: 13px;
  color: rgba(53, 230, 255, 0.92);
  background: none;
  border: 0;
  cursor: pointer;
  text-decoration: underline;
}
.ol-link:hover { color: #8af4ff; }

/* "Forgot password?" is not a peer of the other two links in importance: one
   of them offers a second account (which is exactly the wrong answer, and the
   answer a real player reached for on 2026-09-19 when she could not sign in)
   and the other closes the form. This is the route back into the account she
   already has, so it is the most legible thing in the row - full-strength
   colour and a heavier weight, without a button's worth of visual noise. */
#ol-forgot {
  color: #6fe9ff;
  font-weight: 700;
}
#ol-forgot:hover { color: #b6f6ff; }

/* Keyboard users must be able to see where they are. */
.ol :focus-visible {
  outline: 2px solid rgba(53, 230, 255, 0.9);
  outline-offset: 2px;
}

/* Short screens (landscape phones): the panel scrolls, the list shrinks. */
@media (max-height: 560px) {
  .ol-list { max-height: 32vh; }
  .ol-panel { padding: 16px 16px 14px; }
}

/* ===== Ship customiser =====
   Scoped under .sh-*, but the panel itself is a .ol / .ol-panel, so the frame,
   the backdrop, the title, the close button, the message line and the actions
   are literally the same rules the leaderboard uses. Only the two things the
   leaderboard has no equivalent of are new: the preview and the swatch grid.

   Unlike the account overlay, NOTHING here is gated on a backend - the
   customiser ships in every build, including a file:// open with no network,
   so these rules are always live. */

.sh-preview {
  display: flex;
  justify-content: center;
  margin: 0 0 12px;
}

.sh-preview canvas {
  width: 240px;
  max-width: 100%;
  height: auto;
  border-radius: 12px;
  /* The same neon frame the game canvas wears, at a smaller radius: the
     preview is a little window onto the same screen, not an illustration. */
  box-shadow:
    0 0 0 1px rgba(53, 230, 255, 0.18),
    0 0 26px rgba(53, 230, 255, 0.10),
    0 10px 30px rgba(0, 0, 0, 0.55);
}

/* The crown line. Gold rather than cyan because it is the one sentence in the
   panel that is about the gold hull, and it sits directly under a rocket that
   is either wearing it or is not. Same gold as .ol-myrank and the BEST score
   on the title screen, so "gold means top of the board" stays one idea. */
.sh-crown {
  margin: 0 0 12px;
  font-size: 12px;
  line-height: 1.45;
  text-align: center;
  color: #ffd75e;
}

/* ---- the part tabs ----
   Nose / window / body / fire, one row of four. Each tab carries a dot in
   that part's current colour, so the whole paint job reads at a glance
   without opening each part. Same cell language as the swatches below, at a
   smaller size - they are the same kind of control one level up. */
.sh-parts {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
  margin: 0 0 10px;
}

.sh-part {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 7px 2px;
  min-height: 44px;
  font: inherit;
  color: rgba(190, 220, 240, 0.75);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(53, 230, 255, 0.16);
  border-radius: 10px;
  cursor: pointer;
}
.sh-part:hover { background: rgba(53, 230, 255, 0.10); }
.sh-part .sh-dot { width: 14px; height: 14px; }
.sh-part.is-on {
  background: rgba(53, 230, 255, 0.14);
  border-color: rgba(53, 230, 255, 0.85);
  color: #eaf6ff;
}

/* ---- the swatch grid ----
   Four across. On a 390px phone the panel is 358px wide inside its padding,
   so a cell is ~80px - a 44px-plus tap target with its label under it, and
   the thirteen cells (twelve colours plus the locked gold) land as four rows
   with no scroll. */
.sh-swatches {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin: 0 0 4px;
}

.sh-swatch {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 9px 2px 7px;
  min-height: 62px;
  font: inherit;
  color: rgba(190, 220, 240, 0.8);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(53, 230, 255, 0.16);
  border-radius: 10px;
  cursor: pointer;
}
.sh-swatch:hover { background: rgba(53, 230, 255, 0.10); }

.sh-dot {
  display: block;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  /* A thin white rim, because four of the twelve colours are light enough to
     lose their own edge against the panel and read as a smudge without it. */
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.45);
}

.sh-cap {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.1;
  text-align: center;
}

/* The selected cell. A ring plus a glow rather than a colour change: the cell
   already carries a colour, and the one thing the selection must not do is
   argue with it. */
.sh-swatch.is-on {
  background: rgba(53, 230, 255, 0.14);
  border-color: rgba(53, 230, 255, 0.85);
  color: #eaf6ff;
  box-shadow: 0 0 16px rgba(53, 230, 255, 0.22);
}

/* A colour this part cannot take ON THIS SHIP - a pale window on a pale
   hull, say. Dimmed and crossed rather than hidden: the grid keeps its shape
   when the body changes, and the reason is one tap (or one hover) away.
   The refusal itself lives in SK.Ship.set(); this is only its explanation. */
.sh-swatch.is-blocked {
  cursor: not-allowed;
  opacity: 0.38;
}
.sh-swatch.is-blocked:hover { background: rgba(255, 255, 255, 0.03); }
.sh-swatch.is-blocked .sh-dot {
  background-image: linear-gradient(135deg, transparent 45%, rgba(6, 7, 19, 0.9) 45%,
    rgba(6, 7, 19, 0.9) 55%, transparent 55%) !important;
}

/* The champion's hull: shown to everybody, takeable by nobody. Dashed border
   and a padlock-ish dimming say "this is a state, not a choice" without
   hiding what the colour looks like - which is the whole reason the cell is
   on screen. Tapping it is not silently ignored: js/ui_ship.js answers with
   the rule. */
.sh-swatch.is-locked {
  border-style: dashed;
  border-color: rgba(255, 194, 26, 0.45);
  color: rgba(255, 215, 94, 0.85);
  cursor: not-allowed;
}
.sh-swatch.is-locked:hover { background: rgba(255, 194, 26, 0.08); }
.sh-swatch.is-locked .sh-dot { opacity: 0.85; }

/* Worn, not merely displayed: while the player really is #1 the gold cell is
   the selected one, and it says so in gold rather than in the cyan every
   other selection uses. */
.sh-swatch.is-locked.is-on {
  background: rgba(255, 194, 26, 0.16);
  border-style: solid;
  border-color: rgba(255, 194, 26, 0.9);
  color: #ffe9a8;
  box-shadow: 0 0 18px rgba(255, 194, 26, 0.28);
}

/* Short screens (landscape phones): the preview is the first thing that can
   afford to shrink - the swatches are the controls, and they cannot. */
@media (max-height: 560px) {
  .sh-preview canvas { width: 150px; }
  .sh-preview { margin-bottom: 8px; }
}
