/* ============================================================
   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;
  --lq-warn-text:   #B45309;
  --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)); } }

/* ============================================================
   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);
}
.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; }
/* ============================================================
   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;
  display: none;                 /* support.js flips to display:grid to open */
  place-items: center;
  padding: 32px 20px;
  background: var(--lq-scrim);
  overflow-y: auto;
  animation: lq-fadein .16s ease;
}
.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; }
/* ---- 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; }

/* 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; }
.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; }
.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;
}
.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) ----
   Note: the markup declares no display on the bar itself, only
   align-items. Reproduced as-is; changing it would change layout. */
.lq-shell__tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  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; }
/* Monospaced account identity inside a subheading or note. */
.lq-auth__identity { font-family: var(--lq-font-mono); color: var(--lq-text-2); }
.lq-auth__subheading--loose { line-height: 1.6; }
.lq-auth__card-foot--tight { margin-top: 16px; }
/* 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); }
/* ============================================================
   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; }
.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); 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); }
.lq-plan-ladder__note { margin-top: 8px; font-size: 11px; color: var(--lq-muted); line-height: 1.5; }
/* The disclosure panel's own footnote sits 10px below its chips, not 8px.
   These two were briefly mapped onto one class and the 2px loss reflowed the
   panel; kept separate deliberately. */
.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); }
.lq-account-link { color: var(--lq-accent-text); text-decoration: none; }
/* 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; }
/* 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; }
/* ============================================================
   Authenticated shell layout.

   These rules lived in a `<style>` element rendered by `AppShell.tsx`. The edge enforces
   `style-src 'self'` with no external origin and no `'unsafe-inline'` permitted
   (deploy/edge/lucriq-security-headers.conf), and that directive governs `<style>` *elements* as
   `style-src-elem` — so the browser blocked the whole block on every authenticated page load and
   none of it ever applied in production.

   What that broke, silently: `.lq-app` never became a grid, so the sidebar and the main column
   stacked instead of sitting side by side; `.lq-tabs { display: none }` never applied, so the
   mobile bottom tab bar rendered on desktop as well; and the 899px switch between the two never
   happened at any width. The shell rendered, so nothing crashed and no test noticed — the stubbed
   suites run against the Vite preview and the jsdom suites have no layout at all.

   As a stylesheet imported through the bundle it is served from our own origin and satisfies the
   policy unchanged. The breakpoint is written out rather than interpolated from a TS constant,
   which is the price of the CSP being honoured; it is the same 899px `theme.css` already uses for
   `.lq-only-mobile` / `.lq-only-desktop`, and the two must agree.

   React inline `style` props are unaffected — those are CSSOM property writes, not style
   attributes, and CSP does not govern them.
   ============================================================ */

.lq-app {
  display: grid;
  grid-template-columns: 232px 1fr;
  min-height: 100vh;
}

.lq-sidebar {
  display: flex;
}

.lq-tabs {
  display: none;
}

@media (max-width: 899px) {
  .lq-app {
    grid-template-columns: 1fr;
  }

  .lq-sidebar {
    display: none;
  }

  .lq-tabs {
    display: grid;
    /* Device chrome clearance, matching the design tree's own `.lq-shell__tabbar`. The bar is
       `position: fixed; bottom: 0`, so without this the home indicator overlaps the tab labels
       themselves. It composes with the `.lq-shell-main` clearance rather than competing with it:
       that rule is `calc(86px + env(safe-area-inset-bottom, 0px))`, which already accounts for
       the inset separately from the 86px, so the bar growing by the inset and the clearance
       growing by the inset cancel. Playwright resolves the inset to zero, so this is invisible to
       the visual suite in either direction. */
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
}
