/* Hellringer menu: a near-black world, iron panels, and vivid light that burns through the dark. */

:root {
  --void: #050405;
  --bone: #eadfcc;
  --parchment: #c9b28d;
  --ash: #8a7f70;
  --smoke: #5d554c;
  --gold: #b8925a;
  --ember: #ff5a1f;
  --hot: #ffb547;
  --white-hot: #fff1d6;
  /* The pointer is the game's own: a dark iron arrowhead with a parchment edge, its tip glowing over what you can use. */
  --cursor: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath d=%27M2 2 3.1 19.6 7.3 15.4 10.8 22.4 13.4 21.2 10 14.3 16.4 13.9Z%27 fill=%27%23120d0f%27 stroke=%27%23c9b28d%27 stroke-width=%271.2%27 stroke-linejoin=%27round%27/%3E%3Cpath d=%27M3.6 5 4.3 15.6%27 stroke=%27%23eadfcc%27 stroke-opacity=%27.35%27 stroke-width=%27.8%27 stroke-linecap=%27round%27/%3E%3Ccircle cx=%272.9%27 cy=%273%27 r=%270.1%27 fill=%27%238a7f70%27 fill-opacity=%27.45%27/%3E%3Ccircle cx=%272.7%27 cy=%272.8%27 r=%271.1%27 fill=%27%238a7f70%27/%3E%3C/svg%3E") 2 2, default;
  --cursor-hot: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath d=%27M2 2 3.1 19.6 7.3 15.4 10.8 22.4 13.4 21.2 10 14.3 16.4 13.9Z%27 fill=%27%23120d0f%27 stroke=%27%23ffcf8a%27 stroke-width=%271.2%27 stroke-linejoin=%27round%27/%3E%3Cpath d=%27M3.6 5 4.3 15.6%27 stroke=%27%23eadfcc%27 stroke-opacity=%27.35%27 stroke-width=%27.8%27 stroke-linecap=%27round%27/%3E%3Ccircle cx=%272.9%27 cy=%273%27 r=%272.4%27 fill=%27%23ff5a1f%27 fill-opacity=%27.45%27/%3E%3Ccircle cx=%272.7%27 cy=%272.8%27 r=%271.1%27 fill=%27%23ff5a1f%27/%3E%3C/svg%3E") 2 2, pointer;
  --toll: #ff1f4b;
  --spirit: #3ff0ff;
  --blood-moon: #b3180c;
  --ember-soft: rgba(255, 90, 31, 0.16);
  --blood: #7a1a10;
  --blood-deep: #3e0a06;
  --iron: rgba(14, 12, 12, 0.8);
  --line: rgba(201, 178, 141, 0.16);
  --serif: 'Cormorant Garamond', Georgia, serif;
  --caps: 'Cinzel', 'Trajan Pro', Georgia, serif;
  --display: 'Cinzel Decorative', 'Cinzel', Georgia, serif;
  --ease-out: cubic-bezier(0.16, 0.8, 0.2, 1);
  /* Where the bell hangs on screen: tolls radiate from here. */
  --tower-x: 64%;
  --tower-y: 25%;
  /* 0..1, set by main.js on every deep drum hit and decaying in between. */
  --pulse: 0;
  /* 0..1, the music's intensity, set by main.js: the edges of the screen run red as the song swells. */
  --mood: 0;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

/* Nothing on the menu can be dragged off like a web page's images and links. */
* {
  -webkit-user-drag: none;
}

/* A game screen, not a page: no text selection or text cursor, no long-press menu or tap flash, no overscroll and no
   pinch zoom (panning stays allowed, there is just nothing to pan). */
html,
body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
  background: var(--void);
  color: var(--bone);
  font-family: var(--serif);
  -webkit-font-smoothing: antialiased;
  cursor: var(--cursor);
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: pan-x pan-y;
}

/* Text fields stay selectable, so the caret and select all work while typing. */
:read-write {
  -webkit-user-select: text;
  user-select: text;
}

/* Every control draws its own focus in firelight (its :focus-visible rules); the browser's ring never shows. */
:focus {
  outline: none;
}

button::-moz-focus-inner {
  border: 0;
}

body.no-webgl {
  background: radial-gradient(ellipse at 64% 25%, #3a0d08 0%, #12070a 35%, var(--void) 70%);
}

::selection {
  background: var(--blood);
  color: var(--bone);
}

/* ---- Layers over the 3D world ------------------------------------------------------------------------------- */

#world {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.veil {
  position: fixed;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 85% 75% at 62% 42%, transparent 35%, rgba(0, 0, 0, 0.55) 78%, rgba(0, 0, 0, 0.92) 100%),
    linear-gradient(180deg, rgba(0, 0, 0, 0.35) 0%, transparent 20%, transparent 75%, rgba(0, 0, 0, 0.6) 100%);
}

/* Blood seeping in from the edges as the music swells (slowly, never on the beat). */
.veil::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 90% 80% at 62% 42%, transparent 50%, rgba(150, 10, 14, 0.22) 82%, rgba(179, 24, 12, 0.4) 100%);
  mix-blend-mode: screen;
  opacity: calc(0.15 + 0.6 * var(--mood));
}

.veil::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(3, 2, 3, 0.86) 0%, rgba(3, 2, 3, 0.55) 32%, transparent 58%);
  opacity: 0;
  transition: opacity 2s ease;
}

body:not([data-stage='splash']) .veil::after {
  opacity: 1;
}

/* Ember light pooling under the menu column; it swells on every drum hit. */
.underglow {
  position: fixed;
  left: -25vw;
  bottom: -35vh;
  width: 85vw;
  height: 90vh;
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: 0;
  transition: opacity 2.5s ease;
}

.underglow::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(closest-side, rgba(255, 90, 31, 0.24), rgba(255, 31, 75, 0.09) 55%, transparent);
  opacity: calc(0.3 + 0.3 * var(--mood) + 0.6 * var(--pulse));
}

body:not([data-stage='splash']) .underglow {
  opacity: 1;
}

.grain {
  position: fixed;
  inset: -50%;
  pointer-events: none;
  opacity: 0.08;
  mix-blend-mode: overlay;
  background-size: 160px 160px;
  animation: grain 0.9s steps(5) infinite;
}

@keyframes grain {
  0% { transform: translate(0, 0); }
  20% { transform: translate(-3%, 2%); }
  40% { transform: translate(2%, -4%); }
  60% { transform: translate(-4%, -1%); }
  80% { transform: translate(3%, 3%); }
  100% { transform: translate(0, 0); }
}

/* Full-screen light flashes, driven by main.js (short, rate limited, off with "Reduce flashes"). */
.flash {
  position: fixed;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  mix-blend-mode: screen;
  will-change: opacity;
}

/* Lightning: spirit cyan from the sky. */
.flash--sky {
  background: radial-gradient(ellipse 120% 85% at 58% -8%, rgba(225, 255, 255, 0.6) 0%, rgba(63, 240, 255, 0.5) 22%, rgba(63, 240, 255, 0.14) 58%, transparent 88%);
}

/* The toll: crimson light bursting from the bell. */
.flash--toll {
  background: radial-gradient(circle at var(--tower-x) var(--tower-y), rgba(255, 241, 214, 0.55) 0%, rgba(255, 31, 75, 0.8) 5%, rgba(255, 31, 75, 0.34) 24%, rgba(179, 24, 12, 0.16) 50%, transparent 82%);
}

/* Black over the world: the opening lifts it (main.js), lobby.js lowers it again on the way into a game. */
.curtain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  background: var(--void);
  opacity: 0;
}

/* Holds the SVG fire filters; it must render (display: none would switch the filters off) but take no space. */
.fx-defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* ---- Opening ------------------------------------------------------------------------------------------------ */
/* data-opening sits on the body from the first paint, so nothing half-loaded ever shows: the screen is pure black
   ('dark') until the fonts, the fire, the world and the music are in. main.js then shows the title alone on the black
   ('title'), lets "Hell" catch fire and glides it to its place while it lifts the curtain, and removes the attribute
   as the panel, the divider and the HUD come in (their entrances start only then). */
body[data-opening] .curtain {
  opacity: 1;
}

body[data-opening='dark'] .title,
body[data-opening] .divider {
  opacity: 0;
}

body[data-opening] #app .login,
body[data-opening] #app .login > *,
body[data-opening] .hud {
  animation: none;
}

body[data-opening] #app .login,
body[data-opening] .hud {
  opacity: 0;
  pointer-events: none;
}

/* The HUD follows the panel in once the opening is over, not on its own clock from the page load. */
body:not([data-opening]) .hud {
  animation-delay: 0.35s;
}

/* ---- Layout ------------------------------------------------------------------------------------------------- */

#app {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 clamp(20px, 8vw, 140px);
}

#app[data-stage='splash'] {
  align-items: center;
  text-align: center;
  cursor: var(--cursor-hot);
}

/* Past the splash the title and what stands under it start at a fixed height for the window (lower on a tall one,
   higher on a short one), never centred on what is shown: a taller view (Dig a grave, the gates, a lobby) only
   reaches further down, and nothing above it moves when you go from one to another. Never so high that the flames
   over "Hell" run off the top of the screen: at least three quarters of the title's size stays free above it. */
#app:not([data-stage='splash']) {
  --title-size: clamp(40px, min(5.6vw, 10.5vh), 92px);
  align-items: flex-start;
  justify-content: flex-start;
  padding-top: clamp(calc(var(--title-size) * 0.75), calc(50vh - 400px), 18vh);
  text-align: left;
}

.brand {
  transform-origin: left top;
}

#app[data-stage='splash'] .brand {
  transform-origin: center top;
  animation: rise 2.8s 0.9s var(--ease-out) both;
}

@keyframes rise {
  from {
    opacity: 0;
    filter: blur(14px);
    letter-spacing: 0.3em;
  }
}

/* The bell in the tower can be rung (main.js): over it the pointer turns hot. */
body.on-bell,
body.on-bell #app {
  cursor: var(--cursor-hot);
}

/* ---- Title ------------------------------------------------------------------------------------------------- */

/* The filter list must match TITLE_FILTER in main.js, which animates it when light hits the title. */
.title {
  margin: 0;
  font-family: var(--display);
  font-weight: 900;
  font-size: clamp(54px, 10.5vw, 176px);
  line-height: 0.92;
  letter-spacing: 0.035em;
  color: transparent;
  filter: brightness(1) drop-shadow(0 3px 0 rgba(0, 0, 0, 0.95)) drop-shadow(0 0 28px rgba(255, 60, 15, 0.28));
}

/* "ringer": title-fire.js paints it in WebGL beside "Hell", as forged and polished gold lit by the fire. The glyphs
   stay in the page for the layout and screen readers. Without WebGL (.no-fire) they are the gold themselves, with a
   glint that sweeps over them now and then; while the canvas is on its way they wait as dim, cold gold, the way the
   canvas starts, and once it shows they go transparent. */
.title__ringer {
  background:
    linear-gradient(105deg, transparent 40%, rgba(255, 90, 31, 0.55) 46%, rgba(255, 241, 214, 0.95) 50%, rgba(255, 90, 31, 0.55) 54%, transparent 60%) no-repeat,
    linear-gradient(180deg, #f6ead4 0%, #d6bf98 34%, #85694a 60%, #3a2a1c 100%);
  background-size: 300% 100%, 100% 100%;
  background-position: 160% 0, 0 0;
  -webkit-background-clip: text;
  background-clip: text;
  animation: glint 9s 4s ease-in-out infinite;
}

.title__hell:not(.is-lit):not(.no-fire) + .title__ringer {
  background: linear-gradient(180deg, #7d6d58 0%, #5c4c3a 45%, #261d16 100%);
  -webkit-background-clip: text;
  background-clip: text;
  animation: none;
}

.title__hell.is-lit + .title__ringer {
  background: none;
  animation: none;
}

#app:not([data-stage='splash']) .title {
  font-size: var(--title-size);
}

@keyframes glint {
  0%, 70% { background-position: 160% 0, 0 0; }
  100% { background-position: -60% 0, 0 0; }
}

/* "Hell" burns: title-fire.js paints it in WebGL as forged iron with lava in its cracks, real flames rising behind it
   and sparks drifting up, all swelling with the music. The glyphs stay in the page for the layout and screen readers;
   they show as cold iron until the fire starts, and molten (without the fire) when there is no WebGL. The same canvas
   paints "ringer" beside it in gold that catches the fire's light. */
.title__hell {
  position: relative;
  display: inline-block;
}

/* Cold iron until the fire takes over, so the word starts dark and catches fire. */
.hell__glyphs {
  color: transparent;
  background: linear-gradient(180deg, #2b211c 0%, #160e0b 100%);
  -webkit-background-clip: text;
  background-clip: text;
}

.title__hell.is-lit .hell__glyphs {
  background: none;
}

.title__hell.no-fire .hell__glyphs {
  background: linear-gradient(180deg, var(--white-hot) 0%, var(--hot) 32%, var(--ember) 64%, var(--blood-moon) 100%);
  -webkit-background-clip: text;
  background-clip: text;
}

/* The baseline markers (one at the end of each word) title-fire.js reads to line its letters up with the page's. */
.hell__base {
  display: inline-block;
  width: 0;
  height: 0;
}

/* Room for the flames above the word and a little spill to the sides; the fire fades out before the edges. To the
   right the canvas runs on over "ringer": title-fire.js sets --fire-reach to where that word ends, plus a little. */
.hell__fire {
  position: absolute;
  left: -0.5em;
  top: -1.6em;
  width: calc(100% + 0.5em + var(--fire-reach, 0.5em));
  height: calc(100% + 1.9em);
  pointer-events: none;
  visibility: hidden;
}

.title__hell.is-lit .hell__fire {
  visibility: visible;
}

.divider {
  display: flex;
  align-items: center;
  margin: 18px auto 12px;
  width: min(420px, 70vw);
  color: var(--gold);
}

#app:not([data-stage='splash']) .divider {
  margin-left: 0;
  width: min(300px, 70vw);
}

.divider__line {
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--gold) 50%, transparent);
  opacity: 0.6;
}

/* ---- Iron panel (login and settings) ------------------------------------------------------------------------ */

.panel {
  position: relative;
  width: min(410px, 100%);
  padding: 26px 28px 28px;
  background:
    radial-gradient(ellipse at 50% 0%, rgba(255, 90, 31, 0.07), transparent 60%),
    linear-gradient(180deg, rgba(16, 13, 13, 0.82), rgba(7, 6, 6, 0.9));
  border: 1px solid var(--line);
  box-shadow:
    0 40px 90px rgba(0, 0, 0, 0.65),
    inset 0 0 0 1px rgba(0, 0, 0, 0.7),
    inset 0 1px 0 rgba(255, 230, 200, 0.04);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

/* Iron corner fittings. */
.panel::before {
  content: '';
  position: absolute;
  inset: -1px;
  pointer-events: none;
  --c: var(--gold);
  background:
    linear-gradient(var(--c), var(--c)) top left / 22px 2px no-repeat,
    linear-gradient(var(--c), var(--c)) top left / 2px 22px no-repeat,
    linear-gradient(var(--c), var(--c)) top right / 22px 2px no-repeat,
    linear-gradient(var(--c), var(--c)) top right / 2px 22px no-repeat,
    linear-gradient(var(--c), var(--c)) bottom left / 22px 2px no-repeat,
    linear-gradient(var(--c), var(--c)) bottom left / 2px 22px no-repeat,
    linear-gradient(var(--c), var(--c)) bottom right / 22px 2px no-repeat,
    linear-gradient(var(--c), var(--c)) bottom right / 2px 22px no-repeat;
  opacity: 0.75;
}

/* Molten seam along the top edge; it flares with the drums. */
.panel::after {
  content: '';
  position: absolute;
  left: 28px;
  right: 28px;
  top: -1px;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(255, 110, 50, 0.85), rgba(255, 181, 71, 0.9) 50%, rgba(255, 110, 50, 0.85), transparent);
  box-shadow: 0 0 calc(6px + 14px * var(--pulse)) rgba(255, 90, 31, 0.7);
  opacity: calc(0.65 + 0.35 * var(--pulse));
}

