/* Aiopta Store Frontend — Design-System styling (issue #460).
 *
 * The public storefront is a standalone static SPA served by nginx and does
 * NOT import @aiopta/ui/styles.css, so the Design-System token layer is
 * inlined here. The dark-default token VALUES are synced to the newest DS —
 * the redesign-2026 SIGNAL palette (redesign-2026/tokens.css +
 * ui_kits/addon-store/store.css from the design-system handoff): violet accent
 * (#7859f7), deep blue-black surfaces (#090d17 / #0d1320), cool-tinted
 * hairlines. The --aiopta-color-* → --ds-* ABI names are kept stable so all
 * component rules below consume the --ds-* ABI unchanged: adopting a newer DS
 * value means pasting the token value here, never re-deriving a shade or
 * touching a component rule.
 *
 * PACK-ADAPTIVITY (canon 2026-07-20) — documented exception: this store is a
 * STANDALONE static site served by nginx (Dockerfile.marketplace, /store/),
 * separate from the instance runtime; it cannot import @aiopta/ui or observe a
 * runtime-applied design-pack (that is phase 3). Per owner directive it
 * consumes the DS tokens STATICALLY (default SIGNAL design) — so the values are
 * compiled in here. The pack-ADAPTIVE store is the IN-APP store (store.jsx,
 * phase 4), which re-themes off the active pack at runtime and is out of scope
 * for this file. Forward path so hand-copies are not the end-state: generate
 * this token layer from the pack/token source (DTCG export) at build time
 * instead of pasting — TODO(build: sync tokens from redesign-2026/tokens.css).
 * A value drift-guard test pins the current values meanwhile
 * (build-store-frontend.test.cjs).
 *
 * 2026-07-21 gate follow-up: the CI "Pack-Swap" gate wants an alternative
 * token set to visibly change BOTH color and layout. Color already does
 * (theme + [data-color-scheme] accent schemes below); layout (grid
 * columns/radii/spacing scale) does not, because — per the paragraph
 * above — this static build has no pack-loader to source an alternative
 * layout from. Tracked as addons#840 (architecture decision needed, not
 * a fix to improvise here).
 *
 * Theme/scheme aware: dark is the :root default (matches <meta color-scheme>);
 * light applies via [data-theme="light"] AND, when the host sets no explicit
 * theme, via prefers-color-scheme. Curated accent schemes recolour ONLY the
 * accent channel via [data-color-scheme="..."] (ADR-045). The embed host
 * (core#517) may set these attributes on <html>; we react in pure CSS and do
 * NOT touch the postMessage handshake. Mobile-first: fluid grid + a <=720px
 * layout collapse + 44px DS touch targets. */

