/* Styles for play.js -- the mobile play shell.

   This file was previously wrapped in a JS IIFE, which is valid JavaScript and
   INVALID CSS: linked via <link rel=stylesheet> it parsed to zero rules, so the
   canvas kept touch-action:auto, the swipe kept scrolling the page, and play
   mode never became position:fixed. Keep this a plain stylesheet.

   Two jobs:
     1. stop the swipe from scrolling the page while playing (the actual bug)
     2. make play mode look like a normal mobile game: no nav, no page chrome,
        a persistent top-left Exit, and a canvas that fills the screen */

/* ---- the fix: gestures inside the PLAYFIELD never reach the scroller ----

   This used to sit on `.ocurp-surface`, which is the whole game PANEL. That
   silently killed scrolling on the game page: a swipe anywhere on screen -- on
   the text, the padding, the heading -- could not scroll the document, so a
   player who had not entered fullscreen could not reach anything below the
   game. The gesture lock belongs on the thing that actually consumes swipes
   (the canvas, or the DOM board), never on the container around it. */
/* ONLY while playing. This was unconditional, so a game's own board element
   kept touch-action:none for the whole time the page was open, not just during
   play. On meme-sound-generator the detected surface is the 300x934 pad grid
   (y 231..1165 on a 390x844 phone), which covers most of the first screen, so a
   swipe anywhere over it could not scroll the page -- exactly the "I still
   can't scroll down" report, on a page that is in fact 3525px tall.

   Browsing must stay fully scrollable. Play mode is the only time a gesture
   should be captured by the board instead of the document. */
html.ocurp-playing .ocurp-canvas,
html.ocurp-playing .ocurp-surface-inner {
  touch-action: none;
}
/* Contain the scroll chain on the BOARD only.

   This used to sit on `html.ocurp-touch, html.ocurp-touch body` as
   `overscroll-behavior: none`. That is what made a game page unscrollable on a
   phone: overscroll-behavior on the ROOT element disables the document's own
   scroll chaining, so a swipe anywhere on the page -- not just on the board --
   stopped scrolling. Tool pages never get .ocurp-touch, which is exactly why
   they scrolled fine and the games did not. The root element must stay `auto`;
   containing the board is sufficient to stop the game pulling the page. */
html.ocurp-touch .ocurp-canvas,
html.ocurp-touch .ocurp-surface-inner {
  overscroll-behavior: contain;
}

/* ---- play mode: hide page chrome, give the canvas the whole screen ---- */
html.ocurp-playing,
html.ocurp-playing body {
  overflow: hidden;
  height: 100%;
}
/* Hide the page chrome in play mode -- but NEVER the surface itself or any of
   its ancestors. The previous rule was `body > *:not(.ocurp-playbar)`, and the
   surface lives at section.ws-panel inside div.relative.z-10 inside body, so it
   hid that whole wrapper: play mode blanked the page to black and the game
   vanished instead of filling the screen. Walk up from the surface and hide each
   ancestor's OTHER children instead, which is precise by construction. */
html.ocurp-playing body > *:not(.ocurp-playbar):not(.ocurp-shell) {
  display: none !important;
}
/* The shell only needs to SURVIVE the chrome-hiding rule above -- it is not
   itself the fullscreen layer. Giving it position:fixed; inset:0 collapsed the
   surface to 0x0 in production: the surface is position:fixed with inset:0, so
   it resolved against a fixed parent that had no content height, and the whole
   play area vanished. Locally the page was short enough to hide this.
   Let the shell stay in normal flow and let the surface do the positioning.

   `padding: 0 !important` here was WRONG and shipped a visual regression on
   every game page, not just during play: this class is applied permanently at
   install time, so it stripped `px-5` off the page wrapper permanently. Every
   game panel went edge-to-edge on mobile, with the section headings crowded
   against the screen edge. Only reset these while play mode is actually on. */
