/*
 * Yeja AI — Shared Component Library (Phase 4, 2026-07-19)
 * apps/shared/design-tokens.css defines the raw tokens; this file
 * assembles them into the reusable UI shapes named in
 * memory/fable-design-1-plan.md's Phase 4. Additive, not a forced
 * rewrite — every app's existing local CSS keeps working untouched;
 * apps swap their local class names for these opportunistically.
 *
 * migration status: apps/profile is the first (and so far only)
 * adopter — see its styles.css/app.js for the reference migration.
 */

/* ── Card ──
 * Canonical shape already agreed on, byte-for-byte, by 6 of 7 apps that
 * have a "card" concept (Profile's .prof-card, Wallet's .wal-card,
 * Fundraisers' .fr-card, Galleries' #modal-inner, Auctions' .auc-card,
 * Themes' .themes-preview-frame) before this file existed — this is a
 * pure extraction of an already-converged pattern, not a new design. */
.yeja-card {
  border: 1px solid var(--line);
  border-radius: var(--radius-card, 12px);
  padding: var(--space-3, 24px);
  background: var(--surface);
}

.yeja-card h2,
.yeja-card h3 {
  margin: 0 0 var(--space-2, 16px);
  font-size: var(--text-h3-size, 16px);
  font-weight: var(--text-h3-weight, 600);
  color: var(--ink);
}

/* ── Chip / tag-chip ──
 * .yeja-chip: a clickable filter pill (My Studio's .chip). .yeja-tag-chip:
 * a smaller, non-pill-radius-critical tag (My Studio's .tag-chip) — kept
 * distinct since a tag reads differently from a filter toggle even
 * though the base shape is close. */
.yeja-chip {
  padding: 4px 12px;
  border-radius: var(--radius-pill, 24px);
  border: 1px solid var(--line);
  background: transparent;
  color: var(--muted);
  font-size: var(--text-label-size, 12px);
  font-family: inherit;
  cursor: pointer;
  transition: border-color 0.12s ease-in-out, color 0.12s ease-in-out, background 0.12s ease-in-out;
}

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

.yeja-chip.active {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

.yeja-tag-chip {
  padding: 3px 10px;
  border-radius: var(--radius-pill, 24px);
  border: 1px solid var(--line);
  background: transparent;
  color: var(--muted);
  font-size: var(--text-label-size, 12px);
  font-family: inherit;
}

/* ── Status chip ──
 * Deliberately bucket-based, NOT word-based (2026-07-19 design
 * decision). Profile, My Studio, and Social each previously defined a
 * bare, unscoped .status-chip with a DIFFERENT radius/sizing/casing/
 * color-vocabulary — and because apps/shared/inject-stylesheet.js never
 * removes a previous app's <link> on unmount, visiting multiple apps in
 * one shell session loaded all three simultaneously, so whichever was
 * injected last silently won the cascade for every .status-chip on the
 * page, including ones belonging to a DIFFERENT currently-mounted app.
 * A literal merged status-WORD table (draft/pending/scheduled/...) was
 * considered and rejected: Profile itself reuses its own bucket names
 * as loose color shorthand in places unrelated to their literal meaning
 * (e.g. status-cancelled for a SOLD-artwork badge, status-published for
 * a won-bid badge) — a shared word namespace would have just relocated
 * the collision risk, not removed it. Buckets have no word to collide
 * on: each app's own render code maps ITS OWN status word to whichever
 * bucket is semantically correct for that app, via a small local
 * mapping function (see apps/profile/app.js's orderStatusBucket() for
 * the reference implementation). */
.yeja-status-chip {
  display: inline-block;
  flex: 0 0 auto;
  padding: 4px 12px;
  border-radius: var(--radius-pill, 24px);
  font-size: var(--text-label-size, 12px);
  font-weight: 600;
  text-transform: capitalize;
}

/* Chip text must pass AA (4.5:1) on its tint. The raw hues did not: measured
 * 2026-10-15 info 3.2, success 2.9, warning 1.6, danger 3.7 in light; info
 * 3.6, success 3.8, danger 3.8 in dark. Light darkens toward ink, dark
 * lightens toward gray-0: now ≥5.0 in light and ≥5.6 in dark. */
.yeja-status-chip.neutral { background: var(--surface-alt); color: var(--muted); }
.yeja-status-chip.info    { background: var(--yeja-info-bg); color: var(--yeja-cobalt-ink); }
.yeja-status-chip.success { background: var(--yeja-success-bg); color: color-mix(in oklch, var(--yeja-mint) 70%, var(--yeja-gray-1000)); }
.yeja-status-chip.warning { background: var(--yeja-warning-bg); color: var(--yeja-amber-ink); }
.yeja-status-chip.danger  { background: var(--yeja-error-bg); color: color-mix(in oklch, var(--yeja-crimson) 80%, var(--yeja-gray-1000)); }
:root[data-theme="dark"] .yeja-status-chip.info    { color: color-mix(in oklch, var(--yeja-cobalt) 70%, var(--yeja-gray-0)); }
:root[data-theme="dark"] .yeja-status-chip.success { color: color-mix(in oklch, var(--yeja-mint) 70%, var(--yeja-gray-0)); }
:root[data-theme="dark"] .yeja-status-chip.warning { color: var(--yeja-amber); }
:root[data-theme="dark"] .yeja-status-chip.danger  { color: color-mix(in oklch, var(--yeja-crimson) 70%, var(--yeja-gray-0)); }
/* High Contrast tints are pure black, so the light recipe left dark text on
 * black (3.5:1 measured); lighten the accent and outline the chip instead. */
:root[data-theme="high-contrast"] .yeja-status-chip { box-shadow: inset 0 0 0 1px currentColor; }
:root[data-theme="high-contrast"] .yeja-status-chip.info    { color: color-mix(in oklch, var(--yeja-cobalt) 60%, var(--yeja-gray-0)); }
:root[data-theme="high-contrast"] .yeja-status-chip.success { color: color-mix(in oklch, var(--yeja-mint) 60%, var(--yeja-gray-0)); }
:root[data-theme="high-contrast"] .yeja-status-chip.warning { color: var(--yeja-amber); }
:root[data-theme="high-contrast"] .yeja-status-chip.danger  { color: color-mix(in oklch, var(--yeja-crimson) 60%, var(--yeja-gray-0)); }

/* ── Row list ──
 * The repeated title+meta+trailing-value/chip row shape — extracted
 * verbatim from Profile's own orders/bids/RFPs rows, which were already
 * unified under one comma-selector rule internally before this file
 * existed (see apps/profile/styles.css's migration). */
.yeja-row-list-item {
  display: flex;
  align-items: center;
  gap: var(--space-2, 16px);
  padding: var(--space-2, 16px) 0;
  border-bottom: 1px solid var(--line);
}

.yeja-row-list-item:last-child {
  border-bottom: none;
}

.yeja-row-list-text {
  flex: 1;
  min-width: 0;
}

.yeja-row-list-title {
  margin: 0;
  font-size: var(--text-body-size, 14px);
  font-weight: 600;
  color: var(--ink);
}

.yeja-row-list-meta {
  margin: 2px 0 0;
  font-size: var(--text-label-size, 12px);
  color: var(--muted);
}

.yeja-row-list-value {
  flex: 0 0 auto;
  font-size: var(--text-body-size, 14px);
  font-weight: 600;
  color: var(--ink);
}

/* ── Row list group ──
 * Wraps a run of .yeja-row-list-item rows that need their own visual
 * boundary from a preceding form/section (e.g. Profile's organization
 * pending-invites list) — same border/spacing tokens as the row items
 * themselves, not a new visual language. */
.prof-org-pending-invites {
  margin-top: var(--space-2, 16px);
  border-top: 1px solid var(--line);
}

.prof-org-permissions-panel {
  padding: var(--space-1, 8px) 0 var(--space-2, 16px);
  border-bottom: 1px solid var(--line);
}

/* ── Empty state ──
 * Text-only tier (Wallet's .wal-empty, Profile's .prof-empty-state,
 * Galleries' .empty-msg) — the plainest, most common tier. A richer
 * icon+heading+text tier exists too (Fundraisers' .fr-empty, Social's
 * .social-empty) but is deliberately NOT unified in this pass: it's a
 * genuinely different, heavier shape (icon + h3 + p + optional action
 * button) that only 2 apps currently use, vs. this simpler tier which
 * ~4 apps already converged on independently. Revisit if a 3rd/4th app
 * adopts the richer tier. No text-align here — Profile's original
 * .prof-empty-state (this pattern's source) was left-aligned inline
 * text within a card, not centered; Wallet's own .wal-empty adds
 * text-align: center itself where it wants that, rather than baking a
 * center-alignment assumption into the shared shape. */
.yeja-empty-state {
  color: var(--muted);
  font-size: var(--text-body-size, 14px);
  padding: var(--space-2, 16px) 0;
}

/* ── Dialog / overlay ──
 * Backdrop-dialog shape only (dimmed backdrop + centered card) — see
 * apps/shared/overlay.js for the open/close/Escape/backdrop-click JS.
 * Deliberately does NOT cover Profile's portfolio-preview modal, which
 * is a different shape entirely (opaque full-viewport view, no
 * backdrop, closed only by an explicit button) — forcing it into this
 * shape would add behavior (Escape-to-close) it was never designed
 * with, a real product decision outside this phase's scope. */
.yeja-overlay-backdrop {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: color-mix(in oklch, var(--yeja-backdrop) 55%, transparent);
  display: flex;
  align-items: center;
  justify-content: center;
}

.yeja-dialog-card {
  background: var(--paper);
  color: var(--ink);
  border-radius: var(--radius-menu, 16px);
  padding: var(--space-4, 32px);
  max-width: 480px;
  width: 90vw;
  /* 90vw + padding overflowed the viewport on phones (seen 2026-10-15 at 390px). */
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: var(--space-3, 24px);
  box-shadow: var(--yeja-elevation-3);
  animation: yeja-dialog-enter 0.18s ease-out;
}

@keyframes yeja-dialog-enter {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

/* ── Mono data (2026-10-20) ──
 * Amounts, dates, codes, addresses and card digits (guide §2, §8 Wallet).
 * The design system ships this utility; the repo did not. */
.yeja-mono {
  font-family: var(--font-family-mono);
  font-variant-numeric: tabular-nums;
}

/* ── Sheet (2026-10-20, apps/shared/sheet.js) ──
 * A dialog card that becomes a bottom sheet at phone width: anchored to
 * the bottom edge, top corners only, a drag handle, scrolls inside itself.
 * Same scrim and the same yeja-dialog-enter motion as every dialog. */
.yeja-sheet {
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  gap: 0;
}
.yeja-sheet__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2, 16px);
}
.yeja-sheet__handle { display: none; }
@media (max-width: 640px) {
  .yeja-sheet-backdrop { align-items: flex-end; }
  .yeja-sheet {
    width: 100%;
    max-width: none;
    max-height: calc(100vh - 32px);
    border-radius: var(--radius-menu, 16px) var(--radius-menu, 16px) 0 0;
    padding: 12px 20px calc(24px + env(safe-area-inset-bottom, 0px));
    touch-action: pan-y;
  }
  .yeja-sheet__handle {
    display: block;
    flex: 0 0 auto;
    width: 36px;
    height: 4px;
    margin: 0 auto var(--space-2, 16px);
    border-radius: 2px;
    background: var(--line);
  }
}

/* ── Box grid (2026-10-20) ──
 * The Account Hub's box grid (apps/profile .prof-hub-*) as a shared
 * component, first used by Wallet Settings (wireframe 2r: "same box grid as
 * Account Hub"): icon disc, title, one-line description, and a count or
 * status at the top-right. 3 columns from 1024px, 2 on tablets, 1 on
 * phones. Hover changes the border and background only (no lift: guide §4
 * allows no motion here). */
.yeja-box-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-2, 16px);
}
@media (max-width: 1023px) { .yeja-box-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) { .yeja-box-grid { grid-template-columns: minmax(0, 1fr); } }
.yeja-box {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2, 16px);
  width: 100%;
  box-sizing: border-box;
  padding: 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius-card, 12px);
  background: var(--paper);
  color: inherit;
  font: inherit;
  text-align: start;
  text-decoration: none;
  cursor: pointer;
}
.yeja-box:hover { background: var(--surface-alt); border-color: var(--yeja-border-strong); }
.yeja-box__icon {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill, 999px);
  background: var(--surface-alt);
  color: var(--yeja-cobalt);
}
.yeja-box__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.yeja-box__title { font-size: 15.5px; font-weight: 700; line-height: 1.3; color: var(--ink); }
.yeja-box__desc { font-size: 14px; line-height: 1.45; color: var(--muted); }
.yeja-box__trail { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; color: var(--muted); }

/* ── Dialog text input ──
 * For apps/shared/confirm-dialog.js's promptDialog() — a themed
 * replacement for native prompt(). Deliberately its own class, not a
 * reuse of .yeja-pill-btn (a button shape, not an editable field). */
.yeja-dialog-actions {
  display: flex;
  gap: 12px;
  justify-content: flex-end;
  flex-wrap: wrap;
}

