/**
 * The entry screen.
 *
 * A split composition: identity holds the left field, the entry card sits at a
 * fixed 440px on the right. The screen it replaced put the logo, the lede and
 * the card all in one 476px column and centred it in a 1440px viewport, using
 * a third of the width and reading as a phone screenshot on a monitor.
 *
 * Colour lives in themes.css under `.entry`, which is on the section itself.
 * Nothing here may hard-code a colour — the same rule as everywhere else, and
 * the reason the whole app can reskin from one file.
 *
 * Below 64rem it collapses back to one column. That is not a lesser version:
 * the single column was designed on purpose for a phone and nothing about it
 * was broken. It inherits the new type scale and the new palette, and the two
 * layouts differ only in how they use width they actually have.
 */

.entry {
  /* The design is drawn at 1440 with 96px gutters, so its content box is
     1248px. Cap the composition at exactly that and centre it: above 1440 the
     layout is then IDENTICAL to the drawing rather than merely bounded. A
     looser cap still worked but left the card slack in its column, opening the
     gap to 337px where the design has 161. Unbounded it reached 1800px at
     3112. In rem, so a skin's --root-size still scales it. */
  --e-max: 73.5rem;

  min-height: 100%;
  position: relative;
  /* Deliberately NOT overflow:hidden. The decorations are all `inset: 0` so
     they cannot overflow on their own, and clipping here meant that when the
     card grew — four seats in the revival picker — the footer and the bottom
     of the card were cut off with no way to scroll to them. */
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 6rem 7rem;
  background: radial-gradient(120% 90% at 50% 8%,
              var(--e-void-hi) 0%, #080C0B 44%, var(--e-void) 100%);
  font-family: var(--font-display);
}

.entry__inner {
  position: relative;
  width: 100%; max-width: var(--e-max); margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr);
  align-items: center;
  column-gap: 4.5rem;
}

/* Three stacked atmospheres, none of which may catch a click: a slow verdigris
   bloom off-centre, a vignette that pulls the corners down, and a hairline at
   the very bottom so the screen has an edge rather than fading out. */
.entry__glow, .entry__vignette, .entry__edge { position: absolute; pointer-events: none; }
.entry__glow {
  inset: 0;
  background: radial-gradient(52% 40% at 32% 30%,
              rgb(var(--e-verdigris) / .09) 0%, rgb(var(--e-verdigris) / 0) 72%);
  animation: entry-breathe 11s ease-in-out infinite;
}
.entry__vignette { inset: 0; box-shadow: inset 0 0 240px 70px rgb(0 0 0 / .8); }
.entry__edge {
  left: 0; right: 0; bottom: 0; height: 1px;
  background: linear-gradient(90deg, rgb(var(--e-verdigris) / 0) 0%,
              rgb(var(--e-verdigris) / .4) 50%, rgb(var(--e-verdigris) / 0) 100%);
}
@keyframes entry-breathe { 0%, 100% { opacity: .55 } 50% { opacity: 1 } }
@media (prefers-reduced-motion: reduce) {
  .entry__glow { animation: none; opacity: .8; }
}

/* ── the left field ──────────────────────────────────────────────────────── */

.entry__identity {
  position: relative; min-width: 0; max-width: 100%;
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 1.75rem;
}
.entry__mark {
  display: block; width: 100%; max-width: 38.75rem; height: auto;
  margin-left: -1.1rem;          /* optical: the lockup carries its own bearing */
  filter: drop-shadow(0 14px 38px rgb(0 0 0 / .75));
}
.entry__tagline {
  margin: 0; font-size: 2.125rem; line-height: 1.2;
  font-style: italic; color: var(--e-tagline);
}
.entry__lede {
  margin: 0; max-width: 44ch; font-size: 1.25rem; line-height: 1.7;
  color: var(--e-body); text-wrap: pretty;
}
/* Mono, wide-tracked, tiny. Every UI label on this screen is set this way so
   that the serif is reserved for the game's voice and never used as chrome. */
.entry__kicker {
  margin: .25rem 0 0; font-family: var(--font-mono);
  font-size: .75rem; letter-spacing: .3em; text-transform: uppercase;
  color: var(--e-quiet);
}

/* ── the card ────────────────────────────────────────────────────────────── */

