:root {
  color-scheme: dark;
  --bg: #090d14;
  --canvas: #101722;
  --panel: #182231;
  --panel-raised: #233044;
  --panel-soft: #151d2a;
  --line: #3d4b60;
  --line-soft: #2a3546;
  --text: #edf4ff;
  --text-soft: #c5d2df;
  --text-muted: #8d9baa;
  --selection: #b985ff;
  --gold: #d9ba68;
  --green: #7dbb96;
  --green-text: #bce6cd;
  --green-border: rgba(125, 187, 150, 0.64);
  --amber: #d39a62;
  --amber-text: #f1c992;
  --amber-border: rgba(211, 154, 98, 0.72);
  --rose-text: #f3a9b0;
  --rose-border: rgba(207, 34, 46, 0.6);
  --violet: #9d8cff;
  --violet-text: #d6ccff;
  --violet-border: rgba(157, 140, 255, 0.72);
  --blue: #87b5ff;
  --topper-a: #b985ff;
  --topper-b: #d9ba68;
  --topper-c: #7dbb96;
  --topper-d: #87b5ff;
  --poster-bg: #18263a;
  --avatar-bg: #203047;
  --shadow: 0 20px 60px rgba(0, 0, 0, 0.34);
  --radius: 8px;
  --rail: 216px;
  --font-ui: Inter, "Atkinson Hyperlegible", system-ui, -apple-system, BlinkMacSystemFont,
    "Segoe UI", sans-serif;
  --font-display: "Source Serif 4", Georgia, "Times New Roman", serif;
}

* {
  box-sizing: border-box;
}

/* Dev-only UI (#122). Elements marked [data-dev-only] are hidden by default so
   they never flash in production; main.js sets data-dev on <html> in a dev
   environment, which disables this rule and lets them render normally. Higher
   specificity than the component display rules, so no !important is needed.
   The data-dev signal is an interim location heuristic, superseded by a
   build-time env flag in a later stage. */
html:not([data-dev]) [data-dev-only] {
  display: none;
}

/* The `hidden` attribute is only a user-agent stylesheet rule, so any author
   `display` on the same element -- every .badge, .panel, button and grid in
   this file -- silently beats it, and `el.hidden = true` did nothing on those.
   That is why person pages always showed a "Claimed" badge and why account
   badges never hid. Restated here with !important so the attribute means what
   every script assumes it means; the per-surface copies further down predate
   this and are harmless. Elements that must animate or lay out while "hidden"
   should use a class instead of the attribute. */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  line-height: 1.45;
}

button,
input,
textarea {
  font: inherit;
}

button {
  cursor: pointer;
}

.app-shell {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  min-height: 100vh;
  background: var(--canvas);
}

.rail {
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding: 18px 14px;
  border-right: 1px solid var(--line-soft);
  background: color-mix(in srgb, var(--bg) 84%, #000 16%);
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
}

.brand-mark {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--selection);
  border-radius: 6px;
  color: var(--gold);
  font-family: var(--font-display);
  font-size: 21px;
  font-weight: 700;
}

.brand-name {
  font-family: var(--font-display);
  font-size: 24px;
  line-height: 1;
}

.rail-nav {
  display: grid;
  gap: 6px;
}

.rail-link {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 38px;
  padding: 0 11px;
  border-radius: 6px;
  color: var(--text-soft);
  font-size: 14px;
  text-decoration: none;
}

.rail-link.active {
  background: color-mix(in srgb, var(--selection) 20%, transparent);
  color: var(--text);
  box-shadow: inset 3px 0 0 var(--selection);
}

.rail-link span {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border: 1px solid var(--line);
  border-radius: 5px;
  color: var(--gold);
  font-size: 12px;
}

/* Unread-notification badge, injected next to the "Me" nav item by main.js
   (#116) — one place so it shows up on every page without per-page markup. */
.nav-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  margin-left: auto;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--rose-border);
  color: var(--rose-text);
  font-size: 11px;
  line-height: 1;
}

.rail-footer {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: auto;
  padding-top: 18px;
  border-top: 1px solid var(--line-soft);
  color: var(--text-muted);
  font-size: 12px;
}

.page-footer {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 28px;
  padding-top: 14px;
  border-top: 1px solid var(--line-soft);
}

.footer-link {
  color: var(--text-muted);
  font-size: 12px;
  text-decoration: none;
}

.footer-link:hover,
.footer-link:focus-visible {
  color: var(--text-soft);
  text-decoration: underline;
}

.status-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--amber);
}

.status-dot.ok {
  background: var(--green);
}

.status-dot.error {
  background: var(--selection);
}

.app-content {
  min-width: 0;
  padding: 24px;
}

.topbar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 14px;
  align-items: center;
  margin-bottom: 22px;
}

.topbar-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: flex-end;
}

.app-view {
  display: none;
}

.app-view.active {
  display: block;
}

.app-view.show-surface.active {
  display: grid;
}

.searchbox {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  color: var(--text-muted);
}

.searchbox:focus-within {
  outline: 2px solid var(--selection);
  outline-offset: 2px;
}

.searchbox input {
  width: 100%;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--text);
}

.hero-panel,
.panel {
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--panel);
}

.hero-panel {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 18px;
  align-items: end;
  margin-bottom: 16px;
  padding: 22px;
}

.kicker {
  margin: 0 0 8px;
  color: var(--text-muted);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

h1,
h2,
p {
  margin-top: 0;
}

h1 {
  margin-bottom: 14px;
  font-family: var(--font-display);
  font-size: 48px;
  line-height: 1;
}

.hero-subtitle {
  max-width: 680px;
  margin-bottom: 14px;
  color: var(--text-soft);
}

h2 {
  margin-bottom: 2px;
  font-size: 15px;
  line-height: 1.2;
  text-transform: uppercase;
}

.panel-header p,
.poster-copy > span,
.row-title span {
  color: var(--text-muted);
  font-size: 12px;
}

.chip-row,
.hero-actions,
.button-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.chip,
.badge,
.tiny-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 28px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--panel-soft);
  color: var(--text-soft);
  font-size: 12px;
  white-space: nowrap;
}

.badge.green {
  border-color: var(--green-border);
  color: var(--green-text);
}

.badge.amber {
  border-color: var(--amber-border);
  color: var(--amber-text);
}

.badge.red {
  border-color: var(--rose-border);
  color: var(--rose-text);
}

.badge.violet {
  border-color: var(--violet-border);
  color: var(--violet-text);
}

.primary-button,
.secondary-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: 0 14px;
  border-radius: 6px;
  border: 1px solid transparent;
  color: var(--text);
  background: var(--selection);
  text-decoration: none;
}

.secondary-button {
  border-color: var(--line);
  background: var(--panel-raised);
  color: var(--text-soft);
}

/* Light text on --selection is 2.4:1; the page background on it is well past
   4.5:1 (axe color-contrast on every page, #370). */
.primary-button {
  color: var(--bg);
}

button:disabled {
  cursor: not-allowed;
  opacity: 0.56;
}

.tiny-button {
  min-height: 30px;
  background: transparent;
}

.content-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 16px;
}

.span-5 {
  grid-column: span 5;
}

.span-7 {
  grid-column: span 7;
}

.span-12 {
  grid-column: 1 / -1;
}

.panel-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 14px;
  align-items: center;
  min-height: 56px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--line-soft);
}

.panel-header p {
  margin-bottom: 0;
}

.panel-header h1 {
  margin-bottom: 2px;
  font-family: var(--font-ui);
  font-size: 15px;
  line-height: 1.2;
  text-transform: uppercase;
}

.data-list {
  display: grid;
}

.empty-state {
  padding: 18px;
  color: var(--text-muted);
  font-size: 13px;
}

