/* ReportSkills application styles (app shell + report paper).
 * Shares brand tokens with site.css; app-only tokens live on .app-root. */

.app-root {
  --ink: #17263b;
  --ink-deep: #0f1c2e;
  --ink-soft: #5b6b82;
  --paper: #f4f1ea;
  --card: #ffffff;
  --line: #e4ddd1;
  --line-strong: #d3cabc;
  --amber: #e8a33d;
  /* darkened so link/warn ink clears WCAG AA on paper and on the amber wash */
  --amber-dark: #8f5f0d;
  --warn-ink: #87590a;
  --green: #2e7d5b;
  --red: #b04432;
  --ring: rgba(143, 95, 13, 0.85);
  /* report paper: warm neutral greys, all AA-safe on white (was cool #8b94a3/#9aa3b0) */
  --rp-label: #5f6a79;
  --rp-text: #3c4a5e;
  --rp-foot: #66707f;
  --rp-miss: #6b7482;
  --rp-check: #77808e;
  --rp-rule: #e4ddd1;
  --rp-rule-soft: #efe9df;
  --radius: 14px;
  --shadow: 0 1px 2px rgba(23, 38, 59, 0.06), 0 10px 28px rgba(23, 38, 59, 0.10);
  /* the width the shipped page really uses — the editor preview scales this */
  --rp-page-w: 640px;

  position: fixed;
  inset: 0;
  display: flex;
  background: var(--paper);
  color: var(--ink);
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 15px;
  line-height: 1.55;
}

.app-root * { box-sizing: border-box; }
.app-root a { color: var(--amber-dark); text-decoration: none; }

/* ---------- sidebar ---------- */
.app-side {
  width: 240px;
  flex: 0 0 240px;
  background: var(--ink);
  color: #cfd8e6;
  display: flex;
  flex-direction: column;
  padding: 18px 14px;
  /* the bench rail + account block can outgrow the viewport — scroll inside */
  overflow-y: auto;
}
.app-side .side-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 10px 18px;
  color: #fff;
  font-weight: 700;
  font-size: 16px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  margin-bottom: 14px;
  /* the shell-mode pill rides the same row; a long language must drop it, never clip it */
  flex-wrap: wrap;
  row-gap: 6px;
  height: 78px;
  box-sizing: border-box;
}
.app-side .side-brand img { width: 30px; height: 30px; }
.app-side .side-brand .accent { color: var(--amber); }
/* which shell is this, answered by the first row instead of by pixel memory:
   the wordmark is identical in both, so the pill and the group labels carry the difference */