@keyframes panel-in {
  from { opacity: 0; transform: translateY(24px); clip-path: inset(0 0 100% 0); }
  to { clip-path: inset(0 0 0 0); }
}

@keyframes field-in {
  from { opacity: 0; transform: translateY(10px); }
}

/* ---- Login panel -------------------------------------------------------------------------------------------- */

.login {
  display: none;
  margin-top: 34px;
}

#app[data-stage='menu'] .login {
  display: block;
  animation: panel-in 0.45s 0.1s var(--ease-out) both;
}

#app[data-stage='menu'] .login > * {
  animation: field-in 0.9s var(--ease-out) both;
}

#app[data-stage='menu'] .login > :nth-child(1) { animation-delay: 0.8s; }
#app[data-stage='menu'] .login > :nth-child(2) { animation-delay: 0.9s; }
#app[data-stage='menu'] .login > :nth-child(3) { animation-delay: 1s; }
#app[data-stage='menu'] .login > :nth-child(4) { animation-delay: 1.05s; }
#app[data-stage='menu'] .login > :nth-child(5) { animation-delay: 1.1s; }
#app[data-stage='menu'] .login > :nth-child(6) { animation-delay: 1.15s; }
#app[data-stage='menu'] .login > :nth-child(7) { animation-delay: 1.25s; }
#app[data-stage='menu'] .login > :nth-child(8) { animation-delay: 1.35s; }

/* Signed in: the panel's edges catch fire and it lifts away at once, so the main menu follows quickly. */
#app[data-stage='menu'] .login.is-sworn {
  pointer-events: none;
  animation: sworn 0.55s ease forwards;
}

@keyframes sworn {
  0% { opacity: 1; }
  22% {
    opacity: 1;
    transform: none;
    border-color: rgba(255, 181, 71, 0.7);
    box-shadow: 0 40px 90px rgba(0, 0, 0, 0.65), 0 0 0 1px rgba(255, 181, 71, 0.6), 0 0 60px rgba(255, 90, 31, 0.5), 0 0 120px rgba(255, 31, 75, 0.2);
  }
  100% { opacity: 0; transform: translateY(-18px); }
}

/* A new account: while the grave rises in it (Login panel: the grave) the panel's edges dim to a faint crimson, as if
   lit from the earth. When it is done (.is-sworn) nothing lifts: the crimson goes out of the edges and the panel sinks
   a few pixels into the dark as it fades, over --grave-out (0.7 s, 0.3 s when skipped; main.js sets it), and the main
   menu burns in where it stood. */
#app[data-stage='menu'] .login.is-grave {
  pointer-events: none;
  animation: grave-hush 2s ease-out forwards;
}

#app[data-stage='menu'] .login.is-grave.is-sworn {
  animation: grave-out var(--grave-out, 0.7s) cubic-bezier(0.4, 0, 0.6, 1) forwards;
}

@keyframes grave-hush {
  100% {
    border-color: rgba(122, 26, 16, 0.42);
    box-shadow: 0 40px 90px rgba(0, 0, 0, 0.65), inset 0 0 0 1px rgba(0, 0, 0, 0.7), inset 0 1px 0 rgba(255, 230, 200, 0.04), 0 0 36px rgba(179, 24, 12, 0.16);
  }
}

@keyframes grave-out {
  0% {
    opacity: 1;
    transform: none;
    border-color: rgba(122, 26, 16, 0.42);
    box-shadow: 0 40px 90px rgba(0, 0, 0, 0.65), inset 0 0 0 1px rgba(0, 0, 0, 0.7), inset 0 1px 0 rgba(255, 230, 200, 0.04), 0 0 36px rgba(179, 24, 12, 0.16);
  }
  45% {
    opacity: 0.8;
    border-color: rgba(40, 14, 10, 0.3);
    box-shadow: 0 40px 90px rgba(0, 0, 0, 0.5), inset 0 0 0 1px rgba(0, 0, 0, 0.7), inset 0 1px 0 rgba(255, 230, 200, 0), 0 0 36px rgba(179, 24, 12, 0);
  }
  100% {
    opacity: 0;
    transform: translateY(8px) scale(0.988);
    border-color: rgba(40, 14, 10, 0);
    box-shadow: 0 40px 90px rgba(0, 0, 0, 0), inset 0 0 0 1px rgba(0, 0, 0, 0), inset 0 1px 0 rgba(255, 230, 200, 0), 0 0 36px rgba(179, 24, 12, 0);
  }
}

.tabs {
  display: flex;
  gap: 26px;
  margin-bottom: 24px;
  border-bottom: 1px solid var(--line);
}

.tab {
  position: relative;
  padding: 0 0 12px;
  border: 0;
  background: none;
  font-family: var(--caps);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--smoke);
  cursor: var(--cursor-hot);
  transition: color 0.3s ease;
}

.tab::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background: linear-gradient(90deg, var(--ember), var(--hot));
  box-shadow: 0 0 12px var(--ember);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.45s var(--ease-out);
}

.tab:hover,
.tab:focus-visible {
  color: var(--parchment);
  outline: none;
}

.tab:focus-visible {
  text-shadow: 0 0 12px rgba(255, 90, 31, 0.6);
}

.tab.is-active {
  color: var(--bone);
}

.tab.is-active::after {
  transform: scaleX(1);
}

.field {
  display: block;
  margin-bottom: 18px;
}

.field__label {
  display: block;
  margin-bottom: 8px;
  font-family: var(--caps);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ash);
}

/* The headstone after "Swear it at your grave", in the label's colour. */
.field__grave {
  width: 10px;
  height: 12px;
  margin-left: 4px;
  vertical-align: -1px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.1;
  stroke-linecap: round;
}

.field__grave-lines {
  opacity: 0.55;
}

/* The field you see draws its own glyphs, caret and selection (main.js). The real input lies over the whole field,
   invisible, and takes the keys, the caret and the selection. */
.field__box {
  position: relative;
  display: flex;
  align-items: center;
  height: 47px;
  padding: 0 14px;
  overflow: hidden;
  border: 1px solid rgba(201, 178, 141, 0.13);
  background: rgba(0, 0, 0, 0.42);
  box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.6);
  transition: border-color 0.3s ease, box-shadow 0.3s ease, background 0.3s ease;
}

.field:hover .field__box {
  border-color: rgba(201, 178, 141, 0.26);
}

.field:focus-within .field__box {
  border-color: rgba(255, 106, 42, 0.7);
  background: rgba(20, 8, 5, 0.55);
  box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.6), 0 0 0 3px var(--ember-soft), 0 0 28px rgba(255, 70, 20, 0.2);
}

.field.is-invalid .field__box {
  border-color: rgba(255, 31, 75, 0.8);
  box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.6), 0 0 22px rgba(255, 31, 75, 0.25);
  animation: shake 0.4s ease;
}

.field__in {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  font-size: 16px; /* phones do not zoom in on it */
  color: transparent;
  caret-color: transparent;
  opacity: 0;
  outline: none;
  cursor: var(--cursor-hot);
  -webkit-appearance: none;
  appearance: none;
}

.field__in::selection {
  background: transparent;
  color: transparent;
}

.field__row {
  flex: none;
  display: flex;
  align-items: center;
  transition: transform 0.12s var(--ease-out);
}

.field__glyphs {
  position: relative;
  flex: none;
  display: flex;
  align-items: center;
}

.field__glyph {
  flex: none;
  white-space: pre;
  font-family: var(--caps);
  font-size: 19px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.1em;
  color: var(--bone);
  text-shadow: 0 0 12px rgba(255, 90, 31, 0.18);
}

/* The oath: one ember diamond per character, never the character itself. */
.field--secret .field__glyph {
  position: relative;
  width: 12px;
  height: 14px;
}

.field--secret .field__glyph::before {
  content: '';
  position: absolute;
  left: 2px;
  top: 4px;
  width: 6px;
  height: 6px;
  background: var(--ember);
  box-shadow: inset 1px 1px 0 rgba(255, 200, 120, 0.55), 0 0 6px rgba(255, 90, 31, 0.7);
  transform: rotate(45deg);
}

/* A thin ember caret after the last glyph: a soft blink while the field has focus, steady while you type. */
.field__caret {
  flex: none;
  width: 2px;
  height: 20px;
  margin-left: 2px;
  border-radius: 1px;
  background: linear-gradient(180deg, var(--white-hot), var(--hot) 35%, var(--ember));
  box-shadow: 0 0 6px rgba(255, 120, 40, 0.9), 0 0 14px rgba(255, 90, 31, 0.45);
  opacity: 0;
  transition: opacity 0.2s ease;
}

.field:focus-within .field__caret {
  opacity: 1;
  animation: caret-glow 1.1s ease-in-out infinite;
}

.field.is-typing .field__caret {
  animation: none;
}

/* Typing: a new glyph flashes white-hot and settles into place almost at once, so the field never lags behind the keys;
   a deleted one fades where it stood in a blink. */
.field__glyph.is-new {
  animation: glyph-burn 0.12s ease-out;
}

.field--secret .field__glyph.is-new {
  animation: glyph-settle 0.12s ease-out;
}

.field--secret .field__glyph.is-new::before {
  animation: rune-burn 0.12s ease-out;
}

.field .field__glyph.is-gone {
  position: absolute;
  animation: glyph-gone 0.09s ease-in forwards;
}

/* Sparks off the caret (main.js). */
.field__spark {
  position: absolute;
  z-index: 2;
  width: 2px;
  height: 2px;
  margin: -1px 0 0 -1px;
  border-radius: 50%;
  background: var(--hot);
  box-shadow: 0 0 4px 1px rgba(255, 110, 40, 0.85);
  pointer-events: none;
}

@keyframes caret-glow {
  0%, 45%, 100% { opacity: 1; }
  65%, 85% { opacity: 0.18; }
}

@keyframes glyph-burn {
  from {
    color: var(--white-hot);
    text-shadow: 0 0 4px rgba(255, 241, 214, 0.9), 0 0 10px rgba(255, 150, 60, 0.8);
    transform: translateY(2px);
  }
}

@keyframes glyph-settle {
  from { transform: translateY(2px); }
}

@keyframes rune-burn {
  from {
    background-color: var(--white-hot);
    box-shadow: 0 0 4px 1px rgba(255, 241, 214, 0.9), 0 0 10px rgba(255, 150, 60, 0.8);
  }
}

@keyframes glyph-gone {
  to { opacity: 0; transform: translateY(-2px); }
}

@keyframes shake {
  20% { transform: translateX(-6px); }
  40% { transform: translateX(5px); }
  60% { transform: translateX(-3px); }
  80% { transform: translateX(2px); }
}

/* The confirm field folds open in register mode. */
/* The third field is there only for Dig a grave. It comes and goes in one layout step; main.js (morphMode) makes it
   look smooth with transforms, so nothing reflows while it moves. */
.field-wrap--confirm {
  display: none;
}

.login[data-mode='register'] .field-wrap--confirm {
  display: block;
}

/* Under the button, the other way in: "No grave yet? Dig a grave first", and from there back to Sign in. */
.login__other {
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: 10px;
  margin: 16px 0 -6px;
  font-style: italic;
  font-size: 16px;
  color: var(--ash);
}

.login__other-go {
  padding: 2px 0;
  border: 0;
  background: none;
  font-family: var(--caps);
  font-style: normal;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--parchment);
  cursor: var(--cursor-hot);
  transition: color 0.25s ease, text-shadow 0.25s ease;
}

.login__other-go:is(:hover, :focus-visible) {
  outline: none;
  color: var(--hot);
  text-shadow: 0 0 10px rgba(255, 140, 40, 0.7), 0 0 22px rgba(255, 60, 20, 0.35);
}

.check {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 2px 0 6px;
  font-size: 17px;
  color: var(--ash);
  cursor: var(--cursor-hot);
  user-select: none;
}

.check input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.check__box {
  position: relative;
  width: 16px;
  height: 16px;
  border: 1px solid rgba(201, 178, 141, 0.35);
  background: rgba(0, 0, 0, 0.5);
  transition: border-color 0.2s ease;
}

.check__box::after {
  content: '';
  position: absolute;
  inset: 3px;
  background: var(--ember);
  box-shadow: 0 0 10px var(--ember);
  transform: scale(0) rotate(45deg);
  transition: transform 0.25s var(--ease-out);
}

.check input:checked + .check__box::after {
  transform: scale(1) rotate(45deg);
}

.check input:focus-visible + .check__box,
.check:hover .check__box {
  border-color: var(--parchment);
}

.check input:focus-visible + .check__box {
  box-shadow: 0 0 0 3px var(--ember-soft);
}

.error {
  min-height: 24px;
  margin: 4px 0 10px;
  font-style: italic;
  font-size: 17px;
  color: #ff7a55;
  text-shadow: 0 0 14px rgba(255, 60, 20, 0.35);
}

/* ---- Login panel: the forge ----------------------------------------------------------------------------------- */
/* The corner fittings glow like iron in a forge, lava runs along a seam at the top and the bottom, sparks drift up
   behind the fields, the field you type in gets a line of fire under it and its label catches the light, the active
   tab and the "Remember me" ember flicker, and flames climb the button when you point at it. */

.login::before {
  --c: #ff7a3a;
  filter: drop-shadow(0 0 4px rgba(255, 90, 31, 0.8));
  animation: forge-glow 3.2s ease-in-out infinite;
}

.login {
  isolation: isolate;
}

/* A seam is a 2px band of lava running sideways through the middle of a taller strip, so its glow stays inside the
   mask that fades the ends. */
.login::after,
.login__embers::before {
  left: 14px;
  right: 14px;
  height: 14px;
  background: repeating-linear-gradient(90deg, #ff1f4b 0, #ff5a1f 40px, #ffb547 80px, #fff1d6 100px, #ffb547 120px, #ff5a1f 160px, #ff1f4b 200px) 0 50% / 200px 2px repeat-x;
  box-shadow: none;
  filter: drop-shadow(0 0 calc(4px + 6px * var(--pulse)) rgba(255, 90, 31, 0.9));
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent);
}

.login::after {
  top: -7px;
  animation: lava 5s linear infinite;
}

.login__embers {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

/* The bottom seam (half of it, the rest is past the edge). */
.login__embers::before {
  content: '';
  position: absolute;
  bottom: -6px;
  opacity: 0.8;
  animation: lava 7s linear infinite reverse;
}

.login__embers i {
  position: absolute;
  bottom: -6px;
  left: var(--x);
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: #ffb547;
  box-shadow: 0 0 6px 1px rgba(255, 110, 40, 0.85);
  opacity: 0;
  animation: panel-ember var(--d) var(--delay) linear infinite;
}

.login__embers i:nth-child(1) { --x: 8%; --d: 7s; --delay: 0s; --drift: 24px; }
.login__embers i:nth-child(2) { --x: 19%; --d: 9s; --delay: 3.1s; --drift: -18px; }
.login__embers i:nth-child(3) { --x: 29%; --d: 6.5s; --delay: 1.4s; --drift: 30px; }
.login__embers i:nth-child(4) { --x: 41%; --d: 8.5s; --delay: 5s; --drift: -26px; }
.login__embers i:nth-child(5) { --x: 52%; --d: 7.5s; --delay: 2.2s; --drift: 14px; }
.login__embers i:nth-child(6) { --x: 61%; --d: 10s; --delay: 6.2s; --drift: -32px; }
.login__embers i:nth-child(7) { --x: 70%; --d: 6.8s; --delay: 0.8s; --drift: 20px; }
.login__embers i:nth-child(8) { --x: 79%; --d: 9.5s; --delay: 4.3s; --drift: -14px; }
.login__embers i:nth-child(9) { --x: 87%; --d: 7.2s; --delay: 2.9s; --drift: 28px; }
.login__embers i:nth-child(10) { --x: 94%; --d: 8.8s; --delay: 5.7s; --drift: -22px; }

.field {
  position: relative;
}

.field::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--ember) 20%, var(--hot) 50%, var(--ember) 80%, transparent);
  box-shadow: 0 0 12px rgba(255, 90, 31, 0.8);
  transform: scaleX(0);
  transition: transform 0.5s var(--ease-out);
}