html.ocurp-playing .ocurp-shell {
  position: static;
  margin: 0 !important;
  padding: 0 !important;
  max-width: none !important;
}
html.ocurp-playing .ocurp-surface {
  position: fixed;
  inset: 0;
  z-index: 9000;
  /* DO NOT force `display: flex` here. The play surface is sometimes the game's
     own laid-out element rather than a wrapper we created: checkers' #board IS
     the surface, and it is `display:grid grid-cols-8`. Forcing flex on it made
     every one of its 64 cells compute to 0x0, so the board rendered as a single
     black square with a stray dot in the middle. Centering comes from the
     element's own box instead. */
  background: var(--dark, #050505);
  /* The play bar is a 20px fixed strip at the top. The surface is inset:0, so
     without top padding the top row of the board sits UNDER it -- measured
     overlap 18px on checkers and 11px on connect-four, which is why the top
     row of pieces looked cut off. Reserve the bar's height. */
  padding: calc(20px + env(safe-area-inset-top)) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
}
html.ocurp-playing .ocurp-surface .ws-panel {
  border: 0 !important;
  background: transparent !important;
  padding: 0 !important;
  max-width: none !important;
}
html.ocurp-playing .ocurp-canvas {
  width: 100% !important;
  height: 100% !important;
  max-width: none !important;
  max-height: none !important;
  object-fit: contain;
  border: 0 !important;
  border-radius: 0 !important;
  touch-action: none;
}
/* the side panel (start/reset/score) has no room on a phone in play mode */
html.ocurp-playing .ocurp-side {
  display: none !important;
}
/* the canvas wrapper must not keep its width cap in play mode.

   This rule used to set `display: flex` too, which is what actually broke the
   DOM-board games. On canvas games the wrapper is an empty div and flex was
   harmless — it centred the canvas. But play.js also puts `ocurp-canvas-wrap` on
   the detected surface, and for a DOM-board game the surface IS the board:
   checkers' #board is `display:grid grid-cols-8`. Flexing it made all 64 cells
   compute to 0x0 and the board drew as one black square with a stray dot in the
   middle. So only flex when a canvas is actually inside. */
html.ocurp-playing .ocurp-canvas-wrap:not(:has(canvas)) {
  display: grid !important;
  place-content: center;
}
html.ocurp-playing .ocurp-canvas-wrap:has(canvas) {
  width: 100% !important;
  height: 100% !important;
  max-width: none !important;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ---- the bar: top-left Exit, exactly where a normal game puts it ---- */
/* Two states, two z-indexes. Getting this wrong in EITHER direction is a real
   user-facing bug, and both were shipped at some point:

   BROWSING  z-index 40, below the page's own chrome (50). At 9500 the bar
             painted over the "Games" back link and the site nav, and because a
             bar is a full-width fixed strip, everything in that strip became
             untappable -- "go back does nothing".
   PLAYING   z-index 9500, ABOVE the surface (9000). At 40 the surface covered
             the Exit button outright, so tapping it did nothing and the
             player was stuck in fullscreen. A/B verified: 40 -> Exit dead,
             9500 -> Exit works.

   The page chrome is display:none while playing, so lifting the bar above the
   surface in that state cannot cover anything the user needs. */
.ocurp-playbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 40;
  display: flex;
  gap: 10px;
  align-items: center;
  padding: calc(10px + env(safe-area-inset-top)) 12px 10px 12px;
  pointer-events: none;
}
html.ocurp-playing .ocurp-playbar {
  z-index: 9500;
}
/* Only the buttons take clicks. The bar is a full-width fixed strip, so without
   this it is a 60px transparent dead zone across the top of every game page. */
.ocurp-playbar > * { pointer-events: none; }
.ocurp-playbar button { pointer-events: auto; }
.ocurp-playbar button {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 44px;
  padding: 0 14px;
  border-radius: 9999px;
  font-family: "IBM Plex Sans", monospace;
  font-size: 10px;
  font-weight: 900;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: opacity 0.18s ease, transform 0.18s ease;
}

/* Exit: top-left, hidden until play mode so it never floats over the page.

   ORDERING TRAP: `.ocurp-playbar button` above sets pointer-events:auto and is
   MORE specific than this single-class rule, so it won every time. The result
   was a permanently invisible Exit button sitting on top of the page's own
   "Games" back link -- the tap hit the button, the link never fired, and the
   player saw nothing happen. Both rules below are >= that specificity so the
   hidden state genuinely disables it. */
.ocurp-playbar .ocurp-exit,
.ocurp-playbar button.ocurp-exit {
  position: absolute;
  top: calc(10px + env(safe-area-inset-top));
  left: 12px;
  background: rgba(10, 10, 10, 0.72);
  color: rgba(255, 255, 255, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.22);
  backdrop-filter: blur(8px);
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
}
html.ocurp-playing .ocurp-playbar .ocurp-exit,
html.ocurp-playing .ocurp-playbar button.ocurp-exit {
  opacity: 1;
  pointer-events: auto;
  visibility: visible;
}
.ocurp-exit:active {
  transform: scale(0.96);
}
.ocurp-exit__x {
  width: 11px;
  height: 11px;
  position: relative;
  flex: 0 0 auto;
}
.ocurp-exit__x::before,
.ocurp-exit__x::after {
  content: "";
  position: absolute;
  left: 0;
  top: 5px;
  width: 11px;
  height: 1.5px;
  background: currentColor;
  border-radius: 2px;
}
.ocurp-exit__x::before {
  transform: rotate(45deg);
}
.ocurp-exit__x::after {
  transform: rotate(-45deg);
}

/* Fullscreen button: the affordance that gets you there in the first place.

   It used to sit at the top-RIGHT, pinned by `margin-left:auto` inside the
   fixed full-width bar. On a phone that put it straight on top of the game
   title. Measured on conect-four at 390px: the pill occupied x 249..378,
   y 10..50, and the <h1> occupied x 136..290, y 32..60 -- a real overlap on
   the word "Four". The back button at x 20..120 was not affected.

   The strip beside the title is only ~150px wide and the pill is 129px, so
   side-by-side still clipped the last few letters. There is no room in that
   row on a 390px phone. So it goes UNDER the header, top-right, clear of both
   the back button and the title, and out of the way of the board. */
.ocurp-playbar button.ocurp-enter {
  position: absolute;
  top: calc(10px + env(safe-area-inset-top));
  right: 12px;
  background: color-mix(in srgb, var(--hue, #5ec8ff) 14%, transparent);
  color: var(--hue, #5ec8ff);
  border: 1px solid color-mix(in srgb, var(--hue, #5ec8ff) 40%, transparent);
}
/* Where the pill goes on a phone, from measured geometry (connect-four, 390px):
     back link  x  20..120   y 38..71
     h1         x 136..290   y 32..60
     subtitle   x 136..290   y 62..77
     panel top  y 109
   The header band is 32..77 and the free strip is y 78..108. A pill at
   top:64 sat on the subtitle; at top:10 it sat on the title. So anchor it to
   84px, which is below every header text and 25px clear of the panel. */
@media (max-width: 640px) {
  .ocurp-playbar button.ocurp-enter {
    /* The header band ends at 77 and the panel starts at 109, so the free strip
       is only 32px tall and the pill is 40. Rather than overlap either, place it
       in the panel's own top padding: it reads as a panel-level control and its
       rounded corner no longer crosses the panel border. */
    top: calc(78px + env(safe-area-inset-top));
  }
}
.ocurp-enter:active {
  transform: scale(0.96);
}
/* The Fullscreen pill is redundant once play mode is on, and it is wide. Hide it
   properly -- opacity alone leaves a target that still swallows taps aimed at
   the board underneath. */
html.ocurp-playing .ocurp-playbar button.ocurp-enter {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
}
.ocurp-enter__ico {
  width: 12px;
  height: 12px;
  flex: 0 0 auto;
  border: 1.5px solid currentColor;
  border-radius: 2px;
  position: relative;
}
.ocurp-enter__ico::before {
  content: "";
  position: absolute;
  inset: -1.5px -1.5px auto -1.5px;
  height: 6px;
  background: currentColor;
  clip-path: polygon(0 0, 100% 0, 100% 55%, 0 55%);
}

/* landscape phones: the bar hugs the notch correctly */
@media (orientation: landscape) and (pointer: coarse) {
  .ocurp-playbar {
    padding-top: calc(6px + env(safe-area-inset-top));
  }
  .ocurp-exit,
  .ocurp-enter {
    min-height: 36px;
    padding: 0 12px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ocurp-playbar button {
    transition: none;
  }
}

/* ---- the fullscreen consent sheet ----

   Shown when the player presses Start on a touch device. NOT window.confirm():
   a native dialog mid-game reads as a scam, and mobile Chrome suppresses it
   often enough to strand the player. Declining still drops into CSS-only play
   mode, so it is never a dead end. */
html.ocurp-sheet-open,
html.ocurp-sheet-open body {
  overflow: hidden;
}
.ocurp-sheet {
  position: fixed;
  inset: 0;
  z-index: 9800;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 16px;
  padding-bottom: calc(16px + env(safe-area-inset-bottom));
  background: rgba(3, 3, 3, 0.72);
  backdrop-filter: blur(6px);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.18s ease, visibility 0.18s ease;
}
.ocurp-sheet.is-open {
  opacity: 1;
  visibility: visible;
}
.ocurp-sheet__card {
  width: 100%;
  max-width: 420px;
  background: #0d0d0d;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 18px;
  padding: 20px;
  font-family: "IBM Plex Sans", ui-monospace, monospace;
  color: #f5f5f5;
  transform: translateY(12px);
  transition: transform 0.2s ease;
}
.ocurp-sheet.is-open .ocurp-sheet__card {
  transform: translateY(0);
}
.ocurp-sheet__title {
  margin: 0 0 8px;
  font-size: 17px;
  font-weight: 900;
  letter-spacing: -0.01em;
}
.ocurp-sheet__body {
  margin: 0 0 16px;
  font-size: 12.5px;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.66);
}
.ocurp-sheet__row {
  display: flex;
  gap: 10px;
}
.ocurp-sheet__btn {
  flex: 1 1 0;
  min-height: 46px;          /* thumb-sized, not a 32px desktop button */
  border-radius: 12px;
  font-family: inherit;
  font-size: 12px;
  font-weight: 900;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  border: 1px solid transparent;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.12s ease, background 0.12s ease;
}
.ocurp-sheet__btn:active {
  transform: scale(0.97);
}
.ocurp-sheet__btn--ghost {
  flex: 0 0 auto;
  min-width: 104px;
  background: transparent;
  border-color: rgba(255, 255, 255, 0.16);
  color: rgba(255, 255, 255, 0.72);
}
.ocurp-sheet__btn--go {
  background: var(--hue, #5ec8ff);
  color: #140600;
}
.ocurp-sheet__btn--go:focus-visible,
.ocurp-sheet__btn--ghost:focus-visible {
  outline: 2px solid var(--hue, #5ec8ff);
  outline-offset: 2px;
}
@media (min-width: 640px) {
  .ocurp-sheet {
    align-items: center;
  }
  .ocurp-sheet__card {
    border-radius: 16px;
  }
}

/* The game's own `touch-none` class locked the page for the whole time the
   page was open, not just during play. A canvas that fills the first screen then
   made the whole document unscrollable while browsing: measured delta 0 on
   falling-sand, whose canvas spans y~560-900 of a 2161px page. Browsing must
   always scroll; only play mode captures gestures. Paired with the
   html.ocurp-playing scope in play.css, this re-enables the page scroll while
   leaving the gesture lock intact once play mode is on. */
html:not(.ocurp-playing) .touch-none {
  touch-action: auto !important;
}
