/* Scry's own stylesheet: layout, cue typography, the dimming overlay.
 *
 * PRD R9. Every colour in here is a theme.park variable — `var(--main-bg-color)`,
 * `var(--text)`, `rgb(var(--accent-color))` and friends — so editing the one file
 * `themepark_config/nightshade.css` restyles Scry together with Jellyfin and the
 * *arr apps. There is exactly one block of colour literals below, and it is generated
 * by `tools/theme-fallback.mjs`; a unit test fails if it drifts from the theme file.
 */

/* === BEGIN GENERATED THEME FALLBACK === */
/* Generated by tools/theme-fallback.mjs from themepark_config/nightshade.css. Do not edit by hand.
 * The page links the live theme.park stylesheet first; this block only keeps the
 * reader legible when that host cannot be reached. Run `npm run theme` after any
 * change to the theme file, and `npm test` will tell you if you forget. */
:root {
  --main-bg-color: #120B1B;
  --modal-bg-color: #1A1226;
  --modal-header-color: #1A1226;
  --modal-footer-color: #1A1226;
  --drop-down-menu-bg: #1A1226;
  --button-color: #7C3AED;
  --button-color-hover: #9F6BFF;
  --button-text: #F4EEFF;
  --button-text-hover: #FFFFFF;
  --accent-color: 167, 139, 250;
  --accent-color-hover: rgb(var(--accent-color),.8);
  --link-color: #C4B5FD;
  --link-color-hover: #E9D5FF;
  --label-text-color: #120B1B;
  --text: #EFEAF8;
  --text-hover: #FFFFFF;
  --text-muted: #9C93B3;
  --arr-queue-color: #A78BFA;
  --plex-poster-unwatched: #A78BFA;
  --petio-spinner: invert(72%) sepia(35%) saturate(2600%) hue-rotate(220deg) brightness(101%) contrast(97%);
  --gitea-color-primary-dark-4: 167, 139, 250;
  --overseerr-gradient: linear-gradient(180deg, rgba(18, 11, 27, 0.17) 0%, rgba(18, 11, 27) 100%);
}
/* === END GENERATED THEME FALLBACK === */

/* ------------------------------------------------------------------ the page */

/* `hidden` must actually hide, and this is the only rule that makes it so.
 *
 * `[hidden] { display: none }` lives in the **user-agent** stylesheet, and the cascade
 * compares origin before specificity: any author `display:` declaration beats it,
 * however weak its selector. So `.notice-card { display: flex }` — three words, one
 * class — wins over the user agent, and an element carrying `hidden` stays on screen
 * with the attribute set correctly. A reader hit exactly that on a phone: a banner would
 * not dismiss and "Got it" on the install hint did nothing, while the JavaScript was
 * doing its job the whole time.
 *
 * `!important` is what puts an author rule above the other author rules. Without it this
 * line would merely join them and lose to whichever came last.
 *
 * One rule, at the top, for every element — not a `[hidden]` guard remembered per class,
 * which is what was here before and was applied to four of nine. */
[hidden] {
  display: none !important;
}

* {
  box-sizing: border-box;
}

/*
 * Lists of stacked option buttons — the Words sheet, the tap-to-sync cues, the archive
 * candidates — all size their rows this way, and the reason is worth writing down.
 *
 * A `<button>` that is a grid item reports a **max-content block size of its own
 * `min-height`**, not of its content. `grid-auto-rows: auto` is `minmax(auto,
 * max-content)`, and a specified `min-height` also replaces the automatic minimum size
 * on the `min` side — so both halves came out 44px, every row was 44px tall whatever was
 * in it, the button was stretched to fit the row, and its own stacked children painted
 * straight through the next option's top border. Measured at 375px in mobile Chromium
 * and mobile WebKit: 36 of 36 options were 44px tall around 66px of content, and 36
 * children escaped their button's box.
 *
 * `min-content` is the one measurement the engines get right for a button (66px, with
 * the 44px floor still applied by the button's own `min-height`). In a single-column
 * list of block content min-content and max-content are the same thing anyway, so this
 * costs nothing and asks the engine only what it answers correctly.
 *
 * `test/e2e/reader.spec.js` measures every child against its option's box in all three
 * lists; it fails without this line.
 */
