/*
 * Yeja AI — Shared Design Tokens
 * Single source of truth for memory/Yeja_AI_Master_Design_Guide_20260704.md.
 * Every app should `@import` or <link> this file and consume these custom
 * properties instead of redeclaring its own color/radius/spacing constants.
 * Dark mode is switched by [data-theme="dark"] on <html>, set by
 * apps/shared/theme.js — do not hardcode a `prefers-color-scheme` query here,
 * theme.js already resolves "system" preference and stamps the attribute.
 *
 * v2026.3 (2026-07-13, Phase 1 of memory/project_design_guide_v2026_3_platform_rollout.md):
 * retokened to the "Gallery Minimal" warm-gray ramp proven out in
 * apps/experimental/tokens.css — see memory/Yeja_AI_Master_Design_Guide_20260704.md
 * §1 for full token tables and rationale. Legacy cool-gray values
 * (--yeja-n*, #F1F3F4/#DADCE0/#9AA0A6/#5F6368/#202124) are gone; every
 * --yeja-* semantic name is preserved so existing app CSS/JS keeps
 * resolving without an app-side rename, only the values moved.
 */

:root {
  /* ── Core brand accents (unchanged — semantic roles only per the guide) ── */
  --yeja-crimson: #E53935;
  --yeja-cobalt:  #1E88E5;
  --yeja-amber:   #FFB300;
  --yeja-mint:    #43A047;

  /* ── Logo ramp — NEVER themed (2026-08-05) ────────────────────────────
   * The same four values as the brand accents above, held separately so
   * the Yeja mark cannot be repainted by anything that re-points the
   * accent tokens.
   *
   * Why this is not paranoia: applyPortfolioThemeTokens() in
   * apps/profile/app.js maps an artist's chosen theme onto --yeja-cobalt /
   * --yeja-mint / --yeja-amber, scoped to .pf-page. That is deliberate and
   * correct for the PAGE's accents — but the brand mark is built from
   * exactly those three tokens and sits inside .pf-page, so it inherited
   * the artist's palette. Measured live on the yeja-ai Atelier: cobalt
   * resolved to #FFB300 and mint to #E0A030, and the mark painted with two
   * distinct colours instead of four.
   *
   * Guide §7 already treats brand accents as one of the two
   * theme-invariant exceptions (scrims are the other) — a logo that
   * changes colour per artist is not a logo. Any surface that renders the
   * mark should consume THESE, not the accent tokens. */
  --yeja-logo-crimson: #E53935;
  --yeja-logo-cobalt:  #1E88E5;
  --yeja-logo-amber:   #FFB300;
  --yeja-logo-mint:    #43A047;

  /* ── Reaction tints (2026-08-04, backlog 4b717850) ────────────────────
   * One per reaction in the five-emotion picker. These are SEPARATE
   * tokens rather than reuses of the brand accents above because two of
   * the obvious reuses FAIL contrast, measured not guessed (WCAG
   * non-text minimum 3:1, checked against white, the light canvas
   * #F5F3EF and the dark canvas #0A0A0A):
   *   --yeja-amber #FFB300 -> 1.62:1 on light. Unusable.
   *   --yeja-mint  #43A047 -> 2.98:1 on light. Just under.
   * so Care and Haha get darkened variants. This is the recorded
   * map-palette bug class — a colour chosen by NAME without checking
   * both themes paints an invisible glyph.
   *
   * Identical in dark: every value clears 3:1 on all three backgrounds,
   * so no per-theme override is needed (verified: min margin 3.25:1).
   * Colour is never the only cue — each reaction also has a distinct
   * glyph shape, and the selected state fills the icon (FILL 0->1). */
  --reaction-like: #1E88E5;  /* = cobalt.  white 3.68 canvas 3.32 dark 5.38 */
  --reaction-love: #E53935;  /* = crimson. white 4.23 canvas 3.81 dark 4.68 */
  --reaction-care: #B37A00;  /* darkened amber. white 3.68 canvas 3.32 dark 5.37 */
  --reaction-haha: #2E7D32;  /* darkened mint.  white 5.13 canvas 4.63 dark 3.86 */
  --reaction-wow:  #8E5BD0;  /* new violet.     white 4.61 canvas 4.16 dark 4.30 */

  /* ── Semantic backgrounds (light) ── */
  --yeja-error-bg:   #FDECEA;
  --yeja-success-bg: #E8F5E9;
  --yeja-info-bg:    #E3F2FD;
  --yeja-warning-bg: #FFF3DC;

  /* ── Warm gray ramp (foundation palette) — 15 steps, gray-0..gray-1000.
   * Every chrome color below derives from this ramp; never invent an
   * off-ramp gray in app CSS. ── */
  --yeja-gray-0:    #FFFFFF;
  --yeja-gray-50:   #FCFBFA;
  --yeja-gray-100:  #F2F1F0;
  --yeja-gray-200:  #DEDEDE;
  --yeja-gray-300:  #D0CDCD;
  --yeja-gray-400:  #ADADAD;
  --yeja-gray-500:  #919191;
  --yeja-gray-600:  #757575;
  --yeja-gray-700:  #5A5A5A;
  --yeja-gray-800:  #434242;
  --yeja-gray-850:  #242424;
  --yeja-gray-900:  #323131;
  --yeja-gray-950:  #212020;
  --yeja-gray-975:  #141414;
  --yeja-gray-1000: #0D0D0D;

  /* ── Theme canvas (light) — semantic names kept, values now the warm ramp ── */
  --yeja-canvas:        var(--yeja-gray-50);   /* was flat #FFFFFF */
  --yeja-canvas-paper:  #F7F5F3;               /* alt warm surface, new in v2026.3 */
  --yeja-sidebar:       var(--yeja-gray-0);    /* was #F1F3F4 — rail already reads --surface (canvas), not this; kept for back-compat of anything reading --yeja-sidebar directly */
  --yeja-active-hover:  var(--yeja-gray-0);
  --yeja-text-primary:  var(--yeja-gray-1000); /* ink — never pure black, was #202124 */
  --yeja-text-muted:    var(--yeja-gray-700);  /* ink-secondary. Fixed 2026-07-22: was --yeja-gray-500 #919191, which measures 3.05:1 against --yeja-canvas — the Master Design Guide's own §6 accessibility line claimed this passed 4.5:1 (it didn't); see the guide's §1 token-table note for the full writeup. --yeja-gray-700 clears 6.67:1. Originally --yeja-n700 #5F6368 pre-v2026.3. */
  --yeja-text-tertiary: var(--yeja-gray-600);  /* new in v2026.3 — faintest text tier */
  --yeja-border:        var(--yeja-gray-200);  /* border-subtle, was --yeja-n300 #DADCE0 */
  --yeja-border-strong: var(--yeja-gray-300);  /* new in v2026.3 — stronger structural borders */
  --yeja-backdrop:      var(--yeja-gray-950);  /* new in v2026.3 — modal/detail dim layer */

  /* ── Hover-surface trio — new in v2026.3, no prior equivalent existed ── */
  --yeja-hover-tertiary: #F8F8F7;
  --yeja-hover-primary:  #E8E6E4;
  --yeja-hover-secondary: #E8E6E4;

  /* ── Aliases used across app stylesheets (map legacy names onto tokens) ── */
  --accent:      var(--yeja-cobalt);
  --ink:         var(--yeja-text-primary);
  --muted:       var(--yeja-text-muted);
  --paper:       var(--yeja-canvas);
  --surface:     var(--yeja-canvas);
  --surface-alt: var(--yeja-sidebar);
  --line:        var(--yeja-border);

  /* ── Rail (left nav) tokens — added 2026-07-18 as Phase 0 of the
   * platform redesign plan (memory/falble-design-1-plan.md). Aliased to
   * whatever token the rail ACTUALLY renders with today, verified
   * against apps/platform/styles.css's .rail rule before wiring these
   * in — not to --yeja-sidebar, despite the name: .rail's own background
   * is var(--surface) (i.e. --yeja-canvas), --yeja-sidebar is unused by
   * the rail today. Aliasing to --yeja-sidebar here would have been a
   * real, unintended visual change (canvas white -> sidebar grey) the
   * moment .rail's background rule below started reading this token —
   * caught before wiring it in, not after. Nothing visually changes
   * until a theme actually sets these (apps/shared/platform-theme.js,
   * Phase 1). ── */
  --rail-bg:         var(--yeja-canvas);
  --rail-active-bg:  var(--yeja-active-hover);
  --rail-hover-bg:   var(--yeja-active-hover);
  --rail-text:       var(--yeja-text-primary);
  --rail-text-muted: var(--yeja-text-muted);

  /* ── Signature pill button tokens — added 2026-07-18, same Phase 0.
   * Defaulted to today's exact hardcoded values (see .yeja-pill-btn
   * below, now driven from these instead of literal hex). Text color is
   * deliberately NOT tokenized here — .yeja-pill-btn never set `color`
   * itself, it inherits from whatever context it's placed in, and that's
   * a real (if implicit) part of today's behavior, not an oversight to
   * "fix" while detokenizing bg/border. ── */
  --pill-btn-bg:     #EAEAEA;
  --pill-btn-border: #000000;

  /* ── Border radii hierarchy — v2026.3 adds two new tokens rather than
   * repurposing --radius-card's existing 12px value, which ~13 files
   * across nearly every app already consume for general card/panel
   * containers (grep-confirmed 2026-07-13). Changing that value here
   * would silently reflow every one of those surfaces platform-wide as
   * a side effect of a "tokens only" phase — exactly the kind of
   * unscoped ripple this migration is sequenced to avoid. Per the
   * guide's real three-tier system: --radius-media (3px, artwork/media
   * frames) and --radius-menu (16px, dropdowns/modals/detail views) are
   * NEW, additive tokens. --radius-card stays 12px/its current meaning
   * unchanged; each app's Phase 3 component retrofit decides, per
   * surface, whether that surface's "card" is actually a media frame
   * (→ --radius-media), a menu/modal (→ --radius-menu), or a genuine
   * generic container that keeps --radius-card as-is. ── */
  --radius-media:   8px;   /* revised 2026-07-14 (was 3px) — user asked for visibly rounded artwork/media frames; reuses the lg utility value rather than inventing a new one. Applies platform-wide. */
  --radius-menu:    16px;  /* new in v2026.3 — dropdowns, modals, detail-view container */
  --radius-card:    12px;  /* UNCHANGED — general container/panel default */
  --radius-control: 8px;
  --radius-pill:    24px;
  --radius-circle:  50%;

  /* ── 8pt grid spacing ── */
  --space-1: 8px;
  --space-2: 16px;
  --space-3: 24px;
  --space-4: 32px;
  --space-6: 48px;
  --space-8: 64px;

  /* ── Shell dimensions ── */
  --shell-header-height:    64px;
  --shell-sidebar-width:    260px;
  --shell-sidebar-collapsed: 68px;

  /* ── Typography — v2026.3 adds the Inter/JetBrains Mono stacks and a
   * mono metadata font family. --font-family-base's VALUE changes here
   * (Plus Jakarta Sans -> Inter) since the guide names this the platform
   * UI face outright, not an additive/optional token like the new radii
   * above — every app already reads --font-family-base for its base
   * text, so this is the one deliberate exception to "additive only"
   * in Phase 1. The --text-* size/line/weight scale is UNCHANGED (still
   * the pre-v2026.3 H1-H3/body/label scale) — the guide's new 15.5/14/
   * 12.5px UI scale is a Phase 3 component-level change (it also
   * changes which weight/face pairs go with which size), not a blind
   * value swap on these generic tokens. ── */
  --font-family-base: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-family-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace; /* new in v2026.3 */
  --text-h1-size: 32px; --text-h1-line: 40px; --text-h1-weight: 700;
  --text-h2-size: 24px; --text-h2-line: 32px; --text-h2-weight: 600;
  --text-h3-size: 16px; --text-h3-line: 24px; --text-h3-weight: 600;
  --text-body-size: 14px; --text-body-line: 20px; --text-body-weight: 500;
  --text-label-size: 12px; --text-label-line: 16px; --text-label-weight: 400;

  color-scheme: light;
}

