/* Super Blocko */

:root {
  --bg: #0a0a14;
  --panel: #15152a;
  --panel-2: #1c1c36;
  --line: #2a2a4a;
  --text: #eceaf6;
  --muted: #9a9ac0;
  --accent: #5858FD;
  --amber: #ffb703;
}

* { box-sizing: border-box; }

/* A class that sets `display` beats the browser's own `[hidden]` rule, so an
   element hidden from JavaScript with `el.hidden = true` stays on screen and
   the code looks correct while doing nothing. That is exactly how the Fire
   button stayed visible without a blaster: `.tbtn` sets `display: grid`. This
   reset settles it for every element, now and later. */
[hidden] { display: none !important; }


html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: "Segoe UI", system-ui, -apple-system, Roboto, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

#shell {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: max(10px, env(safe-area-inset-top)) max(12px, env(safe-area-inset-right))
           max(10px, env(safe-area-inset-bottom)) max(12px, env(safe-area-inset-left));
}

/* HUD */
.hud {
  width: 100%;
  max-width: 1100px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 16px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel);
  flex-shrink: 0;
}
.hud-back { color: var(--muted); text-decoration: none; font-size: 13.5px; white-space: nowrap; }
.hud-back:hover { color: var(--text); }
.hud-back::before { content: "< "; }
.hud-stats { display: flex; gap: 22px; flex-wrap: wrap; justify-content: center; }
.stat { display: flex; flex-direction: column; align-items: center; }
.stat-label {
  font-size: 9.5px;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--muted);
}
.stat-value {
  font-family: "Consolas", "Courier New", monospace;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0.5px;
}
.hud-btn {
  padding: 8px 16px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 13.5px;
  cursor: pointer;
  white-space: nowrap;
}
.hud-btn:hover { color: var(--text); border-color: var(--accent); }
.hud-buttons { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.hud-btn .sound-off { display: none; }
.hud-btn[aria-pressed="true"] .sound-on { display: none; }
.hud-btn[aria-pressed="true"] .sound-off { display: inline; }
.hud-btn[aria-pressed="true"] { color: #ff8c8c; border-color: rgba(255, 140, 140, 0.35); }

@media (prefers-reduced-motion: reduce) {
  .btn:hover { transform: none; }
}

/* Screen */
.screen {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  place-items: center;
  width: 100%;
}
#game {
  display: block;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
}
/* Set by fitCanvas when the buffer is already drawn at screen resolution.
   Nearest neighbour is right for a stretched low resolution buffer and wrong
   for this one, where it only adds aliasing. */
#game.is-hires {
  image-rendering: auto;
}
#game {
  border-radius: 10px;
  border: 1px solid var(--line);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.55);
  background: #5c94fc;
  max-width: 100%;
  max-height: 100%;
}

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

/* Touch controls, only shown on touch devices */
.touch {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  justify-content: space-between;
  gap: 10px;
  /* Sized off the viewport and kept clear of notches and the home bar, so the
     jump button can never end up half off the edge of a narrow phone. */
  padding: 0 max(10px, env(safe-area-inset-right)) max(12px, env(safe-area-inset-bottom))
             max(10px, env(safe-area-inset-left));
  pointer-events: none;
}
.touch[hidden] { display: none; }
.touch-left, .touch-right {
  display: flex;
  align-items: flex-end;
  min-width: 0;
}
/* The two directions are spaced apart deliberately. Sitting them side by side
   puts both under one thumb, which is how a run turns into a stutter. */
.touch-left { gap: clamp(14px, 5vw, 26px); }
/* Run and Fire share a row above Jump. Three buttons side by side plus the two
   directions came to more than a 375px phone is wide, and the group would have
   reached in over the play area. */
.touch-right {
  display: grid;
  grid-template-columns: auto auto;
  grid-template-areas:
    "run  fire"
    ".    jump";
  gap: clamp(8px, 2.6vw, 14px);
  justify-items: end;
}
.tbtn-run { grid-area: run; }
.tbtn-fire { grid-area: fire; }
.tbtn-jump { grid-area: jump; }
.tbtn-fire {
  width: clamp(56px, 16vw, 68px);
  height: clamp(56px, 16vw, 68px);
  font-size: 12px;
  background: rgba(255, 122, 26, 0.3);
  border-color: rgba(255, 183, 3, 0.5);
}
.tbtn {
  pointer-events: auto;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: clamp(58px, 17vw, 74px);
  height: clamp(58px, 17vw, 74px);
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.3);
  background: rgba(10, 10, 22, 0.5);
  color: #fff;
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  -webkit-tap-highlight-color: transparent;
  touch-action: none;
  user-select: none;
}
.tbtn svg { width: 46%; height: 46%; display: block; }
/* The button itself is the only hit target: a press landing on the arrow inside
   it must not read as a press on a different element. */