/* === DS token ABI — --aiopta-* source values + --ds-* alias layer ======== */
:root {
  /* Background / surface (DS dark default — redesign-2026 SIGNAL palette,
     synced from redesign-2026/tokens.css + ui_kits/addon-store/store.css).
     Full 4-step ladder matches tokens.css: bg-0 #05070d is the true page
     background (the prototype's <body> base), bg-1 #090d17 the header/nav
     translucency, bg-2 #0e1420 the modal surface, bg-3 #141b2b the elevated
     step. (#838 started the ladder one step too light at #090d17.) */
  --aiopta-color-bg-primary: #05070d;
  --aiopta-color-bg-secondary: #090d17;
  --aiopta-color-bg-tertiary: #0e1420;
  --aiopta-color-bg-elevated: #141b2b;
  --aiopta-color-surface: #0d1320;
  --aiopta-color-surface-hover: #131a2a;
  --aiopta-color-surface-active: #1a2236;
  --aiopta-color-surface-sunken: #080c15;
  /* Text (redesign-2026 signal foreground) */
  --aiopta-color-text-primary: rgba(233, 239, 253, 0.95);
  --aiopta-color-text-secondary: rgba(233, 239, 253, 0.64);
  --aiopta-color-text-tertiary: rgba(233, 239, 253, 0.42);
  --aiopta-color-text-inverse: #070a12;
  /* Border (redesign-2026 cool-tinted hairlines) */
  --aiopta-color-border: rgba(151, 173, 233, 0.10);
  --aiopta-color-border-hover: rgba(151, 173, 233, 0.20);
  --aiopta-color-border-focus: rgba(124, 92, 255, 0.55);
  /* Accent (redesign-2026 SIGNAL violet) */
  --aiopta-color-accent: #7859f7;
  --aiopta-color-accent-hover: #927aff;
  --aiopta-color-accent-active: #6847ea;
  --aiopta-color-accent-muted: rgba(124, 92, 255, 0.16);
  --aiopta-color-accent-subtle: rgba(124, 92, 255, 0.08);
  --aiopta-color-accent-text: #a695ff;
  /* Signal channel (redesign-2026 cyan→violet signature — the sigline bar,
     brandmark tile and progress rails). Fixed brand hues by design; the
     accent channel above still re-themes per scheme. */
  --aiopta-color-signal-a: #37e6ff;
  --aiopta-color-signal-b: #7c5cff;
  /* Semantic (redesign-2026 signal) */
  --aiopta-color-success: #3ddc97;
  --aiopta-color-success-muted: rgba(61, 220, 151, 0.14);
  --aiopta-color-warning: #ffb454;
  --aiopta-color-warning-muted: rgba(255, 180, 84, 0.14);
  --aiopta-color-error: #ff5c69;
  --aiopta-color-error-muted: rgba(255, 92, 105, 0.14);
  --aiopta-color-info: #4da3ff;
  /* Overlay (modal backdrop scrim — theme-invariant by design, dims the
     page behind a modal the same way regardless of light/dark). */
  --aiopta-color-bg-overlay: rgba(6, 6, 12, 0.68);
  /* Typography (redesign-2026 faces first, system fallback). The faces are
     NOT network-loaded — the strict marketplace CSP forbids external fonts
     and no OFL woff2 is bundled yet, so these render only where the OS has
     them and fall back gracefully otherwise. Self-hosting the woff2 (CSP-
     and airgap-safe) is the follow-up to fully realise the display face. */
  --aiopta-font-display: "Space Grotesk", "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --aiopta-font-sans: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --aiopta-font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  /* Spacing */
  --aiopta-space-1: 4px;  --aiopta-space-2: 8px;  --aiopta-space-3: 12px;
  --aiopta-space-4: 16px; --aiopta-space-5: 20px; --aiopta-space-6: 24px;
  --aiopta-space-8: 32px; --aiopta-space-10: 40px; --aiopta-space-12: 48px;
  --aiopta-space-16: 64px; --aiopta-space-20: 80px;
  /* Radius (redesign-2026 SIGNAL band — synced from tokens.css: the store
     was on the tighter #441 band [8/12/16/22]; the prototype is rounder). */
  --aiopta-radius-xs: 6px;  --aiopta-radius-sm: 10px; --aiopta-radius-md: 14px;
  --aiopta-radius-lg: 20px; --aiopta-radius-xl: 28px; --aiopta-radius-full: 9999px;
  /* Card inner padding (tokens.css layer-3 --aiopta-card-pad). */
  --aiopta-card-pad: 18px;
  /* Shadow */
  --aiopta-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
  --aiopta-shadow-md: 0 6px 16px rgba(0, 0, 0, 0.38);
  --aiopta-shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.46);
  --aiopta-shadow-glow: 0 0 0 1px var(--aiopta-color-accent-muted), 0 0 24px rgba(124, 92, 255, 0.28);
  /* Motion */
  --aiopta-duration-fast: 120ms;
  --aiopta-duration-normal: 200ms;
  --aiopta-ease-default: cubic-bezier(0.25, 0.1, 0.25, 1);
  --aiopta-ease-out: cubic-bezier(0, 0, 0.2, 1);
  /* Touch + layout */
  --aiopta-touch-target: 44px;
  --aiopta-content-padding-x: 1.25rem;

  /* --ds-* ABI aliases (generated layer, ds-tokens.generated.css). Backed by
     the --aiopta-* ABI so they stay theme/scheme-aware automatically. */
  --ds-bg-0: var(--aiopta-color-bg-primary);
  --ds-bg-1: var(--aiopta-color-bg-secondary);
  --ds-bg-2: var(--aiopta-color-bg-tertiary);
  --ds-bg-3: var(--aiopta-color-bg-elevated);
  --ds-surface: var(--aiopta-color-surface);
  --ds-surface-hover: var(--aiopta-color-surface-hover);
  --ds-surface-active: var(--aiopta-color-surface-active);
  --ds-surface-sunken: var(--aiopta-color-surface-sunken);
  --ds-text-1: var(--aiopta-color-text-primary);
  --ds-text-2: var(--aiopta-color-text-secondary);
  --ds-text-3: var(--aiopta-color-text-tertiary);
  --ds-text-inverse: var(--aiopta-color-text-inverse);
  --ds-border: var(--aiopta-color-border);
  --ds-border-strong: var(--aiopta-color-border-hover);
  --ds-border-focus: var(--aiopta-color-border-focus);
  --ds-accent: var(--aiopta-color-accent);
  --ds-accent-hover: var(--aiopta-color-accent-hover);
  --ds-accent-active: var(--aiopta-color-accent-active);
  --ds-accent-muted: var(--aiopta-color-accent-muted);
  --ds-accent-subtle: var(--aiopta-color-accent-subtle);
  --ds-accent-on: #ffffff;
  --ds-accent-text: var(--aiopta-color-accent-text);
  --ds-signal-a: var(--aiopta-color-signal-a);
  --ds-signal-b: var(--aiopta-color-signal-b);
  --ds-signal: linear-gradient(100deg, var(--ds-signal-a), var(--ds-signal-b));
  --ds-success: var(--aiopta-color-success);
  --ds-success-muted: var(--aiopta-color-success-muted);
  --ds-warning: var(--aiopta-color-warning);
  --ds-warning-muted: var(--aiopta-color-warning-muted);
  --ds-error: var(--aiopta-color-error);
  --ds-error-muted: var(--aiopta-color-error-muted);
  --ds-info: var(--aiopta-color-info);
  --ds-overlay: var(--aiopta-color-bg-overlay);
  --ds-font-sans: var(--aiopta-font-sans);
  --ds-font-display: var(--aiopta-font-display);
  --ds-font-mono: var(--aiopta-font-mono);
  --ds-radius-xs: var(--aiopta-radius-xs);
  --ds-radius-sm: var(--aiopta-radius-sm);
  --ds-radius-md: var(--aiopta-radius-md);
  --ds-radius-lg: var(--aiopta-radius-lg);
  --ds-radius-xl: var(--aiopta-radius-xl);
  --ds-radius-full: var(--aiopta-radius-full);
  --ds-card-pad: var(--aiopta-card-pad);
  --ds-shadow-sm: var(--aiopta-shadow-sm);
  --ds-shadow-md: var(--aiopta-shadow-md);
  --ds-shadow-lg: var(--aiopta-shadow-lg);
  --ds-glow-accent: var(--aiopta-shadow-glow);
  --ds-duration-fast: var(--aiopta-duration-fast);
  --ds-duration-normal: var(--aiopta-duration-normal);
  --ds-ease-default: var(--aiopta-ease-default);
  --ds-ease-out: var(--aiopta-ease-out);
  --ds-touch-target: var(--aiopta-touch-target);
  /* Store-scoped: catalog reading width stays tighter than the app shell. */
  --store-max-width: 1180px;

  /* Category tint slots (Marketplace Gestalt — „farbige Icons pro Addon").
     Every value derives from the DS hue channels (accent/info/success/
     warning/error) directly or via color-mix — NO raw hex here, so the
     tints re-theme automatically with light mode + accent schemes. The
     slot number is assigned at build time (sectionTint) and shipped on
     the projected addon as `tint`. */
  --aiopta-tint-1-fg: var(--aiopta-color-accent-hover);
  --aiopta-tint-1-bg: var(--aiopta-color-accent-muted);
  --aiopta-tint-2-fg: var(--aiopta-color-info);
  --aiopta-tint-2-bg: color-mix(in srgb, var(--aiopta-color-info) 15%, transparent);
  --aiopta-tint-3-fg: var(--aiopta-color-success);
  --aiopta-tint-3-bg: var(--aiopta-color-success-muted);
  --aiopta-tint-4-fg: var(--aiopta-color-warning);
  --aiopta-tint-4-bg: var(--aiopta-color-warning-muted);
  --aiopta-tint-5-fg: var(--aiopta-color-error);
  --aiopta-tint-5-bg: var(--aiopta-color-error-muted);
  --aiopta-tint-6-fg: color-mix(in srgb, var(--aiopta-color-info) 45%, var(--aiopta-color-success) 55%);
  --aiopta-tint-6-bg: color-mix(in srgb, var(--aiopta-tint-6-fg) 15%, transparent);
  --aiopta-tint-7-fg: color-mix(in srgb, var(--aiopta-color-warning) 55%, var(--aiopta-color-error) 45%);
  --aiopta-tint-7-bg: color-mix(in srgb, var(--aiopta-tint-7-fg) 15%, transparent);
  --aiopta-tint-8-fg: color-mix(in srgb, var(--aiopta-color-accent) 55%, var(--aiopta-color-error) 45%);
  --aiopta-tint-8-bg: color-mix(in srgb, var(--aiopta-tint-8-fg) 15%, transparent);
  --ds-tint-1-fg: var(--aiopta-tint-1-fg); --ds-tint-1-bg: var(--aiopta-tint-1-bg);
  --ds-tint-2-fg: var(--aiopta-tint-2-fg); --ds-tint-2-bg: var(--aiopta-tint-2-bg);
  --ds-tint-3-fg: var(--aiopta-tint-3-fg); --ds-tint-3-bg: var(--aiopta-tint-3-bg);
  --ds-tint-4-fg: var(--aiopta-tint-4-fg); --ds-tint-4-bg: var(--aiopta-tint-4-bg);
  --ds-tint-5-fg: var(--aiopta-tint-5-fg); --ds-tint-5-bg: var(--aiopta-tint-5-bg);
  --ds-tint-6-fg: var(--aiopta-tint-6-fg); --ds-tint-6-bg: var(--aiopta-tint-6-bg);
  --ds-tint-7-fg: var(--aiopta-tint-7-fg); --ds-tint-7-bg: var(--aiopta-tint-7-bg);
  --ds-tint-8-fg: var(--aiopta-tint-8-fg); --ds-tint-8-bg: var(--aiopta-tint-8-bg);
}

/* Light theme — DS values (independently tuned, not inverted dark). Applied
   both on explicit opt-in and, when the host sets no theme, by OS preference. */
