/* ==========================================================================
   A quiet place — page chrome.

   The whole interface is one card that hides itself. Its colours are not
   fixed: --sky, --sky-deep and --glow are written from the running scene, so
   the card is dusk-pink at dusk and indigo at midnight. Everything here is
   built for this page alone.
   ========================================================================== */

:root {
  /* Written live by ui.js; these are just sane values for the first frame. */
  --sky: #6f7d92;
  --sky-deep: #1d2436;
  --glow: #ffb066;
  --night: 0.5;

  --ink: 16, 13, 11;
  --paper: #f4ece0;
  --paper-dim: rgba(244, 236, 224, 0.62);
  --paper-faint: rgba(244, 236, 224, 0.34);
  --hair: rgba(244, 236, 224, 0.14);

  --display: "Noto Serif TC", "Songti TC", serif;
  --ui: "Noto Sans TC", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: #0a0908;
  font-family: var(--ui);
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}

#scene {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: none;
  cursor: grab;
}
#scene:active { cursor: grabbing; }

#ui {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 10;
}
#ui > * { pointer-events: auto; }

/* --------------------------------------------------------------------------
   Intro veil
   -------------------------------------------------------------------------- */

.veil {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  background:
    radial-gradient(120% 90% at 50% 42%, rgba(58, 42, 30, 0.55), transparent 70%),
    #0b0a09;
  z-index: 40;
  transition: opacity 1.2s var(--ease);
  cursor: pointer;
  text-align: center;
  padding: 6vh 7vw;
}
.veil.is-gone { opacity: 0; pointer-events: none; }

.veil-inner { max-width: 30rem; }

/* The mark is the page's one piece of scale: a single character, very large,
   set in the serif. Everything else on the veil is small and quiet. */
.veil-mark {
  font-family: var(--display);
  font-size: clamp(4.5rem, 13vw, 8rem);
  font-weight: 200;
  line-height: 1;
  color: var(--paper);
  opacity: 0.92;
  text-shadow: 0 0 60px rgba(255, 176, 102, 0.35);
  animation: markIn 2.4s var(--ease) both;
}

.veil-title {
  margin: 1.6rem 0 0;
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(1.1rem, 3.4vw, 1.45rem);
  letter-spacing: 0.42em;
  text-indent: 0.42em;
  color: var(--paper);
  animation: riseIn 1.4s 0.35s var(--ease) both;
}

.veil-lead {
  margin: 1.1rem auto 0;
  max-width: 26rem;
  font-size: 0.9rem;
  line-height: 2;
  color: var(--paper-dim);
  animation: riseIn 1.4s 0.55s var(--ease) both;
}

.veil-enter {
  margin-top: 2.4rem;
  padding: 0.85rem 3.2rem;
  font-family: var(--display);
  font-size: 0.95rem;
  letter-spacing: 0.5em;
  text-indent: 0.5em;
  color: var(--paper);
  background: transparent;
  border: 1px solid rgba(244, 236, 224, 0.28);
  border-radius: 999px;
  cursor: pointer;
  transition: border-color 0.5s var(--ease), background 0.5s var(--ease), letter-spacing 0.5s var(--ease);
  animation: riseIn 1.4s 0.75s var(--ease) both;
}
.veil-enter:hover {
  border-color: rgba(255, 176, 102, 0.7);
  background: rgba(255, 176, 102, 0.08);
  letter-spacing: 0.62em;
  text-indent: 0.62em;
}

.veil-note {
  margin: 2.2rem 0 0;
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  color: var(--paper-faint);
  animation: riseIn 1.4s 0.95s var(--ease) both;
}

@keyframes markIn {
  from { opacity: 0; transform: translateY(14px) scale(0.94); filter: blur(6px); }
  to { opacity: 0.92; transform: none; filter: none; }
}
@keyframes riseIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   Hint + idle
   -------------------------------------------------------------------------- */

.hint {
  position: fixed;
  left: 50%;
  bottom: 3.2rem;
  transform: translateX(-50%);
  font-size: 0.72rem;
  letter-spacing: 0.24em;
  color: rgba(244, 236, 224, 0.5);
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.6);
  opacity: 0;
  transition: opacity 1.6s var(--ease);
  pointer-events: none;
  white-space: nowrap;
}
.hint.is-visible { opacity: 1; }

/* When you stop touching anything, the chrome leaves entirely. */
#ui.is-idle .tab { opacity: 0; transform: translate(14px, -50%); pointer-events: none; }

