/* The shared look.
 *
 * Every game in this repository uses this sheet, so they read as one family:
 * same dark ground, same panel treatment, same direction pad, same axis
 * colours. A game adds only what is genuinely its own -- a score readout, a
 * game-over card -- and inherits the rest.
 *
 * The axis colours are the part that matters most. Orange is always axis 0,
 * green axis 1, blue axis 2, purple the fourth dimension, in every game. That
 * is a piece of spatial vocabulary the player carries between them. */

:root {
  --bg: #0e1116; --panel: #161c26; --line: #2c3646;
  --ink: #e8eef7; --dim: #8fa0b8; --accent: #4cc9f0; --good: #6ee7a8;
  --bad: #ff5d6c;
  /* One accent per axis, so the pairs read as pairs. */
  --ax0: #ff9e6d; --ax1: #6ee7a8; --ax2: #7cc4ff; --ax3: #c89bff;
}
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; overflow: hidden; background: var(--bg); }
body { font: 14px/1.5 ui-sans-serif, system-ui, -apple-system, sans-serif; color: var(--ink); }

/* dvh follows the visible area as a mobile browser's chrome slides in and out;
   vh does not, and leaves the bottom of the board under the URL bar. */
#view { display: block; width: 100vw; height: 100vh; height: 100dvh;
        touch-action: none; cursor: grab; }
#view:active { cursor: grabbing; }

.panel {
  position: fixed;
  /* A drag that starts on a panel should not smear a text selection across
     the UI. */
  user-select: none;
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px;
  backdrop-filter: blur(8px);
}

/* Wide enough for the two inverted-T key clusters side by side. They are the
   widest thing the panel holds, so they set its width rather than being
   squeezed into whatever was left over. */
#hud { top: 14px; left: 14px; width: 290px; }
#hud h1 { margin: 0 0 2px; font-size: 15px; letter-spacing: .02em; }
#blurb { margin: 0 0 10px; color: var(--dim); font-size: 12.5px; }
#status { margin-top: 8px; font-weight: 600; letter-spacing: .08em; font-size: 12px; }
#status.solved { color: var(--good); }

/* Direction pad */
.padhead { font-weight: 600; margin: 12px 0 8px; }
/* The direction pad, as inverted-T clusters laid out the way the keys are.
 *
 * A cluster is one T: its up key above, the other three in a row beneath, so
 * the pad is a picture of the keyboard rather than a list of directions. A
 * player hunting for "the key above S" finds it in the same relative place on
 * screen as it sits under their hand.
 *
 * `.padcluster` is one T on its own, which is what a game uses when it puts
 * each cluster somewhere different -- above the panel showing the plane those
 * keys move in, say. `#pad` is both Ts side by side, for a game that keeps
 * them together.
 *
 * Buttons are placed by which axis and sign they carry rather than by their
 * order in the markup, so the pad's logical order stays whatever the game
 * chose and only the arrangement lives here.
 */
.padcluster {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: auto auto;
  gap: 6px;
}
/* Vertical cluster: W above, A S D beneath. W is up (axis 1 +), S is down
   (axis 1 -), A and D are the fourth dimension. */
.padcluster [data-axis="1"][data-sign="1"]  { grid-column: 2; grid-row: 1; }
.padcluster [data-axis="3"][data-sign="-1"] { grid-column: 1; grid-row: 2; }
.padcluster [data-axis="1"][data-sign="-1"] { grid-column: 2; grid-row: 2; }
.padcluster [data-axis="3"][data-sign="1"]  { grid-column: 3; grid-row: 2; }
/* Horizontal cluster: up-arrow above, left down right beneath. Axis 2 is
   north/south, axis 0 east/west. */
.padcluster [data-axis="2"][data-sign="-1"] { grid-column: 2; grid-row: 1; }
.padcluster [data-axis="0"][data-sign="-1"] { grid-column: 1; grid-row: 2; }
.padcluster [data-axis="2"][data-sign="1"]  { grid-column: 2; grid-row: 2; }
.padcluster [data-axis="0"][data-sign="1"]  { grid-column: 3; grid-row: 2; }