[data-theme="light"] {
  /* redesign-2026 SIGNAL light (independently tuned, not inverted). */
  --aiopta-color-bg-primary: #eef1f8;
  --aiopta-color-bg-secondary: #f4f6fb;
  --aiopta-color-bg-tertiary: #ffffff;
  --aiopta-color-bg-elevated: #e6eaf4;
  --aiopta-color-surface: #ffffff;
  --aiopta-color-surface-hover: #f2f4fa;
  --aiopta-color-surface-active: #e8ebf5;
  --aiopta-color-surface-sunken: #e9edf6;
  --aiopta-color-text-primary: rgba(14, 18, 32, 0.92);
  --aiopta-color-text-secondary: rgba(14, 18, 32, 0.60);
  --aiopta-color-text-tertiary: rgba(14, 18, 32, 0.46);
  --aiopta-color-text-inverse: #ffffff;
  --aiopta-color-border: rgba(14, 18, 32, 0.09);
  --aiopta-color-border-hover: rgba(14, 18, 32, 0.18);
  --aiopta-color-border-focus: rgba(98, 71, 229, 0.5);
  --aiopta-color-accent: #6247e5;
  --aiopta-color-accent-hover: #7c5cff;
  --aiopta-color-accent-active: #4f36c9;
  --aiopta-color-accent-muted: rgba(98, 71, 229, 0.13);
  --aiopta-color-accent-subtle: rgba(98, 71, 229, 0.07);
  --aiopta-color-accent-text: #4f36c9;
  --aiopta-color-signal-a: #08a8c9;
  --aiopta-color-signal-b: #6247e5;
  --aiopta-color-success: #128a52;
  --aiopta-color-success-muted: rgba(18, 138, 82, 0.13);
  --aiopta-color-warning: #9c6408;
  --aiopta-color-warning-muted: rgba(156, 100, 8, 0.13);
  --aiopta-color-error: #d33641;
  --aiopta-color-error-muted: rgba(211, 54, 65, 0.13);
  --aiopta-color-info: #1c6fd6;
  --aiopta-shadow-sm: 0 1px 2px rgba(20, 24, 40, 0.08);
  --aiopta-shadow-md: 0 4px 16px -4px rgba(20, 24, 40, 0.12);
  --aiopta-shadow-lg: 0 16px 48px -12px rgba(20, 24, 40, 0.18);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]):not([data-theme="light"]) {
    --aiopta-color-bg-primary: #eef1f8;
    --aiopta-color-bg-secondary: #f4f6fb;
    --aiopta-color-bg-tertiary: #ffffff;
    --aiopta-color-bg-elevated: #e6eaf4;
    --aiopta-color-surface: #ffffff;
    --aiopta-color-surface-hover: #f2f4fa;
    --aiopta-color-surface-active: #e8ebf5;
    --aiopta-color-surface-sunken: #e9edf6;
    --aiopta-color-text-primary: rgba(14, 18, 32, 0.92);
    --aiopta-color-text-secondary: rgba(14, 18, 32, 0.60);
    --aiopta-color-text-tertiary: rgba(14, 18, 32, 0.46);
    --aiopta-color-text-inverse: #ffffff;
    --aiopta-color-border: rgba(14, 18, 32, 0.09);
    --aiopta-color-border-hover: rgba(14, 18, 32, 0.18);
    --aiopta-color-border-focus: rgba(98, 71, 229, 0.5);
    --aiopta-color-accent: #6247e5;
    --aiopta-color-accent-hover: #7c5cff;
    --aiopta-color-accent-active: #4f36c9;
    --aiopta-color-accent-muted: rgba(98, 71, 229, 0.13);
    --aiopta-color-accent-subtle: rgba(98, 71, 229, 0.07);
    --aiopta-color-accent-text: #4f36c9;
    --aiopta-color-signal-a: #08a8c9;
    --aiopta-color-signal-b: #6247e5;
    --aiopta-color-success: #128a52;
    --aiopta-color-warning: #9c6408;
    --aiopta-color-error: #d33641;
    --aiopta-color-info: #1c6fd6;
    --aiopta-shadow-sm: 0 1px 2px rgba(20, 24, 40, 0.08);
    --aiopta-shadow-md: 0 4px 16px -4px rgba(20, 24, 40, 0.12);
    --aiopta-shadow-lg: 0 16px 48px -12px rgba(20, 24, 40, 0.18);
  }
}

/* Curated accent schemes (ADR-045) — accent channel only; status colours are
   scheme-stable by design. The host embed sets [data-color-scheme]. */
[data-color-scheme="ocean"] {
  --aiopta-color-accent: #0ea5e9; --aiopta-color-accent-hover: #38bdf8;
  --aiopta-color-accent-active: #0284c7; --aiopta-color-accent-muted: rgba(14, 165, 233, 0.16);
  --aiopta-color-accent-subtle: rgba(14, 165, 233, 0.08); --aiopta-color-border-focus: rgba(14, 165, 233, 0.5);
}
[data-color-scheme="ocean"][data-theme="light"] {
  --aiopta-color-accent: #027ab0; --aiopta-color-accent-hover: #0ea5e9; --aiopta-color-accent-active: #015c85;
  --aiopta-color-accent-muted: rgba(2, 122, 176, 0.13); --aiopta-color-accent-subtle: rgba(2, 122, 176, 0.07);
}
[data-color-scheme="forest"] {
  --aiopta-color-accent: #17b878; --aiopta-color-accent-hover: #34d399;
  --aiopta-color-accent-active: #0f9d63; --aiopta-color-accent-muted: rgba(23, 184, 120, 0.16);
  --aiopta-color-accent-subtle: rgba(23, 184, 120, 0.08); --aiopta-color-border-focus: rgba(23, 184, 120, 0.5);
}
[data-color-scheme="sunset"] {
  --aiopta-color-accent: #f9772f; --aiopta-color-accent-hover: #fb923c;
  --aiopta-color-accent-active: #ea580c; --aiopta-color-accent-muted: rgba(249, 119, 47, 0.16);
  --aiopta-color-accent-subtle: rgba(249, 119, 47, 0.08); --aiopta-color-border-focus: rgba(249, 119, 47, 0.5);
}
[data-color-scheme="mono"] {
  --aiopta-color-accent: #9aa4b2; --aiopta-color-accent-hover: #cbd5e1;
  --aiopta-color-accent-active: #78828f; --aiopta-color-accent-muted: rgba(154, 164, 178, 0.16);
  --aiopta-color-accent-subtle: rgba(154, 164, 178, 0.08); --aiopta-color-border-focus: rgba(154, 164, 178, 0.5);
}

/* === base ================================================================ */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: var(--ds-bg-0); color: var(--ds-text-1);
  font-family: var(--ds-font-sans); font-size: 15px; line-height: 1.5;
  -webkit-font-smoothing: antialiased; }
body { position: relative; }
/* Ambient signal wash (prototype body::before): two soft radial glows in
   the accent + signal-cyan channels. Token-only via color-mix — sits behind
   all content (z-index:-1) so it tints the page without touching hit-testing. */
body::before { content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(900px 480px at 80% -10%, color-mix(in srgb, var(--ds-accent) 14%, transparent), transparent 60%),
    radial-gradient(700px 400px at 0% 0%, color-mix(in srgb, var(--ds-signal-a) 8%, transparent), transparent 60%); }
/* Display face for headings + brand (redesign-2026 Space Grotesk). */
h1, h2, h3, .brandmark, .topbar .brand-text b, .card .name { font-family: var(--ds-font-display); }
a { color: var(--ds-accent); text-decoration: none; }
a:hover { color: var(--ds-accent-hover); text-decoration: underline; }
img { max-width: 100%; height: auto; }
button, input, select { font: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--ds-border-focus); outline-offset: 2px; border-radius: var(--ds-radius-sm); }

/* === layout ============================================================== */
.container { max-width: var(--store-max-width); margin: 0 auto; padding: 0 var(--aiopta-content-padding-x); }
.site-header { padding: var(--aiopta-space-8) 0 var(--aiopta-space-4); border-bottom: 1px solid var(--ds-border);
  background: linear-gradient(180deg, var(--ds-bg-1) 0%, var(--ds-bg-0) 100%); }
.site-header h1 { margin: 0 0 var(--aiopta-space-1); font-size: 1.75rem; font-weight: 650; letter-spacing: -0.01em; color: var(--ds-text-1); }
.site-header .subtitle { margin: 0; color: var(--ds-text-2); font-size: 0.9375rem; }
.site-header .meta { margin: var(--aiopta-space-2) 0 0; color: var(--ds-text-3); font-size: 0.75rem; }
.site-header .meta code { font-family: var(--ds-font-mono); color: var(--ds-text-2); }
.site-footer { padding: var(--aiopta-space-6) 0; border-top: 1px solid var(--ds-border); color: var(--ds-text-3);
  font-size: 0.75rem; text-align: center; }
.site-footer code { font-family: var(--ds-font-mono); color: var(--ds-text-2); }

/* === topbar (Marketplace Gestalt — brandmark + tabs + who) ============== */
/* Prototype parity: sticky translucent header with brandmark tile, title
   over subline, Store/Konto tabs and the sign-in area right-aligned. */
.topbar { position: sticky; top: 0; z-index: 40; border-bottom: 1px solid var(--ds-border);
  background: color-mix(in srgb, var(--ds-bg-0) 82%, transparent);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); }
.topbar-in { max-width: var(--store-max-width); margin: 0 auto; display: flex; align-items: center;
  gap: var(--aiopta-space-3); padding: var(--aiopta-space-3) var(--aiopta-content-padding-x); }
.brandmark { width: 36px; height: 36px; border-radius: 10px; flex: none; display: grid; place-items: center;
  font-weight: 700; font-size: 1.0625rem; color: var(--ds-accent-on);
  background: var(--ds-signal); }
.topbar .brand-text { min-width: 0; }
.topbar .brand-text b { display: block; font-size: 1rem; font-weight: 700; letter-spacing: -0.01em;
  color: var(--ds-text-1); white-space: nowrap; }
