* { box-sizing: border-box; }

/* ---------- Theme ----------
   All colors come from these variables. app.js resolves the user's choice
   (auto/light/dark) and stamps data-theme="light|dark" on <html>. */

/* "Ledger" design system: Swiss-precision trust instrument. Near-white
   ground, hairline rules, one commanding cobalt accent, verification green
   reserved for recorded/signed signals, Archivo UI type with IBM Plex Mono
   for every figure. Sharp corners (1-2px), obsessive alignment. */

:root,
:root[data-theme="light"] {
  color-scheme: light;

  --bg: #f4f4f2;              /* page ground, near-white */
  --surface: #ffffff;         /* cards, sheets */
  --surface-alt: #fafaf9;
  --surface-chart: #fcfcfc;
  --surface-hover: #f4f4f2;
  --surface-muted: #ececea;

  --text: #10141c;            /* ink */
  --muted: #4c5364;
  --faint: #878e9e;

  --border: #c9cbd2;          /* stronger hairline */
  --border-soft: #e2e3e6;     /* hairline */
  --rule-ink: #10141c;        /* structural rule (header/footer) */
  --focus-border: #1130c9;

  --accent: #1130c9;          /* international cobalt */
  --accent-btn: #1130c9;      /* primary button / avatar background */
  --accent-btn-hover: #0b2296;
  --accent-wash: #eef1fd;     /* faint cobalt tint (selection, active rows) */
  --on-accent: #ffffff;

  /* Sidebar header bands: a cobalt-tinted step down from the panel they sit
     on, so a section header never reads as the first row of its list. */
  --sidebar-head-bg: #e3e7f4;
  --sidebar-head-text: #2f3c6b;
  --sidebar-head-border: #c2c9e2;

  --green-bg: #e8f4ee;        /* verification signals only */
  --green-border: #b9dcc9;
  --green-text: #0b7a4b;
  --green-muted: #4b7a62;

  --red-bg: #fdeeee;
  --red-border: #e0b3b3;
  --red-text: #a02c2c;

  --amber-bg: #fdf4e3;
  --amber-border: #e6cf9d;
  --amber-text: #7a5a1e;

  --sim-line: #0b7a4b;
  --sim-dot: #1130c9;

  --controls-bg: rgba(255, 255, 255, 0.2);
  --controls-border: rgba(16, 20, 28, 0.08);
  --backdrop: rgba(16, 20, 28, 0.45);
  --shadow-card: 0 1px 2px rgba(16, 20, 28, 0.05), 0 8px 28px rgba(16, 20, 28, 0.06);
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --bg: #0d0f14;
  --surface: #14171e;
  --surface-alt: #12151b;
  --surface-chart: #12151b;
  --surface-hover: #1a1e27;
  --surface-muted: #1c202a;

  --text: #e9ebf0;
  --muted: #a3aabb;
  --faint: #656d7f;

  --border: #363b47;
  --border-soft: #262a34;
  --rule-ink: #e9ebf0;
  --focus-border: #7d94ff;

  --accent: #7d94ff;
  --accent-btn: #7d94ff;
  --accent-btn-hover: #a7b6ff;
  --accent-wash: #181d31;
  --on-accent: #0a1030;

  --sidebar-head-bg: #101627;
  --sidebar-head-text: #a8b8ee;
  --sidebar-head-border: #2b3350;

  --green-bg: #12251c;
  --green-border: #2c5c45;
  --green-text: #3ecb8e;
  --green-muted: #7fb99d;

  --red-bg: #33201f;
  --red-border: #6e3d3d;
  --red-text: #e59d9d;

  --amber-bg: #332b1c;
  --amber-border: #6e5c33;
  --amber-text: #dcbd77;

  --sim-line: #3ecb8e;
  --sim-dot: #7d94ff;

  --controls-bg: rgba(0, 0, 0, 0.25);
  --controls-border: rgba(255, 255, 255, 0.08);
  --backdrop: rgba(0, 0, 0, 0.6);
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 28px rgba(0, 0, 0, 0.35);
}

body {
  margin: 0;
  font-family: "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  letter-spacing: -0.006em;
  background: var(--bg);
  color: var(--text);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* Every figure, timestamp, and meta line reads in tabular mono. */
.mono {
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-feature-settings: "tnum" 1;
  letter-spacing: 0;
}
.micro {
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.68rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--faint);
}

.page {
  width: 100%;
  margin: 0 auto;
  padding: 1.5rem clamp(1.25rem, 3vw, 2.5rem) 4.5rem;
}

/* Top bar: full-width, closed off by a structural ink rule (the "ledger
   edge" — the strongest line on the page, shared with the bottom bar). */
.site-header {
  background: var(--surface);
  border-bottom: 1px solid var(--rule-ink);
  padding: 0.55rem clamp(1.25rem, 3vw, 2.5rem);
}

.site-header .header-row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 0.75rem;
}

/* The wordmark and the tagline beside it sit on one shared baseline (the
   reel mark is centered against the wordmark instead of participating, so
   the baseline comes from the type rather than from the graphic's edge). */
.header-left {
  display: flex;
  align-items: baseline;
  gap: 0.85rem;
  min-width: 0;
}

/* The wordmark: film-reel-as-inkwell mark in cobalt; the reel spins one
   spool-hole step (72°) on hover while the unspooling line stays put. */
.brand {
  display: inline-flex;
  align-items: baseline;
  gap: 0.6rem;
}
.brand-reel {
  display: block;
  flex: none;
  color: var(--accent);
  /* A replaced element baselines on its bottom margin edge, so aligning it
     on the wordmark's baseline would leave the mark sitting low. This pulls
     it up until its centerline meets the wordmark's optical centre — half of
     Archivo's cap height (~0.73em of the 1.24rem wordmark) above the
     baseline — while keeping the shared baseline the header aligns on. */
  /* Centering the mark on the flex line leaves it low, because the line
     carries the wordmark's descender space below the baseline. Measured
     against the rendered cap band, its centreline lands 5px under the
     wordmark's; the nudge is a transform so it corrects the optics without
     disturbing the baseline the header aligns on. (The hover spin animates
     the inner <g>, so the two transforms don't collide.) */
  align-self: center; /* opts out of baseline alignment, see .header-left */
  transform: translateY(-0.3125rem); /* 5px at the default root size */
}
.brand .reel-spin {
  transform-origin: 17px 17px;
  transition: transform 0.9s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.brand:hover .reel-spin { transform: rotate(72deg); }
.brand-word {
  font-size: 1.24rem;
  font-weight: 800;
  letter-spacing: -0.035em;
  line-height: 1;
  color: var(--text);
}
.brand-word span { color: var(--accent); }
@media (prefers-reduced-motion: reduce) {
  .brand .reel-spin { transition: none; }
}

/* The two top-bar labels that sit between the wordmark and the account nav:
   "Write it, prove it" (the promo overlay) and "Products". */
.header-center {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.promo-link,
.products-link {
  border: none;
  background: none;
  padding: 0.2rem 0.5rem;
  font-family: inherit;
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--accent);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
}
.promo-link:hover,
.products-link:hover { text-decoration: underline; }

/* Hover menus on the top-bar labels. The menu is a child of the anchor with no
   gap, so the pointer can travel from label to menu without leaving :hover;
   :focus-within keeps it reachable by keyboard. */
.nav-menu-anchor { position: relative; display: inline-flex; }
.nav-menu {
  display: none;
  position: absolute;
  left: 0;
  top: 100%;
  min-width: 160px;
  padding: 0.3rem 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 2px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.14);
  z-index: 30;
}
.nav-menu-anchor:hover .nav-menu,
.nav-menu-anchor:focus-within .nav-menu { display: block; }
.nav-menu button,
.nav-menu a {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.45rem 0.9rem;
  border: none;
  background: none;
  font-family: inherit;
  font-size: 0.92rem;
  font-weight: 400;
  color: var(--text);
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}
.nav-menu button:hover,
.nav-menu a:hover { background: var(--surface-hover); color: var(--accent); }

@media (max-width: 760px) {
  .site-header .header-row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
  }
  .site-header .subheader { display: none; }
}

.site-header h1 {
  margin: 0;
  font-size: 1.35rem;
}
.site-header h1 a { color: inherit; text-decoration: none; }

.site-header .subheader {
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.66rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--faint);
  padding-left: 0.85rem;
  border-left: 1px solid var(--border-soft);
}

.tagline {
  margin: 0 0 1.1rem;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.68rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--faint);
}

/* ---------- Editor ---------- */

.editor {
  width: 100%;
  min-height: 55vh;
  font-family: inherit;
  font-size: 1rem;
  line-height: 1.75;
  letter-spacing: -0.004em;
  /* The deep bottom padding is the landing space for the reel control that
     floats in that corner: without it the last line of a full document would
     sit under the button with no way to scroll clear of it. */
  padding: 1.4rem 1.6rem 4.2rem;
  border: 1px solid var(--border-soft);
  border-top: 3px double var(--rule-ink);
  border-radius: 2px;
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-card);
  resize: vertical;
  outline: none;
}

.editor:focus { box-shadow: var(--shadow-card), 0 0 0 1px var(--accent); }

/* On the writing screen the textarea — and on the reel replay screen the
   stage — grows to fill the space between the top bar and the fixed bottom
   bar (min-heights still apply as the floor on short windows). The editor's
   manual resize handle is disabled because flex sizing would ignore a
   dragged height. */
.editor-screen,
.viewer-screen {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}
.editor-screen .page,
.viewer-screen .page {
  flex: 1;
  display: flex;
  flex-direction: column;
}
.editor-screen main,
.viewer-screen main {
  flex: 1;
  display: flex;
  flex-direction: column;
}
.editor-screen .editor {
  flex: 1;
  resize: none;
}

/* The writing screen is a fixed app frame: header / content / bottom bar
   fill the viewport exactly and the window never scrolls — the editor
   absorbs the leftover height and scrolls internally. (The bottom bar sits
   in normal flow here, so it can't overlap the Generate button the way the
   fixed-position variant on long scrolling pages could.) */
.editor-screen {
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}
.editor-screen .page {
  min-height: 0;
  overflow-y: auto; /* short-window fallback: the content column scrolls, not the window */
  padding-bottom: 1.1rem;
}
.editor-screen main { min-height: 0; }
.editor-screen .editor { min-height: 6rem; }
.editor-screen .rich-editor { min-height: 6rem; }
.editor-screen .site-footer { position: static; flex: none; }
.viewer-screen .stage-row {
  flex: 1;
  display: flex;
  min-height: 0;
}
.viewer-screen .stage-wrap {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
}
.viewer-screen .stage {
  flex: 1;
  height: auto;
  /* No tall floor here: the replay pane must be free to take exactly the
     space the frame leaves it, so it scrolls its content instead of growing
     and pushing the playback controls off the screen. */
  min-height: 10rem;
}

/* Like the writing screen, the replay screen is a fixed app frame: header /
   content / bottom bar fill the viewport and the window never scrolls, so
   the stage has a definite height for its overflow-y to act on and the
   controls stay put however long the document gets. */