/* A cluster whose keys the player have all been used fades out, then gives its
   space back.
 *
 * Two stages, and the order matters: the fade runs first, and the collapse
 * waits for it. By the time the panels move, the thing above them is already
 * invisible, so what the eye sees is a panel gliding up into space that has
 * emptied -- not something vanishing out from under it. Collapsing straight
 * away would yank the board upward mid-move, as a reward for having learned
 * the controls.
 *
 * `display: none` at the end rather than a max-height animation: the cluster
 * is a grid whose tracks are sized from its contents, and animating a height
 * bound on it makes the browser re-resolve that layout every frame. Waiting
 * out the fade and then removing it outright is both simpler and steadier.
 */
.padcluster, #pad { transition: opacity .45s ease; }
.padcluster.taught, #pad.taught { opacity: 0; pointer-events: none; }
.padcluster.gone, #pad.gone { display: none; }
/* A direction the board does not have is hidden, not greyed: greying says
   "not right now", and these will never be available on this board. */
.padbtn.absent, .padcluster.absent { display: none; }

/* Both Ts in one grid, for a game that keeps them together. Seven columns:
   three per cluster with a narrow spacer between. */
#pad {
  display: grid;
  grid-template-columns: repeat(3, 1fr) 10px repeat(3, 1fr);
  grid-template-rows: auto auto;
  gap: 6px;
  justify-items: stretch;
}
#pad [data-axis="1"][data-sign="1"]  { grid-column: 2; grid-row: 1; }
#pad [data-axis="3"][data-sign="-1"] { grid-column: 1; grid-row: 2; }
#pad [data-axis="1"][data-sign="-1"] { grid-column: 2; grid-row: 2; }
#pad [data-axis="3"][data-sign="1"]  { grid-column: 3; grid-row: 2; }
#pad [data-axis="2"][data-sign="-1"] { grid-column: 6; grid-row: 1; }
#pad [data-axis="0"][data-sign="-1"] { grid-column: 5; grid-row: 2; }
#pad [data-axis="2"][data-sign="1"]  { grid-column: 6; grid-row: 2; }
#pad [data-axis="0"][data-sign="1"]  { grid-column: 7; grid-row: 2; }
.padbtn {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 7px 0 5px; border-radius: 7px; cursor: pointer;
  background: #1e2634; border: 1px solid var(--line); color: var(--ink);
  font: inherit; transition: border-color .12s, background .12s, opacity .12s;
}
.padbtn .glyph { font-size: 15px; line-height: 1; }
.padbtn .nm { font-size: 9.5px; color: var(--dim); letter-spacing: .02em; }
.padbtn:hover { border-color: var(--accent); }
.padbtn.ax0 .glyph { color: var(--ax0); }
.padbtn.ax1 .glyph { color: var(--ax1); }
.padbtn.ax2 .glyph { color: var(--ax2); }
.padbtn.ax3 .glyph { color: var(--ax3); }
.padbtn.dead { opacity: .3; cursor: default; }
.padbtn.dead:hover { border-color: var(--line); }
.padbtn.hit { animation: hit .22s ease-out; }
.padbtn.miss { animation: miss .22s ease-out; }
@keyframes hit  { from { background: #2f6b4e; } to { background: #1e2634; } }
@keyframes miss { from { background: #6b2f3d; } to { background: #1e2634; } }

.padfoot { margin-top: 8px; display: flex; flex-direction: column; gap: 3px;
           color: var(--dim); font-size: 11px; }
kbd { background: #232c3b; border: 1px solid var(--line); border-radius: 4px;
      padding: 0 4px; font: inherit; font-size: 10.5px; }

.controls { margin-top: 10px; display: flex; gap: 8px; }
select, button {
  font: inherit; font-size: 13px; color: var(--ink); background: #1e2634;
  border: 1px solid var(--line); border-radius: 6px; padding: 5px 8px;
  cursor: pointer;
}
select { flex: 1; }
button:hover, select:hover { border-color: var(--accent); }

/* A small rocking overview of the whole board, for decoding busy scenes.
   Opaque, so a halo drawn in the panel's own colour reads as a clean break
   rather than a dark smudge. */
#mapPanel { bottom: 14px; right: 14px; padding: 8px; width: 256px;
            background: #161c26; }
#minimap { display: block; width: 240px; height: 160px; }

/* ---------------------------------------------------------------------
   Narrow screens.

   One panel now, and the pad is the part a thumb has to reach, so the whole
   thing sits along the bottom. The title and status ride on one line above
   the buttons, and the map takes the free space at the top right.
   --------------------------------------------------------------------- */
@media (max-width: 620px) {
  .panel { padding: 9px 11px; border-radius: 9px; }

  #hud { top: auto; bottom: 8px; left: 8px; right: 8px; width: auto; }
  #hud h1 { font-size: 13.5px; display: inline; }
  #blurb { display: none; }
  #status { display: inline; margin-left: 12px; }
  /* The heading row doubles as the pad's label, so the label itself goes. */
  .padhead { display: none; }
  #pad { gap: 8px; margin-top: 8px; }
  .padbtn { padding: 10px 0 8px; }
  .padbtn .glyph { font-size: 17px; }
  .padfoot { display: none; }
  #hud .controls { margin-top: 8px; }

  /* Clear of the pause button in the corner. */
  #mapPanel { top: 8px; right: 52px; bottom: auto; width: 132px; padding: 6px; }
  #minimap { width: 120px; height: 80px; }
  /* The names under the glyphs are the first thing to go when space is tight:
     the arrow or letter says which key it is. */
  .padbtn .nm { display: none; }
}

/* A touch screen has no keyboard to match a letter to, so the letter keys show
   their direction instead -- "up", "kata" -- in the axis colour the letter had.
   The arrows stay: an arrow already IS its direction. Keyed on the pointer
   rather than the width, since a tablet is wide and still has no W key. */
@media (pointer: coarse) {
  .padbtn.ax1 .glyph, .padbtn.ax3 .glyph { display: none; }
  .padbtn.ax0 .nm, .padbtn.ax2 .nm { display: none; }
  .padbtn.ax1 .nm, .padbtn.ax3 .nm {
    display: block; font-size: 13px; line-height: 1.25; letter-spacing: .01em;
  }
  .padbtn.ax1 .nm { color: var(--ax1); }
  .padbtn.ax3 .nm { color: var(--ax3); }
}

/* The pause button: top right in every game, the one place no panel uses.
   Dim until hovered, so it is findable without competing with the board. */
#pauseBtn {
  position: fixed; top: 14px; right: 14px; z-index: 20;
  width: 36px; height: 36px; padding: 0 0 1px; border-radius: 9px;
  /* The glyph is two separate bars, and they have to READ as two. Negative
     tracking used to close the gap between them until, on a phone's pixel
     density, they merged into one block that looked like a stop button rather
     than a pause. The gap is the whole difference between the two symbols, so
     it gets positive tracking: enough to survive rounding to a whole pixel.
     `text-indent` puts back the centring the trailing space would otherwise
     throw off. */
  font-size: 11px; letter-spacing: 2px; text-indent: 2px; color: var(--dim);
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  border: 1px solid var(--line); backdrop-filter: blur(8px);
  user-select: none;
}
#pauseBtn:hover { color: var(--ink); border-color: var(--accent); }
@media (max-width: 620px) {
  #pauseBtn { top: 8px; right: 8px; }
  /* The button moves up, so the card's clearance follows it. */
  #over, #tutOver { padding: 50px 8px 8px; }
}

/* Any short window -- a phone in landscape included -- has no room for the map
   between the status bar and the controls, so drop it and keep the controls.
   Keyed on height alone, since a landscape phone is wide. */
@media (max-height: 460px) {
  #mapPanel { display: none; }
}

/* ---------------------------------------------------------------------
   The game-over card.

   The board behind it is the point. Seeing exactly what killed you -- the wall
   you did not see, the trail you cut across, how much room you had left -- is
   most of what makes the next run better, and that is a picture worth reading
   rather than a backdrop.

   So the screen is DARKENED and not blurred. Both push the card forward, but a
   blur destroys the very detail the player is there to look at: a lattice of
   thin lines is exactly the thing a 3px blur turns to mush. Dimming costs the
   board some contrast and keeps every edge.

   And the card sits in the top-right corner rather than the middle. Centred,
   it covers the middle of the board, which is where the action was -- these
   games all aim the camera at the middle of the world, so the most
   interesting part of the final frame is precisely what a centred card hides.
   --------------------------------------------------------------------- */
#over, #tutOver {
  position: fixed; inset: 0; display: none; z-index: 20;
  /* Darker than the old 62%, since the blur is no longer helping to separate
     the card from what is behind it. */
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  /* The card goes to the top right. `place-items` on the grid rather than a
     position on the card, so the card itself stays a plain box that any game
     can restyle. */
  place-items: start end;
  /* Clear of the pause button, which is fixed at top-right in every game and
     at the same stacking level. The card starts below it rather than over it:
     pausing is still a thing you might want to do with the card up, and a
     button you cannot see is a button that is not there. */
  padding: 62px 14px 14px;
}
#over.show, #tutOver.show { display: grid; }
#over .card, #tutOver .card {
  background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
  padding: 26px 32px; text-align: center; min-width: 240px;
  /* A stronger shadow than a centred card needs: in a corner, over a board
     that is still legible, the card has to sit clearly ON TOP of the picture
     rather than in it. */
  box-shadow: 0 18px 50px rgba(0, 0, 0, .7);
}
#over h2, #tutOver h2 { margin: 0 0 4px; font-size: 20px; letter-spacing: .02em; }
#over .cause, #tutOver .cause { margin: 0 0 16px; color: var(--dim); font-size: 13px; }
#over .score { font-size: 40px; font-weight: 700; line-height: 1.1;
               color: var(--accent); }