/* Terms acceptance (apps/shared/terms-gate.js, 2026-10-15) */
.yeja-terms-gate h2 { margin: 0; font-size: 20px; }
.yeja-terms-gate p { margin: 0; line-height: 1.5; }
.yeja-terms-gate a { color: var(--yeja-cobalt-ink); }
/* cobalt-ink is 3.4:1 on the dark card; plain cobalt passes there. */
:root[data-theme="dark"] .yeja-terms-gate a { color: var(--yeja-cobalt); }
.yeja-terms-gate__agree {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 14px;
}

.yeja-dialog-input {
  border: 1px solid var(--line);
  border-radius: var(--radius-control, 8px);
  padding: 0.5em 0.75em;
  font: inherit;
  color: var(--ink);
  background: var(--surface);
}

/* ── Danger pill button ──
 * Same base shape as .yeja-pill-btn, recolored for a destructive
 * confirm action (e.g. "Delete", "Withdraw") so its stakes read at a
 * glance next to a neutral Cancel button. */
.yeja-pill-btn-danger {
  background: var(--yeja-error-bg);
  border-color: var(--yeja-crimson);
  color: var(--yeja-crimson);
}

/* ── Ghost pill button ──
 * Same base shape as .yeja-pill-btn, transparent fill with a border only
 * — for a secondary action sitting next to a primary one (e.g. "Cancel"
 * next to "Send"), added for Wallet's Phase 3 send-USDT modal
 * (2026-07-13) but kept here rather than scoped to apps/wallet since the
 * primary/secondary pairing is a platform-wide pattern, not Wallet-specific. */
.yeja-pill-btn-ghost {
  background: transparent;
  /* Design guide §5 Buttons: Ghost border must read --muted (--yeja-
   * text-muted, "ink-secondary"), not --line/--yeja-border — the guide's
   * own dark-border contrast rule (design-tokens.css's dark block,
   * "Components needing a visible dark-mode border should read
   * --yeja-text-muted... not --line/--yeja-border") flags --line as
   * ~1.05:1 against --yeja-canvas in dark mode, i.e. functionally
   * invisible. Fixed 2026-07-18 — every Ghost button platform-wide
   * shares this one base rule. */
  border: 1px solid var(--muted);
  color: var(--ink);
}
.yeja-pill-btn-ghost:disabled {
  opacity: 0.5;
  cursor: default;
}

/* ── Yeja Toolbar system (2026-07-15) ──
 * Extracted from apps/my_studio/styles.css, the platform's first real
 * implementation of this bar (Folders dropdown / category pills /
 * Settings dropdown / collapse toggle). User instruction: "no single
 * app's bar can be different the my_studio bar" — every app's own
 * content-level toolbar should be built from these classes so a future
 * design change updates every app at once, instead of each app carrying
 * its own copy that silently drifts (exactly what caused the star/cart/
 * notif floater bug: My Studio's own bare, unscoped .dropdown__panel
 * rule leaked into and broke the universal shell's top bar once My
 * Studio had ever been mounted in a session — apps/shared/
 * inject-stylesheet.js never removes a <link> on unmount, so a stale
 * app stylesheet stays loaded for the rest of the session). Every class
 * here is .yeja--prefixed specifically so it can never again collide
 * with an app-local bare class name the way .dropdown/.dropdown__panel/
 * .action-item did.
 *
 * Anatomy: .yeja-toolbar (persistent outer bar, never collapses, holds
 * the toggle) > .yeja-toolbar__content (the collapsible part — real
 * toolbar content goes here) + .yeja-toolbar__toggle (chevron button,
 * floats over the canvas via position:absolute only while collapsed).
 * This exact split is the toolbar's 4th and final design after 3 wrong
 * attempts in apps/my_studio/app.js's own history (button inside the
 * collapsing element = no way back once collapsed; a whole separate
 * bar below the toolbar = visually disconnected; content-only collapse
 * with the bar still sized to the toggle button = never actually
 * disappeared) — see memory/project-my-studio-redesign-plan-20260714.md
 * for the full incident history before changing this shape again. */
.yeja-toolbar {
  /* Universal Shell v2 (2026-10-21, design 1n): every app draws its own
   * sub-panel, and every one of them looks and collapses the same way:
   * one 52px row (48 on phones), edge to edge, sticky under the top bar.
   * The view rail scrolls sideways when it overflows (48px fade at the
   * trailing edge); the other controls keep their size, so dropdowns in
   * the bar are never clipped. Collapsed, the bar is gone entirely and only
   * the chevron stays, floating at the same spot over the canvas (owner,
   * 2026-10-21: the 32px labelled row "doesn't collapse all the way"). On
   * phones it slides away while the canvas scrolls down (yeja-shell-scroll). */
  display: flex;
  align-items: center;
  gap: 8px;
  box-sizing: border-box;
  /* A fixed height, not a minimum: a tall control used to stretch one
   * app's row to 61px against everyone else's 54. */
  block-size: var(--yeja-subpanel-h, 52px);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  flex-shrink: 0;
  position: sticky;
  top: 0;
  z-index: 5;
  padding-inline: 24px;
  transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.yeja-toolbar.is-scroll-hidden { transform: translateY(-100%); }
.yeja-toolbar.is-collapsed {
  block-size: 0;
  border-bottom: 0;
}
.yeja-toolbar__content {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 16px;
  flex: 1;
  min-width: 0;
  block-size: 100%;
  padding-block: 0;
}
.yeja-toolbar__content > * { flex-shrink: 0; }
.yeja-toolbar.is-collapsed .yeja-toolbar__content { display: none; }
.yeja-toolbar__toggle {
  flex-shrink: 0;
  margin-inline-start: auto;
}
.yeja-toolbar.is-collapsed .yeja-toolbar__toggle {
  position: absolute;
  inset-block-start: 8px;
  inset-inline-end: 24px;
  width: 32px;
  height: 32px;
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--yeja-elevation-1);
}
@media (max-width: 640px) {
  .yeja-toolbar.is-collapsed .yeja-toolbar__toggle { inset-inline-end: 16px; }
}
/* The view rail: one row that scrolls sideways, snapping to each pill, with
 * a fade on the trailing edge only while it actually overflows. */
.yeja-toolbar__content > .yeja-category-pills,
.yeja-toolbar [data-toolbar-rail] {
  flex: 0 1 auto;
  flex-wrap: nowrap;
  min-width: 0;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
}
.yeja-toolbar__content > .yeja-category-pills::-webkit-scrollbar,
.yeja-toolbar [data-toolbar-rail]::-webkit-scrollbar { display: none; }
.yeja-toolbar__content > .yeja-category-pills > *,
.yeja-toolbar [data-toolbar-rail] > * { scroll-snap-align: start; flex-shrink: 0; }
.yeja-toolbar .is-rail-overflowing {
  -webkit-mask-image: linear-gradient(to var(--yeja-rail-fade-to, right), #000 calc(100% - 48px), transparent);
  mask-image: linear-gradient(to var(--yeja-rail-fade-to, right), #000 calc(100% - 48px), transparent);
}
:root[dir="rtl"] .yeja-toolbar { --yeja-rail-fade-to: left; }
@media (max-width: 640px) {
  .yeja-toolbar { padding-inline: 16px; }
}
@media (prefers-reduced-motion: reduce) {
  .yeja-toolbar { transition: none; }
}
/* "More" (toolbar-collapse.js): while the row cannot fit every control,
 * the controls beside the view rail live in a sheet behind this button. */
.yeja-toolbar.is-overflowing .yeja-toolbar__content > :not(.yeja-category-pills):not([data-toolbar-rail]) { display: none; }
.yeja-toolbar__more { flex-shrink: 0; }
.yeja-toolbar__more[hidden],
.yeja-toolbar.is-collapsed .yeja-toolbar__more { display: none; }
.yeja-toolbar-more__head { display: flex; align-items: center; }
.yeja-toolbar-more__title { margin: 0; font-size: var(--text-body-size, 14px); font-weight: 700; }
.yeja-toolbar-more__body {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
}
.yeja-toolbar-more__body > * { margin-inline-start: 0; }
.yeja-toolbar-more__body .yeja-toolbar__right { flex-wrap: wrap; justify-content: flex-start; }
/* Layout menu (apps/shared/layout-menu.js): Grid Size and Image Fit, at the
 * sub-panel's far end, just before the chevron. Never moves into "More". */
.yeja-toolbar > .yeja-layout-menu { flex-shrink: 0; }
.yeja-toolbar.is-collapsed > .yeja-layout-menu { display: none; }
.yeja-toolbar > .yeja-layout-menu + .yeja-toolbar__toggle { margin-inline-start: 0; }
.yeja-layout-menu .yeja-layout-menu__panel {
  inset-inline-start: auto;
  inset-inline-end: 0;
  left: auto;
  min-width: 0;
  padding: 12px 16px;
}
.yeja-layout-menu__panel.is-open { display: flex; flex-direction: column; gap: 12px; }
.yeja-layout-menu__group { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.yeja-layout-menu__label { font-size: var(--text-label-size, 12px); color: var(--muted); }
.yeja-layout-menu .segmented__option--text { width: auto; gap: 6px; padding-inline: 12px 14px; font: inherit; font-size: var(--text-label-size, 12px); }
.yeja-layout-menu .segmented__option--text .yeja-icon { font-size: 18px; }
/* Fit Height rows (apps/shared/justified-grid.js): each card's width and
 * --justified-h are set by the engine; the media fills the frame exactly. */
.justified {
  flex: 1 0 100%;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--justified-gap, 8px);
}
.justified__row { display: flex; gap: var(--justified-gap, 8px); }
.justified__row > * { flex: 0 1 auto; min-width: 0; }
.justified .justified__row .masonry__frame { block-size: var(--justified-h); }
.justified .justified__row .masonry__frame > img,
.justified .justified__row .masonry__frame > video,
.justified .justified__row .masonry__frame > .card-placeholder {
  inline-size: 100%;
  block-size: 100%;
  aspect-ratio: auto;
  object-fit: cover;
}
/* Forced colours: shadows are dropped, so raised surfaces get a border. */
@media (forced-colors: active) {
  .yeja-dropdown__panel,
  .yeja-dialog-card,
  .toast,
  .yeja-tooltip { border: 1px solid CanvasText; }
}
.yeja-toolbar__right {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-inline-start: auto;
}

/* ── Icon button — 36px circle, transparent until hover. Used for the
 * toolbar's collapse toggle and any other icon-only action (Upload,
 * Settings trigger, etc). ── */
.yeja-btn-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-pill, 999px);
  border: none;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: background 0.15s cubic-bezier(0.4, 0, 0.2, 1), transform 0.15s cubic-bezier(0.4, 0, 0.2, 1);
}
.yeja-btn-icon:hover { background: var(--surface-alt); }
.yeja-btn-icon.is-flipped { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
  .yeja-btn-icon.is-flipped { transition: none; }
}

/* ── Outline button — a dropdown trigger that's navigational chrome, not
 * a primary action (e.g. a "Folders"-style trigger). ── */
.yeja-btn-outline {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill, 999px);
  padding: 8px 16px;
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
  cursor: pointer;
  transition: background 0.15s cubic-bezier(0.4, 0, 0.2, 1);
}
.yeja-btn-outline:hover { background: var(--surface-alt); }

/* ── Category / filter pills — inline row of toggle pills inside a
 * toolbar (e.g. Featured/Draft/Ready/Minted/Sold/Starred). Real pill
 * treatment: ink-fill active, muted text inactive, no background at
 * rest. ── */
.yeja-category-pills { display: flex; align-items: center; gap: 8px; overflow-x: auto; scrollbar-width: none; }
.yeja-category-pills::-webkit-scrollbar { display: none; }
.yeja-category-pill {
  flex-shrink: 0;
  padding: 8px 16px;
  border-radius: var(--radius-pill, 999px);
  border: none;
  background: transparent;
  color: var(--muted);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s cubic-bezier(0.4, 0, 0.2, 1), color 0.15s cubic-bezier(0.4, 0, 0.2, 1);
}
.yeja-category-pill:hover:not(.active) { background: var(--surface-alt); color: var(--ink); }
.yeja-category-pill.active { background: var(--ink); color: var(--paper); }
/* A .yeja-category-pill used as a dropdown menu item (e.g. My Studio's
 * Manage NFTs dropdown, 2026-07-15) stacks full-width instead of sitting
 * in a horizontal scrolling row — same click/active-state contract as the
 * flat pill row, different container shape. */
.yeja-dropdown__panel .yeja-category-pill {
  display: block;
  width: 100%;
  text-align: start;
}

/* ── Dropdown panel — trigger + absolutely-positioned panel, 16px
 * radius, Panel shadow, 8px outer padding. Matches the universal shell's
 * own top-bar Favorites/Cart/Create dropdowns exactly (same classes —
 * this IS that same system, not a lookalike), so a dropdown built with
 * these classes anywhere in the platform behaves identically. Add
 * .yeja-toolbar__right as an ancestor to right-anchor the panel (grows
 * leftward) instead of the default left-anchor (grows rightward) —
 * same convention as the shell's own top-bar icons. ── */
