/* ============================================================
   Lucriq SaaS — theme.css
   Entry point only: it declares nothing itself and @imports the
   layered partials that hold the tokens, base rules, components
   and page layouts.
   Dark is default (:root). Light overrides on [data-theme="light"].
   A shell wraps every page in <div data-theme="..."> so toggling
   the attribute re-themes the whole subtree through CSS-variable
   cascade — no re-render, no prop drilling.
   The import order below IS the cascade: abstracts → base →
   components → layout, and inside components/ and layout/ the rules
   that predate the inline-style extraction load first. Order settles
   ties between selectors of EQUAL specificity and nothing more. It
   does not preserve an inline attribute's authority: a declaration
   loses that the moment it leaves the attribute, and only a heavier
   selector or !important can outrank a class rule afterwards. What
   the order guarantees is that where a pre-existing rule and an
   extracted rule collide at equal specificity, the extracted one
   wins — which is what the inline declaration did. Where the
   pre-existing rule is heavier, or carries !important, it still
   wins and the extracted declaration is simply dead there. Every
   collision was checked per rule, per property; see each partial's
   own comment.
   ============================================================ */

/* ============================================================
   Design tokens — the sole source of truth for colour, type,
   radius and shadow. Dark is default (:root); light re-declares
   the themed subset on [data-theme="light"].
   ============================================================ */

:root,
[data-theme="dark"] {
  /* type */
  --lq-font-head: 'Geist', system-ui, sans-serif;
  --lq-font-body: 'DM Sans', system-ui, -apple-system, sans-serif;
  --lq-font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', monospace;

  /* ground + surfaces */
  --lq-bg:        #0F172A;
  --lq-bg-2:      #0B1224;
  --lq-surface:   #131F35;
  --lq-surface-2: #0B1628;
  --lq-surface-3: #1B2942;
  --lq-border:    #1E293B;
  --lq-border-2:  #2A3A54;

  /* text */
  --lq-text:   #F1F5F9;
  --lq-text-2: #C3CEDE;
  /* slate-400, was slate-500 #64748B: that measured 3.75:1 on --lq-bg and
     3.46:1 on --lq-surface. The token carries real copy, so it clears 4.5:1
     on the worse of its two grounds. Now 6.96:1 / 6.42:1. */
  --lq-muted:  #94A3B8;

  /* accent — emerald = good / live / money */
  --lq-accent:        #10B981;
  --lq-accent-2:      #059669;
  --lq-accent-3:      #34D399;
  /* One on-accent ink for both themes (was #04231A here, #FFFFFF in light):
     6.77:1 on --lq-accent, 4.55:1 on --lq-accent-2 — the far stop of the
     avatars' gradient — so one value clears 4.5:1 on both grounds. White
     measured 2.53:1 and 3.77:1 on the same two. See CHANGELOG §I. */
  --lq-accent-ink:    #042018;
  --lq-accent-text:   #6EE7B7;
  --lq-accent-soft:   rgba(16,185,129,.13);
  --lq-accent-border: rgba(16,185,129,.34);

  /* amber — stale / attention */
  --lq-warn:        #F59E0B;
  --lq-warn-text:   #FCD34D;
  --lq-warn-soft:   rgba(245,158,11,.13);
  --lq-warn-border: rgba(245,158,11,.34);

  /* red — critical */
  --lq-danger:        #F0556A;
  --lq-danger-text:   #FCA5B0;
  --lq-danger-soft:   rgba(240,85,106,.13);
  --lq-danger-border: rgba(240,85,106,.36);

  /* blue — info */
  --lq-info:        #4B8BF5;
  --lq-info-text:   #A9C8FB;
  --lq-info-soft:   rgba(75,139,245,.13);
  --lq-info-border: rgba(75,139,245,.34);

  /* violet — plan / secondary */
  --lq-plum:        #A78BFA;
  --lq-plum-soft:   rgba(167,139,250,.13);
  --lq-plum-border: rgba(167,139,250,.34);

  --lq-shadow-sm: 0 1px 2px rgba(0,0,0,.36);
  --lq-shadow:    0 1px 2px rgba(0,0,0,.4), 0 14px 34px rgba(0,0,0,.32);
  --lq-shadow-lg: 0 24px 60px rgba(0,0,0,.5);
  --lq-ring:      0 0 0 3px rgba(16,185,129,.28);
  --lq-scrim:     rgba(2,6,16,.62);

  /* radii + rhythm */
  --lq-r-sm: 8px;
  --lq-r:    12px;
  --lq-r-lg: 16px;
}

[data-theme="light"] {
  --lq-bg:        #EEF2F7;
  --lq-bg-2:      #FFFFFF;
  --lq-surface:   #FFFFFF;
  --lq-surface-2: #F1F5F9;
  --lq-surface-3: #F8FAFC;
  --lq-border:    #E2E8F0;
  --lq-border-2:  #CBD5E1;

  --lq-text:   #0F172A;
  --lq-text-2: #334155;
  /* slate-600, was slate-500 #64748B: 4.23:1 on --lq-bg. Now 6.74:1 / 7.58:1. */
  --lq-muted:  #475569;

  --lq-accent:        #10B981;
  --lq-accent-2:      #059669;
  --lq-accent-3:      #059669;
  /* Same value as dark: this token no longer differs by theme. Still declared
     here, so the 43-name / 80-declaration token counts do not move. */
  --lq-accent-ink:    #042018;
  /* was #047857: 4.45:1 on accent-soft over --lq-bg, failing by 0.05. Now 6.24:1.
     Light only — dark measures 8.76:1 and is unchanged. */
  --lq-accent-text:   #065F46;
  --lq-accent-soft:   rgba(16,185,129,.11);
  --lq-accent-border: rgba(16,185,129,.30);

  --lq-warn:        #D97706;
  /* was #B45309: 4.47:1 on warn-soft over --lq-surface, failing by 0.03, and
     3.99:1 on warn-soft over --lq-bg. Now 5.10 and 4.56 — this value clears AA
     on every ground the token meets in the tree, the tightest being warn-soft
     over --lq-bg; #b35208, #b15006 and #af4e04 each clear the surface but not
     that one. Light only — dark's #FCD34D on its own warn-soft measures 8.13:1
     on the tightest of its five grounds (--lq-surface-3) and 10.64:1 on the
     loosest, so it clears AA everywhere and is unchanged. Consumers: nine rules,
     four of them text on warn-soft. */
  --lq-warn-text:   #AA4900;
  --lq-warn-soft:   rgba(217,119,6,.11);
  --lq-warn-border: rgba(217,119,6,.28);

  --lq-danger:        #DC2626;
  --lq-danger-text:   #B91C1C;
  --lq-danger-soft:   rgba(220,38,38,.09);
  --lq-danger-border: rgba(220,38,38,.26);

  --lq-info:        #2563EB;
  --lq-info-text:   #1D4ED8;
  --lq-info-soft:   rgba(37,99,235,.09);
  --lq-info-border: rgba(37,99,235,.24);

  --lq-plum:        #7C3AED;
  --lq-plum-soft:   rgba(124,58,237,.09);
  --lq-plum-border: rgba(124,58,237,.26);

  --lq-shadow-sm: 0 1px 2px rgba(15,23,42,.06);
  --lq-shadow:    0 1px 2px rgba(15,23,42,.06), 0 10px 28px rgba(15,23,42,.08);
  --lq-shadow-lg: 0 24px 60px rgba(15,23,42,.16);
  --lq-ring:      0 0 0 3px rgba(16,185,129,.22);
  --lq-scrim:     rgba(15,23,42,.42);
}

/* ============================================================
   Self-hosted brand typefaces — DM Sans, Geist, JetBrains Mono.

   The specifications used to link these from `fonts.googleapis.com`. Three
   separate things made that wrong:

   1. The application cannot load them. The edge enforces `style-src 'self';
      font-src 'self'` with no external origin permitted
      (`lucriq-backend/deploy/edge/lucriq-security-headers.conf`), so every
      shipped page rendered in `system-ui` instead of the drawn typefaces.
   2. The mirror was incomplete. `assets/css/` is copied verbatim into the
      application, but the fonts were not in it — so the application had to
      keep its own parallel copy, which is a second expression of one design.
   3. Baseline capture was not reproducible. Fetching three families over the
      network on every capture made `document.fonts.ready` resolve early and
      re-wrapped text between identical runs.

   The `.woff2` files in `../fonts/` are byte-identical to what Google serves:
   fetched from the same css2 URL the specifications linked, latin and
   latin-ext subsets, which are the only subsets any Phase 0 copy needs. The
   glyphs are therefore the ones these specifications were drawn with.

   Paths are relative to this file, so the whole `assets/css/` tree relocates
   without edits — including into `webapp/src/styles/`.

   Regenerate by re-fetching that css2 URL with a browser User-Agent and
   rewriting each `src: url(...)` to `../fonts/<family>-<subset>.woff2`.
   ============================================================ */

@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/assets/dm-sans-latin-ext-oos9povj.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: 'DM Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/assets/dm-sans-latin-Bf69Tn_J.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;
}

@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/assets/dm-sans-latin-ext-oos9povj.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: 'DM Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/assets/dm-sans-latin-Bf69Tn_J.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;
}

@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/assets/dm-sans-latin-ext-oos9povj.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: 'DM Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/assets/dm-sans-latin-Bf69Tn_J.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;
}

