/* Three vendored faces, all SIL OFL 1.1, fetched by tools/fetch-fonts.sh with
   their licences beside them because the licence requires that.

   Self-hosted so font-src stays 'self'. A font on fonts.gstatic.com would
   report the IP of everyone who opens the app, on every visit, for
   typography — and CLAUDE.md names keeping that directive honest as the
   reason there were no webfonts at all until recently.

     Limelight    Art Deco display. The wordmark, and nothing else — it is
                  caps-shaped and unreadable at any length.
     Jost         A Futura revival. The deco era set its SIGNAGE in display
                  faces and its TEXT in geometric sans; this is the second
                  half of that, and it is why the interface can look of a
                  piece with the wordmark without shouting alongside it.
     Bodoni Moda  A Didone — which is what theatre bills were set in for a
                  century before anybody said "art deco". Show titles only.

   Jost and Bodoni are VARIABLE: one file each covering every weight the
   stylesheet asks for, which is 300 through 800 for the sans alone.

   swap on all three. The fallbacks are the old system stack, so a slow
   connection gets the previous design rather than blank text. */
@font-face {
  font-family: "Limelight";
  src: url("vendor/fonts/limelight-latin.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2212, U+FEFF;
}
@font-face {
  font-family: "Limelight";
  src: url("vendor/fonts/limelight-latin-ext.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+1E00-1E9F, U+2020, U+20A0-20AB,
                 U+2113, U+A720-A7FF;
}
@font-face {
  font-family: "Jost";
  src: url("vendor/fonts/jost-latin.woff2") format("woff2");
  font-weight: 300 800; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2212, U+FEFF;
}
@font-face {
  font-family: "Jost";
  src: url("vendor/fonts/jost-latin-ext.woff2") format("woff2");
  font-weight: 300 800; font-style: normal; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+1E00-1E9F, U+2020, U+20A0-20AB,
                 U+2113, U+A720-A7FF;
}
@font-face {
  font-family: "Bodoni Moda";
  src: url("vendor/fonts/bodoni-latin.woff2") format("woff2");
  font-weight: 400 700; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2212, U+FEFF;
}

:root {
  --display: "Limelight", "Helvetica Neue", Helvetica, Arial, sans-serif;

  /* The house */
  --paper:     #14100f;   /* warm near-black, not blue-black */
  --paper-2:   #1e1917;   /* raised surfaces: cards, sheets */
  --paper-3:   #2a2320;   /* pressed, hovered */
  --ink:       #f7f2e7;   /* warm white; pure white glares in a dark room */
  --rule:      #3a302b;
  --muted:     #a8998c;

  /* The marquee */
  --gold:      #f4c95d;
  --gold-deep: #d9a326;
  --gold-dim:  rgba(244, 201, 93, .16);

  /* The curtain */
  --red:       #d02b32;
  --red-deep:  #8f1a20;
  --good:      #3fa06b;

  /* The programme card. Fixed in both schemes — see rule 3. */
  --card:       #f7f4ed;
  --card-rule:  #d8d1c2;
  --card-muted: #6a6155;

  --radius: 10px;
  --shadow: 0 1px 2px rgba(0,0,0,.5), 0 10px 30px rgba(0,0,0,.45);

  --sans: "Jost", "Helvetica Neue", Helvetica, Arial, system-ui, sans-serif;
  --serif: "Bodoni Moda", Georgia, "Iowan Old Style", "Times New Roman", serif;
}

/* The programme variant. Same three rules, read in daylight. */
@media (prefers-color-scheme: light) {
  :root {
    --paper:     #f7f4ed;
    --paper-2:   #efeadf;
    --paper-3:   #e6dfd1;
    --ink:       #14110f;
    --rule:      #d8d1c2;
    --muted:     #6a6155;

    --gold:      #c9971f;
    --gold-deep: #a37b12;
    --gold-dim:  rgba(184, 134, 11, .14);

    --red:       #b5231f;
    --red-deep:  #8c1a17;
    --good:      #1f6f4a;

    --shadow: 0 1px 2px rgba(20,17,15,.06), 0 6px 20px rgba(20,17,15,.08);
  }
}

* { box-sizing: border-box; }

/* Gold carries every interactive thing, and a link is one. Without this the
   only links in the app — the privacy page, from inside Your data — came out
   browser blue on a near-black card, which is the one colour on screen that
   belongs to nothing. */
a { color: var(--gold-deep); text-underline-offset: .18em; }
a:hover { text-decoration: none; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 16px/1.5 var(--sans);
  -webkit-text-size-adjust: 100%;
}

.hidden { display: none !important; }
.muted { color: var(--muted); }
.spacer { flex: 1; }

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

/* ------------------------------- header -------------------------------- */

/* The header is a marquee. The bulb strip is a repeating radial gradient
   rather than elements, so it costs no DOM and scales to any width; the glow
   is a second, blurrier copy of the same gradient sitting under it, because a
   bulb with no spill reads as a dot. The drawn symbols already carry bulbs —
   this is the same idea at the size of the building. */
.top {
  display: flex; flex-wrap: wrap; gap: 16px;
  align-items: flex-start; justify-content: space-between;
  padding: 18px 20px 22px;
  background: var(--paper);
  position: relative;
}
.top::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 10px;
  background:
    radial-gradient(circle at 9px 5px, var(--gold) 2px, transparent 2.6px)
      0 0 / 18px 10px repeat-x,
    radial-gradient(circle at 9px 5px, var(--gold-dim) 5px, transparent 6px)
      0 0 / 18px 10px repeat-x;
}

/* Deco is geometry and spacing before it is a typeface, which is lucky —
   font-src is 'self' and this project vendors everything or does without.
   Wide-tracked caps over PAIRED RULES, one thick one thin, is the period's
   most reliable signature and costs no bytes at all. */
.brand h1 {
  margin: 0;
  font: 400 clamp(23px, 4.1vw, 33px)/1.05 var(--display);
  /* Limelight is drawn wide and deco already, so the tracking that made the
     system sans read as period would now be shouting. Weight drops to 400 for
     the same reason: the face has its own presence. */
  letter-spacing: .06em;
  text-transform: uppercase;
  /* Tracking adds space AFTER the last letter too, which shoves a centred
     word off-centre and leaves the rule below it looking misaligned. */
  text-indent: .06em;
  margin-left: -.06em;
}
.brand h1::after {
  content: ""; display: block; width: 62px; height: 4px; margin-top: 8px;
  background: var(--gold);
  /* The thin companion, as an offset copy. Spread pulls it in on every side,
     so 4px tall becomes 2px and 62 wide becomes 58 — a narrower second rule,
     which is what the period actually did. */
  box-shadow: 0 6px 0 -1px var(--gold), 0 0 14px var(--gold-dim);
}
/* A marquee reads big name, then smaller caps under it — NOW PLAYING,
   STARRING. The tagline takes that role, so it is tracked caps rather than
   the italic serif it was: four words in two symmetrical halves is exactly
   what wide tracking wants, where a long sentence set this way goes limp. */
.tagline {
  margin: 9px 0 0; color: var(--muted);
  font: 700 11px/1.3 var(--sans);
  text-transform: uppercase; letter-spacing: .22em;
  text-indent: .22em; margin-left: -.22em;
}

.account { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
.who { display: flex; align-items: center; gap: 8px; }
.who-name { font-weight: 600; font-size: 14px; max-width: 22ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.avatar {
  display: grid; place-items: center;
  width: 30px; height: 30px; border-radius: 50%;
  background: var(--ink); color: var(--paper);
  font-size: 12px; font-weight: 700;
}
.sync { margin: 0; font-size: 12px; color: var(--muted); }
.sync.warn { color: var(--red); }

.setup {
  margin: 0; padding: 12px 20px;
  background: var(--gold); color: #14110f; font-size: 14px;
}
.setup code { background: rgba(0,0,0,.12); padding: 1px 5px; border-radius: 4px; }

/* ------------------------------- buttons ------------------------------- */

.btn {
  font: 600 14px/1 var(--sans);
  padding: 10px 14px;
  border: 1.5px solid var(--ink);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
  cursor: pointer;
}
.btn:hover { background: var(--paper-2); }
.btn:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
.btn:disabled { opacity: .45; cursor: not-allowed; }
/* Gold carries every interactive thing, so the one button that matters most
   is gold rather than merely outlined in it. Dark text on gold, always — gold
   on gold is the mistake this palette invites. */
.btn.primary {
  background: var(--gold); color: #14100f; border-color: var(--gold);
  font-weight: 700;
}
.btn.primary:hover { background: var(--gold-deep); border-color: var(--gold-deep); }
.btn.ghost { border-color: var(--rule); }
.btn.tiny { padding: 6px 9px; font-size: 12px; }
.btn.danger { border-color: var(--red); color: var(--red); }
.btn.danger:hover { background: var(--red); color: #fff; }

/* -------------------------------- tabs --------------------------------- */

.tabs { display: flex; gap: 4px; padding: 12px 20px 0; border-bottom: 1px solid var(--rule); }
.tab {
  font: 700 12px/1 var(--sans); text-transform: uppercase; letter-spacing: .16em;
  padding: 11px 14px; border: 0; background: none; color: var(--muted); cursor: pointer;
  border-bottom: 3px solid transparent; margin-bottom: -1px;
}
.tab[aria-selected="true"] {
  color: var(--ink); border-bottom-color: var(--gold);
  text-shadow: 0 0 18px var(--gold-dim);
}
.tab:hover { color: var(--ink); }

main { padding: 20px; max-width: 940px; margin: 0 auto; }
.pane { display: none; }
body.view-log #log-view,
body.view-people #people-view,
body.view-houses #houses-view,
body.view-wrapped #wrapped-view { display: block; }

/* ------------------------------- logbook ------------------------------- */

.log-head {
  display: flex; flex-wrap: wrap; gap: 14px;
  align-items: center; justify-content: space-between; margin-bottom: 18px;
}
/* A deco tally: the figure large and light, its label small and spaced out
   underneath. Lining tabular numerals so a column of them stays a column. */
.stats { display: flex; flex-wrap: wrap; gap: 20px; font-size: 10px;
         color: var(--muted); text-transform: uppercase; letter-spacing: .16em; }
.stats strong { display: block; font: 300 26px/1 var(--sans); color: var(--ink);
                font-variant-numeric: tabular-nums lining-nums;
                letter-spacing: .02em; margin-bottom: 5px; }

.year {
  display: flex; align-items: baseline; justify-content: space-between;
  margin: 30px 0 12px; padding-bottom: 7px;
  font: 700 14px/1 var(--sans); letter-spacing: .2em;
  border-bottom: 2px solid var(--ink);
  position: relative;
}
/* The thin half of the pair. A separate solid element rather than a second
   stop in a gradient: a gradient's stops land on fractional device pixels and
   the hairline visibly bands along its own length. */
.year::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -4px; height: 1px;
  background: var(--ink);
}
.year em { font-style: normal; color: var(--muted); font-weight: 600; font-size: 13px; }

.card {
  background: var(--paper-2);
  border: 1px solid var(--rule);
  border-left: 4px solid var(--gold);
  border-radius: var(--radius);
  padding: 14px 16px; margin-bottom: 10px;
  cursor: pointer; box-shadow: var(--shadow);
}
.card:hover { border-left-color: var(--gold-deep); }
.card:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }

.card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.card-show { margin: 0; font: 700 18px/1.2 var(--serif); }
.rating { font: 700 13px/1 var(--sans); color: var(--gold-deep); font-variant-numeric: tabular-nums; }

.card-meta { margin: 6px 0 0; font-size: 13px; color: var(--muted); display: flex; flex-wrap: wrap; gap: 10px; }
.card-meta .slot { font-weight: 700; color: var(--ink); }
.card-meta .seat { font-variant-numeric: tabular-nums; }

.cast-list { list-style: none; margin: 10px 0 0; padding: 10px 0 0; border-top: 1px dashed var(--rule); }
.cast-list li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; font-size: 14px; padding: 2px 0; }
.cast-list .role { font-weight: 700; min-width: 8ch; }
.cast-list .role::after { content: " —"; color: var(--muted); }
.cast-list .usual { color: var(--muted); font-style: italic; font-size: 12px; }

.pill {
  font: 700 10px/1 var(--sans); text-transform: uppercase; letter-spacing: .05em;
  padding: 3px 6px; border-radius: 20px; background: var(--rule); color: var(--ink);
}
/* A cover going on is THE event this app exists to record, so it gets the
   curtain rather than the marquee. Gold is reserved for things you can press;
   a pill is not one, and a screen where the only red is a delete button
   spends its loudest colour on its least interesting idea. Filled red here
   does not collide with .btn.danger, which is red OUTLINED until hovered. */
.pill.cover-understudy, .pill.cover-standby, .pill.cover-swing,
.pill.cover-universal-swing, .pill.cover-alternate,
.pill.cover-partial, .pill.cover-put-in, .pill.cover-seen {
  background: var(--red); color: #fff;
}
.pill.cover-principal { background: transparent; border: 1px solid var(--rule); color: var(--muted); }
.pill.from-slip { background: var(--good); color: #fff; align-self: center; }

.tag-row { display: flex; flex-wrap: wrap; gap: 5px; margin: 10px 0 0; }
.card-notes { margin: 10px 0 0; font: 14px/1.5 var(--serif); white-space: pre-wrap; overflow-wrap: anywhere; }

.empty { text-align: center; padding: 50px 20px; color: var(--muted); }
.empty p:first-child { font: 700 19px/1.3 var(--serif); color: var(--ink); margin-bottom: 6px; }

/* -------------------------------- houses -------------------------------- */

.houses-note { margin: 0 0 10px; color: var(--muted); font-size: 14px; max-width: 60ch; }
.bar { height: 8px; background: var(--rule); border-radius: 20px; overflow: hidden; }
.bar-fill { display: block; height: 100%; width: 0; background: var(--gold); transition: width .3s; }

.map { height: 400px; margin: 16px 0; border: 1px solid var(--rule); border-radius: var(--radius); background: var(--paper-2); }
.map-note { padding: 24px; text-align: center; color: var(--muted); }
.leaflet-marker-icon.seen { filter: hue-rotate(70deg) saturate(1.7); }

.pop-name { font: 700 15px/1.2 var(--serif); }
.pop-region { font-size: 12px; color: var(--muted); margin-top: 2px; }
.pop-aka { font-size: 11px; color: var(--muted); font-style: italic; margin-top: 3px; }
.pop-seen { margin: 8px 0 0; padding-left: 16px; font-size: 12px; }
.pop-none { margin: 8px 0 0; font-size: 12px; color: var(--muted); }

.house-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 2px; }
.house-row {
  display: grid; grid-template-columns: 20px 1fr auto; gap: 8px; align-items: baseline;
  padding: 8px 10px; border-bottom: 1px solid var(--rule); cursor: pointer; font-size: 14px;
}
.house-row:hover { background: var(--paper-2); }
.house-mark { color: var(--rule); font-weight: 700; text-align: center; }
.house-row.done .house-mark { color: var(--good); }
.house-row.done .house-name { font-weight: 700; }
.house-sub { font-size: 11px; color: var(--muted); }

/* ------------------------------- dialogs -------------------------------- */

.sheet {
  border: 0; border-radius: 14px; padding: 0;
  width: min(720px, 94vw); max-height: 92vh;
  background: var(--paper); color: var(--ink); box-shadow: var(--shadow);
}
.sheet.narrow { width: min(460px, 94vw); }
.sheet::backdrop { background: rgba(20,17,15,.55); }
.sheet form { display: contents; }

.sheet-head {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 16px 20px; border-bottom: 2px solid var(--ink); background: var(--paper);
}
.sheet-head h2 { margin: 0; font: 800 17px/1 var(--sans); text-transform: uppercase; letter-spacing: .04em; }

.sheet-body { padding: 18px 20px; overflow-y: auto; max-height: calc(92vh - 130px); }

.sheet-foot {
  position: sticky; bottom: 0;
  display: flex; gap: 8px; align-items: center;
  padding: 14px 20px; border-top: 1px solid var(--rule); background: var(--paper);
}

/* -------------------------------- form ---------------------------------- */

.field { display: block; margin-bottom: 14px; }
.field > span { display: block; font: 700 12px/1 var(--sans); text-transform: uppercase; letter-spacing: .05em; margin-bottom: 5px; }
.field > span em { font-style: normal; font-weight: 400; color: var(--muted); text-transform: none; letter-spacing: 0; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

input, select, textarea {
  width: 100%; font: 15px/1.4 var(--sans);
  padding: 9px 10px; color: var(--ink); background: var(--paper);
  border: 1.5px solid var(--rule); border-radius: 8px;
}
input:focus, select:focus, textarea:focus { outline: 0; border-color: var(--ink); box-shadow: 0 0 0 3px rgba(242,193,78,.4); }
textarea { resize: vertical; }

.hint { margin: -8px 0 14px; font-size: 12.5px; color: var(--muted); }
.counter { display: block; text-align: right; font-size: 11px; color: var(--muted); margin-top: 3px; }
.counter.over { color: var(--red); font-weight: 700; }

fieldset { border: 1px solid var(--rule); border-radius: var(--radius); padding: 14px; margin: 0 0 16px; }
legend { font: 800 12px/1 var(--sans); text-transform: uppercase; letter-spacing: .06em; padding: 0 6px; }

.cast { border-color: var(--ink); border-width: 2px; }
.cast-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.cast-rows.over { outline: 2px solid var(--red); outline-offset: 6px; border-radius: 6px; }

.cast-row {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr minmax(150px, 1.3fr) auto auto;
  gap: 8px; align-items: end;
  padding: 10px 0; border-top: 1px dashed var(--rule);
}
.cast-row:first-child { border-top: 0; }
.cast-field { display: block; }
.cast-field > span { display: block; font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin-bottom: 3px; }
.cast-field input, .cast-field select { padding: 7px 8px; font-size: 14px; }
.cast-row .remove { align-self: end; }
.empty-note { margin: 0; font-size: 13px; color: var(--muted); font-style: italic; }

.tag-boxes { display: flex; flex-wrap: wrap; gap: 6px; }
.tag-box { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; padding: 5px 9px; border: 1px solid var(--rule); border-radius: 20px; cursor: pointer; }
.tag-box input { width: auto; }
.tag-box:has(input:checked) { background: var(--gold); border-color: var(--gold-deep); font-weight: 600; }

/* --------------------------------- scan ---------------------------------- */

.privacy-note {
  margin: 0 0 14px; padding: 10px 12px; font-size: 13px;
  background: var(--paper-2); border-left: 3px solid var(--good); border-radius: 0 6px 6px 0;
}
.progress { text-align: center; padding: 20px 0; }
.progress p { margin: 0 0 10px; font-weight: 600; }

.confirm-lede { margin: 0 0 12px; font-size: 14px; }
.confidence { display: flex; flex-direction: column; gap: 2px; padding: 10px 12px; border-radius: 8px; margin-bottom: 12px; font-size: 13px; }
.confidence strong { font-size: 17px; }
.confidence.good { background: rgba(31,111,74,.14); }
.confidence.fair { background: rgba(242,193,78,.28); }
.confidence.poor { background: rgba(163,39,31,.14); }

.warnings p { margin: 0 0 6px; padding: 8px 10px; font-size: 13px; background: rgba(242,193,78,.22); border-radius: 6px; }

.scan-row { padding: 10px 0; border-top: 1px dashed var(--rule); }
.scan-main { margin: 0; font-size: 15px; }
.scan-main strong { font-family: var(--serif); }
.scan-usual { margin: 3px 0 0; font-size: 12px; color: var(--muted); font-style: italic; }
.scan-flag { margin: 4px 0 0; font-size: 12px; color: var(--gold-deep); font-weight: 600; }

.raw { margin-top: 14px; font-size: 13px; }
.raw summary { cursor: pointer; color: var(--muted); }
.raw pre {
  margin: 8px 0 0; padding: 10px; font-size: 12px; line-height: 1.45;
  background: var(--paper-2); border-radius: 6px; overflow-x: auto; white-space: pre-wrap;
  overflow-wrap: anywhere; max-height: 220px;
}

.data-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 16px 0 10px; }

/* -------------------------------- consent -------------------------------- */

.consent {
  position: fixed; left: 12px; right: 12px; bottom: 12px; z-index: 60;
  max-width: 520px; margin: 0 auto; padding: 14px 16px;
  background: var(--paper); border: 2px solid var(--ink);
  border-radius: var(--radius); box-shadow: var(--shadow);
}
.consent p { margin: 0 0 10px; font-size: 14px; }
/* Both buttons weighted identically — declining must never be the harder path. */
.consent-btns { display: flex; gap: 8px; }
.consent-btns .btn { flex: 1; }

/* -------------------------------- narrow --------------------------------- */

@media (max-width: 700px) {
  .field-row { grid-template-columns: 1fr; }
  .cast-row { grid-template-columns: 1fr 1fr; }
  .cast-row .remove { grid-column: 2; justify-self: end; }
  .map { height: 300px; }
  .stats { gap: 12px; }
  .stats strong { font-size: 18px; }
  .sheet { width: 100vw; max-width: 100vw; max-height: 100vh; height: 100vh; border-radius: 0; }
  .sheet-body { max-height: calc(100vh - 130px); }
}

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

/* ---------------------------- circuit selector ---------------------------- */

.seg-row { display: flex; flex-wrap: wrap; gap: 4px; margin: 12px 0 8px; }
.seg {
  font: 700 12px/1 var(--sans); text-transform: uppercase; letter-spacing: .04em;
  padding: 8px 11px; border: 1.5px solid var(--rule); border-radius: 20px;
  background: var(--paper); color: var(--muted); cursor: pointer;
}
.seg:hover { background: var(--paper-2); }
.seg[aria-pressed="true"] {
  background: var(--ink); border-color: var(--ink); color: var(--paper);
}
.seg:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
.houses-count { margin: 0 0 6px; font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; }

/* ------------------------- cover vocabulary ---------------------------- */

/* Every kind of cover reads as "not the usual person", so they share the
   accent. Principal is the exception: it means nothing unusual happened. */
.pill.cover-understudy, .pill.cover-standby, .pill.cover-walking-cover,
.pill.cover-first-cover, .pill.cover-second-cover, .pill.cover-third-cover,
.pill.cover-swing, .pill.cover-universal-swing, .pill.cover-alternate,
.pill.cover-partial, .pill.cover-put-in { background: var(--red); color: #fff; }
.pill.cover-principal { background: transparent; border: 1px solid var(--rule); color: var(--muted); }

.cover-note { display: block; margin-top: 3px; font-size: 11px; color: var(--muted); line-height: 1.3; }
.cast-field select optgroup { font-style: normal; font-weight: 700; }

/* ------------------------------- sharing -------------------------------- */

.share { border-color: var(--good); }
.share-toggle { border-color: var(--good); }
.share-hint { margin: 8px 0 0; }
.share-gate { margin: 8px 0 0; color: var(--gold-deep); font-weight: 600; }

.who-else { margin-top: 12px; padding-top: 10px; border-top: 1px dashed var(--rule); }
.who-else-head { margin: 0 0 8px; font-size: 13px; font-weight: 700; }
.who-else-credit { margin: 8px 0 0; font-size: 11px; color: var(--muted); }

.agree-row {
  display: grid; grid-template-columns: minmax(7ch, 14ch) 1fr;
  gap: 8px; align-items: baseline; padding: 5px 0; font-size: 13px;
}
.agree-row .role { font-weight: 700; }
.agree-names { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.agree-name { display: inline-flex; align-items: center; gap: 4px; }
.agree-count { color: var(--muted); font-variant-numeric: tabular-nums; font-size: 11px; }

/* A disagreement is shown, never resolved. */
.agree-row.disputed { background: rgba(242,193,78,.14); border-radius: 6px; padding: 6px 8px; }
.agree-flag {
  grid-column: 2; font-size: 11px; color: var(--gold-deep); font-weight: 600;
}

/* --------------------- progressive disclosure --------------------------- */

.more { margin: 0 0 16px; border: 1px solid var(--rule); border-radius: var(--radius); }
.more summary {
  cursor: pointer; padding: 12px 14px;
  font: 700 12px/1 var(--sans); text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted);
}
.more summary:hover { color: var(--ink); }
.more[open] summary { border-bottom: 1px solid var(--rule); }
.more-body { padding: 14px; }
.more-body fieldset { margin-bottom: 14px; }
.more-body .field:last-child { margin-bottom: 0; }

/* ------------------------------- phones --------------------------------- */

@media (max-width: 700px) {
  /* A cast row is four related fields about ONE person. Two columns split
     them into an order nobody reads in — Role, Who, Cover, Usually — with the
     cover hint shoving its neighbour around. One column per row instead. */
  .cast-row { grid-template-columns: 1fr; gap: 10px; }
  .cast-row .remove { grid-column: 1; justify-self: start; }

  /* The header is chrome. On a 812px screen it was taking 340px before any
     content, which matters when the app is opened standing in a lobby. */
  .top { padding: 12px 16px 18px; }
  .brand h1 { font-size: 21px; }
  .brand h1::after { height: 3px; margin-top: 5px; width: 46px;
                     box-shadow: 0 5px 0 -1px var(--gold), 0 0 14px var(--gold-dim); }

  /* The tagline used to be hidden here, and should not be any more. It was
     hidden when it was a 35-character sentence that wrapped to three lines on
     a phone; "Every night. Every name." is 24 and fits on one. Hiding it now
     would throw away the bottom third of the marquee — name, light bar,
     tracked caps — on the only screen most people will ever see. */
  .tagline { display: block; font-size: 9px; letter-spacing: .18em;
             text-indent: .18em; margin: 7px 0 0 -.18em; }

  .account { flex-direction: row; align-items: center; gap: 8px; }

  main { padding: 14px; }
  .log-head { gap: 10px; }

  /* Deco tracking is expensive in a fixed-width row. At .16em the four tabs
     no longer fit 375px and "Your year" wrapped onto a second line, which
     pushed the whole bar out of alignment. Tighter here, and the caps still
     read as period. */
  .tabs { padding: 8px 12px 0; gap: 0; }
  .tab { letter-spacing: .08em; padding: 11px 10px; font-size: 11px; }
}

/* --------------------------- date shortcuts ----------------------------- */

.date-quick { display: flex; gap: 6px; margin-top: 6px; }
.chip {
  font: 600 12px/1 var(--sans); padding: 6px 10px;
  border: 1px solid var(--rule); border-radius: 20px;
  background: var(--paper); color: var(--muted); cursor: pointer;
}
.chip:hover { background: var(--paper-2); color: var(--ink); }
.chip:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }

/* ------------------------ public page & following ----------------------- */

body.view-person #person-view { display: block; }
body.view-person #log-view,
body.view-person #people-view,
body.view-person #houses-view,
body.view-person .tabs { display: none; }

.person-card {
  padding: 18px; margin: 14px 0 18px;
  background: var(--paper-2);
  border: 1px solid var(--rule); border-left: 4px solid var(--gold);
  border-radius: var(--radius);
}
.person-name { margin: 0; font: 800 24px/1.1 var(--sans); }
.person-bio {
  margin: 8px 0 12px; font: 15px/1.5 var(--serif);
  white-space: pre-wrap; overflow-wrap: anywhere; max-width: 60ch;
}
.person-card .stats { margin-bottom: 12px; }
.person-card .stats strong { display: inline; font-size: 16px; }
.person-sub {
  margin: 0 0 10px; font: 800 13px/1 var(--sans);
  text-transform: uppercase; letter-spacing: .06em;
}
#person-back { margin-bottom: 4px; }

.p-counts { display: flex; flex-wrap: wrap; gap: 16px; margin: 4px 0 16px; }
.p-count { display: flex; flex-direction: column; font-size: 12px; color: var(--muted); }
.p-count strong { font-size: 22px; color: var(--ink); font-variant-numeric: tabular-nums; }

.p-link { margin: 10px 0 0; font-size: 12px; overflow-wrap: anywhere; }
.p-link a { color: var(--gold-deep); }

/* --------------------------- Playbill cover ----------------------------- */

.cover-scan { margin: 0 0 12px; }
.cover-scan .hint { display: block; margin-top: 5px; }
.cover-result {
  margin: 0 0 14px; padding: 12px;
  background: var(--paper-2); border: 1px solid var(--rule);
  border-left: 3px solid var(--gold); border-radius: 0 6px 6px 0;
  font-size: 13px;
}
.cover-head { margin: 6px 0; font-size: 15px; }
.cover-choices { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.cover-choices .chip.chosen {
  background: var(--ink); color: var(--paper); border-color: var(--ink);
}
.warn-line {
  margin: 0 0 6px; padding: 7px 9px; font-size: 12.5px;
  background: rgba(242,193,78,.22); border-radius: 6px;
}

/* ---- Cast baseline -------------------------------------------------------
   The roster we believe was on, shown before anyone records a change. Every
   line here is a GUESS, and it is styled to look like one: quieter than the
   rows people fill in themselves, and carrying the word "assumed". Do not
   make these look like confirmed data. */

.baseline { margin: 0 0 1rem; }

.baseline-choice { display: flex; gap: .5rem; margin-bottom: .6rem; }
.baseline-choice .btn.choice { flex: 1; }
.baseline-choice .btn.on {
  border-color: var(--gold-deep);
  box-shadow: inset 0 0 0 1px var(--gold-deep);
}

#baseline-note.warn { color: var(--gold-deep); }

.baseline-rows { display: flex; flex-direction: column; }

.assumed-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: .75rem; width: 100%; text-align: left;
  padding: .6rem .25rem; background: none; border: 0;
  border-bottom: 1px solid var(--rule); color: inherit;
  font: inherit; cursor: pointer;
  /* 44px minimum target — this is a phone-first control. */
  min-height: 44px;
}
.assumed-row:hover, .assumed-row:focus-visible { background: var(--paper-2); }

.assumed-who { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.assumed-role { font-size: .95rem; }
.assumed-name { font-size: .85rem; color: var(--muted); }
.assumed-who > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.pill.assumed {
  font-size: .7rem; color: var(--muted);
  background: var(--paper-2); border: 1px solid var(--rule);
  padding: .15rem .45rem; border-radius: 999px; flex: none;
}

/* A baseline line whose role has had no landmark for over a year. It is a
   much weaker claim than the rest of the roster and used to look identical. */
.pill.assumed.stale {
  color: var(--gold-deep);
  border-color: var(--gold-deep);
}

/* The supporting cast, folded away. Kept visually quieter than the billed
   roles above it — the point of the split is that these are not the reason
   anyone bought the ticket. */
.baseline-more{border-top:1px solid var(--rule);margin-top:.2rem}
.baseline-more > summary{
  padding:.6rem .25rem;min-height:44px;display:flex;align-items:center;
  font-size:.85rem;color:var(--muted);cursor:pointer;list-style:none;
}
.baseline-more > summary::-webkit-details-marker{display:none}
.baseline-more > summary::before{content:"▸";margin-right:.45rem;font-size:.7rem}
.baseline-more[open] > summary::before{content:"▾"}
.baseline-more > summary:hover{color:var(--ink)}

/* The native date picker's calendar button is drawn black, which on this
   theme's dark fields is invisible — the control was there the whole time and
   simply could not be seen. Invert it, and give the whole field a pointer so
   it reads as clickable. */
input[type="date"]{ cursor: pointer; }
input[type="date"]::-webkit-calendar-picker-indicator{
  filter: invert(1);
  opacity: .8;
  cursor: pointer;
  padding-left: 6px;
}
input[type="date"]::-webkit-calendar-picker-indicator:hover{ opacity: 1; }
@media (prefers-color-scheme: light){
  /* Light mode draws it dark-on-light already, which is correct. */
  input[type="date"]::-webkit-calendar-picker-indicator{ filter: none; }
}
.field-note.warn { color: var(--gold-deep); }

/* A secondary control tucked under its main field — the seat's rating under
   the seat. Narrower and quieter, so the field still reads as one thing. */
.sub-select { margin-top: .4rem; font-size: .9rem; }

/* The show's score reads differently from the night's — same shape, quieter
   fill, so a card carrying both is scannable rather than two competing pills. */
/* The show's score and the night's are the same shape on purpose, so a card
   carrying both reads as one idea measured twice. A box around one of them
   made it look selected rather than quieter. */
.rating.show { background: transparent; border: 0; color: var(--muted); font-weight: 600; }

/* --- Year in review ------------------------------------------------------
   Big numbers, few of them. The point is a card somebody wants to screenshot,
   so everything here is sized to be legible at phone width first. */
.wrapped-head { display: flex; align-items: flex-end; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }
.field.inline { flex-direction: row; align-items: center; gap: .5rem; margin: 0; }
.field.inline select { width: auto; }

.wrapped-body { display: flex; flex-direction: column; gap: 1.1rem; }

.w-hero { text-align: center; padding: 1.4rem 1rem 1.2rem;
  background: var(--paper-2); border: 1px solid var(--rule); border-radius: var(--radius); }
.w-hero .big { font-family: var(--serif); font-size: clamp(2.6rem, 12vw, 4rem);
  line-height: 1; color: var(--gold-deep); }
.w-hero .cap { font-size: .95rem; color: var(--muted); margin-top: .35rem; }
.w-hero .sub { font-size: .85rem; color: var(--muted); margin-top: .5rem; }

.w-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: .7rem; }
.w-stat { background: var(--paper-2); border: 1px solid var(--rule);
  border-radius: var(--radius); padding: .8rem .9rem; }
.w-stat .n { font-family: var(--serif); font-size: 1.7rem; line-height: 1.1; }
.w-stat .k { font-size: .78rem; color: var(--muted); margin-top: .2rem; }

.w-card { background: var(--paper-2); border: 1px solid var(--rule);
  border-radius: var(--radius); padding: .95rem 1rem; }
.w-card h3 { font-size: .78rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 .6rem; font-weight: 600; }
.w-row { display: flex; align-items: baseline; justify-content: space-between;
  gap: .8rem; padding: .35rem 0; border-bottom: 1px solid var(--rule); }
.w-row:last-child { border-bottom: 0; }
/* Name over context, not beside it — the parent was nowrap, so the sub-line
   sat on the same row and got clipped. */
.w-row .who { min-width: 0; display: flex; flex-direction: column;
  align-items: flex-start; gap: .1rem; }
.w-row .who > .sub { font-size: .74rem; color: var(--muted); }
.w-row .who { max-width: 100%; }
.w-row .who > .nm, .w-row .who > .sub {
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.w-row .n { flex: none; font-family: var(--serif); font-size: 1.05rem; }

.w-bar { display: flex; height: 8px; border-radius: 999px; overflow: hidden;
  background: var(--paper); border: 1px solid var(--rule); margin-bottom: .5rem; }
.w-bar span { display: block; }
.w-legend { display: flex; flex-wrap: wrap; gap: .3rem .9rem; font-size: .8rem; }
.w-legend i { display: inline-block; width: 9px; height: 9px; border-radius: 2px;
  margin-right: .35rem; font-style: normal; }
.w-empty { color: var(--muted); font-size: .9rem; }

/* --- Production symbol ---------------------------------------------------
   Drawn, never fetched — see data/symbol.js for why it is not a Playbill.
   Sits top-left of the card with the text flowing beside it. */
.card { position: relative; }
.card > .sym {
  position: absolute; top: .95rem; left: .95rem;
  width: 34px; height: auto; flex: none;
}
.card > .sym + .card-head,
.card > .sym ~ .card-meta,
.card > .sym ~ .cast-list { padding-left: 46px; }
@media (max-width: 420px) {
  .card > .sym { width: 28px; top: .8rem; left: .8rem; }
  .card > .sym + .card-head,
  .card > .sym ~ .card-meta,
  .card > .sym ~ .cast-list { padding-left: 38px; }
}

/* The image the OCR engine actually read. Shown inside the raw-output panel
   so a disappointing scan can be diagnosed instead of guessed at. */
.scan-image { margin: .5rem 0; }
.scan-image .shot {
  display: block; width: 100%; max-width: 260px; height: auto;
  border: 1px solid var(--rule); border-radius: 6px; background: #fff;
}
.crop-note { font-size: .82rem; color: var(--muted); margin: 0 0 .2rem; }

/* ---------------------------------------------------------------- People

   One row per performer, not per sighting. The count is the point, so it
   leads; the roles and shows are why the count is what it is.
*/
.people-head { display: flex; gap: .75rem; flex-wrap: wrap; align-items: end;
               margin-bottom: .5rem; }
.people-head #people-search { min-width: 15rem; }
.people-list { display: flex; flex-direction: column; gap: .35rem; }

.person-row { display: grid; grid-template-columns: 2.6rem 1fr auto;
              gap: .75rem; align-items: baseline;
              padding: .6rem .75rem; border: 1px solid var(--rule);
              border-radius: .5rem; background: var(--paper); }
.person-row .times { font: 700 1.15rem/1 var(--serif); text-align: right;
                     font-variant-numeric: tabular-nums; }
.person-row .who { font-weight: 600; }
.person-row .detail { display: block; color: var(--muted); font-size: .85rem;
                      margin-top: .15rem; }

/* An assumed sighting is the app's guess, not the logger's word, and the two
   must never look alike. Same reasoning as the baseline rows. */
.person-row .pill.assumed-only { opacity: .75; }
.people-empty { color: var(--muted); padding: 1rem 0; }

@media (max-width: 34rem) {
  .person-row { grid-template-columns: 2.2rem 1fr; }
  .person-row .covers { grid-column: 2; }
}

/* ------------------------------------------------------- Prose pages

   /privacy, and anything else that is words rather than an interface. Its own
   narrow measure, because a policy set to the full width of a logbook is a
   policy nobody finishes.
*/
/* The app sets these on body; a standalone page has to do it itself or it
   inherits the browser's defaults and the dark scheme half-applies. */
.prose-page { background: var(--paper); color: var(--ink);
              font-family: var(--sans, system-ui, sans-serif); }
.prose a:hover { text-decoration: none; }
.prose { max-width: 36rem; margin: 0 auto; padding: 2.5rem 1.25rem 4rem; }
.prose h1 { margin: 0 0 .5rem; }
.prose h2 { margin: 2rem 0 .5rem; font-size: 1.1rem; }
.prose p, .prose li { line-height: 1.6; }
.prose ul { padding-left: 1.2rem; }
.prose li { margin: .35rem 0; }
.prose .lede { font-size: 1.05rem; }
.prose .muted { font-size: .85rem; }
.prose-back { margin: 0 0 1.5rem; font-size: .9rem; }

/* ------------------------------------------------------- Beta gate

   Covers the app rather than replacing it. See beta-gate.js on why that is
   honest: this hides an unfinished interface, it does not protect data.
*/
.beta-gate { position: fixed; inset: 0; z-index: 9999;
             display: grid; place-items: center; padding: 1.25rem;
             background: var(--paper); }
.beta-card { max-width: 22rem; width: 100%; text-align: center; }
.beta-card h1 { margin: 0 0 .25rem; }
.beta-card p { color: var(--muted); margin: 0 0 1.25rem; }
.beta-card form { display: flex; gap: .5rem; }
.beta-card input { flex: 1; }
.beta-err { min-height: 1.2em; margin: .75rem 0 0 !important; color: var(--red); }

/* ---------------------------------------------------- The house ornament

   Static it fills an empty screen; animated it is the loading indicator, and
   the animation is a marquee chase rather than a spinner — the same idea as
   the bulb strip under the header, so the app explains "working" one way.
*/
.ornament { display: block; width: 132px; height: auto; margin: 0 auto 18px; }
.ornament .ray    { fill: var(--gold); opacity: .22; }
.ornament .arc    { stroke: var(--gold); opacity: .5; }
.ornament .plinth { fill: var(--gold); opacity: .5; }

/* Small and quiet where it stands in for a line of text. */
.ornament.is-loading { width: 76px; margin-bottom: 10px; }

@keyframes marquee-chase {
  0%, 42%, 100% { opacity: .18; }
  16%           { opacity: 1; }
}
.ornament.is-loading .ray {
  animation: marquee-chase 1.5s linear infinite;
  /* --i is set per ray when it is drawn. Each lights one step behind the
     last, which is a chase; lighting them together would be a pulse. */
  animation-delay: calc(var(--i) * .1s);
}

/* A chase is a repeating flash, which is exactly what a vestibular or
   photosensitivity trigger looks like. Anybody who has asked for less motion
   gets a lit ornament and no movement at all — the state still reads. */
@media (prefers-reduced-motion: reduce) {
  .ornament.is-loading .ray { animation: none; opacity: .5; }
}

.empty .ornament { opacity: .9; }

/* ------------------------------------------------- Cards on a phone

   The head is a flex row: title on the left, the show's score and the
   night's on the right. At 375px they fight — "The Great Gatsby" wrapped to
   two lines to make room for two pills, while "Wicked" did not, so two cards
   in the same list laid out differently and the list stopped scanning.

   The title takes the whole line and the scores drop beneath it, together.
   Every card the same shape, whatever its title is called.
*/
@media (max-width: 480px) {
  .card-head { flex-wrap: wrap; row-gap: 3px; }
  .card-show { flex: 1 1 100%; font-size: 17px; }
  .card-head .rating { font-size: 12px; }

  /* Which leaves the scores hard against the left margin, where the title
     starts — a column of three left edges reads as one thing. */
  .card-head .rating.show { margin-right: auto; }
}

/* ------------------------------------------------------- Share row

   Text-and-link networks. Quieter than the image button on purpose: on a
   phone the share sheet is the one that reaches Instagram, and these cannot
   carry a picture at all.
*/
.share-row { display: inline-flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.share-net {
  font: 600 11px/1 var(--sans); text-transform: uppercase; letter-spacing: .1em;
  padding: 8px 10px; border: 1px solid var(--rule); border-radius: 20px;
  color: var(--muted); text-decoration: none; white-space: nowrap;
}
.share-net:hover { color: var(--ink); border-color: var(--gold); }
.share-net:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }

/* ------------------------------------------------- Sign-in methods

   Linking is the only defence against a second account, and a second account
   is silent — no error, just an empty logbook. So this sits in front of
   people rather than behind a settings page.
*/
.signin-methods { margin: 0 0 18px; padding: 14px 16px;
                  border: 1px solid var(--rule); border-radius: var(--radius);
                  background: var(--paper-2); }
.signin-methods h3 { margin: 0 0 8px; font: 700 14px/1.2 var(--sans);
                     text-transform: uppercase; letter-spacing: .12em; }
.signin-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.signin-row { display: flex; align-items: center; gap: 8px;
              border: 1px solid var(--rule); border-radius: 20px;
              padding: 6px 8px 6px 12px; font-size: 13px; }
.signin-row .on { color: var(--good); font-weight: 700; }
.signin-row .off { color: var(--muted); }