.row {
  display: grid;
  grid-template-columns: minmax(190px, 1.45fr) minmax(120px, 0.8fr) 132px;
  gap: 18px;
  align-items: center;
  min-height: 62px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--line-soft);
}

.moderation-row,
.audit-row {
  display: grid;
  grid-template-columns: minmax(180px, 1fr) 104px 96px;
  gap: 14px;
  align-items: center;
  min-height: 64px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--line-soft);
}

.audit-row {
  grid-template-columns: minmax(180px, 1fr) 120px;
}

.moderation-row:last-child,
.audit-row:last-child {
  border-bottom: 0;
}

.moderation-row[role="button"] {
  cursor: pointer;
}

.moderation-row[role="button"]:hover,
.moderation-row[role="button"]:focus-visible {
  background: var(--panel-soft);
  outline: none;
}

.moderation-row.selected {
  background: var(--selection);
  box-shadow: inset 3px 0 0 var(--violet-border);
}

.review-detail {
  display: grid;
  gap: 6px;
  margin-bottom: 14px;
}

.detail-line {
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: 12px;
  align-items: baseline;
  font-size: 13px;
}

.detail-line span {
  color: var(--text-muted);
}

.detail-line strong {
  color: var(--text);
  overflow-wrap: anywhere;
}

.moderation-row .badge,
.audit-row .badge {
  justify-self: end;
}

.row:last-child {
  border-bottom: 0;
}

.row-title {
  min-width: 0;
}

.row-title strong {
  display: block;
  color: var(--text);
  font-weight: 650;
  overflow-wrap: anywhere;
}

.review-headline-preview {
  display: block;
  margin-top: 6px;
  color: var(--text-soft);
  font-size: 13px;
  font-weight: 650;
}

.review-body-preview {
  display: -webkit-box;
  margin-top: 4px;
  margin-bottom: 0;
  overflow: hidden;
  color: var(--text-muted);
  font-size: 13px;
  line-height: 1.4;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
}

.rating {
  color: var(--gold);
  font-size: 18px;
  font-variant-numeric: tabular-nums;
}

.row-actions {
  display: flex;
  justify-content: flex-end;
  gap: 6px;
}

.row-actions .badge {
  width: 112px;
}

.row > .badge,
.row-actions {
  justify-self: end;
  width: 132px;
}

.icon-button {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--panel-soft);
  color: var(--text-soft);
  font-size: 16px;
  line-height: 1;
}

.summary-stack {
  display: grid;
  gap: 12px;
  padding: 18px;
}

.summary-card,
.selected-performance {
  display: grid;
  gap: 5px;
  min-height: 82px;
  padding: 12px;
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  background: var(--panel-soft);
}

.summary-card strong,
.selected-performance strong {
  overflow-wrap: anywhere;
}

.summary-card span:last-child,
.selected-performance span:last-child {
  color: var(--text-muted);
  font-size: 12px;
}

.summary-label {
  color: var(--text-soft);
  font-size: 12px;
  text-transform: uppercase;
}

.stat-strip {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}

.stat {
  display: grid;
  gap: 2px;
  min-height: 50px;
  padding: 8px;
  border: 1px solid var(--line-soft);
  border-radius: 7px;
  background: var(--panel);
}

.stat strong {
  color: var(--gold);
  font-size: 18px;
  font-variant-numeric: tabular-nums;
}

.stat span {
  color: var(--text-muted);
  font-size: 11px;
}

.form-stack,
.form-grid {
  padding: 18px;
}

.panel-subform {
  border-top: 1px solid var(--line-soft);
}

.form-stack {
  display: grid;
  gap: 14px;
}

.form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
}

.form-field {
  display: grid;
  gap: 6px;
}

.form-field.wide,
.form-actions.wide,
.selected-performance.wide {
  grid-column: 1 / -1;
}

.form-field span {
  color: var(--text-soft);
  font-size: 13px;
}

.form-field input,
.form-field select,
.form-field textarea {
  width: 100%;
  min-height: 42px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--panel-soft);
  color: var(--text);
}

.form-field textarea {
  resize: vertical;
}

.combobox {
  position: relative;
}

.combobox-list {
  list-style: none;
  margin: 4px 0 0;
  padding: 4px;
  position: absolute;
  z-index: 30;
  top: 100%;
  left: 0;
  right: 0;
  max-height: 280px;
  overflow-y: auto;
  background: var(--panel-raised);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: var(--shadow);
}

.combobox-list[hidden] {
  display: none;
}

.combobox-option {
  padding: 8px 10px;
  border-radius: 6px;
  cursor: pointer;
  color: var(--text);
}

.combobox-option:hover,
.combobox-option.is-active {
  background: var(--selection);
}

.combobox-option-meta {
  display: block;
  font-size: 0.85em;
  color: var(--text-muted);
}

.combobox-empty {
  padding: 8px 10px;
  color: var(--text-muted);
}

.discover-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 16px;
}

.filter-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.85em;
  color: var(--text-muted);
}

.filter-field select {
  min-height: 38px;
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--panel-soft);
  color: var(--text);
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
}

.form-status {
  color: var(--text-muted);
  font-size: 13px;
}

.panel-body {
  padding: 16px 18px 18px;
}

.panel-body > .form-status:first-child {
  margin-top: 0;
}

.social-counts {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  padding: 0 18px 14px;
  color: var(--text-muted);
  font-size: 13px;
}

.upload-list {
  display: grid;
  gap: 8px;
  min-height: 42px;
}

.upload-file {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 12px;
  align-items: center;
  min-height: 42px;
  padding: 10px 12px;
  border: 1px solid var(--line-soft);
  border-radius: 7px;
  background: var(--panel-soft);
}

.upload-file strong {
  overflow-wrap: anywhere;
}

.upload-file span,
.upload-result span {
  color: var(--text-muted);
  font-size: 12px;
}

.upload-result-panel {
  margin-top: 16px;
}

.upload-result {
  display: grid;
  gap: 10px;
}

.upload-result-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  padding: 18px;
}

.upload-result-item {
  display: grid;
  gap: 5px;
  min-height: 62px;
  padding: 12px;
  border: 1px solid var(--line-soft);
  border-radius: 7px;
  background: var(--panel-soft);
}

.upload-result-item strong {
  overflow-wrap: anywhere;
}

.review-workbench {
  display: grid;
  grid-template-columns: minmax(280px, 0.9fr) minmax(0, 1.1fr);
  gap: 16px;
  margin-top: 16px;
}

.review-image-list {
  display: grid;
  gap: 12px;
  padding: 18px;
}

.review-image-item {
  display: grid;
  gap: 10px;
  padding: 12px;
  border: 1px solid var(--line-soft);
  border-radius: 7px;
  background: var(--panel-soft);
}

.review-image-header {
  display: grid;
  gap: 4px;
}

.review-image-header span {
  color: var(--text-muted);
  font-size: 12px;
}

.review-image-item img {
  width: 100%;
  max-height: 520px;
  object-fit: contain;
  border: 1px solid var(--line-soft);
  border-radius: 6px;
  background: var(--bg);
}

/* Sensitive images (#132): blurred until the viewer taps "Show image". The
   wrapper clips the blur's soft edge and holds the button over the middle. */
.sensitive-media {
  position: relative;
  display: grid;
  overflow: hidden;
  border-radius: 6px;
}

.sensitive-media-image {
  filter: blur(28px);
}

.sensitive-media-button {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--panel-raised);
}

.sensitive-media-button:focus-visible {
  outline: 2px solid var(--selection);
  outline-offset: 2px;
}

.ocr-output {
  min-height: 420px;
  max-height: 640px;
  margin: 0;
  padding: 18px;
  overflow: auto;
  color: var(--text-soft);
  font-size: 12px;
  white-space: pre-wrap;
}

.review-image-meta {
  display: flex;
  align-items: center;
  gap: 8px;
}