/* --------------------------------------------------------------------------
   Tab
   -------------------------------------------------------------------------- */

.tab {
  position: fixed;
  right: 0;
  top: 50%;
  /* Above the walk prompt: while explore mode is paused that prompt covers the
     viewport, and the almanac has to stay reachable through it. */
  z-index: 9;
  transform: translateY(-50%);
  padding: 1.15rem 0.62rem 1.15rem 0.7rem;
  background: rgba(var(--ink), 0.74);
  border: 1px solid var(--hair);
  border-right: none;
  border-radius: 14px 0 0 14px;
  backdrop-filter: blur(16px) saturate(1.2);
  -webkit-backdrop-filter: blur(16px) saturate(1.2);
  cursor: pointer;
  transition: opacity 0.8s var(--ease), transform 0.6s var(--ease),
              background 0.5s var(--ease), border-color 0.5s var(--ease);
}
.tab::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--sky-deep);
  opacity: 0.45;
  z-index: -1;
}
.tab:hover { background: rgba(var(--ink), 0.66); border-color: rgba(255, 176, 102, 0.4); }
.tab.is-hidden { opacity: 0; transform: translate(30px, -50%); pointer-events: none; }

.tab-glyph {
  display: block;
  font-family: var(--display);
  font-size: 1.05rem;
  font-weight: 300;
  color: var(--paper);
  writing-mode: vertical-rl;
  letter-spacing: 0.2em;
}

/* --------------------------------------------------------------------------
   Card
   -------------------------------------------------------------------------- */

.card {
  position: fixed;
  right: 1.4rem;
  top: 50%;
  z-index: 9;              /* see .tab — the paused walk prompt must not bury it */
  transform: translate(calc(100% + 2.5rem), -50%);
  width: min(23.5rem, calc(100vw - 2.8rem));
  display: flex;
  border-radius: 22px;
  border: 1px solid var(--hair);
  /* Solid enough to stay legible over a bright noon scene, since backdrop
     blur is a progressive enhancement and not every browser will apply it. */
  background: rgba(var(--ink), 0.86);
  backdrop-filter: blur(26px) saturate(1.25);
  -webkit-backdrop-filter: blur(26px) saturate(1.25);
  box-shadow:
    0 30px 80px -28px rgba(0, 0, 0, 0.85),
    inset 0 1px 0 rgba(255, 255, 255, 0.07);
  opacity: 0;
  overflow: hidden;
  transition: transform 0.72s var(--ease), opacity 0.55s var(--ease);
}
.card::before {
  /* The scene's own sky, washed under the panel. */
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(200deg, var(--sky), var(--sky-deep) 62%);
  opacity: 0.28;
  z-index: -1;
}
.card.is-open { transform: translate(0, -50%); opacity: 1; }

.card-close {
  position: absolute;
  top: 0.7rem;
  right: 0.7rem;
  z-index: 3;
  width: 2rem;
  height: 2rem;
  display: grid;
  place-items: center;
  padding: 0;
  color: var(--paper-faint);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--hair);
  border-radius: 50%;
  cursor: pointer;
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease), background 0.3s var(--ease);
}
.card-close:hover {
  color: var(--paper);
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 176, 102, 0.5);
}
.card-close svg { width: 13px; height: 13px; }

/* Spine: the season, set vertically, is the card's title. */
.spine {
  flex: 0 0 3.1rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
  padding: 1.5rem 0 1.4rem;
  border-right: 1px solid var(--hair);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent);
}
.spine-glyph {
  font-family: var(--display);
  font-weight: 300;
  font-size: 1.85rem;
  line-height: 1;
  color: var(--paper);
  text-shadow: 0 0 26px rgba(255, 176, 102, calc(0.15 + var(--night) * 0.4));
}
/* A hairline running down the spine, fading out — the only ornament. */
.spine-rule {
  width: 1px;
  flex: 1 1 auto;
  min-height: 2rem;
  background: linear-gradient(180deg, var(--hair), transparent);
}

.card-body {
  flex: 1 1 auto;
  min-width: 0;
  padding: 1.15rem 1.25rem 1rem;
}

/* --------------------------------------------------------------------------
   Dial — the signature control
   -------------------------------------------------------------------------- */

.dial-wrap {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-bottom: 0.9rem;
}