.viewer-screen {
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}
.viewer-screen .page {
  min-height: 0;
  overflow-y: auto; /* short-window fallback: the column scrolls, not the window */
  padding-bottom: 1.1rem;
}
.viewer-screen main { min-height: 0; }
.viewer-screen .site-footer { position: static; flex: none; }

/* Demo layout: the reel narrows to 75% and an explanatory column (walking the
   visitor through what they're seeing) fills the rest; stacked on narrow
   screens. */
main.demo-layout .stage-row { gap: 1.5rem; }
main.demo-layout .stage-wrap { flex: 0 0 75%; }

.demo-explainer {
  flex: 1;
  min-width: 0;
  overflow-y: auto;
  font-size: 0.92rem;
  line-height: 1.55;
  animation: demo-fade-in 1.4s ease;
  /* Frame + dots sit vertically centered relative to the reel pane; the
     auto margins collapse gracefully if the text ever outgrows the column. */
  display: flex;
  flex-direction: column;
}
.demo-explainer p { margin: 0 0 0.9rem; }

@keyframes demo-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* The tour steps are stacked in one grid cell and slide horizontally: the
   outgoing step exits toward the earlier side, the incoming one enters from
   the later side (and the reverse when stepping back via the dots). */
/* Label + frame + dots center as one group: the auto margins live on the
   group's first and last elements (and collapse if the column overflows). */
.demo-scenario-label {
  margin-top: auto;
  margin-bottom: 0.4rem;
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}

.demo-steps {
  display: grid;
  overflow: hidden;
  padding: 1rem 1.1rem;
  border: 1px solid var(--border);
}
.demo-step p:last-child { margin-bottom: 0; }
.demo-step {
  grid-area: 1 / 1;
  transition: transform 0.6s ease, opacity 0.6s ease;
}
.demo-step.pos-left { transform: translateX(-110%); opacity: 0; pointer-events: none; }
.demo-step.pos-right { transform: translateX(110%); opacity: 0; pointer-events: none; }

.demo-restart {
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  font-style: italic;
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
}

.demo-dots {
  display: flex;
  justify-content: center;
  gap: 0.55rem;
  margin-top: 0.75rem;
  margin-bottom: auto;
}
.demo-dot {
  width: 9px;
  height: 9px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--border);
  cursor: pointer;
}
.demo-dot:hover { background: var(--focus-border); }
.demo-dot.active { background: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .demo-explainer { animation: none; }
  .demo-step { transition: none; }
  .redact-hint { transition: none; }
  .redact-hint-pulse { animation: none; }
}

@media (max-width: 700px) {
  .viewer-screen main.demo-layout .stage-row { flex-direction: column; }
  .viewer-screen main.demo-layout .stage-wrap { flex: 0 0 auto; }
}

/* One line, always: the Generate Reel box grows in place when it becomes the
   reel link, so the counters give up room (truncating) rather than letting
   the box wrap onto a row of its own. */
.editor-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 0.75rem;
  gap: 1rem;
  flex-wrap: nowrap;
}

.counters {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-feature-settings: "tnum" 1;
  color: var(--muted);
  font-size: 0.76rem;
}

.btn {
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 500;
  padding: 0.5rem 1rem;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  text-decoration: none;
  display: inline-block;
}

.btn:hover:not(:disabled) { background: var(--surface-hover); }
.btn:disabled { opacity: 0.45; cursor: default; }

.btn-primary {
  background: var(--accent-btn);
  border-color: var(--accent-btn);
  color: var(--on-accent);
  font-weight: 700;
  letter-spacing: 0.02em;
}
.btn-primary:hover:not(:disabled) {
  background: var(--accent-btn-hover);
  border-color: var(--accent-btn-hover);
}

/* The Generate Reel button: the wordmark's reel on a plain cobalt square.
   Square rather than round — the design system's corners are 1-2px
   throughout, and a disc around a disc reads as a target. Its 42px matches
   .result-inline's min-height, so the morph between them changes width only
   and the box never jumps in height. */
#generateBtn {
  position: relative;
  width: 42px;
  height: 42px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* No vertical nudge here, unlike the wordmark's copy of this mark: that one
   opts out of a baseline-aligned flex line, while this button centres its
   items, and the mark's ink already sits centred in its own viewBox (the
   spool spans it top to bottom, and the tail tucks inside that span). */
.btn-reel {
  display: block;
  flex: none;
}
/* Hovering spins the reel one spool-hole step, exactly as the wordmark does —
   the inner <g> turns while the unspooling tail stays put. Shared by the
   floating button and its twin in the document panel. */
.reel-btn .reel-spin {
  transform-origin: 17px 17px;
  transition: transform 0.9s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.reel-btn:hover:not(:disabled) .reel-spin { transform: rotate(72deg); }
/* Mid-generate the reel runs under its own power — the state the floating
   button used to spell out as "Generating…" before it lost its label. */
@keyframes reel-run { to { transform: rotate(360deg); } }
.reel-btn.is-generating .reel-spin {
  animation: reel-run 1.5s linear infinite;
  transition: none;
}
@media (prefers-reduced-motion: reduce) {
  .reel-btn .reel-spin { transition: none; }
  .reel-btn.is-generating .reel-spin { animation: none; }
}
/* Lifted off the page it now sits on, so it reads as resting on the sheet
   rather than as a hole punched in it. */
#generateBtn { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18); }
#generateBtn:hover:not(:disabled) {
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18),
              0 0 0 1px var(--surface), 0 0 0 2px var(--accent);
}
#generateBtn:active:not(:disabled) { transform: translateY(1px); }

.popover-anchor { position: relative; }

/* ---------- Generate Reel box ----------
   One box that changes what it holds: the button, or — once a reel exists —
   the link and its copy actions. The width animates between the two so it
   reads as the same object growing, not a panel appearing over the page.

   It sits over the bottom-right of the writing surface. Anchoring it on
   `right` and `bottom` is what pins that corner: the box can only grow
   leftwards and upwards from it, so the corner the pointer just clicked stays
   exactly where it was through the morph — no JS bookkeeping involved. */
.editor-stack {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}
.generate-box {
  position: absolute;
  right: 1rem;
  bottom: 1rem;
  z-index: 5;
  /* Never wider than the sheet it floats on, however long the reel URL is. */
  max-width: calc(100% - 2rem);
  /* A flex container, so the result inside can give up width (the link
     ellipsizes) when a sidebar narrows the writing column, rather than
     overflowing and hiding its own close button. */
  display: flex;
  justify-content: flex-end;
  white-space: nowrap;
}
/* Clipped only while the width animates — steady-state it must stay visible
   so the "?" popup can extend above the box. */
.generate-box.animating {
  overflow: hidden;
  transition: width 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.result-inline {
  display: flex;
  flex: 0 1 auto;
  min-width: 0;
  align-items: center;
  gap: 0.4rem;
  /* Matches the Generate Reel button's 42px, so swapping one for the other
     grows the box sideways only — with the box anchored bottom-right, an
     unequal height would visibly shove its top edge around mid-morph. */
  min-height: 42px;
  padding: 0.15rem 0.3rem 0.15rem 0.7rem;
  border: 1px solid var(--green-border);
  border-radius: 2px;
  background: var(--green-bg);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
}
.result-inline .cert-link {
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.82rem;
  color: var(--accent);
  text-decoration: none;
  /* The only part that gives: the copy and close buttons must stay reachable
     however narrow the column gets. */
  flex: 0 1 auto;
  min-width: 2rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.result-inline .cert-link:hover { text-decoration: underline; }
.result-inline .icon-btn { flex: none; padding: 0.3rem 0.4rem; }
/* "?" — a short explanation of the link and the two copy buttons. */
.result-help {
  flex: none;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 1px solid var(--green-border);
  border-radius: 50%;
  background: none;
  font-family: inherit;
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1;
  color: var(--green-muted);
  cursor: pointer;
}
.result-help:hover,
.result-help[aria-expanded="true"] {
  background: var(--surface);
  border-color: var(--accent);
  color: var(--accent);
}
/* The anchor keeps the popup positioned against the button; it must not
   itself become a flex item that can shrink. */
.result-inline .popover-anchor { flex: none; display: inline-flex; }

.help-pop {
  position: absolute;
  bottom: calc(100% + 11px);
  right: -0.5rem;
  left: auto;
  margin: 0;
  width: max-content;
  max-width: min(340px, 70vw);
  padding: 0.75rem 0.9rem;
  font-size: 0.84rem;
  line-height: 1.45;
  white-space: normal;
  text-align: left;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 2px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
  outline: none;
  z-index: 20;
}
.help-pop p { margin: 0 0 0.55rem; }
.help-pop p:last-child { margin-bottom: 0; }
.help-pop .help-icon {
  display: inline-flex;
  vertical-align: -3px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: 2px;
  color: var(--muted);
}
/* Wedge pointing down at the "?" */
.help-pop::after {
  content: "";
  position: absolute;
  bottom: -7px;
  right: 1.1rem;
  width: 12px;
  height: 12px;
  background: var(--surface);
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  transform: rotate(45deg);
}

.result-close {
  flex: none;
  border: none;
  background: none;
  font-size: 1.2rem;
  line-height: 1;
  padding: 0.1rem 0.35rem;
  color: var(--green-muted);
  cursor: pointer;
}
.result-close:hover { color: var(--text); }

@media (prefers-reduced-motion: reduce) {
  .generate-box.animating { transition: none; }
}




.dialog-close {
  position: absolute;
  top: 0.5rem;
  right: 0.6rem;
  border: none;
  background: none;
  font-size: 1.3rem;
  line-height: 1;
  padding: 0.25rem;
  color: var(--green-muted);
  cursor: pointer;
}
.dialog-close:hover { color: var(--text); }

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.5rem 0.65rem;
}
.icon-btn .icon-check { color: var(--accent); }


/* Inline settings rows in the Generate Reel popup */
.pref-inline { flex-wrap: wrap; }
.pref-days {
  width: 4.6rem;
  padding: 0.3rem 0.4rem;
  font-size: 0.9rem;
  font-family: inherit;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  color: var(--text);
}
/* The dash a switched-off expiry field shows. The UA's default placeholder is
   already translucent, and the row it sits in is dimmed too, so it needs an
   explicit opaque colour or the two fades compound into nothing. */
.pref-days::placeholder { color: var(--muted); opacity: 1; }
.pref-inline select {
  padding: 0.3rem 0.4rem;
  font-size: 0.9rem;
  font-family: inherit;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  color: var(--text);
}
.error {
  margin-top: 1rem;
  padding: 0.75rem 1rem;
  background: var(--red-bg);
  border: 1px solid var(--red-border);
  border-radius: 2px;
  color: var(--red-text);
}

.hidden { display: none !important; }

/* ---------- Viewer ---------- */

.cert-header { margin-bottom: 1rem; }
/* The reel's facts as a grid rather than one long line: label above value,
   wrapping into as many columns as fit — two or three rows at any sane panel
   width, which is far easier to scan than seven facts joined by middots. */
/* Large-insertion markers on the similarity charts. Amber like the flag counts
   in the fact grid, and drawn under the playhead dot so the playhead is never
   obscured by one. */
.sim-flag {
  fill: var(--amber-text);
  fill-opacity: 0.85;
  stroke: var(--surface);
  stroke-width: 0.8;
}
/* Imported content is a different claim from a fast insertion, so it reads
   differently: hollow rather than filled. */
.sim-flag.is-imported {
  fill: var(--surface);
  stroke: var(--amber-text);
  stroke-width: 1.4;
}

.cert-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: 0.55rem 1.1rem;
  align-items: start;
}
.cert-fact { display: flex; flex-direction: column; min-width: 0; }
.cert-fact-label {
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.6rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--faint);
}
.cert-fact-value {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text);
  overflow-wrap: break-word;
}
/* Flag counts read as facts, not alarms — the colour is the only thing marking
   them out, and the tooltip says what they mean. */
