/* ============================================================
   App components — built exclusively from tokens.css values.
   Layout: sidebar shell, responsive below 760px.
   ============================================================ */
* { box-sizing: border-box; }

/* The hidden attribute must always win, even over classes that set display. */
[hidden] { display: none !important; }

/* Stroke icons. Paths live in frontend/src/icons.ts; markup uses data-icon. */
svg[data-icon] {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Native number spinners look out of place in every theme — values are typed
   or stepped with our own controls. */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { appearance: textfield; -moz-appearance: textfield; }

html, body { height: 100%; overflow: hidden; }
body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-md);
  line-height: var(--leading);
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

/* ---- Developer-mode banner (issue 8) ----
   Only ever shown when the operator set ETSY_APP_DEV_ORIGINS. It is fixed to
   the top of the window and the shell is shortened to match, so it cannot be
   scrolled away or covered — a build talking to local test servers or the Etsy
   sandbox must be unmistakable at a glance. */
:root { --devbanner-h: 30px; }
.devbanner {
  position: fixed;
  inset: 0 0 auto 0;
  height: var(--devbanner-h);
  z-index: 999;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0 12px;
  background: var(--danger);
  color: #fff;
  font-size: var(--text-sm);
  font-weight: 650;
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.devbanner b { font-weight: 800; }
.devbanner .devbanner-detail { font-weight: 500; opacity: 0.92; }
/* The topbar is fixed, so the shell is offset by its height (plus the dev
   banner when that is up) rather than flowing after it. */
:root { --sidebar-w: 210px; }
.shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  height: calc(100vh - var(--topbar-h));
  margin-top: var(--topbar-h);
}
body.dev-mode .shell {
  height: calc(100vh - var(--topbar-h) - var(--devbanner-h));
  margin-top: calc(var(--topbar-h) + var(--devbanner-h));
}

.sidebar {
  display: flex;
  flex-direction: column;
  background: var(--sidebar-bg);
  border-right: 1px solid var(--border);
  padding: var(--space-3);
  gap: var(--space-3);
  min-width: 0;
  overflow: hidden;
}
.sidebar .nav { flex: 1; min-height: 0; overflow-y: auto; }

.brand-ver {
  font-size: var(--text-2xs);
  font-weight: 500;
  color: var(--text-faint);
  align-self: flex-end;
  padding-bottom: 2px;
}
/* Neutral, as in cms4 — the mark reads as chrome, not as a logo. */
.brand-mark {
  width: 22px;
  height: 22px;
  border-radius: var(--radius-md);
  background: var(--topbar-hover-bg);
  color: var(--topbar-text);
  display: grid;
  place-items: center;
  font-size: 0.8rem;
  font-weight: 800;
}

.nav { display: flex; flex-direction: column; gap: 1px; }
.nav-sec {
  margin: var(--space-3) var(--space-3) var(--space-1);
  font-size: var(--text-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
}
.nav-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 5px var(--space-3);
  border: none;
  border-radius: var(--radius-md);
  background: none;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 550;
  color: var(--text-muted);
  cursor: pointer;
  text-align: left;
  transition: background 120ms, color 120ms;
}
.nav-item svg {
  width: 16px;
  height: 16px;
  flex: none;
}
.nav-item:hover { background: var(--sidebar-item-hover); color: var(--text); }
.nav-item:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.nav-item.active {
  background: var(--sidebar-item-active);
  color: var(--text);
  box-shadow: var(--shadow-1);
}

.nav-badge {
  margin-left: auto;
  min-width: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--primary);
  color: var(--on-primary);
  font-size: 0.65rem;
  font-weight: 600;
  text-align: center;
  line-height: 18px;
}
/* Amber earns attention — a parcel that stopped moving. Grey is just a count. */
.nav-badge-warn { background: var(--st-pending-dot); color: #171717; }
.nav-badge-mute { background: var(--border-strong); color: var(--text); }

.conn-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--text-faint);
  flex: none;
}
.conn-dot.ok { background: var(--success); box-shadow: 0 0 0 3px var(--success-soft); }
.conn-dot.bad { background: var(--danger); box-shadow: 0 0 0 3px var(--danger-soft); }

/* ---- Topbar (fixed chrome bar, spans above the sidebar) ---- */
.topbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  height: var(--topbar-h);
  padding: 0 var(--space-5);
  border-bottom: 1px solid var(--topbar-border);
  background: linear-gradient(to right, var(--topbar-bg), var(--topbar-bg-bright));
  color: var(--topbar-text);
}
body.dev-mode .topbar { top: var(--devbanner-h); }

.topbar-left,
.topbar-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.topbar-actions { margin-left: auto; }

.topbar-brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--topbar-text);
}
.topbar-brand .brand-mark { width: 26px; height: 26px; }
.brand-text { display: inline-flex; align-items: baseline; gap: var(--space-2); white-space: nowrap; }
.brand-name { font-size: var(--text-lg); font-weight: 600; letter-spacing: -0.01em; }
.topbar .brand-ver { color: var(--topbar-text-muted); align-self: baseline; padding-bottom: 0; }

/* Mobile-only, as in cms4: on desktop the sidebar is always open. */
.sidebar-toggle {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--topbar-text-muted);
  cursor: pointer;
}
.sidebar-toggle:hover,
.sidebar-toggle:focus-visible { background: var(--topbar-hover-bg); outline: none; }
.sidebar-toggle-bar {
  display: block;
  width: 1rem;
  height: 2px;
  border-radius: 999px;
  background: currentColor;
}