.topbar .brand-sub { color: var(--ds-text-3); font-size: 0.6875rem; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.topnav { display: flex; gap: 4px; margin-left: var(--aiopta-space-2); }
.topnav a { height: 36px; padding: 0 13px; border-radius: var(--ds-radius-full); display: inline-flex;
  align-items: center; font-weight: 600; font-size: 0.8125rem; color: var(--ds-text-2); white-space: nowrap;
  transition: background var(--ds-duration-fast) var(--ds-ease-out), color var(--ds-duration-fast) var(--ds-ease-out); }
.topnav a:hover { background: var(--ds-surface-hover); color: var(--ds-text-1); text-decoration: none; }
.topnav a[aria-current="page"] { background: var(--ds-accent-muted); color: var(--ds-accent-hover); }
.topbar .auth-bar { margin-left: auto; margin-top: 0; }
/* Auth cluster (who label + sign-in/out) — a flex row that WRAPS, shared by
   the topbar and the legacy site-header. The topbar rules for .who/.btn-link
   used to be scoped to .site-header only, so inside the topbar the button and
   the "Angemeldet als …" label had no flex/gap and could overlap the brand
   title on narrow screens (Owner mobile bug, 430px). Unscoped here so the
   cluster is a proper gapped, wrapping row everywhere. */
.auth-bar { display: flex; align-items: center; gap: var(--aiopta-space-2); flex-wrap: wrap; min-width: 0; }
.auth-bar .who { color: var(--ds-text-2); font-size: 0.8125rem; min-width: 0; overflow-wrap: anywhere; }
.auth-bar .who b { color: var(--ds-text-1); }
.auth-bar .btn-link { background: transparent; color: var(--ds-accent); border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-full); padding: var(--aiopta-space-1) var(--aiopta-space-3); cursor: pointer;
  font-size: 0.8125rem; font-family: inherit; min-height: 36px; white-space: nowrap;
  transition: border-color var(--ds-duration-fast) var(--ds-ease-out), background var(--ds-duration-fast) var(--ds-ease-out); }
.auth-bar .btn-link:hover { border-color: var(--ds-accent); background: var(--ds-accent-subtle); }
.auth-bar .btn-link:focus-visible { outline: 2px solid var(--ds-border-focus); outline-offset: 2px; }

/* Badges (prototype .badge family) — token-only pills for owned/price. */
.badge { display: inline-flex; align-items: center; gap: 5px; height: 22px; padding: 0 9px;
  border-radius: var(--ds-radius-full); font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.02em; }
.badge.badge-ok { background: var(--ds-success-muted); color: var(--ds-success); }
.badge.badge-acc { background: var(--ds-accent-muted); color: var(--ds-accent-text); }
.badge.badge-mut { background: var(--ds-surface-active); color: var(--ds-text-2); }
.badge.badge-warn { background: var(--ds-warning-muted); color: var(--ds-warning); }
.badge.badge-err { background: var(--ds-error-muted); color: var(--ds-error); }
.badge[hidden] { display: none; }

/* Mobile bottom nav (prototype .mbnav — App-Feel wie Hauptanwendung). */
.mbnav { display: none; }
@media (max-width: 860px) {
  .topnav { display: none; }
  .topbar .brand-sub { display: none; }
  /* Wrap the topbar so the auth cluster drops onto its OWN full-width row,
     right-aligned, instead of colliding with the brand title (Owner 430px
     bug: "Angemeldet als …" + Abmelden overlapping the row title). */
  .topbar-in { flex-wrap: wrap; row-gap: var(--aiopta-space-2); }
  .topbar .brand-text { flex: 1 1 auto; }
  .topbar .auth-bar { width: 100%; margin-left: 0; justify-content: flex-end; }
  body { padding-bottom: 76px; }
  .mbnav { display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
    background: color-mix(in srgb, var(--ds-bg-1) 94%, transparent);
    -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
    border-top: 1px solid var(--ds-border);
    padding: 6px 8px calc(6px + env(safe-area-inset-bottom, 0px)); }
  .mbnav a { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px;
    color: var(--ds-text-3); font-weight: 600; font-size: 0.6875rem; padding: 6px 0;
    border-radius: var(--ds-radius-md); min-height: var(--ds-touch-target); justify-content: center; }
  .mbnav a:hover { text-decoration: none; }
  .mbnav a[aria-current="page"] { color: var(--ds-accent-hover); background: var(--ds-accent-muted); }
  .mbnav .mbi svg { width: 18px; height: 18px; display: block; }
}

/* === toolbar (search + category/author/pricing filters) ================= */
.toolbar { display: flex; flex-wrap: wrap; gap: var(--aiopta-space-3); align-items: center;
  padding: var(--aiopta-space-5) 0; border-bottom: 1px solid var(--ds-border); }
/* #870 - kein direktes Kind darf breiter werden als die Zeile.
   flex-wrap bricht NUR ZWISCHEN Kindern um; ein einzelnes unteilbares Kind
   schiebt trotzdem den Dokument-Scroller auf. Genau das passierte bei 390 px:
   die beiden <select> ziehen ihre Breite aus der laengsten Option (Kategorie-
   bzw. Autorenname + Zaehler, zur Laufzeit aus dem Katalog), und "flex: 0 0 auto"
   verbot ihnen das Schrumpfen. "min-width: 0" ist dabei Pflicht - ohne das
   schrumpft ein Flex-Item nicht unter seine Inhaltsbreite, "max-width" allein
   genuegt nicht.
   KEINE Backticks in diesem Kommentar: das Stylesheet liegt in einem
   Template-Literal, ein Backtick hier beendet es. */
.toolbar > * { min-width: 0; max-width: 100%; }
.toolbar input[type="search"], .toolbar select {
  background: var(--ds-surface); color: var(--ds-text-1); border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-sm); padding: 0 var(--aiopta-space-3); min-width: 0;
  min-height: var(--ds-touch-target); transition: border-color var(--ds-duration-fast) var(--ds-ease-out),
  box-shadow var(--ds-duration-fast) var(--ds-ease-out); }
.toolbar input[type="search"] { flex: 1 1 240px; }
.toolbar select { flex: 0 1 auto; cursor: pointer; }
.toolbar input[type="search"]:hover, .toolbar select:hover { border-color: var(--ds-border-strong); }
.toolbar input[type="search"]:focus, .toolbar select:focus {
  outline: none; border-color: var(--ds-border-focus); box-shadow: 0 0 0 3px var(--ds-accent-subtle); }
.toolbar .pill-group { display: inline-flex; background: var(--ds-surface-sunken); border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-full); padding: 3px; gap: 2px; overflow-x: auto; }
.toolbar .pill-group button { background: transparent; border: 0; color: var(--ds-text-2);
  padding: var(--aiopta-space-2) var(--aiopta-space-3); border-radius: var(--ds-radius-full); cursor: pointer;
  font-size: 0.8125rem; font-weight: 500; transition: background var(--ds-duration-fast) var(--ds-ease-out),
  color var(--ds-duration-fast) var(--ds-ease-out); }
.toolbar .pill-group button:hover { color: var(--ds-text-1); }
.toolbar .pill-group button[aria-pressed="true"] { background: var(--ds-accent); color: var(--ds-accent-on);
  font-weight: 600; box-shadow: var(--ds-shadow-sm); }
.toolbar .count { margin-left: auto; color: var(--ds-text-2); font-size: 0.8125rem; }

/* === hero (store-browse) — .mkhero, redesign-2026 S8 ==================== */
/* Prototype parity: the hero is NOT a boxed banner — it is a light block of
   a 3px signal sigline bar over the title + subline. The decorative gradient
   lives on the page body (body::before), matching the prototype; here the
   only gradient is the sigline itself (--ds-signal, token-only, no raw hex). */
.mkhero { position: relative; margin: var(--aiopta-space-8) 0 var(--aiopta-space-2);
  padding: 0; display: flex; flex-direction: column; }
.mkhero::before { content: ""; display: block; height: 3px; width: 110px; border-radius: 2px;
  background: var(--ds-signal); margin: 0 0 var(--aiopta-space-3); }
.mkhero .kicker { margin: 0 0 var(--aiopta-space-2); font-size: 0.6875rem;
  font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ds-text-3); }