.entry__card {
  position: relative; justify-self: end;
  width: 27.5rem; max-width: 100%; min-width: 0;
  padding: 2.75rem 2.5rem 2.5rem;
  border: 1px solid rgb(var(--e-rule) / .10); border-radius: 4px;
  background: linear-gradient(180deg, rgb(var(--e-card-top) / .96) 0%,
              rgb(var(--e-card-bot) / .96) 100%);
  box-shadow: 0 40px 90px rgb(0 0 0 / .65), inset 0 1px 0 rgb(255 255 255 / .04);
  display: flex; flex-direction: column; gap: 1.5rem;
}
.entry__group { display: flex; flex-direction: column; gap: .625rem; }
.entry__label {
  font-family: var(--font-mono); font-size: .6875rem; letter-spacing: .22em;
  text-transform: uppercase; color: var(--e-label);
}
.entry__input {
  width: 100%; box-sizing: border-box; padding: .9375rem 1.125rem;
  font-family: var(--font-display); font-size: 1.3125rem;
  color: var(--e-bone); background: var(--e-field);
  border: 1px solid rgb(255 255 255 / .09); border-radius: 3px; outline: none;
  transition: border-color .15s, box-shadow .15s;
}
.entry__input::placeholder { color: var(--e-quiet); }
.entry__input:focus {
  border-color: rgb(var(--e-verdigris) / .6);
  box-shadow: 0 0 0 3px rgb(var(--e-verdigris) / .14);
}
/* The sigil is four characters read aloud down a phone. Mono, tracked wide,
   centred: it is a code, not a word. */
.entry__input--sigil {
  flex: 1; min-width: 0; font-family: var(--font-mono);
  font-size: 1rem; letter-spacing: .3em; text-transform: uppercase;
}

/* Bone, not gold and not grey. The button this replaced was #c9d0d8 — a COOL
   desaturated grey, which is the universal disabled convention, and that is
   why it read as switched off rather than because of its contrast, which was
   already 12.9:1. Bone is the same low chroma but warm and lighter, and it is
   the highest-contrast surface available on this base at 16.7:1. */
.entry__go {
  width: 100%; padding: 1.0625rem 1.25rem;
  font-family: var(--font-display); font-size: 1.375rem; font-weight: 600;
  letter-spacing: .02em; color: var(--e-ink); background: var(--e-bone);
  border: 1px solid #FFFDF6; border-radius: 3px; cursor: pointer;
  box-shadow: 0 10px 30px rgb(240 234 220 / .14);
  transition: background .15s, box-shadow .15s;
}
.entry__go:hover { background: #FFFBF0; box-shadow: 0 12px 34px rgb(240 234 220 / .2); }

.entry__or {
  display: flex; align-items: center; gap: 1rem;
  font-family: var(--font-mono); font-size: .6875rem; letter-spacing: .2em;
  text-transform: uppercase; color: var(--e-quiet);
}
.entry__or::before, .entry__or::after {
  content: ""; flex: 1; height: 1px; background: rgb(var(--e-rule) / .08);
}

.entry__row { display: flex; gap: .625rem; }
/* Secondary actions are verdigris, the same hue as focus and hover. One colour
   with one meaning: this is live, you may touch it. */
.entry__answer {
  padding: .875rem 1.375rem; font-family: var(--font-display); font-size: 1.1875rem;
  color: var(--e-verd-lit); background: transparent;
  border: 1px solid rgb(var(--e-verdigris) / .42); border-radius: 3px;
  cursor: pointer; transition: background .15s, border-color .15s;
}
.entry__answer:hover {
  background: rgb(var(--e-verdigris) / .12);
  border-color: rgb(var(--e-verdigris) / .75);
}

/* ── revival ─────────────────────────────────────────────────────────────── */

.entry__seats { display: flex; flex-direction: column; gap: .5rem; }
.entry__seat {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  gap: .75rem; padding: .625rem .875rem; text-align: left; cursor: pointer;
  font-family: var(--font-display); font-size: 1.0625rem; color: var(--e-bone);
  background: rgb(var(--e-verdigris) / .06);
  border: 1px solid rgb(var(--e-verdigris) / .3); border-radius: 3px;
  transition: border-color .15s, background .15s;
}
.entry__seat:hover {
  border-color: rgb(var(--e-verdigris) / .7);
  background: rgb(var(--e-verdigris) / .13);
}
.entry__seat-state {
  font-family: var(--font-mono); font-size: .625rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--e-quiet);
}
.entry__seat-state--free { color: var(--e-verd-lit); }
.entry__note {
  margin: 0; font-family: var(--font-mono); font-size: .75rem; line-height: 1.6;
  letter-spacing: .04em; color: var(--e-label);
}
.entry__none {
  align-self: flex-start; font-family: var(--font-mono); font-size: .6875rem;
  letter-spacing: .1em; color: var(--e-quiet); background: none; border: 0;
  cursor: pointer; padding: 0;
}
.entry__none:hover { color: var(--e-verd-lit); }
/* Rust, and only here. Errors do not get to be the accent colour. */
.entry__error {
  margin: 0; font-family: var(--font-mono); font-size: .75rem;
  letter-spacing: .04em; color: var(--e-rust);
}