.fact-flag {
  color: var(--amber-text);
  text-decoration-line: underline;
  text-decoration-style: dashed;
  text-decoration-color: var(--amber-text);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

.cert-meta strong { color: var(--text); font-weight: 600; }


.stage-wrap {
  position: relative;
}

.stage {
  position: relative;
  height: 60vh;
  overflow-y: auto;
  margin: 0;
  padding: 1.2rem 1.4rem 5rem;
  /* Monospace, unlike the editor: the replay toggles between redacted (☒)
     and clear text, and a fixed advance width keeps every character in
     place through the switch instead of the line reflowing. */
  font-family: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
  font-size: 0.95rem;
  line-height: 1.6;
  white-space: pre-wrap;
  word-wrap: break-word;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  color: var(--text);
}

/* Links inside the replay pane: the demo's email finale renders the appended
   provenance footer as real rich text, the way the recipient sees it. */
.stage a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.stage a:hover { text-decoration-thickness: 2px; }

.break-banner {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 0.4rem 1rem;
  background: rgba(16, 20, 28, 0.85);
  color: #fff;
  font-size: 0.88rem;
  border-radius: 2px;
  pointer-events: none;
  white-space: nowrap;
}

/* Demo-only redaction toggle: a pill mirroring the sim-score capsule but at
   the bottom-left, and the whole stage is clickable to flip it. */
.redact-toggle {
  position: absolute;
  left: 0.75rem;
  bottom: 4.2rem;
  padding: 0.25rem 0.65rem;
  background: rgba(16, 20, 28, 0.55);
  color: #fff;
  font-size: 0.8rem;
  border: none;
  border-radius: 2px;
  white-space: nowrap;
  cursor: pointer;
}
.redact-toggle:hover { background: rgba(16, 20, 28, 0.7); }

.stage-toggles-redaction { cursor: pointer; }

/* Tour hint: pulsing info text (no capsule) over the lower middle of the
   stage, arrows on all four sides = "click anywhere in here". Once redaction
   is on it morphs to "Click here" ▼ and slides to sit above the similarity
   capsule (that position is measured and set inline by viewer.js); with the
   similarity details open it slides back home as "Click to proceed", four
   arrows again; in the email finale it slides to sit as "◀ Link to reel"
   beside the reel link at the end of the signature. */
.redact-hint {
  position: absolute;
  left: 50%;
  top: calc(75% - 30px);
  transform: translate(-50%, -50%);
  padding: 0;
  background: none;
  border: none;
  color: var(--text);
  font-size: 0.95rem;
  font-weight: 600;
  /* soft halo of the stage background so it stays readable over reel text */
  text-shadow: 0 0 8px var(--surface), 0 0 3px var(--surface);
  white-space: nowrap;
  cursor: pointer;
  transition: left 0.6s ease, top 0.6s ease;
}
/* The pulse lives on an inner wrapper, NOT the positioned element: Safari
   resolves the percentage translate inside a running keyframe animation
   against a stale width when the element resizes (the "Click here" morph),
   which threw the centering off by half the width change. The static
   translate above is always current. */
.redact-hint-pulse {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  animation: redact-hint-pulse 1.6s ease-in-out infinite;
}
.redact-hint-row {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
}
.redact-hint-arrow {
  font-size: 0.75em;
  line-height: 1;
  opacity: 0.85;
}
.redact-hint.at-sim .arrow-up,
.redact-hint.at-sim .arrow-left,
.redact-hint.at-sim .arrow-right { display: none; }
/* Finale: the hint is a pure attention cue pointing at the reel link, not a
   click target — clicks pass through it to the stage. Accent green so it
   stands out from the email text around it. */
.redact-hint.at-link {
  pointer-events: none;
  color: var(--accent);
}
.redact-hint.at-link .arrow-up,
.redact-hint.at-link .arrow-right,
.redact-hint.at-link .arrow-down { display: none; }

@keyframes redact-hint-pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.07); opacity: 0.7; }
}

/* Demo finale: the reel pane becomes the email as Ms. Alvarez receives it —
   playback controls, the similarity capsule, and the redaction pill hide. */
.demo-final .controls,
.demo-final .sim-score,
.demo-final .redact-toggle,
.demo-final .break-banner { display: none; }

.caret-off { visibility: hidden; }

.sim-score {
  position: absolute;
  right: 0.75rem;
  bottom: 4.2rem;
  width: 128px;
  max-height: 2rem;
  overflow: hidden;
  padding: 0.25rem 0.4rem 0.25rem 0.65rem;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  background: rgba(11, 122, 75, 0.9);
  color: #fff;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  border: 1px solid transparent;
  border-radius: 2px;
  transition:
    width 0.28s ease,
    max-height 0.28s ease,
    padding 0.28s ease,
    background-color 0.28s ease,
    color 0.28s ease,
    border-color 0.28s ease,
    border-radius 0.28s ease,
    box-shadow 0.28s ease;
}

.sim-score:not(.expanded) { cursor: pointer; }

.sim-score-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.4rem;
}
/* The score label is display-only and must not be a click target: its text
   node is rewritten every playback frame, and WebKit drops a click when the
   node under the pointer is replaced between mousedown and mouseup — which
   made clicks on the label fail to open the details in Safari. */
.sim-score-row > span { pointer-events: none; }

.sim-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.sim-toggle:hover { background: rgba(255, 255, 255, 0.2); }

.sim-score.expanded {
  width: 240px;
  max-height: 240px;
  padding: 0.6rem 0.75rem;
  background: var(--surface);
  color: var(--text);
  border-color: var(--border);
  border-radius: 2px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.12);
}
.sim-score.expanded .sim-toggle:hover { background: var(--surface-hover); }

.sim-charts {
  position: relative;
  margin-top: 0.5rem;
  opacity: 0;
  transition: opacity 0.2s ease;
}
.sim-score.expanded .sim-charts { opacity: 1; transition-delay: 0.12s; }

@media (prefers-reduced-motion: reduce) {
  .sim-score, .sim-charts { transition: none; }
}

.sim-chart-title {
  font-size: 0.7rem;
  color: var(--muted);
  margin: 0.35rem 0 0.15rem;
}
.sim-chart-title:first-child { margin-top: 0; }

.sim-chart {
  display: block;
  width: 100%;
  height: 48px;
  background: var(--surface-chart);
  border: 1px solid var(--border-soft);
  border-radius: 2px;
}

.sim-line {
  fill: none;
  stroke: var(--sim-line);
  stroke-width: 2;
  vector-effect: non-scaling-stroke;
}

.sim-dot {
  fill: var(--sim-dot);
  stroke: var(--surface);
  stroke-width: 1.5;
}


.caret {
  display: inline-block;
  width: 2px;
  height: 1.1em;
  vertical-align: text-bottom;
  background: var(--accent);
  animation: blink 1s steps(1) infinite;
}

@keyframes blink { 50% { opacity: 0; } }

.controls {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 0.75rem;
  background: var(--controls-bg);
  border-top: 1px solid var(--controls-border);
  border-radius: 0;
}

.play-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.5rem 0.8rem;
}

.slider { flex: 1; min-width: 100px; accent-color: var(--accent-btn); }

.time-label {
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-feature-settings: "tnum" 1;
  font-size: 0.82rem;
  color: var(--muted);
  min-width: 5.5rem;
}

.write-your-own {
  margin-top: 1.5rem;
  font-size: 0.85rem;
  color: var(--muted);
}
.write-your-own a { color: var(--accent); }

.loading { color: var(--muted); padding: 2rem 0; }

/* ---------- 404 / tombstones ---------- */

.notfound { text-align: center; padding: 4rem 0; }
.notfound h2 { font-size: 1.4rem; }
.notfound a { color: var(--accent); }

/* ---------- Site navigation ---------- */

.site-header .header-row { position: relative; }

.site-nav {
  margin-left: auto;
  justify-self: end;
  display: flex;
  align-items: center;
  gap: 0.9rem;
}

.nav-link {
  color: var(--accent);
  font-size: 0.92rem;
  font-weight: 600;
  text-decoration: none;
}
.nav-link:hover { text-decoration: underline; }

.user-menu { position: relative; }
.user-menu summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
}
.user-menu summary::-webkit-details-marker { display: none; }

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--accent-btn);
  color: var(--on-accent);
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  user-select: none;
  box-shadow: 0 0 0 1px var(--surface), 0 0 0 2px var(--border);
}
.user-menu summary:hover .avatar { box-shadow: 0 0 0 1px var(--surface), 0 0 0 2px var(--accent); }

.user-menu .menu {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  min-width: 200px;
  padding: 0.4rem 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 2px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.14);
  z-index: 30;
}
.user-menu .menu-email {
  padding: 0.4rem 0.9rem 0.5rem;
  font-size: 0.8rem;
  color: var(--faint);
  border-bottom: 1px solid var(--border-soft);
  margin-bottom: 0.3rem;
  overflow: hidden;
  text-overflow: ellipsis;
}
.user-menu .menu a,
.user-menu .menu button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.45rem 0.9rem;
  font-size: 0.92rem;
  color: var(--text);
  background: none;
  border: none;
  text-decoration: none;
  cursor: pointer;
  font-family: inherit;
}
.user-menu .menu a:hover,
.user-menu .menu button:hover { background: var(--surface-hover); }

/* ---------- Auth cards & forms ---------- */

.auth-card {
  max-width: 400px;
  margin: 3rem auto 0;
  padding: 1.75rem 1.75rem 1.5rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 2px;
}
.auth-card h2 { margin: 0 0 0.35rem; font-size: 1.25rem; }
.auth-card .auth-sub { margin: 0 0 1.25rem; color: var(--muted); font-size: 0.92rem; }
.auth-card .auth-alt {
  margin: 1.1rem 0 0;
  font-size: 0.88rem;
  color: var(--muted);
  text-align: center;
}
.auth-card .auth-alt a { color: var(--accent); }

.form-field { margin-bottom: 0.9rem; }
.form-field label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  margin-bottom: 0.3rem;
  color: var(--muted);
}
.form-field input {
  width: 100%;
  padding: 0.55rem 0.65rem;
  font-size: 0.95rem;
  font-family: inherit;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  color: var(--text);
  outline: none;
}
.form-field input:focus { border-color: var(--focus-border); }
.form-field .field-hint { font-size: 0.78rem; color: var(--faint); margin-top: 0.25rem; }
.field-hint { font-size: 0.78rem; color: var(--faint); margin-top: 0.25rem; }

