/* ==========================================================================
   MriiaBot — design system
   Source of truth: the "InstagramBot Design System" Claude Design project
   (see design_system.md). Token values below are transcribed from its
   tokens/*.css; change them there first, then mirror here.

   The one idea: a light statement of account. Hairline-bordered white panels
   on a faint cool ground; the only saturated colour in a resting view is the
   brand dot. Colour means state, never decoration. Money and identifiers are
   monospace so they read as data, not prose.

   Note this replaced the previous dark "Signal Panel" surface, and with it the
   signal-red brand: red is now destructive-only, and the brand is slate blue.
   The legacy --signal/--ink/--paper names survive as aliases onto the new ramp
   so several hundred existing declarations keep working.
   ========================================================================== */

/* Self-hosted (wwwroot/fonts/) rather than loaded from Google's CDN: this
   app's own infra (Ollama, Postgres, Redis) is fully self-hosted per
   docker-compose.yml, so an outbound font dependency would be the only
   external network call the admin UI makes, and would hard-fail rendering
   in an offline/air-gapped deployment. Latin + Cyrillic subsets only (the
   UI's only scripts); Greek/Vietnamese subsets from the source webfont are
   dropped. */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/inter-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/inter-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, 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: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/inter-400-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/inter-400-cyrillic-ext.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/inter-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/inter-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, 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: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/inter-500-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/inter-500-cyrillic-ext.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/inter-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/inter-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, 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: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/inter-600-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/inter-600-cyrillic-ext.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/jbmono-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, 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/jbmono-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, 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/jbmono-400-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/jbmono-400-cyrillic-ext.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/jbmono-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, 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/jbmono-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, 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/jbmono-500-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/jbmono-500-cyrillic-ext.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/jbmono-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, 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/jbmono-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, 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/jbmono-600-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/jbmono-600-cyrillic-ext.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* Space Grotesk ships no dedicated Cyrillic subset upstream — Ukrainian
   headings fall back to Inter (below) via the --font-display stack, which
   is why --font-display lists both. Latin-script headings use Space
   Grotesk; Cyrillic ones render in Inter at the same weight. */
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/spacegrotesk-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/spacegrotesk-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, 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: 'Space Grotesk';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/spacegrotesk-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/spacegrotesk-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, 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: 'Space Grotesk';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/spacegrotesk-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/spacegrotesk-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, 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;
}

:root {
  /* ---- ink: surfaces, lightest to strongest ---- */
  --ink-900: #f6f8fc;   /* app / page ground */
  --ink-800: #ffffff;   /* card, panel */
  --ink-700: #fbfcff;   /* raised panel / table head */
  --ink-600: #eff2f8;   /* input, hover row */
  --ink-500: #e4e8f0;   /* border hairline */
  --ink-400: #ccd3e0;   /* border strong */

  /* ---- paper: text ---- */
  --paper: #121722;
  --paper-soft: #525b6b;
  --paper-dim: #87909f;
  --paper-faint: #adb5c2;

  /* ---- brand: muted slate blue (desaturated on purpose — an electric blue
     at full chroma overwhelms the pale ground and reads as generic SaaS) ---- */
  --brand-600: #3f6fd1;
  --brand-700: #33599f;
  --brand-800: #294780;
  --brand-300: #a8c0ea;
  --brand-100: #e6edf9;
  --brand-050: #f3f7fd;

  /* ---- semantic state family: one set driving the dot, badges, quota bars
     and banners alike, each with a paired -dim tint ---- */
  --state-ok: #12a150;
  --state-ok-dim: #e4f7ec;
  --state-wait: #b07607;
  --state-wait-dim: #fdf3dc;
  --state-off: #87909f;
  --state-off-dim: #edeff4;
  --state-danger: #d92d20;
  --state-danger-dim: #fdeceb;

  /* ---- chat palette: ONE set, aliased by .ig and .dm below ---- */
  --chat-bg: var(--brand-050);
  --chat-in-bg: #ffffff;            /* customer message */
  --chat-in-fg: var(--paper);
  --chat-out-bg: var(--brand-600);  /* bot message */
  --chat-out-fg: #ffffff;
  --chat-meta: var(--paper-dim);
  --chat-typing: var(--paper-faint);

  --chat-ink: var(--brand-050);
  --chat-surface: var(--surface-panel);
  --chat-raised: var(--surface-input);
  --chat-line: var(--border-hairline);
  --chat-paper: var(--paper);
  --chat-paper-dim: var(--paper-soft);
  --chat-paper-faint: var(--paper-dim);
  --chat-accent: var(--brand-600);
  --chat-accent-hover: var(--brand-700);
  --chat-accent-dim: var(--brand-100);
  --chat-live: var(--state-ok);
  --chat-danger: var(--state-danger);
  --chat-danger-bg: var(--state-danger-dim);

  --dm-ink: var(--brand-050);
  --dm-surface: var(--surface-panel);
  --dm-raised: var(--surface-input);
  --dm-line: var(--border-hairline);
  --dm-paper: var(--paper);
  --dm-paper-dim: var(--paper-soft);
  --dm-paper-faint: var(--paper-dim);
  --dm-accent: var(--brand-600);
  --dm-danger: var(--state-danger);

  --ig-ink: var(--brand-050);
  --ig-surface: var(--surface-panel);
  --ig-surface-raised: var(--surface-input);
  --ig-line: var(--border-hairline);
  --ig-paper: var(--paper);
  --ig-paper-dim: var(--paper-soft);
  --ig-paper-faint: var(--paper-dim);
  --ig-accent: var(--brand-600);
  --ig-accent-dim: var(--brand-100);
  --ig-live: var(--state-ok);

  /* ---- semantic aliases ---- */
  --bg-app: var(--ink-900);
  --surface-panel: var(--ink-800);
  --surface-raised: var(--ink-700);
  --surface-input: var(--ink-600);
  --surface-hover: var(--ink-600);
  --border-hairline: var(--ink-500);
  --border-strong: var(--ink-400);
  --text-body: var(--paper);
  --text-muted: var(--paper-soft);
  --text-label: var(--paper-dim);
  --text-disabled: var(--paper-faint);
  --accent: var(--brand-600);
  --accent-hover: var(--brand-700);
  --accent-press: var(--brand-800);
  --link: var(--brand-600);
  --link-hover: var(--brand-800);

  /* Legacy aliases. The brand is slate blue now; red is destructive-only, so
     --signal maps to the brand ramp rather than to red. These keep the several
     hundred pre-existing --ink/--paper-dim/--signal declarations working. */
  --signal: var(--brand-600);
  --signal-hi: var(--brand-700);
  --signal-lo: var(--brand-800);
  --signal-dim: var(--brand-100);
  --deep-red: #8f1d1d;
  --ink: var(--ink-900);
  --ink-raised: var(--ink-800);
  --ink-line: var(--ink-500);
  --ok: var(--state-ok);
  --ok-dim: var(--state-ok-dim);
  --wait: var(--state-wait);
  --wait-dim: var(--state-wait-dim);

  /* ---- type ---- */
  /* Headings are Inter. Space Grotesk ships no Cyrillic subset, so every
     Ukrainian heading already fell back to Inter — the system makes that
     explicit. --font-display is Latin-only decoration (product name, model
     names); never set a Ukrainian heading in it. */
  --font-display: 'Space Grotesk', 'Inter', 'Segoe UI', sans-serif;
  --font-body: 'Inter', 'Segoe UI', sans-serif;
  --font-data: 'JetBrains Mono', 'Consolas', monospace;

  --text-2xs: 11px;
  --text-xs: 12px;
  --text-sm: 13px;
  --text-base: 15px;
  --text-md: 17px;
  --text-lg: 21px;
  --text-xl: 26px;
  --text-2xl: 33px;
  --text-3xl: 42px;
  --text-4xl: 54px;

  --leading-tight: 1.15;
  --leading-snug: 1.3;
  --leading-body: 1.55;
  --leading-loose: 1.7;

  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;

  --tracking-caps: .09em;    /* panel h2, table th — the only uppercase */
  --tracking-tight: -.015em; /* display sizes */
  --tracking-data: .01em;

  /* ---- spacing ---- */
  --space-0: 0;
  --space-1: 2px;
  --space-2: 4px;
  --space-3: 6px;
  --space-4: 8px;
  --space-5: 12px;
  --space-6: 16px;
  --space-7: 20px;
  --space-8: 24px;
  --space-9: 32px;
  --space-10: 40px;
  --space-11: 56px;
  --space-12: 72px;
  --space-13: 96px;
  --space-14: 128px;

  /* Density: admin tables are dense, tenant pages are not. */
  --pad-cell-dense: 8px 12px;
  --pad-cell: 12px 16px;
  --pad-cell-roomy: 16px 20px;
  --pad-panel-dense: 16px 18px;
  --pad-panel: 24px 28px;
  --pad-control: 9px 16px;
  --pad-control-lg: 13px 26px;

  --row-h-dense: 34px;
  --row-h: 44px;
  --tap-min: 44px;

  --measure-prose: 68ch;
  --shell-max: 1240px;
  --panel-gap: var(--space-8);

  --radius-0: 0;
  --radius-1: 8px;      /* inputs, cells, badges */
  --radius-2: 14px;     /* panels, cards */
  --radius-3: 22px;     /* feature cards, phone mock, hero surfaces */
  --radius-pill: 999px; /* every button, every nav chip */
  --hairline: 1px;
  --radius: var(--radius-1); /* legacy alias */
  --gap: var(--space-8);

  /* ---- elevation: hairline border + soft blue-cast shadow ---- */
  --elev-0: none;
  --elev-1: 0 1px 2px rgba(18, 28, 52, .04);
  --elev-2: 0 2px 6px rgba(18, 28, 52, .05);
  --elev-3: 0 10px 30px rgba(18, 28, 52, .08);
  --elev-4: 0 24px 60px rgba(18, 28, 52, .12);
  --elev-overlay: 0 40px 100px rgba(12, 18, 34, .4);
  --elev-brand: 0 8px 24px rgba(63, 111, 209, .22);

  --halo-ok: 0 0 0 3px var(--state-ok-dim);
  --halo-wait: 0 0 0 3px var(--state-wait-dim);
  --halo-off: 0 0 0 3px var(--state-off-dim);
  --halo-brand: 0 0 0 3px var(--brand-100);

  --ring-focus: 0 0 0 2px #fff, 0 0 0 4px rgba(63, 111, 209, .36);
  --dot-size: 9px;

  /* ---- motion: short and functional ---- */
  --dur-1: 90ms;   /* hover, press */
  --dur-2: 160ms;  /* expand-in-place */
  --dur-3: 260ms;  /* banner, lightbox */
  --ease-out: cubic-bezier(.2, .7, .3, 1);
  --ease-in-out: cubic-bezier(.4, 0, .2, 1);
  --transition-control:
    background-color var(--dur-1) var(--ease-out),
    border-color var(--dur-1) var(--ease-out),
    color var(--dur-1) var(--ease-out);

  /* ---- landing motifs ---- */
  --cloud-blue: radial-gradient(circle at 50% 50%, var(--brand-300) 0%, transparent 70%);
  --wash-hero: linear-gradient(180deg, #fff 0%, var(--brand-050) 100%);
  --orb-brand: radial-gradient(circle at 30% 25%, #9db8e8 0%, var(--brand-600) 55%, #24406f 100%);
  --blur-cloud: 70px;
  --dur-drift: 34s;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-1: 0ms;
    --dur-2: 0ms;
    --dur-3: 0ms;
  }
}

/* ==========================================================================
   Chat palette aliases. The landing (.ig) and test chat (.dm) each kept their
   own near-identical copy of these values, so a colour fixed in one silently
   failed to reach the other. Both now alias the single --chat-* set above;
   the --ig-*/--dm-* names survive so existing declarations keep working.
   ========================================================================== */

.ig,
.dm {
  --chat-ink: var(--brand-050);
  --chat-surface: var(--surface-panel);
  --chat-raised: var(--surface-input);
  --chat-line: var(--border-hairline);
  --chat-paper: var(--paper);
  --chat-paper-dim: var(--paper-soft);
  --chat-paper-faint: var(--paper-dim);
  --chat-accent: var(--brand-600);
  --chat-accent-hover: var(--brand-700);
  --chat-accent-dim: var(--brand-100);
  --chat-live: var(--state-ok);
  --chat-danger: var(--state-danger);
  --chat-danger-bg: var(--state-danger-dim);

  --chat-font-display: var(--font-body);
  --chat-font-body: var(--font-body);
  --chat-font-data: var(--font-data);
}

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg-app);
  color: var(--text-body);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--link);
  text-decoration-color: var(--brand-300);
  text-underline-offset: 3px;
}