.dial {
  width: 11.2rem;
  height: 11.2rem;
  cursor: pointer;
  touch-action: none;
  overflow: visible;
}
.dial:focus-visible { outline: 2px solid var(--glow); outline-offset: 6px; border-radius: 50%; }

.seg {
  fill: none;
  stroke-width: 7;
  stroke-linecap: butt;
  opacity: 0.9;
}
.tick { stroke: rgba(244, 236, 224, 0.4); stroke-width: 1; }

.marker-halo { fill: var(--glow); opacity: 0.28; }
.marker-dot { fill: #fff; stroke: rgba(0, 0, 0, 0.35); stroke-width: 1; }
.marker { filter: drop-shadow(0 0 8px var(--glow)); }

.dial-time {
  font-family: var(--display);
  font-size: 15px;
  font-weight: 300;
  letter-spacing: 0.1em;
  fill: var(--paper);
  font-variant-numeric: tabular-nums;
}

.auto-btn {
  margin-top: 0.55rem;
  padding: 0.24rem 0.9rem;
  font-family: var(--ui);
  font-size: 0.68rem;
  letter-spacing: 0.18em;
  color: var(--paper-faint);
  background: transparent;
  border: 1px solid var(--hair);
  border-radius: 999px;
  cursor: pointer;
  transition: color 0.4s var(--ease), border-color 0.4s var(--ease), background 0.4s var(--ease);
}
.auto-btn.is-on {
  color: #1a1410;
  background: var(--glow);
  border-color: transparent;
}

/* --------------------------------------------------------------------------
   Rows
   -------------------------------------------------------------------------- */

.row {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  padding: 0.52rem 0;
  border-top: 1px solid var(--hair);
}

.row-label {
  flex: 0 0 2.7rem;
  padding-top: 0.34rem;
  font-family: var(--display);
  font-size: 0.74rem;
  font-weight: 300;
  letter-spacing: 0.22em;
  color: var(--paper-faint);
}

.row-opts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.34rem;
  flex: 1 1 auto;
}

.chip {
  padding: 0.34rem 0.72rem;
  font-family: var(--ui);
  font-size: 0.76rem;
  letter-spacing: 0.06em;
  color: var(--paper-dim);
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid transparent;
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.35s var(--ease), background 0.35s var(--ease),
              border-color 0.35s var(--ease), box-shadow 0.35s var(--ease);
}
.chip:hover { color: var(--paper); background: rgba(255, 255, 255, 0.1); }
.chip.is-on {
  color: var(--paper);
  background: rgba(255, 255, 255, 0.06);
  border-color: var(--glow);
  box-shadow: 0 0 0 1px rgba(255, 176, 102, 0.12), 0 0 18px -4px var(--glow);
}

.row-tight .chip { padding: 0.34rem 0.62rem; }
.row-tight.row .row-opts { gap: 0.3rem; }

/* --------------------------------------------------------------------------
   Sound
   -------------------------------------------------------------------------- */

.row-sound { align-items: center; }
.sound-box { flex: 1 1 auto; display: flex; flex-direction: column; gap: 0.4rem; }

.slider {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.slider-label {
  flex: 0 0 2.4rem;
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  color: var(--paper-faint);
}

input[type="range"] {
  flex: 1 1 auto;
  -webkit-appearance: none;
  appearance: none;
  height: 2px;
  background: rgba(244, 236, 224, 0.2);
  border-radius: 2px;
  cursor: pointer;
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--paper);
  box-shadow: 0 0 10px -2px var(--glow);
}
input[type="range"]::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border: none;
  border-radius: 50%;
  background: var(--paper);
}
input[type="range"]:focus-visible { outline: 2px solid var(--glow); outline-offset: 4px; }

.mute-btn { align-self: flex-start; margin-top: 0.1rem; }

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

.card-foot {
  display: flex;
  gap: 0.4rem;
  padding-top: 0.75rem;
  margin-top: 0.4rem;
  border-top: 1px solid var(--hair);
}
.foot-btn {
  flex: 1 1 auto;
  padding: 0.42rem 0.5rem;
  font-family: var(--ui);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-align: center;
  text-decoration: none;
  color: var(--paper-faint);
  background: transparent;
  border: 1px solid var(--hair);
  border-radius: 10px;
  cursor: pointer;
  transition: color 0.35s var(--ease), border-color 0.35s var(--ease);
}
.foot-btn:hover { color: var(--paper); border-color: rgba(255, 176, 102, 0.45); }