.option-list {
  grid-auto-rows: min-content;
}

html,
body {
  margin: 0;
  padding: 0;
  min-height: 100%;
}

body {
  background: var(--main-bg-color);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

a {
  color: var(--link-color);
}
a:hover {
  color: var(--link-color-hover);
}

.screen {
  display: none;
  min-height: 100dvh;
}
.screen[data-active='true'] {
  display: block;
}

.wrap {
  max-width: 44rem;
  margin: 0 auto;
  padding-block: 1.5rem 3rem;
  padding-inline: max(1rem, env(safe-area-inset-left)) max(1rem, env(safe-area-inset-right));
}

/* ------------------------------------------------------------- the list screen */

.masthead {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  margin-bottom: 0.25rem;
}

.masthead h1 {
  font-size: 1.6rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin: 0;
  color: rgb(var(--accent-color));
}

.masthead p {
  margin: 0;
  color: var(--text-muted);
  font-size: 0.9rem;
}

.section-note {
  color: var(--text-muted);
  font-size: 0.9rem;
  margin: 0 0 1.25rem;
}

.screenings {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* `minmax(0, 1fr)`, not `1fr`: a grid track sizes to its item's min-content by
   * default, and a dropdown of thirty-six language names has a very wide min-content.
   * Without this the card grows past a 375px phone and the whole page scrolls sideways.
   */
  grid-template-columns: minmax(0, 1fr);
  gap: 0.75rem;
}

.screenings > li {
  min-width: 0;
}

/*
 * The card is the bordered box; the button inside it is only the tap target, so the
 * language control can sit in the same box without being inside a `<button>` (illegal,
 * and a tap on the menu would also open the screening). Field fixes round 3.
 */
.screening-card {
  min-width: 0;
  border-radius: 0.75rem;
  border: 1px solid color-mix(in srgb, rgb(var(--accent-color)) 28%, transparent);
  background: var(--modal-bg-color);
  overflow: hidden;
}

.screening-card:focus-within {
  border-color: rgb(var(--accent-color));
}

.screening {
  width: 100%;
  text-align: left;
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 1rem;
  border: 0;
  border-radius: 0.75rem 0.75rem 0 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  cursor: pointer;
}

.screening:hover,
.screening:focus-visible {
  color: var(--text-hover);
  background: color-mix(in srgb, rgb(var(--accent-color)) 10%, transparent);
  outline: none;
}

/* The words beside the poster. `min-width: 0` so a long title wraps rather than pushing
   the card wider — the same lesson as the dropdown in round 3. */
.screening-words {
  display: grid;
  gap: 0.35rem;
  min-width: 0;
  flex: 1 1 auto;
}

.screening .title {
  font-size: 1.15rem;
  font-weight: 600;
}

.screening .sub,
.screening .meta {
  color: var(--text-muted);
  font-size: 0.9rem;
}

/*
 * One row, and it wraps rather than pushing the title and the position out of sight on
 * a 375px phone. The dropdown grows to the row; the native menu shows the full name
 * however narrow the closed control is.
 */
.screening-lang {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  padding: 0 1rem 0.85rem;
  border-top: 1px solid color-mix(in srgb, var(--text-muted) 20%, transparent);
  padding-top: 0.7rem;
}

.lang-pick {
  display: flex;
  flex: 1 1 100%;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

.lang-pick-name {
  color: var(--text-muted);
  font-size: 0.85rem;
  flex: 0 0 auto;
}

.lang-select {
  flex: 1 1 auto;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  /* The closed control shows a truncated name; the menu it opens shows the whole one. */
  text-overflow: ellipsis;
}

.lang-note {
  color: var(--text-muted);
  font-size: 0.85rem;
}

.lang-note[data-kind='loading'] {
  font-style: italic;
}

/* The door on a card that has one track or none — exactly the cards whose reader most
   needs another language. A quiet button, not a one-option dropdown. */
.lang-ask {
  flex: 0 0 auto;
}

/* ------------------------------------------------------- artwork (round 6) */

/*
 * A fixed box whatever is in it. `aspect-ratio` plus `flex: 0 0 auto` means a screening
 * with a poster and one without are the same shape, so the state-and-position line never
 * moves and a slow image cannot make the list jump as it loads.
 */
.art {
  position: relative;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  overflow: hidden;
  border-radius: 0.4rem;
  background: color-mix(in srgb, rgb(var(--accent-color)) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--text-muted) 25%, transparent);
}

.art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* The stand-in: the title's first letter, quiet, never a broken-image glyph. */
.art-letter {
  position: absolute;
  font-weight: 600;
  color: color-mix(in srgb, rgb(var(--accent-color)) 70%, var(--text-muted));
  user-select: none;
}

.art[data-has='true'] .art-letter {
  display: none;
}

/* 2:3 is the poster shape every library uses. 64px keeps a 343px card readable. */
.art-card {
  width: 64px;
  aspect-ratio: 2 / 3;
  max-width: 100%;
}

.art-card .art-letter {
  font-size: 1.5rem;
}

/* Small enough to leave the title and the status pill their room in the top bar. */
.art-small {
  width: 28px;
  aspect-ratio: 2 / 3;
}

/* The second and last surface the artwork-derived colour touches: a ring and a glow
   around the poster. Furniture, never text (PRD R9's bounded exception, round 8). */
body[data-tinted='true'] .art-small {
  border-color: color-mix(in srgb, var(--scry-art-tint) 55%, transparent);
  box-shadow: 0 0 0.5rem color-mix(in srgb, var(--scry-art-tint) 35%, transparent);
}

.art-small .art-letter {
  font-size: 0.8rem;
}

/* --------------------------------------------- the language picker (round 5) */

.lang-list {
  display: grid;
  gap: 0.45rem;
  max-height: 44dvh;
  overflow-y: auto;
}

/* Which of the three groups a row belongs to, said once above the group rather than
   repeated on every row. Not an option: it is a heading inside a list of buttons. */
.lang-group {
  margin: 0.35rem 0 0;
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.lang-group:first-child {
  margin-top: 0;
}

#lang-search {
  width: 100%;
}

.empty {
  padding: 2.5rem 1rem;
  text-align: center;
  color: var(--text-muted);
}

.empty strong {
  display: block;
  color: var(--text);
  font-size: 1.1rem;
  margin-bottom: 0.5rem;
  font-weight: 600;
}

/* ----------------------------------------------------------- the reader screen */

#reader {
  position: relative;
}