/* ── the foot ────────────────────────────────────────────────────────────── */

/* Absolute so the composition above it can be vertically centred against the
   whole screen. `.entry` grows with its content, so on a short viewport this
   moves down with the card rather than sitting on top of it. */
.entry__foot {
  position: absolute; left: 6rem; right: 6rem; bottom: 2.75rem;
  max-width: var(--e-max); margin-inline: auto;
  display: flex; align-items: center; justify-content: space-between; gap: 2rem;
}
.entry__nav { display: flex; flex-wrap: wrap; gap: 1.75rem; font-size: 1rem; }
.entry__nav a { color: var(--e-nav); text-decoration: none; }
.entry__nav a:hover { color: var(--e-verd-lit); }
.entry__legal {
  margin: 0; max-width: 52ch; text-align: right;
  font-size: .8125rem; line-height: 1.6; color: var(--e-legal);
}
.entry__legal b { font-weight: 400; color: var(--e-nav); }

/* ── one column ──────────────────────────────────────────────────────────── */

@media (max-width: 64rem) {
  .entry { padding: 3rem 1.5rem 3.5rem; }
  .entry__inner {
    grid-template-columns: minmax(0, 1fr); justify-items: center;
    row-gap: 2.5rem;
  }
  .entry__identity { align-items: center; text-align: center; gap: 1.25rem; }
  .entry__mark { margin-left: 0; max-width: 30rem; }
  .entry__tagline { font-size: 1.75rem; }
  .entry__lede { font-size: 1.0625rem; }
  /* width, not max-width, was the bug: a fixed 440px card forces the grid
     column to 440px, so on a 390px phone everything centres against a column
     wider than the screen and the whole composition shifts right. `.entry`
     clips with overflow:hidden, so it does not even report a scroll. */
  .entry__card { justify-self: center; width: 100%; max-width: 27.5rem; }
  /* The foot stops being absolute or it sits on top of the card. */
  .entry__foot {
    position: static; flex-direction: column; gap: 1.25rem; text-align: center;
  }
  .entry__nav { justify-content: center; gap: 1.25rem; font-size: .9375rem; }
  .entry__legal { text-align: center; }
}

@media (max-width: 30rem) {
  .entry { padding: 2rem 1rem 2.5rem; }
  .entry__card { padding: 1.75rem 1.25rem 1.5rem; }
  .entry__tagline { font-size: 1.5rem; }
  .entry__mark { max-width: 22rem; }
}

/* ══ The Uprising (the lobby) ══════════════════════════════════════════════
   Same palette and the same type discipline as the entry screen; see
   themes.css, where `.gathering` shares the `.entry` block.

   Three structural moves over the screen it replaces, each measured against
   it: the action sat 309px ABOVE the second setting it commits you to, the
   empty seats were a "(1/4)" you had to interpret, and Mode was a native
   select sitting two sections below a set of option cards. */