a:hover {
  color: var(--link-hover);
  text-decoration-color: var(--link-hover);
}

a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible {
  outline: none;
  box-shadow: var(--ring-focus);
  border-radius: var(--radius-1);
}

/* Ukrainian headings resolve to Inter by design — --font-display is Latin-only
   decoration and is applied deliberately (the wordmark), never blanket-set. */
h1, h2, h3 {
  font-family: var(--font-body);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tight);
  margin: 0 0 var(--space-4);
  color: var(--text-body);
}

p {
  margin: 0 0 var(--space-5);
  color: var(--text-muted);
}

/* -------------------------------------------------------------------------
   Shell — header bar + content column, shared by every authenticated page
   ------------------------------------------------------------------------- */

.shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.shell-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  padding: var(--space-6) var(--space-8);
  background: var(--surface-panel);
  border-bottom: var(--hairline) solid var(--border-hairline);
  flex-wrap: wrap;
}

/* "MriiaBot" is a Latin string, so --font-display genuinely applies here —
   one of the few places in this Ukrainian UI where it does. */
.shell-brand {
  display: flex;
  align-items: baseline;
  gap: var(--space-5);
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: var(--text-md);
  letter-spacing: var(--tracking-tight);
  color: var(--text-body);
  text-decoration: none;
}

.shell-brand::before {
  content: '';
  width: var(--dot-size);
  height: var(--dot-size);
  border-radius: 50%;
  background: var(--accent);
  box-shadow: var(--halo-brand);
  flex-shrink: 0;
}

.shell-nav {
  display: flex;
  gap: 1.5rem;
  font-size: 0.85rem;
}

.shell-nav a {
  color: var(--paper-dim);
  text-decoration: none;
  font-weight: 500;
  padding-bottom: 2px;
  border-bottom: 2px solid transparent;
}

.shell-nav a:hover,
.shell-nav a.active {
  color: var(--paper);
  border-bottom-color: var(--signal);
}

/* Collapsed admin menu. Opens on hover and on :focus-within so it is reachable
   by keyboard — the trigger is a tabindex=0 span, not a link, because it has no
   destination of its own. */
.shell-nav-menu {
  position: relative;
}

.shell-nav-menu-trigger {
  color: var(--paper-dim);
  font-weight: 500;
  padding-bottom: 2px;
  border-bottom: 2px solid transparent;
  cursor: default;
  user-select: none;
}

.shell-nav-menu-trigger::after {
  content: '';
  display: inline-block;
  margin-left: 0.4rem;
  border: 3px solid transparent;
  border-top-color: currentColor;
  transform: translateY(1px);
}

.shell-nav-menu:hover .shell-nav-menu-trigger,
.shell-nav-menu:focus-within .shell-nav-menu-trigger,
.shell-nav-menu-trigger.active {
  color: var(--paper);
  border-bottom-color: var(--signal);
}

.shell-nav-menu-items {
  display: none;
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 20;
  /* Bridges the gap between trigger and panel so the hover does not drop
     while the pointer crosses it. */
  margin-top: 0.5rem;
  padding: 0.5rem 0;
  min-width: 12rem;
  flex-direction: column;
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-2);
  box-shadow: var(--elev-4);
}

.shell-nav-menu:hover .shell-nav-menu-items,
.shell-nav-menu:focus-within .shell-nav-menu-items {
  display: flex;
}

.shell-nav-menu-items a {
  padding: 0.45rem 0.9rem;
  border-bottom: none;
  white-space: nowrap;
}

.shell-nav-menu-items a:hover,
.shell-nav-menu-items a.active {
  border-bottom-color: transparent;
  background: var(--ink-line);
}

/* Sits beside .shell-nav in the header's space-between row. */
.shell-account {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.shell-avatar {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.shell-account-name {
  font-size: 0.85rem;
  color: var(--paper-dim);
  max-width: 14ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The bare `button` rule below gives every button the filled signal look, so
   the logout control resets it to read as one more nav item, not a primary action. */
.shell-logout .btn-link {
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--paper-dim);
  cursor: pointer;
}

.shell-logout .btn-link:hover {
  background: none;
  color: var(--paper);
}

.shell-main {
  flex: 1;
  width: 100%;
  max-width: 780px;
  margin: 0 auto;
  padding: 2.5rem 1.75rem 4rem;
}

/* Wider column for pages whose content is inherently horizontal — the
   catalog's photo-led product grid needs more than a single reading
   column's width to breathe. */
.shell-main.is-wide {
  max-width: 1180px;
}

/* Table-led pages get far more room than the reading column: a table's width
   is set by its columns, not by a comfortable measure, so capping it at 780px
   only moved the width into a horizontal scrollbar. Not edge-to-edge though —
   a table running into the bezels of a wide monitor reads as broken layout,
   and rows that span 2500px are hard to track across. The cap is generous
   enough that the tables here stop needing to scroll, and the wider side
   padding keeps the panel from touching the viewport edge. */
.shell-main.is-full {
  max-width: 1700px;
  padding-left: 2.5rem;
  padding-right: 2.5rem;
}

/* Only the tables want the full viewport. A form control stretched across a
   wide monitor is harder to use, not easier — an API key field 2000px wide
   reads worse than the reading column it replaced — so inputs keep a sane
   measure while the table beside them spreads. */
.shell-main.is-full .field input,
.shell-main.is-full .field select,
.shell-main.is-full .field textarea {
  max-width: 42rem;
}

.shell-eyebrow {
  font-family: var(--font-data);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--paper-faint);
  margin: 0 0 0.4rem;
}

.shell-main h1 {
  font-size: 1.6rem;
  margin-bottom: 2rem;
}

/* Auth/onboarding pages get a centered card instead of the app shell */

.auth-screen {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
}

.auth-card {
  width: 100%;
  max-width: 380px;
  text-align: center;
}

.auth-card .shell-brand {
  justify-content: center;
  margin-bottom: 2rem;
  font-size: 1.2rem;
}

.auth-card h1 {
  font-size: 1.4rem;
  margin-bottom: 0.6rem;
}

.auth-card p {
  margin-bottom: 1.75rem;
}

.auth-card .btn + .btn {
  margin-top: 0.75rem;
}

.auth-card .auth-note {
  margin-top: 1.5rem;
  margin-bottom: 0;
  font-size: 0.8rem;
  opacity: 0.7;
}

.auth-card.is-wide {
  max-width: 420px;
  text-align: left;
}

.auth-card.is-wide .shell-brand {
  justify-content: flex-start;
}

/* -------------------------------------------------------------------------
   Panels — the section block used throughout Panel/Usage/Admin pages
   ------------------------------------------------------------------------- */

.panel {
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-2);
  padding: var(--pad-panel);
  margin-bottom: var(--panel-gap);
  box-shadow: var(--elev-2);
}

/* Uppercase + letterspacing is reserved for exactly two things in this
   system: panel titles and table th. Never a button. */
.panel h2 {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--text-label);
  margin-bottom: var(--space-7);
}

.panel + .panel {
  margin-top: 0;
}

/* -------------------------------------------------------------------------
   Status — the signature element. A dot + label pair used for connection
   state, subscription state, and any other binary/tri-state system health.
   ------------------------------------------------------------------------- */

.status-line {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  font-family: var(--font-data);
  font-size: var(--text-sm);
}

.status-dot {
  width: var(--dot-size);
  height: var(--dot-size);
  border-radius: 50%;
  flex-shrink: 0;
}

.status-dot.is-ok { background: var(--state-ok); box-shadow: var(--halo-ok); }
.status-dot.is-wait { background: var(--state-wait); box-shadow: var(--halo-wait); }
.status-dot.is-off { background: var(--state-off); box-shadow: var(--halo-off); }

.status-label.is-ok { color: var(--state-ok); }
.status-label.is-wait { color: var(--state-wait); }
.status-label.is-off { color: var(--state-off); }

.status-meta {
  color: var(--text-label);
  font-family: var(--font-body);
  font-size: var(--text-sm);
}

/* -------------------------------------------------------------------------
   Forms
   ------------------------------------------------------------------------- */

label {
  display: block;
  font: var(--weight-medium) var(--text-sm)/var(--leading-snug) var(--font-body);
  color: var(--text-label);
  margin-bottom: var(--space-2);
}

input[type="text"],
input[type="number"],
input:not([type]),
textarea,
select {
  width: 100%;
  background: var(--surface-input);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-1);
  color: var(--text-body);
  font-family: var(--font-body);
  font-size: var(--text-base);
  padding: var(--pad-control);
  transition: var(--transition-control);
}

textarea {
  font-family: var(--font-data);
  font-size: var(--text-sm);
  resize: vertical;
}

input[type="text"]:focus,
input[type="number"]:focus,
input:not([type]):focus,
textarea:focus,
select:focus {
  border-color: var(--accent);
  background: var(--surface-panel);
}

button,
.btn,
.btn-primary {
  font-family: var(--font-body);
  font-weight: var(--weight-semibold);
  font-size: var(--text-sm);
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: var(--radius-pill);
  padding: var(--pad-control);
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  transition: var(--transition-control);
}

/* :hover/:active add a type selector, making these (0,1,1) — heavier than any
   single-class component rule like `.ig-calc-tier` or `.tab`, which then lost
   their own colours the moment a pointer touched them. `:where()` contributes
   zero specificity, so these stay at (0,1,0) and a component class always
   wins. */
button:where(:hover),
.btn:where(:hover),
.btn-primary:where(:hover) {
  background: var(--accent-hover);
  color: #fff;
}

button:where(:active),
.btn:where(:active),
.btn-primary:where(:active) {
  background: var(--accent-press);
}

button.btn-secondary,
.btn.btn-secondary {
  background: var(--surface-panel);
  color: var(--text-body);
  border: var(--hairline) solid var(--border-hairline);
}

button.btn-secondary:hover,
.btn.btn-secondary:hover {
  border-color: var(--border-strong);
  background: var(--surface-hover);
  color: var(--text-body);
}

/* The default button is the brand blue, so an irreversible action reads as
   heavier by being the only red thing on the page — red is destructive-only
   in this system, never the brand. */
button.btn-danger,
.btn.btn-danger {
  background: var(--deep-red);
  color: #fff;
}

button.btn-danger:hover,
.btn.btn-danger:hover {
  background: #a72424;
  color: #fff;
}

button:disabled,
.btn:disabled,
.btn-primary:disabled {
  opacity: .45;
  cursor: not-allowed;
}

.field {
  margin-bottom: 1.1rem;
}

.field-row,
.form-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.6rem;
}

/* Checkbox + its label as one click target, so the consequence note below can
   sit outside the label without breaking the hit area. */
.toggle-row {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  cursor: pointer;
}

.status-message {
  font-size: 0.85rem;
  color: var(--paper-dim);
  margin-top: 0.75rem;
  margin-bottom: 0;
}

.status-message.is-error {
  color: var(--state-danger);
}

.form-error {
  color: var(--state-danger);
  font-size: var(--text-sm);
  margin-top: var(--space-3);
  margin-bottom: var(--space-4);
}

/* -------------------------------------------------------------------------
   Tier selector — radio group styled as a row of toggles
   ------------------------------------------------------------------------- */

.tier-options {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-bottom: 1.25rem;
}

.tier-option {
  position: relative;
}

.tier-option input {
  position: absolute;
  opacity: 0;
  inset: 0;
  margin: 0;
  cursor: pointer;
}

.tier-option label {
  display: block;
  margin: 0;
  padding: 0.6rem 1rem;
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  font-family: var(--font-data);
  font-size: 0.85rem;
  color: var(--paper-dim);
  cursor: pointer;
}

.tier-option input:checked + label {
  border-color: var(--accent);
  color: var(--text-body);
  background: var(--brand-050);
  box-shadow: var(--elev-3);
}

.tier-option input:focus-visible + label {
  outline: 2px solid var(--signal);
  outline-offset: 2px;
}

/* -------------------------------------------------------------------------
   Tables — usage/pricing/tenant lists. Figures set in monospace and
   right-aligned so numbers stack for scanning, like a statement.
   ------------------------------------------------------------------------- */

table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}

/* Uppercase mono th — the second of the two places uppercase is allowed. */
th {
  text-align: left;
  font: var(--type-th, var(--weight-medium) var(--text-2xs)/1.2 var(--font-data));
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-label);
  padding: var(--space-4) var(--space-5);
  background: var(--surface-raised);
  border-bottom: var(--hairline) solid var(--border-hairline);
}