.field:focus-within::after {
  transform: scaleX(1);
  animation: flicker 1.4s ease-in-out infinite alternate;
}

.field__label {
  transition: color 0.3s ease, text-shadow 0.3s ease;
}

.field:focus-within .field__label {
  color: var(--hot);
  text-shadow: 0 0 10px rgba(255, 90, 31, 0.6);
}

.tab.is-active::after,
.check input:checked + .check__box::after {
  animation: flicker 1.4s ease-in-out infinite alternate;
}

.enter__text {
  position: relative;
  z-index: 1;
}

.enter::after {
  content: '';
  position: absolute;
  left: -5%;
  right: -5%;
  bottom: -30%;
  height: 120%;
  background: linear-gradient(0deg, rgba(255, 241, 214, 0.9), rgba(255, 181, 71, 0.75) 25%, rgba(255, 90, 31, 0.5) 55%, transparent 85%);
  filter: url(#flames);
  mix-blend-mode: screen;
  opacity: 0;
  transform: translateY(35%);
  transition: opacity 0.4s ease, transform 0.6s var(--ease-out);
  pointer-events: none;
}

.enter:hover::after,
.enter:focus-visible::after {
  opacity: 0.85;
  transform: translateY(0);
}

/* The graveyard in the Enter button: a low skyline of headstones, a short run of iron fence and one bare tree, in two
   layers darker than the button with a thin ember rim along their tops (each layer is drawn twice in index.html: the
   rim, and half a unit lower the body). On hover both slide up into place with a short, precise ease, the near layer
   a little further than the far one, over a fine low haze between them; nothing sways. The tallest shapes stand at
   the ends, clear of the label, which stays on top; the flames burn a little lower here so the two do not fight. */
.enter__yard {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  height: 16px;
  pointer-events: none;
}

.yard {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 0.16s ease-in, transform 0.18s ease-in;
}

.yard--far {
  z-index: 0;
  transform: translateY(3px);
}

.yard--near {
  z-index: 2;
  transform: translateY(6px);
}

.yard--far .yard__rim { color: rgba(255, 110, 40, 0.32); }
.yard--far .yard__body { color: #230806; }
.yard--near .yard__rim { color: rgba(255, 140, 60, 0.62); }
.yard--near .yard__body { color: #0b0302; }

.enter:hover .yard,
.enter:focus-visible .yard {
  opacity: 1;
  transform: none;
  transition: opacity 0.2s ease-out, transform 0.3s cubic-bezier(0.2, 0.85, 0.25, 1);
}

.enter:hover .yard--far,
.enter:focus-visible .yard--far {
  transition-delay: 0.04s;
}

/* The haze: a fine band of pale air over the far stones' feet, behind the near ones. */
.enter__yard::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  height: 5px;
  background: linear-gradient(0deg, rgba(255, 222, 192, 0.16), rgba(255, 222, 192, 0.05) 60%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.enter:hover .enter__yard::after,
.enter:focus-visible .enter__yard::after {
  opacity: 1;
}

.login .enter:hover::after,
.login .enter:focus-visible::after {
  opacity: 0.72;
}

@keyframes forge-glow {
  0%, 100% { opacity: 0.6; }
  50% { opacity: 1; }
}

@keyframes lava {
  to { background-position: -200px 50%; }
}

@keyframes panel-ember {
  0% { opacity: 0; transform: translate(0, 0); }
  10% { opacity: 0.9; }
  70% { opacity: 0.45; }
  100% { opacity: 0; transform: translate(var(--drift), -280px); }
}

@keyframes flicker {
  from { opacity: 0.75; }
  to { opacity: 1; box-shadow: 0 0 18px rgba(255, 120, 40, 0.95); }
}

@keyframes breathe {
  50% { opacity: 0.45; }
}

/* ---- Login panel: the grave ----------------------------------------------------------------------------------- */
/* A new account gets a grave (main.js, playGrave: it draws it into .login__grave and adds .is-grave, then .is-sworn
   when it is done; about 4 to 4.5 s in all). The panel goes dark and a soil line draws out from the middle; the earth
   cracks open along it with an ember gleam deep inside and kicks up a few clods, and a worn headstone grinds up out of
   it, slow and heavy, with the heat from below on its foot, sheds grit off its face and settles a hair crooked in a
   breath of dust. The name burns into it letter by letter at a pace the eye can follow, each letter traced white-hot
   for an instant and cooling through ember to a dark groove with a faint ember edge. A breath later the Ringer's two
   slanted eyes open in the dark above the stone, hold, and close; as they close the stone sinks back into the earth,
   the gleam goes out and the panel fades (Login panel, grave-out). A skip (.is-hasty) only fades the panel, quicker.
   Nothing lights the whole screen. Reduce flashes softens every glow. */

.login__grave {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  place-items: center;
  overflow: hidden;
  pointer-events: none;
  visibility: hidden;
}

.login.is-grave .login__grave {
  visibility: visible;
}

/* Night over the panel. */
.login__grave::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 75% 60% at 50% 55%, rgba(5, 4, 5, 0.96), rgba(5, 4, 5, 0.86) 60%, rgba(5, 4, 5, 0.7));
  opacity: 0;
}

/* The scene sizes from the stone's width (--w); --h is how far the stone stands above the soil line, which runs 18px
   above the plot's bottom edge. */
.grave {
  --w: min(164px, 44vw);
  --h: calc(var(--w) * 198 / 160);
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* His eyes, in the dark above the stone. */
.grave__eyes {
  display: block;
  width: calc(var(--w) * 0.44);
  height: auto;
  aspect-ratio: 100 / 30;
  margin-bottom: calc(var(--w) * 0.14);
  overflow: visible;
  filter: drop-shadow(0 0 3px rgba(255, 31, 75, 0.75)) drop-shadow(0 0 10px rgba(179, 24, 12, 0.5));
}

body.reduce-flashes .grave__eyes {
  filter: drop-shadow(0 0 2px rgba(255, 31, 75, 0.45)) drop-shadow(0 0 6px rgba(179, 24, 12, 0.28));
}

/* Each lid closes about the long axis of its own eye. */
.grave__lid {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
}

.grave__iris {
  fill: var(--toll);
}

.grave__core {
  fill: rgba(255, 196, 180, 0.85);
}

.grave__plot {
  position: relative;
  isolation: isolate;
  width: calc(var(--w) * 1.9);
  height: calc(var(--h) + 18px);
}

/* The stone rises inside this box, which ends at the soil line, so whatever is still under the earth stays hidden; it
   reaches above and beside the stone so grit and sparks never clip. */
.grave__rise {
  position: absolute;
  left: 0;
  right: 0;
  top: -40px;
  bottom: 18px;
  z-index: 2;
  overflow: hidden;
}

/* The stone's foot stands 12 of its 210 units under the soil line. */
.grave__stone {
  position: absolute;
  left: calc(50% - var(--w) / 2);
  bottom: calc(var(--w) * -12 / 160);
  width: var(--w);
  aspect-ratio: 160 / 210;
  transform-origin: 50% 100%;
}

.grave__slab,
.grave__heat {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

/* The glow of the earth on the stone's foot, cooling as it comes up. */
.grave__heat {
  mix-blend-mode: screen;
  opacity: 0;
}

body.reduce-flashes .grave__heat path {
  opacity: 0.55;
}

/* The name, in the upper face. main.js wraps every letter (words kept whole) and fits the size; each letter is traced
   in white-hot from its left edge, cools through the fire's colours to a dark groove with a faint ember edge, and
   throws one spark as it catches. */
.grave__name {
  position: absolute;
  left: 14%;
  right: 14%;
  top: 41%;
  transform: translateY(-50%);
  font-family: var(--caps);
  font-size: 22px;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: 0.06em;
  text-align: center;
  text-wrap: balance;
}

.grave__word {
  display: inline-block;
  white-space: nowrap;
}

.grave__letter {
  position: relative;
  display: inline-block;
  color: #140b09;
  -webkit-text-stroke: 0.5px rgba(255, 90, 31, 0.28);
}

.grave__letter::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 18%;
  width: 2px;
  height: 2px;
  margin-left: -1px;
  border-radius: 50%;
  background: var(--white-hot);
  box-shadow: 0 0 4px 1px rgba(255, 130, 50, 0.85);
  opacity: 0;
}

.grave__letter:nth-child(even)::after {
  --sx: 4px;
}

body.reduce-flashes .grave__letter::after {
  box-shadow: 0 0 2px rgba(255, 130, 50, 0.5);
}

/* Grit off the stone's face as it comes up (--x across the stone, --y in its units from the top), and clods kicked
   out of the opening earth (--x from the middle, in stone widths); both drop back behind the soil. */
.grave__grit,
.grave__clod {
  position: absolute;
  width: var(--s);
  height: var(--s);
  border-radius: 40%;
  background: var(--c, #5c4a3c);
  opacity: 0;
}

.grave__grit {
  left: calc(50% + (var(--x) - 0.5) * var(--w));
  bottom: calc((198 - var(--y)) * var(--w) / 160);
}

.grave__grit:nth-of-type(1) { --x: 0.14; --y: 70; --s: 2px; --d: 0.52s; --delay: 1.14s; --dx: -5px; }
.grave__grit:nth-of-type(2) { --x: 0.27; --y: 36; --s: 2.5px; --d: 0.58s; --delay: 1.3s; --dx: -3px; }
.grave__grit:nth-of-type(3) { --x: 0.44; --y: 18; --s: 2px; --d: 0.62s; --delay: 1.39s; --dx: -2px; --c: #7a4a30; }
.grave__grit:nth-of-type(4) { --x: 0.58; --y: 21; --s: 1.5px; --d: 0.6s; --delay: 1.56s; --dx: 2px; }
.grave__grit:nth-of-type(5) { --x: 0.75; --y: 36; --s: 3px; --d: 0.56s; --delay: 1.27s; --dx: 4px; }
.grave__grit:nth-of-type(6) { --x: 0.88; --y: 64; --s: 2px; --d: 0.52s; --delay: 1.46s; --dx: 6px; --c: #7a4a30; }
.grave__grit:nth-of-type(7) { --x: 0.34; --y: 104; --s: 1.5px; --d: 0.45s; --delay: 1.07s; --dx: -2px; }
.grave__grit:nth-of-type(8) { --x: 0.67; --y: 128; --s: 2px; --d: 0.4s; --delay: 1.32s; --dx: 3px; }
.grave__grit:nth-of-type(9) { --x: 0.51; --y: 150; --s: 1.5px; --d: 0.34s; --delay: 1.64s; --dx: 1px; }

.grave__clod {
  --c: #5a4334;
  left: calc(50% + var(--x) * var(--w));
  bottom: 18px;
  z-index: 3;
}

.grave__clod:nth-of-type(1) { --x: -0.62; --up: 10px; --dx: -8px; --s: 2.5px; --d: 0.55s; --delay: 0.6s; }
.grave__clod:nth-of-type(2) { --x: -0.4; --up: 15px; --dx: -6px; --s: 2px; --d: 0.6s; --delay: 0.57s; --c: #7a4a30; }
.grave__clod:nth-of-type(3) { --x: -0.2; --up: 12px; --dx: -4px; --s: 3px; --d: 0.55s; --delay: 0.64s; }
.grave__clod:nth-of-type(4) { --x: 0.18; --up: 16px; --dx: 5px; --s: 2px; --d: 0.6s; --delay: 0.61s; }
.grave__clod:nth-of-type(5) { --x: 0.41; --up: 11px; --dx: 7px; --s: 2.5px; --d: 0.55s; --delay: 0.58s; --c: #7a4a30; }
.grave__clod:nth-of-type(6) { --x: 0.63; --up: 9px; --dx: 9px; --s: 2px; --d: 0.5s; --delay: 0.66s; }

/* The heaped earth in front of the stone; its top is the soil line. The ends fade into the dark. */
.grave__ground {
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 4;
  display: block;
  width: 100%;
  height: 24px;
  overflow: visible;
  transform-origin: 50% 100%;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 20%, #000 80%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 20%, #000 80%, transparent);
  opacity: 0;
}

/* The gleam in the opened earth, behind the stone and half hidden by the soil in front. */
.grave__rift {
  position: absolute;
  left: calc(50% - var(--w) * 0.72);
  bottom: 9px;
  z-index: 1;
  width: calc(var(--w) * 1.44);
  height: 18px;
  opacity: 0;
}

.grave__rift::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(closest-side, rgba(255, 150, 70, 0.6), rgba(255, 90, 31, 0.32) 40%, rgba(179, 24, 12, 0.14) 70%, transparent);
}

body.reduce-flashes .grave__rift::before {
  opacity: 0.55;
}

/* A breath of dust where the stone settles. */
.grave__dust {
  position: absolute;
  left: calc(50% - var(--w) * 0.8);
  bottom: 10px;
  z-index: 5;
  width: calc(var(--w) * 1.6);
  height: 30px;
  background: radial-gradient(closest-side, rgba(120, 100, 84, 0.3), rgba(90, 74, 62, 0.14) 55%, transparent);
  opacity: 0;
  transform-origin: 50% 100%;
}

/* The order, matching GRAVE in main.js: dark (0 s), the soil line (0.05 s), the earth cracks open (0.25 s), the stone
   grinds up (0.5 s, past its mark at 1.42 s, settled at 1.6 s), clods, grit and dust (0.57 s to 2.4 s), the name (from
   --carve-at, 1.85 s, 0.15 s a letter, 0.85 s at most for a long name), the eyes (--eyes-at, half a second after the
   last letter, open for a second), the grave sinks as they close (.is-sworn, 0.6 s after they open). */
.login.is-grave .login__grave::before {
  animation: grave-dark 0.5s ease-out forwards;
}

.login.is-grave .grave__ground {
  animation: grave-ground 1.3s 0.05s both;
}

.login.is-grave .grave__rift {
  animation: grave-rift 2.6s 0.25s ease-out both;
}

.login.is-grave .grave__clod {
  animation: grave-clod var(--d) var(--delay) linear both;
}

.login.is-grave .grave__stone {
  will-change: transform;
  animation: grave-rise 1.1s 0.5s both;
}

.login.is-grave .grave__heat {
  animation: grave-heat 2s 0.5s ease-out both;
}

.login.is-grave .grave__grit {
  animation: grave-grit var(--d) var(--delay) cubic-bezier(0.45, 0, 0.85, 0.55) both;
}

.login.is-grave .grave__dust {
  animation: grave-dust 1s 1.4s ease-out both;
}

.login.is-grave .grave__letter {
  animation: grave-burn 0.7s calc(var(--carve-at, 1.85s) + var(--i) * var(--step, 0.12s)) linear both;
}

.login.is-grave .grave__letter::after {
  animation: grave-spark 0.45s calc(var(--carve-at, 1.85s) + var(--i) * var(--step, 0.12s)) ease-out both;
}

.login.is-grave .grave__lid {
  animation: grave-glimpse 1s var(--eyes-at, 3.15s) linear both;
}

body.reduce-flashes .login.is-grave .grave__letter {
  animation-name: grave-burn-soft;
}

/* As his eyes close the grave goes, over the same --grave-out as the panel (grave-out): the stone sinks a little way
   back into the earth and darkens, the gleam in the earth and the heat on the stone go out first. A skip (.is-hasty)
   leaves the grave as it is and only fades the panel. */
.login.is-grave.is-sworn:not(.is-hasty) .grave__stone {
  animation: grave-sink var(--grave-out, 0.7s) cubic-bezier(0.5, 0, 0.75, 0.45) forwards;
}

.login.is-grave.is-sworn:not(.is-hasty) .grave__rift {
  --ember: 0.32;
  animation: grave-ember-out calc(var(--grave-out, 0.7s) * 0.6) ease-out forwards;
}

.login.is-grave.is-sworn:not(.is-hasty) .grave__heat {
  --ember: 0.2;
  animation: grave-ember-out calc(var(--grave-out, 0.7s) * 0.6) ease-out forwards;
}

@keyframes grave-dark {
  to { opacity: 1; }
}

@keyframes grave-sink {
  from { transform: translateY(0) rotate(-0.7deg); filter: brightness(1); }
  to { transform: translateY(14%) rotate(-0.7deg); filter: brightness(0.4); }
}

@keyframes grave-ember-out {
  from { opacity: var(--ember); }
  to { opacity: 0; }
}

/* The soil line draws out from the middle, then the earth heaves once as the stone breaks through. */
@keyframes grave-ground {
  0% { opacity: 0; transform: scale(0.04, 1) translateY(0); animation-timing-function: cubic-bezier(0.2, 0.7, 0.3, 1); }
  30% { opacity: 1; transform: scale(1, 1) translateY(0); animation-timing-function: ease-out; }
  44% { transform: scale(1.01, 1.18) translateY(-1px); animation-timing-function: ease-in-out; }
  70%, 100% { opacity: 1; transform: scale(1, 1) translateY(0); }
}

/* A thin seam of light first, open by the time the stone is halfway up, then sinking to embers. */
@keyframes grave-rift {
  0% { opacity: 0; transform: scale(0.05, 0.2); }
  14% { opacity: 0.7; transform: scale(0.85, 0.3); }
  28% { opacity: 1; transform: scale(1, 1); }
  55% { opacity: 0.6; }
  100% { opacity: 0.32; transform: scale(1, 1); }
}

@keyframes grave-clod {
  0% { opacity: 0; transform: translate(0, 4px); animation-timing-function: cubic-bezier(0.2, 0.6, 0.4, 1); }
  12% { opacity: 1; }
  45% { transform: translate(calc(var(--dx) * 0.55), calc(var(--up) * -1)); animation-timing-function: cubic-bezier(0.5, 0, 0.9, 0.6); }
  85% { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--dx), 8px); }
}

/* Heavy: it gets going slowly, grinds up out of the ground rocking a little against the earth (the curves meet at the
   same speed, so there is no jolt), comes up a hair too far and settles back a little crooked. */
@keyframes grave-rise {
  0% { transform: translateY(100%) rotate(0deg); animation-timing-function: cubic-bezier(0.5, 0, 0.75, 0.7); }
  34% { transform: translateY(62%) rotate(0.4deg); animation-timing-function: cubic-bezier(0.3, 0.25, 0.7, 0.8); }
  58% { transform: translateY(24%) rotate(-0.2deg); animation-timing-function: cubic-bezier(0.25, 0.27, 0.25, 1); }
  84% { transform: translateY(-1.2%) rotate(0.25deg); animation-timing-function: ease-in-out; }
  100% { transform: translateY(0) rotate(-0.7deg); }
}

@keyframes grave-heat {
  0% { opacity: 1; }
  40% { opacity: 0.8; }
  100% { opacity: 0.2; }
}

/* Down the face to the soil line, gathering speed. */
@keyframes grave-grit {
  0% { opacity: 0; transform: translate(0, 0); }
  10% { opacity: 1; }
  100% { opacity: 0.8; transform: translate(var(--dx), calc((198 - var(--y)) * var(--w) / 160 + 4px)); }
}

@keyframes grave-dust {
  0% { opacity: 0; transform: translateY(0) scale(0.55, 0.4); }
  30% { opacity: 1; }
  100% { opacity: 0; transform: translateY(-5px) scale(1.2, 1); }
}

/* Traced in from the left white-hot, then hot, ember and a dark red, ending as the groove: dark, its edge still faintly
   lit. */
@keyframes grave-burn {
  0% {
    opacity: 0;
    clip-path: inset(-20px 100% -20px 0);
    color: #fff;
    -webkit-text-stroke-color: rgba(255, 241, 214, 0.9);
    text-shadow: 0 0 2px rgba(255, 241, 214, 0.95), 0 0 7px rgba(255, 181, 71, 0.85), 0 0 16px rgba(255, 90, 31, 0.55), 0 1px 0 rgba(234, 223, 204, 0);
  }
  4% { opacity: 1; }
  14% {
    clip-path: inset(-20px -20px -20px -20px);
    color: #fff;
    -webkit-text-stroke-color: rgba(255, 241, 214, 0.9);
    text-shadow: 0 0 2px rgba(255, 241, 214, 0.95), 0 0 7px rgba(255, 181, 71, 0.85), 0 0 16px rgba(255, 90, 31, 0.55), 0 1px 0 rgba(234, 223, 204, 0);
  }
  32% {
    color: var(--hot);
    -webkit-text-stroke-color: rgba(255, 181, 71, 0.75);
    text-shadow: 0 0 1px rgba(255, 181, 71, 0.8), 0 0 6px rgba(255, 90, 31, 0.7), 0 0 13px rgba(179, 24, 12, 0.45), 0 1px 0 rgba(234, 223, 204, 0);
  }
  54% {
    color: var(--ember);
    -webkit-text-stroke-color: rgba(255, 90, 31, 0.6);
    text-shadow: 0 0 1px rgba(255, 90, 31, 0.65), 0 0 5px rgba(255, 60, 20, 0.5), 0 0 10px rgba(179, 24, 12, 0.3), 0 1px 0 rgba(234, 223, 204, 0.03);
  }
  76% {
    color: #4a1208;
    -webkit-text-stroke-color: rgba(255, 90, 31, 0.42);
    text-shadow: 0 0 1px rgba(255, 90, 31, 0.45), 0 0 4px rgba(179, 24, 12, 0.3), 0 0 6px rgba(179, 24, 12, 0.12), 0 1px 0 rgba(234, 223, 204, 0.06);
  }
  100% {
    opacity: 1;
    clip-path: inset(-20px -20px -20px -20px);
    color: #140b09;
    -webkit-text-stroke-color: rgba(255, 90, 31, 0.28);
    text-shadow: 0 0 1px rgba(255, 90, 31, 0.35), 0 0 4px rgba(179, 24, 12, 0.22), 0 0 0 rgba(179, 24, 12, 0), 0 1px 0 rgba(234, 223, 204, 0.09);
  }
}

/* Reduce flashes: no white-hot, smaller glows. */
@keyframes grave-burn-soft {
  0% {
    opacity: 0;
    clip-path: inset(-20px 100% -20px 0);
    color: var(--hot);
    -webkit-text-stroke-color: rgba(255, 181, 71, 0.6);
    text-shadow: 0 0 1px rgba(255, 181, 71, 0.6), 0 0 4px rgba(255, 90, 31, 0.4), 0 0 0 rgba(255, 90, 31, 0), 0 1px 0 rgba(234, 223, 204, 0);
  }
  4% { opacity: 1; }
  14% {
    clip-path: inset(-20px -20px -20px -20px);
    color: var(--hot);
    -webkit-text-stroke-color: rgba(255, 181, 71, 0.6);
    text-shadow: 0 0 1px rgba(255, 181, 71, 0.6), 0 0 4px rgba(255, 90, 31, 0.4), 0 0 0 rgba(255, 90, 31, 0), 0 1px 0 rgba(234, 223, 204, 0);
  }
  45% {
    color: var(--ember);
    -webkit-text-stroke-color: rgba(255, 90, 31, 0.5);
    text-shadow: 0 0 1px rgba(255, 90, 31, 0.5), 0 0 3px rgba(255, 60, 20, 0.3), 0 0 0 rgba(179, 24, 12, 0), 0 1px 0 rgba(234, 223, 204, 0.03);
  }
  75% {
    color: #4a1208;
    -webkit-text-stroke-color: rgba(255, 90, 31, 0.38);
    text-shadow: 0 0 1px rgba(255, 90, 31, 0.4), 0 0 3px rgba(179, 24, 12, 0.24), 0 0 0 rgba(179, 24, 12, 0), 0 1px 0 rgba(234, 223, 204, 0.06);
  }
  100% {
    opacity: 1;
    clip-path: inset(-20px -20px -20px -20px);
    color: #140b09;
    -webkit-text-stroke-color: rgba(255, 90, 31, 0.28);
    text-shadow: 0 0 1px rgba(255, 90, 31, 0.35), 0 0 4px rgba(179, 24, 12, 0.22), 0 0 0 rgba(179, 24, 12, 0), 0 1px 0 rgba(234, 223, 204, 0.09);
  }
}

@keyframes grave-spark {
  0% { opacity: 0; transform: translate(0, 0); }
  12% { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--sx, -4px), -15px); }
}

/* His eyes: they open, narrow a touch as they settle on the name and hold it, and close as the grave goes. */
@keyframes grave-glimpse {
  0% { opacity: 0; transform: scaleY(0.06); animation-timing-function: cubic-bezier(0.2, 0.7, 0.3, 1); }
  22% { opacity: 1; transform: scaleY(1); animation-timing-function: ease-in-out; }
  44% { opacity: 1; transform: scaleY(0.86); }
  62% { opacity: 1; transform: scaleY(0.86); animation-timing-function: cubic-bezier(0.6, 0, 0.85, 0.4); }
  100% { opacity: 0; transform: scaleY(0.06); }
}

/* ---- Buttons ------------------------------------------------------------------------------------------------ */

.enter {
  position: relative;
  width: 100%;
  overflow: hidden;
  padding: 16px 20px 15px;
  border: 1px solid rgba(255, 120, 60, 0.38);
  background: linear-gradient(180deg, #8e2214 0%, var(--blood) 45%, var(--blood-deep) 100%);
  font-family: var(--caps);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: #f8ecd8;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.7);
  cursor: var(--cursor-hot);
  box-shadow: inset 0 1px 0 rgba(255, 200, 160, 0.2), inset 0 -10px 20px rgba(0, 0, 0, 0.35), 0 10px 30px rgba(110, 18, 8, 0.4);
  transition: box-shadow 0.35s ease, filter 0.35s ease, transform 0.1s ease;
}

.enter::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: -60%;
  width: 40%;
  background: linear-gradient(100deg, transparent, rgba(255, 241, 214, 0.4), transparent);
  transform: skewX(-18deg);
  transition: left 0.8s var(--ease-out);
}

.enter:hover,
.enter:focus-visible {
  outline: none;
  filter: brightness(1.25);
  box-shadow: inset 0 1px 0 rgba(255, 200, 160, 0.25), inset 0 -10px 20px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 181, 71, 0.55), 0 0 40px rgba(255, 90, 31, 0.55), 0 0 90px rgba(255, 31, 75, 0.22);
}

.enter:hover::before,
.enter:focus-visible::before {
  left: 120%;
}

.enter:active {
  transform: translateY(1px);
}

.enter[disabled] {
  cursor: var(--cursor);
  filter: brightness(0.9) saturate(0.8);
}

.enter[disabled] .enter__text {
  animation: breathe 1.6s ease-in-out infinite;
}

.ghost {
  padding: 12px 22px 11px;
  border: 1px solid var(--line);
  background: rgba(0, 0, 0, 0.35);
  font-family: var(--caps);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ash);
  cursor: var(--cursor-hot);
  transition: color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
}

.ghost:hover,
.ghost:focus-visible {
  outline: none;
  color: var(--white-hot);
  border-color: rgba(255, 106, 42, 0.6);
  box-shadow: 0 0 22px rgba(255, 90, 31, 0.3), inset 0 0 12px rgba(255, 90, 31, 0.12);
}

/* ---- Main menu: the hall ------------------------------------------------------------------------------------ */

.hall {
  display: none;
  margin-top: clamp(14px, 3.2vh, 36px);
}

#app[data-stage='main'] .hall {
  display: block;
}