.gathering {
  --e-max: 73.5rem;
  min-height: 100%;
  position: relative;
  display: flex; flex-direction: column;
  padding: 1.75rem 4.5rem 1.25rem;
  box-sizing: border-box;
  background: radial-gradient(120% 90% at 50% 6%,
              var(--e-void-hi) 0%, #080C0B 44%, var(--e-void) 100%);
  font-family: var(--font-display);
}
.gath__inner {
  position: relative; width: 100%; max-width: var(--e-max); margin-inline: auto;
  flex: 1; display: flex; flex-direction: column;
}

/* ── header ─────────────────────────────────────────────────────────────── */
.gath__head {
  display: flex; align-items: center; justify-content: space-between; gap: 2rem;
  padding-bottom: 1.375rem;
  border-bottom: 1px solid rgb(var(--e-rule) / .07);
}
/* 212px in a bar rather than 300px of vertical space: the full lockup has
   already done its job one screen earlier. */
.gath__mark { display: block; width: 13.25rem; height: auto; }
.gath__head-right { display: flex; align-items: center; gap: 1.75rem; }
/* A button, because it was the only bare-text control on a page where Copy
   sigil, Copy link, the cycle cards and Descend are all bordered -- set in the
   serif beside a mono label, it read as a caption rather than as something you
   press. It takes the page's own secondary treatment (.gath__copy) rather than
   a new one, so the lobby has one outline button and not two that nearly
   match. Not the accent fill: that belongs to Descend, and not red either,
   since leaving a lobby costs nothing. */
.gath__leave {
  font-family: var(--font-display); font-size: .9375rem; color: var(--e-nav);
  background: none; cursor: pointer; white-space: nowrap;
  border: 1px solid rgb(var(--ui-text-600-rgb) / .4);
  padding: .5rem .95rem; border-radius: .375rem;
  transition: color .14s, border-color .14s;
}
.gath__leave:hover { color: var(--e-bone); border-color: rgb(var(--ui-text-600-rgb) / .75); }

/* ── the two shapes of a lobby ──────────────────────────────────── */
/* CENTRED OVER THE CARD, IN THE SERIF, AND ATTACHED TO IT. As a mono label in
   the page header it was the smallest type in the lobby and sat outside the
   thing it governed, so the choice made before every other one was the one
   nobody looked at. It is the modal's own header now -- file-folder tabs, the
   chosen one continuing the card's top edge, so the tab and the card read as
   one object rather than as a control and a panel. */
/* `width: 100%` IS LOAD-BEARING BESIDE `margin-inline: auto`. `.gath__inner`
   is a flex column, so an auto inline margin on a stretched item turns it
   shrink-to-fit: the strip collapsed to its own text and the two tabs sat as
   small boxes in the middle of the page instead of spanning the card. Same
   trap the lightbox figure hit, where auto margins beat `justify-self:
   stretch` and collapsed the art to 82px. */
.gath__tabs {
  display: flex; gap: .5rem;
  width: 100%; max-width: 34rem; margin: 1.75rem auto 0;
}
.gath__tab {
  flex: 1; position: relative; cursor: pointer;
  padding: .8rem 1rem .85rem; display: flex;
  align-items: center; justify-content: center; gap: .55rem;
  font-family: var(--font-display); font-size: 1.375rem; line-height: 1;
  white-space: nowrap; color: var(--e-quiet);
  background: none;
  border: 1px solid transparent; border-bottom: 0;
  border-radius: 4px 4px 0 0;
  transition: color .15s, background .15s, border-color .15s;
}
.gath__tab:hover:not(:disabled) { color: var(--e-body); }
/* The chosen tab IS the top of the card: same border, same gradient, and one
   pixel of the card's own fill dropped over the seam so the two share an edge
   instead of stacking. */
.gath__tab.is-on {
  color: var(--e-bone);
  border-color: rgb(var(--e-rule) / .10);
  background: linear-gradient(180deg, rgb(var(--e-card-top) / .96) 0%,
              rgb(var(--e-card-top) / .96) 100%);
}
.gath__tab.is-on::after {
  content: ""; position: absolute; left: 1px; right: 1px; bottom: -1px;
  height: 2px; background: rgb(var(--e-card-top) / .96);
}
/* Disabled ONLY on the solo tab, and only once somebody has actually arrived.
   It is not a dimmed control to be ignored: it is the lobby saying the choice
   has been made for you by a person joining. */
.gath__tab:disabled { cursor: not-allowed; color: var(--e-legal); }
/* The seat count on the tab, so how many people are here is legible without
   opening the panel that lists them. */
.gath__tab-n {
  font-family: var(--font-mono); font-size: .6875rem; line-height: 1;
  padding: .24rem .4rem; border-radius: 2px;
  color: var(--e-verdigris); background: rgb(var(--e-verdigris) / .13);
}

/* ── body ───────────────────────────────────────────────────────────────── */
/* No top padding: the chosen tab is the card's top edge and a gap between
   them makes the two read as a control and a panel again. */
.gath__body { flex: 1; display: block; }
.gath__card { max-width: 34rem; margin-inline: auto; border-radius: 0 0 4px 4px; }
.gath__left { display: flex; flex-direction: column; gap: 1.75rem; }
/* ONE CENTRED MODAL AT EVERY WIDTH. The sigil column used to be the grid's
   other half, which made the tab above one column reveal a panel in the other
   -- a control pointing away from what it does. It is the card's first group
   now, so there is one layout rather than two and the tab strip governs
   exactly what sits beneath it. 34rem is the measure the card already had
   (1.05fr of a 2.05fr grid at the max width), so nothing reflowed. */
.gath__left { padding-bottom: .5rem; }

/* The sigil is a utility, not the hero: it is a code you hand to a friend.
   Left-aligned with the two things you do to it sitting beside it. */
.gath__sigil-row { display: flex; align-items: center; gap: 1.25rem; }
.gath__sigil {
  font-family: var(--font-display); font-size: 3.75rem; line-height: 1;
  letter-spacing: .16em; color: var(--e-bone);
  font-variant-numeric: lining-nums;
}
.gath__copies { display: flex; flex-direction: column; gap: .5rem; }
.gath__copy {
  padding: .5625rem 1rem; font-family: var(--font-mono); font-size: .6875rem;
  letter-spacing: .16em; text-transform: uppercase; cursor: pointer;
  color: var(--e-label); background: transparent;
  border: 1px solid rgb(var(--e-rule) / .14); border-radius: 3px;
  transition: color .15s, border-color .15s, background .15s;
}
.gath__copy:hover { color: var(--e-pewter); border-color: rgb(var(--e-rule) / .3); }
.gath__copy--on { color: var(--e-verd-lit); border-color: rgb(var(--e-verdigris) / .42); }
.gath__copy--on:hover {
  background: rgb(var(--e-verdigris) / .12); color: var(--e-verd-lit);
  border-color: rgb(var(--e-verdigris) / .75);
}

/* ── seats ──────────────────────────────────────────────────────────────── */
.gath__seats-head { display: flex; align-items: baseline; justify-content: space-between; }
.gath__count {
  font-family: var(--font-mono); font-size: .6875rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--e-quiet);
}
.gath__seats { display: flex; flex-direction: column; gap: .5rem; }
.gath__seat {
  display: flex; align-items: center; gap: .875rem; padding: 1rem 1.25rem;
  background: rgb(var(--e-card-top) / .9);
  border: 1px solid rgb(var(--e-verdigris) / .28); border-radius: 3px;
}
/* Dashed and unfilled: an empty seat should look like an absence, not a row. */
.gath__seat--empty { background: none; border: 1px dashed rgb(var(--e-rule) / .13); }
.gath__dot {
  width: .5rem; height: .5rem; border-radius: 50%;
  background: rgba(110, 154, 141, .35);
}
.gath__dot.is-live {
  background: rgb(var(--e-verdigris)); box-shadow: 0 0 10px rgb(var(--e-verdigris) / .8);
}
.gath__dot--empty {
  background: none; border: 1px solid rgb(var(--e-rule) / .22);
  animation: gath-pulse 3.4s ease-in-out infinite;
}
@keyframes gath-pulse { 0%, 100% { opacity: .35 } 50% { opacity: .9 } }
@media (prefers-reduced-motion: reduce) { .gath__dot--empty { animation: none } }
.gath__name { flex: 1; font-size: 1.3125rem; color: var(--e-bone); }
.gath__empty {
  flex: 1; font-family: var(--font-mono); font-size: .8125rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--e-quiet);
}
.gath__tag {
  font-family: var(--font-mono); font-size: .6875rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--e-quiet);
}
.gath__tag--first { color: var(--e-verd-lit); }