td {
  padding: var(--pad-cell);
  /* The softer between-rows rule is what makes a table read as a statement
     rather than a grid. */
  border-bottom: var(--hairline) solid var(--ink-700);
  color: var(--text-body);
}

tbody tr:hover td {
  background: var(--surface-hover);
}

tbody tr:last-child td {
  border-bottom: none;
}

.text-right,
th.text-right,
td.text-right,
.is-num {
  text-align: right;
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
}

.text-center,
th.text-center,
td.text-center {
  text-align: center;
}

.text-muted,
.muted {
  color: var(--text-muted);
}

tfoot td {
  border-bottom: none;
  border-top: var(--hairline) solid var(--border-strong);
  padding-top: var(--space-6);
  font-family: var(--font-data);
  font-weight: var(--weight-semibold);
}

/* Admin tables carry 6–9 columns; the dense padding is theirs. */
.shell-main.is-full td {
  padding: var(--pad-cell-dense);
}

.price-input {
  width: 8rem;
  text-align: right;
}

.field-label {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.field-hint,
.field-note {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-top: var(--space-2);
  margin-bottom: var(--space-3);
  display: block;
  line-height: var(--leading-body);
}

span.field-hint:not([data-tooltip]) {
  display: inline;
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.field-tooltip,
span.field-hint[data-tooltip] {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.05rem;
  height: 1.05rem;
  border-radius: 50%;
  border: 1px solid var(--border-hairline);
  color: var(--text-label);
  font-size: 0.7rem;
  font-style: italic;
  font-family: Georgia, "Times New Roman", serif;
  cursor: help;
  user-select: none;
}

/* CSS-only tooltip, not the native `title` attribute — `title` waits ~1s before showing and is
   easy to miss/dismiss by accident, which is why the field-hint icons alone were visible without
   their explanation reliably appearing. This shows immediately on hover/focus (keyboard-reachable
   via tabindex) and stays open long enough to read the (longer, impact-focused) explanations. */
.field-tooltip::after,
span.field-hint[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 0.5rem);
  left: 50%;
  transform: translateX(-50%) translateY(0.25rem);
  width: max-content;
  max-width: 22rem;
  padding: 0.6rem 0.75rem;
  border-radius: var(--radius-1);
  border: 1px solid var(--border-hairline);
  background: var(--surface-panel);
  color: var(--text-body);
  font-size: 0.8rem;
  font-style: normal;
  font-family: var(--font-body, inherit);
  line-height: 1.4;
  text-align: left;
  white-space: normal;
  box-shadow: var(--elev-4);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease, transform 0.12s ease;
  z-index: 20;
}

.field-tooltip:hover::after,
.field-tooltip:focus-visible::after,
span.field-hint[data-tooltip]:hover::after,
span.field-hint[data-tooltip]:focus-visible::after {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}

/* -------------------------------------------------------------------------
   Catalog grid — product cards, not a spreadsheet. Catalog rows carry
   arbitrary tenant-defined columns (color, size, category...) plus a photo,
   so a table forces either horizontal scroll or wrapped mush; a photo-led
   card is the shape this content actually has. Availability reuses the
   app's existing status-dot language (in stock / low / out) rather than
   inventing a new state indicator.
   ------------------------------------------------------------------------- */

.catalog-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 1.1rem;
}

.catalog-card {
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-2);
  box-shadow: var(--elev-1);
  overflow: hidden;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  transition: border-color var(--dur-1) var(--ease-out),
              box-shadow var(--dur-1) var(--ease-out);
}

.catalog-card:hover {
  border-color: var(--border-strong);
  box-shadow: var(--elev-3);
}

/* An expanded card spans the whole grid, so the collapsed 4/5 aspect-ratio would
   scale the photo box to the full row width — a screen-tall empty rectangle above
   the details, worst of all on items with no photo at all. Expanded, the photo is
   pinned to a fixed column beside the details instead of driving the card's height. */
.catalog-card.is-expanded {
  border-color: var(--signal);
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: minmax(0, 260px) minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  align-items: start;
}

.catalog-card.is-expanded .catalog-card-photo {
  aspect-ratio: 4 / 5;
  max-height: 320px;
  grid-row: 1;
  grid-column: 1;
}

.catalog-card.is-expanded .catalog-card-photo img {
  object-fit: contain;
}

.catalog-card.is-expanded .catalog-card-body {
  grid-row: 1;
  grid-column: 2;
}

.catalog-card.is-expanded .catalog-detail {
  grid-row: 2;
  grid-column: 1 / -1;
}

@media (max-width: 640px) {
  .catalog-card.is-expanded {
    grid-template-columns: minmax(0, 1fr);
  }

  .catalog-card.is-expanded .catalog-card-photo,
  .catalog-card.is-expanded .catalog-card-body,
  .catalog-card.is-expanded .catalog-detail {
    grid-column: 1;
    grid-row: auto;
  }
}

.catalog-card-photo {
  aspect-ratio: 4 / 5;
  background: var(--surface-input);
  overflow: hidden;
}

.catalog-card-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.catalog-card-photo.is-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--paper-faint);
  font-size: 0.75rem;
}

.catalog-card-body {
  padding: 0.9rem 1rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  flex: 1;
}

.catalog-card-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--paper);
  line-height: 1.3;
}

.catalog-card-price {
  font-family: var(--font-data);
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--paper);
}

.catalog-card-attrs {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  font-size: 0.8rem;
  color: var(--paper-dim);
  margin-top: 0.2rem;
}

.catalog-card-attrs span {
  color: var(--paper-faint);
  font-family: var(--font-data);
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-right: 0.4rem;
}

.catalog-card-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: auto;
  padding-top: 0.6rem;
}

.catalog-card-photo-count {
  font-family: var(--font-data);
  font-size: 0.75rem;
  color: var(--paper-faint);
}

/* -------------------------------------------------------------------------
   Catalog detail — expanded card content: full attribute list, photo
   gallery with reorder/delete, raw-row inspector, danger action.
   ------------------------------------------------------------------------- */

.catalog-detail {
  padding: 0 1rem 1.25rem;
  border-top: 1px solid var(--ink-line);
  margin-top: 0.5rem;
  padding-top: 1rem;
}

.catalog-detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 0.6rem 1.5rem;
  margin-bottom: 1.25rem;
}

.catalog-detail-attr {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.catalog-detail-attr dt {
  font-family: var(--font-data);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--paper-faint);
}

.catalog-detail-attr dd {
  margin: 0;
  font-size: 0.9rem;
  color: var(--paper);
}

.catalog-detail h4 {
  font-family: var(--font-display);
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--paper-dim);
  margin: 0 0 0.6rem;
}

.catalog-photo-progress {
  margin: 0.5rem 0 0;
  font-size: 0.78rem;
  color: var(--paper-faint);
}

.photo-gallery {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.photo-gallery-item {
  width: 120px;
}

.photo-gallery-item img {
  width: 120px;
  height: 150px;
  object-fit: cover;
  border-radius: var(--radius);
  border: 1px solid var(--ink-line);
  display: block;
}

.photo-gallery-actions {
  display: flex;
  gap: 0.3rem;
  margin-top: 0.4rem;
}

.photo-gallery-actions button {
  flex: 1;
  padding: 0.3rem 0.4rem;
  font-size: 0.72rem;
}

.catalog-availability {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.78rem;
}

/* -------------------------------------------------------------------------
   Pending-deletion confirm banner
   ------------------------------------------------------------------------- */

.import-confirm {
  margin-top: 1rem;
  padding: 0.9rem 1rem;
  border: var(--hairline) solid var(--state-wait);
  background: var(--state-wait-dim);
  border-radius: var(--radius);
}

.import-confirm p {
  color: var(--paper);
  margin-bottom: 0.75rem;
}

.import-confirm .field-row {
  display: flex;
  gap: 0.6rem;
  margin-bottom: 0;
}

.import-progress {
  margin-top: 1.25rem;
  padding: 1rem 1.25rem;
  background: var(--surface-raised);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-2);
}

.import-progress p {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--text-body);
  margin-bottom: var(--space-3);
}

.import-progress progress {
  width: 100%;
  height: 6px;
  border: none;
  border-radius: 999px;
  background: var(--surface-input);
  overflow: hidden;
  display: block;
}

.import-progress progress::-webkit-progress-bar {
  background: var(--surface-input);
  border-radius: 999px;
}

.import-progress progress::-webkit-progress-value {
  background: var(--brand-600);
  border-radius: 999px;
  transition: width var(--dur-2) var(--ease-out);
}

.import-progress progress::-moz-progress-bar {
  background: var(--brand-600);
  border-radius: 999px;
}

.import-spinner {
  width: 14px;
  height: 14px;
  border: 2px solid var(--border-strong);
  border-top-color: var(--brand-600);
  border-radius: 50%;
  display: inline-block;
  animation: spin 0.8s linear infinite;
}

/* Stands in for the colour input while a vision call decides the colour. Sized to match the input
   it replaces so the gallery doesn't reflow when detection starts and finishes. */
.photo-color-detecting {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 2.25rem;
  font-size: 0.78rem;
  color: var(--text-muted);
}

.import-hint {
  font-size: var(--text-xs) !important;
  color: var(--text-muted) !important;
  margin-top: var(--space-3) !important;
  margin-bottom: 0 !important;
}

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-6);
  margin-top: var(--space-8);
  padding-top: var(--space-6);
  border-top: var(--hairline) solid var(--border-hairline);
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.pagination span {
  font-variant-numeric: tabular-nums;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

@media (max-width: 640px) {
  .catalog-grid {
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 0.75rem;
  }
}

/* -------------------------------------------------------------------------
   LLM health table — mixed text (model/status) and numeric (success/failure
   count) columns, unlike the statement-style tables above where every column
   past the first is a right-aligned figure — so alignment is set per-column
   here instead of by nth-child.
   ------------------------------------------------------------------------- */

.llm-health-table th,
.llm-health-table td {
  text-align: left;
  font-family: var(--font-body);
}

/* Nine columns with non-wrapping identifiers overflow on narrow viewports.
   The wrapper scrolls, not the page; the table keeps table layout so its
   columns still size to content (display:block on the table would break
   that). .panel is shared with other pages, so it is left alone. */
.llm-health-scroll {
  overflow-x: auto;
}

/* Columns: 1 step, 2 model, 3 key, 4 requests/day, 5 requests/min, 6 success,
   7 failure, 8 status, 9 last ping. The key and the three figure columns are
   monospaced so masked suffixes and counters line up down the column. */
.llm-health-table th:nth-child(3),
.llm-health-table td:nth-child(3) {
  font-family: var(--font-data);
  white-space: nowrap;
}

/* The model name and the group header must not wrap mid-identifier — a key
   masked to "…St5g" split across two lines is unreadable, and wrapped model
   names make the group header hard to pick out from its key rows. */
.llm-health-table td:nth-child(2),
.llm-health-table .model-group-row td {
  white-space: nowrap;
}

.llm-health-table th:nth-child(6),
.llm-health-table td:nth-child(6),
.llm-health-table th:nth-child(7),
.llm-health-table td:nth-child(7) {
  text-align: right;
  font-family: var(--font-data);
}

/* Model group header — one row per model, its keys listed beneath, mirroring
   how the chain is written in config. Without it the table repeats the same
   model name once per key with nothing distinguishing the rows. */
.llm-health-table .model-group-row td {
  border-top: 1px solid var(--ink-line);
  padding-top: 0.75rem;
  font-weight: 600;
  font-family: var(--font-data);
}

.llm-health-table tbody tr:first-child.model-group-row td {
  border-top: none;
}

/* -------------------------------------------------------------------------
   Quota cells — "used / limit" with a fill bar, per (key × model) pair. The
   bar carries the reading at a glance; the figures stay for the exact value.
   ------------------------------------------------------------------------- */

.quota-cell {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.quota-bar {
  flex: 0 0 3rem;
  height: 4px;
  background: var(--ink-line);
  border-radius: var(--radius);
  overflow: hidden;
}

.quota-bar-fill {
  display: block;
  height: 100%;
  background: var(--ok);
}

.quota-text {
  font-family: var(--font-data);
  font-size: 0.85em;
  white-space: nowrap;
}

/* Exhausted reads as spent, not as broken: the step is still tried, just last. */
.quota-cell.is-exhausted .quota-bar-fill {
  background: var(--signal);
}

.quota-cell.is-exhausted .quota-text {
  color: var(--signal);
}

/* -------------------------------------------------------------------------
   Admin tabs — per-tier switch on the LLM health page. Same underline-active
   language as .shell-nav, but as buttons (client-side tab switch, not a
   route change) rather than links.
   ------------------------------------------------------------------------- */

.admin-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin: 1.25rem 0;
}

.admin-tabs {
  display: flex;
  gap: 1.5rem;
  margin-bottom: 1.25rem;
  border-bottom: 1px solid var(--ink-line);
}

.admin-tabs button {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  padding: 0 0 0.75rem;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 0.9rem;
  color: var(--paper-dim);
  cursor: pointer;
}

.admin-tabs button:hover {
  background: none;
  color: var(--paper);
}

.admin-tabs button.is-active,
.admin-tabs button.is-active:hover {
  background: none;
  color: var(--paper);
  border-bottom-color: var(--signal);
}

/* -------------------------------------------------------------------------
   Metric row — used at the top of the usage page for the month's headline
   numbers, set as a lit dashboard readout.
   ------------------------------------------------------------------------- */

.metric-row {
  display: flex;
  gap: 2.25rem;
  flex-wrap: wrap;
}

.metric {
  min-width: 140px;
}

.metric-value {
  font-family: var(--font-data);
  font-size: 1.7rem;
  font-weight: 600;
  color: var(--paper);
  line-height: 1.2;
}

.metric-value.is-accent {
  color: var(--signal);
}

.metric-label {
  font-size: 0.78rem;
  color: var(--paper-faint);
  margin-top: 0.3rem;
}

.shell-banner {
  background: var(--signal-dim);
  border-left: 3px solid var(--signal);
  color: var(--paper);
  padding: 0.85rem 1.1rem;
  margin-bottom: var(--gap);
  border-radius: var(--radius);
  font-size: 0.9rem;
}

.quota-bar {
  height: 8px;
  background: var(--ink-line);
  border-radius: var(--radius);
  overflow: hidden;
  margin: 0.9rem 0 0.5rem;
}

.quota-bar-fill {
  height: 100%;
  background: var(--ok);
  transition: width 0.2s ease;
}

.quota-bar-fill.is-warning {
  background: var(--wait);
}

.quota-bar-fill.is-exceeded {
  background: var(--signal);
}

/* -------------------------------------------------------------------------
   Stat cards — the headline figures at the top of a page, each in its own
   hairline card sitting directly on the ground rather than grouped inside one
   panel. A panel says "these belong together"; three separate cards say
   "three independent readings", which is what a balance summary is.

   auto-fit + minmax rather than a fixed column count: the same markup carries
   two cards or four without a variant class, and collapses to one column on a
   phone with no media query.
   ------------------------------------------------------------------------- */

.stat-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--space-7);
  margin-bottom: var(--panel-gap);
}