/* ---- Topbar: active shop ----
   One control, two facts: the dot is the Etsy connection, the name is the shop.
   They were two chips that both spelled out the shop name. Reads against the
   dark bar, not the page surface. */
.shop-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 1.75rem;
  padding: 0 var(--space-3);
  border: 1px solid var(--topbar-border);
  border-radius: 999px;
  background: var(--topbar-hover-bg);
  color: var(--topbar-text);
  font-size: var(--text-xs);
  font-weight: 600;
  white-space: nowrap;
}
/* The page's dot tones are mixed for light surfaces: on the bar the fill goes
   muddy and the soft halo turns into a pale ring around it. The vivid status
   dots read on dark in both themes, and the halo is mixed from the dot itself. */
.shop-chip .conn-dot.ok {
  background: var(--st-active-dot);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--st-active-dot) 22%, transparent);
}
.shop-chip .conn-dot.bad {
  background: var(--st-danger-dot);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--st-danger-dot) 22%, transparent);
}
/* One shop is a name, not a choice: the select is swapped for plain text so no
   caret promises a switch that isn't there. Neither may run long enough to push
   the account menu off the bar. */
.shop-chip-name,
.shop-chip-select {
  max-width: 12rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.shop-chip-select {
  height: 1.5rem;
  /* Right padding clears the caret; the chip's own padding closes the gap. */
  padding: 0 16px 0 0;
  margin-right: calc(-1 * var(--space-1));
  background-color: transparent;
  background-position: right 0 center;
  border: 0;
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--topbar-text);
}
.shop-chip-select:focus-visible { outline: none; box-shadow: var(--topbar-focus-ring); }
/* The popup list is drawn on the page's surface, never the bar's. */
.shop-chip-select option { background: var(--surface); color: var(--text); }

/* ---- Topbar: overdue ----
   The one number up here that means "act now", so it carries a clock and the
   sidebar's amber rather than passing as another neutral control. It is hidden
   at zero, so the colour is only ever spent on a real backlog. */
.overdue-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 1.75rem;
  padding: 0 var(--space-3);
  border: 1px solid color-mix(in srgb, var(--st-pending-dot) 40%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--st-pending-dot) 16%, transparent);
  color: var(--st-pending-dot);
  font: inherit;
  font-size: var(--text-xs);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: background 120ms;
}
.overdue-pill svg { width: 14px; height: 14px; flex: none; }
.overdue-pill:hover { background: color-mix(in srgb, var(--st-pending-dot) 28%, transparent); }
.overdue-pill:focus-visible { outline: none; box-shadow: var(--topbar-focus-ring); }

/* ---- Topbar account menu ---- */
.menu-wrap { position: relative; }
.user-trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 2rem;
  padding: 0 var(--space-2);
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--topbar-text);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
}
.user-trigger:hover,
.user-trigger[aria-expanded="true"] { background: var(--topbar-hover-bg); }
.user-trigger:focus-visible { outline: none; box-shadow: var(--topbar-focus-ring); }

.avatar {
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  flex: none;
  border-radius: var(--radius-sm);
  /* cms4's account avatar is the graphite "brand" tone, not a colour. */
  background: var(--primary);
  color: var(--on-primary);
  font-size: var(--text-2xs);
  font-weight: 700;
  text-transform: uppercase;
}
.avatar-md { width: 2.25rem; height: 2.25rem; font-size: var(--text-sm); }

.topbar-menu {
  position: absolute;
  top: calc(100% + var(--space-2));
  right: 0;
  z-index: 40;
  min-width: 17rem;
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-2);
}
.account-card {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-bottom: var(--space-3);
}
.account-info { min-width: 0; }
.account-name-row { display: flex; align-items: baseline; gap: var(--space-2); }
.account-role {
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-faint);
}
.account-email {
  font-size: var(--text-xs);
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Symmetric vertical padding so the row's contents sit centred in the band
   between its border-top and the next row, rather than hard against the bottom. */
.menu-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-top: 1px solid var(--border);
}
/* The menu's own padding closes the last row. */
.menu-row:last-child { padding-bottom: 0; }
.menu-row-label { font-size: var(--text-sm); font-weight: 500; }
/* Segmented control: the track is the recessed tone so the checked pill reads
   as raised in both themes. */
.seg-switch {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg);
}
.seg-option {
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.5rem;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  transition: background 120ms, color 120ms;
}
.seg-option svg {
  width: 14px;
  height: 14px;
}
.seg-option:hover { color: var(--text); background: var(--surface-hover); }
.seg-option:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.seg-option[aria-checked="true"] {
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-1);
}
/* The hit area bleeds outward so the label still sits on the row's padding
   edge — otherwise the button's own padding stacks on the row's and the text
   drifts away from the border above it. */