.stage {
  position: fixed;
  inset: 0;
  display: grid;
  grid-template-rows: auto 1fr auto auto;
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--main-bg-color);
}

/* The dimming veil: the page's own ground at partial strength, so a dark room stays
   dark. No colour of its own — it is the theme's background, dialled down. */
.veil {
  position: fixed;
  inset: 0;
  background: var(--main-bg-color);
  opacity: var(--scry-dim, 0);
  pointer-events: none;
  z-index: 5;
  transition: opacity 0.2s ease;
}

/* The OLED pure-black option (PRD R9) is deliberately absent in Phase 1: it is the one
 * colour Scry would have to state for itself, and Phase 2's brightness work is where
 * it belongs. The veil above already dims the room without naming a colour. */

.cue-area {
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 0.75rem;
  padding-inline: max(1rem, env(safe-area-inset-left)) max(1rem, env(safe-area-inset-right));
  overflow: hidden;
  /* Above the backdrop, which is the only thing that sits behind it. */
  position: relative;
  z-index: 1;
}

/* --------------------------------------------------- the artwork behind the words
 *
 * The reader asked for some of the title's own character on the reading screen. The
 * reading comes first, so the numbers are held by a test rather than by taste — and by
 * a test that looks at **rendered pixels**: `test/e2e` screenshots the strip the glyphs
 * occupy, takes the darkest and lightest pixels in it, and holds the ratio between them
 * to 7:1 at the bottom, the middle and the top of the slider (PRD section 7). It reads
 * the words the reader reads; nothing about how a colour is serialised can flatter it,
 * and a scrim of the wrong *colour* collapses it, which a companion test proves.
 *
 * What keeps it safe is not this element. The image runs the whole height at whatever
 * brightness the slider says; the protection is the scrim on `.cue-block` below, which
 * covers the words and nothing else.
 */
