/* ============================================================
   Self-hosted brand typefaces.

   `index.html` linked these from `fonts.googleapis.com`, which the edge's own
   Content-Security-Policy — `style-src 'self'; font-src 'self'`, no external origin
   permitted (deploy/edge/lucriq-security-headers.conf) — refuses to load. The shipped
   app therefore rendered every page in `system-ui`, never in Geist/DM Sans/JetBrains
   Mono, on every merchant's screen.

   Serving the same files from our own origin satisfies the policy without weakening it.
   Byte-identical to what Google serves, so the glyphs are the ones the design
   specifications were drawn with: fetched from the css2 URL `index.html` used, latin and
   latin-ext subsets (the only ones any Phase 0 copy needs).

   Regenerate by re-fetching that 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(/fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/fonts/geist-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/fonts/geist-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/fonts/geist-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/fonts/geist-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url(/fonts/geist-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url(/fonts/geist-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/fonts/jetbrains-mono-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/fonts/jetbrains-mono-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/fonts/jetbrains-mono-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/fonts/jetbrains-mono-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/fonts/jetbrains-mono-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/fonts/jetbrains-mono-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* ============================================================
   Lucriq SaaS — theme.css
   The ONLY place colors, type and global CSS live.
   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 (incl. imported child DCs)
   through CSS-variable cascade — no re-render, no prop drilling.
   Everything else in the app is inline-styled via var(--lq-*).
   ============================================================ */

: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;
  --lq-muted:  #64748B;

  /* accent — emerald = good / live / money */
  --lq-accent:        #10B981;
  --lq-accent-2:      #059669;
  --lq-accent-3:      #34D399;
  --lq-accent-ink:    #04231A;
  --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;
  --lq-muted:  #64748B;

  --lq-accent:        #10B981;
  --lq-accent-2:      #059669;
  --lq-accent-3:      #059669;
  --lq-accent-ink:    #FFFFFF;
  --lq-accent-text:   #047857;
  --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);
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--lq-bg);
  color: var(--lq-text);
  font-family: var(--lq-font-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* 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); }

/* 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)); } }

/* ---- 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-storehead        { display: none !important; }
  .lq-storerow         { display: flex !important; flex-direction: column !important; align-items: stretch !important; gap: 12px !important; }
  .lq-hide-mobile      { display: none !important; }
}

@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%); } }

/* ============================================================
   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; } }

/* 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-m:hover   { color: var(--lq-text); }
.lq-store-row:hover   { background: var(--lq-surface-3); }
.lq-btn-primary:hover { filter: brightness(1.06); }
.lq-input:focus       { border-color: var(--lq-accent); box-shadow: var(--lq-ring); }

/* Theme-toggle knob + icons follow [data-theme] (was JS-computed knobX / icon colors) */
.lq-tt-knob { transform: translateX(0); }
[data-theme="light"] .lq-tt-knob { transform: translateX(30px); }
.lq-tt-moon { stroke: var(--lq-accent-ink); }
[data-theme="light"] .lq-tt-moon { stroke: var(--lq-muted); }
.lq-tt-sun  { stroke: var(--lq-muted); }
[data-theme="light"] .lq-tt-sun  { stroke: var(--lq-accent-ink); }

/* 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); }

/* Inline link-style button (session revoke, etc.) */
.lq-linkbtn:hover { text-decoration: underline; }

/* ---- 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-pw-check { display: none; }
.js-lq-pw-rule.is-met { color: var(--lq-text-2) !important; }
.js-lq-pw-rule.is-met .lq-pw-ring { background: var(--lq-accent) !important; }
.js-lq-pw-rule.is-met .lq-pw-dot  { display: none; }
.js-lq-pw-rule.is-met .lq-pw-check { display: block; }

/* ============================================================
   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); }
.lq-btn-secondary:hover { background: var(--lq-surface-3); }

/* Keyboard focus ring inside dialogs (inputs keep their .lq-input:focus rule) */
.lq-modal__close:focus-visible,
.lq-btn-primary:focus-visible,
.lq-btn-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)); }
}