.menu-action {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  width: calc(100% + var(--space-2) * 2);
  padding: var(--space-2);
  margin: calc(-1 * var(--space-2));
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
}
.menu-action svg {
  width: 15px;
  height: 15px;
}
.menu-action:hover { background: var(--surface-hover); }
.menu-action:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.menu-action-danger { color: var(--danger-text, #dc2626); }

/* ---- Icon button ---- */
.icon-btn {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text-muted);
  cursor: pointer;
  transition: background 120ms, color 120ms;
}
.icon-btn:hover { background: var(--surface-hover); color: var(--text); }
.icon-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.icon-btn svg {
  width: 14px;
  height: 14px;
}
.badge-plain::before { display: none; }
.late { color: var(--danger); font-weight: 650; white-space: nowrap; }

/* ---- Sortable table headers ---- */
th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { color: var(--text-muted); }

/* ---- Variant table controls ---- */
.var-text {
  width: 120px;
  padding: var(--space-1) var(--space-2);
  font-size: var(--text-sm);
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.var-text:focus-visible { outline: none; border-color: var(--focus); box-shadow: var(--focus-ring); }
/* A native select sizes from its own line-height, not the padding, so it comes
   out taller than a sibling input. Pin the box explicitly. */
.var-text {
  height: 30px;
  box-sizing: border-box;
}

/* ---- Modal ---- */
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: var(--space-5);
  background: light-dark(rgb(23 25 28 / 0.4), rgb(0 0 0 / 0.55));
}
.modal {
  width: 560px;
  max-width: 100%;
  /* No overflow clipping — the carrier dropdown menu must be able to
     extend past the modal box. */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  padding: var(--space-6) var(--space-6) var(--space-5);
}
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-1);
}
.modal-head h3 { margin: 0; font-size: var(--text-lg); }
.modal .field { margin-bottom: var(--space-4); }
.modal .hint { margin-bottom: var(--space-4); }
.modal .check { gap: var(--space-2); }
.modal .input:disabled {
  opacity: 0.5;
  cursor: default;
}
.modal-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}
.modal-foot .btn { white-space: nowrap; }
.modal-foot .btn-primary { min-width: 140px; }
.modal .status { margin-top: var(--space-3); min-height: 0; text-align: center; }

/* Zonos: enabled/disabled dropdown + key input share one row */
.zonos-row { display: flex; gap: var(--space-2); align-items: stretch; }
.zonos-row .select { width: auto; min-width: 8.5rem; }
.zonos-row .input { flex: 1; min-width: 0; }

/* ---- Orders search ---- */
.search-box { position: relative; display: inline-flex; align-items: center; }
.search-icon {
  position: absolute;
  left: var(--space-2);
  width: 14px;
  height: 14px;
  stroke: var(--text-faint);
  stroke-width: 2;
  pointer-events: none;
}
/* Scoped to .search-box so it outranks the generic `.input` padding shorthand,
   which is declared later in this file. */
.search-box .search-input {
  width: 260px;
  padding: var(--space-2) var(--space-5) var(--space-2) calc(var(--space-2) * 2 + 14px);
  font-family: var(--font-sans);
}
.search-input::-webkit-search-cancel-button { display: none; } /* using our own */
.search-clear {
  position: absolute;
  right: var(--space-1);
  border: none;
  background: none;
  color: var(--text-faint);
  font-size: var(--text-xs);
  cursor: pointer;
  padding: var(--space-1);
  line-height: 1;
  border-radius: var(--radius-sm);
}
.search-clear:hover { color: var(--text); }
/* SVG rather than "✕": U+2715 is absent from the Inter subset. */
.search-clear svg {
  display: block;
  width: 13px;
  height: 13px;
}
/* Search hides rows without disturbing their expanded/collapsed state. */
.filtered-out { display: none !important; }
.link-btn.danger { color: var(--danger); font-weight: 600; }
.btn.danger { color: var(--danger); font-weight: 600; }

@media (max-width: 640px) {
  .modal-overlay { padding: var(--space-3); }
  .modal { padding: var(--space-5) var(--space-4) var(--space-4); }
  .modal-foot { flex-direction: column-reverse; }
  .modal-foot .btn { width: 100%; }
}

/* ---- Width toggle ---- */
body.wide .content > div, body.wide .content > header { max-width: none; }
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  border: none;
  border-radius: var(--radius-md);
  font: inherit;
  font-size: var(--text-xs);
  font-weight: 650;
  cursor: pointer;
  background: var(--st-info-bg);
  color: var(--st-info-text);
  transition: filter 120ms;
}
.pill:hover { filter: brightness(0.96); }
.pill:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.content {
  overflow-y: auto;
  /* Grid items default to min-width:auto, so a wide table would push the
     column past 1fr and scroll the whole page sideways. */
  min-width: 0;
  padding: var(--space-6);
}
.content > div,
.content > header,
.content > section {
  max-width: 1060px;
  width: 100%;
  margin-left: auto;
  margin-right: auto;
}
/* Per-user content width: release the reading measure so wide tables can use
   the whole window. The signed-out auth card keeps its own narrower cap. */
body.width-full .content > div,
body.width-full .content > header,
body.width-full .content > section {
  max-width: none;
}

/* ---- Page head ---- */
.page-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin: 0 0 var(--space-4);
}
  .page-head .card-title { margin-right: 0; }
  .page-head .count-note { margin-right: auto; }
  .row-details td { white-space: pre-wrap; font-size: var(--text-sm); color: var(--text-muted); }