:root[data-theme="dark"] {
  --yeja-error-bg:   #3B1717;
  --yeja-success-bg: #1A3A1F;
  --yeja-info-bg:    #143152;
  --yeja-warning-bg: #3D2E0F;

  --yeja-canvas:        var(--yeja-gray-1000);  /* was flat #121212 */
  --yeja-canvas-paper:  var(--yeja-gray-975);
  --yeja-sidebar:       var(--yeja-gray-950);   /* was #1E1E1F */
  --yeja-active-hover:  var(--yeja-gray-900);   /* was #2D2D30 */
  --yeja-text-primary:  var(--yeja-gray-0);
  --yeja-text-muted:    var(--yeja-gray-400);   /* was #9AA0A6 */
  --yeja-text-tertiary: var(--yeja-gray-500);
  --yeja-border:        var(--yeja-gray-850);   /* was #333333 */
  --yeja-border-strong: var(--yeja-gray-900);
  --yeja-backdrop:      #000000;

  --yeja-hover-tertiary:  #181818;
  --yeja-hover-primary:   #232323;
  --yeja-hover-secondary: #373636;

  --ink:         var(--yeja-text-primary);
  --muted:       var(--yeja-text-muted);
  --paper:       var(--yeja-canvas);
  --surface:     var(--yeja-canvas);
  --surface-alt: var(--yeja-sidebar);
  --line:        var(--yeja-border);

  /* --rail-* aren't redeclared here — they alias --yeja-sidebar/
   * --yeja-active-hover (both already redefined above for dark mode),
   * so var() resolution picks up the dark values automatically.
   * --pill-btn-* ARE literal hex, not aliases, so those do need an
   * explicit dark-mode override, same as the rule below always had.
   * NOTE (v2026.3): --pill-btn-* values are intentionally NOT changed
   * here — the Signature Outline Pill Button is deprecated per the
   * guide and gets removed in Phase 4, not restyled now. */
  --pill-btn-bg:     #2D2D30;
  --pill-btn-border: #FFFFFF;

  /* Dark-border contrast rule (guide §1): --yeja-border (gray-850
   * #242424) is only ~1.05:1 against --yeja-canvas (gray-1000 #0D0D0D)
   * in dark mode — invisible for any border that must actually be SEEN
   * (button outlines, input borders). Components needing a visible
   * dark-mode border should read --yeja-text-muted (ink-secondary,
   * gray-400), not --line/--yeja-border, for that purpose — this is a
   * consumption-pattern note for Phase 2/3, not a token this file can
   * enforce by itself. */

  color-scheme: dark;
}

