/* ═══════════════════════════════════════════════════════════════════
   BUSINESS app shell (what end users see). This file is served on
   business hosts, so it stays brand-clean — no platform naming.
   Customer App design: collapsible icon rail (engine-level chrome — the
   menus it shows are driven by the product manifest + panel registry),
   no top bar (the brand row lives inside the page), full-bleed photo
   hero on Home. Themed per business via --brand; Instrument Sans;
   brand-isolated (no platform look). Rail palette (--rl-*) is emitted
   inline by shell.php because it derives from the business accent.
   ═══════════════════════════════════════════════════════════════════ */

@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/InstrumentSans-latin-ext.woff2") format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/InstrumentSans-latin.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --brand: #2563eb;             /* overridden inline per business */
  --brand-ink: #ffffff;
  /* Greys corrected 2026-08-01 alongside dashboard.css — customers read
     these on every business's app, and --muted (2.43:1), --faint (1.92:1)
     and --sub (3.54:1) all failed WCAG AA against the white panel. Same
     ramp, same reasoning: darker until each one that carries words clears
     4.5:1, hierarchy kept. */
  --ink: #0f172a; --ink-2: #4b5563; --ink-3: #5d6470; --muted: #6b7280; --faint: #7e8693;
  --ink-strong: #0f172a;
  --sub: #666e7d; --lab: #374151; --body: #475569; --dim: #5b6472;
  --bg: #ffffff; --panel: #ffffff; --border: #ecedf1; --border-2: #e7e9f0;
  --sep: #f0f2f6; --sep-2: #eef0f4; --canvas: #f8fafc; --gbord: #e4e7ec;
  --hover: #eceef2; --row: #f4f6f9; --head: #fafbfd; --dash: #d7ddea; --ghost-hov: #f6f7f9;
  --pillg-bg: #f1f5f9; --pillg-fg: #475569;
  --in-bord: #dfe3ea; --in-focus: #b6c4d8;
  --ok-bg: #ecfdf5; --ok-fg: #059669; --warn-bg: #fffbeb; --warn-fg: #b45309;
  --bad-bg: #fef2f2; --bad-fg: #b91c1c;
  --shadow: 0 1px 2px rgba(16,24,40,.04);
}

/* Dark theme — the end user's own switch (rail toggle, per-device).
   Token swap only; layout never changes. Brand accent stays the
   business's color in both themes. */
html[data-theme="dark"] {
  color-scheme: dark;
  /* Dark side of the same 2026-08-01 correction: --faint was 3.02:1 and
     --muted 4.64:1 on the panel; lighter, not darker, is what helps here. */
  --ink: #e6e9ee; --ink-2: #b9c0cb; --ink-3: #9aa3af; --muted: #89919e; --faint: #767e8a;
  --ink-strong: #f2f5f9;
  --sub: #949cab; --lab: #c6cdd8; --body: #b3bbc7; --dim: #98a1ad;
  --bg: #12151a; --panel: #171b21; --border: #1f242b; --border-2: #242a32;
  --sep: #1f242b; --sep-2: #20262e; --canvas: #10141a; --gbord: #2a313b;
  --hover: #1d2229; --row: #1a1f26; --head: #141820; --dash: #39414d; --ghost-hov: #1d2229;
  --pillg-bg: #1d2229; --pillg-fg: #aeb6c2;
  --in-bord: #2a313b; --in-focus: #3d4756;
  --ok-bg: rgba(16,185,129,.14); --ok-fg: #34d399;
  --warn-bg: rgba(245,158,11,.12); --warn-fg: #fbbf24;
  --bad-bg: rgba(239,68,68,.12); --bad-fg: #f87171;
  --shadow: 0 1px 2px rgba(0,0,0,.35);
}

html, body { margin: 0; padding: 0; }
* { box-sizing: border-box; }
body {
  font-family: "Instrument Sans", ui-sans-serif, system-ui, sans-serif;
  color: var(--ink); background: var(--panel); -webkit-font-smoothing: antialiased;
}
a { color: var(--brand); text-decoration: none; }
button { font-family: inherit; }
[hidden] { display: none !important; }

.sh { width: 100vw; height: 100vh; display: flex; overflow: hidden; background: var(--panel); }

/* ── Icon rail (collapsed 54px by default; html[data-rail=open] → 232px).
      The rail is ENGINE-LEVEL CHROME: every engine renders inside it, and
      the items come from the panel registry the product manifest drives.

      54px, NOT 68px (Uche, 2026-08-06: "make LaundryStack and the others
      exactly the width of the Sitejac one — that's the width I like"). The
      HTML editor's own rail has always been 54px, so every product on the
      shell sat 14px wider than Site Creator and the two never matched
      side by side. The 40px pill is unchanged; only the gutter either side
      narrows, 14px → 7px (40 + 7 + 7 = 54). ── */
.sh-rail {
  width: 54px; height: 100%; flex: none; display: flex; flex-direction: column;
  padding: 14px 7px; overflow: hidden;
  background: var(--rl-bg); border-right: 1px solid var(--rl-border);
}
html[data-rail="open"] .sh-rail { width: 232px; padding: 14px 12px; }

.rl-nav {
  flex: 1; overflow-y: auto; overflow-x: hidden; display: flex; flex-direction: column;
  gap: 3px; margin-top: 12px; min-height: 0;
  scrollbar-width: none; -ms-overflow-style: none;
}
.rl-nav::-webkit-scrollbar { display: none; width: 0; height: 0; }

.rl-it {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; margin: 0 auto; border-radius: 12px;
  border: none; background: none; padding: 0; text-align: left;
  color: var(--rl-idle); font-size: 13.5px; font-weight: 500;
  cursor: pointer; transition: background .12s;
}
.rl-it:hover { background: var(--rl-hover); }
/* The rail's brand tile — the same 40px cell as every other rail item, with
   the business mark in it, growing a name when the rail is opened. Rendered
   only on a product's own welcome page; see shell.php. */