.mkhero h2 { margin: 0; font-size: clamp(22px, 3.2vw, 28px); font-weight: 700;
  letter-spacing: -0.01em; color: var(--ds-text-1); text-wrap: balance; }
.mkhero .hero-sub { margin: var(--aiopta-space-3) 0 0; max-width: 640px;
  color: var(--ds-text-2); font-size: 0.9375rem; text-wrap: pretty; }
.mkhero .hero-meta { margin: var(--aiopta-space-3) 0 0; color: var(--ds-text-3);
  font-size: 0.75rem; }
.mkhero .hero-meta code { font-family: var(--ds-font-mono); color: var(--ds-text-2); }
/* Visually-hidden utility (document-outline h1 under the topbar brand). */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

/* === category chips (store-browse) — .chip/.chip.on, redesign-2026 S8 === */
/* Progressive enhancement: the row ships [hidden] + only the "all" chip;
   the client JS appends the per-section chips, unhides the row and hides
   the legacy <select id="category"> (which stays in the DOM as the
   no-chip fallback + the a11y-labelled filter control). */
.chip-row { display: flex; flex-wrap: wrap; gap: var(--aiopta-space-2);
  padding: var(--aiopta-space-4) 0 0; }
.chip-row[hidden] { display: none; }
.chip { display: inline-flex; align-items: center; gap: var(--aiopta-space-2); min-height: 32px;
  padding: 0 var(--aiopta-space-3); border-radius: var(--ds-radius-full);
  background: var(--ds-surface-active); color: var(--ds-text-2); font-size: 0.8125rem; font-weight: 600;
  border: 1px solid var(--ds-border); cursor: pointer; white-space: nowrap;
  transition: background var(--ds-duration-fast) var(--ds-ease-out),
  color var(--ds-duration-fast) var(--ds-ease-out),
  border-color var(--ds-duration-fast) var(--ds-ease-out); }
.chip:hover { color: var(--ds-text-1); border-color: var(--ds-border-strong); }
.chip.on { background: var(--ds-accent-muted); color: var(--ds-accent-text); border-color: transparent; }

/* === card grid (store-browse) — mkcard anatomy (redesign-2026 S8) ======= */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: var(--aiopta-space-4); padding: var(--aiopta-space-6) 0; }
/* #870 - der Grid-Boden. 1fr ist minmax(auto, 1fr); das auto als Minimum
   heisst AUTOMATISCHE MINDESTGROESSE: die Spur kann nicht unter die
   min-content-Breite des Items schrumpfen. Bei 390px mass die Karte deshalb
   411px in einem 343px-Container und schob den Dokument-Scroller auf
   (Live-QA Build #527/#533). min-width: 0 nimmt der Spur dieses Minimum. */
.card { min-width: 0; background: var(--ds-surface); border: 1px solid var(--ds-border); border-radius: var(--ds-radius-md);
  padding: var(--aiopta-space-4); display: flex; flex-direction: column; gap: var(--aiopta-space-3);
  box-shadow: var(--ds-shadow-sm);
  transition: background var(--ds-duration-fast) var(--ds-ease-out),
  transform var(--ds-duration-fast) var(--ds-ease-out),
  border-color var(--ds-duration-fast) var(--ds-ease-out),
  box-shadow var(--ds-duration-fast) var(--ds-ease-out); }
.card:hover { background: var(--ds-surface-hover); border-color: var(--ds-border-strong);
  transform: translateY(-2px); box-shadow: var(--ds-shadow-md); }
.card:focus-within { border-color: var(--ds-border-focus); box-shadow: 0 0 0 3px var(--ds-accent-subtle); }
/* mkcard header row (prototype tile anatomy): 44px tinted icon tile left,
   owned/price badge right. The whole card is clickable via the stretched
   name link (::after covers the card) — no separate footer CTA. */
.card { position: relative; }
/* #870 - icon und head-badges tragen beide flex:none. Ohne Umbruch ist
   die min-content-Breite der Kopfzeile deren Summe, und die Karte kann trotz
   min-width:0 nicht unter diese Breite. Der Umbruch greift nur, wenn der
   Platz fehlt - auf dem Desktop aendert sich nichts. */
.card .head { display: flex; flex-wrap: wrap; gap: var(--aiopta-space-3); align-items: flex-start; min-width: 0; }
.card .head-badges { margin-left: auto; display: inline-flex; gap: var(--aiopta-space-2); flex: none; }
.card .icon { width: 44px; height: 44px; flex: none; font-size: 1.25rem; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--ds-radius-md); background: var(--ds-accent-muted); color: var(--ds-accent); }
.card .icon svg { width: 20px; height: 20px; display: block; }
/* Category tint slots — build-time assigned (sectionTint), token-only. */
.icon.tint-1 { background: var(--ds-tint-1-bg); color: var(--ds-tint-1-fg); }
.icon.tint-2 { background: var(--ds-tint-2-bg); color: var(--ds-tint-2-fg); }
.icon.tint-3 { background: var(--ds-tint-3-bg); color: var(--ds-tint-3-fg); }
.icon.tint-4 { background: var(--ds-tint-4-bg); color: var(--ds-tint-4-fg); }
.icon.tint-5 { background: var(--ds-tint-5-bg); color: var(--ds-tint-5-fg); }
.icon.tint-6 { background: var(--ds-tint-6-bg); color: var(--ds-tint-6-fg); }
.icon.tint-7 { background: var(--ds-tint-7-bg); color: var(--ds-tint-7-fg); }
.icon.tint-8 { background: var(--ds-tint-8-bg); color: var(--ds-tint-8-fg); }
.card .name { font-size: 0.9375rem; font-weight: 700; margin: 0; color: var(--ds-text-1); overflow-wrap: anywhere; }
.card .name a { color: inherit; }
.card .name a:hover { color: var(--ds-accent); text-decoration: none; }
/* Stretched link: the name anchor covers the whole card (prototype: the
   tile itself is the detail trigger). */
