:root { --paper: #e8c95a; --ink: #2a1a10; --bg: #1a0f0a; }
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; background: var(--bg); overflow: hidden;
  font-family: "Kaiti TC", 楷體, DFKai-SB, BiauKai, serif; color: #f3e6c8;
  -webkit-user-select: none; user-select: none; touch-action: manipulation;
  /* mashing near the top edge must not pull-to-refresh mid-ritual */
  overscroll-behavior: none; }
.noscript { padding: 2rem; text-align: center; }
#app { height: 100%; display: flex; align-items: center; justify-content: center;
  background:
    repeating-linear-gradient(0deg, #241511 0 6px, #1f120d 6px 12px); }
#stage-wrap { position: relative; aspect-ratio: 9 / 16;
  height: min(100vh, calc(100vw * 16 / 9));
  height: min(100dvh, calc(100vw * 16 / 9)); }
#stage { position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  background: #000; cursor: none;
  /* `manipulation` still makes the browser wait to rule out a pinch/pan before
     it commits the touch. `none` hands us pointerdown immediately, which is the
     whole game on mobile. Zooming elsewhere on the page is unaffected. */
  touch-action: none; -webkit-tap-highlight-color: transparent; }
.overlay { position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 14px;
  padding: calc(24px + env(safe-area-inset-top)) calc(24px + env(safe-area-inset-right))
           calc(24px + env(safe-area-inset-bottom)) calc(24px + env(safe-area-inset-left));
  background: rgba(16, 8, 5, 0.92); text-align: center; z-index: 2;
  overflow-y: auto; overscroll-behavior: contain; }
.overlay[hidden] { display: none; }
.overlay h1 { font-size: 3rem; letter-spacing: 0.3em; text-indent: 0.3em; color: var(--paper); }
.overlay .sub { font-size: 0.95rem; opacity: 0.75; line-height: 1.6; }
.overlay label { font-size: 1rem; }
.overlay input[type="text"] { font: inherit; font-size: 1.2rem; text-align: center;
  padding: 10px 14px; width: min(320px, 80%); border-radius: 8px;
  border: 1px solid #7a5a30; background: #120a06; color: var(--paper); }
/* every control clears the 44px minimum touch target */
.file-btn, .mode-btn, .end-btn { font: inherit; font-size: 1.1rem; padding: 12px 26px;
  min-height: 48px; border-radius: 10px; border: 1px solid #7a5a30; background: #2c1a0e;
  color: #f3e6c8; cursor: pointer; touch-action: manipulation;
  -webkit-tap-highlight-color: transparent; }
.mode-btn.primary { background: #7c1f14; border-color: #a8442f; font-size: 1.35rem;
  min-height: 56px; }
/* ── game-style entry flow ──────────────────────────────────────────────
   Swipe steps (打手/場景) are full-width scroll-snap strips over the
   live canvas; [data-step] on #entry decides which strip shows and how
   heavy the veil is — the scene step is mostly transparent because the
   canvas itself is the preview. */
#entry.entry { justify-content: flex-start; gap: 10px;
  padding-left: 0; padding-right: 0; }        /* strips span the full stage */
#entry[data-step="skin"] { background: rgba(16, 8, 5, 0.82); }
#entry[data-step="scene"] { background: linear-gradient(rgba(16, 8, 5, 0.78),
  rgba(16, 8, 5, 0.22) 18%, rgba(16, 8, 5, 0) 30%, rgba(16, 8, 5, 0) 64%,
  rgba(16, 8, 5, 0.82) 88%); }
.entry-top { position: relative; width: 100%; display: flex; align-items: center;
  justify-content: center; min-height: 48px; flex-shrink: 0; }
#entry-back { position: absolute; left: calc(14px + env(safe-area-inset-left));
  width: 44px; height: 44px; border-radius: 999px; border: 1px solid #7a5a30;
  background: rgba(18, 10, 6, 0.75); color: #f3e6c8; display: flex;
  align-items: center; justify-content: center; cursor: pointer;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
