/* The Wild Dust — interface styling.
 *
 * The palette is sampled from the volume 1 cover painting: burnt sand, violet
 * rock, dusk blue, and one saturated red that is only ever used for Sarisa's
 * scarf and for danger. Nothing else on screen is allowed to be that red.
 *
 * Thai text is rendered by the DOM rather than by canvas on purpose — Thai
 * stacked vowels and tone marks drift badly in canvas text layout.
 */

:root {
  --ink: #140c10;
  --ink-2: #201417;
  --ink-3: #2d1e22;
  --rock: #734f4e;
  --rock-dark: #34313e;
  --sand: #d38d62;
  --sand-light: #efccb5;
  --sand-pale: #fae7d2;
  --dusk: #425079;
  --violet: #a48d9a;
  --scarf: #d93a2b;
  --good: #8fae86;
  --warn: #e0a355;

  --font-body: 'Noto Sans Thai', 'IBM Plex Sans Thai', 'Sarabun', 'Leelawadee UI',
    system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-display: 'Noto Serif Thai', 'Sarabun', Georgia, 'Times New Roman', serif;

  --radius: 4px;
  --pad: clamp(0.75rem, 2vw, 1.5rem);
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  height: 100%;
  background: var(--ink);
  color: var(--sand-pale);
  font-family: var(--font-body);
  font-size: clamp(15px, 1.05vw + 12px, 18px);
  line-height: 1.65;
  -webkit-text-size-adjust: 100%;
}

#app {
  position: relative;
  height: 100%;
  overflow: hidden;
}

#stage {
  position: absolute;
  inset: 0;
  z-index: 0;
}

#stage canvas {
  display: block;
  width: 100% !important;
  height: 100% !important;
}

#ui {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  pointer-events: none;
}

#ui > * {
  pointer-events: auto;
}

/* ------------------------------------------------------------------ topbar */

/* A hidden attribute has to beat the layout rule below. */
#topbar[hidden],
#modal[hidden] {
  display: none;
}

#topbar {
  display: flex;
  align-items: center;
  gap: var(--pad);
  padding: 0.5rem var(--pad);
  background: linear-gradient(180deg, rgba(20, 12, 16, 0.94), rgba(20, 12, 16, 0.55));
  border-bottom: 1px solid rgba(239, 204, 181, 0.12);
  flex-wrap: wrap;
}

.crumb {
  display: flex;
  flex-direction: column;
  min-width: 8rem;
}

#crumb-chapter {
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--sand);
  opacity: 0.85;
}

#crumb-place {
  font-family: var(--font-display);
  font-size: 1rem;
  color: var(--sand-pale);
}

/* --------------------------------------------------------------------- hud */

#hud {
  display: flex;
  gap: 0.9rem;
  flex: 1 1 auto;
  flex-wrap: wrap;
  align-items: center;
}

.meter {
  min-width: 5.5rem;
  flex: 0 1 8rem;
}

.meter-head {
  display: flex;
  justify-content: space-between;
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--violet);
}

.meter-track {
  display: block;
  height: 5px;
  margin-top: 3px;
  background: rgba(239, 204, 181, 0.14);
  border-radius: 999px;
  overflow: hidden;
}

.meter-bar {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--sand);
  transition: width 400ms ease, background-color 400ms ease;
}

.meter[data-key='trust'] .meter-bar {
  background: var(--dusk);
}

/* Heat: the arrow says which way is bad, the red line says where bad starts,
 * and the bar warms as it climbs. Every other meter is one flat colour, so a
 * changing colour reads as "this one is different" before it reads as heat. */
.meter .worse {
  color: var(--warn);
  font-size: 0.85em;
  letter-spacing: 0;
}

.meter.inverted[data-tone='cool'] .meter-bar {
  background: var(--dusk);
}
.meter.inverted[data-tone='warm'] .meter-bar {
  background: var(--warn);
}
.meter.inverted[data-tone='hot'] .meter-bar {
  background: var(--scarf);
}