/* ── High-Contrast theme (added 2026-07-20, per the design guide's
 * Light/Dark/High-Contrast preset trio — theme.js already supported a
 * "system" mode option that the earlier design-guide note missed; this
 * was the one genuinely missing preset). Pure black/white with maximum
 * separation and strong borders everywhere, targeting WCAG AAA-range
 * contrast rather than the AA-range the warm-gray ramp above targets.
 * Semantic accents (crimson/cobalt/amber/mint) are intentionally left
 * unchanged — recoloring them for "contrast" would break color-meaning
 * consistency with the other two themes; the ramp/canvas/border tokens
 * below already carry the actual contrast burden. ── */
:root[data-theme="high-contrast"] {
  --yeja-error-bg:   #000000;
  --yeja-success-bg: #000000;
  --yeja-info-bg:    #000000;
  --yeja-warning-bg: #000000;

  --yeja-canvas:        #000000;
  --yeja-canvas-paper:  #000000;
  --yeja-sidebar:       #000000;
  --yeja-active-hover:  #1a1a1a;
  --yeja-text-primary:  #FFFFFF;
  --yeja-text-muted:    #FFFFFF;
  --yeja-text-tertiary: #E0E0E0;
  --yeja-border:        #FFFFFF;
  --yeja-border-strong: #FFFFFF;
  --yeja-backdrop:      #000000;

  --yeja-hover-tertiary:  #1a1a1a;
  --yeja-hover-primary:   #2a2a2a;
  --yeja-hover-secondary: #2a2a2a;

  --ink:         var(--yeja-text-primary);
  --muted:       var(--yeja-text-muted);
  --paper:       var(--yeja-canvas);
  --surface:     var(--yeja-canvas);
  --surface-alt: var(--yeja-sidebar);
  --line:        var(--yeja-border);

  --pill-btn-bg:     #000000;
  --pill-btn-border: #FFFFFF;

  color-scheme: dark;
}

