* { box-sizing: border-box; }
html, body {
  margin: 0; height: 100%; background: #05080f; color: #e8e4d8;
  font-family: -apple-system, system-ui, sans-serif; overflow: hidden;
  /* Nothing in the app is selectable text: without this, iOS treats a
     long-press (e.g. holding fast-forward) as a text selection and pops the
     Copy / Look Up / Translate callout over the game. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

#title-screen {
  position: fixed; inset: 0; display: flex; flex-direction: column;
  /* Bottom-aligned like mi2hd: the key art owns the upper screen and the menu
     sits in the darkened band beneath it. */
  align-items: center; justify-content: flex-end; gap: 18px;
  /* The SE cover art, WHOLE and at its own aspect ratio, scaled to fit the
     screen. `contain` not `cover`: cover crops to fill, which on a wide window
     zoomed this portrait art ~2.8x and left the menu sitting on top of the
     wordmark. contain never crops and never distorts -- the art letterboxes
     into whatever shape the window is, which is the tradeoff we want.
     The scrim is only at the very bottom, purely so the buttons stay readable
     against whatever part of the art lands behind them; it is deliberately
     light, so it darkens the menu strip without dimming the artwork.
     The flat colour underneath is both the letterbox and the first-paint
     fallback before the jpeg decodes. */
  background:
    linear-gradient(to bottom, rgba(5,8,15,0) 35%, rgba(5,8,15,0.55) 62%, rgba(5,8,15,0.92) 88%),
    url("icons/title-frame1.jpg") center center / contain no-repeat,
    #05080f;
  /* The footer band is four stacked rows, mi2hd's exact geometry so the two
     games' title screens sit the same: status line (bottom 100), the update
     bar (88), the "Check for updates" row (48) and the version (10). The
     topmost starts 117px from the bottom, so the menu has to end above that --
     124px leaves a 7px gap. All four are absolutely positioned, so they are
     outside this flex column and cannot push it; this padding is what stops the
     column growing down into them. The safe-area term is not decoration: on a
     phone with a home indicator a flat 124px puts the version under it. */
  padding: 24px;
  padding-bottom: max(124px, calc(env(safe-area-inset-bottom, 0px) + 124px));
  text-align: center;
}

/* The key art is painted as the background above; this img is the accessible
   and no-CSS fallback only. */
#title-screen img.logo { display: none; }

/* The art carries the wordmark, so the heading would be a second title over
   the first. Hidden visually, still read aloud and still there if the art is
   missing (a fresh clone has no icons/keyart.jpg). */
#title-screen h1 {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
/* The cover art already says "Special Edition" on its own banner, so showing
   it again right above the menu was saying it twice. Kept in the DOM for
   screen readers and the no-art fallback, same as the h1. */
#title-screen .subtitle {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.menu-buttons { display: flex; flex-direction: column; gap: 10px; width: 220px; margin-top: 8px; }
.menu-buttons button {
  font: 600 16px/1 -apple-system, system-ui, sans-serif; padding: 14px 0;
  background: rgba(10,14,22,0.55);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  color: #e8e4d8;
  border: 1px solid rgba(242,217,138,0.35); border-radius: 10px;
  -webkit-tap-highlight-color: transparent;
}
.menu-buttons button:active { background: rgba(242,217,138,0.18); }
.menu-buttons button:disabled { opacity: 0.4; }
.menu-buttons button.primary { background: rgba(242,217,138,0.85); color: #1a1408; border-color: transparent; }
.menu-buttons button.primary:active { background: rgba(242,217,138,1); }
.menu-buttons button.selected { border-color: #f2d98a; background: rgba(242,217,138,0.22); box-shadow: 0 0 0 1px #f2d98a inset; }

/* Settings sliders */
.slider-row {
  display: flex; flex-direction: column; gap: 6px; width: 220px;
  font-size: 13px; color: #cbd5df; margin: 4px 0 10px;
}
.slider-row .slider-ends { font-size: 11px; color: #7d8b95; }
.slider-row .slider-val { float: right; font-weight: bold; color: #f2d98a; font-variant-numeric: tabular-nums; }
.slider-row input[type=range] { width: 100%; accent-color: #f2d98a; height: 26px; }

/* The footer text sits on the cover art, and `contain` scaling means WHICH part
   of the art lands behind it changes with the window. The scrim is deliberately
   light so it does not dim the painting, so the text carries its own shadow
   instead. This stopped being cosmetic when the refresh label started carrying
   progress ("Updating scummvm.js - 272 KB / 384 KB"): unreadable decoration is
   survivable, an unreadable status line is not. */
#version-badge, #refresh-button { text-shadow: 0 1px 3px rgba(0,0,0,0.9), 0 0 10px rgba(0,0,0,0.7); }
#version-badge {
  position: absolute; bottom: 10px; right: 10px; padding: 8px;
  color: rgba(255,255,255,0.7); font-size: 13px; font-weight: 600;
  background: none; border: none;
}
/* Sits directly ABOVE the version string, right-aligned with it -- mi2hd's
   layout. Label and glyph are one tap target. The old 34px icon shared an edge
   with the version number and the two fought each other on a phone. */
#refresh-button {
  position: absolute; bottom: 48px; right: 8px;
  display: flex; align-items: center; gap: 7px;
  padding: 7px 10px; height: auto; width: auto;
  color: rgba(255,255,255,0.72); background: none; border: none;
  -webkit-tap-highlight-color: transparent;
}
#refresh-label {
  font-size: 12px; font-weight: 500; letter-spacing: 0.01em;
  /* It carries progress text ("Updating scummvm.wasm — 4.1 / 19.1 MB"), so cap
     it and clip rather than let it wrap into the menu. */
  max-width: min(260px, 62vw); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#refresh-icon { font-size: 19px; line-height: 1; display: inline-block; }
#refresh-button:active { color: #f2d98a; }
/* Spin the glyph only -- spinning the words looks broken. */
#refresh-button.spinning #refresh-icon { animation: spin 0.8s linear infinite; }

/* --- title-screen update feed (ported from mi2hd) -------------------------
   Blue on purpose: the amber #progress bar means "the game is starting", this
   one means "the app is fetching itself" -- two different waits, two colours,
   so a glance tells them apart.
   ABSOLUTE, not in the flow: in the column flow this div's height would appear
   and disappear with it, shoving the whole menu up and down. It floats above
   the "Check for updates" row and changes nothing else on screen. */
#update-feed {
  position: absolute; bottom: 88px; left: 50%; transform: translateX(-50%);
  display: none; flex-direction: column; align-items: center; gap: 6px;
  width: 260px;
}
#update-feed.active { display: flex; }
#update-bar-track {
  width: 100%; height: 6px; border-radius: 3px; overflow: hidden;
  background: rgba(120, 180, 255, 0.16);
}
#update-bar {
  width: 0%; height: 100%; border-radius: 3px;
  background: linear-gradient(90deg, #3b82f6, #60a5fa);
  box-shadow: 0 0 8px rgba(96, 165, 250, 0.55);
  transition: width 0.25s linear;
}
/* Indeterminate: no Content-Length (or a cache hit), so sweep instead of lie. */
#update-bar.indeterminate { width: 35%; animation: updateSweep 1.1s ease-in-out infinite; }
@keyframes updateSweep {
  0%   { margin-left: 0%; }
  50%  { margin-left: 65%; }
  100% { margin-left: 0%; }
}
/* An explicit button: a silent retry loop is how an updater thrashes. */
#update-retry {
  margin-top: 4px; padding: 6px 14px; font-size: 12px;
  border-radius: 6px; border: 1px solid #60a5fa;
  background: rgba(59, 130, 246, 0.18); color: #cfe4ff;
}
#update-retry:active { background: rgba(59, 130, 246, 0.32); }
@keyframes spin { to { transform: rotate(360deg); } }

