/* ==========================================================================
   TETROS98 - layout + game-specific styles, layered on 98.css.
   (styles/98.css is the theme - do not edit it; put everything here.)

   SECTIONS: base / tweaks / screens / game / overlays / touch controls.

   CUSTOM CLASSES ONLY: .menu, .screen, .game-grid, .side-stack,
   .stat-row, .mini-well, .board-well, .dialog, .tab-panel,
   .slider-row, .touch-controls, .last-clear.
   ========================================================================== */

/* ===== base ===== */
:root {
  --bg: #c0c0c0;           /* silver, same as window face - no desktop */
  --face: #c0c0c0;         /* silver window face */
  --face-hi: #ffffff;      /* raised highlight */
  --face-lo: #808080;      /* raised shadow */
  --edge: #0a0a0a;         /* black outline */
  --text: #000000;         /* black text on silver */
  --text-dim: #404040;     /* dimmed text */
  --navy-a: #000080;       /* title bar navy */
  --navy-b: #1084d0;       /* title bar gradient end */
  --accent: #1084d0;
  --board-bg: #c0c0c0;     /* silver, matches the window face */
  --field: #ffffff;        /* white input fields */
}

/* ===== base ===== */
html, body {
  height: 100%;
  height: 100dvh; /* track the visible viewport under browser chrome */
  margin: 0; /* kill default 8px body margin - it showed grey around the window */
  overflow: hidden;
  overscroll-behavior: none;
  -webkit-tap-highlight-color: transparent;
  background: var(--bg);
  color: var(--text);
  -webkit-user-select: none;
  user-select: none;
}

#desktop {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: var(--bg);
}

/* 98.css provides all the theming - no custom re-skin needed. */

/* window chrome: title bar flush to the top, body flexes, status bar pinned */
.window {
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  padding: env(safe-area-inset-top) 0 0; /* clear the status bar when edge-to-edge */
}
.title-bar { flex-shrink: 0; }
.version-badge {
  font-size: 9px;
  font-weight: normal;
  color: #c0c0c0;
  margin-left: 6px;
  vertical-align: middle;
}
.window-body {
  margin: 0;
  padding: 8px;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  display: flex;
  position: relative; /* overlays anchor here: inset 0 = full body, no gap */
  flex-direction: column;
}
.status-bar { flex-shrink: 0; }

/* ===== screens ===== */
.screen {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  position: relative;
  /* fill the window body so the board can actually use desktop space -
     without this the board size loop converges on content size, not window */
  flex: 1 1 auto;
  min-height: 0;
}