.rl-brand { margin-bottom: 6px; }
.rl-brand .mk {
  width: 32px; height: 32px; flex: none; border-radius: 10px; overflow: hidden;
  display: grid; place-items: center;
  background: var(--brand); color: var(--brand-ink);
  font: 700 14px/1 Inter, system-ui, sans-serif;
  box-shadow: 0 6px 14px -7px color-mix(in srgb, var(--brand) 56%, transparent);
}
.rl-brand .mk img { width: 100%; height: 100%; object-fit: cover; display: block; }
html[data-rail="open"] .rl-brand .lb { font-weight: 700; color: var(--rl-label); }
.rl-it .ic { display: flex; color: var(--rl-idle); flex: none; position: relative; }
.rl-it .lb { display: none; }
.rl-it .lk { display: none; }
html[data-rail="open"] .rl-it {
  width: 100%; height: auto; margin: 0; justify-content: flex-start;
  gap: 11px; padding: 9px 11px; border-radius: 10px; color: var(--rl-label);
}
html[data-rail="open"] .rl-it .lb { display: block; flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
html[data-rail="open"] .rl-it .lk { display: flex; color: var(--rl-lock); }
.rl-it.on { background: var(--rl-on-bg); box-shadow: var(--rl-on-shadow); }
.rl-it.on:hover { background: var(--rl-on-bg); }
.rl-it.on .ic { color: var(--rl-on-ink); }
html[data-rail="open"] .rl-it.on { color: var(--rl-on-ink); font-weight: 600; }

.rl-div { height: 1px; flex: none; background: var(--rl-div); margin: 9px 8px; }
html[data-rail="open"] .rl-div { margin: 9px 10px; }
.rl-div--foot { margin: 10px 8px; }
html[data-rail="open"] .rl-div--foot { margin: 10px 10px; }

.rl-foot { margin-top: 2px; }

/* Bell unread badge */
.rl-bdg {
  position: absolute; top: -5px; right: -6px; min-width: 14px; height: 14px; padding: 0 3px;
  border-radius: 999px; background: #ef4444; color: #fff;
  font-size: 9px; font-weight: 700; line-height: 14px; text-align: center;
}

/* The switcher — an <a>, not a <button>, because /switch is a real navigation
   off this host. .rl-it was written for buttons, so restate the two things an
   anchor brings with it: an underline, and the browser's link colour. */
.rl-switch { text-decoration: none; color: var(--rl-idle); }
.rl-switch:hover { background: var(--rl-hover); color: var(--rl-label); }

/* Member avatar in the rail */
.rl-av {
  width: 26px; height: 26px; border-radius: 99px; flex: none;
  background: color-mix(in srgb, var(--brand) 20%, transparent); color: var(--rl-av-ink);
  display: flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 700;
}

/* Theme toggle icon swap */
.sh-theme-moon { display: flex; }
.sh-theme-sun { display: none; }
html[data-theme="dark"] .sh-theme-sun { display: flex; }
html[data-theme="dark"] .sh-theme-moon { display: none; }

/* ── Rail tooltip (collapsed rail only) ── */
.sh-tip {
  position: fixed; left: 72px; transform: translateY(-50%);
  padding: 8px 13px; border-radius: 8px; background: #0b101d; color: #fff;
  font-size: 12.5px; font-weight: 600; white-space: nowrap;
  box-shadow: 0 12px 28px -10px rgba(0,0,0,.5); z-index: 200; pointer-events: none;
  animation: tipIn .15s ease;
}
.sh-tip .arr {
  position: absolute; left: -4px; top: 50%; width: 9px; height: 9px;
  background: #0b101d; transform: translateY(-50%) rotate(45deg); border-radius: 2px;
}
@keyframes tipIn { from { opacity: 0; transform: translateY(-50%) translateX(-5px); } to { opacity: 1; transform: translateY(-50%) translateX(0); } }

/* ── Bell drawer (bottom-left, next to the rail) ── */
.sh-drawer {
  position: fixed; left: 76px; bottom: 16px; width: 330px; max-height: 420px; overflow: auto;
  background: var(--panel); border: 1px solid var(--border-2); border-radius: 14px;
  box-shadow: 0 18px 40px -12px rgba(16,24,40,.28); z-index: 60;
}
html[data-rail="open"] .sh-drawer { left: 240px; }
.sh-drawer .hd { display: flex; align-items: center; padding: 13px 16px; border-bottom: 1px solid var(--sep); }
.sh-drawer .hd .t { font-size: 13.5px; font-weight: 700; color: var(--ink-strong); flex: 1; }
.sh-drawer .hd .mk { font-size: 12px; font-weight: 600; color: var(--brand); cursor: pointer; border: none; background: none; padding: 0; }
.sh-drawer .it { display: flex; gap: 11px; padding: 12px 16px; border-bottom: 1px solid var(--ghost-hov); cursor: pointer; }
.sh-drawer .it:last-child { border-bottom: none; }
.sh-drawer .it:hover { background: var(--canvas); }
.sh-drawer .dot { width: 7px; height: 7px; border-radius: 99px; background: var(--gbord); flex: none; margin-top: 5px; }
.sh-drawer .it.unread .dot { background: var(--brand); }
.sh-drawer .bt { display: block; font-size: 13px; font-weight: 600; color: var(--ink-strong); }
.sh-drawer .bb { display: block; font-size: 12px; color: var(--sub); margin-top: 2px; line-height: 1.45; }
.sh-drawer .bw { display: block; font-size: 11px; color: var(--muted); margin-top: 3px; }
.sh-drawer .empty { padding: 26px 16px; text-align: center; font-size: 12.5px; color: var(--muted); }

/* ── Page (scrolls as one document; hero backdrop sits behind everything) ── */
.sh-page { flex: 1; overflow-y: auto; min-width: 0; }
.sh-pagein { position: relative; min-height: 100%; display: flex; flex-direction: column; }
.sh-backdrop { position: absolute; inset: 0; z-index: 0; display: none; }
.sh-pagein.hero .sh-backdrop { display: block; }

/* ── A PRODUCT'S OWN WELCOME: TWO COLUMNS, PITCH THEN FILM (2026-08-06) ──
   Site Creator has always read left-to-right — the pitch column, then the
   film beside it — and every product is now cloned onto that page. This is
   the half that makes the order right.

   What it replaces, and why it looked wrong: the old `.sh-backdrop` is a
   FULL-WIDTH wash behind the whole page, with the panel column floating in
   the middle of it (`#shContent > .pn` is `margin: 0 auto`). Dropping a
   photograph into that slot put the picture underneath the words and left
   the two side by side in the wrong order — Uche, 2026-08-06: *"you
   accidentally flipped the position of the video and the content."*

   So on this page only: the column goes hard left and opaque, and the film
   is held to everything on its right. Every other panel is untouched — the
   wash and the centred column are exactly as they were.

   KEYED ON THE WRAPPER, NOT ON `.hero`. That was the first attempt and it did
   nothing: `.hero` is added server-side but the panel router takes it straight
   back off — `page.classList.toggle('hero', !!content.querySelector('[data-hero]'))`
   in shell.php, and this column stopped shipping a `[data-hero]` marker when it
   was redrawn. `.sh-herocanvas` exists only when a product hero was rendered,
   so it is the honest signal and it cannot be toggled away underneath us. */
.sh-pagein:has(.sh-herocanvas) { --sh-pitch: 420px; }
/* 420px — the editor's own pane width, so a product on the shell and Site
   Creator line up exactly (Uche, 2026-08-06: "make the other ones exactly
   that width"). It was 440px, tuned to give the welcome copy room to fit
   without a scrollbar; the note there warned not to shrink it without
   re-checking the height. Re-checked at 1728x1000 on both live hero
   products — LaundryStack and EstateStack — and neither scrolls. If you
   narrow it further, check again: narrower wraps more, grows taller, and
   the scrollbar comes back. */
.sh-pagein:has(.sh-herocanvas) .sh-herocanvas {
  position: absolute; top: 0; right: 0; bottom: 0; left: var(--sh-pitch);
  z-index: 0; overflow: hidden;
}
/* The column is opaque, so the photograph stops cleanly at its edge instead
   of washing under the type.

   AND IT ENDS ON A LINE (Uche, 2026-08-07: "put a thin line between the
   customer pages panel and the home page… still beautiful but very thin").
   The pitch column and the picture met on a bare edge, which reads as the
   photograph having been cropped rather than as two panes of one page. An
   INSET shadow, not a border: a border would widen the pane to 421px and put
   it half a pixel out of step with the film beside it and with Site Creator's
   own pane, which is measured from the same 420px. --border-2 rather than the
   fainter --sep the lists inside use: this one has a photograph on the other
   side of it, and --sep disappears against anything but white. */
.sh-pagein:has(.sh-herocanvas)::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0;
  width: var(--sh-pitch); background: var(--bg); z-index: 0;
  box-shadow: inset -1px 0 0 var(--border-2);
}
.sh-pagein:has(.sh-herocanvas) #shContent > .pn {
  max-width: var(--sh-pitch); margin: 0;
  /* Breathing room at the top, added HERE and not in the panel itself.
     The panel opens with 6px because in Site Creator's editor it sits under a
     toolbar that already provides the gap; on this page there is nothing above
     it at all, so 6px put the badge hard against the top of the window while
     the column ran out of content well before the bottom (Uche, 2026-08-06:
     *"the content moved up to the very top, not looking good… plenty of space
     at the bottom"*). 30px also lands the badge level with the Log in /
     Get started buttons floating opposite it. The footer is pinned by
     margin-top:auto, so this spends the slack at the bottom rather than
     making the column any taller. */
  padding-top: 30px;
}
/* THE MARK IS IN THE RAIL ON THIS PAGE (Uche, 2026-08-06: *"the logo for
   sitejac is placed inside the sidebar as a small icon… move it out of the
   page and do it the way it was done"*). shell.php stops rendering the brand
   button here, so the row has only its two buttons left — and it lifts out of
   the flow to float over the film's top-right corner. That is what lets the
   pitch start at the very top of its column instead of a row of height below
   it. The tile itself is `.rl-brand`, further down. */
.sh-pagein:has(.sh-herocanvas) .sh-brandrow {
  position: absolute; top: 22px; right: 40px; left: auto; z-index: 4; padding: 0;
}
.sh-pagein:has(.sh-herocanvas) .sh-brandrow .sp { display: none; }

/* No room for two columns on a phone: the film steps aside and the pitch
   column becomes the page, which is what it already was before today. */
@media (max-width: 900px) {
  .sh-pagein:has(.sh-herocanvas) .sh-herocanvas { display: none; }
  .sh-pagein:has(.sh-herocanvas)::before { display: none; }
  .sh-pagein:has(.sh-herocanvas) #shContent > .pn { max-width: 560px; margin: 0 auto; }
  .sh-pagein:has(.sh-herocanvas) .sh-brandrow .word { color: var(--ink-strong); }
}

/* Brand row — page content, not a bar */
.sh-brandrow { position: relative; z-index: 3; display: flex; align-items: center; gap: 11px; padding: 22px 40px 0; }
.sh-brandrow .bmark { display: flex; align-items: center; gap: 11px; cursor: pointer; border: none; background: none; padding: 0; }
.sh-brandrow .mark {
  width: 36px; height: 36px; border-radius: 11px; background: var(--brand); color: var(--brand-ink);
  font-size: 15.5px; font-weight: 700; display: flex; align-items: center; justify-content: center;
  flex: none; overflow: hidden; box-shadow: 0 6px 14px -7px color-mix(in srgb, var(--brand) 56%, transparent);
}
.sh-brandrow .mark img { width: 100%; height: 100%; object-fit: cover; }
.sh-brandrow .word { font-size: 17px; font-weight: 700; letter-spacing: -.01em; color: var(--ink-strong); white-space: nowrap; }
.sh-pagein.hero .sh-brandrow .word { color: #ffffff; }
.sh-brandrow .sp { flex: 1; }
.sh-brandrow .tgh {
  height: 36px; padding: 0 15px; border-radius: 10px; border: 1px solid var(--gbord);
  background: var(--panel); color: var(--lab); font-size: 13px; font-weight: 600; cursor: pointer;
}
.sh-pagein.hero .sh-brandrow .tgh {
  border-color: rgba(255,255,255,.3); background: rgba(255,255,255,.1); color: #fff; backdrop-filter: blur(4px);
}
.sh-brandrow .who {
  display: flex; align-items: center; gap: 9px; font-size: 13px; font-weight: 600; color: #374151;
  height: 36px; padding: 0 13px 0 6px; border-radius: 999px;
  background: rgba(255,255,255,.92); border: 1px solid #e4e7ec; backdrop-filter: blur(6px);
}
html[data-theme="dark"] .sh-pagein:not(.hero) .sh-brandrow .who { background: var(--head); border-color: var(--border-2); color: var(--lab); }
.sh-brandrow .who .av {
  width: 28px; height: 28px; border-radius: 99px; background: color-mix(in srgb, var(--brand) 10%, transparent);
  color: var(--brand); display: flex; align-items: center; justify-content: center; font-size: 10.5px; font-weight: 700;
}

#shContent { position: relative; z-index: 2; flex: 1; display: flex; flex-direction: column; min-width: 0; }
/* Every customer page is DRAWN FOR A COLUMN (the design pack's 410px panel,
   which is also what the engine editor gives them). Let one run the full
   width of a desktop window and it falls apart: a 16:10 video becomes a
   1800px wall, buttons stretch end to end, and 12px meta text sits on a
   line a metre long. So the shell reads them in a column too — same page,
   same proportions, wherever it is shown. */
#shContent > .pn {
  flex: 1; display: flex; flex-direction: column; min-width: 0;
  width: 100%; max-width: 560px; margin: 0 auto;
}
/* Panels that deliberately own the whole area (the engine workspace, the
   hero landing) opt out by marking themselves full-bleed. */
#shContent > .pn:has(> [data-full]) { max-width: none; }

/* ── THE PANEL'S NAME, WRITTEN ONCE, BY THE FRAME ──────────────────────
   Every customer page used to print its own name at the top, and whatever
   was framing it printed the name again above that — "Add-ons" sitting
   under "ADD-ONS", "Billing" under "BILLING". Two rival headers had grown
   up as well: a compact sticky bar on Billing, Wallet, Downloads and
   Project, a 22px hero title on Add-ons, Support, Notes and Legal, so the
   same furniture changed shape as you moved around the rail.

   The frame owns the name now. It already knows it — the rail item that
   opened the panel carries the label, from the manifest — so the page
   cannot disagree with the chrome, and the content starts at the top.

   A panel with something to say BESIDE its name ships one .pn-meta block
   as its first element. The frame lifts it into the header: the shell
   into .pn-head here, the engine workspace into its column header. Move
   the node rather than copy it, so ids and listeners survive. */
.pn-head {
  position: sticky; top: 0; z-index: 6; flex: none;
  display: flex; align-items: center; gap: 10px;
  min-height: 42px; padding: 0 14px;
  background: var(--bg); border-bottom: 1px solid var(--sep);
}
.pn-head__t { margin: 0; font: 600 13px/1 Inter, sans-serif; color: var(--ink-strong); }
/* It is invisible until a frame has lifted it, so it never flashes in the
   body on the way up. data-lifted is set by whichever frame took it, which
   keeps this rule from having to name any engine's own classes. */