.card .name a::after { content: ""; position: absolute; inset: 0; }
.card .desc { color: var(--ds-text-2); font-size: 0.8125rem; margin: 0; flex: 1 1 auto; display: -webkit-box;
  -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
/* mkcard footer (prototype .tile .foot): Kategorie · Version · Autor. */
.card .foot { display: flex; flex-wrap: wrap; gap: var(--aiopta-space-2); margin-top: auto; }
.card .foot .tag { background: var(--ds-surface-sunken); color: var(--ds-text-2);
  padding: 3px var(--aiopta-space-2); border-radius: var(--ds-radius-full); font-size: 0.6875rem;
  border: 1px solid var(--ds-border); }
.card .foot .tag.section { background: var(--ds-accent-muted); color: var(--ds-accent-text); border-color: transparent; }
.card .foot .tag.version { background: var(--ds-success-muted); color: var(--ds-success); border-color: transparent;
  font-family: var(--ds-font-mono); font-weight: 600; }

/* === detail page (store-detail) ========================================= */
.detail { padding: var(--aiopta-space-6) 0; }
.detail .breadcrumb { color: var(--ds-text-3); font-size: 0.75rem; margin-bottom: var(--aiopta-space-3); }
.detail .header { display: flex; gap: var(--aiopta-space-4); align-items: flex-start;
  padding-bottom: var(--aiopta-space-4); border-bottom: 1px solid var(--ds-border); }
.detail .header .icon { font-size: 2.25rem; line-height: 1; width: 72px; height: 72px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center; border-radius: var(--ds-radius-lg);
  background: var(--ds-accent-muted); color: var(--ds-accent); }
.detail .header .icon svg { width: 32px; height: 32px; display: block; }
.detail .header h1 { margin: 0 0 var(--aiopta-space-2); font-size: 1.75rem; font-weight: 650; color: var(--ds-text-1); }
.detail .header .meta-row { color: var(--ds-text-2); font-size: 0.8125rem; display: flex; flex-wrap: wrap; gap: var(--aiopta-space-2); }
.detail .header .meta-row .pill { background: var(--ds-surface-sunken); color: var(--ds-text-2);
  padding: 3px var(--aiopta-space-3); border-radius: var(--ds-radius-full); border: 1px solid var(--ds-border); }
.detail .header .meta-row .pill.section { background: var(--ds-accent-muted); color: var(--ds-accent-hover); border-color: transparent; }
.detail .header .meta-row .pill.version { background: var(--ds-success-muted); color: var(--ds-success); border-color: transparent; }
.detail .header .meta-row .pill.pricing { background: var(--ds-warning-muted); color: var(--ds-warning); border-color: transparent; }
.detail .header .actions { margin-left: auto; display: flex; gap: var(--aiopta-space-2); }
.detail .btn { background: var(--ds-accent); color: var(--ds-accent-on); border: 0; border-radius: var(--ds-radius-sm);
  padding: 0 var(--aiopta-space-4); min-height: var(--ds-touch-target); font-weight: 600; cursor: pointer;
  text-decoration: none; display: inline-flex; align-items: center; justify-content: center; gap: var(--aiopta-space-2);
  box-shadow: var(--ds-shadow-sm); transition: background var(--ds-duration-fast) var(--ds-ease-out),
  box-shadow var(--ds-duration-fast) var(--ds-ease-out), transform var(--ds-duration-fast) var(--ds-ease-out); }
.detail .btn:hover { background: var(--ds-accent-hover); color: var(--ds-accent-on); text-decoration: none;
  box-shadow: var(--ds-glow-accent); }
.detail .btn:active { background: var(--ds-accent-active); transform: translateY(1px); }
.detail .btn.secondary { background: transparent; color: var(--ds-text-1); border: 1px solid var(--ds-border); box-shadow: none; }
.detail .btn.secondary:hover { border-color: var(--ds-accent); background: var(--ds-accent-subtle); box-shadow: none; }
.detail section { padding: var(--aiopta-space-5) 0; border-bottom: 1px solid var(--ds-border); }
.detail section h2 { font-size: 0.6875rem; margin: 0 0 var(--aiopta-space-3); color: var(--ds-text-3);
  text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; }
.detail .description { color: var(--ds-text-1); white-space: pre-wrap; }
.detail .tags { display: flex; flex-wrap: wrap; gap: var(--aiopta-space-2); }
.detail .tags .tag { background: var(--ds-surface-sunken); padding: 4px var(--aiopta-space-3);
  border-radius: var(--ds-radius-full); font-size: 0.75rem; color: var(--ds-text-2); border: 1px solid var(--ds-border); }
.detail .endpoints { margin: 0; padding: 0; list-style: none; }
.detail .endpoints li { font-family: var(--ds-font-mono); background: var(--ds-surface-sunken);
  padding: var(--aiopta-space-2) var(--aiopta-space-3); border-radius: var(--ds-radius-sm);
  margin-bottom: var(--aiopta-space-1); font-size: 0.8125rem; border: 1px solid var(--ds-border); }
.detail .hash { font-family: var(--ds-font-mono); font-size: 0.75rem; color: var(--ds-text-3); word-break: break-all; }
.detail .placeholder { color: var(--ds-text-3); font-style: italic; font-size: 0.8125rem; padding: var(--aiopta-space-2) 0; }
.detail .placeholder-note { color: var(--ds-text-3); font-size: 0.75rem; margin-top: var(--aiopta-space-1); }

/* === details panel: demo / screenshots / readme / changelog (issue #91) = */
.detail .demo-link { display: inline-flex; align-items: center; gap: var(--aiopta-space-2); }
.detail .screenshots {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(228px, 1fr));
  gap: var(--aiopta-space-3);
  margin: var(--aiopta-space-2) 0 var(--aiopta-space-1);
}
.detail .screenshot {
  margin: 0;
  background: var(--ds-surface);
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-lg);
  overflow: hidden;
  box-shadow: var(--ds-shadow-sm);
  transition: border-color var(--ds-duration-normal) var(--ds-ease-out),
    transform var(--ds-duration-normal) var(--ds-ease-out),
    box-shadow var(--ds-duration-normal) var(--ds-ease-out);
}
.detail .screenshot:hover,
.detail .screenshot:focus-visible { border-color: var(--ds-accent); transform: translateY(-2px);
  box-shadow: var(--ds-shadow-md); outline: none; }
.detail .screenshot img { display: block; width: 100%; height: 180px; object-fit: cover; background: var(--ds-surface-sunken); }
.detail .screenshot figcaption {
  padding: var(--aiopta-space-2) var(--aiopta-space-3);
  font-size: 0.75rem;
  color: var(--ds-text-2);
  border-top: 1px solid var(--ds-border);
  display: flex;
  justify-content: space-between;
  gap: var(--aiopta-space-2);
}
.detail .screenshot .filesize { color: var(--ds-text-3); font-family: var(--ds-font-mono); }
.detail .markdown-body {
  color: var(--ds-text-1);
  font-size: 0.875rem;
  line-height: 1.6;
}
.detail .markdown-body h1,
.detail .markdown-body h2,
.detail .markdown-body h3,
.detail .markdown-body h4,
.detail .markdown-body h5,
.detail .markdown-body h6 { color: var(--ds-text-1); margin: var(--aiopta-space-4) 0 var(--aiopta-space-2); line-height: 1.3; }
.detail .markdown-body h1 { font-size: 1.375rem; border-bottom: 1px solid var(--ds-border); padding-bottom: var(--aiopta-space-2); }
.detail .markdown-body h2 { font-size: 1.125rem; }
.detail .markdown-body h3 { font-size: 0.9375rem; color: var(--ds-text-2); }
.detail .markdown-body p { margin: var(--aiopta-space-2) 0; }
.detail .markdown-body ul,
.detail .markdown-body ol { margin: var(--aiopta-space-2) 0; padding-left: var(--aiopta-space-5); }
.detail .markdown-body li { margin: 3px 0; }
.detail .markdown-body code { background: var(--ds-surface-sunken); color: var(--ds-accent-hover);
  padding: 1px 6px; border-radius: var(--ds-radius-sm); font-family: var(--ds-font-mono); font-size: 0.75rem; }
.detail .markdown-body pre { background: var(--ds-bg-1); border: 1px solid var(--ds-border);
  padding: var(--aiopta-space-3); border-radius: var(--ds-radius-md); overflow-x: auto; }
.detail .markdown-body pre code { background: transparent; color: var(--ds-text-1); padding: 0; }
.detail .markdown-body blockquote { border-left: 3px solid var(--ds-accent); margin: var(--aiopta-space-2) 0;
  padding: var(--aiopta-space-1) var(--aiopta-space-3); color: var(--ds-text-2); background: var(--ds-accent-subtle); }
.detail .markdown-body img { max-width: 100%; border-radius: var(--ds-radius-md); border: 1px solid var(--ds-border); }
.detail .markdown-body hr { border: 0; border-top: 1px solid var(--ds-border); margin: var(--aiopta-space-4) 0; }
.detail .markdown-body a { color: var(--ds-accent); }
.detail .markdown-body a:hover { color: var(--ds-accent-hover); }

/* === empty state ========================================================= */
.empty { padding: var(--aiopta-space-16) var(--aiopta-space-5); text-align: center; color: var(--ds-text-2); }
.empty h2 { color: var(--ds-text-1); margin: 0 0 var(--aiopta-space-2); font-size: 1.125rem; font-weight: 600; }

/* === responsive grid tiers (redesign-2026 app.css 613-697 parity) ======= */
@media (min-width: 861px) and (max-width: 1200px) {
  .grid { grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); }
}
@media (min-width: 768px) and (max-width: 860px) {
  .grid { grid-template-columns: repeat(3, 1fr); }
  .container { padding: 0 var(--aiopta-space-6); }
}
@media (max-width: 767px) {
  .grid { grid-template-columns: 1fr; }
  .mkhero { margin-top: var(--aiopta-space-6); }
}

/* === responsive (mobile-first collapse) ================================= */
@media (max-width: 720px) {
  .container { padding: 0 var(--aiopta-space-4); }
  .site-header { padding: var(--aiopta-space-6) 0 var(--aiopta-space-3); }
  .site-header h1 { font-size: 1.375rem; }
  .toolbar { gap: var(--aiopta-space-2); }
  .toolbar input[type="search"] { flex: 1 1 100%; }
  .toolbar .count { margin-left: 0; }
  .grid { grid-template-columns: 1fr; gap: var(--aiopta-space-3); }
  .detail .header { flex-direction: column; align-items: flex-start; }
  .detail .header .actions { margin-left: 0; width: 100%; }
  .detail .btn { flex: 1; justify-content: center; }
}