.review-page-text {
  margin: 0;
  font-size: 12px;
  overflow-wrap: anywhere;
}

.review-credits-group + .review-credits-group {
  margin-top: 20px;
}

.review-credits-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}

.review-credits-head h3 {
  margin: 0;
  font-size: 15px;
}

.review-credit-list {
  display: grid;
  gap: 12px;
}

.review-credit-row {
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  padding: 14px;
  background: var(--panel-soft);
}

.review-credit-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

.review-include {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
}

.review-suggestion {
  font-size: 12px;
  color: var(--text-muted);
}

.review-credit-head .tiny-button {
  margin-left: auto;
}

.review-credit-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}

@media (max-width: 720px) {
  .review-credit-fields {
    grid-template-columns: 1fr;
  }
}

.poster-rail {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  padding: 18px;
}

.poster-tile {
  position: relative;
  display: block;
  min-height: 238px;
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  background: var(--panel-soft);
  overflow: hidden;
  transition:
    border-color 120ms ease,
    transform 120ms ease,
    box-shadow 120ms ease;
}

.poster-link {
  display: block;
  min-height: inherit;
  color: inherit;
}

a.poster-link {
  text-decoration: none;
  cursor: pointer;
}

.poster-tile:has(a.poster-link:hover) {
  border-color: var(--selection);
  transform: translateY(-2px);
  box-shadow: var(--shadow);
}

a.poster-link:focus-visible {
  outline: 2px solid var(--selection);
  outline-offset: -2px;
}

.wishlist-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-width: 44px;
  min-height: 44px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--panel-soft);
  color: var(--text-soft);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}

.wishlist-toggle .wishlist-heart {
  font-size: 20px;
  line-height: 1;
}

.wishlist-toggle:hover,
.wishlist-toggle[aria-pressed="true"] {
  border-color: var(--selection);
  color: var(--selection);
}

.wishlist-toggle:focus-visible {
  outline: 2px solid var(--selection);
  outline-offset: 2px;
}

.wishlist-toggle:disabled {
  opacity: 0.6;
  cursor: progress;
}

.poster-tile .wishlist-toggle {
  position: absolute;
  top: 6px;
  right: 6px;
}

.show-wishlist {
  margin-bottom: 8px;
}

.show-wishlist:empty {
  display: none;
}

.poster-art {
  display: grid;
  align-content: end;
  min-height: 148px;
  padding: 12px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--poster-bg);
  overflow: hidden;
}

.poster-art.topper-a {
  border-top: 4px solid var(--topper-a);
}

.poster-art.topper-b {
  border-top: 4px solid var(--topper-b);
}

.poster-art.topper-c {
  border-top: 4px solid var(--topper-c);
}

.poster-art.topper-d {
  border-top: 4px solid var(--topper-d);
}

.poster-art strong {
  display: block;
  max-width: 100%;
  font-family: var(--font-display);
  font-size: 30px;
  line-height: 0.96;
  overflow-wrap: break-word;
  text-wrap: balance;
}

.poster-art strong.long-title {
  font-size: 25px;
}

.poster-art strong.extra-long-title {
  font-size: 22px;
}

.poster-copy {
  display: grid;
  gap: 5px;
  padding: 12px;
}

.poster-copy strong {
  font-size: 14px;
}

.segmented-control {
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel-soft);
}

.segment {
  min-height: 30px;
  padding: 0 11px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text-soft);
  white-space: nowrap;
}

.segment.active {
  background: color-mix(in srgb, var(--selection) 28%, var(--panel-raised));
  color: var(--text);
}

.discover-board {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 22px;
  padding: 18px;
  align-items: start;
}

.discover-region {
  display: grid;
  gap: 10px;
}

.discover-region h3 {
  margin: 0;
  color: var(--text-soft);
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.discover-rail {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}

.discover-rail .poster-tile {
  min-height: 262px;
}

.discover-rail .poster-art {
  min-height: 170px;
}

.poster-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 3px;
}

.entity-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 18px;
  align-items: end;
  padding: 22px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--panel);
}

.entity-header h1,
.entity-header h2 {
  margin-bottom: 8px;
  font-family: var(--font-display);
  font-size: 42px;
  line-height: 1;
  text-transform: none;
}

.entity-header p:last-child {
  max-width: 720px;
  margin-bottom: 0;
  color: var(--text-soft);
}

.entity-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: flex-end;
}

.show-surface {
  gap: 16px;
}

.show-layout {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}

.show-wide {
  grid-column: 1 / -1;
}

.compact-list,
.performer-list,
.production-list {
  display: grid;
}

.compact-item,
.performer-item,
.production-row {
  display: grid;
  gap: 8px;
  min-height: 64px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--line-soft);
}

.compact-item > div,
.performer-item > div,
.production-row > div {
  display: grid;
  gap: 4px;
  min-width: 0;
}

.compact-item:last-child,
.performer-item:last-child,
.production-row:last-child {
  border-bottom: 0;
}

.compact-item {
  grid-template-columns: minmax(0, 1fr) 96px;
  align-items: center;
}

.compact-item.unread {
  box-shadow: inset 3px 0 0 var(--selection);
}

.compact-item.unread strong {
  color: var(--text);
}

.compact-item strong,
.performer-item strong,
.production-row strong {
  overflow-wrap: anywhere;
}

.compact-item span,
.performer-item span,
.production-row span {
  color: var(--text-muted);
  font-size: 12px;
}

.performer-item {
  grid-template-columns: minmax(0, 1fr) 132px;
  align-items: center;
}

.performer-source {
  justify-self: end;
  width: 132px;
}

.production-row {
  grid-template-columns: minmax(180px, 1.3fr) minmax(140px, 1fr) 124px;
  align-items: center;
}

.production-row .badge {
  justify-self: end;
  width: 112px;
}

a.production-row {
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition:
    background 120ms ease,
    border-color 120ms ease;
}

a.production-row:hover {
  background: var(--panel-soft);
}

/* The title carries the link affordance at rest, not only on hover (#370). */
a.production-row strong {
  text-decoration: underline;
  text-decoration-color: var(--line);
  text-underline-offset: 3px;
}

a.production-row:hover strong {
  color: var(--selection);
  text-decoration-color: currentColor;
}

a.production-row:focus-visible {
  outline: 2px solid var(--selection);
  outline-offset: -2px;
}

.mobile-tabbar {
  display: none;
}

:root {
  /* Fixed bottom tab bar: 6px padding + 44px tap target + 6px padding, plus
     its 10px gap to the viewport edge. Content reserves this much room (#394). */
  --tabbar-height: 66px;
}

@media (max-width: 1080px) {
  .app-shell {
    grid-template-columns: 1fr;
  }

  .rail {
    display: none;
  }

  .span-5,
  .span-7,
  .span-12 {
    grid-column: 1 / -1;
  }

  .discover-board {
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  }
}

@media (max-width: 720px) {
  .app-content {
    padding: 14px 14px calc(var(--tabbar-height) + 10px + env(safe-area-inset-bottom, 0px));
  }

  .topbar,
  .hero-panel,
  .panel-header,
  .entity-header,
  .form-grid,
  .upload-result-grid,
  .poster-rail,
  .row,
  .show-layout,
  .review-workbench,
    .compact-item,
    .performer-item,
    .production-row,
    .moderation-row,
    .audit-row {
    grid-template-columns: 1fr;
  }

  h1 {
    font-size: 34px;
  }

  .entity-header h1,
  .entity-header h2 {
    font-size: 34px;
  }

  .row {
    gap: 6px;
  }

  .row > .badge,
  .row-actions {
    justify-self: start;
    width: auto;
  }

  .row-actions {
    justify-content: flex-start;
  }

  .segmented-control {
    width: 100%;
  }

  .segment {
    flex: 1;
    padding: 0 8px;
  }

  .topbar-actions {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    width: 100%;
  }

  .topbar-actions button {
    min-width: 0;
  }

  .discover-rail {
    grid-template-columns: 1fr;
  }

  .performer-source,
  .production-row .badge {
    justify-self: start;
    width: auto;
  }

  .stat-strip {
    grid-template-columns: 1fr;
  }
}