.pn-meta { display: none; }
.pn-meta[data-lifted] {
  margin-left: auto; display: flex; align-items: center; gap: 10px;
  font-size: 11.5px; color: var(--muted);
}
.pn-meta__a { font-weight: 600; color: var(--brand); cursor: pointer; }
.pn-meta__a:hover { text-decoration: underline; }

/* ── Buttons, inputs, pills ── */
.shbtn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border: none; border-radius: 9px; background: var(--brand); color: var(--brand-ink);
  font-weight: 600; font-family: inherit; cursor: pointer;
  box-shadow: 0 6px 14px -6px color-mix(in srgb, var(--brand) 50%, transparent);
  text-decoration: none;
}
.shbtn:hover { filter: brightness(.92); }
.shbtn:disabled { opacity: .6; cursor: default; filter: none; }
.shbtn.lg { border-radius: 11px; }
.shbtn.flat { box-shadow: none; }
.shbtn.tint { background: color-mix(in srgb, var(--brand) 8.6%, transparent); color: var(--brand); box-shadow: none; }
.shghost {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  border: 1px solid var(--gbord); border-radius: 8px; background: var(--panel); color: var(--lab);
  font-weight: 600; font-family: inherit; cursor: pointer; text-decoration: none;
}
.shghost:hover { background: var(--ghost-hov); }

.shin {
  border: 1px solid var(--in-bord); border-radius: 10px; font-family: inherit;
  color: var(--ink); background: var(--panel); outline: none;
  transition: border-color .12s, box-shadow .12s;
}
.shin:focus { border-color: var(--in-focus); box-shadow: 0 0 0 3px rgba(15,23,42,.06); }
html[data-theme="dark"] .shin:focus { box-shadow: 0 0 0 3px rgba(255,255,255,.05); }
.shin::placeholder { color: var(--muted); }

.pill {
  display: inline-flex; align-items: center; padding: 3px 10px; border-radius: 999px;
  background: color-mix(in srgb, var(--brand) 8.6%, transparent); color: var(--brand);
  font-size: 11.5px; font-weight: 600; white-space: nowrap;
}
.pill.gray { background: var(--pillg-bg); color: var(--pillg-fg); }
.pill.bad { background: var(--bad-bg); color: var(--bad-fg); }
.pill.ok { background: var(--ok-bg); color: var(--ok-fg); }

.au-err {
  display: none; margin: 0 0 12px; padding: 9px 12px; border-radius: 9px;
  background: var(--bad-bg); color: var(--bad-fg);
  font-size: 12.5px; font-weight: 500; line-height: 1.45;
}
.au-err.show { display: block; }

/* Field-specific login errors: only the guilty field goes red and shakes. */
.shin-bad {
  border-color: var(--bad-fg) !important;
  box-shadow: 0 0 0 3px rgba(220, 38, 38, .12) !important;
}
@keyframes shinShake {
  20% { transform: translateX(-5px); }
  40% { transform: translateX(5px); }
  60% { transform: translateX(-3px); }
  80% { transform: translateX(3px); }
}
.shin-shake { animation: shinShake .3s ease; }

/* ── Page scaffolding (design content pages) ── */
.pg { padding: 36px 40px 60px; width: 100%; }
.pg-h { margin: 0; font-size: 22px; font-weight: 700; letter-spacing: -.02em; color: var(--ink-strong); }
.pg-sub { margin: 7px 0 0; font-size: 13.5px; color: var(--sub); }
.crd { border: 1px solid var(--border-2); border-radius: 16px; background: var(--panel); }

.shrow { transition: background .12s; }
.shrow:hover { background: var(--canvas); }

/* Numbered content rows (tutorials / what's new) */
.crow { display: flex; align-items: flex-start; gap: 14px; padding: 16px 6px; border-bottom: 1px solid var(--sep); border-radius: 10px; }
.crow .num {
  width: 28px; height: 28px; border-radius: 9px; flex: none; margin-top: 1px;
  background: color-mix(in srgb, var(--brand) 7.8%, transparent); color: var(--brand);
  display: flex; align-items: center; justify-content: center; font-size: 12.5px; font-weight: 700;
}
.crow .ct { display: block; font-size: 14px; font-weight: 700; color: var(--ink-strong); }
.crow .cb { display: block; font-size: 13px; color: var(--sub); margin-top: 3px; line-height: 1.55; }
.crow .tag { font-size: 11.5px; color: var(--muted); flex: none; }

/* ── Home hero (full-bleed photo backdrop) ── */
/* The reserved height and the padding are what decide whether this page
   scrolls, and it must not: the whole point is the pitch and its button
   landing in one screen. 400px/64px/56px held 158px of empty space above a
   product whose feature cards carry body text (LaundryStack), which put it
   41px past the fold. Content still drives the height — this is only the
   floor. */