.tbtn > * { pointer-events: none; }
.tbtn:active, .tbtn.is-down { background: rgba(88, 88, 253, 0.6); }
.tbtn-dir { background: rgba(10, 10, 22, 0.58); }
.tbtn-jump { width: clamp(74px, 23vw, 96px); height: clamp(74px, 23vw, 96px); }
.tbtn-run {
  width: clamp(56px, 16vw, 68px);
  height: clamp(56px, 16vw, 68px);
  font-size: 12px;
}
/* Latched controls stay lit, so it is obvious the game is still in run mode */
.tbtn[aria-pressed="true"] {
  background: rgba(255, 183, 3, 0.55);
  border-color: rgba(255, 214, 120, 0.9);
  color: #1a1400;
}

/* ------------------------------------------------------- touch device layout */
/* Set from setupTouch(), so a desktop keeps its framed, padded presentation. */
body.has-touch #shell {
  gap: 6px;
  padding: max(4px, env(safe-area-inset-top)) max(6px, env(safe-area-inset-right))
           max(4px, env(safe-area-inset-bottom)) max(6px, env(safe-area-inset-left));
}
body.has-touch .hud {
  padding: 5px 10px;
  gap: 10px;
  border-radius: 8px;
}
body.has-touch .hud-back { display: none; }
body.has-touch .hud-stats { gap: 12px; }
body.has-touch .stat-label { font-size: 8px; letter-spacing: 1px; }
body.has-touch .stat-value { font-size: 14px; }
body.has-touch .hud-btn { padding: 5px 8px; font-size: 11px; }
body.has-touch .footnote { display: none; }
body.has-touch #game {
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

/* Portrait on a phone is the tightest case: the level is only as wide as the
   screen, so every row of chrome above it costs picture. The HUD is squeezed
   onto two short rows and the game is pinned just under it, which also puts it
   above the thumbs rather than behind them. */
@media (orientation: portrait) {
  body.has-touch .hud { padding: 4px 8px; gap: 6px; }
  body.has-touch .hud-stats { gap: 10px; }
  body.has-touch .stat-label { font-size: 7.5px; letter-spacing: 0.8px; }
  body.has-touch .stat-value { font-size: 12.5px; }
  body.has-touch .hud-btn { padding: 4px 7px; font-size: 10px; }
  body.has-touch .hud-buttons { gap: 6px; }
  body.has-touch .screen { align-items: start; }
}

/* Landscape hint */
.rotate {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(4, 4, 12, 0.94);
  text-align: center;
}
.rotate[hidden] { display: none; }
.rotate-inner { max-width: 320px; }
.rotate h2 { margin: 0 0 10px; font-size: 21px; }
.rotate p { margin: 0 0 22px; color: var(--muted); font-size: 14.5px; line-height: 1.6; }
.rotate-icon {
  display: block;
  width: 64px;
  height: 40px;
  margin: 0 auto 22px;
  border: 3px solid var(--accent);
  border-radius: 7px;
  animation: tilt 1.8s ease-in-out infinite;
}
@keyframes tilt {
  0%, 45% { transform: rotate(90deg); }
  55%, 100% { transform: rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  .rotate-icon { animation: none; transform: rotate(0deg); }
}

/* Overlays */
.overlay {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  place-items: center;
  padding: max(14px, env(safe-area-inset-top)) max(14px, env(safe-area-inset-right))
           max(14px, env(safe-area-inset-bottom)) max(14px, env(safe-area-inset-left));
  background: rgba(4, 4, 12, 0.82);
  backdrop-filter: blur(6px);
}
.overlay[hidden] { display: none; }

.modal {
  width: min(600px, 100%);
  max-height: min(86vh, 620px);
  max-height: min(86dvh, 620px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
}
.modal-sm { width: min(440px, 100%); }

.modal-head {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 24px 14px;
  border-bottom: 1px solid var(--line);
}
.modal-head h1 { margin: 0; font-size: 30px; letter-spacing: -0.8px; }
.modal-head h2 { margin: 0; font-size: 21px; }
.kicker {
  margin: 0 0 4px;
  font-size: 11px;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--accent);
}
.link-back { color: var(--muted); font-size: 13.5px; text-decoration: none; white-space: nowrap; }
.link-back:hover { color: var(--text); }
.icon-btn {
  width: 32px; height: 32px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--muted);
  font: inherit;
  cursor: pointer;
}
.icon-btn:hover { color: var(--text); border-color: var(--accent); }