.yeja-dropdown { position: relative; }
.yeja-dropdown__panel {
  position: absolute;
  top: calc(100% + 8px);
  inset-inline-start: 0;
  min-width: 280px;
  background: var(--surface);
  border-radius: var(--radius-menu, 16px);
  box-shadow: var(--yeja-elevation-2);
  padding: 8px;
  display: none;
  z-index: 200;
}
.yeja-toolbar__right .yeja-dropdown__panel { left: auto; right: 0; }
.yeja-dropdown__panel.is-open { display: block; }
.yeja-dropdown__divider { height: 1px; background: var(--line); margin: 6px 4px; }

.yeja-action-item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
  gap: 2px;
  padding: 10px 12px;
  border: 0;
  background: transparent;
  border-radius: 12px;
  text-align: start;
  cursor: pointer;
  transition: background 0.15s cubic-bezier(0.4, 0, 0.2, 1);
}
.yeja-action-item:hover { background: var(--surface-alt); }
.yeja-action-item__label { font-size: 14px; font-weight: 700; color: var(--ink); }
.yeja-action-item__desc { font-size: 12px; color: var(--muted); }

/* ── Tooltip system (2026-07-15) — apps/shared/tooltip.js's initTooltips()
 * repositions and shows/hides this ONE shared node for whichever
 * [data-tooltip] element is currently hovered/focused, platform-wide.
 * z-index above every other layered surface (dropdowns at 200, the
 * upload progress overlay at 9999) since a tooltip must always be able
 * to appear over anything else that's open. Dark, high-contrast bubble
 * on purpose — unlike .yeja-dropdown__panel (which follows the
 * light/dark theme), a tooltip needs to read clearly regardless of
 * what's underneath it, same convention most platforms use. */
.yeja-tooltip {
  position: fixed;
  top: 0;
  inset-inline-start: 0;
  z-index: 10000;
  max-width: 240px;
  padding: 6px 10px;
  border-radius: var(--radius-control, 6px);
  background: var(--ink);
  color: var(--paper);
  box-shadow: var(--yeja-elevation-1);
  font-size: 12px;
  line-height: 1.4;
  font-weight: 500;
  pointer-events: none;
  opacity: 0;
  transform: translateY(2px);
  transition: opacity 0.12s ease-in-out, transform 0.12s ease-in-out;
}
.yeja-tooltip.is-visible {
  opacity: 1;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .yeja-tooltip { transition: opacity 0.12s ease-in-out; transform: none; }
}

/* ── Shared "Choose From My Studio" asset picker (extracted 2026-07-16
 * from apps/auctions/app.js — Fundraisers needed the same real picker to
 * fix a bug where its plain-text "Cover Image URL" field let a
 * time-limited signed Storage URL get pasted in instead of a permanent
 * public_url). Used by apps/shared/studio-asset-picker.js's
 * openStudioAssetPicker(). ── */
.yeja-studio-picker-card { max-width: 720px; }
.yeja-studio-picker-title { font-size: var(--text-h2-size); line-height: var(--text-h2-line); font-weight: 700; margin-bottom: var(--space-1); }
.yeja-studio-picker-loading { color: var(--yeja-text-muted); font-size: var(--text-body-size); }
.yeja-studio-picker-empty { color: var(--yeja-text-muted); text-align: center; padding: var(--space-4) 0; }

.yeja-studio-picker-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--space-2);
  max-height: 50vh;
  overflow-y: auto;
  margin-bottom: var(--space-2);
}

.yeja-studio-picker-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1);
  border: 1px solid var(--yeja-border);
  border-radius: var(--radius-control);
  background: var(--yeja-canvas);
  cursor: pointer;
  text-align: start;
  transition: background 0.15s ease-in-out, transform 0.15s ease-in-out;
}
.yeja-studio-picker-item:hover { background: var(--yeja-active-hover); transform: translateY(-1px); }

.yeja-studio-picker-thumb {
  width: 48px;
  height: 48px;
  flex: 0 0 auto;
  border-radius: var(--radius-control);
  overflow: hidden;
  background: var(--yeja-sidebar);
  display: grid;
  place-items: center;
}
.yeja-studio-picker-thumb img { width: 100%; height: 100%; object-fit: cover; }
.yeja-studio-picker-thumb-placeholder { color: var(--yeja-text-muted); font-weight: 700; }