.backdrop {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

.backdrop[data-has='false'] {
  display: none;
}

.backdrop img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  /* Set from the slider. The artwork's own brightness, and nothing else's business. */
  opacity: var(--scry-art, 0.6);
  filter: blur(18px) saturate(1);
  /* A little larger than the box, so the blur has something to bleed from and the edges
     do not go pale where the kernel runs out of image. */
  transform: scale(1.12);
}



/*
 * A last touch of the title's colour at the top, where there is no text. Flat rather
 * than a gradient so the contrast test can model it exactly from its computed value
 * instead of guessing at stops; it stops short of the first line, and the measurement
 * includes it anyway, so it cannot cost the words anything even if it did not.
 */
.backdrop-wash {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--scry-art-tint, rgb(var(--accent-color))) 7%, transparent);
}

/*
 * **The scrim, and why it is this shape.**
 *
 * It used to be the page's ground laid over the *whole* backdrop, and the reader's
 * verdict was "you just can't make out any of it". The contrast floor was being paid for
 * across the entire screen, including the large areas where no text sits and nothing
 * needed protecting.
 *
 * So it is the text block instead: a panel sized to the words, feathered out past them
 * by a large soft shadow of the same colour, leaving the rest of the glass free for the
 * artwork.
 *
 * `--scry-scrim-colour` is set by the reader from the slider — `55% + 40% × level` of
 * the theme's own ground, written as a `color-mix` so even the runtime value names a
 * theme variable rather than a colour the code invented. The brighter the picture, the
 * stronger the panel: the reader chooses how much of the poster they see, never whether
 * they can read. The screenshot test above is what holds that to 7:1.
 *
 * A flat colour rather than a gradient, deliberately: one value behind the whole of the
 * text is a thing that can be reasoned about, and a gradient's stops are not.
 */
.cue-block {
  display: grid;
  justify-items: center;
  gap: 0.75rem;
  padding: 1.35rem 1.6rem;
  border-radius: 2.5rem;
  max-width: 100%;
  background: var(--scry-scrim-colour, transparent);
  box-shadow: 0 0 3rem 2.25rem var(--scry-scrim-colour, transparent);
}

.cue {
  margin: 0;
  text-align: center;
  font-size: var(--scry-cue-size, 1.5rem);
  line-height: 1.45;
  font-weight: 600;
  white-space: pre-line;
  word-break: normal;
  overflow-wrap: anywhere;
  line-break: strict;
  max-width: 24ch;
  color: var(--text);
}

.cue:lang(zh-Hans),
.cue:lang(zh-Hant) {
  font-family: 'PingFang SC', 'PingFang TC', 'Noto Sans CJK SC', 'Noto Sans CJK TC', 'Source Han Sans SC',
    'Microsoft YaHei', system-ui, sans-serif;
  max-width: 20ch;
  letter-spacing: 0.02em;
}

.cue-previous {
  font-size: calc(var(--scry-cue-size, 1.5rem) * 0.62);
  font-weight: 400;
  color: var(--text-muted);
  opacity: 0.65;
  max-width: 28ch;
}

/* Two lines back: fainter again, so the eye still lands on the current line first. */
.cue-previous-2 {
  font-size: calc(var(--scry-cue-size, 1.5rem) * 0.55);
  opacity: 0.38;
}

.cue-previous:empty,
.cue-previous-2:empty {
  display: none;
}

