/* EventAvenue design system — the Event Avenue Design System, in our stack.
 *
 * THE DESIGN SYSTEM IS THE LAW (founder, 2026-08-20). The source of truth is
 * assets/design_system/: styles.css and the tokens/ modules it imports, the
 * guidelines/ cards, and the ui_kits/ JSX as reference designs. This file is
 * the PORT, not a copy — the kit ships JSX and two Google-Fonts @imports, and
 * neither may exist here (no Node, no build step, no CDN: the product must run
 * offline and keyless). What is taken is the design: the tokens, the geometry,
 * the type, the states.
 *
 * THREE LAYERS, and nothing may skip one:
 *
 *   1. PRIMITIVES   raw values. The only place a hex, a px or a ms may appear.
 *   2. SEMANTICS    what a primitive MEANS here (surface, ink, state, action).
 *                   Components read these, never the primitives.
 *   3. COMPONENTS   built only from semantics. A component that hard-codes a
 *                   value is a bug, and tests/test_design_system.py fails it.
 *
 * NAMES. Every token carries the design system's OWN name (--ea-signal,
 * --radius-xl, --duration-sm, --ease-snap, --bp-md, --tracking-label), so a
 * value can be checked against assets/design_system/tokens/ by reading. The
 * short names this product already speaks (--c-*, --fs-*, --r-*, --t-*, --e-*)
 * stay as ALIASES in the primitives layer — one value, two spellings, never
 * two values.
 *
 * THE COLOUR LAW. Two accents, split by MEANING, so they never compete:
 *
 *     SIGNAL cyan #2BC8D9 is INTENT — the single most important action in a
 *     view, the current selection, work in flight. One per view.
 *     PULSE green #16B888 is TRUTH — live on air, confirmed, healthy.
 *
 * Status stays off the accent axis: warn amber, alert crimson, info blue —
 * machine state, never mood. Red is failure only. docs/UI_RULEBOOK.html
 * carries the same law in words.
 *
 * TYPE IS ARCHIVO, and only Archivo. "Data" is a STYLE, not a second family:
 * Archivo 500, uppercase, .14em tracking, tabular figures. Big Shoulders,
 * IBM Plex Mono and Inter are retired from the interface (the RENDERED assets
 * are unaffected — pipeline/poster.py still picks a venue's own face).
 *
 * DEPTH, NOT GLASS. Surfaces step by VALUE — sunken -> canvas -> raised ->
 * overlay. There is no backdrop-filter in this file and there may not be one:
 * blur costs frames on the cheap Android sticks behind venue screens, and a
 * client's rendered artwork must never be judged through a material. The kit's
 * frosted and veil blur tokens, and the two materials built from them, are
 * deliberately NOT ported for that reason.
 */

/* ---------------------------------------------------------------- 1. PRIMITIVES */

/* FONT SUBSTITUTION, written down because it is a substitution.
 *
 * The design system names two faces and ships NEITHER as a file:
 * assets/design_system/tokens/fonts.css pulls Archivo and Chakra Petch from
 * fonts.googleapis.com. A CDN is forbidden here, so:
 *
 *   - ARCHIVO is the interface family. One binary is on disk
 *     (static/fonts/Archivo-Black.ttf), so only the display weight is real and
 *     is served as "Archivo Display". Body and data name "Archivo" first (a
 *     locally installed copy is used when there is one) and otherwise fall
 *     through to the platform sans stack the design system itself names.
 *     Drop Archivo-Regular/Medium/SemiBold/Bold into static/fonts/ and add
 *     four @font-face rules here — nothing else changes.
 *   - CHAKRA PETCH is the BRAND face and sets exactly one thing: the logo
 *     lockup. No file was supplied, so --font-brand names it first and
 *     substitutes Archivo, then the platform sans. It never sets UI copy.
 */