#game-screen {
  position: fixed; inset: 0; background: #000; display: none;
  align-items: center; justify-content: center;
  box-sizing: border-box;
  /* The picture is sized to this padded (safe) area, so it never slides under
     the notch / Dynamic Island / home indicator. In landscape it fills the
     safe area; in portrait it stays 4:3 within it. */
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
}
/* 4:3 wrapper sized by JS (fitCanvas); the canvas fills it, so the game keeps
   its proportions in both orientations instead of stretching. */
#canvas-wrap { position: relative; line-height: 0; }
/* !important so ScummVM/SDL can't override the display size with an inline
   style -- the wrapper's size (set by fitCanvas) always wins. */
#canvas { display: block; width: 100% !important; height: 100% !important; }

/* In-game overlay: Skip / Save-Load / Keyboard / Help. Deliberately faint so
   they don't compete with the game's own UI. */
#game-overlay {
  position: absolute; top: calc(6px + env(safe-area-inset-top));
  right: calc(6px + env(safe-area-inset-right));
  display: flex; gap: 6px; z-index: 10;
  flex-wrap: wrap; justify-content: flex-end;
  transition: opacity 0.4s ease;
}
/* Fade the controls away after inactivity, and let taps pass to the game. */
#game-overlay.faded { opacity: 0; pointer-events: none; }
#game-overlay button {
  width: 38px; height: 38px; border-radius: 10px; border: none;
  background: rgba(255,255,255,0.10); color: rgba(255,255,255,0.7);
  font-size: 17px; line-height: 1; -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
#game-overlay button:active { background: rgba(255,255,255,0.28); }
#game-overlay button.active { background: rgba(242,217,138,0.85); color: #1a1408; }
#help-button { font-weight: 700; }
/* The text-label toggle (HD/DOS): 17px text overflows the 38px button, and a
   fixed box clipped the label on the owner's phone in mi2hd. Self-sizing:
   let the label decide the width instead of betting on font metrics. */
#hdgfx-button {
  width: auto; min-width: 38px; padding: 0 6px;
  font-size: 12px; font-weight: 700; letter-spacing: -0.2px;
}
/* Prevent the long-press/callout from interfering with hold-to-fast-forward */
#ff-button { touch-action: none; }

/* Help modal control glyphs */
.help-list { list-style: none; margin: 0; padding: 0; }
.help-list li { margin-bottom: 10px; font-size: 14px; line-height: 1.45; color: #cbd5df; }
.help-list .ctl {
  display: inline-block; min-width: 24px; text-align: center; margin-right: 4px;
  padding: 1px 4px; border-radius: 6px; background: rgba(255,255,255,0.12);
}

/* Hidden input used to summon the iOS keyboard; kept on-screen (1px) so
   focusing it is allowed, but invisible. */
#keyboard-proxy {
  position: absolute; bottom: 0; left: 0; width: 1px; height: 1px;
  opacity: 0.01; border: none; padding: 0; background: transparent; color: transparent;
  -webkit-user-select: text; user-select: text; /* inputs still need it */
}

/* Hidden video that mirrors the canvas for the mini player (PiP). Must be
   rendered (not display:none) or iOS refuses to float it, so it sits 1px
   in a corner like the keyboard proxy. */
#pip-video {
  position: absolute; bottom: 0; right: 0; width: 1px; height: 1px;
  opacity: 0.01; pointer-events: none;
}

/* Transient status toast (bottom-center), e.g. mini-player errors */
#toast {
  position: fixed; z-index: 30; left: 50%; transform: translateX(-50%);
  bottom: calc(16px + env(safe-area-inset-bottom)); max-width: 90%;
  display: none; padding: 10px 16px; border-radius: 20px;
  font: 600 13px/1.3 -apple-system, system-ui, sans-serif;
  background: rgba(16,24,38,0.95); color: #e8e4d8;
  border: 1px solid rgba(242,217,138,0.4);
  box-shadow: 0 4px 14px rgba(0,0,0,0.5); text-align: center;
}
#toast.show { display: block; }

/* Download progress for the one-time game data fetch */
#progress {
  display: none; width: 220px; height: 6px; border-radius: 3px;
  background: rgba(255,255,255,0.12); overflow: hidden;
}
#progress-bar {
  width: 0%; height: 100%; border-radius: 3px;
  background: #f2d98a; transition: width 0.2s linear;
}

/* Boot overlay over the game canvas (download / startup progress) */
#boot-overlay {
  position: absolute; inset: 0; display: none; z-index: 15;
  flex-direction: column; align-items: center; justify-content: center; gap: 16px;
  background: radial-gradient(ellipse at center, #0d2436 0%, #05080f 80%);
  padding: 24px; text-align: center;
  opacity: 1; transition: opacity 0.4s ease;
}
#boot-overlay .boot-logo {
  width: 84px; height: 84px; border-radius: 16px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.5);
}
/* The boot screen is a short checklist, not a caption: what it is waiting on,
   how long each part took, and -- underneath -- what the engine is actually
   pulling off this device while it starts. */
#boot-head {
  display: flex; align-items: baseline; gap: 10px;
  width: min(320px, 78vw);
}
#boot-note { flex: 1; font-size: 14px; font-weight: 600; color: #e8e4d8; text-align: left; }
#boot-elapsed { font-size: 12px; color: #7d8b95; font-variant-numeric: tabular-nums; }
#boot-steps { width: min(320px, 78vw); }
#boot-steps .brow {
  display: grid; grid-template-columns: auto 1fr auto;
  gap: 0 8px; align-items: baseline; margin: 5px 0 6px;
  font-size: 12px; color: #cfc8b6; font-variant-numeric: tabular-nums; text-align: left;
}
#boot-steps .bdetail { color: #7d8b95; font-size: 11px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
#boot-steps .btime { color: #7d8b95; font-size: 11px; }
#boot-steps .bbar {
  grid-column: 1 / -1; height: 4px; border-radius: 2px; margin-top: 3px;
  background: rgba(120, 180, 255, 0.16); overflow: hidden;
}
#boot-steps .bbar > i {
  display: block; height: 100%; width: 0%; border-radius: 2px;
  background: linear-gradient(90deg, #3b82f6, #60a5fa);
  box-shadow: 0 0 6px rgba(96, 165, 250, 0.45);
}
/* No fraction to report: sweep rather than invent a percentage. */
#boot-steps .bbar > i.sweep { animation: bootSweep 1.1s ease-in-out infinite; }
@keyframes bootSweep {
  0% { margin-left: 0%; } 50% { margin-left: 62%; } 100% { margin-left: 0%; }
}
#boot-steps .brow.good .bname { color: #b9d9a8; }
/* A finished step keeps its bar so the checklist stays a checklist, but dimmed
   -- four bars all shouting the same blue and the eye cannot find the live one. */
