/* =====================================================================
   The dialogue box — js/dialogue.js

   One RPG box for the whole OS: a portrait, a nameplate, a line that types
   itself, an advance cue and a menu. Everything here is chrome; nothing in it
   knows who is speaking.

   It is drawn as furniture rather than as spectacle, so it takes the OS's own
   language and adds nothing: 3px black borders, a hard offset shadow, flat
   fills, VT323 throughout including every numeral, and an inverted
   black-on-white selected row. No radius, no gradient, no growth on hover —
   the selected state moves colour, never size.

   THREE SKINS, ONE BOX. The base is the ordinary desktop; the two theme blocks
   at the foot of the file are the same box in each theme's palette, scoped the
   way every other themed surface here is. That matters for one beat in
   particular: the father's last conversation begins under the Devil theme and
   the theme comes off partway through it, so the box has to repaint in the same
   frame as the desktop rather than be stranded in a palette nothing else is
   wearing any more.

   ⚠️ THE MENU IS role="button" SPANS, NOT <button>. css/devil.css and
   css/matrix.css both restyle `button` globally with `!important` and would
   drop a gradient slab over every row. That is the same reason the welcome
   card's controls are spans; see architecture.md. js/dialogue.js gives them
   Enter, Space, the arrow keys and the number row by hand.
   ===================================================================== */

.rpg-box {
  display: flex;
  align-items: flex-end;
  gap: 14px;
  opacity: 0;
  transition: opacity 0.35s ease;
  cursor: pointer;
  font-family: "VT323", monospace;
}

.rpg-box.is-visible {
  opacity: 1;
}

/* Handed the screen to something else. Off rather than dimmed, because the
   thing it hands over to is translucent and the box would show through it. */
.rpg-box.is-paused {
  display: none;
}

/* Pinned to the foot of the viewport, which is where the game this borrows from
   put it. Above body.devil-theme::after at 99998, because left under those
   scanlines the nameplate and the line both go murky; below .purge-destroyed at
   100100, which is the only surface in the OS that outranks everything. */
.rpg-box.is-overlay {
  position: fixed;
  right: 0;
  bottom: 4vh;
  left: 0;
  z-index: 100000;
  width: min(1080px, calc(100% - 8vw));
  margin-inline: auto;
}

/* Laid into whatever built it — the fight's own overlay, or one day a window. */
.rpg-box.is-inline {
  position: relative;
  width: 100%;
}

/* --- The portrait ---
   A framed square beside the panel rather than inside it, so a long line never
   has to flow around a face. It keeps its frame even with no picture in it,
   because somebody is still standing there. */
.rpg-box-portrait {
  position: relative;
  display: none;
  flex: 0 0 auto;
  align-items: flex-end;
  justify-content: center;
  width: 96px;
  height: 96px;
  border: 3px solid #000;
  background: #e0e0e0;
  box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.9);
  overflow: hidden;
}

.rpg-box.has-portrait .rpg-box-portrait {
  display: flex;
}

.rpg-box-face {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center bottom;
  /* The art is drawn rather than pixel-gridded, so it is left to the browser's
     smooth filter. Do not add image-rendering: pixelated here — see the welcome
     banner note in architecture.md for why a downscale only gains aliasing. */
}

/* The fallback, and it is load-bearing rather than defensive: the father's
   pictures are not in the repo yet, and a broken-image glyph in the middle of
   the only conversation he and his son ever have would be worse than no
   portrait at all. A pale cursor sitting where somebody stopped typing is the
   same device #archive-journal .journal-page.is-unfinished uses on his last
   page, and it says exactly the true thing — there is something there and it
   has no picture. */
.rpg-box-blank {
  display: none;
  width: 22px;
  height: 34px;
  margin-bottom: 20px;
  background: #333;
}

.rpg-box-portrait.is-blank .rpg-box-face {
  display: none;
}

.rpg-box-portrait.is-blank .rpg-box-blank {
  display: block;
  animation: rpgBoxCursor 1.1s steps(2, end) infinite;
}

@keyframes rpgBoxCursor {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.18;
  }
}