@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/assets/dm-sans-latin-ext-oos9povj.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: 'DM Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/assets/dm-sans-latin-Bf69Tn_J.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;
}

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/assets/geist-latin-ext-DC-KSUi6.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: 'Geist';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/assets/geist-latin-BgDaEnEv.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;
}

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/assets/geist-latin-ext-DC-KSUi6.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: 'Geist';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/assets/geist-latin-BgDaEnEv.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;
}

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url(/assets/geist-latin-ext-DC-KSUi6.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: 'Geist';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url(/assets/geist-latin-BgDaEnEv.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;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/assets/jetbrains-mono-latin-ext-DIC32ArD.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: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/assets/jetbrains-mono-latin-6fWv1k7M.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;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/assets/jetbrains-mono-latin-ext-DIC32ArD.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: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/assets/jetbrains-mono-latin-6fWv1k7M.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;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/assets/jetbrains-mono-latin-ext-DIC32ArD.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: 'JetBrains Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/assets/jetbrains-mono-latin-6fWv1k7M.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;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }

/* thin ~4px scrollbars */
* { scrollbar-width: thin; scrollbar-color: var(--lq-border-2) transparent; }
*::-webkit-scrollbar { width: 5px; height: 5px; }
*::-webkit-scrollbar-thumb { background: var(--lq-border-2); border-radius: 99px; }
*::-webkit-scrollbar-track { background: transparent; }

::selection { background: rgba(16,185,129,.28); }
body {
  background: var(--lq-bg);
  color: var(--lq-text);
  font-family: var(--lq-font-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
@keyframes lq-ping   { 0% { transform: scale(1); opacity: .55; } 70%,100% { transform: scale(2.6); opacity: 0; } }
@keyframes lq-breathe{ 0%,100% { transform: scale(1); opacity: 1; } 50% { transform: scale(.9); opacity: .78; } }
@keyframes lq-fade   { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes lq-fadein { from { opacity: 0; } to { opacity: 1; } }
@keyframes lq-pop    { from { opacity: 0; transform: translateY(10px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes lq-rise   { from { transform: scaleY(.3); opacity: .4; } to { transform: scaleY(1); opacity: 1; } }
@keyframes lq-spin   { to { transform: rotate(360deg); } }
@keyframes lq-sheen  { from { transform: translateX(-120%); } to { transform: translateX(320%); } }

/* ============================================================
   Reduced-motion guard. Moved here 16 August 2026 from the web app's
   `a11y-overrides.css`, verbatim, so the specifications carry their own guard
   and the application stops needing a file to add one.

   Why it is needed: the animations above are applied unconditionally. A
   merchant who has asked their operating system for reduced motion still got
   every one of them. `lq-pop` and `lq-fadein` animate opacity from 0, which
   makes it measurable rather than merely irritating — mid-fade, `--lq-text-2`
   on the signup card composites to 2.74:1 against a 4.5:1 requirement, so copy
   is briefly unreadable. That is exactly what the setting exists to prevent.

   Why in this file rather than a new partial: this is the motion concern and
   the motion concern lives here. Note the scope is wider than the keyframes
   above — the universal selector also settles the six `transition:`
   declarations elsewhere in the tree, which no `@keyframes` block covers.

   Why `!important` and `*`: the guard has to beat whatever an individual rule
   declares, including future ones, and a user preference outranks a house
   style. These six declarations are the only `!important` in `base/` and they
   are inert unless the preference is set — outside the media query this file
   changes nothing. They do move the tree-wide `!important` count from 21
   declarations on 17 lines to 27 on 23; see `CHANGELOG-saas-static.md` §E
   item 3, which now carries both figures and the reason.

   Scope note: this is the merchant-facing half only. It does NOT make the
   Playwright suite deterministic — `reducedMotion: "reduce"` as a context
   option does not reach the page in Playwright 1.62.1; only an explicit
   `page.emulateMedia()` does. The suite settles animations itself.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 1ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }
}

/* Fixed bottom docks (toast, preview switcher) sit above the mobile bottom nav.
   These elements carry NO inline `bottom`, so this applies with normal specificity. */
.lq-safe-bottom { bottom: 18px; }
@media (max-width: 899px) { .lq-safe-bottom { bottom: calc(80px + env(safe-area-inset-bottom, 0px)); } }

/* Hidden from the page, present in the accessibility tree. Written for the
   Stores visible-row live region (LUCRIQ-DECISIONS.md §4.32): the application
   had to hide that element with nine inline declarations, the CSS mirror being
   byte-identical to this tree and carrying no class for it to wear. These are
   those nine, verbatim — the clipped-1px idiom, deliberately not display:none
   (which leaves nothing to announce) and not opacity or visibility (which keep
   the box in layout). The plugin surface holds the same rule under its
   pre-standard name .lq-sr-only; the name here is the full-word one the SaaS
   naming rules ask for. A utility like .lq-safe-bottom above, not a block. */
.lq-visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* ============================================================
   Static-conversion helpers (added when the SaaS mocks were
   converted from DC/React to fully-static .dc.html).
   Behavior lives in support.js.
   These reproduce, in CSS, presentation the DC runtime used to
   compute in JS (responsive chrome, active nav, hover/focus,
   theme-toggle state). No color/type token is changed here —
   visual language stays owned by the mocks.
   ============================================================ */

/* Responsive shell chrome — desktop nav vs mobile header/tabs at the 899px break */
@media (min-width: 900px) { .lq-only-mobile  { display: none !important; } }
@media (max-width: 899px) { .lq-only-desktop { display: none !important; } }
/* Buttons.
   One block, .lq-button, with a modifier per treatment and a compound
   modifier per treatment-and-size: --primary / --primary-small, --secondary /
   --secondary-medium, --link. Pass 2 took those names from the rename table
   in documents/LUCRIQ-SAAS-CODING-STANDARDS.md; the pre-standard names they
   replaced are listed in CHANGELOG-saas-static.md §G. Size lives on the
   treatment rather than on the block because the values differ between them —
   primary --medium is 11px 15px / 13px, secondary --medium 9px 15px / 12.5px —
   so a shared .lq-button--medium would have to be two rules. No bare .lq-button
   rule is declared: no element in the specifications wears one, and pass 2
   renames rather than restructures, so nothing was added to the markup.
   The :hover, :disabled and :disabled:hover rules predate the extraction and
   stay more specific than every base/modifier rule below, so hover filter,
   disabled opacity and disabled cursor still win.
   Rule order matters and is deliberate: base -> size -> layout -> treatment.
   A treatment sits last so .lq-button--primary-quiet's type size beats the size
   modifier it is combined with, which is what the markup does inline. */
.lq-button--primary:hover { filter: brightness(1.06); }
.lq-button--secondary:hover { background: var(--lq-surface-3); }

/* Inline link-style button (session revoke, etc.) */
.lq-button--link:hover { text-decoration: underline; }
/* The class sets no colour of its own, which was safe while it only dressed a
   <button> inheriting from its parent. On an <a href> the anchor falls back to
   the user-agent #0000EE — 1.75:1 on a dark card. Dark-only defect; #0000EE on
   white measures 9.40:1.
   :where() is the fix, not decoration: it contributes zero specificity, so any
   page rule declaring its own colour still wins — .lq-session__revoke keeps
   --lq-danger-text and .lq-store-detail-back keeps --lq-muted — while an
   element with no colour of its own gets the accessible one. Written bare the
   selector is (0,1,0), a tie with those two broken by source order, and
   a.lq-button--link:link at (0,2,1) beats .lq-store-detail-back outright. Do not
   remove the :where(). */
:where(.lq-button--link, a.lq-button--link:link, a.lq-button--link:visited) { color: var(--lq-accent-text); }

/* Disabled action buttons (a not-due position renders its actions visible + disabled) */
.lq-button--secondary:disabled, .lq-button--primary:disabled { opacity: .5; cursor: not-allowed; }
.lq-button--secondary:disabled:hover { background: var(--lq-surface); }
.lq-button--primary:disabled:hover { filter: none; }

/* ---- Primary ----
   Base is the filled accent treatment every one of the source's primary
   buttons now shares. The 9px radius has no token. The gradient variant was
   the only treatment in the tree that ignored both --lq-accent-ink and the
   theme; it is flattened onto this fill, leaving the modifier below carrying
   nothing but its lift. See CHANGELOG-saas-static.md §I. */
.lq-button--primary {
  border-radius: 9px;
  border: 1px solid transparent;
  background: var(--lq-accent);
  color: var(--lq-accent-ink);
  font-family: var(--lq-font-body);
  font-weight: 700;
  cursor: pointer;
  text-decoration: none;
}
.lq-button--primary-small  { padding: 9px 16px; font-size: 12.5px; }
.lq-button--primary-medium { padding: 11px 15px; font-size: 13px; }
.lq-button--primary-wide   { padding: 10px 20px; font-size: 13px; }
.lq-button--primary-large  { padding: 12px; font-size: 13.5px; }
/* --full is width only; --block also blockifies and centres, for anchors. */
.lq-button--primary-full  { width: 100%; }
.lq-button--primary-block { display: block; width: 100%; text-align: center; }
/* Raised call-to-action treatment: the base primary plus a soft accent lift.
   Fill, ink and border are the base's, so this declares only the shadow — the
   one property no other primary has. Worn by the seven auth-flow CTAs, which
   are the single action on a centred card; nothing else wears it. */
.lq-button--primary-raised {
  box-shadow: 0 6px 16px var(--lq-accent-soft);
}
/* Quiet treatment: a primary button carrying a secondary appearance, which is
   how the markup writes it. Phase 1 preserves that classification. */
.lq-button--primary-quiet { border-color: var(--lq-border-2); background: var(--lq-surface-2); color: var(--lq-text); font-size: 13px; }

/* ---- Secondary ----
   Accepted adoption, recorded rather than fixed: this base reaches seven
   icon-only Back buttons on Connect store and Stores — pages pass 1 has not
   extracted — giving them font-family and font-weight their inline styles
   never declared. Their only child is an <svg>, so no text can render and the
   declarations cannot paint. Verified zero rect drift at 1280 and 390 in both
   themes. If either button ever gains a text label, this becomes visible. */
.lq-button--secondary {
  border-radius: 9px;
  border: 1px solid var(--lq-border-2);
  background: var(--lq-surface);
  color: var(--lq-text);
  font-family: var(--lq-font-body);
  font-weight: 600;
  cursor: pointer;
}
.lq-button--secondary-medium { padding: 9px 15px; font-size: 12.5px; }
.lq-button--secondary-large  { padding: 11px 15px; font-size: 13px; }
.lq-button--secondary-full   { width: 100%; }

/* Statically-rendered keyboard focus state (the specs show the ring applied).
   Same declarations as the :focus-visible rule in components/_modal.css. */
.lq-button--primary.is-focused, .lq-button--secondary.is-focused { outline: none; box-shadow: var(--lq-ring); }
.lq-input:focus       { border-color: var(--lq-accent); box-shadow: var(--lq-ring); }

/* Base skeleton shared by every .lq-input in the source. Deliberately
   omits padding, background, font-size and outline: those differ per
   treatment, and putting outline:none here would remove the focus
   outline from inputs on pages not yet extracted. The 9px radius has no
   token. The :focus rule above stays more specific than any modifier. */
.lq-input {
  width: 100%;
  border-radius: 9px;
  border: 1px solid var(--lq-border-2);
  color: var(--lq-text);
  font-family: var(--lq-font-body);
}
/* Auth-card field treatment. */
.lq-input--large { padding: 11px 13px; background: var(--lq-surface-2); font-size: 13.5px; outline: none; }
/* Room for a trailing affix button; must follow the size modifiers. */
.lq-input--trailing-icon { padding: 11px 42px 11px 13px; }
/* Invite-token field: monospaced, accent-tinted. */
.lq-input--token {
  padding: 11px 13px;
  border-color: var(--lq-accent-border);
  background: var(--lq-accent-soft);
  font-family: var(--lq-font-mono);
  font-size: 13px;
  letter-spacing: .04em;
  outline: none;
}
/* Dialog field treatments. */
.lq-input--dialog { padding: 9px 12px; background: var(--lq-surface); font-size: 13px; }
.lq-input--search { padding: 9px 12px 9px 33px; background: var(--lq-surface); font-size: 12.5px; }
/* Statically-rendered focus state (the specs show the focus ring applied).
   Matches the :focus rule above; both are 0,2,0 and set the same values. */
.lq-input.is-focused { border-color: var(--lq-accent); box-shadow: var(--lq-ring); outline: none; }
/* ---- Signup: live password-rule meter (behavior in support.js) ----
   Each requirement chip (.js-lq-pw-rule) fills its ring with the accent and
   swaps its dot for a check as the rule is satisfied while typing. */
.lq-password-check { display: none; }
.js-lq-pw-rule.is-met { color: var(--lq-text-2) !important; }
.js-lq-pw-rule.is-met .lq-password-ring { background: var(--lq-accent) !important; }
.js-lq-pw-rule.is-met .lq-password-dot  { display: none; }
.js-lq-pw-rule.is-met .lq-password-check { display: block; }

/* Structure extracted from the rule chips' inline styles. Every class here
   now spells its words out — pass 2 renamed the pre-standard names to
   .lq-password-check / -ring / -dot. The .js-lq-pw-rule hook keeps its name:
   support.js queries it. None of the rules below sets a property
   the .is-met rules above set — colour, ring background and the dot/check
   display swap stay theirs, and two of those carry !important, so the
   extracted colour is simply dead once a rule is met. That is expected. */
.lq-password-rules { display: flex; flex-wrap: wrap; gap: 7px 14px; margin-top: 10px; }
.lq-password-rule { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600; color: var(--lq-muted); transition: color .18s ease; }
.lq-password-ring { display: grid; place-items: center; width: 15px; height: 15px; border-radius: 50%; flex-shrink: 0; background: var(--lq-surface-2); transition: background .18s ease; }
.lq-password-dot { width: 4px; height: 4px; border-radius: 50%; background: var(--lq-muted); }
.lq-password-check { width: 9px; height: 9px; fill: none; stroke: var(--lq-accent-ink); stroke-width: 3.4; stroke-linecap: round; stroke-linejoin: round; }
/* Theme-toggle knob + icons follow [data-theme] (was JS-computed knobX / icon colors) */
.lq-theme-toggle__knob { transform: translateX(0); }
[data-theme="light"] .lq-theme-toggle__knob { transform: translateX(30px); }
.lq-theme-toggle__moon { stroke: var(--lq-accent-ink); }
[data-theme="light"] .lq-theme-toggle__moon { stroke: var(--lq-muted); }
.lq-theme-toggle__sun  { stroke: var(--lq-muted); }
[data-theme="light"] .lq-theme-toggle__sun  { stroke: var(--lq-accent-ink); }

/* Structure, extracted from the inline styles the toggle carried.
   Appended after the state rules above: no property overlaps, so the
   cascade is unchanged. The knob's rgba() shadow was already a literal
   in that markup and no token carries it. */
.lq-theme-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  width: 60px;
  height: 30px;
  padding: 3px;
  border-radius: 99px;
  border: 1px solid var(--lq-border);
  background: var(--lq-surface-2);
  cursor: pointer;
  font-family: var(--lq-font-body);
  flex-shrink: 0;
}
.lq-theme-toggle__knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--lq-accent);
  box-shadow: 0 2px 6px rgba(0,0,0,.35);
  transition: transform .24s cubic-bezier(.34,1.3,.5,1);
}
.lq-theme-toggle__icon { position: relative; z-index: 1; flex: 1; display: grid; place-items: center; }
.lq-theme-toggle__moon, .lq-theme-toggle__sun { fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.lq-theme-toggle__moon { width: 14px; height: 14px; }
.lq-theme-toggle__sun  { width: 15px; height: 15px; }
/* Overview preview-switcher buttons (were the btn() active/inactive inline styles) */
.lq-ovbtn { font-weight: 600; background: transparent; color: var(--lq-text-2); }
.lq-ovbtn.is-active { font-weight: 700; background: var(--lq-accent); color: var(--lq-accent-ink); }

/* Auth-flow state preview switcher buttons (Verify email) */
.lq-vbtn { font-weight: 600; background: var(--lq-surface-2); color: var(--lq-text-2); }
.lq-vbtn.is-active { font-weight: 700; background: var(--lq-accent); color: var(--lq-accent-ink); }

/* Switcher chrome, extracted from the same prototype-only markup. Stays in
   this file so the whole question of whether these belong in a shared
   stylesheet remains one deletable unit. The base rules below add no
   property the state rules above set. */
.lq-preview-switcher {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  margin-top: 24px;
  padding-top: 16px;
  border-top: 1px solid var(--lq-border);
}
.lq-preview-switcher__label {
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--lq-muted);
  padding-right: 4px;
}
.lq-vbtn { display: inline-flex; align-items: center; padding: 5px 11px; border: none; border-radius: 99px; font-family: var(--lq-font-body); font-size: 11px; cursor: pointer; }

/* Overview's fixed switcher dock. .lq-safe-bottom supplies `bottom`.
   Prototype-only, kept in this file with the rest of the switcher CSS. */
.lq-preview-dock {
  position: fixed;
  right: 12px;
  z-index: 120;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 5px;
  max-width: calc(100vw - 24px);
  background: var(--lq-surface-3);
  border: 1px solid var(--lq-border-2);
  border-radius: var(--lq-r-lg);
  padding: 5px 7px;
  box-shadow: var(--lq-shadow);
}
/* A dock inside a modal (Store detail's recovery switcher) top-docks below the
   tree's 899px break. Bottom-docked, it lands on the dialog's footer at 390×844
   and swallows the clicks meant for Cancel and the primary. Scoped by context
   rather than a modifier so no third name joins the two the reference capture
   excludes, and so Overview's page-level dock is untouched. (0,2,0) beats
   .lq-safe-bottom's (0,1,0), so `bottom` is released without !important.
   67px = the 55px mobile shell header + the dock's own 12px gutter: the header
   is sticky and, through its ancestor stacking context, takes the hit test from
   anything overlapping it, so the dock has to clear it rather than merely
   outrank it. Below that it still clears the tallest view's head by ~63px. */
@media (max-width: 899px) {
  .lq-modal .lq-preview-dock { top: 67px; bottom: auto; }
}
.lq-preview-dock__label { font-size: 9.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--lq-muted); padding: 0 6px; }
/* Geometry for .lq-ovbtn; the state rules above set only font-weight,
   background and color, so nothing here collides with them. */
.lq-ovbtn { display: inline-flex; align-items: center; padding: 6px 12px; border: none; border-radius: 99px; font-family: var(--lq-font-body); font-size: 11.5px; cursor: pointer; }
/* Accessible disclosure for the per-position exact instant + frozen member list
   (expandable text — reachable by keyboard and touch, unlike a native title tooltip) */
.lq-details > summary { list-style: none; cursor: pointer; }
.lq-details > summary::-webkit-details-marker { display: none; }
.lq-details[open] .lq-chev { transform: rotate(180deg); }

/* Structure extracted from the per-slot variation disclosure. The rules above
   set list-style, cursor and the open-state chevron rotation; nothing below
   touches those. */
.lq-details__summary { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; font-weight: 600; color: var(--lq-text-2); }
.lq-details__summary-icon { display: inline-flex; color: var(--lq-muted); }
.lq-details__summary-glyph { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }
.lq-chev { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transition: transform .18s ease; flex-shrink: 0; }
.lq-details__panel { margin-top: 9px; padding: 12px; background: var(--lq-surface-2); border: 1px solid var(--lq-border); border-radius: 9px; }
/* ---- Unlock-slot allocation (Plan & Usage) ---- */
/* Picker rows behave like selectable controls */
.lq-slot-picker:hover { background: var(--lq-surface-3); }
/* Mobile action crowding: an occupied variable row's three actions stack full-width */
@media (max-width: 560px) {
  .lq-slot-actions { width: 100%; flex-direction: column; align-items: stretch; }
  .lq-slot-actions button { width: 100%; }
}

/* Structure extracted from the slot rows, pickers and their actions. The
   :hover rule and the max-560px stacking block above set no property below.
   .lq-slot-row and .lq-slot-actions keep their pre-existing names. Pass 2
   renamed the picker and the summary-borne state class to .lq-slot-picker and
   .lq-slot-row__state-warn (renamed in pass 2, see CHANGELOG-saas-static.md
   §G): the state class's block sits on a sibling element, so the modifier
   spelling was never true. --muted is worn
   with .lq-slot-row__state and stays a modifier. */
.lq-slot-row { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 16px; padding: 14px 18px; border-top: 1px solid var(--lq-border); }
.lq-slot-row__product { display: flex; align-items: center; gap: 11px; min-width: 0; flex: 1 1 230px; }
.lq-slot-row__when { font-size: 11.5px; color: var(--lq-muted); }
.lq-slot-row__state { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; font-weight: 600; color: var(--lq-accent-text); }
.lq-slot-row__state-warn { gap: 6px; color: var(--lq-warn-text); }
.lq-slot-row__state--muted { gap: 6px; color: var(--lq-text-2); }
.lq-slot-row__state-glyph { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2.1; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }
.lq-slot-row__state-icon { display: inline-flex; }
.lq-slot-row__state-icon--warn { color: var(--lq-warn); }
.lq-slot-row__state-icon--muted { color: var(--lq-muted); }
.lq-slot-row__stack { display: flex; flex-direction: column; gap: 5px; align-items: flex-start; }
.lq-slot-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; flex: 1 1 100%; }
.lq-slot-action { padding: 7px 12px; border-radius: var(--lq-r-sm); font-size: 12px; }
.lq-slot-picker { width: 100%; display: flex; align-items: center; gap: 12px; padding: 11px 20px; border: none; border-bottom: 1px solid var(--lq-border); background: transparent; text-align: left; cursor: pointer; color: var(--lq-text); font-family: var(--lq-font-body); }
.lq-slot-action--wide { padding: 10px 20px; font-size: 13px; }

/* First run — no slot has ever been assigned. Distinct from an empty slot,
   which is a row with an Assign action, and distinct from
   .lq-unlock-slots-notice, which is the scheduled-staging banner and explains
   a rule rather than offering a way in. This one has no data to show, so it
   fills the panel body instead of sitting above it: centred column, one line
   of explanation, one primary action. It is the only slots state that renders
   with no .lq-slot-row present, which is what makes it first-run rather than
   empty. Lives here rather than in layout/ because the slots panel is rendered
   by both Plan & Usage and Unlock Slots. */
.lq-slots-first-run { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 10px; padding: 34px 24px; }
.lq-slots-first-run__icon { width: 40px; height: 40px; border-radius: 10px; display: grid; place-items: center; background: var(--lq-surface-2); border: 1px solid var(--lq-border); color: var(--lq-accent); }
.lq-slots-first-run__glyph { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.lq-slots-first-run__title { font-size: 13.5px; font-weight: 700; color: var(--lq-text); }
.lq-slots-first-run__text { font-size: 12px; color: var(--lq-text-2); line-height: 1.55; max-width: 46ch; text-wrap: pretty; }
/* Spacing only. The size comes from a .lq-button--primary-* modifier in the
   markup — --primary-wide for this one — because .lq-button--primary declares
   no padding or font-size and a bare pairing renders at UA defaults. Declaring
   them here instead would fork the size scale into a component rule, which is
   what .lq-unlock-slots-notice__action had to do and records as a restatement. */
.lq-slots-first-run__action { margin-top: 4px; }
/* ============================================================
   Modal / dialog — shared overlay component (Lucriq_Modal).
   Reusable surface only: overlay scrim + centered dialog with a
   pinned header/footer and a single scrolling body. Derived
   strictly from existing tokens. Live pages open it via the
   existing support.js hook ([data-lq-modal-open] sets
   display:grid); the showcase sets display:grid statically inside
   simulated frames (with inline position:absolute). No color or
   type token is introduced here.
   ============================================================ */
.lq-modal {
  position: fixed; inset: 0; z-index: 120;
  /* Closed is the resting state, so it is the default: every page holding
     dialogs in the DOM keeps them out of layout without marking each one. */
  display: none;
  place-items: center;
  padding: 32px 20px;
  background: var(--lq-scrim);
  overflow-y: auto;
  animation: lq-fadein .16s ease;
}

/* Open state. The tree had no way to say "open": the mock wrote display:grid as
   an inline style, which no adopter can reproduce without a stylesheet of its own.
   .is-open is the state hook the naming standard already defines, and the tree
   already toggles display through one: components/_password-check.css's
   .js-lq-pw-rule.is-met swaps .lq-password-dot for .lq-password-check that way,
   without !important on either. Three state classes are live besides this one —
   .is-met, .is-focused, .is-active. support.js toggles this class instead of the
   inline style, so the reference capture and the application agree on an open
   dialog's class list. Equal specificity (0,2,0) to components/_showcase.css's
   .lq-modal-preview .lq-modal { display: flex }, which loads one import later and
   so still wins for a dialog catalogued inside a showcase frame. Lucriq_Modal's
   own frames use a third mechanism, .lq-modal-frame__overlay (0,1,0), which never
   carries this class and declares grid regardless. */
.lq-modal.is-open { display: grid; }
.lq-modal__dialog {
  display: flex; flex-direction: column;
  width: 100%; max-width: 520px;
  max-height: min(640px, calc(100% - 24px));
  background: var(--lq-bg-2);
  border: 1px solid var(--lq-border);
  border-radius: var(--lq-r-lg);
  box-shadow: var(--lq-shadow-lg);
  overflow: hidden;
  animation: lq-pop .2s ease;
}
.lq-modal__head, .lq-modal__foot { flex-shrink: 0; }
.lq-modal__body { flex: 1 1 auto; min-height: 0; overflow: hidden auto; }
.lq-modal__foot button { white-space: nowrap; }
.lq-modal__close:hover { background: var(--lq-surface-3); color: var(--lq-text); }

/* Keyboard focus ring inside dialogs (inputs keep their .lq-input:focus rule) */
.lq-modal__close:focus-visible,
.lq-button--primary:focus-visible,
.lq-button--secondary:focus-visible,
.lq-modal a:focus-visible { outline: none; box-shadow: var(--lq-ring); }

/* Mobile: dock to the bottom and clear the fixed bottom tab bar (~80px + safe area) */
@media (max-width: 899px) {
  .lq-modal { place-items: end center; padding: 14px 12px calc(80px + 14px + env(safe-area-inset-bottom, 0px)); }
  .lq-modal__dialog { max-width: 100%; max-height: calc(100% - 80px - 28px - env(safe-area-inset-bottom, 0px)); }
}

/* ---- Dialog internals, extracted from the overlay specifications ----
   The four rules above (.lq-modal__head/__foot flex-shrink, __body flex,
   __foot button, __close:hover) predate this and set no property below.
   Head, body and foot each have a compact/centred variant because the
   phone frame and the blocking alert use different padding. */
/* Base holds only what EVERY dialog head in the source declares. The padding,
   the divider and the spread belong to the standard head and are modifiers:
   Store detail's head has its own padding and no divider, and a base carrying
   those would draw a border it never had. Same reasoning for foot and close. */
.lq-modal__head { display: flex; align-items: flex-start; gap: 14px; }
.lq-modal__head--standard { justify-content: space-between; padding: 17px 20px; }
.lq-modal__head--divided { border-bottom: 1px solid var(--lq-border); }
.lq-modal__head--compact { padding: 15px 18px; }
.lq-modal__titles { flex: 1; min-width: 0; }
.lq-modal__title { font-family: var(--lq-font-head); font-weight: 800; font-size: 15.5px; letter-spacing: -.01em; color: var(--lq-text); }
.lq-modal__title--compact { font-size: 15px; }
.lq-modal__subtitle { font-size: 11.5px; color: var(--lq-muted); margin-top: 3px; }
.lq-modal__subtitle--compact { font-size: 11px; }
.lq-modal__close {
  display: grid;
  place-items: center;
  border: none;
  background: transparent;
  color: var(--lq-muted);
  cursor: pointer;
}
.lq-modal__close--boxed { width: 30px; height: 30px; flex-shrink: 0; border-radius: var(--lq-r-sm); }
.lq-modal__close-glyph { width: 16px; height: 16px; }

.lq-modal__body--padded { padding: 18px 20px; font-size: 13px; color: var(--lq-text-2); line-height: 1.6; }
.lq-modal__body--compact { padding: 16px 18px; font-size: 12.5px; color: var(--lq-text-2); line-height: 1.6; }
.lq-modal__body--centered { padding: 26px 24px; text-align: center; }
.lq-modal__paragraph { margin: 0 0 12px; text-wrap: pretty; }
.lq-modal__paragraph--last { margin-bottom: 0; }
.lq-modal__code { font-family: var(--lq-font-mono); font-size: 11.5px; color: var(--lq-text); }
.lq-modal__label { display: block; font-size: 11.5px; font-weight: 600; color: var(--lq-text); margin-bottom: 6px; }

/* Pinned band between head and body (a dialog's own search / filter row). */
.lq-modal__toolbar { flex-shrink: 0; padding: 13px 20px; border-bottom: 1px solid var(--lq-border); }
.lq-modal__toolbar-hint { font-size: 11px; color: var(--lq-muted); margin-top: 9px; }

.lq-modal__foot { display: flex; }
/* Do NOT combine --standard with --stacked: see the note on --stacked below. */
.lq-modal__foot--standard { align-items: center; justify-content: flex-end; gap: 10px; padding: 14px 20px; }
.lq-modal__foot--divided { border-top: 1px solid var(--lq-border); }
.lq-modal__foot--between { justify-content: space-between; }
.lq-modal__foot--centered { align-items: center; justify-content: center; padding: 14px 20px 18px; }
/* Stacked feet declare no align-items or justify-content, so they must NOT be
   combined with --standard: on a column-reverse foot align-items is the
   horizontal axis, and center would shrink the full-width buttons. */
.lq-modal__foot--stacked { flex-direction: column-reverse; gap: 9px; padding: 13px 18px; }
.lq-modal__foot-actions { display: flex; gap: 10px; }
.lq-modal__foot-note { font-size: 11.5px; color: var(--lq-muted); }

/* Blocking-alert dialog contents. The 46px disc has no radius token (50%). */
.lq-modal__alert-icon {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  margin: 0 auto 15px;
  display: grid;
  place-items: center;
  background: var(--lq-danger-soft);
  color: var(--lq-danger);
}
.lq-modal__alert-glyph { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.lq-modal__alert-title { font-family: var(--lq-font-head); font-weight: 800; font-size: 16px; letter-spacing: -.01em; color: var(--lq-text); margin-bottom: 7px; }
.lq-modal__alert-text { margin: 0; font-size: 12.5px; color: var(--lq-text-2); line-height: 1.55; text-wrap: pretty; }

/* Title variant for heads whose title must not wrap. */
.lq-modal__title--nowrap { white-space: nowrap; }
/* ---- Component-showcase framing (Lucriq_Unlock_Slots / Lucriq_Modal) ----
   Render the fixed .lq-modal overlay inside a bounded, statically-open frame so
   every dialog state can be catalogued without covering the page. Chrome only —
   the dialog markup inside stays byte-identical to the live pages. */
.lq-modal-preview { position: relative; overflow: hidden; }
.lq-modal-preview .lq-modal { position: absolute; display: flex; align-items: center; justify-content: center; animation: none; }
.lq-modal-preview .lq-modal__dialog { animation: none; }
.lq-modal-preview--mobile .lq-modal { align-items: flex-end; padding: 14px 12px 78px; }

/* ---- Specification-page canvas ----
   The standalone page chrome every shared-component spec renders in:
   a full-height themed sheet with a monospace caption naming the spec. */
.lq-showcase {
  min-height: 100vh;
  background: var(--lq-bg);
  color: var(--lq-text);
  font-family: var(--lq-font-body);
  padding: 40px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  align-items: flex-start;
}
.lq-showcase__caption {
  font-family: var(--lq-font-mono);
  font-size: 11px;
  color: var(--lq-muted);
  letter-spacing: .04em;
  text-transform: uppercase;
}
.lq-showcase__note { font-size: 12.5px; color: var(--lq-muted); }
.lq-showcase__code { font-family: var(--lq-font-mono); }
.lq-showcase__row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.lq-showcase__caption--sub { font-size: 10px; letter-spacing: .05em; margin-top: 6px; }
.lq-showcase--tight { gap: 14px; }
.lq-showcase__intro { font-size: 12.5px; color: var(--lq-text-2); line-height: 1.55; max-width: 70ch; text-wrap: pretty; margin-bottom: 14px; }
.lq-showcase__code--strong { color: var(--lq-text); }
.lq-showcase__frame-label { font-family: var(--lq-font-mono); font-size: 10.5px; color: var(--lq-muted); letter-spacing: .03em; }
.lq-showcase__frame-label--spaced { margin-top: 22px; }
/* A specimen that needs the width of a real content column rather than its own
   intrinsic width — a panel, a banner, anything the page renders full-bleed. */
.lq-showcase__specimen { width: 100%; max-width: 620px; }
/* ---- Heartbeat — connection status indicator + label ----
   Spec page: Lucriq_Heartbeat. Also used by Overview, Stores and
   Store detail. The two rgba() literals below were already literals
   in the inline markup and no token carries them, so they move
   across verbatim. */
.lq-heartbeat { display: inline-flex; align-items: center; gap: 11px; font-family: var(--lq-font-body); }
.lq-heartbeat__indicator { position: relative; display: inline-flex; flex-shrink: 0; width: 12px; height: 12px; }
.lq-heartbeat__ping { position: absolute; inset: 0; border-radius: 50%; background: var(--lq-accent); animation: lq-ping 2.2s ease-out infinite; }
.lq-heartbeat__dot { width: 100%; height: 100%; border-radius: 50%; }
.lq-heartbeat__dot--live { background: var(--lq-accent); box-shadow: 0 0 0 3px var(--lq-accent-soft), 0 0 12px 1px rgba(16,185,129,.55); animation: lq-breathe 2.6s ease-in-out infinite; }
.lq-heartbeat__dot--stale { background: var(--lq-warn); box-shadow: 0 0 0 3px var(--lq-warn-soft); }
.lq-heartbeat__dot--offline { background: transparent; border: 2px solid var(--lq-muted); box-shadow: 0 0 0 3px rgba(100,116,139,.10); }
.lq-heartbeat__text { line-height: 1.3; }
.lq-heartbeat__label { display: block; font-size: 12.5px; font-weight: 700; }
.lq-heartbeat__label--live { color: var(--lq-accent-text); }
.lq-heartbeat__label--stale { color: var(--lq-warn-text); }
.lq-heartbeat__label--offline { color: var(--lq-muted); }
.lq-heartbeat__meta { display: block; font-size: 11px; color: var(--lq-muted); font-family: var(--lq-font-mono); margin-top: 1px; }
/* ---- Metric card — icon, label, figure, optional note ----
   The summary tile the Overview grid is built from. The 38px disc's 10px
   radius has no token. */
.lq-metric-card {
  background: var(--lq-surface);
  border: 1px solid var(--lq-border);
  border-radius: var(--lq-r);
  padding: 16px 17px;
  box-shadow: var(--lq-shadow-sm);
  display: flex;
  gap: 13px;
  align-items: flex-start;
}
.lq-metric-card__icon { width: 38px; height: 38px; border-radius: 10px; flex-shrink: 0; display: grid; place-items: center; }
.lq-metric-card__icon--accent { background: var(--lq-accent-soft); color: var(--lq-accent); }
.lq-metric-card__icon--warn   { background: var(--lq-warn-soft); color: var(--lq-warn); }
.lq-metric-card__icon--plum   { background: var(--lq-plum-soft); color: var(--lq-plum); }
.lq-metric-card__icon--info   { background: var(--lq-info-soft); color: var(--lq-info); }

/* Stroked glyphs. The filled glyph below is deliberately NOT built on this
   base: the base sets stroke:currentColor, and the filled icon's markup has
   no stroke at all, so inheriting one would paint an outline it never had. */
.lq-metric-card__glyph { fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; }
.lq-metric-card__glyph--large { width: 19px; height: 19px; }
.lq-metric-card__glyph--small { width: 18px; height: 18px; }
.lq-metric-card__glyph--stroke-light { stroke-width: 1.9; }
.lq-metric-card__glyph--stroke-regular { stroke-width: 2; }
.lq-metric-card__glyph-filled { width: 18px; height: 18px; fill: currentColor; }

.lq-metric-card__label { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--lq-muted); }
.lq-metric-card__value { color: var(--lq-text); }
.lq-metric-card__value--figure { font-family: var(--lq-font-mono); font-size: 26px; font-weight: 600; line-height: 1.1; margin-top: 3px; }
.lq-metric-card__value--ratio  { font-family: var(--lq-font-mono); font-size: 20px; font-weight: 600; line-height: 1.15; margin-top: 4px; }
.lq-metric-card__value--name   { font-family: var(--lq-font-head); font-size: 21px; font-weight: 800; line-height: 1.2; margin-top: 3px; }
.lq-metric-card__unit { font-size: 12.5px; color: var(--lq-muted); }
.lq-metric-card__note { font-size: 11px; color: var(--lq-text-2); margin-top: 1px; }
.lq-metric-card__note--status { font-weight: 600; margin-top: 2px; }
.lq-metric-card__note--accent { color: var(--lq-accent-text); }
.lq-metric-card__note--warn   { color: var(--lq-warn-text); }
/* ---- Panel — titled surface with an optional header action ----
   The card the app pages group content in. */
.lq-panel { background: var(--lq-surface); border: 1px solid var(--lq-border); border-radius: var(--lq-r); box-shadow: var(--lq-shadow-sm); }
.lq-panel--clipped { margin-bottom: 16px; overflow: hidden; }
/* Base holds only what every panel head in the source declares. Store detail's
   heads are icon+title rows with no justify-content at all, so spreading is a
   modifier: a base that spread them would push their title to the far edge. */
.lq-panel__head { padding: 14px 18px; border-bottom: 1px solid var(--lq-border); display: flex; align-items: center; }
.lq-panel__head--between { justify-content: space-between; }
.lq-panel__head--gapped { gap: 10px; }
.lq-panel__head--tight { gap: 9px; }
.lq-panel__title { font-size: 14px; font-weight: 700; color: var(--lq-text); }
.lq-panel__meta { font-size: 11px; color: var(--lq-muted); font-family: var(--lq-font-mono); }
.lq-panel__link { font-size: 12px; font-weight: 600; color: var(--lq-accent-text); text-decoration: none; }
.lq-panel__link--with-icon { display: inline-flex; align-items: center; gap: 6px; }
.lq-panel__link-glyph { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.lq-panel__body { padding: 16px 18px; }
.lq-panel__body--stack { padding: 18px; display: flex; flex-direction: column; gap: 18px; }
.lq-panel--spaced { margin-bottom: 16px; }
.lq-panel__head--wrap { flex-wrap: wrap; }
.lq-panel__head-group { display: flex; align-items: center; gap: 9px; }
.lq-panel__body--stack-16 { padding: 18px; display: flex; flex-direction: column; gap: 16px; }
/* Head with no trailing action. Worn WITH .lq-panel__head rather than instead
   of it, so the padding and rule are declared once. Undoes the two properties
   the base adds that this head never declared. align-items has no effect on a
   block container, so it costs no pixels either way — it is reset so the
   COMPUTED style matches the source exactly and a computed-style diff stays
   silent, rather than reporting two inert differences forever. */
.lq-panel__head--block { display: block; align-items: normal; }
.lq-panel__body--roomy { padding: 20px 18px; }
.lq-panel__head--wrap-between { justify-content: space-between; gap: 12px; flex-wrap: wrap; }
/* ---- Store row — a store as a clickable list row ----
   Base holds ONLY what every .lq-store-row in the source declares. Overview
   lays its rows out as wrapping flex and Stores as a five-column grid, so
   display, gap and padding stay with the page. The :hover rule predates this
   and lives in layout/_stores.css, which imports later and still wins. */
.lq-store-row { align-items: center; border-top: 1px solid var(--lq-border); text-decoration: none; color: inherit; }
.lq-store-row__thumb {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  flex-shrink: 0;
  background: var(--lq-surface-2);
  border: 1px solid var(--lq-border);
  display: grid;
  place-items: center;
  color: var(--lq-text-2);
}
.lq-store-row__thumb-glyph { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.lq-store-row__identity { min-width: 0; flex: 1 1 160px; }
.lq-store-row__name { font-size: 14px; font-weight: 700; color: var(--lq-text); }
.lq-store-row__domain { font-family: var(--lq-font-mono); font-size: 11px; color: var(--lq-muted); margin-top: 1px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lq-store-row__coverage { flex: 1 1 150px; min-width: 140px; }
.lq-store-row__coverage-head { display: flex; justify-content: space-between; font-size: 10.5px; color: var(--lq-muted); margin-bottom: 4px; }
.lq-store-row__coverage-value { font-family: var(--lq-font-mono); color: var(--lq-text-2); }
.lq-store-row__status { flex: 0 0 auto; }
.lq-store-row__chevron { width: 18px; height: 18px; flex-shrink: 0; fill: none; stroke: var(--lq-muted); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* ---- Progress bar — thin inline coverage track ----
   Shared by the Overview and Stores store rows. The fill's width is the datum
   it reports and stays an inline declaration. */
.lq-progress { height: 5px; border-radius: 99px; background: var(--lq-surface-2); overflow: hidden; }
.lq-progress__fill { height: 100%; }
.lq-progress__fill--accent { background: var(--lq-accent); }
.lq-progress__fill--warn { background: var(--lq-warn); }
/* ---- Notices — inline explanatory blocks ----
   Three shapes: a toned banner, a quiet footnote with a leading icon, and a
   dashed standalone note. The 10px radii have no token. */
.lq-notice { display: flex; gap: 10px; align-items: flex-start; border-radius: 10px; padding: 11px 14px; margin-bottom: 16px; }
.lq-notice--plum { background: var(--lq-plum-soft); border: 1px solid var(--lq-plum-border); }
.lq-notice--info { background: var(--lq-info-soft); border: 1px solid var(--lq-info-border); color: var(--lq-info-text); }
.lq-notice__glyph { width: 16px; height: 16px; flex-shrink: 0; margin-top: 1px; fill: none; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.lq-notice__glyph--plum { stroke: var(--lq-plum); }
.lq-notice__glyph--current { stroke: currentColor; }
.lq-notice__text { font-size: 12px; color: var(--lq-text-2); line-height: 1.5; }
.lq-notice__strong { color: var(--lq-text); }

.lq-footnote { display: flex; gap: 8px; align-items: flex-start; border-top: 1px solid var(--lq-border); padding-top: 13px; }
.lq-footnote__glyph { width: 14px; height: 14px; flex-shrink: 0; margin-top: 1px; fill: none; stroke: var(--lq-muted); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.lq-footnote__text { font-size: 11.5px; color: var(--lq-muted); line-height: 1.5; }

.lq-dashed-note { display: flex; gap: 9px; align-items: center; justify-content: center; padding: 13px; border: 1px dashed var(--lq-border-2); border-radius: 10px; color: var(--lq-muted); }
.lq-dashed-note__glyph { width: 15px; height: 15px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.lq-dashed-note__text { font-size: 12px; line-height: 1.5; }
/* ---- Checklist — ticked allowance rows ---- */
.lq-checklist { display: flex; flex-direction: column; gap: 11px; }
.lq-checklist__item { display: flex; align-items: center; gap: 10px; font-size: 12.5px; color: var(--lq-text-2); }
.lq-checklist__tick { width: 15px; height: 15px; flex-shrink: 0; fill: none; stroke: var(--lq-accent); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.lq-checklist__item--tight { gap: 9px; }
/* ---- Chip — monospaced token in a list (variation names) ----
   The 6px radius has no token. */
.lq-chip { font-family: var(--lq-font-mono); font-size: 10.5px; color: var(--lq-text-2); background: var(--lq-surface); border: 1px solid var(--lq-border); border-radius: 6px; padding: 3px 7px; white-space: nowrap; }
.lq-chip-set { display: flex; flex-wrap: wrap; gap: 6px; }
/* ---- Thumb — square product/entity avatar ----
   Two sizes appear in the source, both at an 8px radius (var(--lq-r-sm)):
   --small is 32px, on the in-page slot and product rows, --medium 34px, in
   the dialogs' picker rows. Split rather than merged: the values are set
   independently in the markup and are not a scale. Pass 2 named them for
   their role; they were --32 / --34. */
.lq-thumb { border-radius: var(--lq-r-sm); flex-shrink: 0; background: var(--lq-surface-2); border: 1px solid var(--lq-border); display: grid; place-items: center; color: var(--lq-muted); }
.lq-thumb--small { width: 32px; height: 32px; }
.lq-thumb--medium { width: 34px; height: 34px; }
.lq-thumb--dashed { border-style: dashed; border-color: var(--lq-border-2); }
.lq-thumb__glyph { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
/* ---- Entity — name over a monospaced identifier ----
   The two-line block beside a thumb, in slot rows and dialog pickers. */
.lq-entity { min-width: 0; }
.lq-entity--grow { flex: 1; }
.lq-entity__name { font-size: 13px; font-weight: 600; color: var(--lq-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lq-entity__name--muted { color: var(--lq-text-2); }
.lq-entity__meta { font-family: var(--lq-font-mono); font-size: 11px; color: var(--lq-muted); }
.lq-entity__meta--spaced { font-size: 11.5px; margin-top: 2px; }
/* ---- Selection dot — the unselected picker affordance ---- */
.lq-radio { width: 18px; height: 18px; border-radius: 50%; border: 2px solid var(--lq-border-2); flex-shrink: 0; }
/* ---- Tier badge — plan tier label with a rotated square mark ----
   Spec page: Lucriq_Tier_Badge. The inline markup's 8px radius is
   var(--lq-r-sm), which resolves the same in both themes. The mark's
   2px radius and the Agency gradient's two rgba() stops have no token,
   so they move across verbatim. */
.lq-tier-badge {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 12px;
  border-radius: var(--lq-r-sm);
  line-height: 1;
  white-space: nowrap;
  font-family: var(--lq-font-head);
  font-weight: 700;
  font-size: 12.5px;
  letter-spacing: .01em;
}
.lq-tier-badge--free    { background: var(--lq-surface-2); border: 1px solid var(--lq-border-2); color: var(--lq-text-2); }
.lq-tier-badge--starter { background: var(--lq-info-soft); border: 1px solid var(--lq-info-border); color: var(--lq-info-text); }
.lq-tier-badge--growth  { background: var(--lq-accent-soft); border: 1px solid var(--lq-accent-border); color: var(--lq-accent-text); }
.lq-tier-badge--scale   { background: var(--lq-plum-soft); border: 1px solid var(--lq-plum-border); color: var(--lq-plum); }
.lq-tier-badge--agency  { background: linear-gradient(135deg,rgba(245,158,11,.18),rgba(217,119,6,.14)); border: 1px solid var(--lq-warn-border); color: var(--lq-warn-text); }

.lq-tier-badge__mark { width: 8px; height: 8px; border-radius: 2px; transform: rotate(45deg); flex-shrink: 0; }
.lq-tier-badge__mark--free    { background: var(--lq-muted); }
.lq-tier-badge__mark--starter { background: var(--lq-info); }
.lq-tier-badge__mark--growth  { background: var(--lq-accent); }
.lq-tier-badge__mark--scale   { background: var(--lq-plum); }
.lq-tier-badge__mark--agency  { background: var(--lq-warn); }
/* ---- Pill — small status label, optional leading dot ----
   Spec page: Lucriq_Pill. Tone modifiers carry the background /
   border / colour triplet; the dot takes its own modifier so a page
   can pair a dot colour with any tone without a specificity fight. */
.lq-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 99px;
  line-height: 1;
  white-space: nowrap;
  font-family: var(--lq-font-body);
  font-size: 11.5px;
  font-weight: 700;
}
.lq-pill--accent  { background: var(--lq-accent-soft); border: 1px solid var(--lq-accent-border); color: var(--lq-accent-text); }
.lq-pill--warn    { background: var(--lq-warn-soft); border: 1px solid var(--lq-warn-border); color: var(--lq-warn-text); }
.lq-pill--danger  { background: var(--lq-danger-soft); border: 1px solid var(--lq-danger-border); color: var(--lq-danger-text); }
.lq-pill--info    { background: var(--lq-info-soft); border: 1px solid var(--lq-info-border); color: var(--lq-info-text); }
.lq-pill--plum    { background: var(--lq-plum-soft); border: 1px solid var(--lq-plum-border); color: var(--lq-plum); }
.lq-pill--neutral { background: var(--lq-surface-2); border: 1px solid var(--lq-border-2); color: var(--lq-text-2); }

.lq-pill__dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.lq-pill__dot--accent { background: var(--lq-accent); }
.lq-pill__dot--warn   { background: var(--lq-warn); }
.lq-pill__dot--danger { background: var(--lq-danger); }
.lq-pill__dot--info   { background: var(--lq-info); }
.lq-pill__dot--plum   { background: var(--lq-plum); }
/* Reason tag inside a dense list row. */
.lq-pill--tiny { padding: 3px 9px; font-size: 10.5px; }
/* Tone-only tiny pill: no dot, no gap declared in the markup. */
.lq-pill--bare { gap: normal; font-family: inherit; }
.lq-pill--muted { background: var(--lq-surface-2); border: 1px solid var(--lq-border-2); color: var(--lq-muted); }
/* ---- Toast — transient confirmation / notice ----
   Spec page: Lucriq_Toast. Tone lives on the icon disc only; the
   surface is the same for every tone. The 11px radius was a literal
   in the inline markup and no token carries it. */
.lq-toast {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  max-width: min(90vw, 460px);
  padding: 12px 16px 12px 13px;
  border-radius: 11px;
  background: var(--lq-surface-3);
  border: 1px solid var(--lq-border-2);
  box-shadow: var(--lq-shadow-lg);
  font-family: var(--lq-font-body);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--lq-text);
  line-height: 1.4;
}
.lq-toast__icon {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;
}
.lq-toast__icon--accent { background: var(--lq-accent-soft); color: var(--lq-accent-text); }
.lq-toast__icon--info   { background: var(--lq-info-soft); color: var(--lq-info-text); }
.lq-toast__icon--warn   { background: var(--lq-warn-soft); color: var(--lq-warn-text); }
.lq-toast__glyph { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }

/* Fixed dock the live pages render the toast in. NO display declaration:
   support.js writes display:flex / none inline to show and hide it, and the
   markup's initial display:none is runtime-owned and stays inline. */
.lq-toast-dock { position: fixed; left: 0; right: 0; z-index: 200; justify-content: center; padding: 0 16px; pointer-events: none; }
/* The toast inside the dock re-enables pointer events and animates in. */
.lq-toast--live { pointer-events: auto; animation: lq-pop .22s ease; }
/* ---- Usage meter — labelled quota bar ----
   Spec page: Lucriq_Usage_Meter. The fill's width stays an inline
   declaration: it is the datum the meter reports, the one case the
   inline-style rule allows. The unlimited track restates
   overflow:visible so that base + modifier reproduces the original
   element exactly rather than inheriting the clipped track. The track's
   overflow:hidden and the modifier's overflow:visible cancel by
   design — deleting either one changes rendering. */
.lq-usage-meter { font-family: var(--lq-font-body); }
.lq-usage-meter__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 9px;
}
.lq-usage-meter__label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--lq-text);
  min-width: 0;
}
.lq-usage-meter__label-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lq-usage-meter__flag {
  flex-shrink: 0;
  font-family: var(--lq-font-body);
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  padding: 2px 6px;
  border-radius: 5px;
}
.lq-usage-meter__flag--danger { color: var(--lq-danger-text); background: var(--lq-danger-soft); border: 1px solid var(--lq-danger-border); }
.lq-usage-meter__value {
  font-family: var(--lq-font-mono);
  font-size: 15px;
  font-weight: 600;
  color: var(--lq-text);
  white-space: nowrap;
  flex-shrink: 0;
}
.lq-usage-meter__value--danger { color: var(--lq-danger-text); }
.lq-usage-meter__limit { color: var(--lq-muted); font-size: 12.5px; font-weight: 500; }
.lq-usage-meter__limit--unlimited { color: var(--lq-accent-text); font-size: 12.5px; font-weight: 600; font-family: var(--lq-font-body); }
/* overflow:hidden clips the fill; --unlimited cancels it (see head comment). */
.lq-usage-meter__track { height: 8px; border-radius: 99px; background: var(--lq-surface-2); overflow: hidden; }
.lq-usage-meter__track--unlimited {
  background: linear-gradient(90deg, var(--lq-accent-soft), transparent);
  border: 1px dashed var(--lq-accent-border);
  opacity: .6;
  overflow: visible;            /* cancels the base track's clip — deliberate, not redundant */
}
.lq-usage-meter__fill { height: 100%; border-radius: 99px; transition: width .5s ease; }
.lq-usage-meter__fill--accent { background: linear-gradient(90deg, var(--lq-accent-2), var(--lq-accent-3)); }
.lq-usage-meter__fill--warn   { background: var(--lq-warn); }
.lq-usage-meter__fill--danger { background: var(--lq-danger); }
/* ---- Brand — Lucriq wordmark lockup ----
   Spec page: Lucriq_Brand. The mark inherits its ink from the host
   (the emerald counter-shape is fill="#10b981" inside the SVG, which
   is artwork, not styling). Host height stays with the consumer:
   the shell, the auth layout and the spec page each set their own. */
.lq-brand { color: var(--lq-text); }
.lq-brand__mark { height: 100%; width: auto; display: block; }
/* App-chrome lockup: 30px tall, vertically centred. Used by the app
   shell's sidebar/header and by the auth layout's centred brand. */
.lq-brand--chrome { display: inline-flex; align-items: center; height: 30px; }
/* ---- Inline icon paint setup ----
   The stroke geometry every 2px-weight line icon in the source shares.
   Size stays with the consumer: the same glyph is drawn at 13, 14, 15,
   17 and 20px in different places, and a naming scheme for those would
   invent a size scale the token set does not have. Icons at other
   stroke weights (the password reveal at 1.9, the toast tick at 2.4)
   keep their own component rule. */
.lq-icon { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* ---- Form field — label, control wrapper, password reveal ----
   First used by Lucriq_Login; the other auth pages, the account pages
   and the dialogs use the same field skeleton. */
.lq-field { display: block; margin-bottom: 15px; }
.lq-field--last { margin-bottom: 20px; }
.lq-field--tight { margin-bottom: 14px; }
.lq-field--medium { margin-bottom: 13px; }
.lq-field--flush { margin-bottom: 0; }
.lq-field__label { font-size: 12px; font-weight: 600; color: var(--lq-text-2); }
.lq-field__label--stacked { display: block; margin-bottom: 7px; }
.lq-field__label-row { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 7px; }
.lq-field__label-row--center { align-items: center; }
/* Label followed by a small adornment rather than a right-aligned note. */
.lq-field__label-group { display: flex; align-items: center; gap: 7px; margin-bottom: 7px; }
.lq-field__note { font-size: 11px; font-weight: 600; color: var(--lq-muted); }
.lq-field__help { display: block; font-size: 11px; color: var(--lq-muted); margin-top: 7px; line-height: 1.5; }
/* Positioning context for an input affix (the reveal button). */
.lq-field__control { position: relative; display: block; }
.lq-password-toggle {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: none;
  background: transparent;
  cursor: pointer;
  color: var(--lq-muted);
}
.lq-password-toggle__glyph { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
/* ---- Accent text link ----
   Deliberately NOT applied to .lq-button--link. That class carries its own colour
   rule in components/_buttons.css, wrapped in :where() so page rules still
   outrank it; a second, ordinary-specificity source of the same colour here
   would defeat the wrapper. */
.lq-link { color: var(--lq-accent-text); font-weight: 600; text-decoration: none; }
.lq-link--small { font-size: 11.5px; }
/* ---- Labelled divider — hairline, caption, hairline ---- */
.lq-divider { display: flex; align-items: center; gap: 12px; margin: 22px 0 16px; }
.lq-divider__line { flex: 1; height: 1px; background: var(--lq-border); }
.lq-divider__label { font-size: 11px; color: var(--lq-muted); }
/* ---- Search field — leading icon inside the input ----
   Used by the dialog toolbars (Modal, Connect store, Unlock slots) and the
   Stores toolbar. The icon is absolutely positioned and click-through. */
.lq-search { position: relative; display: flex; align-items: center; }
.lq-search__icon {
  position: absolute;
  left: 11px;
  width: 15px;
  height: 15px;
  fill: none;
  stroke: var(--lq-muted);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  pointer-events: none;
}
.lq-search__icon--accent { stroke: var(--lq-accent); }
/* ---- Product row — thumbnail, name/SKU, optional reason tag ----
   The list row shared by the dialog product pickers. */
.lq-product-row { display: flex; align-items: center; gap: 12px; padding: 11px 20px; border-bottom: 1px solid var(--lq-border); }
.lq-product-row__thumb {
  width: 34px;
  height: 34px;
  border-radius: var(--lq-r-sm);
  flex-shrink: 0;
  background: var(--lq-surface-2);
  border: 1px solid var(--lq-border);
  display: grid;
  place-items: center;
  color: var(--lq-muted);
}
.lq-product-row__thumb-glyph { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.lq-product-row__text { min-width: 0; flex: 1; }
.lq-product-row__name { font-size: 13px; font-weight: 600; color: var(--lq-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lq-product-row__sku { font-family: var(--lq-font-mono); font-size: 11px; color: var(--lq-muted); }
/* ---- Plan row — a plan tier badge beside its price ----
   Overview and Store detail both wear it, so pass 2 moved it out of the
   Overview page partial and gave it this neutral name: a page partial is the
   wrong owner for a shared class. Four declarations, unchanged, and the only
   class on either element, so no tie depends on where this partial imports.
   The old name is in the pass-2 rename table in CHANGELOG-saas-static.md. */
.lq-plan-row { display: flex; align-items: center; gap: 10px; margin-bottom: 15px; }
/* ---- Load-error region ----
   The failed-read state of an async region, and the one control that leaves it:
   "Try again" re-issues the read. It replaces the region’s content, so it is a
   state and never sits beside the data it stands for. Composed on vocabulary the
   tree already has — the toned-banner geometry of .lq-notice in the danger tone,
   and .lq-button--secondary --secondary-medium for the action — so only the
   region’s own layout is declared here. Text stays on the neutral tokens, as
   .lq-notice--plum does: the tone is carried by the ground, the border and the
   glyph. Like .lq-notice, the 10px radius has no token.
   Desktop-first, at the tree’s 899px break: at mobile widths the action wraps to
   its own full-width line and the icon and message keep the first. */
.lq-load-error {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border-radius: 10px;
  border: 1px solid var(--lq-danger-border);
  background: var(--lq-danger-soft);
}
.lq-load-error__glyph { width: 18px; height: 18px; flex-shrink: 0; fill: none; stroke: var(--lq-danger); stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.lq-load-error__body { flex: 1; min-width: 0; }
.lq-load-error__title { font-size: 13px; font-weight: 700; color: var(--lq-text); }
.lq-load-error__text { font-size: 12px; color: var(--lq-text-2); line-height: 1.5; margin-top: 2px; }
.lq-load-error__button { flex-shrink: 0; }

@media (max-width: 899px) {
  .lq-load-error { flex-wrap: wrap; align-items: flex-start; }
  .lq-load-error__button { width: 100%; }
}
/* ---- Alert — the persistent error a request left behind ----
   The outcome of something the merchant just did, on authenticated surfaces:
   the account panels, the sessions panel, and the connect / recover / unlock
   dialogs. It carries role="alert" and it does NOT expire — a validation
   failure the merchant has to act on cannot be allowed to time out, which is
   why the toast could only ever take the success half (LUCRIQ-DECISIONS.md,
   Hakob, 18 August). This is the authenticated sibling of .lq-auth__notice,
   which stays auth-scoped: same geometry, so the two read as one family across
   the sign-in boundary, but a block of its own rather than a widened auth class.

   Deliberately tone-less: there is no --success modifier and there should not
   be, because a success modifier here would be a second presentation of a
   message the toast already owns. The danger tone is baked in for the same
   reason .lq-load-error bakes it in — one tone, no modifier to choose.
   The 10px radius has no token, as every other notice in the tree.

   Contrast, measured on all four grounds an authenticated surface can put
   behind it: --lq-danger-text on --lq-danger-soft reads 5.04-5.61:1 light and
   6.72-8.42:1 dark, so it clears AA at 12px everywhere, tightest over
   --lq-bg light. */
.lq-alert { display: flex; gap: 9px; align-items: flex-start; padding: 11px 13px; border-radius: 10px; border: 1px solid var(--lq-danger-border); background: var(--lq-danger-soft); }
.lq-alert__glyph { width: 15px; height: 15px; flex-shrink: 0; margin-top: 1px; fill: none; stroke: var(--lq-danger); stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.lq-alert__text { font-size: 12px; line-height: 1.5; color: var(--lq-danger-text); }
/* Spacing is a modifier, not a base declaration: this block sits above a form
   on one surface and inside a dialog body on another, and .lq-notice's baked-in
   16px would be wrong in the second. Same shape as .lq-panel--spaced. */
.lq-alert--spaced { margin-bottom: 16px; }
/* ---- Quiet text primitives ----
   Two of them, and the file is closed at two: this is not a place to put text
   rules that could not find a home. Both were rendered by the application from
   unnamed inline blocks and are named here so the mirror can carry them.

   .lq-status-text is the paragraph an async region shows while it is working
   or when it has nothing to report — the loading states and Plan & Usage's
   quiet lines. Muted at 12px, one step down from .lq-notice__text, which is
   the same size on --lq-text-2: this one is not addressed to the reader so
   much as it is the region admitting it has nothing yet.

   .lq-fallback-text is the stand-in for a value that does not exist — a
   "Never", a "Not set", an em dash. One declaration, deliberately: it dresses
   a span inside a line that already has its size and weight, and giving it a
   size would make it a different sentence from the one it sits in.

   Both hold --lq-muted, which clears AA on every ground in the tree: 6.74-7.58:1
   light and 5.68-7.06:1 dark. */
.lq-status-text { font-size: 12px; color: var(--lq-muted); line-height: 1.5; }
.lq-fallback-text { color: var(--lq-muted); }

/* Shell <main> padding (was computed per-breakpoint in the shell logic) */
.lq-shell-main { padding: 26px 34px 64px; }
@media (max-width: 899px) { .lq-shell-main { padding: 16px 15px calc(86px + env(safe-area-inset-bottom, 0px)); } }

/* Active nav / bottom-tab, driven by [data-lq-active] on the shell root (was the `active` prop) */
[data-lq-active="overview"] [data-lq-nav="overview"],
[data-lq-active="stores"]   [data-lq-nav="stores"],
[data-lq-active="plan"]     [data-lq-nav="plan"],
[data-lq-active="account"]  [data-lq-nav="account"] {
  color: var(--lq-accent-text) !important;
  background: var(--lq-accent-soft) !important;
  box-shadow: inset 3px 0 0 var(--lq-accent) !important;
}
[data-lq-active="overview"] [data-lq-nav="overview"] svg,
[data-lq-active="stores"]   [data-lq-nav="stores"] svg,
[data-lq-active="plan"]     [data-lq-nav="plan"] svg,
[data-lq-active="account"]  [data-lq-nav="account"] svg { stroke: var(--lq-accent) !important; }

[data-lq-active="overview"] [data-lq-tab="overview"],
[data-lq-active="stores"]   [data-lq-tab="stores"],
[data-lq-active="plan"]     [data-lq-tab="plan"],
[data-lq-active="account"]  [data-lq-tab="account"] { color: var(--lq-accent-text) !important; }
[data-lq-active="overview"] [data-lq-tab="overview"] svg,
[data-lq-active="stores"]   [data-lq-tab="stores"] svg,
[data-lq-active="plan"]     [data-lq-tab="plan"] svg,
[data-lq-active="account"]  [data-lq-tab="account"] svg { stroke: var(--lq-accent) !important; }

/* Hover / focus (were style-hover / style-focus on the DC elements) */
[data-lq-nav]:hover   { background: var(--lq-surface-3); color: var(--lq-text); }
.lq-signout:hover     { background: var(--lq-surface-3); color: var(--lq-text); }
.lq-signout--mobile:hover   { color: var(--lq-text); }

/* ============================================================
   Shell chrome, extracted from Lucriq_App_Shell's inline styles.
   Every rule below is a NEW rule; the pre-existing blocks above are
   untouched, including their six !important declarations.

   Cascade, checked per rule against those blocks:
   - [data-lq-active] [data-lq-nav] sets color, background and
     box-shadow with !important, so .lq-shell__nav-link's color and
     background are dead on the active item. Expected, not a defect.
   - the same for the nav/tab svg stroke rules over
     .lq-shell__nav-icon and .lq-shell__tab-icon.
   - [data-lq-nav]:hover, .lq-signout:hover and .lq-signout--mobile:hover
     are 0,2,0 and now win over these 0,1,0 base rules. The inline
     styles used to defeat them. See the note in the batch report.
   The 9px radii have no token; the tab bar's rgba shadow has none. */
.lq-shell {
  min-height: 100vh;
  width: 100%;
  background: var(--lq-bg);
  color: var(--lq-text);
  font-family: var(--lq-font-body);
  display: flex;
  flex-direction: column;
}
.lq-shell__frame { flex: 1; display: flex; align-items: stretch; min-height: 0; }

/* ---- Sidebar (desktop) ---- */
.lq-shell__sidebar {
  position: sticky;
  top: 0;
  align-self: flex-start;
  width: 246px;
  flex-shrink: 0;
  height: 100vh;
  background: var(--lq-bg-2);
  border-right: 1px solid var(--lq-border);
  display: flex;
  flex-direction: column;
  padding: 20px 15px 16px;
}
.lq-shell__brand { padding: 2px 8px 4px; }
.lq-shell__domain { font-family: var(--lq-font-mono); font-size: 10.5px; color: var(--lq-muted); margin-top: 9px; padding-left: 1px; letter-spacing: .02em; }
.lq-shell__nav { display: flex; flex-direction: column; gap: 4px; margin-top: 22px; }
.lq-shell__nav-link {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px 12px;
  border-radius: 9px;
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: none;
  color: var(--lq-text-2);
  background: transparent;
}
.lq-shell__nav-icon { width: 18px; height: 18px; flex-shrink: 0; fill: none; stroke: var(--lq-muted); stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.lq-shell__sidebar-foot { margin-top: auto; display: flex; flex-direction: column; gap: 14px; }
.lq-shell__theme-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 2px 8px; }
.lq-shell__theme-label { font-size: 11px; font-weight: 600; color: var(--lq-muted); }
.lq-shell__user { border-top: 1px solid var(--lq-border); padding-top: 13px; display: flex; align-items: center; gap: 10px; }
.lq-shell__avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  flex-shrink: 0;
  background: linear-gradient(135deg, var(--lq-accent-2), var(--lq-accent-3));
  color: var(--lq-accent-ink);
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: 13px;
}
.lq-shell__user-text { min-width: 0; line-height: 1.3; flex: 1; }
.lq-shell__user-name { font-size: 12.5px; font-weight: 600; color: var(--lq-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lq-shell__user-email { font-size: 10.5px; color: var(--lq-muted); font-family: var(--lq-font-mono); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Sign out is a <button>, not a link. The account signs out through
   LUCRIQ-API.md §8.0.12 POST /logout — a state-changing, CSRF-protected
   request (LUCRIQ-SAAS-UI.md §Authenticated) — so the control performs an
   action and is not a navigation. The six declarations after text-decoration
   neutralise the UA button appearance so the box and the paint stay the ones
   the anchor had: with border-box the 30×30 box is unchanged, place-items
   still centres the 16px icon, and the (0,2,0) :hover rule above still wins
   over this background. text-decoration is inert on a button and is kept, so
   the rule holds for either tag while the application adopts it. */
.lq-signout { display: grid; place-items: center; width: 30px; height: 30px; flex-shrink: 0; border-radius: var(--lq-r-sm); color: var(--lq-muted); text-decoration: none; appearance: none; border: 0; padding: 0; background: none; font: inherit; cursor: pointer; }
.lq-shell__signout-icon { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

/* ---- Content column ---- */
.lq-shell__content { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.lq-shell__header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in srgb, var(--lq-bg) 82%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--lq-border);
  padding: 16px 34px;
  display: flex;
  align-items: center;
  gap: 18px;
}
.lq-shell__header--mobile {
  background: color-mix(in srgb, var(--lq-bg) 90%, transparent);
  padding: 10px 15px;
  justify-content: space-between;
  gap: 12px;
}
.lq-shell__header-titles { min-width: 0; flex: 1; }
.lq-shell__header-actions { display: flex; align-items: center; gap: 14px; flex-shrink: 0; }
.lq-shell__header-actions--mobile { gap: 9px; }
.lq-shell__title { font-family: var(--lq-font-head); font-weight: 800; font-size: 22px; letter-spacing: -.015em; color: var(--lq-text); margin: 0; line-height: 1.15; }
.lq-shell__title--mobile { font-size: 20px; line-height: 1.2; }
/* Worn alone, without .lq-signout: the mobile header's sign-out is a
   different element from the sidebar's, not the same one restyled. The name
   comes from the rename table (renamed in pass 2, see
   CHANGELOG-saas-static.md §G). Do not "complete" the
   pair by adding .lq-signout to the markup — that injects width, height,
   border-radius and colour this element never had. */
.lq-signout--mobile {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  border-radius: 9px;
  border: 1px solid var(--lq-border);
  background: var(--lq-surface-2);
  color: var(--lq-muted);
  text-decoration: none;
  /* Same button, same reasons as .lq-signout. This block already declares its
     own border and background, so only the UA padding, font and cursor are
     neutralised; the 34×34 border-box and the centred 17px icon are unchanged. */
  appearance: none;
  padding: 0;
  font: inherit;
  cursor: pointer;
}
.lq-shell__signout-icon--mobile { width: 17px; height: 17px; }
.lq-shell__mobile-title { padding: 17px 15px 2px; }
.lq-shell__mobile-title-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.lq-shell__mobile-badge { flex-shrink: 0; margin-top: 1px; }
/* New rule rather than an edit to the pre-existing .lq-shell-main block above. */
.lq-shell-main { flex: 1; min-width: 0; }
.lq-shell__page { max-width: 1120px; margin: 0 auto; animation: lq-fadein .3s ease; }

/* ---- Bottom tab bar (mobile) ----
   display: flex is this rule's own, not the markup's. The extracted markup
   declared align-items: stretch and no display, and align-items is inert on a
   block: the bar became a block and stacked its four .lq-shell__tab children
   four rows deep, 215px tall against .lq-shell-main's 86px of clearance, so
   131px of it covered content that no scroll position could lift clear. The
   tree's own two simulations of this same bar — .lq-modal-tabbar and
   .lq-connect-store-tabbar — both declare display: flex; align-items: stretch,
   which is the geometry align-items was written for. Restoring it costs no
   document height, and .lq-only-mobile's display: none !important still takes
   the bar out above 899px. */
.lq-shell__tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  display: flex;
  align-items: stretch;
  background: color-mix(in srgb, var(--lq-bg-2) 92%, transparent);
  backdrop-filter: blur(14px);
  border-top: 1px solid var(--lq-border);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  box-shadow: 0 -6px 20px rgba(0,0,0,.18);
}
.lq-shell__tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  flex: 1;
  padding: 9px 2px 8px;
  text-decoration: none;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--lq-muted);
}
.lq-shell__tab-icon { width: 21px; height: 21px; fill: none; stroke: var(--lq-muted); stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.lq-shell__subtitle { font-size: 12.5px; color: var(--lq-muted); margin-top: 3px; }
.lq-shell__subtitle--mobile { margin-top: 4px; }
/* ---- Auth layout — shared unauthenticated chrome ----
   Spec page: Lucriq_Auth_Layout; also renders Login, Signup, Forgot
   password, Reset password and Verify email. The theme toggle and the
   wordmark inside it come from their own component partials. */
.lq-auth {
  position: relative;
  min-height: 100vh;
  width: 100%;
  background: var(--lq-bg);
  color: var(--lq-text);
  font-family: var(--lq-font-body);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.lq-auth__glow {
  position: absolute;
  top: -160px;
  left: 50%;
  transform: translateX(-50%);
  width: 760px;
  height: 520px;
  background: radial-gradient(ellipse at center, var(--lq-accent-soft), transparent 62%);
  pointer-events: none;
}
.lq-auth__toolbar { position: relative; display: flex; justify-content: flex-end; padding: 10px 16px 0; }
.lq-auth__main {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 24px 22px 34px;
}
.lq-auth__brand { margin-bottom: 22px; }
.lq-auth__card {
  width: 100%;
  max-width: 430px;
  background: var(--lq-surface);
  border: 1px solid var(--lq-border);
  border-radius: var(--lq-r-lg);
  box-shadow: var(--lq-shadow);
  padding: clamp(20px, 5vw, 30px) clamp(20px, 5vw, 30px) clamp(22px, 5vw, 32px);
  animation: lq-pop .32s ease;
}
.lq-auth__footer { margin-top: 22px; font-size: 11.5px; color: var(--lq-muted); display: flex; align-items: center; gap: 14px; }
.lq-auth__domain { font-family: var(--lq-font-mono); }
.lq-auth__footer-dot { width: 3px; height: 3px; border-radius: 50%; background: var(--lq-border-2); }

/* Card contents shared by all five auth pages. */
.lq-auth__card-head { text-align: center; margin-bottom: 24px; }
.lq-auth__heading { font-family: var(--lq-font-head); font-size: 21px; font-weight: 800; color: var(--lq-text); margin: 0 0 6px; letter-spacing: -.01em; }
.lq-auth__subheading { font-size: 12.5px; color: var(--lq-muted); margin: 0; }
.lq-auth__card-foot { text-align: center; font-size: 12.5px; color: var(--lq-muted); }
.lq-auth__card--wide { max-width: 462px; }
.lq-auth__card-head--compact { margin-bottom: 20px; }
.lq-auth__subheading--relaxed { line-height: 1.5; }
.lq-auth__card-foot--spaced { margin-top: 18px; }

/* Hero icon disc above the heading, and the post-action result panel.
   Both are shared by Forgot password and Reset password.
   The 13px and 14px disc radii have no token. A result panel keeps its
   inline display: support.js clears that inline value to reveal the
   panel, so a class-level display:none could never be undone. */
.lq-auth__card-head--icon { margin-bottom: 22px; }
.lq-auth__icon {
  width: 48px;
  height: 48px;
  border-radius: 13px;
  background: var(--lq-accent-soft);
  border: 1px solid var(--lq-accent-border);
  display: grid;
  place-items: center;
  margin: 0 auto 14px;
  color: var(--lq-accent);
}
.lq-auth__icon--large { width: 54px; height: 54px; border-radius: 14px; margin: 0 auto 16px; }
.lq-auth__icon--danger { background: var(--lq-danger-soft); border-color: var(--lq-danger-border); color: var(--lq-danger); }
/* Glyph: paint setup, the two shared sizes, and the three stroke weights the
   auth discs use. One-off sizes stay in the page partial that needs them. */
.lq-auth__icon-glyph { fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; }
.lq-auth__icon-glyph--medium { width: 23px; height: 23px; }
.lq-auth__icon-glyph--large { width: 27px; height: 27px; }
.lq-auth__icon-glyph--stroke-light { stroke-width: 1.7; }
.lq-auth__icon-glyph--stroke-medium { stroke-width: 1.8; }
.lq-auth__icon-glyph--stroke-regular { stroke-width: 2; }
.lq-auth__result { text-align: center; animation: lq-pop .3s ease; }
.lq-auth__heading--spaced { margin-bottom: 8px; }
.lq-auth__note { font-size: 12.5px; color: var(--lq-muted); margin: 0 auto 22px; line-height: 1.6; }
.lq-auth__note--narrow { max-width: 40ch; }
.lq-auth__subheading--loose { line-height: 1.6; }
.lq-auth__card-foot--tight { margin-top: 16px; }

/* Submit outcome inside an auth card: the failed POST, and the succeeded one that
   stays on the page. The application renders these inline at the top of the form
   and the tree had nowhere for them, so both variants are specified here.
   Exactly one is ever shown, and both ship display:none like .lq-auth__result —
   see the note above on why that display stays inline.
   Unlike .lq-recovery__notice (CHANGELOG §K/§L) the base hard-codes no tone: it
   declares layout and a transparent border, and each variant supplies ground,
   border, glyph and text through descendant selectors, so a new tone needs one
   block rather than four parallel modifier classes. 10px radius, no token, as
   every other notice in the tree. */
.lq-auth__notice {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  padding: 11px 13px;
  margin-bottom: 14px;
  border: 1px solid transparent;
  border-radius: 10px;
  text-align: left;
}
.lq-auth__notice-glyph { width: 15px; height: 15px; flex-shrink: 0; margin-top: 1px; fill: none; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.lq-auth__notice-text { font-size: 12px; line-height: 1.5; }
.lq-auth__notice--error { background: var(--lq-danger-soft); border-color: var(--lq-danger-border); }
.lq-auth__notice--error .lq-auth__notice-glyph { stroke: var(--lq-danger); }
.lq-auth__notice--error .lq-auth__notice-text { color: var(--lq-danger-text); }
.lq-auth__notice--success { background: var(--lq-accent-soft); border-color: var(--lq-accent-border); }
.lq-auth__notice--success .lq-auth__notice-glyph { stroke: var(--lq-accent); }
.lq-auth__notice--success .lq-auth__notice-text { color: var(--lq-accent-text); }
/* Lucriq_Signup — page-only rules.
   The invite badge is the warn pill at this page's size, and the name row
   is one of a family of one-off auto-fit grids (every page's minmax and
   gap differ), so it stays page layout. */
/* line-height and white-space are the adoption costs of re-basing this badge
   onto .lq-pill; both restore what the inline badge inherited and .lq-pill
   overrides. Neither is redundant — do not delete them.
   line-height: the inline badge declared none and inherited normal, while
   .lq-pill imposes 1, which shrank the line box below the 13px icon and lifted
   the whole form by 1px. Measured defect, fixed here.
   white-space: .lq-pill imposes nowrap. Inert today because the badge text fits
   on one line, but latent — a longer string or a narrower card would wrap
   pre-refactor and not after. Pinned rather than accepted so the badge cannot
   silently change behaviour when its copy changes. */
.lq-signup-badge { padding: 5px 11px; font-size: 11px; line-height: normal; white-space: normal; margin-bottom: 13px; }
.lq-signup-badge__icon { width: 13px; height: 13px; }
.lq-signup-name-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr)); gap: 12px; margin-bottom: 14px; }
.lq-signup-token-icon { width: 13px; height: 13px; stroke: var(--lq-accent); }
/* Lucriq_Forgot_Password — page-only rules.
   "Try another email" is a link-styled button that does NOT carry
   .lq-button--link in the markup; Phase 1 neither adds that class nor
   reclassifies the element, so it gets its own page rule. */
.lq-forgot-lead { font-size: 12.5px; color: var(--lq-text-2); margin: 0 0 4px; line-height: 1.6; }
.lq-forgot-email { font-family: var(--lq-font-mono); font-size: 13px; color: var(--lq-text); margin: 0 0 12px; }
.lq-forgot-open-link { margin-bottom: 14px; }
.lq-forgot-retry-line { font-size: 12px; color: var(--lq-muted); }
.lq-forgot-retry {
  border: none;
  background: none;
  color: var(--lq-accent-text);
  font-weight: 600;
  font-size: 12px;
  cursor: pointer;
  font-family: inherit;
  padding: 0;
}
/* Lucriq_Reset_Password — page-only rules.
   The strength meter's fill width and colour are written by support.js as
   the password is typed, so those stay inline declarations; everything
   around them is extracted. */
.lq-reset-strength { display: flex; align-items: center; gap: 8px; margin-bottom: 15px; }
.lq-reset-strength__track { flex: 1; height: 5px; border-radius: 99px; background: var(--lq-surface-2); overflow: hidden; }
.lq-reset-strength__fill { height: 100%; border-radius: 99px; transition: width .25s ease, background .25s ease; }
.lq-reset-strength__label { font-size: 11px; font-weight: 600; min-width: 52px; text-align: right; }
/* Empty-password state, which is what this specification renders. The fill's
   width is the datum it reports and stays inline; its colour is a state and
   is a modifier, matching how the usage meter's tones are handled. */
.lq-reset-strength__fill--empty { background: var(--lq-border-2); }
.lq-reset-strength__label--empty { color: var(--lq-border-2); }
.lq-reset-note { max-width: 38ch; }
/* Lucriq_Verify_Email — page-only rules. The two glyph sizes here (22px in
   the spinner, 26px on the error disc) are used by no other page, so they
   stay here while the shared sizes and stroke weights come from
   layout/_auth-layout.css. */
.lq-verify-loading { text-align: center; padding: 6px 0; }
.lq-verify-spinner { position: relative; width: 56px; height: 56px; margin: 0 auto 18px; }
.lq-verify-spinner__ring { position: absolute; inset: 0; border-radius: 50%; border: 3px solid var(--lq-border); }
.lq-verify-spinner__arc {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 3px solid transparent;
  border-top-color: var(--lq-accent);
  animation: lq-spin .9s linear infinite;
}
.lq-verify-spinner__icon { position: absolute; inset: 0; display: grid; place-items: center; color: var(--lq-accent); }
.lq-verify-spinner__glyph { width: 22px; height: 22px; }
.lq-verify-heading { font-size: 20px; margin-bottom: 7px; }
.lq-verify-error-glyph { width: 26px; height: 26px; }
/* Lucriq_Modal — specification page framing.
   Each frame is a simulated viewport with a decorative page skeleton behind a
   statically-open overlay. Chrome only: the dialog markup inside is the same
   as the live pages'.
   The overlay rules below deliberately do NOT reuse .lq-modal-preview from
   components/_showcase.css: that sets display:flex and animation:none, while
   these frames render the overlay as the live grid overlay with its entry
   animation intact. Both exist because they show different things.
   The dialog max-width rules sit in layout/, after components/_modal.css, so
   they win over the component's mobile media query exactly as the inline
   declarations they replace did.
   The 26px phone-frame radius, the 7px skeleton bar radius and the tab bar's
   rgba shadow have no token. */
.lq-modal-frame {
  position: relative;
  width: 100%;
  max-width: 960px;
  border-radius: var(--lq-r);
  border: 1px solid var(--lq-border);
  background: var(--lq-bg);
  overflow: hidden;
  box-shadow: var(--lq-shadow-sm);
}
.lq-modal-frame--anatomy  { height: 460px; }
.lq-modal-frame--scrolling { height: 520px; }
.lq-modal-frame--blocking { height: 420px; }
.lq-modal-frame--phone {
  width: 390px;
  max-width: 100%;
  height: 720px;
  border-radius: 26px;
  border-color: var(--lq-border-2);
  box-shadow: var(--lq-shadow-lg);
}
.lq-modal-frame__overlay { position: absolute; display: grid; }
.lq-modal-frame__overlay--docked { inset: 0; place-items: end center; padding: 14px 12px 78px; }

/* Decorative page behind the scrim. Block heights are arbitrary placeholder
   geometry, so the modifiers are named for the height they set rather than
   for a meaning they do not have. */
.lq-modal-skeleton {
  position: absolute;
  inset: 0;
  padding: 22px 26px;
  display: flex;
  flex-direction: column;
  gap: 13px;
  opacity: .4;
  pointer-events: none;
}
.lq-modal-skeleton--phone { padding: 16px; gap: 12px; opacity: .45; }
.lq-modal-skeleton__bar { height: 26px; width: 220px; border-radius: var(--lq-r-sm); background: var(--lq-surface); }
.lq-modal-skeleton__bar--phone { height: 24px; width: 150px; border-radius: 7px; }
.lq-modal-skeleton__block { border-radius: var(--lq-r); background: var(--lq-surface); border: 1px solid var(--lq-border); }
.lq-modal-skeleton__block--height-96  { height: 96px; }
.lq-modal-skeleton__block--height-120 { height: 120px; }
.lq-modal-skeleton__block--height-140 { height: 140px; }
.lq-modal-skeleton__block--height-150 { height: 150px; }
.lq-modal-skeleton__block--height-180 { height: 180px; }
.lq-modal-skeleton__block--height-220 { height: 220px; }

.lq-modal-dialog-anatomy   { max-width: 500px; }
.lq-modal-dialog-scrolling { max-width: 520px; max-height: 440px; }
.lq-modal-dialog-blocking  { max-width: 420px; }
.lq-modal-dialog-phone     { max-width: 100%; max-height: calc(100% - 92px); }

.lq-modal-focus-demo {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-items: center;
  padding: 20px 22px;
  border-radius: var(--lq-r);
  border: 1px solid var(--lq-border);
  background: var(--lq-bg-2);
  box-shadow: var(--lq-shadow-sm);
}
.lq-modal-focus-demo__search { min-width: 230px; }

/* Simulated fixed tab bar, shown so the dialog's bottom clearance is visible.
   The real bottom navigation lives in the app shell and has its own geometry. */
.lq-modal-tabbar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 64px;
  z-index: 2;
  display: flex;
  align-items: stretch;
  background: color-mix(in srgb, var(--lq-bg-2) 92%, transparent);
  backdrop-filter: blur(14px);
  border-top: 1px solid var(--lq-border);
  box-shadow: 0 -6px 20px rgba(0,0,0,.18);
}
.lq-modal-tabbar__item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  font-size: 10px;
  font-weight: 600;
  color: var(--lq-muted);
}
.lq-modal-tabbar__item--active { font-weight: 700; color: var(--lq-accent-text); }
.lq-modal-tabbar__icon { width: 20px; height: 20px; fill: none; stroke: var(--lq-muted); stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.lq-modal-tabbar__icon--active { stroke: var(--lq-accent); }
/* Lucriq_Overview — page rules, and page-private again as of pass 2.
   The plan badge row Store detail also wears left this partial in that pass:
   it is now .lq-plan-row in components/_plan-row.css. Everything below is
   Overview only, so the lq-overview-* prefix reaches one page.
   The two auto-fit grids differ in both minmax and gap from every other
   auto-fit grid in the source, so they stay page layout. The store rows'
   wrapping-flex layout is here rather than on .lq-store-row because Stores
   lays the same class out as a grid.
   The 8px empty-state button radius and 16px hero disc radius have no token
   at those positions; the 62px disc's 16px radius is var(--lq-r-lg). */
.lq-overview-metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr)); gap: 15px; margin-bottom: 16px; }
.lq-overview-columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); gap: 16px; align-items: start; }
.lq-overview-store-row { display: flex; flex-wrap: wrap; gap: 12px 16px; padding: 15px 18px; }
.lq-overview-plan-price { font-size: 12px; color: var(--lq-muted); font-family: var(--lq-font-mono); }
.lq-overview-closing-note { margin-top: 18px; }

/* Empty state */
.lq-overview-empty {
  background: var(--lq-surface);
  border: 1px solid var(--lq-border);
  border-radius: var(--lq-r-lg);
  box-shadow: var(--lq-shadow-sm);
  padding: 46px 34px;
  text-align: center;
}
.lq-overview-empty__icon {
  width: 62px;
  height: 62px;
  border-radius: var(--lq-r-lg);
  background: var(--lq-accent-soft);
  border: 1px solid var(--lq-accent-border);
  display: grid;
  place-items: center;
  margin: 0 auto 18px;
  color: var(--lq-accent);
}
.lq-overview-empty__glyph { width: 30px; height: 30px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.lq-overview-empty__title { font-family: var(--lq-font-head); font-weight: 800; font-size: 21px; color: var(--lq-text); margin: 0 0 7px; }
.lq-overview-empty__text { font-size: 13px; color: var(--lq-text-2); max-width: 52ch; margin: 0 auto 26px; line-height: 1.6; }
.lq-overview-steps { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; max-width: 660px; margin: 0 auto 28px; text-align: left; }
.lq-overview-step { flex: 1; min-width: 180px; display: flex; gap: 11px; align-items: flex-start; }
.lq-overview-step__number {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--lq-accent-soft);
  color: var(--lq-accent-text);
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: 12px;
  font-family: var(--lq-font-mono);
}
.lq-overview-step__title { font-size: 12.5px; font-weight: 700; color: var(--lq-text); margin-bottom: 2px; }
.lq-overview-step__text { font-size: 11.5px; color: var(--lq-muted); line-height: 1.5; }
/* Claim button. Deliberately NOT .lq-button--primary-raised: that modifier adds
   a box-shadow lift this button's inline style never declared, so adopting it
   would paint a shadow the specification does not have. The gradient trio this
   rule used to restate is gone — it now takes the base primary's fill, ink and
   transparent border, which the element already wears (CHANGELOG §I). Its 8px
   radius is var(--lq-r-sm) and its weight is 600, both unlike every other
   primary, so they are page-level too. */
.lq-overview-claim {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 11px 18px;
  border-radius: var(--lq-r-sm);
  font-size: 13px;
  font-weight: 600;
}
.lq-overview-claim__glyph { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* ---- Responsive: Stores list → stacked cards on phones ---- */
@media (max-width: 720px) {
  .lq-store-toolbar    { flex-direction: column !important; align-items: stretch !important; }
  .lq-store-actions    { width: 100% !important; }
  .lq-store-search-wrap{ flex: 1 1 auto !important; }
  .lq-store-search     { width: 100% !important; }
  .lq-store-filters    { overflow-x: auto; }
  .lq-store-filters button { flex-shrink: 0; white-space: nowrap; }
  .lq-stores-table__head        { display: none !important; }
  .lq-stores-table__row         { display: flex !important; flex-direction: column !important; align-items: stretch !important; gap: 12px !important; }
  .lq-hide-mobile      { display: none !important; }
}

.lq-store-row:hover   { background: var(--lq-surface-3); }

/* ============================================================
   Stores — extracted from the page's inline styles.
   Appended AFTER the pre-existing @media (max-width: 720px) block above:
   its ten !important declarations still win below 720px, so the rules
   below are simply dead there. That is the same result the inline styles
   produced and is expected, not a defect.

   These are base rules for classes already on the elements rather than new
   class names. Pass 2 renamed the two pre-standard families that ran their
   words together into elements of the table they sit in,
   .lq-stores-table__head* and .lq-stores-table__row* (renamed in pass 2, see
   CHANGELOG-saas-static.md §G). .lq-store-row (components/_store-row.css) is a
   different thing — the store row itself — and keeps its name; the near-miss
   between the two old spellings is what pass 2 removed here.

   Cascade note on .lq-store-search: it is also .lq-input, whose base sets
   width:100%. Both are (0,1,0), so the width below only wins because
   components/_inputs.css imports before layout/_stores.css. Do not reorder
   the imports.
   The 9px thumb radius has no token. ============================================================ */
.lq-store-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; margin-bottom: 16px; }
.lq-store-filters { display: flex; align-items: center; gap: 8px; background: var(--lq-surface); border: 1px solid var(--lq-border); border-radius: 99px; padding: 4px; }
.lq-store-filter { gap: 6px; padding: 7px 13px; font-size: 12px; }
.lq-store-filter__count { opacity: .6; }
.lq-store-actions { display: flex; align-items: center; gap: 10px; }
.lq-store-search-wrap { position: relative; display: inline-flex; align-items: center; }
.lq-store-search { width: 210px; padding: 9px 13px 9px 33px; background: var(--lq-surface-2); font-size: 12.5px; outline: none; }
.lq-store-search__icon { position: absolute; left: 11px; width: 15px; height: 15px; fill: none; stroke: var(--lq-muted); stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; pointer-events: none; }
/* Toolbar call to action. Deliberately not .lq-button--primary-raised: the
   markup carries no button class at all and declares no box-shadow. Its fill
   and ink are now the base primary's (CHANGELOG §I); the border keeps its 1px,
   transparent, so the box does not move. */
.lq-stores-connect {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 9px 14px;
  border-radius: 9px;
  border: 1px solid transparent;
  background: var(--lq-accent);
  color: var(--lq-accent-ink);
  font-family: var(--lq-font-body);
  font-weight: 600;
  font-size: 12.5px;
  cursor: pointer;
  white-space: nowrap;
}
.lq-stores-connect__glyph { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

.lq-stores-table { background: var(--lq-surface); border: 1px solid var(--lq-border); border-radius: var(--lq-r); box-shadow: var(--lq-shadow-sm); overflow: hidden; }
.lq-stores-table__head { display: grid; grid-template-columns: 2.2fr 1.6fr 1fr 1.3fr 28px; gap: 16px; padding: 11px 20px; border-bottom: 1px solid var(--lq-border); background: var(--lq-surface-2); }
/* Same five declarations as .lq-metric-card__label. Kept separate: a column
   header is not a metric label, and coupling them would tie two components. */
.lq-stores-table__head-cell { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--lq-muted); }
/* Row layout. .lq-store-row (components) supplies align-items, border-top,
   text-decoration and colour; this supplies the grid the page needs. */
.lq-stores-table__row { display: grid; grid-template-columns: 2.2fr 1.6fr 1fr 1.3fr 28px; gap: 16px; padding: 15px 20px; }
.lq-stores-table__row-store { display: flex; align-items: center; gap: 13px; min-width: 0; }
.lq-stores-table__row-thumb { width: 38px; height: 38px; border-radius: 9px; flex-shrink: 0; background: var(--lq-surface-2); border: 1px solid var(--lq-border); display: grid; place-items: center; color: var(--lq-text-2); }
.lq-stores-table__row-thumb-glyph { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.lq-stores-table__row-identity { min-width: 0; }
.lq-stores-table__row-name { font-size: 13.5px; font-weight: 700; color: var(--lq-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lq-stores-table__row-domain { font-family: var(--lq-font-mono); font-size: 11px; color: var(--lq-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lq-stores-table__row-chevron { width: 18px; height: 18px; fill: none; stroke: var(--lq-muted); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* Empty result row. No display declaration: support.js reveals it by clearing
   the inline value, so the markup's display:none stays inline. */
.lq-stores-empty { padding: 40px 20px; text-align: center; color: var(--lq-muted); font-size: 12.5px; }
.lq-stores-note { margin-top: 14px; display: flex; gap: 8px; align-items: center; justify-content: center; color: var(--lq-muted); font-size: 11.5px; line-height: 1.5; }
.lq-stores-note__glyph { width: 14px; height: 14px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
/* ============================================================
   Store detail — page rules.
   Wears .lq-plan-row from components/_plan-row.css for the plan badge row:
   four declarations, exact match. Pass 2 moved that class out of
   layout/_overview.css, so this page no longer depends on another page's
   prefix.
   The back link keeps its pre-existing .lq-button--link class but gets NO base
   rule on it: .lq-button--link is also on Account security's inline danger button,
   and the two inline styles intersect in exactly ONE declaration
   (font-weight: 600 — color and font-size are in both but at different
   values). A one-declaration base is not worth the coupling, so the back
   link's appearance is a page rule. Pass 2 renamed the class to
   .lq-button--link and left that split as it is.
   The credential recovery dialog's head, foot and close button had no
   stylesheet rules before pass 1 — every declaration was inline — so they
   are the sharpest test of the narrowed components/_modal.css bases. They
   fit: the head declares display/align-items/gap and no justify-content or
   border, the foot declares display only, and the close button declares
   the base's paint set plus its own padding and radius.
   Note the close button's inline `background:none` and the base's
   `background:transparent` compute to the same value.
   The 3px strips, the 7px/9px/11px/14px radii and the #D97706 gradient
   stop have no token. ============================================================ */
/* RESOLVED 16 Aug 2026 — this element keeps .lq-button--link, and the colour rule for
   that class now lives in components/_buttons.css wrapped in :where(), at zero
   specificity, so this (0,1,0) rule wins and the link stays muted.
   The earlier note here read DO NOT FIX HERE and pointed at a11y-overrides.css.
   Its arithmetic still holds and is the reason the wrapper is mandatory: because
   this is an <a href>, an unwrapped a.lq-button--link:link at (0,2,1) would outrank
   this rule on specificity and render the link accent green: --lq-muted ->
   --lq-accent-text, today rgb(148,163,184) -> rgb(110,231,183) in dark and
   rgb(71,85,105) -> rgb(6,95,70) in light. (Named by token, not only by value,
   so the note cannot go stale the next time a token moves — the rgb pair it
   carried before this edit was the pre-16-Aug --lq-muted.) Do not raise
   specificity here, do not drop .lq-button--link, and do not unwrap the :where(). */
.lq-store-detail-back { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--lq-muted); text-decoration: none; margin-bottom: 16px; }
.lq-store-detail-back__glyph { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transform: rotate(180deg); }

.lq-store-detail-hero { position: relative; overflow: hidden; background: var(--lq-surface); border: 1px solid var(--lq-border); border-radius: var(--lq-r); box-shadow: var(--lq-shadow-sm); margin-bottom: 16px; }
.lq-store-detail-hero__strip { height: 3px; background: linear-gradient(90deg, var(--lq-accent-2), var(--lq-accent-3), var(--lq-accent-2)); }
.lq-store-detail-hero__top { padding: 22px 24px; display: flex; justify-content: space-between; align-items: flex-start; gap: 18px; flex-wrap: wrap; }
.lq-store-detail-hero__identity { display: flex; align-items: center; gap: 16px; }
.lq-store-detail-hero__icon { width: 56px; height: 56px; border-radius: 14px; flex-shrink: 0; background: var(--lq-accent-soft); border: 1px solid var(--lq-accent-border); display: grid; place-items: center; color: var(--lq-accent); }
.lq-store-detail-hero__glyph { width: 28px; height: 28px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.lq-store-detail-hero__name-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.lq-store-detail-hero__name { font-family: var(--lq-font-head); font-weight: 800; font-size: 24px; letter-spacing: -.015em; color: var(--lq-text); margin: 0; }
.lq-store-detail-hero__domain { font-family: var(--lq-font-mono); font-size: 12px; color: var(--lq-muted); margin-top: 5px; }
.lq-store-detail-hero__action { display: inline-flex; align-items: center; gap: 7px; padding: 9px 14px; border-radius: 9px; font-size: 12px; font-weight: 600; cursor: pointer; border: 1px solid var(--lq-border-2); background: var(--lq-surface-2); color: var(--lq-text); text-decoration: none; white-space: nowrap; }
.lq-store-detail-hero__action-glyph { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.lq-store-detail-hero__bottom { padding: 0 24px 22px; }

.lq-store-detail-facts { background: var(--lq-surface-2); border: 1px solid var(--lq-border); border-radius: 11px; padding: 15px 17px; }
.lq-store-detail-facts__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr)); gap: 10px; }
.lq-store-detail-fact { background: var(--lq-surface); border: 1px solid var(--lq-border); border-radius: 9px; padding: 11px 13px; }
.lq-store-detail-fact__head { display: flex; align-items: center; gap: 7px; margin-bottom: 7px; }
.lq-store-detail-fact__dot { width: 7px; height: 7px; border-radius: 50%; background: var(--lq-accent); box-shadow: 0 0 0 3px var(--lq-accent-soft); flex-shrink: 0; }
.lq-store-detail-fact__label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--lq-muted); }
.lq-store-detail-fact__value { font-size: 14px; font-weight: 700; color: var(--lq-text); }
.lq-store-detail-fact__stamp { font-size: 10.5px; color: var(--lq-muted); font-family: var(--lq-font-mono); margin-top: 2px; }
.lq-store-detail-facts__note { margin-top: 11px; display: flex; align-items: center; gap: 7px; font-size: 11px; color: var(--lq-muted); }
.lq-store-detail-facts__note-glyph { width: 13px; height: 13px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

.lq-store-detail-info { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr)); gap: 14px; margin-bottom: 16px; }
.lq-store-detail-info-card { background: var(--lq-surface); border: 1px solid var(--lq-border); border-radius: var(--lq-r); padding: 14px 16px; display: flex; gap: 12px; align-items: center; box-shadow: var(--lq-shadow-sm); }
.lq-store-detail-info-card__icon { width: 34px; height: 34px; border-radius: 9px; flex-shrink: 0; display: grid; place-items: center; }
.lq-store-detail-info-card__icon--info { background: var(--lq-info-soft); color: var(--lq-info); }
.lq-store-detail-info-card__icon--plum { background: var(--lq-plum-soft); color: var(--lq-plum); }
.lq-store-detail-info-card__icon--accent { background: var(--lq-accent-soft); color: var(--lq-accent); }
.lq-store-detail-info-card__glyph { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.lq-store-detail-info-card__label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--lq-muted); white-space: nowrap; }
.lq-store-detail-info-card__value { font-size: 14px; font-weight: 700; color: var(--lq-text); margin-top: 2px; }
.lq-store-detail-info-card__value--mono { font-family: var(--lq-font-mono); }

.lq-store-detail-columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr)); gap: 16px; align-items: start; }
.lq-store-detail-columns--spaced { margin-bottom: 16px; }
.lq-store-detail-panel-glyph { width: 16px; height: 16px; }
.lq-store-detail-panel-glyph--plum-filled { fill: var(--lq-plum); }
.lq-store-detail-panel-glyph--accent { fill: none; stroke: var(--lq-accent); stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.lq-store-detail-panel-glyph--warn { fill: none; stroke: var(--lq-warn); stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.lq-store-detail-plan-cadence { font-size: 12px; color: var(--lq-muted); }
.lq-store-detail-plan-note { margin-top: 15px; padding: 11px 12px; border-radius: 9px; background: var(--lq-surface-2); display: flex; gap: 8px; align-items: flex-start; }
.lq-store-detail-plan-note__glyph { width: 14px; height: 14px; flex-shrink: 0; margin-top: 1px; fill: none; stroke: var(--lq-muted); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.lq-store-detail-plan-note__text { font-size: 11px; color: var(--lq-muted); line-height: 1.5; }

.lq-store-detail-specs { padding: 4px 18px 12px; }
.lq-store-detail-spec { display: flex; justify-content: space-between; align-items: center; padding: 11px 0; }
.lq-store-detail-spec--divided { border-bottom: 1px solid var(--lq-border); }
.lq-store-detail-spec__label { font-size: 12.5px; color: var(--lq-text-2); }
.lq-store-detail-spec__value { font-family: var(--lq-font-mono); font-size: 12px; color: var(--lq-text); }

.lq-store-detail-recovery { background: var(--lq-surface); border: 1px solid var(--lq-warn-border); border-radius: var(--lq-r); box-shadow: var(--lq-shadow-sm); overflow: hidden; }
.lq-store-detail-recovery__strip { height: 3px; background: linear-gradient(90deg, var(--lq-warn), #D97706); }
.lq-store-detail-recovery__body { padding: 15px 18px 17px; }
.lq-store-detail-recovery__head { display: flex; align-items: center; gap: 9px; margin-bottom: 10px; }
.lq-store-detail-recovery__text { font-size: 12px; color: var(--lq-text-2); line-height: 1.6; margin: 0 0 13px; }
.lq-store-detail-recovery__emphasis { color: var(--lq-text); }
.lq-store-detail-recovery__button { display: inline-flex; align-items: center; gap: 7px; padding: 9px 14px; border-radius: 9px; font-size: 12px; font-weight: 600; cursor: pointer; border: 1px solid var(--lq-warn-border); background: var(--lq-warn-soft); color: var(--lq-warn-text); font-family: var(--lq-font-body); }
.lq-store-detail-recovery__button-glyph { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

/* ---- Credential recovery dialog ---- */
.lq-recovery__head { padding: 20px 22px 6px; }
.lq-recovery__icon { width: 42px; height: 42px; border-radius: 11px; flex-shrink: 0; background: var(--lq-warn-soft); border: 1px solid var(--lq-warn-border); display: grid; place-items: center; color: var(--lq-warn); }
.lq-recovery__icon-glyph { width: 21px; height: 21px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.lq-recovery__titles { flex: 1; }
.lq-recovery__title { font-family: var(--lq-font-head); font-size: 16px; font-weight: 800; color: var(--lq-text); margin: 0 0 3px; }
.lq-recovery__subtitle { font-size: 12.5px; color: var(--lq-muted); margin: 0; }
.lq-recovery__store { color: var(--lq-text-2); }
.lq-recovery__close { padding: 4px; border-radius: 7px; }
.lq-recovery__close-glyph { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.lq-recovery__body { padding: 10px 22px 4px; }
.lq-recovery__lead { font-size: 12.5px; color: var(--lq-text-2); line-height: 1.6; margin: 0 0 12px; }
.lq-recovery__steps { display: flex; flex-direction: column; gap: 9px; margin-bottom: 14px; }
.lq-recovery__step { display: flex; gap: 10px; align-items: flex-start; }
.lq-recovery__step-number { width: 22px; height: 22px; border-radius: 50%; flex-shrink: 0; background: var(--lq-accent-soft); color: var(--lq-accent-text); display: grid; place-items: center; font-weight: 700; font-size: 11px; font-family: var(--lq-font-mono); }
.lq-recovery__step-text { font-size: 12px; color: var(--lq-text-2); line-height: 1.5; }
.lq-recovery__notice { padding: 11px 13px; border-radius: 9px; background: var(--lq-info-soft); border: 1px solid var(--lq-info-border); display: flex; gap: 9px; align-items: flex-start; }
.lq-recovery__notice-glyph { width: 15px; height: 15px; flex-shrink: 0; margin-top: 1px; fill: none; stroke: var(--lq-info); stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.lq-recovery__notice-text { font-size: 11.5px; color: var(--lq-info-text); line-height: 1.5; }
.lq-recovery__foot { padding: 16px 22px 20px; justify-content: flex-end; gap: 9px; }
/* Both dialog buttons are 8px-radius / 12.5px / weight 600 — the secondary
   differs from .lq-button--secondary-medium in radius and surface, so it keeps
   a rule of its own. The primary no longer needs one: with the gradient and
   the #fff ink gone (CHANGELOG §I), its border, fill and ink are exactly the
   .lq-button--primary its element already wears, so the rule and the --primary
   modifier are both gone. */
.lq-recovery__button { padding: 9px 15px; border-radius: var(--lq-r-sm); font-size: 12.5px; font-weight: 600; }
.lq-recovery__button--secondary { background: var(--lq-surface-2); }

/* Three-view dialog: confirm signed-in → handoff, with a not-signed-in view.
   Like the /stores claim dialog the view wrapper is display:contents, so the
   visible .lq-modal__dialog stays the grid item the base .lq-modal centres and
   docks; unlike that page's two surfaces this class belongs to one dialog, so it
   needs no [data-lq-modal] scope. */
.lq-recovery__views { display: contents; }
/* The confirm view asks a question, so its foot carries both answers: the
   decline on the left, the cancel/confirm pair on the right. --split overrides
   the block's flex-end, and the pair keeps the block's own 9px gap rather than
   .lq-modal__foot-actions' 10px, so those two buttons sit where they always did.
   At the tree's 899px break the decline drops to its own line above the pair. */
.lq-recovery__foot--split { justify-content: space-between; }
.lq-recovery__actions { display: flex; gap: 9px; }
/* The decline is a link-style button. .lq-button--link declares only colour and
   a hover underline, so — exactly as .lq-session__revoke does for the session
   list — the element class carries the UA-neutralising set and the type. Without
   it the control renders as a raw UA button: grey ground, outset border, Arial,
   and --lq-accent-text on that grey fails contrast. Colour is left to
   .lq-button--link's :where() rule, which contributes no specificity. */
.lq-recovery__decline { border: none; background: none; font-family: var(--lq-font-body); font-weight: 600; font-size: 11.5px; padding: 0; cursor: pointer; }
.lq-recovery__strong { color: var(--lq-text); }
/* Two notices stack in the handoff view; .lq-recovery__notice has no margin. */
.lq-recovery__notices { display: flex; flex-direction: column; gap: 9px; }
/* Warn tone for the handoff expiry. The base notice hard-codes the info tone on
   its ground, glyph and text, so each needs its own modifier. */
.lq-recovery__notice--warn { background: var(--lq-warn-soft); border-color: var(--lq-warn-border); }
.lq-recovery__notice-glyph--warn { stroke: var(--lq-warn); }
.lq-recovery__notice-text--warn { color: var(--lq-warn-text); }
/* Danger tone for the failed-request notice in the error view, and the bottom
   margin it needs as the first thing in the body. 12px matches .lq-recovery__lead's
   own bottom margin, so the notice sits in the body's existing rhythm. */
.lq-recovery__notice--danger { background: var(--lq-danger-soft); border-color: var(--lq-danger-border); }
.lq-recovery__notice-glyph--danger { stroke: var(--lq-danger); }
.lq-recovery__notice-text--danger { color: var(--lq-danger-text); }
.lq-recovery__notice--leading { margin-bottom: 12px; }

@media (max-width: 899px) {
  .lq-recovery__foot--split { flex-wrap: wrap; justify-content: flex-end; }
  .lq-recovery__decline { flex: 1 0 100%; text-align: right; }
}
/* ============================================================
   Plan — the /plan page (Lucriq_Plan_Usage) and the Unlock slots
   specification page (Lucriq_Unlock_Slots), which are one feature.
   lq-plan-* is that feature's prefix, not a page prefix: fifteen of these
   classes are worn by both pages, and pass 2 settled the cross-page note
   this header used to carry by renaming the partial to the feature it
   serves (it was layout/_plan-usage.css) rather than re-prefixing 51
   classes or moving fifteen rules into an earlier cascade layer. Page-only
   names elsewhere stay page-scoped: lq-unlock-slots-* is the specification
   page's own framing.
   (lq-slot-* is shared by both pages too and needs no note: it lives in
   components/, which is what components/ is for.)

   Shares the modal, slot, thumb, entity, chip, radio, panel,
   usage-meter, pill and tier-badge components; only the page's own hero,
   plan ladder and upgrade banner are here.
   The dialogs' heads, feet and close buttons use the narrowed
   components/_modal.css bases plus --standard / --divided / --boxed,
   which match their inline declarations exactly.
   The 10px ladder-tile radius, 11px upgrade-icon radius and 9px
   disclosure-panel radius have no token.
   ============================================================ */
.lq-plan-hero { position: relative; overflow: hidden; background: var(--lq-surface); border: 1px solid var(--lq-border); border-radius: var(--lq-r); box-shadow: var(--lq-shadow-sm); margin-bottom: 16px; }
.lq-plan-hero__glow { position: absolute; top: -60px; right: -30px; width: 280px; height: 220px; background: radial-gradient(circle, var(--lq-accent-soft), transparent 70%); pointer-events: none; }
.lq-plan-hero__inner { position: relative; padding: 22px 24px; display: flex; justify-content: space-between; align-items: center; gap: 20px; flex-wrap: wrap; }
.lq-plan-hero__badges { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.lq-plan-hero__price { font-family: var(--lq-font-mono); font-size: 13px; color: var(--lq-muted); }
.lq-plan-hero__title { font-family: var(--lq-font-head); font-weight: 800; font-size: 22px; letter-spacing: -.015em; color: var(--lq-text); margin: 0 0 5px; }
.lq-plan-hero__text { font-size: 12.5px; color: var(--lq-text-2); margin: 0; line-height: 1.55; max-width: 52ch; }
.lq-plan-hero__store { color: var(--lq-text); }
.lq-plan-hero__aside { color: var(--lq-muted); }
.lq-plan-hero__figures { display: flex; flex-direction: column; gap: 8px; min-width: 220px; }
.lq-plan-hero__figure { display: flex; justify-content: space-between; font-size: 12px; }
.lq-plan-hero__figure-label { color: var(--lq-muted); }
.lq-plan-hero__figure-value { font-family: var(--lq-font-mono); color: var(--lq-text); font-weight: 600; }
.lq-plan-hero__figure-value--accent { color: var(--lq-accent-text); }

.lq-plan-columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr)); gap: 16px; align-items: start; margin-bottom: 16px; }
.lq-plan-note { font-size: 11.5px; color: var(--lq-muted); line-height: 1.5; }
/* Two sizes in the source, not one: the Unlock-slots head's meta is 11px and
   the Unlocked-products head's is 11.5px. They were briefly collapsed onto one
   class, which grew the 11px box by 5.6px inside a --wrap flex head. Keep the
   base at the size the majority declares and carry the other as a modifier. */
.lq-plan-panel-meta { font-size: 11.5px; color: var(--lq-muted); }
.lq-plan-panel-meta--small { font-size: 11px; }
.lq-plan-panel-meta__value { font-family: var(--lq-font-mono); color: var(--lq-text-2); }
.lq-plan-panel-glyph { width: 16px; height: 16px; fill: none; stroke: var(--lq-accent); stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

.lq-plan-upgrade { display: flex; gap: 16px; align-items: center; justify-content: space-between; flex-wrap: wrap; background: var(--lq-surface-2); border: 1px solid var(--lq-border); border-radius: var(--lq-r); padding: 18px 22px; margin-bottom: 16px; }
.lq-plan-upgrade__body { display: flex; align-items: center; gap: 14px; min-width: 0; }
.lq-plan-upgrade__icon { width: 42px; height: 42px; border-radius: 11px; flex-shrink: 0; background: var(--lq-accent-soft); border: 1px solid var(--lq-accent-border); display: grid; place-items: center; color: var(--lq-accent); }
.lq-plan-upgrade__glyph { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.lq-plan-upgrade__title { font-size: 13.5px; font-weight: 700; color: var(--lq-text); margin-bottom: 2px; }
.lq-plan-upgrade__text { font-size: 12px; color: var(--lq-text-2); line-height: 1.5; }
/* Upgrade banner action. It is an <a href> to the marketing site's public
   pricing page (LUCRIQ-DECISIONS.md §4.33, LUCRIQ-SAAS-UI.md §Plan & Usage), so
   text-decoration here is the rule's own and not a neutralisation: without it the
   anchor underlines, this tree declaring no element-level a rule anywhere. Inert
   on the <button> the control used to be, the way .lq-signout keeps it for the
   tag it is not. The UA differences that cost no paint — appearance,
   text-rendering — are left alone, as §P left them on the auth primaries. */
.lq-plan-upgrade__button { display: inline-flex; align-items: center; gap: 7px; padding: 10px 15px; border-radius: 9px; border: 1px solid var(--lq-border-2); background: var(--lq-surface); color: var(--lq-text); text-decoration: none; font-family: var(--lq-font-body); font-weight: 600; font-size: 12.5px; cursor: pointer; white-space: nowrap; }
.lq-plan-upgrade__button-glyph { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

.lq-plan-ladder__label { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--lq-muted); margin-bottom: 10px; }
.lq-plan-ladder__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 88px), 1fr)); gap: 8px; }
.lq-plan-ladder__tile { padding: 12px 13px; border-radius: 10px; text-align: center; border: 1px solid var(--lq-border); background: var(--lq-surface); }
.lq-plan-ladder__tile--current { border-color: var(--lq-accent-border); background: var(--lq-accent-soft); }
.lq-plan-ladder__name { font-family: var(--lq-font-head); font-weight: 700; font-size: 13px; color: var(--lq-text); }
.lq-plan-ladder__name--current { color: var(--lq-accent-text); }
.lq-plan-ladder__figure { font-family: var(--lq-font-mono); font-size: 11.5px; color: var(--lq-muted); margin-top: 2px; }
.lq-plan-ladder__flag { margin-top: 6px; font-size: 9.5px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--lq-accent-text); }
/* The disclosure panel's own footnote sits 10px below its chips. The ladder's
   footnote sat at 8px under a class of its own: the two were briefly mapped onto
   one class and the 2px loss reflowed the panel, which is why they were kept
   apart. CHANGELOG §U removed the last element wearing the ladder class and §V
   deleted its rule, so the 10px below is this footnote's own value — a ladder
   footnote coming back needs its 8px written again, not this rule widened to
   carry both. */
.lq-plan-details-note { margin-top: 10px; font-size: 11px; color: var(--lq-muted); line-height: 1.5; }

/* Dialog extras */
.lq-plan-dialog-narrow { max-width: 460px; }
.lq-plan-dialog__hint { font-size: 11.5px; color: var(--lq-text-2); line-height: 1.5; margin-top: 10px; text-wrap: pretty; }
.lq-plan-dialog__group-label { font-size: 11px; color: var(--lq-muted); margin-top: 10px; }
.lq-plan-dialog__lead { margin: 0 0 14px; font-size: 13px; color: var(--lq-text-2); line-height: 1.6; text-wrap: pretty; }
.lq-plan-dialog__section-label { font-size: 11.5px; font-weight: 600; color: var(--lq-text); margin-bottom: 9px; }
.lq-plan-dialog__chip-panel { display: flex; flex-wrap: wrap; gap: 6px; padding: 12px; background: var(--lq-surface-2); border: 1px solid var(--lq-border); border-radius: 9px; }
.lq-plan-dialog__footnote { margin: 12px 0 0; font-size: 11px; color: var(--lq-muted); line-height: 1.5; text-wrap: pretty; }
.lq-plan-slot-hint { font-size: 11px; color: var(--lq-muted); margin-top: 5px; padding-left: 1px; line-height: 1.5; }
.lq-plan-slot-details { flex: 1 1 100%; margin-top: 2px; }
/* Plan & usage renders the meter head without the ellipsis wrapper, so its
   label declares only type and colour. Deliberately NOT .lq-usage-meter__label:
   that base adds display:inline-flex, align-items, gap and min-width, none of
   which the markup declares — neutralising four properties back to their
   initial values would be worse than not adopting it. Same for the value,
   which the base would give flex-shrink:0. */
.lq-plan-meter-label { font-size: 12.5px; font-weight: 600; color: var(--lq-text); }
.lq-plan-meter-value { font-family: var(--lq-font-mono); font-size: 15px; font-weight: 600; color: var(--lq-text); white-space: nowrap; }
.lq-plan-slot-details-flush { margin: 0; }
.lq-plan-empty-glyph { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* ============================================================
   Unlock slots — specification page (Lucriq_Unlock_Slots).
   Lives in this partial because it is the same feature as Plan usage's
   unlock flow: every dialog inside its frames uses the .lq-slot-*,
   .lq-plan-dialog-* and components/_modal.css classes above, unchanged.
   Only the catalogue framing and three dialog states are new.
   Names spell the page out in full (lq-unlock-slots-*) per the naming
   table — never an abbreviation.
   The 7px skeleton-bar and 10px notice-icon radii have no token.
   ============================================================ */
/* The catalogue page's canvas: .lq-showcase with a 16px gap rather than 24px. */
.lq-unlock-slots-showcase { gap: 16px; }
.lq-unlock-slots-intro { font-size: 12.5px; color: var(--lq-text-2); line-height: 1.55; max-width: 74ch; text-wrap: pretty; }
.lq-unlock-slots-label { font-family: var(--lq-font-mono); font-size: 10.5px; color: var(--lq-muted); letter-spacing: .03em; margin-top: 20px; }
.lq-unlock-slots-frame { width: 100%; max-width: 900px; border-radius: var(--lq-r); border: 1px solid var(--lq-border); background: var(--lq-bg); box-shadow: var(--lq-shadow-sm); }
.lq-unlock-slots-frame--error { height: 380px; }
.lq-unlock-slots-frame--release { height: 430px; }
.lq-unlock-slots-frame--search { height: 440px; }
.lq-unlock-slots-frame--picker-single { height: 500px; }
.lq-unlock-slots-frame--picker-multi { height: 520px; }
.lq-unlock-slots-frame--refresh { height: 540px; }
.lq-unlock-slots-phone { width: 390px; max-width: 100%; height: 720px; border-radius: 26px; border: 1px solid var(--lq-border-2); background: var(--lq-bg); box-shadow: var(--lq-shadow-lg); }
.lq-unlock-slots-panel { width: 100%; max-width: 900px; background: var(--lq-surface); border: 1px solid var(--lq-border); border-radius: var(--lq-r); box-shadow: var(--lq-shadow-sm); }
.lq-unlock-slots-skeleton { position: absolute; inset: 0; padding: 20px 24px; display: flex; flex-direction: column; gap: 12px; opacity: .4; pointer-events: none; }
.lq-unlock-slots-skeleton__bar { height: 24px; width: 200px; border-radius: 7px; background: var(--lq-surface); }
.lq-unlock-slots-skeleton__block { height: 130px; border-radius: var(--lq-r); background: var(--lq-surface); border: 1px solid var(--lq-border); }

/* Guidance banner above the catalogue. */
.lq-unlock-slots-notice { width: 100%; max-width: 900px; display: flex; gap: 15px; align-items: flex-start; background: var(--lq-info-soft); border: 1px solid var(--lq-info-border); border-radius: var(--lq-r); padding: 16px 18px; }
.lq-unlock-slots-notice__icon { width: 38px; height: 38px; border-radius: 10px; flex-shrink: 0; background: var(--lq-bg-2); border: 1px solid var(--lq-info-border); display: grid; place-items: center; color: var(--lq-info); }
.lq-unlock-slots-notice__glyph { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.lq-unlock-slots-notice__title { font-size: 13.5px; font-weight: 700; color: var(--lq-text); margin-bottom: 3px; text-wrap: pretty; }
.lq-unlock-slots-notice__text { font-size: 12px; color: var(--lq-text-2); line-height: 1.55; text-wrap: pretty; }
/* Restates font-size: this button is .lq-button--primary with no size modifier, and
   the base declares no font-size, so without it the button inherits the page's
   and grows. 12.5px is what the source declared inline. */
.lq-unlock-slots-notice__action { align-self: center; padding: 9px 15px; font-size: 12.5px; white-space: nowrap; }

/* Selected picker row, and the check that replaces the empty radio. */
.lq-slot-picker--selected { background: var(--lq-accent-soft); }
.lq-radio--checked { border: none; background: var(--lq-accent); display: grid; place-items: center; }
.lq-radio__check { width: 12px; height: 12px; fill: none; stroke: var(--lq-accent-ink); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }

/* Dialog body states: loading spinner and empty result. */
.lq-unlock-slots-loading { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding: 44px 20px; }
.lq-unlock-slots-spinner { width: 26px; height: 26px; border-radius: 50%; border: 2px solid var(--lq-border-2); border-top-color: var(--lq-accent); animation: lq-spin .7s linear infinite; }
.lq-unlock-slots-loading__text { font-size: 12px; color: var(--lq-muted); }
.lq-unlock-slots-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; padding: 40px 24px; text-align: center; }
.lq-unlock-slots-empty__icon { width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center; background: var(--lq-surface-2); border: 1px solid var(--lq-border); color: var(--lq-muted); }
.lq-unlock-slots-empty__glyph { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.lq-unlock-slots-empty__title { font-size: 13px; font-weight: 600; color: var(--lq-text); }
.lq-unlock-slots-empty__text { font-size: 11.5px; color: var(--lq-muted); line-height: 1.5; }
/* Toolbar hint band above a picker list. */
.lq-unlock-slots-hint-band { padding: 11px 18px; border-bottom: 1px solid var(--lq-border); background: var(--lq-surface-2); font-size: 11.5px; color: var(--lq-text-2); line-height: 1.5; }
/* Tab-bar icons in this page's phone frame declare only size, fill, stroke and
   stroke-width — NOT stroke-linecap/linejoin, which .lq-modal-tabbar__icon adds.
   Declining the adoption rather than inheriting two properties the source omits. */
.lq-unlock-slots-tab-glyph { width: 20px; height: 20px; fill: none; stroke: var(--lq-muted); stroke-width: 1.9; }
.lq-unlock-slots-tab-glyph--active { stroke: var(--lq-accent); }
/* ============================================================
   Account — the tab frame shared by Account profile and Account
   security, plus the profile form's own pieces.
   Named for the SECTION, not one page: both account pages render the
   same tab strip, so pass 2 left the lq-account-* prefix alone — it is
   already section-scoped. Same convention as layout/_app-shell.css and
   layout/_auth-layout.css.
   Recorded in saas/CSS-REFACTOR-LITERALS.md's cross-page table.
   ============================================================ */
.lq-account-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--lq-border); margin-bottom: 22px; }
.lq-account-tab { padding: 10px 4px; margin-right: 18px; font-size: 13px; font-weight: 600; text-decoration: none; color: var(--lq-muted); border-bottom: 2px solid transparent; }
.lq-account-tab--current { color: var(--lq-text); border-bottom-color: var(--lq-accent); }
.lq-account-form { max-width: 620px; display: flex; flex-direction: column; gap: 16px; }

.lq-account-identity { display: flex; align-items: center; gap: 16px; margin-bottom: 20px; }
.lq-account-avatar { width: 56px; height: 56px; border-radius: 50%; flex-shrink: 0; background: linear-gradient(135deg, var(--lq-accent-2), var(--lq-accent-3)); color: var(--lq-accent-ink); display: grid; place-items: center; font-weight: 800; font-size: 22px; font-family: var(--lq-font-head); }
.lq-account-identity__name { font-size: 16px; font-weight: 700; color: var(--lq-text); }
.lq-account-identity__email { font-size: 12px; color: var(--lq-muted); font-family: var(--lq-font-mono); }

/* Form field. Its own block rather than .lq-field: this section's labels carry
   no bottom margin by default, while .lq-field's base is margin-bottom:15px. */
.lq-account-field { display: block; }
.lq-account-field--spaced { margin-bottom: 18px; }
.lq-account-field-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr)); gap: 12px; margin-bottom: 15px; }
.lq-account-field-row--spaced { margin-bottom: 18px; }
.lq-account-label-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 7px; }
.lq-account-flag { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 600; color: var(--lq-warn-text); }
/* Accent affordance inside .lq-account-flag. It dresses a <button>: the email
   verify affordance requests or re-sends the verification email
   (LUCRIQ-SAAS-UI.md §Account → Profile, through LUCRIQ-API.md §8.0.5.1), so it
   is an action and not a link to the verification page. The six declarations
   after text-decoration neutralise the UA button appearance; font: inherit takes
   the flag's 11px/600 and its line-height — what the anchor inherited — so the
   label row keeps its height and only the flag's own width follows the copy. */
.lq-account-link { color: var(--lq-accent-text); text-decoration: none; appearance: none; border: 0; padding: 0; background: none; font: inherit; cursor: pointer; }
/* Read-only email field. A standalone BLOCK, deliberately not .lq-input at all.
   Declining the adoption is the point: .lq-input carries a (0,2,0) :focus rule,
   and at 54c3de2 this element had no class, so no :focus rule reached it. The
   source removed its focus affordance on purpose — outline:none and
   cursor:default are both declared inline — and adopting .lq-input would hand
   it an accent border and the focus ring, a stronger affordance than any
   editable field had before extraction. The alternative, a :focus rule written
   only to cancel a :focus rule, is a rule pass 1 must not add. */
.lq-account-readonly-field {
  width: 100%;
  padding: 11px 13px;
  border-radius: 9px;
  border: 1px solid var(--lq-border);
  background: var(--lq-surface-3);
  color: var(--lq-muted);
  font-family: var(--lq-font-body);
  font-size: 13.5px;
  outline: none;
  cursor: default;
}
/* Form actions. Neither reuses a shared button modifier: the primary carries no
   lift and both are 10px/16px at weight 600, unlike every other primary. The
   primary's gradient and #fff ink are now the base primary's flat fill and
   token ink (CHANGELOG §I); its border keeps 1px, transparent, so the box does
   not move. */
.lq-account-button { display: inline-flex; align-items: center; gap: 7px; padding: 10px 16px; border-radius: 9px; font-family: var(--lq-font-body); font-weight: 600; font-size: 12.5px; cursor: pointer; }
.lq-account-button--primary { border: 1px solid transparent; background: var(--lq-accent); color: var(--lq-accent-ink); }
.lq-account-button--secondary { border: 1px solid var(--lq-border-2); background: var(--lq-surface-2); color: var(--lq-text); }

/* ---- Account security ---- */
.lq-account-panel { max-width: 760px; }
.lq-account-section-title { font-size: 14px; font-weight: 700; color: var(--lq-text); }
.lq-account-section-note { font-size: 11.5px; color: var(--lq-muted); margin-top: 2px; }
.lq-account-danger-button { display: inline-flex; align-items: center; gap: 7px; padding: 8px 13px; border-radius: 9px; border: 1px solid var(--lq-danger-border); background: var(--lq-danger-soft); color: var(--lq-danger-text); font-family: var(--lq-font-body); font-weight: 600; font-size: 12px; cursor: pointer; }
.lq-account-danger-button__glyph { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

.lq-session { display: flex; align-items: center; gap: 15px; padding: 15px 18px; border-top: 1px solid var(--lq-border); }
.lq-session__icon { width: 40px; height: 40px; border-radius: 10px; flex-shrink: 0; display: grid; place-items: center; }
.lq-session__icon--current { background: var(--lq-accent-soft); color: var(--lq-accent); }
.lq-session__icon--other { background: var(--lq-surface-2); color: var(--lq-text-2); }
.lq-session__glyph { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.lq-session__body { min-width: 0; flex: 1; }
.lq-session__title-row { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.lq-session__name { font-size: 13.5px; font-weight: 700; color: var(--lq-text); }
.lq-session__where { font-size: 11.5px; color: var(--lq-muted); margin-top: 3px; }
.lq-session__ip { font-family: var(--lq-font-mono); }
.lq-session__aside { text-align: right; flex-shrink: 0; }
.lq-session__when { font-family: var(--lq-font-mono); font-size: 11.5px; color: var(--lq-text-2); }
.lq-session__state { margin-top: 5px; font-size: 11px; color: var(--lq-muted); }
/* Revoke control. .lq-button--link stays on the element and gets NO base rule (its
   two consumers intersect in one declaration only — see layout/_store-detail.css).
   RESOLVED 16 Aug 2026 — the fix landed in this tree after all, as
   :where(.lq-button--link, a.lq-button--link:link, a.lq-button--link:visited) { color:
   var(--lq-accent-text) } in components/_buttons.css. At zero specificity it
   cannot outrank this (0,1,0) rule, so these buttons keep --lq-danger-text.
   The earlier note here read DO NOT FIX HERE and pointed at a11y-overrides.css:
   sound while the app owned the rule, wrong once an accessibility hole in a
   specification counts as a hole in the design. The arithmetic it recorded still
   holds — a bare .lq-button--link colour rule at (0,1,0) would tie with this one and
   win on source order, turning both buttons accent green (measured: dark #FCA5B0
   -> #6EE7B7, light #B91C1C -> #065F46). That is the failure the :where()
   prevents. Do not raise specificity here and do not drop .lq-button--link. */
.lq-session__revoke { margin-top: 5px; border: none; background: none; color: var(--lq-danger-text); font-family: var(--lq-font-body); font-weight: 600; font-size: 11.5px; cursor: pointer; padding: 0; }
.lq-account-note { margin-top: 14px; display: flex; gap: 9px; align-items: flex-start; padding: 13px 16px; border: 1px dashed var(--lq-border-2); border-radius: 10px; color: var(--lq-muted); }
.lq-account-note__glyph { width: 15px; height: 15px; flex-shrink: 0; margin-top: 1px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.lq-account-note__text { font-size: 11.5px; line-height: 1.55; }
/* The sessions live region before it has anything to announce. It reserves the
   vertical space of one .lq-session row so the panel does not jump when the
   list arrives, and it carries NO border-top on purpose: .lq-session draws the
   rule that separates it from the row above, and an empty region drawing one
   would put a line under a panel head with nothing beneath it. Padding only —
   the same padding, so the reservation is honest. */
.lq-session-placeholder { padding: 15px 18px; }
/* Connect-store live dialog: desktop vs mobile surface swap at the shell's 899px break.
   Scoped to the connect-store modal only — no generic .lq-modal* rule is affected, and no
   other modal carries these classes. Each surface's dialogs use display:contents so the
   visible .lq-modal__dialog remains the grid item centred/docked by the base .lq-modal. */
[data-lq-modal="connect-store"] .lq-connect-store__surface--desktop { display: contents; }
[data-lq-modal="connect-store"] .lq-connect-store__surface--mobile  { display: none; }
@media (max-width: 899px) {
  [data-lq-modal="connect-store"] .lq-connect-store__surface--desktop { display: none; }
  [data-lq-modal="connect-store"] .lq-connect-store__surface--mobile  { display: contents; }
}

/* ============================================================
   Connect-a-store flow — dialog internals.
   This partial is named for the page but owns the FLOW: the same dialog
   renders live inside Lucriq_Stores and as fixtures in
   Lucriq_Connect_Store, so both use these classes. Names use the
   .lq-connect-store__ namespace the naming table assigns to this flow.
   Pass 2 brought the showcase classes onto the same prefix (renamed in pass 2,
   see CHANGELOG-saas-static.md §G), so the partial now holds one vocabulary in two
   shapes: lq-connect-store__* is the dialog flow, lq-connect-store-* the
   showcase framing around it. One pair reads alike and does not mean the
   same thing — .lq-connect-store-label--spaced is a showcase caption worn
   with its block, .lq-connect-store__label-spaced a form label in the flow.

   Several variants deliberately do NOT reuse a components/_modal.css
   modifier that looks close:
   - __body vs .lq-modal__body--padded: --padded also sets font-size,
     colour and line-height, which these bodies never declared.
   - __foot--stacked-pad vs .lq-modal__foot--stacked alone: the stacked
     foot here is padded 13px 16px, not 13px 18px.
   The 10px notice radius and 9px badge radius have no token.
   ============================================================ */
.lq-connect-store__dialog { max-width: 468px; }
.lq-connect-store__dialog-mobile { max-width: 100%; max-height: calc(100% - 92px); }

.lq-connect-store__head-snug { padding: 15px 20px; }
.lq-connect-store__head-mobile { align-items: center; justify-content: space-between; gap: 12px; padding: 14px 16px; }
.lq-connect-store__head-mobile-snug { align-items: center; justify-content: space-between; gap: 10px; padding: 13px 16px; }
.lq-connect-store__head-group { flex: 1; min-width: 0; display: flex; align-items: center; gap: 11px; }
.lq-connect-store__head-group--tight { gap: 9px; }
.lq-connect-store__head-group-mobile { display: flex; align-items: center; gap: 8px; min-width: 0; }

.lq-connect-store__badge { display: grid; place-items: center; width: 32px; height: 32px; flex-shrink: 0; border-radius: 9px; background: var(--lq-accent-soft); color: var(--lq-accent-text); }
.lq-connect-store__badge-glyph { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
/* Icon-only Back control. .lq-button--secondary supplies border, background and
   font metrics; the svg-only content means its font declarations cannot
   paint (recorded in components/_buttons.css). */
.lq-connect-store__back { display: grid; place-items: center; width: 30px; height: 30px; flex-shrink: 0; padding: 0; border-radius: var(--lq-r-sm); color: var(--lq-text-2); }
.lq-connect-store__back-glyph { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

.lq-connect-store__body { padding: 18px 20px; }
.lq-connect-store__body-mobile { padding: 15px 16px; }
.lq-connect-store__lead { margin: 0 0 16px; font-size: 12.5px; color: var(--lq-text-2); line-height: 1.55; text-wrap: pretty; }
.lq-connect-store__lead--mobile { margin-bottom: 14px; }
.lq-connect-store__label-spaced { margin: 14px 0 6px; }
.lq-connect-store__label-spaced-mobile { margin: 13px 0 6px; }
.lq-connect-store__input-mobile { padding: 10px 12px; }
/* The code field is not .lq-input--dialog (its padding and type differ), so it
   must restate the surface background itself: the .lq-input base deliberately
   omits background, and without it the field falls through to the user agent's
   default input colour. */
.lq-connect-store__code { padding: 10px 12px; background: var(--lq-surface); font-size: 16px; font-family: var(--lq-font-mono); letter-spacing: .35em; }
.lq-connect-store__code--mobile { padding: 11px 12px; }

.lq-connect-store__status { padding: 11px 13px; }
.lq-connect-store__status-mobile { gap: 9px; padding: 10px 12px; margin-bottom: 14px; }
.lq-connect-store__status-glyph-mobile { width: 15px; height: 15px; }
.lq-connect-store__status-text { font-size: 12px; line-height: 1.5; text-wrap: pretty; }
.lq-connect-store__status-text--mobile { font-size: 11.5px; }

.lq-connect-store__help-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 9px; }
.lq-connect-store__help-row--mobile { gap: 10px; }
.lq-connect-store__help { font-size: 11px; color: var(--lq-muted); }
.lq-connect-store__resend {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--lq-accent-text);
  font-family: var(--lq-font-body);
  font-weight: 600;
  font-size: 11.5px;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
}
.lq-connect-store__resend-glyph { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

.lq-connect-store__foot-stacked-pad { padding: 13px 16px; }
.lq-connect-store__foot-mobile { padding: 13px 16px; }

/* ============================================================
   Connect-a-store SPECIFICATION page (Lucriq_Connect_Store).
   The five-state catalogue: each state rendered inside a simulated
   desktop or phone viewport. Everything below is showcase framing —
   the dialog markup inside the frames uses the flow classes above,
   unchanged, which is what makes the two surfaces comparable.
   Several near-miss adoptions are declined rather than neutralised
   (noted at each): the catalogue's badges and status dots declare
   fewer properties than the shared components, and the missing ones
   (line-height, letter-spacing, flex-shrink) have real visual effect.
   The 6/7/9/12/26px radii and the two color-mix danger rings have no token.
   ============================================================ */
.lq-connect-store-page-title { font-family: var(--lq-font-head); font-weight: 800; font-size: 22px; letter-spacing: -.015em; color: var(--lq-text); margin: 0; }
.lq-connect-store-intro { font-size: 12.5px; color: var(--lq-text-2); line-height: 1.55; max-width: 78ch; text-wrap: pretty; margin-bottom: 10px; }
.lq-connect-store-intro__strong { color: var(--lq-text); font-weight: 600; }
.lq-connect-store-outro { font-size: 11.5px; color: var(--lq-muted); line-height: 1.5; max-width: 78ch; }
.lq-connect-store-section {
  font-family: var(--lq-font-head);
  font-weight: 800;
  font-size: 13px;
  letter-spacing: .02em;
  color: var(--lq-text-2);
  text-transform: uppercase;
  margin-top: 26px;
  padding-top: 14px;
  border-top: 1px solid var(--lq-border);
  width: 100%;
}
.lq-connect-store-section--first { margin-top: 12px; }
.lq-connect-store-label { font-family: var(--lq-font-mono); font-size: 10.5px; color: var(--lq-muted); letter-spacing: .03em; }
.lq-connect-store-label--spaced { margin-top: 22px; }
.lq-connect-store-label--tight { margin-top: 8px; }
.lq-connect-store-row { display: flex; flex-wrap: wrap; gap: 20px; align-items: flex-start; }

/* ---- Simulated viewports ---- */
.lq-connect-store-frame { width: 100%; max-width: 980px; border-radius: var(--lq-r); border: 1px solid var(--lq-border); background: var(--lq-bg); box-shadow: var(--lq-shadow-sm); }
.lq-connect-store-frame--entry { height: 470px; }
.lq-connect-store-frame--code { height: 490px; }
.lq-connect-store-frame--error { height: 500px; }
.lq-connect-store-frame--plain { position: relative; overflow: hidden; background: var(--lq-bg-2); height: 470px; }
.lq-connect-store-phone { position: relative; overflow: hidden; width: 340px; height: 660px; border-radius: 26px; border: 1px solid var(--lq-border-2); background: var(--lq-bg); box-shadow: var(--lq-shadow-lg); }

.lq-connect-store-skeleton { position: absolute; inset: 0; display: flex; flex-direction: column; }
.lq-connect-store-skeleton--desktop { padding: 20px 24px; gap: 12px; opacity: .4; pointer-events: none; }
.lq-connect-store-skeleton--phone { padding: 16px; gap: 11px; opacity: .45; pointer-events: none; }
.lq-connect-store-skeleton--page { padding: 22px 26px; gap: 14px; }
.lq-connect-store-skeleton--page-phone { padding: 16px; gap: 12px; }
.lq-connect-store-bar { background: var(--lq-surface); }
.lq-connect-store-bar--title { height: 22px; width: 120px; border-radius: 7px; }
.lq-connect-store-bar--title-phone { height: 20px; width: 100px; border-radius: 6px; }
.lq-connect-store-bar--pill { height: 34px; width: 240px; border-radius: 99px; }
.lq-connect-store-bar--button { height: 34px; width: 150px; border-radius: 9px; }
.lq-connect-store-bar--field { height: 38px; border-radius: 12px; }
.lq-connect-store-block { border-radius: var(--lq-r); background: var(--lq-surface); border: 1px solid var(--lq-border); }
.lq-connect-store-block--desktop { height: 54px; }
.lq-connect-store-block--phone { height: 64px; }
.lq-connect-store-stack { display: flex; flex-direction: column; gap: 8px; }
.lq-connect-store-split { display: flex; justify-content: space-between; gap: 12px; }

/* Simulated phone tab bar. Distinct from layout/_modal.css's .lq-modal-tabbar:
   60px not 64px, no box-shadow, 9.5px labels not 10px. */
.lq-connect-store-tabbar { position: absolute; left: 0; right: 0; bottom: 0; height: 60px; z-index: 2; display: flex; align-items: stretch; background: color-mix(in srgb, var(--lq-bg-2) 92%, transparent); backdrop-filter: blur(14px); border-top: 1px solid var(--lq-border); }
.lq-connect-store-tabbar__item { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; font-size: 9.5px; font-weight: 600; color: var(--lq-muted); }
.lq-connect-store-tabbar__item--active { font-weight: 700; color: var(--lq-accent-text); }
.lq-connect-store-tabbar__icon { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.lq-connect-store-tabbar__icon--active { stroke: var(--lq-accent); }

/* Toast fixtures inside frames: positioned in the frame, not the viewport, and
   with no entry animation because these are static states. */
.lq-connect-store-toast-dock { position: absolute; left: 0; right: 0; bottom: 18px; display: flex; justify-content: center; padding: 0 16px; pointer-events: none; }
.lq-connect-store-toast-dock--phone { bottom: 74px; padding: 0 14px; pointer-events: auto; }
.lq-connect-store-toast { pointer-events: auto; max-width: min(90%, 460px); }
.lq-connect-store-toast-phone { pointer-events: auto; max-width: 100%; }

/* ---- Store picker / summary rows ---- */
.lq-connect-store-entity { display: flex; align-items: center; gap: 12px; min-width: 0; }
.lq-connect-store-entity--tight { gap: 11px; }
.lq-connect-store-avatar { border-radius: 9px; flex-shrink: 0; background: var(--lq-surface-2); border: 1px solid var(--lq-border); display: grid; place-items: center; color: var(--lq-text-2); }
.lq-connect-store-avatar--desktop { width: 36px; height: 36px; }
.lq-connect-store-avatar--phone { width: 34px; height: 34px; }
/* Named for the surface each renders on, not the pixel value. The block
   .lq-connect-store-avatar__glyph is deliberately not declared — the two sizes
   share only the paint setup, which every glyph in the tree also has — so
   pass 2 dropped the modifier punctuation rather than inventing a base rule
   these elements never wore. */
.lq-connect-store-avatar__glyph-desktop { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.lq-connect-store-avatar__glyph-phone { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.lq-connect-store-name { font-size: 13.5px; font-weight: 700; color: var(--lq-text); }
.lq-connect-store-name--small { font-size: 13px; }
.lq-connect-store-meta { font-family: var(--lq-font-mono); font-size: 11px; color: var(--lq-muted); }
.lq-connect-store-meta--small { font-size: 10.5px; }
.lq-connect-store-meta-text2 { font-family: var(--lq-font-mono); font-size: 11px; color: var(--lq-text-2); }
.lq-connect-store-meta-danger { font-family: var(--lq-font-mono); font-size: 11px; color: var(--lq-danger); }
.lq-connect-store-mono { font-family: var(--lq-font-mono); }
.lq-connect-store-mono-text { font-family: var(--lq-font-mono); color: var(--lq-text); }
.lq-connect-store-mono-text2 { font-family: var(--lq-font-mono); color: var(--lq-text-2); }

/* Connection state. Declines .lq-heartbeat: that base uses gap 11px and a 12px
   dot, these are gap 10px with 11px and 10px dots. */
.lq-connect-store-state { display: inline-flex; align-items: center; gap: 10px; }
.lq-connect-store-state--stacked { display: flex; align-items: center; gap: 9px; margin-top: 11px; }
.lq-connect-store-dot { border-radius: 50%; background: var(--lq-accent); box-shadow: 0 0 0 3px var(--lq-accent-soft); flex-shrink: 0; }
.lq-connect-store-dot--desktop { width: 11px; height: 11px; }
.lq-connect-store-dot--phone { width: 10px; height: 10px; }
.lq-connect-store-state__text { line-height: 1.3; }
.lq-connect-store-state__label { display: block; font-size: 12.5px; font-weight: 700; color: var(--lq-accent-text); }
.lq-connect-store-state__label--inline { font-size: 12px; }
.lq-connect-store-state__meta { display: block; font-size: 11px; color: var(--lq-muted); font-family: var(--lq-font-mono); }
.lq-connect-store-state__meta--inline { display: inline; }

/* Tier badge in the catalogue. Declines .lq-tier-badge: the shared base adds
   line-height:1, letter-spacing:.01em and white-space:nowrap, none of which this
   markup declares, and the first two change rendering. */
.lq-connect-store-badge { justify-self: start; display: inline-flex; align-items: center; gap: 7px; padding: 5px 12px; border-radius: var(--lq-r-sm); font-family: var(--lq-font-head); font-weight: 700; font-size: 12.5px; background: var(--lq-surface-2); border: 1px solid var(--lq-border-2); color: var(--lq-text-2); }
.lq-connect-store-badge__mark { width: 8px; height: 8px; border-radius: 2px; transform: rotate(45deg); background: var(--lq-muted); }

/* ---- Error / invalid states ---- */
.lq-connect-store-input-error { border-color: var(--lq-danger); box-shadow: 0 0 0 3px color-mix(in srgb, var(--lq-danger) 20%, transparent); outline: none; }
.lq-connect-store-code-focus { border-color: var(--lq-accent); box-shadow: var(--lq-ring); outline: none; }
.lq-connect-store-code-error { border-color: var(--lq-danger); box-shadow: 0 0 0 3px color-mix(in srgb, var(--lq-danger) 22%, transparent); outline: none; }
.lq-connect-store-field-error { font-size: 11px; color: var(--lq-danger); margin-top: 6px; }
.lq-connect-store-alert { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; border-radius: 9px; background: var(--lq-danger-soft); color: var(--lq-danger); }
.lq-connect-store-alert--after { margin-top: 10px; }
.lq-connect-store-alert--before { margin-bottom: 14px; }
.lq-connect-store-alert__glyph { width: 15px; height: 15px; flex-shrink: 0; margin-top: 1px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.lq-connect-store-alert__text { font-size: 12px; font-weight: 600; line-height: 1.5; }
.lq-connect-store-alert__text--pretty { text-wrap: pretty; }

/* ---- Summary cards + flow table ---- */
.lq-connect-store-card { background: var(--lq-surface); border: 1px solid var(--lq-border); border-radius: var(--lq-r); padding: 15px 16px; box-shadow: var(--lq-shadow-sm); }
.lq-connect-store-card--compact { padding: 13px 14px; box-shadow: none; }
.lq-connect-store-card--selected { border-color: var(--lq-accent); box-shadow: var(--lq-ring); }
.lq-connect-store-card__title { font-size: 12px; font-weight: 700; color: var(--lq-text); margin-bottom: 6px; }
.lq-connect-store-card__text { font-size: 11.5px; color: var(--lq-text-2); line-height: 1.55; text-wrap: pretty; }
.lq-connect-store-card__text--plain { line-height: 1.5; }
.lq-connect-store-cards { width: 100%; max-width: 980px; display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; }

.lq-connect-store-table { width: 100%; max-width: 980px; background: var(--lq-surface); border: 1px solid var(--lq-border); border-radius: var(--lq-r); overflow: hidden; box-shadow: var(--lq-shadow-sm); }
.lq-connect-store-table--flush { width: auto; max-width: none; }
.lq-connect-store-table__head { display: grid; grid-template-columns: 1.1fr 1.6fr 1.5fr; gap: 14px; padding: 11px 18px; border-bottom: 1px solid var(--lq-border); background: var(--lq-surface-2); }
.lq-connect-store-table__cell { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--lq-muted); }
.lq-connect-store-table__row { display: grid; grid-template-columns: 1.1fr 1.6fr 1.5fr; gap: 14px; padding: 12px 18px; border-top: 1px solid var(--lq-border); align-items: baseline; }
.lq-connect-store-table__text { font-size: 12px; color: var(--lq-text); }
.lq-connect-store-table__note { font-size: 11.5px; color: var(--lq-text-2); line-height: 1.5; }
.lq-connect-store-pick-row { display: grid; grid-template-columns: 2.4fr 1.5fr 1fr; gap: 16px; padding: 15px 20px; align-items: center; }
.lq-connect-store-pick-row--divided { border-top: 1px solid var(--lq-border); }
.lq-connect-store-pick-row--selected { border: 1px solid var(--lq-accent); border-radius: var(--lq-r); box-shadow: var(--lq-ring); }
.lq-connect-store-frame-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.lq-connect-store-frame-title { font-family: var(--lq-font-head); font-weight: 800; font-size: 19px; letter-spacing: -.015em; color: var(--lq-text); }
.lq-connect-store-frame-title--phone { font-size: 17px; }
.lq-connect-store-frame-sub { font-size: 12px; color: var(--lq-muted); margin-top: 3px; }
/* Showcase call to action: same treatment as the Stores toolbar's, base-primary
   fill and ink since CHANGELOG §I, border 1px transparent so the box holds. */
.lq-connect-store-frame-cta { display: inline-flex; align-items: center; gap: 7px; padding: 9px 14px; border-radius: 9px; border: 1px solid transparent; background: var(--lq-accent); color: var(--lq-accent-ink); font-family: var(--lq-font-body); font-weight: 600; font-size: 12.5px; cursor: pointer; white-space: nowrap; }
.lq-connect-store-frame-cta__glyph { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.lq-connect-store-hint { font-size: 11px; color: var(--lq-muted); }
.lq-connect-store-help-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 11px; }
/* Lucriq_Heartbeat — the specification page's variant stack. */
.lq-heartbeat-variants { display: flex; flex-direction: column; gap: 18px; }
/* Lucriq_Theme_Toggle — the specification page's control + note row. */
.lq-theme-toggle-row { display: flex; align-items: center; gap: 16px; }
/* Lucriq_Toast — the specification page's variant stack. */
.lq-toast-variants { display: flex; flex-direction: column; gap: 14px; }
/* Lucriq_Usage_Meter — the specification page's variant stack and the
   fixed measuring width each example meter is shown at. */
.lq-usage-meter-variants { display: flex; flex-direction: column; gap: 20px; }
.lq-usage-meter-example { width: 360px; max-width: 100%; }
/* Lucriq_Brand — specification page framing.
   The light swatch is deliberately theme-independent: it shows the
   wordmark on fixed brand white. Its three literals were literals in
   the inline markup and stay literals — swapping tokens in would make
   the swatch follow the theme and change what the dark page renders.
   The 12px radius is not one of them: var(--lq-r) resolves the same in
   both themes, so it is the token, not a literal. */
.lq-brand-variants { display: flex; flex-direction: column; gap: 20px; align-items: flex-start; }
.lq-brand-sample { height: 44px; }
.lq-brand-swatch { background: #FFFFFF; border: 1px solid #E2E8F0; border-radius: var(--lq-r); padding: 22px 26px; }
.lq-brand-swatch__sample { height: 40px; color: #0F172A; }
