/* ============================================================================
   RESPONSIVE BREAKPOINT SCALE - apply these five widths consistently.
   Media queries can't read CSS vars, so treat these as the canonical set:
     xs  <=480   phone            (single column, drawer nav)
     sm   640    large phone      (2-up metrics/cards)
     md   768    tablet portrait  (tables -> cards below this)
     lg  1024    small laptop     (fixed sidebar returns at >=1024)
     xl  1280    desktop          (max content width cap)
   New rules should use max-width:1023 / 767 / 639 / 480 and min-width:768 /
   1024. Legacy one-off widths are being migrated onto this scale.
   ============================================================================ */
:root {
  --bg: #ffffff;
  --panel: #ffffff;
  --panel-subtle: #fafafa;
  --sidebar: #ffffff;
  --sidebar-muted: #6b6b6b;
  --text: #0a0a0a;
  --muted: #6b6b6b;
  --faint: #9a9a9a;
  --line: #e6e6e6;
  --line-soft: #efefef;
  --line-strong: #1a1a1a;
  --hover: #f6f6f6;
  --accent: #147c64;
  --accent-weak: #e7f4ef;
  --blue: #5b6b80;
  --blue-weak: #eef1f5;
  --amber: #9a7415;
  --amber-weak: #fdf8ec;
  --red: #b42318;
  --red-weak: #fdf1ef;
  --green: #1f7a4d;
  --green-weak: #f3faf6;
  --shadow: none;
  --radius: 8px;
  --radius-sm: 6px;
  --shadow-pop: 0 8px 24px -12px rgba(10, 10, 10, 0.18);
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  --dur-slow: 0.24s;
  --z-modal: 100;
  --z-toast: 120;
  --sidebar-width: 224px;
  /* Horizontal inset between the app frame and page content. Every element that
     has to line up with the page content derives its inset from this, so
     changing it here moves the whole app together.
     8px is deliberate: it keeps the near-full-bleed width without cards butting
     into the sidebar's border and the scrollbar, which is what 0 looked like. */
  --page-gutter: 8px;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  min-height: 100%;
}

body {
  color: var(--text);
  background: var(--bg);
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 13px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

/* Thin, minimal scrollbars */
* {
  scrollbar-width: thin;
  scrollbar-color: #d0d0d0 transparent;
}

*::-webkit-scrollbar {
  width: 7px;
  height: 7px;
}

*::-webkit-scrollbar-track {
  background: transparent;
}

*::-webkit-scrollbar-thumb {
  background: #d4d4d4;
  border-radius: 999px;
}

*::-webkit-scrollbar-thumb:hover {
  background: #b8b8b8;
}

*::-webkit-scrollbar-corner {
  background: transparent;
}

h1,
h2,
h3,
h4,
p {
  margin: 0;
}

h1,
h2,
h3,
h4 {
  letter-spacing: 0;
  line-height: 1.2;
}

a {
  color: inherit;
  text-decoration: none;
}

/* Inline "Upgrade" links inside body/hint text - accent + underline so they
   read as clickable (base <a> is color:inherit). */
.upgrade-link {
  color: var(--accent);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.upgrade-link:hover { text-decoration: none; }

button,
input,
select,
textarea {
  font: inherit;
}

.page-noise {
  display: none;
}

.app-frame {
  display: grid;
  grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
  min-height: 100vh;
  transition: grid-template-columns 0.18s ease;
}

.sidebar {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  padding: 18px 14px;
  color: var(--text);
  background: var(--sidebar);
  border-right: 1px solid var(--line);
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 4px 8px 14px;
  border-bottom: 1px solid var(--line);
}

.brand strong,
.brand small {
  display: block;
}

.brand strong {
  font-size: 14px;
  font-weight: 700;
}

.brand small {
  margin-top: 2px;
  color: var(--faint);
  font-size: 12px;
}

.brand-mark {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 6px;
  color: var(--accent);
  background: transparent;
  border: 1px solid var(--line-strong);
}

.brand-mark svg {
  width: 16px;
  height: 16px;
  display: block;
}

.nav-stack {
  display: grid;
  gap: 2px;
  margin-top: 14px;
}

.nav-link {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 36px;
  padding: 0 10px;
  border-radius: 5px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
}

.nav-icon {
  flex: none;
}

/* Sidebar collapse toggle */
.sidebar-toggle {
  position: absolute;
  top: 15px;
  right: 11px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  color: var(--muted);
  background: var(--panel);
  border: 1px solid transparent;
  border-radius: 6px;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.sidebar-toggle:hover {
  color: var(--text);
  background: var(--hover);
}

.sidebar-toggle:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-weak);
}

/* Collapsed icon-rail state */
html.nav-rail {
  --sidebar-width: 64px;
}

html.nav-rail .brand-text,
html.nav-rail .nav-stack + .eyebrow,
html.nav-rail .sidebar .eyebrow,
html.nav-rail .nav-label {
  display: none;
}

/* Collapsed rail: keep the profile as an avatar-only marker. */
html.nav-rail .sidebar-footnote {
  padding: 10px 0;
}

html.nav-rail .sidebar-profile {
  justify-content: center;
  gap: 0;
  padding: 0;
}

html.nav-rail .sidebar-profile:hover {
  background: transparent;
}

html.nav-rail .sidebar-profile-text {
  display: none;
}

html.nav-rail .brand {
  justify-content: center;
  padding-left: 0;
  padding-right: 0;
  margin-top: 38px;
  border-bottom-color: transparent;
}

html.nav-rail .nav-link {
  justify-content: center;
  padding: 0;
  gap: 0;
}

html.nav-rail .sidebar-toggle {
  right: 50%;
  transform: translateX(50%);
}

.nav-link:hover {
  color: var(--text);
  background: var(--hover);
}

.nav-link.is-active {
  color: var(--accent);
  background: var(--accent-weak);
  font-weight: 550;
}

.sidebar-footnote {
  margin-top: auto;
  padding: 10px 8px;
  border-top: 1px solid var(--line);
}

.sidebar-profile {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  border-radius: 10px;
  transition: background .15s ease;
}

.sidebar-profile:hover {
  background: var(--hover);
}

.sidebar-profile-avatar {
  flex: none;
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: var(--text);
  color: #fff;
  display: grid;
  place-items: center;
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: .02em;
}

.sidebar-profile-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.3;
}

.sidebar-profile-text strong {
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sidebar-profile-owner {
  color: var(--muted);
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sidebar-profile-domain {
  color: var(--faint);
  font-size: 11.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.workspace {
  min-width: 0;
  padding: 0;
}

/* ---- Mobile top bar + drawer scrim. Both hidden on desktop; the <=1023px
   media query below turns the sidebar into a slide-in drawer and reveals these. */
.app-topbar {
  display: none;
  position: sticky;
  top: 0;
  z-index: 40;
  align-items: center;
  gap: 12px;
  min-height: 56px;
  padding: 10px 16px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
}

.topbar-burger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  margin-left: -6px;
  color: var(--text);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 9px;
  cursor: pointer;
}

.topbar-burger:hover {
  background: var(--hover);
}

.topbar-burger:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-weak);
}

.app-topbar-title {
  flex: 1;
  min-width: 0;
  font-size: 16px;
  font-weight: 680;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.app-topbar-avatar {
  flex: none;
  width: 32px;
  height: 32px;
  border-radius: 9px;
  display: grid;
  place-items: center;
  font-size: 11.5px;
  font-weight: 700;
  color: #082e25;
  background: linear-gradient(140deg, #7cf0c6, #147c64);
}

.nav-scrim {
  display: none;
}

.eyebrow {
  color: var(--muted);
  font-size: 11px;
  font-weight: 760;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.page-content {
  display: grid;
  gap: 16px;
  /* Vertical padding stays: at 0 the first heading on a page would touch the top
     of the window, which is a typography problem rather than wasted space. */
  padding: 14px var(--page-gutter) 40px;
}

/* Bare page furniture - titles, section labels and loose action rows - carries no
   card padding of its own, so at --page-gutter: 0 it would sit hard against the
   sidebar and the window edge. This floor holds it clear of both and cancels
   itself once the gutter is wide enough to do the job on its own.
   Cards, heroes and tables are deliberately NOT in this list: bleeding those to
   the edge is the whole point of a 0 gutter.
   A new page with bare furniture of its own should join this list. */
.dhead,
.dash2 .section-head,
.dashboard .section-head,
.orders-head,
.support-head,
.auto-inv-head,
.set-sec-label,
.ntf-sec-label,
.brnd-sec-label,
.notif-actions {
  padding-left: max(0px, calc(14px - var(--page-gutter)));
  padding-right: max(0px, calc(14px - var(--page-gutter)));
}

.public-shell {
  max-width: 720px;
  margin: 0 auto;
  padding: 24px 20px 48px;
}

.public-brand {
  margin-bottom: 18px;
}

.public-content {
  display: grid;
  gap: 16px;
}

.pill,
.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 22px;
  padding: 0 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  white-space: nowrap;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--muted);
  background: transparent;
}

.pill {
  color: var(--accent);
  background: var(--accent-weak);
  border-color: #cce7dd;
}

.pill-muted {
  color: var(--muted);
  background: #ffffff;
  border-color: var(--line);
}

.badge-success {
  color: var(--green);
  background: var(--green-weak);
  border-color: #cfe8d8;
}

.badge-warn {
  color: var(--amber);
  background: var(--amber-weak);
  border-color: #f0e0bf;
}

.badge-danger {
  color: var(--red);
  background: var(--red-weak);
  border-color: #f1c9c3;
}

.badge-muted {
  color: var(--muted);
  background: #ffffff;
  border-color: var(--line);
}

.trial-banner,
.sync-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 42px;
  /* These bars span the full width and inset their own text to line up with the
     edge of the page's cards. The floor keeps that text off the frame once the
     gutter reaches 0. */
  padding: 8px max(var(--page-gutter), 16px);
  border-bottom: 1px solid var(--line);
  font-size: 13px;
  overflow: hidden;
  transition: opacity 0.35s ease, min-height 0.35s ease, padding 0.35s ease;
}

.trial-banner {
  justify-content: space-between;
  color: #553300;
  background: var(--amber-weak);
}

.trial-banner a {
  color: #553300;
  font-weight: 760;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.reauth-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 12px max(var(--page-gutter), 16px);
  color: var(--red);
  background: var(--red-weak);
  border-bottom: 1px solid #f1c9c3;
  font-size: 13px;
  font-weight: 550;
}

.reauth-banner .button {
  background: var(--red);
  border-color: var(--red);
  color: #fff;
}

.reauth-banner .button:hover {
  background: #fff;
  color: var(--red);
}

/* Pinned to the top of the content column and kept visible (while a sync is
   active) as the page scrolls, so import progress is never lost. */
.sync-banner {
  position: sticky;
  top: 0;
  z-index: 60;
  flex-direction: column;
  align-items: stretch;
  gap: 9px;
  background: var(--panel);
  box-shadow: 0 6px 18px -12px rgba(8, 20, 16, 0.4);
}

.sync-banner.is-hidden {
  display: none;
}

/* Top row: status label + count on the left, % readout on the right. */
.sync-banner-main {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

/* The progress track - a full-width bar under the copy. */
.sync-loader {
  position: relative;
  width: 100%;
  height: 6px;
  border-radius: 5px;
  overflow: hidden;
  background: var(--line-soft);
}

.sync-loader-fill {
  position: relative;
  display: block;
  height: 100%;
  width: 0%;
  border-radius: 5px;
  overflow: hidden;
  background: var(--accent);
  transition: width 0.3s ease;
}

/* Indeterminate shimmer sweeps across the filled portion while running. */
.sync-banner[data-status="running"] .sync-loader-fill::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);
  transform: translateX(-100%);
  animation: sync-shimmer 1.4s linear infinite;
}

@keyframes sync-shimmer {
  to { transform: translateX(100%); }
}

.sync-copy {
  min-width: 0;
  flex: 1;
}

.sync-copy strong,
.sync-copy span {
  display: block;
}

.sync-copy span {
  color: var(--muted);
  font-size: 12px;
}

/* Status dot precedes the label. Pseudo-element survives JS textContent updates. */
.sync-banner[data-status="running"] [data-role="status-label"],
.sync-banner[data-status="queued"] [data-role="status-label"],
.sync-banner[data-status="failed"] [data-role="status-label"] {
  position: relative;
  padding-left: 16px;
}

.sync-banner[data-status="running"] [data-role="status-label"]::before,
.sync-banner[data-status="queued"] [data-role="status-label"]::before,
.sync-banner[data-status="failed"] [data-role="status-label"]::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 7px;
  height: 7px;
  margin-top: -3.5px;
  border-radius: 50%;
  background: var(--accent);
}

.sync-banner[data-status="running"] [data-role="status-label"]::before,
.sync-banner[data-status="queued"] [data-role="status-label"]::before {
  animation: sync-pulse 1.2s ease-in-out infinite;
}

.sync-banner[data-status="failed"] [data-role="status-label"]::before {
  background: var(--red);
}

@keyframes sync-pulse {
  50% { opacity: 0.3; transform: scale(0.8); }
}

.sync-progress-value {
  min-width: 40px;
  color: var(--accent);
  font-size: 15px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* The % readout is only meaningful while a determinate sync is running. */
.sync-banner:not([data-status="running"]) .sync-progress-value {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  .sync-banner[data-status="running"] .sync-loader-fill::after,
  .sync-banner[data-status="running"] [data-role="status-label"]::before,
  .sync-banner[data-status="queued"] [data-role="status-label"]::before,
  /* The export banner borrows this loader, so it has to be listed here too. */
  .export-banner[data-status="running"] .sync-loader-fill::after {
    animation: none;
  }
}

.sync-banner-note {
  width: 100%;
  margin: 0;
  color: var(--muted);
  font-size: 12px;
}

.sync-request-meta,
.sync-request-error {
  color: var(--muted);
  font-size: 12px;
}

.sync-request-card {
  display: grid;
  gap: 6px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel-subtle);
}

.sync-request-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.sync-request-title {
  font-size: 13px;
  font-weight: 720;
}

.sync-request-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.sync-request-error {
  line-height: 1.4;
  display: block;
  padding: 5px 7px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  background: #fff;
  border: 1px solid var(--line-soft);
  border-radius: 6px;
  overflow-wrap: anywhere;
}

.sync-request-confirm {
  color: var(--red);
  font-size: 12px;
  font-weight: 600;
}

.sync-request-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.sync-request-button {
  min-height: 28px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--text);
  background: #ffffff;
  cursor: pointer;
  font-size: 12px;
  font-weight: 720;
}

.sync-request-button:hover {
  background: var(--line-soft);
}

.sync-request-button.is-danger {
  color: var(--red);
  border-color: #f1c3bc;
  background: var(--red-weak);
}

.sync-request-button:disabled {
  opacity: 0.6;
  cursor: wait;
}

.section-card,
.table-shell,
.template-preview,
.preview-card,
.hero-card,
.print-sheet {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.section-card,
.hero-card {
  padding: 16px;
}

.hero-card {
  max-width: 640px;
}

.hero-card h1,
.section-card h2 {
  font-size: 18px;
  font-weight: 760;
}

.hero-card p,
.section-note,
.muted {
  color: var(--muted);
}

.hero-card p {
  margin-top: 8px;
}

.hero-card code {
  display: inline-block;
  margin-top: 12px;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel-subtle);
  font-size: 12px;
}

.toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
}

.back-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-bottom: 10px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  transition: color 0.15s ease;
}

.back-link:hover {
  color: var(--accent);
}

.back-link svg {
  transition: transform 0.15s ease;
}

.back-link:hover svg {
  transform: translateX(-2px);
}

.toolbar h2 {
  margin-top: 2px;
}

.button-inline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

/* Bottom-align filter buttons with the inputs (whose labels add height above). */
.filter-actions {
  align-self: end;
}

.orders-filters {
  display: grid;
  grid-template-columns: minmax(180px, 1.5fr) minmax(180px, 1.2fr) minmax(140px, 1fr) minmax(140px, 1fr) auto;
  gap: 12px;
  align-items: end;
}

@media (max-width: 1040px) {
  .orders-filters {
    grid-template-columns: 1fr 1fr;
  }

  .orders-filters .filter-actions {
    grid-column: 1 / -1;
  }
}

@media (max-width: 560px) {
  .orders-filters {
    grid-template-columns: 1fr;
  }
}

.stack {
  display: grid;
  gap: 12px;
}

.grid-two {
  display: grid;
  gap: 12px;
}

.grid-two {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* Vendor payout summary: net-payable hero + calculation breakdown. */
.payout-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  margin-top: 16px;
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
}

.payout-net {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 22px;
  background: var(--accent);
  color: #ffffff;
}

.payout-net-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.85;
}

.payout-net-value {
  font-size: 32px;
  font-weight: 770;
  font-variant-numeric: tabular-nums;
}

.payout-net-note {
  font-size: 12px;
  opacity: 0.85;
}

.payout-breakdown {
  padding: 6px 20px;
}

.payout-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: 14px;
}

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

.payout-row .k small {
  margin-left: 6px;
  font-size: 12px;
  color: var(--muted);
}

.payout-row .amt {
  font-weight: 640;
  font-variant-numeric: tabular-nums;
}

.payout-row .amt.neg {
  color: #b23b3b;
}

.payout-row-total {
  margin-top: 2px;
  border-top: 1px solid var(--line);
}

.payout-row-total .k {
  font-weight: 720;
}

.payout-row-total .amt {
  font-weight: 760;
  color: var(--accent);
}

@media (max-width: 640px) {
  .payout-hero {
    grid-template-columns: 1fr;
  }
}

/* Inline notice for plan-limited views (e.g. Free plan data window). */
.window-note {
  margin-top: 14px;
  padding: 10px 12px;
  background: var(--accent-weak);
  border-radius: 8px;
  font-size: 13px;
  color: var(--accent);
}

/* Plan cards: a name band on top (filled green on the current plan) keeps the
   plan names highlighted and the prices aligned across cards. Flex column so
   the action can be pinned to the bottom. */
.plan-card {
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.plan-band {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 52px;
  margin: -16px -16px 0;
  padding: 0 16px;
  border-bottom: 1px solid var(--line-soft);
}

.plan-band .plan-name {
  font-size: 17px;
  font-weight: 760;
}

.plan-band .plan-tag {
  margin-left: auto;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--accent);
}

.plan-tagline {
  margin-top: 16px;
}

.plan-price {
  margin-top: 12px;
  font-size: 30px;
  font-weight: 780;
  letter-spacing: -0.02em;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* The "/mo" suffix stays small and muted beside the big price. */
.plan-price small {
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0;
}

.plan-features {
  margin-top: 16px;
  padding-left: 18px;
}

/* Recommended is a suggestion, so it gets a thin ring and nothing more. The
   plan the merchant is actually on has to read louder than the one we're
   nudging them towards - otherwise the two compete and neither wins. */
.plan-card.is-recommended {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}

/* The current plan: tinted panel, plus a solid accent rail down the leading
   edge. The rail is drawn as an inset shadow rather than a border so the card
   keeps the same geometry as its neighbours and nothing shifts by a pixel. */
.plan-card.is-active {
  border-color: var(--accent);
  background: var(--accent-weak);
  box-shadow: inset 4px 0 0 0 var(--accent);
}

/* The current plan's band fills green with white text. */
.plan-card.is-active .plan-band {
  background: var(--accent);
  border-bottom-color: var(--accent);
}

.plan-card.is-active .plan-band .plan-name,
.plan-card.is-active .plan-band .plan-tag {
  color: #fff;
}

/* On the tint, the muted tagline and the "/mo" suffix lose too much contrast
   against their white-card values, so both step up a shade. */
.plan-card.is-active .plan-tagline,
.plan-card.is-active .plan-price small {
  color: var(--text);
  opacity: 0.72;
}

/* Action pinned to the bottom (aligned across cards) and full width. */
.plan-card .button-inline {
  width: 100%;
  margin-top: auto;
  padding-top: 20px;
}

.plan-card .button-inline form,
.plan-card .button-inline .button {
  width: 100%;
}

/* Selected plan's bottom action: a solid, non-interactive "Current plan" bar. */
.plan-card .button-inline .button.is-current,
.plan-card .button-inline .button.is-current:hover {
  background: var(--accent);
  color: #ffffff;
  cursor: default;
}

.value {
  margin-top: 6px;
  font-size: 28px;
  font-weight: 780;
  line-height: 1.05;
}

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

.field label {
  color: var(--muted);
  font-size: 12px;
  font-weight: 720;
}

.field small {
  color: var(--faint);
  font-size: 12px;
}

/* .field-hint only ever inherited its look from `.field small` above, so a hint used
   OUTSIDE a .field - the destination notes in the schedule wizard and on /delivery, for
   instance - fell back to default paragraph size and the body text colour, and out-weighed
   the row it was annotating. The class now carries its own style wherever it's used. */
.field-hint {
  color: var(--faint);
  font-size: 12px;
  line-height: 1.5;
}

.field input,
.field select,
.field textarea,
.data-table input {
  width: 100%;
  min-height: 36px;
  padding: 7px 9px;
  color: var(--text);
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}

.field textarea {
  min-height: 96px;
  resize: vertical;
}

/* Normalize native selects so they match the inputs/buttons (uniform 36px row). */
.field select {
  height: 36px;
  appearance: none;
  -webkit-appearance: none;
  padding-right: 30px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%236b6b6b' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  cursor: pointer;
}

.field input:focus,
.field select:focus,
.field textarea:focus,
.data-table input:focus {
  outline: 2px solid rgba(20, 124, 100, 0.16);
  border-color: var(--accent);
}

.checkbox-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.checkbox-row input {
  width: 16px;
  height: 16px;
  accent-color: var(--accent);
}

.button,
.button-secondary,
.button-ghost,
.button-danger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: 36px;
  padding: 0 15px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease,
    box-shadow 0.15s ease, transform 0.04s ease;
}

.button:active,
.button-secondary:active,
.button-ghost:active,
.button-danger:active {
  transform: translateY(1px);
}

.button:focus-visible,
.button-secondary:focus-visible,
.button-ghost:focus-visible,
.button-danger:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-weak);
}

/* Primary: green outline at rest, fills green on hover. */
.button {
  color: var(--accent);
  background: #ffffff;
  border-color: var(--accent);
}

.button:hover {
  color: #ffffff;
  background: var(--accent);
}

/* Disabled (e.g. Next: Delivery before a trigger is chosen): muted + inert,
   and it must not fill green on hover. */
.button:disabled,
.button[disabled] {
  opacity: .45;
  cursor: not-allowed;
}

.button:disabled:hover,
.button[disabled]:hover {
  color: var(--accent);
  background: #ffffff;
}

/* Primary: solid brand green - the single loudest accent on a screen. */
.button-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: 36px;
  padding: 0 15px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 13px;
  font-weight: 640;
  white-space: nowrap;
  color: #ffffff;
  background: var(--accent);
  transition: background 0.15s ease, border-color 0.15s ease;
}

.button-primary:hover {
  background: #106b56;
  border-color: #106b56;
}

.button-primary:disabled {
  opacity: 0.6;
  cursor: default;
}

/* Secondary: ink outline at rest, fills ink on hover. */
.button-secondary {
  color: var(--text);
  background: #ffffff;
  border-color: var(--line-strong);
}

.button-secondary:hover {
  color: #ffffff;
  background: var(--text);
  border-color: var(--text);
}

/* Ghost: quiet hairline; darkens on hover. */
.button-ghost {
  color: var(--muted);
  background: #ffffff;
  border-color: var(--line);
}

.button-ghost:hover {
  color: var(--text);
  border-color: #cfcfcf;
}

/* Danger: red outline at rest, fills red on hover. */
.button-danger {
  color: var(--red);
  background: #ffffff;
  border-color: #f1c9c3;
}

.button-danger:hover {
  color: #ffffff;
  background: var(--red);
  border-color: var(--red);
}

.button-danger:focus-visible {
  box-shadow: 0 0 0 3px var(--red-weak);
}

.table-shell {
  overflow: auto;
}

/* The orders table carries many columns; give it room and let the shell scroll
   horizontally rather than cramming. */
.orders-table {
  min-width: 1240px;
}

/* Freeze the first (Order) column: it stays pinned to the left edge while the
   rest of the wide table scrolls underneath. The header row is already
   sticky-top (z-index:1), so layering is:
     header first cell (top+left)  z-index 3  - top-left corner, above both
     body first cells   (left)     z-index 2  - above the scrolling body cells
   Each frozen cell needs an opaque background so scrolling content can't show
   through, and a hairline right divider marks the freeze boundary. */
.orders-table th:first-child,
.orders-table td:first-child {
  position: sticky;
  left: 0;
  background: var(--panel);
  box-shadow: 1px 0 0 var(--line-soft);
}

.orders-table td:first-child {
  z-index: 2;
}

.orders-table th:first-child {
  z-index: 3;
  /* Matches the rest of the header row, which is now the plain panel surface. */
  background: var(--panel);
}

/* Keep the pinned cell's background in step with the row's hover/selected
   states, otherwise it would stay white while the rest of the row highlights. */
.orders-table tbody tr:hover td:first-child {
  background: var(--hover);
}

.orders-table tbody tr.is-selected td:first-child {
  background: var(--accent-weak);
}

.data-table td.nowrap {
  white-space: nowrap;
}

/* Sortable column header (Date) - label + a small arrow, Shopify-style. */
.th-sort {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: inherit;
  text-decoration: none;
}

.th-sort:hover {
  color: var(--text);
}

/* Arrow is hidden until you hover the header, and stays visible on the column
   currently sorted so the sort state is always clear. */
.th-sort-arrow {
  display: inline-flex;
  color: var(--muted);
  opacity: 0;
  transition: opacity 0.12s ease;
}

.th-sort:hover .th-sort-arrow,
.th-sort.is-active .th-sort-arrow {
  opacity: 1;
}

.th-sort-arrow.is-asc svg {
  transform: rotate(180deg);
}

/* Tags can be long - cap the column and clip so it doesn't blow out the row. */
.order-tags-cell {
  max-width: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Summary footer under the orders table: count + current page range. */
.table-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 16px;
  border-top: 1px solid var(--line);
  background: #fcfcfc;
  font-size: 12.5px;
  color: var(--muted);
}

.table-footer strong {
  color: var(--text);
}

.table-footer-nav {
  display: flex;
  align-items: center;
  gap: 12px;
}

.table-footer-nav .button-ghost {
  min-height: 30px;
  padding: 0 12px;
}

.data-table {
  width: 100%;
  min-width: 860px;
  border-collapse: separate;
  border-spacing: 0;
}

/* ---- md breakpoint: list tables flatten into stacked cards on phones.
   A table opts in with .table-cards; each <td> is named via data-label and
   secondary cells carry .t-sec to drop out of the compact card. Above md the
   table renders normally (min-width scroll fallback intact). ---- */
@media (max-width: 767px) {
  .table-cards {
    min-width: 0;
    border-collapse: collapse;
  }

  /* Hide the header visually - each cell's data-label carries the column name. */
  .table-cards thead {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }

  .table-cards tbody tr {
    display: block;
    padding: 12px 14px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--panel);
  }

  .table-cards tbody tr + tr {
    margin-top: 10px;
  }

  .table-cards tbody td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 5px 0;
    border: 0;
    text-align: right;
  }

  .table-cards tbody td::before {
    content: attr(data-label);
    flex: none;
    color: var(--muted);
    font-size: 11.5px;
    font-weight: 650;
    text-align: left;
  }

  /* Secondary columns are dropped from the compact card. */
  .table-cards .t-sec {
    display: none !important;
  }

  /* Frozen first column / sticky header don't apply in card mode. */
  .table-cards th:first-child,
  .table-cards td:first-child {
    position: static;
  }

  /* Lead cell (order #/name) reads as the card title. */
  .table-cards tbody td:first-child {
    padding-top: 0;
    font-size: 14px;
    font-weight: 680;
  }

  .table-cards tbody td:first-child::before,
  .table-cards tbody td.row-actions-cell::before {
    content: none;
  }

  /* Actions cell: buttons align to the right, no label. */
  .table-cards tbody td.row-actions-cell {
    justify-content: flex-end;
    padding-top: 10px;
  }
}

.row-actions {
  display: inline-flex;
  gap: 8px;
  white-space: nowrap;
}

.row-actions .button-ghost {
  min-height: 30px;
  padding: 0 10px;
  font-size: 12px;
}

/* Square icon actions (preview / download) used in the orders + draft-orders tables. */
.icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #ffffff;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}

.icon-button svg {
  width: 18px;
  height: 18px;
}