.meter-redline {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  background: repeating-linear-gradient(
    -45deg,
    rgba(217, 58, 43, 0.55) 0 3px,
    rgba(217, 58, 43, 0.18) 3px 6px
  );
  border-left: 1px solid var(--scarf);
  pointer-events: none;
}

.meter.inverted .meter-track {
  position: relative;
}

.meter.low .meter-bar {
  background: var(--scarf);
}
.meter.low .meter-head {
  color: var(--scarf);
}
.meter.inverted.low .worse {
  color: var(--scarf);
}

/* --------------------------------------------------------------- supplies */

/* Counts, not bars — these are things you spend, and the number is the point. */
.supplies {
  display: flex;
  gap: 0.15rem 0.85rem;
  flex-wrap: wrap;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--sand);
  white-space: nowrap;
}

.supply.out {
  color: var(--scarf);
}

/* ----------------------------------------------------------------- rations */

.rations {
  margin-top: 1rem;
}

.ration-row {
  display: flex;
  gap: 0;
  border: 1px solid rgba(239, 204, 181, 0.24);
  border-radius: var(--radius);
  overflow: hidden;
}

button.ration {
  flex: 1 1 0;
  font: inherit;
  font-size: 0.85rem;
  padding: 0.4rem 0.5rem;
  background: transparent;
  color: var(--sand-light);
  border: 0;
  cursor: pointer;
}

button.ration[aria-pressed='true'] {
  background: var(--sand);
  color: var(--ink);
}

/* -------------------------------------------------------------------- shop */

.shop {
  margin: 1.2rem 0 0.4rem;
  padding: 0.9rem 1rem;
  border: 1px solid rgba(239, 204, 181, 0.16);
  border-radius: var(--radius);
  background: rgba(66, 80, 121, 0.1);
}

.shop h3 {
  margin: 0 0 0.2rem;
  font-family: var(--font-display);
  font-size: 1.05rem;
}

.trades {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
  gap: 0.4rem;
  margin-bottom: 0.7rem;
}

button.trade {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.6rem;
  font: inherit;
  font-size: 0.88rem;
  text-align: left;
  padding: 0.5rem 0.7rem;
  color: var(--sand-pale);
  background: rgba(115, 79, 78, 0.16);
  border: 1px solid rgba(239, 204, 181, 0.16);
  border-radius: var(--radius);
  cursor: pointer;
}

button.trade:hover:not(:disabled) {
  border-color: var(--sand);
}

button.trade:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

button.trade .price {
  color: var(--warn);
  white-space: nowrap;
}

button.trade.owned .price {
  color: var(--good);
}

button.trade.upgrade {
  align-items: flex-start;
}

button.trade .lock {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.76rem;
  color: var(--violet);
}

/* ------------------------------------------------------------------ screen */

#screen {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--pad);
  display: flex;
  justify-content: center;
  outline: none;
}

.panel {
  position: relative;
  width: min(56rem, 100%);
  margin: auto 0;
  background: linear-gradient(160deg, rgba(32, 20, 23, 0.9), rgba(20, 12, 16, 0.86));
  border: 1px solid rgba(239, 204, 181, 0.14);
  border-radius: var(--radius);
  padding: clamp(1rem, 3vw, 2.2rem);
  backdrop-filter: blur(3px);
  animation: rise 320ms ease both;
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
}

@media@media (prefers-reduced-motion: reduce) {
  .panel {
    animation: none;
  }
  /* Drifting specks are exactly what this setting is asking us not to do. The
     field stays in the DOM but stops moving, at a fixed low opacity. */
  .mote {
    animation: none;
    opacity: 0.35 !important;
  }
  .meter-bar {
    transition: none;
  }
}

.eyebrow {
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--sand);
  margin: 0 0 0.4rem;
}