@font-face {
  font-family: "Archivo Display";
  src: url("/static/fonts/Archivo-Black.ttf") format("truetype");
  font-weight: 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Neutrals — true neutral, faintly cool, so cyan reads as light rather than
     paint. No pure black in the interface. */
  --ea-black:     #08080A;   /* the canvas */
  --ea-ink-900:   #101014;   /* a card on it */
  --ea-ink-800:   #17171C;   /* something above the card */
  --ea-ink-700:   #1F1F25;
  --ea-ink-600:   #2A2A31;
  --ea-ink-500:   #3A3A43;
  --ea-sunken:    #050507;
  --ea-grey-400:  #8B8B96;
  --ea-grey-300:  #AFAFB9;
  --ea-grey-200:  #CFCFD7;
  --ea-grey-100:  #E9E9EE;
  --ea-paper:     #F6F6F8;
  --ea-white:     #FCFCFD;

  /* Signal — intent. The same cyan as the mark. The one colour that means
     "act" or "the system is working". Bright, so a fill takes near-black ink. */
  --ea-signal:      #2BC8D9;
  --ea-signal-hot:  #63E4F2;
  --ea-signal-deep: #0E4E5A;

  /* Pulse — truth. Cool green: the last hue to desaturate as a room goes
     dark, which is why live state gets it rather than the accent. */
  --ea-pulse:      #16B888;
  --ea-pulse-hot:  #33D6A5;
  --ea-pulse-deep: #0C7A5C;

  /* Machine state, never mood. Deliberately not cyan, so a warning can never
     be mistaken for the action. */
  --ea-warn:      #EFC53F;
  --ea-alert:     #E2455C;
  --ea-info:      #6E8BF5;

  /* Ink on a fill. Near-black, mixed toward the fill's own hue. */
  --ea-on-signal: #04191D;
  --ea-on-pulse:  #04140F;

  /* Alphas. A semantic token may not invent an rgba(), so they live here. */
  --a-hairline:    rgba(255, 255, 255, .07);
  --a-strong:      rgba(255, 255, 255, .14);
  --a-ghost:       rgba(255, 255, 255, .05);
  --a-ghost-up:    rgba(255, 255, 255, .10);
  --a-bevel:       rgba(255, 255, 255, .05);
  --a-scrim:       rgba(8, 8, 10, .68);
  --a-signal-wash: rgba(43, 200, 217, .15);
  --a-signal-halo: rgba(43, 200, 217, .28);
  --a-pulse-wash:  rgba(22, 184, 136, .14);
  --a-warn-wash:   rgba(239, 197, 63, .14);
  --a-alert-wash:  rgba(226, 69, 92, .14);
  --a-info-wash:   rgba(110, 139, 245, .14);
  --a-idle-wash:   rgba(139, 139, 150, .12);

  /* The short spellings this product's pages already speak. */
  --c-black:       var(--ea-black);
  --c-ink-900:     var(--ea-ink-900);
  --c-ink-800:     var(--ea-ink-800);
  --c-ink-700:     var(--ea-ink-700);
  --c-ink-600:     var(--ea-ink-600);
  --c-ink-500:     var(--ea-ink-500);
  --c-sunken:      var(--ea-sunken);
  --c-grey-400:    var(--ea-grey-400);
  --c-grey-300:    var(--ea-grey-300);
  --c-grey-200:    var(--ea-grey-200);
  --c-grey-100:    var(--ea-grey-100);
  --c-paper:       var(--ea-paper);
  --c-white:       var(--ea-white);
  --c-signal:      var(--ea-signal);
  --c-signal-hot:  var(--ea-signal-hot);
  --c-signal-deep: var(--ea-signal-deep);
  --c-pulse:       var(--ea-pulse);
  --c-pulse-hot:   var(--ea-pulse-hot);
  --c-pulse-deep:  var(--ea-pulse-deep);
  --c-warn:        var(--ea-warn);
  --c-alert:       var(--ea-alert);
  --c-alert-hot:   var(--ea-alert);
  --c-info:        var(--ea-info);

  /* Type. Archivo, and nothing else — see the substitution note above. */
  --font-display: "Archivo Display", "Archivo", system-ui, -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-body:    "Archivo", system-ui, -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-data:    var(--font-body);   /* data is a STYLE, not a family */
  --font-brand:   "Chakra Petch", "Archivo", system-ui, -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif;
  --f-display:    var(--font-display);
  --f-sans:       var(--font-body);
  --f-data:       var(--font-data);
  --f-brand:      var(--font-brand);

  /* Nine-step fluid scale, all clamp() — the type never jumps at a breakpoint. */
  --text-2xs: .75rem;
  --text-xs:  .8125rem;
  --text-sm:  .9375rem;
  --text-md:  clamp(1.06rem, calc(.2vw + 1.02rem), 1.25rem);
  --text-lg:  clamp(1.25rem, calc(.5vw + 1.14rem), 1.75rem);
  --text-xl:  clamp(1.5rem, calc(.95vw + 1.3rem), 2.375rem);
  --text-2xl: clamp(1.9rem, calc(1.6vw + 1.58rem), 3.1rem);
  --text-3xl: clamp(2.4rem, calc(2.4vw + 1.92rem), 4.2rem);
  --text-4xl: clamp(3rem, calc(3.6vw + 2.28rem), 5.6rem);
  --fs-2xs:     var(--text-2xs);
  --fs-xs:      var(--text-xs);
  --fs-sm:      var(--text-sm);
  --fs-md:      var(--text-md);
  --fs-lg:      var(--text-lg);
  --fs-xl:      var(--text-xl);
  --fs-2xl:     var(--text-2xl);
  --fs-3xl:     var(--text-3xl);
  --fs-4xl:     var(--text-4xl);
  --fs-display: var(--text-xl);

  --leading-tight:  1.06;
  --leading-snug:   1.24;
  --leading-normal: calc(6px + 2.05ex);
  --leading-loose:  1.6;
  --lh-display: var(--leading-tight);
  --lh-title:   var(--leading-snug);
  --lh-body:    var(--leading-normal);
  --lh-loose:   var(--leading-loose);

  /* Display is set tight and negative-tracked; data is wide-tracked caps. The
     contrast between the two is the brand's signature. */
  --tracking-display: -.022em;
  --tracking-body:    0em;
  --tracking-data:    .08em;
  --tracking-label:   .14em;
  --tr-display: var(--tracking-display);
  --tr-body:    var(--tracking-body);
  --tr-mid:     var(--tracking-data);
  --tr-data:    var(--tracking-label);

  --weight-regular:  400;
  --weight-medium:   500;
  --weight-semibold: 600;
  --weight-bold:     700;
  --fw-regular:  var(--weight-regular);
  --fw-medium:   var(--weight-medium);
  --fw-semibold: var(--weight-semibold);
  --fw-bold:     var(--weight-bold);
  --fw-display:  900;      /* the only weight we hold on disk */
  --measure: 67ch;

  /* Space — static for component interiors, fluid clamps for page rhythm. */
  --space-2xs: 2px;
  --space-xs:  4px;
  --space-sm:  8px;
  --space-md:  16px;
  --space-lg:  32px;
  --space-xl:  48px;
  --space-2xl: 80px;
  --space-fluid-xs:  clamp(4px, .25vw + 2px, 8px);
  --space-fluid-sm:  clamp(8px, .5vw + 6px, 16px);
  --space-fluid-md:  clamp(16px, 1.25vw + 12px, 36px);
  --space-fluid-lg:  clamp(32px, 2.75vw + 24px, 76px);
  --space-fluid-xl:  clamp(48px, 3vw + 38px, 96px);
  --space-fluid-2xl: clamp(80px, 7.5vw + 56px, 200px);

  /* Radius is a finish, not a style. Small, consistent, never pill. */
  --radius-xs:  2px;
  --radius-sm:  4px;
  --radius-md:  6px;
  --radius-lg:  8px;    /* compact controls */
  --radius-xl:  10px;   /* default controls */
  --radius-2xl: 12px;   /* notifications, panels */
  --radius-3xl: 14px;   /* cards, dialogs */
  --radius-4xl: 18px;
  --radius-screen: 4px; /* physical bezels are barely rounded */
  --radius-full: 9999px;/* status dots only */
  --r-xs:     var(--radius-xs);
  --r-sm:     var(--radius-sm);
  --r-md:     var(--radius-md);
  --r-lg:     var(--radius-lg);
  --r-xl:     var(--radius-xl);
  --r-2xl:    var(--radius-2xl);
  --r-3xl:    var(--radius-3xl);
  --r-4xl:    var(--radius-4xl);
  --r-screen: var(--radius-screen);
  --r-full:   var(--radius-full);

  /* Motion. ONE power ladder: every duration is the previous one x1.6, and
     nothing in the system may invent a value between them. */
  --duration-xs: .12s;
  --duration-sm: .2s;
  --duration-md: .32s;
  --duration-lg: .52s;
  --duration-xl: .84s;
  --t-xs: var(--duration-xs);
  --t-sm: var(--duration-sm);
  --t-md: var(--duration-md);
  --t-lg: var(--duration-lg);

  /* ONE easing family. All five curves share the same steep tail, so
     everything decelerates with the same weight. */
  --ease-in:     cubic-bezier(.2, .8, .24, 1);   /* arriving */
  --ease-out:    cubic-bezier(.76, .2, .9, .4);  /* leaving */
  --ease-in-out: cubic-bezier(.62, .04, .34, 1); /* ambient */
  --ease-snap:   cubic-bezier(.2, .9, .24, 1);   /* controls — the "snappy" */
  --ease-gamma:  cubic-bezier(.34, .02, .2, 1);  /* panels */

  /* Identity reveal — deliberately outside the control ladder. Off is slower
     than on, the way a real backlit panel behaves. */
  --duration-reveal-in:  .52s;
  --duration-reveal-out: .72s;
  --stagger-reveal:      .09s;
  --dur-reveal: var(--duration-reveal-in);
  --transition-control:
    background-color var(--duration-sm) var(--ease-in),
    color var(--duration-sm) var(--ease-in),
    border-color var(--duration-sm) var(--ease-in),
    box-shadow var(--duration-sm) var(--ease-in);

  /* Depth — quiet: value first, then a wide low-opacity shadow. */
  --depth-0: none;
  --depth-1: 0 1px 2px rgba(0, 0, 0, .40), 0 2px 6px rgba(0, 0, 0, .24);
  --depth-2: 0 2px 6px rgba(0, 0, 0, .38), 0 12px 30px rgba(0, 0, 0, .34);
  --depth-3: 0 6px 14px rgba(0, 0, 0, .42), 0 26px 64px rgba(0, 0, 0, .46);
  --depth-4: 0 12px 28px rgba(0, 0, 0, .5), 0 56px 120px rgba(0, 0, 0, .6);
  --depth-well: inset 0 2px 6px rgba(0, 0, 0, .45);
  --e-1: var(--depth-1);
  --e-2: var(--depth-2);
  --e-3: var(--depth-3);
  --e-4: var(--depth-4);
  --e-well: var(--depth-well);
  /* The shadow triplet, for anything that reads as printed rather than lit. */
  --shadow-sm: 0 3px 8px rgba(0, 0, 0, .24);
  --shadow-md: 0 4px 16px rgba(0, 0, 0, .32);
  --shadow-lg: 0 8px 40px rgba(0, 0, 0, .44);
  --bevel-top: inset 0 1px 0 rgba(255, 255, 255, .05);
  --lift-hover: translateY(-1px);
  --lift-press: translateY(1px);

  /* The pixel language. Every backdrop is three layers, never more: a base
     surface value, ONE pattern, and at most one wash. Ink never exceeds 10%. */
  --pixel-unit: 4px;
  --pattern-tint:        rgba(255, 255, 255, .055);
  --pattern-tint-strong: rgba(255, 255, 255, .10);
  --pattern-pixel-grid: linear-gradient(var(--pattern-tint) 1px, transparent 1px),
                        linear-gradient(90deg, var(--pattern-tint) 1px, transparent 1px);
  --pattern-pixel-grid-size: 32px 32px;
  --pattern-dot-matrix: radial-gradient(var(--pattern-tint-strong) 1px, transparent 1px);
  --pattern-dot-matrix-size: 12px 12px;
  --pattern-scanline: repeating-linear-gradient(180deg, rgba(255, 255, 255, .04) 0 1px, transparent 1px 4px);
  --pattern-column-rule: repeating-linear-gradient(90deg, var(--pattern-tint) 0 1px, transparent 1px 96px);
  --pattern-crt: repeating-linear-gradient(180deg, rgba(0, 0, 0, .55) 0 1px, transparent 1px 4px);
  --pattern-checker: repeating-conic-gradient(rgba(255, 255, 255, .035) 0% 25%, transparent 0% 50%);
  --pattern-checker-size: 16px 16px;
  --stagelight-signal: radial-gradient(120% 80% at 50% 0%, rgba(43, 200, 217, .20), transparent 62%);
  --stagelight-pulse:  radial-gradient(120% 80% at 50% 100%, rgba(22, 184, 136, .16), transparent 62%);
  --vignette: radial-gradient(120% 90% at 50% 40%, transparent 45%, rgba(0, 0, 0, .55) 100%);

  /* Breakpoints. Declared as tokens so a page and a test can name the same
     number; CSS cannot read a var() in a media query, so a @media writes the
     px and this is what it is checked against. */
  --bp-xs:  480px;
  --bp-sm:  760px;
  --bp-md:  1000px;
  --bp-lg:  1300px;
  --bp-xl:  1760px;
  --bp-2xl: 1920px;
  --grid-columns: 12;
  --grid-gap: var(--space-md);
  --grid-margin: clamp(16px, 3vw, 64px);
  --grid-max: 1760px;

  /* The four canonical venue screen formats. Any creative survives all four. */
  --screen-portrait:  9 / 16;
  --screen-landscape: 16 / 9;
  --screen-strip:     32 / 9;
  --screen-square:    1 / 1;

  /* Structural dimensions are part of the system too — a rail width picked in
     one file and a padding matching it in another is how two things that must
     agree quietly stop agreeing. */
  --w-rail: 216px;
  --w-rail-phone: 44px;
  --sz-icon: 16px;
  --sz-icon-lg: 20px;
  --sz-step: 16px;
  --sz-dot: 5px;
  --hit: 44px;        /* a touch target is never smaller than this */
  --track: 3px;       /* meter and progress tracks */
  /* Control geometry, from the kit's Button: 14/20 default, 8/14 compact. */
  --pad-control:  14px 20px;
  --pad-compact:  8px 14px;
  --hit-compact:  34px;
}