.input-readonly { background: var(--surface-muted) !important; color: var(--muted) !important; }

.btn-block { display: block; width: 100%; text-align: center; }
.btn-danger {
  background: var(--surface);
  border-color: var(--red-border);
  color: var(--red-text);
}
.btn-danger:hover:not(:disabled) { background: var(--red-bg); }
.btn-small { font-size: 0.82rem; padding: 0.3rem 0.6rem; border-radius: 2px; }

/* ---------- Section pages (reels, account, admin) ---------- */

.page-title { margin: 0 0 0.25rem; font-size: 1.3rem; }
.page-sub { margin: 0 0 1.5rem; color: var(--muted); font-size: 0.92rem; }

.section { margin-bottom: 2.25rem; }
.section > h2 {
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--muted);
  margin: 0 0 0.75rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--border-soft);
}

.item-list { display: flex; flex-direction: column; gap: 0.6rem; }

.item-card {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.75rem 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 2px;
  flex-wrap: wrap;
}
.item-main { flex: 1; min-width: 220px; }
.item-title {
  font-weight: 600;
  font-size: 0.98rem;
  margin: 0 0 0.15rem;
  word-break: break-word;
}
.item-title a { color: var(--text); text-decoration: none; }
.item-title a:hover { color: var(--accent); }
.item-meta {
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-feature-settings: "tnum" 1;
  color: var(--faint);
  font-size: 0.72rem;
}
.item-actions {
  display: flex;
  gap: 0.4rem;
  align-items: center;
  flex-wrap: wrap;
}

.badge {
  display: inline-block;
  padding: 0.12rem 0.42rem 0.08rem;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.6rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  border-radius: 1px;
  border: 1px solid transparent;
  vertical-align: 1px;
}
.badge-green { background: var(--green-bg); border-color: var(--green-border); color: var(--green-text); }
.badge-gray { background: var(--surface-muted); border-color: var(--border); color: var(--muted); }
.badge-red { background: var(--red-bg); border-color: var(--red-border); color: var(--red-text); }
.badge-amber { background: var(--amber-bg); border-color: var(--amber-border); color: var(--amber-text); }

.empty-state {
  padding: 1.5rem 1rem;
  text-align: center;
  color: var(--muted);
  font-size: 0.92rem;
  background: var(--surface-alt);
  border: 1px dashed var(--border);
  border-radius: 2px;
}
.empty-state a { color: var(--accent); }

/* ---------- Products page ---------- */

.products-page {
  max-width: 46rem;
  padding-bottom: 4rem;
}
.products-title {
  margin: 0 0 0.6rem;
  font-size: 1.6rem;
  letter-spacing: -0.02em;
}
.products-lead {
  margin: 0 0 2rem;
  font-size: 1rem;
  line-height: 1.6;
  color: var(--muted);
}
.products-lead strong { color: var(--text); }

.product-card {
  padding: 1.25rem 1.4rem;
  margin-bottom: 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 2px;
  /* The nav menu deep-links to #web / #iphone / …; without this the sticky
     header would sit on top of whichever card was jumped to. */
  scroll-margin-top: 5rem;
}
.product-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.7rem;
  padding-bottom: 0.55rem;
  border-bottom: 1px solid var(--border-soft);
}
.product-head h3 {
  margin: 0;
  font-size: 1.12rem;
  letter-spacing: -0.015em;
}
.product-status {
  flex: none;
  padding: 0.15rem 0.5rem;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.64rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  background: var(--surface-muted);
  border: 1px solid var(--border-soft);
  border-radius: 2px;
  white-space: nowrap;
}
/* Green is reserved for verification signals elsewhere; here it marks the one
   product a reader can actually go and use right now. */
.product-status.is-live {
  color: var(--green-text);
  background: var(--green-bg);
  border-color: var(--green-border);
}
.product-card p {
  margin: 0 0 0.7rem;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--muted);
}
.product-card p:last-child { margin-bottom: 0; }
.product-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1rem;
}

/* ---------- Partner page ---------- */

/* Says plainly that the thing being pitched isn't built yet. Amber rather than
   green: this is a caveat, not a feature. */
.status-banner {
  margin: 0 0 2rem;
  padding: 0.8rem 1rem;
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--amber-text);
  background: var(--amber-bg);
  border: 1px solid var(--amber-border);
  border-radius: 2px;
}

.sub-head {
  margin: 1.4rem 0 0.5rem;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.66rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  color: var(--faint);
}
.product-card > .sub-head:first-of-type { margin-top: 0.4rem; }

.step-list {
  margin: 0 0 0.7rem;
  padding-left: 1.3rem;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--muted);
}
.step-list li { margin-bottom: 0.5rem; }
.step-list strong { color: var(--text); }

.code-block {
  margin: 0 0 0.9rem;
  padding: 0.7rem 0.85rem;
  /* Wide lines scroll inside the block rather than widening the page. */
  overflow-x: auto;
  font-size: 0.8rem;
  line-height: 1.55;
  background: var(--surface-alt);
  border: 1px solid var(--border-soft);
  border-radius: 2px;
}
.code-block code {
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--text);
  white-space: pre;
}
.product-card p code {
  padding: 0.05rem 0.25rem;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.86em;
  background: var(--surface-muted);
  border-radius: 2px;
}

/* The one instruction a partner most regrets skipping. */
.callout {
  padding: 0.7rem 0.9rem;
  border-left: 3px solid var(--accent);
  background: var(--accent-wash);
}
.product-card p.callout { color: var(--text); }

.products-foot {
  margin: 2rem 0 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border-soft);
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--faint);
}

/* ---------- Banners & indicators ---------- */

.banner {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-bottom: 0.9rem;
  padding: 0.6rem 0.9rem;
  background: var(--amber-bg);
  border: 1px solid var(--amber-border);
  border-radius: 2px;
  font-size: 0.9rem;
  color: var(--amber-text);
}
.banner .banner-actions { margin-left: auto; display: flex; gap: 0.4rem; }

/* An earlier version of a document: a snapshot, so the writing surface is
   frozen. Stated plainly rather than by graying the text out — the text is
   still there to be read, it just can't be changed. */
.version-banner {
  background: var(--surface-alt);
  border-color: var(--border);
  color: var(--muted);
}
body.version-readonly .editor,
body.version-readonly .rich-editor .ql-container {
  background: var(--surface-alt);
  border-style: dashed;
}
body.version-readonly .editor:focus { box-shadow: var(--shadow-card); }
/* The formatting toolbar and the plain/rich switch have nothing to act on. */
body.version-readonly .rich-editor .ql-toolbar { opacity: 0.45; pointer-events: none; }
/* Scoped to the toolbar's plain/rich toggle: the document panel's tabs use
   the same segmented control, and they must stay live while an earlier
   version is on screen — switching back to Current is how you leave it. */
body.version-readonly .doc-toolbar .format-seg button { opacity: 0.45; cursor: not-allowed; }

.save-indicator { color: var(--faint); font-size: 0.76rem; }
.save-indicator.saving { color: var(--amber-text); }
.save-indicator.error { color: var(--red-text); }
.save-indicator a { color: inherit; }

/* ---------- Admin ---------- */

.tabs {
  display: flex;
  gap: 0.25rem;
  margin-bottom: 1.25rem;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
.tab {
  padding: 0.45rem 0.9rem;
  font-size: 0.92rem;
  font-family: inherit;
  color: var(--muted);
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  cursor: pointer;
}
.tab:hover { color: var(--text); }
.tab.active {
  color: var(--accent);
  font-weight: 600;
  border-bottom-color: var(--accent);
}

/* The overview is one framed table rather than a row of tiles: four figures
   read faster stacked with their labels than spread across the page. */
.stat-frame { max-width: 26rem; }
.stat-table { width: 100%; border-collapse: collapse; }
.stat-table th,
.stat-table td {
  padding: 0.4rem 0.1rem;
  border-bottom: 1px solid var(--border-soft);
  font-size: 0.9rem;
}
.stat-table tr:last-child th,
.stat-table tr:last-child td { border-bottom: none; }
.stat-table th {
  text-align: left;
  font-weight: 500;
  color: var(--muted);
}
.stat-table td {
  text-align: right;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-feature-settings: "tnum" 1;
  font-weight: 600;
}

.table-wrap { overflow-x: auto; }
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.88rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 2px;
  overflow: hidden;
}
.data-table th, .data-table td {
  text-align: left;
  padding: 0.55rem 0.75rem;
  border-bottom: 1px solid var(--border-soft);
  vertical-align: middle;
}
.data-table th {
  background: var(--surface-alt);
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.68rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
}
.data-table tr:last-child td { border-bottom: none; }
.data-table .cell-actions { display: flex; gap: 0.35rem; flex-wrap: wrap; }

.search-row { margin-bottom: 0.8rem; }
.search-row input {
  width: 100%;
  max-width: 320px;
  padding: 0.45rem 0.65rem;
  font-size: 0.9rem;
  font-family: inherit;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  color: var(--text);
  outline: none;
}
.search-row input:focus { border-color: var(--focus-border); }

.cell-sub { color: var(--faint); font-size: 0.8rem; }

/* The chevron that opens a row: a user's detail screen, or a document's
   reels. Hidden until the row is hovered, like the options panel's "?" —
   the resting table is data, not controls. */
.chevron-cell { width: 1px; white-space: nowrap; text-align: right; }
.row-chevron {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.15rem;
  border: none;
  background: none;
  color: var(--faint);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease, transform 0.18s ease, color 0.12s ease;
}
.data-table tr:hover .row-chevron,
.row-chevron:focus-visible,
.row-chevron.open { opacity: 1; }
.row-chevron:hover:not(:disabled) { color: var(--accent); }
.row-chevron:disabled { opacity: 0 !important; cursor: default; }
/* An expander turns to point down at what it opened; a navigation chevron
   keeps pointing the way it takes you. */
.row-expander.open { transform: rotate(90deg); color: var(--accent); }
@media (hover: none) { .row-chevron:not(:disabled) { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .row-chevron { transition: none; } }

/* A document's reels, in a row spanning the table underneath it. */
.doc-row-detail > td { padding: 0 0.75rem 0.75rem; background: var(--surface-alt); }
.nested-table { border: 1px solid var(--border-soft); font-size: 0.82rem; }
.nested-table th { background: var(--surface); }

/* Users tab: the list and one user's detail share a slot and slide between
   them. The stage clips, so the offstage pane is genuinely out of sight
   rather than merely translated past the edge of a scrolling page. */
.slider-stage {
  position: relative;
  overflow-x: hidden;
}
.slider-pane {
  position: relative;
  transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1), opacity 0.3s ease;
}
/* Whichever pane is offstage drops out of flow, so the stage is always the
   height of the one you can actually see. (Stacking both in one grid cell
   instead would pad the stage to the taller pane's height even when the
   shorter one is showing.) */
.slider-pane.is-offstage,
.slider-pane.is-offstage-left {
  position: absolute;
  inset: 0 0 auto 0;
  opacity: 0;
  pointer-events: none;
}
.slider-pane.is-offstage { transform: translateX(100%); }
.slider-pane.is-offstage-left { transform: translateX(-100%); }
.slider-stage.no-slide .slider-pane { transition: none; }
@media (prefers-reduced-motion: reduce) {
  .slider-pane { transition: none; }
}

