/* =====================================================================
   engine.css — the engine's design system, copied from HQ.
   ---------------------------------------------------------------------
   Every engine page (public/*.html) is framed inside HQ (gym-ads-os-admin,
   Next.js + Tailwind, LIGHT) as a station of the Clock Off Line. This file
   is the one stylesheet those pages share so the seam between an HQ page
   and the engine page inside it disappears. Values are lifted verbatim
   from HQ: app/globals.css (ground #f5f6f8, 13/15px system sans), the
   Tailwind v4 palette (gray/blue/emerald/amber/red as oklch — the exact
   colours HQ paints), and the classes in components/WorkspaceUI.tsx
   (Kpi, Card, Row, Empty, PageHead, Chip), Factory.tsx, AdCreator.tsx
   (buttons, segmented switch, notice, progress, modal), CopyBank.tsx
   (table), TopBar.tsx (tabs), NewClientForm.tsx (INPUT / LABEL).

   HOW A PAGE ADOPTS IT (one page at a time, no big bang):
     1. <link rel="stylesheet" href="/engine.css"> in <head>, BEFORE the
        page's own <style>, so the page can still win where it must.
     2. Add class "e" to <body>. That sets the light ground, the ink, the
        font and color-scheme:light. Delete the page's :root dark tokens
        (--bg/--panel/--card/--border/--text/--accent) and every hard-coded
        #0e0f13 / #1b1e27 / #2a2e3a / #e11d48. Anything left dark is a bug.
     3. Replace the page's own chrome with the classes below:
          .e-page  > .e-header (h1 + .e-sub + .e-actions) > content > .e-next
        Cards -> .e-card, numbers -> .e-kpi, lists -> .e-table, states ->
        .e-chip, buttons -> .e-btn, filters -> .e-input/.e-select/.e-label,
        in-page sub-tabs -> .e-tabs, per-niche pages -> .e-niche,
        prompt()/confirm()/alert() -> .e-notice or .e-modal/.e-drawer,
        "nothing here" -> .e-empty, cost/AI progress -> .e-progress.
     4. Put class "e-embedded-hide" on the page's own brand topbar
        (header.topbar, div.top …): operator.js marks <html class="hq-embedded">
        when framed, and this file hides it — HQ's bar is the header then.
        Standalone (own tab) it stays.
     5. Sticky things that used the old topbar height (top:59px/66px/70px)
        -> .e-sticky (top:0). Inside the frame there is no bar above them.
     6. No emoji in chrome, no gym/F45/"Ad Studio"/"AI Studio" words, no
        explanatory paragraphs (that copy lives in HQ's How it works). One
        13px line under the title says what the page is for.

   Ad templates (templates/, images/, creatives/, lib/house_layouts.js,
   lib/classify_fitness.js, data/copy_system_v1.json) are Vijay's lane and
   never read this file: it styles page chrome only.

   DARK MODE: yes, since 2026-09-09 — and it is still "the engine follows
   HQ". That line used to read "none, on purpose", written when HQ was light
   only. HQ grew a light/dark toggle on 2026-09-04 (lib/theme.ts,
   app/globals.css `html.dark`), so following HQ now MEANS following it into
   dark; staying light was the thing that had started to look broken, an
   ink-white page inside a dark shell.
   The rule is unchanged in spirit: this file does not invent a palette. Every
   dark value in section 1b is HQ's own `html.dark` value for the same
   Tailwind colour the light token was lifted from, so the seam stays
   invisible in both themes. A page's own dark inline styles are still what we
   REMOVE — the theme belongs to the tokens, not to a page.
   Still no prefers-color-scheme blocks in here: the media query is read once,
   in operator.js, and turned into the `dark` class. One switch, so a page
   cannot disagree with the shell it is framed in.

   NAMING: every class starts with e- ; state modifiers are bare words
   (.on, .good, .warn, .bad, .blue, .mute, .primary, .ghost, .sm); parts
   use __ (.e-kpi__label). Tokens are --e-* and are the only place a
   colour, size or radius is written.
   ===================================================================== */