h1,
h2 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.25;
  margin: 0 0 0.6rem;
}

h1 {
  font-size: clamp(1.8rem, 5vw, 3rem);
}

h2 {
  font-size: clamp(1.2rem, 3vw, 1.7rem);
}

p {
  margin: 0 0 1rem;
  color: var(--sand-light);
}

.lede {
  font-size: 1.03rem;
  color: var(--sand-pale);
}

.dim {
  color: var(--violet);
  font-size: 0.85rem;
}

/* Station text that lands after a choice result — a recording, a broadcast.
   Set apart from the branch prose so it does not read as more of the same
   paragraph. */
.reveal {
  margin-top: 0.9rem;
  padding-left: 0.9rem;
  border-left: 2px solid var(--scarf);
  color: var(--sand-light);
}

/* ----------------------------------------------------------------- choices */

.choices {
  display: grid;
  gap: 0.6rem;
  margin-top: 1.2rem;
}

.choice {
  display: flex;
  gap: 0.85rem;
  align-items: flex-start;
  width: 100%;
  text-align: left;
  padding: 0.85rem 1rem;
  color: var(--sand-pale);
  background: rgba(115, 79, 78, 0.16);
  border: 1px solid rgba(239, 204, 181, 0.16);
  border-radius: var(--radius);
  font: inherit;
  cursor: pointer;
  transition: border-color 160ms ease, background-color 160ms ease, transform 120ms ease;
}

.choice:hover:not(:disabled),
.choice:focus-visible {
  border-color: var(--sand);
  background: rgba(211, 141, 98, 0.2);
  outline: none;
}

.choice:active:not(:disabled) {
  transform: translateY(1px);
}

.choice:disabled {
  opacity: 0.42;
  cursor: not-allowed;
}

.choice .key {
  flex: 0 0 1.4rem;
  font-variant-numeric: tabular-nums;
  color: var(--sand);
}

.choice .lock {
  display: block;
  font-size: 0.78rem;
  color: var(--warn);
  margin-top: 0.2rem;
}

/* ----------------------------------------------------------------- buttons */

.actions {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-top: 1.4rem;
}

button.primary,
button.ghost,
a.primary,
a.ghost {
  font: inherit;
  padding: 0.6rem 1.4rem;
  border-radius: var(--radius);
  cursor: pointer;
  text-decoration: none;
  display: inline-block;
  transition: background-color 160ms ease, border-color 160ms ease;
}

button.primary,
a.primary {
  background: var(--scarf);
  color: #fff;
  border: 1px solid var(--scarf);
}

button.primary:hover,
a.primary:hover {
  background: #c3301f;
}

button.ghost,
a.ghost {
  background: transparent;
  color: var(--sand-light);
  border: 1px solid rgba(239, 204, 181, 0.28);
}

button.ghost:hover,
a.ghost:hover {
  border-color: var(--sand);
  color: var(--sand-pale);
}

/* Always in the viewport's top right, not in the topbar. The topbar is hidden
   on the title screen and on every full-screen panel, and the way into the
   menu should not vanish with it. Fixed to the viewport rather than the panel
   so it sits in the same place on every screen. */
#menu-button {
  position: fixed;
  top: 0.6rem;
  right: 0.6rem;
  z-index: 6;
  padding: 0.3rem 0.7rem;
  font-size: 1.1rem;
  line-height: 1;
  background: rgba(20, 12, 16, 0.72);
  backdrop-filter: blur(2px);
}

/* Close is an × in the panel's own corner rather than a button in the stack:
   it is the way out, not one of the choices. */
.modal-close {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  padding: 0.1rem 0.55rem;
  font-size: 1.15rem;
  line-height: 1.4;
  border-color: transparent;
  background: transparent;
  color: var(--violet);
}

.modal-close:hover,
.modal-close:focus-visible {
  color: var(--sand-pale);
  border-color: rgba(239, 204, 181, 0.28);
}