.back-link {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  margin-bottom: 0.9rem;
  padding: 0.2rem 0.4rem 0.2rem 0.1rem;
  border: none;
  background: none;
  font-family: inherit;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--accent);
  cursor: pointer;
}
.back-link:hover { text-decoration: underline; }

.detail-head { display: flex; align-items: center; gap: 0.8rem; margin-bottom: 1rem; }
.detail-avatar { width: 42px; height: 42px; font-size: 1rem; }
.detail-name { margin: 0; font-size: 1.15rem; }
.detail-sub { margin: 0.1rem 0 0; font-size: 0.85rem; color: var(--muted); }
#userDetail .opt-frame { max-width: 26rem; margin-bottom: 1rem; }
.detail-actions { display: flex; gap: 0.4rem; flex-wrap: wrap; padding-top: 0.2rem; }

.exp-note { margin: 0 0 0.6rem; font-size: 0.85rem; line-height: 1.5; color: var(--muted); }
.exp-note:last-child { margin-bottom: 0; }
.exp-note a { color: var(--accent); }

/* ---------- Admin: API access ---------- */

/* Laid out as a few two-up rows rather than one dense grid: the fields pair
   naturally (who they are / how to reach them, the app's id / its public name,
   the limits), and paired rows collapse to one column when there isn't room. */
.api-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin: 1rem 0 1.3rem;
  padding-bottom: 1.3rem;
  border-bottom: 1px solid var(--border-soft);
}
.api-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1rem 1.4rem;
  align-items: start;
}
/* Label and control share a line. A two-column grid rather than a flex row,
   so the hint under a field lands in the control's column instead of under
   the label. The fixed label column keeps the inputs of stacked rows starting
   at the same x — which is why no label here is longer than it fits. */
.api-field {
  display: grid;
  grid-template-columns: 7.5rem 1fr;
  align-items: center;
  gap: 0.3rem 0.7rem;
  min-width: 0;
}
.api-field .api-hint { grid-column: 2; }
/* Notes is the exception: a multi-line box gets its label above it, since an
   inline label would sit marooned against a tall control. */
.api-field-stacked { grid-template-columns: 1fr; }
.api-field-stacked label { margin-bottom: 0.1rem; }
.api-actions { display: flex; gap: 0.5rem; }
.api-field label {
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.62rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--faint);
}
.api-field input,
.api-field select,
.api-field textarea {
  padding: 0.4rem 0.5rem;
  font-family: inherit;
  font-size: 0.88rem;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  color: var(--text);
  outline: none;
  min-width: 0;
}
.api-field input:focus,
.api-field textarea:focus { border-color: var(--focus-border); }
.api-field textarea {
  resize: vertical;
  line-height: 1.5;
}
.api-hint { font-size: 0.72rem; color: var(--faint); }

/* Shown once, and deliberately hard to miss: there is no second chance to
   read a key, because only its hash is kept. */
.new-key {
  margin-bottom: 1.2rem;
  padding: 0.85rem 1rem;
  background: var(--amber-bg);
  border: 1px solid var(--amber-border);
  border-radius: 2px;
  color: var(--amber-text);
  font-size: 0.88rem;
}
.new-key p { margin: 0 0 0.6rem; }
.new-key-row { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.new-key-row code {
  flex: 1;
  min-width: 12rem;
  padding: 0.4rem 0.55rem;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.82rem;
  overflow-x: auto;
  background: var(--surface);
  border: 1px solid var(--amber-border);
  border-radius: 2px;
  color: var(--text);
}

/* Notes are typed into a multi-line box, so they are shown as typed rather
   than collapsed into one paragraph. */
.org-notes { white-space: pre-wrap; }

.api-orgs { display: flex; flex-direction: column; gap: 0.9rem; }
.api-org {
  padding: 0.9rem 1rem;
  background: var(--surface-alt);
  border: 1px solid var(--border-soft);
  border-radius: 2px;
}
.api-org-head { display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; }
.api-org-head h4 { margin: 0; font-size: 1rem; }

.api-client {
  margin: 0.7rem 0;
  padding: 0.7rem 0.85rem;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: 2px;
}
.api-client-head { display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; }
.api-slug {
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.76rem;
  color: var(--muted);
}
.api-client p { margin: 0.25rem 0 0; }

.api-keys { display: flex; flex-direction: column; gap: 0.3rem; margin: 0.6rem 0 0; }
.api-key-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  font-size: 0.82rem;
}
.api-key-row code {
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--text);
}
.api-key-row.revoked { opacity: 0.5; }
.api-key-row.revoked code { text-decoration: line-through; }

/* Present for screen readers, absent visually — for the header cell above a
   column of chevrons, which has nothing worth showing. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Account (modal) ---------- */

.settings-card {
  padding: 1.25rem 1.4rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 2px;
  max-width: 480px;
}
.settings-card + .settings-card { margin-top: 1rem; }
.settings-card h3 { margin: 0 0 0.75rem; font-size: 1rem; }
.settings-card .card-sub { margin: -0.4rem 0 0.9rem; color: var(--muted); font-size: 0.85rem; }

.danger-zone { border-color: var(--red-border); }
.danger-zone h3 { color: var(--red-text); }
.danger-zone p { font-size: 0.88rem; color: var(--muted); margin: 0 0 0.75rem; }

.form-msg { font-size: 0.86rem; margin-top: 0.5rem; }
.form-msg.ok { color: var(--accent); }
.form-msg.err { color: var(--red-text); }

/* Tabbed modals are pinned to the height of their tallest tab at open time
   (RD.sizeModalToTallestTab), so switching tabs never resizes the box; the
   body scrolls when max-height caps the size on small screens. */
.modal {
  margin: auto;
  padding: 0;
  width: min(600px, 94vw);
  max-height: 86vh;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 2px;
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.3);
}
.modal[open] { display: flex; flex-direction: column; }
.modal::backdrop { background: var(--backdrop); }

.modal-sm { width: min(420px, 94vw); }
.modal-lg { width: min(900px, 94vw); }

.modal-header {
  position: relative;
  padding: 1.1rem 1.4rem 0;
}
.modal-header h2 { margin: 0 0 0.1rem; font-size: 1.2rem; }
.modal-header .modal-sub { margin: 0 0 0.75rem; color: var(--muted); font-size: 0.86rem; }
.modal-header .dialog-close { color: var(--muted); top: 0.75rem; right: 0.9rem; }
.modal-tabs { margin-bottom: 0; }

.modal-body {
  /* Size from content (basis auto) so height-auto modals like the promo
     overlay aren't collapsed to 0; min-height 0 lets it shrink and scroll
     when the dialog's height is pinned or capped by max-height. */
  flex: 1 1 auto;
  min-height: 0;
  padding: 1.1rem 1.4rem 1.4rem;
  overflow-y: auto;
}

.auth-modal .auth-sub { margin: 0 0 1rem; color: var(--muted); font-size: 0.92rem; }
.auth-modal .error { margin-top: 0.9rem; }

.pref-row {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0.65rem 0 0.25rem;
  font-size: 0.92rem;
  cursor: pointer;
  user-select: none;
  flex-wrap: wrap;
}
.pref-row input[type="checkbox"] { accent-color: var(--accent-btn); margin: 0; }
.pref-row select,
.pref-row .pref-days {
  padding: 0.3rem 0.4rem;
  font-size: 0.9rem;
  font-family: inherit;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  color: var(--text);
}

/* ---------- Promo overlay ---------- */

.promo-grid {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: 1.75rem;
  align-items: start;
}
@media (max-width: 700px) {
  .promo-grid { grid-template-columns: 1fr; }
}

.promo-text p { margin: 0 0 0.9rem; font-size: 0.95rem; }

.promo-lead {
  font-size: 1.05rem;
  font-weight: 700;
}

/* The promo content is ~690px tall at full width; the default 86vh modal cap
   makes it scroll on ~800px-tall windows, so allow it a little more height. */
.promo-modal { max-height: 92vh; }

/* Two-part segmented control (same look as .theme-seg, sized for text)
   switching the pitch between "How it works" and "Features". */
.promo-seg {
  display: flex;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  overflow: hidden;
  margin-bottom: 1rem;
}
.promo-seg button {
  flex: 1;
  padding: 0.4rem 0.5rem;
  border: none;
  background: none;
  font-family: inherit;
  font-size: 0.9rem;
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
}
.promo-seg button + button { border-left: 1px solid var(--border-soft); }
.promo-seg button:hover { color: var(--text); }
.promo-seg button.active {
  background: var(--surface-hover);
  color: var(--accent);
  font-weight: 600;
}

.promo-features {
  margin: 0 0 0.9rem;
  padding-left: 1.2rem;
  font-size: 0.92rem;
}
.promo-features li { margin-bottom: 0.3rem; }

.promo-more { margin: 0; font-size: 0.92rem; }
.promo-more a { color: var(--accent); font-weight: 600; }

/* FAQ tab: much taller than the other panels, so it scrolls within its own
   box instead of stretching the modal past the screenshots column. */
.promo-faq {
  max-height: min(560px, 60vh);
  overflow-y: auto;
  padding-right: 0.6rem;
}
.faq-title { margin: 0 0 0.8rem; font-size: 1.02rem; }
/* Each Q&A is a <details>, collapsed by default; the question is its
   <summary>, keeping the browser's disclosure triangle as the affordance. */
.promo-faq .faq-item { margin-bottom: 0.7rem; }
.promo-faq .faq-item[open] { margin-bottom: 1.15rem; }
.promo-faq .faq-q {
  font-weight: 600;
  cursor: pointer;
  margin-bottom: 0.35rem;
  /* Replace the browser's disclosure triangle with a quieter chevron, drawn
     from two borders and rotated right→down on expand. Positioned absolutely
     so multi-line questions wrap with a clean hanging indent. */
  list-style: none;
  position: relative;
  padding-left: 1.05em;
}
.promo-faq .faq-q::-webkit-details-marker { display: none; }
.promo-faq .faq-q::before {
  content: '';
  position: absolute;
  left: 0.12em;
  top: 0.42em;
  width: 0.38em;
  height: 0.38em;
  border-right: 1.5px solid var(--muted);
  border-bottom: 1.5px solid var(--muted);
  transform: rotate(-45deg);
  transition: transform 0.18s ease;
}
.promo-faq .faq-item[open] > .faq-q::before { transform: rotate(45deg); }
.promo-faq .faq-q:hover { color: var(--accent); }
.promo-faq .faq-q:hover::before { border-color: var(--accent); }
/* The answer wrapper is what the open/close animation resizes. Its overflow
   is clipped only mid-animation: steady-state it must stay visible so the
   privacy popover can extend above its anchor. No bottom margin may escape
   the wrapper, or the measured height would come up short and the following
   items would jump at the end of the animation. */
.promo-faq .faq-a > :last-child { margin-bottom: 0; }
.promo-faq .faq-item.animating .faq-a { overflow: hidden; }
/* The FAQ's privacy popover opens upward from a link at the panel's bottom;
   .privacy-pop supplies the look, .popover-anchor the positioning context. */