.hall__greeting {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: 0.45em;
  margin: 0;
  padding-bottom: 12px;
  font-family: var(--caps);
  font-weight: 700;
}

/* A burning fuse under the greeting. */
.hall__greeting::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: min(340px, 80vw);
  height: 1px;
  background: linear-gradient(90deg, var(--white-hot), var(--hot) 12%, var(--ember) 40%, rgba(255, 31, 75, 0.4) 75%, transparent);
  box-shadow: 0 0 10px rgba(255, 90, 31, 0.8);
  transform-origin: left center;
}

.hall__rank {
  font-size: clamp(14px, 1.3vw, 19px);
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--parchment);
  text-shadow: 0 1px 2px #000;
}

.hall__name {
  font-size: clamp(28px, min(3.2vw, 6vh), 48px);
  letter-spacing: 0.05em;
  color: transparent;
  background: linear-gradient(180deg, var(--white-hot) 0%, #f2d9a8 40%, var(--hot) 70%, var(--ember) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  filter: drop-shadow(0 2px 0 #000) drop-shadow(0 0 calc(14px + 18px * var(--pulse)) rgba(255, 90, 31, calc(0.4 + 0.4 * var(--pulse))));
}

/* Back from a sub-view the greeting only flares for a moment; the first arrival after signing in (data-enter="first",
   set by main.js) burns it in. Both share the keyframes, so switching while the greeting is on screen only retimes
   the finished animation instead of replaying it. */
#app[data-stage='main'] .hall__greeting {
  animation: burn-in 0.18s var(--ease-out) both;
}

#app[data-stage='main'] .hall__greeting::after {
  animation: fuse 0.18s var(--ease-out) both;
}

#app[data-stage='main'] .hall[data-enter='first'] .hall__greeting {
  animation-duration: 0.9s;
}

#app[data-stage='main'] .hall[data-enter='first'] .hall__greeting::after {
  animation-duration: 1s;
  animation-delay: 0.15s;
}

@keyframes burn-in {
  0% { opacity: 0; filter: blur(10px) brightness(2.4); }
  40% { opacity: 1; filter: blur(2px) brightness(1.6); }
  100% { opacity: 1; filter: blur(0) brightness(1); }
}

@keyframes fuse {
  from { transform: scaleX(0); }
}

/* ---- Main menu: the gates (menu entries) -------------------------------------------------------------------- */

.gates {
  margin-top: clamp(12px, 2.6vh, 28px);
}

.gates__list {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(0px, 0.5vh, 6px);
  margin: 0;
  padding: 0;
  list-style: none;
}

.gates__list > li:last-child {
  margin-top: clamp(4px, 1.4vh, 14px);
}

/* Back from a sub-view the gates are all there at once, with a short slide and no delay. The first arrival after
   signing in (data-enter="first", set by main.js) lights them one by one, quickly. main.js only switches the
   attribute while the gates are hidden, so neither entrance ever replays in sight. */
#app[data-stage='main'] .gates__list > li {
  animation: gate-back 0.16s var(--ease-out) both;
}

#app[data-stage='main'] .hall[data-enter='first'] .gates__list > li {
  animation: gate-in 0.45s var(--ease-out) both;
}

#app[data-stage='main'] .hall[data-enter='first'] .gates__list > li:nth-child(1) { animation-delay: 0.15s; }
#app[data-stage='main'] .hall[data-enter='first'] .gates__list > li:nth-child(2) { animation-delay: 0.19s; }
#app[data-stage='main'] .hall[data-enter='first'] .gates__list > li:nth-child(3) { animation-delay: 0.23s; }
#app[data-stage='main'] .hall[data-enter='first'] .gates__list > li:nth-child(4) { animation-delay: 0.27s; }

@keyframes gate-in {
  0% { opacity: 0; transform: translateX(-28px); filter: brightness(3) blur(4px); }
  60% { opacity: 1; filter: brightness(1.3) blur(0); }
  100% { opacity: 1; transform: none; filter: none; }
}

@keyframes gate-back {
  from { opacity: 0; transform: translateX(-10px); }
}