.yeja-studio-picker-item-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.yeja-studio-picker-item-text strong {
  font-size: var(--text-body-size);
  color: var(--yeja-text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.yeja-studio-picker-item-text span {
  font-size: var(--text-label-size);
  color: var(--yeja-text-muted);
}
.yeja-studio-picker-no-image { color: var(--yeja-crimson) !important; }

.yeja-studio-picker-section-label {
  font-size: var(--text-label-size);
  font-weight: 700;
  color: var(--yeja-text-muted);
  margin: var(--space-2) 0 var(--space-1);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.yeja-studio-picker-section-label:first-of-type { margin-top: 0; }

.yeja-studio-picker-back { align-self: center; }

/* ── Cover-image preview chip (thumbnail + clear button) shown once an
 * image has been picked from the studio-asset picker — same pattern
 * apps/auctions/styles.css's own .auc-cover-picker/.auc-cover-preview
 * originated 2026-07-15, generalized here so Fundraisers' cover-image
 * field can reuse it too. ── */
.yeja-cover-picker { display: flex; flex-direction: column; gap: var(--space-1); align-items: flex-start; }
.yeja-cover-preview {
  position: relative;
  width: 160px;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-control);
  overflow: hidden;
  background: var(--yeja-sidebar);
}
.yeja-cover-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
.yeja-cover-clear {
  position: absolute; top: 4px; right: 4px;
  width: 22px; height: 22px;
  border: none; border-radius: 50%;
  background: rgba(20, 20, 20, 0.6); color: #fff;
  font-size: 14px; line-height: 1; cursor: pointer;
  display: grid; place-items: center;
}

/* ── Toast notifications (2026-07-22) ──
 * Shared apps/shared/toast.js's real component — lifted verbatim from
 * apps/admin/admin.css's own block (the only one of 8 duplicated toast
 * implementations that already used real design tokens rather than
 * hardcoded hex). Every app migrating onto the shared toast.js module
 * gets this styling for free; no per-app toast CSS needed anymore. */
.toast-container {
  position: fixed;
  bottom: var(--space-3);
  inset-inline-end: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  z-index: 9999;
}
.toast {
  padding: 0.6rem 1rem;
  border-radius: var(--radius-control);
  font-size: var(--text-body-size);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.2s, transform 0.2s;
  pointer-events: none;
  max-width: 320px;
  box-shadow: var(--yeja-elevation-2);
}
.toast.show { opacity: 1; transform: translateY(0); }
/* Ink follows .yeja-status-chip's measured recipe: raw mint on the success
 * tint is 2.9:1 and raw crimson on the error tint 3.7:1, both under AA. */
.toast-success { background: var(--yeja-success-bg); color: color-mix(in oklch, var(--yeja-mint) 70%, var(--yeja-gray-1000)); border: 1px solid var(--yeja-mint); }
.toast-error   { background: var(--yeja-error-bg); color: color-mix(in oklch, var(--yeja-crimson) 80%, var(--yeja-gray-1000)); border: 1px solid var(--yeja-crimson); }
:root[data-theme="dark"] .toast-success { color: color-mix(in oklch, var(--yeja-mint) 70%, var(--yeja-gray-0)); }
:root[data-theme="dark"] .toast-error   { color: color-mix(in oklch, var(--yeja-crimson) 70%, var(--yeja-gray-0)); }
:root[data-theme="high-contrast"] .toast-success,
:root[data-theme="high-contrast"] .toast-error { color: var(--yeja-text-primary); }
.toast-info    { background: var(--yeja-canvas); color: var(--yeja-text-primary); border: 1px solid var(--yeja-border); }
@media (prefers-reduced-motion: reduce) {
  .toast { transition: none; }
}
/* Toast v2 (2026-10-22): a toast with an action or a close button must take
 * clicks, so only those opt back into pointer events. The action is a ghost
 * pill in the toast's own ink, so it inherits the measured text colour. */
.toast.toast--interactive {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: var(--space-1);
  max-width: 420px;
}
.toast__text { flex: 1; min-width: 0; }
.toast__action {
  flex: none;
  height: 28px;
  padding: 0 12px;
  border-radius: var(--radius-pill);
  border: 1px solid currentColor;
  background: transparent;
  color: inherit;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.toast__action:hover { background: color-mix(in oklch, currentColor 10%, transparent); }
.toast__close {
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 0;
  background: transparent;
  color: inherit;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.toast__close .yeja-icon { font-size: 18px; }
.toast__close:hover { background: color-mix(in oklch, currentColor 10%, transparent); }
.toast__action:focus-visible,
.toast__close:focus-visible { outline: 2px solid var(--yeja-focus); outline-offset: 2px; }

/* ── Action Notice (2026-10-22, My Studio 2.0) ──
 * apps/shared/action-notice.js. A blocked step with the button that fixes
 * it, in place: in the canvas under an app's sub-panel or at the top of a
 * detail rail, never above the sub-panel (guide §0s.3). Tinted by meaning
 * with the same state tints and darkened ink as .yeja-status-chip. */
.yeja-action-notice {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 12px 12px 16px;
  border-radius: var(--radius-card);
  border: 1px solid var(--yeja-border-strong);
  background: var(--yeja-canvas);
  color: var(--yeja-text-primary);
}
.yeja-action-notice__icon { flex: none; font-size: 20px; }
.yeja-action-notice__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.yeja-action-notice__title { font-weight: 600; font-size: var(--text-body-size); line-height: 1.3; }
.yeja-action-notice__text { font-size: 13.5px; line-height: 1.4; }
.yeja-action-notice__cta { flex: none; }
.yeja-action-notice__dismiss {
  flex: none;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.yeja-action-notice__dismiss:hover { background: color-mix(in oklch, currentColor 10%, transparent); }
.yeja-action-notice__dismiss:focus-visible { outline: 2px solid var(--yeja-focus); outline-offset: 2px; }
.yeja-action-notice--info    { background: var(--yeja-info-bg);    border-color: var(--yeja-cobalt);  color: var(--yeja-cobalt-ink); }
.yeja-action-notice--warning { background: var(--yeja-warning-bg); border-color: var(--yeja-amber);   color: var(--yeja-amber-ink); }
.yeja-action-notice--error   { background: var(--yeja-error-bg);   border-color: var(--yeja-crimson); color: color-mix(in oklch, var(--yeja-crimson) 80%, var(--yeja-gray-1000)); }
.yeja-action-notice--success { background: var(--yeja-success-bg); border-color: var(--yeja-mint);    color: color-mix(in oklch, var(--yeja-mint) 70%, var(--yeja-gray-1000)); }
:root[data-theme="dark"] .yeja-action-notice--info    { color: color-mix(in oklch, var(--yeja-cobalt) 70%, var(--yeja-gray-0)); }
:root[data-theme="dark"] .yeja-action-notice--error   { color: color-mix(in oklch, var(--yeja-crimson) 70%, var(--yeja-gray-0)); }
:root[data-theme="dark"] .yeja-action-notice--success { color: color-mix(in oklch, var(--yeja-mint) 70%, var(--yeja-gray-0)); }
:root[data-theme="high-contrast"] .yeja-action-notice { color: var(--yeja-text-primary); }
.yeja-action-notice[hidden] { display: none; }
@media (max-width: 640px) {
  .yeja-action-notice { flex-wrap: wrap; }
  .yeja-action-notice__cta { order: 3; flex-basis: 100%; }
  .yeja-action-notice__cta .ink-pill { width: 100%; }
}
@media (forced-colors: active) {
  .yeja-action-notice { border: 1px solid CanvasText; }
}

/* ── Ink Pill / Ghost buttons (Master Design Guide §5) ──
 * Promoted here 2026-07-25 (Plan v2 Phase 0, tracker a441bf7b) — previously
 * three independent local copies (apps/my_studio/styles.css, apps/crm/
 * styles.css, apps/art_board/styles.css) that had already drifted: Art
 * Board's copy used font-weight 600 (spec says 500), 0.9 hover opacity
 * (spec says 0.85), and a different border token, plus never had the
 * --compact modifier. My Studio's/CRM's copy matched the Guide's §5 spec
 * exactly, so it's the source of truth here, with Art Board's design-token
 * padding (--space-1/--space-3) kept since it resolves to the same 6px/24px
 * values as the hardcoded numbers. Every app already injects this file
 * before its own local styles.css (apps/shared/inject-stylesheet.js), so
 * removing each app's local copy lets this definition take over with no
 * load-order change needed. */
.ink-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: var(--yeja-btn-primary-bg);
  color: var(--yeja-btn-primary-fg);
  border: none;
  border-radius: var(--radius-pill, 999px);
  padding: 10px 24px;
  font-size: 15.5px;
  font-weight: 500;
  cursor: pointer;
  transition: opacity 0.15s cubic-bezier(0.4, 0, 0.2, 1);
}
.ink-pill:hover { opacity: 0.85; }
.ink-pill:disabled { opacity: 0.5; cursor: default; }
.ink-pill--compact { padding: 8px 16px; font-size: 14px; }
.ghost-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--muted);
  border-radius: var(--radius-pill, 999px);
  padding: 10px 24px;
  font-size: 15.5px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s cubic-bezier(0.4, 0, 0.2, 1);
}
.ghost-pill:hover { background: var(--surface-alt); }
.ghost-pill:disabled { opacity: 0.5; cursor: default; pointer-events: none; }
.ghost-pill--compact { padding: 8px 16px; font-size: 14px; }
.ghost-pill--danger { color: var(--yeja-crimson, #E53935); border-color: var(--yeja-crimson, #E53935); }
/* Danger fill (guide §5 Buttons: crimson pill, white text) for an
 * irreversible delete that is the primary of its row or dialog. */
.ink-pill--danger { background: var(--yeja-crimson); color: var(--yeja-gray-0); border-color: var(--yeja-crimson); }
.ink-pill--danger[hidden] { display: none; }
.ghost-pill--danger:hover { background: color-mix(in oklch, var(--yeja-crimson, #E53935) 12%, transparent); }
.ghost-pill--full { width: 100%; height: 40px; padding-top: 0; padding-bottom: 0; }

/* Provenance card (2026-07-26, Plan v2 Phase 1, tracker 75416daa) — one
 * shared definition backing apps/shared/provenance-card.js, wired into
 * Art Board, Auctions, Profile/Portfolio, and My Studio's detail modals.
 * Built here from day one per the backlog item's own instruction, rather
 * than letting it become a 4th/5th/6th per-app copy the way .ink-pill did
 * before its own Phase 0 fix above. */
.provenance-card {
  border: 1px solid var(--muted);
  border-radius: var(--radius-card, 12px);
  padding: 16px;
  margin-top: 16px;
}
.provenance-card__heading {
  font-size: 13px;
  font-weight: 700;
  color: var(--muted);
  margin: 0 0 12px;
}
.provenance-card__row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 0;
  border-bottom: 1px solid var(--surface-alt);
  font-size: 13.5px;
}
.provenance-card__row:last-of-type { border-bottom: none; }
.provenance-card__label { color: var(--muted); }
.provenance-card__value { color: var(--ink); text-align: right; }
.provenance-card__empty { font-size: 13px; color: var(--muted); margin: 0; }
.provenance-card__cert-btn { margin-top: 12px; width: 100%; }

/* Lineage timeline (2026-10-02): the artwork's hash-chained history, newest
 * first, two entries inline and the rest behind "Show Full History". The
 * trust chip reuses .yeja-status-chip buckets; no icons, because this card
 * renders on hosts that may not load the icon font. */
.provenance-card__mono {
  font-family: var(--font-family-mono);
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
.provenance-card__lineage {
  margin-top: 16px;
  padding-top: 12px;
  border-top: 1px solid var(--surface-alt);
}
.provenance-card__lineage-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
}
.provenance-card__subheading {
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  margin: 0;
}
.provenance-card__timeline {
  list-style: none;
  margin: 0;
  padding: 0;
}
.provenance-card__event {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: 8px 0;
  border-bottom: 1px solid var(--surface-alt);
}
.provenance-card__event:last-child { border-bottom: none; }
.provenance-card__event[hidden] { display: none; }
.provenance-card__event-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  width: 100%;
}
.provenance-card__event-title {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink);
}
.provenance-card__event--private .provenance-card__event-title { color: var(--muted); font-weight: 400; }
.provenance-card__event-date {
  font-family: var(--font-family-mono);
  font-size: 12.5px;
  color: var(--muted);
  white-space: nowrap;
}
.provenance-card__event-detail {
  margin: 0;
  font-size: 13px;
  color: var(--muted);
  overflow-wrap: anywhere;
}
.provenance-card__more { margin-top: 8px; width: 100%; }
.provenance-card__link {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.provenance-card__verify-link {
  display: inline-block;
  margin-top: 12px;
  font-size: 13px;
}
/* Status chips on lineage surfaces. Labels are already Title Case, and the
 * shared chip's capitalize turns "Recorded by Yeja" into "Recorded By Yeja".
 * The shared accent-on-tint text measures 1.6-3.7:1 in light mode, under AA
 * for 12px text, so the label uses ink and the tint carries the meaning.
 * The neutral chip gets an outline: its fill matches the card surface. */
.provenance-card .yeja-status-chip { text-transform: none; }
.provenance-card .yeja-status-chip.success,
.provenance-card .yeja-status-chip.info,
.provenance-card .yeja-status-chip.danger { color: var(--yeja-text-primary); }
.provenance-card .yeja-status-chip.warning { color: var(--yeja-amber-ink); }
.provenance-card .yeja-status-chip.neutral { box-shadow: inset 0 0 0 1px var(--muted); }
/* Frame 7d (Provenance Passport, 2026-10-21): the expanded card's recent
 * entries as label + date rows, the full-width View Provenance pill, and a
 * one-row compact variant for Auctions. Still no icons (hosts may not load
 * the font). */
.provenance-card__event--row { flex-direction: row; justify-content: space-between; align-items: baseline; gap: 8px; font-size: 13px; }
.provenance-card__muted { color: var(--muted); }
.provenance-card__view-btn { margin-top: 12px; box-sizing: border-box; text-decoration: none; }
.provenance-card__more-link {
  background: none; border: 0; padding: 4px 0 0; font: inherit; font-size: 13px;
  color: var(--ink); text-decoration: underline; text-underline-offset: 2px; cursor: pointer;
}
.provenance-card--compact .provenance-card__body {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; font-size: 14px;
}
.provenance-card--compact { padding: 12px 16px; }
.provenance-card__avatar {
  width: 28px; height: 28px; border-radius: 50%; box-sizing: border-box; flex: none;
  display: inline-flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 600;
  background: var(--yeja-canvas-paper); border: 1px solid var(--line);
}
.provenance-card__creator { font-weight: 600; }
.provenance-card--compact .provenance-card__mono { color: var(--muted); }
.provenance-card__view { margin-inline-start: auto; }
.provenance-card .yeja-status-chip.neutral { color: var(--yeja-text-primary); }
.provenance-card__more-link:focus-visible,
.provenance-card__view-btn:focus-visible,
.provenance-card__link:focus-visible,
.provenance-card__more:focus-visible,
.provenance-card__cert-btn:focus-visible {
  outline: 2px solid var(--yeja-focus);
  outline-offset: 2px;
}

/* Collapsible variant (backlog 26c851a9) — Art Board's detail box opens
 * this only for minted pieces. A native <details>/<summary>, so keyboard
 * operation and expanded/collapsed state come from the platform rather
 * than being reimplemented. */
.provenance-card--collapsible > .provenance-card__heading {
  cursor: pointer;
  /* Collapsed, the heading IS the whole card — the 12px bottom margin
   * inherited from the base rule would otherwise leave dead space under
   * it. Restored when open, below. */
  margin-bottom: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  list-style: none; /* Firefox */
}
/* Safari/Chrome draw their own triangle via this pseudo-element, which
 * ::marker does not cover. Both are needed to fully replace the default
 * with the chevron below. */
.provenance-card--collapsible > .provenance-card__heading::-webkit-details-marker { display: none; }
.provenance-card--collapsible > .provenance-card__heading::marker { content: ''; }
.provenance-card--collapsible[open] > .provenance-card__heading { margin-bottom: 12px; }
/* CSS-drawn chevron rather than a Material Symbols ligature: this
 * component renders in four apps and cannot assume every host page loads
 * the icon font — an unloaded ligature paints its NAME as literal text,
 * which already shipped broken once on the public portfolio. */
.provenance-card--collapsible > .provenance-card__heading::after {
  content: '';
  width: 6px;
  height: 6px;
  margin-inline-start: auto;
  border-inline-end: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 0.15s ease;
}
.provenance-card--collapsible[open] > .provenance-card__heading::after {
  transform: rotate(-135deg) translate(-2px, -2px);
}
.provenance-card--collapsible > .provenance-card__heading:focus-visible {
  outline: 2px solid var(--yeja-focus);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .provenance-card--collapsible > .provenance-card__heading::after { transition: none; }
}

/* ── Breadcrumbs (backlog 5f81ff03, 2026-08-02) ──
 * Consolidates FIVE separately-maintained copies (profile, my_studio,
 * art_board, world_galleries, admin).
 *
 * On the color drift specifically: the copies disagreed on token NAMES
 * (--yeja-text-primary/--yeja-text-muted in Art Board vs --ink/--muted
 * elsewhere). That was checked rather than assumed — --ink is defined as
 * var(--yeja-text-primary) in all three themes, so both always resolved
 * identically and there was no visible difference. The real cost was
 * five implementations to keep in step, NONE of which had list
 * semantics, aria-current, or a focus ring.
 *
 * --ink/--muted are canonical here: what most copies used and what this
 * file already uses throughout, so adopting the shared block changes
 * nothing visually in any app.
 *
 * Markup is semantic (nav > ol > li) per apps/shared/breadcrumbs.js, so
 * this styles a real list rather than a row of spans. */
.yeja-crumbs {
  font-size: var(--text-body-size, 14px);
  font-weight: 600;
  color: var(--muted);
}
.yeja-crumbs-list {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.yeja-crumb {
  display: flex;
  align-items: center;
  gap: 6px;
  /* min-width:0 lets a long crumb shrink instead of forcing the row to
   * overflow its container — the trail wraps rather than pushing the
   * page sideways. */
  min-width: 0;
}
.yeja-crumb-current {
  color: var(--ink);
  /* The current crumb is the one most worth reading in full, but it is
   * also the most likely to be a long user-supplied name — truncate
   * rather than wrap it awkwardly mid-word. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.yeja-crumb-btn {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: inherit;
  font: inherit;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.yeja-crumb-btn:hover { color: var(--ink); text-decoration: underline; }
/* Cobalt focus ring, 2px/2px (guide §3). None of the five copies had a
 * focus style at all, so keyboard users had no visible position in the
 * trail. */
.yeja-crumb-btn:focus-visible {
  outline: 2px solid var(--yeja-cobalt, #1E88E5);
  outline-offset: 2px;
  border-radius: 2px;
}
.yeja-crumb-sep { color: var(--muted); }

/* ── Brand mark: the hummingbird (2026-08-21) ──────────────────────────────
 * Replaces the four-color ring that was copy-pasted into five stylesheets as
 * .brand-mark / .brand-ring / .atelier-brand-mark, each re-deriving its own
 * box-shadow offsets per size. Markup comes from apps/shared/brand-logo.js;
 * those three legacy class names are kept below as aliases so existing markup
 * that has not been migrated still sizes correctly.
 *
 * The SVG's own fills read --yeja-logo-* directly. Only geometry lives here. */
.yeja-brand-logo {
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
  display: block;
  /* The artwork is 727x797 — taller than wide. Every call site sizes it in a
   * square box, so preserve the aspect ratio inside that box rather than
   * stretching the bird. overflow is visible because the flutter rotates the
   * wings slightly outside the viewBox at the extremes. */
  overflow: visible;
}
.yeja-brand-logo path {
  stroke-width: var(--yeja-logo-stroke, 1.75px);
}

/* Stroke ramp. The weight has to scale with the box or the outline eats the
 * artwork: the authored 2.5px is 1% of the 220px box it was drawn in but 12%
 * of a 20px topbar, and since every shape is outlined on both sides those
 * strokes collide — at 16-24px the tail, beak and all of the amber are lost.
 *
 * Utility classes so any surface can opt into the right weight by size. The
 * token's own default is the 56px value (1.75px), the mark's reference size. */
.yeja-logo-16  { --yeja-logo-stroke: 0.4px; }
.yeja-logo-20  { --yeja-logo-stroke: 0.6px; }
.yeja-logo-24  { --yeja-logo-stroke: 0.85px; }
.yeja-logo-32  { --yeja-logo-stroke: 1.25px; }
.yeja-logo-56  { --yeja-logo-stroke: 1.75px; }
.yeja-logo-120 { --yeja-logo-stroke: 2.5px; }

/* ── Motion: a 1:1 port of memory/design/yeja-hummingbird-logo-animation-preview.html ──
 * That preview IS the spec. Three layers, exactly as approved there:
 *
 *   1. bob      — the whole bird floats, 2.6s, always running.
 *   2. wingFlap — a gentle idle wingbeat, 1.7s, always running.
 *   3. flutter  — a burst that starts at full speed and eases to a stop,
 *                 played once on load and once per hover/focus. It replaces
 *                 the idle flap for its duration, and the tail lifts 15deg.
 *
 * NOTHING ROTATES THE WHOLE MARK. The old ring's hover animation
 * (@keyframes yeja-brand-spin, deleted from design-tokens.css 2026-08-21)
 * turned the entire logo 180deg, which on a bird reads as it flipping upside
 * down. Wings move; the bird does not.
 *
 * bob translates in viewBox units, so its amplitude scales with the mark —
 * the preview's 16px against a 797-unit viewBox is ~2% of the height, the
 * same proportion at 20px as at 220px.
 *
 * Parts are addressed by CLASS, not id: most marketing pages render the mark
 * twice (topbar and footer), and duplicate ids would be invalid HTML with
 * only the first one matching.
 *
 * transform-origins are the SVG's own values, matching the preview. */
.yeja-brand-logo .yl-bird-root { animation: yeja-bird-bob 2.6s ease-in-out infinite; }
.yeja-brand-logo .yl-wing {
  transform-origin: 601.5px 508.5px;
  animation: yeja-wing-flap 1.7s ease-in-out infinite;
}
.yeja-brand-logo .yl-wing2 {
  transform-origin: 581.5px 509px;
  animation: yeja-wing2-flap 1.7s ease-in-out infinite;
}
.yeja-brand-logo .yl-tail {
  transform-origin: 543px 755px;
  transition: transform .35s ease-in-out;
}
.yeja-brand-logo.is-flutter .yl-wing  { animation: yeja-wing-flutter 3s ease-in-out 1; }
.yeja-brand-logo.is-flutter .yl-wing2 { animation: yeja-wing2-flutter 3.38s ease-in-out 1; animation-delay: .05s; }
.yeja-brand-logo.is-flutter .yl-tail  { transform: rotate(15deg); }

@keyframes yeja-bird-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-16px); }
}
@keyframes yeja-wing-flap {
  0%, 100% { transform: rotate(0deg) scaleX(1); }
  50%      { transform: rotate(-6deg) scaleX(.94); }
}
@keyframes yeja-wing2-flap {
  0%, 100% { transform: rotate(0deg) scaleX(1); }
  50%      { transform: rotate(-4deg) scaleX(.96); }
}
@keyframes yeja-wing-flutter{0%{transform:rotate(0deg) scaleX(1)}2.33%{transform:rotate(-16deg) scaleX(.82)}4.66%{transform:rotate(0deg) scaleX(1)}6.99%{transform:rotate(-16deg) scaleX(.82)}9.32%{transform:rotate(0deg) scaleX(1)}11.65%{transform:rotate(-16deg) scaleX(.82)}13.98%{transform:rotate(0deg) scaleX(1)}16.31%{transform:rotate(-16deg) scaleX(.82)}18.64%{transform:rotate(0deg) scaleX(1)}20.97%{transform:rotate(-16deg) scaleX(.82)}23.3%{transform:rotate(0deg) scaleX(1)}25.63%{transform:rotate(-16deg) scaleX(.82)}27.96%{transform:rotate(0deg) scaleX(1)}30.29%{transform:rotate(-16deg) scaleX(.82)}32.62%{transform:rotate(0deg) scaleX(1)}34.95%{transform:rotate(-16deg) scaleX(.82)}37.28%{transform:rotate(0deg) scaleX(1)}40.14%{transform:rotate(-15deg) scaleX(.83)}43.01%{transform:rotate(0deg) scaleX(1)}46.42%{transform:rotate(-13deg) scaleX(.85)}49.82%{transform:rotate(0deg) scaleX(1)}53.76%{transform:rotate(-11deg) scaleX(.88)}57.71%{transform:rotate(0deg) scaleX(1)}62.19%{transform:rotate(-9deg) scaleX(.90)}66.67%{transform:rotate(0deg) scaleX(1)}71.68%{transform:rotate(-7deg) scaleX(.92)}76.7%{transform:rotate(0deg) scaleX(1)}82.26%{transform:rotate(-5deg) scaleX(.94)}87.81%{transform:rotate(0deg) scaleX(1)}93.91%{transform:rotate(-4deg) scaleX(.96)}100%{transform:rotate(0deg) scaleX(1)}}
@keyframes yeja-wing2-flutter{0%{transform:rotate(0deg) scaleX(1)}2.33%{transform:rotate(-11deg) scaleX(.88)}4.66%{transform:rotate(0deg) scaleX(1)}6.99%{transform:rotate(-11deg) scaleX(.88)}9.32%{transform:rotate(0deg) scaleX(1)}11.65%{transform:rotate(-11deg) scaleX(.88)}13.98%{transform:rotate(0deg) scaleX(1)}16.31%{transform:rotate(-11deg) scaleX(.88)}18.64%{transform:rotate(0deg) scaleX(1)}20.97%{transform:rotate(-11deg) scaleX(.88)}23.3%{transform:rotate(0deg) scaleX(1)}25.63%{transform:rotate(-11deg) scaleX(.88)}27.96%{transform:rotate(0deg) scaleX(1)}30.29%{transform:rotate(-11deg) scaleX(.88)}32.62%{transform:rotate(0deg) scaleX(1)}34.95%{transform:rotate(-11deg) scaleX(.88)}37.28%{transform:rotate(0deg) scaleX(1)}40.14%{transform:rotate(-10deg) scaleX(.89)}43.01%{transform:rotate(0deg) scaleX(1)}46.42%{transform:rotate(-9deg) scaleX(.90)}49.82%{transform:rotate(0deg) scaleX(1)}53.76%{transform:rotate(-8deg) scaleX(.91)}57.71%{transform:rotate(0deg) scaleX(1)}62.19%{transform:rotate(-6deg) scaleX(.93)}66.67%{transform:rotate(0deg) scaleX(1)}71.68%{transform:rotate(-5deg) scaleX(.95)}76.7%{transform:rotate(0deg) scaleX(1)}82.26%{transform:rotate(-3deg) scaleX(.97)}87.81%{transform:rotate(0deg) scaleX(1)}93.91%{transform:rotate(-3deg) scaleX(.97)}100%{transform:rotate(0deg) scaleX(1)}}

/* prefers-reduced-motion: everything stops — bob, idle flap and burst alike.
 * The global reset in design-tokens.css only neutralizes DURATION, which
 * would strand the wings mid-beat, so this kills the animations outright and
 * returns every part to its rest position. */
@media (prefers-reduced-motion: reduce) {
  .yeja-brand-logo .yl-bird-root,
  .yeja-brand-logo .yl-wing,
  .yeja-brand-logo .yl-wing2,
  .yeja-brand-logo.is-flutter .yl-wing,
  .yeja-brand-logo.is-flutter .yl-wing2 { animation: none !important; transform: none; }
  .yeja-brand-logo.is-flutter .yl-tail  { transform: none; }
}

/* Legacy wrappers. The mark used to be an empty <span> sized by each app's own
 * .brand-mark/.brand-ring/.atelier-brand-mark rule; that rule still supplies
 * the box, and the SVG now fills it.
 *
 * The wrapper MUST establish a definite size for its child. As a plain block
 * with no intrinsic width, `width:100%` on the SVG resolves against nothing and
 * the SVG falls back to its viewBox's natural 727x797 — which is exactly how
 * this first shipped: a 740px bird in a 20px topbar. inline-flex plus the
 * explicit inheritance below keeps the child pinned to the wrapper's real box. */
.brand-mark,
.brand-ring,
.atelier-brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.brand-mark > .yeja-brand-logo,
.brand-ring > .yeja-brand-logo,
.atelier-brand-mark > .yeja-brand-logo {
  width: 100%;
  height: 100%;
  flex: 1 1 auto;
  min-width: 0;
}

/* ── Universal loading spinner (backlog fdf9ccb3, 2026-08-03) ──
 * The user's ask: "replace all the 'loading' text with our cool universal
 * loading page 'spinner' UI design". ~71 surfaces each painted a bare
 * "Loading…" before this; they all call apps/shared/loading.js instead, which
 * emits this class. 123 call sites depend on it, so the CLASS CONTRACT IS
 * FROZEN — .yeja-spinner / -sm / -lg and the aria-hidden + role="status"
 * pairing in loading.js. Only what it draws changed.
 *
 * 2026-08-21: it now draws the hummingbird rather than the ring-and-blobs,
 * because the brand mark itself did. The point of this component was always
 * that a load reads as THIS product loading rather than a generic arc, so it
 * has to follow the mark. The spinner is sized by --yeja-spinner-size and
 * hosts the SVG from apps/shared/brand-logo.js.
 *
 * WHY THIS ONE BEATS ITS WINGS CONTINUOUSLY when the logo deliberately does
 * not: a loader's whole job is to signal "still working", and stillness here
 * reads as a hung page. The logo has no such job. Same artwork, opposite
 * requirement. */
.yeja-spinner {
  --yeja-spinner-size: 24px;
  width: var(--yeja-spinner-size);
  height: var(--yeja-spinner-size);
  flex: 0 0 var(--yeja-spinner-size);
  box-sizing: border-box;
  display: block;
  overflow: visible;
}
.yeja-spinner path { stroke-width: var(--yeja-logo-stroke, 1.5px); }
.yeja-spinner .yl-wing  { transform-origin: 601.5px 508.5px; animation: yeja-wing-flutter 1.6s linear infinite; }
.yeja-spinner .yl-wing2 { transform-origin: 581.5px 509px;  animation: yeja-wing2-flutter 1.6s linear infinite; }

/* No ring fallback. An earlier revision kept the old four-color ring here as
 * a span:empty fallback "so a slow surface still shows something" — and the
 * result was the OLD LOGO, ROTATING, on every boot screen (admin, chores,
 * share, file-request), months after the ring was retired. Every shipped
 * spinner now carries the bird SVG inline (loading.js emits it; the four boot
 * HTML files embed it), so an empty spinner span is a bug to fix in the
 * caller, not something to paper over with a retired brandmark. */
/* Stroke follows the size, per the ramp above: 24px default sits between the
 * 20px and 32px steps, 16px takes the 16px value. */
.yeja-spinner    { --yeja-logo-stroke: 0.85px; }
.yeja-spinner-sm { --yeja-spinner-size: 16px; --yeja-logo-stroke: 0.4px; }
/* Page and pane loaders are the homepage logo's size, 56px with a 1.75px
 * stroke, everywhere (owner, 2026-10-16: Art Board's was too large, Profile's
 * and Influencer Marketplace's too small). -lg and -56 are both that size;
 * -lg is kept because boot screens and callers still name it. Only the
 * inline row form stays small, since it sits inside buttons and table rows. */
.yeja-spinner-lg,
.yeja-spinner-56,
.yeja-loading:not(.yeja-loading-row) > .yeja-spinner { --yeja-spinner-size: 56px; --yeja-logo-stroke: 1.75px; }

/* The block form: a centered spinner with an optional caption, for the
 * whole-pane "this surface is still loading" case that the word
 * "Loading…" used to occupy on its own. */
.yeja-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 40px 20px;
  color: var(--muted);
}
.yeja-loading-row {
  flex-direction: row;
  padding: 12px 0;
}

/* The picker dialog itself. Wider than the 480px .yeja-dialog-card default
   because the whole point is scrubbing a video frame accurately — at 480px
   a 16:9 clip is too small to tell two similar frames apart. */
.yeja-poster-picker { max-width: 720px; }
.yeja-poster-picker__title { font-size: 18px; font-weight: 700; }
.yeja-poster-picker__hint { font-size: 13px; color: var(--muted); }
.yeja-poster-picker__video {
  width: 100%;
  /* max-height + object-fit:contain, NOT height:auto: a portrait clip would
     otherwise run past the viewport and push the actions off-screen. contain
     letterboxes rather than crops, so the frame being CHOSEN is the frame
     actually shown — a cover crop here would misrepresent the capture. */
  max-height: 60vh;
  object-fit: contain;
  background: var(--yeja-gray-1000);
  border-radius: var(--radius-media, 8px);
}
.yeja-poster-picker__actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

/* ── Busy button (withBusyBird, apps/shared/busy.js) ───────────────────
 * The hummingbird hovers just above a button while its action loads,
 * absolutely placed so the button keeps its size (owner: "hovering the
 * hummingbird around the button not inside of it"). A gentle 4px bob, no
 * rotation; still under reduced motion. */
.yeja-busy-host { position: relative; }
[aria-busy="true"] > .yeja-busy-bird {
  position: absolute;
  inset-block-end: calc(100% + 6px);
  inset-inline-start: 50%;
  translate: -50% 0;
  pointer-events: none;
  z-index: 1;
  animation: yeja-busy-hover 0.9s cubic-bezier(0.4, 0, 0.2, 1) infinite alternate;
}
[aria-busy="true"] > .yeja-busy-bird .yeja-loading-row { padding: 0; }
:root[dir="rtl"] [aria-busy="true"] > .yeja-busy-bird { translate: 50% 0; }
@keyframes yeja-busy-hover {
  from { transform: translateY(0); }
  to { transform: translateY(-4px); }
}
@media (prefers-reduced-motion: reduce) {
  [aria-busy="true"] > .yeja-busy-bird { animation: none !important; }
}

/* prefers-reduced-motion: the global safety net in design-tokens.css
 * neutralizes animation-duration for everything, which would freeze this
 * mid-rotation and leave a static ring that looks like a broken or
 * finished state. A loader still has to signal "working", so it opts back
 * in with a slow opacity pulse — no rotation, no movement, and well
 * inside the intent of the setting. !important is required to beat that
 * global reset, which is itself !important. */
@media (prefers-reduced-motion: reduce) {
  .yeja-spinner {
    animation: yeja-spinner-fade 1.6s ease-in-out infinite !important;
    animation-duration: 1.6s !important;
    animation-iteration-count: infinite !important;
  }
  /* The whole bird pulses as one; the wings must stop beating or the pulse
   * would ride on top of continuing motion. */
  .yeja-spinner .yl-wing,
  .yeja-spinner .yl-wing2 { animation: none !important; }
  @keyframes yeja-spinner-fade {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.45; }
  }
}

/* Screen-reader-only text (moved here from apps/profile/styles.css,
 * 2026-08-03, backlog fdf9ccb3). It lived in one app's stylesheet while
 * being used by apps/portfolio-public and apps/collection-public too, and
 * apps/shared/loading.js now emits it from shared code reachable by every
 * app — a utility that shared code depends on cannot live in one app's
 * private stylesheet. Rule copied verbatim; behavior unchanged. */
.yeja-visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── Artwork specifications (tracker 4bd0a715, Track A / A0) ──────────────
 * Shared promotion of apps/profile's .pf-spec-* block so Art Board, My
 * Studio and Profile render artwork details identically rather than each
 * inventing a layout. Consumed by apps/shared/asset-specs.js.
 *
 * Values only in mono (guide §2 numeric/technical metadata voice); labels
 * stay in the body face so the block does not read as code. */
.yeja-spec-block {
  margin-top: var(--space-3, 24px);
  padding-top: var(--space-2, 16px);
  border-top: 1px solid var(--line);
}
.yeja-spec-heading {
  margin: 0 0 var(--space-1, 8px);
  font-size: var(--text-label-size, 12px);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.yeja-spec-list { margin: 0; }
.yeja-spec-row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-2, 16px);
  padding: 6px 0;
  border-bottom: 1px solid var(--line);
}
.yeja-spec-row:last-child { border-bottom: 0; }
.yeja-spec-label {
  flex: 0 0 auto;
  font-size: var(--text-label-size, 12px);
  color: var(--muted);
}
.yeja-spec-value {
  margin: 0;
  flex: 1 1 auto;
  text-align: end;
  font-family: var(--font-family-mono, 'JetBrains Mono', ui-monospace, monospace);
  font-size: var(--text-label-size, 12px);
  color: var(--ink);
  overflow-wrap: anywhere;
}

/* ── Reaction picker (2026-08-04, backlog 4b717850) ────────────────────
 * The five-emotion panel behind the merged heart/star control.
 *
 * BODY PORTAL + position:fixed, NOT .yeja-dropdown__panel. That panel is
 * position:absolute and would be CLIPPED by .masonry__frame's
 * overflow:hidden — the exact bug apps/art_board's card-menu portal was
 * built to dodge (see its comment at styles.css:344). It is also
 * min-width:280px, far too wide for five 40px icons.
 *
 * The portal node carries the owning app's scope class for --yeja-*
 * resolution, which means it also matches that class's base rule — in
 * Art Board that sets min-height:100% and would stretch this to full
 * viewport height. Hence the override below, same as the card menu. */
/* ⚠️ SPECIFICITY, not just the property. An app scope class like
 * .art-board-app sets `min-height: 100%` on ITSELF, and this portal
 * CARRIES that class (it needs it for --yeja-* resolution), so that base
 * rule matches the portal too. A plain `.yeja-reaction-picker` is 0-1-0 —
 * the SAME specificity — and the app stylesheet loads AFTER
 * components.css, so the app won on source order and the panel stretched
 * to the full viewport (measured live: 900px tall, twice).
 *
 * :where() was the WRONG fix and is recorded here so it is not retried:
 * it zeroes the scope class's specificity, leaving the compound selector
 * still at 0-1-0 — measured live, .art-board-app still won.
 *
 * :is() keeps the class's weight, making this 0-2-0 and an unambiguous
 * win regardless of load order — the same shape as
 * .art-board-app.card-menu__panel, generalised so any app's portal is
 * covered without naming each one twice. */
.yeja-reaction-picker,
:is(.art-board-app, .my-studio-app, .auctions-app, .wg-app).yeja-reaction-picker {
  position: fixed;
  min-height: auto;
  height: auto;
  max-height: none;
  width: auto;
  display: none;
  z-index: 210;            /* above dropdowns (200), below tooltips (10000) */
  padding: 6px;
  gap: 2px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  /* Panel-strong, per the guide's shadow scale for portalled menus. */
  box-shadow: var(--yeja-elevation-3);
}
.yeja-reaction-picker.is-open { display: flex; align-items: center; }

.yeja-reaction-option {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: none;
  background: none;
  border-radius: var(--radius-circle);
  cursor: pointer;
  color: var(--muted);
  transition: transform 0.14s cubic-bezier(0.22, 1, 0.36, 1), background-color 0.14s ease;
}
.yeja-reaction-option:hover,
.yeja-reaction-option:focus-visible {
  background: var(--surface-alt);
  transform: scale(1.15);
}
.yeja-reaction-option:focus-visible {
  outline: 2px solid var(--yeja-focus);
  outline-offset: 2px;
}
/* Each option paints in its own tint ALWAYS (not only when chosen) — the
 * picker is a palette, so a row of gray glyphs would hide which is which.
 * The tint is set inline from REACTIONS[].tone. */
.yeja-reaction-option .yeja-icon { font-size: 24px; }

/* The currently-chosen reaction. Structural, not color-only (guide §7):
 * the glyph FILLS and the option gets a ring, so it survives grayscale. */
.yeja-reaction-option[aria-checked="true"] {
  background: var(--surface-alt);
  box-shadow: inset 0 0 0 2px currentColor;
}

/* ── The trigger, in the card/modal action row ── */
.yeja-reaction-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: none;
  background: none;
  padding: 4px 8px;
  border-radius: var(--radius-pill);
  cursor: pointer;
  color: var(--muted);
  /* Kills the 300ms tap delay and stops a long-press selecting text or
   * raising the iOS callout menu — both required for press-and-hold. */
  touch-action: manipulation;
  user-select: none;
  -webkit-touch-callout: none;
}
.yeja-reaction-trigger:focus-visible {
  outline: 2px solid var(--yeja-focus);
  outline-offset: 2px;
}
/* Reacted: the glyph fills AND takes its reaction's tint (set inline). */
.yeja-reaction-trigger.is-reacted .yeja-icon { font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 20; }
.yeja-reaction-count { font-size: var(--text-label-size, 12px); font-variant-numeric: tabular-nums; }

@media (prefers-reduced-motion: reduce) {
  .yeja-reaction-option { transition: none; }
  .yeja-reaction-option:hover,
  .yeja-reaction-option:focus-visible { transform: none; }
}

/* ── Rich text editor (Quill) ─────────────────────────────────────────
 * Quill's snow theme ships HARDCODED light-mode colors (#ccc borders,
 * #444 icon strokes, #06c active state). Loaded as-is it is unreadable in
 * dark and high-contrast: dark ink on a dark canvas. These overrides map
 * every one of those onto ramp tokens, which are defined three times in
 * design-tokens.css (:root, [data-theme="dark"], [data-theme="high-contrast"]),
 * so all four theme modes come free and no [data-theme] selector is
 * needed here.
 *
 * The .ql-* class names belong to a third-party stylesheet, so they are
 * the one place in this file that cannot follow the .yeja-* convention.
 * Scoped under .yeja-rich-text (the host element rich-text-editor.js
 * creates) so nothing leaks onto unrelated markup. */
.yeja-rich-text .ql-container {
  font-family: inherit;
  font-size: var(--text-body-size, 14px);
  color: var(--yeja-text-primary);
  border-color: var(--yeja-border);
  border-end-start-radius: var(--radius-control, 6px);
  border-end-end-radius: var(--radius-control, 6px);
  background: var(--yeja-canvas);
}
.yeja-rich-text .ql-editor { min-height: inherit; }
.yeja-rich-text .ql-editor.ql-blank::before {
  color: var(--yeja-text-muted);
  font-style: normal;   /* Quill italicizes the placeholder; nothing else here does */
}
.yeja-rich-text .ql-toolbar {
  border-color: var(--yeja-border);
  border-start-start-radius: var(--radius-control, 6px);
  border-start-end-radius: var(--radius-control, 6px);
  background: var(--yeja-sidebar);
}
/* Toolbar icons are inline SVG with `stroke`/`fill` attributes, so they
 * only follow the theme if the stroke is re-pointed at a token. */
.yeja-rich-text .ql-toolbar .ql-stroke { stroke: var(--yeja-text-primary); }
.yeja-rich-text .ql-toolbar .ql-fill { fill: var(--yeja-text-primary); }
.yeja-rich-text .ql-toolbar .ql-picker-label { color: var(--yeja-text-primary); }
.yeja-rich-text .ql-toolbar button:hover .ql-stroke,
.yeja-rich-text .ql-toolbar button.ql-active .ql-stroke {
  stroke: var(--yeja-cobalt);
}
.yeja-rich-text .ql-toolbar button:hover .ql-fill,
.yeja-rich-text .ql-toolbar button.ql-active .ql-fill {
  fill: var(--yeja-cobalt);
}
.yeja-rich-text .ql-toolbar button.ql-active .ql-picker-label { color: var(--yeja-cobalt); }
/* The dropdown panel is positioned absolutely and inherits nothing. */
.yeja-rich-text .ql-picker-options {
  background: var(--yeja-sidebar);
  border-color: var(--yeja-border);
  color: var(--yeja-text-primary);
}
.yeja-rich-text .ql-editor a { color: var(--yeja-cobalt); }
.yeja-rich-text .ql-editor blockquote {
  border-inline-start: 2px solid var(--yeja-border);
  padding-inline-start: 12px;
  margin-inline-start: 0;
  color: var(--yeja-text-muted);
}
/* Guide §6: every interactive element carries the cobalt ring. Quill's
 * editing surface is a contenteditable div, which gets no ring by
 * default, and its toolbar buttons suppress theirs. */
.yeja-rich-text .ql-editor:focus-visible,
.yeja-rich-text .ql-toolbar button:focus-visible {
  outline: 2px solid var(--yeja-focus);
  outline-offset: 2px;
}

/* ── RTL: bidi isolation for user-authored text (I18N-P6, 2026-08-23) ───────
 *
 * Under dir="rtl" an ENGLISH sentence renders its trailing period at the LEFT
 * edge — "Untitled." becomes ".Untitled" — because a period is a bidi-NEUTRAL
 * character and the Unicode bidi algorithm places neutrals according to the
 * PARAGRAPH direction. Correct for Farsi, visibly broken for everything else.
 *
 * That is not an edge case here. Two things guarantee mixed direction on every
 * Farsi page:
 *   1. Untranslated English is a NORMAL state — the per-field i18n fallback
 *      shows English wherever a translation has not landed.
 *   2. User content is in arbitrary scripts. An artwork titled "Nightline" and
 *      one titled "شب‌خط" sit in the same grid, under whichever direction the
 *      VIEWER chose. Neither should be laid out by the other's rules.
 *
 * `unicode-bidi: plaintext` resolves each element's direction from its own
 * first strong character rather than inheriting the page's. Farsi stays RTL,
 * Latin lays out LTR with its punctuation on the correct side.
 *
 * WHY THIS TARGETS TEXT-BEARING ELEMENTS AND NOT THE CHROME: nav order, rail
 * side, logo placement and button order must GENUINELY mirror under RTL, and
 * the logical properties applied across these stylesheets make them do so.
 * Putting plaintext on a container would defeat that. The landing/marketing
 * equivalent of this block lives in apps/landing/public/landing.css and covers
 * the CMS surfaces; this one covers the in-app surfaces, which had never been
 * exercised under dir=rtl before today. */
[dir="rtl"] .yeja-row-list-title,
[dir="rtl"] .yeja-row-list-meta,
[dir="rtl"] .yeja-card h2,
[dir="rtl"] .yeja-card h3,
[dir="rtl"] .yeja-empty-state,
[dir="rtl"] .section-title,
[dir="rtl"] .modal-title,
[dir="rtl"] .modal-artist-name,
[dir="rtl"] .tree-item-name,
[dir="rtl"] .list-meta,
[dir="rtl"] .note-meta,
[dir="rtl"] .hover-label {
  unicode-bidi: plaintext;
}

/* ── Platform brand fills (2026-10-01) ──
 * Shared so any surface can show a platform mark on its brand color —
 * apps/social/styles.css's .instagram-bg etc. only load inside Social.
 * Values are the theme-invariant --yeja-platform-* tokens. The mark itself
 * paints in currentColor (apps/shared/platform-logos.js). */
.yeja-platform-bg {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-pill, 24px);
  color: var(--yeja-gray-0);
  flex: 0 0 auto;
}
.yeja-platform-bg svg { width: 18px; height: 18px; fill: currentColor; }
.yeja-platform-bg--instagram { background: var(--yeja-platform-instagram); }
.yeja-platform-bg--tiktok    { background: var(--yeja-platform-tiktok); }
.yeja-platform-bg--x         { background: var(--yeja-platform-x); }
.yeja-platform-bg--linkedin  { background: var(--yeja-platform-linkedin); }
.yeja-platform-bg--pinterest { background: var(--yeja-platform-pinterest); }
.yeja-platform-bg--youtube   { background: var(--yeja-platform-youtube); }
.yeja-platform-bg--threads   { background: var(--yeja-platform-threads); }
.yeja-platform-bg--facebook  { background: var(--yeja-platform-facebook); }
.yeja-platform-bg--bluesky   { background: var(--yeja-platform-bluesky); }
.yeja-platform-bg--whatsapp  { background: var(--yeja-platform-whatsapp); }
.yeja-platform-bg--messenger { background: var(--yeja-platform-messenger); }
/* A black brand disc vanishes into the dark canvas; outline it there. */
:root[data-theme="dark"] .yeja-platform-bg--tiktok,
:root[data-theme="dark"] .yeja-platform-bg--x,
:root[data-theme="dark"] .yeja-platform-bg--threads {
  box-shadow: 0 0 0 1px var(--yeja-text-muted);
}