.cue-empty {
  color: var(--text-muted);
  font-weight: 400;
  font-size: 1rem;
}

/* --------------------------------------------------------------- the controls */

.topbar,
.controls {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem max(0.75rem, env(safe-area-inset-left)) 0.6rem max(0.75rem, env(safe-area-inset-right));
  z-index: 10;
  position: relative;
  flex-wrap: wrap;
  transition: opacity 0.35s ease;
}

.controls {
  justify-content: center;
  border-top: 1px solid color-mix(in srgb, var(--text-muted) 22%, transparent);
  background: var(--modal-bg-color);
}

.topbar {
  justify-content: space-between;
  background: var(--modal-bg-color);
  border-bottom: 1px solid color-mix(in srgb, var(--text-muted) 22%, transparent);
}

body[data-controls='hidden'] .topbar,
body[data-controls='hidden'] .controls,
/* The drawer is a control and fades with the rest, so the words have the screen. It
   holds its ground in the two states where fading would take something away from
   someone using it: while it is open, and while a request is running — a progress line
   that fades out is worse than no progress line. `data-hold` is set by the reader
   (round 8). */
body[data-controls='hidden'] .drawer:not([data-hold='true']) {
  opacity: 0;
  pointer-events: none;
}

button {
  font: inherit;
  cursor: pointer;
}

.btn {
  background: var(--button-color);
  color: var(--button-text);
  border: 1px solid transparent;
  border-radius: 0.5rem;
  padding: 0.45rem 0.7rem;
  font-size: 0.9rem;
  min-height: 2.5rem;
  min-width: 2.5rem;
}

.btn:hover,
.btn:focus-visible {
  background: var(--button-color-hover);
  color: var(--button-text-hover);
  outline: none;
}

.btn-quiet {
  background: transparent;
  color: var(--text-muted);
  border-color: color-mix(in srgb, var(--text-muted) 40%, transparent);
}

.btn-quiet:hover,
.btn-quiet:focus-visible {
  background: transparent;
  color: var(--text-hover);
  border-color: rgb(var(--accent-color));
}

.btn[aria-pressed='true'] {
  background: rgb(var(--accent-color));
  color: var(--label-text-color);
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.8rem;
  padding: 0.25rem 0.6rem;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--text-muted) 45%, transparent);
  color: var(--text-muted);
  white-space: nowrap;
}

.pill::before {
  content: '';
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: currentColor;
}

.pill[data-state='playing'] {
  color: rgb(var(--accent-color));
  border-color: rgb(var(--accent-color));
}
.pill[data-state='paused'] {
  color: var(--link-color);
  border-color: color-mix(in srgb, var(--link-color) 60%, transparent);
}
.pill[data-state='re-attuning'],
.pill[data-state='stale'] {
  color: var(--text-muted);
}

.now {
  display: grid;
  gap: 0.1rem;
  min-width: 0;
}

.now .title {
  font-size: 0.95rem;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 14rem;
}

.now .sub {
  font-size: 0.75rem;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 14rem;
}

.offset-readout {
  font-variant-numeric: tabular-nums;
  min-width: 4.5rem;
  text-align: center;
  color: var(--text-muted);
  font-size: 0.9rem;
}

select {
  font: inherit;
  font-size: 0.9rem;
  background: var(--drop-down-menu-bg);
  color: var(--text);
  border: 1px solid color-mix(in srgb, var(--text-muted) 40%, transparent);
  border-radius: 0.5rem;
  padding: 0.4rem 0.5rem;
  min-height: 2.5rem;
  max-width: 11rem;
}

input[type='range'] {
  accent-color: rgb(var(--accent-color));
  width: 7rem;
}

/* A slider says how much, never of what or which way. The word beside it does: `off`,
   `faint`, `soft`, `clear`, `vivid` — so the reader can tell which end shows more of the
   picture without dragging it to find out. */
.range-with-word {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
}

.range-word {
  color: var(--text-muted);
  font-size: 0.85rem;
  min-width: 3.2rem;
}