/* prefers-reduced-motion zeroes every duration token. The @keyframes are NOT
   blanket-disabled: several elements fade IN via animation, and killing that
   outright would leave the content invisible. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --duration-xs: 0s; --duration-sm: 0s; --duration-md: 0s;
    --duration-lg: 0s; --duration-xl: 0s;
  }
}

/* ---------------------------------------------------------------- 2. SEMANTICS */
:root {
  /* Surfaces step by VALUE, never by shadow and never by blur. */
  --surface-sunken:  var(--ea-sunken);
  --surface-canvas:  var(--ea-black);
  --surface-raised:  var(--ea-ink-900);
  --surface-overlay: var(--ea-ink-800);
  --surface-input:   var(--ea-ink-800);
  --surface-hover:   var(--ea-ink-700);
  --surface-inverse: var(--ea-white);
  --surface-scrim:   var(--a-scrim);

  --text-primary:   var(--ea-white);
  --text-secondary: var(--ea-grey-300);
  --text-muted:     var(--ea-grey-400);
  --text-inverse:   var(--ea-black);
  --text-signal:    var(--ea-signal-hot);
  --text-pulse:     var(--ea-pulse-hot);
  --text-disabled:  var(--ea-ink-500);
  --ink:           var(--text-primary);
  --ink-dim:       var(--text-secondary);
  --ink-muted:     var(--text-muted);
  --ink-inverse:   var(--text-inverse);
  --ink-signal:    var(--text-signal);
  --ink-pulse:     var(--text-pulse);
  --ink-disabled:  var(--text-disabled);

  --border-hairline: var(--a-hairline);
  --border-strong:   var(--a-strong);
  --border-signal:   var(--ea-signal);
  --border-pulse:    var(--ea-pulse);
  --seam:          var(--border-hairline);
  --seam-strong:   var(--border-strong);
  --bevel:         var(--a-bevel);

  /* ACTION. Exactly one signal control per view — that is the law, and it is
     what makes the primary action findable at a glance. */
  --action-signal-bg:    var(--ea-signal);
  --action-signal-fg:    var(--ea-on-signal);  /* white on cyan is 1.8:1 */
  --action-signal-hover: var(--ea-signal-hot);
  --action-pulse-bg:     var(--ea-pulse);
  --action-pulse-fg:     var(--ea-on-pulse);
  --action-pulse-hover:  var(--ea-pulse-hot);
  --action-primary-bg:   var(--ea-white);
  --action-primary-fg:   var(--ea-black);
  --action-ghost-bg:     var(--a-ghost);
  --action-ghost-hover:  var(--a-ghost-up);
  --press-halo:          var(--a-signal-halo);
  /* Press: a cyan halo blooms under the control for the length of the
     press, and the face sinks into its own well. */
  --press-shadow:        0 0 0 3px var(--a-signal-halo),
                         0 2px 10px var(--a-signal-halo),
                         var(--depth-well);

  /* STATE. Each colour means exactly one thing. LIVE is Pulse green, work in
     flight is Signal cyan, and red is failure and nothing else. */
  --state-working:   var(--ea-signal);     /* generating, publishing, in flight */
  --state-ready:     var(--ea-warn);       /* rendered, waiting for a human */
  --state-live:      var(--ea-pulse);      /* on air, approved, confirmed */
  --state-failed:    var(--ea-alert);      /* the only red in the interface */
  --state-idle:      var(--ea-grey-400);   /* draft, queued, paused */
  --state-scheduled: var(--ea-info);
  --state-live-wash: var(--a-pulse-wash);
  --state-work-wash: var(--a-signal-wash);
  --state-ready-wash: var(--a-warn-wash);
  --state-failed-wash: var(--a-alert-wash);
  --state-idle-wash: var(--a-idle-wash);
  /* The generation lifecycle, in the design system's own words. Every
     campaign, screen and creative reports exactly one of these. */
  --state-draft:      var(--state-idle);
  --state-queued:     var(--state-idle);
  --state-generating: var(--state-working);
  --state-review:     var(--state-ready);
  --state-approved:   var(--state-live);
  --state-paused:     var(--state-idle);

  --focus: var(--ea-signal-hot);
  --focus-ring: var(--focus);
}