/* The tab bar itself is defined once, at the end of this file, alongside the
   More sheet it opens -- see "Shell navigation and the signed-out landing". */

.review-list {
  display: grid;
  gap: 12px;
}

.review-card {
  display: grid;
  gap: 8px;
  padding: 14px 16px;
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  background: var(--panel-soft, var(--panel));
}

.review-card-head {
  display: flex;
  align-items: start;
  justify-content: space-between;
  gap: 12px;
}

.review-card-head > div {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.review-headline {
  margin: 0;
  font-weight: 650;
}

.review-body {
  margin: 0;
  color: var(--text-soft);
}

/* Spoiler reveal controls (#217). The inline one is a button that reads as
   text; both keep a visible focus ring. */
.spoiler-toggle {
  justify-self: start;
}

.spoiler-inline {
  display: inline;
  min-height: 24px;
  padding: 0 6px;
  border: 1px dashed var(--line);
  border-radius: 4px;
  background: var(--panel-raised);
  color: var(--text-soft);
  font: inherit;
  cursor: pointer;
  text-align: left;
  overflow-wrap: anywhere;
}

.spoiler-toggle:focus-visible,
.spoiler-inline:focus-visible {
  outline: 2px solid var(--selection);
  outline-offset: 2px;
}

.review-own-label {
  justify-self: start;
}

/* Account and privacy screens (#55).
   The danger tokens reuse the existing rose scale so the destructive controls
   read as the same family as the `badge red` already used for warnings. */
.danger-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: 0 14px;
  border-radius: 6px;
  border: 1px solid var(--rose-border);
  background: rgba(207, 34, 46, 0.14);
  color: var(--rose-text);
  font: inherit;
  cursor: pointer;
}
.danger-button:hover:not(:disabled) {
  background: rgba(207, 34, 46, 0.24);
}
.danger-button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.panel-danger {
  border-color: var(--rose-border);
}
.panel-danger .panel-header h2 {
  color: var(--rose-text);
}

.form-check {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 14px 0;
  color: var(--text-soft);
  cursor: pointer;
}
.form-check input {
  width: 18px;
  height: 18px;
  accent-color: var(--selection);
  flex: none;
}
.form-hint {
  color: var(--text-muted);
  font-size: 13px;
}
.form-status.success {
  color: var(--green-text);
}