/* ── the settings card ──────────────────────────────────────────────────── */
.gath__card {
  padding: 1.75rem 2.125rem 1.625rem; box-sizing: border-box;
  border: 1px solid rgb(var(--e-rule) / .10); border-radius: 4px;
  background: linear-gradient(180deg, rgb(var(--e-card-top) / .96) 0%,
              rgb(var(--e-card-bot) / .96) 100%);
  box-shadow: 0 40px 90px rgb(0 0 0 / .6), inset 0 1px 0 rgb(255 255 255 / .04);
  display: flex; flex-direction: column; gap: 1.375rem;
}
/* THE CYCLE IS A SELECT, because three Cycles will not fit a row of cards and
   Cosmic Horror is the third. Native, so a phone gets its own picker; the
   chosen option's sentence sits beneath it, which is the pattern the Mode
   control already proved here. */
.gath__sel {
  width: 100%; box-sizing: border-box; cursor: pointer;
  font-family: var(--font-display); font-size: 1.25rem; color: var(--e-bone);
  padding: .8rem 2.4rem .8rem 1.125rem;
  background: var(--e-field); border: 1px solid rgb(var(--e-rule) / .16);
  border-radius: 3px; appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 1.15rem center, right .85rem center;
  background-size: .3rem .3rem, .3rem .3rem;
  background-repeat: no-repeat;
}
.gath__sel:focus-visible { outline: 2px solid rgb(var(--e-verdigris) / .7); outline-offset: 2px; }
.gath__opts { display: grid; grid-template-columns: 1fr 1fr; gap: .625rem; }
/* Three modes need three columns. Two cards cannot hold Standard plus both
   coaches, and dropping one would delete a shipped feature. */
.gath__opts--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* FIVE RUNGS, and auto-fit rather than five fixed columns: at 390px five
   tracks give each name about 70px and "Implacable" does not fit, so the row
   wraps to two or three of its own accord with no breakpoint and no second
   layout to keep in step. Same device the seats use in the Counter of Fates. */
.gath__opts--rungs { grid-template-columns: repeat(auto-fit, minmax(6.2rem, 1fr)); }
.gath__opt--rung { padding: .55rem .4rem; text-align: center; }

/* ── the ladder, as one axis ─────────────────────────────────────────── */
/* Five rungs on one line, which is the only shape a five-stop SCALE should
   take: as buttons they wrapped 4+1 on a desktop and 3+2 on a phone, and two
   groups of rungs is not what 0 through 12 means. */