/* The tutorial's card says a sentence or two rather than showing a number, so
   its text needs a measure to wrap at -- the score card's `min-width` alone
   would let a paragraph run the width of the screen. */
#tutOver .card { max-width: 380px; }
#tutOver .cause { line-height: 1.5; }
#over .scorelbl { color: var(--dim); font-size: 11px; letter-spacing: .12em;
                  text-transform: uppercase; margin-bottom: 18px; }
#over button, #tutOver button { font-size: 14px; padding: 8px 20px; }

/* ---------------------------------------------------------------------
   The pause menu.

   Every game gets the same one, so it lives here rather than in any of them.
   It reuses the game-over card's SHAPE but not its placement: this one stays
   centred and keeps its blur, because there is nothing behind it worth
   reading. The game-over card moved aside to leave the final position on show
   (see #over above); a pause is an interruption in the middle of play, with no
   result to study, so covering the board costs nothing and the blur helps the
   menu read.
   --------------------------------------------------------------------- */
#pause {
  position: fixed; inset: 0; display: none; place-items: center;
  background: color-mix(in srgb, var(--bg) 62%, transparent);
  backdrop-filter: blur(3px); z-index: 30;
}
#pause.show { display: grid; }
#pause .card {
  background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
  padding: 22px 24px 20px; text-align: center; min-width: 232px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, .55);
}
#pause h2 { margin: 0 0 16px; font-size: 17px; letter-spacing: .08em;
            text-transform: uppercase; color: var(--dim); font-weight: 600; }