button:focus-visible, a:focus-visible {
  outline: 2px solid var(--glow);
  outline-offset: 3px;
}

/* --------------------------------------------------------------------------
   Small screens — the card becomes a sheet
   -------------------------------------------------------------------------- */

@media (max-width: 640px) {
  .tab {
    top: auto;
    bottom: calc(1.1rem + env(safe-area-inset-bottom, 0px));
    right: 1.1rem;
    transform: none;
    border-radius: 14px;
    border-right: 1px solid var(--hair);
    padding: 0.7rem 0.8rem;
  }
  .tab-glyph { writing-mode: horizontal-tb; font-size: 1rem; }
  #ui.is-idle .tab { transform: translateY(14px); }
  .tab.is-hidden { transform: translateY(30px); }

  .card {
    right: 0;
    left: 0;
    top: auto;
    bottom: 0;
    width: 100%;
    max-height: 86vh;
    overflow-y: auto;
    flex-direction: column;
    border-radius: 22px 22px 0 0;
    border-bottom: none;
    transform: translateY(100%);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .card.is-open { transform: translateY(0); }

  .spine {
    flex: 0 0 auto;
    flex-direction: row;
    align-items: center;
    gap: 1rem;
    /* Right padding keeps the hairline clear of the close button. */
    padding: 0.9rem 3.4rem 0.7rem 1.25rem;
    border-right: none;
    border-bottom: 1px solid var(--hair);
  }
  /* The rule runs across instead of down. Without resetting the width it
     inherits `flex: 1 1 auto` from the vertical layout and inflates into a
     block. */
  .spine-rule {
    width: auto;
    height: 1px;
    min-height: 0;
    background: linear-gradient(90deg, var(--hair), transparent);
  }

  .dial { width: 9.6rem; height: 9.6rem; }
  .hint { bottom: 5.5rem; font-size: 0.66rem; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ── 家園 HUD ───────────────────────────────────────────────────────────────
   Game mode's entire interface. There is no build menu and no upgrade list on
   purpose — you touch the valley itself — so all this has to do is say what you
   have, show what a tap was worth, and stay out of the way. It sits at the top
   because the bottom of a phone screen is where the thumb needs to drag. */

.ghud {
  position: fixed;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity .45s ease, visibility .45s;
  z-index: 6;
}

/* This one selector has to out-specify `#ui > * { pointer-events: auto }`
   above, and nothing else should — the layer covers the whole viewport, so if
   it accepts pointer events it swallows every tap before the canvas and
   nothing in the valley can be touched at all. Keep it to pointer-events:
   folding the rest of the block in here would beat `.ghud.is-on` on
   specificity and the HUD would never become visible. */
#ui > .ghud { pointer-events: none; }

.ghud.is-on { opacity: 1; visibility: visible; }

/* Unlike the almanac, this never fades on idle: it is the game's state, and a
   state readout that disappears while you are looking at it is a bug. */
#ui.is-idle .ghud.is-on { opacity: 1; }

.ghud-bar {
  position: absolute;
  top: calc(env(safe-area-inset-top, 0px) + .7rem);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: .35rem;
  padding: 0 .6rem;
  max-width: 100%;
  flex-wrap: wrap;
  justify-content: center;
}

.ghud-chip {
  --fill: 0%;
  position: relative;
  display: flex;
  align-items: baseline;
  gap: .3rem;
  padding: .3rem .6rem;
  border-radius: 999px;
  font-size: .82rem;
  line-height: 1;
  color: #f4ece0;
  background: rgba(18, 15, 12, .52);
  border: 1px solid rgba(244, 236, 224, .16);
  backdrop-filter: blur(10px) saturate(1.1);
  -webkit-backdrop-filter: blur(10px) saturate(1.1);
  overflow: hidden;
}

/* How full the store is, as a quiet wash behind the number. */
.ghud-chip::before {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--fill);
  background: rgba(244, 236, 224, .13);
  transition: width .35s ease;
}

.ghud-chip.is-full { border-color: rgba(255, 154, 68, .6); }
.ghud-chip.is-hidden { display: none; }

.ghud-k { position: relative; opacity: .62; font-size: .74rem; }
.ghud-v { position: relative; font-variant-numeric: tabular-nums; font-weight: 500; }

.ghud-people { border-color: rgba(244, 236, 224, .3); }

.ghud-line {
  position: absolute;
  left: 50%;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 4.4rem);
  transform: translate(-50%, .4rem);
  max-width: min(90vw, 34rem);
  padding: .42rem .9rem;
  border-radius: 999px;
  font-size: .84rem;
  text-align: center;
  color: #f4ece0;
  background: rgba(18, 15, 12, .58);
  border: 1px solid rgba(244, 236, 224, .14);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  opacity: 0;
  transition: opacity .3s ease, transform .3s ease;
}

.ghud-line.is-on { opacity: 1; transform: translate(-50%, 0); }

.ghud-floats { position: absolute; inset: 0; overflow: hidden; }

/* The number that flies out of a tap. This is the game's main feedback, so it
   is large, brief, and unmissable. */
.ghud-float {
  position: absolute;
  transform: translate(-50%, -50%);
  font-size: 1.02rem;
  font-weight: 500;
  color: #fff3df;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .75), 0 0 14px rgba(255, 190, 110, .5);
  white-space: nowrap;
  animation: ghud-rise 1.1s cubic-bezier(.2, .7, .3, 1) forwards;
}

.ghud-float.is-warm { color: #ffcf9a; font-size: 1.2rem; }

@keyframes ghud-rise {
  0%   { opacity: 0; transform: translate(-50%, -30%) scale(.85); }
  18%  { opacity: 1; transform: translate(-50%, -60%) scale(1.06); }
  100% { opacity: 0; transform: translate(-50%, -190%) scale(1); }
}

@media (max-width: 640px) {
  .ghud-chip { font-size: .78rem; padding: .28rem .5rem; }
  .ghud-line { bottom: calc(env(safe-area-inset-bottom, 0px) + 5.2rem); font-size: .8rem; }
}

@media (prefers-reduced-motion: reduce) {
  .ghud-float { animation-duration: .01ms; }
}

/* ── Walking ────────────────────────────────────────────────────────────────
   Explore mode's only chrome. Pointer lock has to be requested from a click,
   so the prompt is unavoidable — it doubles as the pause screen, since Esc is
   how the browser hands the mouse back and there is nothing we can do about
   that except make it look deliberate. */

.walk-prompt {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(11, 10, 9, .55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: #f4ece0;
  text-align: center;
  padding: 2rem;
  opacity: 0;
  visibility: hidden;
  transition: opacity .3s ease, visibility .3s;
  cursor: pointer;
  z-index: 8;
}

.walk-prompt.is-on { opacity: 1; visibility: visible; }

.walk-prompt-title {
  font-family: 'Noto Serif TC', serif;
  font-weight: 300;
  font-size: clamp(1.4rem, 4vw, 2.1rem);
  letter-spacing: .1em;
  margin-bottom: 1rem;
}

.walk-prompt-keys {
  font-size: .84rem;
  opacity: .68;
  line-height: 1.9;
  max-width: 34rem;
}

/* The way out. Clicking anywhere else on the pause screen resumes walking, so
   leaving needs a target of its own. */
.walk-prompt-leave {
  margin-top: 1.6rem;
  padding: .62rem 1.5rem;
  font: inherit;
  font-size: .86rem;
  letter-spacing: .12em;
  color: #f4ece0;
  background: rgba(var(--ink), .5);
  border: 1px solid var(--hair);
  border-radius: 999px;
  cursor: pointer;
  transition: background .2s var(--ease), border-color .2s var(--ease);
}

.walk-prompt-leave:hover {
  background: rgba(var(--ink), .78);
  border-color: rgba(255, 176, 102, .4);
}

.walk-prompt-esc { margin-top: .7rem; opacity: .46; }

/* A crosshair you can barely see. Anything stronger turns a walk into a
   shooter, which is emphatically not the register here. */
.crosshair {
  position: fixed;
  left: 50%;
  top: 50%;
  width: 5px;
  height: 5px;
  margin: -2.5px 0 0 -2.5px;
  border-radius: 50%;
  background: rgba(244, 236, 224, .42);
  box-shadow: 0 0 4px rgba(0, 0, 0, .5);
  opacity: 0;
  transition: opacity .3s ease;
  pointer-events: none;
  z-index: 7;
}

.crosshair.is-on { opacity: 1; }

/* While the pointer is locked the almanac and its tab are in the way of a
   view you are supposed to be inside of. */
#ui.is-walking .tab,
#ui.is-walking .card,
#ui.is-walking .hint { opacity: 0; pointer-events: none; }
