/* Making the five cabinets playable on a phone.
 *
 * The games were built mouse-and-keyboard first and each one was unplayable
 * on touch for the same three reasons, so the fixes live here once rather
 * than five times. Every rule below is about INPUT or WIDTH; none of them
 * change gameplay, because the canvases run on fixed internal coordinates
 * (900x500 and friends) and the arcade board compares scores across devices.
 * A phone must be a harder place to play, never a different game.
 */

/* 1. The canvas owns its gestures.
 *
 * With the default touch-action, the browser waits to see whether a touch is
 * a scroll or a pinch before it tells the page anything — and if it decides
 * "scroll", it cancels the pointer stream outright. That is what made
 * Wrecking Ball's rig unsteerable: it already listened on pointermove, but
 * the browser ate the drag to scroll the page. It also meant a quick
 * double-tap to jump zoomed the page instead. Claiming the gesture here fixes
 * both, and it is scoped to the canvas so the page around it still scrolls. */
.canvas-wrap canvas {
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* iOS needs this wider than the canvas.
 *
 * Reported from iOS Chrome: holding the screen to fly opens the copy/paste
 * callout instead, and the thruster never fires. The properties above are
 * correct and were not enough — WebKit begins the selection gesture from the
 * touch, not from the element under it, so a press that starts on the canvas
 * can still reach for text in the panel beside it. Killing selection across
 * the whole stage removes anything for that gesture to catch hold of. */
@media (hover: none) and (pointer: coarse) {
  .stage, .stage * {
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
  }
  /* The one exception: the score submit dialog has real inputs in it, and a
     wallet address is the single thing on this site people most need to be
     able to select and copy. */
  .stage input, .stage textarea {
    -webkit-user-select: text;
    user-select: text;
  }
}

/* ---------- the thrust pad ---------- */

/* An on-screen control for the one cabinet that needs a HOLD rather than a
 * tap. Suggested by the same bug report, and worth having even once the
 * selection gesture is beaten: a dedicated target cannot be mistaken for a
 * page gesture by any browser, and it puts the control under the thumb that
 * is already holding the phone instead of over the playfield you are trying
 * to watch.
 *
 * Bottom right because the runner flies on the left of the screen — a pad on
 * that side would sit exactly where the thing you are steering is. */
.thrust-pad { display: none; }

@media (hover: none) and (pointer: coarse) {
  /* Square, not round. city.css enforces `border-radius: 0 !important` on
     everything as a deliberate pixel-art rule — a circle here would be dead
     code that reads as a circle in the source and renders as a box. */
  .thrust-pad {
    position: absolute;
    right: 14px;
    bottom: 22px;
    z-index: 5;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 72px;
    height: 72px;
    padding: 0;
    /* Lighter than a normal control: it now sits over live gameplay, so it
       has to be findable by a thumb without hiding the rooftops behind it. */
    border: 2px solid rgba(204, 255, 0, 0.4) !important;
    background: rgba(10, 16, 8, 0.28) !important;
    color: #CCFF00 !important;
    font-size: 26px;
    line-height: 1;
    touch-action: none;
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
    /* Never let the pad swallow a press meant for the game underneath when
       the run is not live — the overlays sit above it. */
    transition: transform .06s ease, background .06s ease;
  }
  .thrust-pad:active,
  .thrust-pad.held {
    background: rgba(204, 255, 0, 0.3) !important;
    transform: scale(0.94);
  }
  .thrust-pad-label {
    position: absolute;
    bottom: -15px;
    left: 0;
    right: 0;
    text-align: center;
    font-size: 8.5px;
    letter-spacing: .14em;
    color: rgba(217, 227, 206, 0.5);
  }
}

/* 2. Touch-device behaviour.
 *
 * Everything in this block is gated on the PRIMARY pointer being coarse and
 * hover being unavailable — a phone or a tablet, not a laptop with a
 * touchscreen. Gating it means a desktop visitor gets byte-for-byte the
 * behaviour the cabinets always had, which matters because the leaderboard
 * already holds scores set under those rules. */
@media (hover: none) and (pointer: coarse) {
  /* Overlays stop swallowing the tap that starts the game.
   *
   * Every cabinet covers its canvas with a full-bleed .overlay for the ready,
   * paused and game-over screens. It is a backdrop, not a control, but it sat
   * on top of the canvas at inset:0 and absorbed the pointerdown the game was
   * listening for — so on a phone there was literally no way to start a run.
   * The real controls inside it are opted back in. */
  .canvas-wrap .overlay { pointer-events: none; }
  .canvas-wrap .overlay button,
  .canvas-wrap .overlay a,
  .canvas-wrap .overlay input,
  .canvas-wrap .overlay select { pointer-events: auto; }

  /* Hint text that matches the device: telling a phone to press Space is
     worse than telling it nothing. */
  .touch-only { display: revert; }
  .fine-only { display: none; }

  /* Tap targets. 44px is the smallest thing a thumb hits reliably, and the
     mute and replay buttons were sized for a mouse cursor. */
  .stage .ghost,
  .canvas-wrap .overlay button { min-height: 44px; }
  .replay-btn { padding-left: 22px; padding-right: 22px; }
}

/* Hidden by default, revealed by the block above. Outside the query so it
   applies on every device that is not a phone, including print and any
   context where the media query does not match. */
.touch-only { display: none; }

/* 4. Width.
 *
 * The stage is the widest thing on the page and the canvas inside it is a
 * 16:9-ish letterbox, so every pixel of padding costs real playfield height
 * on a phone: at 390px wide, 14px of padding either side is 28px of width and
 * about 16px of the sky. The stage goes edge to edge and the chrome around it
 * gets out of the way. */
@media (max-width: 720px) {
  .stage {
    padding: 10px 8px 12px;
    border-radius: 0;
    border-left: 0;
    border-right: 0;
  }

  /* The header put the wallet address, the HUD and the mute button in one
     unwrapping row; the address is 42 characters and pushed the rest off the
     side of the screen. */
  .stage-header {
    flex-wrap: wrap;
    gap: 6px 12px;
  }
  .stage-title {
    /* An address is not worth a line of a phone screen, but it is the proof
       that everyone is running the same city, so it shrinks rather than
       disappearing. */
    flex: 1 1 100%;
    font-size: 10px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .hud {
    flex: 1 1 auto;
    flex-wrap: wrap;
    gap: 10px;
    font-size: 12px;
  }

  .overlay { padding: 12px; gap: 6px; }
  .overlay h2 { font-size: 16px; }
  .overlay p { font-size: 12px; }
  /* The game-over board is the tallest overlay and it has to fit inside a
     ~220px canvas in portrait without spilling over the edges. */
  .board { min-width: 0; width: 100%; font-size: 11px; max-height: 40vh; overflow-y: auto; }
  .key-prompt { font-size: 13px; margin-top: 2px; }
  .controls-hint { font-size: 11px; }
}

/* ---------- landscape play (phones only) ---------- */

/* Driven by arcade-fullscreen.js, which only runs on a coarse pointer, so
   these rules can never reach a desktop: the class and the element they hang
   off are never created there. */

.rotate-gate {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 28px;
  background: #060906;
  color: #D9E3CE;
  font: 13px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
  text-align: center;
}
.rotate-gate[hidden] { display: none; }
.rotate-gate-inner { max-width: 300px; display: flex; flex-direction: column; align-items: center; gap: 14px; }
.rotate-gate h2 {
  margin: 0;
  font-size: 15px;
  letter-spacing: .2em;
  color: #CCFF00;
  font-weight: 700;
}
.rotate-gate p { margin: 0; color: #8FA197; font-size: 12.5px; }

/* A phone turning on its side. Built from borders rather than an image so it
   costs nothing and stays crisp — the cabinets ship no raster assets. */
.rotate-phone {
  width: 46px;
  height: 74px;
  border: 2px solid #CCFF00;
  border-radius: 7px;
  position: relative;
  animation: rotate-hint 2.4s ease-in-out infinite;
}
.rotate-phone i {
  position: absolute;
  left: 50%;
  bottom: 5px;
  width: 14px;
  height: 2px;
  margin-left: -7px;
  background: #CCFF00;
  opacity: .75;
}
@keyframes rotate-hint {
  0%, 30%   { transform: rotate(0deg); }
  55%, 100% { transform: rotate(-90deg); }
}
@media (prefers-reduced-motion: reduce) {
  .rotate-phone { animation: none; transform: rotate(-90deg); }
}

/* Sideways: the cabinet takes the screen.
 *
 * Covering the page rather than hiding its parts one by one — the five
 * cabinets do not share a page structure, and a list of things to hide would
 * be wrong on whichever one changed next. The stage is the only element that
 * has to be right. */
html.cabinet-landscape,
html.cabinet-landscape body { overflow: hidden; height: 100%; }

html.cabinet-landscape .stage {
  position: fixed;
  inset: 0;
  z-index: 900;
  margin: 0;
  padding: 4px 6px 6px;
  border: 0;
  border-radius: 0;
  background: var(--bg, #0a0f0c);
  display: flex;
  flex-direction: column;
  gap: 3px;
  max-width: none;
}
/* min-height:0 is what actually lets the canvas shrink: without it the flex
   item refuses to go below its content height and the playfield overflows the
   bottom of the screen, which is exactly the space we came here to gain. */
html.cabinet-landscape .canvas-wrap {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
html.cabinet-landscape .canvas-wrap canvas {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  border-radius: 0;
}
/* The stage header earns its line — it is the score. Everything explanatory
   goes, because in landscape the screen is 390px tall and prose is the first
   thing that should give up its space. */
/* The site nav is fixed at z-index 1000 and the stage sits at 900, so in
   landscape the bar was painting straight over the score row — full screen in
   name only, and the HUD was the thing it covered. Hidden here rather than
   restacked: "full screen" means the cabinet gets the glass. Turning the
   phone back to portrait brings the whole site back, which is also the only
   gesture a player already knows at that point. */
html.cabinet-landscape .city-nav,
html.cabinet-landscape .city-footer { display: none; }

html.cabinet-landscape .stage-header { flex: 0 0 auto; }
html.cabinet-landscape .stage-title { display: none; }
html.cabinet-landscape .controls-hint,
html.cabinet-landscape .legend,
html.cabinet-landscape .course-note { display: none; }
