/* Verbaliser design system — tokens, primitives, then views. */

/* Palette from the Verbaliser design tokens — Deep Mint primary on warm bone
   neutrals (light) / Deep Forest (dark). --accent* carries the brand primary;
   --accent-soft is primary-subtle, the marker for AI-generated content. */
:root {
  color-scheme: light dark;

  --bg: #f5f3ee;
  --surface: #fdfcf8;
  --surface-2: #ece7dc;
  --border: #ded9cc;
  --border-strong: #cabfa8;
  --text: #131a17;
  --text-muted: #71786f;
  --text-faint: #95998f;
  --accent: #0f7a63;
  --accent-hover: #0a5f4c;
  --accent-soft: #d7f5e9;
  --on-accent: #ffffff;
  --danger: #c0392f;
  --danger-soft: #f6e1df;
  --success: #2e8b57;
  --shadow-sm: 0 1px 2px rgb(19 26 23 / 0.06);
  --shadow-md: 0 4px 16px rgb(19 26 23 / 0.08);
  --shadow-lg: 0 12px 40px rgb(19 26 23 / 0.18);
  --radius: 14px;
  --radius-sm: 10px;
  --font-ui: "Plus Jakarta Sans", -apple-system, "Segoe UI", system-ui, Roboto, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "Cascadia Code", Consolas, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #131c19;
    --surface: #1a2521;
    --surface-2: #24322d;
    --border: #2b3a34;
    --border-strong: #3a4a43;
    --text: #eef0e9;
    --text-muted: #96a39a;
    --text-faint: #75837a;
    --accent: #17a184;
    --accent-hover: #1fbb99;
    --accent-soft: #1e2f29;
    --on-accent: #04120e;
    --danger: #d9584e;
    --danger-soft: #33191b;
    --success: #4fbfa0;
    --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.4);
    --shadow-md: 0 4px 16px rgb(0 0 0 / 0.45);
    --shadow-lg: 0 12px 40px rgb(0 0 0 / 0.6);
  }
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font-ui);
  font-size: 14.5px;
  line-height: 1.5;
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { margin: 0; font-weight: 650; }

/* ---------- primitives ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  padding: 0.55rem 1rem;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  font: inherit;
  font-weight: 550;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
  white-space: nowrap;
  text-decoration: none; /* .btn is also used on <a> */
}
.btn:disabled { opacity: 0.55; cursor: default; }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.btn-primary { background: var(--accent); color: var(--on-accent); }
.btn-primary:hover:not(:disabled) { background: var(--accent-hover); }

.btn-secondary {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--text);
}
.btn-secondary:hover:not(:disabled) { background: var(--surface-2); }

.btn-ghost { background: transparent; color: var(--text-muted); }
.btn-ghost:hover:not(:disabled) { background: var(--surface-2); color: var(--text); }

.btn-danger { background: transparent; border-color: transparent; color: var(--danger); }
.btn-danger:hover:not(:disabled) { background: var(--danger-soft); }

.btn-block { width: 100%; }

.button-row { display: flex; gap: 0.6rem; }
.button-row .btn-primary { flex: 1; }

.input, .textarea {
  width: 100%;
  padding: 0.55rem 0.75rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  transition: border-color 0.12s, box-shadow 0.12s;
}
.input::placeholder, .textarea::placeholder { color: var(--text-faint); }
.input:focus, .textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
.textarea { resize: vertical; min-height: 10rem; line-height: 1.55; }
.textarea-tall { min-height: 7.5rem; }
.clipboard-helper { position: fixed; opacity: 0; }

.field { margin-bottom: 1.1rem; }
.field label {
  display: block;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-muted);
  margin-bottom: 0.35rem;
  letter-spacing: 0.01em;
}
.field .hint { margin: 0.35rem 0 0; font-size: 0.8rem; color: var(--text-faint); }

.form-error {
  display: none;
  margin: 0.75rem 0 0;
  padding: 0.55rem 0.8rem;
  border-radius: var(--radius-sm);
  background: var(--danger-soft);
  color: var(--danger);
  font-size: 0.85rem;
}
.form-error.visible { display: block; }

.form-notice {
  display: none;
  margin: 0.75rem 0 0;
  padding: 0.55rem 0.8rem;
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  color: var(--text-muted);
  font-size: 0.85rem;
}
.form-notice.visible { display: block; }

/* ---------- brand ---------- */

/* Gap is a third of the mark's width (logo rule 05). The inlined mark's
   viewBox is cropped to the ink, so this 8px is the whole gap — there is no
   padding baked into the artwork adding to it. */
.brand { display: inline-flex; align-items: center; gap: 0.5rem; }
/* The mark never sits inside another shape (logo rule 04), so there is no
   tile and no radius. The inlined mark is the mono cut: it takes var(--accent)
   through currentColor and its bars are a true cutout, so the surface behind
   shows through in either theme. Its viewBox ("6 -1 36 44") is symmetric about
   the bubble body, so align-items: center lines the body up with the text and
   lets the tail hang below — sizing off the full 48 box put the body high. */
.brand-mark {
  display: inline-flex;
  color: var(--accent);
  flex: none;
}
.brand-mark svg { display: block; height: 34px; width: 27.82px; }
.brand-name { font-weight: 700; font-size: 1.05rem; letter-spacing: -0.01em; }

/* ---------- app shell ---------- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 10;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
.topbar-inner {
  max-width: 1080px;
  margin: 0 auto;
  padding: 0.7rem 1.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
/* The landing header's Sign in button is the only .btn that sits directly in
   a topbar — the app header's nav tabs/user-chip aren't .btn at all — so left
   at the base .btn size it stands taller than its app-header counterpart and
   the two topbars end up different heights. Trim it to match; the 34px brand
   mark then governs both bars equally. */