.hm-wrap { position: relative; z-index: 2; min-height: calc(100vh - 480px); display: flex; align-items: center; }
.hm-in { width: 100%; max-width: 1160px; padding: 48px 40px 40px 48px; }
.hm-box { max-width: 640px; }
.hm-badge {
  display: inline-flex; align-items: center; gap: 7px; padding: 5px 13px; border-radius: 999px;
  background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.14);
  color: #fff; font-size: 12px; font-weight: 600;
}
.hm-h1 {
  margin: 22px 0 0; font-size: 54px; font-weight: 700; letter-spacing: -.03em; line-height: 1.08;
  color: #ffffff; text-wrap: pretty; text-shadow: 0 2px 20px rgba(2,6,23,.35);
}
.hm-h1 em { font-style: normal; color: color-mix(in srgb, var(--brand) 55%, #ffffff); }
.hm-lead {
  margin: 18px 0 0; font-size: 17px; line-height: 1.65; color: rgba(255,255,255,.74);
  text-wrap: pretty; max-width: 500px; text-shadow: 0 1px 12px rgba(2,6,23,.4);
}
.hm-ctas { display: flex; align-items: center; gap: 14px; margin-top: 32px; }
.hm-ghost {
  height: 52px; padding: 0 24px; border-radius: 12px; border: 1px solid rgba(255,255,255,.28);
  background: rgba(255,255,255,.08); color: #fff; font-size: 15px; font-weight: 600;
  font-family: inherit; cursor: pointer; backdrop-filter: blur(4px);
}
.hm-proof { display: flex; align-items: center; gap: 12px; margin-top: 30px; font-size: 13.5px; color: rgba(255,255,255,.65); }
.hm-proof .avs { display: flex; }
.hm-proof .avs span {
  width: 30px; height: 30px; border-radius: 99px; display: inline-block;
  border: 2px solid rgba(255,255,255,.5); background-position: center; background-size: cover;
}
.hm-proof .avs span + span { margin-left: -9px; }
.hm-feats {
  position: relative; z-index: 2; min-width: 0; width: 100%; max-width: 1160px;
  /* 64px bottom put the page 23px over the fold once the steps row landed
     under it — a scrollbar for dead space. */
  padding: 10px 40px 40px 48px; display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px;
}
.hm-feat {
  border: 1px solid rgba(255,255,255,.14); border-radius: 20px; padding: 26px;
  background: rgba(255,255,255,.07); backdrop-filter: blur(12px);
  transition: transform .16s ease, background .16s ease, border-color .16s ease;
}
.hm-feat:hover { transform: translateY(-2px); background: rgba(255,255,255,.11); border-color: rgba(255,255,255,.22); }
.hm-feat .ic {
  width: 40px; height: 40px; border-radius: 12px; color: #fff;
  background: linear-gradient(135deg, color-mix(in srgb, var(--brand) 40%, transparent), color-mix(in srgb, var(--brand) 15%, transparent));
  display: flex; align-items: center; justify-content: center;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.18);
}
.hm-feat .t { font-size: 16px; font-weight: 700; color: #ffffff; margin-top: 16px; letter-spacing: -.01em; }
.hm-feat p { margin: 8px 0 0; font-size: 13.5px; color: rgba(255,255,255,.62); line-height: 1.6; }

/* ── Pricing ── */
.plc {
  position: relative; width: 300px; border: 1.5px solid var(--border-2); border-radius: 20px;
  padding: 26px 26px 24px; background: var(--panel); text-align: left;
}
.plc.popular { border-color: var(--brand); box-shadow: 0 18px 40px -18px color-mix(in srgb, var(--brand) 33%, transparent); }
.plc .pop {
  position: absolute; top: -11px; left: 50%; transform: translateX(-50%);
  padding: 3px 12px; border-radius: 999px; background: var(--brand); color: var(--brand-ink);
  font-size: 11px; font-weight: 700; white-space: nowrap;
}
.plc .nm { font-size: 13px; font-weight: 700; color: var(--sub); letter-spacing: .06em; text-transform: uppercase; }
.plc .pr { display: flex; align-items: baseline; gap: 5px; margin-top: 8px; }
.plc .pr b { font-size: 38px; font-weight: 700; letter-spacing: -.03em; color: var(--ink-strong); font-variant-numeric: tabular-nums; }
.plc .pr span { font-size: 13.5px; color: var(--muted); }
.plc-feats { display: flex; flex-direction: column; gap: 9px; margin-top: 18px; }
.plc-feats > span { display: flex; gap: 9px; font-size: 13px; color: var(--body); line-height: 1.45; }
.plc-feats svg { flex: none; margin-top: 1px; color: var(--brand); }

/* ── Auth (centered mini form, no card chrome) ── */
.au-wrap { flex: 1; display: flex; align-items: flex-start; justify-content: center; padding: 60px 40px; }
.au-box { width: 100%; max-width: 360px; }
.au-mark {
  width: 52px; height: 52px; border-radius: 14px; background: var(--brand); color: var(--brand-ink);
  font-size: 22px; font-weight: 700; display: flex; align-items: center; justify-content: center;
  margin: 0 auto; overflow: hidden; box-shadow: 0 6px 14px -7px color-mix(in srgb, var(--brand) 56%, transparent);
}
.au-mark img { width: 100%; height: 100%; object-fit: cover; }
.au-box h2 { margin: 18px 0 0; font-size: 23px; font-weight: 700; letter-spacing: -.02em; text-align: center; color: var(--ink-strong); }
.au-box .sub { margin: 8px 0 0; font-size: 13.5px; color: var(--sub); text-align: center; line-height: 1.5; }
.au-fields { display: flex; flex-direction: column; gap: 12px; margin-top: 24px; }
.au-switch { font-size: 13px; color: var(--sub); text-align: center; margin-top: 16px; }
.au-switch a { color: var(--brand); font-weight: 600; cursor: pointer; }

/* Form fields (intake form, support form) */
.au-f { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.au-f label { font-size: 12.5px; font-weight: 600; color: var(--lab); }
.au-f input, .au-f textarea, .au-f select {
  border: 1px solid var(--in-bord); border-radius: 10px; font-family: inherit;
  font-size: 13.5px; background: var(--panel); color: var(--ink); outline: none;
  transition: border-color .12s, box-shadow .12s;
}
.au-f input { height: 42px; padding: 0 13px; }
.au-f textarea { min-height: 84px; resize: vertical; line-height: 1.5; padding: 10px 13px; }
.au-f select { height: 42px; padding: 0 10px; }
.au-f input:focus, .au-f textarea:focus, .au-f select:focus { border-color: var(--in-focus); box-shadow: 0 0 0 3px rgba(15,23,42,.06); }
.au-choices { display: flex; flex-wrap: wrap; gap: 8px; }
.au-choices label {
  display: flex; align-items: center; gap: 7px; padding: 8px 13px; border: 1px solid var(--in-bord);
  border-radius: 9px; font-size: 13px; font-weight: 500; color: var(--ink-2); cursor: pointer;
}
.au-choices input { accent-color: var(--brand); width: 15px; height: 15px; }
.au-choices label:has(input:checked) { border-color: var(--brand); background: color-mix(in srgb, var(--brand) 8%, transparent); color: var(--ink); }

/* ── Workspace (engine slot: project rail + canvas) ── */
.ws { flex: 1; display: flex; margin-top: 18px; border-top: 1px solid var(--sep-2); min-height: 0; }
.ws-side { width: 270px; flex: none; border-right: 1px solid var(--sep-2); display: flex; flex-direction: column; padding: 18px 14px; }
.ws-side .hd { display: flex; align-items: center; gap: 8px; padding: 0 6px; }
.ws-side .hd .t { font-size: 14px; font-weight: 700; color: var(--ink-strong); flex: 1; }
.ws-rows { display: flex; flex-direction: column; gap: 4px; margin-top: 14px; overflow-y: auto; }
.ws-row { display: flex; align-items: center; gap: 10px; padding: 8px 9px; border-radius: 10px; cursor: pointer; }
.ws-row.on { background: color-mix(in srgb, var(--brand) 7.1%, transparent); }
.ws-row .th {
  width: 34px; height: 26px; border-radius: 6px; flex: none; overflow: hidden;
  background: linear-gradient(135deg, color-mix(in srgb, var(--brand) 20%, transparent), color-mix(in srgb, var(--brand) 6.7%, transparent));
  border: 1px solid color-mix(in srgb, var(--brand) 13.3%, transparent);
}
.ws-row .th img { width: 100%; height: 100%; object-fit: cover; object-position: top; }
.ws-row .nm { display: block; font-size: 13px; font-weight: 600; color: var(--ink-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ws-row .mt { display: block; font-size: 11.5px; color: var(--muted); margin-top: 1px; }
.ws-side .hint { margin-top: auto; padding: 12px 6px 0; font-size: 11.5px; color: var(--muted); line-height: 1.5; }
.ws-main { flex: 1; display: flex; flex-direction: column; min-width: 0; background: var(--canvas); }
.ws-bar { flex: none; display: flex; align-items: center; gap: 10px; padding: 12px 18px; border-bottom: 1px solid var(--sep-2); background: var(--panel); }
.ws-bar .t { font-size: 13.5px; font-weight: 700; color: var(--ink-strong); }
.ws-stage { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; padding: 30px; min-height: 0; }
.ws-mock {
  width: 100%; max-width: 640px; background: var(--panel); border-radius: 14px;
  border: 1px solid var(--border-2); box-shadow: 0 20px 44px -24px rgba(15,23,42,.18); overflow: hidden;
}
.ws-mock .chrome { height: 34px; display: flex; align-items: center; gap: 6px; padding: 0 14px; border-bottom: 1px solid var(--sep); }
.ws-mock .chrome span { width: 9px; height: 9px; border-radius: 99px; background: var(--row); }
.ws-mock .bars { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 34px 24px; }
.ws-mock .shot { display: block; width: 100%; max-height: 360px; object-fit: cover; object-position: top; }
.ws-cap { font-size: 12px; color: var(--muted); }

/* Template mini-grid (start from a design) */
.tpl-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px; }
.tpl { border: 1px solid var(--border-2); border-radius: 12px; overflow: hidden; cursor: pointer; background: var(--panel); }
.tpl:hover { border-color: var(--brand); box-shadow: 0 6px 20px -8px color-mix(in srgb, var(--brand) 35%, transparent); }
.tpl .th { aspect-ratio: 16/10; background: var(--row); }
.tpl .th img { width: 100%; height: 100%; object-fit: cover; object-position: top; }
.tpl .nm { padding: 9px 12px; font-size: 12.5px; font-weight: 600; display: flex; justify-content: space-between; align-items: center; color: var(--ink-strong); }
.tpl .nm span.tag { font-size: 10.5px; color: var(--muted); font-weight: 600; text-transform: uppercase; letter-spacing: .05em; }

.pn-loading { flex: 1; display: flex; align-items: center; justify-content: center; color: var(--muted); font-size: 13.5px; min-height: 40vh; }

/* Pricing page scaffold (one-time Buy card) — full padding on desktop,
   phone-sized below the breakpoint. */
.plc-wrap { padding: 44px 40px 60px; }

/* ═══════════════════════════════════════════════════════════════════
   Mobile (≤700px). Desktop never changes — these rules only exist below
   the breakpoint. The icon rail moves to the bottom of the screen (a
   phone tab bar, same items, same order), pages keep the whole width,
   and the wide paddings fold to phone sizes.
   ═══════════════════════════════════════════════════════════════════ */
@media (max-width: 700px) {
  .sh { flex-direction: column; height: 100dvh; }

  /* Rail → bottom tab bar. The open/collapsed switch means nothing on a
     phone, so both states render identically: a row of 40px icon targets. */
  .sh-rail, html[data-rail="open"] .sh-rail {
    order: 2; width: 100%; height: auto; flex-direction: row; align-items: center;
    gap: 2px; padding: 6px 8px calc(6px + env(safe-area-inset-bottom));
    border-right: none; border-top: 1px solid var(--rl-border);
    overflow-x: auto; overflow-y: hidden;
  }
  #shRailToggle { display: none; }
  .rl-nav {
    flex: 1; flex-direction: row; align-items: center; gap: 2px;
    margin-top: 0; overflow-x: auto; overflow-y: hidden;
  }
  html[data-rail="open"] .rl-it {
    width: 40px; height: 40px; margin: 0; justify-content: center;
    gap: 0; padding: 0; border-radius: 12px;
  }
  .rl-it .lb, html[data-rail="open"] .rl-it .lb { display: none; }
  html[data-rail="open"] .rl-it .lk { display: none; }
  .rl-div, html[data-rail="open"] .rl-div,
  .rl-div--foot, html[data-rail="open"] .rl-div--foot {
    width: 1px; height: 24px; margin: 0 5px; align-self: center;
  }
  .rl-foot { margin-top: 0; }
  .sh-tip { display: none; }
  .sh-drawer, html[data-rail="open"] .sh-drawer {
    left: 12px; right: 12px; width: auto;
    bottom: calc(66px + env(safe-area-inset-bottom));
  }

  .sh-page { min-height: 0; }
  .sh-brandrow { padding: 16px 18px 0; flex-wrap: wrap; row-gap: 10px; }
  .sh-brandrow .word { max-width: 42vw; overflow: hidden; text-overflow: ellipsis; }

  /* Page scaffolding + the hero landing, phone-sized */
  .pg { padding: 26px 18px 48px; }
  .hm-in { padding: 44px 20px 40px; }
  .hm-h1 { font-size: 34px; }
  .hm-ctas { flex-wrap: wrap; }
  .hm-feats { padding: 6px 20px 48px; }
  .au-wrap { padding: 36px 18px; }
  .plc-wrap { padding: 30px 18px 44px; }
  .plc { width: 100% !important; }

  /* Workspace: the project rail stacks above the canvas */
  .ws { flex-direction: column; }
  .ws-side { width: 100%; border-right: none; border-bottom: 1px solid var(--sep-2); padding: 14px; }
  .ws-rows { max-height: 180px; }
}

/* ── Card popup (Stripe fields inside the design's 380px sheet) ── */
/* Entering card details is a moment that deserves the whole screen — the
   page carrying on behind a small translucent card is a distraction exactly
   when the customer is being careful (Uche, 2026-08-02). Heavier veil, and
   on a phone the sheet fills the screen outright. */
.lan-modal-overlay {
  position: fixed; inset: 0; background: rgba(15,23,42,.82);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  z-index: 200; display: flex; align-items: center; justify-content: center; padding: 20px;
  overflow-y: auto;
}
@media (max-width: 560px) {
  .lan-modal-overlay { padding: 0; align-items: stretch; }
  .lan-modal-overlay .lan-modal {
    max-width: none; width: 100%; border-radius: 0; min-height: 100%;
    padding: 30px 22px calc(28px + env(safe-area-inset-bottom));
    display: flex; flex-direction: column; justify-content: center;
  }
}
.lan-modal {
  width: 100%; max-width: 380px; background: var(--panel); border-radius: 18px;
  padding: 26px 26px 24px; box-shadow: 0 30px 70px -20px rgba(15,23,42,.5);
  animation: lanPop .18s ease-out;
}
@keyframes lanPop { from { transform: scale(.96) translateY(8px); opacity: 0; } to { transform: none; opacity: 1; } }
.lan-modal .hd { display: flex; align-items: center; gap: 11px; }
.lan-modal .hd .mark {
  width: 38px; height: 38px; border-radius: 11px; background: var(--brand); color: var(--brand-ink);
  font-size: 16px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex: none; overflow: hidden;
}
.lan-modal .hd .mark img { width: 100%; height: 100%; object-fit: cover; }
.lan-modal .hd .t { font-size: 15px; font-weight: 700; color: var(--ink-strong); }
.lan-modal .hd .s { font-size: 12px; color: var(--muted); }
.lan-modal label { display: block; font-size: 12px; font-weight: 600; color: var(--lab); margin: 18px 0 6px; }
.lan-modal .duo { display: flex; gap: 10px; margin-top: 12px; }
.lan-modal .duo > div { flex: 1; }
.lan-modal .duo label { margin: 0 0 6px; }
.lan-modal .shin-box {
  height: 44px; padding: 12px 14px; border: 1px solid var(--in-bord); border-radius: 10px;
  background: var(--panel); transition: border-color .12s, box-shadow .12s;
}
.lan-modal .shin-box.focus { border-color: var(--in-focus); box-shadow: 0 0 0 3px rgba(15,23,42,.06); }
.lan-modal .carderr { color: var(--bad-fg); font-size: 12.5px; min-height: 18px; margin-top: 10px; line-height: 1.4; }
.lan-modal .foot {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  font-size: 11.5px; color: var(--muted); margin-top: 12px; text-align: center;
}

/* The confirm/alert card lives entirely in core/assets/js/shell-dialog.js —
   markup AND styles — so the same dialog works on surfaces that never load
   this file (the App Library and the other engine pages have their own
   tokens). Nothing for it here on purpose. */

/* ═══════════════════════════════════════════════════════════════════════
   THE PREMIUM PANEL LANGUAGE (px-*) — 2026-08-03
   Ported from the Claude Design project "Pancho — customer app panels".
   One vocabulary for every customer panel: header, section, tabs, cards,
   list rows, pills, tiles, empty states. The generated CSS spoke oklch and
   its own token names; everything here is remapped onto the SHELL tokens
   above, so per-business accents, dark mode and the editor's panel-chrome
   remap all keep working with no second theme layer.

   Prefixed px- because the design shipped generic names (.card, .row,
   .tabs) that would collide with what already renders. Use these for
   panel REDESIGNS; leave the design-pack pages (pricing, billing, home…)
   on their existing inline idiom — Uche explicitly kept those.
   ═══════════════════════════════════════════════════════════════════ */

/* Derived accent surfaces — hue-agnostic, mixed in srgb like the rest of
   the codebase, so an orange or violet business reads as deliberately
   designed rather than tinted. */
.px {
  --px-wash: color-mix(in srgb, var(--brand) 7%, var(--panel));
  --px-wash-2: color-mix(in srgb, var(--brand) 13%, var(--panel));
  --px-line: color-mix(in srgb, var(--brand) 32%, var(--border-2));
  --px-brand-ink: color-mix(in srgb, var(--brand) 78%, var(--ink-strong));
  --px-ring: color-mix(in srgb, var(--brand) 22%, transparent);
  --px-hi: inset 0 1px 0 rgba(255,255,255,.85);
  --px-e1: 0 1px 2px rgba(16,24,40,.05);
  --px-e2: 0 1px 2px rgba(16,24,40,.05), 0 6px 16px -8px rgba(16,24,40,.13);
  --px-e3: 0 2px 4px rgba(16,24,40,.06), 0 16px 36px -12px rgba(16,24,40,.19);
}
html[data-theme="dark"] .px {
  --px-wash: color-mix(in srgb, var(--brand) 11%, var(--panel));
  --px-wash-2: color-mix(in srgb, var(--brand) 19%, var(--panel));
  --px-brand-ink: color-mix(in srgb, var(--brand) 55%, var(--ink-strong));
  --px-hi: inset 0 1px 0 rgba(255,255,255,.04);
  --px-e1: 0 1px 2px rgba(0,0,0,.35);
  --px-e2: 0 1px 2px rgba(0,0,0,.4), 0 8px 20px -10px rgba(0,0,0,.55);
  --px-e3: 0 2px 6px rgba(0,0,0,.45), 0 20px 44px -14px rgba(0,0,0,.65);
}

/* ── Page scaffold ── */
.px { padding: 0 18px 56px; display: flex; flex-direction: column; gap: 18px; }
.px-head {
  position: sticky; top: 0; z-index: 6; margin: 0 -18px; padding: 18px 18px 13px;
  background: color-mix(in srgb, var(--panel) 80%, transparent);
  backdrop-filter: blur(14px) saturate(1.6); -webkit-backdrop-filter: blur(14px) saturate(1.6);
  border-bottom: 1px solid var(--sep); display: flex; flex-direction: column; gap: 8px;
}
.px-head-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; }
.px-eyebrow {
  display: inline-flex; align-items: center; gap: 7px; font: 650 10px/1 Inter, sans-serif;
  letter-spacing: .13em; text-transform: uppercase; color: var(--muted);
}
.px-eyebrow::before { content: ""; width: 12px; height: 2px; border-radius: 2px; background: var(--brand); flex: none; }
.px-title { font-size: 22px; line-height: 1.12; font-weight: 700; letter-spacing: -.028em; color: var(--ink-strong); margin: 0; }
.px-sub { font-size: 12.5px; line-height: 1.55; color: var(--sub); margin: 0; max-width: 60ch; text-wrap: pretty; }

.px-sec { display: flex; flex-direction: column; gap: 11px; }
.px-sec-head { display: flex; align-items: center; gap: 9px; min-height: 20px; }
.px-sec-title { font: 650 10px/1 Inter, sans-serif; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.px-sec-note { font-size: 11.5px; color: var(--faint); }
.px-sec-rule { flex: 1; height: 1px; background: var(--sep); }
.px-count {
  font: 600 10.5px/1.6 Inter, sans-serif; color: var(--sub); font-variant-numeric: tabular-nums;
  background: color-mix(in srgb, var(--border-2) 45%, var(--panel)); border-radius: 20px; padding: 1px 7px;
}

/* ── Search + category tabs ── */
/* flex-basis here is for TOOLBAR rows. Inside a flex COLUMN (.px-sec) the
   same basis becomes a 190px HEIGHT and the search icon floats in a void —
   found on the add-ons panel, 2026-08-03. Columns get auto height. */
.px-field { position: relative; flex: 1 1 190px; min-width: 140px; }
.px-sec > .px-field { flex: 0 0 auto; }
.px-field svg { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); color: var(--faint); pointer-events: none; }
.px-input {
  width: 100%; height: 36px; border: 1px solid var(--in-bord); background: var(--panel);
  border-radius: 10px; padding: 0 11px 0 33px; font: 500 13px/1 Inter, sans-serif;
  color: var(--ink-strong); box-shadow: var(--px-e1); outline: none;
  transition: border-color .15s, box-shadow .15s;
}
.px-input::placeholder { color: var(--faint); }
.px-input:focus { border-color: var(--px-line); box-shadow: 0 0 0 3px var(--px-ring); }
.px-tabs {
  display: flex; gap: 2px; padding: 3px; border-radius: 10px; overflow-x: auto; scrollbar-width: none; max-width: 100%;
  background: color-mix(in srgb, var(--border-2) 38%, var(--bg)); border: 1px solid var(--border-2);
}
.px-tabs::-webkit-scrollbar { display: none; }
.px-tab {
  flex: none; /* never shrink into each other in the scroll row */
  white-space: nowrap; font: 500 12px/1 Inter, sans-serif; color: var(--sub);
  padding: 6px 10px; border: 0; border-radius: 7px; background: transparent; cursor: pointer; transition: .14s;
}
.px-tab:hover { color: var(--ink-strong); }
.px-tab[aria-selected="true"] { background: var(--panel); color: var(--ink-strong); font-weight: 600; box-shadow: var(--px-e1), var(--px-hi); }

/* ── Pills, tiles ── */
.px-pill {
  display: inline-flex; align-items: center; gap: 5px; height: 20px; padding: 0 8px;
  border-radius: 6px; font: 650 10.5px/1 Inter, sans-serif; letter-spacing: .02em; white-space: nowrap;
}
.px-pill-n { background: color-mix(in srgb, var(--border-2) 40%, var(--panel)); color: var(--sub); }
.px-pill-b { background: var(--px-wash-2); color: var(--px-brand-ink); box-shadow: inset 0 0 0 1px var(--px-line); }
.px-pill-o { background: transparent; color: var(--muted); box-shadow: inset 0 0 0 1px var(--border-2); }
.px-pill-ok { background: var(--ok-bg); color: var(--ok-fg); }
.px-tile {
  width: 36px; height: 36px; border-radius: 10px; display: flex; align-items: center; justify-content: center;
  flex: none; color: var(--sub); background: color-mix(in srgb, var(--border-2) 30%, var(--panel));
  box-shadow: inset 0 0 0 1px var(--border-2), var(--px-hi);
}
.px-tile-b { background: var(--px-wash-2); box-shadow: inset 0 0 0 1px var(--px-line); color: var(--px-brand-ink); }

/* ── Cards / sheets / rows ── */
.px-card { background: var(--panel); border: 1px solid var(--border-2); border-radius: 13px; box-shadow: var(--px-e2), var(--px-hi); }
.px-sheet { background: var(--panel); border: 1px solid var(--border-2); border-radius: 13px; box-shadow: var(--px-e2), var(--px-hi); overflow: hidden; }
.px-sheet-head {
  display: flex; align-items: center; gap: 9px; padding: 10px 14px;
  background: color-mix(in srgb, var(--border-2) 16%, var(--panel)); border-bottom: 1px solid var(--border-2);
  font: 600 12px/1.4 Inter, sans-serif; color: var(--ink-strong);
}
.px-row { display: flex; align-items: center; gap: 12px; padding: 13px 14px; position: relative; transition: background .15s; }
.px-row + .px-row::before { content: ""; position: absolute; left: 58px; right: 0; top: 0; height: 1px; background: var(--sep); }
.px-row:hover { background: color-mix(in srgb, var(--border-2) 15%, var(--panel)); }
.px-row-main { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 2px; }
.px-row-t { font: 600 13px/1.35 Inter, sans-serif; color: var(--ink-strong); letter-spacing: -.011em; }
.px-row-s { font-size: 11.5px; line-height: 1.45; color: var(--muted); }
.px-money { font: 600 13.5px/1 Inter, sans-serif; color: var(--ink-strong); letter-spacing: -.017em; font-variant-numeric: tabular-nums; white-space: nowrap; }
.px-money span { font-weight: 500; color: var(--muted); font-size: 11px; letter-spacing: 0; }

/* ── The add-on card ── */
.px-acard {
  display: flex; background: var(--panel); border: 1px solid var(--border-2); border-radius: 13px;
  box-shadow: var(--px-e1), var(--px-hi); transition: .18s; position: relative; overflow: hidden;
}
.px-acard:hover { box-shadow: var(--px-e3), var(--px-hi); border-color: color-mix(in srgb, var(--border-2) 50%, var(--muted)); transform: translateY(-1px); }
.px-acard-top { padding: 14px; display: flex; gap: 12px; flex: 1; min-width: 0; }
.px-acard-name { font: 600 13.5px/1.35 Inter, sans-serif; color: var(--ink-strong); letter-spacing: -.016em; }
.px-acard-desc { font-size: 12px; color: var(--sub); text-wrap: pretty; line-height: 1.5; margin: 3px 0 0; }
.px-acard-side {
  display: flex; flex-direction: column; align-items: flex-end; justify-content: center; gap: 9px;
  padding: 14px; border-left: 1px solid var(--sep); min-width: 110px;
  background: color-mix(in srgb, var(--border-2) 9%, var(--panel));
}
.px-acard[data-focus="true"] { border-color: var(--brand); box-shadow: 0 0 0 3px var(--px-ring), var(--px-e2); }
.px-acard[data-focus="true"]::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 2px; background: var(--brand); }

/* ── Buttons ── */
.px-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 32px; padding: 0 13px;
  border: 0; border-radius: 9px; font: 600 12.5px/1 Inter, sans-serif; letter-spacing: -.006em;
  white-space: nowrap; cursor: pointer; transition: .15s;
}
.px-btn-p { background: var(--brand); color: var(--brand-ink); box-shadow: 0 1px 2px var(--px-ring), inset 0 1px 0 rgba(255,255,255,.18); }
.px-btn-p:hover { filter: brightness(.94); box-shadow: 0 2px 10px -2px var(--px-ring), inset 0 1px 0 rgba(255,255,255,.18); }
.px-btn-s { background: var(--panel); color: var(--ink-strong); border: 1px solid var(--border-2); box-shadow: var(--px-e1), var(--px-hi); }
.px-btn-s:hover { border-color: color-mix(in srgb, var(--border-2) 45%, var(--muted)); background: color-mix(in srgb, var(--border-2) 12%, var(--panel)); }
.px-btn:disabled { opacity: .45; cursor: default; }

/* ── Empty state ── */
.px-empty {
  border: 1px solid var(--border-2); border-radius: 14px; background: var(--panel);
  padding: 30px 22px 26px; display: flex; flex-direction: column; align-items: center; text-align: center; gap: 5px;
  box-shadow: var(--px-e2), var(--px-hi); position: relative; overflow: hidden;
}
.px-empty::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 150px; pointer-events: none; opacity: .7;
  background: radial-gradient(75% 100% at 50% 0%, var(--px-wash-2), transparent 72%);
}
.px-empty > * { position: relative; }
.px-empty-t { font: 600 14.5px/1.4 Inter, sans-serif; color: var(--ink-strong); letter-spacing: -.017em; }
.px-empty-b { font-size: 12.5px; color: var(--sub); max-width: 42ch; text-wrap: pretty; line-height: 1.6; }