/* ── Ink pill button (Phase 4, 2026-07-13) ──
 * Base rule updated from the old "Signature outline pill button" (2px
 * black/white border, uppercase, bold, var(--pill-btn-bg) fill) to the
 * ink-pill fill every Phase 3 app's LOCAL override already applies on
 * top of this class (--yeja-text-primary/--yeja-canvas, inverts
 * correctly per theme, no border, sentence case, weight 500). Every
 * app's local override becomes visually redundant with this base rule
 * now — harmless, left in place rather than removed, since deleting 8
 * apps' worth of scoped overrides in the same pass as this base-rule
 * change would make any regression much harder to isolate. This base
 * rule is NOT purely legacy scaffolding: Wallet, the Themes app's live
 * theme-preview iframe, and apps/shared/confirm-dialog.js/mount-error.js
 * (used platform-wide for confirm/prompt/alert dialogs and mount-error
 * retry buttons) have NO local override at all and depend on this rule
 * directly for both shape AND fill — verified via full grep audit
 * before this change, see memory project_design_guide_v2026_3_platform_rollout.md
 * Phase 4 section.
 * --pill-btn-bg/--pill-btn-border are kept declared (still consumed by
 * apps/platform/styles.css's shell chrome + apps/shared/platform-theme.js's
 * live theme apply/reset cycle) but no longer read here. */