/* ── Assisted Publish panel (apps/shared/social/assisted-publish.js) ── */
.assist-card {
  max-width: 640px;
  max-height: 90vh;
  overflow-y: auto;
}
.assist-card__title { margin: 0; font-size: 18px; font-weight: 700; }
.assist-card__intro { margin: 0; color: var(--muted); font-size: 14px; }
.assist-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.assist-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.assist-row__name { flex: 1 1 auto; min-width: 96px; }
.assist-row__actions { display: flex; gap: 8px; flex-wrap: wrap; }
.assist-row__actions a { text-decoration: none; }
.assist-mark {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  gap: 12px;
  align-items: end;
}
.assist-mark__field { display: flex; flex-direction: column; gap: 4px; font-size: 14px; }
.assist-mark__field select,
.assist-mark__field input {
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--muted);
  border-radius: var(--radius-control, 8px);
  padding: 8px 12px;
  min-width: 0;
}
.assist-mark__actions {
  grid-column: 1 / -1;
  display: flex;
  justify-content: flex-end;
  gap: 12px;
}
@media (max-width: 560px) {
  .assist-mark { grid-template-columns: minmax(0, 1fr); }
}

/* ── Connect wizard (apps/shared/social/connect-wizard.js) ── */
.wizard-card { max-width: 520px; max-height: 90vh; overflow-y: auto; }
.wizard-head { display: flex; align-items: center; gap: 12px; }
.wizard-title { margin: 0; font-size: 18px; font-weight: 700; }
.wizard-section h3 { margin: 0 0 4px; font-size: 14px; font-weight: 700; }
.wizard-section p,
.wizard-section ul { margin: 0 0 12px; font-size: 14px; }
.wizard-section ul { padding-inline-start: 20px; }
.wizard-fine { color: var(--muted); font-size: 14px; margin: 0; }
.wizard-form { display: flex; flex-direction: column; gap: 12px; }
.wizard-field { display: flex; flex-direction: column; gap: 4px; font-size: 14px; }
.wizard-field[hidden] { display: none; }
.wizard-field input {
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--muted);
  border-radius: var(--radius-control, 8px);
  padding: 8px 12px;
}
.wizard-error { margin: 0; color: var(--yeja-crimson); font-size: 14px; }
.wizard-actions { display: flex; justify-content: flex-end; gap: 12px; }

