/* Spelling Séance — coven at midnight: deep plum, candle gold, parchment text. */
:root {
  --bg: #17102b;
  --bg2: #241743;
  --card: #2b1d4e;
  --card-line: #4a357f;
  --ink: #f3ead8;
  --ink-dim: #c9bce0;
  --gold: #f7b801;
  --gold-ink: #241a00;
  --violet: #b388ff;
  --scrabble-bg: rgba(179, 136, 255, 0.28);
  --good: #7ee2a8;
  --bad: #ff9d9d;
  --radius: 14px;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  /* touch-action: manipulation disables double-tap-to-zoom (and the old
     300ms tap delay) while keeping pan + pinch — rapid hex taps and rapid
     Deletes on mobile stop zooming the page (Michael, Oct 21, 2026). */
  touch-action: manipulation;
  background: radial-gradient(1200px 700px at 50% -10%, var(--bg2), var(--bg));
  color: var(--ink);
  font-family: 'Libre Franklin', 'Franklin Gothic Medium', 'Franklin Gothic', 'Segoe UI', system-ui, sans-serif;
  min-height: 100vh;
}
.screen { max-width: 680px; margin: 0 auto; padding: 20px 16px 48px; }
.hidden { display: none !important; }

.hero { text-align: center; padding: 18px 0 6px; }
.hero-emoji { font-size: 44px; }
.hero h1 { margin: 4px 0 2px; font-size: 40px; letter-spacing: 0.5px; }
/* Type (Michael's try, Oct 24, 2026): headers in Cormorant Garamond —
   Semibold for the title, Medium for the rest; body copy and the game
   board in Franklin Gothic, served as Libre Franklin (its open-source
   revival — Franklin Gothic itself is commercial, not on Google Fonts). */
h1 { font-family: 'Cormorant Garamond', Georgia, serif; font-weight: 600; }
h2, h3, .cal-title-btn { font-family: 'Cormorant Garamond', Georgia, serif; font-weight: 500; }

.card {
  background: var(--card);
  border: 1px solid var(--card-line);
  border-radius: var(--radius);
  padding: 18px;
  margin: 16px 0;
}
.card h2 { margin: 0 0 8px; font-size: 20px; }
.field-label { display: block; font-weight: 600; margin-bottom: 6px; }
.hint { color: var(--ink-dim); font-size: 14px; line-height: 1.45; }
.hint.small { font-size: 12.5px; }
.gold { color: var(--gold); font-weight: 700; }

input[type="text"] {
  width: 100%;
  background: #1d1338;
  border: 1px solid var(--card-line);
  border-radius: 10px;
  color: var(--ink);
  font-size: 17px;
  padding: 11px 12px;
  outline: none;
}
input[type="text"]:focus { border-color: var(--gold); }

/* The date picker, dressed to match the site's text inputs (Michael,
   Oct 21, 2026: the browser-default pill was an eyesore). color-scheme:
   dark also themes the native calendar popup; the picker icon is
   tinted gold. (No -webkit-appearance: none — that can make the field
   untappable on older iOS Safari.) */
input[type="date"] {
  background: #1d1338;
  border: 1px solid var(--card-line);
  border-radius: 10px;
  color: var(--ink);
  font: inherit;
  font-size: 17px;
  padding: 11px 12px;
  outline: none;
  color-scheme: dark;
}
input[type="date"]:focus { border-color: var(--gold); }
input[type="date"]::-webkit-calendar-picker-indicator {
  filter: invert(72%) sepia(84%) saturate(2200%) hue-rotate(359deg) brightness(101%);
  cursor: pointer;
  padding: 2px;
}

button {
  font: inherit;
  border-radius: 999px;
  border: 1px solid var(--card-line);
  background: #3a2866;
  color: var(--ink);
  padding: 10px 18px;
  cursor: pointer;
}
button:disabled { opacity: 0.45; cursor: not-allowed; }
button.primary { background: var(--gold); border-color: var(--gold); color: var(--gold-ink); font-weight: 700; }
button.secondary { background: transparent; }
button.mini { padding: 5px 12px; font-size: 13.5px; }
#create-btn { width: 100%; margin-top: 10px; font-size: 17px; }
.past-row { display: flex; gap: 8px; align-items: stretch; margin-top: 12px; }
.past-row input[type="date"] { flex: 1; min-width: 0; }
button.nav-btn { padding: 5px 14px; font-size: 19px; line-height: 1; }
button.wide { width: 100%; margin-top: 4px; }

.letter-slots { display: flex; gap: 8px; justify-content: center; margin: 14px 0 6px; flex-wrap: wrap; }
input.slot {
  width: 52px; height: 58px; flex: none;
  text-align: center;
  font-size: 26px !important;
  font-weight: 700;
  text-transform: uppercase;
  cursor: pointer;
  padding: 0 !important;
}
input.slot.is-center { background: var(--gold) !important; color: var(--gold-ink) !important; border-color: var(--gold) !important; }
.board-preview { min-height: 24px; text-align: center; font-size: 14.5px; color: var(--ink-dim); }
.board-preview .warn-error { color: var(--bad); display: block; }
.board-preview .warn-warn { color: var(--gold); display: block; }
.board-preview .stat b { color: var(--ink); }

.join-row { display: flex; gap: 8px; }
.name-row { display: flex; gap: 8px; }
.name-row input { flex: 1; }
.join-row input { flex: 1; text-transform: uppercase; }