/* ---------- 1. TOKENS (HQ's exact values) ---------------------------- */
:root {
  /* which way the native controls lean — read by body.e as color-scheme.
     A literal `light` there would sit BELOW html.dark on the tree and win,
     leaving scrollbars, selects and date pickers paper-white on a dark page. */
  --e-scheme:      light;

  /* ground + surfaces */
  --e-ground:      #f5f6f8;                       /* html,body in HQ globals.css */
  --e-card:        #ffffff;                       /* bg-white */
  --e-card-2:      oklch(98.5% 0.002 247.839);    /* gray-50  ≈ #f9fafb  — table heads, hover rows, input wells */
  --e-line:        oklch(92.8% 0.006 264.531);    /* gray-200 ≈ #e5e7eb  — card + control borders */
  --e-line-2:      oklch(96.7% 0.003 264.542);    /* gray-100 ≈ #f3f4f6  — dividers inside a card */
  --e-line-3:      oklch(87.2% 0.01 258.338);     /* gray-300 ≈ #d1d5db  — disabled button, dashed empties */

  /* ink */
  --e-ink:         oklch(21% 0.034 264.665);      /* gray-900 ≈ #101828  — titles, numbers, body */
  --e-ink-2:       oklch(44.6% 0.03 256.802);     /* gray-600 ≈ #4b5563  — secondary buttons, prose */
  --e-ink-3:       oklch(55.1% 0.027 264.364);    /* gray-500 ≈ #6b7280  — one-liners, feet, labels */
  /* Was gray-400 #9ca3af, which measures 2.54:1 on white and 2.36:1 on the
     ground — well under WCAG AA's 4.5:1 — while this token carries real text:
     uppercase labels, .e-meta, .e-mono, KPI labels, table heads, niche labels.
     It was a live legibility failure on the three adopted pages, not a
     theming problem, so it is fixed here rather than worked around. #5d6673
     measures 5.81:1 on --e-card and 5.37:1 on --e-ground; the dark value is
     HQ's gray-400, which is already 4.88:1 on the dark card. Muted has to be
     solved per theme — light muted goes DARKER, dark muted goes LIGHTER — so
     it cannot be one shared grey. */
  --e-mute:        #5d6673;                       /* label / meta ink — AA on every surface it lands on */
  --e-ink-body:    #1a1a2e;                       /* HQ body colour (globals.css); gray-900 is used on components */

  /* Ink that sits ON a filled accent or a dark pill. It does NOT turn over:
     the fills it lands on (--e-accent, --e-good-btn, --e-ink) are dark in
     both themes, which is HQ's rule — "white text on blue and dark buttons
     stays white". Named because every adopting page needs it and would
     otherwise carry a private #fff, or alias it to the body ink and ship
     near-black on blue. */
  --e-on-accent:   #fff;

  /* accent = blue. Pink #e11d48 is gone. */
  --e-accent:      oklch(54.6% 0.245 262.881);    /* blue-600 ≈ #155dfc  — primary buttons, progress, active tab underline */
  --e-accent-hi:   oklch(48.8% 0.243 264.376);    /* blue-700 ≈ #1447e6  — hover, link text */
  --e-accent-ink:  oklch(42.4% 0.199 265.638);    /* blue-800 ≈ #193cb8  — link hover, notice text */
  --e-accent-mid:  oklch(62.3% 0.214 259.815);    /* blue-500 ≈ #2b7fff  — focus border */
  --e-accent-soft: oklch(97% 0.014 254.604);      /* blue-50  ≈ #eff6ff  — tinted chips/notices/rings */
  --e-accent-soft-2: oklch(93.2% 0.032 255.585);  /* blue-100 ≈ #dbeafe  — soft button hover */
  --e-accent-line: oklch(88.2% 0.059 254.128);    /* blue-200 ≈ #bedbff  — tinted borders */

  /* good = emerald (approved, live, full) */
  --e-good:        oklch(50.8% 0.118 165.612);    /* emerald-700 ≈ #007a55 */
  --e-good-btn:    oklch(59.6% 0.145 163.225);    /* emerald-600 ≈ #009966  — Approve button */
  --e-good-btn-hi: oklch(50.8% 0.118 165.612);    /* emerald-700 */
  --e-good-soft:   oklch(97.9% 0.021 166.113);    /* emerald-50  ≈ #ecfdf5 */
  --e-good-soft-2: oklch(95% 0.052 163.051);      /* emerald-100 ≈ #d0fae5 */
  --e-good-line:   oklch(90.5% 0.093 164.15);     /* emerald-200 ≈ #a4f4cf */

  /* warn = amber — ONLY for "needs a human" (never data emphasis, never brand) */
  --e-warn:        oklch(55.5% 0.163 48.998);     /* amber-700 ≈ #bb4d00  — chip text */
  --e-warn-num:    oklch(66.6% 0.179 58.318);     /* amber-600 ≈ #e17100  — KPI value */
  --e-warn-ink:    oklch(47.3% 0.137 46.201);     /* amber-800 ≈ #973c00  — notice text */
  --e-warn-soft:   oklch(98.7% 0.022 95.277);     /* amber-50  ≈ #fffbeb */
  --e-warn-line:   oklch(92.4% 0.12 95.746);      /* amber-200 ≈ #fee685 */
  --e-warn-line-2: oklch(87.9% 0.169 91.605);     /* amber-300 ≈ #ffd230  — card border with a warning */

  /* bad = red (errors, rejected, hot) */
  --e-bad:         oklch(50.5% 0.213 27.518);     /* red-700 ≈ #c10007 */
  --e-bad-num:     oklch(57.7% 0.245 27.325);     /* red-600 ≈ #e7000b */
  --e-bad-soft:    oklch(97.1% 0.013 17.38);      /* red-50  ≈ #fef2f2 */
  --e-bad-line:    oklch(88.5% 0.062 18.334);     /* red-200 ≈ #ffc9c9 */

  /* HQ's rail (for reference only — the engine never paints a rail) */
  --e-rail:        #16202e;

  /* type — HQ: --text-xs 13/18, --text-sm 15/21, system sans */
  --e-font:        ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --e-mono:        ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --e-fs-label:    11px;    /* uppercase labels: kpi label, th, form section */
  --e-fs-foot:     12px;    /* kpi foot, card feet, small meta */
  --e-fs-ui:       12.5px;  /* secondary buttons, segmented switch, card meta, form labels */
  --e-fs-body:     13px;    /* body, tables, primary button, notices, one-liner */
  --e-fs-tab:      13.5px;  /* tab labels, empty states */
  --e-fs-h2:       14px;    /* card / section title */
  --e-fs-base:     15px;    /* prose (HQ text-sm) */
  --e-fs-bar:      16px;    /* HQ top bar title, modal title */
  --e-fs-h1:       20px;    /* page title (HQ text-xl) */
  --e-fs-kpi:      24px;    /* KPI number (HQ text-2xl) */
  --e-lh-body:     18px;
  --e-lh-base:     21px;
  --e-track-label: 0.12em;  /* KPI label */
  --e-track-th:    0.10em;  /* table head */
  --e-track-form:  0.08em;  /* form label */

  /* radii — HQ: rounded-xl 12 (cards), rounded-lg 8 (buttons/inputs),
     rounded-md 6 (small buttons), rounded 4 (chips), rounded-full (pills),
     rounded-2xl 16 (modal) */
  --e-r-card:      12px;
  --e-r-btn:       8px;
  --e-r-sm:        6px;
  --e-r-chip:      4px;
  --e-r-pill:      999px;
  --e-r-modal:     16px;

  /* spacing — HQ: p-3.5 14, gap-3 12, gap-6 24, px-6 py-8 page, max-w-7xl */
  --e-pad:         14px;
  --e-gap:         12px;
  --e-gap-lg:      24px;
  --e-page-x:      24px;
  --e-page-y:      32px;
  --e-page-max:    1280px;

  /* shadows — HQ: shadow-sm on primary button, shadow-2xl on modal */
  --e-shadow-sm:   0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);
  --e-shadow-2xl:  0 25px 50px -12px rgb(0 0 0 / .25);
  --e-scrim:       rgb(0 0 0 / .4);
}


/* ---------- 1b. DARK (HQ's exact html.dark values) ------------------- */
/* Set by operator.js as class `dark` on <html> — the same switch HQ throws,
   carried across the iframe. Only COLOUR turns over: type, radii, spacing and
   the page's measure are the same page in either theme.

   Each value is HQ's `html.dark` value for the colour the light token above
   was lifted from (app/globals.css). Where a comment says "unchanged", HQ
   deliberately does not turn that colour over, and neither do we — those are
   the surfaces that carry white text, and they have to stay dark enough to
   keep it readable. That is HQ's rule, not a new one: "white text on blue and
   dark buttons stays white". */