/* ---- Cards ---- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  padding: var(--space-5);
  margin-bottom: var(--space-5);
}
.card.flush { padding: 0; overflow: hidden; }
.card-title {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin: 0 0 var(--space-2);
  font-size: var(--text-lg);
  font-weight: 650;
}
.card-sub {
  margin: 0 0 var(--space-4);
  color: var(--text-muted);
  font-size: var(--text-sm);
}

/* ---- Fields ---- */
.field label {
  display: block;
  font-weight: 600;
  font-size: var(--text-sm);
  margin-bottom: var(--space-2);
}
.input {
  width: 100%;
  padding: var(--space-2) var(--space-3);
  min-height: var(--control-h);
  font-size: var(--text-sm);
  font-family: var(--font-sans);
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  outline: none;
  transition: border-color 120ms, box-shadow 120ms;
}
.input-mono { font-family: var(--font-mono); }
.input:focus-visible { border-color: var(--focus); box-shadow: var(--focus-ring); }
.hint {
  margin-top: var(--space-2);
  font-size: var(--text-xs);
  color: var(--text-faint);
}
.hint code { font-family: var(--font-mono); }

/* No font here: the shorthand each select sets for itself is declared earlier in
   the sheet, and would lose to a `font: inherit` shared from this block. */
.select,
.shop-chip-select {
  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'%3E%3Cpath d='M2.5 4.5l3.5 3.5 3.5-3.5' fill='none' stroke='%238e8e96' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  cursor: pointer;
}
.select {
  height: var(--control-h);
  padding: 0 calc(var(--space-3) + 18px) 0 var(--space-3);
  background-color: var(--surface);
  background-position: right var(--space-3) center;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text);
}
.select:focus-visible { outline: none; border-color: var(--focus); box-shadow: var(--focus-ring); }

/* ---- Chip ---- */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding: var(--space-1) var(--space-3);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-muted);
}

/* ---- Scope groups ---- */
#scopes { margin: var(--space-3) 0 var(--space-2); }
.scope-name .badge { margin-left: var(--space-2); text-transform: none; }
.scope-group { margin-top: var(--space-4); }
.scope-group h3 {
  margin: 0 0 var(--space-2);
  font-size: var(--text-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
}
.scope-list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2) var(--space-3);
}

.scope-row {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: border-color 120ms, background 120ms;
}
.scope-row:hover { background: var(--surface-2); }
.scope-row:has(input:checked) {
  border-color: var(--st-info-dot);
  background: var(--st-info-bg);
}
.scope-row input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

/* Standard checkmark — used by scope cards, variation toggles, and any future
   boolean control. Wrap: <label class="check"><input type="checkbox"><span
   class="checkbox">svg</span></label> (scope rows embed the same pair). */
.check {
  position: relative;
  display: inline-flex;
  align-items: center;
  /* The box and its label are one control. This used to be set only inside the
     (now removed) rates modal, so every other checkbox on the app sat flush
     against its own text. */
  gap: var(--space-2);
  cursor: pointer;
}
.check input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.checkbox {
  flex: none;
  width: 18px;
  height: 18px;
  border: 1.5px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  display: grid;
  place-items: center;
  transition: background 120ms, border-color 120ms;
}
.scope-row .checkbox { margin-top: 2px; }
.checkbox svg {
  width: 11px;
  height: 11px;
  stroke: var(--on-primary);
  stroke-width: 3;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 120ms, transform 120ms;
}
.scope-row input:checked + .checkbox,
.check input:checked + .checkbox {
  background: var(--primary);
  border-color: var(--primary);
}
.scope-row input:checked + .checkbox svg,
.check input:checked + .checkbox svg { opacity: 1; transform: scale(1); }
.scope-row input:focus-visible + .checkbox,
.check input:focus-visible + .checkbox { box-shadow: var(--focus-ring); }

.scope-text { min-width: 0; }
.scope-name {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  font-weight: 600;
  font-size: var(--text-sm);
}
.scope-name code {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  font-weight: 400;
}
.scope-desc {
  display: block;
  margin-top: 2px;
  font-size: var(--text-xs);
  color: var(--text-muted);
}

/* ---- Status pills (cms4 bg/text/dot triplets) ---- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 9px 2px 7px;
  border-radius: 999px;
  font-size: var(--text-2xs);
  font-weight: 500;
  white-space: nowrap;
  text-transform: capitalize;
  background: var(--st-draft-bg);
  color: var(--st-draft-text);
}
.badge::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--st-draft-dot);
  flex: none;
}
.badge-accent { background: var(--st-info-bg); color: var(--st-info-text); }
.badge-accent::before { background: var(--st-info-dot); }
.badge-success { background: var(--st-active-bg); color: var(--st-active-text); }
.badge-success::before { background: var(--st-active-dot); }
.badge-muted { background: var(--st-draft-bg); color: var(--st-draft-text); }
/* Amber: something needs a decision but nothing is broken. This variant was
   being used throughout the shipping views before it existed, so every "needs
   attention" badge silently rendered as neutral grey. */
.badge-warn { background: var(--st-pending-bg); color: var(--st-pending-text); }
.badge-warn::before { background: var(--st-pending-dot); }

.link-btn {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--link);
  cursor: pointer;
}
.link-btn:hover { color: var(--link-hover); text-decoration: underline; }