.modal-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 18px 24px 22px;
}
.intro { margin: 0 0 18px; color: var(--muted); font-size: 14.5px; line-height: 1.7; }

.keys { display: grid; gap: 9px; padding-top: 4px; }
.key-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  font-size: 13.5px;
}
.key-row em { font-style: normal; color: var(--muted); }
kbd {
  display: inline-block;
  min-width: 24px;
  padding: 3px 7px;
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 6px;
  background: var(--panel-2);
  color: var(--text);
  font-family: inherit;
  font-size: 11.5px;
  text-align: center;
}
.level-pick { margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--line); }
.pick-label {
  display: block;
  margin-bottom: 9px;
  font-size: 11px;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--muted);
}
.segmented {
  display: inline-flex;
  padding: 4px;
  gap: 4px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel-2);
}
.segmented button {
  padding: 8px 18px;
  border: none;
  border-radius: 7px;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}
.segmented button:hover:not(:disabled) { color: var(--text); }
.segmented button.is-on { background: var(--accent); color: #fff; font-weight: 600; }
.segmented button:disabled { opacity: 0.4; cursor: not-allowed; }
.pick-note { margin: 10px 0 0; font-size: 12.5px; color: var(--muted); }

.tip {
  margin: 20px 0 0;
  padding: 11px 14px;
  border-radius: 9px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--muted);
  font-size: 13px;
}

.stats { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 10px 18px; font-size: 15px; }
.stats dt { color: var(--muted); }
.stats dd { margin: 0; font-weight: 700; text-align: right; font-family: "Consolas", "Courier New", monospace; }

.modal-foot {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 14px 24px;
  border-top: 1px solid var(--line);
  background: var(--panel-2);
}
.foot-note { color: var(--muted); font-size: 12.5px; }

.btn {
  padding: 11px 24px;
  border-radius: 10px;
  border: 1px solid transparent;
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease, transform .12s ease;
}
.btn:hover { transform: translateY(-1px); }
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: #4646e0; }
.btn-ghost { background: transparent; border-color: var(--line); color: var(--text); }
.btn-ghost:hover { border-color: var(--accent); }

@media (max-width: 620px) {
  .hud { padding: 8px 12px; }
  .hud-stats { gap: 12px; }
  .stat-value { font-size: 14px; }
  .modal-head h1 { font-size: 23px; }
}

/* Screen reader only, for status announcements that have no visual equivalent */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}


/* ------------------------------------------------------------ small screens */

@media (max-width: 520px) {
  .hud { padding: 8px 10px; gap: 8px; }
  .hud-stats { gap: 12px; }
  .stat-value { font-size: 16px; }
  .stat-label { font-size: 9px; letter-spacing: 1.2px; }
  .hud-back { font-size: 12px; }
}

/* A landscape phone has no room to spend on a footnote or a tall HUD. */
@media (max-height: 480px) {
  #shell { gap: 8px; }
  .footnote { display: none; }
  .hud { padding: 6px 10px; }
  .stat-value { font-size: 15px; }
  .modal-head { padding-top: 12px; padding-bottom: 12px; }
  .modal-body { padding-top: 12px; padding-bottom: 14px; }
  .modal-foot { padding: 10px 18px; }
}


/* A short text link is a poor tap target on a phone; pad it out to a real one. */
.hud-back, .link-back {
  display: inline-flex;
  align-items: center;
  min-height: 34px;
  padding: 4px 2px;
}

/* ------------------------------------------------------- the More sheet */
/* On a phone the only settings worth a permanent tap target is Pause. Sound,
   music and fullscreen are moved into this sheet by setupMoreMenu, which moves
   the real buttons rather than copying them, so there is no second copy of any
   state to keep in step. */
.hud-more { display: none; }
body.has-touch .hud-more { display: inline-flex; }
/* This game's HUD sits at the top of the screen, so the sheet drops down from
   it. Opening upwards put it off the top edge entirely, which reads as a button
   that does nothing. */