#pause .items { display: flex; flex-direction: column; gap: 7px; }
/* A hidden row is hidden, whatever else the sheet says.
   `display: flex` below sets a display on every item, and a display set by a
   rule beats the `hidden` attribute's own -- so the tutorial row on a game that
   has no tutorial was drawn anyway, as a blank plate. It was selectable too:
   items() filters on `hidden`, so the arrow keys stopped on a row nobody could
   see, which read as the selection skipping a step. */
#pause .items > *[hidden] { display: none; }
/* Items are a key or glyph on the left and what it does on the right, so the
   menu reads as a column of labelled actions rather than a stack of words. */
#pause .items > * {
  display: flex; align-items: center; gap: 12px; text-align: left;
  font: inherit; font-size: 13.5px; color: var(--ink); text-decoration: none;
  background: #1e2634; border: 1px solid var(--line); border-radius: 8px;
  padding: 9px 14px; cursor: pointer;
  transition: border-color .12s, background .12s;
}
/* Hover, keyboard focus and the menu's own selection all look the same, because
   they all mean the same thing: this is the row that will run if you commit.
   The class is what the arrow keys set -- see select() in pause.js, which
   explains why the browser's focus ring cannot be relied on alone. */
#pause .items > *:hover,
#pause .items > *.on,
#pause .items > *:focus-visible {
  border-color: var(--accent); background: #232c3b; outline: none;
}
#pause .k {
  min-width: 30px; text-align: center; font-size: 12px; font-weight: 700;
  color: var(--dim); letter-spacing: .02em;
}
#pause .items > *:hover .k { color: var(--ink); }
#pause .s { flex: 1; }
/* Sound off is dimmed, so the state reads without parsing the label. */
#pause [data-act="sound"].off .s { color: var(--dim); }