/* ── Long-form reading (legal) ── */
.px-prose { max-width: 64ch; font-size: 13.5px; line-height: 1.75; color: var(--body); }
.px-prose h2 {
  font: 700 11px/1.4 Inter, sans-serif; letter-spacing: .13em; text-transform: uppercase;
  color: var(--px-brand-ink); margin: 32px 0 10px; padding-top: 16px; border-top: 1px solid var(--sep);
}
.px-prose h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.px-prose p { margin: 0 0 13px; }
.px-prose p:last-child { margin-bottom: 0; }
.px-lede { font-size: 14.5px; line-height: 1.65; color: var(--sub); letter-spacing: -.008em; }
.px-doc-meta { display: flex; align-items: center; gap: 8px; font-size: 11.5px; color: var(--muted); flex-wrap: wrap; }
/* Used on an <a> (legal) AND on a <button> (support), so it carries the
   button reset itself — without it the browser's own chrome turned the
   support back-link into a full-width grey box. align-self stops it
   stretching inside .px-head, which is a flex column. */
.px-backlink {
  display: inline-flex; align-items: center; gap: 6px; font: 600 12px/1 Inter, sans-serif;
  color: var(--muted); height: 28px; letter-spacing: -.006em; cursor: pointer; text-decoration: none;
  align-self: flex-start; background: none; border: 0; padding: 0;
}
.px-backlink:hover { color: var(--ink-strong); }
.px-doc-card {
  display: flex; gap: 12px; padding: 14px; background: var(--panel); border: 1px solid var(--border-2);
  border-radius: 13px; box-shadow: var(--px-e1), var(--px-hi); text-align: left; transition: .18s;
  align-items: flex-start; width: 100%; text-decoration: none; cursor: pointer;
}
.px-doc-card:hover { border-color: var(--px-line); box-shadow: var(--px-e3), var(--px-hi); transform: translateY(-1px); }
.px-doc-card:hover .px-tile { background: var(--px-wash-2); box-shadow: inset 0 0 0 1px var(--px-line); color: var(--px-brand-ink); }