.topbar .btn {
  padding: 0.4rem 0.9rem;
  font-size: 0.92rem;
}
/* Account menu — the avatar button in the topbar and the panel it drops. */
.user-menu { position: relative; flex: none; }
.user-chip {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.2rem 0.3rem 0.2rem 0.2rem;
  background: none;
  border: 1px solid transparent;
  border-radius: 999px;
  color: var(--text-muted);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.user-chip:hover, .user-chip[aria-expanded="true"] { background: var(--surface-2); border-color: var(--border); }
.user-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.user-chip .avatar {
  width: 1.75rem; height: 1.75rem; border-radius: 999px;
  display: grid; place-items: center;
  background: var(--accent-soft); color: var(--accent);
  font-size: 0.8rem; font-weight: 700; text-transform: uppercase;
}
/* The email is collapsed to nothing until the chip is hovered, focused or
   open, at which point the chip widens rightwards to reveal it. The negative
   margin cancels one of the flex gaps so the collapsed chip stays a tidy
   avatar + chevron. */
.user-chip .email {
  max-width: 0;
  margin-left: -0.3rem;
  opacity: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: 0.85rem;
  color: var(--text);
  transition: max-width 0.18s ease, margin-left 0.18s ease, opacity 0.14s ease;
}
/* Only widen when the topbar can actually spare the room. Brand + divider +
   both nav tabs + the collapsed chip need ~570px; anything narrower and the
   reveal would shove the nav out of the bar, so the chip stays an avatar and
   the email is read from the menu instead (see .menu-email). */
@media (min-width: 900px) {
  .user-chip:hover .email,
  .user-chip:focus-visible .email,
  .user-chip[aria-expanded="true"] .email {
    max-width: min(16rem, 28vw);
    margin-left: 0;
    opacity: 1;
  }
}
@media (prefers-reduced-motion: reduce) {
  .user-chip .email { transition: none; }
}
.user-chip .chev { transition: transform 0.12s; }
.user-chip[aria-expanded="true"] .chev { transform: rotate(180deg); }

.menu {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 30;
  min-width: 13rem; padding: 0.3rem;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-lg);
}
.menu-sep { height: 1px; margin: 0.3rem 0.25rem; background: var(--border); }
/* The signed-in email, in the menu instead of the chip, for the widths where
   the chip can't widen. Hidden once the chip reveal is back in play. */
.menu-email {
  padding: 0.4rem 0.6rem 0.5rem;
  margin-bottom: 0.3rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.82rem;
  color: var(--text-muted);
  overflow-wrap: anywhere;
}
@media (min-width: 900px) { .menu-email { display: none; } }
.menu-item {
  display: block; width: 100%; text-align: left;
  padding: 0.45rem 0.6rem; border: none; border-radius: 8px;
  background: none; font: inherit; font-size: 0.88rem; color: var(--text);
  cursor: pointer;
}
.menu-item:hover { background: var(--surface-2); }
.menu-item.active { color: var(--accent); background: var(--accent-soft); }
.menu-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* In-topbar navigation — the primary page tabs live up in the header rather
   than in a strip under it. The tabs never wrap or ellipsize (a half-read
   "Power BI Integr…" is worse than a scroll), so min-width: 0 + overflow-x
   lets the nav scroll inside itself on a narrow screen instead of forcing the
   whole document wider than the viewport. */
.topnav {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  flex: 1 1 auto;
  min-width: 0;
  margin: 0 0.5rem;
  overflow-x: auto;
  scrollbar-width: none;
}
.topnav::-webkit-scrollbar { display: none; }
.nav-tab {
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  padding: 0.4rem 0.7rem;
  font: inherit;
  font-weight: 550;
  font-size: 0.92rem;
  color: var(--text-muted);
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.12s, color 0.12s;
}
.nav-tab:hover { color: var(--text); background: var(--surface-2); }
.nav-tab.active { color: var(--accent); background: var(--accent-soft); }
.nav-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.topbar-divider {
  width: 1px;
  align-self: stretch;
  min-height: 1.4rem;
  background: var(--border);
  flex: none;
}

/* Below ~620px the bar can't hold brand + wordmark + divider + both tabs +
   the chip. The mark alone still says whose app this is, and the tabs are what
   you actually need to reach, so the wordmark and divider go first. */
@media (max-width: 620px) {
  .topbar-inner { padding: 0.6rem 1rem; gap: 0.5rem; }
  .brand { flex: none; }
  /* Scoped to the app shell — the landing bar is only brand + Sign in, so it
     keeps its wordmark at every width. */
  #app-view .brand-name, .topbar-divider { display: none; }
  .topnav { margin: 0; }
}

.main {
  max-width: 1080px;
  margin: 0 auto;
  padding: 2rem 1.5rem 4rem;
}

.page-head {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}
.page-head h1 { font-size: 1.35rem; letter-spacing: -0.01em; }
.count-badge {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-muted);
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.1rem 0.6rem;
}
.page-head .spacer { flex: 1; }

.usage-row {
  display: flex;
  gap: 0.9rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}
.usage-tile {
  flex: 1 1 180px;
  padding: 0.9rem 1.1rem;
}
.usage-tile .label { display: block; font-size: 0.78rem; color: var(--text-faint); }
.usage-tile .value { display: block; margin-top: 0.2rem; font-size: 1.5rem; font-weight: 600; letter-spacing: -0.01em; }

.search-box { position: relative; }
.search-box .input { width: 240px; padding-left: 2.1rem; }
.search-box svg {
  position: absolute;
  left: 0.65rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-faint);
  pointer-events: none;
}

/* ---------- prompt cards ---------- */

/* Three across, three deep, then it scrolls inside itself. Fixed rather than
   auto-fill because the panel underneath (Power BI setup) has to stay within
   reach: an account with thirty narrations would otherwise push it a couple of
   screens down. --prompt-card-h is the card's own height — every card is the
   same size (one-line title, 3-line clamped preview, footer), so three rows and
   two gaps is an exact cap.
   A scroll box clips whatever leaves it, and a card's hover shadow (--shadow-md,
   16px of blur) leaves it — so the grid is padded by --prompt-bleed and the
   wrapper pulls that padding back out with a matching negative margin. The
   cards stay lined up with the toolbar above; the shadow gets somewhere to
   land. The bleed is added back into the cap so it's still three rows.
   Cards also draw their focus ring inset (below) for the same reason. */
.prompt-grid-wrap {
  /* Card content lands at ~176px (title + three clamped lines + footer); the
     extra few pixels are slack, absorbed by the footer's margin-top:auto
     rather than clipped. */
  --prompt-card-h: 11.25rem;
  --prompt-rows: 3;
  --prompt-gap: 1rem;
  --prompt-bleed: 12px;
  position: relative;
  margin: calc(-1 * var(--prompt-bleed));
}
.prompt-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--prompt-gap);
  padding: var(--prompt-bleed);
  max-height: calc(
    var(--prompt-rows) * var(--prompt-card-h) +
    (var(--prompt-rows) - 1) * var(--prompt-gap) +
    2 * var(--prompt-bleed)
  );
  /* No overscroll-behavior: with fewer than three rows there is nothing to
     scroll, and containing the chain there would swallow the wheel and leave
     the page sitting still under the cursor. */
  overflow-y: auto;
  scroll-padding: var(--prompt-bleed);
}

/* The fade that says the list keeps going. Sits over the last visible row,
   only while there's something scrolled out of view and you aren't already at
   the bottom (.has-more, set in prompts.js). */