.stat-card {
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-2);
  padding: var(--pad-panel);
  box-shadow: var(--elev-2);
}

.stat-card-value {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  font-family: var(--font-data);
  font-size: var(--text-2xl);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--text-body);
}

.stat-card-value.is-accent {
  color: var(--accent);
}

/* The currency sign is set smaller and dimmed so the figure itself stays the
   thing the eye lands on — same treatment as the landing page's calculator. */
.stat-card-currency {
  font-size: var(--text-lg);
  font-weight: var(--weight-medium);
  opacity: .55;
}

/* Third of the three uppercase-letterspaced roles in this system, alongside
   panel h2 and table th: a caption naming a figure. */
.stat-card-label {
  margin: var(--space-4) 0 0;
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-label);
}

/* -------------------------------------------------------------------------
   Field grid — labelled inputs side by side, label stacked above its control.
   The generic .form-row puts label and input on one line, which works for a
   lone narrow field and not for a row of them.
   ------------------------------------------------------------------------- */

.field-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-6);
  margin-bottom: var(--space-7);
}

.field-grid .field-cell label {
  margin-bottom: var(--space-3);
}

/* .price-input is 8rem wide for the admin pricing table's inline cells; inside
   a grid cell it should fill the column it was given. */
.field-grid .field-cell .price-input {
  width: 100%;
  text-align: left;
}

/* -------------------------------------------------------------------------
   Page list (connect-instagram page picker)
   ------------------------------------------------------------------------- */

.pick-list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.pick-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.85rem 1.1rem;
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
}

.pick-row span {
  font-weight: 500;
}

.choice-row {
  display: flex;
  gap: 0.75rem;
  margin-top: 1.25rem;
}

/* -------------------------------------------------------------------------
   Prompt builder (/panel/prompt) — answers on the left, the composed prompt
   on the right. The preview is the signature: the tenant watches their
   answers compile into the literal text the model receives, which is what
   makes a form feel like it hasn't taken the prompt away from them. It is a
   <pre>, not a styled paragraph, precisely because it IS the artifact.
   ------------------------------------------------------------------------- */

.builder-lede {
  max-width: 52ch;
  margin-top: -1.25rem;
  margin-bottom: 2rem;
}

.builder {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 26rem);
  align-items: start;
  gap: var(--gap);
}

.builder-form .panel:last-child {
  margin-bottom: 0;
}

.builder-preview-sticky {
  position: sticky;
  top: var(--space-6);
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-2);
  box-shadow: var(--elev-2);
  padding: var(--pad-panel);
}

.builder-preview-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1rem;
}

.builder-preview-head h2 {
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--paper-dim);
  margin: 0;
}

.builder-preview-count {
  font-family: var(--font-data);
  font-size: 0.72rem;
  color: var(--paper-faint);
  white-space: nowrap;
}

.builder-preview-body {
  margin: 0;
  padding: var(--space-6);
  max-height: 26rem;
  overflow-y: auto;
  background: var(--surface-input);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-1);
  font-family: var(--font-data);
  font-size: 0.78rem;
  line-height: 1.65;
  color: var(--paper);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.builder-preview-empty {
  margin: 0;
  padding: 1rem;
  border: 1px dashed var(--ink-line);
  border-radius: var(--radius);
  font-size: 0.83rem;
  color: var(--paper-faint);
}

.builder-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1.25rem;
}

/* Sits above the first section, so it reads as a shortcut into the form rather
   than one more field inside it. */
.builder-actions-top {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.6rem 0.9rem;
  margin-bottom: var(--gap);
}

.builder-actions-hint {
  font-size: 0.8rem;
  color: var(--paper-faint);
}

.builder-actions-warning {
  flex-basis: 100%;
  margin: 0;
  font-size: 0.85rem;
  color: var(--wait);
}

.field-note {
  margin: 0.4rem 0 0;
  font-size: 0.78rem;
  color: var(--paper-faint);
}

/* The builder's answers are prose the tenant writes, not data — so they get the
   body face, overriding the global monospace `textarea` rule that makes an empty
   field's placeholder read as pre-filled content. */
.builder-form textarea {
  font-family: var(--font-body);
  font-size: 0.92rem;
  line-height: 1.5;
}

/* Placeholders must never be mistakable for a filled-in value: dimmer than the
   real text and italic, so "empty" is legible at a glance. */
.builder-form input::placeholder,
.builder-form textarea::placeholder {
  color: var(--paper-faint);
  font-style: italic;
  opacity: 0.75;
}

/* A field the tenant has actually filled in reads stronger than its neighbours. */
.builder-form input:not(:placeholder-shown),
.builder-form textarea:not(:placeholder-shown) {
  border-color: var(--border-strong);
  color: var(--paper);
}

/* Excerpt of the saved prompt on /panel — clamped, since the panel only needs
   to answer "is this filled in", not to be read. */
.prompt-preview {
  font-family: var(--font-data);
  font-size: 0.8rem;
  line-height: 1.6;
  color: var(--paper-dim);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  margin: 0;
}

@media (max-width: 900px) {
  .builder {
    grid-template-columns: 1fr;
  }
  .builder-preview-sticky {
    position: static;
  }
}

/* -------------------------------------------------------------------------
   Utility
   ------------------------------------------------------------------------- */

.muted {
  color: var(--paper-faint);
}

/* The raw IGSID shown while a name lookup is in flight, or when it came back empty.
   Dimmed so a resolved name reads as the normal case and an id as the exception. */
.conversation-sender-id {
  color: var(--paper-faint);
}

/* Collapsed Telegram group setup guide: long enough to need hiding by default,
   but the first thing a tenant looks for when topics are not working. */
.tg-guide {
  border: 1px solid var(--ink-line);
  border-radius: 8px;
  padding: 0.75rem 1rem;
}

.tg-guide > summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--paper);
}

.tg-guide[open] > summary {
  margin-bottom: 0.75rem;
}

.tg-steps {
  margin: 0.5rem 0 0.75rem;
  padding-left: 1.25rem;
  line-height: 1.65;
}

.tg-steps li + li {
  margin-top: 0.6rem;
}

.tg-note {
  display: block;
  font-size: 0.85rem;
  color: var(--paper-faint);
}

.tg-cmd {
  display: inline-block;
  margin-top: 0.35rem;
  word-break: break-all;
}

.mt-lg {
  margin-top: var(--gap);
}

@media (max-width: 640px) {
  .shell-header {
    padding: 1rem;
  }
  .shell-main {
    padding: 1.75rem 1rem 3rem;
  }
  /* .shell-main.is-full sets padding-left/right as longhands, which outrank
     the shorthand above on specificity regardless of source order — without
     this the full-width pages would keep desktop side padding on a phone. */
  .shell-main.is-full {
    padding-left: 1rem;
    padding-right: 1rem;
  }
  .metric-row {
    gap: 1.5rem;
  }
}

/* ==========================================================================
   Landing page ("/") — the public surface. Same tokens as the app, but a
   marketing rhythm the app does not have: alternating white / --brand-050
   bands, each with a drifting blurred cloud field behind it, and generous
   vertical air.

   The atmosphere is the design system's one background treatment: blurred
   blue clouds behind white content (tokens/motifs.css). No gradients washes,
   no photography, no texture — the softness comes from flat tint plus space.

   Colours and fonts come from the shared palette at the top of this file;
   the --ig-* names below are aliases onto it, not a second definition.
   Headings are Inter: the display face ships no Cyrillic, so a Ukrainian
   heading set in it would silently render Inter anyway.
   ========================================================================== */

.ig {
  /* Nothing on a landing page justifies sideways scrolling; a single track
     that refuses to shrink used to drag the whole page out of alignment. */
  overflow-x: hidden;

  --ig-ink: var(--chat-ink);
  --ig-surface: var(--chat-surface);
  --ig-surface-raised: var(--chat-raised);
  --ig-line: var(--chat-line);
  --ig-paper: var(--chat-paper);
  --ig-paper-dim: var(--chat-paper-dim);
  --ig-paper-faint: var(--chat-paper-faint);
  --ig-accent: var(--chat-accent);
  --ig-accent-dim: var(--chat-accent-dim);
  --ig-live: var(--chat-live);

  --ig-font-display: var(--chat-font-display);
  --ig-font-body: var(--chat-font-body);
  --ig-font-data: var(--chat-font-data);

  background: var(--surface-panel);
  color: var(--ig-paper);
  min-height: 100vh;
  font-family: var(--ig-font-body);
}

.ig h1,
.ig h2,
.ig h3 {
  font-family: var(--ig-font-display);
  color: var(--ig-paper);
  margin: 0 0 var(--space-5);
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-tight);
}

.ig p {
  color: var(--ig-paper-dim);
  margin: 0 0 var(--space-5);
  line-height: var(--leading-body);
}

/* Bare links inherit the surrounding text colour, but not the buttons: `.ig a`
   is 0,1,1 and out-specifies `.ig-btn-primary` at 0,1,0, so a CTA rendered as
   an <a> was taking --paper on its brand fill — dark text on blue. */
.ig a:not(.ig-btn) {
  color: inherit;
}

/* Alternating tint bands. Each .ig-band is a positioned ancestor so its own
   .cloud-field can sit behind its content. */
.ig-band {
  position: relative;
  overflow: hidden;
}

.ig-band.is-tint {
  background: var(--brand-050);
  border-block: var(--hairline) solid var(--border-hairline);
}

.ig-band > * {
  position: relative;
  z-index: 1;
}