.yeja-pill-btn {
  background: var(--yeja-text-primary);
  border: none;
  border-radius: var(--radius-pill);
  font-weight: 500;
  text-transform: none;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0.5em 1.25em;
  cursor: pointer;
  color: var(--yeja-canvas);
  transition: opacity 0.15s ease-in-out;
}
.yeja-pill-btn:hover {
  background: var(--yeja-text-primary);
  opacity: 0.85;
}

/* ── Keyboard focus ring (WCAG) ── */
.yeja-focus-ring:focus-visible,
:where(button, a, input, select, textarea).yeja-focus-ring:focus-visible {
  outline: 2px solid var(--yeja-cobalt);
  outline-offset: 2px;
}

/* ── Disabled state ── */
.yeja-disabled, [disabled].yeja-disabled {
  opacity: 0.5;
  pointer-events: none;
}

/*
 * ── Theme control (light/dark/system switch) ──
 * Moved here 2026-07-07 from apps/platform/styles.css when the control's
 * home moved from the sidebar's Settings submenu into the Themes app
 * (apps/themes only loads this file, not the platform's own stylesheet).
 * Mounted anywhere via a [data-yeja-theme-control] element — see
 * apps/shared/theme.js.
 */
.yeja-theme-control {
  min-height: 42px;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  border-radius: var(--radius-control);
  border: 1px solid var(--yeja-border);
  background: var(--yeja-canvas);
  padding: 3px;
}

.yeja-theme-label,
.yeja-theme-status {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.yeja-theme-control button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--yeja-text-muted);
  cursor: pointer;
  padding: 0;
  transition: background 0.15s ease-in-out, color 0.15s ease-in-out;
}

.yeja-theme-control button:hover,
.yeja-theme-control button:focus-visible,
.yeja-theme-control button.is-active {
  border-color: var(--yeja-cobalt);
  background: var(--yeja-cobalt);
  color: #ffffff;
}

/* ── Notification badges ── */
.yeja-notif-dot {
  position: absolute;
  top: 0; right: 0;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--yeja-crimson);
}
.yeja-notif-badge {
  height: 16px;
  padding: 0 4px;
  border-radius: var(--radius-pill);
  font-size: var(--text-label-size);
  display: inline-flex;
  align-items: center;
}

/* ── Shared L2 tab strip (apps/shared/tabs.js, Phase 3) ──
 * Styling matches apps/profile/styles.css's .prof-tabs/.prof-tab
 * exactly — that pattern is what this was extracted from, so the first
 * adopter (Profile) should see zero visual change when it swaps over. */
.yeja-tabs {
  display: flex;
  gap: var(--space-1, 8px);
  overflow-x: auto;
  border-bottom: 1px solid var(--line);
  padding-bottom: 1px;
}

.yeja-tab {
  flex: 0 0 auto;
  padding: 8px 4px;
  border: none;
  background: none;
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-body-size, 14px);
  font-weight: 500;
  color: var(--muted);
  border-bottom: 2px solid transparent;
  transition: color 0.15s ease-in-out, border-color 0.15s ease-in-out;
  white-space: nowrap;
}

.yeja-tab:hover {
  color: var(--ink);
}