/* Links in the "Try it" panel; .promo-try-link is a button styled as a link
   (it acts on the modal rather than navigating). */
.promo-panel a { color: var(--accent); }
.promo-try-link {
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
}

.promo-tagline {
  font-style: italic;
  margin-top: 1.1rem;
}

.promo-shots {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.promo-shots figure { margin: 0; }
.promo-shots img {
  display: block;
  width: 100%;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
}
.promo-shots figcaption {
  font-size: 0.78rem;
  color: var(--muted);
  margin-top: 0.35rem;
}

/* Click-to-toggle reel: both GIFs stay mounted so their animations keep
   running in step; the redacted one sits on top and fades in/out. */
.promo-reel {
  position: relative;
  cursor: pointer;
  border-radius: 2px;
}
.promo-reel:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.promo-reel .promo-reel-alt {
  position: absolute;
  inset: 0;
  /* Not 0: Chromium skips painting fully transparent elements and then never
     advances the hidden GIF's animation clock, which would break the
     keep-the-playback-position toggle. 1.5% keeps it painted (and running)
     while being imperceptible over the visible reel. */
  opacity: 0.015;
  transition: opacity 0.15s ease;
}
.promo-reel.show-redacted .promo-reel-alt { opacity: 1; }
.promo-reel-badge {
  /* Bottom-left, hovering just above the animation controls of the captured
     window (the controls row starts ~24% up from the frame's bottom). */
  position: absolute;
  bottom: 26%;
  left: 4%;
  padding: 2px 9px;
  background: rgba(0, 0, 0, 0.62);
  color: #fff;
  font-size: 0.7rem;
  border-radius: 2px;
  pointer-events: none;
}

/* ---------- Bottom bar / theme control ---------- */

.site-footer {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.35rem clamp(1.25rem, 3vw, 2.5rem);
  background: var(--surface);
  border-top: 1px solid var(--rule-ink);
  transition: transform 380ms cubic-bezier(0.4, 0, 0.2, 1),
              margin-bottom 380ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* Auto-hidden state (app.js drives it): on the scrolling pages the bar is
   fixed, so it simply slides below the edge. On the app screens it starts out
   in the flex column, so the first hide slides it out by way of a negative
   margin — the height it gives up goes to the editor / replay pane above it,
   which grows into the space as the bar leaves. `--footer-h` is measured live
   in app.js. */
.site-footer.footer-away { transform: translateY(100%); }
.editor-screen .site-footer.footer-away:not(.floating),
.viewer-screen .site-footer.footer-away:not(.floating) {
  transform: none;
  margin-bottom: calc(-1 * var(--footer-h, 2.4rem));
}

/* …and once that first hide lands, app.js hands the bar over to 'floating':
   it leaves the flow permanently, so the space stays reclaimed and later
   reveals slide it *over* the editor (a video-player control bar) instead of
   shoving the Generate Reel row up and down on every pass of the mouse. */
.editor-screen .site-footer.floating,
.viewer-screen .site-footer.floating {
  position: fixed;
  box-shadow: 0 -6px 22px rgba(0, 0, 0, 0.10);
}

/* The tab left behind: a small pull-up lip centered under the main content
   column (`--footer-handle-x` tracks that column in app.js, so an open sidebar
   shifts the tab with it; the 50% fallback is plain window-centering). It
   carries the active theme glyph — a preview of what's down there rather than
   an abstract arrow — and rises as the bar sinks (hence the entry delay),
   dropping away the moment the bar comes back. */
.footer-handle {
  position: fixed;
  bottom: 0;
  left: var(--footer-handle-x, 50%);
  z-index: 21;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  /* Kept just under the app screens' 1.1rem bottom padding so the tab tucks
     into that gap instead of overlapping the content above it, and roughly the
     same band as FOOTER_REVEAL_ZONE in app.js — so hovering the tab is
     essentially the gesture that brings the bar back. */
  height: 17px;
  padding: 0;
  border: 1px solid var(--rule-ink);
  border-bottom: none;
  border-radius: 7px 7px 0 0;
  background: var(--surface);
  color: var(--faint);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, 100%);
  transition: transform 320ms cubic-bezier(0.4, 0, 0.2, 1), opacity 220ms ease, color 140ms ease;
}
.footer-handle.visible {
  opacity: 1;
  pointer-events: auto;
  transform: translate(-50%, 0);
  transition-delay: 220ms, 220ms, 0s;
}
.footer-handle:hover { color: var(--accent); }

/* No hover on touch, so the tab is the only way back to the bar: give it a
   bigger target. It's centered under the content column, well clear of the
   right-aligned Generate Reel button, so the extra height costs nothing. */
@media (pointer: coarse) {
  .footer-handle { width: 72px; height: 24px; }
}

@media (prefers-reduced-motion: reduce) {
  .site-footer,
  .footer-handle { transition-duration: 1ms; }
  .footer-handle.visible { transition-delay: 0s; }
}

/* ---------- Sidebar edge tabs ----------
   The way back to a hidden sidebar, mirroring the theme bar's pull tab: an
   ink lip against the window edge, borderless on the side it tucks into and
   rounded on the two corners that show. Each slides out of the frame when its
   panel is open, so only the panel or its tab is ever present. */
.edge-tab {
  position: fixed;
  top: 50%;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.75rem 0.3rem;
  border: 1px solid var(--rule-ink);
  background: var(--surface);
  color: var(--faint);
  cursor: pointer;
  transition: transform 0.28s ease, opacity 0.2s ease, color 140ms ease;
}
.edge-tab:hover { color: var(--accent); }

.edge-tab-left {
  left: 0;
  border-left: none;
  border-radius: 0 7px 7px 0;
  transform: translateY(-50%);
}
.edge-tab-right {
  right: 0;
  border-right: none;
  border-radius: 7px 0 0 7px;
  transform: translateY(-50%);
}
/* Its panel is showing, so the tab leaves — outwards, the way the panel came
   in. translateY has to be restated: a transform is one value, and dropping
   it here would snap the tab down to its top:50% origin on the way out. */
body.sidebar-open .edge-tab-left {
  transform: translate(-100%, -50%);
  opacity: 0;
  pointer-events: none;
}
body.options-open .edge-tab-right {
  transform: translate(100%, -50%);
  opacity: 0;
  pointer-events: none;
}

/* "Docs", turned a quarter turn to read down the tab. */
.edge-tab-label {
  writing-mode: vertical-rl;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.62rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
}
/* The knob turns a step on hover here too, as it does on the toolbar button
   that opens the same panel. */
.edge-tab .dial-needle {
  transform-origin: 12px 14.2px;
  transition: transform 0.45s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.edge-tab:hover .dial-needle { transform: rotate(45deg); }

@media (prefers-reduced-motion: reduce) {
  .edge-tab,
  .edge-tab .dial-needle { transition: none; }
}

.footer-link {
  border: none;
  background: none;
  padding: 0.2rem 0;
  font-family: inherit;
  font-size: 0.8rem;
  color: var(--faint);
  cursor: pointer;
}
.footer-link:hover { color: var(--accent); text-decoration: underline; }

.privacy-pop {
  position: absolute;
  bottom: calc(100% + 11px);
  left: 0;
  margin: 0;
  width: max-content;
  max-width: min(320px, 80vw);
  padding: 0.7rem 0.9rem;
  font-size: 0.85rem;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 2px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
  outline: none; /* dialog.show() focuses it; the ring adds nothing here */
}
/* Wedge pointing down at the Privacy Policy link */
.privacy-pop::after {
  content: '';
  position: absolute;
  bottom: -7px;
  left: 1.4rem;
  width: 12px;
  height: 12px;
  background: var(--surface);
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  transform: rotate(45deg);
}

.theme-seg {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  overflow: hidden;
}
.theme-seg button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 22px;
  padding: 0;
  border: none;
  background: none;
  color: var(--faint);
  cursor: pointer;
}
.theme-seg button + button { border-left: 1px solid var(--border-soft); }
.theme-seg button:hover { color: var(--text); }
.theme-seg button.active {
  background: var(--accent-btn);
  color: var(--on-accent);
}

/* ---------- Reels sidebar ---------- */

/* Header on top, then a flex row: the sidebar slides in from the left (via
   negative margin, so the page content shifts right rather than being
   covered); on narrow screens it overlays instead. */
.body-row { flex: 1; display: flex; align-items: stretch; min-height: 0; }
.editor-screen .body-row .page { flex: 1; min-width: 0; }

/* The width is a variable so the drag handle can set it and the closed
   state's negative margin always matches it exactly. */
:root { --sidebar-w: 320px; }

.reels-sidebar {
  flex: 0 0 var(--sidebar-w);
  width: var(--sidebar-w);
  margin-left: calc(-1 * var(--sidebar-w));
  visibility: hidden;
  transition: margin-left 0.28s ease, visibility 0.28s;
  border-right: 1px solid var(--border-soft);
  background: var(--surface);
}

/* Drag handles beside each sidebar: a transparent strip over the panel's own
   border that shows an accent line when grabbed. Keyboard users get them too
   (arrows resize, Home/End jump to the limits), which is why they're
   focusable rather than bare divs. */
.sidebar-resizer,
.options-resizer {
  flex: 0 0 6px;
  position: relative;
  align-self: stretch;
  cursor: col-resize;
  background: none;
  border: none;
  padding: 0;
  touch-action: none;
  z-index: 5;
}
body:not(.sidebar-open) .sidebar-resizer { display: none; }
body:not(.options-open) .options-resizer { display: none; }
.sidebar-resizer::after,
.options-resizer::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 2px;
  width: 2px;
  background: transparent;
  transition: background 0.15s ease;
}
.sidebar-resizer:hover::after,
.sidebar-resizer:focus-visible::after,
.options-resizer:hover::after,
.options-resizer:focus-visible::after,
body.resizing .sidebar-resizer::after,
body.resizing .options-resizer::after { background: var(--accent); }
.sidebar-resizer:focus-visible,
.options-resizer:focus-visible { outline: none; }

/* While dragging: no slide transition to fight the pointer, and no text
   selection anywhere on the page. */
body.resizing { cursor: col-resize; user-select: none; }
body.resizing .reels-sidebar,
body.resizing .options-sidebar { transition: none; }
body.sidebar-open .reels-sidebar { margin-left: 0; visibility: visible; }

.sidebar-inner {
  display: flex;
  flex-direction: column;
  height: 100%; /* the app frame bounds .body-row, so 100% is the visible area */
}
/* A section header, not the first row of the list: a cobalt-tinted band, a
   step darker than the panel, with the title as a small mono label — so
   nothing about it reads as an entry. */
.sidebar-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 0.85rem 1rem 0.7rem;
  border-bottom: 1px solid var(--sidebar-head-border);
  background: var(--sidebar-head-bg);
}
.sidebar-head h2 {
  margin: 0;
  flex: none;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.74rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--sidebar-head-text);
}
/* A chevron pointing the way the panel goes when it closes: left for the
   documents sidebar, right for the reel options. */
