/* #279: 44px is the tap floor Apple and Google both publish, and the
   header pill (Home / Mute / Help) is the one control on every screen of
   every product. Measured before this: 31.6px to 42px across 12 products.
   Raised to the floor rather than to taste. */
/* Skyhop — the play surface is a fixed logical world scaled to fit, so the
   physics are identical on every device (same reasoning as Scamper). */
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { height: 100%; overflow: hidden; overscroll-behavior: none; }
body {
  background: #08356b; color: #ccfbf1;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  height: 100vh; height: 100dvh;   /* fallback first: dvh is Safari 15.4+ */
  user-select: none; -webkit-user-select: none; touch-action: none;
}
#app { display: flex; flex-direction: column; height: 100%; }
#hdr {
  flex: 0 0 auto; display: flex; align-items: center; gap: 6px;
  padding: calc(6px + env(safe-area-inset-top)) 10px 6px;
  background: #042f2e; font-weight: 700; font-size: 14px;
}
.pill {
  background: #0b4f9e; color: inherit; border: 0; border-radius: 10px;
  min-width: 44px; height: 44px; font-size: 15px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
/* R-C5: the 44px floor, which this game had been under since it shipped.
   Scoped to the header so a .pill used elsewhere keeps its own size. */
#hdr .pill { min-width: 44px; min-height: 44px; height: auto; }
.pill:active { transform: scale(.94); }

.scr { display: none; flex: 1 1 auto; min-height: 0; flex-direction: column; }
.scr.on { display: flex; }
/* R-C15b: these screens CENTRE and SCROLL, which is the one combination that
   puts content out of reach. When the content is taller than the box,
   `justify-content: center` pushes the first child ABOVE the top edge and
   scrollTop cannot go negative. Measured at 375x620 — a phone with its URL bar
   showing — word-sleuth's help began at y = -585: the first three cards could
   not be read or scrolled to, and had not been since it shipped.
   Auto margins on the end children centre identically while the content fits
   and collapse to zero once it does not. */
#scr-menu, #scr-how {
  align-items: center; gap: 10px;
  padding: 14px; overflow-y: auto; overscroll-behavior: contain;
}
#scr-menu > :first-child, #scr-how > :first-child { margin-top: auto; }
#scr-menu > :last-child,  #scr-how > :last-child  { margin-bottom: auto; }
#scr-menu h1 { font-size: 30px; }
.sub { color: #5eead4; font-size: 14px; text-align: center; max-width: 340px; line-height: 1.45; }
.pickLabel { color: #99f6e4; font-size: 13px; font-weight: 700; margin-top: 4px; }
/* WRAPS, because the number of things in it is not fixed. Six craft at
   "Comet · 60✦" come to 457px of buttons; nowrap made them 497px of content in
   a 347px box, and #scr-menu's overflow-x:auto turned that into a menu the
   player could slide sideways. A row whose contents GROW (craft, modes,
   speeds) must wrap; only a row of fixed peers may stay on one line. */
.pickRow { display: flex; flex-wrap: wrap; justify-content: center;
           gap: 8px; width: 100%; max-width: 380px; }
.big {
  width: 100%; max-width: 380px;
  background: #115e59; color: inherit; border: 0; border-radius: 14px;
  padding: 13px; font-size: 16px; font-weight: 700; cursor: pointer;
}
.big.primary { background: #2dd4bf; color: #042f2e; }
.big:active { transform: translateY(2px); }
.card {
  background: #115e59; border-radius: 14px; padding: 12px 14px;
  width: 100%; max-width: 380px; font-size: 14px; line-height: 1.5;
}
.card h2 { font-size: 15px; margin-bottom: 5px; }

#stageWrap {
  flex: 1 1 auto; min-height: 0; position: relative;
  display: flex; align-items: center; justify-content: center;
  background: #08356b; overflow: hidden;
}
canvas#stage { display: block; touch-action: none; }

.ovl {
  position: absolute; inset: 0; z-index: 30;
  display: none; align-items: flex-start; justify-content: center; padding: 18px;
  background: rgba(4, 47, 46, .93); overflow-y: auto; overscroll-behavior: contain;}
.ovl.on { display: flex; }
.ovlCard {
  background: #115e59; border-radius: 18px; padding: 20px;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  width: 100%; max-width: 330px; text-align: center; margin: auto;}
#ovlTitle { font-size: 23px; font-weight: 800; }
#ovlSub { color: #99f6e4; font-size: 15px; line-height: 1.45; white-space: pre-line; }

.toast-host {
  position: fixed; left: 50%; transform: translateX(-50%);
  top: calc(10px + env(safe-area-inset-top));
  z-index: 9000; display: flex; flex-direction: column; gap: 8px;
  align-items: center; pointer-events: none; width: max-content;
  max-width: calc(100vw - 24px);
}
.toast {
  pointer-events: auto; cursor: pointer;
  padding: 10px 15px; border-radius: 12px;
  background: #f0fdfa; color: #08356b;
  box-shadow: 0 10px 30px rgba(0,0,0,.4);
  font-size: 14px; font-weight: 700;
  opacity: 0; transform: translateY(-10px);
  transition: opacity .2s ease, transform .2s ease;
}
.toast.in { opacity: 1; transform: translateY(0); }
@media (prefers-reduced-motion: reduce) { .toast { transition: none; } }


/* ---- Vapour Trail ------------------------------------------------------- */

/* The progress bar lives in the header, where it is always visible and costs
   no playfield. R15: it is the goal, the score, and the thing that makes
   attempt 31 feel different from attempt 30. */
#hdrTitle {
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; font-weight: 700; font-size: 15px;
}
#bar { flex: 1 1 auto; position: relative; height: 22px; min-width: 60px;
       border-radius: 999px; background: rgba(0,0,0,0.30);
       border: 2px solid rgba(255,255,255,0.16); overflow: hidden; }
#barFill { position: absolute; inset: 0 auto 0 0; width: 0%;
           background: linear-gradient(90deg, #ff6ec7, #ffd166); transition: width .12s linear; }
#barPct { position: absolute; inset: 0; display: flex; align-items: center;
          justify-content: center; font-size: 12px; font-weight: 800;
          color: #fff; text-shadow: 0 1px 2px rgba(0,0,0,.6); }

#stageWrap { position: relative; flex: 1; min-height: 0; }
canvas#stage { display: block; touch-action: none; }

/* The tutorial line sits UNDER the playfield rather than across it. The
   original puts a permanent instruction band and a skip bar over the top of
   the tunnel, which spends screen on a control you use once. */
#tutLine { position: absolute; left: 0; right: 0; bottom: 8px; display: none;
           text-align: center; font-size: 17px; font-weight: 800; color: #fff;
           text-shadow: 0 2px 6px rgba(0,0,0,.75); pointer-events: none;
           padding: 0 12px; }

/* Level tiles. 64px tall clears the 44px tap floor with room for three lines. */
#lvGrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
          gap: 8px; width: 100%; margin: 4px 0 10px; }