/* ===== account row (email-code sign-in, Oct 24, 2026) =====
   Built by src/account.js into #account-card; mirrors the name
   card's rows so it sits quietly at the top of the home screen. */
input[type="email"] {
  width: 100%;
  background: #1d1338;
  border: 1px solid var(--card-line);
  border-radius: 10px;
  color: var(--ink);
  font-size: 17px;
  padding: 11px 12px;
  outline: none;
}
input[type="email"]:focus { border-color: var(--gold); }
.account-row { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.account-label { font-weight: 600; }
.account-who { font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-form { margin-top: 12px; }
.account-line { display: flex; gap: 8px; }
.account-line input { flex: 1; min-width: 0; }
#acct-code { text-align: center; font-weight: 700; letter-spacing: .35em; }
.account-error { color: var(--bad); font-size: 13.5px; margin: 8px 0 0; }
.account-status { color: var(--good); font-size: 13px; margin: 10px 0 0; }
.scrabble-only-sample { background: var(--scrabble-bg); border-radius: 5px; padding: 0 5px; color: #e9dcff; }

/* ===== game screen ===== */
.game-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: nowrap; }
.back { color: var(--ink-dim); text-decoration: none; font-size: 14.5px; white-space: nowrap; }
/* One-line header (Michael, Oct 23, 2026): back link, the meta line,
   and Copy link share a single row. The game code is NOT printed —
   it rides the copied link (and the button's tooltip); the meta line
   takes the middle, centered, and ellipsizes rather than wrapping if
   a narrow screen ever squeezes it. On small phones the back link
   sheds its words and keeps just the arrow. */
.head-meta { flex: 1 1 auto; min-width: 0; display: flex; align-items: baseline; justify-content: center; gap: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mode-badge { font-size: 13px; color: var(--ink-dim); }
/* Copy link stays a small, single-line pill (Michael, Oct 23, 2026):
   compact, but always in the same top-right spot with its label and
   link glyph, so it stays easy to find. nowrap + flex-shrink keep
   the squeezed header from wrapping it into a two-line blob. */
#copy-link-btn { white-space: nowrap; flex-shrink: 0; font-size: 12.5px; padding: 5px 11px; }
.head-meta #board-date:not(.hidden) + .mode-badge::before { content: '· '; }
@media (max-width: 480px) {
  .back-label { display: none; }
  .head-meta { font-size: 12px; }
  .mode-badge { font-size: 12px; }
  .board-date { font-size: 12px; }
}

.presence { display: flex; gap: 8px; flex-wrap: wrap; margin: 8px 0 4px; }
.chip {
  display: inline-flex; align-items: center; gap: 7px;
  border: 1px solid var(--card-line); border-radius: 999px;
  padding: 5px 12px 5px 8px; font-size: 14px; background: var(--card);
}
.chip .dot { width: 12px; height: 12px; border-radius: 50%; }
.chip.offline { opacity: 0.5; }
.chip .pts { color: var(--ink-dim); font-size: 12.5px; }
.chip.me { cursor: pointer; }
.chip.me:hover { border-color: var(--gold); }
.chip .edit-hint { opacity: 0.55; font-size: 12px; }
.rename-chip { gap: 8px; }
.rename-chip input { width: 150px; padding: 4px 8px; font: inherit; }

.rank-row { margin: 8px 0 2px; }
.rank-info { display: flex; justify-content: space-between; align-items: baseline; }
.rank-name { font-size: 20px; font-weight: 700; color: var(--gold); }
.score-line { color: var(--ink-dim); font-size: 14px; }
/* The countdown rides on the bar's own line (compact header pass,
   Oct 23, 2026) instead of owning a row; on a narrow screen it wraps
   under the bar rather than crushing it. */
.progress-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 6px 0 0; }
.progress { flex: 1 1 auto; min-width: 64px; height: 9px; background: #1d1338; border-radius: 99px; overflow: hidden; margin: 0; border: 1px solid var(--card-line); }
.progress-fill { height: 100%; background: linear-gradient(90deg, #8e6bd8, var(--gold)); width: 0%; transition: width 0.4s ease; }
.next-rank { color: var(--ink-dim); font-size: 12.5px; white-space: nowrap; }
.scoring-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 6px; position: relative; }
.scoring-row .seg-btn { padding: 5px 13px; font-size: 14px; }
/* The note is an overlay now (compact header pass, Oct 23, 2026): it
   used to reserve a fixed-height line of its own so the board never
   shifted when it flashed. Floating it as a chip over the play area's
   top air keeps the no-shift behavior — it never takes layout space —
   and gives the line back. */
#scoring-note { position: absolute; top: calc(100% + 5px); left: 0; z-index: 6; pointer-events: none; background: var(--card); border: 1px solid var(--card-line); padding: 3px 9px; border-radius: 999px; opacity: 0; transition: opacity .4s ease; }
#scoring-note.show { opacity: 1; }

.play-area { text-align: center; margin-top: 4px; }
.message { min-height: 24px; font-size: 16px; }
.message.good { color: var(--good); }
.message.bad { color: var(--bad); }
.message .pts-pop { color: var(--gold); font-weight: 700; }
.current-word {
  min-height: 40px; font-size: 30px; font-weight: 700; letter-spacing: 3px;
  text-transform: uppercase;
  /* Default face (Michael, Oct 23, 2026: the IM Fell English SC
     experiment is reverted "for now" — tiles and spelled word both
     inherit the UI font again; re-adding the family here + on .hex
     + the Google Fonts link restores it). */
}
.current-word .not-center { color: var(--violet); }
.current-word .cursor { color: var(--gold); animation: blink 1s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }

.hive { position: relative; width: 300px; height: 300px; margin: 2px auto 8px; }
.hex {
  position: absolute; width: 96px; height: 104px;
  /* Board letters in the default UI face (the IM Fell English SC
     talking-board face was tried Oct 23, 2026 and reverted the same
     day at Michael's request — "for now"). */
  transform: translate(-50%, -50%);
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  background: #3a2866; color: var(--ink);
  font-size: 30px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 0; padding: 0;
  cursor: pointer; user-select: none; -webkit-tap-highlight-color: transparent;
}
.hex:active { filter: brightness(1.35); }
.hex.center { background: var(--gold); color: var(--gold-ink); }
/* Arrange mode (long-press Shuffle, Oct 23, 2026): ring tiles jiggle
   (the transform must keep the centering translate, like every hex
   animation), the center stays put and dims — it can't move — and a
   picked-up tile lifts out of the jiggle with a gold halo. */
.hex.arranging { animation: arrange-jiggle .26s ease-in-out infinite alternate; }
@keyframes arrange-jiggle {
  from { transform: translate(-50%, -50%) rotate(-1.8deg); }
  to { transform: translate(-50%, -50%) rotate(1.8deg); }
}
.hex.arrange-pick {
  animation: arrange-vibrate .16s linear infinite;
  filter: drop-shadow(0 0 9px var(--gold)); z-index: 3;
}
/* The picked-up tile buzzes — fast, jittery, clearly not the gentle
   arrange jiggle (Michael's request, Oct 23, 2026: "make it visibly
   shake or vibrate so people can tell"). */
@keyframes arrange-vibrate {
  0% { transform: translate(-50%, -50%) scale(1.08) translate(0, 0) rotate(0deg); }
  25% { transform: translate(-50%, -50%) scale(1.08) translate(1.4px, -1px) rotate(1.4deg); }
  50% { transform: translate(-50%, -50%) scale(1.08) translate(-1.2px, 1px) rotate(-1.2deg); }
  75% { transform: translate(-50%, -50%) scale(1.08) translate(1px, 1.2px) rotate(-1deg); }
  100% { transform: translate(-50%, -50%) scale(1.08) translate(0, 0) rotate(0deg); }
}
.hex.arrange-fixed { opacity: .65; }
#shuffle-btn.arranging { background: var(--gold); color: var(--gold-ink); border-color: var(--gold); font-weight: 700; }
/* The Shuffle button is long-pressed (Arrange mode): without this,
   iOS answers the hold with text selection and the touch callout on
   the button's label ("⟳ Shuffle" / "✓ Done") instead of the mode
   toggle. -webkit-touch-callout is the iOS-specific half of the fix. */
#shuffle-btn { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
/* One size for both labels (Michael's request, Oct 23, 2026): the
   two labels stack in a single grid cell — an invisible sizer — so
   the button is always as wide as its widest label ("⟳ Shuffle")
   and never resizes when Arrange mode flips it to "✓ Done". */
#shuffle-btn { display: inline-grid; place-items: center; }
#shuffle-btn .sb-label { grid-area: 1 / 1; }
#shuffle-btn .sb-done { visibility: hidden; }
#shuffle-btn.arranging .sb-shuffle { visibility: hidden; }
#shuffle-btn.arranging .sb-done { visibility: visible; }
@media (prefers-reduced-motion: reduce) {
  .hex.arranging { animation: none; }
  .hex.arrange-pick { animation: none; transform: translate(-50%, -50%) scale(1.1); }
}

.controls { display: flex; gap: 10px; justify-content: center; margin-top: 4px; }
.guess-input {
  display: block; margin: 12px auto 0; width: min(300px, 84vw);
  text-align: center; text-transform: uppercase; letter-spacing: 2px;
  font-size: 16px !important; padding: 8px 10px !important; /* >=16px: iOS does not auto-zoom focused inputs */
}
.current-word { cursor: text; }
/* Type-to-enter is a desktop affordance (Michael's call, Oct 23,
   2026): on touch-first devices the bar is hidden entirely — the
   hexes are the keyboard there. (pointer: coarse) tests the primary
   input's capability, not the window width, so touchscreen laptops
   keep the bar while phones and tablets lose it. game.js skips the
   tap-the-word-to-focus path on the same query. */
@media (pointer: coarse) {
  .guess-input { display: none; }
  .current-word { cursor: default; }
}

.found-panel { margin-top: 26px; }
.found-head { display: flex; justify-content: space-between; align-items: center; }
.found-list { list-style: none; margin: 10px 0; padding: 0; columns: 2; column-gap: 26px; }
.found-list li {
  flex-wrap: wrap;
  break-inside: avoid; padding: 4px 6px; margin-bottom: 3px;
  border-radius: 4px;
  display: flex; align-items: baseline; gap: 7px;
  text-transform: lowercase; font-size: 16.5px;
}
.found-list li .pts { margin-left: auto; font-size: 11.5px; color: var(--ink-dim); white-space: nowrap; }
.found-list li[data-word] { cursor: pointer; }
.fdef {
  flex-basis: 100%; padding: 2px 0 4px 16px;
  font-size: 13.5px; line-height: 1.4; color: var(--ink-dim);
  text-transform: none; font-weight: 400;
}
.definition-box {
  margin: 10px 0; padding: 10px 12px; border: 1px solid var(--line, #3a3355);
  border-radius: 10px; background: rgba(141, 127, 184, 0.12); font-size: 15px; line-height: 1.45;
}
.definition-box .def-close { float: right; }
/* Keyboard-open compact mode (game.js toggles .kb-open from visualViewport):
   while typing on a phone, give the hive the whole visible viewport. */
body.kb-open .game-head { display: none; }
body.kb-open .rank-row { display: none; }
body.kb-open .presence { flex-wrap: nowrap; overflow-x: auto; max-height: 48px; }
body.kb-open .found-panel { display: none; }
/* The ⌄ Done button exists only as compact mode's exit door. */
.kb-done { display: none; }
body.kb-open .kb-done { display: inline-block; }
.found-list li.scrabble-only { background: var(--scrabble-bg); }
.found-list li.pangram .w { font-weight: 700; }
.dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: #888; flex: none; }
.legend { color: var(--ink-dim); font-size: 12.5px; line-height: 1.6; }
.found-foot { margin-top: 12px; display: flex; gap: 8px; flex-wrap: wrap; }
.confirm-overlay { position: fixed; inset: 0; background: rgba(8, 6, 20, .68); display: flex; align-items: center; justify-content: center; z-index: 50; padding: 20px; }
.confirm-card { background: var(--card); border: 1px solid var(--card-line); border-radius: var(--radius); padding: 18px; max-width: 340px; width: 100%; }
.confirm-title { font-size: 18px; font-weight: 700; margin-bottom: 6px; }
.confirm-text { color: var(--ink-dim); font-size: 14px; line-height: 1.5; margin: 0 0 14px; }
.confirm-btns { display: flex; gap: 10px; }
.confirm-btns button { flex: 1; }
.vote-panel { background: var(--card); border: 1px solid var(--card-line); border-radius: var(--radius); padding: 10px 12px; margin: 10px 0; font-size: 14.5px; line-height: 1.45; }
.vote-panel .vrow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 8px; font-size: 13.5px; }
.board-date { color: var(--ink-dim); font-size: 13px; margin: 0; }
/* 5 lines visible (Michael, Oct 23, 2026) — the stream still
   holds everything and scrolls; only the window shrank.
   Line ~20px, so 101px ≈ 5 lines. */
.log-stream { background: var(--card); border: 1px solid var(--card-line); border-radius: var(--radius); padding: 8px 12px; margin: 10px 0; max-height: 101px; overflow-y: auto; font-size: 13px; line-height: 1.55; }
.log-stream .lt { color: var(--ink-dim); font-size: 11.5px; font-variant-numeric: tabular-nums; margin-right: 7px; white-space: nowrap; }
.found-list li.missed { opacity: .6; }
.found-list li.missed .w { color: var(--ink-dim); }

.hint-grid { background: var(--card); border: 1px solid var(--card-line); border-radius: var(--radius); padding: 12px 14px; margin: 10px 0; overflow-x: auto; }
.hint-grid table { border-collapse: collapse; font-size: 13.5px; }
.hint-grid th, .hint-grid td { padding: 3px 8px; text-align: center; }
.hint-grid th { color: var(--gold); }
.hint-grid .two-letter { margin-top: 10px; color: var(--ink-dim); font-size: 13.5px; line-height: 1.9; }
.hint-grid .two-letter b { color: var(--ink); }

.celebration { text-align: center; font-size: 19px; padding: 14px; }
@media (max-width: 420px) {
  .hive { width: 270px; height: 270px; }
  .hex { width: 86px; height: 94px; font-size: 27px; }
  input.slot { width: 44px; height: 52px; font-size: 22px !important; }
  .found-list { columns: 1; }
}

/* Your séances (home history) */
.history-list { margin-top: 4px; }
.hist-row { display: grid; grid-template-columns: minmax(0, 1.2fr) .8fr 1fr .8fr .8fr 32px; gap: 10px; align-items: center; padding: 10px 6px; border-bottom: 1px solid var(--card-line); cursor: pointer; border-radius: 6px; }
.hist-row:last-child { border-bottom: none; }
.hist-row:hover, .hist-row:focus-visible { background: rgba(247, 184, 1, .06); outline: none; }
.hc-board { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 700; letter-spacing: .04em; white-space: nowrap; }
.hist-row.is-complete .hc-board { color: var(--gold); }
.hc-code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--ink-dim); font-size: 13px; letter-spacing: .04em; white-space: nowrap; }
.hr-top, .hr-bottom { display: contents; }
/* Explicit placement, columns AND row: the row's cells flatten out
   of the hr-top/hr-bottom wrappers in DOM order (code, date, rank,
   …), which is NOT the header order — columns alone put Date under
   Rank (Michael's "mislabeled" catch), and without the row pin the
   grid packer won't backfill earlier columns, so each séance took
   TWO lines (his "two rows" catch, both Oct 23, 2026). */
.hist-row .hc-board { grid-column: 1; grid-row: 1; }
.hist-row .hc-date { grid-column: 2; grid-row: 1; }
.hist-row .hc-rank { grid-column: 3; grid-row: 1; }
.hist-row .hc-code { grid-column: 4; grid-row: 1; }
.hist-row .hc-updated { grid-column: 5; grid-row: 1; }
.hist-row .hist-forget { grid-column: 6; grid-row: 1; }
.hist-row.is-revealed .hc-board { opacity: .72; }
.hc-date, .hc-updated { color: var(--ink-dim); font-size: 13.5px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.hist-head { display: grid; grid-template-columns: minmax(0, 1.2fr) .8fr 1fr .8fr .8fr 32px; gap: 10px; align-items: center; padding: 2px 6px 6px; margin-top: 8px; border-bottom: 1px solid var(--card-line); }
/* The status filter lives in its own tools row ABOVE the header —
   inside the header grid it read as a phantom seventh column with
   empty cells (Michael's "I still see a status column", Oct 23,
   2026). The header now holds exactly the six column labels. */
.hist-tools { display: flex; justify-content: flex-end; margin-top: 6px; }
.hh { background: none; border: none; color: var(--ink-dim); font: inherit; font-size: 12.5px; font-weight: 700; letter-spacing: .03em; padding: 0; cursor: pointer; text-align: left; white-space: nowrap; }
.hh:hover { color: var(--ink); }
.hh .arr { margin-left: 3px; font-size: 10px; color: var(--gold); }
.hh.active { color: var(--ink); }
.hh-filter .f-state { color: var(--gold); font-weight: 400; }
.hh-filter-wrap { position: relative; }
.filter-menu { position: absolute; top: calc(100% + 8px); right: 0; z-index: 30; background: var(--bg2); border: 1px solid var(--card-line); border-radius: 10px; padding: 8px 10px; display: flex; flex-direction: column; gap: 6px; box-shadow: 0 10px 24px rgba(0, 0, 0, .45); }
.filter-menu label { color: var(--ink); font-size: 13.5px; font-weight: 400; letter-spacing: 0; display: flex; align-items: center; gap: 7px; cursor: pointer; white-space: nowrap; }
.filter-menu input { accent-color: var(--gold); width: 15px; height: 15px; }
@media (max-width: 600px) {
  .hist-head { display: none; }
  .hist-row { grid-template-columns: 1fr auto; grid-template-areas: "top x" "bottom bottom"; row-gap: 3px; }
  .hr-top { display: flex; grid-area: top; align-items: center; justify-content: space-between; gap: 10px; }
  .hr-bottom { display: flex; grid-area: bottom; align-items: center; gap: 10px; flex-wrap: wrap; }
  .hist-row .hist-forget { grid-area: x; grid-column: auto; grid-row: auto; align-self: start; }
}
.rank-pill { display: inline-block; border: 1px solid var(--gold); color: var(--gold); border-radius: 999px; padding: 0 8px; font-size: 12px; font-weight: 700; white-space: nowrap; }
.hist-forget { background: none; border: none; color: var(--ink-dim); cursor: pointer; font-size: 14px; padding: 6px; line-height: 1; }
.hist-forget:hover { color: var(--ink); }
.history-pager { display: flex; align-items: center; gap: 10px; justify-content: center; margin-top: 10px; }
.history-pager.single-page .nav-btn { visibility: hidden; }
button.nav-btn:disabled { cursor: default; opacity: .35; }

/* Word-list toggle (create card) */
.ruleset-row { display: flex; align-items: center; gap: 12px; margin-bottom: 6px; }
.seg { display: inline-flex; border: 1px solid var(--card-line); border-radius: 999px; overflow: hidden; }
.seg-btn { background: transparent; border: none; color: var(--ink-dim); font: inherit; font-weight: 600; padding: 7px 16px; cursor: pointer; }
.seg-btn.is-on { background: var(--gold); color: var(--gold-ink); }

/* Bee calendar (home): month grid of attempted puzzles + best ranks. */
.cal-head { display: flex; align-items: center; justify-content: space-between; margin: 8px 0 6px; }
.cal-title { font-weight: 700; color: var(--gold); }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.cal-dow { color: var(--ink-dim); font-size: 11px; text-align: center; margin-bottom: 2px; }
.cal-day {
  min-height: 46px; border: 1px solid var(--card-line); border-radius: 8px;
  padding: 3px 5px; font-size: 12px; color: var(--ink-dim);
  background: transparent; text-align: left; font-family: inherit;
}
.cal-day.empty { border-color: transparent; }
.cal-day.has-puzzle { color: var(--ink); cursor: pointer; }
.cal-day .dnum { font-weight: 700; }
.cal-day .drank { display: block; font-size: 9.5px; color: var(--ink-dim); margin-top: 1px; }
.cal-day.tried { border-color: #8e6bd8; }
.cal-day.genius { background: rgba(232, 197, 110, .16); border-color: var(--gold); }
.cal-day.genius .drank { color: var(--gold); }
.cal-day.queen { background: rgba(232, 197, 110, .34); border-color: var(--gold); }
.cal-day.today { outline: 2px solid #8e6bd8; outline-offset: -2px; }

/* Genius celebration — "The Hive Hums": a crystal ball materializes
   over the hive's heart, gold ripples roll out of it, the tiles flash
   gold in turn, sparkles rise, and the banner drops in. Fired by
   game.js when the coven's rank crosses into Genius in your view. */
.play-area { position: relative; }
.hive-wrap { position: relative; }
.genius-fx { position: absolute; inset: 0; display: grid; place-items: center; pointer-events: none; opacity: 0; z-index: 4; }
.genius-fx.playing { opacity: 1; }
.fx-ball { font-size: 72px; line-height: 1; filter: drop-shadow(0 0 18px rgba(232, 197, 110, .85)); opacity: 0; }
.genius-fx.playing .fx-ball { animation: fx-ball 2.8s ease forwards; }
@keyframes fx-ball {
  0% { opacity: 0; transform: scale(.3); }
  14% { opacity: 1; transform: scale(1.1); }
  24% { transform: scale(1); }
  78% { opacity: 1; transform: scale(1) translateY(-5px); }
  100% { opacity: 0; transform: scale(1.04) translateY(-8px); }
}
.fx-ripple { position: absolute; left: 50%; top: 50%; width: 110px; height: 110px; margin: -55px 0 0 -55px; border: 2px solid rgba(232, 197, 110, .8); border-radius: 50%; opacity: 0; }
.genius-fx.playing .fx-ripple { animation: fx-ripple 1.9s ease-out forwards; }
.genius-fx.playing .fx-r2 { animation-delay: .28s; }
.genius-fx.playing .fx-r3 { animation-delay: .56s; }
@keyframes fx-ripple {
  0% { opacity: 0; transform: scale(.35); }
  18% { opacity: .9; }
  100% { opacity: 0; transform: scale(2.1); }
}
.fx-spark { position: absolute; font-size: 16px; color: var(--gold); opacity: 0; }
.fx-s1 { left: 38%; top: 46%; }
.fx-s2 { left: 58%; top: 40%; }
.fx-s3 { left: 47%; top: 33%; font-size: 13px; }
.fx-s4 { left: 62%; top: 56%; font-size: 13px; }
.genius-fx.playing .fx-spark { animation: fx-spark 2.2s ease-out forwards; }
.genius-fx.playing .fx-s2 { animation-delay: .25s; }
.genius-fx.playing .fx-s3 { animation-delay: .45s; }
.genius-fx.playing .fx-s4 { animation-delay: .65s; }
@keyframes fx-spark {
  0% { opacity: 0; transform: translateY(6px) scale(.6); }
  25% { opacity: 1; }
  100% { opacity: 0; transform: translateY(-46px) scale(1.15); }
}
.genius-banner {
  position: absolute; top: 2px; left: 50%; z-index: 6; white-space: nowrap;
  transform: translate(-50%, -170%); opacity: 0; pointer-events: none;
  background: linear-gradient(180deg, #3a2a68, #2a1e4f);
  border: 1px solid var(--gold); color: var(--gold);
  padding: 8px 16px; border-radius: 999px; font-size: 14px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, .45);
}
.genius-banner.playing { animation: fx-banner 3s ease forwards; }
@keyframes fx-banner {
  0% { opacity: 0; transform: translate(-50%, -170%); }
  12% { opacity: 1; transform: translate(-50%, 0); }
  82% { opacity: 1; transform: translate(-50%, 0); }
  100% { opacity: 0; transform: translate(-50%, -170%); }
}
.hex.fx-flash { animation: fx-flash .7s ease; }
/* Background only, never color (Michael, Oct 23, 2026): the flash
   also lands on the center hex during the shuffle, and animating
   the letter color made the middle letter flicker purple. */
@keyframes fx-flash { 40% { background: var(--gold); } }
/* Planchette finale (Michael, Oct 23, 2026): once the planchette has
   floated home to the middle, every tile pulses gold a single time —
   the same --gold the other celebrations flash (not a brighter
   yellow; Michael corrected that later the same morning). */
.hive.board-pulse .hex { animation: board-pulse .85s ease-in-out 1; }
@keyframes board-pulse {
  0% { filter: brightness(1); }
  45% { background: var(--gold); filter: brightness(1.22); }
  100% { filter: brightness(1); }
}
@media (prefers-reduced-motion: reduce) {
  .genius-fx, .genius-banner { display: none !important; }
}

/* Queen celebration — "The Emergence": the center tile trembles, the
   swarm bursts out of it into a slowly orbiting court ring, and the
   Queen herself grows out of the tile and holds court. Fired by
   game.js when the coven completes your view's word list. */
.queen-fx { position: absolute; inset: 0; display: grid; place-items: center; pointer-events: none; opacity: 0; z-index: 5; }
.queen-fx.playing { animation: q-master 6.2s ease forwards; }
@keyframes q-master {
  0% { opacity: 0; }
  4% { opacity: 1; }
  88% { opacity: 1; }
  100% { opacity: 0; }
}
.q-ring { position: absolute; left: 50%; top: 50%; width: 0; height: 0; }
.queen-fx.playing .q-ring { animation: q-spin 16s linear infinite; }
@keyframes q-spin { to { transform: rotate(360deg); } }
.q-bee { position: absolute; left: -9px; top: -9px; font-size: 17px; line-height: 1; opacity: 0; }
.queen-fx.playing .q-bee { animation: q-burst 1.7s cubic-bezier(.2, .7, .3, 1) forwards; animation-delay: var(--bd); }
@keyframes q-burst {
  0% { opacity: 0; transform: rotate(var(--ang)) translateX(4px) rotate(calc(var(--ang) * -1)) scale(.4); }
  18% { opacity: 1; }
  100% { opacity: 1; transform: rotate(var(--ang)) translateX(var(--dist)) rotate(calc(var(--ang) * -1)) scale(1); }
}
.q-royal { opacity: 0; }
.queen-fx.playing .q-royal { animation: q-emerge 1.05s cubic-bezier(.2, .9, .3, 1.3) .95s both; }
@keyframes q-emerge {
  0% { opacity: 0; transform: scale(.15) translateY(12px); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}
.q-img { width: 225px; max-width: 64%; display: block; filter: drop-shadow(0 10px 26px rgba(0, 0, 0, .5)); }
.queen-fx.playing .q-img { animation: q-bob 3.4s ease-in-out 2.1s infinite; }
@keyframes q-bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-7px); }
}
.q-caption {
  position: absolute; left: 50%; bottom: 2%; transform: translate(-50%, 8px);
  background: linear-gradient(180deg, #3a2a68, #2a1e4f);
  border: 1px solid var(--gold); color: var(--gold);
  padding: 8px 16px; border-radius: 999px; font-size: 14.5px; white-space: nowrap;
  opacity: 0; box-shadow: 0 6px 24px rgba(0, 0, 0, .45);
}
.queen-fx.playing .q-caption { animation: q-caption 6.2s ease forwards; }
@keyframes q-caption {
  0%, 33% { opacity: 0; transform: translate(-50%, 8px); }
  41% { opacity: 1; transform: translate(-50%, 0); }
  88% { opacity: 1; transform: translate(-50%, 0); }
  100% { opacity: 0; transform: translate(-50%, 8px); }
}
.hex.center.q-tremble { animation: q-tremble .45s ease-in-out, q-glow 1.3s ease; }
@keyframes q-tremble {
  0%, 100% { rotate: 0deg; }
  25% { rotate: -2.5deg; }
  75% { rotate: 2.5deg; }
}
@keyframes q-glow {
  0% { filter: none; }
  30% { filter: brightness(1.35) drop-shadow(0 0 14px rgba(232, 197, 110, .9)); }
  100% { filter: none; }
}
@media (prefers-reduced-motion: reduce) {
  .queen-fx { display: none !important; }
}

/* Rejoin prompt under the date picker: your best existing séance for
   the picked puzzle, offered without blocking a fresh cast. */
.rejoin-note {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 8px; padding: 8px 10px; font-size: 13.5px;
  border: 1px solid var(--gold); border-radius: 10px;
  background: rgba(232, 197, 110, .08);
}

/* Merged calendar-picker (the Bee calendar inside the create card,
   Variant B with a year dropdown in the jump panel). */
.cal-title-btn {
  flex: 1; background: none; border: none; font: inherit; cursor: pointer;
  font-weight: 700; color: var(--gold); padding: 6px 4px; text-align: center;
}
.cal-wrap { position: relative; }
.cal-day.selected { outline: 2px solid var(--gold); outline-offset: -2px; background: rgba(232, 197, 110, .22); }
.cal-jump {
  position: absolute; left: 0; right: 0; top: 24px; z-index: 7;
  background: #241a45; border: 1px solid var(--card-line); border-radius: 12px;
  padding: 10px; box-shadow: 0 14px 34px rgba(0, 0, 0, .5);
}
.cal-jump-year { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink-dim); margin-bottom: 8px; }
.cal-jump-year select {
  flex: 1; background: #17102b; color: var(--ink); font: inherit;
  border: 1px solid var(--card-line); border-radius: 8px; padding: 6px 8px;
}
.cal-months { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.cal-month-btn {
  background: #17102b; border: 1px solid var(--card-line); color: var(--ink);
  border-radius: 8px; padding: 8px 2px; font: inherit; font-size: 13px; cursor: pointer;
}
.cal-month-btn.is-on { border-color: var(--gold); color: var(--gold); }
.cal-month-btn:disabled { opacity: .35; cursor: default; }
.cal-dot { color: var(--gold); font-size: 8px; vertical-align: super; }
.rejoin-list { display: flex; flex-direction: column; gap: 4px; width: 100%; }
.rejoin-row {
  text-align: left; background: #17102b; border: 1px solid var(--card-line);
  color: var(--ink); border-radius: 8px; padding: 6px 10px;
  font: inherit; font-size: 13px; cursor: pointer;
}
.rejoin-row:hover { border-color: var(--gold); }
/* The resumable séance is the primary action when one exists
   (Michael, Oct 23, 2026): the best row turns into the big gold
   button, and Start a new séance steps down to a plain button until
   the picked day has no séance to resume. */
.rejoin-hero {
  background: var(--gold); border-color: var(--gold); color: var(--gold-ink);
  font-weight: 700; font-size: 15px; text-align: center; padding: 12px 10px;
}
.rejoin-hero:hover { border-color: var(--gold); filter: brightness(1.06); }
#create-btn.demoted { background: #3a2866; border-color: var(--card-line); color: var(--ink); font-weight: 400; }

/* Pangram celebrations — the four from the mockup gallery, one
   chosen at random per pangram by the room (pangramfx.js). This
   layer sits below the rank celebrations (z 4/5); the game stands
   pangrams down when a rank celebration fires for the same find. */
.pangram-fx { position: absolute; inset: 0; pointer-events: none; z-index: 3; overflow: hidden; }
.pfx-ripple { position: absolute; width: 16px; height: 16px; margin: -8px 0 0 -8px; border: 2px solid var(--gold); border-radius: 50%; animation: pfx-ripple .8s ease-out forwards; }
@keyframes pfx-ripple { from { transform: scale(.6); opacity: .95; } to { transform: scale(4.4); opacity: 0; } }
.sigil-svg { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; transition: opacity .35s ease; overflow: visible; }
.sigil-svg.playing { opacity: 1; }
/* Chalk white with a plum edge (Michael's pick, Oct 23, 2026): the old
   gold stroke vanished where the star crosses the gold center tile.
   Parchment reads on violet and plum, and deep-plum shadows edge it
   across the gold — a sigil drawn in salt. */
#sigil-star { fill: none; stroke: var(--ink); stroke-width: 2.5; stroke-linejoin: round; stroke-dasharray: 100; stroke-dashoffset: 100; filter: drop-shadow(0 0 3px rgba(23, 16, 43, .95)) drop-shadow(0 0 7px rgba(23, 16, 43, .6)); }
.sigil-svg.playing #sigil-star { animation: sigil-draw 1.5s ease .15s forwards; }
@keyframes sigil-draw { to { stroke-dashoffset: 0; } }
.pfx-spark { position: absolute; transform: translate(-50%, -50%); color: var(--ink); font-size: 15px; opacity: 0; animation: pfx-spark-pop 2.3s ease forwards; text-shadow: 0 0 8px rgba(23, 16, 43, .9), 0 0 2px rgba(23, 16, 43, .9); }
@keyframes pfx-spark-pop { 0% { opacity: 0; scale: .4; } 12% { opacity: 1; scale: 1.2; } 30% { scale: 1; } 78% { opacity: 1; } 100% { opacity: 0; } }
.pangram-witch { position: absolute; left: 0; top: 0; width: 88px; display: none; will-change: transform; }
.pangram-witch img { width: 100%; display: block; transition: transform .08s linear; filter: drop-shadow(0 4px 8px rgba(0, 0, 0, .45)); }
.pangram-witch img.facing-west { transform: scaleX(-1); }
.pfx-trail-dot { position: absolute; width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px; border-radius: 50%; background: var(--gold); box-shadow: 0 0 6px var(--gold); animation: pfx-dot 2.4s ease forwards; }
@keyframes pfx-dot { 0% { opacity: 0; } 15% { opacity: .9; } 75% { opacity: .8; } 100% { opacity: 0; } }
.hex.pfx-gold { background: var(--gold); color: var(--gold-ink); }
.hex.pfx-pulse { animation: pfx-pulse .45s ease; }
@keyframes pfx-pulse { 0% { transform: translate(-50%, -50%) scale(1); } 40% { transform: translate(-50%, -50%) scale(1.16); filter: brightness(1.45); } 100% { transform: translate(-50%, -50%) scale(1); } }
.pangram-shine { position: absolute; top: -4%; bottom: -4%; left: -55%; width: 45%; background: linear-gradient(100deg, transparent, rgba(255, 240, 200, .38), transparent); transform: skewX(-12deg); opacity: 0; }
.pangram-shine.sweep { animation: pfx-sweep .8s ease; }
@keyframes pfx-sweep { 0% { left: -55%; opacity: 1; } 100% { left: 110%; opacity: 1; } }
.pangram-caption { position: absolute; left: 0; right: 0; bottom: 4%; text-align: center; color: var(--gold); font-weight: 700; font-size: 15px; letter-spacing: .03em; text-shadow: 0 2px 10px rgba(0, 0, 0, .8); opacity: 0; transition: opacity .35s ease; }
.pangram-caption.show { opacity: 1; }
.found-list li.fx-word-flash { animation: pfx-word-flash 1.1s ease; }
@keyframes pfx-word-flash { 30% { background: rgba(232, 197, 110, .5); } }
.pangram-planchette { position: absolute; left: 0; top: 0; width: 76px; display: none; will-change: transform, opacity; filter: drop-shadow(0 5px 9px rgba(0, 0, 0, .5)); }
.pangram-planchette svg { width: 100%; display: block; transition: transform .18s ease; }
/* Poof Riffle shuffle particles (spawned into the pangram-fx layer). */
.poof-dot { position: absolute; width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px; border-radius: 50%; background: var(--gold); box-shadow: 0 0 6px var(--gold); pointer-events: none; }
.poof-spark { position: absolute; margin: -8px 0 0 -6px; color: var(--gold); font-size: 13px; text-shadow: 0 0 8px rgba(232, 197, 110, .9); pointer-events: none; }
/* Séance-start deal-in (The Reading): the face-down hex back. Must
   beat the gold center: .hex.center.deal-back raises specificity,
   and the full background SHORTHAND (color in the final layer)
   replaces the center's own shorthand wholesale — longhands alone
   lost that fight on Michael's iPhone (Oct 23, 2026: the E stayed
   letter-up through the whole Reading, then flipped to itself). */
.hex.deal-back, .hex.center.deal-back { color: transparent; background: repeating-linear-gradient(45deg, rgba(232, 197, 110, .13) 0 6px, transparent 6px 12px) #33236b; }
/* The eye lives on the middle tile's back only (injected by
   buildHive); it shows solely while the center hex is face-down. */
.hex .back-art { display: none; }
.hex.center.deal-back .back-art { display: block; position: absolute; inset: 5% 17%; pointer-events: none; }
.hex.center.deal-back .back-art svg { width: 100%; height: 100%; display: block; }

/* The séance log: ONE stream for activity + chat (Michael combined
   them — "I don't want too many elements"), chronological with the
   newest at the bottom and the chat composer pinned beneath it. */
.log-panel { margin: 0; }
.crow { font-size: 13.5px; line-height: 1.35; overflow-wrap: anywhere; }
.crow .lt { opacity: .55; font-size: 11px; margin-right: 6px; }
.crow .cname { font-weight: 700; margin-right: 6px; }
.crow.empty { opacity: .6; font-style: italic; }
.chat-form { display: flex; gap: 6px; margin-top: 8px; }
.chat-form input { flex: 1; min-width: 0; font-size: 16px; padding: 8px 10px; border-radius: 8px; border: 1px solid #4a3878; background: #1d1438; color: var(--ink); }
.chat-note { color: #ff9d9d; font-size: 12.5px; margin: 4px 0 0; }
.chat-note:empty { display: none; }