.sidebar-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  line-height: 1;
  padding: 0.1rem 0.25rem;
  color: var(--sidebar-head-text);
  opacity: 0.7;
  cursor: pointer;
}
.sidebar-close svg { display: block; }
.sidebar-close:hover { opacity: 1; }

/* Marching chevrons: at rest one chevron, and on hover a few sliding the way
   the panel is about to go.
   The box is 40 units wide and the copies sit 14 apart, so two or three are in
   view at once with a clear gap between them. The track shifts by exactly one
   spacing, which is what makes the loop seamless: at the end of a cycle the
   arrangement is identical to the start. Five copies — two beyond each edge —
   so the set stays complete whichever direction it runs. The outer <svg> clips
   to its viewBox, so copies enter and leave at the edges. */
.chev-run .chev-extra { opacity: 0; }
.sidebar-close:hover .chev-run .chev-extra { opacity: 1; }
.chev-track { animation: none; }
.sidebar-close:hover .chev-track { animation: chev-march-left 0.6s linear infinite; }
.sidebar-close:hover .chev-run-right .chev-track { animation-name: chev-march-right; }
/* Fading the ends turns the viewBox's hard clip into chevrons appearing out of
   nothing, which is what sells the endlessness. Only while marching. */
.sidebar-close:hover .chev-run {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 22%, #000 78%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 22%, #000 78%, transparent);
}

@keyframes chev-march-left {
  from { transform: translateX(0); }
  to { transform: translateX(-14px); }
}
@keyframes chev-march-right {
  from { transform: translateX(0); }
  to { transform: translateX(14px); }
}

/* No marching for anyone who asked for less motion — just the one chevron. */
@media (prefers-reduced-motion: reduce) {
  .sidebar-close:hover .chev-track { animation: none; }
  .sidebar-close:hover .chev-run .chev-extra { opacity: 0; }
}

/* One full-bleed list of documents; rows carry their own inset. */
.sidebar-scroll { flex: 1; overflow-y: auto; padding: 0.35rem 0; }

.sb-list { display: flex; flex-direction: column; }
.sb-item {
  position: relative;
  border-left: 2px solid transparent;
}
.sb-item + .sb-item { border-top: 1px solid var(--border-soft); }
.sb-item:hover { background: var(--surface-hover); }
.sb-item.active {
  background: var(--accent-wash);
  border-left-color: var(--accent);
}
.sb-item.active .sb-item-title { color: var(--accent); }
.sb-item-main {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.55rem 0.85rem 0.5rem;
  border: none;
  background: none;
  font-family: inherit;
  color: var(--text);
  cursor: pointer;
  /* Double-click opens the reel options; nothing here is text to select. */
  user-select: none;
}
.sb-item-title {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  padding-right: 1rem;
}
.sb-item-title-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Status pill: shown only for the states worth calling out — a version with
   no link yet, or a link that won't currently serve. */
.sb-status {
  flex: none;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.56rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  padding: 0.1rem 0.35rem 0.06rem;
  border: 1px solid;
  border-radius: 1px;
}
.sb-status.is-draft { color: var(--faint); border-color: var(--border); border-style: dashed; }
.sb-status.is-off { color: var(--red-text); border-color: var(--red-border); background: var(--red-bg); }
.sb-item-meta {
  display: block;
  margin-top: 0.15rem;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-feature-settings: "tnum" 1;
  font-size: 0.68rem;
  color: var(--faint);
}
.sb-item-actions {
  position: absolute;
  top: 0.35rem;
  right: 0.35rem;
  display: flex;
  gap: 0.1rem;
  opacity: 0;
  transition: opacity 0.15s ease;
  background: inherit;
  border-radius: 1px;
}
.sb-item:hover .sb-item-actions,
.sb-item:focus-within .sb-item-actions { opacity: 1; }
.sb-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  border-radius: 1px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}
.sb-action:hover { background: var(--surface-hover); color: var(--text); }
.sb-action.danger:hover,
.sb-action.danger.armed { background: var(--red-bg); color: var(--red-text); }

.sidebar-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.6rem 1rem;
  border-top: 1px solid var(--border-soft);
}
.sb-manage-link { font-size: 0.78rem; color: var(--muted); text-decoration: none; white-space: nowrap; }
.sb-manage-link:hover { color: var(--accent); text-decoration: underline; }

/* Earlier versions of the same document: a quiet disclosure under the
   current version, expanding an indented list of the ones it grew out of. */