.prompt-grid-wrap::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3rem;
  border-radius: 0 0 var(--radius) var(--radius);
  background: linear-gradient(to bottom, transparent, var(--bg) 85%);
  opacity: 0;
  transition: opacity 0.15s;
  pointer-events: none;
}
.prompt-grid-wrap.has-more::after { opacity: 1; }

/* Non-card contents of the grid — the empty state and the "nothing matches"
   line — are one thing, not a column of one, and they're never capped
   (prompts.js sets .unbounded). */
.prompt-grid > .empty-state,
.prompt-grid > .calls-badge { grid-column: 1 / -1; }
.prompt-grid.unbounded { max-height: none; overflow: visible; }

@media (max-width: 900px) {
  .prompt-grid,
  .prompt-grid .group-cards { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) {
  .prompt-grid,
  .prompt-grid .group-cards { grid-template-columns: 1fr; }
}

/* Category grouping: the header and each group's card block span the full
   width of the grid; the cards themselves live in a nested grid that reuses
   the same responsive column template. Only rendered when there's more than
   one group — a single-group list stays a plain flat grid of cards. */
.prompt-grid .group-head {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.4rem;
}
.prompt-grid .group-head:first-child { margin-top: 0; }
.prompt-grid .group-name {
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.prompt-grid .group-count {
  font-size: 0.72rem;
  color: var(--text-faint);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.05rem 0.45rem;
}
.prompt-grid .group-cards {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--prompt-gap);
}

/* Toolbar group filter — sits next to the search box, sized to its content
   rather than stretching like a form input. */
.group-filter {
  width: 11rem;
  flex: none;
}

/* Shared card surface — background/border, and usually radius + shadow too,
   reused by usage tiles, prompt cards, billing cards, and example cards. */
.usage-tile,
.prompt-card,
.billing-plan-card,
.billing-report-card,
.example-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}

/* Fixed height, not content height: it's what makes the grid's three-row cap
   land exactly on a row boundary instead of slicing a card in half. Nothing in
   here grows anyway — the title is one ellipsised line and the preview is
   clamped to three. */
.prompt-card {
  display: flex;
  flex-direction: column;
  height: var(--prompt-card-h, 11.25rem);
  overflow: hidden;
  text-align: left;
  font: inherit;
  color: inherit;
  padding: 1.1rem 1.2rem;
  cursor: pointer;
  transition: border-color 0.12s, box-shadow 0.12s;
}
/* Border and shadow only — no lift. The card sits in a scroll box now, so a
   card that moves on hover moves against a hard clipping edge, and mid-scroll
   there is no padding above it to move into. */
.prompt-card:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-md);
}
/* Inset, not offset: the cards live in a scroll container now, and a ring
   drawn outside the card is clipped at the edges (and would push the container
   into scrolling sideways). */
.prompt-card:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.prompt-card .card-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.4rem;
}
.prompt-card .name {
  font-weight: 650;
  font-size: 0.98rem;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.prompt-card .when { font-size: 0.76rem; color: var(--text-faint); flex: none; }

/* The card's group, sitting where "updated" used to — the group is what tells
   you which card you're looking at; the timestamp is footnote detail. */
.prompt-card .card-group {
  flex: none;
  max-width: 45%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.72rem;
  color: var(--text-muted);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.1rem 0.5rem;
}

/* Three whole lines, then an ellipsis. The height has to be the clamp's own
   height — 3 lines x line-height 1.5 = 4.5em — or the card's fixed height
   shrinks this flex item below the text it's showing and slices the last line
   in half. flex: none for the same reason. */
.prompt-card .preview {
  color: var(--text-muted);
  font-size: 0.86rem;
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
  flex: none;
  height: 4.5em;
  margin-bottom: 0.9rem;
}

.prompt-card .card-foot {
  margin-top: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding-top: 0.8rem;
  border-top: 1px solid var(--border);
}

.calls-badge { font-size: 0.76rem; color: var(--text-faint); flex: none; }

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: none;
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text-muted);
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.icon-btn:hover { background: var(--surface-2); color: var(--text); }
.icon-btn.copied { color: var(--success); border-color: var(--success); }
.icon-btn-lg { width: 38px; height: 38px; }

/* ---------- Power BI panel (under the narration list) ---------- */

/* A second heading level for a block that shares a page with something else —
   smaller than a .page-head h1, same toolbar behaviour. */
.section-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin: 2.25rem 0 1rem;
}
.section-head h2 { font-size: 1.05rem; letter-spacing: -0.01em; }
.section-head .spacer { flex: 1; }

.pbi-section .billing-plan-card { margin-bottom: 0; }
/* Out-specifies `.billing-plan-card .hint { margin: 0 }`, which assumes the
   card holds a single trailing hint. */
.pbi-section .pbi-lede { margin: 0 0 1rem; }

/* Two columns, so four one-line fields cost two rows instead of four — the
   whole point of moving this under the list was to keep it short. */
.pbi-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.9rem 1.25rem;
  margin-bottom: 1rem;
}
@media (max-width: 620px) {
  .pbi-fields { grid-template-columns: 1fr; }
}

.pbi-section #pbi-generate-btn { margin-top: 1rem; }

/* ---------- empty state ---------- */

.empty-state {
  text-align: center;
  padding: 4rem 1rem;
  border: 1.5px dashed var(--border-strong);
  border-radius: 14px;
}
.empty-state .glyph { color: var(--text-faint); margin-bottom: 0.75rem; }
.empty-state h2 { font-size: 1.05rem; margin-bottom: 0.4rem; }
.empty-state p { color: var(--text-muted); max-width: 46ch; margin: 0 auto 1.5rem; font-size: 0.9rem; }

/* First-run steps: one tile per wizard step, numbered with the same chip the
   wizard's own step rail uses (.wiz-steps li span) so the three of them are
   recognisably the same three. Left-aligned inside a centred empty state —
   they're read, not glanced at. */
.empty-steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.9rem;
  max-width: 46rem;
  margin: 0 auto 1.75rem;
  padding: 0;
  list-style: none;
  text-align: left;
}
.empty-steps li {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 0.45rem 0.55rem;
  padding: 0.9rem 1rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.empty-steps .step-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.4rem;
  height: 1.4rem;
  border-radius: 999px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.75rem;
  font-weight: 600;
}
.empty-steps b { font-size: 0.88rem; }
.empty-steps .step-body {
  grid-column: 1 / -1;
  color: var(--text-muted);
  font-size: 0.82rem;
  line-height: 1.5;
}

.empty-cta { display: flex; gap: 0.6rem; justify-content: center; flex-wrap: wrap; }

@media (max-width: 620px) {
  .empty-steps { grid-template-columns: 1fr; }
}

/* ---------- billing ---------- */