.game-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 6px 0 2px;
}
.start-title { font-size: 26px; font-weight: bold; letter-spacing: 5px; color: var(--navy-a); text-shadow: 1px 1px 0 #fff; }
.start-sub { font-size: 10px; color: var(--text-dim); letter-spacing: 2px; }

@media (max-height: 700px) {
  .start-title { margin-top: 20px; font-size: 34px; }
  .start-sub { margin-bottom: 14px; }
  .start-foot { margin-top: 20px; }
  .menu { gap: 6px; }
}

.menu { display: flex; flex-direction: column; gap: 8px; width: 190px; }
.menu-btn { width: 100%; min-height: 28px; }
.menu-btn:focus {
  outline: 1px dotted #000;
}

/* ===== game: single-window groupbox layout ===== */
.game-grid {
  display: flex;
  gap: 12px;
  justify-content: center;
  align-items: stretch; /* stack + well share the grid height - the stack boxes
                           match the board exactly, equal gaps, no ragged edges */
  flex-wrap: nowrap;
  flex: 1 1 auto;
  min-height: 0;
  width: 100%; /* fill the window width - without this the grid was
                  content-width (stack+board) and the board was width-starved */
}

.side-stack {
  display: flex;
  flex-direction: column;
  gap: 12px; /* equal to the grid gap - boxes and board breathe together */
  width: 200px;
  flex-shrink: 0;
  min-height: 0; /* allow shrinking on short screens instead of overflowing */
}

/* groupboxes: authentic 98.css <fieldset> - the library draws the recessed
   border-image + silver legend tab, exactly like a real Win98 groupbox. */
fieldset.stats-groupbox, fieldset.mini-groupbox {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
fieldset.stats-groupbox { flex: 1 1 0; min-width: 0; min-height: 0; }
fieldset.mini-groupbox {
  flex: 1 1 0; /* equal share - stats/hold/next are the same height */
  min-height: 0;
  width: 100%; /* match the stack width - wells used to overhang 8px */
  box-sizing: border-box;
}
legend { font-size: 11px; padding: 0 4px; }

/* stats window - rows spread to fill the box so it matches its siblings */
.stats-body { display: flex; flex-direction: column; gap: 6px; flex: 1 1 auto; justify-content: space-evenly; }
.stat-row {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
}
.stat-label {
  color: var(--text-dim);
  letter-spacing: 1px;
  width: 52px; /* fixed so every value box lines up in one column */
  flex-shrink: 0;
  text-align: left;
}
.stat-value {
  font-size: 14px; /* right-aligned bold scores sit flush - that's the look */
  font-weight: bold;
  color: #000;
  background: #fff;
  padding: 2px 8px;
  flex: 1 1 auto;
  min-width: 0;
  text-align: right;
  line-height: 1.1; /* keep the number vertically centered in the white box */
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  font-variant-numeric: tabular-nums; /* digits line up - no jitter as scores grow */
  box-shadow:
    inset 1px 1px var(--edge), inset -1px -1px var(--face-hi),
    inset 2px 2px var(--edge), inset -2px -2px var(--face);
}
/* 8+ digits (high scores): shrink the number so it stays inside the box
   instead of painting over the bevel */
.stat-value.stat-long { font-size: 11px; padding: 2px 6px; }

/* hold / next groupboxes */
.mini-groupbox { flex-shrink: 0; }

/* gray veil over the board while waiting to start */
.board-well { position: relative; }
.board-overlay {
  position: absolute;
  inset: 0;
  background: rgba(128, 128, 128, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 5;
}
.board-overlay[hidden] { display: none; }
/* pause + game over share the SAME dark backdrop as the modal dialogs
   (the grey board veil stays for the start screen only) */
#overlay-pause, #overlay-gameover { background: rgba(0, 0, 0, 0.6); }
/* menu overlays: the dialog matches the board height, buttons distribute
   evenly across it - same "fills the board" feel as the side stack */
#overlay-pause, #overlay-gameover { align-items: stretch; }
.board-dialog {
  min-width: 0;
  max-width: 90%;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.board-dialog .menu { flex: 1 1 auto; justify-content: space-evenly; }
.board-dialog .dialog-title { text-align: center; }
.board-dialog .menu { gap: 6px; }
.start-btn { width: 50%; max-width: 160px; min-height: 32px; font-size: 13px; font-weight: bold; }
.mini-well {
  background: var(--board-bg);
  padding: 4px;
  display: flex;
  justify-content: center;
  width: 100%;
  box-sizing: border-box;
  overflow: hidden; /* the fit math can land 1px over - never show scrollbars */
}

/* board well: the sunken screen, recessed into the window face */
.board-well {
  background: var(--board-bg);
  padding: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 1 auto; /* wrap the canvas - no stretching, border hugs the grid */
  height: 100%; /* match the side stack exactly - both share the grid height */
  overflow: hidden; /* sunken-panel default is auto - never scrollbars */
  min-width: 0;
  min-height: 0;
  align-self: flex-start; /* top edge aligns with the stats box top */
  margin-top: 0;
  box-shadow:
    inset 1px 1px var(--edge), inset -1px -1px var(--face-hi),
    inset 2px 2px var(--edge), inset -2px -2px var(--face);
}

#canvas-board {
  background: var(--board-bg);
  display: block;
}

canvas { image-rendering: pixelated; display: block; }
.mini-canvas {
  background: var(--board-bg);
  display: block;
}

/* overlays / dialogs */
.overlay {
  position: absolute;
  inset: 0; /* dim edge to edge - no gap around the modal */
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
}

.dialog {
  background: var(--face);
  box-shadow:
    inset -1px -1px var(--edge), inset 1px 1px var(--face-hi),
    inset -2px -2px var(--face-lo), inset 2px 2px var(--face);
  min-width: 240px;
  max-width: 92%;
  max-height: 92%;
  overflow: hidden; /* content scrolls inside .dialog-body, frame never scrolls */
  padding: 3px;
  display: flex;
  flex-direction: column;
}
/* content scrolls INSIDE the body panel so the bottom buttons stay visible;
   scrollbars hidden on every scroll container (WebView overlay mode shows
   a thin modern bar otherwise) */
.dialog, .dialog-body, .settings-page { scrollbar-width: none; -ms-overflow-style: none; }
.dialog::-webkit-scrollbar, .dialog-body::-webkit-scrollbar, .settings-page::-webkit-scrollbar { display: none; width: 0; height: 0; }
.dialog.wide {
  width: min(460px, calc(100vw - 32px)); /* never wider than the screen */
}
.dialog-title {
  background: linear-gradient(90deg, var(--navy-a), var(--navy-b));
  color: #fff;
  font-weight: bold;
  font-size: 12px;
  padding: 4px 8px;
}
.dialog-body {
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: 12px;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto; /* the inset scroll area - buttons below stay visible */
}
.dialog-buttons { display: flex; justify-content: flex-end; gap: 6px; padding: 8px 10px; flex-shrink: 0; }

.field-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.field-row select, .field-row input[type="number"] { width: 120px; }
/* custom dropdowns keep a CONSTANT width - wide enough for the longest
   option ("Guideline (modern)") so the field never resizes when the
   selected value changes */
.field-row .t98-select-wrap { width: 170px; max-width: 100%; }
.settings-hint { font-size: 11px; color: var(--text-dim); margin: 2px 0 4px; line-height: 1.45; max-width: 52ch; }
.radio-row { display: flex; flex-wrap: wrap; gap: 4px 14px; }
.radio-row label { margin-left: 2px; }
.check-row { display: flex; flex-wrap: wrap; gap: 4px 16px; }
.check-row label { margin-left: 2px; }
/* sub-options that hang off a parent checkbox (kick tables under wall kicks) */
.radio-row.sub-group { padding-left: 26px; }
.slider-row { display: flex; align-items: center; gap: 8px; min-height: 24px; }
.slider-row label { width: 120px; flex-shrink: 0; }
.slider-row input[type="range"] { flex: 1; min-width: 80px; height: 21px; margin: 0; }
.slider-val { width: 44px; text-align: right; font-size: 11px; flex-shrink: 0; }

/* settings page: single scrollable list, Tuning is a collapsible section */
.settings-page {
  border: 1px solid;
  border-color: var(--edge) var(--face-hi) var(--face-hi) var(--edge);
  background: var(--face);
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* the beveled frame is the element's own border, so it STAYS while the
     content scrolls inside it - no more bevel scrolling away */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
/* two-column group grid for the small toggles; collapses on narrow screens */
.settings-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  align-items: start;
}
/* group boxes inside settings: even padding, column stack, breathing room */
.settings-page fieldset {
  margin: 0;
  padding: 6px 9px 9px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}
.settings-page fieldset legend { margin-bottom: 3px; }
/* settings scrolls inside its own inset frame, not the dialog body */
#overlay-settings .dialog-body { overflow: visible; }
.tuning-toggle {
  width: 100%;
  text-align: left;
  font-size: 11px;
  font-weight: bold;
  padding: 4px 8px;
}

/* row-clear feedback: soft flash + tiny scoreboard note */
.page-buttons {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 4px;
}
.page-buttons .menu-btn { width: 100%; min-height: 24px; font-size: 11px; }

.last-clear {
  text-align: center;
  font-size: 10px;
  letter-spacing: 1px;
  color: var(--text-dim);
  min-height: 12px;
  opacity: 0;
}
.last-clear.show { animation: last-fade 1.4s ease-out forwards; }
@keyframes last-fade {
  0% { opacity: 1; }
  70% { opacity: 1; }
  100% { opacity: 0; }
}

.level-progress { height: 12px; margin-top: 2px; }

.cfg-tool {
  display: flex;
  flex-direction: column;
  gap: 6px;
  border-top: 1px solid var(--face-hi);
  padding-top: 8px;
  margin-top: 4px;
}
.cfg-tool .field-row { justify-content: flex-start; }
.cfg-tool #cfg-name { flex: 1; min-width: 0; }
/* saved setups: one row per profile - name + Load + Delete, no dropdown */
.cfg-row { display: flex; align-items: center; gap: 6px; padding: 2px 0; }
.cfg-row-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cfg-row .cfg-row-btn { min-width: 68px; font-size: 11px; }

/* ===== 98-style custom dropdowns (no native picker) ===== */
.t98-select-wrap { position: relative; display: inline-block; }
.t98-select-wrap select {
  /* real select kept in the DOM for value/options/JS, but invisible so the
     Android WebView never pops its OS picker dialog */
  position: absolute; opacity: 0; pointer-events: none;
  width: 0; height: 0; border: 0; margin: 0; padding: 0; inset: 0;
}
.t98-select {
  display: flex; align-items: center; justify-content: space-between;
  background: #fff; color: #222; text-shadow: none;
  box-shadow: inset -1px -1px #fff, inset 1px 1px grey,
              inset -2px -2px #dfdfdf, inset 2px 2px #0a0a0a;
  box-sizing: border-box; min-height: 21px; min-width: 100px;
  padding: 2px 3px; text-align: left; width: 100%;
}
.t98-select:active { padding: 2px 3px; }
.t98-select .t98-value {
  flex: 1; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; padding: 0 2px;
}
.t98-select .t98-arrow {
  flex: none; width: 16px; height: 15px; margin-left: 6px;
  background: silver; position: relative;
  box-shadow: inset -1px -1px #0a0a0a, inset 1px 1px #fff,
              inset -2px -2px grey, inset 2px 2px #dfdfdf;
}
.t98-select .t98-arrow::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -60%);
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid #000;
}
.t98-select-list {
  position: absolute; top: calc(100% + 2px); left: 0; z-index: 40;
  background: #fff; list-style: none; margin: 0; padding: 2px;
  min-width: 100%; max-height: 200px; overflow-y: auto;
  box-shadow: inset -1px -1px #0a0a0a, inset 1px 1px #dfdfdf,
              inset -2px -2px grey, inset 2px 2px #fff;
}
.t98-select-list[hidden] { display: none; }
.t98-select-list li { padding: 3px 8px; white-space: nowrap; }
.t98-select-list li:hover, .t98-select-list li.selected {
  background: navy; color: #fff;
}

#gameover-info { font-size: 13px; }

/* controls list */
.controls-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 3px 0;
  min-width: 0; /* let the row shrink inside the dialog - no clipping */
}
.controls-label {
  flex: 0 0 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.key-cap {
  background: var(--face);
  box-shadow: inset -1px -1px var(--edge), inset 1px 1px var(--face-hi),
    inset -2px -2px var(--face-lo), inset 2px 2px var(--face);
  padding: 2px 8px;
  font-size: 11px;
  min-width: 60px;
  text-align: center;
}
.key-cap.listening { background: var(--navy-a); color: #fff; text-shadow: none; }

/* multi-key editor: chips + add button + conflict warning */
.keys-wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  align-items: center;
  justify-content: flex-end;
  max-width: 240px;
  flex: 0 1 auto;
  min-width: 0;
}
.key-chip {
  background: #fff;
  color: #000;
  border: 1px solid var(--edge);
  padding: 2px 8px;
  font-size: 11px;
  min-width: 28px;
  text-align: center;
  box-shadow: inset 1px 1px var(--face-hi), inset -1px -1px var(--face-lo);
}
.key-chip:hover { background: var(--navy-a); color: #fff; }
.key-restore {
  margin-top: 8px;
  font-size: 11px;
}
.key-add {
  background: var(--face);
  box-shadow: inset -1px -1px var(--edge), inset 1px 1px var(--face-hi),
    inset -2px -2px var(--face-lo), inset 2px 2px var(--face);
  padding: 2px 10px;
  font-size: 11px;
  min-width: 28px;
}
.key-add.listening { background: var(--navy-a); color: #fff; text-shadow: none; }
.conflict-flash {
  color: #c00;
  font-weight: bold;
  transition: color 0.2s;
}

/* touch button rebinding rows (Controls dialog) */
.touch-bind-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 3px 0;
}
.touch-btn-name { font-weight: bold; width: 24px; flex-shrink: 0; }
.touch-bind-wrap { width: 170px; max-width: 100%; }

[hidden] { display: none !important; }

/* ===== touch controls (mobile) ===== */
.touch-controls { display: none; }

@media (max-width: 768px), (pointer: coarse) {
  html, body { touch-action: manipulation; }
  * { -webkit-tap-highlight-color: transparent; }
  #desktop { padding: 0; }

  .window {
    width: 100%;
    box-shadow: none;
  }
  .window-body {
    margin: 0;
    padding: 4px;
    min-height: 0;
    flex: 1;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    position: relative; /* keep overlays anchored full-bleed on mobile too */
  }

  #screen-game {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
    width: 100%;
    align-items: stretch;
  }

  .game-grid {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: row;
    gap: 6px;
    overflow: hidden;
    width: 100%;
    align-items: stretch; /* keep the tuned mobile layout as-is */
    justify-content: space-evenly; /* edge margins match the stack/board
                                      gap at any width - no lopsided gaps */
  }

  /* side column: stats + hold + next stacked vertically beside the board */
  .side-stack {
    width: 116px;
    flex: 0 0 116px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    overflow-y: auto;
    justify-content: flex-start;
  }
  fieldset.stats-groupbox { flex: 0 0 auto; }
  fieldset.stats-groupbox, fieldset.mini-groupbox { padding: 5px 4px 4px; gap: 3px; }
  legend { font-size: 9px; }
  .stats-body {
    display: flex;
    flex-direction: column;
    gap: 3px;
  }
  .stat-row { font-size: 10px; }
  .stat-label {
    width: 38px; /* narrow on phones so the value box keeps room for big scores */
  }
  .stat-value {
    font-size: 12px;
    padding: 1px 5px;
    min-width: 0;
  }
  .stat-value.stat-long { font-size: 10px; padding: 1px 3px; }
  .level-progress { height: 8px; margin-top: 1px; }

  .mini-well { padding: 2px; }
  .mini-canvas { width: 36px; height: 36px; }

  .last-clear { display: none; }

  /* board fills the remaining width beside the side column */
  .board-well {
    flex: 0 1 auto; /* NO grow: the frame hugs the canvas horizontally -
                       flex-grow:1 stretched it to the grid width and the
                       silver showed inside the frame on both sides */
    min-height: 0;
    min-width: 0;
    align-self: flex-start;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 3px;
  }
  #canvas-board {
    height: 100%;
    width: auto;
    aspect-ratio: 1 / 2;
    max-height: 100%;
    max-width: 100%;
    touch-action: none;
  }

  .start-title { font-size: 24px; letter-spacing: 3px; margin-top: 12px; }
  .start-sub { margin-bottom: 14px; }
  .menu { width: 180px; }
  .start-foot { margin-top: 16px; }

  .dialog.wide { width: 100%; }
  .status-bar { padding: 2px 4px; padding-bottom: max(2px, env(safe-area-inset-bottom)); flex-wrap: nowrap; }
  #status-hint { display: none; }

  /* pause on touch: a NORMAL full-screen modal - dims the whole game
     (touch controls included) instead of a little board-scoped box */
  #overlay-pause {
    position: fixed;
    inset: 0;
    z-index: 50;
  }
  #overlay-pause .board-dialog { width: min(260px, 80vw); }
  #overlay-pause .menu { width: 100%; }
  #overlay-pause .menu-btn { min-height: 42px; font-size: 13px; }

  /* game over gets the same full-screen treatment on touch */
  #overlay-gameover {
    position: fixed;
    inset: 0;
    z-index: 50;
  }
  #overlay-gameover .board-dialog { width: min(280px, 80vw); }
  #overlay-gameover .menu { width: 100%; }
  #overlay-gameover .menu-btn { min-height: 42px; font-size: 13px; }
  #overlay-gameover #gameover-info { text-align: center; font-size: 14px; }

  /* the window min/max/close buttons are useless chrome on touch - drop
     them (the title text stays) */
  .title-bar-controls { display: none; }

  /* more slider room on phones - narrower labels */
  .slider-row label { width: 100px; font-size: 11px; }

  /* settings groups stack instead of grid on small screens */
  .settings-grid { grid-template-columns: 1fr; }

  /* compact header while waiting to start */
  .game-header { padding: 2px 0 0; gap: 0; }
  .start-title { font-size: 20px; letter-spacing: 3px; }
  .start-sub { font-size: 9px; }
  .page-buttons { gap: 3px; margin-top: 2px; }
  .page-buttons .menu-btn { min-height: 22px; font-size: 10px; }

  /* narrow phones: stack label above its keys so nothing clips */
  .controls-row {
    flex-wrap: wrap;
    gap: 2px 8px;
  }
  .controls-label {
    width: 100%; /* label on its own line */
    flex-basis: 100%;
  }
  .keys-wrap {
    max-width: 100%;
    justify-content: flex-start;
  }

  .touch-controls {
    /* spread to the screen edges: stick under the left thumb, face
       diamond under the right thumb - not clustered mid-screen */
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    margin-top: 2px;
    flex-wrap: nowrap;
    padding: 8px 24px 6px;
    box-sizing: border-box;
    /* Win98 sunken divider above the controls - separates the game window
       from the touch row like a status-bar groove */
    border-top: 2px solid #808080;
    box-shadow: inset 0 1px 0 #dfdfdf;
  }
  .touch-controls button {
    touch-action: none;
    -webkit-user-select: none;
    user-select: none;
    font-family: inherit;
    cursor: pointer;
  }
  /* ===== Win98 button recipe (exact 98.css) =====
     background: silver; border: none; border-radius: 0;
     box-shadow: inset -1px -1px #0a0a0a, inset 1px 1px #fff,
                 inset -2px -2px grey, inset 2px 2px #dfdfdf;
     active: flip to sunken. Applied to every touch control. */
  .touch-controls .face-btn {
    background: #c0c0c0;
    border: none;
    border-radius: 0;
    box-shadow: inset -1px -1px #0a0a0a, inset 1px 1px #fff,
                inset -2px -2px #808080, inset 2px 2px #dfdfdf,
                /* ONE global light (top-left, like the stick): the raised
                   button casts a tight 1px shadow bottom-right */
                1px 1px 0 rgba(0, 0, 0, 0.15);
    color: #000;
    box-sizing: border-box;
    /* reset 98.css's global button min sizes + padding - they force every
       touch button to 75px wide, overflowing its grid cell and clipping
       the pad arms / diamond buttons */
    min-width: 0;
    min-height: 0;
    padding: 0;
  }
  .touch-controls .face-btn.touch-pressed {
    /* oldschool press: the key drops STRAIGHT DOWN (no diagonal slide),
       darkens, and KEEPS its cast shadow so the silhouette never
       shrinks - the pressed button reads the same size as raised */
    transform: translate(0, 2px);
    box-shadow: inset -1px -1px #fff, inset 1px 1px #0a0a0a,
                inset -2px -2px #dfdfdf, inset 2px 2px #808080,
                1px 1px 0 rgba(0, 0, 0, 0.15);
    background: #b0b0b0;
  }
  .touch-controls .face-btn.touch-kathunk {
    /* kathunk: the face is a SLIDESHOW - 5 distinct grey frames stepping
       from pressed to rest (b0 -> b4 -> b8 -> bc -> c0), each slide
       visible; the bevel and return motion step fine (3) underneath. */
    animation: face-kathunk-bg 0.09s, face-kathunk-bevel 0.09s steps(3, end),
               face-kathunk-move 0.09s steps(3, end);
  }
  @keyframes face-kathunk-bg {
    0%   { background: #b0b0b0; animation-timing-function: steps(1, end); }
    25%  { background: #b4b4b4; animation-timing-function: steps(1, end); }
    50%  { background: #b8b8b8; animation-timing-function: steps(1, end); }
    75%  { background: #bcbcbc; animation-timing-function: steps(1, end); }
    100% { background: #c0c0c0; }
  }
  @keyframes face-kathunk-bevel {
    0%   { box-shadow: inset -1px -1px #fff, inset 1px 1px #0a0a0a,
                       inset -2px -2px #dfdfdf, inset 2px 2px #808080,
                       1px 1px 0 rgba(0, 0, 0, 0.15); }
    100% { box-shadow: inset -1px -1px #0a0a0a, inset 1px 1px #fff,
                       inset -2px -2px #808080, inset 2px 2px #dfdfdf,
                       1px 1px 0 rgba(0, 0, 0, 0.15); }
  }
  @keyframes face-kathunk-move {
    0%   { transform: translate(0, 2px); }
    100% { transform: translate(0, 0); }
  }
  .touch-controls .face-btn.touch-kathunk::before {
    /* the colored chip does its own stepped slideshow - a subtle
       brightness climb (1 -> 1.12 -> 1) in 5 slides, same 0.09s */
    animation: face-kathunk-chip 0.09s;
  }
  @keyframes face-kathunk-chip {
    0%   { filter: brightness(1); animation-timing-function: steps(1, end); }
    25%  { filter: brightness(1.1); animation-timing-function: steps(1, end); }
    50%  { filter: brightness(1.2); animation-timing-function: steps(1, end); }
    75%  { filter: brightness(1.1); animation-timing-function: steps(1, end); }
    100% { filter: brightness(1); }
  }
  /* the letter stays solid black + engraved shadow - grey color steps
     on the colored chip read as a flash, so it sits out of the
     slideshow entirely */
  .touch-controls .face-btn:not(.touch-pressed) {
    /* ASYMMETRIC: the press is instant (no transition while pressed -
       a quick tap always reads as a full depression). The return motion
       and bevel belong to the kathunk animation (stepped), so the only
       thing transitioning here is the background fallback. */
    transition: background 0.09s ease-out;
  }
  /* 98.css's :hover/:active paint the pressed bevel and mobile browsers
     stick :hover on the last-tapped element -> looked latched after lift.
     Neutralize both, but NEVER while .touch-pressed is active: mobile sets
     :active for the whole touch, and that rule (same specificity, later
     in source) used to override the pressed bevel - buttons looked raised
     while pressed. The :not() keeps .touch-pressed authoritative. */
  .touch-controls .face-btn:not(.touch-pressed):hover,
  .touch-controls .face-btn:not(.touch-pressed):active {
    box-shadow: inset -1px -1px #0a0a0a, inset 1px 1px #fff,
                inset -2px -2px #808080, inset 2px 2px #dfdfdf;
    background: #c0c0c0;
  }
  /* Analog stick: circular recessed well + knob. The pad is the touch
     surface; the knob follows the thumb, clamps at the well rim, and
     springs back to center on release (Win98 bevels). */
  .pad {
    position: relative;
    /* 160px touch area: big enough that the knob (64px) + 4px shadow at
       full 40px travel (edge at 72+4=76px) never clips the hit surface */
    width: 160px;
    height: 160px;
    flex: 0 0 auto;
    touch-action: none;
    -webkit-user-select: none;
    user-select: none;
  }
  .stick-well {
    /* 3DS-style socket: a visible recessed square the pad sits in. Pad is
       64px, well 100px - an ~18px rim shows around the pad at rest so it
       reads as a stick base, and at full push (40px travel) the pad leans
       clearly out of it. */
    position: absolute;
    left: 50%;
    top: 50%;
    width: 100px;
    height: 100px;
    margin-left: -50px;
    margin-top: -50px;
    background: #a8a8a8;
    /* authentic 98.css recessed bevel, right on the well: 1px #808080 +
       1px #0a0a0a top/left, 1px #fff + 1px #dfdfdf bottom/right. The
       ring-clipped shadows paint OVER it (real socket: the far wall is
       in shadow); the floor shadow never reaches it (interior clip). */
    box-shadow: inset -1px -1px #fff, inset 1px 1px #808080,
                inset -2px -2px #dfdfdf, inset 2px 2px #0a0a0a;
    /* clips the in-well shadows to the socket */
    overflow: hidden;
    transition: background 0.2s ease, box-shadow 0.2s ease;
    z-index: 1; /* above the surface shadow, whose in-well part this opaque face hides */
  }
  .pad.pad-active .stick-well {
    /* pushed: the stick sinks into its base - barely darker, bevel
       deepens (top/left darkens, light edges dim). Subtle enough to
       feel, not announce. */
    background: #a0a0a0;
    box-shadow: inset -1px -1px #e8e8e8, inset 1px 1px #777777,
                inset -2px -2px #d0d0d0, inset 2px 2px #0a0a0a;
  }
  .stick-knob {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 64px;
    height: 64px;
    margin-left: -32px;
    margin-top: -32px;
    background: #c0c0c0;
    box-shadow: inset -1px -1px #0a0a0a, inset 1px 1px #fff,
                inset -2px -2px #808080, inset 2px 2px #dfdfdf;
    /* NO drop shadow here: the surface shadow is a separate element UNDER
       the well so it never paints over the socket (see .stick-shadow-outer) */
    transition: box-shadow 0.15s ease;
    will-change: transform;
    /* pad rides ABOVE the socket so it overhangs it at rest */
    z-index: 2;
    /* press color eases in with the socket - no jump */
    transition: background 0.2s ease, box-shadow 0.2s ease;
  }
  .stick-clip-bevel, .stick-clip-inner {
    /* static clip zones inside the socket, sized to it: the bevel zone is
       the full well (the rim band), the inner zone is inset 3px (the
       floor). The moving shadows inside get masked by these. */
    position: absolute;
    inset: 0;
    pointer-events: none;
  }
  .stick-clip-bevel {
    /* the bevel zone: clipped to the full well (overflow), the interior
       part is COVERED by the inner wrapper's opaque background, so the
       +3 bevel shadow only ever shows in the 3px rim band */
    z-index: 0;
  }
  .stick-clip-inner {
    /* static clip zone for the socket floor: inset 3px + an OPAQUE face
       (same color as the socket) - it masks the bevel shadow out of the
       interior without needing an SVG mask (WebView-safe), and the
       floor shadow paints on top of it */
    z-index: 1;
    clip-path: inset(3px);
    background: #a8a8a8;
  }
  .pad.pad-active .stick-clip-inner { background: #a0a0a0; }
  .stick-shadow {
    /* parallel-light model: every shadow is exactly the cap size (64px);
       depth = offset only. The floor is deepest, so its shadow shifts
       furthest (+10,+8) and the visible crescent reads as a deep hole. */
    position: absolute;
    left: 50%;
    top: 50%;
    width: 64px;
    height: 64px;
    margin-left: -32px;
    margin-top: -32px;
    background: rgba(0, 0, 0, 0.30);
    will-change: transform;
  }
  .stick-shadow-mid {
    /* rim: mid depth, mid offset (+6,+4) */
    position: absolute;
    left: 50%;
    top: 50%;
    width: 64px;
    height: 64px;
    margin-left: -32px;
    margin-top: -32px;
    background: rgba(0, 0, 0, 0.30);
    will-change: transform;
  }
  .stick-shadow-outer {
    /* outer surface: closest to the light, tightest offset (+3,+2) and a
       lighter shade (0.22) so it reads as a different surface. Lives
       UNDER the well - the socket's opaque face hides the in-well part,
       so it only shows outside the rim. */
    position: absolute;
    left: 50%;
    top: 50%;
    width: 64px;
    height: 64px;
    margin-left: -32px;
    margin-top: -32px;
    background: rgba(0, 0, 0, 0.22);
    pointer-events: none;
    will-change: transform;
    z-index: 0;
  }
  .stick-shadow-mid2 {
    /* the bevel's SECOND layer: its own crescent in the band, offset a
       beat shallower (+5,+3) and lighter (0.20) than the mid - the two
       band crescents stagger, reading the double bevel as two layers.
       Clipped to the band by the same clip zone - nothing escapes. */
    position: absolute;
    left: 50%;
    top: 50%;
    width: 64px;
    height: 64px;
    margin-left: -32px;
    margin-top: -32px;
    background: rgba(0, 0, 0, 0.20);
    will-change: transform;
  }
  .pad.pad-active .stick-knob {
    /* pressed = a real analog stick, not a keyboard key: no dark grey.
       The fill LIGHTENS a touch and the bevel ring tightens (top/left
       light edges shrink) - reads as finger pressure, stays silver.
       Depth comes from the two shadow layers, not the knob. */
    background: #c8c8c8;
    box-shadow: inset -1px -1px #0a0a0a, inset 1px 1px #fff,
                inset -1px -1px #808080, inset 1px 1px #dfdfdf;
  }

  /* SNES face-button diamond: X top, Y left, A right, B bottom.
     Square Win98 buttons in the SNES arrangement - colored chips via
     small color swatch INSIDE the silver button, not the whole button. */
  .face-diamond {
    /* scaled up to match the big square stick (well 118px) - the cluster
       should read as one unit, not a giant stick next to tiny buttons */
    display: grid;
    grid-template-columns: repeat(3, 46px);
    grid-template-rows: repeat(3, 46px);
    gap: 3px;
    flex: 0 0 auto;
  }
  .face-x { grid-area: 1 / 2; }
  .face-y { grid-area: 2 / 1; }
  .face-a { grid-area: 2 / 3; }
  .face-b { grid-area: 3 / 2; }
  .face-btn {
    position: relative;
    font-size: 15px;
    font-weight: bold;
    color: #000;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
  }
  .face-btn::before {
    content: '';
    position: absolute;
    top: 4px; left: 4px; right: 4px; bottom: 4px;
    /* SNES color chip SET INTO the silver button - sunken 1px bevel,
       kept faint so the chip blends rather than stickers out */
    background: #c0c0c0;
    box-shadow: inset 1px 1px rgba(255, 255, 255, 0.22),
                inset -1px -1px rgba(0, 0, 0, 0.15);
  }
  .face-x::before { background: #2f43c8; }
  .face-y::before { background: #2f9830; }
  .face-a::before { background: #c02f2f; }
  .face-b::before { background: #c8a800; }
  .face-btn span {
    position: relative;
    z-index: 1;
    text-shadow: 1px 1px 0 rgba(255,255,255,0.6);
  }
  /* compact phones (< 380px): shrink pad + diamond so nothing overflows */
  @media (max-width: 379px) {
    .pad { width: 150px; height: 150px; }
    .stick-well { width: 88px; height: 88px; margin-left: -44px; margin-top: -44px; }
    .stick-knob { width: 56px; height: 56px; margin-left: -28px; margin-top: -28px; }
    .stick-shadow { width: 56px; height: 56px; margin-left: -28px; margin-top: -28px; }
    .stick-shadow-mid { width: 56px; height: 56px; margin-left: -28px; margin-top: -28px; }
    .stick-shadow-outer { width: 56px; height: 56px; margin-left: -28px; margin-top: -28px; }
    .face-diamond { grid-template-columns: repeat(3, 42px); grid-template-rows: repeat(3, 42px); }
    .face-btn { font-size: 13px; }
  }
}