/* The menu's choices stack at one width, the same as the title screen's, so
   the two screens read the same way. */
.title-actions,
.menu-actions {
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
}

.title-actions button,
.menu-actions button {
  width: min(18rem, 100%);
}

/* ------------------------------------------------------------------- title */

/* Two columns where there is width for them: the book on one side, everything
 * the player can act on down the other. The cover is 760x1079 — portrait,
 * because it is a book — and the viewport is landscape, so a single column
 * would either crop the painting or waste the width. Below 60rem it stacks,
 * cover first. */
.title-screen {
  text-align: center;
}

@media (min-width: 60rem) {
  .title-screen {
    display: grid;
    grid-template-columns: minmax(0, 27rem) minmax(0, 1fr);
    grid-template-areas:
      'splash body'
      'splash body';
    align-items: center;
    gap: 1.6rem;
    text-align: center;
  }
  .title-screen > .title-books {
    grid-area: splash;
    align-self: center;
    /* As a column it is the splash, not a footnote under the buttons: the rule
       and the top margin that separated it from them are wrong here. */
    margin: 0;
    padding-top: 0;
    border-top: 0;
  }
  /* Everything that is not the cover flows down the second column in source
     order — no per-child assignment, so adding a row to the title screen later
     does not mean editing this rule. */
  /* On a wide screen the covers stack down their column instead of sitting
     side by side, which lets each one be roughly half again as big. The column
     has the height to spend and the other column is what has to clear the
     fold. On narrow screens they stay side by side — see the base rule. */
  /* Side by side, in a column wide enough to make that worth doing. Stacking
     was tried and measured: two portraits down a column are bounded by the
     viewport's height, and every pixel of cover costs two of column, so the
     covers came out SMALLER than a wider row. Width is the axis with room. */
  .title-screen .book-row {
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: flex-start;
    gap: 0.7rem;
  }
  .title-screen .book {
    flex: 0 0 auto;
    min-width: 0;
    max-width: none;
  }
  .title-screen .book img {
    /* As big as the column affords. The splash is the product shot, so it takes
       the width the layout can spare rather than sitting at thumbnail size. */
    height: min(42vh, 20rem);
  }
  .title-screen > :not(.title-books) {
    grid-column: 2;
  }
  /* The 18rem cap below exists for the narrow, centered stack; in the second
     column it just leaves the buttons stranded short of the column's own
     width. Full width of the column here — menu-actions is a different,
     narrower panel and keeps its cap. */
  .title-screen .title-actions button {
    width: 100%;
  }
}

/* Dust around the lockup. The wrapper is what the motes are positioned
   against, and it is inline-block so it shrinks to the image rather than
   spanning the column — motes drifting through empty space either side of the
   title would read as a screen effect instead of as air around the words. */
.logo-stage {
  position: relative;
  display: inline-block;
  line-height: 0;
}

.dust-field {
  position: absolute;
  /* Slightly wider than the lockup, so motes drift in from outside it rather
     than appearing at its edge. */
  inset: -12% -8%;
  pointer-events: none;
  overflow: hidden;
}

.mote {
  position: absolute;
  display: block;
  border-radius: 50%;
  background: var(--sand-pale);
  box-shadow: 0 0 4px rgba(250, 231, 210, 0.55);
  animation: mote-drift var(--dur, 9s) linear infinite;
  will-change: transform, opacity;
}

/* Up and sideways, fading in and out at the ends so nothing pops. The drift is
   per-mote so the field does not move as one sheet. */
@keyframes mote-drift {
  0% {
    transform: translate3d(0, 14px, 0) scale(0.7);
    opacity: 0;
  }
  22% {
    opacity: 1;
  }
  78% {
    opacity: 1;
  }
  100% {
    transform: translate3d(var(--drift, 0), -30px, 0) scale(1.05);
    opacity: 0;
  }
}