html.dark {
  --e-scheme:      dark;

  /* ground + surfaces — the gray scale turns over, `white` becomes the card */
  --e-ground:      #0f141a;   /* html.dark body */
  --e-card:        #161c26;   /* white     */
  --e-card-2:      #1a212c;   /* gray-50   */
  --e-line:        #2b3544;   /* gray-200  */
  --e-line-2:      #212a37;   /* gray-100  */
  --e-line-3:      #384354;   /* gray-300  */

  /* ink — 900 is the light ink now, 400 stays the quietest */
  --e-ink:         #eef2f6;   /* gray-900  */
  --e-ink-2:       #b4bdc9;   /* gray-600  */
  --e-ink-3:       #98a3b1;   /* gray-500  */
  --e-mute:        #7f8a98;   /* gray-400  — 4.88:1 on --e-card; see the light note */
  --e-ink-body:    #e6ebf2;   /* html.dark body colour */

  /* accent — blue-600 is UNCHANGED: it is the button surface under white text.
     The tints and the link inks are the ones that swap. */
  --e-accent-hi:   #93bdff;   /* blue-700  — link text on a dark ground */
  --e-accent-ink:  #bcd4ff;   /* blue-800  */
  --e-accent-soft: #172540;   /* blue-50   */
  --e-accent-soft-2: #1d3157; /* blue-100  */
  --e-accent-line: #2b4677;   /* blue-200  */

  /* good — emerald-600 stays the Approve button; its HOVER cannot become the
     light emerald-700 or the white label on it disappears, so hover keeps the
     light theme's emerald-700 and simply reads as a deeper green. */
  --e-good:        #6ee7b7;   /* emerald-700 */
  --e-good-btn-hi: #007a55;   /* light emerald-700, kept dark under white text */
  --e-good-soft:   #10291f;   /* emerald-50  */
  --e-good-soft-2: #14382b;   /* emerald-100 */
  --e-good-line:   #1f5a45;   /* emerald-200 */

  /* warn — amber, still ONLY "needs a human" */
  --e-warn:        #fcd34d;   /* amber-700 */
  --e-warn-ink:    #fde68a;   /* amber-800 */
  --e-warn-soft:   #33270f;   /* amber-50  */
  --e-warn-line:   #6b5314;   /* amber-200 */

  /* bad */
  --e-bad:         #fca5a5;   /* red-700 */
  --e-bad-soft:    #33181b;   /* red-50  */
  --e-bad-line:    #6b2a30;   /* red-200 */

  /* a shadow on a dark ground has to be darker than one on paper to read */
  --e-shadow-sm:   0 1px 3px 0 rgb(0 0 0 / .5), 0 1px 2px -1px rgb(0 0 0 / .4);
  --e-shadow-2xl:  0 25px 50px -12px rgb(0 0 0 / .7);
  --e-scrim:       rgb(0 0 0 / .6);
}

/* The two KPI numbers HQ does not turn over sit on a card, not on a tint, and
   the light values (amber-600 #e17100, red-600 #e7000b) go muddy there. These
   are the only two places the engine picks its own dark value; both are the
   500 step of the same ramp, which is what HQ reaches for elsewhere on dark. */
html.dark {
  --e-warn-num:    #f59e0b;   /* amber-500 */
  --e-bad-num:     #fb5a5a;   /* red-500-ish, lifted for contrast on --e-card */
}


/* ---------- 2. PAGE BASE (opt-in: <body class="e">) ------------------ */
body.e {
  margin: 0;
  background: var(--e-ground);
  color: var(--e-ink-body);
  font: 400 var(--e-fs-body) / var(--e-lh-body) var(--e-font);
  color-scheme: var(--e-scheme);       /* date inputs, scrollbars: follow the theme */
  -webkit-font-smoothing: antialiased;
}
body.e *, body.e *::before, body.e *::after { box-sizing: border-box; }
body.e button, body.e input, body.e select, body.e textarea { font: inherit; color: inherit; }
body.e h1, body.e h2, body.e h3, body.e p { margin: 0; }
body.e a { color: var(--e-accent-hi); text-decoration: none; }
body.e a:hover { color: var(--e-accent-ink); }
body.e :focus-visible { outline: 2px solid var(--e-accent-mid); outline-offset: 2px; }
body.e ::placeholder { color: var(--e-mute); }
body.e img { max-width: 100%; }

/* The hidden attribute must win over any display a class sets — a section
   marked hidden that also carries .e-stack (display:flex) was showing: an
   author rule beats the browser's own [hidden] rule. (Library's photo
   panels under the Offers tab, Raj's screenshot, 2026-09-03.) */
[hidden] { display: none !important; }

/* Inside HQ's frame (operator.js adds html.hq-embedded): the page's own
   brand bar / cross-page nav is a second header — hide whatever carries
   this class. Standalone it shows. */
html.hq-embedded .e-embedded-hide { display: none !important; }
/* …and the reverse, for a "← back to HQ" style affordance only useful standalone */
html:not(.hq-embedded) .e-embedded-only { display: none !important; }


/* ---------- 3. LAYOUT ------------------------------------------------ */
/* The page column. HQ station pages: mx-auto max-w-7xl px-6 py-8 */
.e-page {
  max-width: var(--e-page-max);
  margin: 0 auto;
  padding: var(--e-page-y) var(--e-page-x);
  display: flex;
  flex-direction: column;
  gap: var(--e-gap-lg);                /* HQ gap-6 between page blocks */
}
.e-page.wide { max-width: none; }      /* research grids (swipe browse) that want the full frame */
.e-page.tight { padding-top: var(--e-gap-lg); }

/* Rows of things: HQ flex/grid gap-3 */
.e-stack   { display: flex; flex-direction: column; gap: var(--e-gap); }
.e-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.e-toolbar .e-right, .e-right { margin-left: auto; }
.e-grid    { display: grid; gap: var(--e-gap); grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.e-grid.cols-2 { grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); }
.e-grid.cols-4 { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }

/* Master–detail (Library offers): list left, sticky detail right */
.e-split { display: grid; gap: var(--e-gap); grid-template-columns: minmax(280px, 1fr) minmax(0, 1.6fr); align-items: start; }
.e-split > .e-sticky { position: sticky; top: 0; }
@media (max-width: 900px) { .e-split { grid-template-columns: 1fr; } }

/* Sticky inside the frame: there is no page bar above, so top is 0 */
.e-sticky { position: sticky; top: 0; z-index: 5; }


/* ---------- 4. PAGE HEADER (HQ PageHead) ----------------------------- */
/* <div class="e-header">
     <div><h1>Swipe File</h1><p class="e-sub">Proven ads, newest first.</p></div>
     <div class="e-actions"> …e-niche / e-btn … </div>
   </div> */
.e-header {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
}
.e-header h1, .e-h1 {
  font-size: var(--e-fs-h1); font-weight: 700; letter-spacing: -0.025em; line-height: 1.2;
  color: var(--e-ink);
}
.e-header .e-sub, .e-sub {
  margin-top: 2px; font-size: var(--e-fs-body); line-height: var(--e-lh-body); color: var(--e-ink-3);
}
.e-sub .warn { color: var(--e-warn-num); margin-left: 8px; }   /* "Engine did not answer…" */
.e-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; flex-wrap: wrap; justify-content: flex-end; }

/* Section heading between blocks (Factory "The production line") */
.e-section-title {
  margin-bottom: 8px; font-size: var(--e-fs-body); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--e-mute);
}
/* Card / panel title (WorkspaceUI Card h2) */
.e-h2 { font-size: var(--e-fs-h2); font-weight: 600; color: var(--e-ink); }
.e-meta { font-size: var(--e-fs-ui); color: var(--e-mute); }
.e-muted { font-size: var(--e-fs-ui); color: var(--e-ink-3); }
.e-mono { font-family: var(--e-mono); font-size: var(--e-fs-label); color: var(--e-mute); }
.e-num { font-variant-numeric: tabular-nums; }