/* --- The panel --- */

.rpg-box-panel {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 96px;
  padding: 14px 20px 24px;
  border: 3px solid #000;
  background: #f0f0f0;
  box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.9);
}

/* The title bar's own grey and its own white, because a nameplate and a window
   title are the same object in this OS: a label the chrome owns. */
.rpg-box-name {
  display: none;
  margin-bottom: 7px;
  padding: 1px 9px 3px;
  background: #333333;
  color: #fff;
  font-size: 20px;
  line-height: 1.1;
  letter-spacing: 2px;
}

.rpg-box.has-name .rpg-box-name {
  display: inline-block;
}

.rpg-box-line {
  margin: 0;
  color: #111;
  font-size: 26px;
  line-height: 1.3;
  letter-spacing: 1px;
  white-space: pre-wrap;
  /* Two clear lines before the box grows, so an exchange of short lines does
     not make the panel jump on every press. */
  min-height: 2.6em;
}

/* Bottom-right, exactly where the game this borrows from put it, and hidden
   while a line is still typing — which is what makes it mean "your turn"
   rather than "wait". */
.rpg-box-cue {
  position: absolute;
  right: 12px;
  bottom: 6px;
  font-size: 20px;
  line-height: 1;
  color: #111;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.rpg-box-cue.is-shown {
  opacity: 1;
}

/* --- The close control ---
   Drawn only for a scene js/dialogue.js was told may be left. The span carries
   the OS's own `ctrl ctrl-close`, so this is literally the control every window
   title bar has — same 18px circle, same black ring, same behaviour under both
   desktop themes. Do not restate .ctrl's declarations here; only where it sits
   belongs in this file, exactly as #welcome-card .welcome-dismiss does it.

   It is opposite the nameplate, which turns the top of the panel into the row a
   title bar would be: a label at one end and the way out at the other. And it
   is the whole exit on a phone, where there is no Escape key at all — the
   conversation it exists for loops, so without it a touch visitor's only way
   out is reopening the Calculator and closing it. */
.rpg-box-dismiss {
  display: none;
  position: absolute;
  top: 10px;
  right: 10px;
}

.rpg-box.has-dismiss .rpg-box-dismiss {
  display: flex;
}

/* 18px is a small target for the only way out of a looping conversation. The
   circle stays 18px so it still reads as the OS's button; only the hit area
   grows, to 40px, which is the same trade the welcome card's close makes. */
.rpg-box-dismiss::before {
  content: "";
  position: absolute;
  inset: -11px;
}

.rpg-box-dismiss:focus-visible {
  outline: 2px solid #000;
  outline-offset: 3px;
}

/* The control overhangs the panel's own right padding by about twelve pixels on
   a desktop and seventeen on a phone, so the line gives that back rather than
   running under it. Deliberately the line and not the panel: the nameplate is
   short and left-aligned, and the menu sits well below the circle — reserving
   the width on the panel would take it off the menu rows too, which are the
   longest thing the box ever draws and the one place a phone has none to
   spare. */
.rpg-box.has-dismiss .rpg-box-line {
  padding-right: 24px;
}

/* A column header for the menu, not a line of dialogue: it is the chrome
   labelling its own numbers, which is the register the collapse's own bands
   already use. */
.rpg-box-hint {
  display: none;
  margin: 8px 0 -2px;
  color: #111;
  font-size: 16px;
  letter-spacing: 2px;
  opacity: 0.6;
}

.rpg-box.has-hint .rpg-box-hint {
  display: block;
}

/* --- The menu ---
   Two columns on a desktop, which fits a four-option row in the height a
   two-option one already needed, and one column below 600px. */
.rpg-box-choices {
  display: none;
  grid-template-columns: 1fr 1fr;
  gap: 2px 18px;
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
}

.rpg-box.has-choices .rpg-box-choices {
  display: grid;
}

/* A line and a menu at once is allowed and is what a question looks like; a
   menu on its own is what a turn looks like, and it should not leave a gap
   where the line would have been. */
.rpg-box.has-choices .rpg-box-line:empty {
  min-height: 0;
}