.yeja-tab.active {
  color: var(--yeja-cobalt, #1E88E5);
  border-bottom-color: var(--yeja-cobalt, #1E88E5);
}

.yeja-tab:focus-visible {
  outline: 2px solid var(--yeja-cobalt, #1E88E5);
  outline-offset: 2px;
}

/* ── Animated brand mark (2026-07-18, hover-triggered 2026-07-19,
 * redesigned as spin+scale-pulse 2026-07-20) ──
 * Every app's local .brand-mark/.brand-ring declares its own size/border/
 * box-shadow (kept local — see that rule's own history for why a shared
 * base class wasn't introduced), but the animation is centralized here so
 * every one of those declarations gets the same motion by adding this one
 * class, rather than duplicating a keyframe per app.
 *
 * History, all confirmed via real CDP screenshot testing (not assumed
 * from CSS alone, since three earlier versions here each looked correct
 * on paper and weren't): continuous ambient idle spin -> hover-triggered
 * full rotation -> halved to 180deg per "half the current motion"
 * feedback (made it worse — a rotation-only transform on this mark's
 * near-3-fold-symmetric blob arrangement barely changes the silhouette
 * at ANY angle, confirmed by comparing screenshots across a full 360°
 * sweep: multiples of ~120° all look close to the rest state) -> a
 * multi-blob "peel apart and orbit independently" redesign using
 * ::before/::after pseudo-elements, reverted after two rounds of tuning
 * kept finding it broken in new ways (translate() on a ~11px
 * pseudo-element barely moves it; a px-based fix still left blobs fading
 * out while still inside the ring instead of visibly escaping it) — too
 * many independently-timed moving parts to tune reliably at 20px.
 *
 * Final design: ONE transform, rotation combined with a real scale pulse
 * (grow past 1x, shrink below 1x, settle at 1x) — the size change is what
 * actually reads as motion regardless of the blob arrangement's rotational
 * symmetry, rotation adds the color-shuffle on top. A single transform
 * can't end up with parts misaligned or mistimed the way multiple
 * independent animations could. */
/* Motion halved 2026-07-27 (backlog e631f887, "logo animation on hover
 * should have half the current motion") — rotation degrees and each
 * scale delta from 1.0 both cut in half; duration/easing/reduced-motion
 * handling unchanged. */
@keyframes yeja-brand-spin {
  0%   { transform: rotate(0deg)   scale(1); }
  30%  { transform: rotate(80deg)  scale(1.125); }
  60%  { transform: rotate(170deg) scale(0.925); }
  80%  { transform: rotate(195deg) scale(1.04); }
  100% { transform: rotate(180deg) scale(1); }
}
.yeja-brand-animated:hover {
  animation: yeja-brand-spin 0.75s cubic-bezier(0.65, 0, 0.35, 1);
}
@media (prefers-reduced-motion: reduce) {
  .yeja-brand-animated:hover {
    animation: none;
  }
}

/* ── Platform-wide reduced-motion safety net (backlog cf15a276) ──
 * Every app loads this file, so this one block honours the OS-level
 * "reduce motion" setting everywhere at once.
 *
 * WHY GLOBAL RATHER THAN PER-RULE. The backlog item listed 6 unguarded
 * transform animations in apps/profile/styles.css alone. Auditing and
 * hand-guarding each one across 10 app stylesheets is the approach that
 * created the problem: a per-rule guard protects only the rules someone
 * remembered, and every animation added later starts out unguarded
 * again. This inverts the default — motion is off under the setting
 * unless a rule deliberately opts back in.
 *
 * WHY NOT `animation: none`. Killing animations outright can strand a
 * component mid-state when JS waits on animationend/transitionend. The
 * near-zero duration used here still FIRES those events, so any such
 * listener resolves immediately instead of hanging — this is the widely
 * used form of this reset for exactly that reason.
 *
 * `transform` is deliberately NOT reset. Many transforms are layout, not
 * motion (centring via translate(-50%), scaleX(0) collapse states, the
 * tooltip's positioning) — zeroing them would visually BREAK components
 * for the very users asking for less motion. Only the animation and
 * transition are neutralised; the end state still applies.
 *
 * scroll-behavior is included because a smooth-scrolling page is motion
 * too, and scrollIntoView({behavior:'smooth'}) is used in this codebase. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ── Google Material Symbols (2026-07-18) ──
 * Platform-wide icon convention: every icon is a Material Symbols
 * ligature, not a hand-drawn inline <svg>. Requires the caller's <head>
 * to link the family (see snippet below) — this file only declares the
 * usage class, since @import can't itself request the specific
 * ligatures/opsz axis a given page needs and Google's own docs recommend
 * a direct <link>, not @import, for this family.
 *
 *   <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20,400,0,0&display=block" />
 *
 * Usage: <span class="yeja-icon" aria-hidden="true">search</span> — the
 * text content is the icon's ligature name from
 * https://fonts.google.com/icons, NOT a class per icon.
 * opsz 20 + wght 400 approximates the old inline-SVG convention's 16px/
 * 1.75px-stroke look as closely as Material Symbols' own axis allows. */
.yeja-icon {
  font-family: 'Material Symbols Outlined';
  font-weight: normal;
  font-style: normal;
  font-size: 20px;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  display: inline-block;
  white-space: nowrap;
  word-wrap: normal;
  direction: ltr;
  -webkit-font-smoothing: antialiased;
  font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 20;
}
.yeja-icon.yeja-icon-fill {
  font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 20;
}