#boot-steps .brow.good .bbar > i {
  background: #4a6b3f; box-shadow: none;
}
#boot-steps .brow.bad  .bname { color: #f0a0a0; }
#boot-steps .brow.bad  .bbar > i { background: #d07070; box-shadow: none; }
#boot-reads {
  width: min(320px, 78vw); margin-top: 2px;
  font-size: 11px; line-height: 1.4; color: #7e8aa0; text-align: left;
  font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Room-loading overlay: the SE-style loading screen shown while the engine
   holds a transition whose room still has to download. Opaque black (the SE's
   own loading presentation is black) with one of the remaster's menu parchment
   sketches drifting very slowly -- unmistakably deliberate, never mistakable
   for a stuck frame. No timers here: the engine's readiness release is what
   drops it. z-index 14 sits it just UNDER the boot overlay (15), so a hold that
   starts while the boot cover is still up never stacks a second cover. */
#roomload-overlay {
  position: absolute; inset: 0; display: none; z-index: 14;
  flex-direction: column; align-items: center; justify-content: center; gap: 18px;
  background: #000;
  padding: 24px; text-align: center;
  opacity: 0; transition: opacity 0.25s ease;
}
#roomload-art {
  max-width: min(52vw, 420px); max-height: 52vh; object-fit: contain;
  filter: drop-shadow(0 10px 30px rgba(0,0,0,0.8));
  animation: roomload-drift 14s ease-in-out infinite alternate;
}
@keyframes roomload-drift {
  from { transform: scale(1.0) translateY(0); }
  to   { transform: scale(1.045) translateY(-6px); }
}
#roomload-progress {
  width: 240px; max-width: 70vw; height: 8px; border-radius: 4px;
  background: rgba(255,255,255,0.12); overflow: hidden; visibility: hidden;
}
#roomload-bar {
  width: 0%; height: 100%; border-radius: 4px;
  background: #f2d98a; transition: width 0.25s linear;
}
#roomload-note { font-size: 14px; font-weight: 600; color: #e8e4d8; }
/* The network line. Only ever visible when we have decided a chunk is NOT on
   this device and are pulling it from the server -- which should be rare, and
   when it happens the owner wants to see it rather than stare at loading art
   with nothing moving. Tabular numerals so the countdown does not jitter. */
#roomload-detail {
  font-size: 12px; font-weight: 500; color: #b9b1a1; margin-top: 5px;
  font-variant-numeric: tabular-nums; min-height: 15px; text-align: center;
}

/* Starter-bundle gate on the title screen (Play/Continue/Load Game are disabled
   until it fills). ABSOLUTE, in the footer band with #status-note and
   #update-feed -- in the button stack it split the menu in two and printed its
   text across the artwork. It occupies the 96..117px slot: clear of the update
   bar at 88 below it and of the menu, which ends above 117. Same reason
   #status-note floats: the message wraps to two lines and an in-flow element
   would shove every button up and down as it changes. */
/* The patch line has to be READABLE OVER THE CLIP -- it is deliberately shown
   while the title plays, because that is what the clip is for. Above the video
   (z-index 1), with a dark plate behind it so white text on bright artwork is
   not a guessing game. */
#title-screen #starter-gate {
  z-index: 6;
  background: rgba(8, 10, 16, 0.72);
  border-radius: 10px; padding: 8px 12px;
}
#title-screen #starter-note { font-size: 13px; color: #e8e4d8; }
#title-screen #starter-gate:has(#starter-note:empty) { background: none; padding: 0; }
#title-screen #starter-gate {
  position: absolute; bottom: 96px; left: 50%; transform: translateX(-50%);
  width: min(340px, 88%);
  flex-direction: column; align-items: center; gap: 4px;
}
#starter-progress {
  width: 100%; max-width: 260px; height: 5px; border-radius: 3px;
  background: rgba(255,255,255,0.12); overflow: hidden;
}
#starter-bar { width: 0%; height: 100%; border-radius: 3px; background: #f2d98a; transition: width 0.3s linear; }
#starter-note {
  font-size: 13px; color: #c9c2b0; text-align: center;
  text-shadow: 0 1px 3px rgba(0,0,0,0.9), 0 0 10px rgba(0,0,0,0.7);
}

/* Resume overlay (game froze while backgrounded) */
#resume-overlay {
  position: fixed; inset: 0; display: none; z-index: 18;
  flex-direction: column; align-items: center; justify-content: center; gap: 16px;
  background: radial-gradient(ellipse at center, #0d2436 0%, #05080f 80%);
  padding: 24px; text-align: center;
}
#resume-overlay .resume-title { font-size: 20px; font-weight: 700; color: #f2d98a; }
#resume-overlay .resume-text { font-size: 14px; line-height: 1.5; color: #cbd5df; max-width: 300px; }
#resume-overlay #resume-button {
  font: 600 16px/1 -apple-system, system-ui, sans-serif; padding: 14px 28px;
  background: rgba(242,217,138,0.85); color: #1a1408; border: none; border-radius: 10px;
  -webkit-tap-highlight-color: transparent;
}
#resume-overlay #resume-button:active { background: rgba(242,217,138,1); }

/* Resume pill (dismissible "stuck?" hint after a long break) */
#resume-pill {
  position: absolute; z-index: 12; left: 50%; transform: translateX(-50%);
  top: calc(8px + env(safe-area-inset-top)); max-width: 90%;
  display: none; padding: 9px 14px; border-radius: 20px; border: none;
  font: 600 13px/1.2 -apple-system, system-ui, sans-serif;
  background: rgba(242,217,138,0.92); color: #1a1408;
  box-shadow: 0 4px 14px rgba(0,0,0,0.5); -webkit-tap-highlight-color: transparent;
}
#resume-pill.show { display: block; }