.summary-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 14px;
  margin: 0 0 16px;
}
.summary-grid dt {
  color: var(--text-muted);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.summary-grid dd {
  margin: 4px 0 0;
  color: var(--text);
  font-weight: 600;
}

/* Standalone pages render without the rail/top bar, because the accounts that
   reach them cannot load the shell (#55). */
.standalone-page {
  display: flex;
  justify-content: center;
  padding: 48px 20px;
  background: var(--bg);
}
.standalone-shell {
  width: 100%;
  max-width: 560px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.standalone-brand {
  display: flex;
  align-items: center;
  gap: 10px;
}
.standalone-footnote {
  color: var(--text-muted);
  font-size: 13px;
  margin: 0;
}

/* Dual-control progress for the email change (#55). The two-step shape has to
   be legible or a half-finished change reads as a failure. */
.progress-steps {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  margin: 0 0 14px;
  padding: 0;
  counter-reset: step;
}
.progress-steps li {
  position: relative;
  padding-left: 26px;
  color: var(--text-muted);
  font-size: 13px;
  counter-increment: step;
}
.progress-steps li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1px solid var(--line);
  display: grid;
  place-items: center;
  font-size: 11px;
}
.progress-steps li.current {
  color: var(--text);
}
.progress-steps li.current::before {
  border-color: var(--amber-border);
  color: var(--amber-text);
}
.progress-steps li.done {
  color: var(--text-soft);
}
.progress-steps li.done::before {
  content: "✓";
  border-color: var(--green-border);
  color: var(--green-text);
}

/* ---------------------------------------------------------------------------
   Shell navigation and the signed-out landing (#188, #194)

   The tab bar lives here, in one place, rather than in the 720px block above:
   the rail is hidden from 1080px down, so between 721px and 1080px the app had
   no primary navigation at all. It now appears wherever the rail does not.
   --------------------------------------------------------------------------- */

/* The account block sits at the foot of the rail, above the status line. Both
   would otherwise claim `margin-top: auto`, which splits the free space
   between them and leaves the account floating mid-rail, so the footer gives
   its own up. */
.rail-account {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: auto;
}

.rail-footer {
  margin-top: 0;
}

.rail-account-name {
  min-width: 0;
  overflow: hidden;
  color: var(--text-soft);
  font-size: 13px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rail-sign-in {
  width: 100%;
}

.topbar-account {
  display: flex;
  align-items: center;
}

.topbar-account-name {
  display: inline-flex;
  align-items: center;
  max-width: 180px;
  min-height: 40px;
  padding: 0 8px;
  overflow: hidden;
  color: var(--text-soft);
  font-size: 13px;
  text-decoration: none;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar-account-name:hover,
.topbar-account-name:focus-visible {
  color: var(--text);
}

/* Hidden until the tab bar that opens it is on screen. */
.mobile-more-sheet {
  display: none;
}

@media (max-width: 1080px) {
  /* Room for the fixed bar, which is present at this width now too. */
  .app-content {
    padding-bottom: calc(var(--tabbar-height) + 10px + env(safe-area-inset-bottom, 0px));
  }

  .mobile-tabbar {
    position: fixed;
    right: 10px;
    bottom: calc(10px + env(safe-area-inset-bottom, 0px));
    left: 10px;
    z-index: 10;
    display: grid;
    /* Five tabs and More. renderPrimaryNav() rewrites this from what it
       actually painted; the static value is what the first frame uses. */
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: 4px;
    padding: 6px;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: color-mix(in srgb, var(--bg) 86%, #000 14%);
    box-shadow: var(--shadow);
  }

  .mobile-tabbar a,
  .mobile-more-tab {
    position: relative;
    display: grid;
    place-items: center;
    min-width: 0;
    /* 44px is the floor for a tap target; the bar used to sit just under it. */
    min-height: 44px;
    padding: 0 2px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: var(--text-soft);
    font-size: 12px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-decoration: none;
  }

  .mobile-tabbar a .nav-badge,
  .mobile-more-tab .nav-badge {
    position: absolute;
    top: 2px;
    right: 6px;
    margin-left: 0;
  }

  .mobile-tabbar a.active,
  .mobile-more-tab.active {
    background: color-mix(in srgb, var(--selection) 22%, transparent);
    color: var(--text);
  }

  /* Everything the bar has no room for. Sits directly above it, and scrolls
     rather than growing off the top of a short viewport. */
  .mobile-more-sheet {
    position: fixed;
    right: 10px;
    bottom: 72px;
    left: 10px;
    z-index: 11;
    display: grid;
    gap: 2px;
    max-height: calc(100vh - 96px);
    padding: 8px;
    overflow-y: auto;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: color-mix(in srgb, var(--bg) 94%, #000 6%);
    box-shadow: var(--shadow);
  }

  .mobile-more-sheet[hidden] {
    display: none;
  }

  .more-link {
    display: flex;
    align-items: center;
    min-height: 44px;
    padding: 0 12px;
    border-radius: 6px;
    color: var(--text-soft);
    font-size: 14px;
    text-decoration: none;
  }

  .more-link.active {
    background: color-mix(in srgb, var(--selection) 22%, transparent);
    color: var(--text);
  }

  .more-account {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: 6px;
    padding: 8px 4px 0;
    border-top: 1px solid var(--line-soft);
  }

  .more-account-name {
    min-width: 0;
    overflow: hidden;
    color: var(--text-soft);
    font-size: 13px;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .more-account .primary-button {
    width: 100%;
  }
}

/* Six columns in 355px is 54px a tab: enough for "Discover" at 11px, not
   reliably at 12px, and a clipped label is the defect #194 was filed about. */
@media (max-width: 420px) {
  .mobile-tabbar {
    gap: 3px;
    padding: 5px;
  }

  .mobile-tabbar a,
  .mobile-more-tab {
    font-size: 11px;
  }
}

/* The signed-out front door on index.html: shown to a visitor with no session,
   in place of a dashboard that has nothing of theirs to show yet (#188). */
.landing {
  display: grid;
  gap: 16px;
  max-width: 760px;
}

.landing[hidden],
.home-signed-in[hidden] {
  display: none;
}

.landing-intro {
  padding: 4px 2px;
}

.landing-lede {
  max-width: 640px;
  margin-bottom: 0;
  color: var(--text-soft);
  font-size: 17px;
}

.landing-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 18px;
  margin: 0;
}

.landing-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--text-soft);
  font-size: 14px;
}

.landing-links a:hover,
.landing-links a:focus-visible {
  color: var(--text);
}

/* Find and follow people (#192): the search rows and blocked list on
   social.html, the member profile at profile.html, and the actor byline the
   feed cards carry now that every feed item links to whoever logged it.

   Rows are laid out as a three-column grid on a desktop and collapse to a
   single column below 720px, where the badge and the follow button both go
   full width -- a 132px button in a stacked row is a thumb-sized miss. */
.app-view.social-surface.active,
.app-view.profile-surface.active,
.profile-body {
  display: grid;
  gap: 16px;
}

/* `hidden` has to actually hide, and on these pages it did not.
   The attribute is implemented by a *user-agent* rule (`[hidden] { display:
   none }`), and any author `display` declaration beats a UA one whatever the
   specificity -- so `.badge`, `.entity-header`, `.primary-button` and
   `.secondary-button` all quietly defeated it. That is why profile.html's
   Friends badge and follow/block buttons rendered before the profile had
   loaded, and it is the same reason person.html's "Claimed" badge and Website
   link have always shown on a person with neither.

   Scoped to the three surfaces this change owns rather than fixed with a
   global `[hidden]` rule, because several pages are being edited in parallel
   right now and a global rule would change all of them at once. The defect is
   still live elsewhere -- account.html's status badges are hidden the same
   ineffective way. */
.social-surface [hidden],
.profile-surface [hidden],
.person-surface [hidden] {
  display: none;
}

.user-search-form {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px;
  align-items: end;
  margin-bottom: 12px;
}

.user-list {
  display: grid;
}

.user-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 132px 132px;
  gap: 12px;
  align-items: center;
  min-height: 56px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line-soft);
}

.user-row:last-child {
  border-bottom: 0;
}

.user-row-name {
  min-width: 0;
  color: var(--text);
  font-weight: 650;
  overflow-wrap: anywhere;
  text-decoration: none;
}

.user-row-name:hover,
.user-row-name:focus-visible {
  text-decoration: underline;
}

.user-row .badge {
  justify-self: start;
}

.user-row button {
  min-height: 44px;
}

/* The feed's byline. The card is not itself a link -- it carries a rating and
   a review headline -- so the actor's name is the link out to their profile. */
.feed-byline {
  margin: 0;
  color: var(--text-muted);
  font-size: 13px;
}

.feed-actor {
  color: var(--text);
  font-weight: 650;
  text-decoration: none;
}

.feed-actor:hover,
.feed-actor:focus-visible {
  text-decoration: underline;
}

.profile-bio {
  margin-bottom: 14px;
  color: var(--text-soft);
  white-space: pre-line;
}

/* Followers and following are two numbers, not the three .stat-strip assumes. */
.profile-counts {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (max-width: 720px) {
  .user-search-form {
    grid-template-columns: 1fr;
  }

  .user-row {
    grid-template-columns: 1fr;
    gap: 8px;
    padding: 12px 0;
  }

  .user-row .badge,
  .user-row button,
  .user-search-form button {
    width: 100%;
  }

  /* Follow, Block and the block confirmation go full width on a phone: the
     header collapses to one column here, and a 100px button pinned right is a
     thumb-sized miss. */
  .profile-surface .entity-actions {
    justify-content: stretch;
  }

  .profile-surface .entity-actions > *,
  .profile-surface .button-row > button,
  .profile-surface .button-row > .secondary-button {
    flex: 1 1 100%;
    min-height: 44px;
  }
}

/* --------------------------------------------------------------------------
   Playbill upload and review (#195, #196, #198)

   The "my uploads" list both upload.html and playbill-review.html render, the
   per-file media-type rows on the upload form, and the review row's person
   search, confidence cues and blocking messages. Everything here has to stay
   usable at 375px: the rows collapse to a single column and the submit control
   sits clear of the fixed mobile tab bar.
   -------------------------------------------------------------------------- */

.upload-notice {
  display: grid;
  gap: 8px;
  margin: 0 18px 4px;
  padding: 14px;
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  background: var(--panel-soft);
}

.upload-notice p {
  margin: 0;
  color: var(--text-soft);
  font-size: 13px;
}

.upload-budget {
  margin: -4px 0 0;
  color: var(--text-muted);
  font-size: 12px;
}

.upload-status-detail {
  margin: 0;
  padding: 0 18px 16px;
  color: var(--amber-text);
  font-size: 13px;
}

.upload-file-type {
  display: grid;
  gap: 4px;
  min-width: 0;
}

.upload-file-type span {
  color: var(--text-muted);
  font-size: 11px;
  text-transform: uppercase;
}

.upload-file-type select {
  min-height: 38px;
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--panel);
  color: var(--text);
}

.playbill-list {
  display: grid;
  gap: 10px;
  padding: 18px;
}

.playbill-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: 12px;
  align-items: center;
  padding: 12px;
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  background: var(--panel-soft);
}

.playbill-row.is-current {
  border-color: var(--violet-border);
}

.playbill-row-main {
  display: grid;
  gap: 4px;
  min-width: 0;
}

.playbill-row-main span {
  color: var(--text-muted);
  font-size: 12px;
  overflow-wrap: anywhere;
}

/* The link and the delete control share the row's last column, so adding the
   delete button did not have to change the three-column grid above (#216). */
.playbill-row-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.playbill-row-detail {
  grid-column: 1 / -1;
  margin: 0;
  color: var(--amber-text);
  font-size: 12px;
}

.review-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 18px;
}

.review-row-label {
  color: var(--text-muted);
  font-size: 12px;
}

.review-confidence-hint {
  margin: -6px 0 10px;
  color: var(--text-muted);
  font-size: 12px;
}

/* Below the warn threshold the whole row is marked, not just the badge: the
   reviewer has to actively confirm or correct it (SPEC-03 §4.4). */
.review-credit-row.is-low-confidence {
  border-color: var(--amber-border);
}

.review-credit-row.is-blocked {
  border-color: var(--rose-border);
}

.review-row-error {
  margin: 10px 0 0;
  color: var(--rose-text);
  font-size: 12px;
}

.review-person-note {
  margin: 0;
  color: var(--text-muted);
  font-size: 12px;
}

.review-person-note a {
  color: var(--text-soft);
}

.combobox-option.combobox-create {
  border-top: 1px solid var(--line-soft);
  color: var(--text-soft);
  font-size: 13px;
}

/* The credits form sat flush against the panel border, which reads as broken
   once the rows carry their own borders and messages. */
.review-form {
  padding: 18px;
}