/* ── Share sheet (apps/shared/social/share-sheet.js) ── */
.share-card { max-width: 560px; max-height: 90vh; overflow-y: auto; }
.share-link-row { display: flex; gap: 8px; }
.share-link-input {
  flex: 1 1 auto;
  min-width: 0;
  font: inherit;
  font-family: var(--font-family-mono);
  font-size: 12.5px;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--muted);
  border-radius: var(--radius-control, 8px);
  padding: 8px 12px;
}
.share-platforms {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 8px;
}
.share-platform {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-control, 8px);
  color: var(--ink);
  text-decoration: none;
  font-size: 14px;
}
.share-platform:hover { background: var(--yeja-hover-tertiary); }
button.share-platform { background: transparent; font: inherit; font-size: 14px; text-align: start; cursor: pointer; }
.share-handoff-hint { margin: 0; }
.share-post-panel { display: flex; flex-direction: column; gap: 8px; }
.share-post-cover { border-radius: var(--radius-media, 8px); max-width: 96px; height: auto; }
.share-post-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.share-frame-host { display: flex; flex-direction: column; gap: 8px; }
.share-frame-host:empty { display: none; }
.share-use-preview { display: flex; align-items: center; gap: 8px; font-size: 14px; }
.share-post-note { font-size: 12px; color: var(--muted); }
.share-post-result { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
.share-post-result:empty { display: none; }
.share-dm { display: flex; flex-direction: column; gap: 8px; }
.share-dm[hidden] { display: none; }
.share-dm-result { font-size: 14px; }
.share-post-result a.yeja-pill-btn { text-decoration: none; }
.share-owner-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.share-qr summary { cursor: pointer; font-size: 14px; }
.share-qr__code { padding-top: 12px; display: flex; justify-content: center; }
.share-qr__code svg { background: var(--yeja-gray-0); padding: 8px; border-radius: var(--radius-control, 8px); }

/* Primary and ghost pills side by side in the social dialogs read as one
   size (the base .yeja-pill-btn padding is em-based and smaller). */
.wizard-actions .yeja-pill-btn,
.assist-mark__actions .yeja-pill-btn,
.share-link-row .yeja-pill-btn {
  padding: 10px 24px;
  font-size: 15.5px;
  justify-content: center;
}
.assist-row__actions .yeja-pill-btn {
  padding: 8px 16px;
  font-size: 14px;
}

/* ── Bulk post planning (apps/shared/social/bulk-plan.js) ── */
.bulk-channels {
  border: 1px solid var(--line);
  border-radius: var(--radius-control, 8px);
  padding: 12px;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.bulk-channels legend { font-size: 14px; padding: 0 4px; }
.bulk-channel { display: flex; align-items: center; gap: 8px; font-size: 14px; }
.bulk-channel .yeja-platform-bg { width: 24px; height: 24px; }
.bulk-channel .yeja-platform-bg svg { width: 14px; height: 14px; }
.bulk-channel small { color: var(--muted); }
.wizard-field select {
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--muted);
  border-radius: var(--radius-control, 8px);
  padding: 8px 12px;
}

/* ── Post composer (apps/shared/social/composer.js) ── */
.yc-card { max-width: 760px; width: 100%; max-height: 92vh; overflow-y: auto; display: flex; flex-direction: column; gap: 16px; }
.yc-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.yc-section { display: flex; flex-direction: column; gap: 8px; }
.yc-label { margin: 0; font-size: 14px; font-weight: 700; }
.yc-channels { display: flex; flex-wrap: wrap; gap: 8px; }
.yc-channel {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px 6px 6px;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}
.yc-channel:hover { background: var(--yeja-hover-tertiary); }
.yc-channel.is-on { border-color: var(--yeja-cobalt); box-shadow: 0 0 0 1px var(--yeja-cobalt); }
.yc-channel .yeja-platform-bg { width: 28px; height: 28px; }
.yc-channel .yeja-platform-bg svg { width: 16px; height: 16px; }
.yc-channel small { color: var(--muted); }
.yc-channel__check { font-size: 18px; color: var(--muted); }
.yc-channel.is-on .yc-channel__check { color: var(--yeja-cobalt); }
.yc-inline-link { color: inherit; text-decoration: underline; }
.yc-attachment { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.yc-attachment__thumb {
  width: 48px;
  height: 48px;
  border-radius: var(--radius-control, 8px);
  object-fit: contain;
  background: var(--surface-alt);
  flex-shrink: 0;
}
.yc-attachment__title { flex: 1 1 auto; min-width: 0; font-size: 14px; font-weight: 500; overflow-wrap: anywhere; }
.yc-ai { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.yc-variants { display: flex; flex-direction: column; gap: 8px; }
.yc-variant {
  text-align: start;
  font: inherit;
  font-size: 14px;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-control, 8px);
  padding: 8px 12px;
  cursor: pointer;
  white-space: pre-wrap;
}
.yc-variant:hover { background: var(--yeja-hover-tertiary); }
.yc-tabs { display: flex; flex-wrap: wrap; gap: 8px; }
.yc-textarea,
.yc-select {
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--muted);
  border-radius: var(--radius-control, 8px);
  padding: 8px 12px;
  box-sizing: border-box;
  min-width: 0;
}
.yc-textarea { width: 100%; resize: vertical; font-size: 15px; line-height: 1.5; }
.yc-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; align-items: start; }
.yc-split__edit { display: flex; flex-direction: column; gap: 12px; }
.yc-frames {
  border: 1px solid var(--line);
  border-radius: var(--radius-control, 8px);
  padding: 8px 12px 12px;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  font-size: 14px;
}
.yc-frames legend { padding: 0 4px; }
.yc-frames .wizard-fine { flex-basis: 100%; }
.yc-radio-row { flex-basis: 100%; display: flex; flex-wrap: wrap; gap: 8px 16px; }
.yc-radio { display: inline-flex; align-items: center; gap: 6px; }
.yc-preview {
  border: 1px solid var(--line);
  border-radius: var(--radius-control, 8px);
  background: var(--surface);
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.yc-preview__head { display: flex; align-items: center; gap: 8px; font-size: 14px; }
.yc-preview__head .yeja-platform-bg { width: 24px; height: 24px; }
.yc-preview__head .yeja-platform-bg svg { width: 14px; height: 14px; }
.yc-preview__frame { width: 100%; margin-inline: auto; display: flex; align-items: center; justify-content: center; overflow: hidden; border-radius: 4px; outline: 1px solid var(--line); }
.yc-preview__frame[data-mat="white"] { background: var(--yeja-gray-0); }
.yc-preview__frame[data-mat="black"] { background: var(--yeja-gray-1000); }
.yc-preview__frame img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }
.yc-preview__frame:not([style]) img { width: 100%; height: auto; }
.yc-preview__text { margin: 0; font-size: 14px; white-space: pre-wrap; overflow-wrap: anywhere; }
.yc-preview__comment { margin: 0; font-size: 13px; color: var(--muted); }
.yc-schedule { gap: 12px; }
.yc-toggle { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; }
.yc-schedule__fields { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.yc-schedule__fields[hidden] { display: none; }
.yc-best { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.yc-chip {
  font: inherit;
  font-size: 13px;
  color: var(--ink);
  background: var(--surface-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  padding: 4px 12px;
  cursor: pointer;
}
.yc-chip:hover { background: var(--yeja-hover-tertiary); }
.yc-card .wizard-actions .ghost-pill,
.yc-card .wizard-actions .yeja-pill-btn { padding: 10px 24px; font-size: 15.5px; justify-content: center; }
@media (max-width: 640px) {
  .yc-split { grid-template-columns: minmax(0, 1fr); }
  .yc-card .wizard-actions { flex-direction: column-reverse; }
  .yc-card .wizard-actions > * { width: 100%; }
}

/* Per-network checks under the composer text */
.composer-checks {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.8rem;
  color: var(--muted);
}
.composer-check { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.composer-check strong { color: var(--ink); font-weight: 500; }
.composer-check .yeja-icon { font-size: 16px; color: var(--yeja-mint); }
.composer-check--warn .yeja-icon { color: var(--yeja-crimson); }
.composer-check--warn span:last-child { color: var(--ink); }

/* Share links on server-rendered public pages (api/_lib/share-intents.js) */
.yeja-share-links { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 16px; }
.yeja-share-links__label { font-size: 14px; color: var(--muted); margin-inline-end: 4px; }
.yeja-share-links__item { display: inline-flex; border-radius: var(--radius-pill); }
.yeja-share-links__item .yeja-platform-bg { width: 32px; height: 32px; }
.yeja-share-links__item .yeja-platform-bg svg { width: 16px; height: 16px; }
.yeja-share-links__item:focus-visible { outline: 2px solid var(--yeja-focus); outline-offset: 2px; }
/* shareWidgetHTML(): the Share pill takes the links' place once JS runs. */
.yeja-share { margin-top: 16px; }
.yeja-share > .yeja-share-links { margin-top: 0; }
/* Marketing pages load the tokens (with the base .yeja-pill-btn) after this
 * file, so the ghost modifier alone loses on source order there. */
.yeja-share .yeja-pill-btn-ghost { background: transparent; border: 1px solid var(--muted); color: var(--ink); }
.yeja-share-links[hidden] { display: none; }

/* Export for Video (apps/shared/creative-tools.js) */
.video-export__preview { display: flex; justify-content: center; }
.video-export__preview img { height: 320px; width: auto; aspect-ratio: 9 / 16; border-radius: var(--radius-control, 8px); border: 1px solid var(--line); }

/* Share flow (composer.js, plan 8.2) */
.yc-picker {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
  gap: 8px;
  max-height: 320px;
  overflow-y: auto;
  padding: 2px;
}
.yc-pick {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 6px;
  border: 1px solid var(--line);
  border-radius: var(--radius-control, 8px);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  text-align: start;
  cursor: pointer;
}
.yc-pick:hover { background: var(--yeja-hover-tertiary); }
.yc-pick img,
.yc-pick__ph { width: 100%; aspect-ratio: 1; object-fit: contain; background: var(--surface-alt); border-radius: 4px; display: block; }
.yc-pick__title { font-size: 13px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.yc-pick__price { font-size: 12px; color: var(--muted); }
.yc-text-btn {
  align-self: flex-start;
  font: inherit;
  font-size: 14px;
  color: var(--ink);
  background: none;
  border: 0;
  padding: 4px 0;
  text-decoration: underline;
  cursor: pointer;
}
.yc-note { padding: 8px 12px; border-radius: var(--radius-control, 8px); background: var(--yeja-warning-bg); color: var(--ink); }
.yc-suggest { display: flex; flex-wrap: wrap; gap: 8px; }
.yc-suggest .yc-chip { text-align: start; }
.yc-chip.is-on { border-color: var(--yeja-cobalt); box-shadow: 0 0 0 1px var(--yeja-cobalt); }
.yc-channel--phone small { font-size: 12px; }
.yc-channels__hint { flex-basis: 100%; }
.yc-more { border: 1px solid var(--line); border-radius: var(--radius-control, 8px); padding: 8px 12px; }
.yc-more[open] { display: flex; flex-direction: column; gap: 12px; }
.yc-more summary { cursor: pointer; font-size: 14px; font-weight: 500; }
.yc-actions { flex-wrap: wrap; }
[data-yc-main],
[data-yc-done] { display: flex; flex-direction: column; gap: 16px; }
[data-yc-main][hidden],
[data-yc-done][hidden] { display: none; }
.yc-attachment__title small { color: var(--muted); font-weight: 400; margin-inline-start: 6px; }

/* ── App page building blocks (2026-10-15, Promo Network restyle) ──
 * Promoted from patterns the reference apps already share, so a new or
 * rebuilt app composes them instead of inventing its own look:
 *   .yeja-grid / .yeja-tile  — World Galleries' square card grid (frame
 *     with the meta BELOW it, guide §5), following the shell's Grid Size
 *     control through --yeja-grid-columns.
 *   .yeja-section            — a titled block of a sub-view (World
 *     Galleries' Inbox/Bookings section heading).
 *   .yeja-row-list           — the <ul> around .yeja-row-list-item rows,
 *     plus a lead (avatar) and trailing actions slot.
 *   .yeja-stats / .yeja-stat — a metric tile row (label in mono metadata
 *     type, value at the H2 size).
 *   .yeja-empty-state--page  — the page-level empty state of guide §5:
 *     centered, muted, 15.5px, 48px of vertical room.
 *   .yeja-avatar             — a person's round avatar at 28/36/56px. */
.yeja-page-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  padding: var(--space-4) 0 var(--space-6);
  min-width: 0;
}

.yeja-grid {
  display: grid;
  grid-template-columns: repeat(var(--yeja-grid-columns, 5), minmax(0, 1fr));
  gap: var(--space-2);
}
@media (max-width: 900px) {
  .yeja-grid { grid-template-columns: repeat(min(var(--yeja-grid-columns, 5), 3), minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .yeja-grid { grid-template-columns: repeat(min(var(--yeja-grid-columns, 5), 2), minmax(0, 1fr)); }
}
.yeja-tile {
  position: relative;
  display: block;
  min-width: 0;
  cursor: pointer;
  outline: none;
}
.yeja-tile__frame {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-media);
  overflow: hidden;
  background: var(--surface-alt);
  transition: transform 0.15s ease-in-out;
}
.yeja-tile:hover .yeja-tile__frame { transform: translateY(-2px); }
.yeja-tile:focus-visible .yeja-tile__frame {
  outline: 2px solid var(--yeja-focus);
  outline-offset: 2px;
}
.yeja-tile__frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.yeja-tile__placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-h1-size);
  font-weight: 700;
  color: var(--muted);
}
.yeja-tile__badges {
  position: absolute;
  top: var(--space-1);
  inset-inline-start: var(--space-1);
  inset-inline-end: var(--space-1);
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  pointer-events: none;
}
.yeja-tile__badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 9px;
  border-radius: var(--radius-pill);
  background: color-mix(in oklch, var(--yeja-gray-1000) 65%, transparent);
  color: var(--yeja-gray-0);
  font-family: var(--font-family-mono);
  font-size: 11px;
  font-weight: 600;
}
.yeja-tile__badge .yeja-icon { font-size: 14px; }
.yeja-tile__actions {
  position: absolute;
  inset-inline: var(--space-1);
  bottom: var(--space-1);
  display: flex;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 4px;
  opacity: 0;
  transition: opacity 0.15s ease-in-out;
}
.yeja-tile:hover .yeja-tile__actions,
.yeja-tile:focus-within .yeja-tile__actions { opacity: 1; }
@media (hover: none) {
  .yeja-tile__actions { opacity: 1; }
}
.yeja-tile__action {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: var(--text-label-size);
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  box-shadow: 0 1px 4px color-mix(in oklch, var(--yeja-gray-1000) 20%, transparent);
}
/* World Galleries' card, exactly (Art Supplies, 2026-10-27): a logo shown
 * whole on the card surface rather than cropped, and save + More at the
 * top end with the like at the bottom end. */
.yeja-tile__frame--contain {
  background: var(--yeja-sidebar);
  border: 1px solid var(--line);
}
.yeja-tile__frame--contain img {
  object-fit: contain;
  padding: 12px;
  box-sizing: border-box;
}
.yeja-tile__actions--top-end {
  top: var(--space-1);
  bottom: auto;
  inset-inline-start: auto;
}
.yeja-tile__action--ink { background: var(--yeja-btn-primary-bg); color: var(--yeja-btn-primary-fg); }
.yeja-tile__action:disabled { opacity: 0.5; cursor: default; }
.yeja-tile__action .yeja-icon { font-size: 16px; }
.yeja-tile__meta {
  padding: var(--space-1) 2px 0;
  min-width: 0;
}
.yeja-tile__title,
.yeja-tile__sub {
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.yeja-tile__title {
  font-size: var(--text-body-size);
  font-weight: 600;
  color: var(--ink);
}
.yeja-tile__sub {
  margin-top: 2px;
  font-family: var(--font-family-mono);
  font-size: var(--text-label-size);
  color: var(--muted);
}

.yeja-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}
.yeja-section__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-2);
}
.yeja-section__title {
  margin: 0;
  font-size: var(--text-h3-size);
  line-height: var(--text-h3-line);
  font-weight: 700;
  color: var(--ink);
}
.yeja-section__count {
  margin-inline-start: var(--space-1);
  font-family: var(--font-family-mono);
  font-size: var(--text-label-size);
  font-weight: 400;
  color: var(--muted);
}
.yeja-section__note {
  margin: 0;
  font-size: var(--text-body-size);
  color: var(--muted);
}