/* ---------------------------------------------------------------- 3. COMPONENTS */
*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--surface-canvas);
  color: var(--ink);
  font-family: var(--f-sans);
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  letter-spacing: var(--tr-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
::selection { background: var(--c-signal); color: var(--c-white); }

/* The first thing a keyboard meets. It is off-screen until focused, and then
   it is the most important control on the page. */
.ea-skip {
  position: absolute; inset-inline-start: var(--space-sm);
  inset-block-start: calc(var(--space-sm) * -8); z-index: 9999;
  background: var(--action-signal-bg); color: var(--action-signal-fg);
  padding: var(--space-sm) var(--space-md); border-radius: var(--r-lg);
  font-family: var(--f-data); font-size: var(--fs-2xs);
  letter-spacing: var(--tr-data); text-transform: uppercase;
  text-decoration: none;
}
.ea-skip:focus-visible { inset-block-start: var(--space-sm); }

/* --- type ---------------------------------------------------------------- */
.ea-display, .ea-title, .ea-body, .ea-small, .ea-micro, .ea-data { margin: 0; }
.ea-display {
  font-family: var(--f-display); font-size: var(--fs-display);
  line-height: var(--lh-display); letter-spacing: var(--tr-display);
  font-weight: var(--fw-display);
}
.ea-title {
  font-size: var(--fs-lg); line-height: var(--lh-title);
  letter-spacing: var(--tr-display); font-weight: var(--fw-semibold);
}
.ea-body { font-size: var(--fs-sm); line-height: var(--lh-body);
           max-inline-size: var(--measure); }
.ea-small { font-size: var(--fs-xs); line-height: var(--lh-loose);
            color: var(--ink-dim); }
/* Data is a STYLE, not a font: Archivo 500, uppercase, wide-tracked, tabular. */
.ea-micro, .ea-data {
  font-family: var(--f-data); font-size: var(--fs-2xs);
  font-weight: var(--fw-medium); letter-spacing: var(--tr-data);
  text-transform: uppercase; font-variant-numeric: tabular-nums;
  color: var(--ink-muted);
}
.ea-data { letter-spacing: var(--tr-mid); color: var(--ink-dim); }

a { color: var(--ink); text-decoration: none;
    border-block-end: 1px solid var(--seam-strong);
    transition: color var(--t-sm) var(--ease-in); }
a:hover { color: var(--ink-signal); }

/* --- Button -------------------------------------------------------------- */
/* One control, five variants. `signal` is the single most important action in
   a view and there is never a second one. */
/* The control geometry is the kit's Button: 14/20 padding at 44px minimum,
   8/14 at 34px compact, radius-xl and radius-lg. A label is SENTENCE CASE and
   names its result — the label is never uppercased in CSS. */
.ea-btn {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--space-sm);
  min-block-size: var(--hit);
  padding: var(--pad-control);
  border: 0; border-radius: var(--radius-xl);
  background: var(--action-ghost-bg); color: var(--ink);
  font-family: var(--f-sans); font-size: var(--fs-sm);
  font-weight: var(--fw-medium); letter-spacing: var(--tr-body);
  line-height: 1; white-space: nowrap;
  cursor: pointer; text-decoration: none; text-align: center;
  box-shadow: var(--depth-1);
  transition: background-color var(--t-sm) var(--ease-in),
              color var(--t-sm) var(--ease-in),
              box-shadow var(--t-sm) var(--ease-in),
              transform var(--t-xs) var(--ease-snap);
}
.ea-btn:hover { background: var(--action-ghost-hover);
                box-shadow: var(--depth-2); transform: var(--lift-hover); }
/* Solid buttons carry the CRT scanline at 22% — controls share the texture of
   the screens they make. Never on a flat variant. */
.ea-btn[data-variant="signal"]::before,
.ea-btn[data-variant="pulse"]::before,
.ea-btn[data-variant="primary"]::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image: var(--pattern-crt); opacity: .22;
}
.ea-btn > * { position: relative; }
/* Press: the control travels 2px down and shrinks 1.5%, and a cyan halo blooms
   under it for the length of the press — the text has to feel struck. */
.ea-btn:active { transform: translateY(2px) scale(.985);
                 box-shadow: var(--press-shadow); }
.ea-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.ea-btn[disabled], .ea-btn[aria-disabled="true"] {
  opacity: .4; cursor: not-allowed; }
.ea-btn[data-variant="signal"] { background: var(--action-signal-bg);
  color: var(--action-signal-fg); font-weight: var(--fw-bold); }
.ea-btn[data-variant="signal"]:hover { background: var(--action-signal-hover); }
.ea-btn[data-variant="pulse"] { background: var(--action-pulse-bg);
  color: var(--action-pulse-fg); font-weight: var(--fw-bold); }
.ea-btn[data-variant="pulse"]:hover { background: var(--action-pulse-hover); }
.ea-btn[data-variant="primary"] { background: var(--action-primary-bg);
  color: var(--action-primary-fg); font-weight: var(--fw-bold); }
.ea-btn[data-variant="secondary"] { background: var(--surface-overlay);
  box-shadow: inset 0 0 0 1px var(--seam-strong); }
.ea-btn[data-variant="secondary"]:hover { background: var(--surface-hover); }
.ea-btn[data-variant="ghost"] { background: none; color: var(--ink-dim);
  box-shadow: none; }
.ea-btn[data-variant="ghost"]:hover { box-shadow: none; transform: none; }
.ea-btn[data-variant="ghost"]:hover { color: var(--ink);
  background: var(--action-ghost-bg); }
.ea-btn[data-compact="true"] { min-block-size: var(--hit-compact);
  padding: var(--pad-compact); border-radius: var(--radius-lg);
  gap: var(--space-xs); font-size: var(--fs-xs); }
.ea-btn[data-full="true"] { inline-size: 100%; }
.ea-btn[data-loading="true"] { cursor: wait; }
.ea-btn[data-loading="true"] .ea-btn-dot {
  inline-size: var(--sz-dot); block-size: var(--sz-dot);
  border-radius: var(--r-full); background: currentColor;
  animation: ea-pulse var(--t-lg) var(--ease-in-out) infinite; }

/* --- IconButton ---------------------------------------------------------- */
.ea-iconbtn {
  display: inline-flex; align-items: center; justify-content: center;
  min-inline-size: var(--hit); min-block-size: var(--hit);
  padding: var(--space-sm); border: 0; border-radius: var(--r-lg);
  background: none; color: var(--ink-dim); cursor: pointer;
  transition: background-color var(--t-sm) var(--ease-in),
              color var(--t-sm) var(--ease-in);
}
.ea-iconbtn:hover { color: var(--ink); background: var(--action-ghost-bg); }
.ea-iconbtn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.ea-iconbtn[data-variant="secondary"] { background: var(--surface-overlay);
  box-shadow: inset 0 0 0 1px var(--seam); }
.ea-iconbtn[data-active="true"] { color: var(--ink-signal);
  background: var(--state-work-wash); }

/* --- Icon ---------------------------------------------------------------- */
/* Monochrome, stroked, never filled, never coloured except by its parent. */
.ea-ico { inline-size: var(--sz-icon); block-size: var(--sz-icon);
          flex: 0 0 var(--sz-icon); }