.billing-plan-card {
  padding: 1.1rem 1.2rem;
  margin-bottom: 1.5rem;
}
.billing-plan-card .stats-grid { margin-bottom: 0.6rem; }
.billing-plan-card .hint { margin: 0; font-size: 0.82rem; color: var(--text-faint); }

.billing-reports {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 1rem;
}
.billing-report-card {
  padding: 1.1rem 1.2rem;
}
.billing-report-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  margin-bottom: 0.8rem;
}
.billing-report-head .name {
  font-weight: 650;
  font-size: 0.95rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.billing-report-card .limit-bar { margin-bottom: 0; }
/* On the billing page the card is just the name + tier, with no bar under it. */
.billing-report-head:last-child { margin-bottom: 0; }

.tier-badge { flex: none; }
.tier-badge.tier-paid { background: var(--accent-soft); border-color: transparent; color: var(--accent); }
/* Narration states. `active` has no badge at all — the common case shouldn't
   carry decoration; a badge here means "this one isn't earning its keep". */
.tier-badge.tier-paused { background: var(--accent-soft); border-color: transparent; color: var(--muted); }
.tier-badge.tier-draft { background: transparent; border-color: var(--border); color: var(--muted); }

/* The account's shared allowance sits on the usage page and reuses limitBar
   (ui.js), which sets its width through the CSSOM — an inline style attribute
   would be blocked by this app's style-src CSP and the bar would render empty. */
.usage-pool { margin-bottom: 1.5rem; }

/* Card on file + invoice history.

   These use their own `pay-note` rather than the shared `.hint`, because
   `.billing-plan-card .hint` sets margin:0 — written when this card held one
   line of text, it made every paragraph and button here sit flush against its
   neighbour with no separation at all. */
.pay-section { margin-top: 1.75rem; padding-top: 1.35rem; border-top: 1px solid var(--border); }
/* Same contents, but standalone in its own card — no divider above it. */
.pay-block { margin: 0; }
.billing-invoices { margin-top: 1rem; }
.pay-title {
  margin: 0 0 0.75rem;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--text-muted);
}
.pay-note { margin: 0.65rem 0 0; font-size: 0.85rem; color: var(--text-faint); line-height: 1.5; }
.pay-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 1rem; }

/* The card itself, as a discrete object rather than a run of text. */
.pay-card {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.55rem;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.pay-brand { font-weight: 700; letter-spacing: 0.04em; font-size: 0.8rem; }
.pay-digits { font-family: var(--font-mono); font-size: 0.95rem; }
.pay-exp { margin-left: auto; font-size: 0.82rem; color: var(--text-muted); }

.invoice-list { border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.invoice-row {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 0.6rem 0.9rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.9rem;
}
.invoice-row:last-child { border-bottom: none; }
.invoice-date { min-width: 7rem; color: var(--text-muted); }
.invoice-amount { font-weight: 600; min-width: 5rem; }
.invoice-status { text-transform: capitalize; }
.invoice-status.status-paid { background: var(--accent-soft); border-color: transparent; color: var(--accent); }
.invoice-links { margin-left: auto; display: flex; gap: 0.9rem; flex: none; }
.invoice-link { color: var(--accent); text-decoration: none; font-weight: 550; }
.invoice-link:hover { text-decoration: underline; }

@media (max-width: 560px) {
  /* Stack rather than squeeze: the links wrap under the amount on a phone. */
  .invoice-row { flex-wrap: wrap; gap: 0.5rem 0.9rem; }
  .invoice-links { margin-left: 0; width: 100%; }
}

.notice {
  margin-top: 0.75rem;
  padding: 0.6rem 0.75rem;
  border-radius: var(--radius-sm, 6px);
  font-size: 0.85rem;
  line-height: 1.45;
}
.notice-warn { background: var(--accent-soft); color: var(--text); }


/* ---------- drawer ---------- */

.drawer-backdrop {
  position: fixed;
  inset: 0;
  background: rgb(10 12 16 / 0.45);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s;
  z-index: 40;
}
.drawer-backdrop.open { opacity: 1; pointer-events: auto; }

.drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(480px, 100vw);
  background: var(--surface);
  border-left: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  transform: translateX(102%);
  transition: transform 0.22s cubic-bezier(0.32, 0.72, 0.28, 1);
  z-index: 50;
  display: flex;
  flex-direction: column;
}
.drawer.open { transform: translateX(0); }

/* ---------- auth modal ---------- */

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgb(10 12 16 / 0.45);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s;
  z-index: 60;
}
.modal-backdrop.open { opacity: 1; pointer-events: auto; }

.modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -46%) scale(0.97);
  width: min(400px, calc(100vw - 2.5rem));
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s, transform 0.18s;
  z-index: 61;
  display: flex;
  flex-direction: column;
}
.modal.open { opacity: 1; pointer-events: auto; transform: translate(-50%, -50%) scale(1); }

/* These MUST come after `.modal` above. Both are single-class selectors, so
   specificity ties and source order decides — declared earlier, `.modal`'s
   opacity:0 / pointer-events:none / width won, and the checkout modal mounted
   invisible and unclickable. Keep this block below `.modal`.

   Checkout is deliberately wide: Stripe's embedded form switches to a
   two-column layout (order summary beside the payment fields) once it has the
   room, and at the app's default 400px modal width it stacks into a very tall
   single column. */
.checkout-modal { width: min(960px, calc(100vw - 2rem)); }
.card-modal { width: min(460px, calc(100vw - 2rem)); }

.checkout-modal, .card-modal {
  max-height: calc(100vh - 2rem);
  padding: 0;              /* the header and body carry their own */
  overflow: hidden;        /* clip Stripe's white iframe to the panel radius */
}

/* A real row for the close button. It used to be absolutely positioned over
   the panel, which put it on top of Stripe's iframe and covered the corner of
   the form — the iframe is a separate document, so nothing inside it can move
   out of the way. */
.checkout-head {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.55rem 0.6rem 0.55rem 1.1rem;
  border-bottom: 1px solid var(--border);
}
.checkout-head:has(> :only-child) { justify-content: flex-end; } /* close on its own */

.checkout-body {
  flex: 1 1 auto;
  min-height: 0;           /* lets the flex child actually scroll */
  overflow-y: auto;
}
.card-modal-body { padding: 1.1rem; }
.card-modal-title { margin: 0; font-size: 1rem; }
.card-modal-save { margin-top: 1rem; }

/* The cost gate: shown *before* the wizard opens, because building a narration
   means wiring up Power BI at the other end and the price is no use to someone
   part-way through that. */
.cost-modal { padding: 1.5rem; text-align: left; }
.cost-title { margin-bottom: 0.5rem; font-size: 1.05rem; }
.cost-body { margin: 0 0 1.25rem; }
.cost-actions { justify-content: flex-end; }
.cost-actions .btn-primary { flex: none; }