#entry-back svg { width: 22px; height: 22px; }
#entry-back[hidden] { display: none; }
#entry-title { font-size: 1.7rem; letter-spacing: 0.25em; text-indent: 0.25em;
  color: var(--paper); }
.swipe-wrap { display: none; position: relative; width: 100%; flex: 1;
  min-height: 0; flex-direction: column; }
/* Side arrows: the affordance the strip itself has none of. Absolutely placed
   in the wrap (not the strip) so they stay put while the cards scroll past, and
   in the gap beside the card — at 402px the card is 249 of 392, leaving 71px a
   side for a 44px button. */
/* Nudged up by half the dots row: the wrap holds strip + dots, so a plain 50%
   would sit the arrows below the card's own middle (measured: 18px does it). */
.swipe-nav { position: absolute; top: calc(50% - 18px); transform: translateY(-50%);
  width: 44px; height: 44px; border-radius: 999px; border: 1px solid #7a5a30;
  background: rgba(18, 10, 6, 0.75); color: #f3e6c8; display: flex;
  align-items: center; justify-content: center; cursor: pointer; z-index: 1;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
.swipe-nav svg { width: 22px; height: 22px; }
.swipe-nav--prev { left: calc(8px + env(safe-area-inset-left)); }
.swipe-nav--next { right: calc(8px + env(safe-area-inset-right)); }
.swipe-nav--next svg { transform: scaleX(-1); }
.swipe-nav.is-end { opacity: 0.28; cursor: default; }
.swipe-nav[hidden] { display: none; }
#entry[data-step="skin"] .swipe-wrap[data-for="skin"],
#entry[data-step="scene"] .swipe-wrap[data-for="scene"] { display: flex; }
.swipe-strip { flex: 1; min-height: 0; display: flex; overflow-x: auto;
  overflow-y: hidden; scroll-snap-type: x mandatory; scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  /* a mouse cannot pan a scroll container, so say it is draggable and let
     entry.js do the panning */
  cursor: grab; }
/* While entry.js drags the strip, snapping has to be OFF: with mandatory snap
   still on, every scrollLeft it writes is re-snapped to the nearest page and
   the card jitters against the cursor instead of following it. */
.swipe-strip.is-dragging { scroll-snap-type: none; cursor: grabbing; }
.swipe-strip::-webkit-scrollbar { display: none; }
.swipe-page { flex: 0 0 100%; scroll-snap-align: center; scroll-snap-stop: always;
  position: relative; display: flex; align-items: center; justify-content: center; }
/* ── 打手 card: the whole figure, lit like an altar niche ────────────────
   The figure (characters.js art.card, the full source painting) stands in a
   pool of joss-candle light over dark temple wood, feet grounded by a faint
   floor glow, name on a plaque below. Locked skins keep the SAME figure but
   CSS blacks it into a paper-cut silhouette (剪影): the player sees exactly
   who is waiting — shape, hat, implement — and nothing else. */
.char-card { width: min(66vw, 310px); aspect-ratio: 3 / 4.4; border-radius: 18px;
  border: 2px solid #7a5a30; position: relative; display: flex;
  flex-direction: column; overflow: hidden;
  background:
    radial-gradient(90% 52% at 50% 36%, rgba(232, 185, 95, 0.30), rgba(232, 185, 95, 0) 72%),
    linear-gradient(#33200f, #1b0e07);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.45),
    inset 0 0 0 1px rgba(232, 201, 90, 0.10); }
.char-figure { flex: 1; position: relative; min-height: 0;
  margin: 12px 10px 0; }
/* floor glow first, figure after — she stands ON the light, not behind it */
.char-figure::before { content: ""; position: absolute; left: 14%; right: 14%;
  bottom: 0; height: 7%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(232, 201, 90, 0.30), transparent 72%); }
.char-figure::after { content: ""; position: absolute; inset: 0;
  background: var(--figure) center bottom / contain no-repeat;
  filter: drop-shadow(0 10px 14px rgba(0, 0, 0, 0.5)); }
.char-figure--classic { display: flex; align-items: center; justify-content: center; }
.char-figure--classic svg { width: 52%; height: auto;
  filter: drop-shadow(0 10px 14px rgba(0, 0, 0, 0.35)); }
/* 經典版 keeps its stick figure, on parchment light instead of temple gold */
.char-figure--classic::after { content: none; }
.char-card--classic { background:
    radial-gradient(90% 52% at 50% 36%, rgba(238, 209, 85, 0.45), rgba(238, 209, 85, 0) 72%),
    linear-gradient(#33200f, #1b0e07); }
/* locked: the candle goes out and the figure becomes its own paper cut-out */
.char-card.is-locked { background:
    radial-gradient(90% 52% at 50% 36%, rgba(160, 150, 130, 0.14), rgba(160, 150, 130, 0) 72%),
    linear-gradient(#241610, #150c06); }
.is-locked .char-figure::before { background:
    radial-gradient(50% 50% at 50% 50%, rgba(200, 190, 170, 0.12), transparent 72%); }
.is-locked .char-figure::after { filter: brightness(0) drop-shadow(0 0 1px rgba(232, 201, 90, 0.35));
  opacity: 0.93; }
/* z-index 1: the figure is the .char-figure::after layer, which paints AFTER
   its parent's children — without this the silhouette sits on top and the
   gold lock shows through it at 7% */
.lock-badge { position: absolute; inset: 0; display: flex; align-items: center;
  justify-content: center; color: var(--paper); z-index: 1; }
/* a coin of dark lacquer behind the lock — gold-on-black was invisible where
   the badge landed on the silhouette's chest */
.lock-badge svg { width: 40px; height: 40px; padding: 15px; border-radius: 999px;
  background: rgba(16, 8, 5, 0.72); border: 1px solid rgba(232, 201, 90, 0.45);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.6); }
.char-name { display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 12px; font-size: 1.3rem; letter-spacing: 0.12em; text-indent: 0.12em;
  background: linear-gradient(rgba(16, 8, 5, 0.55), rgba(16, 8, 5, 0.92));
  border-top: 1px solid rgba(232, 201, 90, 0.22); }
.lock-tag { font-size: 0.75rem; padding: 2px 10px; border-radius: 999px;
  border: 1px solid #7a5a30; opacity: 0.8; white-space: nowrap; }
/* a LIVE limited window — urgency, not a lock */
.lock-tag--live { border-color: var(--paper); color: var(--paper); opacity: 1; }
.scene-label.is-locked { opacity: 0.7; }
.scene-label .lock-tag { margin-left: 8px; }
.card-nudge { animation: card-nudge 0.45s ease-out; }
@keyframes card-nudge {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-10px); }
  50% { transform: translateX(8px); }
  75% { transform: translateX(-5px); }
}
.scene-page { align-items: flex-end; padding-bottom: 8vh; }
.scene-label { font-size: 1.6rem; letter-spacing: 0.2em; text-indent: 0.2em;
  color: var(--paper); background: rgba(16, 8, 5, 0.72); padding: 10px 26px;
  border-radius: 999px; border: 1px solid #7a5a30; }
.dots { display: flex; justify-content: center; padding: 4px 0 0; flex-shrink: 0; }
/* dots are buttons — a direct jump for mouse and keyboard users, and the only
   affordance that shows how many 打手 there are; padding gives each a ~30px hit
   area while the visible dot stays small */
.dot { padding: 10px 5px; border: 0; background: none; cursor: pointer;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
.dot::before { content: ""; display: block; width: 9px; height: 9px;
  border-radius: 999px; background: rgba(243, 230, 200, 0.3); }
.dot.on::before { background: var(--paper); }
#entry-advance { flex-shrink: 0; font: inherit; font-size: 1.25rem; min-height: 54px;
  width: min(320px, 82%); border-radius: 12px; border: 1px solid #a8442f;
  background: #7c1f14; color: #f3e6c8; cursor: pointer; margin-bottom: 6px;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
#entry-advance.is-locked { background: #2c1a0e; border-color: #7a5a30; opacity: 0.65; }
#entry-advance[hidden] { display: none; }
.ready-panel { display: none; width: 100%; flex: 1; min-height: 0; overflow-y: auto;
  flex-direction: column; align-items: center; gap: 14px;
  padding: 8px 24px calc(10px + env(safe-area-inset-bottom)); }
#entry[data-step="ready"] .ready-panel { display: flex; }
/* Consent row — only rendered while a photo is loaded. The [hidden] rule is
   NOT redundant: `display: flex` here would otherwise beat the UA's
   [hidden] { display: none }, exactly as it does for .overlay above. */
.consent { display: flex; gap: 10px; align-items: flex-start; text-align: left;
  max-width: min(340px, 86%); font-size: 0.8rem; line-height: 1.6;
  padding: 10px 12px; border-radius: 8px; border: 1px solid #7a5a30;
  background: #120a06; cursor: pointer; }
.consent[hidden] { display: none; }
.consent input { flex: 0 0 auto; width: 22px; height: 22px; margin-top: 1px;
  accent-color: var(--paper); }
/* pressing a locked mode button points at the reason instead of doing nothing */
.consent--nudge { animation: consent-nudge 0.6s ease-out; }
@keyframes consent-nudge {
  0%, 100% { border-color: #7a5a30; background: #120a06; }
  25%, 75% { border-color: #a8442f; background: #2a1008; }
}
.mode-btn.is-locked { opacity: 0.4; }
.fineprint { font-size: 0.75rem; opacity: 0.55; line-height: 1.7; }
#photo-input { display: none; }
#end-video { width: min(300px, 70%); aspect-ratio: 9 / 16; border-radius: 12px;
  border: 1px solid #7a5a30; background: #000; }
.end-row { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }
/* clear of the notch, and big enough to hit while mashing */
#hud-stop { position: absolute; z-index: 1; font: inherit;
  top: calc(12px + env(safe-area-inset-top));
  left: calc(12px + env(safe-area-inset-left));
  font-size: 1rem; padding: 10px 20px; min-height: 44px; border-radius: 999px;
  border: 1px solid #7a5a30; background: rgba(18, 10, 6, 0.75); color: #f3e6c8;
  cursor: pointer; touch-action: manipulation;
  -webkit-tap-highlight-color: transparent; }
#hud-stop[hidden] { display: none; }
/* Support link, mirroring #hud-stop across the top edge. z-index 3 puts it
   ABOVE .overlay (2) — at 1 it would be buried under the near-opaque entry
   screen, i.e. invisible exactly when it is meant to be seen. It is hidden
   while a mode runs so a mash near the top edge cannot navigate away. */
#coffee-btn { position: absolute; z-index: 3;
  /* The canvas HUD draws "打咗 N 下" right-aligned at stage y 26 with 34px text,
     i.e. down to y 60 of 1280 — 4.7% of stage height. A fixed px offset would
     collide with it at some viewport sizes, so sit below it proportionally. */
  top: calc(6% + env(safe-area-inset-top));
  right: calc(12px + env(safe-area-inset-right));
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 999px;
  border: 1px solid #7a5a30; background: rgba(18, 10, 6, 0.75); color: #f3e6c8;
  text-decoration: none; touch-action: manipulation;
  -webkit-tap-highlight-color: transparent; }
#coffee-btn:hover { background: rgba(44, 26, 14, 0.9); color: var(--paper); }
#coffee-btn[hidden] { display: none; }