.review-submit-result {
  margin-top: 12px;
  padding: 12px;
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  background: var(--panel-soft);
}

.review-submit-result p {
  margin: 0 0 6px;
  font-size: 13px;
}

.review-submit-result p:last-child {
  margin-bottom: 0;
}

.review-warning {
  color: var(--amber-text);
}

@media (max-width: 720px) {
  .upload-file {
    grid-template-columns: 1fr;
  }

  .playbill-row {
    grid-template-columns: 1fr;
    justify-items: start;
  }

  .playbill-row-actions {
    width: 100%;
  }

  .playbill-row .secondary-button {
    width: 100%;
  }

  .review-toolbar .secondary-button,
  .review-submit-actions .primary-button {
    width: 100%;
  }

  .review-credit-head {
    gap: 8px;
  }

  /* Keeps "Remove" from being pushed off the row once the badges wrap. */
  .review-credit-head .tiny-button {
    margin-left: 0;
  }
}

/* Contribute and moderation: typed contribution forms, the contributor's own
   submission list, and a queue row a moderator can actually read (#194, #199).

   The queue row grew from three inline cells to a stacked block, because a
   moderator ruling on a contribution needs the field-by-field before/after and
   the submitter's record in front of them, not just an id and a status. */

.contribution-details {
  grid-column: 1 / -1;
  padding: 10px 12px;
  border: 1px solid var(--line-soft);
  border-radius: 7px;
  background: var(--panel-soft);
}

.contribution-details > summary {
  color: var(--text-soft);
  font-size: 13px;
  cursor: pointer;
}

.contribution-details[open] > summary {
  margin-bottom: 12px;
}

.contribution-credit-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
}

.contribution-target-fields {
  display: grid;
  gap: 14px;
}

.contribution-target-fields[hidden] {
  display: none;
}

.contribution-result {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  padding: 18px;
}

.contribution-result-line {
  margin: 0;
  color: var(--text);
}

.contribution-result .form-hint,
.contribution-result .empty-state {
  flex-basis: 100%;
  margin: 0;
  padding: 0;
}

.submission-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 12px;
  align-items: start;
  padding: 12px 18px;
  border-bottom: 1px solid var(--line-soft);
  font-size: 14px;
}

.submission-row:last-child {
  border-bottom: 0;
}

.submission-row .badge {
  justify-self: end;
}

/* The sent-on line and the moderator's reason are separate facts, and the
   inherited .row-title span rule leaves them inline, so they ran together. */
.submission-row .row-title span {
  display: block;
}

.submission-reason {
  color: var(--text-soft);
}

/* A non-moderator gets one sentence instead of a workbench that 403s. */
.moderation-notice {
  margin-bottom: 16px;
}

.moderation-workbench[hidden] {
  display: none;
}

.moderation-setup {
  margin-top: 16px;
  padding: 14px 18px;
}

.moderation-setup > details > summary {
  color: var(--text-soft);
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
}

.moderation-setup .panel-body {
  padding: 14px 0 0;
}

.moderation-row {
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "title badge"
    "detail detail"
    "actions actions";
  align-items: start;
  row-gap: 10px;
}

.moderation-row > .row-title {
  grid-area: title;
}

.moderation-row > .badge {
  grid-area: badge;
}

.moderation-row > .queue-detail {
  grid-area: detail;
}

.moderation-row > .row-actions {
  grid-area: actions;
  justify-self: start;
  justify-content: flex-start;
  flex-wrap: wrap;
  width: auto;
}

.queue-detail > summary {
  color: var(--text-soft);
  font-size: 13px;
  cursor: pointer;
}

.queue-detail-body {
  display: grid;
  gap: 10px;
  margin-top: 10px;
  padding: 10px 12px;
  border: 1px solid var(--line-soft);
  border-radius: 7px;
  background: var(--panel-soft);
}

/* table-layout: fixed plus anywhere-wrapping keeps three columns of arbitrary
   archive values inside a 375px viewport instead of pushing the page sideways. */
.change-table {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
  font-size: 13px;
}

.change-table th,
.change-table td {
  padding: 6px 8px;
  border-bottom: 1px solid var(--line-soft);
  text-align: left;
  vertical-align: top;
  overflow-wrap: anywhere;
}

.change-table thead th {
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.change-table tbody th {
  color: var(--text-soft);
  font-weight: 600;
}

.change-table tbody td {
  color: var(--text);
}

.change-table tr:last-child th,
.change-table tr:last-child td {
  border-bottom: 0;
}

.queue-raw > summary {
  color: var(--text-muted);
  font-size: 12px;
  cursor: pointer;
}

.queue-raw-json {
  max-height: 220px;
  margin: 8px 0 0;
  padding: 8px 10px;
  overflow: auto;
  border: 1px solid var(--line-soft);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text-soft);
  font-size: 12px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.queue-submitter {
  align-items: center;
}

.queue-submitter-label {
  color: var(--text-muted);
  font-size: 12px;
}

@media (max-width: 720px) {
  .contribution-credit-fields {
    grid-template-columns: 1fr;
  }

  .moderation-row {
    grid-template-columns: 1fr;
    grid-template-areas:
      "title"
      "badge"
      "detail"
      "actions";
  }

  .moderation-row > .badge {
    justify-self: start;
  }
}

/* Log entry editing: the review-tag picker and visibility controls on the log
   form (#190), and the edit/confirm-then-delete controls on a log row and a
   review card (#191). Every one of these is a touch target first: the actions
   are 44px tall and wrap rather than shrink, because at 375px the log row
   collapses to a single column and they end up under the title. */

/* .form-field span is (0,1,1), so the hint inside a field needs the extra
   class to stay quieter than the field label above it. */
.form-hint,
.form-field .form-hint {
  margin: 0;
  color: var(--text-muted);
  font-size: 12px;
}

.inline-link {
  color: var(--blue);
}

.tag-picker {
  display: grid;
  gap: 12px;
}

.tag-group {
  display: grid;
  gap: 6px;
}

.tag-group-label {
  color: var(--text-muted);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.tag-chip {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--panel-soft);
  color: var(--text-soft);
  font-size: 13px;
  white-space: nowrap;
}

.tag-chip.is-selected {
  border-color: var(--violet-border);
  background: color-mix(in srgb, var(--selection) 20%, transparent);
  color: var(--text);
}

.tag-chip:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* The shared .form-field rule stretches every input to full width and 42px
   tall, which would turn a checkbox into a slab. */
.checkbox-field {
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 10px;
}

.checkbox-field input[type="checkbox"] {
  width: 20px;
  height: 20px;
  min-height: 0;
  padding: 0;
  accent-color: var(--selection);
}

.visibility-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}

.visibility-option {
  display: grid;
  gap: 4px;
  min-width: 0;
}

.visibility-option span {
  color: var(--text-muted);
  font-size: 12px;
}

/* A row action (edit, delete, and the two buttons of the confirmation). Same
   shape whether it is a <button> or the <a> that opens the log form. */
.row-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: transparent;
  color: var(--text-soft);
  font-size: 13px;
  white-space: nowrap;
  text-decoration: none;
}

.row-action:hover:not(:disabled) {
  background: var(--panel-raised);
}

.row-action.danger {
  border-color: var(--rose-border);
  color: var(--rose-text);
}

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

/* The log list needs a wider action column than the 132px the generic .row
   reserves, now that a row carries a badge and two 44px controls. */
.log-row {
  grid-template-columns: minmax(160px, 1.4fr) minmax(80px, 0.5fr) minmax(0, auto);
}

.log-row .row-actions {
  width: auto;
  flex-wrap: wrap;
  align-items: center;
}

.log-row .row-actions .badge {
  width: auto;
}

/* The delete confirmation, on a log row and on a review card. It spans the
   whole row so the sentence naming what is lost is readable rather than
   squeezed into the action column. */