/* ---------------------------------------------------------------------
   The tutorial card.

   A card in the corner, not a modal. The lesson is PLAYED, so the board has
   to stay visible and reachable the whole time -- a dialog that had to be
   dismissed before trying anything would be teaching by reading, which is
   what everyone else already does badly.

   It sits top-centre, clear of the HUD on the left and the slice panels on
   the right, so it never covers the thing the lesson is about.
   --------------------------------------------------------------------- */
#tut {
  position: fixed; top: 14px; left: 50%; transform: translateX(-50%);
  display: none; z-index: 25; max-width: 430px; user-select: none;
}
#tut.show { display: block; }
#tut .card {
  background: color-mix(in srgb, var(--panel) 94%, transparent);
  border: 1px solid var(--line); border-radius: 12px;
  padding: 14px 18px 13px; backdrop-filter: blur(8px);
  box-shadow: 0 12px 36px rgba(0, 0, 0, .45);
}
#tut .step { font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
             color: var(--dim); margin-bottom: 5px; }
#tut h2 { margin: 0 0 6px; font-size: 15.5px; letter-spacing: .01em; }
#tut p { margin: 0; font-size: 13px; color: var(--dim); line-height: 1.5; }
#tut p b { color: var(--ink); font-weight: 600; }
#tut p i { color: var(--ink); font-style: italic; }
#tut .row { display: flex; gap: 8px; align-items: center; margin-top: 11px; }
#tut .row:empty { display: none; }
#tut button { font-size: 13px; padding: 6px 16px; }
/* Skip is available throughout but never the obvious thing to press.
 *
 * It used to be drawn with no background and no border, which made it a word
 * floating on the card rather than a control -- and on the card's own ground
 * there was nothing to tell you it could be clicked at all. It now takes the
 * pause menu's treatment, which is the shape a secondary action has everywhere
 * else in these games: a panel-dark plate with a quiet border, lifting to the
 * accent on hover. It stays subordinate to the primary button beside it by
 * being DIMMER, not by being invisible. */
#tut button.quiet {
  background: #1e2634; border: 1px solid var(--line); color: var(--dim);
  padding: 6px 14px; border-radius: 8px;
  transition: border-color .12s, background .12s, color .12s;
}
#tut button.quiet:hover,
#tut button.quiet:focus-visible {
  color: var(--ink); border-color: var(--accent); background: #232c3b;
  outline: none;
}