/* 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"] .cs-surface--desktop { display: contents; }
[data-lq-modal="connect-store"] .cs-surface--mobile  { display: none; }
@media (max-width: 899px) {
  [data-lq-modal="connect-store"] .cs-surface--desktop { display: none; }
  [data-lq-modal="connect-store"] .cs-surface--mobile  { display: contents; }
}

/* ---- Unlock-slot allocation (Plan & Usage) ---- */
/* Disabled action buttons (a not-due position renders its actions visible + disabled) */
.lq-btn-secondary:disabled, .lq-btn-primary:disabled { opacity: .5; cursor: not-allowed; }
.lq-btn-secondary:disabled:hover { background: var(--lq-surface); }
.lq-btn-primary:disabled:hover { filter: none; }
/* Picker rows behave like selectable controls */
.lq-slot-pick:hover { background: var(--lq-surface-3); }
/* 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); }
/* 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%; }
}

/* ---- 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; }
/* ============================================================
   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-side {
  display: flex;
}

.lq-tabs {
  display: none;
}

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

  .lq-side {
    display: none;
  }

  .lq-tabs {
    display: grid;
  }
}
/* ============================================================
   Accessibility overrides — layered AFTER theme.css.

   LUCRIQ-SAAS-UI.md delegates visual presentation to `../design/saas/`, but that delegation
   "cannot authorize or redefine behavior, controls, feature scope, copy contracts, data
   contracts, security, ACCESSIBILITY, phase unlocks, navigation, or which pages exist". Where a
   specification conflicts with the accessibility contract the owner contract wins, and the
   deviation is recorded — see repository-root DESIGN-DRIFTS.md.

   These live in a separate file rather than as edits to `theme.css` on purpose: that file is
   vendored byte-identical from `design/saas/assets/css/theme.css` and is verifiable as such, so
   editing it in place would destroy the one property that makes token drift detectable.
   ============================================================ */

/* `--lq-muted` is #64748B in BOTH themes, which the browser axe run measured at 3.75:1 on the
   dark ground and 4.23:1 on the light one. WCAG 2 AA requires 4.5:1 for normal-size text, and the
   token is used for real copy (the auth footer, session location lines, slot change dates), not
   for decoration. Raised on each ground to the nearest value that clears the threshold while
   staying recognisably the same muted step in the palette. */
:root,
[data-theme="dark"] {
  --lq-muted: #94a3b8;
}

[data-theme="light"] {
  --lq-muted: #475569;
}

/* `.lq-linkbtn` carries only a `:hover` rule in theme.css — it was written for the "inline
   link-style BUTTON (session revoke, etc.)" named in its own comment, and a <button> inherits its
   parent's colour. Applying the class to a react-router <Link>, which renders an <a href>, leaves
   the anchor on the user-agent default #0000EE: the browser axe run measured 1.75:1 against the
   card surface. Nothing in jsdom could see it, because jsdom applies no UA stylesheet.

   Given an explicit colour here, the class works for both element types. Accent text rather than
   body text so the control still reads as interactive without relying on colour alone — the
   underline on hover and focus carries that too. */
.lq-linkbtn,
a.lq-linkbtn:link,
a.lq-linkbtn:visited {
  color: var(--lq-accent-text);
}

/* `theme.css` declares seven keyframe animations and applies them unconditionally — the auth card's
   `lq-pop` entrance, the modal's `lq-fadein`/`lq-pop`, the status pulses. None is guarded by
   `prefers-reduced-motion`, so a merchant who has asked their operating system for reduced motion
   still gets every one of them.

   `lq-pop` and `lq-fadein` animate opacity from 0, which additionally makes the request measurable:
   while the auth card is mid-fade its text is composited against the ground at partial alpha, and
   `--lq-text-2` on the signup card measures 2.74:1 at opacity .4 against a 4.5:1 requirement. Copy
   that is briefly unreadable is exactly what the setting exists to prevent.

   This is the merchant-facing half only. It does NOT make the browser suite deterministic —
   `playwright.config.ts` sets `reducedMotion: "reduce"`, but that context option does not reach the
   page in Playwright 1.62.1 (`matchMedia("(prefers-reduced-motion: reduce)").matches` is `false`
   under it; only an explicit `page.emulateMedia()` flips it). The suite settles animations itself;
   see `settleAnimations` in `e2e/a11y.browser.spec.ts`. */
@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;
  }
}

/* `--lq-accent-text` is the foreground for accent-tinted surfaces — the active navigation item,
   the active account tab, verified-state copy. In the light theme it is #047857 on the
   `--lq-accent-soft` ground #D6ECEA, which the browser axe run measured at 4.45:1 against a 4.5:1
   requirement. Failing by 0.05 is still failing, and it is the one state a merchant looks at most,
   since it marks where they are.

   Darkened one step in light only; the dark theme's value already clears the threshold
   comfortably and is left alone. */
[data-theme="light"] {
  --lq-accent-text: #065f46;
}