.icon-button:hover {
  color: var(--accent, var(--text));
  border-color: var(--accent, #cfcfcf);
  background: var(--accent-weak, #f7f9f7);
}

.icon-button:focus-visible {
  outline: 2px solid var(--accent, #4b5563);
  outline-offset: 2px;
}

.data-table th,
.data-table td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--line-soft);
  text-align: left;
  vertical-align: middle;
}

/* The header separates from the rows by contrast alone: same white surface, but
   the labels at full text colour over a firm 2px rule. The old --panel-subtle
   grey was a neutral the green-tinted pages never redefined, so it landed within
   a hair of the white rows - invisible once the header became sticky and rows
   started passing beneath it. */
.data-table th {
  position: sticky;
  top: 0;
  z-index: 1;
  color: var(--text);
  background: var(--panel);
  border-bottom: 2px solid var(--text);
  font-size: 11px;
  font-weight: 760;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* Keep the orders / draft-orders column headers on a single line - the shell
   scrolls horizontally if they don't all fit, rather than wrapping to two rows. */
.orders-table thead th,
.draft-orders-table thead th { white-space: nowrap; }

/* --- Compact rows for the two list tables ------------------------------------
   These are for scanning a page of orders, so rows should cost as little height
   as they can while staying readable. A row was 55px: 10px padding top and
   bottom around a 34px icon button, which was the tallest thing in it and set
   the floor no matter how small the text got. Shrinking the buttons and the
   padding together takes a row to ~37px - about 17 rows in a 900px viewport
   instead of 11.
   Only above the card breakpoint: below 768px these tables become stacked cards
   where the same buttons are the primary touch targets and must stay big. */
@media (min-width: 768px) {
  .orders-table tbody td,
  .draft-orders-table tbody td { padding-top: 5px; padding-bottom: 5px; }

  .orders-table thead th,
  .draft-orders-table thead th { padding-top: 7px; padding-bottom: 7px; }

  .orders-table .row-actions-cell .icon-button,
  .draft-orders-table .row-actions-cell .icon-button {
    width: 26px;
    height: 26px;
    min-height: 0;
    border-radius: var(--radius-sm);
  }
  .orders-table .row-actions-cell .icon-button svg,
  .draft-orders-table .row-actions-cell .icon-button svg { width: 15px; height: 15px; }
  .orders-table .row-actions,
  .draft-orders-table .row-actions { gap: 4px; }

  /* Badges are the next tallest thing after the buttons. */
  .orders-table tbody .badge,
  .draft-orders-table tbody .badge { min-height: 19px; padding: 0 7px; font-size: 10.5px; }

  /* A wrapped customer name made some rows 4px taller than the rest, which reads
     as a ragged grid. One line, ellipsised, with the full name on hover - the row
     opens the order anyway. */
  .orders-table tbody td[data-label="Customer"],
  .draft-orders-table tbody td[data-label="Customer"] {
    max-width: 180px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

.data-table tbody tr:hover td {
  background: var(--hover);
}

.order-row {
  cursor: pointer;
}

.order-link {
  color: var(--text);
  text-decoration: none;
}

.order-row:hover .order-link {
  color: var(--accent);
}

.data-table tbody tr.is-selected td {
  background: var(--accent-weak);
}

.data-table td {
  font-size: 13px;
}

.data-table input {
  max-width: 112px;
  margin-left: auto;
  text-align: right;
}

.align-right {
  text-align: right !important;
}

/* Empty state for a table or panel with no rows. Grid + gap so a call to
   action below the message spaces itself; centred because every use sits in a
   table shell or a column block. Defined once - a second, left-aligned copy
   further down this file used to win and strip the padding. */
.empty-state {
  display: grid;
  gap: 12px;
  justify-items: center;
  align-content: start;
  padding: 32px;
  color: var(--muted);
  text-align: center;
}

.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.order-note {
  display: grid;
  gap: 6px;
  padding: 12px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  background: var(--panel-subtle);
}

.order-note strong {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* ===================== Order detail - Option A (invoice document) ===================== */
.order-detail-a {
  display: block;
}

/* Top bar: the "Back to orders" strip, separated from the header by a rule. */
.order-detail-a .odt-topbar {
  border-bottom: 1px solid var(--line);
  padding-bottom: 12px;
  margin-bottom: 12px;
}

/* Soft-pill back button (Option A). Opt in with .back-pill on any page -
   order detail, draft detail and vendor commission detail use it. */
.back-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 38px;
  padding: 0 16px 0 13px;
  border-radius: 999px;
  background: #fff;
  border: 1px solid var(--line);
  color: var(--text);
  font-size: 13.5px;
  font-weight: 650;
  margin-bottom: 0;
  transition: border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}
.back-pill svg { width: 16px; height: 16px; }
.back-pill:hover {
  color: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 6px 16px -10px rgba(8, 46, 37, 0.45);
}
.back-pill:hover svg { transform: translateX(-3px); }

.order-detail-a .odt-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 10px;
  margin-top: 8px;
  align-items: start;
}

.order-detail-a .odt-main {
  display: grid;
  gap: 24px;
  min-width: 0;
}

/* Invoice document card */
.order-detail-a .odt-doc {
  border: none;
  border-radius: 16px;
  overflow: hidden;
  background: #fff;
}

.order-detail-a .odt-doc-head {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  padding: 24px 28px;
  background: #fffdf9;
  border-bottom: 1px solid var(--line);
}

.order-detail-a .odt-billed small {
  display: block;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 5px;
}

.order-detail-a .odt-billed strong {
  font-size: 15px;
}

.order-detail-a .odt-invmeta {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.order-detail-a .odt-invmeta b {
  font-size: 15px;
}

.order-detail-a .odt-invmeta span {
  display: block;
  color: var(--muted);
  font-size: 12px;
  margin-top: 3px;
}

/* Shopify's real payment terms on the order document card. A pill rather than
   another muted line, because it is a fact ABOUT the invoice (where its due date
   comes from) rather than another field of it - and because most orders have no
   terms, so when it appears it should be legible as something extra. */
.order-detail-a .odt-invmeta .odt-terms {
  display: inline-block;
  margin-top: 6px;
  padding: 1px 7px;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--text);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.01em;
}

.order-detail-a .odt-invmeta .odt-terms-due {
  margin-top: 3px;
  font-size: 11px;
}

/* Overdue is the one state worth colouring: it is the reason a merchant would be
   looking. Uses the shared semantic token, not a new red. */
.order-detail-a .odt-invmeta .odt-terms-due.is-overdue {
  color: var(--red);
  font-weight: 600;
}

.order-detail-a .odt-addr {
  color: var(--muted);
  font-size: 13px;
  line-height: 1.6;
  margin-top: 8px;
}

.order-detail-a .odt-addr p {
  margin: 0;
}

.order-detail-a .odt-doc-body {
  padding: 22px 28px 26px;
}

/* Items ledger */
.order-detail-a .odt-items {
  width: 100%;
  border-collapse: collapse;
}

.order-detail-a .odt-items th {
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
  text-align: left;
  font-weight: 600;
  padding: 0 0 10px;
}

.order-detail-a .odt-items td {
  padding: 14px 0;
  border-top: 1px solid var(--line-soft);
  vertical-align: top;
  font-size: 14px;
}

.order-detail-a .odt-items .r {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.order-detail-a .odt-items .sub {
  color: var(--muted);
  font-size: 12px;
  margin-top: 2px;
}

/* Document footer: pair a summary column on the left with the totals ledger on
   the right so the space beside the totals isn't left blank. */
.order-detail-a .odt-doc-foot {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 40px;
  margin-top: 22px;
  flex-wrap: wrap;
}

.order-detail-a .odt-doc-foot-left {
  flex: 1 1 220px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  padding-top: 4px;
}

.order-detail-a .odt-foot-meta {
  margin: 0;
  display: grid;
  gap: 12px;
}

.order-detail-a .odt-foot-meta > div {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.order-detail-a .odt-foot-meta dt {
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 600;
}

.order-detail-a .odt-foot-meta dd {
  margin: 0;
  font-size: 13.5px;
  color: var(--text);
}

.order-detail-a .odt-foot-note {
  margin: 0;
  font-size: 13px;
  color: var(--muted);
}

/* Totals ledger */
.order-detail-a .odt-totals {
  flex: 0 0 auto;
  margin: 0 0 0 auto;
  width: min(320px, 100%);
}

.order-detail-a .odt-totals .row {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 7px 0;
  font-size: 13px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.order-detail-a .odt-totals .row strong {
  color: var(--text);
  font-weight: 600;
}

.order-detail-a .odt-totals .grand {
  border-top: 1px solid var(--line);
  margin-top: 6px;
  padding-top: 12px;
  font-size: 16px;
  color: var(--text);
}

.order-detail-a .odt-totals .grand span:first-child {
  font-weight: 600;
}

.order-detail-a .odt-totals .grand span:last-child {
  font-weight: 700;
}

/* Paid stamp */
.order-detail-a .odt-stamp {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
}

.order-detail-a .odt-stamp.is-paid {
  color: var(--accent);
}

/* Right rail */
.order-detail-a .odt-rail {
  display: grid;
  gap: 16px;
  position: sticky;
  top: 16px;
}

.order-detail-a .odt-rail .section-card {
  padding: 18px 20px;
}

.order-detail-a .odt-rail h3 {
  margin: 0 0 14px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text);
}

.order-detail-a .odt-facts {
  margin: 0;
}

.order-detail-a .odt-facts .row {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 9px 0;
  border-top: 1px solid var(--line-soft);
  font-size: 13px;
}

.order-detail-a .odt-facts .row:first-child {
  border-top: 0;
}

.order-detail-a .odt-facts dt {
  color: var(--muted);
}

.order-detail-a .odt-facts dd {
  margin: 0;
  text-align: right;
  max-width: 60%;
  overflow-wrap: anywhere;
}

.order-detail-a .odt-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

/* Transactions + delivery history - compact row lists */
.order-detail-a .odt-tables {
  display: grid;
  gap: 24px;
  align-items: start;
}

.order-detail-a .odt-block {
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 20px 22px 8px;
  background: #fff;
  min-width: 0;
}

.order-detail-a .odt-block-head {
  margin-bottom: 6px;
}

.order-detail-a .odt-block-head .eyebrow {
  margin-bottom: 2px;
}

.order-detail-a .odt-block-head h3 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-transform: none;
  color: var(--text);
}

/* Documents card: one row per document type (invoice / receipt / gift receipt). */

.order-detail-a .odt-doc-sub {
  margin: 0;
  font-size: 13px;
  color: var(--muted);
}

/* Locked documents: mute the row and show a compact reason instead of buttons. */

.order-detail-a .odt-doc-lock {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  color: var(--muted);
  padding: 4px 10px;
  border-radius: 20px;
  background: var(--line-soft);
  text-decoration: none;
}

.order-detail-a .odt-doc-lock.is-link:hover {
  color: var(--accent);
  background: var(--accent-weak);
}

.order-detail-a .odt-list {
  display: flex;
  flex-direction: column;
}

.order-detail-a .odt-list-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 0;
  border-top: 1px solid var(--line-soft);
}

.order-detail-a .odt-list-row:first-child {
  border-top: 0;
}

.order-detail-a .odt-list-main {
  flex: 1;
  min-width: 0;
}

.order-detail-a .odt-list-main strong {
  font-size: 14px;
}

.order-detail-a .odt-list-meta {
  display: block;
  color: var(--muted);
  font-size: 12px;
  margin-top: 3px;
  overflow-wrap: anywhere;
}

.order-detail-a .odt-list-side {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
}

.order-detail-a .odt-list-amt {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  white-space: nowrap;
}

@media (max-width: 960px) {
  .order-detail-a .odt-layout {
    grid-template-columns: minmax(0, 1fr);
  }
  .order-detail-a .odt-rail {
    position: static;
  }
}

@media (max-width: 680px) {
  .order-detail-a .odt-tables {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ===================== Settings - immersive ===================== */
body:has(.settings-page) .workspace { background: #edf4f0; }

/* Uncapped: the rows below lay out as a grid, so the width is used rather than
   stretching each row's label away from its button. */
.settings-page { min-width: 0; }

/* A run of setting rows. The section headings and the account-history table are
   siblings of these groups, so they keep the full width to themselves. */
.set-group {
  display: grid;
  /* min(460px, 100%) - a bare 460px minimum would hold the track wider than a
     phone screen and scroll the whole page sideways. */
  grid-template-columns: repeat(auto-fit, minmax(min(460px, 100%), 1fr));
  gap: 9px;
  /* Rows share one height, so cards sitting side by side match even when one
     description wraps to two lines and its neighbour doesn't. */
  grid-auto-rows: 1fr;
}

/* A floor as well as the shared row height: without it a section whose rows all
   hold one short line would sit noticeably squatter than the rest of the page. */
.set-group .set-row { min-height: 76px; }

.set-hero {
  background: linear-gradient(140deg, #082e25, #0f4c3d 82%);
  border-radius: 14px;
  color: #f3efe3;
  padding: 22px 24px;
}

.set-hero-eyebrow {
  font-size: 10.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: #7cf0c6;
  font-weight: 700;
}

.set-hero-id { display: flex; align-items: center; gap: 15px; margin-top: 12px; }

.set-avatar {
  width: 52px;
  height: 52px;
  border-radius: 14px;
  background: linear-gradient(150deg, #1c8f6f, #0c5f49);
  border: 1px solid rgba(124, 240, 198, .25);
  display: grid;
  place-items: center;
  font-size: 18px;
  font-weight: 750;
  color: #eafff6;
  flex: none;
}
/* With a brand logo the tile is just a frame for it - drop the green fill so a
   transparent PNG doesn't sit on a colour it wasn't designed for. */
.set-avatar.has-logo { background: #fff; border-color: rgba(255, 255, 255, .3); padding: 6px; }
.set-avatar.has-logo img { width: 100%; height: 100%; object-fit: contain; }

.set-who strong { display: block; font-size: 19px; font-weight: 740; letter-spacing: -.01em; }
.set-who span { font-size: 12.5px; color: rgba(243, 239, 227, .66); }

.set-hero-stats {
  display: flex;
  gap: 30px;
  flex-wrap: wrap;
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid rgba(243, 239, 227, .16);
}

.set-hstat { display: flex; flex-direction: column; gap: 2px; }
.set-hstat b { font-size: 18px; font-weight: 720; letter-spacing: -.01em; font-variant-numeric: tabular-nums; }
.set-hstat b.mint { color: #7cf0c6; }
.set-hstat span { font-size: 10.5px; color: rgba(243, 239, 227, .6); text-transform: uppercase; letter-spacing: .07em; font-weight: 600; }

.set-sec-label {
  font-size: 10.5px;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 700;
  margin: 22px 0 10px;
}

/* Full-width configuration rows. */
.set-row {
  display: flex;
  align-items: center;
  gap: 15px;
  padding: 15px 18px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 12px;
  scroll-margin-top: 20px;
}

/* Spacing belongs to .set-group's gap now - keeping the margin as well would
   double it between rows in the same column. */

.set-ico {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  background: var(--accent-weak);
  color: var(--accent);
  display: grid;
  place-items: center;
}

.set-ico svg { width: 19px; height: 19px; }
.set-ico.is-gray { background: var(--panel-subtle); color: var(--muted); }

.set-row-txt { flex: 1; min-width: 0; }
.set-row-txt strong { font-size: 14px; font-weight: 700; display: block; }
.set-row-txt p { margin: 2px 0 0; color: var(--muted); font-size: 12.5px; }

.set-row .button-sm { white-space: nowrap; }
.set-ico.is-danger { background: var(--red-weak); color: var(--red); }

/* Settings -> Diagnostics -> Background jobs: a set-row that opens in place to
   list the failed jobs. The card is the frame, so the row inside drops its own. */
.set-jobs { background: #fff; border: 1px solid var(--line); border-radius: 12px; min-width: 0; }
.set-jobs.has-failures { border-color: #f1c9c3; }
.set-jobs > .set-row { border: 0; background: none; }
.set-jobs-body { display: grid; gap: 8px; padding: 0 18px 16px 71px; }
/* The Review toggle is a .button-ghost, whose display beats the UA [hidden] rule. */
.set-jobs [hidden] { display: none; }
.set-jobs-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.set-jobs-head strong { display: block; font-size: 13px; font-weight: 700; }
.set-jobs-head span { color: var(--muted); font-size: 12px; }
.set-jobs-list { display: grid; gap: 8px; }
.set-jobs-empty { margin: 0; padding: 16px 12px; text-align: center; color: var(--muted); font-size: 12.5px; border: 1px dashed var(--line); border-radius: var(--radius-sm); }
.set-jobs-empty strong { display: block; color: var(--text); }

@media (max-width: 640px) {
  .set-row { flex-wrap: wrap; }
  .set-jobs-body { padding: 0 14px 14px; }
  .set-hero-stats { gap: 22px; }
}

/* ============ Notifications (slimmed) ============ */
body:has(.ntf-page) .workspace { background: #edf4f0; }
.ntf-page { min-width: 0; }
.ntf-page .back-link { margin-bottom: 16px; }
.ntf-hero, .tpl-hero, .plans-hero, .brnd-hero, .hub-hero, .einv-hero {
  background: linear-gradient(140deg, #082e25, #0f4c3d 82%);
  border-radius: 14px; color: #f3efe3; padding: 22px 24px; margin-bottom: 18px;
}
.ntf-hero-eyebrow, .tpl-hero-eyebrow, .plans-hero-eyebrow, .brnd-hero-eyebrow, .hub-eyebrow, .einv-hero-eyebrow {
  font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: #7cf0c6; font-weight: 700;
}
.ntf-hero h1, .tpl-hero h1, .plans-hero h1, .brnd-hero h1, .hub-hero h1, .einv-hero h1 { margin: 9px 0 0; font-size: 22px; font-weight: 750; letter-spacing: -.01em; color: #fff; }
.ntf-hero p, .tpl-hero p, .plans-hero p, .brnd-hero p, .hub-hero p, .einv-hero p { margin: 6px 0 0; font-size: 12.5px; color: rgba(243, 239, 227, .72); max-width: 66ch; }
.ntf-hero p a, .tpl-hero p a, .plans-hero p a { color: #7cf0c6; text-decoration: underline; }
.ntf-hero-stats, .tpl-hero-stats, .brnd-hero-stats, .hub-hero-stats, .einv-hero-stats { display: flex; gap: 28px; flex-wrap: wrap; margin-top: 16px; padding-top: 14px; border-top: 1px solid rgba(243, 239, 227, .16); }
.ntf-hstat, .tpl-hstat, .brnd-hstat, .hub-hstat, .einv-hstat { display: flex; flex-direction: column; gap: 2px; }
.ntf-hstat b, .tpl-hstat b, .brnd-hstat b, .hub-hstat b, .einv-hstat b { font-size: 18px; font-weight: 720; font-variant-numeric: tabular-nums; }
.ntf-hstat b.mint, .tpl-hstat b.mint, .brnd-hstat b.mint, .hub-hstat b.mint, .einv-hstat b.mint { color: #7cf0c6; }
.ntf-hstat span, .tpl-hstat span, .brnd-hstat span, .hub-hstat span, .einv-hstat span { font-size: 10px; color: rgba(243, 239, 227, .6); text-transform: uppercase; letter-spacing: .07em; font-weight: 600; }

.ntf-sec-label, .brnd-sec-label { font-size: 10.5px; letter-spacing: .09em; text-transform: uppercase; color: var(--faint); font-weight: 700; margin: 0 0 10px; }
/* align-items:start matters: the order-event and Slack cards are far taller than
   the Email card, which would otherwise stretch to match its row. */
.ntf-stack {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(460px, 100%), 1fr));
  gap: 10px;
  align-items: start;
  /* Same measure as the matrix above, which also means auto-fit yields one column: the
     Email card is a single row and Slack is a block, so side by side they left a large
     empty space under Email that read as a missing card. */
  max-width: 820px;
}
.ntf-card { background: #fff; border: 1px solid var(--line); border-radius: var(--radius); padding: 15px 18px; display: flex; align-items: center; gap: 14px; }
.ntf-card-tall { flex-direction: column; align-items: stretch; gap: 0; }
.ntf-card-row { display: flex; align-items: center; gap: 14px; }
.ntf-card-body { display: grid; gap: 14px; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line-soft); }
.ntf-ico { flex: 0 0 auto; width: 38px; height: 38px; border-radius: 10px; background: var(--accent-weak); color: var(--accent); display: grid; place-items: center; }
.ntf-ico--brand { background: #fff; border: 1px solid var(--line); }
.ntf-card-txt { flex: 1; min-width: 0; }
.ntf-card-txt strong { font-size: 14px; font-weight: 700; display: block; }
.ntf-card-txt p { margin: 2px 0 0; color: var(--muted); font-size: 12.5px; }
.ntf-card-txt p a { color: var(--accent); }

/* ---- Order-event matrix -----------------------------------------------------
   Events down, audiences across. This replaced two separate per-audience checkbox
   grids: the same six events rendered twice, 11 boxes across two cards of unequal
   height, so "who gets told when an order is refunded?" meant comparing two grids.
   A table because the data genuinely has two axes - the row and column headers are
   what let a screen reader announce "Refund issued, You, checkbox". */
.evt-matrix-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  /* Capped deliberately. Full-bleed, the event name column ran ~1000px wide and the
     checkboxes sat at the far right of the screen, so pairing a row with its box meant
     tracking across empty space. This measure keeps label and control in one glance -
     and it collapses .ntf-stack below to a single column, which removes the hole that
     sat under the short Email card next to the tall Slack one. */
  max-width: 820px;
}

/* A section label that follows content needs to read as a break, not as a caption for
   the card above it. */
.ntf-page .evt-matrix-card + .ntf-sec-label,
.ntf-page .ntf-stack + .ntf-sec-label { margin-top: 26px; }

.evt-matrix { width: 100%; border-collapse: collapse; }

.evt-matrix th,
.evt-matrix td {
  text-align: left;
  padding: 11px 18px;
  border-bottom: 1px solid var(--line-soft);
}

.evt-matrix thead th {
  vertical-align: bottom;
  background: var(--panel-subtle);
  border-bottom: 1px solid var(--line);
}

/* The two audience columns are equal and narrow; the event name takes the rest, so the
   checkboxes line up as a column the eye can run down. */
.evt-matrix thead th + th { width: 190px; }
.evt-matrix tbody tr:last-child th,
.evt-matrix tbody tr:last-child td { border-bottom: 0; }
.evt-matrix tbody tr:hover th,
.evt-matrix tbody tr:hover td { background: var(--hover); }

.evt-matrix tbody th { font-size: 13px; font-weight: 600; }

.evt-col-name { display: block; font-size: 12.5px; font-weight: 700; }
.evt-col-sub { display: block; font-size: 11.5px; color: var(--faint); margin-top: 1px; }
.evt-col-sub--warn { color: var(--amber); font-weight: 600; }
.evt-owner-field { max-width: 340px; }
.evt-owner-field label { font-size: 12px; }

.evt-check { display: inline-flex; cursor: pointer; padding: 2px; }
.evt-check input { accent-color: var(--accent); width: 16px; height: 16px; margin: 0; }

/* An empty cell reads as a rendering bug, so the one event that has no owner alert
   says so. */
.evt-na { font-size: 11.5px; color: var(--faint); }

/* Per-event wording, in a second row spanning the table. The divider moves onto that
   row so the pair reads as one block: a rule between an event and its own editor would
   split them apart. Hover stays on the event row - highlighting a form while typing in
   it is noise. */
.evt-matrix tbody tr:not(.evt-copy-row) th,
.evt-matrix tbody tr:not(.evt-copy-row) td { border-bottom: 0; }
.evt-matrix tbody tr.evt-copy-row td { padding-top: 0; padding-bottom: 0; }
.evt-matrix tbody tr.evt-copy-row:hover td { background: transparent; }
.evt-matrix tbody tr.evt-copy-row:last-child td { border-bottom: 0; }

.evt-copy > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 0 11px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--faint);
}
.evt-copy > summary::-webkit-details-marker { display: none; }
.evt-copy > summary:hover,
.evt-copy[open] > summary { color: var(--text); }
.evt-copy-panel { display: grid; gap: 16px; padding: 2px 0 18px; }
.evt-copy-block { display: grid; gap: 10px; align-content: start; }
.evt-copy-who {
  margin: 0;
  font-size: 10.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--faint);
}
.evt-copy-block textarea.inp { resize: vertical; min-height: 84px; }
.evt-copy-hint .mono { margin-right: 6px; white-space: nowrap; }

/* Two audiences side by side once there's room; the hint runs under both. */
@media (min-width: 760px) {
  .evt-copy-panel { grid-template-columns: 1fr 1fr; gap: 16px 22px; }
  .evt-copy-hint { grid-column: 1 / -1; }
  /* One audience (Order fulfilled) - a single column capped so the fields don't
     stretch the width of the table. */
  .evt-copy-panel--single { grid-template-columns: minmax(0, 520px); }
}

.evt-matrix-notes {
  display: grid;
  gap: 8px;
  padding: 13px 18px 15px;
  border-top: 1px solid var(--line);
  background: var(--panel-subtle);
}

.evt-warn {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
  font-weight: 600;
  color: var(--amber);
}

.evt-warn svg { flex: none; margin-top: 2px; }

/* Below sm a three-column matrix stops being readable, so each event becomes its own
   block with its audience labels shown inline (the same shape .table-cards uses). */
@media (max-width: 639px) {
  .evt-matrix thead { display: none; }
  .evt-matrix,
  .evt-matrix tbody,
  .evt-matrix tr,
  .evt-matrix th,
  .evt-matrix td { display: block; width: auto; }
  .evt-matrix tbody tr {
    padding: 4px 0 10px;
    border-bottom: 1px solid var(--line-soft);
  }
  .evt-matrix tbody tr:last-child { border-bottom: 0; }
  .evt-matrix tbody th { padding-bottom: 4px; border-bottom: 0; }
  .evt-matrix tbody td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 5px 18px;
    border-bottom: 0;
  }
  .evt-matrix tbody td::before {
    content: attr(data-label);
    font-size: 12px;
    color: var(--muted);
  }
}

/* ============ Brand (settings sub-page) ============ */
body:has(.brnd-page) .workspace { background: #edf4f0; }
.brnd-page { min-width: 0; }
.brnd-page .back-link { margin-bottom: 16px; }
.brnd-file { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; }
.brnd-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.brnd-card { background: #fff; border: 1px solid var(--line); border-radius: var(--radius); padding: 16px 18px 18px; }
.brnd-card-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.brnd-card-head strong { font-size: 14px; font-weight: 700; }
/* Taller now the cards are full width - a bigger logo/signature preview is more
   useful than a wider band of empty card. */
.brnd-preview {
  margin: 13px 0 11px; height: 150px; border-radius: 12px; border: 1px dashed var(--line);
  background: #f7faf8; display: grid; place-items: center; padding: 12px; overflow: hidden;
}
.brnd-preview img { max-width: 100%; max-height: 100%; object-fit: contain; }
.brnd-initials {
  width: 56px; height: 56px; border-radius: 13px; background: var(--accent); color: #d8fdec;
  display: grid; place-items: center; font-size: 19px; font-weight: 700; letter-spacing: .02em;
}
.brnd-empty { font-size: 12.5px; color: var(--faint); }
.brnd-meta { margin: 0 0 11px; font-size: 12.5px; color: var(--muted); }
.brnd-note {
  display: flex; gap: 9px; align-items: flex-start; margin: 14px 0 0; padding: 12px 14px;
  border-radius: 12px; background: var(--accent-weak); color: var(--muted); font-size: 12.5px; line-height: 1.5;
}
.brnd-note svg { flex: 0 0 auto; margin-top: 1px; color: var(--accent); }
.brnd-note a { color: var(--accent); }
.brnd-page .notif-actions { margin-top: 18px; }
@media (max-width: 640px) {
  .brnd-grid { grid-template-columns: 1fr; }
}

/* ============ Templates hub (every document type) ============ */
/* The hero is the shared immersive band (see .hub-hero above, grouped with the
   other four). Everything below is the family/card grid, which is the hub's own. */
body:has(.hub-page) .workspace { background: #edf4f0; }
.hub-page { min-width: 0; }
.hub-hero { display: flex; align-items: flex-end; justify-content: space-between; gap: 20px; flex-wrap: wrap; }
.hub-hero-main { min-width: 0; }

.hub-family { margin-top: 22px; }
/* The family label reads as a divider so the four groups scan as one list with
   sections, not four unrelated panels. */
.hub-family-head { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.hub-family-head h2 { margin: 0; font-size: 13px; font-weight: 750; letter-spacing: .01em; }
.hub-family-note { font-size: 11.5px; color: var(--faint); }
.hub-family-note::before { content: "· "; }

.hub-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); gap: 12px; }
/* The cell, not the card, is the grid item: an assignable type carries a picker
   form beside its card, and the two must share one column. */
.hub-cell { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.hub-cell > .hub-card { flex: 1 1 auto; }
.hub-card {
  display: flex; flex-direction: column; gap: 7px; padding: 15px 16px 14px;
  border: 1px solid var(--line); border-radius: var(--radius); background: #fff;
  color: var(--text); text-decoration: none; min-width: 0;
}
a.hub-card:hover { border-color: var(--accent); }
a.hub-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.hub-card-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.hub-card-top b { font-size: 14px; font-weight: 700; letter-spacing: -.008em; line-height: 1.3; }
.hub-crown { flex: 0 0 auto; margin-top: 2px; }
.hub-card-blurb { font-size: 12px; color: var(--muted); line-height: 1.5; }
.hub-card-foot { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-height: 18px; }
.hub-card-count {
  font-size: 10px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--accent); background: var(--accent-weak); border-radius: 4px; padding: 2px 6px;
}
.hub-card-count.is-muted { color: var(--faint); background: var(--hover); }
.hub-card-default { font-size: 11.5px; color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.hub-card-shares { font-size: 11px; color: var(--faint); font-style: italic; }
.hub-card-go { margin-top: auto; padding-top: 4px; font-size: 11.5px; font-weight: 700; color: var(--accent); }
/* Locked and planned are different states and must not look alike: locked is a
   real destination behind a plan, planned is not a destination at all. */
.hub-card.is-locked .hub-card-go { color: var(--muted); }
.hub-card.is-planned { border-style: dashed; background: var(--panel-subtle); }
.hub-card.is-planned .hub-card-top b { color: var(--muted); }
.hub-phase {
  flex: 0 0 auto; font-size: 9.5px; font-weight: 700; letter-spacing: .09em;
  color: var(--muted); background: var(--hover); border-radius: 4px; padding: 2px 5px;
}
/* Per-type template picker. Reads as an attachment to the card above it, not a
   second card - hence no border on top and a tighter, quieter frame. */
/* Grid, not flex: in a 232px column a row of label + select + button starved the
   select until "Same as invoice" truncated to "Same as invoic". The label spans its
   own line so the select keeps the full width beside the button. */
.hub-assign {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 6px; align-items: center;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--panel-subtle);
}
.hub-assign label {
  grid-column: 1 / -1; font-size: 10px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--faint);
}
.hub-assign select {
  min-width: 0; width: 100%; min-height: 30px; font-size: 12px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 4px 6px; background: #fff; color: var(--text);
}
.hub-foot { margin: 24px 0 0; font-size: 12px; color: var(--faint); max-width: 72ch; }
.hub-foot a { color: var(--accent); }

/* ============ Invoice templates (gallery) ============ */
body:has(.tpl-page) .workspace { background: #edf4f0; }
.tpl-page .back-link { margin-bottom: 16px; }
.tpl-hero { display: flex; align-items: flex-end; justify-content: space-between; gap: 20px; flex-wrap: wrap; }
.tpl-hero-main { min-width: 0; }
.tpl-hero-locked { display: block; font-size: 12px; color: rgba(243, 239, 227, .72); margin-bottom: 8px; }

/* "New template" tile that matches the card grid. */
/* display:block (not flex) so the native <details> collapse keeps the create
   form hidden until the summary is clicked; the summary centers itself. */
.tpl-card-new { border: 1.5px dashed var(--line); border-radius: var(--radius); background: #fff; display: block; min-height: 190px; }
.tpl-card-new:hover { border-color: var(--accent); }
.tpl-card-new[open] { padding: 16px; }
.tpl-new-summary { list-style: none; display: flex; flex-direction: column; align-items: center; gap: 8px; color: var(--accent); font-weight: 700; font-size: 13px; cursor: pointer; height: 100%; justify-content: center; min-height: 190px; text-align: center; }
.tpl-new-summary::-webkit-details-marker { display: none; }
/* When open the summary becomes a compact header above the form. */
.tpl-card-new[open] .tpl-new-summary { display: none; }
.tpl-new-plus { font-size: 26px; line-height: 1; }
/* Hidden until the card is open - an explicit display beats the UA "hide closed
   <details> children" rule, so it must be gated on [open], not left as grid. */
.tpl-new-form { display: none; gap: 8px; }
.tpl-card-new[open] .tpl-new-form { display: grid; }
.tpl-new-form input { min-height: 34px; border: 1px solid var(--line); border-radius: 6px; padding: 6px 9px; }

/* ============ Plans (comparison) ============ */
body:has(.plans-page) .workspace { background: #edf4f0; }
.plans-page .back-link { margin-bottom: 16px; }
.plans-hero { text-align: center; }
.plans-hero p { margin-left: auto; margin-right: auto; }
/* Four tiers: 4-up on a wide screen, 2x2 rather than 3+1 when it can't fit -
   a lone card on its own row reads as a mistake. */
/* Single call to action above the cards, for the Shopify-hosted plan flow. */
.plans-cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  margin-bottom: 18px;
  text-align: center;
}

.plans-cta .button { min-width: 240px; justify-content: center; }
.plans-cta p { font-size: 13px; margin: 0; max-width: 52ch; }

.plans-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; align-items: stretch; }
@media (max-width: 1180px) { .plans-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 820px) { .plans-grid { grid-template-columns: 1fr; } }
.plans-page .plan-card { display: flex; flex-direction: column; }
.plans-page .plan-tag.is-current { background: #0f4c3d; color: #7cf0c6; }
.plans-page .plan-features { list-style: none; margin: 14px 0 18px; padding: 0; display: grid; gap: 8px; }
.plans-page .plan-features li { display: flex; gap: 9px; align-items: flex-start; font-size: 12.5px; color: var(--text); }
.plans-page .plan-features svg { color: var(--accent); flex: none; margin-top: 2px; }
.plans-page .plan-cta { margin-top: auto; }
.plans-page .plan-cta .button, .plans-page .plan-cta form, .plans-page .plan-cta .button-ghost { width: 100%; justify-content: center; }
.plans-page .plan-cta button { width: 100%; }

/* ===================== Delivery (Invoice sending) - immersive ===================== */
body:has(.dlv-page) .workspace { background: #edf4f0; }

/* Full width, unlike the other settings pages: the rail plus a fields/preview
   split uses everything it's given. The pane body caps itself further down so
   an ultra-wide monitor doesn't produce a 2,000px subject input. */
.dlv-page { max-width: none; }
.dlv-page .back-link { margin-bottom: 16px; }
.dlv-page .dlv-hero { margin-bottom: 18px; }

.dlv-hero {
  background: linear-gradient(140deg, #082e25, #0f4c3d 82%);
  border-radius: 14px;
  color: #f3efe3;
  padding: 22px 24px;
  margin-bottom: 18px;
}

.dlv-hero-eyebrow {
  font-size: 10.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: #7cf0c6;
  font-weight: 700;
}

.dlv-hero h1 { margin: 9px 0 0; font-size: 22px; font-weight: 750; letter-spacing: -.01em; color: #fff; }
.dlv-hero p { margin: 6px 0 0; font-size: 12.5px; color: rgba(243, 239, 227, .72); max-width: 64ch; }

/* The delivery form is an anchor only - its fields live inside the channel panes
   and associate through form="dlv-form", because those panes carry POST forms of
   their own and forms can't nest. It must take up no space. */
.dlv-anchor { display: block; margin: 0; padding: 0; }

/* ---- Console: destination rail + detail pane ---------------------------- */
.cn-shell {
  display: grid;
  grid-template-columns: 236px minmax(0, 1fr);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
  min-height: 420px;
}

.cn-rail {
  border-right: 1px solid var(--line);
  background: var(--panel-subtle);
  padding: 12px;
}

.cn-rail-label {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10px;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 600;
  padding: 4px 8px 9px;
}

.cn-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  padding: 9px 10px;
  border-radius: 9px;
  border: 1px solid transparent;
  background: none;
  font: inherit;
  color: var(--text);
  cursor: pointer;
}

.cn-item + .cn-item { margin-top: 3px; }
.cn-item:hover { background: #fff; }
.cn-item[aria-selected="true"] { background: #fff; border-color: var(--line); }
.cn-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.cn-item-ico {
  width: 26px;
  height: 26px;
  border-radius: 7px;
  flex: none;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  background: #fff;
}

.cn-item-ico svg { display: block; }

.cn-item-txt { min-width: 0; flex: 1; }
.cn-item-txt strong { display: block; font-size: 12.5px; font-weight: 650; }

/* The state line is the point of this layout: it answers "is this destination
   actually working?" without opening the pane. Mono so the three lines align. */
.cn-item-txt span {
  display: block;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10.5px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cn-item[data-state="on"] .cn-item-txt span { color: var(--green); }
.cn-item[data-state="todo"] .cn-item-txt span { color: var(--amber); }

.cn-led { width: 7px; height: 7px; border-radius: 50%; flex: none; background: #d7dcda; }
.cn-item[data-state="on"] .cn-led { background: var(--accent); }
.cn-item[data-state="todo"] .cn-led { background: var(--amber); }

.cn-pane { display: none; padding: 20px 22px; min-width: 0; }
.cn-pane.is-on { display: block; }

.cn-pane-head {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--line-soft);
}

.cn-pane-title { flex: 1; min-width: 0; }
.cn-pane-title h2 { font-size: 17px; font-weight: 700; }
.cn-pane-title p { margin-top: 3px; font-size: 12px; color: var(--muted); max-width: 62ch; }

/* The cap that keeps a full-width page usable: past this the inputs get harder
   to read, not easier, so the pane stops growing and the page gutters instead. */
.cn-pane-body { display: grid; gap: 16px; padding-top: 16px; max-width: 1560px; }

/* Grid items stretch, which turns a lone Connect button into a full-width slab. */
.cn-pane-body > .button,
.cn-pane-body > .button-primary,
.cn-pane-body > .button-secondary,
.cn-block-body > .button,
.cn-block-body > .button-primary,
.cn-block-body > .button-secondary { justify-self: start; }

/* Email isn't connected: sends look fine but sit in the outbox, so say so where
   the merchant is about to write the copy. */
.cn-notice {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--amber);
  background: var(--amber-weak);
  border: 1px solid color-mix(in srgb, var(--amber) 25%, transparent);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
}

/* A broken connection the merchant has to repair before the pane's settings mean
   anything - so it sits above them and carries its own fix. Red-weak rather than
   the full-bleed .reauth-banner treatment: this is scoped to one destination, not
   to the whole app. */
.cn-alert {
  display: grid;
  gap: 8px;
  justify-items: start;
  background: var(--red-weak);
  border: 1px solid #f1c9c3;
  border-radius: 9px;
  padding: 12px 13px;
  color: var(--red);
  font-size: 13px;
}

.cn-alert strong { font-weight: 760; }
.cn-alert p { margin: 0; color: inherit; }

/* A settled fact plus the one action that changes it: From address, workspace,
   Drive account, Drive folder. */
.cn-kv {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  background: var(--panel-subtle);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 9px 11px;
}

.cn-kv-label {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10.5px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
  flex: none;
}

.cn-kv code {
  font-size: 12px;
  padding: 2px 6px;
  border-radius: 4px;
  background: rgba(0, 0, 0, .04);
  word-break: break-all;
}

.cn-kv-action { margin-left: auto; flex: none; }
.cn-kv-action .button-link,
.cn-kv .button-link {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 12px;
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.cn-kv .button-link:hover { color: var(--text); }

/* Documents share one form; only the selected one is on screen. */
.cn-docs {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--line-soft);
}

.cn-doc {
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
  border: 0;
  background: none;
  padding: 6px 10px 8px;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.cn-doc[aria-selected="true"] { color: var(--accent); border-bottom-color: var(--accent); }
.cn-doc:hover { color: var(--text); }
.cn-doc:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

.cn-split {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
  gap: 18px;
  align-items: start;
}

.cn-docfields { display: grid; gap: 14px; min-width: 0; }
.cn-docpanel { display: grid; gap: 14px; }
/* A class with a display wins over the UA sheet's [hidden] rule, so the
   unselected documents would all stay on screen without this. */
.cn-docpanel[hidden],
.cn-pane[hidden] { display: none; }

/* The gift receipt's printed lines aren't email copy, so they sit apart. */
.cn-subgroup {
  display: grid;
  gap: 14px;
  border-top: 1px solid var(--line-soft);
  padding-top: 14px;
}

.cn-subgroup-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--muted);
}

.cn-narrow { max-width: 360px; }

/* Sending domain, nested inside the Email pane - it's the From address, not a
   destination of its own. */
.cn-block {
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow: hidden;
}

.cn-block-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 14px;
  background: var(--panel-subtle);
  border-bottom: 1px solid var(--line);
}

.cn-block-head h3 { font-size: 13.5px; font-weight: 700; flex: 1; }
.cn-block-body { padding: 14px; display: grid; gap: 12px; }
.cn-inline-form { display: grid; gap: 12px; justify-items: start; }
.cn-quiet-form { justify-self: start; }
.cn-quiet-form .button-link {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 12px;
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.cn-quiet-form .button-link:hover { color: var(--text); }

/* Jumped to from "Use your own domain" - a brief flag so the target is obvious. */
.cn-block.is-flagged { border-color: var(--accent); }
.cn-block.is-flagged .cn-block-head { background: var(--accent-weak); }

/* ---- Empty states: a destination with nothing to configure yet -----------
   Rather than a lone button on a white field, the pane previews the artefact
   the merchant would get - the Slack alert, the Drive folder - because "what
   does this actually produce?" is the question stopping them from connecting. */
.es {
  min-height: 340px;
  display: grid;
  place-items: center;
  padding: 24px 20px;
}

.es-inner {
  width: 100%;
  max-width: 620px;
  display: grid;
  justify-items: center;
  gap: 15px;
  text-align: center;
}

/* Holds the mock. The wash off the bottom is what marks it as a preview
   rather than live data. */
.es-stage {
  width: 100%;
  position: relative;
  padding: 20px;
  border-radius: 12px;
  background: linear-gradient(180deg, var(--panel-subtle), #fff);
  border: 1px solid var(--line-soft);
}

.es-stage::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 12px;
  pointer-events: none;
  background: linear-gradient(180deg, transparent 55%, rgba(255, 255, 255, .78));
}

.es-tag {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 600;
}

.es-copy h3 { font-size: 16px; font-weight: 700; }
.es-copy p {
  margin-top: 5px;
  font-size: 12.5px;
  color: var(--muted);
  max-width: 52ch;
  margin-inline: auto;
}

/* Slack message mock. Desaturated so it reads as an example, not a live feed. */
.es-slack {
  display: flex;
  gap: 10px;
  text-align: left;
  filter: saturate(.55);
  opacity: .88;
}

.es-slack-avatar {
  width: 32px;
  height: 32px;
  border-radius: 7px;
  flex: none;
  background: var(--forest-2, #0f4c3d);
  display: grid;
  place-items: center;
}

.es-slack-body { min-width: 0; flex: 1; }

.es-slack-meta {
  display: flex;
  align-items: baseline;
  gap: 7px;
  flex-wrap: wrap;
}

.es-slack-name { font-size: 13px; font-weight: 700; color: #1d1c1d; }

.es-slack-badge {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  background: #e8e8e8;
  color: #616061;
  border-radius: 3px;
  padding: 1px 4px;
}

.es-slack-time { font-size: 11px; color: #616061; }
.es-slack-lines { margin-top: 3px; display: grid; gap: 2px; }

.es-slack-lines p {
  font-size: 12.5px;
  color: #1d1c1d;
  display: flex;
  gap: 6px;
  align-items: baseline;
}

.es-slack-em { flex: none; font-size: 12px; }

/* Drive folder mock. */
.es-drive { text-align: left; filter: saturate(.55); opacity: .92; }

.es-drive-crumb {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  color: var(--muted);
  padding-bottom: 9px;
  border-bottom: 1px solid var(--line);
}

.es-drive-row {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 2px;
  border-bottom: 1px solid var(--line-soft);
}

.es-drive-row:last-child { border-bottom: 0; }
.es-drive-row.is-ghost { color: #cdd4d1; }

.es-drive-name {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11.5px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Stands in for "and the ones after it" without inventing filenames. */
.es-drive-bar {
  height: 7px;
  border-radius: 999px;
  background: var(--line);
}

@media (max-width: 639px) {
  .es { min-height: 0; padding: 18px 4px; }
  .es-stage { padding: 15px; }
}

@media (max-width: 1023px) {
  .cn-split { grid-template-columns: minmax(0, 1fr); }
  .mail-prev { position: static; }
}

@media (max-width: 767px) {
  .cn-shell { grid-template-columns: minmax(0, 1fr); }
  .cn-rail { border-right: 0; border-bottom: 1px solid var(--line); }
  .cn-pane { padding: 16px; }
}

.ph-label {
  margin-top: 12px;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 600;
}

.ph-chip {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11.5px;
  color: var(--muted);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 3px 7px;
}

/* Connected-account block (Slack): status row, actions, and note aligned. */
.notif-connect {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
}

.notif-connect-status {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text);
}

.notif-connect-dot {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-weak);
}

.notif-connect .button-inline {
  margin: 0;
}

.notif-connect-note {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.5;
}

.notif-actions {
  margin-top: 22px;
}

/* Toggle switch */
.switch {
  position: relative;
  display: inline-block;
  width: 38px;
  height: 22px;
  flex: 0 0 auto;
}

.switch input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.switch-track {
  position: absolute;
  inset: 0;
  background: var(--line);
  border-radius: 999px;
  transition: background 0.15s ease;
  pointer-events: none;
}

.switch-track::before {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  background: #fff;
  border-radius: 50%;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
  transition: transform 0.15s ease;
}

.switch input:checked + .switch-track {
  background: var(--accent);
}

.switch input:checked + .switch-track::before {
  transform: translateX(16px);
}

.switch input:focus-visible + .switch-track {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ===================== Support ===================== */
.support-head {
  margin-bottom: 28px;
}

.support-head h1 {
  margin: 0;
  font-size: 26px;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.support-head p {
  margin: 8px 0 0;
  color: var(--muted);
  font-size: 14px;
}

.support-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 24px;
  align-items: start;
}

.support-card {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: #fff;
  padding: 22px 24px;
}

.support-card-head {
  margin-bottom: 18px;
}

.support-card-head h2 {
  margin: 0 0 4px;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.support-card-head p {
  margin: 0;
  color: var(--muted);
  font-size: 13px;
}

.support-actions {
  margin-top: 16px;
}

.support-rail {
  display: grid;
  gap: 16px;
}

.support-rail .support-card {
  padding: 20px;
}

.support-card h3 {
  margin: 0 0 4px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text);
}

.support-sub {
  margin: 0;
  color: var(--muted);
  font-size: 12.5px;
}

.support-reach {
  margin-top: 6px;
}

.support-chan {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 14px 0;
  border-top: 1px solid var(--line-soft);
  color: inherit;
}

.support-chan:first-child {
  border-top: 0;
}

.support-ico {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  background: var(--accent-weak);
  color: var(--accent);
  display: grid;
  place-items: center;
}

.support-ico svg {
  width: 19px;
  height: 19px;
}

.support-chan strong {
  display: block;
  font-size: 14px;
}

.support-chan span {
  color: var(--muted);
  font-size: 12.5px;
}

.support-faq {
  margin-top: 24px;
}

.support-faq .faq {
  margin-top: 6px;
}

.support-faq details {
  border-top: 1px solid var(--line-soft);
}

.support-faq details:first-child {
  border-top: 0;
}

.support-faq summary {
  list-style: none;
  cursor: pointer;
  padding: 15px 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  font-size: 14px;
  font-weight: 500;
}

.support-faq summary::-webkit-details-marker {
  display: none;
}

.support-faq summary::after {
  content: "";
  width: 9px;
  height: 9px;
  border-right: 1.6px solid var(--faint);
  border-bottom: 1.6px solid var(--faint);
  transform: rotate(45deg);
  transition: transform 0.15s ease;
  flex: 0 0 auto;
  margin-right: 3px;
}

.support-faq details[open] summary::after {
  transform: rotate(-135deg);
}

.support-faq details p {
  margin: 0 0 15px;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.55;
  max-width: 90%;
}

@media (max-width: 900px) {
  .support-layout {
    grid-template-columns: minmax(0, 1fr);
  }
}

.order-missing-state {
  border-color: #f3c9c2;
  background: var(--red-weak);
}

.preview-card {
  padding: 14px;
}

.preview-card h3 {
  font-size: 14px;
  font-weight: 760;
}

.split-layout {
  display: grid;
  grid-template-columns: minmax(280px, 360px) minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}

.split-layout > .section-card {
  max-height: calc(100vh - 118px);
  overflow: auto;
}

.invoice-editor-shell {
  gap: 16px;
}

/* Keep Cancel/Save reachable. The shell is the scroll container, so with a long
   section open the actions used to sit below its fold - you had to scroll the
   rail back down to save what you had just changed. */
.invoice-editor-shell > .button-inline:last-child {
  position: sticky;
  bottom: -16px;
  z-index: 1;
  /* Negative margins match .section-card's 16px padding so the bar spans the
     full card width and sits flush with its bottom edge. */
  margin: 0 -16px -16px;
  padding: 12px 16px;
  background: var(--panel);
  border-top: 1px solid var(--line-soft);
}

/* Group the editor accordions into a few labelled clusters (Design / Content /
   Line items / Layout) so the panel reads as 4 groups, not a wall of sections. */
.editor-group {
  display: grid;
  gap: 8px;
}

.editor-group-title {
  margin: 2px 2px 1px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--faint);
}

/* The invoice and packing editor sections: <details name="editor">, so the
   browser keeps one open at a time. */
.acc-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  list-style: none;
  cursor: pointer;
  color: var(--text);
}

.acc-header:hover .acc-chevron {
  color: var(--text);
}

.acc-header:focus-visible {
  outline: 2px solid var(--accent-weak);
  outline-offset: 4px;
  border-radius: 4px;
}

.acc-chevron {
  flex: none;
  color: var(--muted);
  transition: transform 0.18s ease;
}

.acc-header::-webkit-details-marker {
  display: none;
}

.acc-item[open] .acc-chevron {
  transform: rotate(180deg);
}

.acc-body {
  margin-top: 12px;
}

/* Inline "this is on tier X" chip, for locks that sit in a sentence rather
   than on a card corner. */
.plan-badge {
  display: inline-flex;
  align-items: center;
  border-radius: 6px;
  padding: 2px 7px;
  margin-right: 6px;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: var(--accent-weak);
  color: var(--accent);
}

/* One-row, horizontally scrollable layout filter (was a 3-row wrap). */
.preset-filter {
  display: flex;
  flex-wrap: nowrap;
  gap: 6px;
  overflow-x: auto;
  padding-bottom: 3px;
  scrollbar-width: none;
}

.preset-filter::-webkit-scrollbar {
  display: none;
}

.preset-chip {
  flex: none;
  font: inherit;
  font-size: 11px;
  font-weight: 640;
  color: var(--muted);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 10px;
  cursor: pointer;
  white-space: nowrap;
}

.preset-chip:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* Palette filter chips carry their own colour dot, so a merchant picks a
   palette by eye rather than by name. */

.preset-chip-dot {
  width: 10px;
  height: 10px;
  border-radius: 999px;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12);
}

/* A palette the plan doesn't include still filters (so the locked cards can be
   found and their upgrade badge read), but reads as unavailable. */
.preset-chip.is-locked {
  opacity: 0.55;
}

/* The other filter rows hold a handful of chips and scroll sideways. The palette
   row holds 21, which is unusable as a scroll strip, so it wraps instead. */

.lib-preset-more {
  align-self: center;
  margin-top: 10px;
}

.preset-chip[aria-pressed="true"] {
  color: #fff;
  background: var(--line-strong);
  border-color: var(--line-strong);
}

/* ---- Library "Start from a preset": minimal swatch + name cards ---- */
.lib-preset-scroll {
  max-height: 430px;
  overflow-y: auto;
  margin: 0 -4px;
  padding: 2px 4px;
}

.lib-preset-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 8px;
}

.lib-preset {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 11px;
  text-align: left;
  font: inherit;
  color: var(--text);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 10px;
  cursor: pointer;
}

.lib-preset:hover {
  border-color: var(--accent);
  background: var(--accent-weak);
}

.lib-preset-swatch {
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 8px;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
}

.lib-preset-text {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.lib-preset-name {
  font-weight: 680;
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.lib-preset-meta {
  font-size: 11px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.lib-preset.is-locked {
  cursor: not-allowed;
}

.lib-preset.is-locked:hover {
  border-color: var(--line);
  background: #fff;
}

.lib-preset.is-locked .lib-preset-swatch {
  filter: grayscale(1);
  opacity: 0.55;
}

.lib-preset.is-locked .lib-preset-name {
  color: var(--muted);
}

/* --- Block builder ------------------------------------------------------
   Block content classes (.bb-text/-img/-cols/-items/-totals/-divider) are
   shared by the printed invoice (partials/invoice_blocks.erb) and the editor
   canvas; the .bb-block chrome is editor-only. */
.bb-text { white-space: pre-line; }
.bb-img { max-width: 100%; display: inline-block; }
.bb-cols { display: grid; gap: 20px; }
.bb-cols-2 { grid-template-columns: 1fr 1fr; }
.bb-cols-3 { grid-template-columns: 1fr 1fr 1fr; }
.bb-items { width: 100%; border-collapse: collapse; margin: 4px 0; }
.bb-items th { text-align: left; font-size: 10px; text-transform: uppercase; letter-spacing: .03em; background: var(--invoice-accent, #147c64); color: #fff; padding: 7px 8px; }
.bb-items td { padding: 8px; border-bottom: 1px solid var(--line-soft); font-size: 13px; }
.bb-items .r { text-align: right; }
.bb-totals { max-width: 260px; margin-left: auto; }
.bb-total-row { display: flex; justify-content: space-between; padding: 3px 0; font-size: 13px; }
.bb-total-grand { border-top: 1px solid var(--line); margin-top: 4px; padding-top: 7px; font-weight: 700; color: var(--invoice-accent, #147c64); }
.bb-divider { border: 0; }
.bb-muted { color: var(--muted); font-style: italic; }
.bb-doc { color: #24282d; font-family: var(--invoice-font, inherit); }

.bb-editor-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.bb-brandbar { display: flex; flex-wrap: wrap; gap: 16px; align-items: end; padding: 14px; border: 1px solid var(--line); border-radius: 10px; margin-bottom: 16px; }
.bb-brandbar .field { margin: 0; }
.bb-brandbar input[type="text"] { min-width: 160px; }
.bb-logo-thumb { height: 30px; margin-left: 8px; vertical-align: middle; }

.bb-editor-grid { display: grid; grid-template-columns: 150px minmax(0, 1fr) 260px; gap: 18px; align-items: start; }
.bb-palette { display: flex; flex-direction: column; gap: 8px; position: sticky; top: 12px; }
.bb-palette .eyebrow { margin-bottom: 2px; }
.bb-canvas-wrap { background: #ececea; border: 1px solid var(--line); border-radius: 12px; padding: 20px; max-height: 80vh; overflow: auto; }
.bb-canvas { background: #fff; border-radius: 4px; padding: 40px 44px; box-shadow: 0 1px 3px rgba(0,0,0,.08); font-size: 13px; color: #24282d; }
.bb-props { position: sticky; top: 12px; border: 1px solid var(--line); border-radius: 10px; padding: 14px; max-height: 80vh; overflow: auto; }
.bb-props-empty { color: var(--muted); font-size: 13px; }
.bb-props .field { margin-bottom: 10px; }
.bb-props textarea { min-height: 80px; }
.bb-token-chips { display: flex; flex-wrap: wrap; gap: 4px; margin: 2px 0 12px; }
.bb-token-chips .ph-label { width: 100%; margin: 0 0 2px; }
.bb-chip { border: 1px solid var(--line); background: #fff; border-radius: 20px; padding: 1px 8px; font-size: 11px; color: var(--muted); cursor: pointer; }
.bb-chip:hover { color: var(--text); border-color: var(--accent); }

.bb-block { position: relative; border: 1px dashed transparent; border-radius: 6px; margin: 2px 0; }
.bb-block:hover { border-color: var(--line); }
.bb-block.is-selected { border-color: var(--accent); border-style: solid; }
.bb-block.is-dragging { opacity: .5; }
.bb-block-bar { display: none; align-items: center; gap: 6px; position: absolute; top: -11px; right: 6px; background: #fff; border: 1px solid var(--line); border-radius: 20px; padding: 1px 6px; font-size: 11px; z-index: 2; }
.bb-block:hover .bb-block-bar, .bb-block.is-selected .bb-block-bar { display: flex; }
.bb-block-type { color: var(--muted); text-transform: capitalize; }
.bb-drag { cursor: grab; color: var(--muted); }
.bb-block-actions button { border: 0; background: none; cursor: pointer; color: var(--muted); font-size: 12px; padding: 0 2px; }
.bb-block-actions button:hover { color: var(--text); }
.bb-block-body { cursor: pointer; padding: 2px; }

@media (max-width: 1000px) {
  .bb-editor-grid { grid-template-columns: 1fr; }
  .bb-palette { position: static; flex-direction: row; flex-wrap: wrap; }
  .bb-props { position: static; }
}

/* "Show all N presets" - spans the grid; hidden once expanded or when filtering. */

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

/* Bundled IBM Plex families (app/assets/fonts, OFL) so the on-screen
   invoice renders the SAME face the Prawn PDF embeds. Only the family actually
   referenced by --invoice-font is fetched by the browser. */
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/IBMPlexSans-Regular-dc244000.ttf") format("truetype"); }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/IBMPlexSans-Bold-59908dd7.ttf") format("truetype"); }
@font-face { font-family: "IBM Plex Sans"; font-style: italic; font-weight: 400; font-display: swap; src: url("/assets/IBMPlexSans-Italic-e7ed684d.ttf") format("truetype"); }
@font-face { font-family: "IBM Plex Sans"; font-style: italic; font-weight: 700; font-display: swap; src: url("/assets/IBMPlexSans-BoldItalic-b67bbef8.ttf") format("truetype"); }
@font-face { font-family: "IBM Plex Serif"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/IBMPlexSerif-Regular-1f61978b.ttf") format("truetype"); }
@font-face { font-family: "IBM Plex Serif"; font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/IBMPlexSerif-Bold-580fe936.ttf") format("truetype"); }
@font-face { font-family: "IBM Plex Serif"; font-style: italic; font-weight: 400; font-display: swap; src: url("/assets/IBMPlexSerif-Italic-aae1d91c.ttf") format("truetype"); }
@font-face { font-family: "IBM Plex Serif"; font-style: italic; font-weight: 700; font-display: swap; src: url("/assets/IBMPlexSerif-BoldItalic-c35a2ff6.ttf") format("truetype"); }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/IBMPlexMono-Regular-923ca06c.ttf") format("truetype"); }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/IBMPlexMono-Bold-ac6d30f8.ttf") format("truetype"); }
@font-face { font-family: "IBM Plex Mono"; font-style: italic; font-weight: 400; font-display: swap; src: url("/assets/IBMPlexMono-Italic-895c9a28.ttf") format("truetype"); }
@font-face { font-family: "IBM Plex Mono"; font-style: italic; font-weight: 700; font-display: swap; src: url("/assets/IBMPlexMono-BoldItalic-e02c9d85.ttf") format("truetype"); }
/* Display-only script face for the calligraphic wordmark on the
   `script_aesthetic` format. Regular only - see InvoiceFonts::SCRIPT_FAMILY. */
@font-face { font-family: "Invoice Script"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/GreatVibes-Regular-056a76e5.ttf") format("truetype"); }

/* The A-J theme faces (InvoiceFonts::THEME_STYLE_FILES, all OFL). Declared here
   because InvoiceFonts.theme_css_stack leads with these family names, and a
   family the browser cannot load falls back to a system face WITHOUT any error -
   the HTML would quietly stop matching the PDF, which is the one thing the
   self-hosting is for.
   Weight RANGES, not single weights: each file is one static cut, so the range
   maps every weight a theme might ask for onto the nearest cut we actually ship
   rather than letting the browser synthesise a bolder face than Prawn embeds.
   Where a family has no true italic the italic rule points at the upright file,
   mirroring how InvoiceFonts aliases those slots - a browser-synthesised oblique
   would look nothing like the PDF. */
@font-face { font-family: "Space Grotesk"; font-style: normal; font-weight: 300 500; font-display: swap; src: url("/assets/SpaceGrotesk-Medium-c65dbd35.ttf") format("truetype"); }
@font-face { font-family: "Space Grotesk"; font-style: normal; font-weight: 600 700; font-display: swap; src: url("/assets/SpaceGrotesk-Bold-5f81027f.ttf") format("truetype"); }
@font-face { font-family: "Space Grotesk"; font-style: italic; font-weight: 300 500; font-display: swap; src: url("/assets/SpaceGrotesk-Medium-c65dbd35.ttf") format("truetype"); }
@font-face { font-family: "Space Grotesk"; font-style: italic; font-weight: 600 700; font-display: swap; src: url("/assets/SpaceGrotesk-Bold-5f81027f.ttf") format("truetype"); }
@font-face { font-family: "Newsreader"; font-style: normal; font-weight: 300 500; font-display: swap; src: url("/assets/Newsreader-Regular-396caf21.ttf") format("truetype"); }
@font-face { font-family: "Newsreader"; font-style: normal; font-weight: 600 700; font-display: swap; src: url("/assets/Newsreader-SemiBold-217eda82.ttf") format("truetype"); }
@font-face { font-family: "Newsreader"; font-style: italic; font-weight: 300 500; font-display: swap; src: url("/assets/Newsreader-Italic-19b1a9e7.ttf") format("truetype"); }
@font-face { font-family: "Newsreader"; font-style: italic; font-weight: 600 700; font-display: swap; src: url("/assets/Newsreader-SemiBoldItalic-a64c3cc8.ttf") format("truetype"); }
@font-face { font-family: "Manrope"; font-style: normal; font-weight: 200 300; font-display: swap; src: url("/assets/Manrope-Light-77c6d5c7.ttf") format("truetype"); }
@font-face { font-family: "Manrope"; font-style: normal; font-weight: 400 500; font-display: swap; src: url("/assets/Manrope-Regular-9b466567.ttf") format("truetype"); }
@font-face { font-family: "Manrope"; font-style: normal; font-weight: 600 800; font-display: swap; src: url("/assets/Manrope-Bold-2740f0f8.ttf") format("truetype"); }
@font-face { font-family: "Manrope"; font-style: italic; font-weight: 200 500; font-display: swap; src: url("/assets/Manrope-Regular-9b466567.ttf") format("truetype"); }
@font-face { font-family: "Manrope"; font-style: italic; font-weight: 600 800; font-display: swap; src: url("/assets/Manrope-Bold-2740f0f8.ttf") format("truetype"); }
@font-face { font-family: "Fraunces"; font-style: normal; font-weight: 300 500; font-display: swap; src: url("/assets/Fraunces-Medium-77b3edbc.ttf") format("truetype"); }
@font-face { font-family: "Fraunces"; font-style: normal; font-weight: 600 700; font-display: swap; src: url("/assets/Fraunces-SemiBold-aba81d1d.ttf") format("truetype"); }
@font-face { font-family: "Fraunces"; font-style: italic; font-weight: 300 500; font-display: swap; src: url("/assets/Fraunces-Medium-77b3edbc.ttf") format("truetype"); }
@font-face { font-family: "Fraunces"; font-style: italic; font-weight: 600 700; font-display: swap; src: url("/assets/Fraunces-SemiBold-aba81d1d.ttf") format("truetype"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 300 500; font-display: swap; src: url("/assets/Inter-Regular-912de628.ttf") format("truetype"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 600; font-display: swap; src: url("/assets/Inter-SemiBold-e66a0128.ttf") format("truetype"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 700 800; font-display: swap; src: url("/assets/Inter-Bold-8ac93012.ttf") format("truetype"); }
@font-face { font-family: "Inter"; font-style: italic; font-weight: 300 500; font-display: swap; src: url("/assets/Inter-Regular-912de628.ttf") format("truetype"); }
@font-face { font-family: "Inter"; font-style: italic; font-weight: 600 800; font-display: swap; src: url("/assets/Inter-Bold-8ac93012.ttf") format("truetype"); }

.template-preview {
  --invoice-accent: var(--accent);
  --invoice-font: "IBM Plex Sans", "Aptos", "Segoe UI", sans-serif;
  position: sticky;
  top: 94px;
  padding: 28px;
  color: #24282d;
  background: #ffffff;
  font-family: var(--invoice-font);
  transition: background 180ms ease, color 180ms ease, border-color 180ms ease, box-shadow 180ms ease;
}

.preview-header {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding-bottom: 16px;
  border-bottom: 2px solid #24282d;
}

.preview-meta,
.totals-stack {
  display: grid;
  gap: 5px;
}

.preview-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
  margin: 22px 0;
}

.preview-line-items {
  width: 100%;
  border-collapse: collapse;
}

.preview-line-items th,
.preview-line-items td {
  padding: 9px 0;
  border-bottom: 1px solid var(--line-soft);
}

.preview-brand {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}

.preview-logo-mark {
  display: inline-grid;
  place-items: center;
  min-width: 42px;
  height: 42px;
  padding: 0 10px;
  border-radius: 12px;
  color: #fff;
  background: var(--invoice-accent);
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.04em;
}

/* The logo is either the image OR the monogram, never both. The display rules
   above would otherwise beat the [hidden] attribute's UA display:none, so the
   hidden one must be explicitly removed. */
.preview-logo-mark[hidden],
.preview-logo-img[hidden] {
  display: none;
}

/* Status banner: what this document is about, stated above the parties. Matches
   InvoicePdf#render_status_banner - a tinted full-width strip in the accent. Only
   payment reminders carry one today; credit notes will reuse it. */
.preview-status-banner {
  margin: 14px 0 0;
  padding: 6px 10px;
  border-radius: 4px;
  color: var(--invoice-accent);
  background: color-mix(in srgb, var(--invoice-accent) 9%, white);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
}

/* On a reminder the emphasized figure is the BALANCE, so the invoice total steps
   back rather than competing with it. */
.totals-stack.is-reminder span[data-section="totals"]:not(.totals-owing) {
  color: var(--muted);
  font-weight: 400;
}
.totals-stack .totals-owing {
  margin-top: 3px;
  padding-top: 6px;
  border-top: 1px solid var(--line-soft);
  color: var(--invoice-accent);
  font-weight: 700;
}

/* "This template is what your Invoice, Receipt and Credit Note go out as." */
.format-serves { margin: -4px 0 0; font-size: 12px; line-height: 1.55; }
.format-serves strong { color: var(--text); font-weight: 650; }
.format-serves a { color: var(--accent); margin-left: 4px; }

/* Payment-reminder cadence: three short numbers that read as one sentence. */
.reminder-cadence { margin-top: 10px; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel-subtle); }
.reminder-cadence[hidden] { display: none; }
.reminder-cadence-lead { margin: 0 0 10px; }
.reminder-cadence-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.reminder-cadence-grid .field { margin: 0; }
.suffix-input { display: flex; align-items: center; gap: 7px; }
.suffix-input input { width: 78px; }
.suffix-input span { font-size: 12px; color: var(--muted); white-space: nowrap; }

/* PAID marker on a receipt's meta column. */
.preview-paid-badge {
  display: inline-flex;
  align-items: center;
  align-self: flex-end;
  margin-top: 4px;
  padding: 1px 8px;
  border-radius: 999px;
  color: var(--invoice-accent);
  background: color-mix(in srgb, var(--invoice-accent) 12%, white);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.08em;
}

.template-preview [data-preview-key="address"],
.template-preview [data-preview-key="client_address"],
.template-preview [data-preview-key="notes"],
.template-preview [data-preview-key="bank_details"],
.template-preview [data-preview-key="terms"] {
  white-space: pre-line;
}

.template-preview[data-surface-tone="paper"] {
  background: #fffdf8;
}

.template-preview[data-surface-tone="mist"] {
  background: #f7fbff;
}

.template-preview[data-surface-tone="sand"] {
  background: #fff7eb;
}

.template-preview[data-surface-tone="mint"] {
  background: #f3fcf8;
}

.template-preview[data-surface-tone="night"] {
  color: #f8fafc;
  background: #0f1720;
  border-color: #0f1720;
}

.template-preview[data-surface-tone="night"] .muted,
.template-preview[data-surface-tone="night"] .eyebrow,
.template-preview[data-surface-tone="night"] .preview-line-items th,
.template-preview[data-surface-tone="night"] .preview-line-items td,
.template-preview[data-surface-tone="night"] .preview-meta,
.template-preview[data-surface-tone="night"] .totals-stack {
  color: #d7dee7;
}

.template-preview[data-surface-tone="night"] .preview-line-items th,
.template-preview[data-surface-tone="night"] .preview-line-items td {
  border-bottom-color: rgba(255, 255, 255, 0.14);
}

.template-preview[data-header-align="stacked"] .preview-header {
  align-items: flex-start;
  flex-direction: column;
}

.template-preview[data-header-align="left"] .preview-header {
  display: grid;
  justify-content: flex-start;
}

.template-preview[data-density="compact"] {
  padding: 20px;
}

.template-preview[data-density="compact"] .preview-grid {
  gap: 12px;
  margin: 14px 0;
}

.template-preview[data-density="compact"] .preview-line-items th,
.template-preview[data-density="compact"] .preview-line-items td {
  padding: 6px 0;
}

.template-preview[data-density="spacious"] {
  padding: 38px;
}

.template-preview[data-density="spacious"] .preview-grid {
  margin: 28px 0;
}

.template-preview[data-density="spacious"] .preview-line-items th,
.template-preview[data-density="spacious"] .preview-line-items td {
  padding: 12px 0;
}








































/* ==========================================================================
   Format library (see lib/send_invoice/invoice_formats.rb)

   Two groups of rules. The PARAMETRIC axes below (header arrangement, table
   treatment, palette) are each keyed off an attribute that is ABSENT on any
   template predating the library - so those templates keep the appearance
   defined above, and nothing here can regress them. The BESPOKE designs
   (data-format) come after, and are allowed to restyle the whole sheet.

   --invoice-tint / --invoice-ink are set inline from the chosen palette and
   fall back to the historical hairline and ink when no palette is set.
   ========================================================================== */

.template-preview {
  --invoice-tint: var(--line-soft);
  --invoice-ink: inherit;
}

/* The document wordmark. Hidden unless a format's header calls for it. */
.preview-doc-title {
  display: none;
  margin: 0 0 18px;
  color: var(--invoice-accent);
  font-size: 40px;
  font-weight: 800;
  letter-spacing: 0.01em;
  line-height: 1;
  text-transform: uppercase;
}

/* The accent glyph that precedes the company name (LOGO_MARKS). */
.preview-glyph {
  margin-right: 0.4em;
  color: var(--invoice-accent);
}

/* --- Axis 1: header arrangement ----------------------------------------
   split / stacked / left are the pre-existing values and are styled earlier in
   this file; right / center / band arrived with the reference set. */

.template-preview[data-header-align="right"] .preview-header {
  flex-direction: row-reverse;
}

.template-preview[data-header-align="right"] .preview-meta {
  text-align: left;
}

.template-preview[data-header-align="right"] .preview-doc-title,
.template-preview[data-header-align="center"] .preview-doc-title,
.template-preview[data-header-align="band"] .preview-doc-title {
  display: block;
}

.template-preview[data-header-align="right"] .preview-doc-title {
  text-align: right;
}

.template-preview[data-header-align="center"] .preview-header {
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.template-preview[data-header-align="center"] .preview-doc-title {
  text-align: center;
}

.template-preview[data-header-align="center"] .preview-brand {
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.template-preview[data-header-align="center"] .preview-meta {
  justify-items: center;
}

/* `band`: the wordmark sits reversed-out on a full-bleed accent bar. The
   negative margins bleed to the sheet edge, cancelling the sheet's padding -
   which is why the value tracks the three density paddings below. */
.template-preview[data-header-align="band"] .preview-doc-title {
  margin: -28px -28px 26px;
  padding: 22px 28px;
  color: #fff;
  background: var(--invoice-accent);
}

.template-preview[data-density="compact"][data-header-align="band"] .preview-doc-title {
  margin: -20px -20px 20px;
  padding: 16px 20px;
}

.template-preview[data-density="spacious"][data-header-align="band"] .preview-doc-title {
  margin: -38px -38px 30px;
  padding: 26px 38px;
}

.template-preview[data-header-align="band"] .preview-header {
  border-bottom-width: 1px;
}

/* --- Axis 2: table treatment ------------------------------------------- */

/* `filled`: reversed-out header row in the accent. */
.template-preview[data-table-style="filled"] .preview-line-items th {
  padding: 10px 8px;
  color: #fff;
  background: var(--invoice-accent);
  border-bottom: none;
}

.template-preview[data-table-style="filled"] .preview-line-items td {
  padding-left: 8px;
  padding-right: 8px;
}

/* `bordered`: every cell ruled in the palette tint. */
.template-preview[data-table-style="bordered"] .preview-line-items th,
.template-preview[data-table-style="bordered"] .preview-line-items td {
  padding: 9px 8px;
  border: 1px solid var(--invoice-tint);
}

.template-preview[data-table-style="bordered"] .preview-line-items th {
  color: var(--invoice-accent);
}

/* `striped`: alternating rows washed with the tint. */
.template-preview[data-table-style="striped"] .preview-line-items tbody tr:nth-child(even) {
  background: var(--invoice-tint);
}

.template-preview[data-table-style="striped"] .preview-line-items th {
  color: var(--invoice-accent);
  border-bottom: 1px solid var(--invoice-accent);
}

.template-preview[data-table-style="striped"] .preview-line-items td {
  padding-left: 8px;
  padding-right: 8px;
  border-bottom: none;
}

/* `minimal`: no rules at all bar a muted header underline. */
.template-preview[data-table-style="minimal"] .preview-line-items th {
  color: #888888;
  border-bottom: 2px solid #dddddd;
}

.template-preview[data-table-style="minimal"] .preview-line-items td {
  border-bottom: none;
}

/* `underline`: hairline under every row, in the palette tint. */
.template-preview[data-table-style="underline"] .preview-line-items th {
  color: var(--invoice-accent);
  border-bottom: 1px solid var(--invoice-accent);
}

.template-preview[data-table-style="underline"] .preview-line-items td {
  border-bottom: 1px solid var(--invoice-tint);
}

/* --- Axis 3: palette ---------------------------------------------------
   A palette supplies body ink and the tint. Accent stays independently
   editable (accent_color), so a merchant can keep a palette's structure and
   still brand it. Muted/label text is left alone - it reads as hierarchy, not
   palette. */

.template-preview[data-palette] {
  color: var(--invoice-ink);
}

.template-preview[data-palette] .totals-stack strong {
  color: var(--invoice-ink);
}

/* --- Bespoke designs ---------------------------------------------------
   Each is a named page from the reference set. These may restyle freely; they
   only ever apply when a merchant has explicitly picked that format. */

/* Freelancer Band: tinted logo panel, filled head, tinted grand-total row. */
.template-preview[data-format="freelancer_band"] {
  padding: 44px;
  background: #ffffff;
}

.template-preview[data-format="freelancer_band"] .preview-header {
  align-items: flex-start;
  border-bottom: none;
}

.template-preview[data-format="freelancer_band"] .preview-logo-mark {
  width: 96px;
  height: 96px;
  border-radius: 0;
  color: #3c3c3c;
  background: var(--invoice-tint, #ebe9e4);
  font-size: 24px;
}

.template-preview[data-format="freelancer_band"] .preview-line-items th {
  padding: 12px 10px;
  color: #3c3c3c;
  background: var(--invoice-tint, #ebe9e4);
  border-bottom: none;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.template-preview[data-format="freelancer_band"] .preview-line-items td {
  padding: 11px 10px;
  border-bottom: none;
}

.template-preview[data-format="freelancer_band"] .totals-stack span:last-child {
  padding: 8px 10px;
  background: var(--invoice-tint, #ebe9e4);
}

/* Wordmark Rule: letterspaced wordmark trailed by a long hairline. */
.template-preview[data-format="wordmark_rule"] {
  padding: 46px;
}

.template-preview[data-format="wordmark_rule"] .preview-doc-title {
  display: flex;
  align-items: center;
  gap: 22px;
  margin-bottom: 34px;
  color: #2b2b2b;
  font-size: 44px;
  font-weight: 300;
  letter-spacing: 0.22em;
}

.template-preview[data-format="wordmark_rule"] .preview-doc-title::after {
  flex: 1;
  height: 1px;
  background: #9a9a9a;
  content: "";
}

.template-preview[data-format="wordmark_rule"] .preview-header {
  border-bottom: none;
}

.template-preview[data-format="wordmark_rule"] .preview-line-items th {
  color: #6b6b6b;
  border-bottom: 1px solid #9a9a9a;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.template-preview[data-format="wordmark_rule"] .preview-line-items td {
  border-bottom: none;
}

/* Centered Monogram: brand centered on a tinted band across the head.
   The band bleeds to the sheet edge via negative margins that cancel the
   sheet's own padding. Do NOT invert this by zeroing the sheet padding and
   re-padding each child: a table with `border-collapse: collapse` ignores its
   own padding, so the line items would bleed off the page. */
.template-preview[data-format="centered_monogram"] {
  padding: 40px;
}

.template-preview[data-format="centered_monogram"] .preview-doc-title {
  display: none;
}

.template-preview[data-format="centered_monogram"] .preview-header {
  flex-direction: column;
  align-items: center;
  margin: -40px -40px 34px;
  padding: 46px 40px;
  background: var(--invoice-tint, #e8e6e1);
  border-bottom: none;
  text-align: center;
}

.template-preview[data-format="centered_monogram"] .preview-brand {
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.template-preview[data-format="centered_monogram"] .preview-logo-mark {
  width: 78px;
  height: 78px;
  border-radius: 0;
  color: #2f2f2f;
  background: transparent;
  font-size: 34px;
}

.template-preview[data-format="centered_monogram"] .preview-meta {
  justify-items: center;
}

.template-preview[data-format="centered_monogram"] .preview-line-items th {
  border-bottom: 1px solid #6f6f6f;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* Studio Neutral: quiet sheet, ruled table, nothing shouting. */
.template-preview[data-format="studio_neutral"] {
  padding: 44px;
  background: #fbfbf9;
}

.template-preview[data-format="studio_neutral"] .preview-header {
  padding-bottom: 20px;
  border-bottom: 1px solid #dcdcd6;
}

.template-preview[data-format="studio_neutral"] .preview-line-items th {
  color: #5f5f58;
  border-bottom: 1px solid #b9b9b1;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.template-preview[data-format="studio_neutral"] .preview-line-items td {
  border-bottom: 1px solid #ecece6;
}

/* Script Aesthetic: calligraphic wordmark over a warm sheet. */
.template-preview[data-format="script_aesthetic"] {
  padding: 48px;
  background: #f7f3ec;
}

.template-preview[data-format="script_aesthetic"] .preview-doc-title {
  display: block;
  margin-bottom: 30px;
  color: #2f2b26;
  font-family: "Invoice Script", "Great Vibes", cursive;
  font-size: 74px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
}

.template-preview[data-format="script_aesthetic"] .preview-header {
  border-bottom: none;
}

.template-preview[data-format="script_aesthetic"] .preview-logo-mark {
  color: #2f2b26;
  background: transparent;
  font-size: 20px;
}

.template-preview[data-format="script_aesthetic"] .preview-line-items th {
  color: #2f2b26;
  border-top: 1px solid #bdb5a6;
  border-bottom: 1px solid #bdb5a6;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.template-preview[data-format="script_aesthetic"] .preview-line-items td {
  border-bottom: none;
}

/* Wedding Ornament: tinted band, dotted row rules, payment panel. Column flex
   for the same reason as `gradient_header` - the band leads, the wordmark
   follows, and the DOM keeps the wordmark first. */
.template-preview[data-format="wedding_ornament"] {
  display: flex;
  flex-direction: column;
  padding: 40px;
  background: #ffffff;
}

.template-preview[data-format="wedding_ornament"] .preview-doc-title {
  display: block;
  margin: 0 0 16px;
  font-weight: 400;
  letter-spacing: 0.04em;
}

.template-preview[data-format="wedding_ornament"] .preview-header {
  flex-direction: column;
  align-items: center;
  order: -1;
  margin: -40px -40px 26px;
  padding: 30px 40px;
  background: var(--invoice-tint, #f3ece0);
  border-bottom: none;
  text-align: center;
}

.template-preview[data-format="wedding_ornament"] .preview-brand {
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

.template-preview[data-format="wedding_ornament"] .preview-logo-mark {
  color: var(--invoice-accent);
  background: transparent;
  font-size: 30px;
}

.template-preview[data-format="wedding_ornament"] .preview-line-items th {
  border-bottom: 2px solid #4a4a4a;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.template-preview[data-format="wedding_ornament"] .preview-line-items td {
  border-bottom: 1px dotted #b9a98c;
}

.template-preview[data-format="wedding_ornament"] .totals-block {
  padding-top: 18px;
  background: var(--invoice-tint, #f9f4ea);
}

/* Geometric Teal: corner geometry, tinted bill-to strip, numbered rows. */
.template-preview[data-format="geometric_teal"] {
  padding: 44px;
  background: #f6f6ef;
}

.template-preview[data-format="geometric_teal"] .preview-doc-title {
  display: block;
  margin-bottom: 22px;
  font-size: 30px;
  letter-spacing: 0.06em;
  text-align: center;
}

.template-preview[data-format="geometric_teal"] .preview-header {
  border-bottom: none;
}

.template-preview[data-format="geometric_teal"] .preview-grid {
  padding: 20px 24px;
  background: var(--invoice-tint, #c3e6df);
}

.template-preview[data-format="geometric_teal"] .preview-line-items th {
  padding: 13px 10px;
  color: #2f3436;
  background: var(--invoice-accent);
  border-bottom: none;
  font-weight: 800;
}

.template-preview[data-format="geometric_teal"] .preview-line-items td {
  padding: 14px 10px;
  border-bottom: none;
}

.template-preview[data-format="geometric_teal"] .totals-block {
  border-top: 2px solid #2f3436;
}

/* Gradient Header: full-width gradient head, circular mark, gradient totals.
   Column flex so the header can `order` itself above the wordmark: in this
   design the gradient band comes first, but the wordmark precedes it in the
   DOM (where it belongs for every other format, and for screen readers). */
.template-preview[data-format="gradient_header"] {
  display: flex;
  flex-direction: column;
  padding: 44px;
  background: #ffffff;
}

.template-preview[data-format="gradient_header"] .preview-doc-title {
  display: block;
  margin: 0 0 20px;
  color: #1c1c1c;
  font-size: 52px;
}

/* The header bleeds edge-to-edge and sits ABOVE the wordmark, so it also has to
   move out of the doc-title's way - hence `order`, which the flex parent honours
   for both. */
.template-preview[data-format="gradient_header"] .preview-header {
  align-items: center;
  order: -1;
  margin: -44px -44px 26px;
  padding: 30px 44px;
  background: linear-gradient(100deg, var(--invoice-accent), color-mix(in srgb, var(--invoice-accent) 55%, white));
  border-bottom: none;
  color: #ffffff;
}

.template-preview[data-format="gradient_header"] .preview-header h2,
.template-preview[data-format="gradient_header"] .preview-header .muted,
.template-preview[data-format="gradient_header"] .preview-meta {
  color: #ffffff;
}

.template-preview[data-format="gradient_header"] .preview-logo-mark {
  width: 68px;
  height: 68px;
  border-radius: 999px;
  color: #ffffff;
  background: #1c1c1c;
}

/* The gradient goes on the ROW, not each cell: per-cell gradients restart at
   every column and read as vertical banding. */
.template-preview[data-format="gradient_header"] .preview-line-items thead tr {
  background: linear-gradient(100deg, var(--invoice-accent), color-mix(in srgb, var(--invoice-accent) 55%, white));
}

.template-preview[data-format="gradient_header"] .preview-line-items th {
  padding: 14px 10px;
  color: #ffffff;
  background: transparent;
  border-bottom: none;
}

.template-preview[data-format="gradient_header"] .preview-line-items td {
  padding: 13px 10px;
}

.template-preview[data-format="gradient_header"] .totals-stack span:last-child {
  padding: 12px 16px;
  color: #ffffff;
  background: linear-gradient(100deg, var(--invoice-accent), color-mix(in srgb, var(--invoice-accent) 55%, white));
}

.stepper {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
}

.step {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 999px;
  color: var(--muted);
  background: #e9edf2;
  font-size: 12px;
  font-weight: 760;
}

.step.is-active {
  color: #ffffff;
  background: var(--accent);
}

.print-body {
  padding: 24px;
  background: #f3f4f6;
}

.print-toolbar {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-bottom: 12px;
}

.print-sheet {
  max-width: 920px;
  margin: 0 auto;
  padding: 40px;
}

/* --- Packing slip preview -------------------------------------------------
   The sheet renders at its real paper size (width/height set inline in inches
   by the view, from PageSize), so A4 vs 4x6 vs a custom size all preview true
   to scale. Layout (standard/compact) is chosen via [data-layout]. */
.pack-sheet {
  box-sizing: border-box;
  margin: 0 auto;
  padding: 0.5in;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 4px;
  color: #24282d;
  font-family: var(--pack-font, inherit);
  font-size: 12px;
  line-height: 1.4;
}
.pack-sheet[data-layout="compact"] { padding: 0.2in; font-size: 11px; }
.pack-sheet p { margin: 0; }
.pack-muted { color: #667085; }
.pack-accent { color: var(--pack-accent); }
.pack-eyebrow {
  font-size: 9px; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: #667085; margin-bottom: 4px;
}

.pack-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; }
.pack-brand { display: flex; gap: 12px; align-items: flex-start; }
.pack-brand h2 { margin: 2px 0 0; font-size: 18px; }
.pack-logo-mark {
  width: 42px; height: 42px; border-radius: 6px; flex: none;
  background: var(--pack-accent); color: #fff; font-weight: 700;
  display: flex; align-items: center; justify-content: center; font-size: 14px;
}
.pack-logo-img { width: 42px; height: 42px; object-fit: contain; }
.pack-meta { text-align: right; display: flex; flex-direction: column; gap: 1px; }
.pack-meta strong { font-size: 14px; }
.pack-meta span { color: #667085; font-size: 11px; }
.pack-accent-rule { height: 2px; background: var(--pack-accent); margin: 14px 0 16px; }
.pack-rule { height: 1px; background: var(--line); margin: 10px 0; }

.pack-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 18px; }
.pack-grid p { margin: 1px 0; }

.pack-items { width: 100%; border-collapse: collapse; }
.pack-items th {
  text-align: left; font-size: 10px; text-transform: uppercase; letter-spacing: 0.03em;
  background: var(--pack-accent); color: #fff; padding: 7px 8px;
}
.pack-items td { padding: 8px; border-bottom: 1px solid var(--line-soft); }
.pack-items .r { text-align: right; }

.pack-footer { margin-top: 16px; }
.pack-footer p { margin: 3px 0; }
.pack-sign { margin-top: 18px; }

/* Return-form checklists. The tick box is a drawn square in both surfaces - the PDF
   renderer uses Prawn's built-in fonts, which cannot print a "☐" at all, so a glyph
   here would have the preview and the PDF disagree. */
.pack-checklist { list-style: none; margin: 6px 0 12px; padding: 0; display: grid; gap: 7px; }
.pack-checklist.is-two-up { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 18px; }
.pack-checklist li { display: flex; align-items: flex-start; gap: 8px; font-size: 12px; }
.pack-tick {
  flex: 0 0 auto; width: 11px; height: 11px; margin-top: 2px;
  border: 1px solid var(--muted); border-radius: 2px;
}
.pack-sign-line { display: inline-block; width: 180px; border-bottom: 1px solid #667085; }

.pack-compact-head { display: flex; flex-direction: column; gap: 2px; }
.pack-number { font-size: 18px; font-weight: 700; }
.pack-compact-items { list-style: none; margin: 0; padding: 0; }
.pack-compact-items li { padding: 2px 0; }

/* Packing template library thumbnails (paper-shape preview at true ratio). */
.pack-thumb {
  display: flex; align-items: center; justify-content: center;
  background: #f6f7f5; border: 1px solid var(--line); border-radius: 10px;
  height: 150px; padding: 14px; text-decoration: none;
}
.pack-thumb-paper {
  background: #fff; border: 1px solid var(--line); border-radius: 3px;
  height: 100%; max-height: 122px; padding: 8px 7px;
  display: flex; flex-direction: column; gap: 5px; box-shadow: 0 1px 2px rgba(0,0,0,.05);
}
.pack-thumb-paper.is-wide { height: auto; width: 122px; }
.pack-thumb-bar { height: 4px; border-radius: 2px; background: #e2e5e1; }
.pack-thumb-bar.accent { background: var(--accent); width: 60%; }
.pack-thumb-bar.short { width: 40%; }

/* Packing template editor.
   Shares the invoice editor's shell (.editor-topbar / .split-layout /
   .invoice-editor-shell / .acc-item): the control rail scrolls inside its own
   card so the preview stays put. It used to be a plain two-column grid whose
   left column ran ~1600px past the preview, leaving half the page as dead grey
   space and the sheet far off screen by the time you reached the last field. */
/* [hidden] must beat the display rules on .field / preview blocks. */
[data-pack-editor] [hidden] { display: none !important; }
.pack-custom-row { display: flex; align-items: center; gap: 8px; }
.pack-custom-row input[type="number"] { width: 84px; }
.pack-custom-row select { width: 68px; }
.pack-toggle-list { display: flex; flex-direction: column; gap: 8px; }

/* Layout picker: two cards, not a dropdown. The choice swaps the whole document
   (compact drops the branding), which a <select> gave no hint of. */
.pack-layout-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.pack-layout-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel);
  cursor: pointer;
}
.pack-layout-card:hover { border-color: var(--line-strong); }
.pack-layout-card.is-selected { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
/* The radio drives selection; hidden so the whole card is the target, but it
   stays focusable for the keyboard. */
.pack-layout-card input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.pack-layout-card:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.pack-layout-name { font-size: 12.5px; font-weight: 700; line-height: 1.2; }
.pack-layout-hint { font-size: 10.5px; color: var(--faint); line-height: 1.3; }

/* Miniature of each layout, drawn in CSS - a rule, a couple of text lines and a
   table band for standard; a narrow receipt for compact. */
.pack-layout-thumb {
  height: 46px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: #fff;
  display: block;
  position: relative;
  overflow: hidden;
}
.pack-layout-thumb::before,
.pack-layout-thumb::after { content: ""; position: absolute; background: var(--line-strong); }
.pack-layout-thumb-standard::before {
  left: 7px; right: 7px; top: 9px; height: 2px; background: var(--accent);
}
.pack-layout-thumb-standard::after {
  left: 7px; right: 7px; top: 18px; height: 12px;
  background: repeating-linear-gradient(to bottom, var(--line) 0 2px, transparent 2px 5px);
}
.pack-layout-thumb-compact::before {
  left: 26px; right: 26px; top: 6px; bottom: 6px; background: #fff; border: 1px solid var(--line);
}
.pack-layout-thumb-compact::after {
  left: 30px; right: 30px; top: 12px; height: 20px;
  background: repeating-linear-gradient(to bottom, var(--line) 0 2px, transparent 2px 5px);
}

/* Return reasons: a picked list, not free text. Each row prints as its own
   tick-box on the return form, so they read as discrete items here too. */
.pack-reason-list { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 6px; }
.pack-reason-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 8px 6px 10px;
  font-size: 12.5px;
  background: var(--panel-subtle);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.pack-reason-remove {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--faint);
  font-size: 16px;
  line-height: 1;
  padding: 2px 4px;
  cursor: pointer;
  border-radius: 4px;
}
.pack-reason-remove:hover { color: var(--text); background: var(--line-soft); }
.pack-reason-remove:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.pack-reason-add { display: flex; gap: 6px; margin-top: 8px; }
.pack-reason-add input { flex: 1; min-width: 0; }
.pack-reason-add button { flex: none; }
.pack-reason-add input:disabled { background: var(--line-soft); cursor: not-allowed; }

.pack-editor-preview { position: sticky; top: 16px; display: grid; gap: 8px; align-content: start; }
.pack-preview-bar {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 0 2px;
}
.pack-preview-dims { font-size: 11.5px; font-weight: 650; color: var(--muted); font-variant-numeric: tabular-nums; }
.pack-preview-frame {
  background: #ececea; border: 1px solid var(--line); border-radius: 12px;
  padding: 16px; display: flex; justify-content: center; align-items: flex-start;
  /* auto, not hidden: a full A4 is taller than the viewport even scaled, and
     clipping it would hide the footer the merchant just switched on. */
  overflow: auto; max-height: calc(100vh - 160px);
}
/* The sheet renders at true pixel size and the scaler shrinks it to fit, so the
   proportions stay honest - a 4x6 label must read as a label beside an A4 page. */
.pack-sheet-scaler { transform-origin: top center; flex: none; }
.pack-editor-preview .pack-sheet { box-shadow: 0 2px 8px rgba(0, 0, 0, .08); }

@media (max-width: 1023px) {
  .pack-editor-preview { position: static; }
}

/* ---- lg breakpoint: below 1024px the fixed sidebar becomes a slide-in drawer
   and the mobile top bar takes over navigation. ---- */
@media (max-width: 1023px) {
  .app-frame {
    display: block;
  }

  .app-topbar {
    display: flex;
  }

  /* Sidebar -> off-canvas drawer. It keeps its brand-green styling verbatim. */
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 60;
    width: min(284px, 82vw);
    height: 100vh;
    transform: translateX(-100%);
    transition: transform 0.24s ease;
    overflow-y: auto;
  }

  html.nav-open .sidebar {
    transform: translateX(0);
    box-shadow: 0 24px 60px -20px rgba(0, 0, 0, 0.5);
  }

  .nav-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 55;
    background: rgba(8, 20, 16, 0.44);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.24s ease, visibility 0.24s ease;
  }

  html.nav-open .nav-scrim {
    opacity: 1;
    visibility: visible;
  }

  html.nav-open,
  html.nav-open body {
    overflow: hidden;
  }

  /* The desktop rail-collapse toggle has no meaning inside the drawer. */
  .sidebar-toggle {
    display: none;
  }

  /* A persisted nav-rail collapse must not hide labels in the drawer: restore
     the full-width nav regardless of the stored rail state. */
  html.nav-rail {
    --sidebar-width: 224px;
  }

  html.nav-rail .brand-text {
    display: block;
  }

  html.nav-rail .nav-label {
    display: inline;
  }

  html.nav-rail .sidebar-profile-text {
    display: flex;
  }

  html.nav-rail .brand {
    justify-content: flex-start;
    margin-top: 0;
    padding: 2px 6px 16px;
    border-bottom-color: transparent;
  }

  html.nav-rail .nav-link {
    justify-content: flex-start;
    padding: 0 11px;
    gap: 11px;
  }

  html.nav-rail .nav-link.is-active::before {
    display: block;
  }

  html.nav-rail .sidebar-profile {
    justify-content: flex-start;
    gap: 10px;
    padding: 9px;
  }

  html.nav-rail .sidebar-footnote {
    padding: 12px 4px 2px;
  }

  /* Content-level collapses retained from the previous shell breakpoint. */
  .split-layout {
    grid-template-columns: 1fr;
  }

  .split-layout > .section-card,
  .template-preview {
    position: static;
    max-height: none;
  }
}

@media (max-width: 720px) {
  .toolbar,
  .trial-banner,
  .sync-banner,
  .pagination {
    align-items: stretch;
    flex-direction: column;
  }

  /* Phones and tablets follow the same gutter as the desktop rather than a
     hardcoded 16px, so the app bleeds consistently at every size. Cards carry
     their own padding, so their text still clears the screen edge. */
  .page-content {
    padding-left: var(--page-gutter);
    padding-right: var(--page-gutter);
  }

  .trial-banner,
  .sync-banner {
    padding-left: max(var(--page-gutter), 16px);
    padding-right: max(var(--page-gutter), 16px);
  }

  .grid-two,
  .preview-grid {
    grid-template-columns: 1fr;
  }

  .sync-request-row {
    align-items: flex-start;
    flex-direction: column;
  }

  .sync-request-actions {
    width: 100%;
    flex-wrap: wrap;
  }

  .value {
    font-size: 24px;
  }

  .public-shell {
    padding: 16px;
  }
}

@media print {
  .sidebar,
  .trial-banner,
  .sync-banner,
  .toast-region,
  .print-toolbar,
  .button,
  .button-secondary,
  .button-ghost {
    display: none !important;
  }

  .app-frame,
  .workspace,
  .page-content {
    display: block;
    padding: 0;
  }

  .template-preview,
  .print-sheet {
    position: static;
    box-shadow: none;
    border: 0;
  }
}

/* A from/to pair of native date inputs. */
.date-range-native {
  display: flex;
  align-items: center;
  gap: 8px;
}

.date-range-native input {
  flex: 1;
}

.date-range-native-sep {
  color: var(--muted);
  font-size: 12px;
}

/* Status pill */
.dashboard .pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 11px;
  font-weight: 660;
  color: var(--muted);
  white-space: nowrap;
}

.dashboard .pill.is-ok {
  color: var(--accent);
  border-color: var(--accent-weak);
  background: var(--accent-weak);
}

/* Section card header row with trend chip */

.dashboard .delta {
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.dashboard .delta.up {
  color: var(--accent);
}

.dashboard .delta.down {
  color: var(--red);
}

/* Dashboard - Total sales breakdown (Shopify-style gross -> total + KPIs) */
.dashboard .sb-card { padding: 0; overflow: hidden; }
.dashboard .sb-head { padding: 18px 24px 16px; border-bottom: 1px solid var(--line-soft); }
.dashboard .sb-head h3 { font-size: 15px; font-weight: 700; }
.dashboard .sb-head .sub { color: var(--muted); font-size: 13px; margin-top: 3px; }

.dashboard .sb-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 232px;
  gap: 0 24px;
  padding: 12px 24px 18px;
  align-items: stretch;
}
.dashboard .sb-rows { min-width: 0; }
.dashboard .sb-row {
  display: grid;
  grid-template-columns: 1fr auto 62px;
  align-items: baseline;
  gap: 16px;
  padding: 10px 12px;
  border-radius: 8px;
  font-size: 14px;
}
/* Zebra shading matches the reference (Discounts / Net / Return fees / Total). */
.dashboard .sb-row:nth-child(even) { background: var(--hover); }
.dashboard .sb-row .k { color: var(--text); }
.dashboard .sb-row .amt { text-align: right; font-weight: 640; }
.dashboard .sb-row .amt.neg { color: var(--red); }
.dashboard .sb-row .sb-dc { justify-self: end; text-align: right; white-space: nowrap; }
.dashboard .sb-none { color: var(--faint); font-weight: 600; }
.dashboard .sb-row-net .k,
.dashboard .sb-row-net .amt { font-weight: 720; }
.dashboard .sb-row-total { margin-top: 4px; border-top: 1px solid var(--line); border-radius: 0 0 8px 8px; }
.dashboard .sb-row-total .k { font-weight: 760; }
.dashboard .sb-row-total .amt { font-weight: 780; color: var(--accent); }

/* Supporting KPIs: a vertical stack that fills the rows' height so both
   columns line up top and bottom. */
.dashboard .sb-kpis { display: flex; flex-direction: column; gap: 10px; }
.dashboard .sb-kpi {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  border: 1px solid var(--line-soft);
  border-radius: 10px;
  padding: 12px 15px;
  background: var(--hover);
}
.dashboard .sb-kpi-k { font-size: 12px; color: var(--muted); }
.dashboard .sb-kpi-v { font-size: 19px; font-weight: 740; margin-top: 4px; letter-spacing: -0.01em; }

@media (max-width: 768px) {
  .dashboard .sb-body { grid-template-columns: 1fr; gap: 16px; }
  .dashboard .sb-kpis { flex-direction: row; flex-wrap: wrap; }
  .dashboard .sb-kpi { flex: 1 1 45%; }
}

/* Right rail cards */

.dashboard .section-head {
  margin: 0 0 12px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text);
}

.dashboard .usage {
  margin-top: 14px;
}

/* ---- Invoice template library ---- */
.button-sm {
  min-height: 30px;
  padding: 0 11px;
  font-size: 12px;
}

.pill-default {
  display: inline-flex;
  align-items: center;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--accent-weak);
  border-radius: 20px;
  padding: 2px 8px;
}

.library-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
}

.library-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

.library-locked {
  display: flex;
  align-items: center;
  gap: 10px;
}

.tpl-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 16px;
  margin-top: 8px;
}

.tpl-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.tpl-meta {
  padding: 12px 14px 4px;
}

.tpl-name {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
}

.tpl-sub {
  margin-top: 3px;
  font-size: 12px;
  color: var(--faint);
}

.tpl-actions {
  padding: 10px 14px 14px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

.inline-form {
  display: inline-flex;
}

.rename-pop summary {
  list-style: none;
}

.rename-pop summary::-webkit-details-marker {
  display: none;
}

.rename-form {
  margin-top: 8px;
  display: flex;
  gap: 6px;
  align-items: center;
}

.rename-form input[type="text"] {
  width: 150px;
}

/* ---- Section formatting (Phase 2) ---- */
.section-format-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.section-format-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.sf-label {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text);
}

.fmt-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 8px;
  background: var(--panel-subtle);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}

.fmt-select {
  height: 30px;
  border: 1px solid var(--line);
  background: var(--bg);
  border-radius: 5px;
  font-size: 12px;
  color: var(--text);
  font-family: inherit;
  padding: 0 6px;
}

.fmt-check {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 30px;
  padding: 0 8px;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: var(--bg);
  font-size: 12px;
  color: var(--muted);
  cursor: pointer;
  user-select: none;
}

.fmt-check .fmt-i {
  font-style: italic;
  font-weight: 700;
}

.fmt-check:has(input:checked) {
  color: var(--accent);
  background: var(--accent-weak);
  border-color: transparent;
}

.fmt-color {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 8px 0 8px;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: var(--bg);
  cursor: pointer;
}

.fmt-color input[type="color"] {
  width: 26px;
  height: 20px;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
}

.fmt-color input[type="color"]:disabled {
  opacity: 0.4;
  cursor: default;
}

/* Editor top bar (above the split layout). */
.editor-topbar {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

.editor-title {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.editor-title-ico {
  flex: none;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border-radius: 11px;
  background: var(--accent-weak);
  color: var(--accent);
}

.editor-title-ico svg {
  width: 19px;
  height: 19px;
}

.editor-title-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.editor-title-name {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.editor-title strong {
  font-size: 20px;
  font-weight: 760;
  letter-spacing: -0.02em;
  line-height: 1.05;
}

/* ---- Line-item columns + sample rows (Phase 3) ---- */
.chip-btn {
  border: 1px dashed var(--line);
  background: transparent;
  color: var(--accent);
  border-radius: 6px;
  font-size: 11.5px;
  font-weight: 600;
  padding: 6px 11px;
  cursor: pointer;
}

.chip-btn:disabled {
  color: var(--faint);
  border-color: var(--line-soft);
  cursor: default;
}

.column-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.column-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 9px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel-subtle);
}

.column-item.dragging {
  opacity: 0.6;
  border-color: var(--accent);
}

.column-item .grip {
  color: var(--faint);
  cursor: grab;
  font-size: 12px;
  line-height: 1;
}

.column-item .col-vis {
  flex: none;
  cursor: pointer;
}

.column-item .col-label {
  flex: 1;
  min-width: 60px;
  height: 30px;
  border: 1px solid var(--line);
  background: var(--bg);
  border-radius: 5px;
  font-size: 12px;
  color: var(--text);
  font-family: inherit;
  padding: 0 7px;
}

.col-remove {
  border: 0;
  background: transparent;
  color: var(--faint);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
}

.col-remove:hover {
  color: var(--red);
}

.col-remove:disabled {
  color: var(--line);
  cursor: default;
}

/* Merged Logo control: a Monogram | Image toggle inside Visual controls. */
.logo-field { grid-column: 1 / -1; }
.logo-seg {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  margin: 2px 0 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel-subtle);
}
.logo-seg button {
  font: inherit;
  font-size: 12px;
  font-weight: 640;
  color: var(--muted);
  background: transparent;
  border: 0;
  border-radius: 6px;
  padding: 5px 14px;
  cursor: pointer;
}
.logo-seg button.is-on {
  color: var(--text);
  background: var(--panel);
  box-shadow: 0 1px 2px rgba(16, 35, 28, 0.08);
}
.logo-pane[hidden] { display: none; }
.logo-field .hint { margin-top: 4px; font-size: 11.5px; color: var(--faint); }

.logo-drop {
  display: flex;
  align-items: center;
  gap: 14px;
  border: 1.5px dashed var(--line);
  border-radius: 10px;
  padding: 14px;
  background: var(--panel-subtle);
}

.logo-cur {
  width: 56px;
  height: 56px;
  border-radius: 10px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: #fff;
  border: 1px solid var(--line);
}

.logo-cur img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.logo-cur.mono {
  background: var(--accent);
  color: #fff;
  font-weight: 700;
  font-size: 18px;
  border: 0;
}

.logo-meta {
  flex: 1;
  min-width: 0;
}

.logo-meta .fname {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.logo-meta .info {
  font-size: 11px;
  color: var(--faint);
  margin-top: 2px;
}

.logo-actions {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}

.file-btn {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line-strong);
  background: var(--text);
  color: var(--bg);
  padding: 6px 12px;
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: 12px;
  cursor: pointer;
}

.ghost-btn {
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--muted);
  padding: 6px 12px;
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: 12px;
  cursor: pointer;
}

.ghost-btn:hover {
  color: var(--text);
  border-color: #cfcfcf;
}

.logo-err {
  margin: 8px 0 0;
  font-size: 11.5px;
  color: var(--red);
}

.preview-logo-img {
  width: 48px;
  height: 48px;
  object-fit: contain;
  border-radius: 8px;
}

/* ---- Layout: ordered blocks + custom fields/sections (Phase 5) ---- */
.preview-body {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.totals-block {
  display: flex;
  justify-content: flex-end;
}

.preview-footer-stack {
  border-top: 1px solid var(--line-soft);
  padding-top: 14px;
}

.preview-custom-fields {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px dashed var(--line);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.preview-custom-fields .cf-line {
  display: flex;
  justify-content: space-between;
  gap: 14px;
}

.preview-custom-fields .cf-k { color: var(--muted); }
.preview-custom-fields .cf-v { color: var(--text); font-weight: 600; }

.footer-custom .eyebrow {
  margin-bottom: 2px;
}

/* editor: reorder + repeatable lists */
.order-list,
.cf-list,
.cs-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.order-item,
.cf-item,
.cs-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 9px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel-subtle);
}

.cs-item {
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
}

.cs-item .cs-top {
  display: flex;
  align-items: center;
  gap: 8px;
}

.order-item.dragging,
.cf-item.dragging,
.cs-item.dragging {
  opacity: 0.6;
  border-color: var(--accent);
}

.order-item .grip,
.cf-item .grip,
.cs-item .grip {
  color: var(--faint);
  cursor: grab;
  font-size: 12px;
  line-height: 1;
}

.order-item .order-label {
  font-size: 12.5px;
  font-weight: 600;
}

/* Pro-gated layout sections: inline "Pro" tag on the header + a read-only,
   dimmed preview of the current config with an upgrade prompt. */

.acc-upsell {
  margin: 0 0 10px;
  padding: 9px 11px;
  border: 1px dashed var(--line);
  border-radius: 8px;
  background: var(--accent-weak);
  color: var(--muted);
  font-size: 12.5px;
}

.acc-upsell a {
  color: var(--accent);
  font-weight: 700;
}

.acc-locked .is-readonly {
  opacity: 0.55;
  pointer-events: none;
  user-select: none;
}

.acc-locked .is-readonly .grip {
  cursor: default;
}

.cf-item.is-static .cf-ro-label {
  font-weight: 600;
  font-size: 12.5px;
}

.cf-item.is-static .cf-ro-value {
  flex: 1;
  min-width: 0;
  color: var(--muted);
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cs-item.is-static .cs-ro-title {
  font-weight: 600;
  font-size: 12.5px;
}

.cs-item.is-static .cs-ro-body {
  margin: 0;
  color: var(--muted);
  font-size: 12px;
  white-space: pre-line;
}

.cf-input,
.cs-title {
  flex: 1;
  min-width: 50px;
  height: 30px;
  border: 1px solid var(--line);
  background: var(--bg);
  border-radius: 5px;
  font-size: 12px;
  color: var(--text);
  font-family: inherit;
  padding: 0 7px;
}

.cs-body {
  width: 100%;
  min-height: 46px;
  resize: vertical;
  border: 1px solid var(--line);
  background: var(--bg);
  border-radius: 5px;
  font-size: 12px;
  color: var(--text);
  font-family: inherit;
  padding: 6px 8px;
}

/* ---- Library "Start from a preset" gallery (Phase 6) ---- */
.preset-start {
  margin-top: 22px;
}

.preset-start > summary {
  list-style: none;
  display: inline-flex;
  cursor: pointer;
}

.preset-start > summary::-webkit-details-marker {
  display: none;
}

.preset-start-panel {
  margin-top: 20px;
  display: grid;
  gap: 18px;
}

/* Let the card button sit directly in the grid; the wrapping form is invisible. */
.preset-card-form {
  display: contents;
}

[data-lib-preset].is-hidden,
[data-pack-preset].is-hidden {
  display: none;
}

/* ---- Automatic invoices (scheduling) ---- */
.upgrade-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel-subtle);
  padding: 16px 18px;
}

.upgrade-title {
  font-size: 14px;
  font-weight: 600;
}

/* Standard locked-feature state (partials/plan_lock.erb): illustration + copy +
   "Upgrade to <tier>" CTA. Reused across gated pages so locks look consistent. */
.plan-lock {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: 15px;
  padding: 48px 32px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: linear-gradient(180deg, var(--accent-weak), transparent 70%);
}

.plan-lock-art {
  color: var(--accent);
  line-height: 0;
}

.plan-lock-art svg {
  width: 84px;
  height: 84px;
}

.plan-lock-copy {
  display: grid;
  gap: 6px;
  justify-items: center;
}

.plan-lock-title {
  margin: 0;
  font-size: 17px;
  font-weight: 750;
  letter-spacing: -0.01em;
}

.plan-lock-body {
  margin: 0;
  color: var(--muted);
  max-width: 44ch;
}

.plan-lock .button {
  margin-top: 4px;
}

.schedule-list {
  display: flex;
  flex-direction: column;
}

.schedule-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 14px 0;
  border-top: 1px solid var(--line-soft);
}

/* The rail is the row's first item; .schedule-main takes the slack so the
   actions stay hard right. The edit form still wraps to its own line. */
.schedule-main { flex: 1; min-width: 0; }

/* No top rule above the first row. :first-child covers the no-JS case;
   .is-first is set by script to the first row of the current page. */
.schedule-list > .schedule-row:first-child,
.schedule-row.is-first {
  border-top: 0;
  padding-top: 2px;
}

.schedule-row[hidden] {
  display: none;
}

.schedule-row.is-paused {
  opacity: 0.62;
}

.schedule-title {
  font-size: 13.5px;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 8px;
}

.schedule-sub {
  font-size: 12px;
  color: var(--faint);
  margin-top: 2px;
}

.schedule-actions {
  display: flex;
  gap: 6px;
}

/* Nickname chip beside the schedule title. */
.schedule-nick {
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  padding: 1px 7px;
  border: 1px solid var(--line);
  border-radius: 20px;
}

/* Accent pill flagging a receipt / gift-receipt schedule. */
.schedule-doc-pill {
  font-size: 11px;
  font-weight: 700;
  color: var(--accent);
  background: var(--accent-weak);
  padding: 1px 8px;
  border-radius: 20px;
}

/* Advanced-rule chips under a schedule's summary. */
.schedule-rules {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 5px;
}

.schedule-rule-chip {
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  padding: 1px 8px;
  border: 1px solid var(--line);
  border-radius: 20px;
  font-variant-numeric: tabular-nums;
}

/* Advanced-rules disclosure: a distinct pill toggle with a sliders glyph and a
   chevron that flips open. */
.schedule-advanced {
  margin-top: 18px;
}

.schedule-advanced > summary.adv-toggle {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  width: fit-content;
  padding: 9px 15px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--panel);
  font-size: 12.5px;
  font-weight: 650;
  color: var(--muted);
  transition: border-color .14s ease, color .14s ease, background .14s ease, box-shadow .14s ease;
}

.schedule-advanced > summary.adv-toggle::-webkit-details-marker {
  display: none;
}

.schedule-advanced > summary.adv-toggle:hover {
  border-color: var(--accent);
  color: var(--accent-ink);
  background: var(--accent-weak);
}

.schedule-advanced > summary.adv-toggle:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-weak);
}

.schedule-advanced[open] > summary.adv-toggle {
  border-color: var(--accent);
  color: var(--accent-ink);
  background: var(--accent-weak);
}

.adv-ico {
  flex: none;
  color: var(--accent);
}

.adv-chev {
  flex: none;
  color: var(--faint);
  transition: transform .18s ease, color .14s ease;
}

.schedule-advanced[open] .adv-chev {
  transform: rotate(180deg);
  color: var(--accent);
}

.schedule-advanced > summary.adv-toggle:hover .adv-chev {
  color: var(--accent);
}

/* Advanced rules: a distinct inset panel with a tidy two-column field grid
   (full-width rows for the paired date/time controls and their notes). */
.schedule-advanced-body {
  margin-top: 14px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 15px 18px;
  align-items: start;
  padding: 18px 20px;
  background: var(--panel-subtle);
  border: 1px solid var(--line);
  border-radius: 12px;
}

.schedule-advanced-body > .schedule-timing,
.schedule-advanced-body > .section-note {
  grid-column: 1 / -1;
}

.schedule-advanced-body > .section-note {
  margin-top: -6px;
}

@media (max-width: 640px) {
  .schedule-advanced-body { grid-template-columns: 1fr; }
}

/* Align an inline checkbox with sibling inputs in a timing grid cell. */
.schedule-timing .checkbox-field .checkbox-row {
  min-height: 36px;
}

/* Inline edit form wraps to a full-width row below the schedule summary. */
.schedule-edit {
  flex-basis: 100%;
  width: 100%;
  margin-top: 11px;
  padding-top: 13px;
  border-top: 1px solid var(--line-soft);
}

.schedule-edit[hidden] {
  display: none;
}

.schedule-add-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px 18px;
  align-items: start;
}

/* Nickname spans the full row so it doesn't sit orphaned under one column. */
.schedule-add-grid .field-wide {
  grid-column: 1 / -1;
}

/* Even, gap-free row: the visible timing controls share the width instead of
   leaving empty cells from a fixed 3-column track. */
.schedule-timing {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 14px 18px;
  align-items: end;
}

@media (max-width: 640px) {
  .schedule-add-grid,
  .schedule-timing { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  .schedule-add-grid { grid-template-columns: 1fr; }
}

.schedule-next {
  font-size: 11px;
  color: var(--accent);
  margin-top: 3px;
  font-variant-numeric: tabular-nums;
}

.schedule-last {
  font-size: 11px;
  color: var(--faint);
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}

/* Sent / Runs switcher above the activity column. Two views of the same
   automation: per invoice, and per window it covered. */
.sent-tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--line-soft);
}

.sent-tab {
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
  border: 0;
  background: none;
  padding: 6px 10px 8px;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.sent-tab.is-current { color: var(--accent); border-bottom-color: var(--accent); }
.sent-tab:hover { color: var(--text); }
.sent-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* A failed-run count, so the tab itself reports there is something to look at. */
.sent-tab-badge {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0;
  color: #fff;
  background: var(--red);
  border-radius: 999px;
  padding: 1px 6px;
}

/* As with .cn-docpanel, a class with a display would beat the UA [hidden] rule. */
.act-pane { display: block; min-height: 0; }
.act-pane[hidden] { display: none; }

/* The activity column is narrow, so a run reads as two lines - status + name,
   then the counts - rather than one row that clips. */
.run-list { display: grid; gap: 2px; margin: 10px 0 0; padding: 0; list-style: none; }

.run-item { border-bottom: 1px solid var(--line-soft); min-width: 0; }
.run-item:last-child { border-bottom: 0; }

.run-summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
  padding: 9px 2px;
  min-width: 0;
}

.run-summary::-webkit-details-marker { display: none; }
.run-summary:hover { background: var(--panel-subtle); }
.run-summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 4px; }

.run-name { font-size: 12.5px; font-weight: 600; min-width: 0; overflow-wrap: anywhere; }
/* Counts and time share the second line, whatever the first line's width. */
.run-count,
.run-at {
  font-size: 11px;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}
.run-count { flex: 1 0 100%; order: 3; }
.run-at { order: 4; margin-left: auto; }

.run-body { padding: 2px 2px 12px; display: grid; gap: 8px; min-width: 0; }

/* .activity-error carries grid-area: error for the sent-item grid. That area
   doesn't exist here, so it conjures an implicit second column and everything
   after it flows sideways - same reuse trap as .status-pill above. */
.run-body .activity-error { grid-area: auto; }
.run-window { font-size: 11.5px; color: var(--muted); }
.run-label { font-weight: 650; color: var(--text); }

.run-log { display: grid; gap: 7px; margin: 0; padding: 0; list-style: none; }

/* Stacked, not a three-column grid: at this width a fixed status column left
   the message wrapping after two or three words. */
.run-log li {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px 7px;
  align-items: baseline;
  font-size: 11.5px;
  min-width: 0;
}

.run-log-status {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--muted);
}

.run-log-msg { grid-column: 2; overflow-wrap: anywhere; }
.run-log-at { grid-column: 2; }

.run-log-error { color: var(--red); }
.run-log-queued, .run-log-success { color: var(--green); }
.run-log-msg { color: var(--text); overflow-wrap: anywhere; }
.run-log-at { color: var(--faint); font-variant-numeric: tabular-nums; }

.run-retry { display: grid; gap: 6px; justify-items: start; margin-top: 4px; }

/* .status-pill is placed for the sent-item grid (grid-area: pill, right
   aligned); in a run row it's the leading element of a flex line. Its
   text-transform: capitalize also has to go - it renders the run labels as
   "Nothing To Send". */
.run-summary .status-pill {
  grid-area: auto;
  justify-self: start;
  flex: none;
  text-transform: none;
}

/* The pill defaults to green, which would read as success. Only "Sent" earns
   that: "Nothing to send" and "Skipped" are neutral outcomes, and a run still
   in flight hasn't earned anything yet. */
.status-pill.status-blank,
.status-pill.status-suppressed,
.status-pill.status-claimed {
  color: var(--muted);
  background: #f1f3f2;
}

/* The "Sent" column: a flex stack whose middle list scrolls while a search +
   filter header pins to the top and a pager pins to the bottom. Borderless by
   design - only hairlines separate the regions, so it stays in step with the
   page's "no box chrome" rule. The header and pager reveal via .js-sent; with
   no script the list still scrolls on its own. */
.sent-panel {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.sent-head {
  display: none;
  flex-direction: column;
  gap: 10px;
  padding-bottom: 12px;
  margin-bottom: 2px;
  border-bottom: 1px solid var(--line-soft);
}

.js-sent .sent-head {
  display: flex;
}

/* Search field - matches the app's form inputs, with a leading glass icon and
   a clear affordance that appears once there's a query. */
.sent-search {
  position: relative;
}

.sent-search > svg {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--faint);
  pointer-events: none;
}

.sent-search input {
  width: 100%;
  min-height: 34px;
  padding: 0 30px 0 32px;
  color: var(--text);
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 13px;
}

.sent-search input::placeholder {
  color: var(--faint);
}

.sent-search input:focus {
  outline: 2px solid rgba(20, 124, 100, 0.16);
  border-color: var(--accent);
}

/* Hide the native search "×" so only our themed clear button shows. */
.sent-search input::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
}

.sent-search-clear {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  display: none;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--faint);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}

.sent-search-clear:hover {
  background: var(--hover);
  color: var(--muted);
}

.sent-search.has-value .sent-search-clear {
  display: inline-flex;
}

/* Quick filter chips (All / Auto / Test / Failed) with live counts. */
.sent-filters {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: 26px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: 20px;
  background: #ffffff;
  color: var(--muted);
  font-size: 11.5px;
  font-weight: 600;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

.chip:hover {
  color: var(--text);
  border-color: #cfcfcf;
}

.chip.is-active {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-weak);
}

.chip .chip-n {
  font-size: 10.5px;
  font-weight: 700;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}

.chip.is-active .chip-n {
  color: var(--accent);
}

/* Scrolling body - grows to fill the panel, scrolls when the page overflows. */
.sent-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  margin: 0 -4px;
  padding: 0 4px;
}

.sent-list {
  display: flex;
  flex-direction: column;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* One delivery per row: order + status pill on the right, recipient and the
   kind · time caption stacked on the left. */
.sent-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "order     pill"
    "recipient pill"
    "meta      pill"
    "error     error";
  align-items: center;
  column-gap: 10px;
  padding: 11px 0;
  border-top: 1px solid var(--line-soft);
}

.sent-item[hidden] {
  display: none;
}

/* No top rule above the first visible row. :first-child covers the no-JS case;
   .is-first is set by script to the first row of the current page. */
.sent-list > .sent-item:first-child,
.sent-item.is-first {
  border-top: 0;
  padding-top: 2px;
}

.sent-order {
  grid-area: order;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}

.sent-recipient {
  grid-area: recipient;
  font-size: 12px;
  color: var(--muted);
  margin-top: 1px;
  overflow-wrap: anywhere;
}

.sent-meta {
  grid-area: meta;
  font-size: 11px;
  color: var(--faint);
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}

.sent-kind {
  text-transform: capitalize;
}

/* Names the delivery channel on rows that aren't an email to the recipient shown
   (a Google Drive archive). Sits in the meta line rather than
   the pill column so it can't collide with the status pill's grid area. */
.sent-channel {
  font-weight: 700;
  color: var(--accent);
}

/* Shown when SMTP is unconfigured: every send is being written to disk instead
   of delivered, which the neutral "Queued" pill alone doesn't convey. */
.sent-alert {
  margin: 0 0 12px;
  padding: 9px 12px;
  border-radius: 8px;
  font-size: 12px;
  line-height: 1.5;
  color: #553300;
  background: var(--amber-weak);
}

.sent-alert strong {
  font-weight: 700;
}

.status-pill {
  grid-area: pill;
  display: inline-flex;
  align-items: center;
  align-self: center;
  justify-self: end;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: capitalize;
  border-radius: 20px;
  padding: 2px 9px;
  color: var(--green);
  background: var(--green-weak);
}

.status-pill.status-failed {
  color: var(--red);
  background: var(--red-weak);
}

/* Queued ("outbox") is a normal resting state, not a warning - keep it neutral
   so only genuine failures read as red. */
.status-pill.status-outbox {
  color: var(--muted);
  background: #f1f3f2;
}

.activity-error {
  grid-area: error;
  display: block;
  font-size: 11px;
  color: var(--red);
  margin-top: 4px;
}

.sent-empty {
  padding: 28px 8px;
  text-align: center;
  color: var(--muted);
  font-size: 12px;
}

/* Pager pinned below the list. */
.sent-foot {
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding-top: 10px;
  margin-top: 2px;
  border-top: 1px solid var(--line-soft);
}

.js-sent .sent-foot {
  display: flex;
}

.js-sent .sent-foot[hidden] {
  display: none;
}

.sent-foot-status {
  font-size: 11.5px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.sent-foot-status b {
  color: var(--text);
  font-weight: 700;
}

.sent-foot-nav {
  display: flex;
  align-items: center;
  gap: 6px;
}

.pager-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 30px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: #ffffff;
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.pager-btn:hover:not(:disabled) {
  color: var(--text);
  border-color: #cfcfcf;
}

.pager-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

.pager-btn svg {
  display: block;
}

/* Your-schedules panel: search header + arrows-only pager wrap the schedule
   list, mirroring the Sent panel. Both tools reveal via .js-sched. */
.sched-panel {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.sched-head {
  display: none;
  padding-bottom: 12px;
  margin-bottom: 2px;
  border-bottom: 1px solid var(--line-soft);
}

.js-sched .sched-head {
  display: block;
}

.sched-foot {
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding-top: 10px;
  /* auto top margin pins the pager to the bottom of the full-height panel so it
     lines up straight with the Sent pager across the gutter (the Sent pager is
     pinned by its scroll area growing). */
  margin-top: auto;
  border-top: 1px solid var(--line-soft);
}

.js-sched .sched-foot {
  display: flex;
}

.js-sched .sched-foot[hidden] {
  display: none;
}

.sched-foot-nav {
  display: flex;
  align-items: center;
  gap: 6px;
}

/* Two-step schedule wizard (Schedule rule -> Delivery). Progressive
   enhancement: the .js-wizard class is added by script; without it every
   step renders stacked so the form still works. */
.schedule-wizard {
  display: grid;
  gap: 16px;
}

.wizard-tabs {
  display: none;
}

.js-wizard .wizard-tabs {
  display: flex;
  gap: 18px;
  list-style: none;
  padding: 0 0 4px;
  margin: 0;
  border-bottom: 1px solid var(--line-soft);
}

.wizard-tabs li {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--faint);
  cursor: pointer;
}

.wizard-tabs li.is-current {
  color: var(--text);
}

.wizard-tab-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  background: var(--line-soft);
  color: var(--muted);
  font-size: 11px;
}

.wizard-tabs li.is-current .wizard-tab-num {
  background: var(--accent);
  color: #fff;
}

.wizard-step {
  display: grid;
  gap: 8px;
}

.js-wizard .wizard-step:not(.is-current) {
  display: none;
}

.wizard-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 16px;
}

.wizard-nav [data-wizard-next],
.wizard-nav [data-wizard-back] {
  display: none;
}

.js-wizard .wizard-nav [data-wizard-next],
.js-wizard .wizard-nav [data-wizard-back] {
  display: inline-flex;
}

.wizard-nav--center {
  justify-content: flex-end;
  gap: 12px;
}

/* Card header: form title on the left, "What each trigger does" popover right. */
.wizard-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

/* Delivery channel rows (email / Slack) with an optional
   per-channel custom-message editor. */

/* Delivery step: expandable channel cards (Email / Slack). */
/* Delivery channels as tabs. The old three-column grid pinned an expanded
   channel to a third of the width, which squeezed the email fields and the
   placeholder tokens into a single column. */
.chan-tabs {
  display: inline-flex;
  gap: 6px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px;
  margin-bottom: 16px;
  /* The wizard step is a grid, so inline-flex alone would still stretch. */
  width: max-content;
  max-width: 100%;
  justify-self: start;
}

.chan-tab {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 12.5px;
  font-weight: 650;
  color: var(--muted);
  padding: 7px 15px;
  border-radius: 999px;
  cursor: pointer;
}

.chan-tab.is-on { background: #0f4c3d; color: #fff; }
.chan-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The destination is a radio group now (one destination per schedule), so the
   input itself is visually replaced by the pill. Kept focusable rather than
   display:none so keyboard users can still reach and arrow through it. */
.chan-tab input[type="radio"] {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  pointer-events: none;
}
.chan-tab:has(input[type="radio"]:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* A destination the shop hasn't connected yet: dimmed, but still selectable - its
   panel holds the only instructions for connecting it, so it has to be reachable. */
.chan-tab.is-unavailable:not(.is-on) { opacity: .6; }

.chan-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); flex: none; }
.chan-tab.is-on .chan-dot { background: #7cf0c6; }
.chan-tab:not(.is-on) .chan-dot { background: #cdd4d1; }
.chan-dot.is-off { background: #cdd4d1; }

.chan-hint { font-size: 12px; margin: -8px 0 16px; }

.chan-panel { display: none; }
.chan-panel.is-on { display: block; }
.chan-panel > * + * { margin-top: 14px; }

/* Delivery step: decisions on the left, the docket on the right.
   The step used to be a ~500px column in a full-width card, so two thirds of it was
   empty; the docket fills that with the one thing the step never said - what the
   schedule will actually do. Stacks at md, where a side-by-side would squeeze both. */
.dlv-split {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 0.65fr);
  gap: 26px;
  align-items: start;
}

/* One vertical rhythm for the whole column.
   It previously had three competing ones: .chan-panel > * + * spaced the panel's own
   children, the elements inside [data-chan-status] had no rule at all, and paragraphs
   fell back to their default margins. That's why a kv row butted straight into the
   button under it while a one-line hint opened a 24px gap. */
.dlv-main {
  min-width: 0;
  display: grid;
  gap: 18px;
  align-content: start;
}

.dlv-main > .chan-tabs { margin-bottom: 0; }
/* The picker's caption, so it sits with the picker rather than floating between. */
.dlv-main > .chan-hint { margin: -14px 0 0; }
.dlv-main [data-chan-status] { display: grid; gap: 14px; }
.dlv-main .chan-fields { gap: 16px; }

/* A note that explains the control above it belongs to that control - full rhythm
   spacing reads as a new thought instead of a caption. */
.dlv-main .cn-kv + .field-hint,
.dlv-main .cn-inline-form + .field-hint,
.dlv-main .notif-connect-note + .field-hint { margin-top: -9px; }

/* Lighter definition rows. A filled, bordered, full-bleed bar is a heavy container for
   twenty characters of value; a hairline carries the same structure at a fraction of the
   visual weight, and lets the docket be the only strong surface on the step. */
.dlv-main .cn-kv {
  background: none;
  border: 0;
  border-bottom: 1px solid var(--line-soft);
  border-radius: 0;
  padding: 7px 2px;
}

/* Setting up a destination is subordinate to saving the schedule, so it must not
   out-weigh it: --line-strong is near-black and read heavier than the primary button. */
.dlv-main .button-secondary { border-color: var(--line); }
.dlv-main .button-secondary:hover { border-color: var(--text); }

@media (max-width: 1023px) {
  .dlv-split { grid-template-columns: 1fr; gap: 20px; }
}

/* Inline edit forms live inside a list row, roughly half the width of the add page.
   Splitting that squeezes the docket to ~270px, where the To line wraps to three lines
   and the message sample stops being readable - so stack it there whatever the viewport. */
.schedule-row .dlv-split { grid-template-columns: 1fr; gap: 18px; }
.schedule-row .docket { position: static; }

/* The docket. Same immersive dark-green treatment as the email preview it replaces -
   this is the app's established "here's the outcome" surface, and one preview surface
   for all three destinations beats three inconsistent ones. */
.docket {
  background: linear-gradient(150deg, #082e25, #0f4c3d);
  border-radius: 11px;
  padding: 16px 18px;
  color: #f3efe3;
  display: grid;
  gap: 12px;
  position: sticky;
  top: 14px;
}

.docket-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.docket-eyebrow {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #7cf0c6;
}

/* Names the destination in the corner of the eye, so the step's one consequential
   choice stays legible while you work further down the form. */
.docket-stamp {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10px;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: #7cf0c6;
  border: 1.5px solid rgba(124, 240, 198, .55);
  border-radius: 4px;
  padding: 3px 7px;
  transform: rotate(-2.5deg);
}

.docket-lines { margin: 0; display: grid; gap: 8px; }

.docket-line {
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr);
  gap: 10px;
  align-items: baseline;
}

.docket-line dt {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: rgba(243, 239, 227, .5);
}

.docket-line dd {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.45;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.docket-line dd.is-mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 500;
  font-size: 11.5px;
}

/* Torn edge: above it is the schedule's own configuration, below it is one real order
   run through that configuration. The two are different kinds of claim. */
.docket-perf {
  height: 9px;
  margin: 0 -18px;
  border-top: 1px dashed rgba(243, 239, 227, .28);
  background: radial-gradient(circle at 6px -4px, transparent 5px, transparent 5px) repeat-x;
  background-size: 13px 9px;
}

.docket-sample { display: grid; gap: 8px; }
.docket-sample[hidden] { display: none; }

.docket-subject { margin: 0; font-size: 14px; font-weight: 680; }

.docket-body {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.6;
  color: rgba(243, 239, 227, .88);
  white-space: pre-line;
}

.docket-file {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 11.5px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: #7cf0c6;
  border: 1px solid rgba(124, 240, 198, .3);
  border-radius: 7px;
  padding: 5px 9px;
  justify-self: start;
  max-width: 100%;
  overflow-wrap: anywhere;
}

.docket-note,
.docket-foot {
  font-size: 11px;
  line-height: 1.45;
  color: rgba(243, 239, 227, .55);
}

/* A schedule that looks healthy and delivers nothing is the failure this whole step
   exists to prevent, so it gets amber inside the card rather than a footnote. */
.docket-flag {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0;
  font-size: 11.5px;
  line-height: 1.45;
  font-weight: 600;
  color: #f7d9a6;
  background: rgba(247, 217, 166, .12);
  border: 1px solid rgba(247, 217, 166, .3);
  border-radius: 7px;
  padding: 8px 10px;
}

.docket-flag[hidden] { display: none; }
.docket-flag svg { flex: none; margin-top: 1px; }

/* Separates the invoice email from the receipt copy inside the Email tab. */

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

/* Immersive email preview. */
.mail-prev {
  background: linear-gradient(150deg, #082e25, #0f4c3d);
  border-radius: 11px;
  padding: 16px 18px;
  color: #f3efe3;
  display: grid;
  gap: 9px;
  position: sticky;
  top: 14px;
}

.mail-prev .eyebrow { color: #7cf0c6; }
.mail-prev-sub { margin: 0; font-size: 14.5px; font-weight: 680; }
.mail-prev-meta { margin: 0; font-size: 11.5px; color: rgba(243, 239, 227, .6); }

.mail-prev-body {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.6;
  color: rgba(243, 239, 227, .88);
  border-top: 1px solid rgba(243, 239, 227, .16);
  padding-top: 10px;
  white-space: pre-line;
}

.mail-prev-attach {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 11.5px;
  color: #7cf0c6;
  border: 1px solid rgba(124, 240, 198, .3);
  border-radius: 7px;
  padding: 5px 9px;
  justify-self: start;
}

.mail-prev-note { font-size: 11px; color: rgba(243, 239, 227, .5); }

/* Placeholder tokens are buttons that insert at the cursor. */
.ph-chip {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  background: #f1f3f2;
  border: 1px solid var(--line-soft);
  border-radius: 5px;
  padding: 3px 7px;
  color: var(--muted);
  cursor: pointer;
}

.ph-chip:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-weak); }
.ph-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

@media (max-width: 860px) {
  .mail-prev { position: static; }
}

/* Automatic Invoices list page: header with an Add button, and the schedule
   list beside the "Sent" activity column. */
body:has(.auto-inv-page) .workspace { background: #edf4f0; }

/* Fill the viewport: the card reaches the bottom of the screen instead of
   floating with an empty band below it, and each column scrolls internally
   when its list is long. The downstream panels already carry min-height:0 and
   overflow - this just lets the height flow down from the frame. Desktop only;
   the stacked mobile layout keeps its natural height. */
@media (min-width: 901px) {
  body:has(.auto-inv-page) .workspace {
    display: flex;
    flex-direction: column;
  }

  body:has(.auto-inv-page) .page-content {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
  }

  body:has(.auto-inv-page) .section-card.auto-inv-page {
    flex: 1 1 auto;
    min-height: 0;
    /* Override .stack's grid so .auto-inv-columns can take the leftover height. */
    display: flex;
    flex-direction: column;
  }

  body:has(.auto-inv-page) .auto-inv-columns {
    flex: 1 1 auto;
    min-height: 0;
  }

  /* Mirror the Sent panel's scroll on the schedules side. */
  body:has(.auto-inv-page) .schedule-list {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
  }
}

/* ---- Automatic invoices empty state: a single autopilot on-ramp card with the
   primary Add-schedule CTA. Shown only when the shop has no schedules yet. ---- */
.autopilot {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 40px 24px;
  text-align: center;
  border: 1.5px dashed var(--line);
  border-radius: 14px;
  background: var(--panel-subtle);
}

.autopilot-ic {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  margin-bottom: 8px;
  border-radius: 13px;
  color: var(--accent);
  background: var(--accent-weak);
}

.autopilot-title {
  font-size: 18px;
  font-weight: 730;
  letter-spacing: -0.01em;
  color: var(--text);
}

.autopilot-copy {
  max-width: 42ch;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--muted);
}

.autopilot-add {
  gap: 7px;
  margin-top: 18px;
}

/* Desktop viewport-fill: let the autopilot block grow to fill the card like the
   populated columns do, so its content centers in the available space. */
@media (min-width: 901px) {
  body:has(.auto-inv-page) .autopilot {
    flex: 1 1 auto;
    min-height: 0;
  }
}

@media (max-width: 640px) {
  .autopilot {
    padding: 30px 18px;
  }
}

/* Sentence-first builder: the schedule restated in plain English, updated live. */
.sched-sentence {
  background: linear-gradient(140deg, #082e25, #0f4c3d 84%);
  border-radius: 11px;
  padding: 16px 18px;
  color: #f3efe3;
  display: grid;
  gap: 7px;
  margin-bottom: 18px;
}

.sched-sentence .eyebrow { color: #7cf0c6; }
.sched-sentence p { margin: 0; font-size: 15px; line-height: 1.45; }

/* ---- Add-schedule (new) page: immersive pastel canvas + hero, so the builder
   matches the Automatic Invoices list instead of floating on plain white. The
   outer section-card goes bare; the inner .preview-card is the white form card. */
body:has(.auto-new-page) .workspace { background: #edf4f0; }

.section-card.auto-new-page {
  background: transparent;
  border: 0;
  box-shadow: none;
  padding: 0;
}

.auto-new-hero {
  padding: 0 2px;
}

.auto-new-hero h2 {
  font-size: 24px;
  font-weight: 730;
  letter-spacing: -0.02em;
}

.auto-new-hero .section-note {
  margin-top: 4px;
}

/* Section headers: an accent number chip + label, with room above so each
   step reads as its own block (esp. "3 · What goes out" under the When row).
   The number comes from a CSS counter so it stays sequential when the When
   section is hidden for event triggers (display:none rows don't count). */
.schedule-wizard {
  counter-reset: sched-step;
  /* The trigger grids size to THIS form's width, not the viewport, so the
     inline edit-in-place form (narrow, beside the Sent rail) and the full-page
     add form each lay their cards out cleanly. */
  container-type: inline-size;
}

.grp-label {
  counter-increment: sched-step;
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--text);
  font-weight: 800;
  margin: 30px 0 13px;
}

/* The first header follows the summary card; a bit less top room than the rest. */
.wizard-step > .grp-label {
  margin-top: 18px;
}

.grp-num {
  display: inline-grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 7px;
  background: var(--accent-weak);
  color: var(--accent-ink);
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0;
}

.grp-num::before {
  content: counter(sched-step);
}

.trig-group { display: grid; gap: 14px; margin-bottom: 0; }
.trig-heading { margin: 0 0 8px; font-size: 12px; font-weight: 700; color: var(--muted); display: flex; align-items: center; gap: 7px; }
.trig-heading-ic { display: inline-flex; color: var(--accent); }
/* Trigger cards flow by the form's own width (container query). Each group keeps
   a balanced layout at every size instead of orphaning a card on a second row:
   the 4-card repeating group is 2×2 when cramped and 4-across when roomy; the
   3-card event group stays 3-across until it must stack. */
.trig-grid { display: grid; gap: 9px; grid-template-columns: 1fr; }
@container (min-width: 440px) {
  .trig-grid--wide4 { grid-template-columns: repeat(2, 1fr); }
  .trig-grid--wide3 { grid-template-columns: repeat(3, 1fr); }
}
@container (min-width: 760px) {
  .trig-grid--wide4 { grid-template-columns: repeat(4, 1fr); }
  .trig-grid--wide3 { grid-template-columns: repeat(3, 1fr); }
}

/* Either/or: the trigger sub-group without the current selection dims out. It
   stays clickable - picking a card there just moves the selection across. */
.trig-heading.is-muted,
.trig-grid.is-muted {
  opacity: .4;
  transition: opacity .15s ease;
}

/* Radio cards. The input stays in the DOM (and focusable) but is visually
   replaced by the card, so keyboard and screen readers still get a radio group. */
.trig {
  position: relative;
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 11px 32px 11px 13px;
  cursor: pointer;
  display: grid;
  gap: 3px;
  align-content: start;
  background: #fff;
  transition: border-color .12s ease, background .12s ease, box-shadow .12s ease;
}

.trig input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
}

.trig b { font-size: 12.5px; font-weight: 720; }
.trig .trig-desc { font-size: 11.5px; color: var(--muted); line-height: 1.35; }
.trig:hover { border-color: var(--accent); }

/* A trigger the plan doesn't include: visible, named with the tier that
   unlocks it, but not selectable. */
.trig.is-locked {
  opacity: 0.62;
  cursor: not-allowed;
}

.trig.is-locked:hover { border-color: var(--line); }
.trig.is-locked b { color: var(--muted); }

.trig-lock {
  position: absolute;
  top: 8px;
  right: 8px;
  border-radius: 5px;
  padding: 2px 6px;
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: var(--accent-weak);
  color: var(--accent);
}

/* Selected state resolves via :checked (works without JS) AND the JS-added
   .is-on, so exactly one card ever reads as chosen - with a check badge. */
.trig:has(input:checked),
.trig.is-on {
  border-color: var(--accent);
  background: var(--accent-weak);
  box-shadow: inset 0 0 0 1px var(--accent);
}

.trig-check {
  position: absolute;
  top: 9px;
  right: 9px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--accent);
  color: #fff;
  opacity: 0;
  transform: scale(.6);
  transition: opacity .12s ease, transform .12s ease;
}

.trig:has(input:checked) .trig-check,
.trig.is-on .trig-check {
  opacity: 1;
  transform: none;
}

.trig:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Automation status strip - the immersive moment on this page. */
.auto-hero {
  background: linear-gradient(140deg, #082e25, #0f4c3d 82%);
  border-radius: 12px;
  color: #f3efe3;
  padding: 20px 22px;
}

.auto-hero-eyebrow {
  font-size: 10.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: #7cf0c6;
  font-weight: 700;
}

.auto-hero-stats {
  display: flex;
  gap: 34px;
  flex-wrap: wrap;
  margin-top: 12px;
}

.ahstat { display: flex; flex-direction: column; gap: 2px; }

.ahstat b {
  font-size: 24px;
  font-weight: 760;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}

.ahstat span {
  font-size: 11px;
  color: rgba(243, 239, 227, .62);
  text-transform: uppercase;
  letter-spacing: .07em;
  font-weight: 600;
}

.ahstat.is-next b { color: #7cf0c6; }

/* Schedules that can't deliver. Amber rather than the mint used for healthy
   numbers - this one is asking to be acted on, and the strip sits on a dark
   panel, so it needs its own tone rather than the light-background amber. */
.ahstat.is-warn b { color: #ffcc66; }
.ahstat.is-warn span { color: rgba(255, 204, 102, .8); }

/* Status rail: pairs with the "Paused" pill, never the only signal. */
.schedule-rail {
  width: 3px;
  align-self: stretch;
  border-radius: 3px;
  background: var(--accent);
  flex: none;
}

.schedule-rail.is-off { background: #d8dedb; }

/* Overflow menu for the rare + destructive row actions. <details> so it works
   without JS; a script closes it on outside click. */
.row-menu { position: relative; }

.row-menu-trigger {
  list-style: none;
  width: 28px;
  height: 26px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: #fff;
  color: var(--muted);
  cursor: pointer;
}

.row-menu-trigger::-webkit-details-marker { display: none; }
.row-menu-trigger:hover { border-color: var(--accent); color: var(--accent); }
.row-menu[open] .row-menu-trigger { border-color: var(--accent); color: var(--accent); }

.row-menu-pop {
  position: absolute;
  right: 0;
  top: calc(100% + 5px);
  z-index: 20;
  min-width: 150px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 5px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  box-shadow: 0 6px 18px rgba(8, 46, 37, .1);
}

.row-menu-item {
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  text-align: left;
  width: 100%;
  padding: 7px 9px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--text);
  cursor: pointer;
}

.row-menu-item:hover { background: var(--hover); }
.row-menu-item.is-danger { color: var(--red); }
.row-menu-item.is-danger:hover { background: var(--red-weak); }

/* Quick send: the menu shows the destination under each channel, grouped by who
   it reaches - "to customer" actually delivers, "share internally" only posts to
   the merchant's own workspace. */
.row-menu-group {
  margin: 5px 0 2px;
  padding: 0 9px;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--faint);
}
.row-menu-group:first-child { margin-top: 1px; }

.row-menu-item.is-stacked {
  display: flex;
  flex-direction: column;
  gap: 1px;
  text-decoration: none;
}
.row-menu-item.is-stacked strong { font-size: 12.5px; font-weight: 650; color: inherit; }
.row-menu-item.is-stacked small {
  font-size: 11px;
  font-weight: 500;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
.row-menu-item.is-muted { color: var(--faint); cursor: default; }
.row-menu-item.is-muted:hover { background: transparent; }
a.row-menu-item.is-muted { cursor: pointer; }
a.row-menu-item.is-muted:hover { background: var(--hover); }

/* "Send to another email" - an inline recipient field at the foot of the group. */
.row-menu-form {
  margin: 4px 4px 2px;
  padding: 8px 5px 2px;
  border-top: 1px solid var(--line-soft);
}
.row-menu-form label {
  display: block;
  margin-bottom: 5px;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
}
.row-menu-form-line { display: flex; gap: 5px; }
.row-menu-form input {
  flex: 1;
  min-width: 0;
  padding: 6px 8px;
  font: inherit;
  font-size: 12px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: #fff;
  color: var(--text);
}
.row-menu-form input:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }
.row-menu-form button {
  flex: 0 0 auto;
  width: 30px;
  display: grid;
  place-items: center;
  border: 1px solid var(--accent);
  border-radius: 6px;
  background: var(--accent);
  color: #fff;
  cursor: pointer;
}
.row-menu-form button:hover { filter: brightness(1.08); }

.odt-sendmenu .row-menu-pop { min-width: 232px; }
.odt-sendbtn {
  width: auto;
  height: 34px;
  gap: 5px;
  grid-auto-flow: column;
  padding: 0 11px;
  font-size: 12.5px;
  font-weight: 650;
  color: var(--text);
  border-radius: 8px;
}

.auto-inv-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}

.auto-inv-head .button {
  flex: none;
  white-space: nowrap;
}

.auto-inv-columns {
  display: grid;
  grid-template-columns: minmax(0, 1.9fr) minmax(0, 1fr);
  gap: 28px;
  /* Stretch both columns to the same height so each panel's pager can pin to a
     shared baseline at the bottom of the card. */
  align-items: stretch;
}

/* Plain labelled regions - no box chrome, so the only borders on the page are
   the outer panel and the hairline dividers between schedule rows. The panel
   row (below the eyebrow) fills the column height so its pager sits at the
   bottom. */
.auto-inv-col {
  gap: 8px;
  grid-template-rows: auto 1fr;
  min-height: 0;
}

/* Divider between "Your schedules" and "Sent". With the 28px grid gap plus this
   28px padding, the hairline sits centered in the gutter. */
.auto-inv-sent {
  border-left: 1px solid var(--line);
  padding-left: 28px;
}

@media (max-width: 900px) {
  .auto-inv-columns { grid-template-columns: 1fr; }

  /* Stacked layout: the divider moves above "Sent" as a top rule. */
  .auto-inv-sent {
    border-left: 0;
    padding-left: 0;
    border-top: 1px solid var(--line);
    padding-top: 16px;
  }
}

.placeholder-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}

.placeholder-chips code {
  font-size: 11px;
  background: var(--line-soft);
  padding: 2px 6px;
  border-radius: 5px;
  color: var(--muted);
}

/* ===== Dashboard redesign - "Focus Hero" (minimal-immersive) ===== */
.dash2 { display: flex; flex-direction: column; gap: 20px; padding-top: 4px; }
.metric-group { display: flex; flex-direction: column; gap: 10px; }
.tnum { font-variant-numeric: tabular-nums; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.dash2 .dhead, .dashboard .dhead { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.dash2 .dhead .dt h2 { font-size: 22px; font-weight: 700; letter-spacing: -0.01em; }
.dash2 .dhead .dt p { color: var(--muted); margin-top: 4px; }
.dash2 .dact { display: flex; align-items: center; gap: 12px; }
.dash2 .updated { color: var(--faint); font-size: 12px; display: flex; align-items: center; gap: 6px; }
.dash2 .updated .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--green); }
.dash-refresh svg { width: 15px; height: 15px; }
.dash2 .section-head { font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); margin: 0; }

/* Bold brand-green hero banner (Total Sales) with translucent KPI tiles. */
.hero2 {
  position: relative; overflow: hidden; border-radius: var(--radius);
  padding: 26px 28px; color: #eafaf3;
  background: linear-gradient(135deg, #082e25, #0f4c3d 72%, #147c64);
}
.hero2-orb { position: absolute; right: -70px; top: -90px; width: 260px; height: 260px; border-radius: 50%;
  background: radial-gradient(circle, rgba(124,240,198,0.26), transparent 70%); pointer-events: none; }
.hero2-back { position: absolute; left: 0; right: 0; bottom: 0; height: 56%; pointer-events: none; }
.hero2-in { position: relative; z-index: 1; display: flex; justify-content: space-between; align-items: flex-end; gap: 28px; flex-wrap: wrap; }
.hero2-main { min-width: 0; }
.hero2-lab { font-size: 12px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; color: var(--mint, #7cf0c6); display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.hero2-chip { font-size: 11px; font-weight: 700; padding: 3px 9px; border-radius: 999px; text-transform: none; letter-spacing: 0; color: #eafaf3; background: rgba(124,240,198,0.18); }
.hero2-chip.down { color: #ffd9d0; background: rgba(194,90,72,0.28); }
.hero2-big { font-size: 50px; font-weight: 800; letter-spacing: -0.03em; line-height: 1; margin-top: 12px; }
.hero2-foot { color: rgba(234,250,243,0.72); margin-top: 9px; font-size: 14px; }
.hero2-tiles { display: flex; gap: 12px; flex-wrap: wrap; }
.hero2-tile { background: rgba(255,255,255,0.08); border: 1px solid rgba(124,240,198,0.16); border-radius: 12px; padding: 13px 16px; min-width: 132px; }
.hero2-tile .k { font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: rgba(234,250,243,0.66); }
.hero2-tile .v { font-size: 24px; font-weight: 760; letter-spacing: -0.01em; margin-top: 6px; }
.hero2-tile .d { font-size: 11px; font-weight: 700; margin-top: 4px; color: var(--mint, #7cf0c6); }
.hero2-tile .d.down { color: #ffb5a6; }
@media (max-width: 720px) { .hero2-big { font-size: 40px; } .hero2-tiles { width: 100%; } .hero2-tile { flex: 1; } }

.metric-row { display: grid; gap: 12px; }
.metric-row-3 { grid-template-columns: repeat(3, 1fr); }
.metric-row-5 { grid-template-columns: repeat(5, 1fr); }
.op2 { padding: 14px; }
/* Invoicing tiles carry no meter, so keep them compact. */
.metric-row-3 .op2 { padding: 12px 14px; }
.metric-row-3 .op2-val { font-size: 20px; margin-top: 6px; }
.metric-row-3 .op2-rate { margin-top: 1px; }
.op2-top { display: flex; align-items: center; gap: 8px; }
.op2-ic { width: 26px; height: 26px; border-radius: 7px; display: grid; place-items: center; flex: none; }
.op2-ic svg { width: 14px; height: 14px; }
.op2-ic.ok { background: var(--green-weak); color: var(--green); }
.op2-ic.warn { background: var(--amber-weak); color: var(--amber); }
.op2-ic.pay { background: var(--accent-weak); color: var(--accent); }
.op2-ic.ship { background: var(--blue-weak); color: var(--blue); }
.op2-ic.ret { background: var(--red-weak); color: var(--red); }
.op2-lab { font-size: 12px; font-weight: 600; color: var(--muted); }
.op2-val { font-size: 24px; font-weight: 730; letter-spacing: -0.01em; margin-top: 10px; }
.op2-of { font-size: 14px; font-weight: 600; color: var(--faint); margin-left: 2px; }
.op2-rate { font-size: 12px; color: var(--muted); margin-top: 2px; }
.op2-meter { height: 5px; border-radius: 999px; background: var(--line-soft); margin-top: 10px; overflow: hidden; }
.op2-meter i { display: block; height: 100%; border-radius: 999px; background: var(--accent); }
.op2-meter i.warn { background: var(--amber); }
.op2-meter i.ret { background: var(--red); }
.op2-meter i.ship { background: var(--blue); }

.chart2 { padding: 18px; }
.chart2-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.chart2-head h3 { font-size: 15px; font-weight: 700; }
.chart2-head .sub { color: var(--muted); margin-top: 3px; }

/* Performance-chart range selector: a segmented pill that reloads the chart. */
.range-seg { display: inline-flex; align-items: center; gap: 2px; padding: 3px; background: #fff; border: 1px solid var(--line); border-radius: 999px; flex: none; }
.range-seg-btn { appearance: none; border: 0; background: transparent; font: inherit; font-size: 12px; font-weight: 650; color: var(--muted); padding: 6px 13px; border-radius: 999px; cursor: pointer; transition: background .14s ease, color .14s ease; }
.range-seg-btn:hover { color: var(--text); }
.range-seg-btn.is-on { background: var(--accent); color: #fff; }
.range-seg-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-weak); }

/* Performance-chart controls: a metric multi-select, a mark-style toggle, and
   the range control, all in the card header (wrapping to the right). */
.chart2-controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }
.metric-seg { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 14px; }
.metric-chip { appearance: none; display: inline-flex; align-items: center; gap: 7px; font: inherit; font-size: 12px; font-weight: 600; color: var(--muted); background: #fff; border: 1px solid var(--line); border-radius: 999px; padding: 6px 11px; cursor: pointer; transition: background .14s ease, color .14s ease, border-color .14s ease; }
.metric-chip .mp-dot { width: 9px; height: 9px; border-radius: 3px; opacity: .45; transition: opacity .14s ease; }
.metric-chip:hover { color: var(--text); border-color: var(--muted); }
.metric-chip.is-on { color: var(--text); border-color: var(--accent); background: var(--accent-weak); }
.metric-chip.is-on .mp-dot { opacity: 1; }
.metric-chip:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-weak); }
.style-seg { display: inline-flex; align-items: center; gap: 2px; padding: 3px; background: #fff; border: 1px solid var(--line); border-radius: 999px; flex: none; }
.style-seg button { appearance: none; border: 0; background: transparent; font: inherit; font-size: 12px; font-weight: 650; color: var(--muted); padding: 6px 12px; border-radius: 999px; cursor: pointer; transition: background .14s ease, color .14s ease; }
.style-seg button:hover { color: var(--text); }
.style-seg button.is-on { background: var(--accent); color: #fff; }
.style-seg button:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-weak); }

.chart2-summary { display: flex; gap: 34px; margin: 16px 0 6px; flex-wrap: wrap; }
.chart2-summary .k { font-size: 12px; color: var(--muted); font-weight: 600; display: flex; align-items: center; gap: 7px; }
.chart2-summary .sw { width: 10px; height: 10px; border-radius: 3px; }
.chart2-summary .v { font-size: 22px; font-weight: 730; letter-spacing: -0.01em; margin-top: 3px; }
.chart2-wrap { position: relative; margin-top: 10px; height: 264px; }
.chart2-wrap canvas { width: 100% !important; }

.dash-empty { padding: 48px 24px; text-align: center; display: flex; flex-direction: column; align-items: center; gap: 10px; }
.dash-empty-ic { width: 48px; height: 48px; border-radius: 12px; background: var(--panel-subtle); color: var(--muted); display: grid; place-items: center; }
.dash-empty-ic svg { width: 24px; height: 24px; }
.dash-empty h3 { font-size: 16px; font-weight: 700; }
.dash-empty p { color: var(--muted); max-width: 44ch; }
.dash-empty .button { margin-top: 6px; }

/* ---- Light-pastel theme (the immersive green/mint identity in a soft, light
   key), scoped to the dashboard page only: a gentle mint-cream ground, white
   cards, deep-green ink. Overriding these tokens re-themes every component,
   including the inline-SVG chart which paints with var(--accent)/var(--blue). ---- */
body:has(.dashboard) .workspace { background: #edf4f0; }
.dashboard {
  --accent: #147c64; --accent-weak: #e2f1ea;
  /* Orders series in soft sage so it reads clearly against the green revenue line. */
  --blue: #6fa693; --blue-weak: #e7efeb;
  --green: #1f9469; --green-weak: #e2f4ec;
  --amber: #b58a2a; --amber-weak: #f6eede;
  --red: #c25a48; --red-weak: #f7e7e3;
  --text: #10231c; --muted: #566b62; --faint: #8ba095;
  --panel: #ffffff; --line: #dbe8e2; --line-soft: #e9f0ec;
  --hover: #eef5f1;
  color: #10231c;
}
.dashboard .section-card { background: #fff; border-color: var(--line); }
.dashboard .op2-meter { background: #e9f0ec; }

.dashboard .dash-refresh { background: #fff; border: 1px solid var(--line); color: var(--text); }
.dashboard .dash-refresh:hover, .dashboard .button-ghost:hover { color: var(--accent); border-color: var(--accent); background: #fff; }
.dashboard .button { border-color: var(--accent); color: var(--accent); background: #fff; }
.dashboard .button:hover { background: var(--accent); color: #fff; }
.dashboard .dash-empty-ic { background: #eef5f1; color: var(--muted); }

/* ---- Brand-green sidebar (global chrome). Same scoped-token approach: redefine
   the sidebar's tokens so its nav, brand mark, active state and profile all
   re-theme to cream-on-green with a mint accent. ---- */
.sidebar {
  --sidebar: #0f4c3d;
  --text: #f3efe3;
  --muted: rgba(243, 239, 227, .68);
  --faint: rgba(243, 239, 227, .48);
  --line: rgba(255, 255, 255, .11);
  --line-strong: rgba(255, 255, 255, .22);
  --hover: rgba(255, 255, 255, .07);
  --accent: #7cf0c6;
  --accent-weak: rgba(124, 240, 198, .16);
}
.sidebar-toggle { background: transparent; }

/* ---- Sidebar redesign: solid gradient brand mark + avatar, roomier rounded
   pill nav with a clear mint active state, and a carded profile. ---- */
.sidebar { padding: 20px 12px; }
.brand { padding: 2px 6px 16px; gap: 11px; }
.brand-mark { width: 30px; height: 30px; border: 0; border-radius: 9px; color: #082e25; background: linear-gradient(140deg, #7cf0c6, #147c64); }
.brand-mark svg { width: 17px; height: 17px; }
.brand strong { font-size: 14.5px; letter-spacing: -0.01em; }
.brand small { color: rgba(243, 239, 227, .55); }
.nav-stack { gap: 3px; margin-top: 8px; }
.nav-link { position: relative; min-height: 40px; padding: 0 11px; border-radius: 9px; }
/* Every icon lives in a fixed 28px slot, so the filled active chip drops in
   without nudging the labels out of alignment row-to-row. */
.nav-link .nav-icon {
  width: 28px; height: 28px; padding: 5px; box-sizing: border-box;
  border-radius: 8px; opacity: .82;
  transition: opacity .15s ease, background .15s ease, color .15s ease;
}
.nav-link:hover { background: rgba(255, 255, 255, .06); }
.nav-link:hover .nav-icon { opacity: 1; }
/* Active row (B+C): tinted pill with a mint inset ring and a short accent rail,
   plus the icon in a filled mint-gradient chip. */
.nav-link.is-active {
  background: rgba(124, 240, 198, .14);
  color: #bafbe0;
  font-weight: 600;
  box-shadow: inset 0 0 0 1px rgba(124, 240, 198, .22);
}
.nav-link.is-active .nav-icon {
  opacity: 1;
  color: #082e25;
  background: linear-gradient(140deg, #7cf0c6, #22b98d);
}
.nav-link.is-active::before {
  content: "";
  position: absolute;
  left: 3px; top: 50%; transform: translateY(-50%);
  width: 3px; height: 18px;
  border-radius: 3px;
  background: #7cf0c6;
}
/* Collapsed rail: the filled chip is indicator enough - drop the edge rail. */
html.nav-rail .nav-link.is-active::before { display: none; }
.sidebar .eyebrow { font-size: 10.5px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(243, 239, 227, .42); padding: 0 11px; margin: 16px 0 6px; }
.sidebar-footnote { margin-top: auto; padding: 12px 4px 2px; border-top: 1px solid rgba(255, 255, 255, .09); }
.sidebar-profile { padding: 9px; border-radius: 12px; background: rgba(255, 255, 255, .045); border: 1px solid rgba(255, 255, 255, .08); }
.sidebar-profile:hover { background: rgba(255, 255, 255, .09); }
.sidebar-profile-avatar { background: linear-gradient(140deg, #7cf0c6, #147c64); color: #082e25; border-radius: 10px; }

/* Identity chips carrying the brand logo: a white tile framing the image, so a
   transparent PNG reads correctly against the dark rail. */
.sidebar-profile-avatar.has-logo,
.app-topbar-avatar.has-logo { background: #fff; padding: 4px; }
.sidebar-profile-avatar.has-logo img,
.app-topbar-avatar.has-logo img { width: 100%; height: 100%; object-fit: contain; display: block; }

@media (max-width: 1080px) { .metric-row-5 { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 860px) {
  .metric-row-3 { grid-template-columns: 1fr; }
  .metric-row-5 { grid-template-columns: repeat(2, 1fr); }
  .hero2-in { align-items: flex-start; }
  .hero2-big { font-size: 38px; }
}
@media (max-width: 560px) { .metric-row-5 { grid-template-columns: 1fr; } }

/* ============================================================
   Orders page redesign - pastel green/mint identity (matching
   the dashboard's light key) + Orders/Draft Orders switcher.
   Scoping the pastel tokens to .orders-page re-themes the
   section cards, tables, badges and buttons in one place.
   ============================================================ */
body:has(.orders-page) .workspace { background: #edf4f0; }
.orders-page {
  /* Grid item in .page-content: without min-width:0 it defaults to min-width:auto
     and expands to the orders table's 1240px min-width, overflowing the workspace
     (the green bg then appears to "cut off"). Zeroing it lets .table-shell's
     overflow:auto scroll the wide table internally instead. */
  min-width: 0;
  /* Uniform vertical rhythm between header, switcher, filter card and table. */
  display: flex; flex-direction: column; gap: 18px;
  --accent: #147c64; --accent-weak: #e2f1ea;
  --blue: #6fa693; --blue-weak: #e7efeb;
  --green: #1f9469; --green-weak: #e2f4ec;
  --amber: #b58a2a; --amber-weak: #f6eede;
  --red: #c25a48; --red-weak: #f7e7e3;
  --text: #10231c; --muted: #566b62; --faint: #8ba095;
  --panel: #ffffff; --line: #dbe8e2; --line-soft: #e9f0ec;
  --hover: #eef5f1;
  color: #10231c;
}
/* Drop block-flow margins so they don't double the flex gap above. */
.orders-page > .orders-head,
.orders-page > .view-switch { margin-bottom: 0; }
.orders-page .section-card,
.orders-page .table-shell { background: #fff; border-color: var(--line); }

/* --- Orders + Draft Orders: pagination sits below the scroll, not inside it ---
   The shell used to be the scroll container with the footer as its last child,
   which cost two things: the horizontal scrollbar is painted at the bottom edge
   of the scroll box, so it landed on top of the footer; and on a full page of
   rows the footer sat past the fold, so "Showing 1-25 of 150" and Previous/Next
   could only be reached by scrolling the table down. Giving the rows their own
   .table-scroll child and leaving the footer outside it pins the footer to the
   bottom of the card - which is the empty space under a short table - and keeps
   the scrollbar off it. The sticky thead just re-anchors to .table-scroll. */
.orders-page > .table-shell {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.orders-page > .table-shell > .table-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}
.orders-page > .table-shell > .table-footer { flex: 0 0 auto; }
.orders-page .button { border-color: var(--accent); color: var(--accent); background: #fff; }
.orders-page .button:hover:not([disabled]) { background: var(--accent); color: #fff; }
.orders-page .button[disabled] { opacity: 0.6; cursor: default; }
.orders-page .button-ghost:hover { color: var(--accent); border-color: var(--accent); }

.orders-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 18px; }
.orders-head-actions { flex: none; }
@media (max-width: 600px) { .orders-head { flex-direction: column; } }
.orders-title { font-size: 24px; font-weight: 700; letter-spacing: -0.01em; margin: 0; color: var(--text); }
.orders-subtitle { color: var(--muted); margin: 5px 0 0; font-size: 14px; }

/* --- Orders header: one primary action, the rest as quiet tools -------------
   Sync now / Full sync / Export PDFs all rendered at roughly the same weight
   (green outline + two white ghosts), so the row had no primary and the two
   occasional actions competed with the everyday one. Now the everyday Sync is
   the single filled button; Full sync and Export PDFs sit behind a hairline as
   borderless tools that only materialise a surface on hover. The sync chip
   carries the state the page otherwise never shows - the global sync banner
   hides itself the moment an import settles, so an idle Orders page had no
   answer to "is this data current?".
   Scoped to .orders-page: .orders-head is shared with Vendors and Payouts. */
.orders-page > .orders-head { align-items: center; }
.orders-page .orders-head-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}
.orders-page .orders-head-primary { padding: 0 14px; }
.orders-page .orders-head-primary .oha-ico { opacity: .85; }
.orders-page .orders-head-primary[disabled] { opacity: .55; }

.orders-head-tools {
  display: flex;
  align-items: center;
  gap: 2px;
  padding-left: 9px;
  border-left: 1px solid var(--line);
}
/* Quiet by default so the filled Sync stays the only loud thing in the row;
   the hairline + white ground only appear under the pointer. */
.orders-page .orders-head-tool {
  position: relative;
  padding: 0 11px;
  background: transparent;
  border-color: transparent;
  color: var(--muted);
  font-weight: 600;
  text-decoration: none;
}
.orders-page .orders-head-tool:hover {
  background: #fff;
  border-color: var(--line);
  color: var(--accent);
}
.orders-page .orders-head-tool .oha-ico { opacity: .7; }
.orders-page .orders-head-tool:hover .oha-ico { opacity: 1; }
/* The plan badge is absolute by default (it corners a .trig card); inline it so
   it reads as a suffix on a button instead of escaping to a positioned parent. */
.orders-page .orders-head-tool.is-locked { color: var(--faint); }
.orders-page .orders-head-tool.is-locked .trig-lock { position: static; margin-left: 1px; }

.orders-sync-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding-right: 2px;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
}
.orders-sync-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 0 3px rgba(31, 148, 105, 0.15);
}
.orders-sync-chip[data-state="running"] { color: var(--accent); }
.orders-sync-chip[data-state="running"] .orders-sync-dot {
  background: var(--accent);
  box-shadow: 0 0 0 3px rgba(20, 124, 100, 0.16);
  animation: orders-sync-pulse 1.6s ease-in-out infinite;
}
.orders-sync-chip[data-state="failed"] { color: var(--red); }
.orders-sync-chip[data-state="failed"] .orders-sync-dot {
  background: var(--red);
  box-shadow: 0 0 0 3px rgba(194, 90, 72, 0.16);
}
.orders-sync-chip[data-state="idle"] { color: var(--faint); }
.orders-sync-chip[data-state="idle"] .orders-sync-dot {
  background: var(--faint);
  box-shadow: 0 0 0 3px rgba(139, 160, 149, 0.16);
}
@keyframes orders-sync-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}
@media (prefers-reduced-motion: reduce) {
  .orders-sync-chip[data-state="running"] .orders-sync-dot { animation: none; }
}

/* Phone: the action row owns its own line under the title, and the tools' divider
   would dangle at the start of a wrapped line - drop it and let them stretch. */
@media (max-width: 600px) {
  .orders-page .orders-head-actions { width: 100%; }
  .orders-head-tools { padding-left: 0; border-left: 0; margin-left: -11px; }
  .orders-sync-chip { order: -1; width: 100%; }
}

/* --- Orders / Draft Orders: one table panel ---------------------------------
   Switcher + search on the first row, filters as pills on the second, table
   underneath - all in a single panel, so the controls visibly belong to the rows
   they filter. Replaces a filter .section-card stacked above a .table-shell,
   whose heading repeated the page title and the tab, and whose four field labels
   repeated the controls under them.
   The panel must NOT clip: the date picker's popover hangs out of the filter row.
   Only the table region below it clips, which is what rounds the bottom corners
   and owns the sideways scroll. */
.orders-page > .otable {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: visible;
}

.otoolbar { display: flex; flex-direction: column; }

.otoolbar-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
}
.otoolbar-row-top { justify-content: space-between; flex-wrap: wrap; }
/* Faintly recessed, so the filter row reads as a strip attached to the table
   rather than a second block of form. */
.otoolbar-row-filters {
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px 12px;
  border-top: 1px solid var(--line-soft);
  background: #fbfdfc;
}
.otoolbar-note {
  margin: 0;
  padding: 10px 14px;
  border-top: 1px solid var(--line-soft);
}

/* The switcher sits inside the panel now: drop the standalone bottom margin and
   let the row's padding place it. */
.otoolbar .view-switch { margin-bottom: 0; }

/* Search: icon inside the field, no label - the placeholder says what it takes. */
.ofilter-search {
  position: relative;
  display: flex;
  align-items: center;
  flex: 0 1 320px;
  min-width: 200px;
}
.ofilter-search-ico {
  position: absolute;
  left: 10px;
  color: var(--faint);
  pointer-events: none;
}
.ofilter-search input {
  width: 100%;
  min-height: 36px;
  padding: 7px 10px 7px 32px;
  color: var(--text);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.ofilter-search input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(20, 124, 100, 0.12);
}

.ofilter-pills { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* Pills carry their own value, so no label is needed above them: at rest they
   read "Any payment", once set they read "Paid" and turn accent. */
.ofilter-pill {
  min-height: 32px;
  padding: 0 28px 0 11px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  background-color: #fff;
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%23566b62' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
}
.ofilter-pill:hover { color: var(--accent); border-color: var(--accent); }
.ofilter-pill:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(20, 124, 100, 0.14);
}
.ofilter-pill.is-set {
  color: #0f5f4c;
  background-color: var(--accent-weak);
  border-color: #b7dccd;
  font-weight: 650;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%230f5f4c' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E");
}

/* The date filter: the native from/to pair, then its preset chips. */
.ofilter-pill-date { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.ofilter-pill-date .range-presets { margin: 0; }
.ofilter-pill-date .date-range-native input {
  min-height: 32px;
  padding: 5px 8px;
  font-size: 13px;
  color: var(--text);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}

/* Rows per page is a view setting, not a filter, so it sits apart: pushed to the
   end of the row and never tinted, or it would read as "a filter is on". */
.ofilter-rows { display: inline-flex; align-items: center; margin-left: auto; }
.ofilter-pill-rows { color: var(--faint); font-weight: 600; }
.ofilter-pill-rows:hover { color: var(--accent); }

.ofilter-clear-wrap { display: inline-flex; align-items: center; gap: 8px; }
.ofilter-clear-wrap[hidden] { display: none; }
.ofilter-sep { width: 1px; align-self: stretch; min-height: 20px; background: var(--line); }

.ofilter-clear {
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  text-decoration: none;
  padding: 0 2px;
}
.ofilter-clear:hover { color: var(--red); text-decoration: underline; }

.ofilter-count {
  margin: 0;
  font-size: 13px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.ofilter-count strong { color: var(--text); font-weight: 700; }
.ofilter-count .draft-synced-note { color: var(--faint); }

/* Table region: owns the clipping, the sideways scroll and the bottom corners.
   Same flex split as .table-shell so the footer stays out of the scrollbar. */
.otable-body {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-top: 1px solid var(--line);
  border-bottom-left-radius: var(--radius);
  border-bottom-right-radius: var(--radius);
}
.otable-body > .table-scroll { flex: 1 1 auto; min-height: 0; overflow: auto; }
.otable-body > .table-footer { flex: 0 0 auto; }

/* --- The list fills the window and scrolls inside itself ----------------------
   The whole page used to scroll, which cost three things on a long list: the
   toolbar (search, filters, rows-per-page) scrolled away, the pagination footer
   sat below the fold so "Showing 1-100 of 148" and Next could only be reached by
   scrolling past every row, and the sticky <thead> ended up floating against the
   window with the panel's border and rounded corners gone - a bare row of labels
   with no card around it.
   Instead the shell is pinned to the viewport and only the rows scroll: title,
   actions and the export banner stay put at the top, the toolbar and the column
   headers stay put on the panel, the footer stays pinned to its bottom edge, and
   one gesture inside the table moves through the orders.
   Height comes from a chain, so every link needs min-height: 0 or a flex child
   refuses to shrink below its content and the overflow never engages.
   Gated on both axes: below 768px these tables are stacked cards (a locked
   viewport would trap the page), and under 600px tall there isn't enough room to
   spend on chrome - both fall back to normal page scrolling. */
@media (min-width: 768px) and (min-height: 600px) {
  body:has(.orders-page) .app-frame {
    height: 100vh;
    min-height: 0;
    overflow: hidden;
  }
  body:has(.orders-page) .workspace {
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
  }
  body:has(.orders-page) .page-content {
    flex: 1 1 auto;
    min-height: 0;
    /* Was a grid; a column flex keeps the same 16px rhythm between the banners
       and the panel while letting the panel take the leftover height. */
    display: flex;
    flex-direction: column;
    /* The panel's own bottom edge is the end of the page now, so the 40px that
       used to sit under the last row would just shrink the table. */
    padding-bottom: 14px;
  }
  .orders-page { flex: 1 1 auto; min-height: 0; }
  .orders-page > .otable { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
  .orders-page > .otable > .otable-body {
    flex: 1 1 auto;
    /* A floor, so a page carrying a sync banner AND a trial banner AND an export
       banner still shows rows rather than squeezing the table to nothing. */
    min-height: 220px;
  }

  /* --- Condense the page header once the list is scrolled into ---------------
     With the shell locked, everything above the panel is height the rows don't
     get. Scrolling into the list is the merchant saying they're reading rows, so
     the chrome shrinks to suit: the title drops to a single compact line and the
     export banner loses its icon tile and detail line. It all returns on the way
     back up.
     Measured payoff, so nobody has to guess: the rows gain 24px with no export
     banner and 44px with one. In WHOLE rows that is 0 or 1, because 24px is under
     one 37px row and whether it crosses a boundary depends on where the window
     height happens to land (measured both: 16.03 -> 16.68 rows at one height, and
     16.8 -> 17.5 at another). Do not sell this as more than it is.
     The header is only 54px to begin with - the actions share the title's line -
     so there is less here to reclaim than it looks. The real remaining chunk is
     the toolbar's two rows (~97px); merging those into one when condensed is where
     two reliable rows would come from, at the cost of cramming the controls this
     redesign just spread out. The honest reason to keep this is the calmer header
     while reading, not the row count.
     What does NOT disappear: the header's actions (Sync now, Full sync, Export
     PDFs) and the export's Download ZIP. Collapsing a ready export out of reach
     would trade rows for the one thing on the page worth clicking, so the banner
     condenses rather than hides. app.js owns the class - see bootOrdersCondense. */
  .orders-page.is-condensed { gap: 12px; }

  .orders-page .orders-title,
  .orders-page .orders-subtitle,
  .orders-page .export-banner,
  .orders-page .export-banner-icon { transition: all 0.18s ease; }

  .orders-page.is-condensed .orders-title { font-size: 18px; }
  .orders-page.is-condensed .orders-subtitle {
    /* One line, so max-height:0 collapses it cleanly - height:auto can't animate. */
    max-height: 0;
    margin-top: 0;
    opacity: 0;
    overflow: hidden;
  }

  .orders-page.is-condensed .export-banner {
    /* The icon column is gone, so the copy takes column 1 and the action takes 2 -
       leaving three columns would strand the gap where the tile used to be. */
    grid-template-columns: minmax(0, 1fr) auto auto;
    padding: 7px 14px;
  }
  .orders-page.is-condensed .export-banner-icon,
  .orders-page.is-condensed .export-banner-meta { display: none; }
  /* Overrides grid-column: 3/4 from the full-size banner. */
  .orders-page.is-condensed .export-banner-action { grid-column: 2; }
  .orders-page.is-condensed .export-banner-dismiss { grid-column: 3; }
  /* The progress bar still spans the row while an export builds, so a condensed
     banner keeps saying how far along it is. */
  .orders-page.is-condensed .export-banner .sync-loader { grid-column: 1 / -1; }
}

/* Scroll-linked motion is exactly what this setting is for: keep the layout
   change, drop the animation. */
@media (prefers-reduced-motion: reduce) {
  .orders-page .orders-title,
  .orders-page .orders-subtitle,
  .orders-page .export-banner,
  .orders-page .export-banner-icon { transition: none; }
}

@media (max-width: 720px) {
  /* Stack: the switcher takes its own line, search goes full width, and the
     count drops under the pills instead of squeezing them. */
  .otoolbar-row-top { flex-direction: column; align-items: stretch; }
  .ofilter-search { flex: 1 1 auto; }
  .otoolbar-row-filters { align-items: flex-start; }
  .ofilter-count { width: 100%; }
}

/* Segmented Orders / Draft Orders switcher */
/* Segmented control: a recessed tray with the active view lifted out of it as a
   white card. Choosing a view is a small navigational act, so this reads quieter
   than the filled pill it replaces - which outweighed both the Sync buttons and
   the table beneath it. The accent survives only in the active count.
   Shared by Orders/Draft Orders (2 tabs, with counts) and Vendors (3 tabs, no
   counts), so nothing here may assume a count is present. */
.view-switch {
  display: inline-flex;
  gap: 3px;
  padding: 3px;
  /* A shade under the pages' pastel ground (#edf4f0) so the tray reads as sunken
     and the white active segment as raised out of it. */
  background: #e2eae6;
  border-radius: 10px;
  margin-bottom: 20px;
  /* Hug the tabs' width even inside a flex-column page (e.g. the vendors
     fill layout), so it matches the compact Orders/Draft Orders switcher. */
  align-self: flex-start;
}

.view-switch-tab {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 15px; border-radius: 7px;
  font-size: 13.5px; font-weight: 600; color: var(--muted); text-decoration: none;
  transition: background 0.12s ease, color 0.12s ease;
}

.view-switch-tab:hover { color: var(--text); }
.view-switch-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.view-switch-tab.is-active {
  background: #fff;
  color: var(--text);
  box-shadow: 0 1px 2px rgba(16, 35, 28, .1);
}

/* A tab whose feature isn't on the plan: still visible, links to plans. */
.view-switch-tab.is-locked { color: var(--faint); }
.view-switch-tab.is-locked .trig-lock { position: static; }

.view-switch-count {
  font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 700;
  padding: 1px 7px; border-radius: 999px;
  /* Tinted off the tray rather than a flat grey, so it settles on both the
     inactive tray and the white active segment. */
  background: rgba(16, 35, 28, .07); color: var(--muted);
}

.view-switch-tab.is-active .view-switch-count { background: var(--accent-weak); color: var(--accent); }

/* Draft-order status badge (OPEN reads as accent) */
.badge-info { color: var(--accent); background: var(--accent-weak); border-color: #bfe0d3; }

.draft-synced-note { color: var(--faint); }
.draft-sync-warn { color: var(--amber); }

/* Draft view states */
.empty-state-title { font-size: 15px; font-weight: 700; color: var(--text); margin: 0 0 4px; }
.empty-state-error { color: var(--muted); }
.empty-state-action { margin-top: 12px; }
.draft-syncing-state { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.draft-spinner {
  width: 22px; height: 22px; border-radius: 50%;
  border: 2.5px solid var(--accent-weak); border-top-color: var(--accent);
  animation: draft-spin 0.8s linear infinite;
}
@keyframes draft-spin { to { transform: rotate(360deg); } }

/* Shared line-item thumbnail (Order/Draft detail + HTML invoice) */
.li-cell { display: flex; gap: 12px; align-items: flex-start; }
.li-thumb { flex: none; border-radius: 9px; overflow: hidden; border: 1px solid var(--line); display: grid; place-items: center; background: #fff; }
.li-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.li-thumb-empty { border-style: dashed; background: var(--line-soft, #eef2f0); color: var(--faint, #8ba095); }
.li-custom-tag { display:inline-block; font-size:10px; font-weight:700; color:var(--accent,#147c64); background:var(--accent-weak,#e2f1ea); border-radius:4px; padding:1px 6px; margin-left:6px; text-transform:uppercase; letter-spacing:.03em; vertical-align:middle; }
.draft-row[data-href] { cursor: pointer; }
/* Product image in HTML invoice line-item desc cell */
.inv-li-cell { display: flex; gap: 10px; align-items: flex-start; }
.inv-li-cell .li-thumb { margin-top: 1px; }
/* Multi-page print: keep line-item rows intact and repeat the table header. */
@media print {
  .preview-line-items tr, .odt-items tr { break-inside: avoid; }
  .preview-line-items thead, .odt-items thead { display: table-header-group; }
}

/* Pastel brand background for the Order & Draft Order detail pages (both render
   inside .order-detail-a), matching the dashboard + orders-list treatment:
   soft mint-cream workspace, white cards, single brand-green accent. */
body:has(.order-detail-a) .workspace { background: #edf4f0; }
.order-detail-a {
  --accent: #147c64; --accent-weak: #e2f1ea;
  --blue: #6fa693; --blue-weak: #e7efeb;
  --green: #1f9469; --green-weak: #e2f4ec;
  --amber: #b58a2a; --amber-weak: #f6eede;
  --red: #c25a48; --red-weak: #f7e7e3;
  --text: #10231c; --muted: #566b62; --faint: #8ba095;
  --panel: #ffffff; --panel-subtle: #f2f8f5; --line: #dbe8e2; --line-soft: #e9f0ec;
  --hover: #eef5f1;
  color: #10231c;
}
.order-detail-a .button:hover:not([disabled]) { background: var(--accent); color: #fff; }
.order-detail-a .button-ghost:hover { color: var(--accent); border-color: var(--accent); }

/* Order detail - immersive hero + minimal Documents panel (B1) */
.order-detail-a .odt-hero{ background:linear-gradient(135deg,#0b3b30,#0f4c3d 62%,#147c64); border-radius:20px; padding:26px 28px; color:#f3efe3; position:relative; overflow:hidden; margin-bottom:16px; }
.order-detail-a .odt-hero::after{ content:""; position:absolute; right:-60px; top:-70px; width:230px; height:230px; border-radius:50%; background:radial-gradient(circle,rgba(124,240,198,.26),transparent 70%); pointer-events:none; }
.order-detail-a .odt-hero-in{ position:relative; z-index:1; display:flex; justify-content:space-between; align-items:flex-start; gap:20px; flex-wrap:wrap; }
.order-detail-a .odt-hero-title{ font-size:26px; font-weight:800; letter-spacing:-0.02em; color:#fff; display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.order-detail-a .odt-hero-sub{ color:rgba(243,239,227,.72); margin-top:8px; font-size:13.5px; }
.order-detail-a .odt-hero-total{ text-align:right; }
.order-detail-a .odt-hero-total small{ display:block; font-size:11px; letter-spacing:.05em; text-transform:uppercase; color:rgba(243,239,227,.6); }
.order-detail-a .odt-hero-total b{ font-size:22px; font-weight:800; color:#fff; }
.order-detail-a .odt-pill{ display:inline-flex; align-items:center; gap:6px; font-size:11px; font-weight:700; padding:4px 11px; border-radius:999px; vertical-align:middle; }
.order-detail-a .odt-pill-dot{ width:6px; height:6px; border-radius:50%; background:currentColor; }
.order-detail-a .odt-pill-solid{ color:#082e25; background:#a9f7db; }
.order-detail-a .odt-pill-ghost{ color:#a9f7db; background:rgba(124,240,198,.15); }
.order-detail-a .odt-pill-warn{ color:#f6d99a; background:rgba(181,138,42,.30); }

.order-detail-a .odt-docpanel{ background:#fff; border:1px solid var(--line); border-radius:var(--radius); overflow:hidden; }
/* ...but an open send menu has to escape the panel, or it gets clipped. */
.order-detail-a .odt-docpanel:has(.row-menu[open]){ overflow:visible; }
.order-detail-a .odt-docpanel-head{ padding:16px 22px 2px; }
.order-detail-a .odt-docpanel-head .odt-doc-sub{ color:var(--muted); font-size:12.5px; margin-top:2px; }
.order-detail-a .odt-docrow{ display:flex; align-items:center; gap:15px; padding:15px 20px; border-top:1px solid var(--line-soft); }
.order-detail-a .odt-docrow:first-of-type{ border-top:0; }
.order-detail-a .odt-docrow-ico{ flex:none; width:40px; height:40px; display:grid; place-items:center; border-radius:11px; background:var(--accent-weak); color:var(--accent); }
.order-detail-a .odt-docrow-ico svg{ width:19px; height:19px; }
.order-detail-a .odt-docrow-info{ flex:1; min-width:0; }
.order-detail-a .odt-docrow-info strong{ display:block; font-size:15px; font-weight:750; }
.order-detail-a .odt-docrow-info span{ color:var(--muted); font-size:12.5px; }
.order-detail-a .odt-docrow-acts{ display:flex; align-items:center; gap:6px; }
.order-detail-a .odt-docrow.is-locked{ opacity:.72; }
.order-detail-a .odt-iconbtn{ width:36px; height:36px; display:grid; place-items:center; border-radius:9px; border:1px solid var(--line); background:#fff; color:var(--muted); cursor:pointer; }
.order-detail-a .odt-iconbtn:hover{ color:var(--accent); border-color:var(--accent); background:var(--accent-weak); }
.order-detail-a .odt-iconbtn svg{ width:17px; height:17px; }

/* Same trick as .odt-wa-form: the form is only there to carry the POST, so it
   must not become a flex item between the icon buttons it sits among. */
.order-detail-a .odt-docrow-send{ display:contents; }
.order-detail-a .odt-doc-note{ color:var(--muted); font-size:12.5px; padding:0 20px 14px; }
.order-detail-a .odt-doc-note + .odt-doc-note{ margin-top:-8px; }

.order-detail-a .odt-docrow .inline-form{ margin:0; }

/* Freeze the back button + hero at the top of the order/draft detail pages
   while the rest scrolls. The window is the scroll container (sidebar is
   already sticky), so sticky top:0 pins to the viewport. The pastel background
   matches the workspace so content scrolls cleanly underneath. */
.order-detail-a .odt-topfreeze {
  position: sticky;
  top: 0;
  z-index: 30;
  background: #edf4f0;
  padding: 6px 0 4px;
}

/* ============ Vendor Commissions ============ */
body:has(.vendors-page) .workspace { background: #edf4f0; }
.vendors-page {
  min-width: 0;
  --accent: #147c64; --accent-weak: #e2f1ea;
  --green: #1f9469; --green-weak: #e2f4ec;
  --amber: #b58a2a; --amber-weak: #f6eede;
  --red: #c25a48; --red-weak: #f7e7e3;
  --text: #10231c; --muted: #566b62; --faint: #8ba095;
  --panel: #ffffff; --line: #dbe8e2; --line-soft: #e9f0ec; --hover: #eef5f1;
  color: #10231c;
}

/* Fill the viewport: the vendor table reaches the bottom of the screen and
   scrolls internally, while the switcher, headline cards and filters stay put.
   The list and summary views end in a .table-shell; the settings view is a
   form and keeps its natural height (no .is-fill). Desktop only, like #34. */
@media (min-width: 901px) {
  /* Cap the frame to the viewport (not just min-height) so the trailing table
     is forced to scroll inside instead of growing the whole page. The fixed
     blocks above it - switcher, cards, filters - sit outside the scroll region. */
  body:has(.vendors-page.is-fill) .app-frame {
    height: 100vh;
  }

  body:has(.vendors-page.is-fill) .workspace {
    display: flex;
    flex-direction: column;
    min-height: 0;
  }

  body:has(.vendors-page.is-fill) .page-content {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
  }

  .vendors-page.is-fill {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    /* Consistent breathing room between the switcher, totals, filter card and
       table - the flex gap is the single source of vertical spacing here. */
    gap: 18px;
  }
  /* Drop the block-flow margins so they don't double the flex gap above. */
  .vendors-page.is-fill > .view-switch,
  .vendors-page.is-fill > .vc-cards { margin-bottom: 0; }

  /* The trailing table is the scroll region; everything above it stays fixed. */
  .vendors-page.is-fill > .table-shell {
    flex: 1 1 auto;
    min-height: 0;
  }

  /* Keep the column headers visible while the rows scroll under them. The
     upward shadow covers the seam a sticky th leaves on fractional device
     pixels, same as the orders table. */
  .vendors-page.is-fill > .table-shell thead th {
    position: sticky;
    top: 0;
    background: var(--panel);
    box-shadow: 0 -3px 0 var(--panel);
    z-index: 1;
  }
}
/* --- Orders + Draft Orders: keep the column headers in view ---------------
   .data-table th is already position:sticky, but it never had anything to stick
   to: .table-shell sets overflow:auto with no height limit, so the shell grows
   to fit the table and the PAGE scrolls instead - taking the header with it.
   Capping the frame to the viewport makes the shell itself the scroll region,
   which is what the sticky header needs. Same treatment the vendors list uses.
   Desktop only: on a phone the table renders as cards and there's no header. */
@media (min-width: 901px) {
  body:has(.orders-page) .app-frame { height: 100vh; }

  body:has(.orders-page) .workspace,
  body:has(.orders-page) .page-content {
    display: flex;
    flex-direction: column;
    min-height: 0;
  }

  body:has(.orders-page) .page-content { flex: 1 1 auto; }

  .orders-page { flex: 1 1 auto; min-height: 0; }

  /* The trailing table - orders or draft orders, whichever view is showing. */
  .orders-page > .table-shell {
    flex: 1 1 auto;
    min-height: 0;
  }

  /* Opaque, or the rows would show through as they scroll underneath. The
     upward shadow paints the sliver above the cell in the header's own colour:
     a sticky th lands on a fractional device pixel at most zoom levels, and the
     row passing beneath shows through the seam without it. */
  .orders-page > .table-shell thead th {
    position: sticky;
    top: 0;
    background: var(--panel);
    box-shadow: 0 -3px 0 var(--panel);
    z-index: 1;
  }

  /* The Order column is pinned left as well, so its header is the corner cell
     and has to sit above both axes. The rule that normally does this
     (.orders-table th:first-child) is less specific than the one above and
     would lose its z-index to it, letting body cells scroll over the corner. */
  .orders-page > .table-shell thead th:first-child {
    left: 0;
    z-index: 3;
    /* Both jobs at once: the column's right-hand divider, and the seam cover. */
    box-shadow: 1px 0 0 var(--line-soft), 0 -3px 0 var(--panel);
  }
}

/* Detail views (vendor commission) carry no .is-fill, so they miss the flex
   gap the list view uses for spacing. Stack them with the same 18px rhythm so
   the cards and table don't butt against each other. */
.vendors-page:not(.is-fill) {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.vendors-page:not(.is-fill) > .orders-head,
.vendors-page:not(.is-fill) > .view-switch,
.vendors-page:not(.is-fill) > .vc-cards { margin-bottom: 0; }

.vendors-page .section-card,
.vendors-page .table-shell { background: #fff; border-color: var(--line); }
.vendors-page .button { border-color: var(--accent); color: var(--accent); background: #fff; }
.vendors-page .button:hover { background: var(--accent); color: #fff; }
.vendors-page .button-ghost:hover { color: var(--accent); border-color: var(--accent); }

/* The four-tile grid. Still used by the vendors list, the payouts list and a
   payout's detail page - the per-vendor breakdown is the ONE page that moved to
   the strip below, and deleting these with it left the other three rendering
   label and value as bare adjacent spans ("TOTAL VENDORS8"). */
.vc-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 18px; }
.vc-card { background: #fff; border: 1px solid var(--line); border-radius: var(--radius); padding: 16px 18px; display: flex; flex-direction: column; gap: 6px; }
.vc-val { font-size: 22px; font-weight: 800; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
.vc-val.is-accent { color: var(--accent); }

/* One strip rather than six equal tiles: five of the six usually read $0.00, and
   at tile size they carried the same weight as the total the page is about. */
.vc-summary {
  display: flex;
  /* flex-end, not baseline: the lead stacks its label OVER its value, so its
     first baseline is the 11px label - and baseline alignment hung the other
     figures off THAT, level with the word "Sales" rather than with the total. */
  align-items: flex-end;
  gap: 26px;
  flex-wrap: wrap;
  margin-bottom: 18px;
  padding: 15px 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
}
.vc-summary-lead {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding-right: 26px;
  border-right: 1px solid var(--line-soft);
}
.vc-summary-value { font-size: 28px; font-weight: 800; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.vc-summary-rest { display: flex; align-items: baseline; gap: 26px; flex-wrap: wrap; padding-bottom: 5px; }
.vc-summary-figure { font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; }
.vc-summary-figure.is-accent { color: var(--accent); }
.vc-summary-figure.is-none { color: var(--faint); font-weight: 600; }
.vc-lbl { font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--faint); }

/* Said once, above the table, instead of on every row. */
.vc-no-rule {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 18px;
  padding: 11px 16px;
  border: 1px solid var(--amber-weak);
  border-radius: var(--radius);
  background: var(--amber-weak);
  color: var(--amber);
}
.vc-no-rule p { margin: 0; flex-grow: 1; font-size: 13px; color: var(--text); }
.vc-no-rule .button-ghost { white-space: nowrap; }
.vc-no-rule.is-pending { border-color: var(--accent-weak); background: var(--accent-weak); }
.vc-pending-spinner { flex: none; width: 14px; height: 14px; border: 2px solid var(--accent); border-right-color: transparent; border-radius: 50%; animation: vc-pending-spin .8s linear infinite; }
@keyframes vc-pending-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .vc-pending-spinner { animation: none; } }
@media (max-width: 820px) { .vc-cards { grid-template-columns: 1fr 1fr; } }

.vc-src { display: inline-flex; font-size: 11px; font-weight: 650; padding: 2px 8px; border-radius: 6px; background: #eef2f0; color: var(--muted); vertical-align: middle; }
.vc-src.is-shopify { background: var(--accent-weak); color: var(--accent); }
.vc-rule { font-size: 12.5px; }
.vc-rule-tag { font-size: 10px; font-weight: 700; color: var(--faint); text-transform: uppercase; letter-spacing: .03em; }
.vc-rule-tag.is-custom { color: var(--accent); }
/* The add-vendor panel. Its handle now lives in the toolbar, so the disclosure's
   own summary is a quiet fallback for a browser without the script - not a second
   green call to action sitting under the filters. */
/* Closed, it occupies nothing. It used to keep its rule and padding whatever its
   state, which was invisible while the summary was visible and became a dead
   bordered band under the filters once the summary moved to the toolbar. */
.vc-addvendor { margin: 0; border-top: 0; padding: 0; }
.vc-addvendor[open] { margin-top: 16px; border-top: 1px solid var(--line-soft); padding-top: 14px; }
/* Hidden, because the toolbar button opens this panel. A <noscript> block in the
   page puts it back: that button needs script, so without it the summary is the
   only way in. */
.vc-addvendor > summary { display: none; }

/* No rule configured. Quiet, because it is the resting state of every vendor on
   a shop that has not set any up - not a warning, and not shouted in caps. */
.vc-rule-none { color: var(--faint); }

/* The vendor breakdown carries six figures, not four - let the cards reflow
   rather than squashing tabular numerals. */
.vc-cards { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }

/* Date-range preset chips. */
/* The breakdown's filter row.
   .orders-filters is a five-column grid built for the orders page; here the same
   controls plus five preset chips stacked into a block four rows deep. This makes
   it one wrapping line, with labels beside their controls rather than above. It
   overrides only layout - every input, name and data- hook is the shared form's. */
.vc-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; }
.vc-filters .field { display: flex; flex-direction: row; align-items: center; gap: 8px; margin: 0; }
.vc-filters .field > label {
  margin: 0;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
}
.vc-filters select { min-height: 34px; }
.vc-filters .date-range-native { margin: 0; }
/* The chips sit against the date pair they act on, and take the rest of the row. */
.vc-filters .range-presets { margin: 0; margin-right: auto; }

.range-presets { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.chip-button {
  font: inherit;
  font-size: 11.5px;
  font-weight: 650;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: #fff;
  color: var(--muted);
  cursor: pointer;
}
.chip-button:hover { color: var(--accent); border-color: var(--accent); }
.chip-button.is-active { color: var(--accent); border-color: var(--accent); background: var(--accent-weak); }

/* Expanding an order in place.
   The detail row is `hidden` until its toggle opens it, so four orders draw as
   four rows. It used to be rendered open-but-collapsed under every order, which
   is what doubled the table before anyone clicked anything. */
.vc-toggle-head { width: 34px; }
.vc-toggle-cell { width: 34px; padding-right: 0 !important; }
.vc-toggle {
  display: flex;
  padding: 4px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: transform 0.15s ease, color 0.15s ease;
}
.vc-toggle:hover { color: var(--text); background: var(--hover); }
.vc-toggle[aria-expanded="true"] { transform: rotate(90deg); color: var(--text); }
.vc-order-row.is-open > td { background: var(--panel-subtle); }
.vc-order-date { font-size: 12px; color: var(--faint); margin-top: 1px; }
.vc-sales { font-weight: 650; font-variant-numeric: tabular-nums; }
.vc-none { color: var(--faint); }
.vc-refunded { color: var(--red); }

.vc-detail-row > td { padding: 0 20px 18px 48px !important; background: var(--panel-subtle); border-top: 0; }
/* stretch, not start: with one line item the left card is short, and `start`
   left a band of the row's grey showing beside the taller money card, which read
   as a gap in the layout rather than as two panels. */
.vc-detail { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: 20px; align-items: stretch; }
.vc-detail-lines { border: 1px solid var(--line); border-radius: var(--radius); background: #fff; overflow: hidden; }
.vc-detail-money {
  margin: 0;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  font-size: 13px;
}
.vc-detail-money > div { display: flex; justify-content: space-between; gap: 12px; padding: 4px 0; }
.vc-detail-money dt { color: var(--muted); }
.vc-detail-money dd { margin: 0; font-variant-numeric: tabular-nums; }
.vc-detail-money .vc-neg { color: var(--red); }
.vc-detail-money .vc-detail-rule { border-top: 1px solid var(--line-soft); margin-top: 4px; padding-top: 8px; }
.vc-detail-money .vc-detail-total { font-weight: 700; }
@media (max-width: 860px) {
  .vc-detail { grid-template-columns: minmax(0, 1fr); }
  .vc-detail-row > td { padding-left: 20px !important; }
}

.vc-lines-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.vc-lines-table th {
  text-align: left;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--faint);
  padding: 4px 8px;
  border-bottom: 1px solid var(--line-soft);
}
.vc-lines-table td { padding: 6px 8px; border-bottom: 1px solid var(--line-soft); }
.vc-lines-table tr:last-child td { border-bottom: 0; }
.vc-lines-table .align-right { text-align: right; font-variant-numeric: tabular-nums; }
@media (max-width: 720px) {
  .vc-lines-table { display: block; overflow-x: auto; }
}

/* --- Vendor payouts --------------------------------------------------- */

/* The drift notice: this payout's figures no longer match the ledger. Amber
   rather than the accent-weak default, because it is a discrepancy to act on,
   not a plan note. */
.window-note.is-warn { background: #fdf3e3; color: #8a5a12; }
.vc-drift { margin: 8px 0 0; padding-left: 18px; }
.vc-drift li { margin-top: 3px; }

/* Settlement panel: the forms stack rather than sitting in the filter row's
   grid, so "approve" and "mark as paid" read as sequential steps. */
.vc-settlement { margin-top: 16px; display: flex; flex-direction: column; gap: 14px; align-items: flex-start; }
.vc-settlement .orders-filters { margin: 0; width: 100%; }
.vc-settlement .button-inline { flex-wrap: wrap; gap: 8px; }

/* Illustrated empty state. Deliberately not .empty-state, which is defined
   twice in this file (the later grid rule wins and strips the padding). */
.empty-rich {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: 14px;
  padding: 52px 32px;
}

.empty-rich-compact { padding: 38px 28px; }

/* Wash fades into the panel, so it stops short of the rounded bottom corners. */
.empty-rich-tinted {
  background: linear-gradient(180deg, var(--accent-weak), transparent 72%);
  border-radius: 7px 7px 0 0;
}

.empty-rich-art { display: block; }
.empty-rich-copy { display: grid; gap: 6px; justify-items: center; }
.empty-rich-title { font-size: 15px; font-weight: 700; margin: 0; color: var(--text); }
.empty-rich-body { margin: 0; color: var(--muted); max-width: 46ch; }

.empty-rich-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  margin-top: 4px;
}

.empty-rich-actions form { margin: 0; }

/* align-items:start matters: .field is itself a grid, so a stretched column
   spreads the slack across its rows and the control drifts below its label.
   The "Effective from" hint makes that column tallest, which staggered the
   whole row. */
/* One flex column drives the whole form's vertical rhythm, so the fields grid,
   checkbox, advanced disclosure, example callout and buttons all sit an even
   16px apart instead of relying on per-block margins that drift out of sync. */
.commission-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* The enhanced <select>: a button that opens a listbox whose options can carry a
   line of description. The native select is still there, driving it and
   submitting; only its box is hidden. Geometry matches .field select exactly, so
   an enhanced control and a plain one sit on the same line without drifting. */
.rich-select { position: relative; }
.rich-select-native { display: none; }
.rich-select-button {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 36px;
  padding: 7px 9px;
  font: inherit;
  text-align: left;
  color: var(--text);
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.rich-select-button:hover { border-color: var(--faint); }
.rich-select-value { flex-grow: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rich-select-chevron { flex-shrink: 0; color: var(--muted); transition: transform 0.15s ease; }
.rich-select-button[aria-expanded="true"] { border-color: var(--accent); }
.rich-select-button[aria-expanded="true"] .rich-select-chevron { transform: rotate(180deg); }

.rich-select-list {
  position: absolute;
  z-index: 40;
  top: calc(100% + 4px);
  left: 0;
  /* Wider than the control when the control is narrow, because the descriptions
     need a readable measure - but never wider than the viewport allows. */
  min-width: 100%;
  max-width: min(360px, calc(100vw - 32px));
  margin: 0;
  padding: 4px;
  list-style: none;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(16, 35, 28, 0.10);
}
.rich-select-list[hidden] { display: none; }
.rich-select-option {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.rich-select-option:hover,
.rich-select-option:focus-visible { background: var(--hover); outline: none; }
.rich-select-option:focus-visible { box-shadow: inset 0 0 0 2px var(--accent); }
.rich-select-option.is-selected { background: var(--accent-weak); }
.rich-select-option-title { font-size: 13.5px; font-weight: 600; }
.rich-select-option.is-selected .rich-select-option-title { color: var(--accent); }
.rich-select-option-desc { font-size: 12px; line-height: 1.4; color: var(--muted); }

.commission-form .cf-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  align-items: start;
  gap: 14px;
}

/* Native date inputs render a pixel taller than text inputs at the same
   min-height, which shows up next to them in the same row. */
.commission-form .field input[type="date"] { height: 36px; }
.cf-hint { color: var(--faint); font-size: 11.5px; margin-top: 5px; }
.cf-advanced summary { font-weight: 700; font-size: 12.5px; color: var(--accent); cursor: pointer; }
.cf-advanced[open] summary { margin-bottom: 10px; }
.cf-advanced .checkbox-row + .checkbox-row { margin-top: 8px; }
.cf-preview { background: linear-gradient(160deg, #0b3b30, #0f4c3d); border-radius: 12px; padding: 16px 18px; color: #f3efe3; max-width: 380px; }
.cf-preview .eyebrow { color: #a9f7db; margin-bottom: 10px; }
.cf-prow { display: flex; justify-content: space-between; gap: 16px; padding: 5px 0; font-size: 13px; color: rgba(243,239,227,.85); font-variant-numeric: tabular-nums; }
.cf-prow.cf-sub { border-top: 1px solid rgba(124,240,198,.18); margin-top: 5px; padding-top: 9px; }
.cf-prow.cf-grand { border-top: 1px solid rgba(124,240,198,.3); margin-top: 5px; padding-top: 10px; font-size: 15px; font-weight: 800; color: #fff; }
.cf-warn { color: var(--amber); font-size: 12.5px; font-weight: 650; }

/* Live-filter results: dim briefly while the filtered page is fetched. */
.is-filtering { opacity: 0.55; transition: opacity 0.12s ease; }

/* ---- First-run orders import state ---- */
.orders-syncing-state { display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; padding: 44px 24px; }
.orders-syncing-spinner {
  width: 26px; height: 26px; border-radius: 50%;
  border: 3px solid var(--accent-weak); border-top-color: var(--accent);
  animation: orders-spin 0.9s linear infinite;
}
@keyframes orders-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .orders-syncing-spinner { animation: none; } }

/* ---- In-app coach-mark tour ---- */
body.tour-active { overflow: hidden; }
.tour-blocker { position: fixed; inset: 0; z-index: 4000; background: transparent; }
.tour-blocker.is-solid { background: rgba(8, 20, 16, 0.6); }
.tour-spot {
  position: fixed; z-index: 4001; border-radius: 12px; pointer-events: none;
  box-shadow: 0 0 0 3px var(--accent), 0 0 0 9999px rgba(8, 20, 16, 0.6);
  transition: top 0.25s ease, left 0.25s ease, width 0.25s ease, height 0.25s ease;
}
@media (prefers-reduced-motion: reduce) { .tour-spot { transition: none; } }
.tour-pop {
  position: fixed; z-index: 4002; width: min(320px, calc(100vw - 28px));
  background: var(--panel, #fff); color: var(--text, #10231c);
  border: 1px solid var(--line, #dbe8e2); border-radius: 14px;
  box-shadow: 0 24px 60px -24px rgba(8, 20, 16, 0.55); padding: 18px 18px 15px;
}
.tour-pop-count { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); }
.tour-pop-title { margin: 8px 0 0; font-size: 17px; font-weight: 800; letter-spacing: -0.01em; }
.tour-pop-body { margin: 7px 0 0; font-size: 13.5px; line-height: 1.5; color: var(--muted, #566b62); }
.tour-pop-actions { display: flex; align-items: center; gap: 8px; margin-top: 16px; }
.tour-pop-actions .tour-sp { flex: 1; }
.tour-btn { appearance: none; font-family: inherit; font-size: 13px; font-weight: 700; border-radius: 40px; padding: 8px 16px; cursor: pointer; border: 1.5px solid transparent; }
.tour-btn.tour-next { background: var(--accent); color: #fff; }
.tour-btn.tour-next:hover { background: #0f6650; }
.tour-btn.tour-back { background: transparent; border-color: var(--line, #dbe8e2); color: var(--text, #10231c); }
.tour-btn.tour-back:hover { border-color: var(--accent); color: var(--accent); }
.tour-btn.tour-skip { background: transparent; color: var(--faint, #8ba095); padding-left: 4px; padding-right: 4px; }
.tour-btn.tour-skip:hover { color: var(--muted, #566b62); }

/* ============================================================================
   RESPONSIVE ADDITIONS (see the breakpoint legend at the top of this file).
   Editor stacking + touch reorder, dense-row wrapping, popover clamps, and
   invoice-preview reflow. Desktop (>=1024px) is unaffected.
   ============================================================================ */

/* ---- Invoice editor: Edit/Preview toggle (shown only on tablet/phone) ---- */
.editor-viewtab {
  display: none;
  margin-left: auto;
  gap: 2px;
  padding: 3px;
  background: var(--panel-subtle);
  border: 1px solid var(--line);
  border-radius: 999px;
}

.editor-viewtab-btn {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--muted);
  font-weight: 650;
  font-size: 13px;
  padding: 6px 16px;
  border-radius: 999px;
  cursor: pointer;
}

.editor-viewtab-btn.is-active {
  background: var(--accent);
  color: #fff;
}

/* ---- Toolbars wrap instead of overflowing ---- */
.toolbar {
  flex-wrap: wrap;
}

/* ---- Floating row menu never exceeds the viewport ---- */
.row-menu-pop {
  max-width: calc(100vw - 24px);
}

/* ---- lg: stack the two-pane template editors and reveal the view toggle ---- */
@media (max-width: 1023px) {
  .editor-viewtab {
    display: inline-flex;
  }

  /* Both editors: the invoice preview and the packing sheet are the same pane
     in this layout, so the toggle has to know about both. */
  .split-layout[data-editor-view="edit"] .template-preview,
  .split-layout[data-editor-view="edit"] .pack-editor-preview {
    display: none;
  }

  .split-layout[data-editor-view="preview"] .invoice-editor-shell {
    display: none;
  }
}

/* ---- md: dense editor rows wrap so their controls stop overflowing ---- */
@media (max-width: 767px) {
  .column-item,
  .order-item,
  .cf-item,
  .cs-item .cs-top {
    flex-wrap: wrap;
  }

  .column-item .col-label {
    flex: 1 1 100%;
  }

  .style-tool-grid {
    grid-template-columns: 1fr;
  }
}

/* ---- sm: invoice preview sheet reflows for phones ---- */
@media (max-width: 639px) {
  .print-sheet {
    padding: 16px;
  }

  /* Let a wide line-item table scroll within the sheet rather than bleed off. */
  .template-preview {
    overflow-x: auto;
  }

  .preview-header {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }

  /* Order-detail invoice: Billed-to and Invoice-meta stack instead of squeezing. */
  .order-detail-a .odt-doc-head {
    flex-wrap: wrap;
  }
}

/* Invoice line-item columns need horizontal breathing room. Cells have no
   inline padding by default, so a right-aligned Qty butts against a
   right-aligned Rate ("3$32.00"). Add a gap before every column after the
   first; outer edges stay flush with the rest of the sheet. Placed last (and
   scoped via .template-preview) so it wins over the per-density / per-template
   padding shorthands that would otherwise reset padding-left to 0. */
.template-preview .preview-line-items th:not(:first-child),
.template-preview .preview-line-items td:not(:first-child) {
  padding-left: 16px;
}

/* --- Custom sending domain (Delivery page) -------------------------------- */
.plan-pill {
  flex: none;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--green);
  background: var(--green-weak);
  border-radius: 20px;
  padding: 3px 10px;
}

.dns-records {
  display: grid;
  gap: 10px;
  margin: 12px 0;
}

.dns-row {
  display: grid;
  grid-template-columns: 130px 1fr auto;
  align-items: center;
  gap: 8px 14px;
  padding: 10px 12px;
  background: var(--panel-subtle);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}

.dns-type {
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.dns-kv {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}

.dns-label {
  font-size: 11px;
  color: var(--muted);
  flex: none;
}

.dns-kv code {
  font-size: 12px;
  word-break: break-all;
  padding: 2px 6px;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.04);
  transition: background 0.15s ease;
}

.dns-kv code.is-copied {
  background: var(--green-weak);
  color: var(--green);
}

.dns-check {
  grid-column: 1 / -1;
  font-size: 11px;
  color: var(--muted);
}

.dns-check.is-ok {
  color: var(--green);
  font-weight: 600;
}

@media (min-width: 620px) {
  .dns-row {
    grid-template-columns: 130px 1fr 1fr auto;
  }
  .dns-check {
    grid-column: auto;
    justify-self: end;
  }
}

/* --- Sending-domain verification method picker ---------------------------- */
/* Two cards rather than a select: the choice is a real trade (deliverability
   vs. needing DNS access), so both options and their one-line consequence stay
   on screen. Radio-styled because exactly one applies. */
.sd-methods {
  display: grid;
  gap: 8px;
  grid-template-columns: 1fr;
}

.sd-method {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  text-align: left;
  font: inherit;
  cursor: pointer;
  padding: 11px 13px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel);
  transition: border-color 0.15s ease, background 0.15s ease;
}

.sd-method:hover { border-color: var(--muted); }
.sd-method.is-current { border-color: var(--accent); background: var(--accent-weak); }
.sd-method:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.sd-method-radio {
  flex: none;
  width: 15px;
  height: 15px;
  margin-top: 2px;
  border-radius: 50%;
  border: 1.5px solid var(--line-strong, var(--muted));
  background: #fff;
  position: relative;
}

.sd-method.is-current .sd-method-radio { border-color: var(--accent); }
.sd-method.is-current .sd-method-radio::after {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: var(--accent);
}

.sd-method-txt { display: grid; gap: 2px; min-width: 0; }
.sd-method-txt b { font-size: 13px; font-weight: 650; }
.sd-method-txt small { font-size: 11.5px; color: var(--muted); }

/* A class with a display wins over the UA sheet's [hidden] rule (same reason
   as .cn-docpanel above), so the unselected panel needs this to stay hidden. */
.sd-panel { display: grid; gap: 13px; }
.sd-panel[hidden] { display: none; }

.sd-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }

/* Which method proved an already-configured sender, next to its status pill. */
.sd-method-tag {
  font-size: 10.5px;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0.02em;
}

.sd-optional { display: grid; gap: 10px; justify-items: start; }

.sd-optional-toggle {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--accent);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

.sd-optional-ico { display: inline-flex; }
/* The plus stroke drops out when open, turning the icon into a minus. */
.sd-optional-toggle[aria-expanded="true"] .sd-optional-plus { display: none; }
.sd-optional-body { width: 100%; }
.sd-optional-body[hidden] { display: none; }

.sd-caveat { border-left: 2px solid var(--line); padding-left: 10px; }

@media (min-width: 620px) {
  .sd-methods { grid-template-columns: 1fr 1fr; }
}

/* --- Google Drive archiving (Delivery page) ------------------------------- */
.drive-badge {
  font-size: 10.5px;
  font-weight: 700;
  color: var(--green);
  background: var(--green-weak);
  border-radius: 20px;
  padding: 2px 9px;
}

[data-drive-picker-error] {
  display: block;
  margin-top: 8px;
  color: var(--red);
}

/* Save for the /delivery anchor form. This page has no sticky save bar - it was
   removed deliberately - so the button sits at the end of the content and needs
   its own separation from the last channel pane above it. */
.dlv-save {
  justify-content: flex-end;
  margin: 24px 0 8px;
}

/* Bulk invoice export: the dialog and its progress banner. The banner reuses the
   sync banner's loader classes so it inherits that animation without a second
   copy of it, but keeps its own element - an export and a sync can be running at
   the same time and neither may hide the other.

   It used to be a --panel-subtle slab holding one line of text and a far-right
   button: on the Orders ground that background is barely a shade off the page,
   so the only actionable thing on the screen read as the quietest. It's now a
   white result card - status rail down the left edge, icon tile, a title line
   over a detail line, and a filled Download - with the rail, tile and title
   switched off data-status so the poller only has to rewrite that attribute. */
.export-banner {
  display: grid;
  /* icon | copy | action | close. The close column is always present even when
     hidden, because `auto` collapses to zero width and that is cheaper than
     re-templating the grid per state. */
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 4px 14px;
  margin: 0 0 18px;
  padding: 13px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
  /* Rail as an inset shadow rather than a border-left: no layout shift, and the
     colour can change per state without moving the copy. */
  box-shadow: inset 3px 0 0 var(--accent), 0 1px 2px rgba(16, 35, 28, 0.04);
  font-size: 13px;
}
.export-banner[data-status="queued"],
.export-banner[data-status="running"] { box-shadow: inset 3px 0 0 var(--faint), 0 1px 2px rgba(16, 35, 28, 0.04); }
/* The old rule here reached for --amber-line and --amber-bg, which are defined
   nowhere in this stylesheet - so a failed export silently kept the neutral
   panel it was trying to tint. These are the warn tokens the rest of the app
   uses (see .op2-ic.warn and the warn note at .window-note). */
.export-banner[data-status="failed"],
.export-banner[data-status="too_large"] {
  border-color: color-mix(in srgb, var(--amber) 25%, transparent);
  box-shadow: inset 3px 0 0 var(--amber), 0 1px 2px rgba(16, 35, 28, 0.04);
}

.export-banner-icon {
  width: 34px;
  height: 34px;
  border-radius: 8px;
  display: grid;
  place-items: center;
  background: var(--accent-weak);
  color: var(--accent);
}
.export-banner[data-status="queued"] .export-banner-icon,
.export-banner[data-status="running"] .export-banner-icon { background: var(--line-soft); color: var(--muted); }
.export-banner[data-status="failed"] .export-banner-icon,
.export-banner[data-status="too_large"] .export-banner-icon { background: var(--amber-weak); color: var(--amber); }

/* One glyph per state, chosen by the status attribute. The title is text, so it
   is swapped in the markup instead - see the note in orders.erb. */
.export-banner .exb-ico { display: none; }
.export-banner[data-status="ready"] .exb-ico-ready,
.export-banner[data-status="queued"] .exb-ico-working,
.export-banner[data-status="running"] .exb-ico-working,
.export-banner[data-status="failed"] .exb-ico-failed,
.export-banner[data-status="too_large"] .exb-ico-failed { display: block; }

.export-banner[data-status="running"] .exb-ico-working { animation: export-spin 1s linear infinite; }
@keyframes export-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .export-banner[data-status="running"] .exb-ico-working { animation: none; }
}

.export-banner-copy { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.export-banner-title { font-size: 13.5px; font-weight: 650; letter-spacing: -0.005em; color: var(--text); }
.export-banner-meta { font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
.export-banner[data-status="failed"] .export-banner-meta,
.export-banner[data-status="too_large"] .export-banner-meta { color: var(--amber); }

/* Progress spans the full width on its own row - the copy keeps column 2 so the
   bar never squeezes the sentence. */
.export-banner .sync-loader { grid-column: 1 / -1; margin-top: 4px; }
.export-banner[data-status="running"] .sync-loader-fill::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);
  transform: translateX(-100%);
  animation: sync-shimmer 1.4s linear infinite;
}

.export-banner-action { grid-column: 3; }
.export-banner-action svg { opacity: 0.88; }

/* Close.

   display:none rather than visibility/opacity while the export is still building:
   it takes the button out of the accessibility tree as well as the layout, so a
   merchant tabbing through a running banner cannot reach a control that would be
   refused server-side anyway (dismiss_invoice_export ignores queued and running). */
.export-banner-dismiss { grid-column: 4; display: flex; }
.export-banner[data-status="queued"] .export-banner-dismiss,
.export-banner[data-status="running"] .export-banner-dismiss { display: none; }
.export-banner-close {
  color: var(--muted);
  /* Quieter than the download beside it - this is the way out, not the point of
     the banner. */
  opacity: 0.7;
}
.export-banner-close:hover { opacity: 1; color: var(--text); }

@media (max-width: 600px) {
  /* The close keeps its own column on the top row so it stays reachable at the
     corner; only the download drops to a full-width row of its own. */
  .export-banner { grid-template-columns: auto minmax(0, 1fr) auto; }
  .export-banner-dismiss { grid-column: 3; }
  .export-banner-action { grid-column: 1 / -1; width: 100%; }
}

/* The export form's first row: date and document side by side.

   Both are one control wide and every export needs both, so stacking them only
   pushed Conditions and the buttons below the fold. Collapses to one column on a
   narrow dialog, where two date inputs plus a select in a row cannot fit. */
.export-primary-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.export-row-hint { margin-top: -6px; }
@media (max-width: 560px) {
  .export-primary-row { grid-template-columns: minmax(0, 1fr); }
}

/* The Conditions summary carries a count and a Clear.

   The marker stays on the summary, so the row is: triangle, label, badge, then
   Clear pushed to the end. Both the badge and the button are written by JS and
   start hidden, so a no-JS dialog shows exactly what it showed before. */
/* .schedule-advanced carries margin-top: 18px for the schedule wizard, where it
   follows prose. Here it follows a grid whose own gap is already 16, so the two
   stacked into a 34px band of nothing between the hint and Conditions. */
.export-conditions.schedule-advanced { margin-top: 0; }
.export-conditions > summary { display: flex; align-items: center; gap: 9px; }
.export-conditions-label { font-weight: 600; }
.export-conditions-spacer { flex-grow: 1; }
.export-conditions-count {
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--accent-weak);
  font-size: 11.5px;
  font-weight: 650;
  color: var(--accent);
}
.export-conditions-clear { padding: 4px 11px; font-size: 12.5px; }
/* `hidden` sets display:none at the UA level, which ANY later display rule
   beats - and .button-ghost sets inline-flex, so the Clear button rendered on an
   empty Conditions section until this existed. Same trap for the confirm row
   below. Both start hidden and are revealed by JS, so getting this wrong shows
   the merchant a control that does nothing yet. */
.export-conditions-count[hidden],
.export-conditions-clear[hidden] { display: none; }

/* The progress view that replaces the form while an export builds. */
.export-progress { display: grid; gap: 14px; padding: 22px; }
.export-progress-bar {
  height: 6px;
  border-radius: 999px;
  background: var(--line-soft);
  overflow: hidden;
}
.export-progress-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
  /* The bar only ever moves forward, one document at a time; without this it
     jumps on each poll. */
  transition: width 0.3s ease;
}
.export-progress[data-status="cancelled"] .export-progress-fill { background: var(--faint); }
.export-progress-count {
  margin: -6px 0 0;
  font-size: 13.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.export-progress-note {
  margin: 0;
  padding: 12px 14px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  background: var(--panel-subtle);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--muted);
}
.export-progress-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.export-progress-spacer { flex-grow: 1; }
.export-stop { color: var(--red); }
.export-stop[disabled] { color: var(--muted); cursor: default; }
.export-progress-download { justify-self: end; }

/* The inline confirm. It takes the whole row under the buttons rather than
   opening a second dialog over this one - a modal on a modal is where focus and
   Escape start fighting each other. */
.export-stop-confirm {
  flex-basis: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border: 1px solid var(--red-weak);
  border-radius: var(--radius-sm);
  background: var(--red-weak);
}
.export-stop-confirm[hidden] { display: none; }
.export-stop-confirm p { margin: 0; flex-grow: 1; font-size: 13px; line-height: 1.45; }
.export-stop-confirm .button-ghost,
.export-stop-confirm .button-danger { white-space: nowrap; }

/* Recent exports, in the export dialog.

   A ZIP lives 24 hours and the manifest lives a year, so most rows here are past
   downloading. The list is therefore built to read as a record first and a
   download second: the state sits where the button would be, in the same column,
   so a scan down the right edge answers "can I still get this one?". */
.export-history {
  /* Its own padding, not the form's: this section is a SIBLING of .export-form
     (a form cannot nest a form, and each row's download is a link beside the
     banner's dismiss form), so it inherits nothing from it. The dialog itself is
     padding: 0. Matching 22px keeps the two blocks on one left edge. */
  margin: 0;
  padding: 18px 22px 22px;
  border-top: 1px solid var(--line-soft);
}
.export-history h3 {
  margin: 0 0 10px;
  font-size: 12px;
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}
.export-history-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.export-history-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  padding: 7px 0;
}
.export-history-row + .export-history-row { border-top: 1px solid var(--line-soft); }
.export-history-copy { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.export-history-title { font-size: 13px; font-weight: 600; color: var(--text); }
.export-history-meta { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.export-history-action { padding: 4px 11px; font-size: 12.5px; }
.export-history-state { font-size: 12px; color: var(--muted); }
.export-history-state.is-failed { color: var(--amber); }

.export-dialog {
  /* 640 rather than 560: the date picker inside is two months wide, and at 560
     it overflowed the dialog by ~107px horizontally (measured), which the UA's
     `overflow: auto` on <dialog> then clipped. */
  width: min(640px, calc(100vw - 32px));
  /* And a ceiling, so a tall form scrolls the dialog
     instead of running off the bottom of the viewport. */
  max-height: min(92vh, 900px);
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
  color: var(--text);
}

.export-dialog::backdrop { background: rgba(8, 22, 18, .45); }
.export-form { display: grid; gap: 16px; padding: 22px; }
.export-head h2 { margin: 0; font-size: 17px; }
.export-head .section-note { margin: 4px 0 0; }
.export-conditions-grid { display: grid; gap: 12px; }
.export-actions { display: flex; justify-content: flex-end; gap: 10px; }

@media (min-width: 620px) {
  .export-conditions-grid { grid-template-columns: 1fr 1fr; }
}

/* ─── Locked features ────────────────────────────────────────────────────────
   A feature the plan doesn't include is shown, not hidden: a merchant who can't
   see a feature has no way to know it exists. The crown is the single marker for
   "not on your plan", so it means the same thing in the nav, on a section header
   and beside a disabled control.

   Deliberately NOT `opacity` on the whole row: that would fade the crown too,
   which is the one part that must stay legible. The label dims, the crown does
   not. */
.crown-icon {
  flex: none;
  color: var(--amber, #b7791f);
}

.nav-link.is-locked .nav-label {
  color: var(--muted);
  opacity: 0.72;
}

.nav-crown {
  margin-left: auto;
}

/* Failed background jobs, counted on Settings (where their list lives). Shown to
   the operator only, and only while something is failing. */
.nav-count {
  margin-left: auto;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: #e5484d;
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* The rail hides labels, so the count becomes a dot on the icon's corner. */
html.nav-rail .nav-count {
  position: absolute;
  top: 4px;
  right: 4px;
  min-width: 8px;
  width: 8px;
  height: 8px;
  padding: 0;
  font-size: 0;
  box-shadow: 0 0 0 2px #0b3a2e;
}

/* Collapsed rail hides labels, so the crown would sit alone against the icon and
   read as a badge on the app rather than on the item. The tooltip and aria-label
   still name the tier. */
html.nav-rail .nav-crown {
  display: none;
}

/* On the active row the crown sits on the filled chip, where amber-on-green is
   muddy - inherit the row's own colour instead. */
.nav-link.is-active .crown-icon {
  color: currentColor;
}

/* The tier pill already says "Elite"; the crown makes it scannable without
   reading. */
.plan-pill .crown-icon {
  margin-right: 4px;
  vertical-align: -2px;
  color: currentColor;
}

/* The same crown on the shared tier badge, so "not on your plan" looks identical
   whether it appears in the nav, on the Export PDFs button, on the Draft Orders
   tab or on a locked schedule trigger.

   Done as a mask on ::before rather than by putting an SVG in the markup: this
   badge is 9.5px uppercase and, on trigger cards, absolutely positioned in a
   corner. An inline <svg> would have to fight both of those in three call sites.
   currentColor via mask keeps it matching whatever colour the badge already uses. */
.trig-lock::before {
  content: "";
  display: inline-block;
  width: 9px;
  height: 9px;
  margin-right: 3px;
  vertical-align: -1px;
  background-color: currentColor;
  -webkit-mask: var(--crown-mask) center / contain no-repeat;
  mask: var(--crown-mask) center / contain no-repeat;
}

:root {
  --crown-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000'%3E%3Cpath d='M3 8.2c.5 0 .9.4.9.9 0 .3-.1.5-.3.7l2.1 6.9h12.6l2.1-6.9a1 1 0 0 1-.3-.7c0-.5.4-.9.9-.9s.9.4.9.9c0 .4-.3.8-.7.9L19.4 19H4.6L2.8 10c-.4-.1-.7-.5-.7-.9 0-.5.4-.9.9-.9z'/%3E%3Cpath d='M12 5.6 15.2 12H8.8z'/%3E%3Ccircle cx='12' cy='4.4' r='1.5'/%3E%3C/svg%3E");
}

/* ==========================================================================
   Format thumbnail (partials/format_thumb.erb)

   A ~90px sketch of an invoice that reflects the format's actual header
   arrangement, table treatment and palette. Replaces two things: the gallery's
   old `pminv` and the saved-template cards' generic grey bars, which made two
   templates on entirely different formats look identical in the library.

   Renders 500+ times on the gallery, so everything here is cheap: no images,
   no gradients, no per-card stylesheet - just flex and two custom properties.
   ========================================================================== */

.fthumb {
  --fa: #333;
  --ft: var(--line-soft);
  display: flex;
  flex-direction: column;
  gap: 5px;
  height: 90px;
  padding: 9px 10px;
  background: #fff;
  border-bottom: 1px solid var(--line-soft);
}

.fthumb-head {
  display: flex;
  align-items: flex-start;
  gap: 6px;
}

.fthumb-mark {
  flex: none;
  width: 14px;
  height: 14px;
  border-radius: 4px;
  background: var(--fa);
}

.fthumb-brand {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 3px;
  padding-top: 2px;
}

.fthumb-brand i {
  height: 3px;
  border-radius: 2px;
  background: #d9dde2;
}

.fthumb-brand i:first-child { width: 62%; }
.fthumb-brand i:last-child { width: 40%; }

.fthumb-word {
  flex: none;
  color: var(--fa);
  font-size: 7px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.fthumb-rows {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 4px;
}

.fthumb-rows i {
  height: 5px;
  border-radius: 1px;
  background: #eef0f3;
}

.fthumb-foot {
  display: flex;
  justify-content: flex-end;
}

.fthumb-total {
  padding: 1px 6px;
  border-radius: 3px;
  color: #fff;
  background: var(--fa);
  font-size: 7px;
  font-weight: 700;
}

/* --- Header arrangement, sketched ------------------------------------- */

.fthumb[data-fthumb-header="right"] .fthumb-head { flex-direction: row-reverse; }

.fthumb[data-fthumb-header="center"] .fthumb-head,
.fthumb[data-fthumb-header="stacked"] .fthumb-head,
.fthumb[data-fthumb-header="left"] .fthumb-head {
  flex-direction: column;
  gap: 4px;
}

.fthumb[data-fthumb-header="center"] .fthumb-head { align-items: center; }
.fthumb[data-fthumb-header="center"] .fthumb-brand { align-items: center; }

/* `band` is the one arrangement with real chrome: the wordmark reverses out of
   a full-bleed accent bar, so the sketch bleeds past the thumbnail padding. */
.fthumb[data-fthumb-header="band"] .fthumb-head {
  align-items: center;
  margin: -9px -10px 0;
  padding: 5px 10px;
  background: var(--fa);
}

.fthumb[data-fthumb-header="band"] .fthumb-word { color: #fff; }
.fthumb[data-fthumb-header="band"] .fthumb-mark { background: rgba(255, 255, 255, 0.55); }
.fthumb[data-fthumb-header="band"] .fthumb-brand i { background: rgba(255, 255, 255, 0.4); }

/* --- Table treatment, sketched ---------------------------------------- */

.fthumb[data-fthumb-table="filled"] .fthumb-rows i:first-child { background: var(--fa); }
.fthumb[data-fthumb-table="striped"] .fthumb-rows i:nth-child(even) { background: var(--ft); }

.fthumb[data-fthumb-table="bordered"] .fthumb-rows i {
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--ft);
}

.fthumb[data-fthumb-table="minimal"] .fthumb-rows i {
  height: 3px;
  background: #f1f3f5;
}

.fthumb[data-fthumb-table="underline"] .fthumb-rows i {
  height: 4px;
  border-radius: 0;
  background: transparent;
  box-shadow: inset 0 -1px 0 var(--ft);
}

/* ==========================================================================
   Invoice template library: sections, cards, format grid

   The page used to be one flat stack - hero, card grid, then two unlabelled
   <details> disclosures - so the primary way to start a template (the format
   library) was the least visible thing on it. Now it reads as three named
   sections in the order they're needed: yours, start from a format, from
   scratch.
   ========================================================================== */

.tpl-section {
  margin-top: 30px;
}

.tpl-section-head {
  margin-bottom: 14px;
}

.tpl-section-head h2 {
  margin: 0 0 2px;
  font-size: 17px;
}

/* The rarest path (block builder) reads as an afterthought on purpose. */
.tpl-section-quiet {
  margin-top: 34px;
  padding-top: 22px;
  border-top: 1px solid var(--line-soft);
}

.tpl-thumb-link {
  display: block;
  border-radius: 12px 12px 0 0;
  overflow: hidden;
}

/* --- Saved-template card actions ---------------------------------------
   One primary action plus a menu. Five equally-weighted buttons per card
   wrapped onto two rows and made Delete as prominent as Edit. */

.tpl-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

/* An open menu takes the whole row so its items aren't squeezed beside Edit. */
.card-menu[open] {
  flex: 1 0 100%;
}

.card-menu-trigger {
  display: inline-grid;
  place-items: center;
  padding: 0 8px;
  min-height: 28px;
  cursor: pointer;
  list-style: none;
}

.card-menu-trigger::-webkit-details-marker,
.card-menu-trigger::marker {
  display: none;
  content: "";
}

.card-menu-panel {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 8px;
  padding: 6px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-soft, #f7faf8);
}

.card-menu-item {
  width: 100%;
  padding: 7px 9px;
  border: 0;
  border-radius: 7px;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}

.card-menu-item:hover {
  background: var(--surface-soft, #f3f6f4);
}

.card-menu-item.is-danger {
  color: var(--danger, #b42318);
}

.card-menu-rename {
  display: grid;
  gap: 4px;
  padding: 7px 9px 9px;
  border-top: 1px solid var(--line-soft);
  margin-top: 2px;
}

.card-menu-rename input[type="text"] {
  width: 100%;
}

/* --- The one lock badge (partials/plan_gate_badge.erb) ----------------- */

.plan-gate-badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 1px 7px;
  border-radius: 999px;
  color: #6b4708;
  background: #fdf1d6;
  font-size: 10px;
  font-weight: 760;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

/* Saved-template cards sit in a grid, so their rows only line up if each card
   fills its cell and pins its actions to the bottom - otherwise a subtitle that
   wraps to two lines pushes that one card's Edit button out of line with the
   rest of the row. */
.tpl-card .tpl-meta {
  flex: 1;
}

.tpl-card .tpl-actions {
  margin-top: auto;
}

/* The editor's Format row: what this template is on, and the way out to change
   it. Replaces a 133-card gallery that was squeezed into this 360px column. */
.format-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.format-row-main {
  display: grid;
  gap: 3px;
  min-width: 0;
}

.format-row-name {
  font-size: 14px;
  line-height: 1.3;
}

.format-row-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 3px;
}

.format-row-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 1px 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--muted);
  font-size: 11px;
  text-transform: capitalize;
}

/* ============================================================================
   A-J INVOICE THEMES (invoice-build-spec.md 3.2 tokens, 4 layout deltas)
   ============================================================================
   Every rule here is keyed on [data-theme], which order_invoice.erb only emits
   for a template carrying a valid theme. An unthemed invoice matches none of
   them and renders exactly as it did before this block existed - the same
   absent-key contract the Ruby side keeps.

   The tokens (--t-*) arrive as inline custom properties from the view, so this
   file never repeats a hex: the ten palettes live in InvoiceThemes, which the
   Prawn renderer reads too. One source, two surfaces.
   ============================================================================ */

.template-preview[data-theme] {
  max-width: var(--t-sheet, 800px);
  margin-inline: auto;
  /* The base rule makes the preview sticky, which is right for the editor's
     side-by-side pane and wrong for a document: on the print sheet it offset the
     invoice 94px down and pushed it out through the bottom of its own container. */
  position: static;
  padding: 44px 52px;
  color: var(--t-ink);
  background: var(--t-paper);
  font-family: var(--t-body);
  --invoice-accent: var(--t-accent);
}

.template-preview[data-theme] h2[data-section="company_name"],
.template-preview[data-theme] .preview-doc-title {
  font-family: var(--t-display);
}

/* The document title is the defining element of every A-J header, so it is
   always shown - unlike the legacy headers, where it is opt-in. */
.template-preview[data-theme] .preview-doc-title {
  display: block;
  color: var(--t-accent);
  font-size: 30px;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: 0 0 4px;
}

.template-preview[data-theme] .preview-header {
  border-bottom: 1px solid var(--t-hair);
  align-items: flex-start;
}

.template-preview[data-theme] .preview-meta,
.template-preview[data-theme] .totals-stack,
.template-preview[data-theme] [data-section="labels"],
.template-preview[data-theme] .eyebrow {
  color: var(--t-muted);
}

.template-preview[data-theme] .preview-logo-mark {
  border: 1px dashed var(--t-hair);
  background: transparent;
  color: var(--t-muted);
  /* Spec 5: the slot is reserved at 96x56 whether or not a logo exists, so the
     header does not reflow between a shop that has uploaded one and one that
     has not. */
  width: 96px;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.template-preview[data-theme] .preview-line-items th {
  color: var(--t-muted);
  border-bottom: 1px solid var(--t-accent);
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.06em;
}

.template-preview[data-theme] .preview-line-items td {
  border-bottom: 1px solid var(--t-hair);
}

.template-preview[data-theme] .totals-owing,
.template-preview[data-theme] [data-section="totals"].totals-owing {
  color: var(--t-accent);
  font-weight: 700;
}

.template-preview[data-theme] .preview-paid-badge,
.template-preview[data-theme] .preview-status-banner {
  background: var(--t-status);
  color: #fff;
  border: 0;
  border-radius: 999px;
  padding: 3px 11px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  display: inline-block;
}

.template-preview[data-theme] [data-section="footer"] {
  border-top: 1px solid var(--t-hair);
  color: var(--t-muted);
}

/* --- A: Swiss Grid - title hard right, heavy accent rule ------------------ */
.template-preview[data-theme="A"] .preview-header {
  border-bottom-width: 3px;
  border-bottom-color: var(--t-accent);
}
.template-preview[data-theme="A"] .preview-meta,
.template-preview[data-theme="A"] .preview-doc-title { text-align: right; }
/* The 3-column bordered strip, with rules BETWEEN cells only. */
.template-preview[data-theme="A"] .preview-grid > * + * {
  border-left: 1px solid var(--t-hair);
  padding-left: 20px;
}

/* --- B: Soft Editorial - warm paper, italic serif title ------------------- */
.template-preview[data-theme="B"] .preview-doc-title,
.template-preview[data-theme="B"] [data-section="tagline"] {
  font-style: italic;
  text-transform: none;
  letter-spacing: 0;
}

/* --- C: Structured Mono - the whole sheet ruled, spec-sheet meta ---------- */
.template-preview[data-theme="C"] { border: 1px solid var(--t-hair); }
.template-preview[data-theme="C"] .preview-doc-title,
.template-preview[data-theme="C"] .totals-stack { font-family: var(--t-mono); }
.template-preview[data-theme="C"] .preview-grid > * {
  border: 1px solid var(--t-hair);
  padding: 10px 12px;
}
.template-preview[data-theme="C"] .totals-stack > * {
  border: 1px solid var(--t-hair);
  padding: 5px 9px;
}

/* --- D: Airy Line - hairlines only, oversized total, light weights -------- */
.template-preview[data-theme="D"] { padding: 56px 60px; }
.template-preview[data-theme="D"] .preview-line-items th { border-bottom-color: var(--t-hair); }
.template-preview[data-theme="D"] .preview-line-items td { padding-block: 14px; }
.template-preview[data-theme="D"] [data-section="body"] { font-weight: 300; }
.template-preview[data-theme="D"] .totals-owing { font-size: 30px; letter-spacing: -0.02em; }

/* --- E: Sidebar Accent - the dark rail ------------------------------------ */
@media (min-width: 641px) {
  /* THE RAIL IS TAKEN OUT OF FLOW. Two grid attempts failed for the same
     reason - a rail has to be as tall as the SHEET, and CSS grid can only span
     rows that exist: `1 / -1` addresses only the explicit grid (the rail stopped
     at its own content height), and `1 / span 100` created a hundred real rows
     and a 3,500px-tall invoice. Absolute positioning inside a padded container
     is height-independent, so it cannot get this wrong at any content length.
     The gradient paints the rail's ground so it reaches the sheet's full height
     even if the rail's own content is short. */
  .template-preview[data-theme="E"] {
    position: relative;
    padding: 0 52px 44px 272px;
    overflow: hidden;
    background: linear-gradient(to right, var(--t-accent) 0 220px, var(--t-paper) 220px);
  }
  .template-preview[data-theme="E"] .preview-header {
    position: absolute;
    inset: 0 auto 0 0;
    width: 220px;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    gap: 18px;
    padding: 32px 24px;
    background: transparent;
    border: 0;
    margin: 0;
    color: #fff;
  }
  /* Everything in the rail, including the tagline: it inherits .muted, which on
     the dark rail rendered as unreadable low-contrast text. */
  .template-preview[data-theme="E"] .preview-header h2[data-section="company_name"],
  .template-preview[data-theme="E"] .preview-header .preview-meta,
  .template-preview[data-theme="E"] .preview-header .preview-meta span,
  .template-preview[data-theme="E"] .preview-header [data-section="tagline"],
  .template-preview[data-theme="E"] .preview-header .muted,
  .template-preview[data-theme="E"] .preview-header [data-section="labels"] { color: #fff; }
  /* The logo and wordmark stack in a 220px rail rather than sitting side by side,
     which wrapped the shop name mid-word. */
  .template-preview[data-theme="E"] .preview-brand {
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
  }
  .template-preview[data-theme="E"] .preview-meta { align-items: flex-start; }
  .template-preview[data-theme="E"] .preview-logo-mark { border-color: rgb(255 255 255 / 0.5); color: #fff; }
  .template-preview[data-theme="E"] > .preview-doc-title { padding-top: 36px; }
}

/* --- F: Centered Card ----------------------------------------------------- */
.template-preview[data-theme="F"] { text-align: center; }
.template-preview[data-theme="F"] .preview-header {
  flex-direction: column;
  align-items: center;
  border: 0;
}
.template-preview[data-theme="F"] .preview-brand { flex-direction: column; align-items: center; }
.template-preview[data-theme="F"] .preview-logo-mark { margin-inline: auto; }
.template-preview[data-theme="F"] .preview-line-items { text-align: left; }
/* The rounded hero card the spec puts above the items table. */
.template-preview[data-theme="F"] .totals-stack {
  background: var(--t-hair);
  border-radius: 14px;
  padding: 20px 24px;
  text-align: center;
}
.template-preview[data-theme="F"] .totals-owing { font-size: 26px; }

/* --- G: Two-Tone Split - the 50/50 banner --------------------------------- */
@media (min-width: 641px) {
  .template-preview[data-theme="G"] { padding: 0; overflow: hidden; }
  .template-preview[data-theme="G"] .preview-header {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0;
    border: 0;
    padding: 0;
  }
  .template-preview[data-theme="G"] .preview-brand {
    background: var(--t-accent);
    color: #fff;
    padding: 30px 28px;
    margin: 0;
  }
  .template-preview[data-theme="G"] .preview-brand h2[data-section="company_name"],
  .template-preview[data-theme="G"] .preview-brand [data-section="tagline"] { color: #fff; }
  .template-preview[data-theme="G"] .preview-logo-mark { border-color: rgb(255 255 255 / 0.5); color: #fff; }
  .template-preview[data-theme="G"] .preview-meta {
    background: var(--t-accent-light);
    padding: 30px 28px;
    margin: 0;
  }
  .template-preview[data-theme="G"] > *:not(.preview-header) { padding-inline: 52px; }
  .template-preview[data-theme="G"] > *:last-child { padding-bottom: 44px; }
}

/* --- H: Dashboard Summary - rounded sheet, stat cards --------------------- */
.template-preview[data-theme="H"] { border-radius: 16px; }
.template-preview[data-theme="H"] .totals-stack {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
}
.template-preview[data-theme="H"] .totals-stack > * {
  background: var(--t-hair);
  border-radius: 8px;
  padding: 14px 16px;
}
.template-preview[data-theme="H"] .totals-stack > *:has(.totals-owing),
.template-preview[data-theme="H"] .totals-owing {
  background: var(--t-accent);
  color: #fff;
  border-radius: 8px;
}

/* --- I: Ledger Line - dotted leaders instead of a table ------------------- */
.template-preview[data-theme="I"] .preview-line-items,
.template-preview[data-theme="I"] .preview-line-items tbody,
.template-preview[data-theme="I"] .preview-line-items tr { display: block; width: 100%; }
.template-preview[data-theme="I"] .preview-line-items thead { display: none; }
.template-preview[data-theme="I"] .preview-line-items tr {
  display: flex;
  /* CENTRE, not baseline. The row was always a single flex line - measured, not
     assumed - but a line item carries a 40px product thumbnail, and baseline
     alignment drops every other cell's text to the image's baseline, so the
     figures rendered a line below their own description. */
  align-items: center;
  /* NOWRAP is load-bearing. The row has four cells (description, qty, rate,
     amount), not the two the leader idea implies, so with wrapping allowed the
     numeric cells dropped onto a second line and the leader became a stub. */
  flex-wrap: nowrap;
  gap: 8px;
  padding: 5px 0;
}
.template-preview[data-theme="I"] .preview-line-items td {
  border: 0;
  padding: 0;
  /* The numeric cells hold their content; only the description flexes. */
  flex: none;
  white-space: nowrap;
}
/* The leader itself: a repeating dotted rule that fills whatever gap is left,
   which is what makes description ···· amount work at any width. */
/* A smaller thumbnail keeps a ledger row a ledger row. */
.template-preview[data-theme="I"] .preview-line-items .li-thumb {
  width: 22px !important;
  height: 22px !important;
  flex: none;
}
.template-preview[data-theme="I"] .preview-line-items td:first-child {
  flex: 1 1 auto;
  /* Without min-width:0 a flex child refuses to shrink below its content's
     intrinsic width - and a description cell carries a product thumbnail, so it
     never shrank and pushed everything else out of the row. */
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 6px;
  overflow: hidden;
}
.template-preview[data-theme="I"] .preview-line-items td:first-child .inv-li-cell {
  min-width: 0;
  flex: 0 1 auto;
  align-items: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.template-preview[data-theme="I"] .preview-line-items td:first-child::after {
  content: "";
  flex: 1 1 auto;
  min-width: 24px;
  border-bottom: 1px dotted var(--t-hair);
}
.template-preview[data-theme="I"] .preview-line-items td:last-child { font-weight: 700; }
.template-preview[data-theme="I"] .totals-stack { font-family: var(--t-display); }

/* --- J: Corner Badge ------------------------------------------------------ */
.template-preview[data-theme="J"] { position: relative; overflow: hidden; }
.template-preview[data-theme="J"] .preview-paid-badge,
.template-preview[data-theme="J"] .preview-status-banner {
  position: absolute;
  top: 22px;
  right: -34px;
  transform: rotate(45deg);
  border-radius: 0;
  padding: 5px 44px;
  z-index: 1;
}

/* --- responsive (spec 7.2) ------------------------------------------------ */
@media (max-width: 640px) {
  .template-preview[data-theme] { padding: 24px 18px; }
  .template-preview[data-theme] .preview-doc-title { font-size: 24px; }
  /* E's rail stacks above the main column, G's banner stacks - both fall out of
     the grids being declared min-width only. */
  .template-preview[data-theme] .preview-grid { grid-template-columns: 1fr; }
  .template-preview[data-theme="A"] .preview-grid > * + * {
    border-left: 0;
    padding-left: 0;
    border-top: 1px solid var(--t-hair);
    padding-top: 12px;
  }
  /* Keep the table scrollable rather than wrapping cells (spec 7.2). */
  .template-preview[data-theme] .preview-line-items { display: block; overflow-x: auto; }
}

/* --- print / PDF (spec 7.1) ---------------------------------------------- */
@media print {
  .template-preview[data-theme] {
    max-width: none;
    width: 100%;
    margin: 0;
    box-shadow: none;
    position: static;
    /* The warm-paper themes have to keep their ground in print, which browsers
       drop by default. */
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .template-preview[data-theme] tr,
  .template-preview[data-theme] .preview-line-items tr { break-inside: avoid; }
  .template-preview[data-theme] thead { display: table-header-group; }
  /* The totals block must never orphan onto a page of its own. */
  .template-preview[data-theme] .totals-stack,
  .template-preview[data-theme] .totals-block { break-inside: avoid; }
}

/* --- A-J theme picker (editor) ------------------------------------------- */

.theme-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(116px, 1fr));
  gap: 10px;
}

.theme-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: pointer;
  background: var(--panel);
}
.theme-card:hover { border-color: var(--line-strong); }
.theme-card.is-selected { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
/* The radio drives selection; hiding it visually keeps the whole card clickable
   without a second control, but it must stay focusable for the keyboard. */
.theme-card input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}
.theme-card:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }

.theme-card-name {
  font-size: 12px;
  font-weight: 600;
  line-height: 1.25;
}
.theme-card-faces {
  font-size: 10px;
  color: var(--faint);
  line-height: 1.2;
}

/* A miniature of the sheet: mark, wordmark, three rows, a total. Same idea as
   .fthumb - CSS only, no images, because this grid renders eleven of them. */
.theme-card-thumb {
  position: relative;
  display: block;
  height: 74px;
  padding: 7px;
  border: 1px solid var(--tc-hair, var(--line));
  background: var(--tc-paper, #fff);
  overflow: hidden;
}
.theme-card-thumb i { display: block; }
.theme-card-thumb .tc-mark {
  width: 14px;
  height: 9px;
  border: 1px dashed var(--tc-hair, #ddd);
  margin-bottom: 5px;
}
.theme-card-thumb .tc-word {
  width: 60%;
  height: 6px;
  background: var(--tc-accent);
  margin-bottom: 7px;
}
.theme-card-thumb .tc-row {
  height: 3px;
  background: var(--tc-hair, #e6e6e6);
  margin-bottom: 4px;
}
.theme-card-thumb .tc-total {
  width: 38%;
  height: 5px;
  background: var(--tc-accent);
  margin-left: auto;
  margin-top: 6px;
}
.theme-card-thumb-none {
  background: repeating-linear-gradient(45deg, var(--panel-subtle) 0 5px, var(--panel) 5px 10px);
}

/* The distinctive layouts read at thumbnail size too, or every card looks the
   same and the grid tells the merchant nothing. */
[data-theme-thumb="E"] { padding-left: 30px; }
[data-theme-thumb="E"]::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 23px;
  background: var(--tc-accent);
}
[data-theme-thumb="G"]::before {
  content: "";
  position: absolute;
  inset: 0 50% auto 0;
  height: 24px;
  background: var(--tc-accent);
}
[data-theme-thumb="G"] { padding-top: 29px; }
[data-theme-thumb="F"] { text-align: center; }
[data-theme-thumb="F"] .tc-mark,
[data-theme-thumb="F"] .tc-word { margin-inline: auto; }
[data-theme-thumb="F"] .tc-total { margin-inline: auto; width: 60%; height: 12px; }
[data-theme-thumb="H"] .tc-total { width: 100%; height: 14px; }
[data-theme-thumb="C"] { outline: 1px solid var(--tc-hair, #ddd); outline-offset: -4px; }
[data-theme-thumb="D"] .tc-row { background: transparent; border-bottom: 1px solid var(--tc-hair, #e6e6e6); }
[data-theme-thumb="D"] .tc-total { height: 9px; }
[data-theme-thumb="I"] .tc-row { background: transparent; border-bottom: 1px dotted var(--tc-accent); }
[data-theme-thumb="J"]::after {
  content: "";
  position: absolute;
  top: 4px;
  right: -8px;
  width: 26px;
  height: 7px;
  background: var(--tc-accent);
  transform: rotate(45deg);
}
[data-theme-thumb="A"] .tc-word { margin-left: auto; }
[data-theme-thumb="A"] { border-bottom: 3px solid var(--tc-accent); }
[data-theme-thumb="B"] .tc-word { width: 45%; transform: skewX(-12deg); }

/* The library's theme grid is the same card at a larger size - the gallery has
   full width where the editor's column does not. `button` and `div` variants
   both appear (a locked card is not a form), so the reset covers both. */
.theme-grid-lib { grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); gap: 14px; }
.theme-grid-lib .theme-card { width: 100%; text-align: left; font: inherit; color: inherit; }
.theme-grid-lib .theme-card-thumb { height: 104px; }
.theme-grid-lib form { display: block; }
.theme-card.is-locked { opacity: 0.55; cursor: default; }

/* --- Vendor settlement panel ------------------------------------------ */

/* "What do I still owe this vendor, and can I pay it right now." The balance
   and its action sit on one row so the number and the button that clears it
   are never separated by a scroll. */
.vc-settle {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 18px;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--accent-weak);
}
.vc-settle-label {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 4px;
}
/* One amount per currency: a vendor owed in two currencies is owed two
   amounts, and a single total would be wrong in both. */
.vc-settle-amount {
  display: block;
  font-size: 24px;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
}
.vc-settle-amount.is-clear { color: var(--faint); }
.vc-settle .section-note { margin: 6px 0 0; }
.vc-settle-action { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
.vc-settle-action .section-note { margin: 0; text-align: right; }

/* The vendors list's unpaid column, one line per currency. */
.vc-unpaid { font-variant-numeric: tabular-nums; }
.vc-unpaid + .vc-unpaid { margin-top: 2px; }

@media (max-width: 720px) {
  .vc-settle { flex-direction: column; align-items: stretch; }
  .vc-settle-action { align-items: stretch; }
  .vc-settle-action .section-note { text-align: left; }
}

/* ============ E-invoicing (Settings -> E-invoicing, order page panel) ============ */
.einv-page { min-width: 0; }
.einv-page .back-link { margin-bottom: 16px; }
.einv-switches { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr)); gap: 10px; margin-bottom: 18px; }
.einv-switch-card, .einv-card {
  background: var(--panel, #fff); border: 1px solid var(--line, #e3e7e5); border-radius: 12px; padding: 14px 16px;
}
.einv-switch-card { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.einv-switch-card strong { display: block; font-size: 13.5px; }
.einv-switch-card small { display: block; color: var(--muted, #5f6b66); font-size: 12px; margin-top: 3px; max-width: 52ch; }
.einv-card { margin-bottom: 18px; }
.einv-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); gap: 4px 16px; }
.einv-reg { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 6px; font-size: 12px; color: var(--muted, #5f6b66); }
.einv-error, .einv-form-error { color: var(--danger, #b42318); font-size: 12px; }
.einv-actions { margin: 4px 0 14px; }
.einv-regform { margin-bottom: 18px; }

.einv-panel {
  background: var(--panel, #fff); border: 1px solid var(--line, #e3e7e5); border-radius: 14px;
  padding: 16px 18px; margin: 14px 0; min-width: 0;
}
.einv-alert { margin: 8px 0 0; padding: 10px 12px; border-radius: 10px; background: #fff6e5; color: #7a4b00; font-size: 12.5px; }
.einv-route { border-top: 1px solid var(--line, #e3e7e5); padding: 12px 0; }
.einv-route:first-of-type { border-top: 0; }
.einv-route-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.einv-route-line { margin: 6px 0 0; font-size: 13px; }
.einv-route-line small, .einv-muted { color: var(--muted, #5f6b66); font-size: 12px; }
.einv-route-line small { display: block; margin-top: 2px; }
.einv-totals { margin: 6px 0 0; font-size: 12px; color: var(--muted, #5f6b66); font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.einv-checklist { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 6px; }
.einv-checklist li { display: flex; gap: 8px; align-items: baseline; font-size: 12.5px; padding: 7px 10px; border-radius: 8px; }
.einv-checklist li.is-fatal { background: #fdecec; color: #8a1c14; }
.einv-checklist li.is-warn { background: #fff6e5; color: #7a4b00; }
.einv-checklist li a { margin-left: auto; font-weight: 650; white-space: nowrap; }
.einv-rule { font-family: var(--mono, ui-monospace, monospace); font-size: 10.5px; opacity: .75; white-space: nowrap; }
.einv-buyer { border-top: 1px solid var(--line, #e3e7e5); padding-top: 12px; }
.einv-buyer summary, .einv-delivery summary { cursor: pointer; font-size: 13px; font-weight: 650; }
.einv-buyer .einv-form { margin-top: 12px; }
.einv-check { display: flex; gap: 8px; align-items: center; font-size: 13px; margin-bottom: 10px; }
.einv-history { border-top: 1px solid var(--line, #e3e7e5); padding-top: 12px; margin-top: 12px; }
.einv-delivery { padding: 8px 0; border-bottom: 1px solid var(--line, #eef1ef); }
.einv-delivery summary { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-weight: 500; }
.einv-delivery summary a { font-size: 12px; font-weight: 650; }
.einv-reason { margin: 8px 0 0; font-size: 12.5px; }
.einv-timeline { margin: 8px 0 0; padding-left: 18px; display: grid; gap: 4px; font-size: 12px; }
.einv-timeline small { display: block; color: var(--muted, #5f6b66); }
.einv-hash { overflow-wrap: anywhere; margin: 6px 0 0; }
@media (max-width: 640px) {
  .einv-switch-card { flex-direction: column; align-items: flex-start; }
  .einv-checklist li { flex-wrap: wrap; }
  .einv-checklist li a { margin-left: 0; }
}

/* ─── Toasts ─────────────────────────────────────────────────────────────────
   Every message the app used to show as a full-width flash bar or a browser
   alert() floats here instead: a white Mono card, top-right, at --z-toast so
   nothing covers it. Colour comes only from the semantic trio in the icon tile
   and the timer line, so the card itself stays quiet.

   The server renders the flash straight into .toast-region, so the message is
   there without JS; app.js adds the dismiss timer, and SendInvoiceUI.toast()
   adds new ones to the same region. Info toasts leave on their own; errors stay
   until closed, because an error that disappears is an error nobody read. */
.toast-region {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
  width: min(380px, calc(100vw - 32px));
  pointer-events: none;
}

.toast {
  --toast-tone: var(--accent);
  --toast-tone-weak: var(--accent-weak);
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  width: 100%;
  padding: 12px 12px 12px 14px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
  box-shadow: var(--shadow-pop), 0 1px 2px rgba(10, 10, 10, 0.04);
  color: var(--text);
  font-size: 13px;
  line-height: 1.45;
  pointer-events: auto;
  animation: toast-in var(--dur-slow) var(--ease) both;
}

.toast-error { --toast-tone: var(--red); --toast-tone-weak: var(--red-weak); }
.toast-warning { --toast-tone: var(--amber); --toast-tone-weak: var(--amber-weak); }

.toast.is-leaving { animation: toast-out var(--dur-slow) var(--ease) both; }

/* The glyph is a mask so its colour is the tone token and one stylesheet rule
   serves the server's toast and the script's alike - no SVG copied into both. */
.toast-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--toast-tone-weak);
}

.toast-icon::before {
  content: "";
  width: 14px;
  height: 14px;
  background: var(--toast-tone);
  -webkit-mask: var(--toast-glyph) center / contain no-repeat;
  mask: var(--toast-glyph) center / contain no-repeat;
}

.toast-info { --toast-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E"); }
.toast-error { --toast-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round'%3E%3Cpath d='M12 6.5v7M12 17.5v.01'/%3E%3C/svg%3E"); }
.toast-warning { --toast-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round'%3E%3Cpath d='M12 6.5v7M12 17.5v.01'/%3E%3C/svg%3E"); }

.toast-message {
  flex: 1;
  min-width: 0;
  margin: 3px 0 0;
  overflow-wrap: anywhere;
}

.toast-close {
  flex: none;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  margin: 0 -2px 0 0;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--faint);
  cursor: pointer;
  transition: color 0.15s var(--ease), background 0.15s var(--ease);
}

.toast-close:hover { color: var(--text); background: var(--hover); }
.toast-close:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-weak); }

/* How long an info toast has left. Hovering or focusing the toast pauses it,
   so a merchant reading a longer message doesn't lose it mid-sentence. */
.toast-timer {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 100%;
  background: var(--toast-tone);
  opacity: 0.55;
  transform-origin: left;
  animation: toast-timer var(--toast-duration, 5s) linear forwards;
}

.toast:hover .toast-timer,
.toast:focus-within .toast-timer { animation-play-state: paused; }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: none; }
}

@keyframes toast-out {
  from { opacity: 1; transform: none; }
  to { opacity: 0; transform: translateX(12px); }
}

@keyframes toast-timer {
  from { transform: scaleX(1); }
  to { transform: scaleX(0); }
}

/* Phones: full width, below the sticky 56px top bar rather than over it. */
@media (max-width: 1023px) {
  .toast-region { top: 68px; right: 16px; left: 16px; width: auto; align-items: stretch; }
}

@media (prefers-reduced-motion: reduce) {
  .toast, .toast.is-leaving { animation-duration: 0.001ms; }
}

/* ─── Confirm dialog ─────────────────────────────────────────────────────────
   Replaces the browser's confirm() for destructive actions. A native <dialog>
   like the export dialog, so focus trapping, Escape and the top layer come from
   the browser. Forms opt in with data-confirm; see bootConfirmations(). */
.confirm-dialog {
  width: min(420px, calc(100vw - 32px));
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
  color: var(--text);
  box-shadow: var(--shadow-pop);
}

.confirm-dialog[open] { animation: toast-in var(--dur-slow) var(--ease) both; }
.confirm-dialog::backdrop { background: rgba(8, 22, 18, .45); }

.confirm-dialog-body {
  display: flex;
  gap: 14px;
  padding: 20px 20px 16px;
}

.confirm-dialog-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--red-weak);
  color: var(--red);
}

.confirm-dialog-title { margin: 0 0 4px; font-size: 15px; font-weight: 700; }
.confirm-dialog-message { margin: 0; color: var(--muted); font-size: 13px; line-height: 1.5; overflow-wrap: anywhere; }

.confirm-dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 12px 20px;
  border-top: 1px solid var(--line-soft);
  background: var(--panel-subtle);
}

@media (max-width: 480px) {
  .confirm-dialog-actions > * { flex: 1; }
}

/* ============================================================================
   Vendor Portal (/portal) - a vendor's read-only dashboard. Its own chrome
   (no merchant sidebar), on the app's tokens and table styles. Phone-first:
   vendors mostly arrive from an email.
   ============================================================================ */
.portal-body { background: var(--panel-subtle); min-height: 100vh; display: flex; flex-direction: column; }
.portal-header { background: var(--panel); border-bottom: 1px solid var(--line); }
.portal-header-inner { max-width: 1100px; margin: 0 auto; padding: 14px 16px; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.portal-brand { display: flex; flex-direction: column; text-decoration: none; color: var(--text); min-width: 0; }
.portal-brand-shop { font-weight: 800; font-size: 16px; letter-spacing: -0.01em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.portal-brand-vendor { font-size: 12.5px; color: var(--muted); }
.portal-signout { margin: 0; }
.portal-nav { max-width: 1100px; margin: 0 auto; padding: 0 16px; display: flex; gap: 4px; overflow-x: auto; scrollbar-width: none; }
.portal-nav::-webkit-scrollbar { display: none; }
.portal-nav a { padding: 10px 12px; font-size: 14px; font-weight: 600; color: var(--muted); text-decoration: none; border-bottom: 2px solid transparent; white-space: nowrap; }
.portal-nav a:hover { color: var(--text); }
.portal-nav a.is-current { color: var(--text); border-bottom-color: var(--accent); }
.portal-main { flex: 1; width: 100%; max-width: 1100px; margin: 0 auto; padding: 22px 16px 40px; box-sizing: border-box; }
.portal-footer { max-width: 1100px; margin: 0 auto; padding: 16px; font-size: 12.5px; color: var(--faint); text-align: center; }
.portal-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin: 6px 0 16px; }
.portal-heading h1 { margin: 0; font-size: 24px; letter-spacing: -0.02em; }
.portal-card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 18px; margin-bottom: 16px; }
.portal-card h1 { margin: 4px 0 8px; font-size: 22px; }
.portal-card h2 { margin: 0 0 12px; font-size: 16px; }
.portal-card-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 8px; }
.portal-card-head h2 { margin: 0; }
.portal-card-head a { font-size: 13px; color: var(--accent); text-decoration: none; font-weight: 600; }
.portal-narrow { max-width: 440px; margin: 40px auto; }
.portal-form { display: grid; gap: 14px; margin-top: 14px; }
.portal-muted { color: var(--muted); }
.portal-small { font-size: 12.5px; margin-bottom: 0; }
.portal-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; padding: 4px 0 12px; }
.portal-stats + .portal-stats { border-top: 1px solid var(--line-soft); padding-top: 12px; }
.portal-stat-label { display: block; font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.portal-stat-value { display: block; font-size: 22px; font-weight: 800; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; margin-top: 2px; }
.portal-stat-value.is-accent { color: var(--accent); }
.portal-next .portal-stat-value { margin: 0 0 4px; }
.portal-range { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-bottom: 12px; }
.portal-heading .portal-range { margin-bottom: 0; }
.portal-chip { padding: 5px 10px; border: 1px solid var(--line); border-radius: 999px; font-size: 12.5px; font-weight: 600; color: var(--muted); text-decoration: none; background: var(--panel); }
.portal-chip.is-current { color: var(--accent); border-color: var(--accent); background: var(--accent-weak); }
.portal-range-custom { display: flex; align-items: center; gap: 6px; margin: 0; font-size: 12.5px; color: var(--muted); }
.portal-range-custom input[type="date"] { padding: 4px 6px; font-size: 12.5px; }
.portal-filters { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; margin-bottom: 12px; }
.portal-filters .field { margin: 0; min-width: 200px; }
.portal-pager { display: flex; align-items: center; justify-content: flex-end; gap: 10px; padding-top: 12px; font-size: 13px; color: var(--muted); }
.portal-calc { margin: 0; display: grid; gap: 0; }
.portal-calc > div { display: flex; justify-content: space-between; gap: 16px; padding: 9px 0; border-bottom: 1px solid var(--line-soft); }
.portal-calc dt { color: var(--muted); }
.portal-calc dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; text-align: right; }
.portal-calc .is-total dt, .portal-calc .is-total dd { color: var(--text); font-weight: 800; }
.portal-calc .is-accent dd { color: var(--accent); }
.portal-notice { border-color: var(--amber-weak); background: var(--amber-weak); }
.portal-notice ul { margin: 8px 0 0; padding-left: 18px; }
@media (max-width: 820px) {
  .portal-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 480px) {
  .portal-stat-value { font-size: 19px; }
  .portal-range-custom { width: 100%; }
  .portal-filters .field { min-width: 0; flex: 1 1 100%; }
}

/* The merchant's Portal Access card on a vendor's page. */
.vc-portal-facts { display: flex; flex-wrap: wrap; gap: 8px 28px; margin: 4px 0 14px; }
.vc-portal-facts dt { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.vc-portal-facts dd { margin: 2px 0 0; font-weight: 600; }
.vc-portal-actions { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
.vc-portal-actions form { margin: 0; }
.vc-portal-invite { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px; }
.vc-portal-invite .field { margin: 0; min-width: 240px; }
.portal-card .portal-subhead { margin: 18px 0 6px; font-size: 14px; }