@media (max-width: 620px) {
  #tut { top: 8px; left: 8px; right: 8px; transform: none; max-width: none; }
  #tut .card { padding: 11px 13px 10px; }
  #tut h2 { font-size: 14px; }
  #tut p { font-size: 12px; }
}

/* Confetti. A canvas over everything, cards included, that takes no clicks and
   is only in the document while something is falling. */
#confetti {
  position: fixed; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 28;
}

/* ---------------------------------------------------------------------------
   The double slice panel.

   Two cross-sections side by side, each with the key cluster that moves in its
   plane directly above it. That pairing is the whole layout: WASD sits over the
   w-y panel because those are the keys that move in the w-y plane, and the
   arrows sit over the x-z panel for the same reason. The cluster IS the legend,
   so the panels need no axis labels round their edges -- the key that moves you
   that way is already there, in the right place and the right colour.

   This lived twice, once in Snake's page and once in Tron's, and the two copies
   had already drifted: Snake hung it on `#mapPanel`, Tron on `.maps`, and their
   phone rules were separately maintained versions of the same idea. Any game
   with four axes wants this same furniture, so it belongs here and the pages
   ask for it by class.
   --------------------------------------------------------------------------- */

/* The row of columns. A game puts this on whatever element holds the pair --
   its own panel, or a player's half of the screen. */
.mapcols { display: flex; gap: 14px; }

/* One column: the keys for a plane, above the panel drawing that plane. */
.mapcol { display: flex; flex-direction: column; }
.mapcol .padcluster { margin-bottom: 8px; }
/* Must out-specify the rule above, or the gap survives the pad's collapse and
   the panels stop short of the space it gave back. */
.mapcol .padcluster.taught { margin-bottom: 0; }

/* A column for an axis the board does not have is not shown at all. Its PANEL
   can go while the keys above it stay, which is what a 3D board wants: W and S
   exist there, the w-y plane does not. */
.mapcol.absent, .mapcols svg.absent, .mapfoot.absent { display: none; }

.mapcols svg { display: block; width: 178px; height: 178px; }

/* What each panel is holding still, under the panel it applies to. */
.mapfoot { font-size: 10px; color: var(--dim); margin-top: 4px;
           text-align: center; }
.mapfoot b { color: var(--ink); font-weight: 600; }

@media (max-width: 720px), (pointer: coarse) {
  /* On a touch screen the BUTTONS set the width, not the maps: 44px is the size
     a thumb hits without aiming, so each cluster gets three of those and the map
     above takes the same width. */
  .mapcols { gap: 6px; justify-content: center; }
  .mapcol { width: 138px; flex: 0 0 auto; }
  .mapcols svg { width: 138px; height: 138px; }
  /* And within each column the keys go BELOW the map they belong to: at the
     bottom of the screen the lowest thing is the nearest thing. */
  .mapcol { flex-direction: column-reverse; }
  .mapcol .padcluster { margin-bottom: 0; margin-top: 6px; }

  /* Tighter all round than on a desktop, and only here.
   *
   * A phone is spending its screen on two maps and eight buttons that each have
   * to stay thumb-sized, so every millimetre of padding around them comes
   * straight out of the board above. The gaps are trimmed rather than the
   * targets: the buttons keep their size, and what goes is the air between and
   * around them. The panel's own padding shrinks furthest, since a panel edge
   * this close to the screen edge is already reading as a boundary without help
   * from a wide margin.
   *
   * Keyed to the panel that HOLDS a map pair, so a game's other panels keep the
   * comfortable spacing -- this is a fix for a crowded control surface, not a
   * house style for phones. */
  .panel.mapcols, .panel:has(.mapcols) { padding: 6px; }
  .mapcols .padcluster { gap: 4px; }
  .mapfoot { margin-top: 2px; }
}