.sheet {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 20;
  background: var(--modal-bg-color);
  border-top: 1px solid color-mix(in srgb, rgb(var(--accent-color)) 40%, transparent);
  padding: 1rem max(1rem, env(safe-area-inset-left)) calc(1rem + env(safe-area-inset-bottom))
    max(1rem, env(safe-area-inset-right));
  display: grid;
  gap: 0.75rem;
}

.sheet h2 {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
}

.sheet .row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.sheet label {
  color: var(--text-muted);
  font-size: 0.9rem;
}

/* The field-test readout: the estimated timecode, large enough to read off a video
   frame when the phone is filmed beside the television (plan section 7). */
.timecode {
  font-variant-numeric: tabular-nums;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: clamp(1.5rem, 9vw, 3rem);
  font-weight: 700;
  letter-spacing: 0.04em;
  color: rgb(var(--accent-color));
  text-align: center;
}

.timecode small {
  display: block;
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-weight: 500;
}

.notice {
  text-align: center;
  color: var(--text-muted);
  font-size: 0.95rem;
  padding-inline: 1.5rem;
  max-width: 32ch;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------- orientation */

/* Landscape on a phone: one long line instead of two short ones, at 32 to 36 px for
   the default step (RESEARCH.md 2.3). The previous cue goes, because the height is
   gone and the current line is the job. */
@media (orientation: landscape) and (max-height: 500px) {
  .cue {
    font-size: calc(var(--scry-cue-size, 1.5rem) * 1.35);
    max-width: 42ch;
  }
  .cue:lang(zh-Hans),
  .cue:lang(zh-Hant) {
    max-width: 34ch;
  }
  .cue-previous,
  .cue-previous-2 {
    display: none;
  }
  .cue-second {
    font-size: calc(var(--scry-cue-size, 1.5rem) * 0.8);
    max-width: 48ch;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
  }
}

/* ------------------------------------------------------- the door (Phase 2 S1) */

.wrap-narrow {
  max-width: 26rem;
}

.gate-form {
  display: grid;
  gap: 0.9rem;
  margin-bottom: 1.25rem;
}

.field {
  display: grid;
  gap: 0.3rem;
}

.field label {
  font-size: 0.85rem;
  color: var(--text-muted);
}

input[type='text'],
input[type='password'] {
  font: inherit;
  font-size: 1rem;
  min-height: 2.75rem;
  padding: 0.5rem 0.65rem;
  border-radius: 0.5rem;
  background: var(--drop-down-menu-bg);
  color: var(--text);
  border: 1px solid color-mix(in srgb, var(--text-muted) 40%, transparent);
}

input[type='text']:focus-visible,
input[type='password']:focus-visible {
  outline: 2px solid rgb(var(--accent-color));
  outline-offset: 1px;
}

.btn-wide {
  width: 100%;
}

.btn-small {
  min-height: 2rem;
  padding: 0.3rem 0.6rem;
  font-size: 0.8rem;
}

.gate-guest {
  border-top: 1px solid color-mix(in srgb, var(--text-muted) 22%, transparent);
  padding-top: 1.25rem;
}

.gate-error:empty {
  display: none;
}

.gate-error {
  margin: 1rem 0 0;
  padding: 0.6rem 0.75rem;
  border-radius: 0.5rem;
  border: 1px solid color-mix(in srgb, var(--link-color) 55%, transparent);
  color: var(--link-color);
  font-size: 0.9rem;
}

/* who is signed in, and the way out */

.who {
  margin-inline-start: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8rem;
  color: var(--text-muted);
}

.who-name[data-guest='true']::after {
  content: ' · no account';
  color: var(--text-muted);
}

/* The shape a notice takes: a line of text the reader can act on or dismiss, as a card
   rather than loose body copy with a button after it, which is how the install hint
   first arrived and what the reader asked to have fixed. */
.notice-card {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  padding: 0.7rem 0.85rem;
  border-radius: 0.6rem;
  border: 1px solid color-mix(in srgb, rgb(var(--accent-color)) 30%, transparent);
  background: var(--modal-bg-color);
  font-size: 0.9rem;
}

.notice-text {
  flex: 1 1 12rem;
}

/* Below the list, because it is about the browser rather than about tonight — so it
   never pushes the screenings around as it appears and goes. */
.install-hint {
  margin-top: 2rem;
  color: var(--text-muted);
}

/* -------------------------------------------- the reading experience (Phase 2 S2) */

/* The second, stacked track for a learner (PRD L5): smaller and quieter than the
   reader's own language, so the eye lands on the right line first. */
.cue-second {
  font-size: calc(var(--scry-cue-size, 1.5rem) * 0.7);
  font-weight: 400;
  color: var(--text-muted);
  max-width: 30ch;
}

/* The low-light option (PRD R4). It names no colour: it warms and softens the
   theme's own text, so a change to `--text` still carries through. A literal amber
   would be the one colour Scry states for itself, which R9 forbids. */
body[data-amber='true'] .cue,
body[data-amber='true'] .cue-previous,
body[data-amber='true'] .cue-second {
  filter: sepia(0.85) saturate(1.45) brightness(0.9);
}

.tap-list {
  display: grid;
  gap: 0.5rem;
  max-height: 50dvh;
  overflow-y: auto;
}

.tap-cue {
  text-align: left;
  font: inherit;
  font-size: 1rem;
  line-height: 1.4;
  padding: 0.65rem 0.75rem;
  border-radius: 0.5rem;
  border: 1px solid color-mix(in srgb, var(--text-muted) 35%, transparent);
  background: var(--drop-down-menu-bg);
  color: var(--text);
  white-space: pre-line;
  min-height: 2.75rem;
}

.tap-cue:hover,
.tap-cue:focus-visible {
  border-color: rgb(var(--accent-color));
  color: var(--text-hover);
  outline: none;
}

/* ------------------------------------------ the missing-words paths (Phase 2 S3) */

/* The two ways of getting words this file does not have, in a drawer at the bottom.
   It used to sit in the middle of the cue area and take half a phone screen; the cue
   area is the product, so this is shut until someone asks for it. */
.drawer {
  z-index: 10;
  position: relative;
  transition: opacity 0.35s ease;
  background: var(--modal-bg-color);
  border-top: 1px solid color-mix(in srgb, rgb(var(--accent-color)) 30%, transparent);
}

.drawer-toggle {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem max(0.75rem, env(safe-area-inset-left)) 0.55rem max(0.75rem, env(safe-area-inset-right));
  font: inherit;
  font-size: 0.85rem;
  text-align: left;
  background: transparent;
  color: var(--text-muted);
  border: 0;
  min-height: 2.5rem;
}

.drawer-toggle:hover,
.drawer-toggle:focus-visible {
  color: var(--text-hover);
  outline: none;
}

.drawer-summary {
  flex: 1 1 auto;
}

.drawer-body {
  display: grid;
  gap: 0.6rem;
  padding: 0 max(0.75rem, env(safe-area-inset-left)) 0.8rem max(0.75rem, env(safe-area-inset-right));
  max-height: 45dvh;
  overflow-y: auto;
}

.source-row,
.drawer-door {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  flex-wrap: wrap;
  text-align: left;
}

/* The door sits under the paths, separated from them, because it changes *which*
   language is being asked for rather than how. */
.drawer-door {
  margin-top: 0.6rem;
  padding-top: 0.6rem;
  border-top: 1px solid color-mix(in srgb, var(--text-muted) 20%, transparent);
}

.source-what {
  flex: 1 1 14rem;
  font-size: 0.85rem;
  color: var(--text-muted);
}

.source-progress:empty {
  display: none;
}

/* An icon beside a word, sized to the text and coloured by whatever it sits in, so
   PRD R9 still holds: not one of these names a colour. */
.icon {
  width: 1em;
  height: 1em;
  flex: 0 0 auto;
  vertical-align: -0.125em;
}

.btn .icon,
.drawer-toggle .icon,
.pill .icon {
  margin-inline-end: 0.35em;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.pill {
  gap: 0;
}

.pill::before {
  content: none;
}

.credit {
  font-size: 0.75rem;
  margin-bottom: 0;
}

.credit a {
  color: var(--text-muted);
}

.source-progress {
  font-size: 0.85rem;
  color: rgb(var(--accent-color));
  margin: 0;
}

.candidates {
  display: grid;
  gap: 0.45rem;
  max-height: 50dvh;
  overflow-y: auto;
}

.candidate {
  display: grid;
  gap: 0.2rem;
  text-align: left;
  font: inherit;
  padding: 0.6rem 0.7rem;
  border-radius: 0.5rem;
  border: 1px solid color-mix(in srgb, var(--text-muted) 35%, transparent);
  background: var(--drop-down-menu-bg);
  color: var(--text);
  min-height: 2.75rem;
}

.candidate:hover:not(:disabled),
.candidate:focus-visible {
  border-color: rgb(var(--accent-color));
  outline: none;
}

.candidate:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.candidate-name {
  font-size: 0.9rem;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.candidate-meta {
  font-size: 0.75rem;
  color: var(--text-muted);
}

/* ------------------------------------------------------------------ focus ring */

/* One visible ring everywhere, for the desktop keyboard case (PRD accessibility). */
:focus-visible {
  outline: 2px solid rgb(var(--accent-color));
  outline-offset: 2px;
}

/* --------------------------------------------------- the host's view (Phase 2 S4) */

.admin-summary,
.admin-facts {
  display: grid;
  gap: 0.25rem;
  margin-bottom: 1rem;
}

.admin-row {
  display: flex;
  gap: 0.75rem;
  justify-content: space-between;
  align-items: baseline;
  flex-wrap: wrap;
  font-size: 0.85rem;
  padding: 0.25rem 0;
  border-bottom: 1px solid color-mix(in srgb, var(--text-muted) 18%, transparent);
}

.admin-key {
  color: var(--text-muted);
}

.admin-value {
  font-variant-numeric: tabular-nums;
  text-align: right;
  overflow-wrap: anywhere;
}

.admin-card {
  border: 1px solid color-mix(in srgb, rgb(var(--accent-color)) 28%, transparent);
  border-radius: 0.75rem;
  background: var(--modal-bg-color);
  padding: 1rem;
  margin-bottom: 1rem;
}

.admin-card h2 {
  margin: 0 0 0.25rem;
  font-size: 1.05rem;
}

.admin-meta {
  margin: 0 0 0.75rem;
  color: var(--text-muted);
  font-size: 0.85rem;
}

.admin-tracks {
  list-style: none;
  margin: 0 0 0.75rem;
  padding: 0;
  display: grid;
  gap: 0.2rem;
  font-size: 0.8rem;
  color: var(--text-muted);
}

/* ------------------------------------------------- the words sheet (the track chooser) */

.words-list {
  display: grid;
  gap: 0.45rem;
  max-height: 50dvh;
  overflow-y: auto;
}

.words-option {
  display: grid;
  gap: 0.15rem;
  text-align: left;
  font: inherit;
  padding: 0.6rem 0.7rem;
  border-radius: 0.5rem;
  border: 1px solid color-mix(in srgb, var(--text-muted) 35%, transparent);
  background: var(--drop-down-menu-bg);
  color: var(--text);
  min-height: 2.75rem;
}

.words-option:hover:not(:disabled),
.words-option:focus-visible {
  border-color: rgb(var(--accent-color));
  outline: none;
}

.words-option:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.words-option[data-chosen='true'] {
  border-color: rgb(var(--accent-color));
  background: var(--modal-bg-color);
}

.words-lang {
  font-size: 1rem;
  font-weight: 600;
}

.words-meta {
  font-size: 0.75rem;
  color: var(--text-muted);
}