/* ---------- 5. CARD (WorkspaceUI Card / Factory station card) -------- */
.e-card {
  background: var(--e-card);
  border: 1px solid var(--e-line);
  border-radius: var(--e-r-card);
  overflow: hidden;
  min-width: 0;
}
.e-card.pad { padding: var(--e-pad); }                              /* a bare card with content */
.e-card.accent { border-color: var(--e-accent-line); box-shadow: 0 0 0 2px var(--e-accent-soft); } /* the station in focus */
.e-card.warn   { border-color: var(--e-warn-line-2); }              /* needs a human */
.e-card.bad    { border-color: var(--e-bad-line); background: var(--e-bad-soft); }
.e-card.good   { border-color: var(--e-good-line); background: var(--e-good-soft); }
.e-card.busy   { border-color: var(--e-accent-line); background: var(--e-accent-soft); } /* a job running */
.e-card.off    { opacity: .6; }                                     /* rejected / retired */
.e-card.dashed { border-style: dashed; border-color: var(--e-line-3); background: transparent; } /* empty slot */
.e-card__head {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  padding: 12px var(--e-pad); border-bottom: 1px solid var(--e-line-2);
}
.e-card__head h2, .e-card__head .e-h2 { font-size: var(--e-fs-h2); font-weight: 600; color: var(--e-ink); }
.e-card__head .e-meta { font-size: var(--e-fs-ui); color: var(--e-mute); }
.e-card__head .e-actions { margin-left: auto; }
.e-card__body { padding: var(--e-pad); }
.e-card__body.flush { padding: 0; }
.e-card__foot {
  padding: 8px var(--e-pad); border-top: 1px solid var(--e-line-2);
  font-size: 11.5px; color: var(--e-mute);
}
/* the strip under a head that carries a notice (CopyBank) */
.e-card__notice {
  padding: 8px var(--e-pad); border-bottom: 1px solid var(--e-line-2);
  background: var(--e-accent-soft); color: var(--e-accent-ink); font-size: var(--e-fs-ui);
}
/* list rows inside a card (WorkspaceUI Row) */
.e-row {
  display: flex; align-items: center; gap: var(--e-gap);
  padding: 10px var(--e-pad); border-bottom: 1px solid var(--e-line-2);
  color: inherit; font-size: var(--e-fs-body);
}
.e-row:last-child { border-bottom: 0; }
a.e-row:hover, .e-row.click:hover { background: var(--e-card-2); cursor: pointer; }
.e-row.on { background: var(--e-accent-soft); box-shadow: inset 3px 0 0 var(--e-accent); }
/* a small tile inside a card grid: title + big mono number + one line (Factory station) */
.e-tile__title { font-size: var(--e-fs-h2); font-weight: 600; color: var(--e-ink); }
.e-tile__num   { font-family: var(--e-mono); font-size: 18px; font-weight: 500; font-variant-numeric: tabular-nums; color: var(--e-ink); }
.e-tile__what  { font-size: var(--e-fs-foot); line-height: 1.375; color: var(--e-ink-3); }


/* ---------- 6. KPI (WorkspaceUI / Factory Kpi) ----------------------- */
/* <div class="e-kpis">
     <div class="e-kpi"><div class="e-kpi__label">Approved</div>
       <div class="e-kpi__value good">128</div>
       <div class="e-kpi__foot">reviewed by a person</div></div>
   </div> */
.e-kpis { display: grid; gap: var(--e-gap); grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
.e-kpi {
  display: flex; flex-direction: column; gap: 2px;
  background: var(--e-card); border: 1px solid var(--e-line); border-radius: var(--e-r-card);
  padding: var(--e-pad);
}
.e-kpi__label {
  font-size: var(--e-fs-label); font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--e-track-label); color: var(--e-mute);
}
.e-kpi__value {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--e-fs-kpi); font-weight: 700; line-height: 1.2;
  font-variant-numeric: tabular-nums; color: var(--e-ink);
}
.e-kpi__value.good  { color: var(--e-good); }
.e-kpi__value.money { color: var(--e-good); }
.e-kpi__value.warn  { color: var(--e-warn-num); }
.e-kpi__value.bad,
.e-kpi__value.hot   { color: var(--e-bad-num); }
.e-kpi__value.mute  { color: var(--e-mute); }
.e-kpi__foot { font-size: var(--e-fs-foot); color: var(--e-ink-3); }


/* ---------- 7. TABLE (CopyBank matrix) ------------------------------- */
/* <div class="e-table-wrap"><table class="e-table">…</table></div>  (inside an .e-card) */
.e-table-wrap { overflow-x: auto; }
.e-table { width: 100%; border-collapse: collapse; font-size: var(--e-fs-body); }
.e-table thead tr { background: color-mix(in oklch, var(--e-card-2) 70%, transparent); }
.e-table th {
  padding: 8px var(--e-pad); text-align: left; white-space: nowrap;
  font-size: var(--e-fs-label); font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--e-track-th); color: var(--e-mute);
}
.e-table th.sticky, .e-table td.sticky { position: sticky; left: 0; z-index: 2; background: var(--e-card); }
.e-table thead th.sticky { background: var(--e-card-2); }
.e-table td { padding: 8px var(--e-pad); border-top: 1px solid var(--e-line-2); color: var(--e-ink); vertical-align: middle; }
.e-table tbody tr:hover { background: var(--e-card-2); }
.e-table tbody tr.on { background: var(--e-accent-soft); }
.e-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.e-table .mute { color: var(--e-mute); }
.e-table .sub { display: block; font-family: var(--e-mono); font-size: 10.5px; font-weight: 400; color: var(--e-mute); text-transform: none; letter-spacing: 0; }
.e-table .e-input, .e-table .e-select { padding: 4px 8px; font-size: var(--e-fs-ui); }  /* editable cells stay quiet */
.e-table.compact th, .e-table.compact td { padding: 6px 10px; }