.modal-overlay {
  position: fixed; inset: 0; background: rgba(0,0,0,0.7); display: none;
  align-items: center; justify-content: center; z-index: 20; padding: 24px;
}
.modal-overlay.open { display: flex; }
.modal-box {
  position: relative;
  background: #101826; border: 1px solid rgba(242,217,138,0.3); border-radius: 12px;
  padding: 20px; max-width: 360px; width: 100%; max-height: 70vh; overflow-y: auto;
}
.modal-box h2 { margin-top: 0; padding-right: 32px; color: #f2d98a; font-size: 18px; }
/* Always-visible corner close: sticks to the top-right while the box scrolls,
   so you never have to scroll to the bottom Close button. */
.modal-x {
  position: sticky; top: 0; float: right; z-index: 5;
  width: 32px; height: 32px; margin: -8px -8px 0 8px;
  border: none; border-radius: 8px;
  background: rgba(255,255,255,0.12); color: #e8e4d8;
  font-size: 22px; line-height: 30px; text-align: center;
  -webkit-tap-highlight-color: transparent;
}
.modal-x:active { background: rgba(242,217,138,0.35); color: #1a1408; }
.modal-box p, .modal-box li { font-size: 14px; line-height: 1.5; color: #cbd5df; }
.modal-close {
  margin-top: 14px; width: 100%; padding: 10px; background: rgba(255,255,255,0.08);
  color: #e8e4d8; border: 1px solid rgba(255,255,255,0.2); border-radius: 8px;
}
.changelog-entry { margin-bottom: 12px; }
.changelog-entry .v { color: #f2d98a; font-weight: 700; }
.changelog-entry .d { color: #7d8b95; font-size: 12px; margin-left: 6px; }

.status-note { font-size: 13px; color: #7d8b95; margin-top: 4px; max-width: 260px; }
/* Out of the flow for the same reason as #update-feed, and mi2hd learned it the
   hard way: reserving ONE line is not enough, because the messages wrap to two
   and the menu jumps by the difference. Floating it means no message, however
   long, can move a single button. It sits in the gap between the menu (which
   ends above 117px) and the update bar (88px). */
#title-screen #status-note {
  position: absolute; bottom: 100px; left: 50%; transform: translateX(-50%);
  width: min(340px, 88%); min-height: 1.3em; margin: 0;
}

/* Save list in the Load Game modal */
#loadgame-list { list-style: none; margin: 0; padding: 0; }
#loadgame-list li { margin-bottom: 8px; }
#loadgame-list li.save-row { display: flex; align-items: stretch; gap: 6px; }
.save-entry-main { flex: 1 1 auto; min-width: 0; flex-direction: column;
  align-items: flex-start; justify-content: center; gap: 2px; }
.save-entry-main .save-name { max-width: 100%; overflow-wrap: anywhere; word-break: break-word; }
.save-entry-main .save-when { max-width: 100%; overflow: hidden;
  text-overflow: ellipsis; }
.save-delete {
  flex: 0 0 auto; width: 52px; padding: 0; font-size: 18px; line-height: 1;
  display: flex; align-items: center; justify-content: center; border-radius: 8px;
  background: rgba(255,255,255,0.06); color: #e8e4d8;
  border: 1px solid rgba(242,217,138,0.25); -webkit-tap-highlight-color: transparent;
}
.save-delete:active { background: rgba(220,80,80,0.28); }
.save-rename:active { background: rgba(242,217,138,0.28); }
.save-entry {
  width: 100%; display: flex; justify-content: space-between; align-items: center;
  gap: 10px; padding: 12px; border-radius: 8px; text-align: left;
  background: rgba(255,255,255,0.06); color: #e8e4d8;
  border: 1px solid rgba(242,217,138,0.25); -webkit-tap-highlight-color: transparent;
}
.save-entry:active { background: rgba(242,217,138,0.18); }
.save-entry .save-name { font-weight: 600; font-size: 14px; }
.save-entry .save-when { font-size: 11px; color: #7d8b95; white-space: nowrap; }

/* Discovery glow overlay: drawn by app.js from engine-pushed hotspot rects;
   stacked over the game canvas, never intercepts touches. */
#hotspot-canvas { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; display: none; z-index: 5; }

/* Big dialogue choices: large tappable rows rendered by app.js over the
   game's own conversation-sentence strip (top is set per frame from the
   engine-pushed verb rects). Styled like the original UI: plain black strip,
   no boxes -- each row is a canvas drawn with the game's own extracted pixel
   font in the verb's own palette colors (system-font text as fallback, which
   is what the inherited font-size and color are for). Row size follows the
   "Dialogue text size" slider; press feedback is the verb's real highlight
   color, repainted by app.js. */
#dialog-overlay {
  position: absolute; left: 0; right: 0; z-index: 6;
  display: none; flex-direction: column; gap: 0;
  padding: 2px 4px;
  background: #000; overflow-y: auto; line-height: 1.2;
  /* Harvested full lists scroll natively: keep iOS momentum, and don't let
     the scroll chain into rubber-banding the whole page. */
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
/* Bottom-anchor the rows when they don't fill the game's own strip
   (margin-top: auto instead of justify-content: flex-end, so the top of an
   overflowing list stays reachable by scrolling). */
#dialog-overlay > .dialog-row:first-child { margin-top: auto; }
/* Arrow hints floating at the list's edges when more sentences are hidden
   beyond the fold or on further pages of the game's own list; positioned by
   updateDialogMore() in app.js. */
#dialog-more, #dialog-more-up {
  position: absolute; z-index: 7; display: none;
  left: 50%; margin-left: -14px; width: 28px; height: 28px;
  border: 1px solid rgba(215, 200, 232, 0.5); border-radius: 50%;
  background: rgba(0, 0, 0, 0.65); color: #d7c8e8;
  font-size: 13px; line-height: 26px; text-align: center;
  padding: 0; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.dialog-row {
  display: block; width: 100%; text-align: left;
  font: inherit; color: #d7c8e8;
  background: none; border: none; border-radius: 0;
  padding: 3px 4px; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.dialog-row-text {
  display: block; max-width: 100%;
  image-rendering: pixelated;
}

/* --- Account: user icon on the title screen + sign-in sheet ---------------- */
#account-button {
  z-index: 4;   /* above #title-video (1); matches #title-sound's corner */
  position: absolute; top: calc(env(safe-area-inset-top) + 12px); right: 14px;
  width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,0.06); border: 1px solid rgba(242,217,138,0.3);
  border-radius: 50%; -webkit-tap-highlight-color: transparent;
}
#account-button:active { background: rgba(242,217,138,0.2); }
#account-button.signed-in { border-color: #f2d98a; box-shadow: 0 0 0 1px #f2d98a inset; }

.account-note {
  background: rgba(242,217,138,0.12); border: 1px solid rgba(242,217,138,0.3);
  color: #f2d98a; border-radius: 8px; padding: 8px 10px; font-size: 13px; margin-bottom: 12px;
}
.account-hint { font-size: 13px; color: #9fb8c4; line-height: 1.5; margin: 0 0 14px; }
.account-tabs { display: flex; gap: 8px; margin-bottom: 12px; }
.account-tab {
  flex: 1; padding: 9px 0; font: 600 14px/1 -apple-system, system-ui, sans-serif;
  background: rgba(255,255,255,0.06); color: #cbd5df;
  border: 1px solid rgba(255,255,255,0.15); border-radius: 8px;
  -webkit-tap-highlight-color: transparent;
}
.account-tab.selected { border-color: #f2d98a; background: rgba(242,217,138,0.2); color: #f2d98a; }
.account-form { display: flex; flex-direction: column; gap: 10px; }
.account-input {
  width: 100%; padding: 12px; font-size: 16px; /* 16px: iOS won't zoom on focus */
  background: rgba(255,255,255,0.06); color: #e8e4d8;
  border: 1px solid rgba(255,255,255,0.2); border-radius: 8px;
}
.account-input:focus { outline: none; border-color: #f2d98a; }
.account-submit {
  padding: 13px; font: 700 16px/1 -apple-system, system-ui, sans-serif; border-radius: 9px;
  border: 1px solid transparent; background: rgba(242,217,138,0.85); color: #1a1408;
}
.account-submit:disabled { opacity: 0.5; }
.account-card {
  background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.12);
  border-radius: 10px; padding: 12px; margin-bottom: 12px;
}
.account-who { display: flex; align-items: center; gap: 12px; }
.account-avatar {
  width: 44px; height: 44px; flex: none; display: flex; align-items: center; justify-content: center;
  background: rgba(242,217,138,0.15); border-radius: 50%;
}
.account-name { font-weight: 700; color: #e8e4d8; font-size: 15px; }
.account-email { font-size: 12px; color: #7d8b95; }
.account-badge {
  display: inline-block; margin-top: 4px; font-size: 10px; font-weight: 800; letter-spacing: 1px;
  color: #1a1408; background: #f2d98a; border-radius: 4px; padding: 2px 6px;
}
.account-actions { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.account-btn {
  padding: 12px; font: 600 15px/1 -apple-system, system-ui, sans-serif; text-align: center;
  background: rgba(255,255,255,0.06); color: #e8e4d8;
  border: 1px solid rgba(255,255,255,0.2); border-radius: 9px;
  -webkit-tap-highlight-color: transparent;
}
.account-btn:active { background: rgba(242,217,138,0.18); }
.account-btn.danger { border-color: rgba(255,120,120,0.5); color: #ff9a9a; }

/* --- Admin room browser --------------------------------------------------- */
.rooms-box { max-width: 460px; max-height: 82vh; }
.rooms-search {
  width: 100%; padding: 11px 12px; font-size: 16px; margin-bottom: 12px;
  background: rgba(255,255,255,0.06); color: #e8e4d8;
  border: 1px solid rgba(255,255,255,0.2); border-radius: 8px;
}
.rooms-search:focus { outline: none; border-color: #f2d98a; }
.rooms-warprow { display: flex; gap: 8px; margin-bottom: 6px; }
.rooms-num {
  flex: 1; min-width: 0; padding: 11px 12px; font-size: 16px;
  background: rgba(255,255,255,0.06); color: #e8e4d8;
  border: 1px solid rgba(255,255,255,0.2); border-radius: 8px;
}
.rooms-num:focus { outline: none; border-color: #f2d98a; }
.rooms-go { flex: none; width: 88px; }
.rooms-list { display: flex; flex-direction: column; gap: 6px; }
.rooms-empty { color: #7d8b95; text-align: center; padding: 20px 0; }
.room-row {
  display: flex; align-items: stretch; gap: 6px; width: 100%;
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.12); border-radius: 10px; overflow: hidden;
}
.room-row.room-fight { border-color: rgba(242,217,138,0.4); background: rgba(242,217,138,0.07); }
.room-go {
  display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0; text-align: left;
  padding: 8px 10px; background: none; border: none; -webkit-tap-highlight-color: transparent;
}
.room-go:active { background: rgba(242,217,138,0.15); }
.room-view {
  flex: none; width: 46px; font-size: 18px; background: rgba(255,255,255,0.05);
  border: none; border-left: 1px solid rgba(255,255,255,0.12);
  -webkit-tap-highlight-color: transparent;
}
.room-view:active { background: rgba(242,217,138,0.2); }
.room-num {
  flex: none; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 15px; color: #f2d98a;
  background: rgba(242,217,138,0.12); border-radius: 8px; font-variant-numeric: tabular-nums;
}
.room-label { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.room-name { font-size: 14px; color: #e8e4d8; line-height: 1.3; }
.room-code { font-size: 11px; color: #7d8b95; }
.room-flag { flex: none; font-size: 16px; }

/* --- Engine error overlay (copyable) -------------------------------------- */
#engine-error-overlay {
  position: fixed; inset: 0; z-index: 60; display: flex;
  align-items: center; justify-content: center; padding: 24px;
  background: rgba(0,0,0,0.85);
}
#engine-error-overlay .ee-box {
  background: #101826; border: 1px solid rgba(255,120,120,0.5); border-radius: 12px;
  padding: 20px; max-width: 380px; width: 100%;
}
#engine-error-overlay .ee-title { color: #ff9a9a; font-size: 18px; font-weight: 700; margin-bottom: 8px; }
#engine-error-overlay .ee-hint { color: #9fb8c4; font-size: 13px; line-height: 1.5; margin-bottom: 12px; }
#engine-error-overlay .ee-msg {
  width: 100%; font: 12px/1.4 ui-monospace, Menlo, monospace; color: #e8e4d8;
  background: #05080f; border: 1px solid rgba(255,255,255,0.2); border-radius: 8px;
  padding: 10px; resize: vertical; -webkit-user-select: text; user-select: text;
}
#engine-error-overlay .ee-actions { display: flex; gap: 8px; margin-top: 12px; }
#engine-error-overlay .ee-actions button {
  flex: 1; padding: 12px; font: 600 15px/1 -apple-system, system-ui, sans-serif;
  border-radius: 9px; border: 1px solid rgba(255,255,255,0.2);
  background: rgba(255,255,255,0.06); color: #e8e4d8; -webkit-tap-highlight-color: transparent;
}
#engine-error-overlay .ee-actions .primary { background: rgba(242,217,138,0.85); color: #1a1408; border-color: transparent; }

/* Room warp badge: ▶ play (correct-state) vs plain warp */
.room-badge {
  flex: none; align-self: center; font-size: 11px; font-weight: 700;
  padding: 3px 7px; border-radius: 6px; margin-left: 4px;
  background: rgba(242,217,138,0.85); color: #1a1408; white-space: nowrap;
}
.room-badge.dim { background: rgba(255,255,255,0.12); color: #9fb8c4; }
.room-row.room-hasboot { border-color: rgba(242,217,138,0.3); }

/* --- Admin DOS explorer (ported from mi2hd's Pak explorer, DOS half) ------ */
.pak-box { max-width: 560px; width: 100%; max-height: 88vh; display: flex; flex-direction: column; }
.pak-browser, .pak-preview { display: flex; flex-direction: column; min-height: 0; flex: 1; }
.pak-browser[hidden], .pak-preview[hidden] { display: none; }
/* Explorer mode bar: HD pak | DOS classic. The two halves describe the same
   rooms from opposite ends, so switching costs one tap, not a second dialog. */
.pak-modebar { display: flex; gap: 6px; margin-bottom: 8px; }
.pak-modebtn {
  flex: 1; padding: 7px 10px; font-size: 13px; color: #9fb8c4;
  background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.18);
  border-radius: 8px; -webkit-tap-highlight-color: transparent;
}
.pak-modebtn.active { color: #f2d98a; border-color: rgba(242,217,138,0.55); background: rgba(242,217,138,0.12); }
.pak-search {
  width: 100%; padding: 11px 12px; font-size: 16px; margin-bottom: 8px;
  background: rgba(255,255,255,0.06); color: #e8e4d8;
  border: 1px solid rgba(255,255,255,0.2); border-radius: 8px;
}
.pak-search:focus { outline: none; border-color: #f2d98a; }
.pak-crumb { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; min-height: 30px; }
.pak-crumbtext { font: 12px/1.4 ui-monospace, Menlo, monospace; color: #9fb8c4; word-break: break-all; }
.pak-up, .pak-chip {
  padding: 6px 12px; font-size: 13px; color: #f2d98a;
  background: rgba(242,217,138,0.1); border: 1px solid rgba(242,217,138,0.35);
  border-radius: 14px; -webkit-tap-highlight-color: transparent;
}
.pak-up:active, .pak-chip:active { background: rgba(242,217,138,0.3); }
.pak-list { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; }
.pak-row {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 9px 10px; background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.12); border-radius: 8px;
  -webkit-tap-highlight-color: transparent;
}
.pak-row:active { background: rgba(242,217,138,0.15); }
.pak-icon { flex: none; font-size: 15px; }
.pak-name { flex: 1; min-width: 0; font: 13px/1.35 ui-monospace, Menlo, monospace; color: #e8e4d8; word-break: break-all; }
.pak-size { flex: none; font-size: 11px; color: #7d8b95; font-variant-numeric: tabular-nums; }
.pak-empty { color: #7d8b95; text-align: center; padding: 18px 0; }
.pak-prevbar { display: flex; gap: 8px; margin-bottom: 8px; }
.pak-prevbar .account-btn { flex: 1; }
.pak-prevtitle { font: 12px/1.4 ui-monospace, Menlo, monospace; color: #f2d98a; word-break: break-all; }
.pak-prevmeta { font-size: 12px; color: #9fb8c4; margin: 2px 0 8px; }
.pak-prevbody { flex: 1; min-height: 0; overflow: auto; border-radius: 8px; }
.pak-canvas {
  display: block; max-width: 100%; height: auto; image-rendering: -webkit-optimize-contrast;
  /* checkerboard so DXT5 transparency is visible */
  background: repeating-conic-gradient(#5a5a66 0% 25%, #8a8a96 0% 50%) 0 0 / 16px 16px;
  border-radius: 8px;
}
.pak-text {
  margin: 0; padding: 10px; font: 11px/1.45 ui-monospace, Menlo, monospace;
  color: #cbd5df; background: #05080f; border: 1px solid rgba(255,255,255,0.15);
  border-radius: 8px; white-space: pre-wrap; word-break: break-all;
  -webkit-user-select: text; user-select: text;
}
.dos-roomcanvas { image-rendering: pixelated; background: #000; }
.dos-sechead { font-size: 13px; color: #f2d98a; margin: 14px 0 6px; }
.dos-table { width: 100%; border-collapse: collapse; font: 11px/1.5 ui-monospace, Menlo, monospace; color: #cbd5df; }
.dos-table th { text-align: left; color: #7d8b95; font-weight: 600; padding: 2px 8px 2px 0; }
.dos-table td { padding: 2px 8px 2px 0; border-top: 1px solid rgba(255,255,255,0.08); word-break: break-word; }
.dos-table tbody tr { cursor: pointer; }
.dos-table tbody tr.sel td { color: #ff4bd8; }
.dos-scripts details { margin-bottom: 4px; }
.dos-scripts summary {
  cursor: pointer; padding: 7px 10px; font: 12px/1.4 ui-monospace, Menlo, monospace;
  color: #e8e4d8; background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.12); border-radius: 8px;
}
.dos-scripts details[open] summary { border-radius: 8px 8px 0 0; color: #f2d98a; }
.dos-scripts details[open] .pak-text { border-radius: 0 0 8px 8px; border-top: none; }
.dos-gallery { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; }
.dos-sprite { margin: 0; text-align: center; max-width: 160px; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.dos-sprite.sel img, .dos-sprite.sel .dos-sprerr { outline: 2px solid #ff4bd8; }
.dos-sprite img {
  display: block; margin: 0 auto; max-width: 150px; height: auto;
  image-rendering: pixelated;
  background: repeating-conic-gradient(#5a5a66 0% 25%, #8a8a96 0% 50%) 0 0 / 12px 12px;
  border-radius: 4px;
}
.dos-sprite figcaption { font: 10px/1.4 ui-monospace, Menlo, monospace; color: #9fb8c4; margin-top: 2px; }
.dos-sprerr {
  width: 40px; height: 40px; margin: 0 auto; display: flex; align-items: center;
  justify-content: center; border: 1px dashed rgba(255,120,120,0.5); border-radius: 4px;
}
.dos-conf {
  margin-left: 8px; padding: 1px 7px; border-radius: 9px;
  font-size: 10px; font-weight: 700; vertical-align: 1px;
}
.dos-conf-high { color: #55d47e; background: rgba(85,212,126,0.12); border: 1px solid rgba(85,212,126,0.4); }
.dos-conf-medium { color: #f2c14e; background: rgba(242,193,78,0.12); border: 1px solid rgba(242,193,78,0.45); }
.dos-conf-low { color: #ff8a7a; background: rgba(255,138,122,0.12); border: 1px solid rgba(255,138,122,0.5); }
.dos-warnflag { margin-left: 6px; font-size: 11px; color: #f2c14e; }
.dos-warnnote {
  margin: 0; padding: 7px 10px; font: 11px/1.5 ui-monospace, Menlo, monospace;
  color: #f2c14e; background: rgba(242,193,78,0.07);
  border: 1px solid rgba(242,193,78,0.3); white-space: pre-wrap;
}
.dos-usedby { display: block; color: #7fd4ff; cursor: pointer; }
.dos-table td.dos-usedby { display: table-cell; }
.dos-xrefpanel { margin: 8px 0; }
.dos-xrefhead { font: 12px/1.5 ui-monospace, Menlo, monospace; color: #7fd4ff; margin: 4px 0 6px; }
.dos-xreflist { display: flex; flex-direction: column; gap: 4px; }
.dos-xrefrow .pak-name { font-size: 12px; }
.dos-refrow {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
  padding: 6px 10px; margin: 0; background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.12);
}
.dos-reflabel { font-size: 11px; color: #7d8b95; }
.dos-refchip {
  padding: 3px 9px; font: 11px/1.4 ui-monospace, Menlo, monospace; color: #7fd4ff;
  background: rgba(80,200,255,0.1); border: 1px solid rgba(80,200,255,0.35);
  border-radius: 10px; -webkit-tap-highlight-color: transparent;
}
.dos-refchip:active { background: rgba(80,200,255,0.28); }
.dos-hlline { background: rgba(255,75,216,0.26); outline: 1px solid rgba(255,75,216,0.5); border-radius: 3px; }
.dos-flash { animation: dosflash 1.5s ease-out; }
@keyframes dosflash {
  0%, 60% { background: rgba(242,217,138,0.28); }
  100% { background: transparent; }
}

/* --- crash-logging admin panel ------------------------------------------- */
.diag-me {
  padding: 12px; margin-bottom: 14px;
  background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.12);
  border-radius: 10px;
}
.diag-mehead { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.diag-melabel { font-size: 13px; color: #f2d98a; }
.diag-medev { font: 11px/1.5 ui-monospace, Menlo, monospace; color: #7d8b95; }
.diag-mestat { margin-top: 8px; font-size: 12px; color: #cbd5df; }
.diag-hint { margin-top: 8px; font-size: 11px; line-height: 1.5; color: #7d8b95; }
.diag-listhead {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 12px; color: #9fb8c4; margin-bottom: 8px;
}
.diag-window {
  background: rgba(255,255,255,0.06); color: #e8eef3;
  border: 1px solid rgba(255,255,255,0.18); border-radius: 8px; padding: 4px 8px;
  font: inherit;
}
.diag-note { min-height: 16px; font-size: 12px; color: #f2c14e; margin-bottom: 6px; }
.diag-list { display: flex; flex-direction: column; gap: 8px; }
.diag-empty { font-size: 12px; line-height: 1.6; color: #7d8b95; }
.diag-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px; background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.12); border-radius: 10px;
}
.diag-row.on { border-color: rgba(85,212,126,0.5); background: rgba(85,212,126,0.08); }
.diag-rowmain { min-width: 0; }
.diag-name { font-size: 13px; color: #e8eef3; word-break: break-word; }
.diag-tag {
  font-size: 10px; color: #f2d98a; background: rgba(242,217,138,0.14);
  border: 1px solid rgba(242,217,138,0.4); border-radius: 8px; padding: 1px 6px;
}
.diag-meta {
  font: 10px/1.5 ui-monospace, Menlo, monospace; color: #7d8b95;
  margin-top: 2px; word-break: break-word;
}
.diag-on { margin-top: 3px; font-size: 11px; color: #55d47e; }
.diag-rowbtns { display: flex; flex-direction: column; gap: 4px; flex-shrink: 0; }
.diag-rowbtns .account-btn { padding: 5px 10px; font-size: 12px; }


/* ---- title video ---------------------------------------------------------
   The clip is laid over the poster background at exactly the same geometry --
   `contain`, centred -- so the handoff from video to still is invisible: the
   last painted video frame and the CSS background occupy the same rectangle.
   It is aria-hidden and untabbable because it carries no information the menu
   does not already give. */
#title-video {
  /* Explicitly BELOW the corner controls. The clip covers the whole title
     screen, and with no z-index of its own it was painting over the account
     icon and the mute button -- both of which the player is meant to be able
     to reach while it plays. */
  z-index: 1;
  /* Its BOX starts below the corner controls, not just its paint order.
     Putting the clip underneath them stopped it covering the icons, but the
     picture still ran up behind them, which is the thing that looked wrong.
     object-fit: contain letterboxes within whatever box it is given, so
     insetting the top simply seats the clip below the mute and account
     buttons instead of behind them. */
  position: absolute; top: calc(env(safe-area-inset-top) + 62px);
  left: 0; right: 0; bottom: 0; width: 100%; height: auto;
  object-fit: contain; object-position: center center;
  pointer-events: none; opacity: 0; transition: opacity 260ms ease;
  background: transparent;
}
#title-video.is-playing { opacity: 1; }
/* Fading OUT to the still rather than cutting: the video's final frame is
   pushed in and has no logo, so a hard cut back to frame 1 would visibly jump. */
#title-video.is-done { opacity: 0; transition: opacity 520ms ease; }

/* The menu fades in over the clip. It starts hidden but NOT display:none --
   it must keep its layout box so nothing reflows when it appears, and so a
   screen reader can still reach it while the decoration plays. */
#title-screen .menu-buttons {
  opacity: 0; transition: opacity 900ms ease 500ms;
}
#title-screen .menu-buttons.is-in { opacity: 1; }
/* Someone who has asked for less motion gets the still and the menu, at once. */
@media (prefers-reduced-motion: reduce) {
  #title-video { display: none; }
  #title-screen .menu-buttons { opacity: 1; transition: none; }
}


/* The title-sound toggle. Only shown while the clip is on screen, and only
   worth showing at all when the browser refused sound -- one tap is the user
   activation that makes it legal, which is the same rule app.js satisfies with
   the Play tap. Sits top-left, clear of the account button on the right. */
#title-sound {
  position: absolute; top: calc(env(safe-area-inset-top) + 12px); left: 14px; z-index: 4;
  width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,0.22);
  background: rgba(5,8,15,0.55); color: #f2d98a;
  font-size: 17px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
#title-sound[hidden] { display: none; }


/* ---- Browse server saves (web/serversaves.js) ---------------------------
   Lifted from mi2hd's stylesheet unchanged. Porting the module without
   these left the dialog working but unreadable -- rows wrapping into each
   other and badges colliding with the text -- which is a good reminder
   that a ported component is its markup AND its rules. */
.srv-box { max-width: 520px; width: 100%; max-height: 88vh; display: flex; flex-direction: column; }
.srv-search {
  width: 100%; padding: 11px 12px; font-size: 16px; /* 16px: iOS won't zoom on focus */
  background: rgba(255,255,255,0.06); color: #e8e4d8;
  border: 1px solid rgba(255,255,255,0.2); border-radius: 8px;
}
.srv-search:focus { outline: none; border-color: #f2d98a; }
.srv-sortrow { display: flex; align-items: center; gap: 6px; margin: 10px 0 8px; }
.srv-sortlabel { font: 600 11px/1 system-ui, sans-serif; color: #7d8b95; text-transform: uppercase; letter-spacing: 0.06em; }
.srv-sort, .srv-dir, .srv-refresh {
  padding: 7px 10px; font: 600 13px/1 -apple-system, system-ui, sans-serif;
  background: rgba(255,255,255,0.06); color: #cbd5df;
  border: 1px solid rgba(255,255,255,0.15); border-radius: 8px;
  -webkit-tap-highlight-color: transparent;
}
.srv-sort.selected { border-color: #f2d98a; background: rgba(242,217,138,0.2); color: #f2d98a; }
.srv-dir { min-width: 34px; }
.srv-refresh { margin-left: auto; }
/* The box is a column flexbox with a flex:1 list, so every control above the
   list must refuse to shrink -- without this the chips row lost half its
   height to the list and the first sticky group header sat on top of it. */
.srv-search, .srv-sortrow, .srv-users, .srv-note { flex: none; }
.srv-users { display: flex; align-items: center; gap: 6px; overflow-x: auto; padding-bottom: 8px; scrollbar-width: none; }
.srv-users::-webkit-scrollbar { display: none; }
.srv-chip {
  flex: none; display: flex; align-items: center; gap: 6px; white-space: nowrap;
  padding: 7px 11px; font: 600 13px/1 -apple-system, system-ui, sans-serif;
  background: rgba(255,255,255,0.06); color: #cbd5df;
  border: 1px solid rgba(255,255,255,0.15); border-radius: 999px;
  -webkit-tap-highlight-color: transparent;
}
.srv-chip.selected { border-color: #f2d98a; background: rgba(242,217,138,0.2); color: #f2d98a; }
.srv-count { font-size: 11px; color: #7d8b95; }
.srv-chip.selected .srv-count { color: #c8b070; }
.srv-note {
  margin: 8px 0; padding: 9px 11px; border-radius: 8px; font-size: 13px; line-height: 1.45;
  background: rgba(242,217,138,0.12); border: 1px solid rgba(242,217,138,0.3); color: #f2d98a;
}
.srv-list { flex: 1; min-height: 0; overflow-y: auto; margin-top: 4px; }
.srv-empty { padding: 24px 8px; text-align: center; color: #7d8b95; font-size: 14px; }
.srv-group { margin-bottom: 10px; }
.srv-ghead {
  position: sticky; top: 0; z-index: 2; display: flex; align-items: center; gap: 8px;
  width: 100%; padding: 9px 4px; margin-bottom: 2px; text-align: left;
  background: #101826; border: none; border-bottom: 1px solid rgba(242,217,138,0.22);
  font: 700 13px/1 -apple-system, system-ui, sans-serif; color: #f2d98a;
  -webkit-tap-highlight-color: transparent;
}
.srv-ghead:active { background: #16203063; }
.srv-caret { flex: none; width: 10px; color: #b9a86a; font-size: 11px; }
.srv-gname { flex: none; }
.srv-glast {
  flex: 1; min-width: 0; font-size: 11px; font-weight: 600; color: #7d8b95;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.srv-you {
  padding: 2px 6px; border-radius: 999px; font-size: 10px; font-weight: 700;
  background: rgba(242,217,138,0.18); color: #f2d98a;
}
.srv-gcount { flex: none; font-size: 11px; font-weight: 700; color: #b9a86a; }
.srv-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 7px 4px; border-bottom: 1px solid rgba(255,255,255,0.06);
}
.srv-thumb {
  flex: none; width: 68px; height: 43px; border-radius: 5px;
  background: rgba(255,255,255,0.06) center/cover no-repeat;
  border: 1px solid rgba(255,255,255,0.12);
}
.srv-text { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 2px; }
.srv-name {
  font: 600 14px/1.25 -apple-system, system-ui, sans-serif; color: #e8e4d8;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.srv-room { font-size: 11px; color: #b9a86a; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.srv-when { font-size: 11px; color: #7d8b95; white-space: nowrap; }
.srv-copy {
  flex: none; min-width: 62px; padding: 8px 10px;
  font: 600 13px/1 -apple-system, system-ui, sans-serif;
  background: rgba(242,217,138,0.18); color: #f2d98a;
  border: 1px solid rgba(242,217,138,0.45); border-radius: 8px;
  -webkit-tap-highlight-color: transparent;
}
.srv-copy:active { background: rgba(242,217,138,0.34); }
.srv-copy.ghost, .srv-copy.done {
  background: rgba(255,255,255,0.05); border-color: rgba(255,255,255,0.14); color: #7d8b95;
}
.srv-copy.done { color: #8fd18f; border-color: rgba(143,209,143,0.35); }


/* Secondary text in the pak explorer rows (sizes, item counts, the
   "…and N more" truncation note). The other pak-* classes already exist from
   the DOS explorer, which is the same dialog shell. */
.pak-dim { color: #7d8b95; font-size: 11px; margin-left: auto; padding-left: 10px; }

.pak-crumb a { color: #b9a86a; text-decoration: none; }
.pak-crumb a:hover { color: #f2d98a; text-decoration: underline; }


/* --- patcher log ---------------------------------------------------------
   Everything the update check prints, where it can be read. The one-line
   status is fine for "what is it doing now" and useless for "what did it
   just do" -- the owner watched a check run and could not tell whether it
   had finished, or what it had decided about any individual file. */
#patchlog {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: calc(env(safe-area-inset-bottom) + 54px);
  width: min(360px, 92%); z-index: 8;
  background: rgba(8, 10, 16, 0.9); border: 1px solid rgba(242,217,138,0.25);
  border-radius: 10px; padding: 6px 10px; display: none;
}
#patchlog.open { display: block; }
/* Collapsed is the normal state: the title, and under it the file being
   checked right now with its bar. The title stays put when the log opens so
   there is always something saying what the panel is. */
#patchlog-now {
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  font-size: 11px; line-height: 1.4; color: #cfc8b6;
  font-variant-numeric: tabular-nums; text-align: left;
}
#patchlog-now .pnow-head { display: flex; align-items: center; gap: 8px; }
#patchlog-now .pnow-label { flex: 1; color: #9a927f; letter-spacing: 0.02em; }
#patchlog-now .pnow-line {
  display: flex; align-items: center; gap: 8px; margin-top: 3px; min-height: 14px;
}
#patchlog.expanded #patchlog-now .pnow-line { display: none; }
#patchlog-now .pnow-name {
  flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#patchlog-now .pbar { flex: 0 0 96px; }
#patchlog-now.good .pnow-name { color: #b9d9a8; }
#patchlog-now.bad  .pnow-name { color: #f0a0a0; }
#patchlog-lines {
  display: none;
  max-height: calc(34vh - 34px); overflow-y: auto; overscroll-behavior: contain;
  margin-top: 6px; padding-top: 6px; border-top: 1px solid rgba(255,255,255,0.10);
  font-size: 11px; line-height: 1.45; color: #cfc8b6;
  font-variant-numeric: tabular-nums; text-align: left;
}
#patchlog.expanded #patchlog-lines { display: block; }
#patchlog-lines .bad { color: #f0a0a0; }
#patchlog-lines .good { color: #b9d9a8; }
/* One row per file: name, size, time, fingerprint, and a bar under all four. */
#patchlog-lines .prow {
  display: grid; grid-template-columns: 1fr auto auto auto;
  gap: 0 8px; align-items: baseline; margin: 3px 0 5px;
}
#patchlog-lines .pname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#patchlog-lines .psize, #patchlog-lines .ptime { color: #9a927f; font-size: 10px; }
#patchlog-lines .phash { color: #7e8aa0; font-size: 10px; }
#patchlog-lines .prow.good .pname { color: #b9d9a8; }
#patchlog-lines .prow.bad  .pname { color: #f0a0a0; }
#patchlog-lines .pbar { grid-column: 1 / -1; width: 140px; max-width: 60%; margin-top: 3px; }
/* The same blue as the update bar on the front page -- one idea of what a
   progress bar looks like, not two. */
#patchlog .pbar {
  height: 4px; border-radius: 2px; overflow: hidden;
  background: rgba(120, 180, 255, 0.16);
}
#patchlog .pbar > i {
  display: block; height: 100%; width: 0%; border-radius: 2px;
  background: linear-gradient(90deg, #3b82f6, #60a5fa);
  box-shadow: 0 0 6px rgba(96, 165, 250, 0.45);
  transition: width 0.2s linear;
}
#patchlog-lines .prow.bad .pbar > i,
#patchlog-now.bad .pbar > i {
  background: #d07070; box-shadow: none;
}
#patchlog-now .pnow-caret {
  flex: 0 0 auto; color: #cfc8b6; font-size: 12px; line-height: 1;
  transition: transform 0.15s ease;
}
#patchlog.expanded #patchlog-now .pnow-caret { transform: rotate(180deg); }