.ea-ico[data-size="lg"] { inline-size: var(--sz-icon-lg);
  block-size: var(--sz-icon-lg); flex-basis: var(--sz-icon-lg); }

/* --- Tag ----------------------------------------------------------------- */
/* Rationed: at most one row per section, never on a tile that already carries
   a status dot. A mono label beats a tag; a dot beats a label. */
.ea-tag {
  display: inline-flex; align-items: center; gap: var(--space-xs);
  padding: var(--space-2xs) var(--space-sm); border-radius: var(--r-sm);
  background: var(--action-ghost-bg); color: var(--ink-dim);
  font-family: var(--f-data); font-size: var(--fs-2xs);
  font-weight: var(--fw-medium); letter-spacing: var(--tr-data);
  text-transform: uppercase;
}
.ea-tag[data-tone="signal"] { color: var(--ink-signal);
  background: var(--state-work-wash); }
.ea-tag[data-tone="live"] { color: var(--ink-pulse);
  background: var(--state-live-wash); }
.ea-tag[data-tone="warn"] { color: var(--state-ready); }
.ea-tag[data-tone="alert"] { color: var(--state-failed); }

/* --- Divider ------------------------------------------------------------- */
.ea-divider { border: 0; border-block-start: 1px solid var(--seam);
              margin-block: var(--space-md); }
.ea-divider[data-orientation="vertical"] { border-block-start: 0;
  border-inline-start: 1px solid var(--seam); margin: 0;
  align-self: stretch; inline-size: 0; }

/* --- Status -------------------------------------------------------------- */
/* The only way lifecycle state is shown. State is NEVER colour alone: the dot
   always travels with its word, because red/green is the commonest colour
   blindness there is. */
.ea-status { display: inline-flex; align-items: center; gap: var(--space-sm);
  font-family: var(--f-data); font-size: var(--fs-2xs);
  font-weight: var(--fw-medium); letter-spacing: var(--tr-data);
  text-transform: uppercase; color: var(--ink-dim); }
.ea-status-dot { inline-size: var(--sz-dot); block-size: var(--sz-dot);
  border-radius: var(--r-full); background: var(--state-idle);
  flex: 0 0 var(--sz-dot); }
.ea-status[data-state="generating"] .ea-status-dot,
.ea-status[data-state="publishing"] .ea-status-dot { background: var(--state-working); }
.ea-status[data-state="review"] .ea-status-dot,
.ea-status[data-state="ready"] .ea-status-dot { background: var(--state-ready); }
.ea-status[data-state="approved"] .ea-status-dot,
.ea-status[data-state="live"] .ea-status-dot { background: var(--state-live); }
.ea-status[data-state="failed"] .ea-status-dot { background: var(--state-failed); }
.ea-status[data-state="scheduled"] .ea-status-dot { background: var(--state-scheduled); }
.ea-status[data-state="generating"] { color: var(--ink-signal); }
.ea-status[data-state="live"], .ea-status[data-state="approved"] { color: var(--ink-pulse); }
.ea-status[data-state="failed"] { color: var(--state-failed); }
.ea-status[data-variant="dot"] .ea-status-label { position: absolute;
  inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); }
.ea-status[data-variant="pill"] { padding: var(--space-2xs) var(--space-sm);
  border-radius: var(--r-full); background: var(--action-ghost-bg); }
.ea-status-track { inline-size: 100%; block-size: var(--track);
  background: var(--surface-sunken); border-radius: var(--r-full);
  overflow: hidden; }
.ea-status-fill { block-size: 100%; background: var(--state-working);
  transition: inline-size var(--t-md) var(--ease-in); }
/* Only `generating` animates — everything else is still. */
@media (prefers-reduced-motion: no-preference) {
  .ea-status[data-state="generating"] .ea-status-dot {
    animation: ea-pulse var(--t-lg) var(--ease-in) infinite alternate; }
}
@keyframes ea-pulse { from { opacity: 1; } to { opacity: .35; } }

/* --- Meter --------------------------------------------------------------- */
.ea-meter { display: flex; flex-direction: column; gap: var(--space-xs); }
.ea-meter-head { display: flex; align-items: baseline;
  justify-content: space-between; gap: var(--space-sm); }
.ea-meter-track { block-size: var(--track); background: var(--surface-sunken);
  border-radius: var(--r-full); overflow: hidden; box-shadow: var(--e-well); }
.ea-meter-fill { block-size: 100%; background: var(--state-working);
  transition: inline-size var(--t-md) var(--ease-in); }
.ea-meter[data-tone="live"] .ea-meter-fill { background: var(--state-live); }
.ea-meter[data-tone="neutral"] .ea-meter-fill { background: var(--ink-dim); }
/* Segmented: an LED strip, for anything that maps to real hardware. */
.ea-meter-segs { display: flex; gap: var(--space-2xs); }
.ea-meter-seg { flex: 1; block-size: var(--track); border-radius: var(--r-xs);
  background: var(--surface-sunken); }
.ea-meter-seg[data-on="true"] { background: var(--state-working); }
.ea-meter[data-tone="live"] .ea-meter-seg[data-on="true"] { background: var(--state-live); }

/* --- RunLog -------------------------------------------------------------- */
/* The visible proof that the machine is working. Newest work stays visible;
   the log is never collapsed behind a spinner, and a failed line keeps its
   text so the operator can read what broke. */
.ea-runlog { display: flex; flex-direction: column; gap: var(--space-sm);
  background: var(--surface-raised); border-radius: var(--r-2xl);
  padding: var(--space-md); }
.ea-runlog-head { display: flex; align-items: center; gap: var(--space-sm);
  justify-content: space-between; }
.ea-runlog-lines { display: flex; flex-direction: column;
  gap: var(--space-xs); margin: 0; padding: 0; list-style: none;
  max-block-size: 46vh; overflow-y: auto; overflow-wrap: anywhere; }
.ea-runlog-line { display: grid;
  grid-template-columns: auto auto 1fr auto; gap: var(--space-sm);
  align-items: baseline; font-size: var(--fs-xs); color: var(--ink-dim); }
.ea-runlog-time, .ea-runlog-elapsed { font-family: var(--f-data);
  font-size: var(--fs-2xs); letter-spacing: var(--tr-mid);
  font-variant-numeric: tabular-nums; color: var(--ink-muted); }
.ea-runlog-detail { grid-column: 3; font-family: var(--f-data);
  font-size: var(--fs-2xs); letter-spacing: var(--tr-mid);
  color: var(--ink-muted); }
.ea-runlog-line[data-state="running"] { color: var(--ink); }
.ea-runlog-line[data-state="failed"] { color: var(--state-failed); }
.ea-runlog-lines:empty::before { content: attr(data-empty);
  color: var(--ink-muted); font-size: var(--fs-xs); }
.ea-runlog[data-compact="true"] { padding: var(--space-sm); }

/* --- Toast --------------------------------------------------------------- */
.ea-toast { display: flex; align-items: center; gap: var(--space-sm);
  background: var(--surface-overlay); color: var(--ink);
  border-radius: var(--r-2xl); padding: var(--space-sm) var(--space-md);
  box-shadow: var(--e-3), inset 0 0 0 1px var(--seam);
  font-size: var(--fs-xs); }
.ea-toast[data-tone="success"] .ea-status-dot { background: var(--state-live); }
.ea-toast[data-tone="warn"] .ea-status-dot { background: var(--state-ready); }
.ea-toast[data-tone="error"] .ea-status-dot { background: var(--state-failed); }
.ea-toast-time { margin-inline-start: auto; }