.gath__rungrow {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; margin-bottom: .55rem;
}
.gath__rungname { font-family: var(--font-display); font-size: 1.25rem; color: var(--e-bone); }
/* Implacable is a DIFFERENT RULE, not a fourth degree of the same one. The
   button said so with a dashed border; a linear track cannot say it with
   spacing, so the name carries it. */
.gath__rungname.is-apart {
  border-bottom: 1px dashed rgb(var(--e-rule) / .5); padding-bottom: .1rem;
}
.gath__slider {
  width: 100%; margin: 0; display: block; cursor: pointer;
  appearance: none; background: none; height: 1.25rem;
}
.gath__slider::-webkit-slider-runnable-track {
  height: .1875rem; border-radius: 2px; background: rgb(var(--e-rule) / .22);
}
.gath__slider::-moz-range-track {
  height: .1875rem; border-radius: 2px; background: rgb(var(--e-rule) / .22);
}
/* The thumb needs a negative margin on webkit to sit ON the track rather than
   hanging off its top edge -- (track height minus thumb height) / 2. */
.gath__slider::-webkit-slider-thumb {
  appearance: none; width: 1.05rem; height: 1.05rem; border-radius: 50%;
  margin-top: -.43rem;
  background: var(--e-bone); border: 0;
  box-shadow: 0 0 0 4px rgb(var(--e-verdigris) / .16);
}
.gath__slider::-moz-range-thumb {
  width: 1.05rem; height: 1.05rem; border-radius: 50%;
  background: var(--e-bone); border: 0;
  box-shadow: 0 0 0 4px rgb(var(--e-verdigris) / .16);
}
.gath__slider:focus-visible { outline: 2px solid rgb(var(--e-verdigris) / .7); outline-offset: 4px; }
.gath__slider:disabled { cursor: not-allowed; opacity: .45; }
/* Ticks say where the other four stops are without naming them, which is what
   buys the single line. `space-between` puts the first and last under the
   thumb's own travel ends. */
.gath__rungticks {
  display: flex; justify-content: space-between;
  padding: 0 .5rem; margin-top: -.15rem;
}
.gath__rungtick {
  width: 1px; height: .3rem; background: rgb(var(--e-rule) / .3);
}
.gath__rungtick.is-on { background: var(--e-verdigris); height: .45rem; }

/* The readout is the slider's own foot, so the group gap above it goes. */
.entry__group--tight { margin-top: -.75rem; }

/* GENERATES NO BOX unless the two-column modal asks it to, so the solo tab and
   every phone get the flex column the card has always been. */
.gath__setup { display: contents; }

/* ── multiplayer is a wider modal, and two columns ───────────────────── */
/* THE TWO TABS HOLD DIFFERENT AMOUNTS, so one width for both is a compromise
   that suits neither: single player is a column of settings, multiplayer is
   that column plus a sigil and four seats, which is ~450px more and put the
   Descend button under the fold. Above the lobby's own breakpoint the card
   widens and splits -- who is here on the left, what you are about to do on
   the right. Below it, both tabs stay the same stacked column.

   `grid-row` IS NOT USED, and that is deliberate. Every row in this card is
   IMPLICIT, so `1 / -1` resolves to line 1 and spans nothing -- the trap the
   log rail hit. The columns are assigned per child instead: the seats take
   column one, everything else takes column two, and auto-placement stacks the
   right-hand column down the rows on its own. */
@media (min-width: 64.001rem) {
  .gath__inner.is-many .gath__tabs,
  .gath__inner.is-many .gath__card { max-width: 55rem; }
  .gath__inner.is-many .gath__card {
    display: grid; align-items: start;
    grid-template-columns: 18rem minmax(0, 1fr);
    column-gap: 2.5rem; row-gap: 1.375rem;
  }
  /* THE SIGIL STACKS IN A COLUMN, which is what the 30rem query already does
     for a phone and for the same reason: beside a 3.75rem sigil the two copy
     buttons had 112px and broke "Copy sigil" over two lines. Stacked, the code
     gets the column's full width and the buttons sit side by side. */
  .gath__inner.is-many .gath__sigil-row {
    flex-direction: column; align-items: flex-start; gap: .875rem;
  }
  .gath__inner.is-many .gath__copies { flex-direction: row; }
  .gath__inner.is-many .gath__left { grid-column: 1; padding-bottom: 0; }
  /* `grid-column: 2` is STATED rather than relied on. Auto-placement already
     puts this in column two -- the seats claim column one of row one, so the
     next item takes the only free cell in that row -- and a check written to
     catch the split breaking passed with it removed. Saying it means the
     column is a fact of the rule rather than a consequence of child order. */
  .gath__inner.is-many .gath__setup {
    grid-column: 2; display: flex; flex-direction: column; gap: 1.375rem;
  }
  /* A tab switch changes the card's width as well as its contents, so the
     change is stated rather than jumped. */
  .gath__tabs, .gath__card { transition: max-width .18s ease; }
}
@media (prefers-reduced-motion: reduce) {
  .gath__tabs, .gath__card { transition: none; }
}
.gath__opt--rung .gath__opt-t { font-size: 1rem; }
/* The top rung is a DIFFERENT RULE, not a fourth degree of the same one, so it
   is set apart rather than given more of whatever the others have. */