.yeja-row-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.yeja-row-list-item--top { align-items: flex-start; }
.yeja-row-list-lead { flex: 0 0 auto; }
.yeja-row-list-meta--mono { font-family: var(--font-family-mono); }
.yeja-row-list-body {
  margin: var(--space-1) 0 0;
  font-size: var(--text-body-size);
  color: var(--ink);
  overflow-wrap: anywhere;
}
.yeja-row-list-actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--space-1);
}
@media (max-width: 640px) {
  .yeja-row-list-item:has(.yeja-row-list-actions) { flex-wrap: wrap; }
  .yeja-row-list-item .yeja-row-list-actions { flex-basis: 100%; justify-content: flex-start; }
}

.yeja-stats {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--space-2);
}
.yeja-stat {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  padding: var(--space-2);
  border-radius: var(--radius-control);
  background: var(--surface-alt);
}
.yeja-stat__label {
  font-family: var(--font-family-mono);
  font-size: var(--text-label-size);
  color: var(--muted);
}
.yeja-stat__value {
  font-size: var(--text-h2-size);
  line-height: var(--text-h2-line);
  font-weight: 700;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.yeja-stat__note {
  font-size: var(--text-label-size);
  color: var(--muted);
}

.yeja-empty-state--page {
  text-align: center;
  font-size: 15.5px;
  padding: var(--space-6) 0;
}
.yeja-empty-state--page .yeja-empty-state__title {
  margin: 0 0 4px;
  font-weight: 600;
  color: var(--ink);
}
.yeja-empty-state--page p { margin: 0; }

.yeja-avatar {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-circle);
  overflow: hidden;
  background: var(--surface-alt);
  color: var(--muted);
  font-size: var(--text-body-size);
  font-weight: 700;
}
.yeja-avatar img { width: 100%; height: 100%; object-fit: cover; }
.yeja-avatar--sm { width: 28px; height: 28px; font-size: var(--text-label-size); }
.yeja-avatar--lg { width: 56px; height: 56px; font-size: var(--text-h3-size); }