/* ---- Buttons ---- */
.actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-5) 0 0;
}
.btn {
  min-height: 28px;
  padding: 0 var(--space-3);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  font: inherit;
  font-size: var(--text-xs);
  font-weight: 500;
  cursor: pointer;
  transition: background 120ms, border-color 120ms;
}
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.btn:disabled { opacity: 0.55; cursor: default; }
.btn-primary { background: var(--primary); color: var(--on-primary); }
.btn-primary:hover:not(:disabled) { background: var(--primary-hover); }
.btn-primary:active:not(:disabled) { background: var(--primary-hover); }
.btn-ghost {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--text);
}
.btn-ghost:hover:not(:disabled) { background: var(--surface-2); }
.btn-sm { min-height: 28px; padding: 0 var(--space-3); font-size: var(--text-xs); }
.btn-block { width: 100%; justify-content: center; }

/* ---- Status ---- */
.status {
  margin-top: var(--space-4);
  font-size: var(--text-sm);
  min-height: 1.4em;
  white-space: pre-wrap;
}
.status.ok { color: var(--success); }
.status.err { color: var(--danger); }

/* ---- Data tables ---- */
.table-wrap { overflow-x: auto; }
table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}
.data th {
  text-align: left;
  font-size: var(--text-xs);
  font-weight: 600;
  /* Sentence case, not upper. Headings like "Value (CAD)" and "HS code" carry
     real capitalisation that ALL CAPS destroys, and they read faster without
     the extra letter-spacing that upper-casing needs to stay legible. */
  letter-spacing: 0.01em;
  color: var(--text-faint);
  background: var(--table-head);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.data td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
.data tbody tr:last-child td { border-bottom: none; }
.data tbody tr.clickable:hover td { background: var(--surface-2); }
.data .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.thumb {
  display: block;
  width: 64px;
  height: 64px;
  border-radius: var(--radius-md);
  object-fit: cover;
  background: var(--surface-2);
}

/* ---- Listing detail view ---- */
.detail-layout {
  display: grid;
  grid-template-columns: minmax(260px, 400px) 1fr;
  gap: var(--space-5);
  align-items: start;
  margin-bottom: var(--space-5);
}
.gallery-main {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: var(--radius-md);
  background: var(--surface-2);
  display: block;
}
.gallery-thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.gallery-thumbs img {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  cursor: pointer;
  border: 2px solid transparent;
}
.gallery-thumbs img.sel { border-color: var(--focus); }

.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.stat-row .stat { background: var(--surface); border: 1px solid var(--border); }
#sparkline { width: 100%; }
#sparkline svg { display: block; width: 100%; }

/* ---- Edit form ----
   Twelve columns, so a field can be sized to the data it holds: a province code
   has no business being as wide as a street address. Fields are half-width
   unless they say otherwise. */
.form-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--space-4);
}
.form-grid > * { grid-column: span 6; }
.form-grid .span-2 { grid-column: span 2; }
.form-grid .span-3 { grid-column: span 3; }
.form-grid .span-4 { grid-column: span 4; }
.form-grid .span-5 { grid-column: span 5; }
.form-grid .span-7 { grid-column: span 7; }
.form-grid .span-8 { grid-column: span 8; }
.form-grid .full { grid-column: 1 / -1; }
/* The default textarea is sized for editing a description; a two-line note to a
   buyer inside a modal would push the buttons off-screen at that height. */
.modal-manual textarea.input { min-height: 62px; }
textarea.input {
  min-height: 150px;
  resize: vertical;
  line-height: var(--leading);
}
.cell-title {
  max-width: 320px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.clickable { cursor: pointer; }

/* ---- Expanded detail rows ---- */
.row-details td {
  background: var(--surface-2);
  font-size: var(--text-sm);
  color: var(--text-muted);
  padding: var(--space-4);
}
.tag {
  padding: 1px var(--space-2);
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--border);
  font-size: var(--text-xs);
  color: var(--text-muted);
  white-space: nowrap;
}

/* Divides a settings card into sections without starting a new card. */
.sub-head {
  margin: var(--space-5) 0 var(--space-2);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
  font-size: var(--text-sm);
  font-weight: 600;
}

.load-more { margin: var(--space-3); }
.view-status {
  padding: var(--space-3);
  font-size: var(--text-sm);
  color: var(--text-muted);
  min-height: 1.4em;
  white-space: pre-wrap;
}
.view-status:empty { display: none; }
.view-status.err { color: var(--danger); }
.count-note { font-size: var(--text-xs); color: var(--text-faint); }
.label-split {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-2);
}
.stat {
  padding: var(--space-3);
  background: var(--surface-2);
  border-radius: var(--radius-md);
}
.stat-label { font-size: var(--text-xs); color: var(--text-faint); margin-bottom: 2px; }
.stat-value { font-weight: 650; font-size: var(--text-md); word-break: break-word; }