/* The heading stays a normal heading: with `line-height: 0` on the h1, the alt
 * text — the whole reason it is there — collapsed into a strip over the tagline
 * whenever the image did not arrive. The halo-clawing negative margins describe
 * the image, so they live on the image. */
.title-screen .logo {
  margin: 0;
}

/* Sized against the viewport's height as well as its width. The lockup is the
 * tallest block in the second column, and on a short landscape window a
 * width-only rule pushes the buttons — the only things here a player can act
 * on — under the fold. 1.848 is the image's own aspect ratio, so the height cap
 * is expressed as the width that produces it. */
.title-screen .logo-art {
  display: block;
  width: min(30rem, 100%, 26vh * 1.848);
  height: auto;
  margin: -1.6rem auto -1.2rem;
}

/* Thai stacks vowels and tone marks above and below the consonant line;
 * letter-spacing and uppercasing both wreck it, so no Thai text gets either. */
:root[data-lang='th'] .eyebrow,
:root[data-lang='th'] .meter-head,
:root[data-lang='th'] #crumb-chapter,
:root[data-lang='th'] .board-table th {
  letter-spacing: normal;
  text-transform: none;
  font-size: 0.78rem;
}

:root[data-lang='th'] body {
  line-height: 1.9;
}

/* The two covers under the buttons. Thumbnail scale on purpose: the row has to
 * be legible without ever costing the "Begin the journey" button its place
 * above the fold, so the covers are sized off the viewport height (9vh ≈ 54px
 * on a 1024×600 screen) rather than off a fixed width. */
.title-books {
  margin-top: 1.4rem;
  padding-top: 1.1rem;
  border-top: 1px solid rgba(239, 204, 181, 0.14);
}

/* Deliberately not `.eyebrow`, and deliberately without letter-spacing or
 * uppercasing: this line and the captions under the covers are Thai in the
 * source language, and both of those wreck Thai stacked vowels and tone marks.
 * Nothing here needs the :root[data-lang='th'] override because nothing here
 * applies either in the first place. */
.books-heading {
  margin: 0 0 0.7rem;
  font-size: 0.78rem;
  color: var(--violet);
}

.book-row {
  display: flex;
  justify-content: center;
  gap: clamp(1rem, 4vw, 2.4rem);
  /* Side by side, and staying that way on a phone: wrapping turns the pair
     into a tall stack that pushes everything else down a small screen. The
     desktop rule opts into stacking deliberately, where there is height. */
  flex-wrap: nowrap;
}

.book-row > .book {
  min-width: 0;
}

.book {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  max-width: 14rem;
  text-decoration: none;
  color: var(--sand-light);
  border-radius: var(--radius);
  padding: 0.35rem;
  transition: transform 180ms ease, color 180ms ease;
}

.book:hover,
.book:focus-visible {
  color: var(--sand-pale);
  transform: translateY(-2px);
}

.book img {
  display: block;
  width: auto;
  max-width: 100%;
  height: clamp(4.5rem, 20vh, 11rem);
  object-fit: contain;
  border-radius: 2px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.55);
}