/* --- InlineNotification -------------------------------------------------- */
/* Never a coloured left-border accent: the tone lives in the mono heading. */
.ea-note { display: flex; flex-direction: column; gap: var(--space-xs);
  background: var(--surface-raised); border-radius: var(--r-2xl);
  padding: var(--space-md); font-size: var(--fs-xs); color: var(--ink-dim); }
.ea-note-head { display: flex; align-items: center; gap: var(--space-sm); }
.ea-note[data-tone="success"] .ea-note-head { color: var(--ink-pulse); }
.ea-note[data-tone="warn"] .ea-note-head { color: var(--state-ready); }
.ea-note[data-tone="error"] .ea-note-head { color: var(--state-failed); }

/* --- StepperHorizontal --------------------------------------------------- */
.ea-stepper { display: flex; gap: var(--space-xs); list-style: none;
  margin: 0; padding: 0; }
.ea-stepper-step { flex: 1; display: flex; flex-direction: column;
  gap: var(--space-xs); font-family: var(--f-data); font-size: var(--fs-2xs);
  letter-spacing: var(--tr-data); text-transform: uppercase;
  color: var(--ink-muted); }
.ea-stepper-step::before { content: ""; block-size: var(--space-2xs);
  background: var(--seam-strong); border-radius: var(--r-full);
  transition: background-color var(--t-sm) var(--ease-in); }
.ea-stepper-step[data-state="done"]::before { background: var(--state-live); }
.ea-stepper-step[data-state="at"]::before { background: var(--state-working); }
.ea-stepper-step[data-state="at"] { color: var(--ink); }

/* --- Input / Select / Checkbox / Switch / Segmented ---------------------- */
.ea-input { display: flex; flex-direction: column; gap: var(--space-xs); }
.ea-input-label { font-family: var(--f-data); font-size: var(--fs-2xs);
  letter-spacing: var(--tr-data); text-transform: uppercase;
  color: var(--ink-muted); }
.ea-input-row { display: flex; align-items: center; gap: var(--space-sm);
  background: var(--surface-input); border-radius: var(--r-xl);
  padding-inline: var(--space-md); box-shadow: inset 0 0 0 1px var(--seam);
  transition: box-shadow var(--t-sm) var(--ease-in); }
.ea-input-row:focus-within { box-shadow: inset 0 0 0 1px var(--focus); }
.ea-field { flex: 1; min-inline-size: 0; background: none; border: 0;
  color: var(--ink); font-family: var(--f-sans); font-size: var(--fs-sm);
  min-block-size: var(--hit); outline: none; }
.ea-field::placeholder { color: var(--ink-muted); }
.ea-input-suffix { font-family: var(--f-data); font-size: var(--fs-2xs);
  letter-spacing: var(--tr-mid); color: var(--ink-muted); }
.ea-input-hint { font-size: var(--fs-2xs); color: var(--ink-muted); }
.ea-input[data-error="true"] .ea-input-row {
  box-shadow: inset 0 0 0 1px var(--state-failed); }
.ea-input-error { font-size: var(--fs-2xs); color: var(--state-failed); }
.ea-input[data-compact="true"] .ea-field { min-block-size: var(--space-lg); }

.ea-select { appearance: none; inline-size: 100%; min-block-size: var(--hit);
  background: var(--surface-input); color: var(--ink); border: 0;
  border-radius: var(--r-xl); padding-inline: var(--space-md);
  box-shadow: inset 0 0 0 1px var(--seam); font-family: var(--f-sans);
  font-size: var(--fs-sm); cursor: pointer; }
.ea-select:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.ea-check, .ea-switch { display: flex; align-items: center;
  gap: var(--space-sm); min-block-size: var(--hit); cursor: pointer;
  font-size: var(--fs-xs); }
.ea-check input { inline-size: var(--space-md); block-size: var(--space-md);
  accent-color: var(--c-signal); cursor: pointer; }
.ea-check input:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.ea-switch input { appearance: none; margin: 0; cursor: pointer;
  inline-size: var(--space-lg); block-size: var(--space-md);
  border-radius: var(--r-full); background: var(--surface-sunken);
  box-shadow: inset 0 0 0 1px var(--seam); position: relative;
  transition: background-color var(--t-sm) var(--ease-snap); }
.ea-switch input::after { content: ""; position: absolute;
  inset-block-start: 2px; inset-inline-start: 2px;
  inline-size: var(--space-sm); block-size: var(--space-sm);
  border-radius: var(--r-full); background: var(--ink);
  transition: transform var(--t-sm) var(--ease-snap); }
.ea-switch input:checked { background: var(--state-live); }
.ea-switch input:checked::after { transform: translateX(var(--space-md)); }
.ea-switch input:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.ea-switch-desc { color: var(--ink-muted); font-size: var(--fs-2xs); }

.ea-seg { display: inline-flex; gap: var(--space-2xs); padding: var(--space-2xs);
  background: var(--surface-sunken); border-radius: var(--r-xl);
  box-shadow: var(--e-well); }
.ea-seg[data-full="true"] { display: flex; inline-size: 100%; }
.ea-seg-item { flex: 1; min-block-size: var(--space-lg); border: 0;
  border-radius: var(--r-lg); background: none; color: var(--ink-dim);
  font-family: var(--f-data); font-size: var(--fs-2xs);
  letter-spacing: var(--tr-data); text-transform: uppercase; cursor: pointer;
  padding-inline: var(--space-md);
  transition: background-color var(--t-sm) var(--ease-snap),
              color var(--t-sm) var(--ease-in); }
.ea-seg-item[aria-pressed="true"], .ea-seg-item[data-on="true"] {
  background: var(--surface-overlay); color: var(--ink); }
.ea-seg-item:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* --- Card / ScreenFrame / wall ------------------------------------------- */
/* Cards are SOLID and never blurred: this is a client's rendered screen, and
   it has to be judged as it will appear on a wall. */
.ea-card {
  background: var(--surface-raised); border: 0;
  border-radius: var(--r-3xl); padding: var(--space-md);
  box-shadow: var(--e-2), var(--bevel);
  display: flex; flex-direction: column; gap: var(--space-sm);
  container-type: inline-size;
  transition: box-shadow var(--t-sm) var(--ease-in),
              transform var(--t-sm) var(--ease-snap);
}
.ea-card:hover { box-shadow: var(--e-3); transform: translateY(-2px); }
.ea-card[data-selected="true"] { box-shadow: inset 0 0 0 1px var(--c-signal), var(--e-2); }
.ea-card[data-state="live"] { box-shadow: inset 0 0 0 1px var(--c-pulse), var(--e-2); }
.ea-card-foot { margin-block-start: auto; display: flex; gap: var(--space-sm);
  flex-wrap: wrap; }
@container (max-width: 340px) { .ea-card .ea-title { font-size: var(--fs-md); } }

/* The physical screen preview. Bezels are barely rounded. */
.ea-screen { background: var(--surface-sunken); border-radius: var(--r-screen);
  overflow: hidden; display: flex; align-items: center;
  justify-content: center; box-shadow: inset 0 0 0 1px var(--seam); }
.ea-screen img, .ea-screen video { inline-size: 100%; block-size: 100%;
  object-fit: contain; opacity: 0;
  transition: opacity var(--t-sm) var(--ease-in); }
.ea-screen img.on, .ea-screen video.on { opacity: 1; }
.ea-shot { background: var(--surface-sunken); border-radius: var(--r-screen);
  overflow: hidden; aspect-ratio: 16 / 9; }