/* Antigravity particles canvas background — signature fluid particle atmosphere */
.antigravity-particles-container {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

.antigravity-canvas {
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* Blurred cloud field — decorative backdrop treatment */
.cloud-field {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

/* A radial gradient, not a flat disc: a solid --brand-300 circle under a 70px
   blur reads as a slab of colour with a soft edge, which is what made the
   tinted bands look like a filled block rather than atmosphere. The gradient
   falls off from the centre, so the blur softens an already-soft shape. */
.cloud {
  position: absolute;
  border-radius: 50%;
  background: var(--cloud-blue);
  filter: blur(var(--blur-cloud));
  opacity: .5;
  will-change: transform;
  animation: ib-drift var(--dur-drift) var(--ease-in-out) infinite alternate;
}

.cloud:nth-child(2) { animation-duration: calc(var(--dur-drift) * 1.45); animation-delay: -6s; }
.cloud:nth-child(3) { animation-duration: calc(var(--dur-drift) * .8); animation-delay: -14s; }
.cloud:nth-child(4) { animation-duration: calc(var(--dur-drift) * 1.2); animation-delay: -3s; }
.cloud:nth-child(5) { animation-duration: calc(var(--dur-drift) * 1.7); animation-delay: -20s; }

@keyframes ib-drift {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to { transform: translate3d(6%, -5%, 0) scale(1.18); }
}

@media (prefers-reduced-motion: reduce) {
  .cloud { animation: none; }
}

/* -------------------------------------------------------------------------
   Nav
   ------------------------------------------------------------------------- */

.ig-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  max-width: var(--shell-max);
  margin: 0 auto;
  padding: var(--space-7) var(--space-8);
}

/* "MriiaBot" is a Latin string, so --font-display genuinely applies here —
   this is one of the few places in the product where it does. */
.ig-wordmark {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: var(--text-md);
  letter-spacing: var(--tracking-tight);
  text-decoration: none;
  color: var(--ig-paper);
  display: flex;
  align-items: center;
  gap: var(--space-5);
}

.ig-wordmark::before {
  content: '';
  width: var(--dot-size);
  height: var(--dot-size);
  border-radius: 50%;
  background: var(--accent);
  box-shadow: var(--halo-brand);
  flex-shrink: 0;
}

/* -------------------------------------------------------------------------
   Buttons
   ------------------------------------------------------------------------- */

/* Matches the design system's Button at size lg: --pad-control-lg with
   --text-base and the 44px tap minimum. It previously took the large padding
   with --text-sm, which read as a big button wrapped around small type.
   Letter-spacing and the border are from the component spec too — primary
   carries a border in its own fill colour, not a transparent one, so it keeps
   the same box as the secondary variant beside it. */
.ig-btn {
  font-family: var(--ig-font-body);
  font-weight: var(--weight-semibold);
  font-size: var(--text-base);
  letter-spacing: -.005em;
  line-height: 1;
  border-radius: var(--radius-pill);
  padding: var(--pad-control-lg);
  min-height: var(--tap-min);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  border: var(--hairline) solid transparent;
  transition: var(--transition-control);
  white-space: nowrap;
}

.ig-btn-primary {
  background: var(--brand-600);
  border-color: var(--brand-600);
  color: #fff;
  box-shadow: var(--elev-brand);
}

.ig-btn-primary:hover {
  background: var(--brand-700);
  border-color: var(--brand-700);
  color: #fff;
}

.ig-btn-primary:active {
  background: var(--brand-800);
  border-color: var(--brand-800);
}

/* The system's `secondary`: white fill, --border-strong, --elev-1. */
.ig-btn-ghost {
  background: var(--surface-panel);
  color: var(--paper);
  border-color: var(--border-strong);
  box-shadow: var(--elev-1);
}

.ig-btn-ghost:hover {
  background: var(--surface-hover);
  color: var(--paper);
}

/* Trailing dot on a CTA — the same status dot the whole system uses as its
   icon set, here as a quiet "live" cue rather than an arrow glyph. It reads as
   part of the label, so it sits closer than the button's own gap allows. */
.ig-btn.has-dot::after {
  content: '';
  width: 6px;
  height: 6px;
  margin-left: calc(var(--space-4) * -1 + var(--space-2));
  border-radius: 50%;
  background: currentColor;
  opacity: .7;
  flex-shrink: 0;
}

/* -------------------------------------------------------------------------
   Badge — the state family as a small label. Same four states as the dot.
   ------------------------------------------------------------------------- */

.ig-badge {
  display: inline-block;
  font-family: var(--ig-font-data);
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  border-radius: var(--radius-1);
  border: var(--hairline) solid currentColor;
  padding: 2px 6px;
  white-space: nowrap;
}

.ig-badge.is-ok { color: var(--state-ok); background: var(--state-ok-dim); }
.ig-badge.is-wait { color: var(--state-wait); background: var(--state-wait-dim); }
.ig-badge.is-off { color: var(--paper-dim); background: var(--state-off-dim); }

/* -------------------------------------------------------------------------
   Hero — copy left, the live DM thread right. The thread IS the pitch, so it
   is a working demo rather than a screenshot, and it keeps its real size.
   ------------------------------------------------------------------------- */

/* minmax(0, 1fr), not 1fr: a bare 1fr track floors at its content's min-content
   width, so the long headline refused to wrap, widened the grid past the page
   and pushed everything below it into a horizontal overflow. */
.ig-hero {
  max-width: var(--shell-max);
  margin: 0 auto;
  padding: var(--space-11) var(--space-8);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 380px);
  gap: var(--space-11);
  align-items: center;
}

/* Pill eyebrow — a bordered chip on white, per the reference. */
.ig-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  font-family: var(--ig-font-body);
  font-weight: var(--weight-medium);
  font-size: var(--text-xs);
  color: var(--ig-paper-dim);
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-pill);
  box-shadow: var(--elev-1);
  padding: var(--space-3) var(--space-6);
  margin: 0 0 var(--space-7);
}

.ig-eyebrow::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--state-ok);
  box-shadow: var(--halo-ok);
  flex-shrink: 0;
}

.ig-hero h1 {
  font-size: var(--text-3xl);
  font-weight: var(--weight-bold);
  margin-bottom: var(--space-7);
}

.ig-lede {
  font-size: var(--text-md);
  line-height: var(--leading-loose);
  max-width: 48ch;
  margin-bottom: var(--space-9);
}

.ig-cta-row {
  display: flex;
  gap: var(--space-5);
  flex-wrap: wrap;
}

/* -------------------------------------------------------------------------
   The phone — the signature element. A DM thread, not a card.
   ------------------------------------------------------------------------- */

.ig-phone {
  background: var(--ig-surface);
  border: var(--hairline) solid var(--ig-line);
  border-radius: var(--radius-3);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: var(--elev-4);
}

.ig-phone-header {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  padding: var(--space-6);
  border-bottom: var(--hairline) solid var(--ig-line);
}

/* The one place Instagram's own gradient appears, and only as an avatar —
   the page must never read as impersonating the Instagram product. */
.ig-avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  flex-shrink: 0;
  background: linear-gradient(135deg, var(--ig-accent), #8b5ef0 60%, #e0538a);
}

.ig-phone-header-text {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.ig-phone-header-text strong {
  font-size: var(--text-sm);
  color: var(--ig-paper);
}

.ig-status {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-xs);
  color: var(--ig-live);
}

.ig-status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--ig-live);
}

.ig-thread {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-6);
  background: var(--chat-bg);
  min-height: 160px;
  max-height: 360px;
  overflow-y: auto;
}

.ig-day-divider {
  text-align: center;
  font-size: var(--text-2xs);
  color: var(--ig-paper-faint);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  margin-bottom: var(--space-2);
}

.ig-bubble {
  max-width: 82%;
  padding: var(--space-5) var(--space-6);
  border-radius: var(--radius-2);
  font-size: var(--text-sm);
  line-height: var(--leading-body);
}

.ig-bubble span {
  color: inherit;
}

.ig-bubble.is-user {
  align-self: flex-end;
  background: var(--chat-out-bg);
  color: var(--chat-out-fg);
  border-bottom-right-radius: var(--space-2);
}

.ig-bubble.is-bot {
  align-self: flex-start;
  background: var(--chat-in-bg);
  color: var(--chat-in-fg);
  border: var(--hairline) solid var(--ig-line);
  border-bottom-left-radius: var(--space-2);
}

/* Without its own rule this fell through to the global .is-error, which is
   sized for a dark ground: red text and no bubble at all. */
.ig-bubble.is-error {
  align-self: flex-start;
  background: var(--state-danger-dim);
  color: var(--state-danger);
  border-bottom-left-radius: var(--space-2);
}

.ig-typing-dots {
  display: inline-flex;
  gap: 3px;
  padding: var(--space-1) 0;
}

.ig-typing-dots i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--chat-typing);
  animation: ig-typing-bounce 1.1s infinite ease-in-out;
}

.ig-typing-dots i:nth-child(2) { animation-delay: 0.15s; }
.ig-typing-dots i:nth-child(3) { animation-delay: 0.3s; }

@keyframes ig-typing-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.5; }
  30% { transform: translateY(-3px); opacity: 1; }
}

.ig-composer {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-5);
  border-top: var(--hairline) solid var(--ig-line);
}

.ig-composer input {
  flex: 1;
  background: var(--surface-input);
  border: var(--hairline) solid var(--ig-line);
  border-radius: var(--radius-pill);
  color: var(--ig-paper);
  font-family: var(--ig-font-body);
  font-size: var(--text-sm);
  padding: var(--space-5) var(--space-6);
  transition: var(--transition-control);
}

.ig-composer input:focus {
  outline: none;
  border-color: var(--ig-accent);
  background: var(--surface-panel);
}

.ig-composer button {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: none;
  background: var(--ig-accent);
  color: #fff;
  font-size: var(--text-base);
  cursor: pointer;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: var(--transition-control);
}

.ig-composer button:hover:not(:disabled) {
  background: var(--accent-hover);
}

.ig-composer button:disabled {
  background: var(--surface-input);
  color: var(--text-disabled);
  cursor: not-allowed;
  opacity: 1;
}

/* The caption and any error belong to the phone, so they live in its column.
   Full-bleed, they started at the page's left edge and read as a stray line
   under empty space rather than as a note about the demo beside them. */
.ig-phone-col {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  min-width: 0;
}

.ig-inline-error {
  margin: 0;
  color: var(--state-danger);
  font-size: var(--text-sm);
}

.ig-demo-caption {
  margin: 0;
  font-size: var(--text-xs);
  line-height: var(--leading-snug);
  color: var(--ig-paper-faint);
  text-align: center;
}

/* -------------------------------------------------------------------------
   Sections
   ------------------------------------------------------------------------- */

/* --space-13 top and bottom used to stack into 192px between neighbours —
   half a screen of nothing, six times down the page. One section owns one
   gap; a band's own padding closes the ends. */
.ig-section {
  max-width: var(--shell-max);
  margin: 0 auto;
  padding: var(--space-11) var(--space-8);
}

/* Consecutive sections inside one band share a single separation rather than
   adding both their paddings. */
.ig-section + .ig-section {
  padding-top: 0;
}

.ig-section-head {
  max-width: 60ch;
  margin-bottom: var(--space-9);
}

.ig-section-title {
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
}

.ig-lede-sm {
  max-width: 62ch;
  font-size: var(--text-base);
  margin-bottom: 0;
}

/* Steps — a real ordered sequence (connect -> configure -> run), so the
   numbering encodes true information rather than decorating a grid of three. */
.ig-steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-8);
}

.ig-step {
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-3);
  box-shadow: var(--elev-2);
  padding: var(--space-9);
}

/* The step marker is the status dot again — when this system reaches for an
   icon, it reaches for the dot. */
.ig-step-mark {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  font-family: var(--ig-font-data);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--ig-accent);
  margin-bottom: var(--space-7);
}

.ig-step-mark::before {
  content: '';
  width: var(--dot-size);
  height: var(--dot-size);
  border-radius: 50%;
  background: var(--accent);
  box-shadow: var(--halo-brand);
  flex-shrink: 0;
}

.ig-step h3 {
  font-size: var(--text-lg);
  margin-bottom: var(--space-5);
}

.ig-step p {
  margin-bottom: 0;
  font-size: var(--text-sm);
}

/* -------------------------------------------------------------------------
   Feature cards — a claim, its explanation, and a visual area showing the
   mechanism rather than a stock illustration.
   ------------------------------------------------------------------------- */

/* Fixed two columns, not auto-fit: at every desktop width the content column
   fits three 360px tracks, so four cards laid out 3 + 1 and left two empty
   cells in the second row. Four cards want 2x2. */
.ig-features {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-8);
}