.gath__opt--rung.is-apart { border-style: dashed; }
.gath__rungsay {
  margin-top: .55rem;
  font-size: .9375rem;
  line-height: 1.5;
  color: var(--e-body);
}
.gath__opt {
  padding: 1rem 1.125rem; text-align: left; cursor: pointer;
  display: flex; flex-direction: column; gap: .375rem;
  background: none; border: 1px solid rgb(var(--e-rule) / .09); border-radius: 3px;
  font-family: var(--font-display);
  transition: border-color .15s, background .15s;
}
.gath__opt:hover:not(:disabled) { border-color: rgb(var(--e-rule) / .22); }
.gath__opt.is-on {
  border-color: rgb(var(--e-verdigris) / .6);
  background: rgb(var(--e-verdigris) / .08);
}
.gath__opt.is-locked { cursor: not-allowed; }
.gath__opt-t { font-size: 1.25rem; color: var(--e-bone); }
.gath__opt.is-locked .gath__opt-t { color: var(--e-label); }
/* The Force mark beside a Rite's name, same brightness split as the deck. */
.gath__opt-pip { margin-left: .4em; color: var(--e-quiet); }
.gath__opt.is-on .gath__opt-pip { color: var(--e-verdigris); }
/* `.gath__opt-n` stood here: the mono "3 DESCENTS" suffix on the Cycle CARD.
   The select carries that in the option's own text now, so the rule matched
   nothing -- named by `check-refs` the moment the cards went, which is the
   whole reason that gate exists. Deleted rather than parked: the cards are
   superseded, not hidden, and `.gath__opt-b` beside it is still live on the
   coaching modes. */
.gath__opt-b { font-size: .9375rem; line-height: 1.5; color: var(--e-body); }
.gath__opt.is-locked .gath__opt-b { color: var(--e-legal); }
/* ── readout ────────────────────────────────────────────────────────────── */
/* Was one 13px run-on line. Value over label, in cells, so it survives the
   numbers changing as players join and as the Cycle changes. */
.gath__readout {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 1px;
  background: rgb(var(--e-rule) / .08);
  border: 1px solid rgb(var(--e-rule) / .08); border-radius: 3px; overflow: hidden;
}
/* CENTRED, because the cells are a ROW of independent facts rather than a
   list to read down. Left-aligned, each numeral sat hard against its own
   divider and the eye had to find five different starting points; over the
   middle of its own label the pair reads as one object. `align-items` as well
   as `text-align`, since a flex column stretches its children by default and
   text-align alone leaves a wrapped label centred inside a full-width box. */
.gath__cell {
  padding: .875rem .75rem; background: #0A0F0D;
  display: flex; flex-direction: column; align-items: center; gap: .3125rem;
  text-align: center;
}
.gath__cell-v {
  font-size: 1.625rem; line-height: 1; color: var(--e-bone);
  font-variant-numeric: lining-nums tabular-nums;
}
.gath__cell-l {
  font-family: var(--font-mono); font-size: .625rem; letter-spacing: .13em;
  text-transform: uppercase; color: var(--e-label); white-space: nowrap;
}

/* ── the commitment, last ───────────────────────────────────────────────── */
.gath__go-wrap { display: flex; flex-direction: column; gap: .625rem; margin-top: auto; }
.gath__consequence {
  text-align: center; font-family: var(--font-mono); font-size: .6875rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--e-quiet);
}
.gath__waiting { margin: 0; text-align: center; font-size: 1rem; color: var(--e-body); }