.ea-shot img, .ea-shot video { inline-size: 100%; block-size: 100%;
  object-fit: cover; opacity: 0;
  transition: opacity var(--t-sm) var(--ease-in); }
.ea-shot img.on, .ea-shot video.on { opacity: 1; }

.ea-wall { display: grid; gap: var(--space-lg);
  grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
  align-content: start; }

/* --- Breadcrumb ---------------------------------------------------------- */
/* Every page opens with one: venue → section → item. The operator should never
   have to guess which venue they are looking at. */
.ea-breadcrumb { display: flex; flex-wrap: wrap; gap: var(--space-sm);
  font-family: var(--f-data); font-size: var(--fs-2xs);
  letter-spacing: var(--tr-data); text-transform: uppercase;
  color: var(--ink-muted); margin: 0; padding: 0; list-style: none; }
/* NEVER the middle dot: machine values are separated by a forward slash. */
.ea-breadcrumb li + li::before { content: "/"; padding-inline-end: var(--space-sm); }
.ea-breadcrumb a { border: 0; color: var(--ink-muted); }
.ea-breadcrumb [aria-current="page"] { color: var(--ink-dim); }

/* --- the pixel language -------------------------------------------------- */
/* Every backdrop is THREE layers, never more: a base surface value, ONE
   pattern, and at most one wash. Each pattern says which kind of space you are
   in — grid = workspace, dots = a collection or an empty state, scan = a
   physical screen, columns = spec or print, checker = transparency. A pattern
   that is not saying that gets deleted. */
.ea-bg-grid { background-image: var(--pattern-pixel-grid);
              background-size: var(--pattern-pixel-grid-size); }
.ea-bg-dots { background-image: var(--pattern-dot-matrix);
              background-size: var(--pattern-dot-matrix-size); }
.ea-bg-scan { background-image: var(--pattern-scanline); }
.ea-bg-columns { background-image: var(--pattern-column-rule); }
.ea-bg-checker { background-image: var(--pattern-checker);
                 background-size: var(--pattern-checker-size); }
.ea-bg-vignette { background-image: var(--vignette); }
/* The stage: one wash over one pattern, and the two semantic backdrops built
   from it. The swap from .ea-bg-os to .ea-bg-working is the ambient signal
   that the machine is busy. */
.ea-bg-stage, .ea-bg-os {
  background-color: var(--surface-canvas);
  background-image: var(--stagelight-signal), var(--pattern-pixel-grid);
  background-size: auto, var(--pattern-pixel-grid-size);
}
.ea-bg-working {
  background-color: var(--surface-canvas);
  background-image: var(--stagelight-pulse), var(--pattern-dot-matrix);
  background-size: auto, var(--pattern-dot-matrix-size);
}
/* Patterns never scroll independently, never parallax and never animate. */
.ea-bg-grid, .ea-bg-dots, .ea-bg-scan, .ea-bg-columns, .ea-bg-checker,
.ea-bg-stage, .ea-bg-os, .ea-bg-working { background-attachment: scroll; }

/* --- states -------------------------------------------------------------- */
/* Hover lifts one step; press travels and shrinks; focus is always visible;
   disabled is opacity, never a recolour; selected and live are a glow, never a
   coloured border. */
[data-state-tone="working"] { color: var(--state-working); }
[data-state-tone="ready"]   { color: var(--state-ready); }
[data-state-tone="live"]    { color: var(--state-live); }
[data-state-tone="failed"]  { color: var(--state-failed); }
[data-state-tone="idle"]    { color: var(--state-idle); }
.ea-selected { box-shadow: inset 0 0 0 1px var(--border-signal), var(--depth-2); }
.ea-onair    { box-shadow: inset 0 0 0 1px var(--border-pulse), var(--depth-2); }
.ea-disabled, [aria-disabled="true"] { opacity: .4; cursor: not-allowed; }

/* --- the rail ------------------------------------------------------------ */
/* Solid, not glass. It separates from the page by VALUE and a hairline seam —
   blur costs frames on the hardware behind a venue screen, and the founder's
   design language does not use it. */
.ea-rail {
  position: fixed; inset-block: 0; inset-inline-start: 0;
  inline-size: var(--w-rail); z-index: 9000;
  display: flex; flex-direction: column; gap: var(--space-2xs);
  padding-block: var(--space-md);
  background: var(--surface-raised);
  border-inline-end: 1px solid var(--seam);
}
.ea-rail-steps { list-style: none; margin: 0; padding: 0;
                 display: flex; flex-direction: column; }
.ea-rail-tools { display: flex; flex-direction: column;
                 margin-block-start: var(--space-md);
                 padding-block-start: var(--space-sm);
                 border-block-start: 1px solid var(--seam); }
.ea-rail-link {
  display: flex; align-items: center; gap: var(--space-sm);
  min-block-size: var(--hit); color: var(--ink-dim); text-decoration: none;
  border: 0; font-family: var(--f-data); font-size: var(--fs-2xs);
  font-weight: var(--fw-medium); letter-spacing: var(--tr-data);
  text-transform: uppercase;
  padding-inline: var(--space-md);
  transition: color var(--t-sm) var(--ease-in),
              background-color var(--t-sm) var(--ease-in);
}
.ea-rail-link:hover { color: var(--ink); background: var(--action-ghost-bg); }
.ea-rail-link:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.ea-rail-link.here { color: var(--ink); background: var(--state-work-wash); }
.ea-rail-link.here .ea-step-n { color: var(--ink-signal); }
.ea-step-n { inline-size: var(--sz-step); flex: 0 0 var(--sz-step);
             text-align: center; color: var(--ink-muted);
             font-variant-numeric: tabular-nums; }
.ea-lbl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ea-rail-lang { margin-block-start: auto; display: flex; gap: var(--space-xs);
                padding-inline: var(--space-md); }
.ea-main { padding-inline-start: var(--w-rail); }
.ea-burger { display: none; }

/* PHONE: THE RAIL IS A DRAWER, AND THE DRAWER IS THE DESKTOP RAIL.
   Hidden under 760px it once left every page with no way off it; lying down
   across the top it fits until a page adds its own sections and the strip
   holds fourteen items, where the one you want is always off the edge
   (founder, 2026-08-06: "create nav bar as a hamburger menu that can be
   opened and uses the same layout as when on desktop"). So nothing INSIDE
   the rail is restyled below — the column is moved, not rebuilt. */
@media (max-width: 760px) {
  .ea-burger {
    display: flex; align-items: center; gap: var(--space-sm);
    position: fixed; inset: 0 0 auto 0; block-size: var(--w-rail-phone);
    z-index: 9001; border: 0;
    border-block-end: 1px solid var(--seam);
    background: var(--surface-raised); color: var(--ink-dim);
    font-family: var(--f-data); font-size: var(--fs-2xs);
    letter-spacing: var(--tr-data); text-transform: uppercase;
    padding-inline: var(--space-md); cursor: pointer;
  }
  .ea-burger[aria-expanded="true"] { color: var(--ink); }
  .ea-burger:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
  /* visibility, not the transform alone: a drawer only moved off the side is
     still focusable, so a phone tabs through every link before the page. */
  .ea-rail {
    inset-block-start: var(--w-rail-phone); overflow-y: auto;
    transform: translateX(-100%); visibility: hidden;
    transition: transform var(--t-sm) var(--ease-snap), visibility var(--t-sm);
  }
  .ea-rail.ea-open { transform: none; visibility: visible; }
  .ea-main { padding-inline-start: 0; padding-block-start: var(--w-rail-phone); }
}
/* Opening is a state change, which is motion that means something. Asked for
   less of it, the drawer is simply already open or shut. */