.more-sheet {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  z-index: 12;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: rgba(12, 12, 26, 0.96);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.55);
  min-width: 148px;
}
.more-sheet[hidden] { display: none; }
.more-sheet .hud-btn { width: 100%; justify-content: center; }
.hud-buttons { position: relative; }

/* A 24px high button is a poor tap target. On touch every HUD control gets a
   real one, which is also what the audit script checks for. */
body.has-touch .hud-btn {
  min-height: 40px;
  min-width: 44px;
  padding-top: 8px;
  padding-bottom: 8px;
}

/* On a short landscape phone a stacked sheet is tall enough to come down over
   the touch controls, and a settings panel that covers Run and Fire mid level
   is how a player dies reading a menu. It lays out as a row there instead. */
@media (max-height: 520px) {
  .more-sheet { flex-direction: row; }
  .more-sheet .hud-btn { width: auto; }
}

/* ------------------------------------------------- input specific guidance */
/* A phone has no Shift key and a desktop has no Go button. Showing either one
   the other's controls is noise at best and wrong at worst, so every guide is
   tagged with the input it describes and only that input's version is shown.
   has-touch is set from the touch setup, so the default is the keyboard. */
/* Hiding only, and marked important on purpose: the elements being gated carry
   layout rules of their own (.controls ul, .keys .key-row) whose specificity
   beats a plain attribute selector, so a polite rule here silently loses and
   both guides show at once. Nothing sets the visible state - whatever display
   the element already had is the right one. */
body:not(.has-touch) [data-input="touch"] { display: none !important; }
body.has-touch [data-input="key"] { display: none !important; }
/* The stand-in for a kbd badge on a touch guide: it names an on screen button
   rather than a key, so it must not look like a key. */
.tag {
  display: inline-block;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.4px;
  color: var(--text);
  white-space: nowrap;
}

/* ----------------------------------------------------------------- awards */
.awards-title {
  margin: 16px 0 8px;
  font-size: 11px;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--muted);
}
.awards { margin: 0; padding: 0; list-style: none; display: grid; gap: 6px; }
.award {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 7px 10px;
  border-radius: 9px;
  border: 1px solid var(--line);
  background: rgba(8, 8, 20, 0.5);
  /* Locked awards stay on the list rather than being hidden: a player can only
     go after something they can see. */
  opacity: 0.45;
}
.award.is-earned { opacity: 1; border-color: rgba(255, 209, 102, 0.45); }
.award.is-new { border-color: var(--amber); box-shadow: 0 0 18px rgba(255, 183, 3, 0.28); }
.award-name { font-weight: 700; font-size: 13px; }
.award-note { flex: 1 1 auto; font-size: 11.5px; color: var(--muted); }
.award-tag {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: #1a1400;
  background: var(--amber);
  padding: 2px 7px;
  border-radius: 999px;
}

/* Confetti, added and removed by celebrate(). Decoration only, and skipped
   outright under prefers-reduced-motion - the award list is the substance. */
.celebrate { position: relative; overflow: hidden; }
.confetti {
  position: absolute;
  top: -12px;
  width: 7px;
  height: 11px;
  border-radius: 2px;
  animation: confetti-fall linear forwards;
  pointer-events: none;
}
@keyframes confetti-fall {
  0% { opacity: 0; transform: translateY(-12px) rotate(0deg); }
  10% { opacity: 1; }
  100% { opacity: 0; transform: translateY(150px) rotate(420deg); }
}

/* ------------------------------------------------------- character picker */
.char-pick { margin-top: 18px; }
.chars {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
  gap: 8px;
  margin-top: 8px;
}
.char {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px 4px 6px;
  border-radius: 10px;
  border: 2px solid transparent;
  outline: 1px solid var(--line);
  background: rgba(8, 8, 20, 0.5);
  cursor: pointer;
  transition: transform .15s ease, border-color .15s ease;
}
.char canvas { width: 44px; height: auto; display: block; pointer-events: none; }
.char-name {
  font-size: 10px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--muted);
  pointer-events: none;
}
.char:hover { transform: translateY(-2px); }
.char.is-on {
  border-color: var(--amber);
  outline-color: var(--amber);
  box-shadow: 0 0 16px rgba(255, 183, 3, 0.3);
}
.char.is-on .char-name { color: var(--text); }