.rpg-box-choice-control {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 2px 8px 4px;
  color: #111;
  font-size: 21px;
  line-height: 1.2;
  outline: none;
}

/* The caret is drawn at all times and only lit when the row is chosen, so
   moving the selection never shifts a label sideways. */
.rpg-box-choice-control::before {
  content: "\25B6";
  flex: 0 0 auto;
  font-size: 15px;
  opacity: 0;
}

.rpg-box-choice.is-selected .rpg-box-choice-control::before {
  opacity: 1;
}

.rpg-box-choice-label {
  flex: 1 1 auto;
  min-width: 0;
}

/* Numerals, so VT323 and nothing else. Never Pixelify Sans. */
.rpg-box-choice-meta {
  flex: 0 0 auto;
  font-size: 18px;
  letter-spacing: 1px;
  opacity: 0.75;
}

.rpg-box-choice-note {
  flex: 0 0 auto;
  font-size: 16px;
  opacity: 0.6;
}

/* The OS's own selected state and no other. */
.rpg-box-choice.is-selected .rpg-box-choice-control {
  background: #000;
  color: #fff;
}

.rpg-box-choice.is-disabled .rpg-box-choice-control {
  opacity: 0.42;
  cursor: default;
}

.rpg-box-choice.is-disabled.is-selected .rpg-box-choice-control {
  background: none;
  color: #111;
}

/* --- Devil theme ---
   The palette the arc's old bespoke battle overlay established, kept when that
   overlay was replaced by this box, so the fight and anything else spoken under
   the theme are visibly the same object as the surfaces around them. */
body.devil-theme .rpg-box-panel {
  border-color: #000;
  background: rgba(20, 5, 4, 0.96);
  box-shadow: 6px 6px 0 rgba(0, 0, 0, 0.9),
    inset 0 0 0 1px rgba(255, 90, 60, 0.22);
}