.book-copy {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.book-title {
  font-size: 0.8rem;
  line-height: 1.35;
}

.book-note {
  font-size: 0.72rem;
  color: var(--sand);
}

/* A short viewport — a 1024×600 laptop, a landscape phone — is where the title
 * screen runs out of room, and it ran out of room before the covers were added:
 * the language switch was already under the fold at 600px. So everything above
 * the covers gives up a little height here rather than the covers being the one
 * thing nobody scrolls to. Untouched on a normal desktop. */
@media (max-height: 700px) {
  .panel {
    padding: 1rem 1.2rem 0.7rem;
  }
  /* Four stacked buttons cost more height than four in a row. Tighten the
     column rather than the covers — the buttons are what has to clear the
     fold, so they are what pays for the space. */
  .title-actions {
    gap: 0.3rem;
    margin-top: 0.7rem;
  }
  .title-actions button {
    padding: 0.45rem 0.9rem;
  }
  /* The lockup gives up the last of the height. It is the biggest single block
     in this column and the one that costs nothing to read smaller. */
  .title-screen .logo-art {
    width: min(30rem, 100%, 20vh * 1.848);
  }
  .title-screen .logo {
    font-size: clamp(1.5rem, 4.5vw, 2.3rem);
    line-height: 1.2;
  }
  .title-screen .sub {
    font-size: 0.95rem;
    margin: 0.15rem 0 0.45rem;
  }
  .title-screen .lede,
  .title-screen .dim {
    margin: 0.1rem 0;
  }
  .title-screen .actions {
    margin: 0.7rem 0 0;
  }
  .title-books {
    margin-top: 0.4rem;
    padding-top: 0.4rem;
  }
  .books-heading {
    margin-bottom: 0.3rem;
  }
  /* A short window has no height to stack into: two portraits down a column
   * need more than 600px allows, so here they go back side by side. The wide
   * rule above stacks them for the desktops that can actually afford it. */
  .title-screen .book-row {
    flex-direction: row;
  }
  .title-screen .book img {
    height: clamp(4rem, 32vh, 10rem);
  }
  .book-title {
    font-size: 0.76rem;
  }
  .book-note {
    font-size: 0.7rem;
  }
}

.lang-switch {
  display: inline-flex;
  gap: 0;
  border: 1px solid rgba(239, 204, 181, 0.28);
  border-radius: var(--radius);
  overflow: hidden;
}

/* The screen's top left, mirroring the menu button in the top right. Fixed to
   the viewport rather than the panel, so the two settings sit in the corners
   at the same height whatever the panel is doing, and neither is in the
   reading order of the buttons a player actually came here to press. */
.lang-corner {
  position: fixed;
  top: 0.6rem;
  left: 0.6rem;
  z-index: 6;
  background: rgba(20, 12, 16, 0.72);
  backdrop-filter: blur(2px);
}

/* Both halves the same width, so the control does not change shape when the
   longer label is the selected one. */
.lang-corner button {
  flex: 1 1 0;
  min-width: 5.2rem;
  text-align: center;
}

.lang-switch button {
  font: inherit;
  padding: 0.35rem 0.9rem;
  background: transparent;
  color: var(--sand-light);
  border: 0;
  cursor: pointer;
}

.lang-switch button[aria-pressed='true'] {
  background: var(--sand);
  color: var(--ink);
}

/* ---------------------------------------------------------------- prologue */

.prologue-art {
  display: block;
  width: auto;
  max-width: min(20rem, 55%);
  max-height: 26vh;
  object-fit: contain;
  float: right;
  margin: 0 0 0.8rem 1.4rem;
  border-radius: 2px;
  filter: drop-shadow(0 8px 26px rgba(0, 0, 0, 0.55));
}

@media (max-width: 620px) {
  .prologue-art {
    float: none;
    margin: 0 auto 1rem;
    max-width: 70%;
  }
}

/* ------------------------------------------------------------------ travel */

/* The ride itself is on the canvas behind this; the panel is only a caption,
 * so it stays small and sits out of the way of the horizon. */
.ration-hint {
  min-height: 2.6em;
  margin: 0 0 0.5rem;
}

.travel-panel {
  width: min(32rem, 100%);
  margin-top: 6vh;
  margin-bottom: auto;
  text-align: center;
  background: linear-gradient(160deg, rgba(32, 20, 23, 0.72), rgba(20, 12, 16, 0.62));
}

.travel-panel .actions {
  justify-content: center;
}

.travel-track {
  display: block;
  height: 4px;
  margin: 1rem 0 0;
  background: rgba(239, 204, 181, 0.16);
  border-radius: 999px;
  overflow: hidden;
}

.travel-bar {
  display: block;
  width: 0;
  height: 100%;
  background: var(--scarf);
  border-radius: 999px;
}

/* ------------------------------------------------------------------ result */

.deltas {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0.8rem 0 0;
  padding: 0;
  list-style: none;
}

.deltas li {
  font-size: 0.8rem;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  border: 1px solid currentColor;
}

.deltas .up {
  color: var(--good);
}
.deltas .down {
  color: var(--scarf);
}
.deltas .item {
  color: var(--dusk);
  border-color: var(--dusk);
}

/* ------------------------------------------------------------------ morse */

/* Letters are separated by a gap, never by a slash: in Morse a slash (a
 * seven-unit gap) is the word break, and the answer here is a single word. */
.morse-line {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem 1.5rem;
  font-family: ui-monospace, 'SF Mono', 'Cascadia Mono', Menlo, monospace;
  font-size: clamp(1rem, 2.6vw, 1.5rem);
  letter-spacing: 0.16em;
  color: var(--warn);
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid rgba(224, 163, 85, 0.3);
  border-radius: var(--radius);
  padding: 0.9rem 1rem;
  margin: 1rem 0;
}

.morse-letter {
  white-space: nowrap;
}

/* Letters already read off the metal: solid, and clearly not the question. */
.morse-line .known {
  color: var(--sand-pale);
  letter-spacing: 0.05em;
}

/* The mark still to be decoded. */
.morse-line .unknown {
  color: var(--warn);
  padding: 0.1em 0.5em;
  border: 1px dashed rgba(224, 163, 85, 0.55);
  border-radius: var(--radius);
}

.morse-chart {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(5.2rem, 1fr));
  gap: 0.2rem 0.6rem;
  font-family: ui-monospace, monospace;
  font-size: 0.78rem;
  color: var(--violet);
  margin: 0.6rem 0 0;
  padding: 0;
  list-style: none;
}