.row-confirm {
  grid-column: 1 / -1;
  display: grid;
  gap: 10px;
  padding: 10px 0 2px;
  border-top: 1px solid var(--line-soft);
}

.row-confirm-copy {
  color: var(--text-soft);
  font-size: 13px;
}

.row-confirm-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.entry-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* `element.hidden = true` is how every one of the blocks the log form swaps in
   and out is toggled, and the UA's `[hidden] { display: none }` loses to any
   author `display` -- which .form-field, .selected-performance, .row-actions
   and .secondary-button all set. Spelled out per component, the way
   .combobox-list[hidden] already is, rather than reaching for !important. */
.form-field[hidden],
.form-hint[hidden],
.selected-performance[hidden],
.secondary-button[hidden],
.row-actions[hidden],
.playbill-row-actions[hidden],
.entry-actions[hidden] {
  display: none;
}

@media (max-width: 720px) {
  .visibility-grid {
    grid-template-columns: 1fr;
  }

  .log-row .row-actions {
    justify-content: flex-start;
  }

  /* Full-width targets once the row is a single column: at 375px two 44px
     buttons side by side leave nothing for the label. */
  .row-confirm-actions .row-action {
    flex: 1 1 140px;
  }
}

/* ---------------------------------------------------------------------------
   Legal pages, consent capture and the profile editor (#63, #118, #189)
   ---------------------------------------------------------------------------
   privacy.html and terms.html are long-form prose, which the rest of the
   stylesheet has no case for: every other panel holds short labelled rows, so
   .panel carries no body padding of its own and h2 is a 15px uppercase section
   marker. .legal-body restores reading typography inside a panel body without
   touching either. */
.legal-body {
  max-width: 68ch;
  color: var(--text-soft);
}
.legal-body h2 {
  margin: 28px 0 10px;
  font-family: var(--font-display);
  font-size: 24px;
  line-height: 1.2;
  text-transform: none;
  color: var(--text);
}
.legal-body h3 {
  margin: 22px 0 8px;
  font-size: 17px;
  color: var(--text);
}
.legal-body h4 {
  margin: 18px 0 6px;
  font-size: 15px;
  color: var(--text);
}
.legal-body > :first-child {
  margin-top: 0;
}
.legal-body p,
.legal-body ul,
.legal-body ol {
  margin: 0 0 12px;
}
.legal-body li {
  margin-bottom: 6px;
}
.legal-body strong {
  color: var(--text);
}
.legal-body a {
  color: var(--violet-text);
}
.legal-body code {
  padding: 1px 5px;
  border-radius: 4px;
  background: var(--panel-soft);
  font-size: 13px;
}
.legal-body pre {
  margin: 0 0 12px;
  padding: 12px 14px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--panel-soft);
  /* The attribution string is quoted verbatim from SPEC-07 6.3 and must not be
     re-wrapped into something that no longer matches; scroll it instead. */
  overflow-x: auto;
}
.legal-body pre code {
  padding: 0;
  background: none;
}
/* Tables scroll inside their own box rather than widening the page: several
   carry three columns of prose and would otherwise force a horizontal
   scrollbar on the whole document at 375px. */
.legal-body .table-scroll {
  margin: 0 0 14px;
  overflow-x: auto;
}
.legal-body .table-scroll:focus-visible {
  outline: 2px solid var(--selection);
  outline-offset: 2px;
}
.legal-body table {
  width: 100%;
  margin: 0;
  border-collapse: collapse;
  font-size: 14px;
}
.legal-body th,
.legal-body td {
  padding: 8px 12px 8px 0;
  border-bottom: 1px solid var(--line-soft);
  text-align: left;
  vertical-align: top;
}
.legal-body th {
  color: var(--text-muted);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.policy-version {
  margin: 0;
  color: var(--text-muted);
  font-size: 12px;
}
/* An unfilled operator decision. Deliberately loud: a policy published with one
   of these still in it is worse than no policy at all, so it must not read as
   ordinary prose. */
.policy-placeholder {
  padding: 0 4px;
  border: 1px dashed var(--amber-border);
  border-radius: 4px;
  color: var(--amber-text);
  font-size: 0.95em;
  white-space: nowrap;
}
.policy-draft {
  padding: 12px 14px;
  border: 1px solid var(--amber-border);
  border-radius: var(--radius);
  background: rgba(211, 154, 98, 0.08);
}
.policy-decisions {
  margin: 0;
  padding: 12px 14px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--panel-soft);
}
.policy-decisions summary {
  cursor: pointer;
  color: var(--text);
}
.policy-decisions[open] summary {
  margin-bottom: 12px;
}

/* The privacy choice on the welcome step is a real radio group, so it needs a
   real fieldset; the browser default border and padding do not match anything
   else on the page. */
.form-fieldset {
  margin: 0 0 4px;
  padding: 0;
  border: 0;
}
.form-fieldset legend {
  padding: 0;
  color: var(--text-muted);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.form-fieldset .form-check {
  margin: 10px 0;
  align-items: flex-start;
}
/* A disabled accept box is the normal state until both documents are opened,
   and it has to read as "not yet" rather than "broken". */
.form-check input:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}
.form-check:has(input:disabled) {
  cursor: default;
  color: var(--text-muted);
}

/* Inline person creation on the Playbill review page (#198). The field sits
   inside the person picker's own .form-field, so the shared input and label
   rules already dress it; only its layout and its hidden state are new. */
.review-new-person {
  display: grid;
  gap: 6px;
}

/* `display: grid` above beats the UA's `[hidden] { display: none }`, the same
   way .form-field[hidden] has to be spelled out further up. */
.review-new-person[hidden] {
  display: none;
}

/* The OCR panel's readable rendering (#267). The panel used to be one <pre> of
   JSON.stringify(ocr_raw_output), and the first real upload on the beta showed
   its owner that dump; the raw envelope now sits in a [data-dev-only]
   <details> and this is what a person actually reads. Laid out like the
   Sources list beside it -- same 18px panel padding, same soft card per page --
   so the two halves of the workbench read as one screen. */
.ocr-readout {
  display: grid;
  gap: 12px;
  padding: 18px;
}

.ocr-readout-summary {
  margin: 0;
  color: var(--text-soft);
  font-size: 13px;
}

.ocr-readout-note {
  margin: 0;
  color: var(--text-muted);
  font-size: 13px;
}

.ocr-warnings {
  display: grid;
  gap: 4px;
  margin: 0;
  padding-left: 18px;
  color: var(--text-muted);
  font-size: 13px;
}

.ocr-page {
  display: grid;
  gap: 8px;
  padding: 12px 14px;
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  background: var(--panel-soft);
}

.ocr-page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.ocr-page-number {
  color: var(--text-muted);
  font-size: 12px;
}

/* "read as Cast page": what the OCR pass made of a page, beside the label the
   uploader gave it (#287). Quieter than the label it annotates, and the same
   in the Sources list as in the readout, because it is the same fact. */
.page-read-as {
  color: var(--text-muted);
  font-size: 12px;
}

.ocr-page-details > summary {
  color: var(--text-soft);
  font-size: 13px;
  cursor: pointer;
}

/* The same look as .ocr-output, but bounded: a page of programme text is long
   and its recognised lines can be arbitrarily wide, so it wraps and scrolls
   inside its own box rather than stretching the panel or pushing a 375px
   screen sideways. */
.ocr-page-text {
  max-height: 320px;
  margin: 8px 0 0;
  padding: 10px 12px;
  overflow: auto;
  border: 1px solid var(--line-soft);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text-soft);
  font-size: 12px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* The developer dump's disclosure. Hidden outside a dev deployment by the
   [data-dev-only] rule at the top of this file, so these only ever apply where
   the raw provider output is meant to be readable. */