/* Each gate: an iron rune and a label that turns molten when selected. */
.gate {
  --molten: linear-gradient(180deg, var(--white-hot) 0%, var(--hot) 36%, var(--ember) 70%, var(--blood-moon) 100%);
  --glow-near: rgba(255, 90, 31, 0.8);
  --glow-far: rgba(255, 31, 75, 0.4);
  --heat: rgba(255, 90, 31, 0.22);
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  gap: 18px;
  padding: clamp(5px, 1vh, 10px) 26px clamp(5px, 1vh, 10px) 0;
  border: 0;
  background: none;
  font-family: var(--caps);
  font-weight: 700;
  font-size: clamp(19px, min(2.3vw, 4.2vh), 34px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-align: left;
  color: var(--parchment);
  cursor: var(--cursor-hot);
  outline: none;
  -webkit-tap-highlight-color: transparent;
}

.gate--leave {
  --molten: linear-gradient(180deg, var(--white-hot) 0%, #ff8aa0 32%, var(--toll) 66%, var(--blood-moon) 100%);
  --glow-near: rgba(255, 31, 75, 0.85);
  --glow-far: rgba(179, 24, 12, 0.5);
  --heat: rgba(255, 31, 75, 0.2);
  font-size: clamp(15px, min(1.6vw, 3vh), 22px);
}

/* Heat haze behind the selected gate. */
.gate::before {
  content: '';
  position: absolute;
  z-index: -1;
  top: 10%;
  bottom: 10%;
  left: -24px;
  right: -80px;
  background: linear-gradient(90deg, transparent 0%, var(--heat) 10%, rgba(255, 31, 75, 0.08) 48%, transparent 88%);
  opacity: 0;
  transform: scaleX(0.2);
  transform-origin: left center;
  transition: opacity 0.35s ease, transform 0.6s var(--ease-out);
}

/* Molten underline that runs out from the rune. */
.gate::after {
  content: '';
  position: absolute;
  left: 60px;
  right: 26px;
  bottom: 2px;
  height: 1px;
  background: linear-gradient(90deg, var(--white-hot), var(--hot) 18%, var(--ember) 50%, rgba(255, 31, 75, 0.5) 80%, transparent);
  box-shadow: 0 0 10px var(--glow-near), 0 0 24px var(--glow-far);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.55s var(--ease-out);
}

.gate--leave::after {
  background: linear-gradient(90deg, var(--white-hot), #ff8aa0 18%, var(--toll) 50%, rgba(179, 24, 12, 0.5) 80%, transparent);
}

.gate.is-current::before {
  opacity: 1;
  transform: none;
}

.gate.is-current::after {
  transform: scaleX(1);
}

.gate__rune {
  position: relative;
  z-index: 0;
  flex: none;
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--smoke);
  transition: color 0.3s ease, text-shadow 0.3s ease;
}

.gate--leave .gate__rune {
  width: 42px;
  height: 34px;
  font-size: 11px;
}

/* The iron diamond. */
.gate__rune::before {
  content: '';
  position: absolute;
  z-index: -1;
  top: 50%;
  left: 50%;
  width: 24px;
  height: 24px;
  margin: -12px 0 0 -12px;
  border: 1px solid rgba(201, 178, 141, 0.3);
  background: rgba(5, 4, 5, 0.7);
  transform: rotate(45deg);
  transition: border-color 0.3s ease, background 0.3s ease, box-shadow 0.3s ease;
}

/* The molten halo; it breathes with the drums. */
.gate__rune::after {
  content: '';
  position: absolute;
  z-index: -2;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255, 181, 71, 0.85), var(--glow-near) 45%, rgba(255, 31, 75, 0.14) 75%, transparent);
  opacity: 0;
}

.gate:hover .gate__rune,
.gate:focus-visible .gate__rune {
  color: var(--parchment);
}

.gate.is-current .gate__rune {
  color: var(--white-hot);
  text-shadow: 0 0 8px var(--hot), 0 1px 1px #000;
}

.gate.is-current .gate__rune::before {
  border-color: rgba(255, 181, 71, 0.9);
  background: linear-gradient(135deg, var(--ember), var(--blood) 70%);
  box-shadow: 0 0 14px var(--glow-near), inset 0 0 6px rgba(255, 241, 214, 0.5);
}

.gate--leave.is-current .gate__rune::before {
  border-color: rgba(255, 138, 160, 0.9);
  background: linear-gradient(135deg, var(--toll), var(--blood-deep) 75%);
}

.gate.is-current .gate__rune::after {
  opacity: calc(0.6 + 0.4 * var(--pulse));
  transform: scale(calc(1.05 + 0.35 * var(--pulse)));
  animation: halo-in 0.5s var(--ease-out);
}

@keyframes halo-in {
  from { opacity: 0; transform: scale(0.4); }
}

/* Embers that jump off a rune when its gate catches fire (spawned by main.js). */
.spark {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 3px;
  height: 3px;
  margin: -1.5px 0 0 -1.5px;
  background: var(--hot);
  box-shadow: 0 0 6px var(--ember), 0 0 12px rgba(255, 31, 75, 0.6);
  pointer-events: none;
}

.gate__label {
  position: relative;
  display: block;
  white-space: nowrap;
  text-shadow: 0 2px 0 #000, 0 0 18px rgba(0, 0, 0, 0.9);
  transition: color 0.3s ease, transform 0.45s var(--ease-out);
}

/* The molten copy of the label, with a white-hot sweep that runs across it when the gate is chosen. */
.gate__label::after {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  color: transparent;
  text-shadow: none;
  background:
    linear-gradient(100deg, transparent 38%, rgba(255, 255, 255, 0.95) 50%, transparent 62%) no-repeat,
    var(--molten);
  background-size: 260% 100%, 100% 100%;
  background-position: 200% 0, 0 0;
  -webkit-background-clip: text;
  background-clip: text;
  filter: drop-shadow(0 0 10px var(--glow-near)) drop-shadow(0 0 26px var(--glow-far));
  opacity: 0;
  transition: opacity 0.25s ease;
}

.gate:hover .gate__label {
  color: var(--bone);
}

/* Join Lobby: one small lantern diamond for each lobby open to join right now, in the colour of a lantern already in
   it and breathing slowly, so you see a friend is waiting before you choose the gate (main.js, showOpen). */
.gate__lanterns {
  display: inline-flex;
  gap: 8px;
  margin-left: -4px;
  transition: transform 0.45s var(--ease-out);
}

.gate__lanterns:empty {
  display: none;
}

.gate__lanterns i {
  width: 7px;
  height: 7px;
  border: 1px solid color-mix(in srgb, var(--c, var(--hot)) 75%, #fff);
  background: var(--c, var(--ember));
  box-shadow: 0 0 8px var(--c, var(--ember)), 0 0 18px color-mix(in srgb, var(--c, var(--ember)) 45%, transparent);
  transform: rotate(45deg);
  animation: lantern-wait 2.6s ease-in-out infinite, lantern-in 0.5s var(--ease-out) backwards;
}

.gate__lanterns i:nth-child(2) {
  animation-delay: -0.9s, 0s;
}

.gate__lanterns i:nth-child(3) {
  animation-delay: -1.7s, 0s;
}

.gate.is-current .gate__lanterns {
  transform: translateX(6px);
}

@keyframes lantern-wait {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

@keyframes lantern-in {
  from { opacity: 0; transform: rotate(45deg) scale(0.3); }
}

.gate.is-current .gate__label {
  transform: translateX(6px);
}

.gate.is-current .gate__label::after {
  opacity: 1;
  animation: sweep 1.1s 0.05s var(--ease-out) both;
}

@keyframes sweep {
  from { background-position: 200% 0, 0 0; }
  to { background-position: -100% 0, 0 0; }
}

/* ---- Main menu: lobbies ------------------------------------------------------------------------------------- */

.lobby,
.lobby__head,
.chat,
.browse {
  display: none;
}

/* A lobby stands down in the bottom left corner, its buttons along the bottom edge, the world open above it (the page
   itself, #app, is the box it is placed in: the whole window, with the page's own side margin). Its name stands up
   under the title, where the greeting was. */
/* Where the lobby and its chat stand (the chat right beside it, see the lobby's chat): the lobby's left edge and
   bottom, the chat's width and its gap to the lobby (none: the picture's own edge fades out there), and the lobby's
   width, as much as is left beside the chat. */
.hall[data-view='lobby'] {
  --lobby-left: clamp(20px, 8vw, 140px);
  --lobby-bottom: clamp(20px, 5vh, 56px);
  --chat-w: min(480px, 34vw);
  --chat-gap: 0px;
  --lobby-w: min(1014px, calc(100% - var(--lobby-left) - clamp(20px, 4vw, 60px) - var(--chat-w) - var(--chat-gap)));
}

.hall[data-view='lobby'] .lobby {
  display: block;
  position: absolute;
  left: var(--lobby-left);
  bottom: var(--lobby-bottom);
  width: var(--lobby-w);
  animation: panel-in 0.28s var(--ease-out) both;
}

/* The name and, right after it on the same line, open to all or private. */
.hall[data-view='lobby'] .lobby__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 26px;
  row-gap: 4px;
  animation: burn-in 0.18s var(--ease-out) both;
}

/* Join Lobby has no panel: the open lobbies stand as lanterns in the fog under the title. */
.hall[data-view='browse'] .browse {
  display: block;
  width: min(760px, 100%);
  margin-top: clamp(12px, 2.6vh, 26px);
  animation: field-in 0.4s var(--ease-out) both;
}

.hall[data-view='lobby'] .gates,
.hall[data-view='lobby'] .hall__greeting,
.hall[data-view='browse'] .gates {
  display: none;
}

/* The lobby's name, a heading the host can rename in place, as wide as the name itself (lobby.js measures it). */
.lobby__name {
  display: block;
  width: min(520px, 100%);
  min-width: 3em;
  max-width: min(520px, 100%);
  margin: 0;
  padding: 4px 0 8px;
  border: 0;
  border-bottom: 1px solid transparent;
  background: none;
  font-family: var(--caps);
  font-weight: 700;
  font-size: clamp(20px, min(2.2vw, 4vh), 30px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bone);
  text-shadow: 0 2px 0 #000, 0 0 18px rgba(255, 90, 31, 0.35);
  caret-color: var(--ember);
  outline: none;
  transition: border-color 0.3s ease;
}

.lobby__name[readonly] {
  cursor: var(--cursor);
}

.lobby__name:not([readonly]):hover {
  border-bottom-color: var(--line);
}

/* Beside the name: open to all or private, a small iron switch only the host can throw (the others see how it
   stands), and a private lobby's code, burning, for everyone in it to pass on. */
.lobby__seal {
  display: flex;
  align-items: center;
  gap: 18px;
  margin: 0;
}

.lobby__private {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px 5px 10px;
  border: 1px solid var(--line);
  background: rgba(5, 4, 5, 0.6);
  font-family: var(--caps);
  font-weight: 700;
  font-size: 10.5px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--parchment);
  cursor: var(--cursor-hot);
  transition: color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
}

/* The lock in front of it: open, or shut and glowing. */
.lobby__private::before {
  content: '';
  width: 9px;
  height: 11px;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 11'%3E%3Cpath d='M2 5V3.2a2.5 2.5 0 0 1 5 0V3H5.6v.2a1.1 1.1 0 0 0-2.2 0V5H9v6H0V5z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 11'%3E%3Cpath d='M2 5V3.2a2.5 2.5 0 0 1 5 0V3H5.6v.2a1.1 1.1 0 0 0-2.2 0V5H9v6H0V5z'/%3E%3C/svg%3E") center / contain no-repeat;
}

.lobby__private[aria-pressed='true']::before {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 11'%3E%3Cpath d='M2 5V3.2a2.5 2.5 0 0 1 5 0V5H9v6H0V5zm1.4 0h2.2V3.2a1.1 1.1 0 0 0-2.2 0z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 11'%3E%3Cpath d='M2 5V3.2a2.5 2.5 0 0 1 5 0V5H9v6H0V5zm1.4 0h2.2V3.2a1.1 1.1 0 0 0-2.2 0z'/%3E%3C/svg%3E");
}

.seal__shut,
.lobby__private[aria-pressed='true'] .seal__open {
  display: none;
}

.lobby__private[aria-pressed='true'] .seal__shut {
  display: inline;
}

.lobby__private[aria-pressed='true'] {
  color: var(--hot);
  border-color: rgba(255, 140, 60, 0.45);
  box-shadow: 0 0 14px rgba(255, 90, 31, 0.2);
}

.lobby__private:not([disabled]):is(:hover, :focus-visible) {
  color: var(--white-hot);
  border-color: rgba(255, 106, 42, 0.6);
  outline: none;
}

.lobby__private[disabled] {
  cursor: var(--cursor);
  opacity: 0.6;
}

.lobby__code {
  font-family: var(--caps);
  font-weight: 700;
  font-size: 10.5px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--ash);
}

.lobby__code[hidden] {
  display: none;
}

.lobby__code b {
  margin-left: 0.5em;
  font-size: 17px;
  letter-spacing: 0.3em;
  color: var(--white-hot);
  text-shadow: 0 0 12px rgba(255, 140, 40, 0.8), 0 1px 2px #000;
  user-select: text;
}

.lobby__name:not([readonly]):focus {
  border-bottom-color: rgba(255, 106, 42, 0.6);
}

/* The Gravediggers on their patch of earth, big enough to see them well; the picture fades into the night at every
   edge. */
.lobby__stage {
  position: relative;
  height: clamp(286px, 52vh, 494px);
}

.lobby__view {
  display: block;
  width: 100%;
  height: 100%;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent), linear-gradient(180deg, transparent, #000 28%, #000 86%, transparent);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent), linear-gradient(180deg, transparent, #000 28%, #000 86%, transparent);
  mask-composite: intersect;
}

.lobby.no-stage .lobby__view {
  visibility: hidden;
}

/* Over your own Gravedigger: his lantern takes another colour, the rest of him an emote (lobby-stage.js). */
.lobby__view.is-pickable {
  cursor: var(--cursor-hot);
}

.lobby__slots {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  pointer-events: none;
}

/* A name under each Gravedigger's feet (main.js places them; without the stage they spread evenly). */
.slot {
  position: absolute;
  left: calc(12.5% + 25% * var(--i));
  top: 84%;
  transform: translate(-50%, 6px);
}

.slot__name {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--caps);
  font-weight: 700;
  font-size: clamp(12px, 1.1vw, 15px);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--parchment);
  text-shadow: 0 1px 2px #000, 0 0 12px #000;
}

.slot.is-taken .slot__name {
  animation: burn-in 1.4s 0.5s var(--ease-out) both;
}