input[type='text'] {
  font: inherit;
  padding: 0.55rem 0.8rem;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid rgba(239, 204, 181, 0.28);
  border-radius: var(--radius);
  color: var(--sand-pale);
  min-width: 12rem;
}

input[type='text']:focus {
  outline: none;
  border-color: var(--sand);
}

.feedback {
  min-height: 1.5rem;
  font-size: 0.9rem;
}
.feedback.ok {
  color: var(--good);
}
.feedback.bad {
  color: var(--scarf);
}

/* ------------------------------------------------------------------- chase */

.chase-overlay {
  position: absolute;
  inset: auto 0 0 0;
  padding: var(--pad);
  text-align: center;
  pointer-events: none;
}

.chase-hint {
  display: inline-block;
  background: rgba(20, 12, 16, 0.72);
  border-radius: var(--radius);
  padding: 0.4rem 0.9rem;
  font-size: 0.85rem;
  color: var(--sand-light);
}

/* #screen sits full-bleed over the canvas (it is a flex child of #ui, which
 * makes every direct child `pointer-events: auto`) and it is emptied rather
 * than removed while the chase runs, so without this an empty DOM box eats
 * every tap before it reaches Phaser and the three-lane tap-to-lane band goes
 * dead. `chase-active` opts #screen back out of hit-testing for the duration
 * of the chase; the arrow buttons opt themselves back in below. */
#screen.chase-active {
  pointer-events: none;
}

/* Fixed to the viewport, like #menu-button and .lang-corner above — not sized
 * or positioned against the lane band (roughly 0.42–0.74 of the height,
 * see ChaseScene.laneY) or the right edge hazards spawn from just off-screen,
 * so the buttons never sit over a lane or a hazard the player has to read.
 * Bottom-right: clear of both, and where a right-hand thumb already rests. */
.chase-controls {
  position: fixed;
  right: 0.9rem;
  bottom: max(0.9rem, env(safe-area-inset-bottom));
  z-index: 5;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  pointer-events: auto;
}