/* A one-line description (add-ons, 2026-08-03: Uche wants every card two
   lines — name, then what it does — so the shop scans instead of scrolls). */
.px-acard-desc-1 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ── Header actions + toolbars ── */
.px-head-acts { display: flex; gap: 6px; flex: none; padding-top: 2px; }
.px-toolbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

/* ── The figures strip (invoices) ── */
.px-stats { display: flex; flex-wrap: wrap; }
.px-stat { flex: 1 1 138px; padding: 13px 15px; border-left: 1px solid var(--sep); }
.px-stat:first-child { border-left: 0; }
.px-stat-k { font: 650 10px/1.4 Inter, sans-serif; letter-spacing: .11em; text-transform: uppercase; color: var(--muted); }
.px-stat-v {
  font-size: 21px; font-weight: 600; letter-spacing: -.028em; color: var(--ink-strong);
  margin-top: 3px; font-variant-numeric: tabular-nums; line-height: 1.15;
}
.px-stat-h { font-size: 11.5px; color: var(--muted); margin-top: 2px; }

.px-pill-s { background: var(--ink-strong); color: var(--panel); }
.px-dot-hollow { width: 5px; height: 5px; border-radius: 50%; border: 1.5px solid currentColor; flex: none; }

/* ══ THE INVOICE LIST ══════════════════════════════════════════════════
   Back to the design pack's row (2026-08-03, Uche: *"go back to the one you
   got from the Claude Design MCP — restore that one and just remove the
   boxes around it"*).

   So this IS the design's row — icon tile, what it was for, its reference
   and date, a status pill, the money, a trailing control — with the two
   containers taken away: the rounded sheet that wrapped the whole list, and
   the card feel that came with it. Rows now sit straight on the page,
   separated by a hairline and air, which is the change that stops it
   reading as boxes inside boxes.

   The middle pass's coloured edge rails are gone too; that was decoration
   doing a job the pill already does. */
.px-inv {
  display: flex; align-items: center; gap: 12px; padding: 14px 2px;
  position: relative; transition: background .14s;
}
.px-inv + .px-inv::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px; background: var(--sep);
}
.px-inv:hover { background: color-mix(in srgb, var(--border-2) 12%, transparent); }

/* Two lines, the full width the row can spare: a real sentence on top, the
   reference and the state under it. */
.px-inv-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.px-inv-t {
  font: 600 13px/1.4 Inter, sans-serif; letter-spacing: -.011em; color: var(--ink-strong);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.px-inv[data-state="void"] .px-inv-t { color: var(--muted); text-decoration: line-through; }
.px-inv-meta {
  font-size: 11.5px; line-height: 1.45; color: var(--muted); font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.px-inv-meta b { font-weight: 600; color: var(--ink-2); }
.px-inv[data-state="overdue"] .px-inv-meta b { color: var(--bad-fg); }
.px-inv-amt {
  flex: none; min-width: 66px; text-align: right;
  font: 600 13.5px/1 Inter, sans-serif; letter-spacing: -.017em;
  color: var(--ink-strong); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.px-inv[data-state="paid"] .px-inv-amt,
.px-inv[data-state="void"] .px-inv-amt { color: var(--muted); font-weight: 500; }
.px-inv[data-state="void"] .px-inv-amt { text-decoration: line-through; }

/* THE PAY BUTTON WEARS THE PILL (Uche: *"I like the to-pay design — take
   that design and make it the actual pay button, then remove the to-pay"*).
   One control saying one thing, in the colour the status used to wear. */
.px-btn-soft {
  flex: none; background: var(--px-wash-2); color: var(--px-brand-ink);
  box-shadow: inset 0 0 0 1px var(--px-line); height: 28px; padding: 0 14px; border-radius: 8px;
  font-size: 12px;
}
.px-btn-soft:hover { background: color-mix(in srgb, var(--brand) 20%, var(--panel)); }
.px-inv[data-state="overdue"] .px-btn-soft {
  background: var(--bad-bg); color: var(--bad-fg);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--bad-fg) 28%, transparent);
}
.px-inv[data-state="overdue"] .px-btn-soft:hover { background: color-mix(in srgb, var(--bad-fg) 14%, var(--panel)); }

/* The instalment breakdown under a part-paid invoice. Indented off a
   hairline so it reads as belonging to the row above it, and quiet, because
   it is a receipt rather than a control. */
.px-inv-log {
  margin: -4px 0 10px 46px; padding-left: 12px; border-left: 1px solid var(--sep);
  display: flex; flex-direction: column; gap: 3px;
}
.px-inv-log[hidden] { display: none; }
/* One instalment = two lines: what and when, then how and why. A single
   flex line does not fit a ~330px panel column, and wrapping it left the
   figure stranded above an orphaned sentence (Uche, 2026-08-03). */
.px-inv-log-row { font-variant-numeric: tabular-nums; padding: 2px 0; }
.px-inv-log-row + .px-inv-log-row { border-top: 1px solid var(--sep-2, var(--sep)); padding-top: 5px; margin-top: 2px; }
.px-inv-log-top { display: flex; align-items: baseline; gap: 8px; }
.px-inv-log-amt { font: 600 12px/1.4 Inter, sans-serif; color: var(--ink-2); }
.px-inv-log-when { margin-left: auto; font-size: 11px; color: var(--muted); white-space: nowrap; }
.px-inv-log-how { font-size: 11px; line-height: 1.45; color: var(--muted); }

/* ── "₦3,555 paid", and the door to the instalments behind it ──────────
   This used to be the tail of the muted meta line — same size, same colour,
   run together with "INV-0004 · Due 3 Aug 2026", so the one number the
   customer actually wants was the hardest thing on the row to pick out
   (Uche, 2026-08-03). It is its own chip now, in the success colour, and
   where there is a payment history it doubles as the control that opens it:
   one thing to look at, one thing to press. */
/* The invoice row is TWO lines (Uche, 2026-08-03). What the bill IS goes on
   top — description, number, due date, and the standing pill right beside
   that date, because "Due 3 Aug · Due now" is one fact, not two. The MONEY
   goes underneath: already paid on the left, still owed and the Pay button
   hard right. The foot claims its own line; the inset clears the icon tile
   so both lines start on the same left edge. */
.px-inv-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* calc, not 100%: a basis of 100% PLUS the 46px inset is wider than the row,
   which pushed the Pay button off the right edge of the panel. */
.px-invfoot {
  flex: 0 0 calc(100% - 46px); margin: 6px 0 0 46px;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
/* An invoice with nothing paid yet has no chip, so the money must still sit
   at the right edge — the auto margin does that with or without one. */
.px-invfoot .px-inv-end { margin-left: auto; }

/* ONE HEIGHT FOR BOTH ENDS OF THE LINE (Uche, 2026-08-03: they "look like
   they're just randomly floating in the card"). The chip was a 20px pill and
   the pay control 27px, so even sharing a line they read as two unrelated
   objects that happened to land near each other. Same height, same optical
   weight — now they read as the two ends of one row: what is paid, what is
   left. */
.px-invfoot .px-paidchip,
.px-invfoot .px-payunit { height: 27px; box-sizing: border-box; }
.px-invfoot .px-paidchip { padding: 0 8px 0 9px; }
.px-invfoot .px-payunit .px-pu-amt,
.px-invfoot .px-payunit .px-pu-go { height: 100%; }
.px-paidchip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 7px 2px 8px;
  border: 1px solid color-mix(in srgb, var(--ok-fg) 26%, transparent);
  border-radius: 999px; background: var(--ok-bg); color: var(--ok-fg);
  font: 550 11px/1.5 Inter, sans-serif; letter-spacing: 0;
  cursor: pointer; transition: background .13s, border-color .13s;
}
.px-paidchip b { font-weight: 700; font-variant-numeric: tabular-nums; }
.px-paidchip:hover { background: color-mix(in srgb, var(--ok-fg) 14%, transparent); }
/* No history to open — same look, but it is a statement, not a button. */
.px-paidchip-flat { cursor: default; }
.px-paidchip-flat:hover { background: var(--ok-bg); }
/* How many payments are folded away. Without it the chevron is a mystery. */
.px-paidchip-n {
  min-width: 14px; height: 14px; padding: 0 3px; border-radius: 999px;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--ok-fg) 20%, transparent);
  font: 700 9.5px/1 Inter, sans-serif;
}
.px-paidchip-v { transition: transform .16s ease; flex: none; }
.px-paidchip[aria-expanded="true"] .px-paidchip-v { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .px-paidchip-v { transition: none; } }