.sb-count {
  margin-right: auto;
  padding-left: 0.55rem;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.62rem;
  color: var(--sidebar-head-text);
  opacity: 0.72;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sb-empty { padding: 0.6rem 0.85rem; font-size: 0.82rem; color: var(--faint); }
.sb-error { padding: 0.6rem 0.85rem; font-size: 0.82rem; color: var(--red-text); }

@media (max-width: 800px) {
  .reels-sidebar {
    position: fixed;
    left: 0;
    top: 0;
    bottom: 0;
    z-index: 40;
    margin-left: 0;
    transform: translateX(-100%);
    transition: transform 0.28s ease, visibility 0.28s;
    box-shadow: 0 0 40px rgba(0, 0, 0, 0.25);
  }
  body.sidebar-open .reels-sidebar { transform: translateX(0); }
  .sidebar-inner { padding-bottom: 0.5rem; }
  /* An overlay drawer has nothing to resize against. */
  .sidebar-resizer { display: none !important; }
}

@media (prefers-reduced-motion: reduce) {
  .reels-sidebar { transition: none; }
}

/* ---------- Document header (title + document toolbar) ----------
   One row: the frameless title takes the space to the left, and the
   document's two controls — the plain/rich format toggle and the button that
   opens the reel options sidebar — are framed together as a small toolbar at
   the right end. */

.doc-row {
  display: flex;
  /* The toolbar has to land *on* the editor's top rule, so the row aligns on
     its bottom edge rather than centering its items against each other. */
  align-items: flex-end;
  gap: 1rem;
  margin-bottom: 0;
}
.doc-header {
  flex: 1;
  min-width: 0;
  padding-bottom: 0.4rem; /* lifts the title clear of the rule below it */
}

/* The mini-toolbar: framed on three sides, with the editor's own top edge
   closing it underneath. Two rules stacked there would read as a seam, and
   the point of the frame is that the toolbar belongs to the sheet below it.
   No right margin and no border-radius on the bottom corners, so its right
   edge lands exactly on the editor's (both are full-width siblings, so the
   row's right edge is the editor's). */
.doc-toolbar {
  flex: none;
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.45rem;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-bottom: none;
  border-radius: 2px 2px 0 0;
}

.doc-title {
  font-size: 1.02rem;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The options control is a bare mark, no frame of its own: the toolbar
   already supplies one, and a second box around a 20px icon next to the
   framed format toggle would read as clutter. Its padding keeps it a hair
   shorter than that toggle, so the toggle stays the toolbar's tallest item
   and sets the height. */
.doc-options-btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.1rem 0.4rem;
  border: none;
  border-radius: 2px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}
.doc-options-btn:hover,
.doc-options-btn[aria-expanded="true"] { color: var(--accent); }
/* With no frame or fill to carry the open state, the dial itself reports it:
   the needle steps to the next mark on hover and stays there while the
   sidebar is open, so the control looks turned on because it is turned. */
.dial { display: block; }
.dial-needle {
  transform-origin: 12px 14.2px;
  transition: transform 0.45s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.doc-options-btn:hover .dial-needle,
.doc-options-btn[aria-expanded="true"] .dial-needle { transform: rotate(45deg); }
@media (prefers-reduced-motion: reduce) {
  .dial-needle { transition: none; }
}

/* Everything on a row shares one baseline — the label, the field, the "days"
   after it, and the smaller note at the end — so differing type sizes line up
   on the text rather than on their box centers. Checkboxes are the exception:
   a replaced element baselines on its bottom edge, so they center on the line
   instead, which is what reads as aligned with the label beside them. */
.doc-opt {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  flex-wrap: wrap;
  margin-top: 0.55rem;
  font-size: 0.92rem;
  cursor: pointer;
  user-select: none;
}
.doc-opt input[type="checkbox"] {
  accent-color: var(--accent-btn);
  align-self: center;
  margin: 0;
}
.doc-opt select {
  padding: 0.3rem 0.4rem;
  font-size: 0.9rem;
  font-family: inherit;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  color: var(--text);
}
.doc-title-input {
  flex: 1;
  min-width: 200px;
  max-width: 340px;
  padding: 0.35rem 0.5rem;
  font-size: 0.92rem;
  font-family: inherit;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  color: var(--text);
  outline: none;
}
.doc-title-input:focus { border-color: var(--focus-border); }

/* ---------- Reel options sidebar (right) ----------
   Mirrors the reels sidebar on the other edge: slides in from the right,
   pushing the writing surface rather than covering it. */

.options-sidebar {
  flex: 0 0 var(--options-w);
  width: var(--options-w);
  margin-right: calc(-1 * var(--options-w));
  visibility: hidden;
  transition: margin-right 0.28s ease, visibility 0.28s;
  border-left: 1px solid var(--border-soft);
  background: var(--surface);
}
body.options-open .options-sidebar { margin-right: 0; visibility: visible; }

:root { --options-w: 340px; }

.opt-scroll { padding: 0.9rem 1rem; }

/* Current Version / Earlier Versions — the same segmented control the editor
   uses for plain/rich, so the two read as one idiom. */
.opt-scroll .doc-tabs {
  display: flex;
  flex: none;
  width: fit-content;
  margin: 0 auto;
}
/* Equal segments, so neither tab reads as the more important one. */
.doc-tabs button { flex: 1; text-align: center; }

.opt-status {
  margin: 0.9rem 0 0;
  font-size: 0.85rem;
  line-height: 1.45;
  color: var(--muted);
}
.opt-status.is-live { color: var(--green-text); }
/* A friendly time with the exact one behind a tooltip. The dotted underline and
   the help cursor are what say hovering is worth doing. */
/* A word with more behind it. The dashed accent-coloured underline is the
   affordance — a dotted one in the text's own colour was invisible at this
   size. The cursor is deliberately left alone: `cursor: help` puts an ugly
   question mark over prose. */
.hint { position: relative; }
/* `fixed`, and placed by RD.installHints rather than by CSS. An absolutely
   positioned popup is still clipped by any scrolling ancestor — the document
   panel is one — and can run off the window when its anchor sits near an edge.
   Taking it out of flow entirely and clamping it to the viewport is the only
   placement that can't be cut off. */
.hint-pop {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 60;
  width: max-content;
  max-width: min(18rem, 60vw);
  padding: 0.4rem 0.55rem;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.74rem;
  font-weight: 400;
  line-height: 1.4;
  text-transform: none;
  letter-spacing: 0;
  white-space: normal;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 2px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
}
/* Opened by script, because the coordinates have to be set before it shows. */
.hint-pop.is-open { display: block; }

/* Longhands rather than the shorthand: a shorthand carrying var() is handled
   as a pending substitution, and it reads less clearly to tooling. */
.ts-hint {
  text-decoration-line: underline;
  text-decoration-style: dashed;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.ts-hint:hover { color: var(--accent); }

.opt-select {
  flex: 1;
  min-width: 0;
  padding: 0.3rem 0.4rem;
  font-size: 0.88rem;
  font-family: inherit;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  color: var(--text);
}

/* The reel settings, boxed with their name set into the top rule — a real
   <fieldset>/<legend>, which is what that pattern is for, so the grouping is
   announced rather than merely drawn. */
.opt-frame {
  margin: 0.9rem 0 0;
  padding: 0.2rem 0.8rem 0.8rem;
  border: 1px solid var(--border-soft);
  border-radius: 2px;
  min-width: 0; /* a fieldset won't shrink below its content without this */
}
.opt-frame legend {
  padding: 0 0.35rem;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.62rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--faint);
}

.panel-actions {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 1rem;
}
/* Full width: the labels swap ("Generate Reel" / "Show Reel" / "Hide Reel",
   "Delete reel" / "Really delete?") and a shrink-to-fit button would jump on
   every change. */
.panel-btn {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.55rem 0.9rem;
  text-align: center;
}
.panel-btn .btn-reel { flex: none; }

/* A fixed band at the foot of the panel, outside the scrolling area: it
   describes the document, not the version being scrolled through, so it neither
   moves nor resizes. `flex: none` because .sidebar-inner is a flex column and
   this must not be shrunk to make room for the scroller. */
.doc-foot {
  flex: none;
  padding: 1rem;
  border-top: 1px solid var(--border);
  background: var(--surface);
}
.doc-facts { margin-bottom: 0.9rem; }
.doc-facts th,
.doc-facts td { font-size: 0.82rem; }
/* A long title wraps rather than forcing the panel wider. */
.doc-facts td { font-weight: 500; word-break: break-word; }
.doc-foot .panel-btn { padding: 0.45rem 0.9rem; font-size: 0.85rem; }
.opt-frame .doc-title-input { max-width: 100%; }

/* One option per row: control on the left, "?" pushed to the right edge.
   Every row is the width of the box, so margin-left:auto puts every "?" on
   the same x without a grid to hold them there. */
.opt-row {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin-top: 0.55rem;
}
.opt-row > .doc-opt { margin-top: 0; }
.opt-label { font-size: 0.92rem; flex: none; }
/* The title field takes whatever the label and the "?" leave it. min-width:0
   overrides the wider floor the same field uses in the account modal, where
   it has a dialog's width to spread into rather than a 340px panel's. */
.opt-row .doc-title-input { flex: 1 1 auto; min-width: 0; }
/* The days field belongs to the Expiration checkbox above it, so it is
   indented under it and greys out with it rather than reading as its own
   independent option. */
.opt-subrow { padding-left: 1.45rem; margin-top: 0.35rem; }
.opt-subrow.is-disabled { opacity: 0.45; }
.opt-subrow.is-disabled .doc-opt { cursor: default; }

.opt-help-anchor {
  position: relative;
  margin-left: auto;
  flex: none;
  display: inline-flex;
}
/* Hidden until its row is hovered, so the resting panel is labels and
   controls only. It fades rather than being removed from the layout: the
   column of "?" slots stays reserved, so rows don't twitch as the pointer
   travels down them. */
.opt-help {
  flex: none;
  width: 17px;
  height: 17px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: none;
  font-family: inherit;
  font-size: 0.66rem;
  font-weight: 600;
  line-height: 1;
  color: var(--faint);
  cursor: help;
  opacity: 0;
  transition: opacity 0.12s ease;
}
.opt-row:hover .opt-help,
.opt-help-anchor:focus-within .opt-help { opacity: 1; }
.opt-help:hover,
.opt-help:focus-visible { border-color: var(--accent); color: var(--accent); }
/* Nothing hovers on a touch screen, so there they simply stay put. */
@media (hover: none) {
  .opt-help { opacity: 1; }
}

/* Shown on hover, and on keyboard focus so the "?" is not mouse-only. Opens
   down and to the left: the panel is a scroll container, which clips, and the
   button already sits against its right edge. */
.opt-pop {
  display: none;
  position: absolute;
  top: calc(100% + 8px);
  right: -2px;
  width: max-content;
  max-width: 250px;
  padding: 0.55rem 0.7rem;
  font-size: 0.8rem;
  line-height: 1.45;
  font-weight: 400;
  text-align: left;
  white-space: normal;
  cursor: default;
  user-select: text;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 2px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
  z-index: 25;
}
.opt-help-anchor:hover .opt-pop,
.opt-help-anchor:focus-within .opt-pop { display: block; }
/* Wedge pointing up at the "?" */
.opt-pop::before {
  content: "";
  position: absolute;
  top: -5px;
  right: 4px;
  width: 9px;
  height: 9px;
  background: var(--surface);
  border-left: 1px solid var(--border);
  border-top: 1px solid var(--border);
  transform: rotate(45deg);
}

.opt-note {
  margin: 0.6rem 0 0;
  font-size: 0.82rem;
  color: var(--muted);
}
@media (max-width: 900px) {
  .options-sidebar {
    position: fixed;
    right: 0;
    top: 0;
    bottom: 0;
    z-index: 40;
    margin-right: 0;
    transform: translateX(100%);
    transition: transform 0.28s ease, visibility 0.28s;
    box-shadow: 0 0 40px rgba(0, 0, 0, 0.25);
  }
  body.options-open .options-sidebar { transform: translateX(0); }
}

/* Restoring a persisted layout on load must not look like the user just
   opened it: transitions are suppressed for that first paint only. */
body.no-slide .reels-sidebar,
body.no-slide .options-sidebar { transition: none !important; }

@media (prefers-reduced-motion: reduce) {
  .options-sidebar { transition: none; }
}

/* ---------- Rich text (editor + viewer) ---------- */

/* Plain/Rich mode toggle (lives in .doc-toolbar beside the Options button,
   which is styled to match it). */
.format-seg {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  overflow: hidden;
}
.format-seg button {
  padding: 0.22rem 0.8rem;
  border: none;
  background: none;
  font-family: inherit;
  font-size: 0.82rem;
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
}
.format-seg button + button { border-left: 1px solid var(--border-soft); }
.format-seg button:hover { color: var(--text); }
.format-seg button.active {
  background: var(--accent-btn);
  color: var(--on-accent);
  font-weight: 600;
}

/* The rich editing surface fills the same slot as the textarea. Quill's
   snow theme is restyled onto the site's variables so both themes work. */
.rich-editor {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 55vh;
}
.rich-editor .ql-toolbar.ql-snow {
  border: 1px solid var(--border);
  border-radius: 2px 2px 0 0;
  background: var(--surface);
  font-family: inherit;
}
.rich-editor .ql-container.ql-snow {
  flex: 1;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-top: none;
  border-radius: 0 0 2px 2px;
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: 1rem;
}
.rich-editor .ql-editor {
  flex: 1;
  line-height: 1.75;
  padding: 1.4rem 1.6rem 4.2rem; /* landing space for the floating reel control */
}
.rich-editor .ql-editor.ql-blank::before {
  color: var(--faint);
  font-style: normal;
  left: 1.6rem;
}
/* Quill's toolbar icons are hardcoded dark strokes; retint for theming. */
.rich-editor .ql-snow .ql-stroke { stroke: var(--muted); }
.rich-editor .ql-snow .ql-fill { fill: var(--muted); }
.rich-editor .ql-snow .ql-picker { color: var(--muted); }
.rich-editor .ql-snow.ql-toolbar button { border-radius: 1px; }
.rich-editor .ql-snow.ql-toolbar button:hover .ql-stroke { stroke: var(--accent); }
.rich-editor .ql-snow.ql-toolbar button:hover .ql-fill { fill: var(--accent); }
/* Quill marks buttons for formats active at the cursor with .ql-active; its
   default cue is a bare color shift, too subtle once retinted — give active
   states a filled pill so bold/italic/etc. read as clearly "on". */
.rich-editor .ql-snow.ql-toolbar button.ql-active { background: var(--green-bg); }
.rich-editor .ql-snow.ql-toolbar button.ql-active .ql-stroke { stroke: var(--green-text); }
.rich-editor .ql-snow.ql-toolbar button.ql-active .ql-fill { fill: var(--green-text); }
.rich-editor .ql-snow .ql-picker-label.ql-active { color: var(--green-text); }
.rich-editor .ql-snow .ql-picker-label.ql-active .ql-stroke { stroke: var(--green-text); }
.rich-editor .ql-snow .ql-picker-item.ql-selected { color: var(--accent); }
.rich-editor .ql-snow .ql-picker-options {
  background: var(--surface);
  border-color: var(--border);
}
.rich-editor .ql-snow .ql-picker.ql-expanded .ql-picker-label { border-color: var(--border); }
.rich-editor .ql-snow .ql-picker-item:hover,
.rich-editor .ql-snow .ql-picker-label:hover { color: var(--accent); }
/* Our link editor. A <dialog> rather than an absolutely-positioned panel, so
   it renders in the top layer and no ancestor's overflow can clip it — which is
   what went wrong with Quill's own tooltip near the editor's edges. */
.link-dialog {
  width: min(26rem, calc(100vw - 2rem));
  padding: 1rem 1.1rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 14px 44px rgba(0, 0, 0, 0.24);
}
.link-dialog::backdrop { background: rgba(0, 0, 0, 0.28); }
.link-dialog h3 {
  margin: 0 0 0.8rem;
  font-size: 1rem;
}
.link-dialog label {
  display: block;
  margin-bottom: 0.25rem;
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.62rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--faint);
}
.link-dialog input {
  width: 100%;
  margin-bottom: 0.75rem;
  padding: 0.4rem 0.5rem;
  font-family: inherit;
  font-size: 0.9rem;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--surface);
  color: var(--text);
  outline: none;
}
.link-dialog input:focus { border-color: var(--focus-border); }
.link-error {
  margin: -0.35rem 0 0.7rem;
  font-size: 0.8rem;
  color: var(--red-text);
}
.link-actions { display: flex; gap: 0.4rem; }
/* Remove sits apart from the pair that dismiss the dialog. */
.link-actions #linkCancel { margin-left: auto; }

.rich-editor .ql-snow .ql-tooltip {
  background: var(--surface);
  color: var(--text);
  border-color: var(--border);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
}
.rich-editor .ql-snow .ql-tooltip input[type="text"] {
  background: var(--surface);
  color: var(--text);
  border-color: var(--border);
}

/* The viewer's stage hosting a read-only Quill for rich reels: proportional
   type (no redaction-alignment concern beyond lengths, which redaction
   preserves), scrolling inside the .ql-editor, our own overlay caret. */
.stage.stage-rich {
  display: flex;
  flex-direction: column;
  padding: 0;
  white-space: normal;
  font-family: "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 1rem;
}
.stage-rich .ql-container {
  flex: 1;
  display: flex;
  flex-direction: column;
  position: relative;
  font-family: inherit;
  font-size: inherit;
  color: var(--text);
}
.stage-rich .ql-editor {
  flex: 1;
  overflow-y: auto;
  line-height: 1.75;
  padding: 1.2rem 1.4rem 5rem;
}
.rich-caret {
  position: absolute;
  width: 2px;
  background: var(--accent);
  animation: blink 1s steps(1) infinite;
  pointer-events: none;
}