/* ---- Responsive ---- */
@media (max-width: 760px) {
  .shell {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr;
    height: 100vh;
  }
  .sidebar {
    flex-direction: row;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border-right: none;
    border-bottom: 1px solid var(--border);
  }
  .topbar { gap: var(--space-2); padding: 0 var(--space-3); }
  .sidebar-toggle { display: inline-flex; }
  .topbar-left, .topbar-actions { min-width: 0; }
  .user-trigger-name { display: none; }
  body.nav-hidden .sidebar { display: none; }
  .nav { flex-direction: row; gap: var(--space-1); }
  .nav-sec { display: none; }
  .nav-item { padding: var(--space-2); }
  .nav-item > span:not(.nav-badge) { display: none; }
  /* The bar is tight here, so the shop name is what gives up width. */
  .shop-chip { min-width: 0; }
  .shop-chip-name, .shop-chip-select { max-width: 7rem; }
  .content { padding: var(--space-4); }
  .scope-list { grid-template-columns: 1fr; }
  .detail-layout { grid-template-columns: 1fr; }
  .form-grid { grid-template-columns: 1fr; }
  .form-grid > * { grid-column: 1 / -1; }
  .cell-title { max-width: 180px; }
  th.opt, td.opt { display: none; }
}
.carrier-head {
  width: 100%;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.carrier-head:hover { background: var(--surface-hover); }
.carrier-head:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.carrier-name { display: flex; flex-direction: column; min-width: 0; }
.carrier-name b { font-size: var(--text-md); font-weight: 600; }
.carrier-name span { font-size: var(--text-xs); color: var(--text-faint); }
.carrier-head .badge { margin-left: auto; }
.carrier-body {
  padding: 0 var(--space-4) var(--space-4);
  border-top: 1px solid var(--border);
}
.carrier-body > :first-child { margin-top: var(--space-3); }

/* Capability pills, rendered from the server's own Capabilities object so they
   can't drift from what the code actually does. */
.caps { display: flex; gap: var(--space-1); flex-wrap: wrap; margin-bottom: var(--space-3); }
.caps .badge { font-weight: 500; }
.chip {
  font-size: var(--text-2xs);
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border-strong);
  color: var(--text-muted);
  background: var(--surface);
  cursor: pointer;
  font-weight: 500;
  font-family: inherit;
}
.chip:hover { background: var(--surface-hover); color: var(--text); }
.chip.on { background: var(--text); color: var(--surface); border-color: var(--text); }
.chip:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.qid {
  font-size: var(--text-sm);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  /* Full Etsy order numbers are 10 digits. The id is the thing you match
     against Etsy, so it keeps its width and the buyer name gives way. */
  flex: none;
  white-space: nowrap;
}

/* Package picker: one control that holds any number of presets. */
.measure { display: flex; gap: var(--space-4); align-items: flex-end; flex-wrap: wrap; }
.measure .field { display: flex; flex-direction: column; gap: 5px; }
.measure label { font-size: var(--text-2xs); color: var(--text-faint); font-weight: 600; letter-spacing: .03em; text-transform: uppercase; }
.input.num { width: 82px; text-align: right; font-variant-numeric: tabular-nums; padding-right: 26px; }

/* Badges default to capitalize, which suits one-word statuses ("shipped") but
   mangles the phrases the shipping workspace uses ("Ready to buy"). */
.badge-phrase { text-transform: none; }

/* ---- Review & buy ----
   A money table that grows by a row per carrier, and typed advisories that
   aggregate across orders. Ten carriers produce the same four advisories. */
.modal-buy { width: 720px; display: flex; flex-direction: column; max-height: calc(100vh - 96px); }
.modal-buy .modal-head { margin-bottom: var(--space-4); }
.modal-buy .modal-head .count-note { margin-right: auto; }
.modal-body { overflow: auto; display: flex; flex-direction: column; gap: var(--space-4); }
.modal-foot {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}
.modal-foot .hint { margin: 0; }

.buy-table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.buy-table th {
  text-align: left;
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--text-faint);
  font-weight: 600;
  padding: var(--space-2) var(--space-3);
  background: var(--table-head);
  white-space: nowrap;
}
.buy-table td { padding: var(--space-2) var(--space-3); border-top: 1px solid var(--border); }
.buy-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.buy-table tfoot td { border-top: 1px solid var(--border-strong); font-weight: 600; }
.buy-prog-row { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); }
.buy-prog-row .badge { margin-left: auto; }

/* Nav labels truncate rather than wrap when the sidebar is dragged narrow. */
.nav-item > span:not(.nav-badge) {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.nav-item svg { flex: none; }

.shop-assign {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-sm);
  margin: 2px 0;
}
#newUserExtras {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin: 0;
}
.drop-card { margin-bottom: var(--space-4); }

/* ---- Dashboard: tiles and insights ----
   Summary, not workspace. Every number is a change worth knowing or a decision
   waiting, and each links to the page where you'd act on it. */
.stat-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.stat-tile {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  text-align: left;
  font: inherit;
  color: inherit;
}
.stat-tile.clickable { cursor: pointer; }
.stat-tile.clickable:hover { background: var(--surface-hover); border-color: var(--border-strong); }
.stat-tile.clickable:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.tile-label {
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--text-faint);
  font-weight: 600;
}
.tile-value {
  font-size: 1.55rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}

/* ---- Consistent control heights ----
   Text inputs and selects each set their own height and sat a few pixels apart.
   They now share --control-h so a row of mixed controls lines up. */
.input,
select.input,
.search-input { height: var(--control-h); }