.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.1rem 1.5rem;
  border-bottom: 1px solid var(--border);
}
.modal-head h2 { font-size: 1.05rem; }

.modal-body { padding: 1.5rem; }

/* Only the pane named by #auth-modal's [data-pane] is shown. */
.modal [data-pane-content] { display: none; }
#auth-modal[data-pane="choose"] [data-pane-content="choose"],
#auth-modal[data-pane="code"] [data-pane-content="code"] { display: block; }

.auth-divider {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1.1rem 0;
  color: var(--text-faint);
  font-size: 0.8rem;
}
.auth-divider::before, .auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

.drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.1rem 1.5rem;
  border-bottom: 1px solid var(--border);
}
.drawer-head h2 { font-size: 1.05rem; }

.drawer-body { flex: 1; overflow-y: auto; padding: 1.5rem; }

.url-section {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.9rem 1rem;
  margin-bottom: 1.1rem;
}
.url-section .label-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.4rem;
}
.url-section .label-row label { font-size: 0.82rem; font-weight: 600; color: var(--text-muted); }
.url-row { display: flex; gap: 0.5rem; }
.url-row .input { font-family: var(--font-mono); font-size: 0.78rem; background: var(--surface); }
.url-section .hint { margin: 0.5rem 0 0; font-size: 0.78rem; color: var(--text-faint); }
.url-section .hint-warn { color: var(--danger); }

.stats-grid {
  display: grid;
  /* auto-fit rather than a fixed pair: the billing card carries four stats and
     forcing them into two columns made a tall block on a wide page. */
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.9rem 1.25rem;
  margin-bottom: 0;
}
.stat { font-size: 0.8rem; color: var(--text-faint); }
.stat b { display: block; color: var(--text-muted); font-weight: 600; }

.limit-bar {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.8rem 0.9rem;
  margin-bottom: 1.1rem;
}
.limit-bar-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 0.5rem;
}
.limit-bar-head .label { font-size: 0.78rem; font-weight: 600; color: var(--text-muted); }
.limit-count { font-size: 0.78rem; font-variant-numeric: tabular-nums; color: var(--text-faint); }
.limit-count.at-limit { color: var(--danger); font-weight: 600; }
.limit-track {
  height: 6px;
  border-radius: 999px;
  background: var(--border);
  overflow: hidden;
}
.limit-fill { height: 100%; border-radius: 999px; background: var(--accent); transition: width 0.2s ease; }
.limit-fill.at-limit { background: var(--danger); }
.limit-note { margin: 0.5rem 0 0; font-size: 0.76rem; color: var(--danger); }

.drawer-foot {
  display: flex;
  gap: 0.6rem;
  padding: 1rem 1.5rem;
  border-top: 1px solid var(--border);
}
.drawer-foot .grow { flex: 1; }

/* ---------- toasts ---------- */

.toast-stack {
  position: fixed;
  bottom: 1.25rem;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  z-index: 100;
  pointer-events: none;
}
.toast {
  background: var(--text);
  color: var(--bg);
  border-radius: 999px;
  padding: 0.55rem 1.1rem;
  font-size: 0.85rem;
  font-weight: 550;
  box-shadow: var(--shadow-md);
  animation: toast-in 0.2s ease-out;
}
.toast.error { background: var(--danger); color: #fff; }
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ---------- landing / marketing ---------- */

.btn-lg { padding: 0.75rem 1.4rem; font-size: 1rem; border-radius: var(--radius); }

.hero-notice { max-width: 760px; margin: 0 auto 1.5rem; }

.hero {
  max-width: 720px;
  margin: 2.5rem auto 4rem;
  text-align: center;
}
.hero-eyebrow {
  display: inline-block;
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 999px;
  padding: 0.25rem 0.75rem;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: 1.1rem;
}
.hero h1 {
  font-size: clamp(1.9rem, 4vw, 2.75rem);
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin-bottom: 1.1rem;
}
.hero-sub {
  color: var(--text-muted);
  font-size: 1.05rem;
  line-height: 1.65;
  max-width: 58ch;
  margin: 0 auto;
}
.hero-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin: 1.9rem 0 1.1rem;
}
.hero-trust { color: var(--text-faint); font-size: 0.82rem; text-align: center; }
#examples .hero-trust { margin-top: 2rem; }

.section { margin-bottom: 4rem; }
.section-title {
  font-size: 1.4rem;
  letter-spacing: -0.01em;
  text-align: center;
  margin-bottom: 0.5rem;
}
.section-sub {
  color: var(--text-muted);
  font-size: 0.95rem;
  text-align: center;
  max-width: 52ch;
  margin: 0 auto 2rem;
}

.example-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1.25rem;
}
.example-card {
  overflow: hidden;
}
.example-img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
}
.example-body { padding: 1rem 1.15rem 1.2rem; }
.example-tag {
  display: inline-block;
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 999px;
  padding: 0.15rem 0.6rem;
  font-size: 0.74rem;
  font-weight: 700;
  margin-bottom: 0.6rem;
}
.example-quote {
  margin: 0;
  color: var(--text-muted);
  font-size: 0.88rem;
  line-height: 1.55;
}

.final-cta {
  text-align: center;
  background: var(--accent-soft);
  border-radius: 20px;
  padding: 3rem 1.5rem;
}
.final-cta h2 { font-size: 1.6rem; letter-spacing: -0.01em; margin-bottom: 0.6rem; }
.final-cta .section-sub { margin-bottom: 1.6rem; }

.landing-footer { border-top: 1px solid var(--border); margin-top: 1rem; }
.landing-footer-inner {
  max-width: 1080px;
  margin: 0 auto;
  padding: 1.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  color: var(--text-faint);
  font-size: 0.82rem;
}

/* Segmented slider — the Length picker in the editor drawer. Equal-width
   columns fill the row regardless of how many options there are. */
.segmented {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 2px;
  padding: 3px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface-2);
}
.segmented button {
  padding: 0.45rem 0.4rem;
  border: none;
  border-radius: calc(var(--radius-sm) - 3px);
  background: transparent;
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  white-space: nowrap;
  text-align: center;
  transition: background 0.12s, color 0.12s;
}
.segmented button:hover { color: var(--text); }
.segmented button.active { background: var(--accent); color: var(--on-accent); }

/* Label + hover-info affordance, replacing the block hint paragraphs. */
.label-row { display: flex; align-items: center; gap: 0.3rem; margin-bottom: 0.35rem; }
.label-row label { margin-bottom: 0; }