/* ---------- 8. CHIP (WorkspaceUI Chip / CopyBank StatusChip / Factory TONE) */
/* <span class="e-chip good">approved</span> — 4px radius like HQ's; .pill for the round one */
.e-chip {
  display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0;
  padding: 2px 6px; border-radius: var(--e-r-chip); border: 1px solid var(--e-line);
  background: var(--e-card-2); color: var(--e-ink-2);
  font-size: var(--e-fs-label); font-weight: 600; line-height: 1.3; white-space: nowrap;
}
.e-chip.good { background: var(--e-good-soft);   color: var(--e-good);       border-color: var(--e-good-line); }
.e-chip.warn { background: var(--e-warn-soft);   color: var(--e-warn);       border-color: var(--e-warn-line); }
.e-chip.bad  { background: var(--e-bad-soft);    color: var(--e-bad);        border-color: var(--e-bad-line); }
.e-chip.blue { background: var(--e-accent-soft); color: var(--e-accent-hi);  border-color: var(--e-accent-line); }
.e-chip.mute { background: var(--e-line-2);      color: var(--e-ink-3);      border-color: var(--e-line); }
.e-chip.ink  { background: var(--e-ink);         color: var(--e-on-accent);                border-color: var(--e-ink); }
.e-chip.pill { border-radius: var(--e-r-pill); padding: 2px 8px; }
.e-chip.upper { text-transform: uppercase; letter-spacing: 0.05em; font-size: 11.5px; }
.e-chip.mono  { font-family: var(--e-mono); font-weight: 500; }
/* a token slot in generic copy ([GYM NAME] → "business") */
.e-token { border-radius: var(--e-r-chip); background: var(--e-accent-soft); color: var(--e-accent-hi); padding: 0 4px; font-family: var(--e-mono); font-size: var(--e-fs-label); }
/* a count bubble on a collapsed header / tab */
.e-count { display: inline-block; min-width: 18px; padding: 0 6px; border-radius: var(--e-r-pill); background: var(--e-line-2); color: var(--e-ink-3); font-size: 11.5px; font-variant-numeric: tabular-nums; text-align: center; }
.e-count.on { background: var(--e-accent-soft); color: var(--e-accent-hi); }
/* selectable filter chip (composition facets, date quick-picks, CTA choices) */
.e-choice {
  display: inline-flex; align-items: center; gap: 4px; cursor: pointer;
  padding: 4px 10px; border-radius: var(--e-r-sm); border: 1px solid var(--e-line);
  background: var(--e-card); color: var(--e-ink-2); font-size: var(--e-fs-ui); font-weight: 500;
}
.e-choice:hover { background: var(--e-card-2); }
.e-choice.on { background: var(--e-accent); border-color: var(--e-accent); color: var(--e-on-accent); }
.e-choice.mono { font-family: var(--e-mono); }
/* the 7px status dot (WorkspaceUI Dot) */
.e-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--e-mute); flex-shrink: 0; }
.e-dot.good { background: var(--e-good-btn); } .e-dot.warn { background: var(--e-warn-num); }
.e-dot.bad { background: var(--e-bad-num); }   .e-dot.blue { background: var(--e-accent); }


/* ---------- 9. BUTTON (AdCreator / Factory / CopyBank) --------------- */
/* secondary is the default: white, gray-200 border, 12.5px medium gray-600 */
.e-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 6px 10px; border-radius: var(--e-r-btn); border: 1px solid var(--e-line);
  background: var(--e-card); color: var(--e-ink-2);
  font-size: var(--e-fs-ui); font-weight: 500; line-height: 1.4; white-space: nowrap;
  cursor: pointer; text-decoration: none; transition: background-color .15s, color .15s, border-color .15s;
}
.e-btn:hover { background: var(--e-card-2); color: var(--e-ink); }
.e-btn:disabled, .e-btn[aria-disabled="true"] { cursor: not-allowed; opacity: .5; }
/* THE one primary action per page: blue-600, white, 13px semibold, shadow-sm */
.e-btn.primary {
  padding: 8px 14px; border-color: transparent; background: var(--e-accent); color: var(--e-on-accent);
  font-size: var(--e-fs-body); font-weight: 600; box-shadow: var(--e-shadow-sm);
}
.e-btn.primary:hover { background: var(--e-accent-hi); color: var(--e-on-accent); }
.e-btn.primary:disabled { background: var(--e-line-3); box-shadow: none; opacity: 1; }
/* quiet: no border, no fill */
.e-btn.ghost { border-color: transparent; background: transparent; }
.e-btn.ghost:hover { background: var(--e-line-2); }
/* tinted secondaries (CopyBank "Approve all" / "Fill this offer") */
.e-btn.soft { background: var(--e-accent-soft); border-color: var(--e-accent-line); color: var(--e-accent-hi); }
.e-btn.soft:hover { background: var(--e-accent-soft-2); }
.e-btn.good { background: var(--e-good-soft); border-color: var(--e-good-line); color: var(--e-good); }
.e-btn.good:hover { background: var(--e-good-soft-2); }
.e-btn.approve { background: var(--e-good-btn); border-color: transparent; color: var(--e-on-accent); font-weight: 600; } /* the ✓ gate */
.e-btn.approve:hover { background: var(--e-good-btn-hi); color: var(--e-on-accent); }
.e-btn.danger { background: var(--e-bad-soft); border-color: var(--e-bad-line); color: var(--e-bad); }
.e-btn.danger:hover { background: var(--e-bad-line); }
/* sizes */
.e-btn.sm { padding: 4px 8px; font-size: var(--e-fs-foot); border-radius: var(--e-r-sm); }
.e-btn.block { width: 100%; }
/* text link that reads as an action ("Open →", "Copy System →") */
.e-link { background: none; border: 0; padding: 0; cursor: pointer; font-size: var(--e-fs-ui); font-weight: 500; color: var(--e-accent-hi); }
.e-link:hover { color: var(--e-accent-ink); }
.e-link.mute { color: var(--e-ink-2); } .e-link.mute:hover { color: var(--e-ink); }
/* a group of buttons (Approve | Reject under a variant) */
.e-btns { display: flex; gap: 4px; } .e-btns > .e-btn { flex: 1; }


/* ---------- 10. SEGMENTED SWITCH + NICHE (AdCreator brand switch) ---- */
/* <div class="e-seg"><button class="on">Fitness</button><button>Property</button></div>
   active = bg-gray-900 text-white, others white gray-600 */
.e-seg {
  display: inline-flex; overflow: hidden; border-radius: var(--e-r-btn); border: 1px solid var(--e-line);
  font-size: var(--e-fs-ui); font-weight: 500; background: var(--e-card);
}
.e-seg > button, .e-seg > a {
  padding: 6px 10px; border: 0; background: var(--e-card); color: var(--e-ink-2); cursor: pointer;
  text-decoration: none; line-height: 1.4; white-space: nowrap;
}
.e-seg > button + button, .e-seg > a + a { border-left: 1px solid var(--e-line); }
.e-seg > button:hover, .e-seg > a:hover { background: var(--e-card-2); }
.e-seg > .on, .e-seg > .on:hover { background: var(--e-ink); color: var(--e-on-accent); }
/* THE niche switch: an .e-seg with a leading word. Put it in .e-actions
   on every page whose content is per niche (fitness / property).
   <div class="e-niche"><span class="e-niche__label">Niche</span>
     <div class="e-seg"><button class="on">Fitness</button><button>Property</button></div></div> */
.e-niche { display: inline-flex; align-items: center; gap: 8px; }
.e-niche__label { font-size: var(--e-fs-label); font-weight: 700; text-transform: uppercase; letter-spacing: var(--e-track-label); color: var(--e-mute); }
/* a niche select, when the list is long enough that buttons would wrap */
.e-niche .e-select { width: auto; min-width: 140px; }


/* ---------- 11. TABS (TopBar tab strip) ------------------------------ */
/* For sub-views INSIDE an engine page (Copy System stages, Copy Intel modes,
   Library Offers/Photos). HQ's own strip above the frame is the station's;
   never repeat it. Active = 2px blue-600 underline, blue-700 semibold. */