/* === reduced motion ====================================================== */
@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
  .card:hover, .detail .screenshot:hover, .detail .btn:active { transform: none; }
}

/* === auth + paywall (issue #89) ========================================= */
.site-header .auth-bar { display: flex; align-items: center; gap: var(--aiopta-space-3); margin-top: var(--aiopta-space-3); }
.site-header .auth-bar .who { color: var(--ds-text-2); font-size: 0.8125rem; }
.site-header .auth-bar .btn-link { background: transparent; color: var(--ds-accent); border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-full); padding: var(--aiopta-space-2) var(--aiopta-space-4); cursor: pointer;
  font-size: 0.8125rem; font-family: inherit; min-height: var(--ds-touch-target);
  transition: border-color var(--ds-duration-fast) var(--ds-ease-out), background var(--ds-duration-fast) var(--ds-ease-out); }
.site-header .auth-bar .btn-link:hover { border-color: var(--ds-accent); background: var(--ds-accent-subtle); }
.site-header .auth-bar .btn-link:focus-visible { outline: 2px solid var(--ds-border-focus); outline-offset: 2px; }

.detail .actions { display: flex; gap: var(--aiopta-space-3); flex-wrap: wrap; align-items: center; }
.detail .actions .btn { padding: 0 var(--aiopta-space-5); }
.detail .actions .btn[disabled] { opacity: 0.55; cursor: not-allowed; }
.detail .actions .price { color: var(--ds-warning); font-weight: 600; margin-right: var(--aiopta-space-2); }
.detail .actions .meta-note { color: var(--ds-text-2); font-size: 0.75rem; }
.detail .actions .btn.buy { background: var(--ds-warning); color: var(--ds-text-inverse); }
.detail .actions .btn.buy:hover { background: var(--ds-warning); filter: brightness(1.08); box-shadow: var(--ds-shadow-md); }
.detail .actions .status { font-size: 0.8125rem; }
.detail .actions .status.error { color: var(--ds-error); }
.detail .actions .status.success { color: var(--ds-success); }
/* Issue #503: the embed-install affordance is only meaningful inside the
   Aiopta Core embed iframe. It starts [hidden] in the markup; the embed-
   handshake code in store.js removes [hidden] once it confirms it is
   framed. Outside the embed it never appears (no stray "Install" button
   on the standalone /store/ detail page). */
.detail .actions .embed-install[hidden] { display: none; }
/* #601 (Slice 4 of #598): the free-anonymous embed install button behaves
   identically — [hidden] until the embed code reveals it inside the Core
   iframe. Standalone /store/ never sees it (no Core to install into). */
.detail .actions .embed-install-free[hidden] { display: none; }

/* auth modal */
.modal-backdrop { position: fixed; inset: 0; background: var(--ds-overlay);
  display: none; align-items: center; justify-content: center; padding: var(--aiopta-space-5); z-index: 1000; }
.modal-backdrop.open { display: flex; }
.modal { background: var(--ds-bg-2); border: 1px solid var(--ds-border-strong); border-radius: var(--ds-radius-lg);
  padding: var(--aiopta-space-6); max-width: 380px; width: 100%; box-shadow: var(--ds-shadow-lg); }
.modal h2 { margin: 0 0 var(--aiopta-space-4); font-size: 1.125rem; color: var(--ds-text-1); }
.modal .field { display: flex; flex-direction: column; gap: var(--aiopta-space-1); margin-bottom: var(--aiopta-space-3); }
.modal .field label { color: var(--ds-text-2); font-size: 0.8125rem; }
.modal .field input { background: var(--ds-surface); color: var(--ds-text-1); border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-sm); padding: var(--aiopta-space-2) var(--aiopta-space-3); font: inherit; min-height: var(--ds-touch-target); }
.modal .field input:focus { outline: none; border-color: var(--ds-border-focus); box-shadow: 0 0 0 3px var(--ds-accent-subtle); }
.modal .row { display: flex; gap: var(--aiopta-space-2); align-items: center; justify-content: space-between; margin-top: var(--aiopta-space-3); }
.modal .row .btn { padding: 0 var(--aiopta-space-4); min-height: var(--ds-touch-target); }
.modal .row .btn.secondary { background: transparent; color: var(--ds-text-1); border: 1px solid var(--ds-border); }
.modal .error { color: var(--ds-error); font-size: 0.8125rem; min-height: 1em; margin-top: var(--aiopta-space-1); }
.modal .switch { color: var(--ds-text-2); font-size: 0.8125rem; text-align: center; margin-top: var(--aiopta-space-3); }
.modal .switch a { color: var(--ds-accent); cursor: pointer; }

/* === license management (issue #544 Slice 1) =========================== */
.licenses { margin: var(--aiopta-space-6) 0; }
.licenses > h2 { font-size: 1.125rem; margin: 0 0 var(--aiopta-space-4); color: var(--ds-text-1); }
.licenses .login-prompt { color: var(--ds-text-2); display: flex; align-items: center; gap: var(--aiopta-space-3);
  flex-wrap: wrap; }
.licenses .login-prompt .btn { padding: 0 var(--aiopta-space-4); min-height: var(--ds-touch-target); }
.licenses .msg { color: var(--ds-text-2); }
.licenses .err { color: var(--ds-error); }
.licenses-table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; }
.licenses-table th, .licenses-table td { text-align: left; padding: var(--aiopta-space-3);
  border-bottom: 1px solid var(--ds-border); vertical-align: middle; }
/* Prototype caption: uppercase eyebrow — 800 weight, wide track, text-3. */
.licenses-table th { color: var(--ds-text-3); font-weight: 800; font-size: 0.6875rem;
  text-transform: uppercase; letter-spacing: 0.08em; }
.licenses-table td { color: var(--ds-text-1); }
.licenses-table tbody tr:last-child td { border-bottom: 0; }
/* Monospace license keys (prototype .mono). */
.licenses-table .key { font-family: var(--ds-font-mono); font-size: 0.78125rem; color: var(--ds-text-2);
  word-break: break-all; }
/* Status renders as a pill badge (prototype .badge-ok "Aktiv" grün); the
   cell keeps its status class for the responsive data-label collapse. */
.licenses-table .status { font-weight: 600; }
.licenses-table .status.active { color: var(--ds-success); }
.licenses-table .status.expired { color: var(--ds-text-3); }
.licenses-table .status.revoked { color: var(--ds-error); }
@media (max-width: 640px) {
  .licenses-table thead { display: none; }
  .licenses-table, .licenses-table tbody, .licenses-table tr, .licenses-table td { display: block; width: 100%; }
  .licenses-table tr { padding: var(--aiopta-space-3) 0; border-bottom: 1px solid var(--ds-border); }
  .licenses-table td { border-bottom: none; padding: var(--aiopta-space-1) 0; }
  .licenses-table td::before { content: attr(data-label) ": "; color: var(--ds-text-2); font-weight: 600;
    font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; }
}
/* Issue #558: offline-key activation form (user-facing, in the licenses section). */
.license-activate { margin-top: var(--aiopta-space-5); padding-top: var(--aiopta-space-4); border-top: 1px solid var(--ds-border); }
.license-activate h3 { font-size: 1rem; margin: 0 0 var(--aiopta-space-3); color: var(--ds-text-1); }
.activate-form { display: flex; flex-direction: column; gap: var(--aiopta-space-3); max-width: 36rem; }
.activate-form label { display: flex; flex-direction: column; gap: var(--aiopta-space-1); font-size: 0.75rem;
  color: var(--ds-text-2); text-transform: uppercase; letter-spacing: 0.04em; font-weight: 600; }
.activate-form textarea { min-height: calc(var(--ds-touch-target) * 1.5); padding: var(--aiopta-space-3);
  border: 1px solid var(--ds-border); border-radius: var(--ds-radius-md); background: var(--ds-surface);
  color: var(--ds-text-1); font-family: var(--ds-font-mono); font-size: 0.8125rem; resize: vertical; }