.info {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 0.95rem;
  height: 0.95rem;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: none;
  color: var(--text-faint);
  cursor: help;
}
.info:hover, .info:focus-visible { color: var(--accent); outline: none; }
/* The bubble text lives inline as a hidden data source; JS renders the
   visible tooltip as a fixed-position .info-tip on <body>, so the scrolling
   drawer never clips it. It flips above/below the icon depending on room. */
.info-pop { display: none; }

.info-tip {
  position: fixed;
  z-index: 120;
  width: max-content;
  max-width: 15rem;
  padding: 0.5rem 0.65rem;
  border-radius: var(--radius-sm);
  background: var(--text);
  color: var(--surface);
  font-size: 0.76rem;
  font-weight: 500;
  line-height: 1.45;
  text-align: left;
  pointer-events: none;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.22);
}
.info-tip[hidden] { display: none; }
.info-tip b { font-weight: 700; color: var(--surface); }
/* A tip carrying a short brief rather than a sentence (the wizard's "what are
   you making?"). Same bubble, just given room to breathe — the class rides
   along on the .info-pop and is copied onto the tip when it opens. */
.info-tip.info-pop-wide { max-width: 22rem; }
.info-tip ul { margin: 0.3rem 0 0.6rem; padding-left: 1rem; }
.info-tip ul:last-child { margin-bottom: 0; }
.info-tip li { margin-bottom: 0.25rem; }
.info-tip li:last-child { margin-bottom: 0; }
.info-tip::after {
  content: "";
  position: absolute;
  left: var(--arrow-x, 50%);
  transform: translateX(-50%);
  border: 5px solid transparent;
}
.info-tip[data-placement="top"]::after { top: 100%; border-top-color: var(--text); }
.info-tip[data-placement="bottom"]::after { bottom: 100%; border-bottom-color: var(--text); }

.btn.disabled { opacity: 0.55; cursor: not-allowed; }

.hidden { display: none !important; }

/* skeleton shimmer while the list loads */
.skeleton {
  height: 150px;
  border-radius: var(--radius);
  background: linear-gradient(100deg, var(--surface) 40%, var(--surface-2) 50%, var(--surface) 60%);
  background-size: 200% 100%;
  animation: shimmer 1.2s infinite;
  border: 1px solid var(--border);
}
@keyframes shimmer {
  to { background-position: -200% 0; }
}

/* Several blocks standing in for a page that's still fetching (ui.js's
   showSkeletons). `bare` is the in-card variant: a bordered skeleton inside an
   already-bordered card reads as two nested boxes. */
.skeleton-stack { display: grid; gap: 0.7rem; }
/* A step darker than the card it sits in — the standalone skeleton is
   --surface, which on a --surface card is invisible between shimmer passes. */
.skeleton-stack.bare .skeleton {
  border: 0;
  border-radius: 8px;
  background-image: linear-gradient(100deg, var(--surface-2) 40%, var(--border-strong) 50%, var(--surface-2) 60%);
}

@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; }
}

/* ---------- boot splash ----------
   Both #landing-view and #app-view start hidden, because which one to show
   isn't known until /api/me answers — so without this the first second or two
   of every visit is an empty page. Shown by the markup (no JS needed, so it
   paints on the first frame) and taken down by app.js the moment either view
   is revealed. */
.boot {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.15rem;
  background: var(--bg);
}
.boot-mark { width: 46px; height: 46px; color: var(--accent); }
.boot-mark svg { width: 100%; height: 100%; display: block; }
/* Indeterminate, not a progress bar: nothing here knows how far along it is,
   and a bar that fills to 90% and waits is a lie about the wait. */
.boot-track {
  position: relative;
  overflow: hidden;
  width: 130px;
  height: 3px;
  border-radius: 999px;
  background: var(--surface-2);
}
.boot-track span {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 40%;
  border-radius: inherit;
  background: var(--accent);
  animation: boot-slide 1.15s ease-in-out infinite;
}
@keyframes boot-slide {
  from { transform: translateX(-110%); }
  to { transform: translateX(260%); }
}
.boot.fading { opacity: 0; transition: opacity 0.18s ease; pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  .boot-track span { animation: none; width: 100%; opacity: 0.45; }
  .boot.fading { transition: none; }
}

/* ---------- legal pages (terms / privacy) ---------- */
.legal { max-width: 68ch; margin: 0 auto; }
.legal h1 { font-size: 1.75rem; letter-spacing: -0.02em; margin-bottom: 0.4rem; }
.legal .legal-updated { color: var(--text-faint); font-size: 0.82rem; margin-bottom: 2.2rem; }
.legal h2 { font-size: 1.05rem; margin: 2.2rem 0 0.75rem; letter-spacing: -0.01em; }
.legal p { color: var(--text-muted); margin: 0 0 0.9rem; }
.legal ul, .legal ol { color: var(--text-muted); margin: 0 0 0.9rem; padding-left: 1.3rem; }
.legal li { margin-bottom: 0.4rem; }
.legal a { color: var(--accent); }
.legal strong { color: var(--text); }
.legal code { font-family: var(--font-mono); font-size: 0.88em; background: var(--surface-2); border-radius: 4px; padding: 0.05rem 0.3rem; }
.legal .placeholder { background: var(--accent-soft); color: var(--accent); border-radius: 4px; padding: 0.05rem 0.35rem; font-size: 0.92em; }

.brand-link { text-decoration: none; color: inherit; }

/* ---------- docs tab ---------- */
/* Nav down the left, one document in a card on the right. The document bodies
   are static fragments (public/docs-content/) rendered through .legal, so the
   typography is the same as the public terms/privacy pages — one voice for
   everything a customer's security reviewer reads. */
.docs-notice {
  margin: 0 0 1.25rem;
  padding: 0.8rem 1rem;
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-muted);
  font-size: 0.85rem;
}
.docs-notice b { color: var(--text); }
.docs-notice a { color: var(--accent); }