.e-tabs { display: flex; gap: 2px; overflow-x: auto; border-bottom: 1px solid var(--e-line); }
.e-tab {
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
  padding: 14px 10px 10px; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px;
  background: none; color: var(--e-ink-2); font-size: var(--e-fs-tab); cursor: pointer; text-decoration: none;
}
.e-tab:hover { color: var(--e-ink); }
.e-tab.on { border-bottom-color: var(--e-accent); color: var(--e-accent-hi); font-weight: 600; }
.e-tabs.compact .e-tab { padding: 8px 10px 6px; font-size: var(--e-fs-body); }


/* ---------- 12. FORMS (NewClientForm INPUT / LABEL, AdCreator) ------- */
/* <label class="e-field"><span class="e-label">Suburb</span><input class="e-input"></label> */
.e-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.e-field.row { flex-direction: row; align-items: center; gap: 8px; }
.e-label {
  display: block; font-size: var(--e-fs-ui); font-weight: 600; text-transform: uppercase;
  letter-spacing: var(--e-track-form); color: var(--e-ink-3);
}
.e-label.small { font-size: var(--e-fs-label); font-weight: 700; letter-spacing: var(--e-track-th); color: var(--e-mute); } /* modal section label */
.e-label .e-link { text-transform: none; letter-spacing: 0; font-weight: 600; margin-left: auto; }
.e-input, .e-select, .e-textarea {
  width: 100%; min-width: 0; appearance: none; -webkit-appearance: none;
  padding: 6px 12px; border-radius: var(--e-r-btn); border: 1px solid var(--e-line);
  background: var(--e-card); color: var(--e-ink);
  font-size: var(--e-fs-body); line-height: var(--e-lh-body); outline: none;
}
.e-input.well, .e-select.well { background: var(--e-card-2); }           /* ClientDetail's gray-50 well */
.e-input:focus, .e-select:focus, .e-textarea:focus { border-color: var(--e-accent-mid); }
.e-input:disabled, .e-select:disabled, .e-textarea:disabled { background: var(--e-line-2); color: var(--e-mute); cursor: not-allowed; }
.e-input.bad, .e-select.bad { border-color: var(--e-bad-line); }
.e-select {
  padding-right: 28px; cursor: pointer;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'><path d='M3 4.5l3 3 3-3' fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat; background-position: right 10px center;
}
.e-select[multiple] { background-image: none; padding-right: 12px; }
.e-textarea { resize: vertical; min-height: 72px; }
.e-input.sm, .e-select.sm { padding: 4px 8px; font-size: var(--e-fs-ui); }
.e-input.sm.e-select, .e-select.sm { padding-right: 26px; }
.e-input[type="number"] { font-variant-numeric: tabular-nums; }
.e-input[type="date"] { color-scheme: var(--e-scheme); }
/* a search box is just an input with a fixed width in a toolbar */
.e-search { width: 240px; }
/* checkbox / radio: HQ accent-blue-600, 14px */
.e-check { display: inline-flex; align-items: center; gap: 8px; font-size: var(--e-fs-body); color: var(--e-ink); cursor: pointer; }
.e-check input { width: 14px; height: 14px; margin: 0; accent-color: var(--e-accent); cursor: pointer; }
.e-check .e-meta { margin-left: auto; }
/* a bordered list of checks (modal "Offers · 3") */
.e-checklist { border: 1px solid var(--e-line); border-radius: var(--e-r-btn); overflow: auto; max-height: 224px; }
.e-checklist .e-check { padding: 6px 10px; border-bottom: 1px solid var(--e-line-2); }
.e-checklist .e-check:last-child { border-bottom: 0; }
/* two-column form grid (modal body) */
.e-form { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.e-form .span { grid-column: 1 / -1; }
.e-help { font-size: var(--e-fs-foot); color: var(--e-ink-3); }
/* the price on the button ("Generate 2 · ~$0.60") stays in the label; a cost line under a form */
.e-cost { font-family: var(--e-mono); font-size: var(--e-fs-foot); font-variant-numeric: tabular-nums; color: var(--e-ink-2); }


/* ---------- 13. NOTICE (AdCreator amber notice / CopyBank blue strip) - */
/* Replaces alert()/confirm(): an inline bar, optionally with actions.
   <div class="e-notice warn">This will classify 42 ads for about US$0.31.
     <span class="e-notice__actions"><button class="e-btn sm">Cancel</button><button class="e-btn sm primary">Classify</button></span></div> */
.e-notice {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  padding: 10px var(--e-pad); border-radius: var(--e-r-btn); border: 1px solid var(--e-line);
  background: var(--e-card); color: var(--e-ink-2); font-size: var(--e-fs-body); line-height: var(--e-lh-body);
}
.e-notice.blue { background: var(--e-accent-soft); border-color: var(--e-accent-line); color: var(--e-accent-ink); }
.e-notice.warn { background: var(--e-warn-soft);   border-color: var(--e-warn-line);   color: var(--e-warn-ink); }
.e-notice.bad  { background: var(--e-bad-soft);    border-color: var(--e-bad-line);    color: var(--e-bad); }
.e-notice.good { background: var(--e-good-soft);   border-color: var(--e-good-line);   color: var(--e-good); }
.e-notice b { font-weight: 600; }
.e-notice__actions { margin-left: auto; display: flex; gap: 6px; }
/* a fixed toast for background results ("Saved", "3 deleted") — bottom centre of the frame */
.e-toast {
  position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%); z-index: 60;
  padding: 8px 14px; border-radius: var(--e-r-btn); border: 1px solid var(--e-line);
  background: var(--e-card); color: var(--e-ink); font-size: var(--e-fs-body); box-shadow: var(--e-shadow-sm);
}
.e-toast.good { border-color: var(--e-good-line); background: var(--e-good-soft); color: var(--e-good); }
.e-toast.bad  { border-color: var(--e-bad-line);  background: var(--e-bad-soft);  color: var(--e-bad); }


/* ---------- 14. PROGRESS (AdCreator job card) ------------------------ */
/* <div class="e-progress"><div class="e-progress__bar" style="width:40%"></div></div>
   Sits inside .e-card.busy (blue-50) for a running job; the bar is blue-600. */
.e-progress { height: 8px; overflow: hidden; border-radius: var(--e-r-pill); background: var(--e-card); border: 1px solid var(--e-line-2); }
.e-card.busy .e-progress, .e-card.good .e-progress, .e-card.bad .e-progress { background: var(--e-card); }
.e-progress__bar { height: 100%; border-radius: var(--e-r-pill); background: var(--e-accent); transition: width .3s; min-width: 2%; }
.e-progress__bar.good { background: var(--e-good-btn); }
.e-progress__bar.bad  { background: var(--e-bad-num); }
.e-progress__bar.warn { background: var(--e-warn-num); }
/* the line above a bar: "Rendering 12 of 40 · $0.00" */
.e-progress__line { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: var(--e-fs-body); }
.e-progress__line b { font-weight: 600; color: var(--e-ink); }
.e-progress__line .e-cost { font-size: var(--e-fs-foot); }
/* a plain horizontal bar chart row (costs by task): label · bar · value */
.e-bar { display: grid; grid-template-columns: minmax(120px, 1fr) 3fr auto; align-items: center; gap: 10px; font-size: var(--e-fs-body); }
.e-bar .e-progress { height: 6px; }