/* ---- Signed-out auth + shell polish ---- */
.form-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-4) 0 0;
}
.form-grid .form-actions { margin-top: var(--space-2); }
.form-grid .select,
.field .select { width: 100%; }
.field { margin: 0 0 var(--space-4); }
.form-grid .field { margin: 0; }
/* A hint that belongs to one field, rather than to the group above it. */
.field .hint { display: block; }
/* Indent past the box so a checkbox's hint lines up with its label text. */
.field label.check + .hint { margin-left: calc(18px + var(--space-2)); }
.field label.check {
  display: inline-flex;
  font-weight: 500;
  margin-bottom: 0;
}
.user-trigger-name {
  max-width: 12rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Until the session request resolves we do not know whether this is the app or
   the login card, and painting either one flashes the wrong answer. Hold the
   first paint; applySession() clears this in both branches. `visibility` rather
   than `display` so nothing reflows when it lifts. */
body.booting .shell,
body.booting .topbar { visibility: hidden; }

body.signed-out .sidebar,
body.signed-out .topbar { display: none; }
/* No topbar to clear when signed out, so the shell reclaims the full height. */
body.signed-out .shell {
  grid-template-columns: 1fr;
  height: 100vh;
  margin-top: 0;
}
/* ---- Auth screens (cms4 split-screen) ----
   A brandless decorative panel on the left and a focused form column on the
   right. No logo and no product name: the signed-out surface says nothing
   about which app this is. The panel collapses away on narrow viewports. */
body.signed-out .shell { grid-template-columns: 42% 1fr; }
.auth-aside { display: none; }

/* Brandless "faceted monolith": a steel-charcoal field cut by large diagonal
   glass planes sharing one 115° axis, a luminous seam along the main facet
   edge, and fine brushed striations that catch an overhead light. */
body.signed-out .auth-aside {
  --steel-white: #ffffff;
  --steel-blue-300: #93c5fd;
  --steel-blue-400: #60a5fa;
  --steel-blue-500: #3b82f6;
  --steel-800: #262626;
  --steel-900: #171717;
  display: block;
  position: relative;
  overflow: hidden;
  background:
    linear-gradient(115deg, transparent 0 34%, color-mix(in srgb, var(--steel-white) 3%, transparent) 34%),
    linear-gradient(115deg, transparent 0 calc(58% - 1px), color-mix(in srgb, var(--steel-white) 12%, transparent) calc(58% - 1px) 58%, color-mix(in srgb, var(--steel-white) 3%, transparent) 58%),
    linear-gradient(295deg, transparent 0 72%, color-mix(in srgb, var(--steel-blue-400) 7%, transparent) 100%),
    radial-gradient(ellipse 120% 60% at 50% 0%, color-mix(in srgb, var(--steel-white) 7%, transparent) 0, transparent 60%),
    linear-gradient(160deg,
      color-mix(in srgb, var(--steel-900) 94%, var(--steel-blue-500)) 0%,
      color-mix(in srgb, var(--steel-800) 90%, var(--steel-blue-500)) 100%);
  box-shadow: inset -1px 0 0 color-mix(in srgb, var(--steel-white) 7%, transparent);
}
body.signed-out .auth-aside::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(115deg, transparent 0 27%, color-mix(in srgb, var(--steel-blue-400) 12%, transparent) 34%, transparent 42%),
    linear-gradient(115deg, transparent 0 calc(34% - 1px), color-mix(in srgb, var(--steel-blue-300) 40%, transparent) calc(34% - 1px) calc(34% + 1px), transparent calc(34% + 1px));
  mask-image: linear-gradient(to bottom, #000 45%, transparent 96%);
}
body.signed-out .auth-aside::after {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(115deg,
    color-mix(in srgb, var(--steel-white) 4%, transparent) 0 1px,
    transparent 1px 13px);
  mask-image: radial-gradient(ellipse 110% 85% at 72% 0%, #000 8%, transparent 68%);
}

body.signed-out .content {
  display: grid;
  place-items: center;
  min-height: 100%;
  padding: clamp(var(--space-5), 5vw, var(--space-7));
  background: var(--surface);
}
/* Borderless form column — structure from layout and spacing alone. */
body.signed-out .content > .auth-card {
  display: grid;
  gap: var(--space-5);
  width: min(100%, 24rem);
  max-width: none;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  text-align: left;
}

.auth-head { display: grid; gap: var(--space-2); }
.auth-title {
  margin: 0;
  font-size: 1.875rem;
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.2;
  color: var(--text);
}
.auth-subtitle { margin: 0; color: var(--text-muted); font-size: 1rem; }

.auth-card form { display: grid; gap: var(--space-4); }
.auth-card .field { margin: 0; }
/* Uppercase micro labels. */
.auth-card .field label {
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.auth-card .input { min-height: 2.5rem; }

/* Leading icon and password reveal. */
.affix { position: relative; display: block; }
.affix .input { width: 100%; }
.affix-icon {
  position: absolute;
  left: var(--space-3);
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  width: 1.05rem;
  height: 1.05rem;
  color: var(--text-muted);
  pointer-events: none;
}
.affix-icon svg,
.affix-toggle svg {
  width: 100%;
  height: 100%;
}
.input.affix-lead { padding-left: calc(var(--space-3) * 2 + 1.05rem); }
.input.affix-trail { padding-right: calc(var(--space-3) * 2 + 1.05rem); }
.affix-toggle {
  position: absolute;
  right: var(--space-1);
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0.45rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
}
.affix-toggle:hover { color: var(--text); background: var(--surface-hover); }
.affix-toggle:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* Left-aligned primary submit with a trailing arrow. */
.auth-submit {
  min-height: 2.5rem;
  font-weight: 600;
  letter-spacing: 0.025em;
}
/* The arrow is an inline SVG, not a "→" glyph: the self-hosted Inter files are
   Latin subsets with no U+2192, so a text arrow falls back to a system font and
   lands at the wrong weight, size and baseline. */
.auth-submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
}
.btn-arrow {
  width: 15px;
  height: 15px;
  flex: none;
}
/* Primary and secondary sit on one row; a stacked secondary reads as an
   afterthought floating below the form. */
.auth-card .form-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-2) 0 0;
}
/* Every button on the row shares the control height — the secondary inherits
   only .btn's 28px base otherwise, and sits 12px short of the primary. */