/* 56px, comfortably clear of the 44px floor — this is a reflex game, a missed
 * tap costs a hit, so the target is bigger than the minimum on purpose. */
.chase-arrow {
  width: 3.5rem;
  height: 3.5rem;
  min-width: 44px;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--sand-pale);
  background: rgba(20, 12, 16, 0.78);
  border: 1px solid rgba(239, 204, 181, 0.32);
  border-radius: 999px;
  cursor: pointer;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
}

.chase-arrow:active {
  background: rgba(211, 141, 98, 0.35);
  border-color: var(--sand);
}

/* --------------------------------------------------------------- ending/CTA */

.score-table {
  width: 100%;
  border-collapse: collapse;
  margin: 1rem 0;
  font-variant-numeric: tabular-nums;
}

.score-table td {
  padding: 0.3rem 0;
  border-bottom: 1px solid rgba(239, 204, 181, 0.1);
}

.score-table td:last-child {
  text-align: right;
}

.score-table tr.total td {
  border-bottom: 0;
  font-size: 1.2rem;
  color: var(--sand-pale);
  padding-top: 0.7rem;
}

.buy {
  display: flex;
  gap: 1.2rem;
  align-items: center;
  margin-top: 1.6rem;
  padding-top: 1.4rem;
  border-top: 1px solid rgba(239, 204, 181, 0.14);
  flex-wrap: wrap;
}

.buy img {
  width: 7.5rem;
  border-radius: 2px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
}

.buy .copy {
  flex: 1 1 16rem;
}

/* ------------------------------------------------------------- leaderboard */

.board-table {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
  font-size: 0.92rem;
}

.board-table th {
  text-align: left;
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--violet);
  padding-bottom: 0.4rem;
}

.board-table td {
  padding: 0.35rem 0.4rem;
  border-bottom: 1px solid rgba(239, 204, 181, 0.08);
}

.board-table td:last-child,
.board-table th:last-child {
  text-align: right;
}

.board-table tr.you td {
  color: var(--sand-pale);
  background: rgba(217, 58, 43, 0.12);
}

/* ------------------------------------------------------------------- modal */

#modal {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  place-items: center;
  padding: var(--pad);
  background: rgba(10, 6, 8, 0.78);
}

#modal[hidden] {
  display: none;
}

#modal .panel {
  width: min(30rem, 100%);
}

.credits-list {
  list-style: none;
  padding: 0;
  margin: 0 0 1rem;
  font-size: 0.92rem;
}

.credits-list li {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.25rem 0;
  border-bottom: 1px solid rgba(239, 204, 181, 0.08);
}

.credits-list span:first-child {
  color: var(--violet);
}

@media (max-width: 620px) {
  /* The in-run topbar (chapter/place crumb, five meters, five supply counts)
   * stacks into four or five wrapped rows on a phone, and the default
   * padding and gaps were sized for a topbar that stays one or two rows on
   * desktop. Every value below is scoped to #topbar's own children so the
   * rest of the page — in particular `:root[data-lang='th'] body`'s
   * line-height: 1.9, which exists so Thai tone marks and stacked vowels
   * don't collide — is untouched. 1.25 here is tight but still clears a
   * single line of Thai glyphs; it has not been tried against a line that
   * wraps, so crumb-place keeps enough width to stay on one line at 390px. */
  #topbar {
    gap: 0.3rem;
    padding: 0.3rem 0.6rem;
  }
  .crumb {
    min-width: 100%;
  }
  #crumb-chapter,
  #crumb-place {
    line-height: 1.25;
  }
  #hud {
    gap: 0.4rem;
  }
  .meter {
    flex: 1 1 5rem;
    min-width: 4.5rem;
  }
  .meter-head {
    line-height: 1.2;
  }
  .meter-track {
    margin-top: 2px;
  }
  .supplies {
    gap: 0.1rem 0.6rem;
  }
}