/* ---------- 15. DRAWER + MODAL (AdCreator Fill dialog) --------------- */
/* Both are position:fixed, so inside HQ they fill the FRAME — which is the
   content column. That is the right place: HQ's rail and bar stay visible.
   <div class="e-scrim"></div>
   <aside class="e-drawer"><div class="e-drawer__head"><h2>…</h2><p>…</p><button class="e-x">×</button></div>
     <div class="e-drawer__body">…</div><div class="e-drawer__foot">…</div></aside> */
.e-scrim { position: fixed; inset: 0; z-index: 50; background: var(--e-scrim); }
.e-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 51;
  width: 440px; max-width: 100vw; display: flex; flex-direction: column;
  background: var(--e-card); border-left: 1px solid var(--e-line); box-shadow: var(--e-shadow-2xl);
}
.e-drawer.wide { width: 640px; }
.e-drawer.left { right: auto; left: 0; border-left: 0; border-right: 1px solid var(--e-line); }
.e-drawer__head, .e-modal__head { position: relative; padding: 16px 20px; border-bottom: 1px solid var(--e-line-2); }
.e-drawer__head h2, .e-modal__head h2 { font-size: var(--e-fs-bar); font-weight: 700; color: var(--e-ink); padding-right: 32px; }
.e-drawer__head p, .e-modal__head p { margin-top: 2px; font-size: var(--e-fs-ui); color: var(--e-ink-3); }
.e-drawer__body, .e-modal__body { flex: 1; min-height: 0; overflow-y: auto; padding: 16px 20px; display: flex; flex-direction: column; gap: 16px; }
.e-drawer__foot, .e-modal__foot { display: flex; align-items: center; gap: 8px; justify-content: flex-end; padding: 12px 20px; border-top: 1px solid var(--e-line-2); background: var(--e-card-2); }
.e-drawer__foot .e-cost, .e-modal__foot .e-cost { margin-right: auto; }
/* the × in a head */
.e-x {
  position: absolute; top: 12px; right: 12px; width: 28px; height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--e-r-sm); background: transparent; color: var(--e-ink-3);
  font-size: 18px; line-height: 1; cursor: pointer;
}
.e-x:hover { background: var(--e-line-2); color: var(--e-ink); }
/* centred dialog (confirmations that need more than a notice bar) */
.e-modal-wrap { position: fixed; inset: 0; z-index: 51; display: flex; align-items: center; justify-content: center; padding: 16px; }
.e-modal {
  display: flex; flex-direction: column; overflow: hidden;
  width: 100%; max-width: 672px; max-height: 90vh;
  background: var(--e-card); border-radius: var(--e-r-modal); box-shadow: var(--e-shadow-2xl);
}
.e-modal.sm { max-width: 440px; }
.e-modal.lg { max-width: 960px; }
/* a right-hand chat panel is a drawer whose body is the thread */
.e-thread { display: flex; flex-direction: column; gap: 8px; }
.e-msg { max-width: 85%; padding: 8px 12px; border-radius: 10px; font-size: var(--e-fs-body); line-height: var(--e-lh-body); }
.e-msg.me   { align-self: flex-end; background: var(--e-accent); color: var(--e-on-accent); }
.e-msg.them { align-self: flex-start; background: var(--e-line-2); color: var(--e-ink); }


/* ---------- 16. EMPTY STATE (WorkspaceUI Empty / AdCreator no-look) -- */
/* <div class="e-empty">No house look is approved yet. <a href="/concepts.html#templates">Approve looks in Templates →</a></div> */
.e-empty {
  padding: 32px var(--e-pad); text-align: center;
  font-size: var(--e-fs-tab); color: var(--e-mute); line-height: 1.5;
}
.e-empty a, .e-empty .e-link { font-weight: 500; color: var(--e-accent-hi); }
.e-empty.card { background: var(--e-card); border: 1px solid var(--e-line); border-radius: var(--e-r-card); }
.e-empty.dashed { border: 1px dashed var(--e-line-3); border-radius: var(--e-r-card); }
.e-empty .e-btn { margin-top: 12px; }
/* loading + error lines inside a card body */
.e-loading { padding: 24px var(--e-pad); font-size: var(--e-fs-body); color: var(--e-mute); }
.e-error   { padding: 24px var(--e-pad); font-size: var(--e-fs-body); color: var(--e-bad); }


/* ---------- 17. NEXT STEP (the one obvious way out of the page) ------ */
/* Last block on every page: where the work goes after here. One sentence,
   one link. Blue-tinted so it reads as the line moving on, not a footer.
   <div class="e-next"><span class="e-next__label">Next</span>
     <span class="e-next__text">Approved? The look is now a column in the Ad Creator.</span>
     <a class="e-btn primary" href="…">Open Ad Creator →</a></div> */
.e-next {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
  padding: 12px var(--e-pad); border-radius: var(--e-r-card);
  border: 1px solid var(--e-accent-line); background: var(--e-accent-soft);
}
.e-next__label {
  font-size: var(--e-fs-label); font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--e-track-label); color: var(--e-accent-hi);
}
.e-next__text { font-size: var(--e-fs-body); color: var(--e-ink-2); }
.e-next > .e-btn, .e-next > a:last-child { margin-left: auto; }
.e-next.quiet { background: var(--e-card); border-color: var(--e-line); }
.e-next.quiet .e-next__label { color: var(--e-mute); }