/* A small diamond in the player's lantern colour (--c, set by lobby.js) that catches fire when he is ready. */
.slot.is-taken .slot__name::before {
  content: '';
  width: 6px;
  height: 6px;
  border: 1px solid color-mix(in srgb, var(--c, #c9b28d) 55%, transparent);
  background: rgba(5, 4, 5, 0.7);
  transform: rotate(45deg);
  transition: border-color 0.4s ease, background 0.4s ease, box-shadow 0.4s ease;
}

.slot.is-me .slot__name {
  color: var(--bone);
}

.slot.is-ready .slot__name {
  color: var(--white-hot);
  text-shadow: 0 1px 2px #000, 0 0 14px rgba(255, 90, 31, 0.7);
}

.slot.is-ready .slot__name::before {
  border-color: color-mix(in srgb, var(--c, var(--hot)) 75%, #fff);
  background: var(--c, var(--ember));
  box-shadow: 0 0 8px var(--c, var(--ember)), 0 0 16px color-mix(in srgb, var(--c, var(--ember)) 50%, transparent);
}

/* The host carries a small bell after his name. Once everyone is ready it swings and glows: the others see whom they
   are waiting for, and the host that the start is his. */
.slot.is-host .slot__name::after {
  content: '';
  width: 9px;
  height: 10px;
  background: var(--gold);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='8 4 48 56'%3E%3Cpath d='M32 6c-2 0-3 1-3 3v2c-9 2-14 10-14 20v10l-6 8h46l-6-8V31c0-10-5-18-14-20V9c0-2-1-3-3-3z'/%3E%3Ccircle cx='32' cy='54' r='5'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='8 4 48 56'%3E%3Cpath d='M32 6c-2 0-3 1-3 3v2c-9 2-14 10-14 20v10l-6 8h46l-6-8V31c0-10-5-18-14-20V9c0-2-1-3-3-3z'/%3E%3Ccircle cx='32' cy='54' r='5'/%3E%3C/svg%3E") center / contain no-repeat;
  transform-origin: 50% 6%;
  filter: drop-shadow(0 1px 1px #000);
  transition: background 0.4s ease, filter 0.4s ease;
}

#lobby.is-all-ready .slot.is-host .slot__name::after {
  background: #ffd9a0;
  filter: drop-shadow(0 0 4px rgba(255, 180, 90, 0.9)) drop-shadow(0 0 10px rgba(255, 90, 31, 0.5));
  animation: host-bell 1.8s ease-in-out infinite;
}

@keyframes host-bell {
  0%, 46%, 100% { transform: rotate(0deg); }
  8% { transform: rotate(16deg); }
  18% { transform: rotate(-12deg); }
  28% { transform: rotate(7deg); }
  38% { transform: rotate(-3deg); }
}

/* Under every other player's name (and his Ready), for the host only: "Cast out", quiet ash until pointed at; armed by
   a first click it burns crimson and asks again (lobby.js). */
.slot__cast {
  display: none;
  position: absolute;
  left: 50%;
  top: calc(100% + 6px);
  translate: -50% 0;
  padding: 3px 8px;
  border: 1px solid transparent;
  background: none;
  font-family: var(--caps);
  font-weight: 700;
  font-size: 9.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--smoke);
  pointer-events: auto;
  cursor: var(--cursor-hot);
  transition: color 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}

#lobby.is-hosting .slot.is-taken:not(.is-me):not(.is-bot) .slot__cast {
  display: block;
}

.slot__cast:is(:hover, :focus-visible) {
  color: #d99aa4;
  border-color: rgba(255, 31, 75, 0.35);
  outline: none;
}

.slot__cast.is-armed {
  color: var(--white-hot);
  border-color: var(--toll);
  background: linear-gradient(180deg, rgba(150, 12, 32, 0.5), rgba(40, 4, 10, 0.7));
  box-shadow: 0 0 16px rgba(255, 31, 75, 0.5);
}

/* A bot (nachtwacht 2017): after his name a small, quiet BOT, cut in the dark iron, so everyone sees nobody plays him.
   A bot never hosts, so the name's ::after (the host's bell) is his for it. */
.slot.is-bot .slot__name::after {
  content: 'bot';
  padding: 2px 4px 1px 5px;
  border: 1px solid rgba(138, 127, 112, 0.3);
  background: rgba(5, 4, 5, 0.55);
  font-size: 8px;
  letter-spacing: 0.22em;
  color: var(--ash);
  text-shadow: 0 1px 2px #000;
}

/* For the host only (lobby.js: can-bots, gone once his Start is asked for): in every empty place "Add a bot", quiet
   ash where a name would stand, behind a hollow diamond like an unlit lantern's, warming to firelight when pointed at;
   and under a bot's name, where a player's "Cast out" would be, "Send away" (one click: he is nobody's). */
.slot__bot,
.slot__send {
  display: none;
  position: absolute;
  left: 50%;
  translate: -50% 0;
  padding: 3px 8px;
  border: 1px solid transparent;
  background: none;
  font-family: var(--caps);
  font-weight: 700;
  font-size: 9.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--smoke);
  pointer-events: auto;
  cursor: var(--cursor-hot);
  transition: color 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease, text-shadow 0.25s ease;
}

.slot__bot {
  top: 0;
  align-items: center;
  gap: 8px;
  font-size: 10px;
  color: var(--ash);
  text-shadow: 0 1px 2px #000, 0 0 10px #000;
  animation: burn-in 1.2s 0.3s var(--ease-out) both;
}

.slot__bot::before {
  content: '';
  width: 6px;
  height: 6px;
  border: 1px solid rgba(138, 127, 112, 0.55);
  background: rgba(5, 4, 5, 0.7);
  transform: rotate(45deg);
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
}

.slot__send {
  top: calc(100% + 6px);
}

#lobby.can-bots .slot:not(.is-taken) .slot__bot {
  display: inline-flex;
}

#lobby.can-bots .slot.is-bot .slot__send {
  display: block;
}

.slot__bot:is(:hover, :focus-visible) {
  color: var(--hot);
  border-color: rgba(255, 140, 60, 0.35);
  text-shadow: 0 1px 2px #000, 0 0 10px rgba(255, 140, 40, 0.7);
  outline: none;
}

.slot__bot:is(:hover, :focus-visible)::before {
  border-color: var(--hot);
  box-shadow: 0 0 6px rgba(255, 140, 40, 0.8);
}

.slot__bot:active {
  color: var(--white-hot);
}

.slot__send:is(:hover, :focus-visible) {
  color: var(--parchment);
  border-color: rgba(201, 178, 141, 0.3);
  outline: none;
}

/* Beside your own character, at his hip, a forged arrowhead each way to another character (lobby.js); placed in
   metres from his feet (--m: the pixels a metre up is there). Dim iron until you point at one; then it glows. */
.slot {
  --m: 90px;
}

.slot__turn {
  display: none;
  position: absolute;
  top: calc(-6px - var(--m) * 0.95);
  left: calc(50% - var(--m) * 0.68);
  width: 30px;
  height: 46px;
  padding: 0;
  border: 0;
  background: none;
  transform: translate(-50%, -50%);
  pointer-events: auto;
  cursor: var(--cursor-hot);
}

.slot__turn--next {
  left: calc(50% + var(--m) * 0.68);
}

.slot.is-me .slot__turn {
  display: block;
}

.lobby.no-stage .slot__turn {
  display: none;
}

.slot__turn:focus-visible {
  outline: none;
}

.slot__turn::before {
  content: '';
  position: absolute;
  inset: 10px 8px;
  background: var(--ash);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 32'%3E%3Cpath d='M21 2 3 16l18 14-5-14z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 32'%3E%3Cpath d='M21 2 3 16l18 14-5-14z'/%3E%3C/svg%3E") center / contain no-repeat;
  opacity: 0.6;
  filter: drop-shadow(0 1px 1px #000);
  transition: background 0.25s ease, opacity 0.25s ease, filter 0.25s ease, transform 0.25s var(--ease-out);
}

.slot__turn--next::before {
  transform: scaleX(-1);
}

.slot__turn:is(:hover, :focus-visible)::before {
  background: var(--hot);
  opacity: 1;
  filter: drop-shadow(0 0 4px rgba(255, 140, 40, 0.8)) drop-shadow(0 0 10px rgba(255, 60, 20, 0.45));
}

.slot__turn--prev:is(:hover, :focus-visible)::before {
  transform: translateX(-2px);
}

.slot__turn--next:is(:hover, :focus-visible)::before {
  transform: scaleX(-1) translateX(-2px);
}

.slot__turn:active::before {
  background: var(--white-hot);
}

/* Over a character's head, the name of the one its player has just become: it burns in as he climbs out of the
   ground, in his lantern's colour, and fades again. */
.slot__hero {
  position: absolute;
  top: calc(-6px - var(--m) * 3.02);
  left: 50%;
  padding-left: 0.34em;
  transform: translate(-50%, -50%);
  font-family: var(--caps);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--bone);
  text-shadow: 0 1px 2px #000, 0 0 12px color-mix(in srgb, var(--c, var(--ember)) 70%, transparent);
  opacity: 0;
  pointer-events: none;
}

.slot__hero.is-shown {
  animation: hero-name 3.4s 0.45s ease both;
}

@keyframes hero-name {
  0% { opacity: 0; filter: blur(3px) brightness(2.2); }
  16% { opacity: 1; filter: none; }
  72% { opacity: 1; }
  100% { opacity: 0; }
}

.lobby__bar {
  display: flex;
  align-items: stretch;
  gap: 12px;
  margin-top: clamp(10px, 2vh, 20px);
}

/* One line of buttons, all as tall: Ready and the host's Start side by side, Leave lobby quieter right after them. */
.lobby__bar > button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 52px;
}

.lobby__bar > button[hidden] {
  display: none;
}

.lobby__ready {
  padding-top: 0;
  padding-bottom: 0;
}

/* Ready: the button says "Ready"; once you are, it offers "Not ready" and turns to dark iron, while READY burns under
   your name and your lantern blazes. */
.ready__on,
.lobby__ready[aria-pressed='true'] .ready__off {
  display: none;
}

.lobby__ready[aria-pressed='true'] .ready__on {
  display: inline;
}

.lobby__ready[aria-pressed='true'] {
  border-color: rgba(255, 140, 60, 0.45);
  background: linear-gradient(180deg, #2a1a14 0%, #160d0a 100%);
  color: var(--parchment);
  box-shadow: inset 0 1px 0 rgba(255, 200, 160, 0.08), 0 0 18px rgba(255, 90, 31, 0.18);
}

.lobby__ready[aria-pressed='true']:is(:hover, :focus-visible) {
  color: var(--white-hot);
  filter: none;
  box-shadow: inset 0 1px 0 rgba(255, 200, 160, 0.12), 0 0 0 1px rgba(255, 140, 60, 0.5), 0 0 26px rgba(255, 90, 31, 0.3);
}

.slot.is-ready::after {
  content: 'Ready';
  display: block;
  margin-top: 5px;
  padding-left: 0.42em;
  text-align: center;
  font-family: var(--caps);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--hot);
  text-shadow: 0 0 10px rgba(255, 140, 40, 0.85), 0 0 22px rgba(255, 60, 20, 0.5), 0 1px 2px #000;
  animation: ready-tag 0.25s var(--ease-out) both;
}

@keyframes ready-tag {
  from { opacity: 0; transform: translateY(-4px); filter: brightness(2.2); }
}

/* Your lantern's colour: a row of glass diamonds hovering over your own character's head (lobby.js moves it into
   your place; until you have one it waits out of sight). Yours burns in its colour, the ones other players carry are
   dark and cannot be picked; pointing at a free one makes it glow. */
.lantern-pick {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 0 6px;
}

.lobby__bar .lantern-pick {
  display: none;
}

.slot .lantern-pick {
  position: absolute;
  left: 50%;
  top: calc(-6px - var(--m) * 2.7);
  gap: 13px;
  padding: 8px 10px;
  transform: translate(-50%, -50%);
  pointer-events: auto;
  animation: burn-in 1.2s 0.6s var(--ease-out) both;
}

.slot .lantern-pick__label {
  display: none;
}

.lobby.no-stage .slot .lantern-pick {
  top: -30px;
}

.lantern-pick__label {
  margin-right: 4px;
  font-family: var(--caps);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ash);
}

.lantern-pick__color {
  width: 20px;
  height: 20px;
  padding: 0;
  border: 1px solid color-mix(in srgb, var(--c) 60%, transparent);
  background: color-mix(in srgb, var(--c) 22%, #050405);
  transform: rotate(45deg);
  cursor: var(--cursor-hot);
  outline: none;
  transition: background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease, opacity 0.15s ease;
}

/* Pointed at: a free colour lights up in full, lifts and glows, so you see what you would get. */
.lantern-pick__color:hover,
.lantern-pick__color:focus-visible {
  border-color: color-mix(in srgb, var(--c) 70%, #fff);
  background: var(--c);
  box-shadow: 0 0 12px var(--c), 0 0 26px color-mix(in srgb, var(--c) 55%, transparent);
  transform: translateY(-3px) rotate(45deg) scale(1.3);
}

.lantern-pick__color[aria-checked='true'] {
  border-color: #fff;
  background: var(--c);
  box-shadow: 0 0 10px var(--c), 0 0 22px color-mix(in srgb, var(--c) 60%, transparent);
  transform: rotate(45deg) scale(1.3);
  cursor: var(--cursor);
}

.lantern-pick__color:disabled {
  opacity: 0.22;
  cursor: var(--cursor);
  pointer-events: none;
}

/* Start and Leave carry their own colours: forged gold that calls when everyone is ready, and blood crimson. */
.lobby__start,
.lobby__leave {
  padding: 0 26px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.32em;
}

/* Ready and Start stand side by side as a pair, just as wide. */
.lobby__ready,
.lobby__start {
  flex: none;
  width: 230px;
  min-width: 0;
}

.lobby__start {
  font-size: 13px;
}

.lobby__start {
  border-color: rgba(255, 181, 71, 0.6);
  background: linear-gradient(180deg, rgba(255, 181, 71, 0.2), rgba(122, 58, 12, 0.28) 60%, rgba(0, 0, 0, 0.45));
  color: var(--white-hot);
  text-shadow: 0 0 10px rgba(255, 181, 71, 0.75), 0 1px 0 #000;
  box-shadow: inset 0 1px 0 rgba(255, 230, 190, 0.25), 0 0 22px rgba(255, 140, 40, 0.3);
  animation: start-call 1.8s ease-in-out infinite;
}

.lobby__start:hover,
.lobby__start:focus-visible {
  color: #fff;
  border-color: var(--hot);
  background: linear-gradient(180deg, rgba(255, 181, 71, 0.34), rgba(150, 70, 14, 0.36) 60%, rgba(0, 0, 0, 0.4));
  box-shadow: inset 0 1px 0 rgba(255, 230, 190, 0.35), 0 0 0 1px rgba(255, 181, 71, 0.5), 0 0 40px rgba(255, 140, 40, 0.55);
}

.lobby__start[disabled] {
  color: var(--parchment);
  text-shadow: 0 1px 0 #000;
  box-shadow: none;
  opacity: 0.4;
  animation: none;
  cursor: var(--cursor);
  pointer-events: none;
}

/* Again from where the lights went out: beside Start, in the same gold but quieter, "Again from" over the stage's
   name. While it shows, Ready, Start and it share the width, so the bar still fits beside the chat. */
.lobby__again {
  flex: 1 1 0;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  padding: 0 14px;
  border-color: rgba(255, 181, 71, 0.42);
  background: linear-gradient(180deg, rgba(255, 181, 71, 0.1), rgba(0, 0, 0, 0.45));
  color: var(--parchment);
  line-height: 1.1;
  text-align: center;
}

.again__from {
  font-size: 9.5px;
  letter-spacing: 0.3em;
  color: var(--ash);
}

.again__stage {
  max-width: 100%;
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--white-hot);
  text-shadow: 0 0 10px rgba(255, 181, 71, 0.5), 0 1px 0 #000;
}

.lobby__again:hover,
.lobby__again:focus-visible {
  border-color: var(--hot);
  box-shadow: inset 0 1px 0 rgba(255, 230, 190, 0.2), 0 0 30px rgba(255, 140, 40, 0.4);
}

.lobby__again[disabled] {
  box-shadow: none;
  opacity: 0.4;
  cursor: var(--cursor);
  pointer-events: none;
}

.lobby__bar.is-again > .lobby__ready,
.lobby__bar.is-again > .lobby__start {
  flex: 1 1 0;
  width: auto;
}

@keyframes start-call {
  50% { box-shadow: inset 0 1px 0 rgba(255, 230, 190, 0.3), 0 0 0 1px rgba(255, 181, 71, 0.35), 0 0 38px rgba(255, 140, 40, 0.55); }
}

/* Leave lobby right after them, a little apart. */
.lobby__leave {
  margin-left: 12px;
  padding: 0 22px;
  font-size: 11px;
  letter-spacing: 0.28em;
  border-color: rgba(255, 31, 75, 0.28);
  background: linear-gradient(180deg, rgba(60, 6, 14, 0.3), rgba(14, 2, 4, 0.5));
  color: #d99aa4;
  text-shadow: 0 1px 0 #000;
}

.lobby__leave:hover,
.lobby__leave:focus-visible {
  color: var(--white-hot);
  border-color: var(--toll);
  background: linear-gradient(180deg, rgba(150, 12, 32, 0.45), rgba(40, 4, 10, 0.6));
  box-shadow: inset 0 0 12px rgba(255, 31, 75, 0.2), 0 0 26px rgba(255, 31, 75, 0.45);
}

/* Join Lobby: every open lobby a lantern in the fog (lobby.js), swaying on its own and lighting the fog round it in the
   colour of the first lantern in it (--c); under it the lobby's name and a diamond per place, lit when taken. */
.browse__list {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 18px 26px;
  margin: 0;
  padding: 8px 0 26px;
  list-style: none;
}

/* The fog they hang in: slow banks drifting past their feet, in front of them. */
.browse__list::after {
  content: '';
  position: absolute;
  left: -12%;
  right: -12%;
  bottom: -6px;
  height: 75%;
  background:
    radial-gradient(ellipse 34% 42% at 18% 72%, rgba(150, 138, 150, 0.11), transparent 70%),
    radial-gradient(ellipse 40% 38% at 58% 82%, rgba(150, 138, 150, 0.09), transparent 70%),
    radial-gradient(ellipse 30% 46% at 88% 70%, rgba(150, 138, 150, 0.1), transparent 70%);
  filter: blur(6px);
  animation: lamp-fog 16s ease-in-out infinite alternate;
  pointer-events: none;
  z-index: 1;
}

.lamp {
  --c: var(--ember);
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 128px;
  padding: 0 0 6px;
  border: 0;
  background: none;
  font-family: var(--caps);
  font-weight: 700;
  font-size: 13px;
  line-height: 1.3;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--parchment);
  cursor: var(--cursor-hot);
  outline: none;
  animation: lamp-in 0.7s var(--ease-out) backwards;
}