/* A pickable option row (the add-ons "when should it stop?" panel). The dot
   fills on hover so it reads as a choice you are about to make rather than a
   paragraph with a border. */
.ad-choice { transition: border-color .15s, background .15s, box-shadow .15s; }
.ad-choice:hover { border-color: var(--px-line) !important; box-shadow: 0 0 0 3px var(--px-ring); }
.ad-choice:hover > span:first-child { box-shadow: inset 0 0 0 5px var(--brand) !important; }

/* ── The filter row: an underline, not a tray of chips ── */
.px-utabs { display: flex; gap: 20px; border-bottom: 1px solid var(--sep); }
.px-utab {
  position: relative; padding: 0 0 10px; background: none; border: 0; cursor: pointer;
  font: 550 13.5px/1 Inter, sans-serif; letter-spacing: -.011em; color: var(--muted); transition: color .16s;
}
.px-utab:hover { color: var(--ink-2); }
.px-utab[aria-selected="true"] { color: var(--ink-strong); font-weight: 600; }
.px-utab[aria-selected="true"]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  border-radius: 2px; background: var(--brand);
}
/* A search with no box around it — an icon, a line, and the words. */
.px-search { position: relative; display: flex; align-items: center; gap: 9px; padding: 0 0 9px;
  border-bottom: 1px solid var(--sep); color: var(--faint); }
.px-search input {
  flex: 1; min-width: 0; border: 0; background: none; outline: none; padding: 0;
  font: 500 13.5px/1 Inter, sans-serif; color: var(--ink-strong);
}
.px-search input::placeholder { color: var(--faint); font-weight: 450; }
.px-search:focus-within { color: var(--brand); border-bottom-color: var(--px-line); }

/* ── Sheet foot, textarea, quiet buttons ── */
.px-sheet-foot {
  padding: 10px 14px; border-top: 1px solid var(--sep); font-size: 11.5px; color: var(--muted);
  background: color-mix(in srgb, var(--border-2) 10%, var(--panel));
}
.px-ta {
  width: 100%; border: 1px solid var(--in-bord); background: var(--panel); border-radius: 10px;
  padding: 10px 12px; font: 500 13px/1.6 Inter, sans-serif; color: var(--ink-strong);
  resize: vertical; min-height: 84px; box-shadow: var(--px-e1); outline: none; transition: border-color .15s, box-shadow .15s;
}
.px-ta::placeholder { color: var(--faint); }
.px-ta:focus { border-color: var(--px-line); box-shadow: 0 0 0 3px var(--px-ring); }
.px-btn-g { background: none; color: var(--sub); padding: 0 9px; }
.px-btn-g:hover { color: var(--ink-strong); background: color-mix(in srgb, var(--border-2) 34%, var(--panel)); }
.px-iconbtn {
  width: 30px; height: 30px; border-radius: 8px; display: inline-flex; align-items: center; justify-content: center;
  color: var(--muted); border: 1px solid transparent; background: none; cursor: pointer; transition: .15s;
}
.px-iconbtn:hover { color: var(--ink-strong); background: var(--panel); border-color: var(--border-2); box-shadow: var(--px-e1); }
.px-dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; flex: none; }

/* A whole list row that is a button (support threads, workspace items). */
button.px-row {
  width: 100%; border: 0; background: none; font: inherit; color: inherit;
  text-align: left; cursor: pointer;
}