/* ── narrow ─────────────────────────────────────────────────────────────── */
@media (max-width: 64rem) {
  .gathering { padding: 1.5rem 1.25rem 2.5rem; }
  .gath__body { row-gap: 2.25rem; }
  .gath__mark { width: 10rem; }
  .gath__tabs { margin-top: 1.25rem; gap: .375rem; }
  .gath__tab { font-size: 1.1875rem; padding: .7rem .5rem .75rem; }
  .gath__head { padding-bottom: 1.25rem; }
  .gath__head-right { gap: 1rem; }
  /* Four lines of wrapped label in a phone header, and redundant: the seat
     below already reads FIRST / YOU and the button says "Descend alone". */
  .gath__head-right .entry__label { display: none; }
  .gath__sigil { font-size: 2.75rem; }
  .gath__opts, .gath__opts--three { grid-template-columns: minmax(0, 1fr); }
  /* NOT one column, and it has to say so. The rule above catches the rungs
     through `.gath__opts`, which is the same trap the lane rename sprang: a
     narrow block naming selectors silently takes in anything that shares one.
     Five rungs stacked is the page-to-read this control was shaped to avoid,
     and `auto-fit` already wraps them properly -- three across at 390px. */
  /* FOUR RITES GO 2x2, NOT 3+1. The ladder was five and wanted three across;
     it is a slider now and this class is the Rite's alone, so the minimum can
     say what four items want. 7rem fits two tracks in a 309px card and still
     lets a desktop take all four across. */
  .gath__opts--rungs { grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); }
  .gath__readout { grid-auto-flow: row; grid-auto-columns: auto;
                   grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* AN ODD CELL COUNT LEAVES A HOLE, and the container's own fill shows
     through the 1px gap grid, so the hole renders as an extra cell with
     nothing in it. The last one spans instead. Three columns would have been
     the other answer and does not fit: "BREACH BONUS" is 96px on one line
     against a 102px track.

     `NTH-CHILD` CANNOT COUNT THESE. Alpine leaves the `x-for` `<template>` in
     the DOM as a sibling, so a four-cell readout is five children and
     `:last-child:nth-child(odd)` fired on every count. The parity is bound
     from `setupCells.length` instead -- asked of the data, which is the only
     thing that actually knows. */
  .gath__readout.is-odd .gath__cell:last-child { grid-column: 1 / -1; }
  .gath__card { padding: 1.5rem 1.25rem; }
}
@media (max-width: 30rem) {
  .gath__sigil-row { flex-direction: column; align-items: flex-start; gap: .875rem; }
  .gath__copies { flex-direction: row; }
}

/* ── the creature smoulders ─────────────────────────────────────────────────
 *
 * Two embers sitting exactly over the eye slits in the artwork. The positions
 * are measured off pewter-1240.webp, not estimated: the sockets centre on
 * 46.3% and 52.8% across and 33.5% down, and because they are percentages of
 * the wrapper they hold at every size the mark is served at.
 *
 * It is ASLEEP. The cycle is long and mostly dark, the light never reaches
 * full, and there is no symmetry to the two — the right ember lags the left by
 * a beat, because two lights pulsing together read as a machine and two
 * lights pulsing apart read as something breathing. */
/* The wrapper must BE the image's box, or the percentages below resolve
   against something else and the embers miss the sockets. Two ways that went
   wrong, both measured: as a full-width block the embers landed 3.1% right,
   and with fit-content they still did on desktop, because the mark carries a
   -1.1rem optical bearing that shifts it INSIDE the wrapper. So the bearing
   moves to the wrapper and the image is neutralised — one box, one origin.
   The responsive max-widths stay on .entry__mark, which keeps the size
   decided in a single place. */
.mark-wrap {
  position: relative; display: block; width: fit-content;
  margin-left: -1.1rem;
}
.mark-wrap .entry__mark { position: relative; z-index: 1; margin-left: 0; }
@media (max-width: 64rem) { .mark-wrap { margin-left: 0; } }

.mark-ember {
  position: absolute; z-index: 2;
  width: 4.6%; aspect-ratio: 1;
  top: 33.5%; transform: translate(-50%, -50%);
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(circle,
    rgba(210, 245, 232, .95) 0%,
    rgba(143, 184, 171, .75) 34%,
    rgba(110, 154, 141, .35) 58%,
    transparent 72%);
  mix-blend-mode: screen;
  filter: blur(1.5px);
  animation: smoulder 9s ease-in-out infinite;
}
.mark-ember--l { left: 46.3%; }
.mark-ember--r { left: 52.8%; animation-delay: .55s; }

@keyframes smoulder {
  0%, 62%   { opacity: 0;    filter: blur(2.4px); }
  68%       { opacity: .30;  filter: blur(1.6px); }
  71%       { opacity: .09;  filter: blur(2.2px); }   /* the catch before it takes */
  76%       { opacity: .72;  filter: blur(1.2px); }
  82%       { opacity: .44;  filter: blur(1.5px); }
  88%       { opacity: .60;  filter: blur(1.3px); }
  100%      { opacity: 0;    filter: blur(2.4px); }
}

/* Something waking up in the corner of your eye is exactly the effect people
 * turn this off for. */
@media (prefers-reduced-motion: reduce) {
  .mark-ember { animation: none; opacity: 0; }
}