.side-mode {
  margin-left: auto; flex: 0 0 auto; padding: 2px 7px; border-radius: 999px;
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  color: #cfd8e6; background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.18);
}
.nav-group { font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: #8fa0b8; padding: 14px 12px 6px; }
.app-nav .nav-group:first-child { padding-top: 4px; }
/* the label that introduces the report name sits right under the brand's own spacing */
.app-side .side-brand + .nav-group { padding-top: 0; }
/* uppercase + letter-spacing is Latin only — CJK labels keep their own shape */
html[lang^="zh"] .side-mode, html[lang^="zh"] .nav-group { text-transform: none; letter-spacing: 0; }
.app-nav { display: flex; flex-direction: column; gap: 2px; }
.app-nav a {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 12px;
  border-radius: 9px;
  color: #cfd8e6;
  font-size: 14px;
  transition: background 0.15s ease, color 0.15s ease;
}
.app-nav a svg { width: 17px; height: 17px; flex: 0 0 17px; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.app-nav a:hover { background: rgba(255, 255, 255, 0.07); color: #fff; }
.app-nav a.active { background: var(--amber); color: var(--ink-deep); font-weight: 600; }
.side-foot { margin-top: auto; padding: 12px 10px 0; font-size: 12px; color: #8fa0b8; border-top: 1px solid rgba(255, 255, 255, 0.12); }
.side-foot a { color: #cfd8e6; }
/* ---------- account block (rendered by runtime.js, same markup in both shells) ---------- */
#account-widget.acct-root { position: relative; margin-top: 10px; }
#account-widget .acct-trigger { display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px; border: 0; border-radius: 10px; background: rgba(255, 255, 255, 0.06); color: inherit; cursor: pointer; text-align: left; font: inherit; }
#account-widget .acct-trigger:hover { background: rgba(255, 255, 255, 0.12); }
#account-widget .acct-avatar { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: 50%; background: rgba(232, 163, 61, 0.18); color: var(--amber); flex: 0 0 auto; }
#account-widget .acct-avatar svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; }
#account-widget .acct-meta { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
#account-widget .acct-email { font-weight: 600; color: #fff; font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#account-widget .acct-plan { margin-top: 2px; font-size: 12px; color: var(--amber); font-weight: 700; }
#account-widget .acct-caret { width: 14px; height: 14px; flex: 0 0 auto; fill: none; stroke: #8fa0b8; stroke-width: 2; }
#account-widget .acct-menu { position: absolute; bottom: calc(100% + 6px); left: 0; right: 0; padding: 4px; border-radius: 10px; background: #1c2a41; border: 1px solid rgba(255, 255, 255, 0.12); box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35); }
#account-widget .acct-menu[hidden] { display: none; }
/* id-scoped so the shell-wide `.app-root a` amber never wins over the dark-surface ink */
#account-widget a.acct-menu-item, #account-widget .acct-menu-item { display: block; width: 100%; padding: 8px 10px; border: 0; border-radius: 8px; background: none; color: #cfd8e6; font-size: 12.5px; font-family: inherit; text-align: left; cursor: pointer; }
#account-widget .acct-menu-item:hover { background: rgba(255, 255, 255, 0.1); color: #fff; }
.side-foot .demo-chip {
  display: inline-block;
  margin-bottom: 8px;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(232, 163, 61, 0.18);
  color: var(--amber);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.04em;
}

/* ---------- main / topbar ---------- */
.app-main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.app-top {
  height: 60px;
  flex: 0 0 60px;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 28px;
  background: rgba(244, 241, 234, 0.9);
  backdrop-filter: blur(6px);
  border-bottom: 1px solid var(--line);
}
.app-top h1 { font-size: 18px; margin: 0; letter-spacing: -0.01em; }
.app-top .top-sub { color: var(--ink-soft); font-size: 13px; }
.app-top .top-actions { margin-left: auto; display: flex; gap: 10px; align-items: center; }
.app-view { flex: 1; overflow-y: auto; padding: 28px; scroll-behavior: smooth; }
.view-in { max-width: 1120px; margin: 0 auto; animation: viewIn 0.28s ease; }
@keyframes viewIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* ---------- buttons & chips ---------- */
.abtn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border: 0;
  cursor: pointer;
  padding: 9px 18px;
  border-radius: 999px;
  font-weight: 600;
  font-size: 14px;
  background: var(--ink);
  color: #fff;
  transition: transform 0.12s ease, background 0.15s ease;
}
.abtn:hover { background: var(--ink-deep); transform: translateY(-1px); }
.abtn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
.abtn-ghost { background: transparent; color: var(--ink); box-shadow: inset 0 0 0 1.5px var(--line-strong); }
.abtn-ghost:hover { background: rgba(23, 38, 59, 0.06); }
.abtn-amber { background: var(--amber); color: var(--ink-deep); }
.abtn-amber:hover { background: #f0b45c; }
.abtn-danger { background: transparent; color: var(--red); box-shadow: inset 0 0 0 1.5px rgba(176, 68, 50, 0.4); }
.abtn-danger:hover { background: rgba(176, 68, 50, 0.08); }
.abtn-sm { padding: 6px 13px; font-size: 13px; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  background: rgba(23, 38, 59, 0.07);
  color: var(--ink-soft);
}
.chip .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.chip-on { background: rgba(46, 125, 91, 0.14); color: #1f6a48; }
.chip-warn { background: rgba(232, 163, 61, 0.22); color: var(--warn-ink); }
.chip-red { background: rgba(176, 68, 50, 0.14); color: #963425; }
.chip-live { background: rgba(46, 125, 91, 0.12); color: var(--green); }
.chip-live .dot { animation: pulse 1.1s ease infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

/* ---------- cards, grids, tables ---------- */
.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px 22px;
}
.card h3 { margin: 0 0 4px; font-size: 16px; }
.card .card-sub { margin: 0 0 14px; color: var(--ink-soft); font-size: 13px; }
.grid { display: grid; gap: 18px; }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-auto { grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); }

.stat .stat-label { font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-soft); }
.stat .stat-value { font-size: 30px; font-weight: 700; letter-spacing: -0.02em; margin: 4px 0 2px; }
.stat .stat-foot { font-size: 12.5px; color: var(--ink-soft); }
.stat svg.spark { width: 100%; height: 34px; margin-top: 8px; }
.spark polyline { fill: none; stroke: var(--amber-dark); stroke-width: 2; }

atable { display: block; }
.tbl { width: 100%; border-collapse: collapse; font-size: 14px; }
.tbl th {
  text-align: left;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  padding: 8px 12px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.tbl td { padding: 11px 12px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.tbl tr:last-child td { border-bottom: 0; }
.tbl tbody tr { transition: background 0.12s ease; }
.tbl tbody tr:hover { background: rgba(232, 163, 61, 0.06); }
.tbl .td-link { cursor: pointer; font-weight: 600; color: var(--ink); }
.tbl .td-link:hover { color: var(--amber-dark); }

.muted { color: var(--ink-soft); }
.small { font-size: 12.5px; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; }
.nowrap { white-space: nowrap; }
.bar-track { height: 6px; border-radius: 3px; background: rgba(23, 38, 59, 0.1); overflow: hidden; }
.bar-fill { height: 100%; border-radius: 3px; background: var(--amber); transition: width 0.5s ease; }
.bar-fill--ok { background: var(--green); }

/* completeness bar in clients list */
.cmp { display: flex; align-items: center; gap: 8px; min-width: 130px; }
.cmp .bar-track { flex: 1; }
.cmp.warn .bar-fill { background: var(--red); }

/* one form control recipe for every select in the app (was duplicated inline) */
.select-in,
.field select {
  border: 1px solid var(--line-strong);
  border-radius: 9px;
  padding: 8px 12px;
  font: inherit;
  font-size: 14px;
  background: #fff;
  color: var(--ink);
  max-width: 100%;
}

/* ---------- template cards ---------- */
.tpl-card { cursor: pointer; transition: transform 0.15s ease, box-shadow 0.15s ease; }
.tpl-card:hover { transform: translateY(-2px); box-shadow: var(--shadow); }
.tpl-mini { display: flex; flex-direction: column; gap: 4px; margin: 12px 0; }
.tpl-mini span {
  font-size: 12px;
  padding: 4px 8px;
  border-radius: 6px;
  background: rgba(23, 38, 59, 0.05);
  color: var(--ink-soft);
  border-left: 3px solid var(--line-strong);
}
.tpl-mini span:first-child { background: var(--ink); color: #fff; border-left-color: var(--amber); }

/* ---------- the workbench's in-place canvas keeps the widget-row recipe
   only where a real list is drawn (the create wizard); the bench edits blocks
   on the paper itself, so no side column and no three-grid shell ---------- */
.widget-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 34px 9px 10px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--card);
  margin-bottom: 8px;
  cursor: grab;
  font-size: 13.5px;
  transition: border-color 0.12s ease, background 0.12s ease;
}
.widget-item:hover { border-color: var(--line-strong); }
.widget-item.sel { border-color: var(--amber); background: rgba(232, 163, 61, 0.08); }
.widget-item.drag-over { border-top: 2px solid var(--amber); }
.widget-item.off { opacity: 0.55; }
.widget-item.off > span:first-child { text-decoration: line-through; }
.widget-item .w-off-tag {
  font-style: normal; font-size: 12px; font-weight: 700;
  color: var(--red);
}
.widget-item .w-type {
  margin-left: auto;
  font-size: 12px;
  color: var(--ink-soft);
}
.widget-item .w-del {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  border: 0; background: none; cursor: pointer; color: var(--ink-soft);
  font-size: 14px; width: 26px; height: 26px; line-height: 1; border-radius: 7px;
}
.widget-item .w-del:hover { color: var(--red); background: rgba(176, 68, 50, 0.1); }
/* keyboard equivalent of drag-to-reorder, revealed on focus/hover */
.widget-item .w-move {
  position: absolute; right: 30px; top: 50%; transform: translateY(-50%);
  display: flex; gap: 2px;
  opacity: 0; transition: opacity 0.12s ease;
}
.widget-item:hover .w-move, .widget-item:focus-within .w-move { opacity: 1; }
.widget-item .w-move button {
  border: 1px solid var(--line-strong); background: var(--card); color: var(--ink-soft);
  width: 22px; height: 22px; border-radius: 6px; cursor: pointer; font-size: 11px; line-height: 1;
}
.widget-item .w-move button:hover:not(:disabled) { color: var(--ink); border-color: var(--amber); }
.widget-item .w-move button:disabled { opacity: 0.35; cursor: default; }
.paper-stage {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 22px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 18px;
  overflow-x: auto;
}
/* one sheet per page, sized as a real A4 page (210×297): it fills the column up
   to the PDF width (640) and floors at 380px, so a narrow (chat-open) column
   scrolls sideways instead of squeezing the page illegibly. The sheet is an
   A4 MINIMUM that grows: a layout-only ::before carries the PDF ratio in the
   same grid cell as the page body, so blank and short pages still read as one
   full sheet while a page with more content than one sheet stretches the paper
   downward. There is no inner scroller — the report column owns the wheel, so
   the canvas never stacks two vertical scrolls on one gesture. */
.wb-page { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.wb-page + .wb-page { padding-top: 16px; border-top: 1px dashed var(--line); }
.wb-page-head { display: flex; align-items: center; gap: 8px; width: 100%; max-width: var(--rp-page-w); }
.wb-page-label { font-size: 12.5px; font-weight: 600; color: var(--ink-soft); letter-spacing: 0.03em;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-page-tools { display: inline-flex; gap: 3px; margin-left: auto; flex: 0 0 auto; }
.wb-page-tools .abtn { padding: 0; width: 24px; min-width: 24px; height: 24px; font-size: 12px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; gap: 0; }
/* the free-layout toggle carries a text label (自由排版), not a single glyph —
   the 24px square above would squeeze it into a vertical column of characters */
.wb-page-tools .abtn[data-page-free] { width: auto; min-width: 0; padding: 0 7px; font-size: 11px; white-space: nowrap; }
.paper-page {
  box-sizing: border-box;
  box-shadow: 0 2px 6px rgba(23, 38, 59, 0.06), 0 18px 44px rgba(23, 38, 59, 0.13);
  border: 1px solid var(--line);
  width: 100%; max-width: var(--rp-page-w); min-width: 380px;
  position: relative;
  display: grid;
  grid-template-columns: 100%;
}
/* the ratio rider: height:0 + padding-bottom is universally supported and
   needs no intrinsic-sizing keyword on the row — the row stretches past it
   whenever the page body is taller than one A4 */
.paper-page::before { content: ""; grid-area: 1 / 1; padding-bottom: calc(100% * 297 / 210); height: 0; align-self: start; pointer-events: none; }
/* the A4 body shares the rider's cell and stretches with it; the footer still
   pins to the bottom of the visible sheet and a blank hint centres. Scoped to
   .paper-page (editor only) — the shipped/live .rp-page keeps plain flow. */
.paper-page > .rp-page { grid-area: 1 / 1; display: flex; flex-direction: column; }
.paper-page .rp-foot { margin-top: auto; }
.paper-page .rp-empty { margin: auto; }
/* hidden pages stay on the canvas as a dimmed sheet with one real Show button,
   so un-hiding is a plain click on the thing it affects — not a separate tray. */
.wb-page-hidden .paper-page > .rp-page { opacity: 0.4; filter: grayscale(0.8); pointer-events: none; }
.paper-hidden-overlay { position: absolute; inset: 0; z-index: 3; display: flex;
  flex-direction: column; align-items: center; justify-content: center; gap: 10px; }
.paper-hidden-badge { font-size: 12.5px; font-weight: 600; letter-spacing: 0.03em; color: var(--ink-soft); }
.wb-page-sel { outline: 2px solid var(--amber); outline-offset: 3px; }
.rp-empty { padding: 40px 30px; text-align: center; color: var(--ink-soft); font-size: 13px; }
/* the add-page button sits in the block report column (not a flex child), so it
   centres with auto margins, not align-self */
.paper-add { display: block; width: max-content; margin: 4px auto 2px; border: 1px dashed var(--line-strong); background: transparent; color: var(--ink-soft);
  border-radius: 999px; padding: 8px 18px; font: inherit; font-size: 13px; cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease; }
.paper-add:hover { background: rgba(232, 163, 61, 0.08); color: var(--ink); border-color: var(--amber); }
.insp-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.insp-label { font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-soft); margin: 14px 0 6px; }
.insp-label:first-child { margin-top: 0; }

/* ---------- workbench (web.html #/{id}): a layered surface -----------------
   L1 the report head (shell topbar) · L2 the chat rail · L3 the tab panel ·
   L4 the tab's own content. Each level reads one step forward of the last: the
   page is the desk, the panel is a raised card, its content is inset inside. */
.wb { display: grid; grid-template-columns: 320px minmax(0, 1fr); gap: 16px;
  height: 100%; padding: 0; }
/* the assistant is a column you can fold away: hidden, the tab panel takes the
   whole width — the rail is display-none, never unmounted, so the transcript holds */
.wb.chat-off { grid-template-columns: minmax(0, 1fr); }
.wb.chat-off .wb-chat { display: none; }
.wb-chat {
  /* the assistant owns the full height: transcript scrolls, composer stays put */
  display: flex; flex-direction: column; min-height: 0;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); overflow: hidden;
}
.wb-kind { font-size: 12px; font-weight: 600; color: var(--ink-soft); text-transform: uppercase; letter-spacing: 0.06em; }
.wb-main { min-width: 0; min-height: 0; display: flex; }
/* L3: one cohesive panel holds the tabs and their content together */
.wb-panel { flex: 1; min-width: 0; display: flex; flex-direction: column;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); overflow: hidden; }
.wb-tabs { display: flex; align-items: center; gap: 8px; padding: 12px 14px; flex: 0 0 auto;
  background: linear-gradient(180deg, rgba(23, 38, 59, 0.035), rgba(23, 38, 59, 0.012));
  border-bottom: 1px solid var(--line); }
.wb-tablist { display: flex; flex-wrap: wrap; gap: 4px; min-width: 0; flex: 1 1 auto; }
/* the three core tabs (Display / Table data / Send management) group on the left;
   Data plugins is an initialisation config, so pin it to the far-right edge of the
   bar — the list fills the row and the plugins tab takes the leftover space */
.wb-tablist .wb-tab[data-tab="plugins"] { margin-left: auto; }
/* the chat fold sits at the head of the tab row — one icon, not a second toolbar */
.wb-tabs-toggle { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border: 1px solid var(--line-strong); border-radius: 9px;
  background: transparent; color: var(--ink-soft); cursor: pointer;
  transition: background 0.14s ease, color 0.14s ease, border-color 0.14s ease; }
.wb-tabs-toggle:hover { color: var(--ink); background: rgba(23, 38, 59, 0.05); }
.wb-tabs-toggle svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
/* folded away is a state worth seeing: the icon stays lit while the rail is hidden */
.wb.chat-off .wb-tabs-toggle { color: var(--amber-dark); border-color: var(--amber); background: rgba(232, 163, 61, 0.12); }
.wb-tab { flex: 0 0 auto; border: 1px solid transparent; background: transparent; border-radius: 9px;
  padding: 8px 15px; font: inherit; font-size: 13.5px; cursor: pointer; color: var(--ink-soft);
  display: inline-flex; align-items: center; gap: 7px;
  transition: background 0.14s ease, color 0.14s ease, box-shadow 0.14s ease; }
.wb-tab:hover { color: var(--ink); background: rgba(23, 38, 59, 0.05); }
.wb-tab.on { background: var(--card); color: var(--ink); font-weight: 600;
  border-color: var(--line-strong); box-shadow: 0 1px 2px rgba(23, 38, 59, 0.08); }
/* L4: the tab's content sits inset inside the panel */
.wb-pane { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 18px; background: rgba(23, 38, 59, 0.015); }
/* tab bodies: the canvas and its inspector are one surface split by a hairline,
   not two floating cards */
/* Display tab body: the canvas and its inspector are one surface split by a
   hairline. The two columns fill the pane height and each scrolls on its own,
   so wheeling over the report never drags the right-hand property panel along —
   only the report column scrolls. */
.wb-cols { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 0; align-items: stretch; height: 100%; min-height: 0; }
.wb-main-col { min-width: 0; min-height: 0; padding-right: 18px; overflow-y: auto; }
.wb-side { border-left: 1px solid var(--line); padding-left: 18px; align-self: stretch; min-height: 0; overflow-y: auto; }
.wb-side-head { margin-bottom: 12px; }
.wb-side-head h3 { margin: 0 0 2px; font-size: 15px; }
/* the report column is the scroll box, so the toolbar must stick: otherwise the
   add-component / preview controls scroll away with a long (multi-page) report and
   you cannot add a block to a lower page. The negative top margin cancels the pane
   padding so the bar sits flush; hidden trays ride along so unhide stays reachable. */
.wb-workbar { position: sticky; top: 0; z-index: 6; background: var(--card);
  margin: 0; padding-bottom: 10px; box-shadow: 0 1px 0 var(--line); }
.wb-workbar .wb-toolbar { margin-bottom: 10px; }
.wb-workbar .wb-hidden-row:last-child { margin-bottom: 10px; }
.wb-addtarget { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--ink-soft); }
.wb-toolbar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 14px; }
/* a labelled control stays one unit and never wraps apart; toolbar actions hug the right */
.wb-tool { display: inline-flex; align-items: center; gap: 8px; }
.wb-tool > .small { color: var(--ink-soft); white-space: nowrap; }
.wb-tool-actions { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; }
/* one control rhythm in every tab toolbar: pill selects sized to match the pill buttons
   (the .select-in base is width:100% for forms — inside a toolbar it must hug content) */
.wb-toolbar .select-in { width: auto; border-radius: 999px; padding: 6px 14px; font-size: 13px; line-height: 1.4; max-width: 220px; }
/* inside the panel the tab's own content scrolls, so a side rail must not keep a
   second viewport-based scrollbar — let it flow with the pane (one scroller) */
.wb-pane .rb-list { position: static; max-height: none; overflow: visible; padding-right: 0; }

/* ---------- in-place block editing on the paper (template tab) ---------- */
/* every rule rides body.route-workbench: the same .rp- markup renders on the
   shipped/live faces, and there a hover ring or dashed frame must never appear */
body.route-workbench .paper [data-wid] { position: relative; cursor: pointer; outline: 2px solid transparent; outline-offset: -2px; border-radius: 4px; transition: outline-color 0.12s ease, opacity 0.12s ease; }
body.route-workbench .paper [data-wid]:hover { outline-color: rgba(232, 163, 61, 0.55); }
body.route-workbench .paper .rp-blk-sel { outline-color: var(--amber); }
body.route-workbench .paper .rp-blk-off { opacity: 0.4; }
body.route-workbench .paper .rp-blk-over { outline-color: var(--green); }
body.route-workbench .paper .rp-blk-sel::before {
  content: ""; position: absolute; top: 6px; right: 6px; width: 8px; height: 8px;
  border-radius: 50%; background: var(--amber); box-shadow: 0 0 0 3px rgba(232, 163, 61, 0.25);
}
/* the block's own controls: revealed on hover/focus, same affordance the old
   widget stack carried (move up/down, remove) — now on the block itself */
body.route-workbench .paper .blk-tools {
  position: absolute; top: 4px; right: 18px; display: flex; gap: 3px; z-index: 2;
  opacity: 0; transition: opacity 0.12s ease;
}
body.route-workbench .paper [data-wid]:hover .blk-tools,
body.route-workbench .paper [data-wid]:focus-within .blk-tools,
body.route-workbench .paper .rp-blk-sel .blk-tools { opacity: 1; }
body.route-workbench .paper .blk-tools button {
  border: 1px solid var(--line-strong); background: #fff; color: var(--ink-soft);
  width: 22px; height: 22px; border-radius: 6px; cursor: pointer; font-size: 11px; line-height: 1;
}
body.route-workbench .paper .blk-tools button:hover:not(:disabled) { color: var(--ink); border-color: var(--amber); }
body.route-workbench .paper .blk-tools button:disabled { opacity: 0.35; cursor: default; }
body.route-workbench .paper .blk-tools .blk-del:hover:not(:disabled) { color: var(--red); border-color: var(--red); }
/* add-block popover in the toolbar */
.wb-add { position: relative; }
.wb-add-menu {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 30; min-width: 240px;
  background: var(--card); border: 1px solid var(--line-strong); border-radius: 12px;
  box-shadow: var(--shadow); padding: 8px; display: grid; grid-template-columns: 1fr 1fr; gap: 4px;
}
.wb-add-menu[hidden] { display: none; }
.wb-add-menu button { border: 0; background: none; text-align: left; padding: 7px 9px; border-radius: 8px;
  font: inherit; font-size: 13px; cursor: pointer; color: var(--ink); }
.wb-add-menu button:hover { background: rgba(232, 163, 61, 0.12); }
/* add target rides inside the add menu (only matters at add time), not as a standing bar label */
.wb-addmenu-foot { grid-column: 1 / -1; display: flex; align-items: center; gap: 8px;
  margin-top: 4px; padding-top: 8px; border-top: 1px solid var(--line); font-size: 12.5px; color: var(--ink-soft); }
.wb-addmenu-foot .select-in { width: auto; flex: 1; border-radius: 999px; padding: 5px 12px; font-size: 13px; }
/* two-zone bar: compose left, preview/view right; low-frequency view controls
   collapse into one popover so the bar reads as a tool strip, not a form */
.wb-tools-right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
/* narrow workbench columns: the strip must never squeeze a control below its
 * text width — CJK labels collapse into one-character vertical columns when a
 * flex item shrinks past its words. Let the strip wrap onto its own rows and
 * keep every control one honest unit. */
.wb-tools-right { flex-wrap: wrap; row-gap: 8px; justify-content: flex-end; }
.wb-tools-right > * { flex: 0 0 auto; }
.wb-snap { white-space: nowrap; }
.wb-view .abtn { white-space: nowrap; }
#wb-openpreview { white-space: nowrap; }
.wb-view { position: relative; }
.wb-viewbtn .wb-viewbtn-caret { margin-left: 2px; opacity: 0.7; }
.wb-viewmenu { position: absolute; right: 0; top: calc(100% + 6px); z-index: 30; min-width: 280px;
  background: var(--card); border: 1px solid var(--line-strong); border-radius: 12px;
  box-shadow: var(--shadow); padding: 12px; display: flex; flex-direction: column; gap: 10px; }
.wb-viewmenu[hidden] { display: none; }
.wb-view-row { display: flex; align-items: center; gap: 10px; }
.wb-view-row > .small { color: var(--ink-soft); white-space: nowrap; flex: 0 0 auto; }
.wb-view-row .select-in { width: auto; flex: 1 1 auto; border-radius: 999px; padding: 6px 14px; font-size: 13px; line-height: 1.4; max-width: 180px; }
.wb-view-row .wc-tierbar { margin-left: 0; }

/* ---------- records tab: batch list + that batch's detail rows ---------- */
/* selection language (contract): the rail's MAIN-axis pick (rule / batch) is the
   amber-bordered card .rb-item.on; SECONDARY picks (mode switch, batch strip in the
   detail) are the dark-filled pill .sc-seg button.on. One system per axis — a
   screen never marks two different levels of choice in the same idiom. */
/* the grid wrapper only exists so .rb-notes can sit UNDER both columns —
   as a direct child of the two-column .rb it fell into column 1 and the
   freeze note read as a stray line dangling off the rail */
.rb-wrap { display: flex; flex-direction: column; }
.rb { display: grid; grid-template-columns: 250px minmax(0, 1fr); gap: 16px; align-items: start; }
/* the rail anchors the selection while the (often much taller) detail card scrolls
   in the pane's single scroller — same sticky shape as the groups page's .recip-side.
   The viewport cap only opens its own scrollbar when the rail outgrows the screen
   (a pinned rail taller than the viewport would hide its last rows forever); a rail
   that fits never shows one, so the one-scroller intent holds for the common case */
.rb-left { position: sticky; top: 0; max-height: calc(100vh - var(--app-top-h, 104px) - 36px); overflow-y: auto; padding-right: 2px; }
.rb-list { display: flex; flex-direction: column; gap: 6px; max-height: calc(100vh - var(--app-top-h, 104px) - 130px); overflow-y: auto; padding-right: 2px; }
.rb-item { display: flex; flex-direction: column; gap: 3px; text-align: left; border: 1px solid var(--line); background: #fff;
  border-radius: 10px; padding: 9px 11px; cursor: pointer; font: inherit; font-size: 13px; color: var(--ink);
  transition: border-color 0.12s ease, background 0.12s ease; }
.rb-item:hover { border-color: var(--line-strong); }
.rb-item.on { border-color: var(--amber); background: rgba(232, 163, 61, 0.08); }
.rb-item .rb-meta { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-soft); flex-wrap: wrap; }
/* slim rule row: status dot + name + cadence on ONE line — the what-time/to-whom
   facts belong to the detail's ruleBar, repeating them here made every rule read twice */
.rb-item.rb-one { flex-direction: row; align-items: center; gap: 7px; }
.rb-one .rb-dot { flex: 0 0 8px; width: 8px; height: 8px; border-radius: 50%; background: var(--line-strong); }
.rb-one .rb-dot.on { background: var(--green); }
.rb-one .rb-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rb-one .rb-meta { flex: 0 0 auto; display: inline; flex-wrap: nowrap; }

/* ---------- recipient groups page: group rail + member table ---------- */
.recip { display: grid; grid-template-columns: 250px minmax(0, 1fr); gap: 18px; align-items: start; }
.recip-side { position: sticky; top: 0; align-self: start; max-height: calc(100vh - var(--app-top-h, 104px) - 28px); overflow-y: auto; }
.grp-item { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; border: 1px solid transparent;
  background: none; padding: 8px 11px; border-radius: 9px; cursor: pointer; font: inherit; font-size: 13.5px; color: var(--ink); }
.grp-item:hover { background: rgba(23, 38, 59, 0.08); }
.grp-item.on { background: var(--ink); color: #fff; font-weight: 600; }
/* a long group name stays on one line and gives its full text to the title
   attribute (set in the view) — it must not push the member count around */
.grp-item .grp-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.grp-item .grp-n { margin-left: auto; font-size: 12px; opacity: 0.7; flex: 0 0 auto; }
.grp-tools { display: none; gap: 2px; flex: 0 0 auto; }
.grp-item:hover .grp-tools, .grp-item:focus-within .grp-tools { display: inline-flex; }
.grp-tools button { border: 0; background: none; cursor: pointer; color: inherit; font-size: 12px; padding: 2px 4px; border-radius: 6px; opacity: 0.8; }
.grp-tools button:hover { opacity: 1; background: rgba(255, 255, 255, 0.18); }
/* the white wash is for the selected (ink) row only: an unselected row is light
   on light, so its tools need the ink wash — keyed on the row, not on row:hover,
   because the tools also open for a keyboard-focused row without a pointer */
.grp-item:not(.on) .grp-tools button:hover { background: rgba(23, 38, 59, 0.1); }
/* a group that still holds members can't be deleted: the delete tool stays
   visible (clicking it explains why) but reads as parked, not ready to fire */
.grp-tools button[data-locked] { opacity: 0.4; }
.grp-tools button[data-locked]:hover { opacity: 0.6; }
/* the create-group action rides the rail's own head row (label + action on one
   line, same shape as .pl-head / .rb-head) instead of floating under the list */
.recip-side-head { display: flex; align-items: center; gap: 8px; }
.recip-side-head .insp-label { margin: 0; flex: 1; }
/* the two library-wide "add a recipient" entries sit together on the left; the
   per-selection move controls ride the right, so the toolbar never reads as one
   undifferentiated row */
.recip-main .wb-toolbar #rec-picked { margin-left: auto; }

/* ---------- table-data tab: the store as one table ------------------------ */
.dg-grid { padding: 14px 16px; }
.dg-foot { margin: 10px 0 0; }
/* host-scoped so reusing .select-in / input never leaks to other surfaces */
.dg-controls { align-items: center; row-gap: 10px; }
.dg-controls .wb-tool { align-items: center; }
.dg-controls .select-in { max-width: 168px; }
.dg-period { flex-wrap: nowrap; gap: 5px; }
.dg-period .select-in { max-width: 150px; }
.dg-period .abtn-sm { padding: 6px 9px; }
#dg-now { margin-left: 2px; }
.dg-controls .dt-q, .dg-controls input[type="text"] { margin-left: auto; }
.dg-switch { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: var(--ink); cursor: pointer; white-space: nowrap; }
.dg-switch input { width: 16px; height: 16px; accent-color: var(--amber); cursor: pointer; }
/* the paste flow lives in a modal behind this header button, not in the table body */
.dg-paste-open svg { width: 15px; height: 15px; margin-right: 6px; vertical-align: -2px; fill: none; stroke: currentColor; stroke-width: 1.8; }
/* the search box is inert when data is not split by recipient — say so, don't just grey it */
.dg-controls .dt-q:disabled { opacity: 0.5; cursor: not-allowed; }
/* empty-state guidance: two ways to get numbers in (buttons only, not the form) */
.dg-empty { margin: 12px 0; }
.dg-empty-card { padding: 14px 16px; border: 1px dashed var(--line-strong); border-radius: 12px; }
.dg-empty-card > .small { margin: 0 0 10px; color: var(--ink); font-weight: 600; }
.dg-empty-acts { display: flex; flex-wrap: wrap; gap: 8px; }
.dg-empty-card > .muted { margin: 10px 0 0; }
.dg-lead { margin: 2px 0 12px; font-size: 13px; line-height: 1.6; }

/* ---------- data-plugins tab: installed plugins and sources as cards ------ */
/* one head row per section: the label is the subject, the actions hug the right,
   so no toolbar line is left hanging with nothing on its left half */
.pl-head { display: flex; align-items: center; gap: 10px; margin: 0 0 8px; }
.pl-head .insp-label { margin: 0; flex: 1; }
/* the second section (Sources) now heads with its own action row; give it the
   same breathing room over its grid the old bare label carried */
.pl > .pl-head:not(:first-child) { margin-top: 18px; }
/* an empty section is a framed invitation, not a lonely line of grey text — the
   same dashed language as the data-tab / send-tab empty states, spanning the full
   grid width rather than sitting in one 250px cell */
.pl-empty { grid-column: 1 / -1; padding: 16px 18px; border: 1px dashed var(--line-strong); border-radius: 12px; }
.pl-empty > .small { margin: 0; }
.pl .conn-item { display: flex; flex-direction: column; }
.pl .conn-item .conn-actions { margin-top: auto; padding-top: 8px; }
/* the pipeline's own explainer sits under the head row, plain language */
.pl-intro { margin: 0 0 12px; }
.chip-warn-text { color: var(--amber-dark); font-weight: 600; }
.conn-item { padding: 12px 14px; }
.conn-item h4 { margin: 0 0 2px; font-size: 13.5px; display: flex; align-items: center; gap: 7px; }
.conn-item .conn-kind { font-size: 12px; font-weight: 600; color: var(--ink-soft); }
.conn-item p { margin: 4px 0; font-size: 12.5px; }
.conn-item .conn-actions { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; }
/* the store ledger rides a native fold, so no JS toggle and no card-in-card */
.dt-lines { padding: 12px 16px; margin-top: 14px; }
.dt-lines summary { cursor: pointer; font-weight: 600; font-size: 13.5px; }
.dt-lines > .card-sub { margin-top: 6px; }
/* the client filter inside a tab toolbar speaks the same chip language as the log filters */
.wb-toolbar .dt-q, .wb-toolbar input[type="text"] {
  border: 1px solid var(--line-strong); border-radius: 999px; padding: 6px 14px;
  font-size: 13px; font-family: inherit; min-width: 160px; background: #fff; color: var(--ink);
}
/* blocks the previewed recipient switched off live outside the canvas — name them */
.wb-hidden-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0 0 12px; }
.wb-hidden-row .chip { cursor: pointer; }
.wt-degrade { margin-left: 0; }

/* ---------- records tab rail head ---------- */
/* the head is a digest row only (the count label), then the view switch, then the
   rail's single primary action — each on its own even row. A full-width ghost
   "add" bar read as an empty text field; the amber fill says "press me", and the
   action sits right above the list it adds to */
.rb-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; margin-bottom: 8px; }
.rb-head .insp-label { margin: 0; flex: 1; }
.sm-acts { display: flex; gap: 8px; width: 100%; margin-bottom: 10px; }
.sm-acts .abtn { flex: 1; justify-content: center; }
/* the batch table is wider than the panel gives it, and the panel clips — so the table
   rides its own sideways scroller inside the card (same shape as the store grid), which
   keeps the card's glass ring that a card-level overflow would have to give up */
.rb-right { padding: 14px 16px; }
.rb-scroll { overflow-x: auto; }
/* 发送管理 tab: the send rules are the axis. A segmented switch (按规则/全部记录)
   heads the rail; a selected rule shows a read-only config strip above its batch
   history, and the chosen batch's per-recipient table rides the same scroller. */
.sm-mode { margin-bottom: 10px; }
/* the two view modes split the rail width evenly so the switch reads as one
   segmented control, not two small pills drifting on the left edge */
.sm-mode button { flex: 1 1 0; }
.sm-batch { margin: 4px 0 14px; }
.sm-rulebar { padding: 0 0 12px; margin-bottom: 12px; border-bottom: 1px solid var(--line); }
.sm-rulebar-top { display: flex; align-items: center; gap: 8px; }
.sm-rulebar-top strong { font-size: 15px; }
.sm-rulebar-acts { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; }
.sm-rulebar > .small.muted { margin: 6px 0 0; }
/* pane-bottom explainers ride one block; action rows carry their own class —
   no ad-hoc inline margins left in the tab's templates */
.rb-notes { margin-top: 12px; }
.rb-notes p { margin: 0 0 6px; }
.rb-notes p:last-child { margin: 0; }
.rb-back-row { margin-top: 14px; }
.rb-retry-row { margin: 10px 0 0; }
.rb-hint { margin-top: 8px; }
/* empty state: the rail goes silent and the detail card is the single voice —
   guidance, its add-rule entry and the freeze note stack centred, dashed like
   the data-tab's dg-empty invitation rather than a bare line of muted text */
.rb-empty { display: flex; flex-direction: column; align-items: center; gap: 10px;
  margin: 6px auto; max-width: 460px; padding: 26px 20px;
  border: 1px dashed var(--line-strong); border-radius: 12px; text-align: center; }
.rb-empty-lead { margin: 0; font-size: 13.5px; color: var(--ink); line-height: 1.6; }
.rb-empty > .small.muted { margin: 0; }

/* ---------- chat rail: full-height, transcript scrolls, composer is pinned ---------- */
.wb-chat-card { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; padding: 16px 16px 14px; }
.wb-chat-head { display: flex; align-items: center; gap: 8px; font-size: 14px; flex: 0 0 auto; }
.wb-chat-ic { width: 26px; height: 26px; border-radius: 8px; flex: 0 0 26px; background: linear-gradient(135deg, var(--amber), #f0b45c); color: #fff; display: flex; align-items: center; justify-content: center; }
.wb-chat-sub { margin: 8px 0 0; font-size: 12.5px; color: var(--ink-soft); line-height: 1.5; flex: 0 0 auto; }
/* scoped to the rail: the transcript borrows the create page's .cr-messages, whose
   max-height:200px (same specificity, later in the sheet) would otherwise pin the
   log short and leave the composer floating in the middle of the rail */
.wb-chat .wb-messages { max-height: none; flex: 1 1 auto; min-height: 0; overflow-y: auto; margin: 14px 0; }
.wb-chat-foot { flex: 0 0 auto; }

/* ---------- forms ---------- */
.field { margin-bottom: 14px; }
.field label { display: block; font-size: 12.5px; font-weight: 600; color: var(--ink-soft); margin-bottom: 5px; }
.field input[type="text"], .field input[type="email"], .field input[type="time"], .field input[type="number"],
.field select, .field textarea, .note-row textarea {
  width: 100%;
  border: 1px solid var(--line-strong);
  border-radius: 9px;
  padding: 9px 12px;
  font: inherit;
  font-size: 14px;
  background: #fff;
  color: var(--ink);
}
.field textarea { min-height: 120px; font-family: ui-monospace, Menlo, monospace; font-size: 12.5px; }
.note-row textarea { min-height: 60px; font-family: inherit; font-size: 13.5px; }
/* one field recipe for every bare select the app builds (preview client, badge, etc.) */
.field input:focus, .field select:focus, .field textarea:focus,
input[type="text"]:focus, input[type="email"]:focus, input[type="number"]:focus,
input[type="time"]:focus, select:focus, textarea:focus { outline: 2px solid var(--ring); outline-offset: 1px; border-color: var(--amber); }
.check-row { display: flex; align-items: center; gap: 8px; padding: 5px 0; font-size: 14px; }
.switch { position: relative; width: 40px; height: 22px; flex: 0 0 40px; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch .track {
  position: absolute; inset: 0; border-radius: 999px; cursor: pointer;
  background: rgba(23, 38, 59, 0.18); transition: background 0.15s ease;
}
.switch .track::before {
  content: ""; position: absolute; width: 16px; height: 16px; border-radius: 50%;
  background: #fff; top: 3px; left: 3px; transition: transform 0.15s ease;
  box-shadow: 0 1px 3px rgba(0,0,0,0.25);
}
.switch input:checked + .track { background: var(--green); }
.switch input:checked + .track::before { transform: translateX(18px); }
/* the real checkbox is invisible by design, so the track carries the focus ring */
.switch input:focus-visible + .track { outline: 2px solid var(--ring); outline-offset: 2px; }

/* ---------- modal / drawer / toast ---------- */
.overlay {
  position: fixed; inset: 0; background: rgba(15, 28, 46, 0.45);
  display: flex; align-items: center; justify-content: center; z-index: 90;
  animation: fadeIn 0.18s ease;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.modal {
  background: var(--card); border-radius: 16px; box-shadow: var(--shadow);
  width: min(600px, calc(100vw - 40px)); max-height: calc(100vh - 80px);
  overflow-y: auto; padding: 24px 26px;
  animation: pop 0.2s ease;
}
@keyframes pop { from { opacity: 0; transform: scale(0.97) translateY(6px); } to { opacity: 1; transform: none; } }
.modal h2 { margin: 0 0 16px; font-size: 18px; }
/* V9 (small-viewport terminal pass): the primary button used to sit below the fold of a
   long dialog — invisible and un-clickable at short viewports. Actions now stick to the
   bottom edge of the scrolling dialog so 完成/关闭 always answer. */
.modal-actions {
  display: flex; justify-content: flex-end; gap: 10px; margin-top: 8px;
  position: sticky; bottom: -24px; background: var(--card);
  padding: 10px 0 24px; margin-top: 0;
}
.modal .steps { display: flex; gap: 8px; margin-bottom: 18px; }
.modal .steps span {
  flex: 1; text-align: center; font-size: 12px; font-weight: 600; color: var(--ink-soft);
  padding: 6px 0; border-bottom: 2px solid var(--line);
}
.modal .steps span.cur { color: var(--amber-dark); border-bottom-color: var(--amber); }

.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(440px, 100vw);
  background: var(--card); box-shadow: -12px 0 40px rgba(15, 28, 46, 0.18);
  z-index: 95; padding: 24px; overflow-y: auto;
  animation: slideIn 0.24s ease;
}
@keyframes slideIn { from { transform: translateX(40px); opacity: 0; } to { transform: none; opacity: 1; } }
.drawer h2 { margin: 0 0 4px; font-size: 17px; }

.toasts { position: fixed; bottom: 22px; right: 22px; display: flex; flex-direction: column; gap: 10px; z-index: 120; }
.toast {
  background: var(--ink); color: #fff; padding: 12px 18px; border-radius: 11px;
  font-size: 14px; box-shadow: var(--shadow); max-width: 340px;
  animation: toastIn 0.25s ease;
}
.toast::before { content: ""; display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--amber); margin-right: 9px; }
/* status-coded: an error must not read like a success */
.toast-err::before { background: #ff8a75; }
.toast-warn::before { background: var(--amber); }
.toast-ok::before { background: #4cc38a; }
.toast-undo {
  margin-left: 10px; background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--amber); font: inherit; font-size: 13px; font-weight: 700; text-decoration: underline;
}
.toast-undo:hover { color: #ffcd84; }
@keyframes toastIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* ---------- skeleton ---------- */
.skel { border-radius: 10px; background: linear-gradient(90deg, rgba(23,38,59,0.06) 25%, rgba(23,38,59,0.12) 50%, rgba(23,38,59,0.06) 75%); background-size: 300% 100%; animation: shimmer 1.3s linear infinite; }
@keyframes shimmer { from { background-position: 300% 0; } to { background-position: -300% 0; } }
.skel-row { height: 46px; margin-bottom: 10px; }
.skel-card { height: 150px; }

/* ---------- run progress ---------- */
.run-report { display: flex; align-items: center; gap: 14px; padding: 12px 0; border-bottom: 1px solid var(--line); }
.run-report:last-child { border-bottom: 0; }
.run-report .rr-name { flex: 0 0 210px; font-weight: 600; }
.run-report .rr-bar { flex: 1; }
.run-report .rr-state { flex: 0 0 130px; text-align: right; }

/* delivery timeline */
.tline { list-style: none; margin: 14px 0; padding: 0; }
.tline li { position: relative; padding: 0 0 18px 24px; }
.tline li::before { content: ""; position: absolute; left: 5px; top: 5px; width: 9px; height: 9px; border-radius: 50%; background: var(--line-strong); }
.tline li.done::before { background: var(--green); }
.tline li.miss::before { background: var(--amber); }
.tline li::after { content: ""; position: absolute; left: 9px; top: 16px; bottom: 2px; width: 1.5px; background: var(--line); }
.tline li:last-child::after { display: none; }
.tline .t-time { font-size: 12px; color: var(--ink-soft); }

/* filter row */
.filters { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 16px; }
.fchip {
  border: 1px solid var(--line-strong); background: #fff; border-radius: 999px;
  padding: 5px 13px; font-size: 13px; cursor: pointer; color: var(--ink-soft);
}
.fchip.on { background: var(--ink); color: #fff; border-color: var(--ink); }
.filters input[type="text"] {
  margin-left: auto; border: 1px solid var(--line-strong); border-radius: 999px;
  padding: 6px 14px; font-size: 13px; min-width: 200px;
}

/* ---------- report paper ---------- */
.paper {
  width: 100%;
  max-width: 640px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 2px 6px rgba(23, 38, 59, 0.05), 0 24px 60px rgba(23, 38, 59, 0.14);
  overflow: hidden;
}
.paper-mini { width: var(--rp-page-w); max-width: none; box-shadow: none; border-radius: 8px; flex: 0 0 auto; transform: scale(var(--pscale, 1)); transform-origin: top left; }
/* the bench preview shows the shipped 640px page scaled into the column, so what you
   compose is what the recipient opens (it used to reflow at an arbitrary width) */
.paper-fit { position: relative; width: calc(var(--rp-page-w) * var(--pscale, 1)); height: calc(var(--paper-h, 0px) * var(--pscale, 1)); }

.rp-cover { color: #fff; padding: 34px 30px 26px; position: relative; }
.rp-cover-mark {
  width: 38px; height: 38px; border-radius: 9px; border: 2px solid currentColor;
  display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 18px;
  margin-bottom: 18px;
}
.rp-cover-logo { display: block; height: 40px; width: auto; max-width: 180px; margin-bottom: 18px; }
.logo-prev { height: 28px; width: auto; max-width: 110px; border: 1px solid var(--line-strong); border-radius: 6px; padding: 2px 6px; background: #fff; }
.rp-cover-title { font-size: 24px; font-weight: 700; letter-spacing: -0.01em; }
.rp-cover-sub { opacity: 0.75; margin-top: 4px; font-size: 14px; }
.rp-cover-client { font-size: 17px; font-weight: 600; margin-top: 16px; color: var(--amber, #e8a33d); }
.rp-cover-foot { position: absolute; right: 30px; bottom: 22px; font-size: 12.5px; font-weight: 600; }

.rp-w { padding: 20px 30px 6px; }
.rp-w-title {
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--rp-label, #5f6a79); margin: 0 0 10px; font-weight: 700;
}
/* min-width:0 + overflow-wrap: a 1fr track's automatic minimum is the item's
   min-content, so one long unbreakable token (design view shows {{bind.vars}}
   verbatim) used to inflate its track past the card row and spill over the
   neighbours — the card now clamps to the track and wraps the token instead. */
.rp-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 10px; }
.rp-kpi { border: 1px solid var(--rp-rule, #e4ddd1); border-radius: 9px; padding: 10px 12px; min-width: 0; }
.rp-kpi span { display: block; font-size: 12px; color: var(--rp-label, #5f6a79); overflow-wrap: anywhere; }
.rp-kpi strong { font-size: 17px; letter-spacing: -0.01em; overflow-wrap: anywhere; }
.rp-kpi-delta { font-style: normal; font-size: 12px; font-weight: 700; margin-left: 6px; }
.rp-kpi-delta--up { color: #2e7d5b; }
.rp-kpi-delta--down { color: #b04432; }
/* a missing figure is not a zero: keep it readable and mark it with words, not colour alone */
.rp-kpi--miss { border-style: dashed; }
.rp-kpi--miss strong { color: var(--rp-miss, #6b7482); }
.rp-kpi--miss::after { content: attr(data-rp-miss); display: block; font-size: 12px; color: var(--rp-miss, #6b7482); }

.rp-chart { width: 100%; height: auto; color: #17263b; }
.rp-chart-area { fill: rgba(232, 163, 61, 0.16); stroke: none; }
.rp-chart-line { fill: none; stroke: #8f5f0d; stroke-width: 2.2; stroke-linecap: round; }
.rp-chart-dots circle { fill: #17263b; }
.rp-bar { fill: #17263b; }
.rp-bar-label { font-size: 12px; fill: #5f6a79; }
.rp-bar-value { font-size: 12px; font-weight: 700; fill: #17263b; }

.rp-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.rp-table th { text-align: left; color: #5f6a79; font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; padding: 6px 8px; border-bottom: 1.5px solid #e4ddd1; }
.rp-table td { padding: 8px; border-bottom: 1px solid #efe9df; }
.rp-table tr:last-child td { border-bottom: 0; }
.rp-chart.rp-pie { width: 160px; height: 160px; }
.rp-piewrap { display: block; }
.rp-legend { list-style: none; margin: 6px 0 0; padding: 0; font-size: 12.5px; }
.rp-legend li { padding: 2px 0; }
.rp-sw { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 6px; }
.rp-image img { display: block; border-radius: inherit; }
.rp-text { margin: 0; font-size: 14px; color: #3c4a5e; }
.rp-checklist { list-style: none; margin: 0; padding: 0; }
.rp-checklist li { display: flex; gap: 9px; align-items: baseline; padding: 4px 0; font-size: 13.5px; color: #3c4a5e; }
.rp-check {
  width: 13px; height: 13px; border-radius: 4px; border: 1.5px solid #77808e;
  display: inline-block; flex: 0 0 13px; position: relative; top: 2px;
}
.rp-checklist li.is-done .rp-check { background: #2e7d5b; border-color: #2e7d5b; }
.rp-checklist li.is-done .rp-check::after { content: ""; position: absolute; left: 3.5px; top: 1px; width: 4px; height: 7px; border: solid #fff; border-width: 0 1.6px 1.6px 0; transform: rotate(43deg); }
.rp-checklist li.is-done { color: #5f6a79; text-decoration: line-through; }

.rp-w--fallback { margin: 12px 30px; padding: 14px 16px; border: 1.5px dashed rgba(143, 95, 13, 0.55); border-radius: 10px; background: rgba(232, 163, 61, 0.07); }
.rp-w-fb-head { display: flex; align-items: center; gap: 8px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.07em; font-weight: 700; color: var(--amber-dark); }
.rp-w-fb-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--amber); }
.rp-w-fb-text { margin: 8px 0 0; font-size: 13px; color: #6f5c36; }

.rp-foot { display: flex; justify-content: space-between; gap: 12px; padding: 18px 30px 22px; margin-top: 10px; border-top: 1px solid #efe9df; font-size: 12px; color: #66707f; }
.rp-foot-brand { font-weight: 700; color: #17263b; }
/* the same render feeds the shipped/live/PDF faces: a sibling page starts a new
   sheet visually so a multi-page report never reads as one long strip. */
.rp-page + .rp-page { border-top: 1px solid #efe9df; margin-top: 20px; padding-top: 20px; }

/* ---------- free layout canvas ----------
   The faces keep their geometry inline (widgets.js / RSReportHtml emit the fixed
   720×1024 layer + absolute px boxes); these rules only host the editor's
   affordances — a fitted scaler, resize handle and grid guide. Flow pages never
   carry rp-abs, so nothing here touches legacy reports. */
.paper-page > .rp-page--abs { display: block; height: auto; overflow: visible; }
/* the artboard frame rides as an outline and the top offset is gone: a border
   or margin here would shift the layer origin, so the first free toggle would
   not land where the flow blocks sat (the faces carry no such inset either) */
.rp-abs-scale { position: relative; overflow: hidden; margin: 0 auto; outline: 1px solid var(--line); border-radius: 6px; }
.rp-abs-scale > .rp-abs-layer { margin: 0; transform-origin: top left; }
.rp-abs-layer {
  background:
    repeating-linear-gradient(0deg, rgba(23, 38, 59, 0.035) 0 1px, transparent 1px 40px),
    repeating-linear-gradient(90deg, rgba(23, 38, 59, 0.035) 0 1px, transparent 1px 40px);
}
.rp-abs { overflow: hidden; }
body.route-workbench .paper [data-wid].rp-abs { cursor: grab; }
body.route-workbench .paper [data-wid].rp-abs.is-dragging { cursor: grabbing; transition: none; }
.rp-resize { position: absolute; right: 0; bottom: 0; width: 14px; height: 14px; cursor: nwse-resize;
  background: linear-gradient(135deg, transparent 0 50%, var(--amber, #e8a33d) 50% 100%);
  border-top-left-radius: 3px; z-index: 6; touch-action: none; }
/* eight-way handles (editor pass 2026-10-18): every edge and corner resizes.
   They sit inset from the box border so the scaled wrapper never clips them. */
body.route-workbench .paper .rp-resize { background: var(--amber, #e8a33d); border: 1px solid #fff; border-radius: 3px; box-shadow: 0 1px 2px rgba(23, 38, 59, 0.25); width: 10px; height: 10px; }
.rp-resize.rp-r-n { left: 50%; top: 2px; right: auto; bottom: auto; transform: translateX(-50%); cursor: ns-resize; }
.rp-resize.rp-r-s { left: 50%; bottom: 2px; right: auto; top: auto; transform: translateX(-50%); cursor: ns-resize; }
.rp-resize.rp-r-e { top: 50%; right: 2px; left: auto; bottom: auto; transform: translateY(-50%); cursor: ew-resize; }
.rp-resize.rp-r-w { top: 50%; left: 2px; right: auto; bottom: auto; transform: translateY(-50%); cursor: ew-resize; }
.rp-resize.rp-r-nw { left: 2px; top: 2px; right: auto; bottom: auto; cursor: nwse-resize; }
.rp-resize.rp-r-ne { right: 2px; top: 2px; left: auto; bottom: auto; cursor: nesw-resize; }
.rp-resize.rp-r-sw { left: 2px; bottom: 2px; right: auto; top: auto; cursor: nesw-resize; }
.rp-resize.rp-r-se { right: 2px; bottom: 2px; left: auto; top: auto; cursor: nwse-resize; }
/* marquee selection rectangle (free pages, editor only) */
.rp-marquee { position: absolute; z-index: 9; border: 1px dashed #626aef; background: rgba(98, 106, 239, 0.10); pointer-events: none; }
/* secondary members of a multi-selection keep a quieter ring than the primary */
body.route-workbench .paper .rp-blk-sel2 { outline-color: rgba(98, 106, 239, 0.85); }
/* overflow truth: the faces clip silently — the canvas marks it instead */
body.route-workbench .paper .rp-ovf { outline: 1px dashed var(--red, #b04432); }
.blk-ovf { display: inline-flex; align-items: center; justify-content: center; width: 15px; height: 15px;
  border-radius: 999px; background: var(--red, #b04432); color: #fff; font-size: 9px; line-height: 1; flex: 0 0 auto; }
body.route-workbench .paper .rp-abs > .blk-ovf, body.route-workbench .paper .rp-styled > .blk-ovf { position: absolute; left: 4px; top: 4px; z-index: 6; }
.wb-page-head .blk-ovf { margin-right: 2px; }
/* in-place text editing: the caret lives on the paper, styled like the face */
body.route-workbench .paper .rp-editing { outline: 2px solid #626aef; outline-offset: -2px; cursor: text; min-height: 1.2em; }
/* floating format bubble over the live selection (canvas inline editing) */
.wt-bubble { position: fixed; z-index: 40; display: flex; align-items: center; gap: 4px;
  padding: 6px 8px; background: var(--card, #fff); border: 1px solid var(--line-strong);
  border-radius: 10px; box-shadow: var(--shadow); }
.wt-bubble[hidden] { display: none; }
.wt-bubble button { min-width: 28px; height: 28px; padding: 0 6px; border: 1px solid var(--line); border-radius: 7px;
  background: #fff; font: inherit; font-size: 12.5px; cursor: pointer; color: var(--ink); }
.wt-bubble button:hover { border-color: var(--amber); }
.wt-bubble input[type=color] { width: 28px; height: 28px; border: 1px solid var(--line); border-radius: 7px; padding: 0; background: #fff; }
.wt-bubble-sizes { display: inline-flex; gap: 2px; margin-left: 4px; padding-left: 6px; border-left: 1px solid var(--line); }
/* right-click menu on the canvas */
.wt-ctxmenu { position: fixed; z-index: 45; min-width: 180px; padding: 6px; display: flex; flex-direction: column;
  background: var(--card, #fff); border: 1px solid var(--line-strong); border-radius: 10px; box-shadow: var(--shadow); }
.wt-ctxmenu button { border: 0; background: none; text-align: left; padding: 7px 10px; border-radius: 7px;
  font: inherit; font-size: 13px; cursor: pointer; color: var(--ink); }
.wt-ctxmenu button:hover:not([disabled]) { background: rgba(232, 163, 61, 0.12); }
.wt-ctxmenu button[disabled] { opacity: 0.4; cursor: default; }
.wt-ctxmenu button.danger { color: var(--red, #b04432); }
.wt-ctx-sep { border: 0; border-top: 1px solid var(--line); margin: 4px 6px; }
/* multi-select action strip (lives inside the sticky workbar) */
.wb-multibar { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; margin: 0 0 10px;
  padding: 6px 10px; background: #fff; border: 1px dashed var(--rp-rule, #e4ddd1); border-radius: 10px; }
.wb-multibar[hidden] { display: none; }
.wb-multibar .abtn { width: 26px; min-width: 26px; height: 26px; padding: 0; display: inline-flex; align-items: center; justify-content: center; }
.wb-multibar .abtn[data-sel-clear] { width: auto; min-width: 0; padding: 0 8px; font-size: 11px; }
.wb-multibar-sep { width: 1px; height: 16px; background: var(--line-strong); margin: 0 4px; }
/* first-visit orientation strip: a plain-language banner above the canvas, dismissible */
.wb-guide { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap;
  margin: 0 0 12px; padding: 12px 14px; border: 1px solid var(--line);
  border-left: 4px solid var(--amber); border-radius: 12px; background: rgba(232, 163, 61, 0.08); }
.wb-guide-body { flex: 1 1 220px; min-width: 200px; }
.wb-guide-body b { display: block; font-size: 13.5px; margin-bottom: 3px; }
.wb-guide-body p { margin: 0; font-size: 13px; line-height: 1.55; color: var(--ink-soft); }
.wb-guide .abtn { flex: 0 0 auto; }
/* history pair + zoom cluster in the workbar */
.wb-hist { display: inline-flex; gap: 3px; margin-right: 10px; }
.wb-hist .abtn { width: 28px; min-width: 28px; height: 28px; padding: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 14px; }
.wb-zoombar { display: inline-flex; align-items: center; gap: 2px; margin-right: 4px; }
.wb-zoombar .abtn { width: 26px; min-width: 26px; height: 26px; padding: 0; display: inline-flex; align-items: center; justify-content: center; }
.wb-zoombar .wt-zoomval { width: auto; min-width: 54px; }

/* ---------- rich text editor (inspector) ---------- */
.rt-wrap { border: 1px solid var(--line-strong); border-radius: 10px; overflow: hidden; margin-top: 6px; }
.rt-bar { display: flex; flex-wrap: wrap; gap: 4px; padding: 6px; background: rgba(23, 38, 59, 0.04); border-bottom: 1px solid var(--line); }
.rt-bar button { min-width: 30px; padding: 3px 8px; border: 1px solid var(--line); border-radius: 6px; background: #fff; font-size: 12px; cursor:pointer; color: var(--ink); }
.rt-bar button:hover { border-color: var(--amber); }
.rt-bar input[type=color] { width: 26px; height: 26px; border: 1px solid var(--line); border-radius: 6px; background: #fff; padding: 0; }
.rt-edit { padding: 10px 12px; min-height: 120px; max-height: 300px; overflow: auto; font-size: 13.5px; line-height: 1.5; color: #3c4a5e; }
.rt-edit:focus { outline: 2px solid rgba(232, 163, 61, 0.35); outline-offset: -2px; }
.rt-edit p, .rt-edit h3, .rt-edit h4, .rt-edit ul, .rt-edit ol { margin: 0.25em 0; }
.rt-edit ul, .rt-edit ol { padding-left: 1.3em; }
.field input[type=color], .field input[type=range] { height: 30px; }
.field input[type=color] { width: 46px; padding: 2px; }
.wb-free-note { font-size: 12px; color: var(--ink-soft); margin: 6px 0 10px; }
/* outline-card inner: the content fills the ring frame and carries its own body css */
.rp-fill, .rp-w.rp-fill { max-width: none; }
/* extended inspector controls */
.insp-style .field { margin-bottom: 8px; }
.insp-style .field-row label, .insp-style .field label { display: block; font-size: 12px; color: var(--ink-soft); margin-bottom: 3px; }
.insp-style select, .insp-style input[type=text], .insp-style input[type=number], .insp-style textarea { width: 100%; box-sizing: border-box; }
.insp-style textarea { font-family: var(--mono, ui-monospace, monospace); font-size: 12px; }
.insp-style details.wc-adv summary { font-size: 12px; }
/* power-tool fold: position/layer/lock and raw CSS stay in the DOM but open
   collapsed, so a first-time author never stares at controls they don't need */
.insp-adv { margin: 2px 0 6px; }
.insp-adv > summary { cursor: pointer; }
.insp-adv > summary.insp-sec-title, .insp-adv > summary.insp-label { margin: 0 0 8px; }
.wb-snap { display: inline-flex; align-items: center; gap: 5px; margin-right: 12px; color: var(--ink-soft); }
/* precision tools: position panel, layer order, lock badge, smart guides */
.insp-pos { margin: 2px 0 10px; }
.preset-row { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 8px; }
.pos-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin: 4px 0 8px; }
.pos-cell label { display: block; font-size: 11px; color: var(--ink-soft); margin-bottom: 2px; }
.pos-cell input { width: 100%; box-sizing: border-box; }
.pos-layer { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 8px; }
.rp-locked { cursor: default !important; outline-color: var(--line-strong) !important; }
.blk-lock { position: absolute; left: 4px; bottom: 4px; font-size: 11px; color: var(--ink-soft); z-index: 6; }
.rp-guide { position: absolute; z-index: 8; background: #626aef; pointer-events: none; }
.rp-guide.v { top: 0; bottom: 0; width: 1px; }
.rp-guide.h { left: 0; right: 0; height: 1px; }

/* QR block */
.qr-block { display: flex; gap: 16px; align-items: center; }
.rp-qr { width: 92px; height: 92px; flex: 0 0 92px; }
.rp-qr .qr-bg { fill: #fff; stroke: #17263b; stroke-width: 1; }
.rp-qr rect:not(.qr-bg):not(.qr-in) { fill: #17263b; }
.rp-qr .qr-in { fill: #17263b; }

/* ---------- recipient standalone view ---------- */
body.route-report .app-side { display: none; }
body.route-report .app-view { padding-top: 36px; }
.rs-badge {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  align-items: center;
  padding: 12px 16px;
  border-radius: 10px;
  background: rgba(23, 38, 59, 0.05);
  border: 1px dashed #d3cabc;
  font-size: 12px;
  color: #8a94a3;
}
.rs-badge a { color: #8f5f0d; font-weight: 600; white-space: nowrap; }

/* ---------- assistant draft bar ---------- */
.ai-bar { border-left: 4px solid var(--amber); }
.ai-bar input:focus { outline: 2px solid rgba(232, 163, 61, 0.45); border-color: var(--amber); }

/* ---------- overview: action queue ---------- */
.ov-summary { margin-bottom: 18px; }
.ov-queue { border-left: 4px solid var(--amber); margin-bottom: 20px; }
.qrow {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 14px; margin-bottom: 8px;
  border: 1px solid var(--line); border-radius: 11px; background: #fff;
  color: var(--ink); transition: border-color 0.14s ease, transform 0.14s ease;
}
.qrow:last-child { margin-bottom: 0; }
.qrow:hover { border-color: var(--line-strong); transform: translateX(2px); }
.qrow .qicon {
  width: 26px; height: 26px; border-radius: 50%; flex: 0 0 26px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; background: rgba(23, 38, 59, 0.07); color: var(--ink-soft);
}
.qrow-red { border-color: rgba(176, 68, 50, 0.4); }
.qrow-red .qicon { background: rgba(176, 68, 50, 0.12); color: var(--red); }
.qrow-warn .qicon { background: rgba(232, 163, 61, 0.18); color: var(--amber-dark); }
.qrow-on .qicon, .qrow-ok .qicon { background: rgba(46, 125, 91, 0.13); color: var(--green); }
.qrow .qtext { flex: 1; font-size: 14px; }
.qrow .qact { font-size: 13px; font-weight: 600; color: var(--amber-dark); white-space: nowrap; }

/* ---------- status pipeline (cards + bench header) ---------- */
/* pills with breathing room + connector arrows — never a jammed inline run */
.pipe { display: flex; flex-wrap: wrap; row-gap: 8px; margin: 12px 0 10px; }
.pipe-node {
  position: relative; display: inline-flex; align-items: center; gap: 8px;
  padding: 5px 13px; margin-right: 20px;
  background: rgba(23, 38, 59, 0.035); border: 1px solid var(--line); border-radius: 999px;
}
.pipe-node:last-child { margin-right: 0; }
.pipe-node + .pipe-node::before {
  content: "›"; position: absolute; left: -14px; top: 50%; transform: translateY(-50%);
  color: #c4b9a6; font-size: 13px;
}
.pipe-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 8px; background: var(--line-strong); }
.pipe-node.ok .pipe-dot { background: var(--green); }
.pipe-node.warn .pipe-dot { background: var(--amber-dark); }
.pipe-node.todo .pipe-dot { background: transparent; border: 1.5px dashed var(--line-strong); }
.pipe-node.na .pipe-dot { background: transparent; border: 1.5px solid var(--line-strong); }
.pipe-node.red .pipe-dot { background: var(--red); }
.pipe-txt { display: flex; align-items: baseline; gap: 7px; line-height: 1.3; }
.pipe-txt em { font-style: normal; font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--ink-soft); }
.pipe-txt strong { font-size: 12.5px; font-weight: 600; }
.pipe-txt .chip { font-size: 12px; padding: 1px 8px; }
/* the data node averages block coverage over recipients — say so where it can be read */
.pipe-node[data-hint] { cursor: help; }

/* ---------- workbench: the view owns the width, a dense two-pane surface ---------- */
body.route-workbench .view-in { max-width: none; height: 100%; }
/* the workbench fills the viewport exactly: the two columns scroll inside
   themselves, so the outer view never grows a second scrollbar */
body.route-workbench .app-view { padding: 14px 16px; overflow: hidden; }
body.route-workbench .wb { height: 100%; }

/* ---------- workbench rail mark + header: title + actions row, pipeline gets its own row ---------- */
body.route-workbench .app-top {
  display: flex;
  flex: 0 0 auto;
  column-gap: 14px;
  height: 98px;
  align-items: center;
  justify-content: space-between;
  background: #fff;
}
body.route-workbench .app-top > div:first-child { display: contents; }
body.route-workbench .app-top h1 { grid-area: title; font-size: 19px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.route-workbench .app-top .top-sub { grid-area: sub; color: var(--ink-soft); white-space: normal; }
body.route-workbench .app-top .top-sub .pipe { margin: 0; }
body.route-workbench .app-top .top-actions { grid-area: actions; margin-left: 0; }
body.route-workbench .app-top .top-toggle { grid-area: toggle; }
/* the head pipeline is a quiet status line, not a row of boxed pills: on the
   workbench head the report name owns the focus, so the boxes step back to
   dot + label · value and a hairline divider. Scoped to the head only — the
   overview cards keep their pill treatment where the box aids scanning. */
body.route-workbench .app-top .top-sub .pipe { row-gap: 4px; }
body.route-workbench .app-top .top-sub .pipe-node {
  background: none; border: 0; padding: 0; margin-right: 22px; gap: 7px;
}
body.route-workbench .app-top .top-sub .pipe-node + .pipe-node::before {
  content: ""; left: -12px; width: 1px; height: 12px; background: var(--line-strong); transform: translateY(-50%);
}
body.route-workbench .app-top .top-sub .pipe-dot { width: 6px; height: 6px; flex: 0 0 6px; }
body.route-workbench .app-top .top-sub .pipe-txt { gap: 6px; }
body.route-workbench .app-top .top-sub .pipe-txt em { font-size: 11.5px; letter-spacing: 0.02em; color: var(--ink-soft); }
body.route-workbench .app-top .top-sub .pipe-txt strong { font-size: 12px; font-weight: 500; color: var(--ink); }
body.route-workbench .app-top .top-sub .pipe-txt .chip { font-size: 11px; padding: 0 7px; }

/* ---------- report cards ---------- */
.card.rep-card { display: flex; flex-direction: column; cursor: pointer; transition: transform 0.15s ease, box-shadow 0.15s ease; }
.card.rep-card:hover { transform: translateY(-2px); box-shadow: var(--shadow); }
.rep-card-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.rep-card-head h3 { margin: 0; font-size: 15.5px; }
.rep-card-foot { margin-top: auto; padding-top: 6px; }
.rep-card-open { margin-top: 8px; font-weight: 600; font-size: 13px; color: var(--amber-dark); opacity: 0; transition: opacity 0.15s ease; }
.rep-card:hover .rep-card-open, .rep-card:focus-within .rep-card-open { opacity: 1; }
/* touch hosts never hover — keep the bench entry visible there */
@media (hover: none) { .rep-card-open { opacity: 1; } }
.ov-empty { text-align: center; padding: 42px 22px; }

/* ---------- sidebar report shortcuts (quick jump to a bench) ---------- */
.side-reports {
  flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
  margin-top: 14px; padding-top: 12px; border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.side-reports[hidden] { display: none; }
.side-reports-label { font-size: 12px; letter-spacing: 0.05em; text-transform: uppercase; color: #8fa0b8; padding: 0 12px 6px; }
/* the list owns its overflow: bounded by the rail's leftover height (flex:1 +
   min-height:0), so long report sets slide inside it instead of growing the
   whole sidebar; the load-more link stays pinned under the scroll area */
.side-reports > div { flex: 1 1 auto; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 1px; scrollbar-width: thin; color-scheme: dark; }
.side-reports > div::-webkit-scrollbar { width: 6px; }
.side-reports > div::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.16); border-radius: 3px; }
/* anchor-scoped: bare .side-rep loses to the shell-wide `.app-root a` amber rule */
.app-side .side-rep { display: flex; align-items: center; gap: 9px; padding: 5px 12px; border-radius: 8px; color: #c9ced6; font-size: 12.5px; transition: background 0.13s ease, color 0.13s ease; }
.app-side .side-rep:hover { background: rgba(255, 255, 255, 0.06); color: #fff; }
.side-rep .sr-dot { width: 6px; height: 6px; flex: 0 0 6px; border-radius: 50%; background: rgba(232, 163, 61, 0.7); }
.side-rep .sr-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* the open report speaks with the bench's own bar-mark, not the solid block */
.app-side .side-rep.on { background: rgba(232, 163, 61, 0.16); color: #fff; box-shadow: inset 3px 0 0 var(--amber); }
/* the load-more affordance reads as a quiet text link, not a second button row */
.side-rep-more { margin: 6px 12px 2px; padding: 4px 0; border: 0; align-self: flex-start;
  background: none; color: #8fa0b8; font: inherit; font-size: 12px; cursor: pointer; transition: color 0.13s ease; }
.side-rep-more:hover { color: var(--amber); background: none; }
.side-rep-more[hidden] { display: none; }

/* ---------- create: world-class AI composer ---------- */
.cr-stage {
  max-width: 720px; margin: 0 auto; position: relative;
  padding: 46px 0 36px; text-align: center;
}
/* Aurora backdrop */
.cr-aurora {
  position: absolute; inset: -80px -60px auto; height: 520px;
  overflow: hidden; pointer-events: none; z-index: 0;
  border-radius: 24px; opacity: 0.6;
}
.cr-aurora-grid {
  position: absolute; inset: 0;
  background-image: linear-gradient(rgba(23, 38, 59, 0.04) 1px, transparent 1px),
    linear-gradient(90deg, rgba(23, 38, 59, 0.04) 1px, transparent 1px);
  background-size: 34px 34px; mask-image: radial-gradient(60% 55% at 50% 22%, #000, transparent 78%);
  -webkit-mask-image: radial-gradient(60% 55% at 50% 22%, #000, transparent 78%);
}
.cr-aurora-blob { position: absolute; border-radius: 50%; filter: blur(80px); }
.cr-aurora-blob1 { width: 340px; height: 340px; top: -30px; left: 8%; background: rgba(232, 163, 61, 0.28); animation: cr-aurora-drift 12s ease-in-out infinite alternate; }
.cr-aurora-blob2 { width: 300px; height: 300px; top: 10px; right: 4%; background: rgba(99, 102, 241, 0.24); animation-delay: -4s; }
.cr-aurora-blob3 { width: 220px; height: 220px; bottom: 30px; left: 35%; background: rgba(46, 125, 91, 0.16); animation-delay: -8s; }
@keyframes cr-aurora-drift {
  0% { transform: translate(0, 0) scale(1); }
  100% { transform: translate(24px, -18px) scale(1.1); }
}
/* the orb: a breathing AI presence above the entry */
.cr-orb { position: relative; width: 68px; height: 68px; margin: 0 auto 18px; display: flex; align-items: center; justify-content: center; }
.cr-orb-core {
  position: relative; z-index: 2; width: 46px; height: 46px; border-radius: 14px;
  background: linear-gradient(135deg, var(--amber), #6366f1);
  display: flex; align-items: center; justify-content: center; color: #fff;
  box-shadow: 0 8px 26px rgba(232, 163, 61, 0.4), 0 2px 8px rgba(99, 102, 241, 0.35);
  animation: cr-orb-float 4.5s ease-in-out infinite;
}
.cr-orb-ring { position: absolute; inset: 0; border-radius: 50%; border: 1.5px solid rgba(232, 163, 61, 0.4); animation: cr-orb-pulse 3s ease-out infinite; }
.cr-orb-ring2 { border-color: rgba(99, 102, 241, 0.34); animation-delay: -1.5s; }
@keyframes cr-orb-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
@keyframes cr-orb-pulse { 0% { transform: scale(0.7); opacity: 0.9; } 100% { transform: scale(1.5); opacity: 0; } }
.cr-eyebrow { margin: 0 0 8px; font-size: 12.5px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--amber-dark); }
html[lang^="zh"] .cr-eyebrow { letter-spacing: 0.06em; }
.cr-wordmark { margin: 0 auto; font-size: clamp(24px, 4vw, 34px); line-height: 1.15; letter-spacing: -0.025em; font-weight: 800; color: var(--ink); max-width: 620px; }
.cr-lead { margin: 12px auto 26px; max-width: 540px; font-size: 15px; line-height: 1.6; color: var(--ink-soft); }
/* Hero card: layered glass feel */
.cr-hero-wrap { position: relative; z-index: 1; }
.cr-hero-card {
  text-align: left;
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: blur(16px) saturate(180%);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.7);
  border-radius: 22px;
  box-shadow: 0 4px 24px rgba(23, 38, 59, 0.08), 0 28px 70px rgba(23, 38, 59, 0.12),
    inset 0 1px 0 rgba(255, 255, 255, 0.85);
  padding: 24px 26px 22px;
  animation: cr-card-in 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes cr-card-in { from { opacity: 0; transform: translateY(12px) scale(0.98); } to { opacity: 1; transform: none; } }
/* generation pipeline: the four automated build stages, run in place */
.cr-gp { margin-top: 20px; padding: 16px 18px 6px; border-radius: 16px;
  background: linear-gradient(180deg, rgba(23, 38, 59, 0.03), rgba(23, 38, 59, 0.008));
  border: 1px solid var(--line); }
.cr-gp-head { display: flex; align-items: center; gap: 9px; margin-bottom: 6px; }
.cr-gp-head b { font-size: 13.5px; color: var(--ink); }
.cr-gp-pulse { width: 9px; height: 9px; border-radius: 50%; background: var(--amber); box-shadow: 0 0 0 0 rgba(232, 163, 61, 0.5); animation: cr-gp-pulse 1.4s ease-out infinite; flex: 0 0 9px; }
/* terminal badge: the pulse gives way to a settled ✓/✕ the moment the pipeline
 * lands, so the head never keeps animating over a finished run */
.cr-gp-tick { flex: 0 0 15px; width: 15px; height: 15px; border-radius: 50%; background: var(--green); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; }
.cr-gp-tick--fail { background: #b04432; }
.cr-gp-eta { margin: 0 0 4px; }
@keyframes cr-gp-pulse { 0% { box-shadow: 0 0 0 0 rgba(232, 163, 61, 0.5); } 100% { box-shadow: 0 0 0 9px rgba(232, 163, 61, 0); } }
.cr-gp-steps { list-style: none; margin: 6px 0 0; padding: 0; }
.gp-step { position: relative; display: flex; gap: 12px; align-items: flex-start; padding: 8px 0; }
.gp-step:not(:last-child)::after { content: ""; position: absolute; left: 13px; top: 30px; bottom: -6px; width: 2px; background: var(--line); }
.gp-step.done:not(:last-child)::after { background: linear-gradient(var(--green), var(--green)); }
.gp-node { position: relative; flex: 0 0 28px; width: 28px; height: 28px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; background: #fff; border: 1.5px solid var(--line-strong); color: var(--ink-soft); font-size: 12px; font-weight: 700; }
.gp-idx { transition: opacity 0.15s ease; }
.gp-spin, .gp-check { position: absolute; display: none; }
.gp-step.run .gp-node { border-color: var(--amber); color: var(--amber-dark); }
.gp-step.run .gp-idx { opacity: 0; }
.gp-step.run .gp-spin { display: block; width: 15px; height: 15px; border-radius: 50%; border: 2px solid rgba(232, 163, 61, 0.3); border-top-color: var(--amber); animation: cr-gp-spin 0.7s linear infinite; }
.gp-step.done .gp-node { background: var(--green); border-color: var(--green); color: #fff; }
.gp-step.done .gp-idx { display: none; }
.gp-step.done .gp-check { display: block; }
@keyframes cr-gp-spin { to { transform: rotate(360deg); } }
.gp-body { display: flex; flex-direction: column; gap: 1px; padding-top: 3px; }
.gp-label { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.gp-step:not(.done) .gp-label { color: var(--ink-soft); }
.gp-detail { font-size: 12px; color: var(--ink-soft); min-height: 15px; }
.gp-step.done .gp-detail { color: var(--green); }
/* landing: preview + one tap into the editor */
.cr-reveal { margin-top: 18px; animation: cr-msg-slide 0.4s cubic-bezier(0.22, 1, 0.36, 1); }
.cr-reveal-head { display: flex; align-items: center; gap: 11px; margin-bottom: 12px; }
.cr-reveal-tick { flex: 0 0 26px; width: 26px; height: 26px; border-radius: 50%; background: var(--green); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 14px; }
.cr-reveal-head b { display: block; font-size: 15px; }
.cr-reveal-head p { margin: 1px 0 0; }
.cr-preview { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; }
.cr-preview-actions { display: flex; gap: 10px; flex-wrap: wrap; }
/* a tall report must not be clipped: the preview box scrolls instead —
 * the window is generous enough that the action buttons below never read
 * as a bar sitting on top of the mid-report charts */
.cr-preview .paper-stage { max-height: 640px; overflow: auto; }
.cr-hero-header { display: flex; align-items: center; gap: 12px; margin-bottom: 4px; }
.cr-hero-icon {
  width: 40px; height: 40px; border-radius: 12px;
  background: linear-gradient(135deg, var(--amber), #f0b45c);
  display: flex; align-items: center; justify-content: center;
  color: #fff; box-shadow: 0 4px 12px rgba(232, 163, 61, 0.35);
}
.cr-hero-card h3 { font-size: 22px; margin: 0; letter-spacing: -0.02em; color: var(--ink); }
.cr-hero-sub { color: var(--ink-soft); font-size: 14px; margin: 0 0 22px; padding-left: 52px; }
/* Chat area: messages with avatars */
.cr-chat-area { margin-bottom: 18px; }
.cr-messages {
  display: flex; flex-direction: column; gap: 12px;
  max-height: 200px; overflow-y: auto; padding: 4px 0;
  scroll-behavior: smooth;
}
.cr-msg { display: flex; align-items: flex-end; gap: 8px; }
.cr-msg-user { justify-content: flex-end; }
.cr-msg-enter { animation: cr-msg-slide 0.3s cubic-bezier(0.22, 1, 0.36, 1); }
@keyframes cr-msg-slide { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.cr-msg-avatar {
  width: 26px; height: 26px; border-radius: 8px; flex: 0 0 26px;
  display: flex; align-items: center; justify-content: center;
}
.cr-avatar-ai { background: linear-gradient(135deg, var(--amber), #f0b45c); color: #fff; }
.cr-avatar-user { background: var(--ink); color: #fff; }
.cr-msg-bubble {
  max-width: 80%; padding: 10px 15px; border-radius: 16px;
  font-size: 14px; line-height: 1.5;
}
.cr-msg-ai .cr-msg-bubble {
  background: rgba(23, 38, 59, 0.05); color: var(--ink);
  border-bottom-left-radius: 5px;
}
.cr-msg-user .cr-msg-bubble {
  background: var(--ink); color: #fff;
  border-bottom-right-radius: 5px;
  box-shadow: 0 2px 8px rgba(23, 38, 59, 0.15);
}
/* Typing indicator */
.cr-typing { display: inline-flex; gap: 4px; padding: 4px 0; }
.cr-typing-dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--ink-soft);
  animation: cr-dot-bounce 1.2s ease-in-out infinite;
}
.cr-typing-dot:nth-child(2) { animation-delay: 0.15s; }
.cr-typing-dot:nth-child(3) { animation-delay: 0.3s; }
@keyframes cr-dot-bounce { 0%, 60%, 100% { transform: translateY(0); } 30% { transform: translateY(-5px); } }
/* Composer: gradient-border input shell */
.cr-composer { margin-top: 6px; }
.cr-input-shell {
  position: relative; border-radius: 16px; padding: 2px;
  background: linear-gradient(135deg, rgba(232,163,61,0.3), rgba(99,102,241,0.15), rgba(232,163,61,0.2));
  transition: box-shadow 0.25s ease, background 0.25s ease;
}
.cr-input-shell:focus-within {
  background: linear-gradient(135deg, var(--amber), #6366f1, var(--amber));
  box-shadow: 0 0 0 3px rgba(232, 163, 61, 0.12), 0 4px 16px rgba(232, 163, 61, 0.12);
}
.cr-input-shell textarea {
  display: block; width: 100%; min-height: 100px; max-height: 160px;
  border: 0; border-radius: 14px; padding: 15px 54px 15px 18px;
  font: inherit; font-size: 15px; line-height: 1.5;
  background: #fff; color: var(--ink); resize: none;
  transition: none;
}
.cr-input-shell textarea:focus { outline: none; }
.cr-input-shell textarea::placeholder { color: var(--ink-soft); opacity: 0.6; }
.cr-send {
  position: absolute; right: 8px; bottom: 8px;
  width: 38px; height: 38px; border-radius: 11px; border: 0;
  background: linear-gradient(135deg, var(--amber), #d4922e);
  color: #fff; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 3px 10px rgba(232, 163, 61, 0.4);
  transition: transform 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;
}
.cr-send:hover { transform: translateY(-1px) scale(1.04); box-shadow: 0 5px 16px rgba(232, 163, 61, 0.5); }
.cr-send:disabled { opacity: 0.5; transform: none; cursor: not-allowed; }
.cr-send-loading { animation: cr-send-pulse 1s ease-in-out infinite; }
@keyframes cr-send-pulse { 0%, 100% { box-shadow: 0 3px 10px rgba(232, 163, 61, 0.4); } 50% { box-shadow: 0 3px 20px rgba(232, 163, 61, 0.7); } }
.cr-send-done { background: var(--green); box-shadow: 0 3px 10px rgba(46, 125, 91, 0.4); }
/* Example chips */
.cr-eg-row { display: flex; align-items: center; gap: 10px; margin-top: 12px; flex-wrap: wrap; }
.cr-eg-hint { font-size: 12px; color: var(--ink-soft); font-weight: 500; white-space: nowrap; }
.cr-eg { display: flex; flex-wrap: wrap; gap: 7px; }
.cr-eg-chip {
  border: 1px solid var(--line-strong); background: #fff;
  border-radius: 999px; padding: 6px 14px; font-size: 12.5px;
  cursor: pointer; color: var(--ink); white-space: nowrap;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease, transform 0.12s ease, box-shadow 0.12s ease;
}
.cr-eg-chip:hover {
  border-color: var(--amber); color: var(--amber-dark); background: #fff;
  transform: translateY(-1px); box-shadow: 0 2px 8px rgba(232, 163, 61, 0.15);
}
/* Wizard footer & steps */
.cr-foot { display: flex; gap: 10px; margin-top: 16px; }
.cr-wiz .steps { display: flex; gap: 8px; margin-bottom: 18px; }
.cr-wiz .steps span {
  flex: 1; text-align: center; font-size: 12px; font-weight: 600; color: var(--ink-soft);
  padding: 6px 0; border-bottom: 2px solid var(--line);
}
.cr-wiz .steps span.cur { color: var(--amber-dark); border-bottom-color: var(--amber); }

/* ---------- bench: in-place editor + advanced fold ---------- */
/* bench editor chrome (was written inline on every element) */
.wb-head-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: flex-end; max-width: 100%; }
.wb-head-actions .save-state { margin-right: auto; }
.insp-title { font-size: 15px; margin: 0 0 10px; }
/* WP4c (2026-10-19 V3 walk): KPI/shape item rows cram name + variable + two buttons
   into the ~190px inspector column; at that width the metric-name input clipped
   (“获客成”). Let the row wrap so the variable + buttons drop to a second line and the
   name keeps room, and let the name shrink below its content instead of pushing out. */
.field-row { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.field-row input[type="text"] { min-width: 0; }
.field-row select { flex: 1; }
.field-hint { margin: 0 0 4px; }
.wc-adv { margin: 12px 0 4px; }
.wc-adv summary { cursor: pointer; font-weight: 600; font-size: 12.5px; color: var(--ink-soft); }

/* ---------- bench data sources (the data tab: grid + connector rail) ---------- */
.ds-type-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; margin-top: 12px; }
.ds-type {
  display: flex; flex-direction: column; gap: 4px; text-align: left; cursor: pointer;
  border: 1px solid var(--line); border-radius: 12px; background: #fff; padding: 13px 14px;
  font: inherit; transition: border-color 0.14s ease, box-shadow 0.14s ease, transform 0.14s ease;
}
.ds-type:hover { border-color: var(--amber); box-shadow: var(--shadow); transform: translateY(-2px); }
.ds-type .dst-icon { font-size: 17px; color: var(--amber-dark); line-height: 1; }
.ds-type b { font-size: 13.5px; color: var(--ink); }

/* ---------- bench style swatches (the head's style dialog) ---------- */
.st-color { margin-left: auto; width: 52px; height: 30px; flex: 0 0 auto; border: 1px solid var(--line-strong); border-radius: 7px; background: var(--card); padding: 2px; }

/* ---------- connector marketplace (App Store-shaped grid + install wizard) ---------- */
.mk-hero { border-left: 4px solid var(--amber); margin-bottom: 18px; }
.mk-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 14px; }
.mk-card {
  display: flex; gap: 12px; align-items: flex-start; padding: 18px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  transition: transform 0.14s ease, box-shadow 0.14s ease, border-color 0.14s ease;
}
.mk-card:hover { transform: translateY(-2px); box-shadow: var(--shadow); border-color: var(--amber); }
.mk-icon {
  width: 42px; height: 42px; flex: 0 0 42px; border-radius: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, var(--amber), #d4922e); color: #fff; font-size: 20px; font-weight: 800;
}
.mk-body { flex: 1 1 auto; min-width: 0; }
.mk-body h3 { margin: 0 0 3px; font-size: 15px; }
.mk-meta { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 0; }
.mk-tpl-grid { display: grid; grid-template-columns: 1fr; gap: 8px; }
.mk-tpl {
  display: flex; gap: 8px; align-items: center; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: 10px; background: #fff; cursor: pointer;
}
.mk-tpl:hover { border-color: var(--amber); }
.mk-tpl input { accent-color: var(--amber); }
.mk-tpl b { font-size: 13.5px; }
.mk-tree { list-style: none; padding: 0; margin: 6px 0; }
.mk-tree li { display: flex; gap: 8px; align-items: baseline; padding: 4px 0; border-bottom: 1px dashed var(--line); font-size: 12.5px; }
.mk-tree li:last-child { border-bottom: 0; }
.mk-tree em { color: var(--ink-soft); font-style: normal; }

/* ---------- layout tier chip (recipients list + canvas preview bar) ---------- */
.chip-tier { background: rgba(23, 38, 59, 0.08); color: var(--ink-soft); font-weight: 600; }
.chip-tier[data-tier="exec"] { background: rgba(99, 102, 241, 0.14); color: #3b3f91; }
.chip-tier[data-tier="standard"] { background: rgba(232, 163, 61, 0.20); color: var(--warn-ink); }
.chip-tier[data-tier="detail"] { background: rgba(46, 125, 91, 0.14); color: #1f6a48; }
.wc-tierbar { display: inline-flex; align-items: center; margin-left: 4px; }

/* ---------- client modal: block order + private blocks ---------- */
.cm-order { list-style: none; padding: 0; margin: 6px 0; }
.cm-order li {
  display: flex; gap: 8px; align-items: center; padding: 6px 8px;
  border: 1px solid var(--line); border-radius: 8px; background: #fff; margin-bottom: 6px;
  font-size: 13px; cursor: grab;
}
.cm-order li[draggable="true"]:hover { border-color: var(--amber); }
.cm-order .cm-handle { color: var(--ink-soft); font-size: 12px; letter-spacing: -2px; }
.cm-order .cm-name { flex: 1; }
.cm-order .cm-actions { display: flex; gap: 4px; }
.cm-order .cm-actions button { width: 24px; height: 24px; border-radius: 6px; border: 1px solid var(--line-strong); background: #fff; cursor: pointer; color: var(--ink-soft); }
.cm-order .cm-actions button:disabled { opacity: 0.35; cursor: default; }
.cm-pb { padding: 8px 0; border-top: 1px dashed var(--line); }
.cm-pb textarea { width: 100%; border: 1px solid var(--line-strong); border-radius: 8px; padding: 6px 8px; font: inherit; font-size: 13px; margin-top: 4px; }

/* ---------- compose: narrative review panel + tone bar ---------- */
.run-tonebar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin: 12px 0 8px; }
.tone-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px;
  border: 1px solid var(--line-strong); border-radius: 999px; background: #fff;
  font-size: 12.5px; cursor: pointer; color: var(--ink-soft);
}
.tone-chip input { accent-color: var(--amber); }
.tone-chip:has(input:checked) { background: var(--ink); color: #fff; border-color: var(--ink); }
.cp-review-row { padding: 10px 0; border-top: 1px solid var(--line); }
.cp-review-head { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.cp-review-head button { margin-left: auto; }
.cp-draft-status { min-height: 16px; margin: 4px 0 0; }

/* ---------- delivery evidence block in drawer ---------- */
.dns-records { margin-top: 10px; }
.dns-row { display: grid; grid-template-columns: 66px 1fr auto; gap: 8px; align-items: center; margin-bottom: 6px; }
.dns-row .dns-kind { font-family: ui-monospace, Menlo, monospace; font-size: 12px; color: var(--ink-soft); }
.dns-row textarea { width: 100%; border: 1px solid var(--line-strong); border-radius: 8px; padding: 6px 8px; font-family: ui-monospace, Menlo, monospace; font-size: 12px; }
.dns-state { justify-self: end; }

/* ---------- report paper: the 6 new widgets ---------- */
.rp-gauge { display: flex; gap: 14px; align-items: center; }
.rp-gauge-track { position: relative; flex: 1; height: 14px; background: rgba(23, 38, 59, 0.08); border-radius: 999px; overflow: hidden; }
.rp-gauge-fill { height: 100%; background: var(--amber); border-radius: 999px; }
.rp-gauge-tick { position: absolute; top: -3px; bottom: -3px; left: 100%; width: 2px; background: var(--ink); }
.rp-gauge--ok .rp-gauge-fill { background: var(--green); }
.rp-gauge--low .rp-gauge-fill { background: var(--red); }
.rp-gauge-meta strong { font-size: 18px; letter-spacing: -0.01em; }
.rp-gauge-meta span { display: block; font-size: 12px; color: var(--rp-label); }
.rp-funnel-row { display: grid; grid-template-columns: 96px 1fr auto; gap: 10px; align-items: center; padding: 4px 0; font-size: 13px; }
.rp-funnel-label { color: var(--rp-label); }
.rp-funnel-track { height: 12px; border-radius: 999px; background: rgba(23, 38, 59, 0.05); overflow: hidden; }
.rp-funnel-bar { display: block; height: 100%; background: var(--ink); }
.rp-funnel-value strong { font-weight: 700; }
.rp-funnel-drop { font-style: normal; font-size: 11px; color: var(--red); margin-left: 4px; }
.rp-quote { border-left: 3px solid var(--amber); padding-left: 14px; }
.rp-quote-text { margin: 0; font-size: 14px; font-style: italic; color: var(--rp-text); }
.rp-quote-who { margin: 6px 0 0; font-size: 12px; color: var(--rp-label); }
.rp-callout { border-radius: 10px; padding: 12px 14px; }
.rp-callout--info { background: rgba(23, 38, 59, 0.05); }
.rp-callout--warn { background: rgba(232, 163, 61, 0.14); }
.rp-callout--risk { background: rgba(176, 68, 50, 0.10); }
.rp-callout-tag { display: inline-block; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--warn-ink); }
.rp-callout--risk .rp-callout-tag { color: #963425; }
.rp-callout-text { margin: 4px 0 0; font-size: 13.5px; color: var(--rp-text); }
.rp-logo-wall { display: grid; grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); gap: 10px; }
.rp-logo-cell { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 10px 6px; border: 1px solid var(--rp-rule); border-radius: 10px; min-width: 0; }
.rp-logo-mark { width: 30px; height: 30px; border-radius: 8px; background: var(--ink); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-weight: 800; font-size: 12px; }
.rp-logo-name { font-size: 11.5px; color: var(--rp-label); text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.rp-ba { display: flex; gap: 16px; align-items: center; }
.rp-ba-col { flex: 1 1 0; min-width: 0; padding: 10px 12px; border: 1px solid var(--rp-rule); border-radius: 10px; }
.rp-ba-tag { display: block; font-style: normal; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--rp-label); margin-bottom: 4px; }
.rp-ba-col strong { font-size: 18px; letter-spacing: -0.01em; overflow-wrap: anywhere; }
.rp-ba-arrow { color: var(--rp-label); font-size: 20px; }
.rp-ba-cap { margin: 8px 0 0; font-size: 12.5px; color: var(--rp-text); }
html[lang^="zh"] .rp-ba-tag { text-transform: none; letter-spacing: 0; }

/* standalone delivery-receipt page (report/evidence.html) */
.ev-wrap { max-width: 620px; margin: 0 auto; }
.ev-head { display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap; margin-bottom: 12px; }
.ev-head h2 { margin: 0; font-size: 20px; }
.ev-kv { display: grid; grid-template-columns: 120px 1fr; gap: 8px 12px; margin: 12px 0; }
.ev-kv dt { color: var(--ink-soft); font-size: 12.5px; text-transform: uppercase; letter-spacing: 0.05em; }
.ev-kv dd { margin: 0; font-size: 13.5px; }
.ev-events { margin: 8px 0 0; }
html[lang^="zh"] .ev-kv dt { text-transform: none; letter-spacing: 0; }

/* ---------- period warehouse browser (view/data/warehouse.html) ---------- */
.wh-hero { border-left: 4px solid var(--amber); margin-bottom: 18px; }
.wh-nav { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0 16px; }
.wh-nav .fchip { border: 1px solid var(--line-strong); background: #fff; border-radius: 999px; padding: 5px 13px; font-size: 13px; cursor: pointer; color: var(--ink-soft); }
.wh-nav .fchip.on { background: var(--ink); color: #fff; border-color: var(--ink); }
.wh-grid-card { overflow-x: auto; }
.wh-cell { cursor: pointer; position: relative; padding-right: 26px !important; }
.wh-cell:hover { background: rgba(232, 163, 61, 0.10); }
.wh-cell:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
.wh-cell--miss { background: repeating-linear-gradient(45deg, rgba(176,68,50,0.05), rgba(176,68,50,0.05) 6px, transparent 6px, transparent 12px); }
.wh-cell--warn { box-shadow: inset 3px 0 0 var(--red); }
.wh-flag { position: absolute; right: 8px; top: 8px; color: var(--red); font-size: 12px; }
.wh-src { position: absolute; right: 8px; top: 6px; font-size: 9px; line-height: 1.4; color: var(--ink-soft); background: rgba(23, 38, 59, 0.06); border-radius: 4px; padding: 1px 4px; opacity: .85; }
/* V8: design-mode sample figures carry a tag — fake numbers are worse than an empty box */
.rp-w { position: relative; }
.rp-sample-tag { position: absolute; right: 10px; top: 8px; font-size: 10px; color: var(--ink-soft); background: rgba(127,127,127,.12); border-radius: 6px; padding: 1px 6px; }
.wg-realnote { display: block; }
/* V6: a stored cell with no provenance chain says “—” quietly instead of nothing at all */
.wh-src--none { opacity: .45; background: none; padding: 1px 2px; }
/* V6 knife 1: the “canvas shows X's real numbers” proof line inside the view menu */
.wt-realnote { display: block; flex: 1 0 100%; margin: 2px 0 0; }
/* 目标角标：◎ 只在设了目标的格子出现，悬停看具体值（与 ⚠/来源点共用右侧轨道） */
.wh-target { position: absolute; right: 36px; top: 7px; font-size: 11px; color: var(--amber-dark); }
.wh-rows { font-style: italic; color: var(--ink-soft); font-size: 12.5px; }
.wh-prov { display: grid; grid-template-columns: 88px 1fr; gap: 6px 12px; margin: 8px 0 14px; }
.wh-prov dt { color: var(--ink-soft); font-size: 12.5px; }
.wh-prov dd { margin: 0; font-size: 13.5px; }
.wh-src-card { margin-top: 18px; }

/* ---------- bind lines (data wiring, one row per widget bind) ---------- */
.bl-wrap { display: block; }
.bl-grp { margin-bottom: 10px; }
.bl-grp-h { font-size: 12.5px; font-weight: 600; color: var(--ink-soft); padding: 6px 0 2px; }
.bl { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; padding: 6px 10px; border-radius: 9px; background: rgba(23, 38, 59, 0.03); border: 1px solid transparent; margin-bottom: 4px; font-size: 13px; }
.bl--ok { background: rgba(46, 125, 91, 0.08); border-color: rgba(46, 125, 91, 0.25); }
.bl--warn { background: rgba(232, 163, 61, 0.10); border-color: rgba(232, 163, 61, 0.32); }
.bl--none { background: rgba(176, 68, 50, 0.06); border-color: rgba(176, 68, 50, 0.28); }
.bl-ic { width: 16px; text-align: center; flex: 0 0 16px; font-weight: 700; }
.bl--ok .bl-ic { color: var(--green); }
.bl--warn .bl-ic { color: var(--amber-dark); }
.bl--none .bl-ic { color: var(--red); }
.bl-arrow { color: var(--ink-soft); }
.bl-field { color: var(--ink); font-weight: 600; }
.bl-src { margin-left: auto; font-size: 12px; }
.bl-msg { color: var(--ink-soft); font-size: 12.5px; }
.bl-act { border: 1px solid currentColor; background: #fff; border-radius: 999px; padding: 3px 10px; font: inherit; font-size: 12px; cursor: pointer; color: var(--ink); }
.bl-act:hover { background: rgba(23, 38, 59, 0.06); }
.bl--none .bl-act { color: var(--amber-dark); border-color: currentColor; }

/* the report's own period slice on the data section */

/* bind lines: candidate picker + the staggered "check is running" pulse */
@keyframes blIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.bl { animation: blIn 0.35s ease both; }
.bl-cand { border: 1px solid var(--line-strong); border-radius: 8px; padding: 3px 8px; font: inherit; font-size: 12.5px; background: #fff; color: var(--ink); }

/* store period timeline: hollow dots mark periods that never arrived */
.wh-tl { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; margin: 4px 0 14px; }
.tl-dot { border: 1px solid var(--line-strong); background: #fff; border-radius: 999px; padding: 5px 13px; font-size: 13px; cursor: pointer; color: var(--ink-soft); }
.tl-dot.on { background: var(--ink); color: #fff; border-color: var(--ink); font-weight: 600; }
.tl-dot.off { width: 30px; padding: 5px 0; text-align: center; color: var(--line-strong); border-style: dashed; cursor: default; }
.wh-spark { width: 46px; height: 14px; margin-left: 7px; vertical-align: middle; }
.wh-spark polyline { fill: none; stroke: var(--ink-soft); stroke-width: 1.6; }
.wh-spark.up polyline { stroke: var(--green); }
.wh-spark.down polyline { stroke: var(--red); }
.wh-th { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
.cp-nostore { display: inline-flex; gap: 8px; align-items: center; flex-wrap: wrap; padding: 8px 12px; background: rgba(232, 163, 61, 0.08); border: 1px dashed rgba(143, 95, 13, 0.4); border-radius: 10px; margin: 0; font-size: 13px; }

/* ---------- compose 3 beats ---------- */
.cp-steps { margin-bottom: 12px; }
.cp-diff { padding: 10px 0; border-top: 1px dashed var(--line); }
.cp-diff-h { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; margin-bottom: 4px; }
.cp-dcs { display: flex; flex-wrap: wrap; gap: 6px; }
.cp-dc { display: inline-flex; gap: 6px; align-items: baseline; padding: 4px 10px; border-radius: 999px; font-size: 12.5px; background: rgba(23, 38, 59, 0.05); }
.cp-dc em { font-style: normal; color: var(--ink-soft); }
.cp-dc b { font-weight: 700; }
.cp-dc.up { background: rgba(46, 125, 91, 0.10); color: #1f6a48; }
.cp-dc.down { background: rgba(176, 68, 50, 0.10); color: #963425; }
.cp-dc.tgt { background: rgba(232, 163, 61, 0.14); color: #8f5f0d; }
/* 证据页第一证据：本期数据行快照折叠区（默认收起，对账时展开逐键核对） */
.ev-snap { margin-top: 14px; }
.ev-snap summary { cursor: pointer; font-size: 12.5px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft); }
.ev-snap .tbl { margin-top: 8px; }
.cp-diff-wait { padding: 8px 0; }

/* ---------- compose pre-flight summary + run outcome ---------- */
.cp-pf { margin-top: 12px; }
.pf-card { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; padding: 10px 12px; border: 1px solid var(--line); border-left: 4px solid var(--amber); border-radius: 10px; background: rgba(23, 38, 59, 0.03); font-size: 13px; }
.pf-list { margin: 8px 0 0; padding-left: 18px; font-size: 12.5px; color: var(--ink-soft); }
.pf-list li { margin: 2px 0; }
/* the pre-send wording gate panel: one row per breaching note, the offender's
   numbers stay on screen next to the reason, and each row ends in a visible call */
.gate-panel { margin-top: 4px; }
.gate-row { padding: 10px 12px; margin-top: 8px; border: 1px solid var(--line); border-left: 4px solid var(--red); border-radius: 10px; }
.gate-row.is-done { border-left-color: var(--green); background: rgba(46, 125, 91, 0.05); }
.gate-row strong { font-size: 13.5px; }
.gate-text { margin: 6px 0 4px; font-size: 13px; color: var(--ink); background: rgba(23, 38, 59, 0.04); border-radius: 8px; padding: 7px 10px; }
.gate-why { margin: 4px 0 8px; padding-left: 18px; font-size: 12.5px; color: var(--red); }
.gate-why li { margin: 2px 0; }
.gate-acts { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.run-summary { margin-top: 14px; border-left: 4px solid var(--green); }
.run-summary .chip { margin-right: 6px; }

/* ---------- mapping: stranded rows + bind cross-check ---------- */
.ds-assign { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; padding: 8px 0; border-top: 1px dashed var(--line); }
.ds-assign code { background: rgba(23, 38, 59, 0.06); padding: 1px 6px; border-radius: 6px; }
.ds-assign select { border: 1px solid var(--line-strong); border-radius: 8px; padding: 6px 8px; font: inherit; font-size: 13px; }
.ds-assign input { border: 1px solid var(--line-strong); border-radius: 8px; padding: 6px 8px; font: inherit; font-size: 13px; }
.ds-cross { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }

/* ---------- delivery drawer: sibling navigation + demo labelling ---------- */
.dd-nav { display: flex; gap: 8px; align-items: center; justify-content: space-between; margin-bottom: 10px; padding-bottom: 8px; border-bottom: 1px solid var(--line); }
.chip-demo { background: rgba(99, 102, 241, 0.12); color: #3f47a8; font-size: 11px; }

/* ---------- schedule editor: one column, sentence at the foot ---------- */
/* the left-hand chat column retired with it: two authorities painting one plan
   read as noise — the form is now the only surface (see console.js scheduleCanvas) */
.sc-panel { display: flex; flex-direction: column; gap: 6px; }
.sc-seg { display: flex; flex-wrap: wrap; gap: 4px; }
.sc-seg button { border: 1px solid var(--line-strong); background: #fff; border-radius: 999px; padding: 5px 12px; font: inherit; font-size: 12.5px; cursor: pointer; color: var(--ink-soft); }
.sc-seg button.on { background: var(--ink); color: #fff; border-color: var(--ink); }
.sc-anchor { display: flex; gap: 10px; flex-wrap: wrap; }
.sc-mini { flex: 0 0 auto; min-width: 110px; }
.sc-mini input[type="number"], .sc-mini input[type="time"], .sc-mini select { width: 100%; border: 1px solid var(--line-strong); border-radius: 8px; padding: 6px 8px; font: inherit; font-size: 13px; }
.sc-rules { margin-top: 6px; }
.sc-rule-gap { margin-top: 8px; }
.sc-months { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.sc-mch { border: 1px solid var(--line-strong); background: #fff; border-radius: 999px; padding: 4px 10px; font: inherit; font-size: 12.5px; cursor: pointer; color: var(--ink-soft); }
.sc-mch.on { background: var(--amber); color: var(--ink-deep); border-color: var(--amber); font-weight: 600; }
.sc-sent-wrap { margin-top: 12px; padding: 10px 12px; background: rgba(23, 38, 59, 0.04); border-left: 3px solid var(--amber); border-radius: 8px; }
.sc-sent { margin: 0; font-size: 13px; line-height: 1.5; color: var(--ink); }
/* recipient picker: the outer library, searched and quick-picked — rows carry
   the name (ellipsis, like every other rail) and the mail-count tail */
.sc-poolhead { display: flex; align-items: center; gap: 10px; }
.sc-q { flex: 1 1 auto; min-width: 0; border: 1px solid var(--line-strong); border-radius: 999px; padding: 6px 14px; font: inherit; font-size: 13px; background: #fff; color: var(--ink); }
.sc-poolacts { display: inline-flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.sc-link { border: 0; background: none; font: inherit; font-size: 12.5px; color: var(--amber-dark); cursor: pointer; padding: 0; }
.sc-link:hover { text-decoration: underline; }
.sc-pool { max-height: 220px; overflow-y: auto; border: 1px solid var(--line); border-radius: 10px; padding: 6px 10px; display: flex; flex-direction: column; gap: 2px; }
.sc-pick { border-radius: 8px; padding: 6px 8px; font-size: 13.5px; transition: background 0.12s ease; }
.sc-pick:hover { background: rgba(23, 38, 59, 0.05); }
.sc-pick.is-picked { background: rgba(232, 163, 61, 0.10); }
.sc-pick .sc-pick-n { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* create wizard's new data-wiring step */
.cr-data-bar { margin: 4px 0 12px; }
.cr-data-stat { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px; background: rgba(23, 38, 59, 0.03); }


/* ---------- bench save state + shared shell chrome ---------- */
.save-state { font-size: 12.5px; color: var(--ink-soft); white-space: nowrap; }
.save-state.is-dirty { color: var(--warn-ink); font-weight: 600; }
/* the rail is unreachable once the sidebar collapses, so the topbar carries the toggle */
.top-toggle {
  display: none; align-items: center; justify-content: center;
  width: 38px; height: 38px; flex: 0 0 38px; border-radius: 10px; cursor: pointer;
  border: 1px solid var(--line-strong); background: var(--card); color: var(--ink);
}
.top-toggle svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.9; stroke-linecap: round; }
.side-scrim {
  position: fixed; inset: 0; z-index: 98; background: rgba(15, 28, 46, 0.45);
  border: 0; padding: 0; cursor: pointer;
}
.side-scrim[hidden] { display: none; }

/* ---------- focus visibility, one rule for the whole shell ---------- */
.app-root a:focus-visible, .abtn:focus-visible, .fchip:focus-visible, .ds-type:focus-visible,
.widget-item:focus-visible, .rep-card:focus-visible, .qrow:focus-visible,
table:focus-visible, .td-link:focus-visible, .acct-trigger:focus-visible, .top-toggle:focus-visible,
.acct-menu-item:focus-visible, .acct-out:focus-visible, .w-del:focus-visible, .w-move button:focus-visible,
.grp-item:focus-visible, .grp-tools button:focus-visible,
.toast-undo:focus-visible, summary:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}
.app-side a:focus-visible, .app-side button:focus-visible, .app-root .app-side :focus-visible { outline-color: #cfd8e6; }

/* ---------- reduced motion: the app animates as much as the marketing pages do ---------- */
@media (prefers-reduced-motion: reduce) {
  .app-root *, .app-root *::before, .app-root *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
  .app-view { scroll-behavior: auto; }
}

/* ---------- CJK typography: no tiny tracked-out capitals for Han characters ---------- */
html[lang^="zh"] .insp-label, html[lang^="zh"] .pipe-txt em, html[lang^="zh"] .stat .stat-label,
html[lang^="zh"] .tbl th, html[lang^="zh"] .rp-w-title, html[lang^="zh"] .rp-table th,
html[lang^="zh"] .rp-w-fb-head, html[lang^="zh"] .side-reports-label, html[lang^="zh"] .demo-chip,
html[lang^="zh"] .wb-kind { text-transform: none; letter-spacing: 0; }

/* ---------- responsive ---------- */
@media (max-width: 1180px) {
  .wb { grid-template-columns: 260px minmax(0, 1fr); }
  .wb-cols { grid-template-columns: minmax(0, 1fr); }
  .wb-main-col { padding-right: 0; }
  .wb-side { border-left: 0; border-top: 1px solid var(--line); padding-left: 0; padding-top: 16px; margin-top: 18px; grid-column: 1 / -1; }
  .recip { grid-template-columns: minmax(0, 1fr); }
  .recip-side { position: static; max-height: none; }
}
@media (max-width: 900px) {
  /* the drawer opens over the page — hiding the rail used to strand phones
     with no section switch, no sign-out and no language control */
  .app-side { display: flex; position: fixed; z-index: 99; inset: 0 auto 0 0; width: min(300px, 86vw); padding: 18px 14px calc(18px + env(safe-area-inset-bottom)); transform: translateX(-102%); transition: transform 0.22s ease; box-shadow: 12px 0 40px rgba(15, 28, 46, 0.28); }
  body.side-open .app-side { transform: none; }
  .top-toggle { display: inline-flex; }
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
  .grid-2 { grid-template-columns: 1fr; }
  /* stacked: the fixed-height grid gives way to a normal scrolling column */
  body.route-workbench .app-view { overflow-y: auto; }
  body.route-workbench .view-in { height: auto; }
  .wb { grid-template-columns: 1fr; height: auto; }
  .wb-chat { height: 60vh; }
  .rb { grid-template-columns: 1fr; }
  .app-view { padding: 18px 14px; }
  body.route-workbench .app-top { padding: 11px 14px 12px; }
  .toasts { left: 14px; right: 14px; bottom: 14px; align-items: flex-end; }
  .toast { max-width: none; width: 100%; }
}

/* =====================================================================
   Liquid Glass shell — the workspace in the same material as the site

   Mirrors the marketing recipe in site.css (body.lg): a colour scene for the
   glass to refract, a specular edge so a surface reads as glass rather than
   as washed-out white, and two grades — quiet glass for content panels,
   stronger floating glass for shell chrome. Two deliberate differences:
   the scene here does not drift (a workspace must not move behind the work),
   and glass stays on the container layer only — chips, rows, tabs and inputs
   inside a panel keep their solid faces, so glass never stacks on glass.
   ===================================================================== */

/* 1) the wallpaper every surface refracts — brand amber, ink and green */
.app-root {
  background-color: #f7f7f5;
  background-image:
    /* a soft white field first: the brand radials alone tint the whole shell
       amber, and the workspace needs to read neutral while you work.
       Held at half strength: a bigger wash turned the middle of the shell into
       a white void, and a white panel over a white field has no edge at all */
    radial-gradient(24% 18% at 40% 6%, rgba(255, 255, 255, 0.5), transparent 80%),
    radial-gradient(58% 44% at 88% 0%, rgba(236, 170, 66, 0.17), transparent 70%),
    radial-gradient(48% 40% at 4% 30%, rgba(23, 38, 59, 0.1), transparent 72%),
    radial-gradient(42% 36% at 62% 100%, rgba(46, 125, 91, 0.1), transparent 76%),
    linear-gradient(180deg, #fcfcfa 0%, #f7f6f2 55%, #f2f1ec 100%);
  background-repeat: no-repeat;
  /* one rim recipe for every glass surface in the shell: bright where the light
     strikes (top-left), then an ink hairline along the shaded side. A rim made
     only of white stops is invisible over this wallpaper, which is what made
     panels read as a shapeless wash — the ink stops are what draw the edge */
  --glass-rim: linear-gradient(140deg,
    rgba(255, 255, 255, 0.95) 0%,
    rgba(23, 38, 59, 0.1) 34%,
    rgba(23, 38, 59, 0.16) 68%,
    rgba(23, 38, 59, 0.22) 100%);
}

/* 2) quiet grade — the content panels */
.app-root .card {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.84), rgba(255, 255, 255, 0.68));
  border-color: transparent;
  -webkit-backdrop-filter: blur(18px) saturate(170%);
  backdrop-filter: blur(18px) saturate(170%);
  margin-bottom: 20px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.9),
    inset 0 -1px 0 rgba(23, 38, 59, 0.05),
    0 1px 2px rgba(23, 38, 59, 0.07),
    0 12px 30px rgba(23, 38, 59, 0.1);
}
/* the card variants that carry an accent stripe must keep it — the transparent
   border above would otherwise silently eat four different rules */
.app-root .ai-bar,
.app-root .ov-queue,
.app-root .mk-hero,
.app-root .wh-hero { border-left-color: var(--amber); }
.app-root .run-summary { border-left-color: var(--green); }
/* .wh-grid-card scrolls sideways: an absolutely placed ring would slide away
   with the rows, so that one keeps the inset rim only */
.app-root .card:not(.wh-grid-card) { position: relative; }
.app-root .card:not(.wh-grid-card)::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: var(--glass-rim);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
}
/* a panel whose control took focus lights up like the site's cards do */
.app-root .card:focus-within {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.95),
    inset 0 0 0 1px rgba(232, 163, 61, 0.32),
    0 12px 30px rgba(23, 38, 59, 0.1);
}
/* sheen only on cards you can actually click — a form panel must not shimmer
   while the reader is typing next to it */
.app-root .card.rep-card::after,
.app-root .card.tpl-card::after,
.app-root .mk-card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(115deg,
    transparent 32%, rgba(255, 255, 255, 0.3) 47%,
    rgba(255, 255, 255, 0.05) 54%, transparent 66%);
  opacity: 0;
  transition: opacity 0.45s ease;
  pointer-events: none;
}
.app-root .card.rep-card:hover::after,
.app-root .card.tpl-card:hover::after,
.app-root .mk-card:hover::after { opacity: 1; }

/* glass never stacks: a panel sitting inside a panel, a sheet or a drawer
   falls back to a flat inner face with a hairline, the way the material does
   when it has nothing of its own to refract */
.app-root .card .card,
.app-root .modal .card,
.app-root .drawer .card {
  background: rgba(255, 255, 255, 0.52);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow: inset 0 0 0 1px var(--line);
}
.app-root .card .card::before,
.app-root .modal .card::before,
.app-root .drawer .card::before { display: none; }
/* a card that carries its own status border colour (the red bounce warnings are
   written inline) keeps that colour honest — the ring would wash the alert red */
.app-root .card[style*="border-color"]::before { display: none; }

/* 3) floating grade — the shell chrome */
.app-root .app-top {
  background: rgba(246, 243, 236, 0.55);
  border-bottom: 0;
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.85),
    inset 0 -1px 0 rgba(23, 38, 59, 0.06),
    0 6px 18px rgba(23, 38, 59, 0.07);
  transition: background 0.3s ease, box-shadow 0.3s ease;
  min-height: 98px;
}
/* scroll edge — the bar densifies once content runs under it (is-scrolled is
   set by runtime.js; the marketing bar does the same thing on a scroll timeline) */
.app-root .app-top.is-scrolled {
  background: rgba(246, 243, 236, 0.8);
  -webkit-backdrop-filter: blur(26px) saturate(190%);
  backdrop-filter: blur(26px) saturate(190%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.9),
    inset 0 -1px 0 rgba(23, 38, 59, 0.08),
    0 10px 26px rgba(23, 38, 59, 0.12);
}
/* the rail becomes a vibrant dark panel, the way macOS sidebars sit over
   the wallpaper. It scrolls, so its edge is an inset rim, not a ring pseudo */
.app-root .app-side {
  background: linear-gradient(180deg, rgba(21, 33, 49, 0.93), rgba(14, 25, 39, 0.88));
  -webkit-backdrop-filter: blur(22px) saturate(150%);
  backdrop-filter: blur(22px) saturate(150%);
  box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.16);
}

/* sheets and popovers — frosted above a blurred backdrop, as on iOS */
.app-root .overlay { background: rgba(15, 28, 46, 0.32); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
.app-root .side-scrim { background: rgba(15, 28, 46, 0.32); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); }
.app-root .modal,
.app-root .drawer {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.88), rgba(255, 255, 255, 0.76));
  border: 1px solid rgba(255, 255, 255, 0.7);
  -webkit-backdrop-filter: blur(26px) saturate(180%);
  backdrop-filter: blur(26px) saturate(180%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.95),
    0 4px 12px rgba(15, 28, 46, 0.1),
    0 30px 80px rgba(15, 28, 46, 0.26);
}
#account-widget .acct-menu {
  background: rgba(28, 42, 65, 0.84);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  backdrop-filter: blur(18px) saturate(160%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16), 0 12px 30px rgba(0, 0, 0, 0.4);
}
/* ::before is the status dot, so the toast carries its highlight as an inset rim */
.app-root .toast {
  background: linear-gradient(180deg, rgba(21, 33, 49, 0.93), rgba(14, 25, 39, 0.88));
  border: 1px solid rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  backdrop-filter: blur(14px) saturate(150%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 2px 6px rgba(15, 28, 46, 0.3), 0 14px 34px rgba(15, 28, 46, 0.26);
}
/* the editor stage: an opaque paper on a translucent stage over the scene —
   the same composition as the hero card on the marketing home */
.app-root .paper-stage {
  background: rgba(255, 255, 255, 0.34);
  border-color: rgba(255, 255, 255, 0.6);
  -webkit-backdrop-filter: blur(12px) saturate(150%);
  backdrop-filter: blur(12px) saturate(150%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8);
}

/* the composer sheet keeps its own aurora but takes the shared grade, so the
   shell reads as one material from screen to screen */
.app-root .cr-hero-card {
  position: relative;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.74), rgba(255, 255, 255, 0.56));
  border-color: transparent;
  -webkit-backdrop-filter: blur(22px) saturate(180%);
  backdrop-filter: blur(22px) saturate(180%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.92),
    inset 0 -1px 0 rgba(23, 38, 59, 0.04),
    0 4px 24px rgba(23, 38, 59, 0.07),
    0 24px 64px rgba(23, 38, 59, 0.12);
}
.app-root .cr-hero-card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: var(--glass-rim);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
}

/* press response across the shell's controls */
.app-root .abtn:active,
.app-root .fchip:active,
.app-root .wb-tab:active,
.app-root .wb-tabs-toggle:active,
.app-root .tl-dot:active,
.app-root .tone-chip:active,
.app-root .sc-seg button:active,
.app-root .sc-mch:active,
.app-root .top-toggle:active,
.app-root .w-move button:active,
.app-root .w-del:active {
  transform: translateY(1px) scale(0.985);
  filter: brightness(0.96);
}

/* the phone drawer slides on a transform, and a transformed backdrop is where
   engines get unreliable — the rail goes near-opaque there instead */
@media (max-width: 900px) {
  .app-root .app-side {
    background: rgba(15, 28, 46, 0.97);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* no backdrop support: keep the shape, lose the translucency */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .app-root .card, .app-root .modal, .app-root .drawer, .app-root .paper-stage,
  .app-root .cr-hero-card { background: var(--card); }
  .app-root .app-top { background: rgba(246, 243, 236, 0.96); }
  .app-root .app-side { background: var(--ink); }
  .app-root .toast { background: var(--ink); }
  #account-widget .acct-menu { background: #1c2a41; }
  .app-root .overlay, .app-root .side-scrim { -webkit-backdrop-filter: none; backdrop-filter: none; }
}

/* increased contrast: take the wash out, put every surface on paper */
@media (prefers-contrast: more) {
  .app-root { background: var(--paper); }
  .app-root .card, .app-root .modal, .app-root .drawer, .app-root .paper-stage,
  .app-root .cr-hero-card, .app-root .app-top, .app-root .app-top.is-scrolled {
    background: #fff;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .app-root .app-side { background: var(--ink-deep); -webkit-backdrop-filter: none; backdrop-filter: none; }
  .app-root .toast { background: var(--ink-deep); -webkit-backdrop-filter: none; backdrop-filter: none; }
}
/* ---------- send-time AI rule on one block + the data wizard ---------- */
/* the corner badge marks blocks whose words the scheduled press rewrites
   (aiFill + aiPrompt); it rides the workbench canvas only, never a shipped face */
body.route-workbench .paper .blk-ai {
  position: absolute; top: 6px; left: 6px; z-index: 2; pointer-events: none;
  font-size: 10px; font-weight: 700; letter-spacing: 0.06em; color: #fff;
  background: var(--amber-dark); border-radius: 5px; padding: 1px 6px;
}
/* the pinned-wording corner marks blocks whose author-written copy wins over the
   bound data on every face (contentPinned); it rides the canvas only, and sits
   bottom-left so it never stacks on the AI badge (top-left) or the tools (top-right) */
body.route-workbench .paper .blk-pin {
  position: absolute; bottom: 6px; left: 6px; z-index: 2; pointer-events: none;
  color: #fff; background: var(--green); border-radius: 5px; padding: 2px 5px; line-height: 0;
}
/* the chat rail names the block its next ask will land on */
.wb-chat-focus {
  display: none; margin: 8px 0 0; flex: 0 0 auto; width: fit-content; max-width: 100%;
  font-size: 12px; color: var(--amber-dark); background: rgba(232, 163, 61, 0.12);
  border: 1px solid rgba(232, 163, 61, 0.4); border-radius: 999px; padding: 3px 10px;
}
.wb-chat-focus.on { display: block; }
/* inspector: the per-block rewrite rule sits in the front layer, one fold below
   the wording fields — this is what the send-time AI is allowed to touch */
.insp-ai { margin-top: 14px; padding-top: 10px; border-top: 1px dashed var(--line-strong); }
.insp-ai textarea { min-height: 76px; }
/* the pin switch shares the rewrite-rule's section styling — both decide what this
   block's words do at send time, so they read as one group under the wording fields */
.insp-pin { margin-top: 14px; padding-top: 10px; border-top: 1px dashed var(--line-strong); }
/* the data wizard: table read back from anything pasted */
.wz-tbl { margin-top: 8px; }
.wz-note { color: var(--amber-dark); }
.wz-hits { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 6px; font-size: 13.5px; }
.wz-hits li { display: flex; gap: 8px; align-items: baseline; }
/* 2026-10-17 variable-centric inspector: four blocks (Default content / AI rules / Style / Custom CSS).
   Each section is a light card with a small heading; adjacent sections share the dashed divider
   rhythm the pre-refactor .insp-ai/.insp-pin already used so existing visual grammar is preserved. */
.insp-section { margin: 0 0 14px; padding: 12px 0 4px; border-top: 1px dashed var(--line-strong); }
.insp-section:first-child { border-top: 0; padding-top: 0; }
.insp-sec-title { margin: 0 0 10px; font-size: 12px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--ink-soft); }
.insp-sec-ai textarea { min-height: 76px; }
.insp-sec-css textarea { font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: 12.5px; }
/* 2026-10-17 N2 variable-governance panel: a table of {{var}} rows with a header
   action button. The AI-fill trigger lives in the card header so it reads as a
   one-shot action, not a persistent modal. Column picker uses existing .select-in. */
.dg-vars-card { padding: 14px 16px 12px; }
.dg-vars-head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 8px; }
.dg-vars-head > div { flex: 1; min-width: 0; }
.dg-vars-head .card-h { margin: 0 0 4px; font-size: 15px; }
.dg-vars-head .card-sub { margin: 0; }
.dg-vars-tbl { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.dg-vars-tbl th { text-align: left; font-weight: 600; font-size: 12px; color: var(--ink-soft); padding: 6px 8px; border-bottom: 1px solid var(--line); }
.dg-vars-tbl td { padding: 8px; border-bottom: 1px solid var(--line); vertical-align: middle; }
/* summary-first variable card (2026-10-18): one honest count line, grouped native <details> sections */
.dg-vars-summary { margin: 2px 0 10px; font-weight: 600; }
.dg-var-group { margin: 0 0 10px; }
.dg-var-group > summary { cursor: pointer; font-size: 13px; font-weight: 600; color: var(--ink-soft); padding: 4px 0; }
.dg-var-group[open] > summary { margin-bottom: 4px; }
/* I2 (2026-10-19): a resolved number row collapses its source controls behind a
   quiet “change source” disclosure — the happy path reads as a ✓ chip, the override
   is one click away. Native <details>, no JS toggle, same fold language as groups. */
.dg-var-adv > summary { cursor: pointer; font-size: 12px; color: var(--ink-soft); list-style: none; display: inline-flex; align-items: center; gap: 4px; }
.dg-var-adv > summary::-webkit-details-marker { display: none; }
.dg-var-adv > summary::before { content: "\25B8"; font-size: 10px; }
.dg-var-adv[open] > summary::before { content: "\25BE"; }
.dg-var-adv[open] > summary { margin-bottom: 6px; }
.dg-var-adv .select-in, .dg-var-adv .dg-var-static { margin-top: 4px; }
.dg-var-row--locked td { background: rgba(128,128,128,.06); }
.dg-var-row[data-system="1"] td { background: rgba(128,128,128,.06); }
.dg-var-row .mono { font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace); }
.dg-var-static { width: 100%; padding: 4px 6px; font-size: 13px; }
/* S1a sheet rail finally gets its own skin (the classes shipped unstyled): one card
   per detected table, header row with the shape pick, and the per-table receipts the
   N6 rework writes after a store-all. */
.dg-sheet-card { border: 1px solid var(--line, #e4ddd1); border-radius: 10px; padding: 10px 12px; margin: 0 0 10px; background: #fff; }
.dg-sheet-h { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.dg-sheet-h .select-in { flex: 0 1 auto; }
.dg-sheet-receipt { display: block; margin: 2px 0; }
.dg-empty-note { margin: 0 0 8px; padding: 6px 8px; background: #f4f7fb; border-radius: 8px; }
/* 2026-10-17 N4: the {{ variable autocomplete popover inside the inspector. Anchored
   to the field's own .field box (position:relative set by the completer). */
.wt-varpop { position: absolute; z-index: 30; left: 0; top: calc(100% + 4px); min-width: 180px; max-height: 220px; overflow: auto; background: var(--card, #fff); border: 1px solid var(--line); border-radius: 10px; box-shadow: 0 8px 24px rgba(16,24,40,.12); padding: 6px; }
.wt-varpop-h { margin: 2px 6px 6px; font-size: 11.5px; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .04em; }
.wt-varpop [data-var-ins] { display: block; width: 100%; text-align: left; border: 0; background: transparent; padding: 6px 8px; border-radius: 8px; font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: 12.5px; cursor: pointer; }
.wt-varpop [data-var-ins]:hover { background: rgba(98,106,239,.10); }
/* WP3-2 (2026-10-19): discoverable “insert a variable” chips under a text block's
   wording — same candidate source as the {{ autocomplete, but always visible. */
.wt-varchips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
.wt-varchips .chip { cursor: pointer; font-family: var(--mono, ui-monospace, Menlo, monospace); font-size: 12px; }

/* ---------- membership (view/account/account.html) ----------
   我的账户会员面：版式语汇全部走本站令牌（纸白卡、墨蓝主按钮、琥珀强调），
   结构对照 qrcard-ai 会员页（Hero 套餐状态 / 周期切换三档卡 / 对比表 /
   结算扫码 / 成功卡），但不引入第二套颜色权威。 */
/* 宽度比壳层 .view-in（1120）窄，必须自己居中——否则宽屏下内容贴左偏 */
.vip-wrap { display: flex; flex-direction: column; gap: 18px; max-width: 980px; margin: 0 auto; }
.vip-eyebrow { margin: 0; font-size: 11.5px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-soft); }
html[lang^="zh"] .vip-eyebrow { text-transform: none; letter-spacing: 0; }
.vip-hero-row { display: flex; align-items: center; justify-content: space-between; gap: 22px; flex-wrap: wrap; margin-top: 10px; }
.vip-hero-main { flex: 1; min-width: 220px; }
.vip-hero-name { display: flex; align-items: center; gap: 14px; }
.vip-hero-name h2 { margin: 0; font-size: 26px; letter-spacing: -0.01em; }
.vip-badge { display: inline-flex; align-items: center; justify-content: center; width: 46px; height: 46px; border-radius: 14px; background: var(--ink); color: var(--amber); flex: 0 0 auto; }
.vip-badge svg { width: 24px; height: 24px; }
.vip-expiry { margin: 8px 0 0; font-size: 13.5px; color: var(--ink-soft); }
.vip-prog { margin-top: 16px; max-width: 340px; }
.vip-prog-row { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; font-size: 12.5px; margin-bottom: 5px; }
.vip-prog-row span { font-weight: 600; color: var(--ink-soft); }
.vip-prog-row b { font-size: 12.5px; color: var(--ink); }
.vip-prog .small { margin: 7px 0 0; }
.vip-full { color: var(--amber-dark) !important; }
.vip-block { display: flex; flex-direction: column; gap: 0; }
.vip-block-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 16px; }
.vip-block-head h2 { margin: 0 0 2px; font-size: 21px; letter-spacing: -0.01em; }
.vip-block-head .muted { margin: 0; font-size: 14px; }
/* 周期切换胶囊：与侧栏 mode pill 同族语言，选中走琥珀淡底 */
.vip-seg { display: inline-flex; padding: 4px; gap: 2px; border-radius: 999px; background: rgba(23, 38, 59, 0.06); border: 1px solid var(--line); }
.vip-seg button { border: 0; background: transparent; cursor: pointer; padding: 6px 14px; border-radius: 999px; font: inherit; font-size: 13.5px; font-weight: 600; color: var(--ink-soft); display: inline-flex; align-items: center; gap: 6px; transition: background 0.15s ease, color 0.15s ease; }
.vip-seg button.on { background: rgba(232, 163, 61, 0.22); color: var(--warn-ink); }
.vip-save { font-size: 10.5px; font-weight: 700; padding: 2px 7px; border-radius: 999px; background: rgba(232, 163, 61, 0.22); color: var(--warn-ink); }
.vip-plans { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.vip-plan { position: relative; text-align: left; display: block; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 22px; font: inherit; color: var(--ink); transition: border-color 0.15s ease, box-shadow 0.15s ease; }
button.vip-plan { cursor: pointer; }
button.vip-plan:hover { border-color: var(--line-strong); box-shadow: var(--shadow); }
.vip-plan.on { border-color: var(--amber); box-shadow: 0 0 0 1px var(--amber), var(--shadow); }
.vip-plan.vip-static, .vip-plan.vip-pro { background: rgba(255, 255, 255, 0.6); }
.vip-tag { position: absolute; top: -10px; left: 18px; font-size: 10.5px; font-weight: 700; padding: 2px 9px; border-radius: 999px; background: rgba(232, 163, 61, 0.22); color: var(--warn-ink); border: 1px solid rgba(232, 163, 61, 0.5); }
.vip-tag-hot { background: var(--ink); color: #fff; border-color: var(--ink); }
.vip-tag-warn { background: rgba(143, 95, 13, 0.1); color: var(--amber-dark); border-color: rgba(143, 95, 13, 0.35); }
.vip-plan-name { margin: 0; font-size: 14px; font-weight: 700; color: var(--ink-soft); }
.vip-plan-brand { color: var(--amber-dark); }
.vip-price { margin: 8px 0 0; font-size: 28px; font-weight: 800; letter-spacing: -0.02em; }
.vip-price-dim { color: var(--ink-soft); }
.vip-per { font-size: 14px; font-weight: 600; color: var(--ink-soft); }
.vip-price-sub { margin: 3px 0 0; font-size: 12px; color: var(--ink-soft); }
.vip-perks { list-style: none; margin: 16px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; font-size: 13.5px; }
.vip-perks li { display: flex; gap: 8px; align-items: baseline; }
.vip-check { color: var(--green); font-weight: 700; flex: 0 0 auto; }
.vip-check-dim { color: var(--line-strong); }
.vip-pro-foot { margin: 16px 0 0; font-size: 12px; color: var(--ink-soft); text-align: center; }
.vip-plan [data-choose] { margin-top: 16px; }
.vip-demo-note { margin: 12px 0 0; }
/* 对比表：当前档一列高亮（与表头同色），数字走阶梯真相 */
.vip-cmp-title { margin: 0 0 2px; font-size: 20px; }
.vip-cmp-wrap { overflow-x: auto; margin-top: 12px; }
.vip-cmp { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.vip-cmp th, .vip-cmp td { padding: 10px 12px; text-align: center; }
.vip-cmp .ta-l, .vip-cmp th:first-child { text-align: left; }
.vip-cmp thead th { font-size: 12px; font-weight: 700; color: var(--ink-soft); border-bottom: 1px solid var(--line); }
.vip-cmp tbody tr { border-top: 1px solid var(--rp-rule-soft, #efe9df); }
.vip-cmp tbody th { font-weight: 600; color: var(--ink-soft); font-size: 13px; }
.vip-cmp td { font-weight: 700; }
.vip-onth { color: var(--amber-dark); }
/* 积分提醒行贴对比表，与团队版注释行拉开一档间距 */
.vip-credit-note { margin: 14px 0 0; }
.vip-credit-note + .small { margin: 6px 0 0; }
/* 结算区：渠道三列、白底盘扫码板（四角括号 + 站内绿） */
.vip-pay-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding-bottom: 14px; border-bottom: 1px solid var(--line); margin-bottom: 18px; }
.vip-pay-head h2 { margin: 0; font-size: 17px; }
.vip-pay-head p { margin: 0; font-size: 13px; color: var(--ink-soft); }
.vip-pay-body { display: flex; flex-direction: column; align-items: center; }
.vip-chs { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; width: 100%; max-width: 640px; }
.vip-ch { position: relative; display: flex; align-items: center; gap: 11px; width: 100%; text-align: left; cursor: pointer; font: inherit; border: 1px solid var(--line); border-radius: 12px; background: var(--card); padding: 13px 15px; transition: border-color 0.15s ease, background 0.15s ease; }
@media (min-width: 560px) { .vip-ch { width: calc((100% - 24px) / 3); } }
.vip-ch:hover { border-color: var(--line-strong); }
.vip-ch.on { border-color: var(--amber); background: rgba(232, 163, 61, 0.08); }
.vip-ch-ok { position: absolute; top: 9px; right: 9px; width: 18px; height: 18px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }
.vip-ch-ok svg { width: 11px; height: 11px; }
.vip-ch-abbr { width: 38px; height: 38px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 15px; font-weight: 800; flex: 0 0 auto; }
.vip-ch-meta { min-width: 0; display: flex; flex-direction: column; }
.vip-ch-meta b { font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vip-ch-meta i { font-style: normal; font-size: 12px; color: var(--ink-soft); }
.vip-payopen { display: flex; flex-direction: column; align-items: center; gap: 4px; margin-top: 22px; }
.vip-pay-state { margin: 0; display: flex; align-items: center; gap: 10px; font-size: 13.5px; }
.vip-pulse { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; animation: pulse 1.1s ease infinite; }
.vip-qr { position: relative; width: 196px; height: 196px; border-radius: 14px; background: #fff; border: 1px solid var(--line); box-shadow: var(--shadow); padding: 14px; margin: 14px 0 6px; }
.vip-qr-c { position: absolute; width: 13px; height: 13px; border: 0 solid var(--green); }
.vip-qr-c.c-tl { top: 5px; left: 5px; border-top-width: 2.5px; border-left-width: 2.5px; border-top-left-radius: 6px; }
.vip-qr-c.c-tr { top: 5px; right: 5px; border-top-width: 2.5px; border-right-width: 2.5px; border-top-right-radius: 6px; }
.vip-qr-c.c-bl { bottom: 5px; left: 5px; border-bottom-width: 2.5px; border-left-width: 2.5px; border-bottom-left-radius: 6px; }
.vip-qr-c.c-br { bottom: 5px; right: 5px; border-bottom-width: 2.5px; border-right-width: 2.5px; border-bottom-right-radius: 6px; }
.vip-qr-inner { position: relative; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
.vip-qr-spin { width: 18px; height: 18px; border-radius: 50%; border: 2px solid rgba(23, 38, 59, 0.18); border-top-color: var(--ink-soft); animation: vipspin 0.8s linear infinite; }
@keyframes vipspin { to { transform: rotate(360deg); } }
.vip-backfree { border: 0; background: none; cursor: pointer; font: inherit; font-size: 12.5px; color: var(--ink-soft); padding: 8px; }
.vip-backfree:hover { color: var(--ink); }
.vip-choosehint { margin: 22px 0 0; }
/* 成功卡：站内绿（与 chip-on 同族底），不引第二种成功色 */
.vip-paid { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 8px; border-color: rgba(46, 125, 91, 0.4); background: rgba(46, 125, 91, 0.06); }
.vip-paid-ic { width: 56px; height: 56px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: rgba(46, 125, 91, 0.14); color: var(--green); }
.vip-paid-ic svg { width: 28px; height: 28px; }
.vip-paid h2 { margin: 6px 0 0; font-size: 20px; }
.vip-paid .abtn { margin-top: 14px; }
@media (max-width: 900px) {
  .vip-plans { grid-template-columns: 1fr; }
  .vip-hero-row { align-items: flex-start; }
}
@media (prefers-reduced-motion: reduce) {
  .vip-pulse, .vip-qr-spin { animation: none; }
}