.ig-feature {
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-3);
  box-shadow: var(--elev-2);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* The visual sits on the tint so it reads as a specimen inside the card
   rather than more card. */
.ig-feature-visual {
  background: var(--brand-050);
  border-bottom: var(--hairline) solid var(--border-hairline);
  padding: var(--space-8);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-5);
  /* Enough to keep the four visuals on a consistent baseline, but not so much
     that a single 96px stat floats in its own empty box. */
  min-height: 176px;
}

/* Cards in a row stretch to the tallest, so the body takes the slack rather
   than leaving it under the last line of text. */
.ig-feature-body {
  padding: var(--space-8) var(--space-9) var(--space-9);
  flex: 1;
}

.ig-feature-body h3 {
  font-size: var(--text-lg);
  margin-bottom: var(--space-5);
}

.ig-feature-body p {
  margin-bottom: 0;
  font-size: var(--text-sm);
}

/* Big stat — a figure that is true by construction (a platform limit, a
   configured constant), never a measured performance claim. The caption
   carries the unit so the number can stay bare. */
.ig-bigstat {
  text-align: center;
}

.ig-bigstat-value {
  width: 96px;
  height: 96px;
  margin: 0 auto;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: var(--elev-brand);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: var(--weight-bold);
  font-size: var(--text-xl);
  line-height: 1;
  letter-spacing: -.02em;
  color: #fff;
}

.ig-bigstat-caption {
  margin-top: var(--space-6);
  font-family: var(--ig-font-data);
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--ig-paper-dim);
}

/* Mini card — one row of the product's own output, used inside a feature
   visual. Elevated above the tint it sits on. */
.ig-mini {
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-2);
  box-shadow: var(--elev-3);
  padding: var(--space-5) var(--space-6);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
}

.ig-mini-title {
  display: block;
  font-weight: var(--weight-semibold);
  font-size: var(--text-sm);
  line-height: 1.2;
  color: var(--ig-paper);
}

.ig-mini-detail {
  display: block;
  margin-top: 2px;
  font-size: var(--text-xs);
  color: var(--ig-paper-faint);
}

/* Confidence rows — the real handover mechanism (CommentConfidenceGate), so
   the figures are illustrative of behaviour, not performance claims. */
.ig-signal {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  font-size: var(--text-sm);
}

.ig-signal-dot {
  width: var(--dot-size);
  height: var(--dot-size);
  border-radius: 50%;
  flex-shrink: 0;
  align-self: center;
}

.ig-signal.is-ok .ig-signal-dot { background: var(--state-ok); box-shadow: var(--halo-ok); }
.ig-signal.is-wait .ig-signal-dot { background: var(--state-wait); box-shadow: var(--halo-wait); }
.ig-signal.is-off .ig-signal-dot { background: var(--state-off); box-shadow: var(--halo-off); }

.ig-signal-label {
  font-family: var(--ig-font-data);
  color: var(--ig-paper);
}

.ig-signal-detail {
  margin-left: auto;
  font-size: var(--text-xs);
  color: var(--ig-paper-faint);
}

/* -------------------------------------------------------------------------
   Channels — a white card with a vertical tab rail. Each tab is one real
   inbound path through ProcessingWorker, not a separate "agent".
   ------------------------------------------------------------------------- */

.ig-channels {
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-3);
  box-shadow: var(--elev-3);
  padding: var(--space-9);
  display: grid;
  /* The rail holds three short labels; 250px left a visible trough between it
     and the pane. max-content sizes it to the longest label. */
  grid-template-columns: max-content 1fr;
  gap: var(--space-9);
  align-items: start;
}

.ig-rail {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* Radio-driven so the tabs work with no JS — the page renders under static
   SSR on first paint, and a click handler would be dead there.

   The inputs are siblings of .ig-channels, not children of .ig-rail: the
   :checked ~ selectors below need them to precede the panel. Hiding them has
   to target that position, and must not use display:none or visibility:hidden,
   which would take them out of the tab order and strand keyboard users. */
.ig-channel-input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The rail label is the visible control, so it carries the focus ring on
   behalf of its visually-hidden input. */
#ch-dm:focus-visible ~ .ig-channels label[for="ch-dm"],
#ch-comments:focus-visible ~ .ig-channels label[for="ch-comments"],
#ch-handoff:focus-visible ~ .ig-channels label[for="ch-handoff"] {
  box-shadow: var(--ring-focus);
}

.ig-rail label {
  display: block;
  margin: 0;
  padding: var(--space-5) var(--space-6);
  border-radius: var(--radius-1);
  border-left: 2px solid transparent;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--ig-paper-dim);
  cursor: pointer;
  transition: var(--transition-control);
}

.ig-rail label:hover {
  background: var(--surface-hover);
  color: var(--ig-paper);
}

.ig-channel-pane {
  display: none;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-9);
  align-items: center;
}

.ig-channel-pane h3 {
  font-size: var(--text-lg);
  margin-bottom: var(--space-6);
}

.ig-channel-pane p {
  font-size: var(--text-sm);
  margin-bottom: 0;
}

.ig-channel-rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

/* Each radio reveals its own pane. Written out per channel because CSS has no
   way to index siblings by position. */
#ch-dm:checked ~ .ig-channels .ig-channel-pane.is-dm,
#ch-comments:checked ~ .ig-channels .ig-channel-pane.is-comments,
#ch-handoff:checked ~ .ig-channels .ig-channel-pane.is-handoff {
  display: grid;
}

#ch-dm:checked ~ .ig-channels label[for="ch-dm"],
#ch-comments:checked ~ .ig-channels label[for="ch-comments"],
#ch-handoff:checked ~ .ig-channels label[for="ch-handoff"] {
  background: var(--brand-050);
  border-left-color: var(--accent);
  color: var(--ig-paper);
  font-weight: var(--weight-semibold);
}

/* -------------------------------------------------------------------------
   Before / after — two columns, the "after" one bordered in brand so the
   difference is carried by the surface rather than by an arrow.
   ------------------------------------------------------------------------- */

.ig-compare {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-8);
}

.ig-compare-col {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.ig-compare-head {
  font-family: var(--ig-font-data);
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--ig-paper-faint);
  padding-bottom: var(--space-3);
}

.ig-compare-col.is-after .ig-compare-head {
  color: var(--accent);
}

.ig-compare-item {
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-2);
  box-shadow: var(--elev-1);
  padding: var(--space-6) var(--space-7);
}

.ig-compare-col.is-after .ig-compare-item {
  border-color: var(--brand-300);
  box-shadow: var(--elev-2);
}

.ig-compare-item strong {
  display: block;
  font-weight: var(--weight-semibold);
  font-size: var(--text-sm);
  line-height: 1.3;
  color: var(--ig-paper-dim);
}

.ig-compare-col.is-after .ig-compare-item strong {
  color: var(--ig-paper);
}

.ig-compare-item span {
  display: block;
  margin-top: var(--space-3);
  font-size: var(--text-xs);
  color: var(--ig-paper-faint);
}

/* -------------------------------------------------------------------------
   FAQ — native <details>, so it works with no JS under static SSR. The
   marker is the system's own ▾, one of its two sanctioned glyphs.
   ------------------------------------------------------------------------- */

.ig-faq {
  display: grid;
  grid-template-columns: 1fr 1.6fr;
  gap: var(--space-9);
  align-items: start;
}

/* The heading column is short next to six accordion rows; without this it
   reads as a stranded label with a column of empty space under it. */
.ig-faq .ig-section-head {
  position: sticky;
  top: var(--space-9);
  margin-bottom: 0;
}

.ig-faq-list {
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-2);
  box-shadow: var(--elev-2);
  overflow: hidden;
}

.ig-faq-item {
  border-bottom: var(--hairline) solid var(--ink-700);
}

.ig-faq-item:last-child {
  border-bottom: none;
}

.ig-faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  padding: var(--space-7) var(--space-8);
  cursor: pointer;
  font-weight: var(--weight-semibold);
  font-size: var(--text-sm);
  color: var(--ig-paper);
  list-style: none;
  transition: var(--transition-control);
}

.ig-faq-item summary::-webkit-details-marker {
  display: none;
}

.ig-faq-item summary:hover {
  background: var(--surface-hover);
}

.ig-faq-item summary::after {
  content: '▾';
  flex-shrink: 0;
  color: var(--ig-paper-faint);
  transition: transform var(--dur-2) var(--ease-out);
}

.ig-faq-item[open] summary::after {
  transform: rotate(180deg);
}

.ig-faq-item p {
  margin: 0;
  padding: 0 var(--space-8) var(--space-7);
  font-size: var(--text-sm);
}

/* -------------------------------------------------------------------------
   Pricing — plain rows, not glossy tiers. The pitch is "pay for use", so the
   presentation stays unglamorous on purpose.
   ------------------------------------------------------------------------- */

.ig-calc {
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-3);
  padding: var(--space-10);
  margin-bottom: var(--space-10);
  box-shadow: var(--elev-3);
}

.ig-calc-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-6);
  margin-bottom: var(--space-5);
}

.ig-calc-row label {
  font-size: var(--text-sm);
  color: var(--ig-paper-dim);
  margin: 0;
}

.ig-calc-count {
  font-family: var(--ig-font-data);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  color: var(--ig-paper);
}

/* display:block, because an inline-level input sits on the text baseline and
   leaves a descender's worth of gap under the track. */
.ig-calc-slider {
  display: block;
  width: 100%;
  appearance: none;
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--border-hairline);
  margin: var(--space-4) 0 var(--space-9);
  cursor: pointer;
}

.ig-calc-slider:focus-visible {
  outline: none;
  box-shadow: var(--ring-focus);
}

.ig-calc-slider::-webkit-slider-thumb {
  appearance: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--ig-accent);
  cursor: pointer;
  border: 3px solid var(--surface-panel);
  box-shadow: var(--elev-brand);
}

.ig-calc-slider::-moz-range-thumb {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--ig-accent);
  cursor: pointer;
  border: 3px solid var(--surface-panel);
  box-shadow: var(--elev-brand);
}

.ig-calc-tiers {
  display: flex;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--space-9);
}

.ig-calc-tier {
  font-family: var(--ig-font-body);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--ig-paper-dim);
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-pill);
  padding: var(--pad-control);
  cursor: pointer;
  transition: var(--transition-control);
}

/* Scoped with :not(.is-active) rather than by piling on specificity: the
   selected chip must keep its brand fill on hover, and a heavier hover rule
   would repaint it light-on-light and make it vanish. */
.ig-calc-tier:not(.is-active):hover,
.ig-calc-tier:not(.is-active):active {
  border-color: var(--border-strong);
  background: var(--surface-hover);
  color: var(--ig-paper);
}

.ig-calc-tier.is-active,
.ig-calc-tier.is-active:hover,
.ig-calc-tier.is-active:active {
  background: var(--brand-600);
  border-color: var(--brand-600);
  color: #fff;
}

.ig-calc-result {
  padding-top: var(--space-8);
  border-top: var(--hairline) solid var(--border-hairline);
}

/* The figure is the point of the whole section, so it is set at display size
   with the currency mark as a lighter companion rather than part of the
   number. Money is data, so both are --font-data. */
.ig-calc-figure {
  display: flex;
  align-items: baseline;
  gap: var(--space-5);
  flex-wrap: wrap;
}

.ig-calc-currency {
  font-family: var(--ig-font-data);
  font-weight: var(--weight-medium);
  font-size: var(--text-2xl);
  color: var(--ig-accent);
  opacity: .55;
}

.ig-calc-result-value {
  font-family: var(--ig-font-data);
  font-weight: var(--weight-bold);
  font-size: var(--text-4xl);
  line-height: 1;
  letter-spacing: var(--tracking-tight);
  color: var(--ig-accent);
}

.ig-calc-result-label {
  font-size: var(--text-sm);
  color: var(--ig-paper-faint);
}

/* Per-tier breakdown under the figure: what the estimate is actually made of,
   so the number is checkable rather than asserted. */
.ig-calc-breakdown {
  margin-top: var(--space-7);
  border-top: var(--hairline) solid var(--ink-700);
  padding-top: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.ig-calc-breakdown-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-6);
  font-family: var(--ig-font-data);
  font-size: var(--text-xs);
  color: var(--ig-paper-faint);
}

.ig-calc-breakdown-row.is-current {
  color: var(--ig-paper-dim);
}

.ig-calc-note {
  margin: var(--space-6) 0 0;
  font-family: var(--ig-font-data);
  font-size: var(--text-2xs);
  color: var(--ig-paper-faint);
}