.lvTile { min-height: 64px; display: flex; flex-direction: column; align-items: center;
          justify-content: center; gap: 1px; padding: 8px 6px; border-radius: 12px;
          border: 2px solid rgba(255,255,255,0.18); background: rgba(0,0,0,0.24);
          color: #eaf4ff; font: inherit; cursor: pointer; }
.lvTile b { font-size: 20px; font-weight: 800; line-height: 1; }
.lvTile span { font-size: 11px; opacity: .85; }
.lvTile i { font-style: normal; font-size: 11px; font-weight: 800; color: #ffd166; }

.skinBtn { min-height: 44px; padding: 8px 14px; border-radius: 999px;
           border: 2px solid rgba(255,255,255,0.18); background: rgba(0,0,0,0.24);
           color: #eaf4ff; font: inherit; font-weight: 700; cursor: pointer; }
.skinBtn.on { border-color: #ffd166; color: #ffd166; }
.skinBtn.locked { opacity: .45; cursor: not-allowed; }

#ovlExtra { display: flex; flex-direction: column; gap: 8px; width: 100%; }

/* THE SAME 380px COLUMN AS EVERYTHING ELSE. #scr-menu centres its children,
   which only reads as centred if they all agree on a width — .big, .pickRow,
   #custom and .sub all cap at 380. .row2 and .modeNote were added in the menu
   overhaul without the cap, so on a wide landscape window the two button rows
   ran edge to edge while every other element stayed in the column. That is the
   "some elements aren't centred" the owner saw: they WERE centred, they were
   just four times wider than their neighbours. */
.row2 { display: flex; gap: 8px; width: 100%; max-width: 380px; }
/* Three peers need smaller type to stay one line at 375px, and the same 380
   column as everything else so the menu reads as one stack (R-C12a). */
.row3 { display: flex; gap: 6px; width: 100%; max-width: 380px; }
.row3 .big { flex: 1; padding: 13px 4px; font-size: 14px; min-height: 44px;}
.courseRow { display: flex; gap: 6px; width: 100%; max-width: 380px;
             align-items: center; justify-content: center; }
.crsLbl { color: #99f6e4; font-size: 13px; font-weight: 700; }
.crsNum { flex: 0 1 92px; min-width: 0; background: rgba(0,0,0,0.30);
          color: #e2f7f3; border: 2px solid rgba(255,255,255,0.16);
          border-radius: 10px; padding: 10px 8px; font: inherit;
          font-size: 16px; font-weight: 700; text-align: center; }
.row2 .big { flex: 1; }
/* The one line under the two mode buttons that says what each will do — which
   level Play will start, and how far Endless has ever got. Two facts, one row,
   so neither button needs to carry a subtitle that changes its width. */
/* PADDING, NOT MARGIN, for the 2px inset. `width: 100%` plus a 2px margin on
   each side is 4px WIDER than the column it sits in — margins live outside the
   width — and the right-hand 2px of that was real horizontal scroll on
   #scr-menu. Padding is inside the box (box-sizing: border-box), so the inset
   costs nothing. */
.modeNote { display: flex; justify-content: space-between; gap: 10px;
            width: 100%; max-width: 380px; box-sizing: border-box;
            margin: -2px 0 2px; padding: 0 2px; color: #99f6e4; font-size: 12px;
            font-weight: 700; min-height: 15px; }

/* R34's legend rows, one width so a stacked legend aligns (#166). */
.lgRow { display: flex; gap: 10px; align-items: flex-start; padding: 6px 0; }
.lgArt { flex: 0 0 34px; height: 34px; display: flex; align-items: center;
         justify-content: center; font-size: 20px; border-radius: 8px;
         background: rgba(0,0,0,0.28); }
.lgTxt b { display: block; font-size: 13px; }
.lgTxt small { display: block; font-size: 12px; opacity: .8; line-height: 1.3; }

/* ⚙️ Settings — the shared disclosure shape (R-C23). */
#custom { width: 100%; max-width: 380px; background: rgba(0,0,0,0.28); border: 3px solid rgba(255,255,255,0.12); border-radius: 16px; padding: 10px 12px; text-align: left; }
#custom summary { font-weight: 800; font-size: 16px; cursor: pointer; list-style: none; }
#custom summary::-webkit-details-marker { display: none; }
/* WRAPS for the same reason .pickRow does. At 320px "Course length" plus its
   three-way segmented control is 271px of content in a 262px row, so the last
   option was clipped — a control you cannot fully see or reliably hit. Wrapping
   drops the control onto its own line only when it has to; at 375px both rows
   still sit on one line, unchanged. */
.field { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 10px; }
.field .lbl { flex: 1; font-weight: 700; font-size: 14px; color: #99f6e4; }
.seg { display: flex; gap: 4px; background: rgba(0,0,0,0.3); border-radius: 12px; padding: 3px; }
.seg button { min-height: 44px; min-width: 66px; border: none; border-radius: 9px; background: transparent; color: #5eead4; font-weight: 800; font-size: 13px; font-family: inherit; cursor: pointer; }
.seg button.on { background: #2dd4bf; color: #042f2e; min-height: 44px; }
.blurb { color: #99f6e4; font-weight: 700; font-size: 12.5px; margin-top: 10px; line-height: 1.45; }

/* #228. A LANDSCAPE WINDOW UNDER 500px TALL MUST NOT SPEND A ROW ON THE
   VERSION CHIP. Every other product in the catalogue floats it into the
   corner below that height; vapour-trail had no landscape rules at all, so it
   was paying ~12% of a 375px-tall window for something nobody reads twice.
   The min-width guard matters: on a narrow landscape window a floated chip
   would sit on top of the playfield. */
@media (orientation: landscape) and (max-height: 500px) and (min-width: 600px) {
  #app { position: relative; }
  #footer { position: absolute; right: 0; bottom: 0;
            padding-bottom: env(safe-area-inset-bottom); }
}