/* ── The empty state's ghost list + its actions ── */
.px-ghost {
  width: 100%; max-width: 250px; display: grid; gap: 7px; margin-bottom: 18px;
  -webkit-mask-image: linear-gradient(180deg, #000 25%, transparent);
          mask-image: linear-gradient(180deg, #000 25%, transparent);
}
.px-g-row {
  height: 38px; border-radius: 9px; border: 1px solid var(--border-2);
  background: var(--panel); position: relative; box-shadow: var(--px-e1);
}
.px-g-row::before {
  content: ""; position: absolute; left: 10px; top: 11px; width: 16px; height: 16px; border-radius: 5px;
  background: color-mix(in srgb, var(--border-2) 70%, var(--panel));
}
.px-g-row::after {
  content: ""; position: absolute; left: 34px; top: 15px; width: 46%; height: 7px; border-radius: 4px;
  background: color-mix(in srgb, var(--border-2) 55%, var(--panel));
}
.px-g-hi { border-color: var(--px-line); background: var(--px-wash); }
.px-g-hi::before { background: color-mix(in srgb, var(--brand) 55%, var(--panel)); }
.px-g-hi::after { background: color-mix(in srgb, var(--brand) 22%, var(--panel)); width: 60%; }
.px-empty-actions { display: flex; gap: 8px; margin-top: 15px; flex-wrap: wrap; justify-content: center; }
.px-empty-hint {
  margin-top: 18px; padding-top: 13px; border-top: 1px solid var(--sep); width: 100%; max-width: 380px;
  font-size: 11.5px; color: var(--muted); line-height: 1.55;
}

/* ── The conversation (support) ──
   Full-width and native, not a chat-app pastiche: the customer's own words
   sit right in a tinted bubble, and the reply from the team is plain text
   under a small avatar — because THEIR message is the one being read, and a
   bubble around an answer makes it look like small talk. */
.px-chat { display: flex; flex-direction: column; gap: 20px; padding: 8px 0 12px; flex: 1; }
.px-chat-msg { display: flex; flex-direction: column; gap: 5px; max-width: 100%; }
.px-chat-agent { display: flex; align-items: center; gap: 8px; }
.px-chat-text { font-size: 13.5px; line-height: 1.65; color: var(--body); max-width: 58ch; }
.px-chat-me { align-items: flex-end; }
.px-chat-bubble {
  background: var(--px-wash-2); color: var(--ink-strong); border-radius: 14px 14px 4px 14px;
  padding: 10px 13px; font-size: 13.5px; line-height: 1.6; max-width: 80%;
  box-shadow: inset 0 0 0 1px var(--px-line);
}
.px-chat-when { font-size: 10.5px; color: var(--faint); font-variant-numeric: tabular-nums; padding-right: 2px; }
.px-chat-note {
  text-align: center; font-size: 11.5px; color: var(--muted); padding: 6px 0;
  border-top: 1px solid var(--sep); border-bottom: 1px solid var(--sep);
}
.px-avatar {
  width: 26px; height: 26px; border-radius: 8px; flex: none; display: flex; align-items: center; justify-content: center;
  font: 700 10px/1 Inter, sans-serif; background: color-mix(in srgb, var(--border-2) 50%, var(--panel));
  color: var(--sub); box-shadow: inset 0 0 0 1px var(--border-2);
}
.px-avatar-b { background: var(--brand); color: var(--brand-ink); box-shadow: none; }
.px-who { font: 600 12px/1 Inter, sans-serif; color: var(--ink-strong); }
.px-when { font-size: 10.5px; color: var(--faint); font-variant-numeric: tabular-nums; }

/* The pinned reply bar. Negative margins cancel .px's own padding so it
   spans the panel edge to edge, exactly as the design has it. */
.px-paybar {
  position: sticky; bottom: 0; z-index: 5; margin: 4px -18px -56px; padding: 12px 18px 16px;
  background: color-mix(in srgb, var(--panel) 80%, transparent);
  backdrop-filter: blur(14px) saturate(1.6); -webkit-backdrop-filter: blur(14px) saturate(1.6);
  border-top: 1px solid var(--border-2); display: flex; align-items: flex-end; gap: 12px;
}

/* ── Layout ── */
.px-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 11px; }
.px-fade { animation: pxfi .22s ease both; }
@keyframes pxfi { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }
@media (max-width: 480px) {
  .px { padding: 0 14px 52px; }
  .px-head { margin: 0 -14px; padding: 14px 14px 12px; }
  .px-title { font-size: 20px; }
  .px-acard-side { min-width: 94px; padding: 12px; }
  .px-paybar { margin: 4px -14px -52px; padding: 12px 14px 16px; }
}

/* ── Invoices, exact (2026-08-03) ─────────────────────────────────────
   Uche: "implement this new invoice design, pixel for pixel… make sure
   everything is exactly the same." These are the design's own invoice
   rules (panels.css § invoices) remapped onto the shell tokens. They sit
   after the earlier .px-inv-* block on purpose — where the two overlap,
   the design wins.

   The design's shape, and what each piece is for:
     .px-row.px-inv-row  tile · row-main · inv-end
     .px-inv-end         the right-hand group: pill, money, one action
     .px-inv-amt         fixed min-width + tabular figures, so the money
                         column lines up without a table
     .px-stat-flag       the Outstanding stat, flagged with a brand rail
     .px-stat-pay        "Billed to" — full width, its own row under the
                         figures, with the card and an Update button
     .px-deep-banner     the "N invoices need attention" strip; its Review
                         button only filters the list to Unpaid (Uche's
                         call — it is a filter, not a pay-all) */
/* Wraps at EVERY width, not just on a phone: the money living on its own
   line under the description is the design now, not a narrow-screen
   fallback (Uche, 2026-08-03). .px-invfoot claims that line. */
.px-inv-row { gap: 10px; flex-wrap: wrap; }
.px-inv-end { display: flex; align-items: center; gap: 10px; flex: none; margin-left: auto; }
.px-inv-end .px-btn-p { height: 28px; padding: 0 11px; }
.px-inv-amt { min-width: 72px; text-align: right; }

.px-stat-flag { background: var(--px-wash); box-shadow: inset 2px 0 0 var(--brand); }
.px-stat-flag .px-stat-v { color: var(--px-brand-ink); }
.px-stat-pay {
  flex: 1 1 100%; display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  border-left: 0; border-top: 1px solid var(--sep);
}
.px-stat-pay .px-stat-k { flex: none; }
.px-stat-pay .px-btn { margin-left: auto; }
.px-stat-card { display: flex; align-items: center; gap: 7px; color: var(--sub); }

.px-deep-banner {
  display: flex; align-items: center; gap: 11px; padding: 12px 14px;
  border: 1px solid var(--px-line); border-radius: 12px;
  background: linear-gradient(180deg, var(--px-wash-2), var(--px-wash));
  box-shadow: var(--px-e1);
}
.px-deep-banner .px-row-t { white-space: normal; overflow: visible; }
.px-num { font-variant-numeric: tabular-nums; }

/* This used to be the narrow-panel arrangement: drop the right-hand group to
   its own full-width line and indent it past the tile. `.px-invfoot` does
   exactly that now, at every width — so the old rule was only still firing
   inside an engine panel (a 442px iframe IS under 470px), where it stretched
   .px-inv-end to 100% and pushed the Pay button onto a THIRD line below the
   paid chip instead of beside it (Uche, 2026-08-03: "the pills are
   completely misaligned"). The foot owns the layout; this only tunes it. */
@media (max-width: 470px) {
  .px-inv-amt { margin-left: auto; }
}

/* ── Invoices, second pass (2026-08-03) ───────────────────────────────
   Uche: the pill carries the STATUS, one word, its own colour — the date
   already sits under the description and had no business being in both
   places. And the amount and the Pay button are ONE control now, built
   like the Review button on the attention banner, which is the one on this
   page he liked. */
.px-pill-bad { background: var(--bad-bg); color: var(--bad-fg); }

/* The money and its button, fused. A bordered, lifted shell with the amount
   on the left in ink and the action on the right in brand — one object to
   look at and one obvious place to press, instead of a number floating
   beside a button. */
.px-payunit {
  display: inline-flex; align-items: stretch; flex: none;
  /* HARD RIGHT. It used to sit a 10px gap from the status pill, which read
     as one clump floating mid-row; the pill belongs with the description,
     the money belongs at the edge of the sheet (Uche, 2026-08-03). */
  margin-left: auto;
  border: 1px solid var(--px-line); border-radius: 8px; overflow: hidden;
  background: var(--panel); box-shadow: var(--px-e1), var(--px-hi);
}
.px-payunit .px-pu-amt {
  display: flex; align-items: center; padding: 0 8px; min-width: 52px;
  justify-content: flex-end;
  font: 650 11.5px/1 Inter, sans-serif; color: var(--ink-strong);
  font-variant-numeric: tabular-nums; letter-spacing: -.01em;
}
.px-payunit .px-pu-go {
  border: 0; border-left: 1px solid var(--px-line);
  height: 25px; padding: 0 10px; cursor: pointer;
  background: var(--brand); color: #fff;
  font: 650 11px/1 Inter, sans-serif; letter-spacing: .01em;
  transition: filter .12s;
}
.px-payunit .px-pu-go:hover { filter: brightness(1.07); }
.px-payunit .px-pu-go:disabled { opacity: .6; cursor: default; }

/* A settled row has no button, so the amount stands on its own — same
   width and the same tabular figures, so the column still lines up with
   the rows that do. */
.px-inv-settled { display: inline-flex; align-items: center; gap: 9px; flex: none; margin-left: auto; }
.px-inv-settled .px-money { min-width: 52px; text-align: right; font-size: 12px; }

/* On a phone the row wraps and the money drops to its own line, where a
   25px button is a poor target. Give it back its full height there. */
@media (max-width: 470px) {
  .px-payunit .px-pu-go { height: 30px; font-size: 12px; padding: 0 13px; }
  .px-payunit .px-pu-amt { font-size: 12.5px; padding: 0 11px; }
}

/* ── Telling one invoice from the next (2026-08-03) ───────────────────
   The generic separator is `.px-row + .px-row::before`, which needs the two
   rows to be ADJACENT siblings. An unpaid invoice has its pay drawer
   sitting between it and the next row, so exactly the rows a customer looks
   at hardest were the ones with no line between them, and the list read as
   one block (Uche: *"they all seem to be running together"*).

   So the rule belongs to the ROW, not to the gap between two of them. It is
   inset past the tile and fades out at both ends rather than butting into
   the sheet's edges — a hairline that stops before it touches anything
   reads as a seam instead of a table border. */
.px-sheet .px-inv-row::before {
  content: ""; position: absolute; left: 60px; right: 14px; top: 0; height: 1px;
  /* --sep (#f0f2f6) is all but invisible on a white panel — that is what
     "running together" was. --border-2 is the weight that actually reads,
     and the fade is short so most of the rule is at full strength. */
  background: linear-gradient(90deg,
    transparent, var(--border-2) 6%, var(--border-2) 94%, transparent);
}
/* The first row a filter leaves visible has the sheet's own border above it;
   a second line there would read as a double rule. apply() moves the class. */
.px-sheet .px-inv-row.px-first::before { display: none; }

/* ── The pay drawer under an unpaid invoice (2026-08-03) ──────────────
   Indented past the tile so it reads as belonging to the row above it, with
   real air between the fields and the button that spends the money — they
   were shoulder to shoulder in one flex line, which is a poor place to put
   an irreversible button. */
.inv-pay { padding: 2px 14px 14px 60px; border-bottom: 1px solid var(--sep); }
.inv-pay-row { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.inv-pay-row .inv-amt { flex: 1 1 120px; min-width: 0; height: 34px; }
.inv-pay-row .inv-src { flex: 1 1 160px; min-width: 0; height: 34px; padding: 0 9px; }
/* The gap before Pay is deliberately bigger than the gap between the fields:
   the fields are one group, the button is the decision. */
.inv-pay-row .inv-go { flex: 0 1 auto; min-width: 0; height: 34px; margin-left: 9px; }
/* Deliberately the quietest thing in the row: it cancels, it does not act.
   Small, low-contrast, and out of the way of the button that spends money. */
.inv-pay-row .inv-close {
  flex: none; height: 24px; padding: 0 8px; border-radius: 7px;
  font-size: 10.5px; font-weight: 550; letter-spacing: .01em;
  color: var(--muted); box-shadow: none; background: none;
  border: 1px solid var(--border-2);
}
.inv-pay-row .inv-close:hover { color: var(--ink-strong); border-color: var(--border-2); background: none; }

/* The line that says where the money comes from. --muted at 11px was too
   faint to read against the panel; this is --sub at 12px, and it turns amber
   when the choice cannot go through as picked. */
.inv-note {
  margin-top: 9px; font-size: 12px; line-height: 1.5; color: var(--sub);
}
.inv-note strong { font-weight: 650; color: var(--ink-strong); font-variant-numeric: tabular-nums; }
.inv-note.warn { color: var(--warn-fg); }
.inv-note.warn strong { color: var(--warn-fg); }
.inv-msg { margin-top: 8px; font-size: 12px; line-height: 1.5; color: var(--bad-fg); }

@media (max-width: 470px) {
  .inv-pay { padding-left: 14px; }
  .inv-pay-row .inv-go { margin-left: 0; flex: 1 1 100%; }
}

/* The paid-it confirmation inside the drawer. Money moving used to end in a
   silent page reload; this holds still and says what was taken. */
.inv-done {
  display: flex; align-items: center; gap: 11px; margin-top: 2px; padding: 11px 13px;
  border: 1px solid var(--px-line); border-radius: 11px;
  background: linear-gradient(180deg, var(--px-wash-2), var(--px-wash));
  box-shadow: var(--px-e1);
  animation: pxDoneIn .22s cubic-bezier(.2,.7,.3,1);
}
@keyframes pxDoneIn { from { opacity: 0; transform: translateY(-3px); } }
.inv-done-ring {
  width: 28px; height: 28px; flex: none; border-radius: 50%;
  display: grid; place-items: center; color: #fff; background: var(--brand);
}
.inv-done-txt { flex: 1; min-width: 0; }
.inv-done-t { font: 650 13px/1.3 Inter, sans-serif; color: var(--ink-strong); letter-spacing: -.01em; }
.inv-done-s { margin-top: 2px; font-size: 11.75px; line-height: 1.45; color: var(--sub); }
.inv-done-go { flex: none; height: 30px; }
@media (prefers-reduced-motion: reduce) { .inv-done { animation: none; } }

/* ── Scrollbars, everywhere: thin and quiet (Uche, 2026-08-08). WebKit rules
      only — setting scrollbar-width as well would make Chrome ignore these. */
*::-webkit-scrollbar { width: 6px; height: 6px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: var(--faint); border-radius: 999px; }
*::-webkit-scrollbar-thumb:hover { background: var(--muted-2); }
*::-webkit-scrollbar-corner { background: transparent; }