.activate-form .btn { align-self: flex-start; }
.activate-status { margin-top: var(--aiopta-space-2); color: var(--ds-text-2); font-size: 0.875rem; }
.activate-status.ok { color: var(--ds-success); }
.activate-status.err { color: var(--ds-error); }
/* Issue #556: admin "Keys ausstellen" section (admin-only, shipped [hidden]). */
.admin-keys { margin: var(--aiopta-space-6) 0; border-top: 1px solid var(--ds-border); padding-top: var(--aiopta-space-6); }
.admin-keys > h2 { font-size: 1.125rem; margin: 0 0 var(--aiopta-space-4); color: var(--ds-text-1); }
.admin-keys h3 { font-size: 1rem; margin: var(--aiopta-space-4) 0 var(--aiopta-space-3); color: var(--ds-text-1); }
.issue-form { display: flex; flex-wrap: wrap; gap: var(--aiopta-space-3); align-items: flex-end; }
.issue-form label { display: flex; flex-direction: column; gap: var(--aiopta-space-1); font-size: 0.75rem;
  color: var(--ds-text-2); text-transform: uppercase; letter-spacing: 0.04em; }
.issue-form input, .issue-form select { min-height: var(--ds-touch-target); padding: 0 var(--aiopta-space-3);
  border: 1px solid var(--ds-border); border-radius: var(--ds-radius-md); background: var(--ds-bg-1); color: var(--ds-text-1); }
.issue-status { color: var(--ds-text-2); margin-top: var(--aiopta-space-3); font-family: var(--ds-font-mono); word-break: break-all; }

/* === Abo-Übersicht (issue #557 Slice 3) =============================== */
/* The subscription overview renders the user's purchase-sourced licenses
   (source:'purchase') with a derived status + a "renew" CTA on expired
   rows. Shares the .licenses-table grid (same columns) so the two views
   stay visually consistent; the .abos wrapper adds its own heading +
   the renew button affordance. */
.abos { margin: var(--aiopta-space-6) 0; border-top: 1px solid var(--ds-border); padding-top: var(--aiopta-space-6); }
.abos > h2 { font-size: 1.125rem; margin: 0 0 var(--aiopta-space-4); color: var(--ds-text-1); }
.abos .login-prompt { color: var(--ds-text-2); display: flex; align-items: center; gap: var(--aiopta-space-3);
  flex-wrap: wrap; }
.abos .login-prompt .btn { padding: 0 var(--aiopta-space-4); min-height: var(--ds-touch-target); }
.abos .msg { color: var(--ds-text-2); }
.abos .err { color: var(--ds-error); }
/* The renew CTA — a compact button rendered in the status cell when the
   derived status is 'expired'. Styled as a warning-toned action so it
   reads as "needs attention" without the error-red alarm. */
.abos .renew-btn { background: var(--ds-warning); color: var(--ds-text-inverse); border: 0;
  border-radius: var(--ds-radius-full); padding: var(--aiopta-space-1) var(--aiopta-space-3);
  font-size: 0.75rem; font-weight: 600; cursor: pointer; margin-left: var(--aiopta-space-2);
  min-height: 0; box-shadow: none; }
.abos .renew-btn:hover { filter: brightness(1.08); box-shadow: var(--ds-shadow-sm); }
.abos .renew-btn:focus-visible { outline: 2px solid var(--ds-border-focus); outline-offset: 2px; }

/* === Konto & Lizenzen page (Marketplace Gestalt) ======================== */
/* Prototype parity: hero + segmented sub-tabs (Abos / Lizenzen /
   Offline-Lizenz / Konto) + card grid. Sub-tab panels toggle client-side;
   the logged-out state shows the "Anmeldung erforderlich" prompt only. */
.account-login { max-width: 420px; margin: var(--aiopta-space-10) auto; display: flex;
  flex-direction: column; gap: var(--aiopta-space-3); text-align: center; }
.account-login h2 { margin: 0; font-size: 1.375rem; color: var(--ds-text-1); }
.account-login p { margin: 0; color: var(--ds-text-2); }
.account-login .btn { align-self: center; }
.account-login[hidden] { display: none; }
.account-panels[hidden] { display: none; }
.btn.btn-primary { background: var(--ds-accent); color: var(--ds-accent-on); border: 0;
  border-radius: var(--ds-radius-sm); padding: 0 var(--aiopta-space-4); min-height: var(--ds-touch-target);
  font-weight: 600; cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
  box-shadow: var(--ds-shadow-sm); }
.btn.btn-primary:hover { background: var(--ds-accent-hover); box-shadow: var(--ds-glow-accent); }
/* Segmented sub-tab control (prototype .seg). */
.seg { display: flex; border: 1px solid var(--ds-border-strong); border-radius: var(--ds-radius-sm);
  overflow: hidden; max-width: 640px; margin-bottom: var(--aiopta-space-5); }
.seg button { flex: 1; min-height: 42px; background: transparent; border: 0; font-weight: 700;
  font-size: 0.8125rem; color: var(--ds-text-2); cursor: pointer; padding: 0 var(--aiopta-space-2);
  transition: background var(--ds-duration-fast) var(--ds-ease-out), color var(--ds-duration-fast) var(--ds-ease-out); }
.seg button:hover { color: var(--ds-text-1); }
.seg button[aria-selected="true"] { background: var(--ds-accent-muted); color: var(--ds-accent-text); }
.acc-panel[hidden] { display: none; }
/* Account card grid (prototype .acc). */
.acc { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--aiopta-space-4); align-items: start; }
.acc-card { background: var(--ds-surface); border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-md); padding: var(--ds-card-pad); box-shadow: var(--ds-shadow-sm); }
.acc-card > h3, .acc-card .kicker { display: block; margin: 0 0 var(--aiopta-space-3);
  font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ds-text-3); }
.acc-card .msg { color: var(--ds-text-2); font-size: 0.875rem; margin: 0; }
/* Real-state placeholders (a backend surface that does not exist on this
   marketplace server yet) read as an intentional empty-state card, not bare
   text — matches the prototype's tidy "nicht verfügbar" placeholder. */
.acc-card p[data-invoices-unavailable],
.acc-card p[data-instance-licenses-unavailable],
.acc-card p[data-instances-unavailable],
.acc-card p[data-profile-unavailable] {
  display: flex; align-items: center; min-height: 60px;
  padding: var(--aiopta-space-3) var(--aiopta-space-4);
  border: 1px dashed var(--ds-border-strong); border-radius: var(--ds-radius-sm);
  background: var(--ds-surface-sunken); color: var(--ds-text-3); font-size: 0.8125rem; }
.acc-card .field { display: flex; flex-direction: column; gap: var(--aiopta-space-1);
  margin-bottom: var(--aiopta-space-3); }
.acc-card .field label { color: var(--ds-text-2); font-size: 0.8125rem; font-weight: 600; }
.acc-card .field input[readonly] { background: var(--ds-surface-sunken); color: var(--ds-text-1);
  border: 1px solid var(--ds-border); border-radius: var(--ds-radius-sm);
  padding: var(--aiopta-space-2) var(--aiopta-space-3); min-height: var(--ds-touch-target); }
/* Real-state note (prototype .note): dashed signal-tinted info block. The
   account page uses it EXCLUSIVELY for surfaces whose backend does not
   exist on this marketplace server yet — real state, never fake data. */
.note { display: flex; gap: var(--aiopta-space-3); align-items: flex-start;
  padding: var(--aiopta-space-3) var(--aiopta-space-4); border-radius: var(--ds-radius-sm);
  border: 1px dashed var(--ds-border-strong); background: var(--ds-accent-subtle);
  color: var(--ds-text-2); font-size: 0.875rem; }
.note b { color: var(--ds-text-1); }
.note .gdot { flex: none; width: 8px; height: 8px; border-radius: var(--ds-radius-full);
  background: var(--ds-info); margin-top: 6px; }
.acc .note { grid-column: 1 / -1; }
/* Account page section reset: the shared #licenses/#abos/#admin-keys
   sections render inside acc-cards — drop their standalone page framing. */
.acc-card .licenses, .acc-card .abos, .acc-card .admin-keys { margin: 0; border-top: 0; padding-top: 0; }
.account-signout { margin-top: var(--aiopta-space-3); }
/* Auth modal note (prototype "Wozu das Konto?"). */
.modal .modal-note { margin-top: var(--aiopta-space-4); }
@media (max-width: 720px) {
  .seg { flex-wrap: wrap; }
  .seg button { flex: 1 1 50%; }
  .acc { grid-template-columns: 1fr; }
}