@media (prefers-reduced-motion: reduce) {
  .ea-rail, .ea-card, .ea-btn { transition: none; }
}

/* --- the setup flow ------------------------------------------------------ */
/* One question fills the view, so there is exactly one thing to do and no form
   to scan. */
.ea-setup { max-inline-size: var(--measure); margin-inline: auto;
            padding: var(--space-xl) var(--space-lg); display: flex;
            flex-direction: column; gap: var(--space-lg); }
.ea-setup-head { display: flex; align-items: center; gap: var(--space-md); }
.ea-progress { display: flex; gap: var(--space-xs); flex: 1; }
.ea-progress-step { block-size: var(--space-2xs); flex: 1;
                    background: var(--seam-strong);
                    transition: background-color var(--t-sm) var(--ease-in); }
.ea-progress-step.done { background: var(--state-live); }
.ea-progress-step.at { background: var(--state-working); }

.ea-q { display: flex; flex-direction: column; gap: var(--space-md); }
.ea-q-n { color: var(--ink-muted); }
.ea-q-why { color: var(--ink-dim); }
.ea-q-row { display: flex; align-items: center; gap: var(--space-sm);
            flex-wrap: wrap; }
.ea-q-hint { color: var(--ink-muted); }
.ea-chips { display: flex; flex-wrap: wrap; gap: var(--space-sm); }
.ea-chip-btn { text-transform: none; letter-spacing: var(--tr-body); }

.ea-recap { display: grid; grid-template-columns: auto 1fr;
            gap: var(--space-sm) var(--space-md); margin: 0; }
.ea-recap dd { margin: 0; }
.ea-recap-empty { color: var(--ink-muted); }

.ea-log { list-style: none; margin: 0; padding: var(--space-md);
          background: var(--surface-raised); border-radius: var(--r-2xl);
          max-block-size: 40vh; overflow-y: auto; overflow-wrap: anywhere;
          font-size: var(--fs-xs); line-height: var(--lh-loose);
          color: var(--ink-dim); }
.ea-log-line.warn { color: var(--state-ready); }

/* The setup page on a desktop: the question, and beside it what it is for.
   Below the split the panel stands down entirely — a phone has no room to
   spend on illustration, and it never downloads the film either. */
.ea-setup-split { display: grid; grid-template-columns: 1fr;
                  min-block-size: 100dvh; gap: var(--space-lg); }
.ea-showcase { display: none; }
@media (min-width: 1100px) {
  .ea-setup-split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
                    padding: var(--space-lg); padding-inline-start: 0; }
  .ea-setup { margin-inline: 0 auto; padding-inline: var(--space-xl);
              align-self: center; }
  .ea-showcase { display: block; position: relative; overflow: hidden;
                 border-radius: var(--r-3xl); background: var(--surface-raised);
                 box-shadow: var(--e-3); }
}
/* Fills its rounded panel, and is never the thing you look at first: the
   question is. */
.ea-showcase-film { position: absolute; inset: 0; inline-size: 100%;
                    block-size: 100%; object-fit: cover; display: block; }
/* The edge, so the film sits IN the page rather than on top of it. */
.ea-showcase::after { content: ""; position: absolute; inset: 0;
                      border-radius: inherit; pointer-events: none;
                      box-shadow: inset 0 0 0 1px var(--seam); }
@media (prefers-reduced-motion: reduce) {
  .ea-showcase-film { display: none; }
  .ea-showcase { background: var(--surface-raised); }
}

/* --- the project home ---------------------------------------------------- */
/* Seven cards, one column, in the order the work happens. One column on
   purpose: a project is read top to bottom once, and a grid of seven equal
   panels is the clutter the founder was looking at. */
/* Full width (founder, 2026-08-20: "full width for this please, too small").
   The reading measure lives on the text blocks, not on the page. */
.ea-project { display: flex; flex-direction: column; gap: var(--space-lg);
              padding: var(--space-lg) var(--space-fluid-md); }
.ea-project .ea-body, .ea-project p { max-inline-size: 78ch; }
.ea-project-top { display: flex; flex-direction: column; gap: var(--space-sm); }
/* Idle is perfectly still (rule 7): with nothing said, the log is not there
   at all rather than an empty panel taking up the top of the page. */
.ea-project .ea-log:empty { display: none; }
/* The section index: the structure, said once, before any of it. */
.ea-project-jump { display: flex; flex-wrap: wrap; gap: var(--space-md); }
.ea-project-jump a { color: var(--ink-muted); text-decoration: none; }
.ea-project-jump a:hover { color: var(--ink); }
.ea-project-jump a:focus-visible { outline: 2px solid var(--focus);
                                   outline-offset: 2px; }

.ea-project-card { display: flex; flex-direction: column; gap: var(--space-md);
                   padding: var(--space-md); background: var(--surface-raised);
                   border-radius: var(--r-3xl);
                   box-shadow: inset 0 0 0 1px var(--seam); }
/* The two questions every card answers, always in the same two places. */
.ea-project-head { display: flex; align-items: baseline; gap: var(--space-md);
                   justify-content: space-between; }
.ea-count { color: var(--ink); font-variant-numeric: tabular-nums; }
.ea-project-foot { display: flex; flex-wrap: wrap; gap: var(--space-sm);
                   align-items: center; }
.ea-project-quote { color: var(--ink-dim); }
.ea-project-list { list-style: none; margin: 0; padding: 0; display: flex;
                   flex-direction: column; gap: var(--space-xs); }
.ea-project-states { display: flex; flex-wrap: wrap; align-items: center;
                     gap: var(--space-sm); margin: 0; }
/* Hairlines between the kinds, never a card inside a card (rule 16). */
.ea-project-kind { display: flex; flex-direction: column; gap: var(--space-sm);
                   padding-block: var(--space-sm);
                   border-block-start: 1px solid var(--seam); }
.ea-project-design { display: flex; flex-direction: column;
                     gap: var(--space-xs); }

/* Thumbnails: small, square, and never the thing you look at first. */
.ea-shelf { display: flex; flex-wrap: wrap; gap: var(--space-sm);
            align-items: flex-end; }
.ea-shelf-item { margin: 0; inline-size: var(--space-2xl); display: flex;
                 flex-direction: column; gap: var(--space-2xs); }
.ea-shelf-item img { inline-size: var(--space-2xl);
                     block-size: var(--space-2xl); object-fit: cover;
                     border-radius: var(--r-screen);
                     background: var(--surface-sunken); display: block; }
.ea-shelf-item figcaption { color: var(--ink-muted); overflow: hidden;
                            text-overflow: ellipsis; white-space: nowrap; }

.ea-swatches { display: flex; flex-wrap: wrap; align-items: center;
               gap: var(--space-sm); }
.ea-swatch { inline-size: var(--space-lg); block-size: var(--space-md);
             border-radius: var(--r-xs);
             box-shadow: inset 0 0 0 1px var(--seam-strong); }

/* The control that adds a file IS a button — it just happens to be a label
   with a file input inside it, because that is the only control a browser
   will open a file picker from. */
.ea-up { display: inline-flex; align-items: center; gap: var(--space-sm);
         cursor: pointer; }
.ea-up[data-busy="true"] { cursor: wait; opacity: .6; }
.ea-up[data-disabled="true"] { cursor: default; color: var(--ink-muted); }