.docs-layout { display: grid; grid-template-columns: 236px minmax(0, 1fr); gap: 1.75rem; align-items: start; }
.docs-nav { position: sticky; top: 1.25rem; display: flex; flex-direction: column; gap: 0.1rem; }
.docs-link {
  padding: 0.45rem 0.6rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: 0.86rem;
  text-align: left;
  cursor: pointer;
}
.docs-link:hover { background: var(--surface-2); color: var(--text); }
.docs-link.active { color: var(--accent); background: var(--accent-soft); font-weight: 600; }
.docs-link:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.doc-panel {
  padding: 1.9rem 2.1rem 2.1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
/* .legal centres itself on a standalone page; inside the panel it's a column. */
.doc-body { max-width: 74ch; margin: 0; }
#doc-head .legal-updated { margin-bottom: 1.6rem; }
#doc-body > :first-child { margin-top: 0; }

@media (max-width: 820px) {
  .docs-layout { grid-template-columns: 1fr; gap: 1rem; }
  .docs-nav { position: static; }
  .doc-panel { padding: 1.25rem 1.25rem 1.5rem; }
}

/* ---------- support tab ---------- */
/* Self-service down the middle, the mailbox in the rail — the order someone
   should try things in, not the order that's easiest to write. */
.support-layout { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 1.75rem; align-items: start; }
.support-main { min-width: 0; }
.support-code {
  margin-right: 0.3rem;
  padding: 0.05rem 0.35rem;
  border-radius: 4px;
  background: var(--surface-2);
  color: var(--text-faint);
  font-family: var(--font-mono);
  font-size: 0.76rem;
}

.support-faq { display: flex; flex-direction: column; gap: 0.35rem; }
.faq {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.faq summary {
  padding: 0.7rem 1.1rem;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
  position: relative;
  padding-right: 2.2rem;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "";
  position: absolute;
  right: 1.1rem;
  top: 1.05rem;
  width: 7px;
  height: 7px;
  border-right: 2px solid var(--text-faint);
  border-bottom: 2px solid var(--text-faint);
  transform: rotate(45deg);
  transition: transform 0.12s;
}
.faq[open] summary::after { transform: rotate(-135deg); top: 1.25rem; }
.faq summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--radius-sm); }
.faq p { margin: 0; padding: 0 1.1rem 0.9rem; color: var(--text-muted); font-size: 0.88rem; max-width: 68ch; }
/* The message codes sit under the answer, quiet enough not to compete with it. */
.faq-codes { padding-top: 0.1rem !important; font-size: 0.8rem !important; color: var(--text-faint) !important; }
.faq-codes span { margin-right: 0.35rem; }