.auth-card .form-actions .btn { min-height: 2.5rem; }
.auth-card .status:empty { display: none; }

@media (max-width: 60rem) {
  body.signed-out .shell { grid-template-columns: 1fr; }
  body.signed-out .auth-aside { display: none; }
}

/* A six-digit code field, sized like every other auth input rather than a
   stunted 12rem box. `text-indent` cancels the trailing letter-space, which
   would otherwise push the digits left of centre. */
.totp-input {
  width: 100%;
  height: 3rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.5em;
  text-indent: 0.5em;
  text-align: center;
  font-size: 1.25rem;
  font-weight: 600;
}
.totp-input::placeholder { color: var(--text-faint); font-weight: 400; }

.totp-enroll {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-5);
  align-items: start;
  margin-bottom: var(--space-4);
}
.totp-qr-wrap {
  background: #fff;
  padding: 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  width: 176px;
  height: 176px;
}
.totp-qr-wrap svg {
  display: block;
  width: 100%;
  height: 100%;
}
@media (max-width: 640px) {
  .totp-enroll {
    grid-template-columns: 1fr;
    justify-items: center;
    text-align: center;
  }
}

/* ---- Carrier roster ----
   One row per carrier, scannable at a glance; settings open in place so the
   page stays the same length however many carriers there are. */
.carrier-roster {
  margin-top: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.carrier-row + .carrier-row { border-top: 1px solid var(--border); }

.carrier-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3) var(--space-4);
  border: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  text-align: left;
}
button.carrier-head { cursor: pointer; }
button.carrier-head:hover { background: var(--surface-hover); }
button.carrier-head:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.carrier-head > svg {
  width: 14px;
  height: 14px;
  flex: none;
  stroke: var(--text-faint);
  stroke-width: 2;
  transition: transform 120ms ease;
}
.carrier-head[aria-expanded="true"] > svg { transform: rotate(90deg); }

.carrier-name { font-weight: 600; margin-right: auto; }
.carrier-mode {
  padding: 2px var(--space-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-2);
  font-size: var(--text-xs);
  color: var(--text-muted);
  text-transform: lowercase;
}
.carrier-health {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 8.5rem;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-muted);
}
.carrier-health .conn-dot { background: var(--text-faint); }
.carrier-health.ok { color: var(--text); }

.carrier-body {
  padding: 0 var(--space-4) var(--space-4);
  border-top: 1px dashed var(--border);
  background: var(--surface-2);
}
.carrier-form { padding-top: var(--space-4); }
/* Blank fields clear stored credentials, so this warning is not decorative and
   should not read as one more grey hint. */
.carrier-warn {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: var(--st-pending-bg);
  color: var(--st-pending-text);
}

.label-split .stored-chip {
  margin-left: var(--space-2);
  padding: 1px var(--space-2);
  border-radius: 999px;
  background: var(--surface-hover);
  font-size: var(--text-2xs);
  font-weight: 600;
  color: var(--text-muted);
}

@media (max-width: 760px) {
  .carrier-head { flex-wrap: wrap; gap: var(--space-2); }
  .carrier-health { min-width: 0; }
}

/* ---- Host installer (/setup) ----
   app.css assumes the signed-in SPA shell (html/body overflow hidden). This
   page is a long form, so it gets its own scrollable surface and reuses the
   signed-out auth field language. */
html:has(body.setup-page),
body.setup-page {
  height: auto;
  min-height: 100%;
  overflow: auto;
}
body.setup-page {
  background: var(--bg);
}
.setup-shell {
  display: grid;
  place-items: start center;
  min-height: 100vh;
  padding: clamp(var(--space-5), 5vw, var(--space-7));
}
.setup-card {
  width: min(100%, 36rem);
  display: grid;
  gap: var(--space-5);
  padding: var(--space-6);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: none;
}
.setup-form { display: grid; gap: var(--space-4); }
.setup-fieldset {
  margin: 0;
  padding: 0;
  border: 0;
  display: grid;
  gap: var(--space-4);
}
.setup-fieldset legend {
  padding: 0;
  margin: 0 0 var(--space-1);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.setup-card .field { margin: 0; }
.setup-card .field label {
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.setup-card .form-actions {
  display: flex;
  margin: var(--space-2) 0 0;
}
.setup-card .form-actions .btn { min-height: 2.5rem; }
.setup-flash {
  margin: 0;
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--accent-soft);
  color: var(--text);
  font-size: var(--text-sm);
}
.setup-token pre {
  margin: var(--space-3) 0 0;
  padding: var(--space-3);
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text);
}
.setup-pending ul {
  margin: var(--space-2) 0 0;
  padding-left: 1.2rem;
  color: var(--text-muted);
  font-size: var(--text-sm);
}
@media (min-width: 40rem) {
  .setup-fieldset { grid-template-columns: 1fr 1fr; }
  .setup-fieldset legend,
  .setup-fieldset .hint,
  .setup-form > .hint,
  .setup-form > .form-actions { grid-column: 1 / -1; }
  .setup-fieldset .field:has(#etsy_redirect_uri) { grid-column: 1 / -1; }
}