/* Its light on the fog round it, breathing as a flame does. */
.lamp::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 48px;
  width: 200px;
  height: 200px;
  translate: -50% -50%;
  border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--c) 30%, transparent), color-mix(in srgb, var(--c) 9%, transparent) 38%, transparent 68%);
  opacity: 0.75;
  animation: lamp-breathe 2.8s var(--phase, 0s) ease-in-out infinite;
  pointer-events: none;
  z-index: -1;
  transition: opacity 0.3s ease;
}

/* The lantern swings a little from its ring, each at its own pace (--sway, --phase from lobby.js). */
.lamp__lantern {
  position: relative;
  width: 44px;
  height: 77px;
  transform-origin: 50% 4%;
  animation: lamp-sway var(--sway, 4.6s) var(--phase, 0s) ease-in-out infinite;
}

/* The flame behind the glass: white-hot at its heart through the lantern's colour, flickering. */
.lamp__glow {
  position: absolute;
  left: 22.5%;
  top: 34.3%;
  width: 55%;
  height: 48.6%;
  background:
    radial-gradient(ellipse 28% 34% at 50% 66%, #fff6e0 0%, color-mix(in srgb, var(--c) 40%, #fff) 35%, transparent 100%),
    radial-gradient(ellipse 70% 70% at 50% 60%, var(--c) 0%, color-mix(in srgb, var(--c) 35%, #000) 100%);
  box-shadow: 0 0 18px color-mix(in srgb, var(--c) 70%, transparent);
  animation: lamp-flicker 1.9s var(--phase, 0s) ease-in-out infinite;
  transition: filter 0.3s ease, opacity 0.3s ease;
}

/* The iron, its edges catching the flame's colour. */
.lamp__iron {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  fill: #110c0b;
  filter: drop-shadow(0 0 1px color-mix(in srgb, var(--c) 60%, transparent));
}

.lamp__name {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-shadow: 0 1px 2px #000;
  transition: color 0.3s ease, text-shadow 0.3s ease;
}

.lamp:hover::before,
.lamp:focus-visible::before {
  opacity: 1;
}

.lamp:hover .lamp__glow,
.lamp:focus-visible .lamp__glow {
  filter: brightness(1.35);
}

.lamp:hover .lamp__name,
.lamp:focus-visible .lamp__name {
  color: var(--white-hot);
  text-shadow: 0 0 12px color-mix(in srgb, var(--c) 80%, transparent), 0 1px 2px #000;
}

/* Full: the lantern burns low and cannot be taken. */
.lamp.is-full {
  color: var(--ash);
  cursor: var(--cursor);
}

.lamp.is-full::before {
  opacity: 0.25;
}

.lamp.is-full .lamp__glow {
  opacity: 0.3;
  animation: none;
}

@keyframes lamp-sway {
  0%, 100% { rotate: -2.5deg; }
  50% { rotate: 2.5deg; }
}

@keyframes lamp-breathe {
  0%, 100% { scale: 1; }
  50% { scale: 1.08; }
}

@keyframes lamp-flicker {
  0%, 100% { opacity: 1; }
  18% { opacity: 0.84; }
  32% { opacity: 0.97; }
  51% { opacity: 0.78; }
  68% { opacity: 1; }
  84% { opacity: 0.9; }
}

@keyframes lamp-in {
  from { opacity: 0; filter: blur(6px); translate: 0 10px; }
}

@keyframes lamp-fog {
  from { translate: -3% 0; }
  to { translate: 3% 0; }
}

.lamp__pips {
  display: flex;
  gap: 7px;
}

.lamp__pips i {
  width: 7px;
  height: 7px;
  border: 1px solid rgba(201, 178, 141, 0.3);
  transform: rotate(45deg);
}

/* A taken place burns in the colour of the lantern already standing there (--c, set by lobby.js). */
.lamp__pips i.is-on {
  border-color: color-mix(in srgb, var(--c, var(--hot)) 75%, #fff);
  background: var(--c, var(--ember));
  box-shadow: 0 0 8px color-mix(in srgb, var(--c, var(--ember)) 80%, transparent);
}

.browse__empty {
  margin: 4px 0 0;
  font-style: italic;
  font-size: 18px;
  color: var(--ash);
}

/* A private lobby's code: a short iron field in capitals and its button, under the lanterns. */
.browse__code {
  display: flex;
  gap: 10px;
  margin-top: 14px;
  max-width: 360px;
}

.browse__code-in {
  width: 140px;
  padding: 10px 12px;
  border: 1px solid rgba(201, 178, 141, 0.24);
  border-bottom-color: rgba(255, 106, 42, 0.55);
  border-radius: 0;
  background: linear-gradient(180deg, rgba(16, 13, 13, 0.78), rgba(7, 6, 6, 0.88));
  font-family: var(--caps);
  font-weight: 700;
  font-size: 16px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--white-hot);
  caret-color: var(--ember);
  outline: none;
  transition: border-color 0.3s ease, box-shadow 0.3s ease;
}

.browse__code-in::placeholder {
  color: var(--parchment);
  opacity: 0.6;
  letter-spacing: 0.2em;
}

.browse__code-in:focus {
  border-color: rgba(255, 106, 42, 0.55);
  border-bottom-color: var(--hot);
  box-shadow: 0 0 18px rgba(255, 90, 31, 0.25);
}

.browse__code-go {
  flex: 1;
}

.browse__back {
  margin-top: 18px;
}

/* ---- Main menu: the lobby's chat ---------------------------------------------------------------------------- */

/* Right beside the lobby, level with its picture and as tall (over its buttons: their 52px and the gap above them),
   over the night with no box round it: what is said rises and fades up into the dark, the newest at the bottom, and
   under it a line to speak on. */
.hall[data-view='lobby'] .chat {
  position: absolute;
  left: calc(var(--lobby-left) + var(--lobby-w) + var(--chat-gap));
  bottom: calc(var(--lobby-bottom) + 52px + clamp(10px, 2vh, 20px));
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 10px;
  width: var(--chat-w);
  height: clamp(286px, 52vh, 494px);
  animation: field-in 0.4s 0.15s var(--ease-out) both;
}

.chat__log {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 6px;
  min-height: 0;
  margin: 0;
  padding: 0;
  overflow: hidden;
  list-style: none;
  -webkit-mask-image: linear-gradient(180deg, transparent, #000 45%);
  mask-image: linear-gradient(180deg, transparent, #000 45%);
}

.chat__line {
  flex: none;
  font-size: 19px;
  line-height: 1.35;
  color: var(--bone);
  text-shadow: 0 1px 2px #000, 0 0 10px rgba(0, 0, 0, 0.85);
  overflow-wrap: anywhere;
  animation: chat-in 0.5s var(--ease-out) both;
}

/* Who said it: in the colour of their lantern (--c, set by lobby.js), glowing a little. */
.chat__who {
  margin-right: 0.6em;
  font-family: var(--caps);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--c, var(--hot));
  text-shadow: 0 0 10px color-mix(in srgb, var(--c, var(--ember)) 60%, transparent), 0 1px 2px #000;
}

/* Who came and who went. */
.chat__line.is-quiet {
  font-style: italic;
  color: var(--ash);
}

@keyframes chat-in {
  from { opacity: 0; filter: blur(4px); translate: 0 6px; }
}

/* Where you speak: a dark iron field with an ember seam along its foot, plain to see, the T that takes you there in
   its corner (gone while you type); it catches fire while you are in it. */
.chat__field {
  position: relative;
  flex: none;
  display: block;
  cursor: var(--cursor-hot);
}

.chat__say {
  display: block;
  width: 100%;
  padding: 12px 46px 12px 14px;
  border: 1px solid rgba(201, 178, 141, 0.24);
  border-bottom-color: rgba(255, 106, 42, 0.55);
  border-radius: 0;
  background: linear-gradient(180deg, rgba(16, 13, 13, 0.78), rgba(7, 6, 6, 0.88));
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.6), 0 0 18px rgba(0, 0, 0, 0.5);
  font-family: var(--serif);
  font-size: 19px;
  color: var(--bone);
  caret-color: var(--ember);
  outline: none;
  cursor: var(--cursor-hot);
  transition: border-color 0.3s ease, box-shadow 0.3s ease;
}

.chat__say::placeholder {
  color: var(--parchment);
  opacity: 0.75;
  font-style: italic;
}

.chat__say:hover {
  border-color: rgba(255, 106, 42, 0.4);
  border-bottom-color: rgba(255, 106, 42, 0.8);
}

.chat__say:focus {
  border-color: rgba(255, 106, 42, 0.55);
  border-bottom-color: var(--hot);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.6), 0 0 22px rgba(255, 90, 31, 0.3);
}

/* The key that takes you to the field, as a small iron key cap. */
.chat__key {
  position: absolute;
  right: 12px;
  top: 50%;
  translate: 0 -50%;
  min-width: 22px;
  padding: 2px 5px;
  border: 1px solid rgba(201, 178, 141, 0.35);
  background: rgba(0, 0, 0, 0.5);
  font-family: var(--caps);
  font-weight: 700;
  font-size: 11px;
  line-height: 1.4;
  text-align: center;
  color: var(--parchment);
  pointer-events: none;
  transition: opacity 0.2s ease;
}

.chat__field:focus-within .chat__key {
  opacity: 0;
}

/* ---- Main menu: whispers ------------------------------------------------------------------------------------ */

/* Only words: never in the way of a click on what lies under it (in a lobby it hangs over the lantern colours). */
.whisper {
  pointer-events: none;
  min-height: 1.6em;
  max-width: 560px;
  margin: clamp(8px, 2vh, 18px) 0 0;
  font-style: italic;
  font-weight: 500;
  font-size: clamp(17px, 1.5vw, 22px);
  letter-spacing: 0.03em;
  color: var(--hot);
  text-shadow: 0 0 18px rgba(255, 90, 31, 0.65), 0 0 2px rgba(0, 0, 0, 0.9), 0 1px 2px #000;
  opacity: 0;
}

.whisper.is-on {
  opacity: 1;
  animation: whisper-in 1.4s var(--ease-out) backwards;
}

.whisper.is-fading {
  opacity: 0;
  transition: opacity 1.2s ease;
}

/* What broke, if the menu broke while it started (index.html): written high in the dark, over everything. */
.broke {
  position: fixed;
  left: 50%;
  top: 14%;
  z-index: 50;
  width: min(640px, calc(100% - 32px));
  margin: 0;
  transform: translateX(-50%);
  text-align: center;
  font-family: var(--serif);
  font-style: italic;
  font-size: 19px;
  line-height: 1.35;
  color: var(--bone);
  text-shadow: 0 0 18px #000, 0 1px 3px #000;
  pointer-events: none;
}

/* A candle-like flicker as the words appear. */
@keyframes whisper-in {
  0% { opacity: 0; filter: blur(8px); transform: translateX(-10px); }
  25% { opacity: 1; filter: blur(1px); }
  32% { opacity: 0.5; }
  45% { opacity: 1; filter: none; transform: none; }
}

/* ---- Main menu: settings ------------------------------------------------------------------------------------ */

.settings {
  display: none;
  width: min(460px, 100%);
  margin-top: clamp(12px, 2.6vh, 26px);
  padding: 22px 26px 24px;
}

/* Settings snaps open: a menu screen, not a page loading. */
.hall[data-view='settings'] .settings {
  display: block;
  animation: panel-in 0.28s var(--ease-out) both;
}

/* Settings takes the greeting's place too, so all of it (down to the way back) fits under the title. */
.hall[data-view='settings'] .gates,
.hall[data-view='settings'] .hall__greeting,
.hall[data-view='settings'] .whisper {
  display: none;
}

.settings__title {
  margin: 0 0 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
  font-family: var(--caps);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--bone);
  text-shadow: 0 0 14px rgba(255, 90, 31, 0.45);
}

/* A row: closer together in a lower window. (The graphics have a view of their own since nachtwacht 2067, so the
   rows have room again.) */
.setting {
  display: grid;
  grid-template-columns: 136px 1fr 38px;
  align-items: center;
  gap: 14px;
  min-height: clamp(32px, 4.7vh, 46px);
}

.setting__label {
  font-family: var(--caps);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ash);
}

.setting__value {
  font-family: var(--caps);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--parchment);
}

.setting--switch {
  cursor: var(--cursor-hot);
  user-select: none;
}

.setting--switch:hover .setting__label {
  color: var(--parchment);
}

.settings__note {
  margin: 2px 0 clamp(10px, 1.8vh, 18px);
  font-style: italic;
  font-size: 16px;
  color: var(--smoke);
}

/* Segmented High / Low choice. */
.seg {
  grid-column: 2 / 4;
  justify-self: start;
  display: inline-flex;
  border: 1px solid var(--line);
}

.seg__opt {
  padding: 8px 18px 7px;
  border: 0;
  background: rgba(0, 0, 0, 0.4);
  font-family: var(--caps);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--smoke);
  cursor: var(--cursor-hot);
  transition: color 0.3s ease, background 0.3s ease, box-shadow 0.3s ease;
}

.seg__opt + .seg__opt {
  border-left: 1px solid var(--line);
}

.seg__opt:hover,
.seg__opt:focus-visible {
  outline: none;
  color: var(--bone);
}

.seg__opt:focus-visible {
  box-shadow: inset 0 0 0 1px rgba(255, 181, 71, 0.8), 0 0 16px rgba(255, 90, 31, 0.4);
}