/* -------------------------------------------------------------------------
   Pricing panels — the calculator beside a plain statement of what counts as
   a billable message, so the estimate and its rules read as one answer.
   ------------------------------------------------------------------------- */

.ig-pricing-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: var(--space-8);
  align-items: start;
  margin-bottom: var(--space-10);
}

.ig-pricing-panel {
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-3);
  box-shadow: var(--elev-2);
  overflow: hidden;
}

.ig-pricing-panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-6);
  padding: var(--space-7) var(--space-8);
  border-bottom: var(--hairline) solid var(--border-hairline);
}

/* Uppercase + letterspacing: a panel title, one of the system's two
   sanctioned uses. */
.ig-pricing-panel-head h3 {
  margin: 0;
  font-family: var(--ig-font-body);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--ig-paper-dim);
}

.ig-pricing-panel-note {
  font-family: var(--ig-font-data);
  font-size: var(--text-2xs);
  text-transform: lowercase;
  letter-spacing: var(--tracking-caps);
  color: var(--ig-paper-faint);
}

.ig-pricing-panel-body {
  padding: var(--space-8);
}

/* What counts — a statement table, per the system's table rules: label left,
   value right, monospace, hairline rules between rows. */
.ig-counts {
  width: 100%;
  border-collapse: collapse;
}

.ig-counts th {
  padding: var(--space-4) var(--space-8);
  background: var(--surface-raised);
  border-bottom: var(--hairline) solid var(--border-hairline);
}

.ig-counts td {
  padding: var(--space-6) var(--space-8);
  border-bottom: var(--hairline) solid var(--ink-700);
  font-size: var(--text-sm);
  color: var(--ig-paper);
}

.ig-counts tr:last-child td {
  border-bottom: none;
}

.ig-counts td:last-child {
  text-align: right;
  font-family: var(--ig-font-data);
  font-size: var(--text-xs);
  color: var(--ig-paper-dim);
  white-space: nowrap;
}

.ig-price-rows {
  background: var(--surface-panel);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-2);
  box-shadow: var(--elev-2);
  overflow: hidden;
}

.ig-price-row {
  display: grid;
  grid-template-columns: 180px minmax(0, 1fr);
  gap: var(--space-8);
  align-items: baseline;
  padding: var(--space-8) var(--space-9);
  border-bottom: var(--hairline) solid var(--ink-700);
}

.ig-price-row:last-child {
  border-bottom: none;
}

.ig-price-name {
  font-family: var(--ig-font-display);
  font-weight: var(--weight-semibold);
  font-size: var(--text-md);
  color: var(--ig-paper);
}

.ig-price-row p {
  margin: 0 0 var(--space-3);
  font-size: var(--text-sm);
}

.ig-price-per-message {
  font-family: var(--ig-font-data);
  font-size: var(--text-xs);
  color: var(--ig-paper-faint);
}

.ig-note {
  margin-top: var(--space-9);
  font-size: var(--text-sm);
  max-width: 68ch;
}

.ig-note a {
  color: var(--link);
  font-weight: var(--weight-medium);
}

/* -------------------------------------------------------------------------
   Footer
   ------------------------------------------------------------------------- */

.ig-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  flex-wrap: wrap;
  max-width: var(--shell-max);
  margin: 0 auto;
  padding: var(--space-10) var(--space-8) var(--space-12);
  border-top: var(--hairline) solid var(--border-hairline);
  color: var(--ig-paper-faint);
  font-size: var(--text-sm);
}

.ig-footer a {
  color: var(--ig-paper-dim);
  text-decoration: none;
}

.ig-footer a:hover {
  color: var(--link);
}

/* -------------------------------------------------------------------------
   Long-form prose (privacy, terms)
   ------------------------------------------------------------------------- */

.ig-prose {
  max-width: var(--measure-prose);
  margin: 0 auto;
  padding: var(--space-11) var(--space-8) var(--space-13);
}

/* On the legal pages the eyebrow is a label, not a liveness indicator, so it
   drops the pill chrome and the status dot the hero's version carries. */
.ig-prose .ig-eyebrow {
  display: block;
  background: none;
  border: none;
  box-shadow: none;
  padding: 0;
  font-family: var(--ig-font-data);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--ig-paper-faint);
  margin-bottom: var(--space-4);
}

.ig-prose .ig-eyebrow::before {
  content: none;
}

.ig-prose h1 {
  font-size: var(--text-2xl);
  margin: var(--space-4) 0 var(--space-6);
}

.ig-prose h2 {
  font-size: var(--text-md);
  margin: var(--space-11) 0 var(--space-5);
  padding-top: var(--space-8);
  border-top: var(--hairline) solid var(--border-hairline);
}

.ig-prose p,
.ig-prose li {
  color: var(--ig-paper-dim);
  line-height: var(--leading-loose);
}

.ig-prose .ig-lede {
  color: var(--ig-paper);
}

.ig-prose ul {
  padding-left: var(--space-7);
  margin: var(--space-5) 0;
}

.ig-prose li {
  margin-bottom: var(--space-5);
}

.ig-prose strong {
  color: var(--ig-paper);
  font-weight: var(--weight-semibold);
}

.ig-prose a {
  color: var(--link);
}

.ig-prose code {
  font-family: var(--ig-font-data);
  font-size: 0.85em;
  background: var(--surface-input);
  border-radius: var(--radius-1);
  padding: 0.1em 0.35em;
}

.ig-meta {
  font-family: var(--ig-font-data);
  font-size: var(--text-xs);
  color: var(--ig-paper-faint);
}

@media (prefers-reduced-motion: reduce) {
  .ig-typing-dots i {
    animation: none;
  }
}

@media (max-width: 860px) {
  .ig-hero {
    grid-template-columns: 1fr;
    gap: var(--space-10);
    padding: var(--space-10) var(--space-6) var(--space-9);
  }
  .ig-hero h1 {
    font-size: var(--text-2xl);
  }
  .ig-nav,
  .ig-footer {
    padding-inline: var(--space-6);
  }
  .ig-section {
    padding: var(--space-11) var(--space-6);
  }
  .ig-steps {
    grid-template-columns: 1fr;
  }
  .ig-features {
    grid-template-columns: 1fr;
  }
  /* The tab rail becomes a horizontal scroller above the pane — a 250px
     sidebar leaves nothing for the content on a phone. */
  .ig-channels {
    grid-template-columns: 1fr;
    gap: var(--space-8);
    padding: var(--space-7);
  }
  .ig-rail {
    flex-direction: row;
    overflow-x: auto;
    gap: var(--space-3);
  }
  .ig-rail label {
    white-space: nowrap;
    border-left: none;
    border-bottom: 2px solid transparent;
  }
  #ch-dm:checked ~ .ig-channels label[for="ch-dm"],
  #ch-comments:checked ~ .ig-channels label[for="ch-comments"],
  #ch-handoff:checked ~ .ig-channels label[for="ch-handoff"] {
    border-left-color: transparent;
    border-bottom-color: var(--accent);
  }
  .ig-channel-pane {
    grid-template-columns: 1fr;
    gap: var(--space-7);
  }
  .ig-faq {
    grid-template-columns: 1fr;
    gap: var(--space-8);
  }
  /* Two columns of four cards each is unreadable on a phone; stacking keeps
     the before/after pairing legible in reading order. */
  .ig-compare {
    grid-template-columns: 1fr;
    gap: var(--space-9);
  }
  .ig-pricing-grid {
    grid-template-columns: 1fr;
  }
  .ig-pricing-panel-body,
  .ig-counts td,
  .ig-counts th {
    padding-inline: var(--space-7);
  }
  .ig-calc-result-value {
    font-size: var(--text-3xl);
  }
  .ig-calc {
    padding: var(--space-8);
  }
  .ig-price-row {
    grid-template-columns: 1fr;
    gap: var(--space-3);
    padding: var(--space-7);
  }
  /* Tenant-facing controls honour the 44px tap minimum; admin ones do not. */
  .ig-btn {
    min-height: var(--tap-min);
  }
  .ig-cta-row .ig-btn {
    flex: 1 1 100%;
  }
}

/* ==========================================================================
   Test chat (/panel/chat) — the bot preview, shaped like an Instagram Direct
   thread and set inside the standard panel chrome. It shares the landing
   page's chat palette, because the tenant is here to judge how a reply will
   look to a customer, and a customer sees it on Instagram's own light thread.
   The --dm-* names below are aliases onto that shared palette, not a second
   copy of it.

   It used to render full-viewport and outside <Shell>, which made it the one
   panel page with no navigation — the tenant had to use a bespoke back arrow
   to return anywhere. The thread is now a bounded, scrolling panel like any
   other section on a panel page.

   Deliberately NOT Instagram's pink/orange brand gradient anywhere except the
   avatar: same reasoning as the landing page — this is a preview of a bot, and
   it must never read as impersonating the Instagram product itself.

   Two fixed rows (scrolling thread / composer) inside a bounded panel height.
   ========================================================================== */

.dm {
  --dm-ink: var(--chat-ink);
  --dm-surface: var(--chat-surface);
  --dm-raised: var(--chat-raised);
  --dm-line: var(--chat-line);
  --dm-paper: var(--chat-paper);
  --dm-paper-dim: var(--chat-paper-dim);
  --dm-paper-faint: var(--chat-paper-faint);
  --dm-accent: var(--chat-accent);
  --dm-danger: var(--chat-danger);

  --dm-font-display: var(--chat-font-display);
  --dm-font-body: var(--chat-font-body);
}

/* .dm only declares variables — it wraps the whole page so the control row
   above the thread can read them too. The thread itself is .dm-panel: a panel
   among panels, not its own screen, scrolling inside a bounded height so the
   composer stays put without the page owning the viewport. */
.dm-panel {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  height: min(62vh, 640px);
  min-height: 22rem;
  margin-bottom: var(--panel-gap);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-2);
  box-shadow: var(--elev-2);
  overflow: hidden;
  background: var(--dm-surface);
  color: var(--dm-paper);
  font-family: var(--dm-font-body);
}

/* --- Topbar --------------------------------------------------------------- */

/* Identity, channel toggle and reset used to live in a full-width topbar with
   a back arrow. Inside <Shell> the nav supplies the way back, so these become
   an ordinary control row in the panel above the thread. */
.dm-controls {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  flex-wrap: wrap;
}

.dm-identity {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  min-width: 0;
}