.support-aside { display: flex; flex-direction: column; gap: 0.9rem; position: sticky; top: 1.25rem; }
.support-card {
  padding: 1.1rem 1.25rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
.support-card h3 { font-size: 0.92rem; margin-bottom: 0.6rem; }
.support-card .hint { margin: 0.55rem 0 0; font-size: 0.8rem; color: var(--text-faint); }
.support-card .url-row { align-items: center; gap: 0.5rem; }
.support-address-line { flex: 1; margin: 0; min-width: 0; overflow-wrap: anywhere; font-size: 0.9rem; }
.support-list { margin: 0; padding-left: 1.1rem; color: var(--text-muted); font-size: 0.84rem; }
/* the list now trails the address inside one card — rule it off from the hours line */
.support-card .hint + .support-list { margin-top: 0.9rem; padding-top: 0.9rem; border-top: 1px solid var(--border); }
.support-list li { margin-bottom: 0.4rem; }
.support-list li:last-child { margin-bottom: 0; }
.support-list b { color: var(--text); }
.support-card a { color: var(--accent); overflow-wrap: anywhere; }
.support-alias-note { color: var(--text-faint); }
.support-card .placeholder { background: var(--accent-soft); color: var(--accent); border-radius: 4px; padding: 0.05rem 0.35rem; }

@media (max-width: 820px) {
  .support-layout { grid-template-columns: 1fr; gap: 1.1rem; }
  .support-aside { position: static; }
}

/* Print: the open document only — this is how a reviewer gets a PDF to attach
   to a vendor assessment. Everything else on the page is chrome. */
@media print {
  .topbar, .docs-nav, .page-head, .docs-notice, .toast-stack { display: none !important; }
  .doc-panel { border: 0; box-shadow: none; padding: 0; background: none; }
  .doc-body { max-width: none; }
  .docs-layout { display: block; }
}

/* ---- Drawer: captured-data preview loop (Stage A) ---- */
.preview-section { margin-top: 1.25rem; padding-top: 1.25rem; border-top: 1px solid var(--border); }
.btn-sm { padding: 0.35rem 0.7rem; font-size: 0.85rem; }
.sample-status { font-size: 0.9rem; color: var(--text-muted); display: flex; align-items: center; gap: 0.4rem; }
.sample-status.received { color: var(--success); }
/* The instruction that sits beside the animated "Waiting for data…" — it's
   the longer half, so let it wrap rather than stretch the row. */
.sample-status { flex-wrap: wrap; }
.sample-note { color: var(--text-faint); }
.sample-cols { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0.6rem 0; }
.sample-cols:empty { display: none; }
.col-chip {
  font-size: 0.78rem; padding: 0.15rem 0.5rem; border-radius: 999px;
  background: var(--surface-2); color: var(--text-muted); border: 1px solid var(--border);
}
.preview-actions { display: flex; align-items: center; gap: 0.6rem; margin-top: 0.4rem; }
.preview-output {
  margin-top: 0.85rem; padding: 0.85rem 1rem; border-radius: var(--radius-sm);
  background: var(--accent-soft); color: var(--text); white-space: pre-wrap;
  font-size: 0.92rem; line-height: 1.5; border: 1px solid var(--border);
}
.preview-output.loading { color: var(--text-muted); font-style: italic; }
.preview-output .preview-cached { display: block; margin-top: 0.5rem; font-size: 0.78rem; color: var(--text-faint); font-style: normal; }

/* ---- Drawer: activity + this narration's share of the allowance ----
   Ruled off from the preview above it, matching how the preview is ruled off
   from the settings: below the line is a record of what happened, not
   something you can change here. */
.activity-section { margin-top: 1.25rem; padding-top: 1.25rem; border-top: 1px solid var(--border); }
.activity-section > .label-row label { font-size: 0.82rem; font-weight: 600; color: var(--text-muted); }
.activity-section .stats-grid { margin-bottom: 0; }
.drawer-usage:not(:empty) { margin-top: 1rem; }
/* The bar already carries its own bottom margin for the billing page's stack;
   here it's the last thing in the drawer body. */
.drawer-usage .limit-bar { margin-bottom: 0.5rem; }
.drawer-usage .hint { margin: 0; font-size: 0.78rem; color: var(--text-faint); }

/* ---- Drawer: the composed instruction ----
   Read-only by design: it's the OUTPUT of the answers above, so it's styled as
   a quiet quoted block rather than an input someone might try to type into. */
.composed-prompt {
  padding: 0.8rem 0.95rem; border-radius: var(--radius-sm);
  border: 1px solid var(--border); border-left: 3px solid var(--border-strong);
  background: var(--surface-2); color: var(--text-muted);
  font-size: 0.88rem; line-height: 1.55; white-space: pre-wrap;
}
.composed-prompt.loading { font-style: italic; color: var(--text-faint); }
/* Stale: the description changed but hasn't been re-read, so what's on screen
   is no longer what a save would store. */
.composed-prompt.stale { opacity: 0.45; }

/* ---- Guided new-prompt wizard ---- */
.wiz {
  position: fixed; top: 50%; left: 50%;
  transform: translate(-50%, -46%) scale(0.97);
  width: min(680px, calc(100vw - 2.5rem)); max-height: calc(100vh - 3rem);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
  opacity: 0; pointer-events: none; transition: opacity 0.18s, transform 0.18s;
  z-index: 61; display: flex; flex-direction: column;
}
.wiz.open { opacity: 1; pointer-events: auto; transform: translate(-50%, -50%) scale(1); }
.wiz-head { display: flex; align-items: center; justify-content: space-between; padding: 1.1rem 1.5rem; border-bottom: 1px solid var(--border); }
.wiz-head h2 { font-size: 1.05rem; }
.wiz-steps { display: flex; gap: 0.5rem; padding: 0.9rem 1.5rem; border-bottom: 1px solid var(--border); list-style: none; flex-wrap: wrap; }
.wiz-steps li { display: flex; align-items: center; gap: 0.4rem; font-size: 0.82rem; color: var(--text-faint); }
.wiz-steps li span {
  display: inline-flex; align-items: center; justify-content: center; width: 1.4rem; height: 1.4rem;
  border-radius: 999px; background: var(--surface-2); color: var(--text-muted); font-size: 0.75rem; font-weight: 600;
}
.wiz-steps li.active { color: var(--text); font-weight: 600; }
.wiz-steps li.active span { background: var(--accent); color: var(--on-accent); }
.wiz-body { padding: 1.5rem; overflow-y: auto; }
.wiz-body .input { width: 100%; }
.wiz-body .label-row { margin-top: 1rem; }
.wiz-body .label-row:first-child, .wiz-pane > .label-row:first-child { margin-top: 0; }
.wiz-intent { resize: vertical; min-height: 3.2rem; line-height: 1.5; font: inherit; }
.wiz-readback { margin: 1.1rem 0 0.9rem; }
.wiz-readback.hidden { display: none; }
/* Starter descriptions — chips that fill the box, not modes that select one.
   Four nowrap rows inside one horizontal scroller: every chip is exactly as
   wide as its own text and sits immediately after the one before it, so the
   strip reads as a dense band of possibilities rather than a wrapped list with
   a ragged right edge. prompts.js packs the rows (shortest-first) so they run
   out at about the same width. */
.wiz-examples { margin-top: 0.6rem; }
.wiz-example-scroll {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  padding-bottom: 0.85rem; /* clears the bottom row's border of the scrollbar */
  scrollbar-width: thin;
  /* Fade whichever edge has more chips behind it (classes from prompts.js) —
     with both stops at 0 the mask is fully opaque, so a strip that fits shows
     no fade at all. */
  --fade-start: 0px;
  --fade-end: 0px;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-start), #000 calc(100% - var(--fade-end)), transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-start), #000 calc(100% - var(--fade-end)), transparent 100%);
}
.wiz-example-scroll.fade-start { --fade-start: 2rem; }
.wiz-example-scroll.fade-end { --fade-end: 2rem; }
.wiz-example-row { display: flex; flex-wrap: nowrap; gap: 0.55rem; width: max-content; }
.wiz-example {
  flex: 0 0 auto;
  white-space: nowrap; /* a chip that wrapped would break its row's height */
  padding: 0.3rem 0.65rem;
  border: 1px dashed var(--border-strong);
  border-radius: 999px;
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: 0.8rem;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.wiz-example:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.wiz-example:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Re-roll the sampled chips — an action, not another suggestion, so no pill.
   It lives under the scroller, so it needs its own left-aligned block. */
.wiz-example-more { display: block; margin-top: 0.15rem; padding-left: 0; border-color: transparent; color: var(--text-faint); text-decoration: underline dotted; }
.wiz-example-more:hover { background: transparent; border-color: transparent; }
/* Examples no longer match the edited description — visibly inert until re-run. */
.wiz-options.stale { opacity: 0.4; pointer-events: none; }
.wiz-conn { margin-top: 1.25rem; padding-top: 1.25rem; border-top: 1px solid var(--border); }
.wiz-conn .label-row { margin-top: 0.85rem; }
.wiz-conn .label-row:first-child { margin-top: 0; }
.wiz-options { display: flex; flex-direction: column; gap: 0.7rem; }
.wiz-card { padding: 0.9rem 1rem; border: 1.5px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); cursor: pointer; transition: border-color 0.12s, background 0.12s; }
.wiz-card:hover { border-color: var(--border-strong); }
.wiz-card.selected { border-color: var(--accent); background: var(--accent-soft); }
.wiz-card-label { display: inline-block; font-size: 0.72rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-faint); margin-bottom: 0.4rem; }
.wiz-card.selected .wiz-card-label { color: var(--accent); }
.wiz-card-text { font-size: 0.92rem; line-height: 1.5; color: var(--text); white-space: pre-wrap; }
.wiz-loading { padding: 1.5rem 0; text-align: center; color: var(--text-muted); font-style: italic; }
.wiz-foot { display: flex; align-items: center; gap: 0.6rem; padding: 1rem 1.5rem; border-top: 1px solid var(--border); }


/* ---- Wizard step 1: side-by-side fields, format row, group combobox ---- */
.wiz-field-row { display: flex; gap: 0.9rem; }
.wiz-field { flex: 1; min-width: 0; }
.wiz-field .input { width: 100%; }
.wiz-field-row .label-row { margin-top: 0; }

.wiz-format-row { display: flex; align-items: stretch; gap: 0.75rem; margin-top: 1rem; }

.combo { position: relative; }
.combo-input { width: 100%; padding-right: 1.9rem; }
/* Select-only combo: reads as a dropdown, so no caret and a pointer cursor. */
.combo-input[readonly] { cursor: pointer; caret-color: transparent; }
/* Chevron so a combo reads as a dropdown, not a plain text box. */
.combo::after {
  content: ""; position: absolute; right: 0.75rem; top: 50%;
  width: 0.45rem; height: 0.45rem; pointer-events: none;
  border-right: 2px solid var(--text-faint); border-bottom: 2px solid var(--text-faint);
  transform: translateY(-70%) rotate(45deg);
}
.combo-list {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 20;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-lg);
  max-height: 220px; overflow-y: auto; padding: 0.25rem;
}
.combo-opt { padding: 0.45rem 0.55rem; border-radius: 8px; cursor: pointer; font-size: 0.9rem; color: var(--text); }
.combo-opt.active, .combo-opt:hover { background: var(--surface-2); }
.combo-opt.create { color: var(--accent); }
.combo-muted { color: var(--text-faint); }