@media (prefers-reduced-motion: reduce) {
  .yeja-tile__frame,
  .yeja-tile__actions { transition: none; }
  .yeja-tile:hover .yeja-tile__frame { transform: none; }
}

/* confirmDialog({ acknowledge }) — the tick an irreversible step asks for
 * before its confirm button works (guide §5 blocking confirmation order). */
.yeja-confirm-ack { display: flex; align-items: center; gap: 8px; margin-block: 4px 16px; font-size: 14px; }
.yeja-confirm-ack input { inline-size: 18px; block-size: 18px; }

/* ── AI draft button (apps/shared/ai-assist.js, 2026-11-01) ──
 * A ghost pill with the auto_awesome mark. It only ever fills a field with a
 * draft; the surface's own Save/Send stays the primary action. */
.yeja-ai-btn {
  font-size: 13px;
  padding: 0.35em 0.9em;
  gap: 6px;
  white-space: nowrap;
}
.yeja-ai-btn .yeja-icon { font-size: 16px; }
.yeja-ai-btn--compact { padding: 0.25em 0.7em; font-size: 12px; }
.yeja-ai-btn[aria-busy="true"] { cursor: progress; }
.yeja-ai-btn[aria-busy="true"] .yeja-icon { animation: yeja-ai-pulse 1.1s ease-in-out infinite; }
@keyframes yeja-ai-pulse { 50% { opacity: 0.35; transform: scale(0.85); } }
@media (prefers-reduced-motion: reduce) {
  .yeja-ai-btn[aria-busy="true"] .yeja-icon { animation: none; opacity: 0.6; }
}
.yeja-ai-btn__label { display: inline-flex; align-items: center; gap: 6px; }
.yeja-ai-btn__pro {
  font-size: 11px;
  font-weight: 600;
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  background: color-mix(in oklch, var(--yeja-cobalt) 14%, transparent);
  color: var(--yeja-cobalt-ink);
}
:root[data-theme="dark"] .yeja-ai-btn__pro { color: var(--yeja-cobalt); }
.yeja-ai-btn__pro[hidden] { display: none; }
/* A read-only AI result (a summary, an explanation) shown in place. */
.yeja-ai-note {
  border: 1px solid var(--line);
  border-radius: var(--radius-card, 12px);
  padding: var(--space-2);
  background: var(--surface);
  color: var(--ink);
  display: grid;
  gap: var(--space-1);
  font-size: 14px;
  line-height: 1.5;
}
.yeja-ai-note[hidden] { display: none; }
.yeja-ai-note__head { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
.yeja-ai-note__head .yeja-icon { font-size: 16px; }
.yeja-ai-note :is(ul, ol) { margin: 0; padding-inline-start: 1.2em; display: grid; gap: 4px; }
.yeja-ai-note p { margin: 0; }
.yeja-ai-note__foot { font-size: 12px; color: var(--muted); }
.yeja-ai-note__link { color: var(--ink); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; border-radius: 4px; }
.yeja-ai-note__detail { color: var(--muted); }
[dir="rtl"] .yeja-ai-note p, [dir="rtl"] .yeja-ai-note li { unicode-bidi: plaintext; }

/* Search by meaning sheet (apps/shared/ai-assist.js openSearchSheet). */
.yeja-ai-search { display: grid; gap: var(--space-2); }
.yeja-ai-search__title { margin: 0; font-size: 20px; }
.yeja-ai-search__row { display: flex; gap: var(--space-1); }
.yeja-ai-search__row input { flex: 1; min-width: 0; }
.yeja-ai-search__hint { margin: 0; font-size: 13px; color: var(--muted); }
.yeja-ai-search__index { background: none; border: 0; padding: 0; color: var(--ink); text-decoration: underline; text-underline-offset: 2px; font: inherit; cursor: pointer; border-radius: 4px; }
.yeja-ai-search__results { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-1); }
.yeja-ai-search__hit { width: 100%; display: grid; gap: 2px; text-align: start; background: var(--surface); color: var(--ink); border: 1px solid var(--line); border-radius: var(--radius-card); padding: var(--space-1) var(--space-2); cursor: pointer; font: inherit; }
.yeja-ai-search__hit:hover { border-color: var(--muted); }
.yeja-ai-search__hit-title { font-weight: 600; }
.yeja-ai-search__hit-snippet { font-size: 13px; color: var(--muted); }
.yeja-ai-search__empty { font-size: 14px; color: var(--muted); }