.seg__opt[aria-checked='true'] {
  color: var(--white-hot);
  background: linear-gradient(180deg, #8e2214, var(--blood) 50%, var(--blood-deep));
  text-shadow: 0 0 10px rgba(255, 181, 71, 0.8);
  box-shadow: inset 0 1px 0 rgba(255, 200, 160, 0.2), 0 0 18px rgba(255, 90, 31, 0.35);
}

/* Iron switch with an ember diamond. */
.switch__input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.switch {
  grid-column: 2 / 4;
  justify-self: start;
  position: relative;
  width: 48px;
  height: 22px;
  border: 1px solid rgba(201, 178, 141, 0.3);
  background: rgba(0, 0, 0, 0.5);
  transition: border-color 0.3s ease, background 0.3s ease, box-shadow 0.3s ease;
}

.switch::after {
  content: '';
  position: absolute;
  top: 4px;
  left: 5px;
  width: 12px;
  height: 12px;
  background: var(--smoke);
  transform: rotate(45deg);
  transition: left 0.35s var(--ease-out), background 0.3s ease, box-shadow 0.3s ease;
}

.switch__input:checked + .switch {
  border-color: rgba(255, 106, 42, 0.6);
  background: rgba(40, 10, 4, 0.6);
}

.switch__input:checked + .switch::after {
  left: 29px;
  background: var(--ember);
  box-shadow: 0 0 12px var(--ember), 0 0 24px rgba(255, 31, 75, 0.4);
}

.switch__input:focus-visible + .switch {
  border-color: var(--hot);
  box-shadow: 0 0 0 3px var(--ember-soft), 0 0 16px rgba(255, 90, 31, 0.35);
}

/* At the foot of Settings: the controls and a new oath side by side, the way back under them. */
.settings__end {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.settings__oath,
.settings__back {
  grid-column: 1 / -1;
}

/* The graphics: a panel of their own in Settings' place, settings.js's rows in it (the very rows of the Esc menu). */
.gfx {
  display: none;
  width: min(460px, 100%);
  margin-top: clamp(12px, 2.6vh, 26px);
  padding: 22px 26px 24px;
}

.hall[data-view='gfx'] .gfx {
  display: block;
  animation: panel-in 0.28s var(--ease-out) both;
}

.hall[data-view='gfx'] .gates,
.hall[data-view='gfx'] .hall__greeting,
.hall[data-view='gfx'] .whisper {
  display: none;
}

.gfx__back {
  width: 100%;
  margin-top: 16px;
}

/* Behind the gates (nachtwacht 2070): the developer panel, over everything, built from the server's answer (main.js). */
.devp {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: grid;
  place-items: center;
  padding: 16px;
  background: rgba(4, 3, 3, 0.72);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  user-select: none;
  -webkit-user-select: none;
  animation: panel-in 0.28s var(--ease-out) both;
}

.devp__box.panel {
  display: flex;
  flex-direction: column;
  width: min(980px, 100%);
  max-height: calc(100vh - 32px);
  padding: 22px 26px;
}

.devp__title {
  margin: 0 0 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
  font-family: var(--caps);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--bone);
  text-shadow: 0 0 14px rgba(255, 90, 31, 0.45);
}

/* Its parts side by side; taller than a low window, they scroll under the title and Back stays in view. */
.devp__cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  align-items: start;
  gap: 18px 26px;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.devp__head {
  margin: 0 0 8px;
  font-family: var(--caps);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ash);
}

.devp__list {
  display: grid;
  gap: 4px;
}

.devp__item {
  display: block;
  width: 100%;
  padding: 7px 10px 6px;
  border: 1px solid transparent;
  border-radius: 0;
  background: rgba(0, 0, 0, 0.35);
  color: var(--parchment);
  font: inherit;
  font-size: 15px;
  text-align: left;
  cursor: var(--cursor-hot);
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease, box-shadow 0.2s ease;
}

.devp__item:hover,
.devp__item:focus-visible {
  outline: none;
  color: var(--white-hot);
  border-color: rgba(255, 106, 42, 0.5);
  background: rgba(40, 10, 4, 0.55);
  box-shadow: 0 0 14px rgba(255, 90, 31, 0.25);
}

.devp__back {
  width: 100%;
  margin-top: 16px;
}

/* A new oath: a panel of its own in Settings' place, the login's iron fields in it. */
.oath {
  display: none;
  width: min(460px, 100%);
  margin-top: clamp(12px, 2.6vh, 26px);
  padding: 22px 26px 24px;
}

.hall[data-view='oath'] .oath {
  display: block;
  animation: panel-in 0.28s var(--ease-out) both;
}

.hall[data-view='oath'] .gates,
.hall[data-view='oath'] .hall__greeting,
.hall[data-view='oath'] .whisper {
  display: none;
}

.oath .field + .field {
  margin-top: 6px;
}

/* Sworn: the note burns gold instead of warning. */
.oath__note.is-sworn {
  color: var(--hot);
  text-shadow: 0 0 14px rgba(255, 140, 40, 0.5);
}

.oath__back {
  width: 100%;
  margin-top: 10px;
}

/* The controls: a panel of their own in Settings' place, each key an iron cap beside what it does. */
.keys {
  display: none;
  width: min(540px, 100%);
  margin-top: clamp(12px, 2.6vh, 26px);
  padding: 22px 26px 24px;
}

.hall[data-view='keys'] .keys {
  display: block;
  animation: panel-in 0.28s var(--ease-out) both;
}

.hall[data-view='keys'] .gates,
.hall[data-view='keys'] .hall__greeting,
.hall[data-view='keys'] .whisper {
  display: none;
}

/* Longer than a low window: the list scrolls under the title, the way back stays in view. */
.keys__list {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: baseline;
  gap: 8px 18px;
  max-height: min(58vh, 560px);
  margin: 0;
  padding-right: 6px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 90, 31, 0.35) transparent;
}

.keys__list dt {
  font-family: var(--caps);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--ash);
}

.keys__list dt b {
  display: inline-block;
  min-width: 24px;
  margin-right: 4px;
  padding: 4px 6px 3px;
  border: 1px solid rgba(201, 178, 141, 0.3);
  background: rgba(0, 0, 0, 0.45);
  box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.55), 0 0 10px rgba(255, 90, 31, 0.12);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-align: center;
  color: var(--bone);
}

.keys__list dd {
  margin: 0;
  font-family: var(--serif);
  font-size: 16px;
  line-height: 1.25;
  color: var(--parchment);
}

.keys__list hr {
  grid-column: 1 / -1;
  width: 100%;
  margin: 2px 0;
  border: 0;
  border-top: 1px solid var(--line);
}

.keys__back {
  width: 100%;
  margin-top: 16px;
}

@media (max-width: 640px) {
  .keys__list {
    column-gap: 12px;
  }

  .keys__list dt {
    white-space: normal;
  }
}

/* ---- Sliders ------------------------------------------------------------------------------------------------ */

.slider {
  width: 100%;
  height: 18px;
  margin: 0;
  appearance: none;
  -webkit-appearance: none;
  background: linear-gradient(90deg, var(--ember) var(--fill, 70%), rgba(201, 178, 141, 0.2) var(--fill, 70%)) center / 100% 2px no-repeat;
  cursor: var(--cursor-hot);
  outline: none;
}

.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 12px;
  height: 12px;
  border: 1px solid #ffb48a;
  background: var(--ember);
  transform: rotate(45deg);
  box-shadow: 0 0 10px var(--ember);
}

.slider::-moz-range-track {
  background: transparent;
}

.slider::-moz-range-thumb {
  width: 11px;
  height: 11px;
  border: 1px solid #ffb48a;
  border-radius: 0;
  background: var(--ember);
  transform: rotate(45deg);
  box-shadow: 0 0 10px var(--ember);
}

.slider:focus-visible::-webkit-slider-thumb {
  background: var(--hot);
  box-shadow: 0 0 0 4px var(--ember-soft), 0 0 16px var(--ember), 0 0 30px rgba(255, 31, 75, 0.5);
}

.slider:focus-visible::-moz-range-thumb {
  background: var(--hot);
  box-shadow: 0 0 0 4px var(--ember-soft), 0 0 16px var(--ember), 0 0 30px rgba(255, 31, 75, 0.5);
}

/* ---- HUD (bottom bar) --------------------------------------------------------------------------------------- */

.hud {
  position: fixed;
  left: clamp(20px, 8vw, 140px);
  right: clamp(20px, 4vw, 60px);
  bottom: 26px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  font-family: var(--caps);
  font-size: 10.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--smoke);
  /* 'both' holds the hidden first keyframe during the delay; the end state is the normal, visible style. */
  animation: hud-in 1.5s 3s ease both;
  /* It spans the whole bottom edge but draws only the sound at its right: the rest lets the pointer through to what is
     under it (the lobby's buttons stand that low, and their lower half did nothing). */
  pointer-events: none;
}

@keyframes hud-in {
  from { opacity: 0; transform: translateY(8px); }
}

.hud__sound {
  display: flex;
  margin-left: auto;
  align-items: center;
  gap: 12px;
  pointer-events: auto;
}

/* Show FPS: the frame counter, quiet ash; it flushes ember for a half second in which a frame came much too late. */
.hud__fps {
  min-width: 6em;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--ash);
  transition: color 0.2s ease;
}

.hud__fps.is-hitch {
  color: var(--ember);
}

.hud__mute {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid var(--line);
  background: rgba(0, 0, 0, 0.4);
  color: var(--parchment);
  cursor: var(--cursor-hot);
  transition: border-color 0.3s ease, color 0.3s ease, box-shadow 0.3s ease;
}

.hud__mute:hover,
.hud__mute:focus-visible {
  outline: none;
  color: var(--white-hot);
  border-color: rgba(255, 106, 42, 0.6);
  box-shadow: 0 0 16px rgba(255, 90, 31, 0.35);
}

.hud__mute svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.hud__mute .cross {
  display: none;
}

.hud__mute[aria-pressed='true'] .wave {
  display: none;
}

.hud__mute[aria-pressed='true'] .cross {
  display: inline;
}

.hud__volume {
  width: 110px;
}

/* ---- Game start --------------------------------------------------------------------------------------------- */
/* The host has pressed Start (game-start.js, added to the body with .is-game-start): the hour strikes in the lobby's
   own scene. One layer over everything; once the night has closed in round the lobby's picture its canvas moves in
   here to fill the screen (lobby-stage.js), without the soft edges it has in the panel, and over it a 2D canvas holds
   the night, the black at the end and, without a 3D stage, the plain version. The film grain stays on top of it, and
   the pointer is gone until the graveyard. */
.game-start {
  position: fixed;
  inset: 0;
  z-index: 30;
  cursor: none;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}

.game-start__veil,
.game-start .lobby__view {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.game-start .lobby__view {
  -webkit-mask-image: none;
  mask-image: none;
  cursor: none;
}

body.is-game-start .grain {
  z-index: 31;
}

/* ---- Stage shake on a bell toll ----------------------------------------------------------------------------- */

#app.is-tolling {
  animation: toll 0.7s ease-out;
}

@keyframes toll {
  10% { transform: translate(-3px, 2px); }
  25% { transform: translate(3px, -2px); }
  45% { transform: translate(-2px, 1px); }
  70% { transform: translate(1px, 0); }
}

/* ---- Graphics quality and Reduce flashes -------------------------------------------------------------------- */

body.quality-low .panel {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

body.quality-low .grain {
  animation: none;
}

body.quality-low .enter::after {
  filter: none;
}

body.reduce-flashes .flash {
  display: none;
}

/* ---- Small screens ------------------------------------------------------------------------------------------ */

/* A low window: the room kept free above the title for its flames comes off the gap above the panel. */
@media (max-height: 860px) {
  #app[data-stage='menu'] .login {
    margin-top: 22px;
  }
}

@media (max-height: 700px) {
  #app:not([data-stage='splash']) .divider {
    margin: 10px 0 6px;
  }
}

@media (max-width: 640px) {
  .panel {
    padding: 22px 18px 22px;
  }

  .setting {
    grid-template-columns: 104px 1fr 34px;
    gap: 10px;
  }

  .hud__volume {
    width: 80px;
  }
}

/* ---- Fields like any (nachtwacht 2075) ------------------------------------------------------------------------ */

/* What is selected: embers behind its glyphs while the field has focus; the caret hides meanwhile, as in any field. */
.field:focus-within .field__glyph.is-sel {
  background: rgba(255, 106, 42, 0.32);
  box-shadow: 0 0 10px rgba(255, 90, 31, 0.3);
}

.field.has-sel:focus-within .field__caret {
  opacity: 0;
  animation: none;
}

/* A password's eye, in the panel's iron at the right of its box: the glyphs keep clear of it. */
.field--secret .field__box {
  padding-right: 48px;
}

.field__eye {
  position: absolute;
  right: 7px;
  bottom: 7px;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 33px;
  height: 33px;
  padding: 0;
  border: 1px solid rgba(201, 178, 141, 0.16);
  border-radius: 0;
  background: linear-gradient(180deg, rgba(46, 38, 32, 0.9), rgba(12, 9, 8, 0.92));
  box-shadow: inset 0 1px 0 rgba(255, 235, 200, 0.07), 0 1px 3px rgba(0, 0, 0, 0.7);
  color: var(--ash);
  cursor: var(--cursor-hot);
  transition: color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}

.field__eye svg {
  width: 20px;
  height: 14px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.field__eye .eye__iris {
  fill: currentColor;
  stroke: none;
}

.field__eye .eye__slash {
  opacity: 0;
  transition: opacity 0.15s ease;
}

.field__eye:hover,
.field__eye:focus-visible {
  outline: none;
  color: var(--hot);
  border-color: rgba(255, 106, 42, 0.55);
  box-shadow: inset 0 1px 0 rgba(255, 235, 200, 0.07), 0 0 12px rgba(255, 90, 31, 0.3);
}

.field.is-open .field__eye {
  color: var(--hot);
  border-color: rgba(255, 106, 42, 0.6);
}

.field.is-open .field__eye .eye__slash {
  opacity: 1;
}

/* Shown: the oath as letters, in a face that tells capitals from small ones. */
.field--secret.is-open .field__glyph {
  width: auto;
  height: auto;
  font-family: var(--serif);
  font-weight: 400;
  font-size: 18px;
  letter-spacing: 0.04em;
}

.field--secret.is-open .field__glyph::before {
  content: none;
}

/* ---- Behind the gates in the hall (nachtwacht 2076) ----------------------------------------------------------- */

/* A developer's own column on the right of the main menu, built from the server's answer (main.js): its parts open on
   a click, one at a time, and scroll under the title when they are long. */
.devside.panel {
  position: fixed;
  top: 50%;
  right: clamp(16px, 3vw, 56px);
  z-index: 20;
  display: none;
  flex-direction: column;
  width: min(300px, 30vw);
  max-height: calc(100vh - 96px);
  padding: 16px 18px 18px;
  translate: 0 -50%;
}

.devside.panel.is-on {
  display: flex;
  animation: panel-in 0.28s var(--ease-out) both;
}

.devside__title {
  margin: 0 0 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
  font-family: var(--caps);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--bone);
  text-shadow: 0 0 14px rgba(255, 90, 31, 0.45);
}

.devside__parts {
  display: grid;
  align-content: start;
  gap: 4px;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.devside__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 8px 4px 7px;
  border: 0;
  border-bottom: 1px solid rgba(201, 178, 141, 0.1);
  border-radius: 0;
  background: transparent;
  font-family: var(--caps);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ash);
  text-align: left;
  cursor: var(--cursor-hot);
  transition: color 0.2s ease, text-shadow 0.2s ease;
}

.devside__head::after {
  content: '';
  width: 6px;
  height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.2s ease;
}

.devside__head:hover,
.devside__head:focus-visible,
.devside__part.is-open .devside__head {
  outline: none;
  color: var(--hot);
  text-shadow: 0 0 10px rgba(255, 90, 31, 0.5);
}

.devside__part.is-open .devside__head::after {
  transform: rotate(45deg);
}

.devside__list {
  display: none;
  gap: 3px;
  padding: 6px 0 8px;
}

.devside__part.is-open .devside__list {
  display: grid;
  animation: panel-in 0.2s var(--ease-out) both;
}

.devside .devp__item {
  padding: 6px 9px 5px;
  font-size: 14px;
}

/* No room beside the menu on a narrow screen: the code's panel is there for it. */
@media (max-width: 820px) {
  .devside.panel.is-on {
    display: none;
  }
}