body.devil-theme .rpg-box-line {
  color: var(--devil-bone, #f2ded7);
}

body.devil-theme .rpg-box-name {
  background: none;
  padding: 0;
  color: var(--devil-red-hot, #ff5a3c);
  letter-spacing: 3px;
}

body.devil-theme .rpg-box-cue,
body.devil-theme .rpg-box-hint,
body.devil-theme .rpg-box-choice-control {
  color: var(--devil-bone, #f2ded7);
}

body.devil-theme .rpg-box-choice.is-selected .rpg-box-choice-control {
  background: var(--devil-bone, #f2ded7);
  color: #140504;
}

body.devil-theme .rpg-box-choice.is-disabled.is-selected .rpg-box-choice-control {
  background: none;
  color: var(--devil-bone, #f2ded7);
}

body.devil-theme .rpg-box-portrait {
  border-color: #c9c9c9;
  background: rgba(201, 201, 201, 0.07);
  box-shadow: inset 0 0 26px rgba(201, 201, 201, 0.12);
}

body.devil-theme .rpg-box-blank {
  background: #c9c9c9;
  box-shadow: 0 0 18px rgba(242, 222, 215, 0.7);
}

/* --- Matrix theme ---
   Reachable because `awaken` is the trust path's reward and the father's own
   console sits on the far side of it, so a visitor can perfectly well be
   talking to him with the desktop green. */
body.matrix-theme .rpg-box-panel {
  background: var(--matrix-panel-strong, rgba(4, 22, 8, 0.98));
  box-shadow: 6px 6px 0 rgba(0, 0, 0, 0.9),
    inset 0 0 0 1px rgba(180, 255, 159, 0.12);
}

body.matrix-theme .rpg-box-line {
  color: var(--matrix-green-bright, #b4ff9f);
}

body.matrix-theme .rpg-box-name {
  background: none;
  padding: 0;
  color: var(--matrix-green, #53ff6a);
  letter-spacing: 3px;
}

body.matrix-theme .rpg-box-cue,
body.matrix-theme .rpg-box-hint,
body.matrix-theme .rpg-box-choice-control {
  color: var(--matrix-green-bright, #b4ff9f);
}

body.matrix-theme .rpg-box-choice.is-selected .rpg-box-choice-control {
  background: var(--matrix-green-bright, #b4ff9f);
  color: #04140a;
}

body.matrix-theme .rpg-box-choice.is-disabled.is-selected .rpg-box-choice-control {
  background: none;
  color: var(--matrix-green-bright, #b4ff9f);
}

body.matrix-theme .rpg-box-portrait {
  border-color: var(--matrix-green-dark, #0f6d24);
  background: rgba(83, 255, 106, 0.07);
}

body.matrix-theme .rpg-box-blank {
  background: var(--matrix-green-bright, #b4ff9f);
}

/* --- Tablet --- */
@media (max-width: 768px) {
  .rpg-box-portrait {
    width: 76px;
    height: 76px;
  }

  .rpg-box-panel {
    min-height: 84px;
    padding: 12px 16px 22px;
  }

  .rpg-box-line {
    font-size: 23px;
  }

  .rpg-box-choice-control {
    font-size: 19px;
  }
}

/* --- Phone ---
   Measured at 320x568, which is where this OS has been bitten before: the
   Calculator's `=` key and the Terminal's prompt row both ended up behind the
   dock, and the Trash's pill row put the centre of a button off-screen and made
   a whole branch unreachable.

   A four-option menu is the same shape of hazard, so it reflows rather than
   shrinks. The row becomes a column — label on its own line, the numbers under
   it — because at this width the label and the numbers on one line leave the
   label about twelve characters, and the longest of the father's lines is
   twenty-eight.

   The arithmetic, at 320px: the box is calc(100% - 20px) = 300px; the panel
   loses 6px of border and 20px of padding, so a box with no portrait has 274px
   of content and one with a portrait has 274 - 48 - 12 = 214px. At 18px VT323,
   whose advance is about 0.455em, 274px is roughly 33 characters and 214px is
   roughly 26 — so a menu row fits on one line with the portrait off and wraps
   to two with it on. The fight turns the portrait off, because its two
   combatants are already drawn on the field above the box. */
@media (max-width: 600px) {
  .rpg-box {
    gap: 12px;
  }

  .rpg-box.is-overlay {
    bottom: 12px;
    width: calc(100% - 20px);
  }

  .rpg-box-portrait {
    width: 48px;
    height: 48px;
    border-width: 2px;
    box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.9);
  }

  .rpg-box-blank {
    width: 12px;
    height: 18px;
    margin-bottom: 9px;
  }

  .rpg-box-panel {
    min-height: 70px;
    padding: 10px 12px 20px;
    box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.9);
  }

  .rpg-box-name {
    margin-bottom: 4px;
    font-size: 17px;
    letter-spacing: 1px;
  }

  .rpg-box-line {
    font-size: 19px;
    letter-spacing: 0;
    min-height: 2.4em;
  }

  .rpg-box-cue {
    right: 8px;
    bottom: 4px;
    font-size: 17px;
  }

  .rpg-box-dismiss {
    top: 7px;
    right: 7px;
  }

  .rpg-box-hint {
    margin: 4px 0 -2px;
    font-size: 14px;
    letter-spacing: 1px;
  }

  .rpg-box-choices {
    grid-template-columns: 1fr;
    gap: 2px;
    margin-top: 6px;
  }

  /* A grid rather than a column flex, so the caret keeps a lane of its own at
     the left instead of taking a line above the label. */
  .rpg-box-choice-control {
    display: grid;
    grid-template-columns: 14px 1fr;
    align-items: baseline;
    gap: 0;
    padding: 2px 6px 3px;
    font-size: 18px;
  }

  .rpg-box-choice-control::before {
    grid-row: 1;
    grid-column: 1;
    font-size: 13px;
  }

  .rpg-box-choice-label,
  .rpg-box-choice-meta,
  .rpg-box-choice-note {
    grid-column: 2;
  }

  .rpg-box-choice-meta {
    font-size: 15px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .rpg-box,
  .rpg-box-cue {
    transition: none;
  }

  .rpg-box-portrait.is-blank .rpg-box-blank {
    animation: none;
  }
}