/* ---------- 18. MEDIA + AD-CARD CHROME (swipe cards, bank thumbs) ---- */
/* The Facebook-facsimile card keeps its structure; this is only its skin. */
.e-ad { display: flex; flex-direction: column; background: var(--e-card); border: 1px solid var(--e-line); border-radius: var(--e-r-card); overflow: hidden; }
.e-ad.on { border-color: var(--e-accent-line); box-shadow: 0 0 0 2px var(--e-accent-soft); }
.e-ad__head { display: flex; align-items: center; gap: 8px; padding: 10px var(--e-pad) 6px; }
.e-ad__page { font-size: var(--e-fs-body); font-weight: 600; color: var(--e-ink); }
.e-ad__sponsored { font-size: var(--e-fs-label); color: var(--e-mute); }
.e-ad__body { padding: 0 var(--e-pad) 10px; font-size: var(--e-fs-body); line-height: var(--e-lh-body); color: var(--e-ink-2); white-space: pre-wrap; }
.e-ad__body.clamp { max-height: 96px; overflow: hidden; position: relative; }
.e-ad__body.clamp::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 32px; background: linear-gradient(transparent, var(--e-card)); }
.e-ad__media { background: var(--e-line-2); aspect-ratio: 1 / 1; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.e-ad__media img, .e-ad__media video { width: 100%; height: 100%; object-fit: cover; display: block; }
.e-ad__media.contain img { object-fit: contain; }         /* heads never cropped (Library photos) */
.e-ad__link { display: flex; align-items: center; gap: 10px; padding: 8px var(--e-pad); background: var(--e-card-2); border-top: 1px solid var(--e-line-2); }
.e-ad__domain { font-size: var(--e-fs-label); text-transform: uppercase; color: var(--e-mute); }
.e-ad__headline { font-size: var(--e-fs-body); font-weight: 600; color: var(--e-ink); }
.e-ad__desc { font-size: var(--e-fs-foot); color: var(--e-ink-3); }
.e-ad__cta { margin-left: auto; flex-shrink: 0; }            /* an .e-chip */
.e-ad__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 8px var(--e-pad); border-top: 1px solid var(--e-line-2); font-size: 11.5px; color: var(--e-ink-3); }
.e-ad__actions { display: flex; gap: 4px; padding: 8px var(--e-pad); border-top: 1px solid var(--e-line-2); }
.e-ad__actions .e-btn { flex: 1; }
/* a frame that takes the ad's real ratio (Reference | Your ad) */
.e-frame { background: var(--e-line-2); border: 1px solid var(--e-line); border-radius: var(--e-r-btn); overflow: hidden; }
.e-frame[data-ratio="4:5"]  { aspect-ratio: 4 / 5; }
.e-frame[data-ratio="9:16"] { aspect-ratio: 9 / 16; }
.e-frame[data-ratio="1:1"]  { aspect-ratio: 1 / 1; }
.e-frame[data-ratio="3:4"]  { aspect-ratio: 3 / 4; }
.e-frame img { width: 100%; height: 100%; object-fit: contain; display: block; }
/* the winner / best-fit ring: emerald, no glow */
.e-frame.best, .e-ad.best { border-color: var(--e-good-line); box-shadow: 0 0 0 2px var(--e-good-soft); }
/* thumbnail strip */
.e-thumbs { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 4px; }
.e-thumb { width: 64px; height: 64px; flex-shrink: 0; border-radius: var(--e-r-sm); border: 2px solid transparent; overflow: hidden; background: var(--e-line-2); cursor: pointer; }
.e-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.e-thumb.on { border-color: var(--e-accent); }
.e-thumb__cap { font-size: var(--e-fs-label); color: var(--e-mute); text-align: center; margin-top: 2px; }
/* legend swatch (Ad Bank: empty / partly / full) */
.e-legend { display: flex; align-items: center; gap: 12px; font-size: 11.5px; color: var(--e-ink-3); }
.e-swatch { display: inline-block; width: 12px; height: 12px; border-radius: var(--e-r-chip); border: 1px solid var(--e-line); vertical-align: -2px; margin-right: 4px; }
.e-swatch.empty { border-style: dashed; border-color: var(--e-line-3); }
.e-swatch.warn { background: var(--e-warn-soft); border-color: var(--e-warn-line); }
.e-swatch.good { background: var(--e-good-soft); border-color: var(--e-good-line); }


/* ---------- 19. FILTER RAIL (swipe browse) --------------------------- */
/* The faceted rail stays; these are its clothes. */
.e-rail { width: 300px; flex-shrink: 0; display: flex; flex-direction: column; gap: 2px; }
.e-with-rail { display: flex; gap: var(--e-gap-lg); align-items: flex-start; }
.e-with-rail > .e-main { flex: 1; min-width: 0; }
.e-facet { border-top: 1px solid var(--e-line-2); }
.e-facet__head {
  display: flex; align-items: center; gap: 6px; width: 100%; padding: 8px 4px;
  border: 0; background: none; cursor: pointer; text-align: left;
  font-size: var(--e-fs-label); font-weight: 700; text-transform: uppercase; letter-spacing: var(--e-track-th); color: var(--e-mute);
}
.e-facet__head:hover { color: var(--e-ink); }
.e-facet__head .e-count { margin-left: auto; }
.e-facet__rows { display: flex; flex-direction: column; gap: 1px; padding-bottom: 6px; }
.e-facet__row {
  display: flex; align-items: center; gap: 8px; padding: 4px 6px; border-radius: var(--e-r-sm);
  font-size: var(--e-fs-ui); color: var(--e-ink-2); cursor: pointer;
}
.e-facet__row:hover { background: var(--e-card-2); }
.e-facet__row.on { background: var(--e-accent-soft); color: var(--e-accent-hi); font-weight: 500; }
.e-facet__row.zero { color: var(--e-mute); }                 /* a selected value at (0) stays visible */
.e-facet__row .e-count { margin-left: auto; }
.e-facet__row.on .e-count { background: var(--e-accent-soft-2); color: var(--e-accent-hi); }
@media (max-width: 900px) { .e-with-rail { flex-direction: column; } .e-rail { width: 100%; } }


/* ---------- 20. UTILITIES ------------------------------------------- */
.e-hide { display: none !important; }
.e-flex { display: flex; align-items: center; gap: 8px; }
.e-wrap { flex-wrap: wrap; }
.e-grow { flex: 1; min-width: 0; }
.e-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.e-pre { white-space: pre-line; }
.e-gap-0 { gap: 0; } .e-gap-1 { gap: 4px; } .e-gap-2 { gap: 8px; } .e-gap-3 { gap: 12px; }
.e-mt-1 { margin-top: 4px; } .e-mt-2 { margin-top: 8px; } .e-mt-3 { margin-top: 12px; } .e-mt-auto { margin-top: auto; }
.e-w-full { width: 100%; }
.e-ink { color: var(--e-ink); } .e-ink-2 { color: var(--e-ink-2); } .e-ink-3 { color: var(--e-ink-3); }
.e-good-text { color: var(--e-good); } .e-warn-text { color: var(--e-warn); } .e-bad-text { color: var(--e-bad); } .e-blue-text { color: var(--e-accent-hi); }
.e-fs-foot { font-size: var(--e-fs-foot); } .e-fs-ui { font-size: var(--e-fs-ui); } .e-fs-body { font-size: var(--e-fs-body); } .e-fs-base { font-size: var(--e-fs-base); line-height: var(--e-lh-base); }
.e-semibold { font-weight: 600; } .e-bold { font-weight: 700; }
.e-upper { text-transform: uppercase; letter-spacing: 0.05em; }
/* a resize handle (rail): 4px gray line, blue on hover — never pink */
.e-handle { width: 4px; cursor: col-resize; border-radius: 2px; background: transparent; }
.e-handle:hover, .e-handle.on { background: var(--e-accent-line); }
/* keyboard hint */
.e-kbd { display: inline-block; padding: 0 5px; border: 1px solid var(--e-line); border-bottom-width: 2px; border-radius: var(--e-r-chip); background: var(--e-card-2); font-family: var(--e-mono); font-size: var(--e-fs-label); color: var(--e-ink-3); }