.ocr-raw {
  padding: 0 18px 18px;
}

.ocr-raw > summary {
  color: var(--text-muted);
  font-size: 12px;
  cursor: pointer;
}

/* Credits grouped by person on the Playbill review screen (#293). A row is a
   person now -- one search, one resolution, one Include -- and each part they
   played is a card inside it. The person's own controls stay narrow so the
   search box does not stretch the width of the panel with nothing beside it;
   the parts reuse .review-credit-fields, so their inputs sit on the same grid
   (and collapse on the same breakpoint) as they always did. */
.review-person-fields {
  display: grid;
  gap: 12px;
  max-width: 420px;
  margin-bottom: 12px;
}

.review-part-list {
  display: grid;
  gap: 10px;
  margin-bottom: 10px;
}

/* Nested one level in, so it is drawn on the darker surface the card sits on:
   a part inside a person, not another row beside it. */
.review-credit-part {
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  padding: 12px;
  background: var(--bg);
}

.review-part-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}

.review-part-head .tiny-button {
  margin-left: auto;
}

.review-part-label {
  color: var(--text-muted);
  font-size: 12px;
}

/* The same two marks the flat rows carried, moved to the line they are now
   about: one doubtful reading marks its own part, not the whole actor. */
/* "Replaces" on a cast-change credit (#301): sits under the credit's fields. */
.review-replaces {
  display: grid;
  gap: 4px;
  margin-top: 12px;
}

.review-replaces[hidden] {
  display: none;
}

.review-credit-part.is-low-confidence {
  border-color: var(--amber-border);
}

.review-credit-part.is-blocked {
  border-color: var(--rose-border);
}

/* "Fill empty roles with…" above the Cast list (#272): a sixteen-headshot
   company page reads as sixteen identical "Ensemble" keystrokes otherwise. It
   only ever touches a blank Role input -- a role already typed is left alone,
   and an empty Role is never accepted at submit -- so the hint beside the
   button says so, not just the label. */
.review-fill-roles {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 12px;
}

.review-fill-roles-label {
  font-size: 12px;
  color: var(--text-soft);
}

.review-fill-roles input {
  min-height: 30px;
  width: auto;
  max-width: 180px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel-raised);
  color: var(--text);
  font-size: 12px;
}

.review-fill-roles-hint {
  color: var(--text-muted);
  font-size: 12px;
}

@media (max-width: 720px) {
  .review-part-head {
    gap: 8px;
  }

  /* Keeps "Remove part" from being pushed off the line once the badges wrap. */
  .review-part-head .tiny-button {
    margin-left: 0;
  }
}

/* The first-administrator offer on the home page (#260). One line above the
   hero, in the amber the rest of the app uses for "this wants your attention
   and will not be here long": at most one account on at most one deployment
   ever sees it, and only until the claim is made. Explicit [hidden] rule
   because this sets `display`, and the UA's own [hidden] would lose to it. */
.bootstrap-admin-notice {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin: 0 0 16px;
  padding: 12px 16px;
  border: 1px solid var(--amber-border);
  border-radius: var(--radius);
  background: var(--panel-soft);
  color: var(--amber-text);
  font-size: 14px;
}

.bootstrap-admin-notice[hidden] {
  display: none;
}

.bootstrap-admin-notice .secondary-button {
  margin-left: auto;
}

@media (max-width: 720px) {
  /* Wrapped onto its own line, the button no longer needs pushing right. */
  .bootstrap-admin-notice .secondary-button {
    margin-left: 0;
  }
}

/* Reporting a review, and the owner's notice that one was removed (#211). */
.review-report {
  margin-top: 8px;
}

.review-report-form {
  margin-top: 8px;
  max-width: 100%;
}

.review-report-form select,
.review-report-form textarea {
  width: 100%;
  max-width: 100%;
}

.review-removed-notice {
  margin: 8px 0;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--panel-soft);
  color: var(--text-soft);
  font-size: 13px;
  overflow-wrap: anywhere;
}

/* A moderator reads the reported text in full, so long unbroken strings must
   wrap rather than push the page wider than a 375px viewport. */
.flagged-review {
  display: grid;
  gap: 6px;
  overflow-wrap: anywhere;
}

.flagged-review p {
  margin: 0;
}

.flagged-review-reason {
  font-weight: 600;
}

.flagged-review-body {
  white-space: pre-wrap;
}

/* The production page (#129): reactions on reviews, and the discussion thread.
   Every control a member taps here is at least 44px, and posts wrap long
   unbroken text so a pasted URL cannot push the page wider than 375px. */
.production-surface .segment {
  min-height: 44px;
}

.review-summary {
  display: grid;
  gap: 8px;
  margin-bottom: 12px;
}

.review-summary:empty {
  display: none;
}

.review-aggregate {
  margin: 0;
  color: var(--gold);
  font-weight: 650;
}

.reaction-row,
.discussion-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.reaction-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 44px;
  min-height: 44px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--panel-soft);
  color: var(--text-soft);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}

.reaction-button:hover,
.reaction-button[aria-pressed="true"] {
  border-color: var(--selection);
  color: var(--selection);
}

.reaction-button:focus-visible,
.discussion-button:focus-visible {
  outline: 2px solid var(--selection);
  outline-offset: 2px;
}

.reaction-count {
  font-variant-numeric: tabular-nums;
}

.reaction-status:empty {
  display: none;
}

.reaction-summary {
  font-size: 13px;
}

.discussion-notice {
  margin: 0 0 12px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel-soft);
  color: var(--text-soft);
}

#discussionLockNotice {
  border-color: var(--amber-border);
  color: var(--amber-text);
}

.discussion-notice a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--selection);
}

.discussion-composer,
.discussion-inline-form,
.discussion-confirm {
  display: grid;
  gap: 6px;
  margin-bottom: 12px;
}

.discussion-composer textarea,
.discussion-inline-form textarea {
  width: 100%;
  max-width: 100%;
}

.discussion-composer p,
.discussion-inline-form p,
.discussion-confirm p {
  margin: 0;
}

.discussion-list {
  display: grid;
  gap: 12px;
}

.discussion-item {
  display: grid;
  gap: 8px;
}

.discussion-post {
  display: grid;
  gap: 6px;
  min-width: 0;
  padding: 12px 14px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--panel-soft);
  overflow-wrap: anywhere;
}

.discussion-post p {
  margin: 0;
}

.discussion-post-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
}

.discussion-post-head time,
.discussion-edited,
.discussion-replying {
  font-size: 12px;
}

.discussion-edited {
  font-style: italic;
}

.discussion-body {
  white-space: pre-wrap;
}

.discussion-placeholder {
  font-style: italic;
}

.discussion-button,
.discussion-post .review-report .tiny-button {
  min-width: 44px;
  min-height: 44px;
  cursor: pointer;
}

.discussion-replies {
  display: grid;
  gap: 8px;
  margin-left: 18px;
  padding-left: 12px;
  border-left: 2px solid var(--line-soft);
}

.discussion-reply-list {
  display: grid;
  gap: 8px;
}

.discussion-reply-list:empty {
  display: none;
}

.discussion-more {
  justify-self: start;
  min-height: 44px;
}

.discussion-load-more {
  margin-top: 12px;
}

.discussion-load-more button {
  min-height: 44px;
}

.moderation-post-actions {
  display: grid;
  gap: 8px;
  margin-top: 12px;
}

.moderation-post-actions p {
  margin: 0;
}

@media (max-width: 420px) {
  .discussion-replies {
    margin-left: 6px;
    padding-left: 8px;
  }
}

/* Person claims (#130). A moderator's note, a pasted link or a long website
   wraps inside the panel instead of pushing it wider than a 375px screen. */
.person-surface .claim-panel,
.person-surface .owner-panel {
  overflow-wrap: anywhere;
}