.dm-avatar {
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 50%;
  flex-shrink: 0;
  background: linear-gradient(135deg, var(--dm-accent), #8b5ef0 60%, #e0538a);
}

.dm-avatar-lg {
  width: 4.5rem;
  height: 4.5rem;
}

.dm-identity-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.dm-identity-text strong {
  font-size: 0.92rem;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.dm-identity-sub {
  font-size: 0.75rem;
  color: var(--dm-paper-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Carries .btn-secondary in the markup for its fill and border; this only
   sets what a pill-shaped, right-aligned variant needs on top. */
.dm-reset {
  flex-shrink: 0;
  margin-left: auto;
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  padding: var(--pad-control);
}

.dm-reset:disabled {
  opacity: 0.45;
  cursor: default;
}

/* --- Channel toggle (Direct / Comment) ------------------------------------ */

.dm-channel-toggle {
  display: inline-flex;
  border: 1px solid var(--dm-line);
  border-radius: 999px;
  overflow: hidden;
  flex-shrink: 0;
}

.dm-channel-btn {
  background: transparent;
  border: none;
  border-radius: 0;
  color: var(--dm-paper-dim);
  font-family: var(--dm-font-body);
  font-weight: 500;
  font-size: 0.75rem;
  padding: 0.35rem 0.75rem;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}

.dm-channel-btn + .dm-channel-btn {
  border-left: 1px solid var(--dm-line);
}

.dm-channel-btn:hover {
  color: var(--dm-paper);
  background: var(--dm-raised);
}

.dm-channel-btn.is-active,
.dm-channel-btn.is-active:hover {
  background: var(--dm-accent);
  color: #fff;
}

/* Marks which channel a bubble belongs to in comment mode, where one generation
   produces both a public reply and a Direct one. */
.dm-channel-label {
  display: block;
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.55;
  margin-bottom: 0.25rem;
}

/* --- Thread --------------------------------------------------------------- */

.dm-thread {
  overflow-y: auto;
  background: var(--dm-ink);
}

.dm-thread-inner {
  max-width: 46rem;
  margin: 0 auto;
  padding: 1.5rem 1rem 2rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

/* Empty state as an invitation: the account card a real Direct thread opens
   with, plus three real questions the tenant's customers actually ask. */
.dm-intro {
  text-align: center;
  padding: 2.5rem 1rem 1rem;
}

.dm-intro .dm-avatar-lg {
  display: inline-block;
  margin-bottom: 0.9rem;
}

.dm-intro-name {
  font-family: var(--dm-font-display);
  font-weight: 600;
  font-size: 1.15rem;
  color: var(--dm-paper);
  margin: 0 0 0.5rem;
}

.dm-intro-note {
  max-width: 34rem;
  margin: 0 auto 1.75rem;
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--dm-paper-dim);
}

.dm-suggestions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: center;
}

.dm-suggestion {
  background: var(--dm-surface);
  border: 1px solid var(--dm-line);
  border-radius: 999px;
  color: var(--dm-paper);
  font-family: var(--dm-font-body);
  font-size: 0.85rem;
  padding: 0.5rem 1.05rem;
  cursor: pointer;
}

/* The global `button:where(:hover)` sets a brand-blue background. It carries no
   specificity, but nothing here overrode `background`, so the chip took the dark
   fill while this rule set the text to the same blue — blue on blue, unreadable.
   The hover state must restate its own background, not just its colour. */
.dm-suggestion:hover {
  background: var(--dm-surface);
  border-color: var(--dm-accent);
  color: var(--dm-accent);
}

.dm-row {
  display: flex;
}

.dm-row.is-user {
  justify-content: flex-end;
}

.dm-row.is-bot {
  justify-content: flex-start;
}

/* Consecutive bubbles from one side tuck together, and only the last of a run
   keeps its outer tail — the grouping a real DM thread has. */
.dm-row + .dm-row.is-user,
.dm-row + .dm-row.is-bot {
  margin-top: 0.1rem;
}

.dm-bubble {
  max-width: min(82%, 34rem);
  padding: 0.55rem 0.9rem;
  border-radius: 18px;
  font-size: 0.9rem;
  line-height: 1.45;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.dm-row.is-user .dm-bubble {
  background: var(--dm-accent, var(--brand-600, #3f6fd1));
  color: #fff;
  border-bottom-right-radius: 5px;
}

.dm-row.is-bot .dm-bubble {
  background: var(--dm-raised, var(--surface-input, #eff2f8));
  color: var(--dm-paper, var(--paper, #121722));
  border-bottom-left-radius: 5px;
}

.dm-row.is-bot .dm-bubble.is-error {
  background: var(--chat-danger-bg);
  color: var(--dm-danger);
}

.dm-bubble.is-photo {
  padding: 0.3rem;
  background: var(--dm-raised);
}

.dm-bubble.is-photo img {
  display: block;
  max-width: 100%;
  max-height: 20rem;
  border-radius: 14px;
  object-fit: cover;
}

.dm-bubble.is-typing {
  padding: 0.7rem 0.95rem;
}

.dm-dots {
  display: inline-flex;
  gap: 4px;
}

.dm-dots i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--dm-paper-faint);
  animation: dm-typing 1.1s infinite ease-in-out;
}

.dm-dots i:nth-child(2) { animation-delay: 0.15s; }
.dm-dots i:nth-child(3) { animation-delay: 0.3s; }

@keyframes dm-typing {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.5; }
  30% { transform: translateY(-3px); opacity: 1; }
}

/* --- Composer ------------------------------------------------------------- */

/* No safe-area padding any more: the composer is the bottom row of a bounded
   panel, not of the viewport, so there is no home indicator to clear. */
.dm-composer {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  max-width: 46rem;
  width: 100%;
  margin: 0 auto;
  padding: var(--space-6);
  border-top: var(--hairline) solid var(--dm-line);
  background: var(--dm-surface);
}

/* Overrides the app-wide dark input styling, which would otherwise paint an
   --ink field into this light thread. */
.dm-composer input[type="text"] {
  flex: 1;
  min-width: 0;
  background: var(--dm-ink);
  border: 1px solid var(--dm-line);
  border-radius: 999px;
  color: var(--dm-paper);
  font-family: var(--dm-font-body);
  font-size: 0.9rem;
  padding: 0.65rem 1.1rem;
}

.dm-composer input[type="text"]::placeholder {
  color: var(--dm-paper-faint);
}

.dm-composer input[type="text"]:focus {
  border-color: var(--dm-accent);
}

.dm-composer input[type="text"]:focus-visible,
.dm-composer button:focus-visible,
.dm-suggestion:focus-visible,
.dm-reset:focus-visible {
  outline: none;
  box-shadow: var(--ring-focus);
}

.dm-composer button {
  width: 2.5rem;
  height: 2.5rem;
  flex-shrink: 0;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--dm-accent);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.dm-composer button svg {
  width: 1.15rem;
  height: 1.15rem;
}

.dm-composer button:hover:not(:disabled) {
  background: var(--chat-accent-hover);
}

.dm-composer button:disabled {
  background: var(--dm-raised);
  color: var(--dm-paper-faint);
  cursor: default;
}

@media (max-width: 640px) {
  /* The controls wrap to their own lines, so the thread takes more of the
     screen rather than less. */
  .dm-panel {
    height: min(70vh, 560px);
  }
  .dm-controls {
    gap: var(--space-5);
  }
  .dm-reset {
    font-size: var(--text-xs);
  }
  .dm-thread-inner {
    padding: 1rem 0.75rem 1.5rem;
  }
  .dm-bubble {
    max-width: 88%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .dm-dots i {
    animation: none;
  }
}

/* -------------------------------------------------------------------------
   Publish wizard — two-tab candidate picker at /panel/publish
   ------------------------------------------------------------------------- */

.tab-row {
  display: flex;
  gap: 0.5rem;
  border-bottom: 1px solid var(--ink-line);
  margin-bottom: 1.25rem;
}

.tab {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--paper-dim);
  font-family: var(--font-body);
  font-size: 0.9rem;
  padding: 0.6rem 0.9rem;
  cursor: pointer;
}

/* The global `button:hover` adds a brand fill, which an underline tab must not
   take — it has no fill of its own in any state. */
.tab:hover {
  background: none;
  color: var(--paper);
}

.tab.is-active,
.tab.is-active:hover {
  background: none;
  color: var(--paper);
  border-bottom-color: var(--signal);
}

/* Captions are edited in place: a post cannot be edited after publishing, so the
   tenant must be able to fix the text before it goes out, not after. */
.caption-input {
  width: 100%;
  min-width: 20rem;
  background: var(--surface-input);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-1);
  color: var(--text-body);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  padding: var(--space-4) var(--space-5);
  resize: vertical;
}

.caption-input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--surface-panel);
}

/* The irreversibility warning before publishing. */
.panel.is-warning {
  border-color: var(--state-wait);
  background: var(--state-wait-dim);
}

.panel.is-warning h2 {
  color: var(--wait);
}

.is-error {
  color: var(--state-danger);
}

.is-muted {
  color: var(--paper-faint);
  font-size: 0.85rem;
}

.button-row {
  display: flex;
  gap: 0.75rem;
  margin-top: 1.25rem;
}

input[type="checkbox"]:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

/* Photo strip in the publish wizard — the carousel exactly as it will be posted,
   in Order. Shown rather than counted because a post cannot be undone. */
.publish-thumbs {
  display: flex;
  gap: 0.3rem;
  align-items: center;
  flex-wrap: wrap;
  max-width: 14rem;
}

.publish-thumb {
  width: 3rem;
  height: 3rem;
  object-fit: cover;
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-1);
  background: var(--surface-input);
}

.publish-thumb-more {
  font-family: var(--font-data);
  font-size: 0.75rem;
  color: var(--wait);
  padding: 0 0.3rem;
}

.button-row.is-tight {
  margin-top: 0;
  margin-bottom: 0.9rem;
}

/* -------------------------------------------------------------------------
   Lightbox — full-screen photo viewer for the publish wizard
   ------------------------------------------------------------------------- */

.publish-thumb-button {
  padding: 0;
  border: none;
  background: none;
  cursor: zoom-in;
  line-height: 0;
  border-radius: var(--radius);
}

.publish-thumb-button:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 2px;
}

.publish-thumb-button:hover {
  background: none;
}

.publish-thumb-button:hover .publish-thumb {
  border-color: var(--paper-faint);
}

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem;
  background: rgba(10, 9, 13, 0.92);
  outline: none;
}

.lightbox-frame {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  max-width: min(90vw, 1100px);
  max-height: 90vh;
}

.lightbox-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  color: rgba(255, 255, 255, 0.72);
  font-size: var(--text-sm);
}

.lightbox-title {
  font-family: var(--font-data);
}

.lightbox-close {
  background: none;
  border: none;
  color: rgba(255, 255, 255, 0.72);
  cursor: pointer;
  font-size: 1.6rem;
  line-height: 1;
  padding: 0 var(--space-3);
}

.lightbox-close:hover {
  background: none;
  color: #fff;
}

.lightbox-stage {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
}

/* Fits the viewport instead of overflowing it, so a tall product shot stays
   fully visible without scrolling. */
/* The scrim stays dark by design so photos read against it, so this backdrop
   must stay dark too — it cannot follow --ink, which is now the light ground. */
.lightbox-image {
  max-width: 100%;
  max-height: 72vh;
  object-fit: contain;
  border-radius: var(--radius-1);
  background: rgba(10, 9, 13, 0.92);
}

/* Everything inside the lightbox sits on the dark scrim, so its colours are
   deliberately literal rather than token-driven: --paper is dark ink now, and
   would render the controls invisible against their own background. */
.lightbox-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background: rgba(28, 27, 33, 0.85);
  border: var(--hairline) solid rgba(255, 255, 255, 0.18);
  border-radius: var(--radius-1);
  color: #fff;
  cursor: pointer;
  font-size: 1.8rem;
  line-height: 1;
  padding: var(--space-2) var(--space-5);
}

.lightbox-nav:hover {
  background: rgba(52, 51, 60, 0.95);
  border-color: rgba(255, 255, 255, 0.4);
  color: #fff;
}

.lightbox-nav.is-prev { left: 0.75rem; }
.lightbox-nav.is-next { right: 0.75rem; }

.lightbox-strip {
  display: flex;
  gap: 0.4rem;
  justify-content: center;
  flex-wrap: wrap;
}

.lightbox-dot {
  padding: 0;
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  background: none;
  cursor: pointer;
  line-height: 0;
  opacity: 0.5;
}

.lightbox-dot.is-active {
  border-color: var(--signal);
  opacity: 1;
}

.lightbox-dot:hover {
  background: none;
  opacity: 1;
}

.lightbox-dot img {
  width: 3rem;
  height: 3rem;
  object-fit: cover;
  display: block;
}

/* Model tester response — a raw model reply or a raw provider error body, both of which
   can be long and are worth reading verbatim, so they wrap and scroll instead of being
   truncated. */
.tester-response {
  margin: 0.6rem 0 0;
  padding: 0.75rem;
  max-height: 22rem;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 0.85rem;
  background: var(--ink-line);
  border-radius: 6px;
}

.tester-response.is-error {
  color: var(--state-danger);
}

/* ── Instagram catalogue import ─────────────────────────────────────────────
   One proposal per card: the post's photos, the fields parsed from the
   merchant's comment, and the comment itself. The raw comment sits beside the
   fields deliberately — a price the model read is only judgeable against the
   text it came from. */

.ig-import-card {
  display: grid;
  grid-template-columns: minmax(0, 200px) minmax(0, 1fr) minmax(0, 260px);
  gap: var(--space-5);
  padding: var(--space-5) 0;
  border-bottom: 1px solid var(--border-hairline);
  align-items: start;
}

.ig-import-card:last-child {
  border-bottom: 0;
}

.ig-import-photos {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.ig-import-photos img {
  width: 92px;
  height: 92px;
  object-fit: cover;
  border-radius: var(--radius-1);
  border: 1px solid var(--border-hairline);
}

.ig-import-fields {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
}

.ig-import-fields label {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  font-size: 0.85rem;
}

.ig-import-fields input,
.ig-import-fields textarea {
  width: 100%;
}

.ig-import-source h3 {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  margin: 0 0 var(--space-2);
}

.ig-import-raw {
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 0.85rem;
  padding: var(--space-3);
  background: var(--surface-input);
  border-radius: var(--radius-1);
  margin: 0 0 var(--space-2);
}

.ig-import-actions {
  grid-column: 1 / -1;
  display: flex;
  gap: var(--space-3);
}

@media (max-width: 900px) {
  .ig-import-card {
    grid-template-columns: minmax(0, 1fr);
  }
}
