/* =============================================================
   BEYLI SHARED COMPONENTS
   Real CSS classes for every reusable component in components/**
   and ui_kits/beyli-app/Shared.jsx, so per-screen HTML files never
   redefine these — they just apply the class. Load after tokens.css
   and base.css. One component per section, each documented with
   its source file and the prop-driven variants it replaces.
   ============================================================= */

/* =============================================================
   Component: Button          (components/actions/Button.jsx)
   Variants: .btn--primary (default) .btn--secondary .btn--ghost .btn--approve
   .btn--waiting .btn--danger
   Sizes:    .btn--sm .btn--md (default) .btn--lg
   States:   [disabled] .btn--loading
   ============================================================= */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 40px;
  padding: 0 20px;
  border: none;
  border-radius: var(--radius-pill);
  font: 600 13px/1 var(--font-body);
  cursor: pointer;
  white-space: nowrap;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  background: var(--beyli-yellow);
  color: var(--beyli-indigo);
  transition: background var(--dur-fast), transform var(--dur-fast) var(--ease-snap), box-shadow var(--dur-fast);
}
.btn:hover { background: var(--beyli-yellow-hover); }
.btn:active { background: var(--beyli-yellow-press); transform: scale(0.97); }
.btn:hover { box-shadow: var(--shadow-yellow-glow); }
.btn:focus-visible { box-shadow: var(--focus-ring); outline: none; }
/* [disabled] and [aria-disabled] look identical but behave differently, and
   the difference is the point. A truly [disabled] button fires no click event,
   so it cannot tell anyone why it is unavailable — the user taps and gets
   silence. Where a control is blocked by a precondition the user can satisfy,
   use aria-disabled: it stays focusable and reachable, announces as disabled,
   and its handler can name the missing step. Reserve [disabled] for states
   with nothing to explain. */
.btn[disabled],
.btn[aria-disabled="true"] {
  background: var(--surface-sunken);
  color: var(--ink-4);
  cursor: not-allowed;
  box-shadow: none;
}
.btn[aria-disabled="true"]:hover { box-shadow: none; }
.btn[aria-busy="true"] { pointer-events: none; opacity: .72; }
.btn[aria-busy="true"]::before { content: ''; width: 13px; height: 13px; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: beyli-spin .7s linear infinite; }
.btn--full { width: 100%; }

.btn--sm { height: 32px; padding: 0 16px; font-size: 12px; }
.btn--lg { height: 48px; padding: 0 26px; font-size: 15px; }

.btn--secondary { background: var(--beyli-indigo); color: #fff; }
.btn--secondary:hover { background: var(--beyli-indigo-hover); }
.btn--secondary:active { background: var(--beyli-indigo-press); }

.btn--ghost {
  background: transparent;
  color: var(--ink-1);
  border: 1.5px solid var(--line-1);
}
.btn--ghost:hover { background: var(--surface-sunken); }
.btn--ghost:active { background: var(--line-2); }

.btn--approve { background: var(--color-success); color: #fff; }
.btn--approve:hover { background: oklch(0.53 0.14 155); }
.btn--approve:active { background: oklch(0.47 0.13 155); }

/* Waiting or review work needs attention, but is not destructive. */
.btn--waiting,
.btn--warning { background: #e69018; color: #fff; }
.btn--waiting:hover,
.btn--warning:hover { background: #bd7110; }
.btn--waiting:active,
.btn--warning:active { background: #98580a; }

/* Reserve red for actions that remove, cancel, block, revoke, or reject. */
.btn--danger { background: var(--color-danger); color: #fff; }
.btn--danger:hover { background: oklch(0.52 0.20 25); }
.btn--danger:active { background: oklch(0.46 0.20 25); }

.btn__spinner {
  width: 1em;
  height: 1em;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: beyli-spin 0.7s linear infinite;
}

/* =============================================================
   Component: IconButton      (components/actions/IconButton.jsx)
   Variants: .icon-btn--ghost (default) .icon-btn--solid .icon-btn--yellow .icon-btn--glass
   Sizes:    .icon-btn--sm .icon-btn--md (default) .icon-btn--lg
   ============================================================= */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  cursor: pointer;
  flex: 0 0 auto;
  -webkit-tap-highlight-color: transparent;
  background: transparent;
  color: var(--ink-1);
  border: 1.5px solid var(--line-1);
  transition: background var(--dur-fast), transform var(--dur-fast) var(--ease-snap);
}
.icon-btn:hover { background: var(--surface-sunken); }
.icon-btn:active { transform: scale(0.94); }
.icon-btn[disabled] { background: var(--surface-sunken); color: var(--ink-4); cursor: not-allowed; }
.icon-btn:focus-visible { box-shadow: var(--focus-ring); outline: none; }

.icon-btn--sm { width: 32px; height: 32px; }
.icon-btn--lg { width: 48px; height: 48px; }

.icon-btn--solid { background: var(--beyli-indigo); color: #fff; border: none; }
.icon-btn--solid:hover { background: var(--beyli-indigo-hover); }

.icon-btn--yellow { background: var(--beyli-yellow); color: var(--beyli-indigo); border: none; }
.icon-btn--yellow:hover { background: var(--beyli-yellow-hover); }

/* Was a flat, borderless dark circle -- readable over video but with no
   depth, indistinguishable from a plain background chip. A visible rim
   plus a real drop shadow is what actually reads as "glass sitting on
   top of the video" rather than "dark spot painted onto it"; used on
   every live room's overlay controls (mute, flip camera, share, more)
   on both surfaces. */
.icon-btn--glass {
  background: rgba(18, 10, 41, 0.45);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.16);
  backdrop-filter: var(--blur-glass);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
.icon-btn--glass:hover { background: rgba(18, 10, 41, 0.65); border-color: rgba(255, 255, 255, 0.26); }
.icon-btn--glass:active { background: rgba(18, 10, 41, 0.78); }
.icon-btn--glass[aria-pressed="true"] { background: var(--beyli-yellow); color: var(--beyli-indigo); border-color: var(--beyli-yellow); }

/* =============================================================
   Component: AuctionTimer    (components/commerce/AuctionTimer.jsx)
   Countdown pill: indigo normally, live-red + pulse when <= urgentAt seconds.
   Sizes: .timer--md (default) .timer--lg
   ============================================================= */
.timer {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 32px;
  padding: 0 12px;
  border-radius: var(--radius-pill);
  background: var(--beyli-indigo);
  color: #fff;
  font: 600 14px var(--font-mono);
}
.timer--lg { height: 44px; padding: 0 18px; font-size: 20px; }
.timer--urgent {
  background: var(--beyli-live);
  animation: beyli-pulse 1s infinite;
}

/* =============================================================
   Component: BidBar          (components/commerce/BidBar.jsx)
   The one-tap live-auction bid control: current price + single yellow
   bid button. ALWAYS on a dark surface (--dark-elevated).
   ============================================================= */
.bid-bar {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 16px;
  background: var(--dark-elevated);
  border-radius: var(--radius-lg);
}
.bid-bar__info { flex: 1; min-width: 0; }
.bid-bar__label {
  font: var(--text-overline);
  letter-spacing: var(--tracking-overline);
  text-transform: uppercase;
  color: var(--dark-ink-3);
}
.bid-bar__price {
  font: 700 26px var(--font-mono);
  color: #fff;
  animation: beyli-pop var(--dur-base) var(--ease-snap);
}
.bid-bar__cta {
  height: 48px;
  padding: 0 26px;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--beyli-yellow);
  color: var(--beyli-indigo);
  font: 700 15px var(--font-body);
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-snap), box-shadow var(--dur-fast);
}
.bid-bar__cta--flash { box-shadow: var(--shadow-yellow-glow); transform: scale(1.05); }

/* =============================================================
   Component: LiveBadge       (components/commerce/LiveBadge.jsx)
   Sizes: .live-badge--md (default) .live-badge--sm
   ============================================================= */
.live-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 26px;
  padding: 0 11px;
  border-radius: var(--radius-pill);
  background: var(--beyli-live);
  color: #fff;
  font: 700 11px/1 var(--font-body);
  letter-spacing: 0.06em;
}
.live-badge--sm { height: 20px; padding: 0 8px; font-size: 10px; }
.live-badge__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #fff;
  animation: beyli-pulse 1.4s infinite;
}
.live-badge--sm .live-badge__dot { width: 5px; height: 5px; }
.live-badge__viewers { font-family: var(--font-mono); font-weight: 600; }

/* =============================================================
   Component: ProductCard     (components/commerce/ProductCard.jsx)
   A product tile: image + live/badge/sold overlay + title/price/seller.
   ============================================================= */
.product-card {
  background: var(--surface-card);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-md);
  overflow: hidden;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--dur-base), transform var(--dur-base) var(--ease-out);
}
.product-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.product-card__media {
  position: relative;
  aspect-ratio: 1;
  background: repeating-linear-gradient(45deg, var(--surface-sunken) 0 12px, var(--line-2) 12px 24px);
}
.product-card__media img { width: 100%; height: 100%; object-fit: cover; }
.product-card__media--live { box-shadow: inset 0 0 0 3px var(--beyli-yellow); }
.product-card__live {
  position: absolute;
  top: 10px;
  inset-inline-start: 10px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 22px;
  padding: 0 9px;
  background: var(--beyli-live);
  color: #fff;
  border-radius: var(--radius-pill);
  font: 700 10px/1 var(--font-body);
  letter-spacing: 0.06em;
}
.product-card__badge {
  position: absolute;
  top: 10px;
  inset-inline-start: 10px;
  height: 22px;
  padding: 0 9px;
  display: inline-flex;
  align-items: center;
  background: var(--beyli-indigo);
  color: #fff;
  border-radius: var(--radius-pill);
  font: 700 10px/1.2 var(--font-body);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.product-card__sold {
  position: absolute;
  inset: 0;
  background: var(--surface-overlay);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font: 800 20px var(--font-display);
  letter-spacing: -0.02em;
}
.product-card__body { padding: 12px; display: flex; flex-direction: column; gap: 6px; }
.product-card__title { font: var(--text-title); color: var(--ink-1); }
.product-card__title,
.product-card__seller { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.product-card__row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
/* price:flex-none so a 3-digit price never shrinks or wraps -- it's
   the one thing here that has to stay a single readable number.
   seller:min-width:0 is what actually lets its ellipsis (set above)
   do its job: a flex item's default min-width is auto, which refuses
   to shrink below the handle's own full-text width and made the row
   wrap instead of truncating, at any grid width narrow enough for the
   card ("@kenzo.cards" beside a 3-digit price broke this in the mobile
   2-up grid at 390px AND the desktop 3-up grid around 800px -- the
   existing max-width:768px stacking fix below only ever covered the
   first case). Now the seller name just truncates to fit beside
   whatever the price needs, on one line, at every width. */
.product-card__price { font: var(--text-price); color: var(--ink-1); flex: none; white-space: nowrap; }
.product-card__seller { font: var(--text-caption); color: var(--ink-3); min-width: 0; flex: 1 1 auto; text-align: end; }

/* =============================================================
   Component: Avatar          (components/display/Avatar.jsx)
   Circular initials/photo avatar. Sizes are set inline via
   `--avatar-size` since the source component takes an arbitrary
   pixel size prop rather than a fixed set of variants.
   Add `.avatar--live` for the yellow live-ring + LIVE tag.
   ============================================================= */
.avatar {
  --avatar-size: 40px;
  position: relative;
  display: inline-block;
  width: var(--avatar-size);
  height: var(--avatar-size);
  flex-shrink: 0;
}
.avatar__circle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  overflow: hidden;
  background: var(--beyli-indigo);
  color: var(--beyli-yellow);
  font: 700 calc(var(--avatar-size) * 0.38) var(--font-display);
}
.avatar__circle img { width: 100%; height: 100%; object-fit: cover; }
.avatar--live .avatar__circle {
  box-shadow: 0 0 0 2px var(--surface-card), 0 0 0 4.5px var(--beyli-yellow);
}
.avatar__live-tag {
  position: absolute;
  bottom: -4px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--beyli-live);
  color: #fff;
  font: 700 8px/1 var(--font-body);
  letter-spacing: 0.06em;
  padding: 3px 6px;
  border-radius: var(--radius-pill);
}

/* =============================================================
   Component: Badge           (components/display/Badge.jsx)
   Kinds: .badge--neutral (default) .badge--yellow .badge--violet
          .badge--success .badge--danger .badge--indigo
   ============================================================= */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 22px;
  padding: 0 9px;
  border-radius: var(--radius-pill);
  font: var(--text-overline);
  letter-spacing: var(--tracking-overline);
  text-transform: uppercase;
  background: var(--surface-sunken);
  color: var(--ink-2);
}
.badge--yellow { background: var(--beyli-yellow-soft); color: var(--color-warning); }
.badge--violet { background: var(--beyli-violet-soft); color: var(--beyli-violet); }
.badge--success { background: var(--color-success-soft); color: var(--color-success); }
.badge--danger { background: var(--color-danger-soft); color: var(--color-danger); }
.badge--indigo { background: var(--beyli-indigo); color: #fff; }

/* =============================================================
   Component: Card            (components/display/Card.jsx)
   Generic elevated surface. Add `.card--interactive` for hover lift.
   ============================================================= */
.card {
  background: var(--surface-card);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-md);
  padding: 16px;
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--dur-base), transform var(--dur-base) var(--ease-out);
}
.card--interactive { cursor: pointer; }
.card--interactive:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }

/* =============================================================
   Component: Tabs            (components/display/Tabs.jsx)
   Variants: .tabs--underline (default) .tabs--pill
   ============================================================= */
.tabs { display: flex; gap: 4px; border-bottom: 1.5px solid var(--line-2); }
.tabs--pill { gap: 8px; border-bottom: none; }
.tabs__tab {
  height: 40px;
  padding: 0 14px;
  border: none;
  background: none;
  border-bottom: 2.5px solid transparent;
  margin-bottom: -1.5px;
  color: var(--ink-3);
  font: 600 14px var(--font-body);
  cursor: pointer;
  transition: all var(--dur-fast);
}
/* Three selectors, because these tabs are two different things. A real
   tablist marks the current tab with aria-selected; a nav row of links marks
   it with aria-current="page" and carries a --active modifier. Only the
   first was styled, so on Commerce Operations, Categories and Users the
   current tab was pixel-identical to the other four and the operator could
   not tell which workspace they were in. */
.tabs__tab[aria-selected="true"],
.tabs__tab[aria-current="page"],
.tabs__tab--active { border-bottom-color: var(--beyli-yellow); color: var(--ink-1); }
.tabs--pill .tabs__tab {
  height: 36px;
  padding: 0 16px;
  border-radius: var(--radius-pill);
  margin-bottom: 0;
}
.tabs--pill .tabs__tab[aria-selected="true"] { background: var(--beyli-indigo); color: #fff; }

/* =============================================================
   Component: Tag             (components/display/Tag.jsx)
   Filter/category chip. Add `.tag--selected` for the active state.
   ============================================================= */
.tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 34px;
  padding: 0 14px;
  border-radius: var(--radius-pill);
  background: var(--surface-card);
  color: var(--ink-1);
  border: 1.5px solid var(--line-1);
  font: 600 13px var(--font-body);
  cursor: pointer;
  transition: all var(--dur-fast);
}
.tag:hover { background: var(--surface-sunken); }
.tag--selected,
.tag--selected:hover {
  background: var(--beyli-indigo);
  color: #fff;
  border-color: var(--beyli-indigo);
}
.tag__remove { font-size: 14px; line-height: 1; opacity: 0.7; }

/* =============================================================
   Component: Dialog           (components/feedback/Dialog.jsx)
   ============================================================= */
.dialog-overlay {
  position: fixed;
  inset: 0;
  background: var(--surface-overlay);
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.dialog {
  width: 440px;
  max-width: 100%;
  max-height: 85vh;
  overflow: auto;
  background: var(--surface-card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 24px;
  animation: beyli-pop var(--dur-slow) var(--ease-snap);
}
.dialog__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.dialog__title { font: var(--text-h3); letter-spacing: var(--tracking-display); color: var(--ink-1); }
.dialog__close {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-sunken);
  color: var(--ink-2);
  font-size: 16px;
  cursor: pointer;
}
.dialog__body { font: var(--text-body-md); color: var(--ink-2); }
.dialog__actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; }

/* =============================================================
   Component: Loader / Skeleton  (components/feedback/Loader.jsx)
   ============================================================= */
.loader { display: inline-flex; align-items: center; gap: 10px; }
.loader__spinner {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 3px solid var(--line-1);
  border-top-color: var(--beyli-violet);
  animation: beyli-spin 0.8s linear infinite;
}
.loader__label { font: var(--text-caption); color: var(--ink-3); }
.skeleton {
  display: block;
  width: 100%;
  height: 16px;
  border-radius: var(--radius-xs);
  background: var(--surface-sunken);
  animation: beyli-pulse 1.4s infinite;
}

/* =============================================================
   Component: Toast            (components/feedback/Toast.jsx)
   Kinds: .toast--success .toast--danger .toast--info (default) .toast--win
   ============================================================= */
.toast {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  width: 340px;
  padding: 14px 16px;
  background: var(--dark-card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  animation: beyli-pop var(--dur-slow) var(--ease-snap);
}
.toast__icon {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font: 700 13px var(--font-body);
  background: var(--beyli-violet);
}
.toast--success .toast__icon { background: var(--color-success); }
.toast--danger .toast__icon { background: var(--color-danger); }
.toast--win .toast__icon { background: var(--beyli-yellow); color: var(--beyli-indigo); }
.toast__title { font: 600 14px var(--font-body); color: #fff; }
.toast__desc { font: var(--text-caption); color: rgba(255, 255, 255, 0.72); margin-top: 2px; }
.toast__close { color: rgba(255, 255, 255, 0.48); font-size: 14px; background: none; border: none; cursor: pointer; }

/* =============================================================
   Component: Tooltip          (components/feedback/Tooltip.jsx)
   ============================================================= */
.tooltip { position: relative; display: inline-block; }
.tooltip__bubble {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--beyli-indigo);
  color: #fff;
  font: var(--text-caption);
  white-space: nowrap;
  padding: 6px 10px;
  border-radius: var(--radius-xs);
  box-shadow: var(--shadow-md);
  z-index: 50;
}

/* =============================================================
   Component: Checkbox / Radio / Switch  (components/forms/*.jsx)
   ============================================================= */
.checkbox, .radio, .switch {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  min-height: 24px;
}
.checkbox__box {
  width: 22px;
  height: 22px;
  border-radius: var(--radius-xs);
  border: 2px solid var(--ink-4);
  background: var(--surface-card);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: all var(--dur-fast);
}
.checkbox input:checked ~ .checkbox__box,
.checkbox--checked .checkbox__box {
  background: var(--beyli-indigo);
  border-color: var(--beyli-indigo);
  color: var(--beyli-yellow);
}
.radio__dot {
  position: relative;
  width: 22px;
  height: 22px;
  flex-shrink: 0;
}
.radio__ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid var(--ink-4);
  background: var(--surface-card);
  transition: all var(--dur-fast);
}
.radio input:checked ~ .radio__dot .radio__ring,
.radio--checked .radio__ring { border-color: var(--beyli-indigo); }
.radio input:checked ~ .radio__dot::after,
.radio--checked .radio__dot::after {
  content: "";
  position: absolute;
  inset: 6px;
  border-radius: 50%;
  background: var(--beyli-indigo);
}
/* The switch input was marked `hidden`, which does not just hide it — it
   removes it from the accessibility tree AND from tab order. notifsettings
   had 23 toggles and exactly ONE tabbable element on the whole screen: the
   settings were reachable by mouse only, and a screen reader was told
   nothing was there at all. Clipping keeps the control real — focusable,
   announced, operable by Space — while showing the track instead. */
.switch input[type="checkbox"] {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0); clip-path: inset(50%);
  overflow: hidden; white-space: nowrap;
}
/* The input is clipped, so the focus ring has to be drawn on what is
   actually visible, or keyboard users cannot see where they are. */
.switch input[type="checkbox"]:focus-visible ~ .switch__track {
  outline: 3px solid var(--beyli-yellow);
  outline-offset: 2px;
}
.switch__track {
  width: 44px;
  height: 26px;
  border-radius: var(--radius-pill);
  background: var(--ink-4);
  padding: 3px;
  display: flex;
  justify-content: flex-start;
  transition: background var(--dur-base);
}
/* Two kinds of switch share these styles: one wraps a real checkbox, one is
   a plain div toggled by catalog-workspace.js. The class used to win for
   both, which pinned the checkbox variant's appearance at page load — fine
   while the input was hidden and unreachable, wrong the moment it became
   focusable, because Space would toggle the control and nothing would move.
   :checked now drives anything with an input; the class drives the rest. */
.switch input:checked ~ .switch__track,
.switch--checked:not(:has(input[type="checkbox"])) .switch__track { background: var(--beyli-indigo); justify-content: flex-end; }
.switch__thumb {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-sm);
  transition: all var(--dur-base) var(--ease-snap);
}
.switch input:checked ~ .switch__track .switch__thumb,
.switch--checked:not(:has(input[type="checkbox"])) .switch__thumb { background: var(--beyli-yellow); }
.checkbox__label, .radio__label, .switch__label { font: var(--text-body-md); color: var(--ink-1); }
.checkbox[data-disabled], .radio[data-disabled], .switch[data-disabled] { cursor: not-allowed; opacity: 0.45; }

/* =============================================================
   Component: Input / Select   (components/forms/Input.jsx, Select.jsx)
   ============================================================= */
.field { display: flex; flex-direction: column; gap: 6px; }
.field__label { font: var(--text-label); color: var(--ink-1); }
.field__control {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 44px;
  padding: 0 14px;
  background: var(--surface-card);
  border: 1.5px solid var(--line-1);
  border-radius: var(--radius-sm);
  transition: border-color var(--dur-fast), box-shadow var(--dur-fast);
}

/* .field__control is a fixed 44px row, which is right for an input and wrong
   for a textarea: a rows="4" composer rendered at 44px and clipped its own
   content, so you could not see what you were typing. The modifier lets the
   box grow and puts the caret at the top instead of vertically centred. */
.field__control--textarea {
  height: auto;
  min-height: 104px;
  align-items: stretch;
  padding: 10px 14px;
  line-height: 1.5;
  resize: vertical;
}
.field__control:focus-within { border-color: var(--beyli-violet); box-shadow: var(--focus-ring); }
.field__control input:focus-visible,
.field__control select:focus-visible,
.field__control textarea:focus-visible { outline: none; box-shadow: none; }
.field--error .field__control { border-color: var(--color-danger); }
/* textarea belongs here with input and select. Left out, it kept the HTML
   default cols="20" and rendered 168px wide inside a 652px control — a
   letterbox with empty space beside it. flex:1 is what makes the others
   fill the row; a textarea needs it for the same reason. */
.field__control input,
.field__control select,
.field__control textarea {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: none;
  font: var(--text-body-md);
  color: var(--ink-1);
}
.field__icon { color: var(--ink-3); display: flex; }
.field__hint { font: var(--text-caption); color: var(--ink-3); }
.field--error .field__hint { color: var(--color-danger); }
.field__select-wrap { position: relative; }
.field__select-caret {
  position: absolute;
  inset-inline-end: 14px;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
  color: var(--ink-3);
  font-size: 12px;
}
select.field__control { appearance: none; width: 100%; cursor: pointer; }

/* =============================================================
   Component: Header / Nav / Pill   (ui_kits/beyli-app/Shared.jsx)
   Mobile-only chrome: sticky top bar, bottom tab bar, small status pill.
   ============================================================= */
.beyli-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 16px;
  background: var(--surface-page);
  position: sticky;
  top: 0;
  z-index: 5;
  flex-shrink: 0;
}
.beyli-header__back {
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  background: var(--surface-sunken);
  color: var(--ink-1);
  font-size: 15px;
  border: none;
  cursor: pointer;
}
.beyli-header__title {
  flex: 1;
  font: 700 18px var(--font-body);
  letter-spacing: -0.01em;
  color: var(--ink-1);
}

.beyli-nav {
  display: flex;
  border-top: 1px solid var(--line-2);
  background: var(--surface-card);
  padding: 6px 0 18px;
  flex-shrink: 0;
}
.beyli-nav__item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 6px 0;
  color: var(--ink-3);
  background: none;
  border: none;
  cursor: pointer;
  font: 600 10px var(--font-body);
}
.beyli-nav__item--active { color: var(--beyli-indigo); font-weight: 700; }

/* The tab bar carries the whole app, so it should read at a glance rather than
   whisper. Icons were inheriting a body-text size and sat smaller than the
   labels under them, which inverts the hierarchy — the glyph is what you aim
   at, the word only confirms it. */
.beyli-nav__item .icon,
.beyli-nav__item svg {
  width: 16px;
  height: 16px;
  stroke-width: 1.9;
}
.beyli-nav__item {
  font: 600 10.5px var(--font-body);
  letter-spacing: 0.01em;
  gap: 4px;
}
/* The active tab thickens its stroke rather than only changing colour, so it
   still reads for anyone who cannot separate indigo from grey. */
.beyli-nav__item--active .icon,
.beyli-nav__item--active svg { stroke-width: 2.4; }
/* A quiet lift on the active glyph, the way the native bars do it. */
.beyli-nav__item--active .icon,
.beyli-nav__item--active svg { transform: translateY(-1px); }

.pill {
  display: inline-flex;
  align-items: center;
  background: var(--surface-sunken);
  color: var(--ink-2);
  border-radius: var(--radius-pill);
  padding: 3px 10px;
  font: 700 11px var(--font-body);
}
.pill--success { background: rgba(26, 127, 78, 0.12); color: var(--color-success); }
.pill--danger, .pill--live { background: rgba(229, 72, 77, 0.12); color: var(--beyli-live); }
.pill--warn { background: rgba(255, 199, 0, 0.22); color: #8a6d00; }
.pill--indigo { background: rgba(42, 26, 110, 0.1); color: var(--beyli-indigo); }

/* =============================================================
   Icon placeholder — every icon glyph in these screens is a Lucide
   icon (`data-lucide="name"`), left as a semantic placeholder rather
   than hand-traced to SVG: a real React conversion drops in
   `lucide-react` and these tags become `<IconName />` almost as-is.
   ============================================================= */
.icon {
  width: 1em;
  height: 1em;
  display: inline-block;
  flex-shrink: 0;
  vertical-align: -0.15em;
}

/* =============================================================
   Component: GlobalNav        (ui_kits/beyli-web/GlobalNav.jsx)
   Sticky top bar used by every web/ screen: logo, search, primary
   links, auth actions.
   ============================================================= */
.global-nav {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--surface-card);
  border-bottom: 1px solid var(--line-2);
}
.global-nav__inner {
  max-width: var(--page-max);
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 24px;
}
.global-nav__logo { height: 34px; cursor: pointer; }
.global-nav__search { flex: 1; max-width: 680px; }
/* Order: logo · switch · Live/Marketplace/Deals · search · Invite & earn · icons.
   The link row sits before the search so the field runs straight into the
   account actions with nothing between. */
.global-nav__links { display: flex; gap: 14px; font: 600 13px var(--font-body); color: var(--ink-2); order: 1; }
.global-nav__search { order: 2; min-width: 355px; }
@media (max-width: 1279px) { .global-nav__search { min-width: 0; } }
.global-nav__menu, .global-nav__actions { order: 3; }
.global-nav__links a { color: inherit; }
.global-nav__referral { color: var(--beyli-violet) !important; display: inline-flex; align-items: center; gap: 5px; }
/* nav.js moves Invite & earn out of the link row and in front of Messages,
   so the search field can take the room the link row gave up. */
.global-nav__actions .global-nav__referral {
  position: relative; overflow: hidden; isolation: isolate;
  font: 700 12px var(--font-body); white-space: nowrap; min-height: 34px; padding: 0 10px 0 8px; margin-inline-end: 2px;
  border-radius: 999px; background: var(--beyli-yellow, #FFD218); color: #17102E !important;
  box-shadow: 0 2px 8px rgba(255, 210, 24, .35);
}
/* A soft shine sweeps across every few seconds so the pill reads as a
   reward, not a menu item. Off for anyone who asked for less motion. */
.global-nav__actions .global-nav__referral::after {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(115deg, transparent 30%, rgba(255,255,255,.75) 50%, transparent 70%);
  transform: translateX(-120%); animation: nav-referral-shine 3.2s ease-in-out infinite;
}
@keyframes nav-referral-shine { 0%, 55% { transform: translateX(-120%); } 85%, 100% { transform: translateX(120%); } }
@media (prefers-reduced-motion: reduce) { .global-nav__actions .global-nav__referral::after { animation: none; } }
.global-nav__actions .global-nav__referral:hover { filter: brightness(1.04); }
.global-nav__actions { display: flex; gap: 6px; align-items: center; }

/* =============================================================
   Component: Footer           (ui_kits/beyli-web/Footer.jsx)
   ALWAYS dark, closes every web/ screen.
   ============================================================= */
.site-footer { background: var(--dark-page); margin-top: 48px; }
.site-footer__inner { max-width: var(--page-max); margin: 0 auto; padding: 40px 24px 28px; }
.site-footer__grid { display: grid; grid-template-columns: 1.4fr repeat(4, 1fr); gap: 28px; }
.site-footer__brand { display: flex; flex-direction: column; gap: 14px; }
.site-footer__logo { height: 36px; align-self: flex-start; }
.site-footer__tagline { font: var(--text-body-md); color: var(--dark-ink-2); max-width: 240px; }
.site-footer__socials { display: flex; gap: 10px; }
.site-footer__social {
  width: 38px;
  height: 38px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--dark-line);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
}
.site-footer__col-head {
  font: var(--text-overline);
  letter-spacing: var(--tracking-overline);
  text-transform: uppercase;
  color: var(--dark-ink-3);
  margin-bottom: 12px;
}
.site-footer__col-links { display: flex; flex-direction: column; gap: 9px; }
.site-footer__col-links a { font: 500 13px var(--font-body); color: var(--dark-ink-2); }
.site-footer__bottom {
  border-top: 1px solid var(--dark-line);
  margin-top: 30px;
  padding-top: 18px;
  display: flex;
  justify-content: space-between;
  font: var(--text-caption);
  color: var(--dark-ink-3);
}

/* =============================================================
   Component: ShellPage / ShellSide / ShellCenter / ShellDoc
   (ui_kits/beyli-web-parity/Shells.jsx)
   Layout shells reused across most web/ screens.
   ============================================================= */
.shell-page { max-width: 1040px; margin: 0 auto; padding: 28px 24px 56px; }
.shell-page__head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 20px; }
.shell-page__title { font: var(--text-h1); font-size: 30px; letter-spacing: -0.02em; color: var(--ink-1); }
.shell-page__sub { font: var(--text-body-md); color: var(--ink-2); margin-top: 6px; }
.shell-page__actions { display: flex; gap: 10px; flex-shrink: 0; }

.shell-side {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 28px 24px 56px;
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  gap: 32px;
  align-items: start;
}
.shell-side__nav { position: sticky; top: 76px; display: flex; flex-direction: column; gap: 2px; }
.shell-side__nav-label {
  font: var(--text-overline);
  text-transform: uppercase;
  letter-spacing: var(--tracking-overline);
  color: var(--ink-3);
  padding: 4px 12px 8px;
}
.shell-side__nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border-radius: var(--radius-sm);
  font: 500 13px var(--font-body);
  color: var(--ink-2);
  background: transparent;
  border: none;
  cursor: pointer;
  text-align: start;
  width: 100%;
}
.shell-side__nav-item--active { background: var(--surface-sunken); color: var(--ink-1); font-weight: 700; }
.shell-side__nav-item--nested { padding-inline-start: 28px; font-size: 12px; }
.shell-side__head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 20px; }
.shell-side__head > :first-child { min-width: 0; }
.shell-side__title { font: var(--text-h1); font-size: 26px; line-height: 1.08; letter-spacing: -0.02em; color: var(--ink-1); }
.shell-side__sub { max-width: 62ch; font: var(--text-body-md); color: var(--ink-2); margin-top: 6px; }

/* A page heading is the visual anchor for every admin workspace.  Keeping
   actions centred to the title block and allowing them to wrap prevents the
   page from jumping or compressing when a screen has a longer description. */
.admin-shell .shell-side__head { min-height: 72px; padding-block: 2px; }
.admin-shell .shell-page__actions { align-self: center; flex-wrap: wrap; justify-content: flex-end; }
.admin-shell .shell-page__actions .btn { min-height: 40px; }

.shell-center { min-height: calc(100vh - 260px); display: flex; align-items: center; justify-content: center; padding: 48px 24px; }
.shell-center__card {
  /* width:460px + max-width:100% reads as safe but is circular inside a
     shrink-to-fit flex parent: the card demanded 460px, .shell-center grew
     to 508px to hold it, and then 100% resolved against that inflated
     parent instead of the viewport. At 390px the card ran 134px off-screen
     on maintenance, notfound, offline and update — every screen a user hits
     when something has already gone wrong. min() never demands more than
     the space available, so the parent has nothing to grow to. */
  width: min(460px, 100%);
  background: var(--surface-card);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  padding: 32px;
}
.shell-center__icon {
  width: 52px;
  height: 52px;
  border-radius: 16px;
  background: var(--surface-sunken);
  color: var(--ink-2);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 18px;
}
.shell-center__icon--yellow { background: var(--beyli-yellow); color: var(--beyli-indigo); }

.shell-doc { max-width: 720px; margin: 0 auto; padding: 40px 24px 72px; }
.shell-doc__title { font: var(--text-h1); font-size: 32px; letter-spacing: -0.02em; color: var(--ink-1); }
.shell-doc__updated { font: 500 12px var(--font-mono); color: var(--ink-3); margin-top: 10px; }
.shell-doc__body { margin-top: 26px; font: var(--text-body-lg); color: var(--ink-2); line-height: 1.7; }

/* =============================================================
   Component: SectionW / RowW / StatW / TableW / EmptyW
   (ui_kits/beyli-web-parity/Shells.jsx) — card-list building blocks
   used throughout the seller/account web screens.
   ============================================================= */
.section-w {
  background: var(--surface-card);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  margin-bottom: 16px;
  overflow: hidden;
}
.section-w__head { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px; border-bottom: 1px solid var(--line-2); }
.section-w__title { font: 700 14px var(--font-body); color: var(--ink-1); }

.row-w {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 18px;
  border-bottom: 1px solid var(--line-2);
}
.row-w:last-child { border-bottom: none; }
/* .row-w--clickable is used on <button> as often as on <div> -- without
   this reset a button kept the browser's default appearance (a visible
   border and slightly different background), showing up as a stray box
   around every clickable row (Profile, Notifications, Language & region,
   Payment methods, ...) that the plain div rows (Email, Phone) never had. */
.row-w--clickable {
  width: 100%;
  border: none;
  background: none;
  font: inherit;
  text-align: start;
  color: inherit;
  cursor: pointer;
}
.row-w--clickable:hover { background: var(--surface-sunken); }
.row-w__icon {
  width: 36px;
  height: 36px;
  border-radius: 11px;
  background: var(--surface-sunken);
  color: var(--ink-2);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.row-w__icon--danger { background: var(--color-danger-soft); color: var(--color-danger); }
.row-w__body { flex: 1; min-width: 0; }
.row-w__title { font: 600 14px var(--font-body); color: var(--ink-1); }
.row-w__title--danger { color: var(--color-danger); }
.row-w__sub { font: 400 12.5px var(--font-body); color: var(--ink-3); margin-top: 2px; }
.row-w__chevron { color: var(--ink-3); }

.stat-w { background: var(--surface-card); border: 1px solid var(--line-2); border-radius: var(--radius-md); box-shadow: var(--shadow-sm); padding: 16px 18px; }
.stat-w__label { font: 600 12px var(--font-body); color: var(--ink-3); }
.stat-w__value { font: 700 26px var(--font-mono); font-variant-numeric: tabular-nums; color: var(--ink-1); margin: 6px 0 2px; }
.stat-w__delta { font: 600 12px var(--font-mono); color: var(--color-success); }
.stat-w__delta--down { color: var(--color-danger); }

.table-w { width: 100%; border-collapse: collapse; font: 500 13px var(--font-body); }
.table-w th {
  text-align: start;
  padding: 11px 18px;
  font: 700 11px var(--font-body);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-3);
  border-bottom: 1px solid var(--line-2);
}
.table-w td { padding: 13px 18px; border-bottom: 1px solid var(--line-2); color: var(--ink-2); vertical-align: middle; }
.table-w tr:last-child td { border-bottom: none; }

.empty-w { text-align: center; padding: 56px 24px; }
.empty-w__icon {
  width: 56px;
  height: 56px;
  border-radius: 18px;
  background: var(--surface-sunken);
  color: var(--ink-3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 16px;
}
.empty-w__title { font: 700 16px var(--font-body); color: var(--ink-1); }
.empty-w__sub { font: 400 13.5px var(--font-body); color: var(--ink-3); margin: 6px auto 0; max-width: 340px; }
.empty-w__cta { margin-top: 18px; }

/* ---------- Currency ----------
   The UAE dirham symbol, introduced by the Central Bank in 2025. No font
   in this system carries the glyph — Hanken Grotesk, Bricolage Grotesque
   and Spline Sans Mono all predate it — so a character would render as
   tofu. It ships as a masked SVG instead, which inherits `color` like
   text and stays crisp at any size.

   Inline-block with a logical margin rather than a flex wrapper, so
   dropping it into an existing price needs no change to the parent:

       <span class="product-card__price"><i class="currency"></i>142</span>

   Sized in `em` so it tracks the price beside it. Prices are monospace
   specifically so digits do not reflow while an auction ticks; the symbol
   has a fixed advance for the same reason.

   English writes symbol-then-amount, Arabic amount-then-symbol. Because
   the margin is logical, the same markup gives both — the order comes
   from `dir`, and the spacing follows it. */
/* Clipped, not hidden. display:none and visibility:hidden are both skipped
   by screen readers, which is the opposite of what this is for. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Thumb-reachable without redrawing the control.

   Three controls on the mobile purchase path were far under the 44x44 a
   thumb needs: cart's Remove at 39x15, checkout3's Change at 45x20, and the
   product back button at 34x34. Remove is destructive — a mis-tap deletes an
   item from the cart — and Change sits on the order review screen where a
   buyer corrects their address before paying.

   Measured, not assumed: checkout2's payment radios look worse at 13x13 and
   are FINE, because each sits inside a label that gives a 343x73 target. The
   difference is whether something larger carries the tap, and these three had
   nothing.

   An invisible centred ::after expands the hit area while the drawn control
   keeps its size and position, so nothing in the layout moves. */
.tap-safe { position: relative; }
.tap-safe::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100%, 44px);
  height: max(100%, 44px);
  transform: translate(-50%, -50%);
  /* Decoration only — it must never be read, and never paint over anything. */
  pointer-events: auto;
}

.currency {
  display: inline-block;
  width: 0.78em;
  height: 0.78em;
  background: currentColor;
  -webkit-mask: url("../../assets/dirham-symbol.svg") center / contain no-repeat;
          mask: url("../../assets/dirham-symbol.svg") center / contain no-repeat;
  margin-inline-end: 0.26em;
  transform: translateY(0.03em);
  flex: none;
}

/* ==========================================================================
   Mobile web layer
   --------------------------------------------------------------------------
   The web screens were built desktop-only — 91 of them, zero media queries —
   so a phone got the desktop layout overflowing off both edges (home.html was
   681px wide in a 390px viewport, 48 elements past the right edge).

   OPT-IN, deliberately. The global nav is shared markup on every screen, and
   collapsing it needs a menu to collapse INTO. Hiding the links everywhere
   would strand navigation on the 86 screens that have no drawer yet. A screen
   opts in with `data-responsive` on <html> once it has the markup to match, so
   this can land screen by screen without half-breaking the rest.
   ========================================================================== */
@media (max-width: 768px) {
  [data-responsive] .page-container { padding-inline: var(--gutter-mobile); }

  /* Nav: logo + search + menu. The desktop links and the referral pill move
     into the drawer — they are what overflowed by 119px and 292px. */
  [data-responsive] .global-nav__inner { gap: 10px; padding: 10px var(--gutter-mobile); }
  [data-responsive] .global-nav__links,
  [data-responsive] .global-nav__referral { display: none; }
  [data-responsive] .global-nav__search { max-width: none; min-width: 0; }
  [data-responsive] .global-nav__actions { gap: 6px; flex: none; }
  [data-responsive] .global-nav__logo { height: 28px; }
  /* Buttons wrapped to two lines at desktop sizes; keep them on one. */
  [data-responsive] .global-nav__actions .btn { white-space: nowrap; padding: 8px 12px; font-size: 13px; }

  [data-responsive] .site-footer__grid { grid-template-columns: 1fr 1fr; gap: 20px; }

  /* Card rails: scroll horizontally inside their own box rather than pushing
     the page wide. Titles were collapsing to a single letter at 90px. */
  [data-responsive] .u-rail-mobile {
    display: flex; gap: 12px; overflow-x: auto; scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch; padding-bottom: 4px;
    scrollbar-width: none;
  }
  [data-responsive] .u-rail-mobile::-webkit-scrollbar { display: none; }
  [data-responsive] .u-rail-mobile > * { flex: 0 0 66%; scroll-snap-align: start; }

  /* Grids that were 4-5 across collapse to 2. */
  [data-responsive] .u-grid-mobile-2 { grid-template-columns: 1fr 1fr !important; gap: 12px !important; }
}

@media (max-width: 480px) {
  [data-responsive] .site-footer__grid { grid-template-columns: 1fr; }
  [data-responsive] .u-rail-mobile > * { flex: 0 0 78%; }
}

/* ---------- Mobile nav drawer ----------
   The counterpart to hiding .global-nav__links above. A screen that opts into
   `data-responsive` MUST carry this markup, or its navigation is unreachable
   on a phone — hiding the links without providing the drawer is the trap this
   pair exists to avoid. */
.global-nav__menu { display: none; }
.nav-drawer { display: none; }

@media (max-width: 768px) {
  [data-responsive] .global-nav__menu {
    display: inline-flex; align-items: center; justify-content: center;
    width: 38px; height: 38px; flex: none;
    border: 1px solid var(--line-2); border-radius: var(--radius-sm);
    background: none; cursor: pointer; color: var(--ink-1);
  }
  [data-responsive] .nav-drawer[data-open] {
    display: block; position: fixed; inset: 0; z-index: 80;
    background: var(--surface-card); overflow-y: auto;
    padding: 16px var(--gutter-mobile) calc(32px + env(safe-area-inset-bottom));
  }
  .nav-drawer__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 18px; }
  .nav-drawer__close {
    width: 38px; height: 38px; border: 1px solid var(--line-2);
    border-radius: var(--radius-sm); background: none; cursor: pointer;
    font-size: 20px; line-height: 1; color: var(--ink-1);
  }
  .nav-drawer__list { display: flex; flex-direction: column; gap: 2px; margin-bottom: 20px; }
  .nav-drawer__item {
    display: flex; align-items: center; justify-content: space-between;
    padding: 14px 4px; border-bottom: 1px solid var(--line-2);
    font: 600 15px var(--font-body); color: var(--ink-1);
    background: none; border-left: 0; border-right: 0; border-top: 0;
    width: 100%; text-align: start; cursor: pointer;
  }
  .nav-drawer__actions { display: flex; flex-direction: column; gap: 10px; }
  .nav-drawer__actions .btn { width: 100%; justify-content: center; }
}

/* Icon-only header controls (cart, notifications). Sized to the same 38px
   target as the mobile menu button so the actions cluster stays on one line. */
.global-nav__icon {
  width: 38px; height: 38px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-2); border-radius: var(--radius-sm);
  background: none; cursor: pointer; color: var(--ink-1);
}
.global-nav__icon:hover { background: var(--surface-subtle, rgba(0,0,0,.04)); }
.notif-badge { position: absolute; top: -4px; right: -4px; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px; background: var(--color-danger, #d64545); color: #fff; font: 700 10px/18px var(--font-body); text-align: center; pointer-events: none; }

/* Tablet gap. Between the mobile breakpoint and a comfortable desktop the
   full link row plus the actions cluster does not fit, and the header wrapped
   to two lines at ~800px. The referral pill is the widest optional item and
   the only one duplicated elsewhere, so it drops first and the row stays on
   one line. */
@media (min-width: 769px) and (max-width: 1000px) {
  .global-nav__referral { display: none; }
  .global-nav__inner { gap: 14px; }
}

/* ---------- Footer: always open, no click required ----------
   The footer carries 24 links in four groups. These used to be <details>,
   collapsed by default on a phone and requiring a tap to reveal each
   group's links. Now plain, always-visible groups on every width — the
   content is what a footer link section is for; requiring a tap to see it
   hid it from anyone who didn't already know to look. */
.site-footer__group > .site-footer__col-links { display: flex; }
.site-footer__group > .site-footer__col-head { cursor: default; }

@media (max-width: 768px) {
  .site-footer__inner { padding: 28px var(--gutter-mobile) 20px; }
  [data-responsive] .site-footer__grid,
  .site-footer__grid { grid-template-columns: 1fr; gap: 0; }

  .site-footer__group { border-top: 1px solid var(--dark-line); }
  .site-footer__group > .site-footer__col-head { padding: 14px 2px 8px; margin: 0; }
  .site-footer__group > .site-footer__col-links { padding: 2px 2px 14px; gap: 12px; }

  .site-footer__brand { gap: 12px; margin-bottom: 6px; }
  .site-footer__tagline { max-width: none; }
  .site-footer__bottom { flex-direction: column; align-items: flex-start; gap: 10px; }
}

/* ---------- Phone header: give the logo room ----------
   Adding cart and notifications pushed the actions cluster to 274px of a 390px
   viewport. Because that cluster does not shrink, everything else absorbed the
   loss: the logo collapsed to 16px and the search field to 0.

   On a phone the header keeps identity and the icon controls; Sign in and
   Start selling move to the drawer, where they already exist. */
@media (max-width: 768px) {
  [data-responsive] .global-nav__logo { height: 26px; min-width: 74px; object-fit: contain; object-position: left center; }
  [data-responsive] .global-nav__inner > a:first-child { flex: none; }

  /* Search collapses to its icon; the full field belongs to the search screen. */
  [data-responsive] .global-nav__search { flex: none; width: 38px; }
  [data-responsive] .global-nav__search .field__control {
    width: 38px; height: 38px; padding: 0; justify-content: center;
  }
  [data-responsive] .global-nav__search input { display: none; }

  /* Start selling moves to the drawer — it is a seller action and it is what
     crushed the logo. Sign in STAYS. Burying the only route to an account
     behind a hamburger costs a logged-out visitor a tap on the second most
     important thing they can do, after getting the app. */
  [data-responsive] .global-nav__actions .btn:not(.btn--ghost) { display: none; }
  [data-responsive] .global-nav__actions .btn--ghost {
    padding: 8px 14px; font-size: 13px; white-space: nowrap;
  }
  [data-responsive] .global-nav__actions { margin-inline-start: auto; }
}

/* A card label that also carries a management link (Queue → Manage,
   Chat → Moderation). The label keeps its own styling; the link sits at the
   end of the row. */
.livehost-card__label--row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.livehost-card__manage { font: 600 12px var(--font-body); color: var(--beyli-violet); text-transform: none; letter-spacing: 0; }

/* The account shell is a fixed 232px nav column beside the content. On a phone
   that leaves the content 46px — the panel does not overflow, it collapses,
   and its children then run off the edge. Stack it. This lives here rather
   than per-screen because .shell-side is shared across the account section. */
@media (max-width: 768px) {
  [data-responsive] .shell-side { grid-template-columns: 1fr !important; gap: 16px; }
  [data-responsive] .shell-side > * { min-width: 0; }
  [data-responsive] .account-nav { position: static !important; }
  /* Stacking alone still put a 12-item vertical list of full-width rows
     ahead of the page's actual content in document order -- on My
     Profile that's ~1200px of nav to scroll past before reaching your
     own profile card. Collapses the column into a horizontal scrolling
     strip of compact pills instead: same nav, same targets, but it
     reads as a tab bar (like the rest of this app's mobile patterns)
     and costs one row of height, not the whole first screen. */
  [data-responsive] .shell-side__nav {
    position: static !important;
    flex-direction: row; flex-wrap: nowrap;
    gap: 6px; overflow-x: auto; padding-bottom: 4px;
    -webkit-overflow-scrolling: touch;
  }
  [data-responsive] .shell-side__nav-label { display: none; }
  [data-responsive] .shell-side__nav-item {
    flex: none; width: auto; white-space: nowrap;
    padding: 8px 14px; border-radius: var(--radius-pill);
    border: 1px solid var(--line-1);
  }
  [data-responsive] .shell-side__nav-item--active { border-color: transparent; }
  [data-responsive] .shell-side__nav-item--nested { padding-inline-start: 14px; }
}

/* Phone header: identity, search, cart, menu — four controls, not six.
   Messages and notifications are account actions; on a 390px header they
   crowd the row and push the logo, and they read better as named rows in the
   drawer than as two more unlabelled glyphs. Both stay in the header from
   769px up, where there is room for them. */
@media (max-width: 768px) {
  [data-responsive] .global-nav__icon[data-go="dms"],
  [data-responsive] .global-nav__icon[data-go="notifications"] { display: none; }
}

/* Product card footer on a phone.
   The price and the seller share one row. In a 2-up grid at 390px each card
   is 149px wide, which fits a two-digit price and not a three-digit one — so
   ₯142 broke across two lines while ₯89 beside it did not, and the handle
   truncated to "@kenzo.ca…". A price that wraps is unreadable at a glance,
   which is the one thing a price has to be.

   Stacked, the price owns its line and the handle gets the full width. */
@media (max-width: 768px) {
  [data-responsive] .product-card__row {
    flex-direction: column; align-items: flex-start; gap: 2px;
  }
  [data-responsive] .product-card__price { white-space: nowrap; }
  [data-responsive] .product-card__seller { max-width: 100%; }
}

/* The landing carries two wordmark assets — light for a white ground, onDark
   for its black plate — and shows one. Every other screen has only the light
   one, so the dark twin is hidden by default rather than per-screen. */
.global-nav__logo--dark { display: none; }
.global-nav__logo--mark { display: none; }

/* ---------- Prose ----------
   Long-copy screens (terms, about, help). The only mobile archetype with no
   prior exemplar, so it lives here rather than in one screen's <style>: three
   screens use it, and a fourth will. */
.prose-body { flex: 1; overflow-y: auto; padding: 4px 16px 26px; }
.prose-updated {
  font: 600 12px var(--font-mono); color: var(--ink-3); margin: 2px 0 14px;
  text-transform: uppercase; letter-spacing: .04em;
}
.prose-toc { display: flex; gap: 7px; overflow-x: auto; padding: 0 0 14px; }
.prose-toc::-webkit-scrollbar { display: none; }
.prose-body h2 {
  font: 800 15px var(--font-body); color: var(--ink-1); margin: 22px 0 7px;
}
.prose-body h2:first-of-type { margin-top: 0; }
.prose-body p {
  font: 400 14px/1.65 var(--font-body); color: var(--ink-2); margin: 0 0 11px;
}
.prose-body p:last-child { margin-bottom: 0; }


/* ---------- Settings rows ----------
   Grouped rows with a label, an optional value and a chevron. Lived in
   settings.html's own <style> until three screens needed it and Task 3 was
   about to make that six copies. Promoted here rather than duplicated again. */
.settings-group {
      margin-bottom: 18px;
    }
.settings-group__title {
      font: 600 11px var(--font-body);
      color: var(--ink-3);
      text-transform: uppercase;
      letter-spacing: 0.07em;
      margin: 0 0 8px;
    }
.settings-group__list {
      background: var(--surface-card);
      border: 1px solid var(--line-2);
      border-radius: var(--radius-md);
      overflow: hidden;
    }
.settings-row {
      all: unset;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 10px;
      width: 100%;
      padding: 13px 14px;
      border-top: 1px solid var(--line-2);
      box-sizing: border-box;
    }
.settings-row:first-child {
      border-top: none;
    }
.settings-row__label {
      font: 600 14px var(--font-body);
      color: var(--ink-1);
    }
.settings-row--danger .settings-row__label {
      color: var(--beyli-live, #E5484D);
    }
.settings-row__value {
      font: 500 13px var(--font-body);
      color: var(--ink-3);
      display: flex;
      align-items: center;
      gap: 6px;
    }
.settings-row__chevron {
      color: var(--ink-4);
    }

/* ---------- People row ----------
   A person with an avatar, a handle and one action. Used by followers,
   following and blocked — three screens, so it belongs here rather than
   being copied into each. */
.people-row { display: flex; align-items: center; gap: 11px; padding: 11px 0; border-bottom: 1px solid var(--line-2); }
.people-row:last-child { border-bottom: 0; }
.people-row__body { flex: 1; min-width: 0; }
.people-row__name { display: block; font: 700 14px var(--font-body); color: var(--ink-1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.people-row__meta { display: block; font: 500 12px var(--font-body); color: var(--ink-3); margin-top: 1px; }
.people-row__action { flex: none; }

/* ---------- Stat pair ----------
   A two-up row of labelled figures, the shape of earnings.html's stat grid
   but under its own name. Task 7 put it in every one of analytics, rewards,
   referral and becomeseller — four screens, the same threshold that promoted
   settings-row and people-row here — so it is defined once instead of pasted
   a fourth time. */
.stat-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 12px 0; }
.stat-box { background: var(--surface-card); border: 1px solid var(--line-2); border-radius: var(--radius-md); padding: 12px; }
.stat-box__label { font: 600 10px var(--font-mono); color: var(--ink-3); text-transform: uppercase; letter-spacing: .06em; }
.stat-box__value { font: 800 20px var(--font-body); color: var(--ink-1); margin-top: 4px; }

/* ============================================================
   Touch targets
   ============================================================
   Measured across 24 app screens: 74 of 131 visible controls were under the
   44px WCAG 2.5.5 / Apple HIG minimum, and 16 were under even 24px. The feed's
   Search and Notifications buttons were 14x14 — the button itself, not an icon
   inside a larger one.

   Two techniques, chosen per control:

   1. min-height on things that are already boxes. A nav row or a button can
      simply be taller.
   2. A pseudo-element on things whose size is the design. Growing a 14px icon
      button to 44px would redraw the header; an invisible ::after centred on
      it grows the target instead and the artwork never moves.

   Inline links inside prose are deliberately untouched. WCAG 2.5.5 exempts
   targets in a sentence, and padding them out would wreck body copy. 564 of
   the controls measured were exactly that. */

/* --- 1. boxes that can simply be bigger ------------------------------- */
.btn,
.icon-btn,
button.tag,
a.tag,
.beyli-header__back,
.beyli-nav__item,
.tabs__tab,
.audio-round-btn {
  min-height: 44px;
}
.icon-btn,
.beyli-header__back,
.audio-round-btn {
  min-width: 44px;
}

/* --- 2. small actions whose drawn size is the design ------------------- */
/* The ::after is the target: invisible, centred, 44px, and outside the flow
   so nothing reflows. The control needs a positioning context and must sit
   above its siblings' stacking, or an adjacent card can swallow the taps. */
.feed-header__action,
.follow-bell,
.pay-card__edit,
.gallery-top__nav,
.gallery-top__close,
.audio-close,
.cat-hero__back,
.product-hero__back,
.search-top__back,
.sr-top__back,
.giveaway-close,
.otp__resend-link,
.tour__skip,
.thread-top__back,
.audio-invite__decline,
.auth__signin-link,
.discover-top__action,
.addr-card__action,
.cart-item__remove,
.alerts-header__action,
.global-nav__referral,
.auth-form__forgot {
  position: relative;
}
.feed-header__action::after,
.follow-bell::after,
.pay-card__edit::after,
.gallery-top__nav::after,
.gallery-top__close::after,
.audio-close::after,
.cat-hero__back::after,
.product-hero__back::after,
.search-top__back::after,
.sr-top__back::after,
.giveaway-close::after,
.otp__resend-link::after,
.tour__skip::after,
.thread-top__back::after,
.audio-invite__decline::after,
.auth__signin-link::after,
.discover-top__action::after,
.addr-card__action::after,
.cart-item__remove::after,
.alerts-header__action::after,
.global-nav__referral::after,
.auth-form__forgot::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
  /* No background: this is a hit area, not a shape. */
}

/* --- 3. web and admin chrome, only when the pointer is a finger -------- */
/* These are desktop surfaces read with a mouse, where 35px already clears the
   24px of WCAG 2.5.8. Forcing 44px there would only bloat the admin console,
   so the rule is scoped to touch. */
@media (pointer: coarse) {
  .shell-side__nav-item,
  .site-footer__social,
  .global-nav__icon,
  .admin-topbar__brand {
    min-height: 44px;
  }
  .site-footer__social,
  .global-nav__icon {
    min-width: 44px;
  }
}

/* Chips, tiles and text buttons: already boxes, so they just get taller.
   Found by sweeping all 71 phone screens rather than a sample — the first
   pass covered a 24-screen slice and missed these entirely. */
.interest-chip,
.cat-tile,
.review-chip,
.search-recent__link,
.shop-hero__toggle-btn,
.confirm-browse,
.thread-top__watch,
.sys-screen__secondary,
.live-giveaway-btn,
.interest-skip {
  min-height: 44px;
}

/* A star rating is five targets in a row. An ::after on each would overlap its
   neighbours and turn a mis-tap into the wrong score, so these grow their own
   box instead; the glyph is unchanged, the stars simply sit further apart. */
.star-rating__star {
  min-width: 44px;
  min-height: 44px;
}

/* These four are declared in their own screens' <style> blocks, which the
   browser reads after this file. At equal specificity the screen wins, so the
   floor above was silently ignored — .live-giveaway-btn was pinned to 28px.
   Qualifying by tag makes these (0,1,1) against the screens' (0,1,0), which
   is enough to win without reaching for !important. A minimum target size is
   exactly the kind of rule that should not be overridable by accident. */
button.search-recent__link,
button.thread-top__watch,
button.live-giveaway-btn,
button.sys-screen__secondary {
  min-height: 44px;
}

/* ============================================================
   Verified mark
   ============================================================
   Shown beside a seller's handle or shop name once a reviewer has approved
   their KYC application. Sized in em, not px, so the same markup works on a
   12px chat tag and a 24px profile heading without a modifier for each.

   Filled rather than outlined: at 12px an outlined tick loses its shape, and
   this has to read instantly at the smallest size it appears in.

   role="img" with an aria-label, because a bare decorative <span> is not
   announced — and "this seller is verified" is information, not decoration. */
.verified-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1em;
  height: 1em;
  margin-inline-start: 0.28em;
  color: var(--verified-blue);
  vertical-align: -0.11em;
  flex-shrink: 0;
}
.verified-badge svg { width: 100%; height: 100%; display: block; }

/* ============================================================
   Checkout summary bar
   ============================================================
   checkout1 and checkout2 carried no monetary value at all — the last figure
   a buyer saw was the cart total, two screens earlier. This keeps the amount
   in view at every step, so nobody has to leave the flow to check it. */
.checkout-sum {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 16px;
  background: var(--surface-card);
  border-top: 1px solid var(--line-2);
  border-bottom: 1px solid var(--line-2);
  font: 600 13px var(--font-body);
  color: var(--ink-2);
  flex-shrink: 0;
}
.checkout-sum__total {
  font: 700 15px var(--font-mono);
  color: var(--ink-1);
  font-variant-numeric: tabular-nums;
}

/* --- the wallet, offered first ---------------------------------------- */
/* A wallet charges the card the buyer already added, without them typing a
   number, an address or a CVC. It leads; it never forces — "or pay another
   way" sits directly below at the same weight.
   Full black, matching the mark's own guidelines rather than Beyli's card
   style — same reasoning as .btn-wallet below: a white card with a black
   Apple/Google mark on it isn't either brand's button, it's a plain list
   row that happens to have their logo in it. */
.pay-wallet {
  border: none;
  border-radius: var(--radius-md);
  background: #000;
  color: #fff;
  padding: 12px 14px;
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  box-sizing: border-box;
  cursor: pointer;
  text-align: start;
}
.pay-wallet__body { flex: 1; min-width: 0; }
.pay-wallet__title { font: 700 14px var(--font-body); color: #fff; display: block; }
.pay-wallet__sub { font: 500 12px var(--font-body); color: rgba(255,255,255,0.7); display: block; margin-top: 1px; }
.pay-wallet__flag {
  font: 700 10px var(--font-body);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: var(--beyli-yellow);
  color: var(--beyli-indigo);
  padding: 4px 8px;
  border-radius: var(--radius-pill);
  flex-shrink: 0;
}
.pay-alt-label {
  font: 600 11px var(--font-body);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 16px 2px 8px;
}

/* The pay button follows Apple's and Google's own specs, not Beyli's button
   style — their guidelines mandate the wording, colour and mark, and
   deviating is a rejection risk at review. */
.btn-wallet {
  all: unset;
  box-sizing: border-box;
  width: 100%;
  min-height: 50px;
  border-radius: var(--radius-pill);
  background: #000;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  font: 600 15px var(--font-body);
  cursor: pointer;
}
.btn-wallet svg { width: 17px; height: 17px; }
.btn-wallet__content,
.btn-wallet__brand {
  display: inline-flex;
  align-items: center;
}
.btn-wallet__content { gap: 5px; }
.btn-wallet__brand { gap: 2px; white-space: nowrap; }

/* ============================================================
   Permission prime
   ============================================================
   A denied OS notification prompt is permanent — fire it cold and that user's
   alerts are gone for good. This asks first, in Beyli's own words, and only
   triggers the real prompt on a yes. "Not now" deliberately does NOT reach the
   OS, which keeps the one real chance intact for later.

   The promise it makes has to stay true. If marketing ever ships through this
   channel, the sheet becomes a lie and the uninstall follows. */
.prime-scrim {
  position: absolute; inset: 0; z-index: 60;
  background: rgba(18, 10, 41, 0.5);
  display: flex; align-items: flex-end;
}
.prime-sheet {
  width: 100%;
  background: var(--surface-card);
  border-radius: 20px 20px 0 0;
  padding: 20px 20px calc(20px + env(safe-area-inset-bottom));
  box-shadow: 0 -10px 40px rgba(18, 10, 41, 0.28);
}
.prime-sheet__icon {
  width: 46px; height: 46px; border-radius: var(--radius-pill);
  background: var(--beyli-yellow); color: var(--beyli-indigo);
  display: flex; align-items: center; justify-content: center;
  font-size: 21px; margin-bottom: 12px;
}
.prime-sheet__title { font: 700 18px var(--font-body); color: var(--ink-1); margin: 0 0 6px; }
.prime-sheet__body { font: 500 13.5px/1.5 var(--font-body); color: var(--ink-2); margin: 0 0 16px; }
.prime-sheet__body b { color: var(--ink-1); }
.prime-sheet .btn { width: 100%; }
.prime-sheet__no {
  all: unset; cursor: pointer; display: block; width: 100%;
  text-align: center; margin-top: 4px; padding: 13px;
  font: 600 14px var(--font-body); color: var(--ink-3);
}
/* Compact variant: a popover beside the control that asked, not a sheet. */
.prime-scrim--pop { align-items: center; justify-content: center; background: rgba(18, 10, 41, 0.18); }
.prime-sheet--pop {
  width: min(320px, calc(100% - 24px)); border-radius: 16px; padding: 14px 14px 12px;
  box-shadow: 0 14px 40px rgba(18, 10, 41, 0.3); border: 1px solid var(--line-2);
}
.prime-sheet--pop .prime-sheet__row { display: flex; gap: 10px; align-items: flex-start; }
.prime-sheet--pop .prime-sheet__icon { width: 34px; height: 34px; font-size: 16px; margin: 0; flex: none; }
.prime-sheet--pop .prime-sheet__title { font-size: 14px; margin: 2px 0 2px; }
.prime-sheet--pop .prime-sheet__body { font-size: 12.5px; line-height: 1.4; margin: 0; }
.prime-sheet--pop .prime-sheet__actions { display: flex; justify-content: flex-end; align-items: center; gap: 6px; margin-top: 10px; }
.prime-sheet--pop .btn { width: auto; }
.prime-sheet--pop .prime-sheet__no { width: auto; margin: 0; padding: 8px 10px; font-size: 13px; }

/* Follow, once it means something. The bell says alerts are on and turns them
   off without unfollowing — the state lives on the screen rather than three
   levels into settings. */
.follow-bell {
  all: unset; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: var(--radius-pill);
  background: var(--surface-sunken); color: var(--beyli-indigo);
  margin-inline-start: 6px; flex-shrink: 0;
}
.follow-bell--off { color: var(--ink-4); }
.follow-bell svg { width: 16px; height: 16px; }

/* ============================================================
   Empty states
   ============================================================
   All 16 list screens shipped populated, so nobody had designed what they say
   with nothing in them — which is exactly what a new account sees. An empty
   list that says nothing reads as a broken screen.

   Each one names what is missing, why it is worth having, and offers the one
   action that fixes it. No illustrations: a drawing costs bytes and says less
   than a sentence that tells you what to do next.

   Shown when the screen carries data-state="empty". The populated markup stays
   in place so both states can be reviewed from the same file. */
.empty {
  display: none;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 48px 32px;
  gap: 6px;
}
[data-state="empty"] .empty { display: flex; }
[data-state="empty"] .empty ~ *,
[data-state="empty"] * + .empty { }
.empty__mark {
  width: 56px; height: 56px; border-radius: var(--radius-pill);
  background: var(--surface-sunken);
  display: flex; align-items: center; justify-content: center;
  font-size: 24px; margin-bottom: 6px;
}
.empty__title { font: 700 16px var(--font-body); color: var(--ink-1); margin: 0; }
.empty__body  { font: 500 13.5px/1.5 var(--font-body); color: var(--ink-3); margin: 0; max-width: 30ch; }
.empty .btn   { margin-top: 14px; }


/* ============================================================
   Header actions
   ============================================================
   Same reasoning as the tab bar: these were inheriting a body-text size and
   sat noticeably smaller than the search field beside them, so the row read
   as a search box with specks after it rather than three equal controls.

   22px rather than the tab bar's 25 — a header icon is a secondary action and
   should not out-shout the thing it sits next to. Stroke matches the tab bar
   so the two rows look like one family. */
.discover-top__action .icon,
.discover-top__action svg,
.feed-header__action .icon,
.feed-header__action svg {
  width: 22px;
  height: 22px;
  stroke-width: 1.9;
}
.discover-top__action,
.feed-header__action {
  align-items: center;
  justify-content: center;
  color: var(--ink-1);
}
/* The unread dot was pinned for a smaller glyph and drifted once the icon
   grew. Anchored to the icon's own corner instead of a fixed offset. */
.feed-header__dot,
.discover-top__dot {
  position: absolute;
  top: -1px;
  inset-inline-end: -2px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-danger, #D64545);
  border: 2px solid var(--surface-card);
}


/* Compact product share sheet: four fixed actions, no clipped horizontal row. */
.share-sheet { padding-top: 10px; }
.share-sheet__handle { width: 38px; height: 4px; margin: 0 auto 18px; border-radius: 9px; background: var(--line-2); }
.share-actions { display: grid; grid-template-columns: repeat(4,minmax(0,1fr)); gap: 8px; margin: 2px 0 18px; }
.share-action { all: unset; min-width: 0; cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 7px; border-radius: var(--radius-md); font: 600 11px var(--font-body); color: var(--ink-2); text-align: center; }
.share-action:focus-visible { outline: 2px solid var(--beyli-indigo); outline-offset: 3px; }
.share-action > span:last-child { width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.share-action__icon { width: 52px; height: 52px; display: grid; place-items: center; border-radius: 50%; color: #fff; box-shadow: 0 4px 14px rgba(18,10,41,.12); }
.share-action__icon svg { width: 25px; height: 25px; fill: currentColor; }
.share-action__icon--messages { background: #34C759; }
.share-action__icon--whatsapp { background: #25D366; }
.share-action__icon--instagram { background: radial-gradient(circle at 32% 100%,#ffd600 0 15%,#ff7a00 32%,#ff0169 58%,#d300c5 78%,#7638fa 100%); }
.share-action__icon--copy { background: var(--ink-1); }
.share-link { display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 0 6px 0 13px; border: 1px solid var(--line-1); border-radius: var(--radius-md); background: var(--surface-sunken); cursor: pointer; }
.share-link:focus-visible { outline: 2px solid var(--beyli-indigo); outline-offset: 2px; }
.share-link__url { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: 500 12px var(--font-mono); color: var(--ink-2); }
.share-link__copy { flex: none; padding: 8px 11px; border-radius: var(--radius-sm); background: var(--surface-card); font: 700 12px var(--font-body); color: var(--beyli-indigo); }

/* =============================================================
   Component: screen loading / error   (mobile, shared)
   Injected by interactions.js for ?state=loading|error. See the
   note there for why these are shared rather than per-screen.
   ============================================================= */
.screen-loading { display: flex; flex-direction: column; gap: 12px; padding: 16px; }
.screen-loading .skeleton { height: 64px; border-radius: var(--radius-md); }
.screen-loading .skeleton:nth-child(2) { width: 94%; }
.screen-loading .skeleton:nth-child(3) { width: 88%; }
.screen-error {
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
  margin: 16px; padding: 18px;
  border: 1px solid var(--line-2); border-radius: var(--radius-md);
  background: var(--surface-card);
}
.screen-error__title { font: 700 15px var(--font-body); color: var(--ink-1); }
.screen-error__body { font: 400 13.5px/1.55 var(--font-body); color: var(--ink-2); margin: 0; }


/* =============================================================
   Component: Modal (window.beyliAdminConfirm)
   One calm, focused confirm dialog reused everywhere a destructive or
   consequential action needs a second tap: admin moderation/audit
   actions, and consumer flows like cancelling an order or ending a
   show. Was admin.css-only, which every mobile/web screen never
   loads -- so the same dialog rendered as an unstyled, unpositioned
   <dialog> UA default outside the admin panel. Every screen loads
   components.css, so it lives here now.
   ============================================================= */
.modal {
  box-sizing: border-box;
  inline-size: min(620px, calc(100vw - 32px));
  max-inline-size: calc(100vw - 32px);
  max-block-size: min(760px, calc(100dvh - 32px));
  margin: auto;
  padding: 0;
  overflow: visible;
  border: 0;
  border-radius: 24px;
  background: transparent;
  color: var(--ink-1);
  box-shadow: 0 28px 80px rgba(25, 16, 55, .28), 0 8px 24px rgba(25, 16, 55, .12);
}

.modal::backdrop {
  background: rgba(22, 16, 45, .46);
  backdrop-filter: blur(8px) saturate(115%);
  -webkit-backdrop-filter: blur(8px) saturate(115%);
  animation: admin-modal-backdrop-in 180ms ease-out;
}

.modal[open] { animation: admin-modal-in 220ms cubic-bezier(.2, .8, .2, 1); }

.modal__content {
  display: flex;
  flex-direction: column;
  max-block-size: inherit;
  margin: 0;
  overflow: hidden;
  border: 1px solid rgba(49, 31, 91, .10);
  border-radius: inherit;
  background: var(--surface-card);
}

.modal__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px;
  padding: 25px 25px 18px;
  border-bottom: 1px solid var(--line-2);
  background: linear-gradient(135deg, #fff 0%, #fff 58%, rgba(246, 241, 255, .8) 100%);
}

.modal__title {
  margin: 0;
  color: var(--ink-1);
  font: 700 25px/1.16 var(--font-display);
  letter-spacing: -.035em;
}

.modal__sub {
  max-inline-size: 470px;
  margin: 7px 0 0;
  color: var(--ink-3);
  font: var(--text-body-md);
  line-height: 1.5;
}

.modal__body {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-block-size: 0;
  padding: 22px 25px;
  overflow: auto;
  background: var(--surface-card);
}

.modal__body .field { display: grid; gap: 8px; }
.modal__body .field__label { color: var(--ink-2); font: 650 13px/1.25 var(--font-body); }
.modal__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.modal__grid .field--full { grid-column: 1 / -1; }
.modal__section { display: grid; gap: 14px; padding: 16px; border: 1px solid var(--line-2); border-radius: 16px; background: var(--surface-sunken); }
.modal__section-title { margin: 0; color: var(--ink-1); font: 700 14px/1.25 var(--font-body); }
.modal__section-copy { margin: -7px 0 0; color: var(--ink-3); font: var(--text-caption); line-height: 1.45; }
.modal__notice { display: flex; gap: 10px; padding: 12px 13px; border-left: 3px solid var(--beyli-yellow); border-radius: 8px; background: rgba(255, 207, 25, .10); color: var(--ink-2); font: var(--text-caption); line-height: 1.45; }
.modal__check { display: flex; align-items: flex-start; gap: 9px; color: var(--ink-2); font: var(--text-caption); line-height: 1.4; cursor: pointer; }
.modal__check input { inline-size: 16px; block-size: 16px; margin: 1px 0 0; accent-color: var(--beyli-violet); }
.modal__meta { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--line-2); color: var(--ink-2); font: var(--text-body-sm); }
.modal__meta:last-child { border-bottom: 0; padding-bottom: 0; }

.modal__body .field__input,
.modal__body input:not([type="checkbox"]):not([type="radio"]),
.modal__body select,
.modal__body textarea {
  box-sizing: border-box;
  inline-size: 100%;
  min-block-size: 46px;
  padding: 11px 13px;
  border: 1px solid var(--line-1);
  border-radius: 12px;
  outline: none;
  background: var(--surface-card);
  color: var(--ink-1);
  font: 500 14px/1.35 var(--font-body);
  box-shadow: inset 0 1px 1px rgba(23, 16, 46, .025);
  transition: border-color 150ms ease, box-shadow 150ms ease, background 150ms ease;
}

.modal__body textarea { min-block-size: 112px; resize: vertical; }
.modal__body select { padding-inline-end: 34px; cursor: pointer; }

.modal__body .field__input::placeholder,
.modal__body input::placeholder,
.modal__body textarea::placeholder { color: var(--ink-4); }

.modal__body .field__input:focus,
.modal__body input:focus,
.modal__body select:focus,
.modal__body textarea:focus {
  border-color: var(--beyli-violet);
  background: #fff;
  box-shadow: 0 0 0 4px rgba(100, 52, 212, .13);
}

.modal__foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  padding: 17px 25px 23px;
  border-top: 1px solid var(--line-2);
  background: linear-gradient(180deg, rgba(250, 249, 253, .6), var(--surface-card));
}

.modal .icon-btn {
  display: inline-grid;
  flex: none;
  place-items: center;
  inline-size: 38px;
  block-size: 38px;
  padding: 0;
  border: 1px solid var(--line-1);
  border-radius: 50%;
  background: var(--surface-card);
  color: var(--ink-2);
  font: 500 22px/1 var(--font-body);
  cursor: pointer;
  transition: background 150ms ease, border-color 150ms ease, transform 150ms ease;
}

.modal .icon-btn:hover { border-color: var(--line-2); background: var(--surface-sunken); transform: scale(1.04); }
.modal .icon-btn:focus-visible { outline: 3px solid rgba(100, 52, 212, .22); outline-offset: 2px; }
.modal .btn { min-block-size: 43px; padding-inline: 17px; border-radius: 12px; font-weight: 700; }

@keyframes admin-modal-in {
  from { opacity: 0; transform: translateY(12px) scale(.985); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes admin-modal-backdrop-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@media (max-width: 600px) {
  .modal { inline-size: min(100%, calc(100vw - 20px)); max-block-size: calc(100dvh - 20px); border-radius: 20px; }
  .modal__head { padding: 21px 19px 16px; }
  .modal__body { padding: 18px 19px; }
  .modal__foot { padding: 15px 19px 19px; }
  .modal__title { font-size: 22px; }
  .modal__foot .btn { flex: 1; padding-inline: 12px; }
  .modal__grid { grid-template-columns: 1fr; }
  .modal__grid .field--full { grid-column: auto; }
}

/* =============================================================
   Component: Share popup       (shared/show-artwork.js)

   Opened only after POST /api/room has returned a real show, and
   built from that response's share payload — the title, the
   schedule, the link and the poster are what was actually saved,
   never what was typed. Youthful on purpose: this is the one
   screen a seller wants to screenshot. Every claim on it is one
   the API can back — the buttons share and download, and nothing
   here says anything was posted anywhere.
   ============================================================= */
.sharepop {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: var(--surface-overlay);
  overflow-y: auto;
}
.sharepop__card {
  width: 420px;
  max-width: 100%;
  max-height: calc(100dvh - 32px);
  overflow: auto;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 20px;
  background: var(--surface-card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  animation: beyli-pop var(--dur-slow) var(--ease-snap);
}
.sharepop__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.sharepop__title { margin: 0; font: var(--text-h3); letter-spacing: var(--tracking-display); color: var(--ink-1); }
.sharepop__close {
  width: 32px; height: 32px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%;
  background: var(--surface-sunken); color: var(--ink-2);
  font-size: 18px; cursor: pointer;
}
/* contain, not cover: 9:16, 1:1 and 4:5 all land in this box and a crop
   here would show the seller a poster they are not about to download. */
.sharepop__poster {
  display: flex; align-items: center; justify-content: center;
  min-height: 120px; padding: 10px;
  border-radius: var(--radius-md);
  background: var(--beyli-indigo);
}
.sharepop__poster img { display: block; max-width: 100%; max-height: 42vh; object-fit: contain; border-radius: 10px; }
.sharepop__meta { display: grid; gap: 2px; }
.sharepop__show-title { font: 700 17px var(--font-body); color: var(--ink-1); }
.sharepop__show-when { font: var(--text-caption); color: var(--ink-3); }
.sharepop__ratios { display: flex; gap: 8px; flex-wrap: wrap; }
.sharepop__link {
  display: flex; align-items: center; gap: 8px;
  padding: 8px; padding-inline-start: 12px;
  border: 1px solid var(--line-1); border-radius: var(--radius-sm);
  background: var(--surface-sunken);
}
.sharepop__link-url {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: 500 12px var(--font-mono); color: var(--ink-2);
}
.sharepop__actions { display: flex; gap: 8px; flex-wrap: wrap; }
.sharepop__actions .btn { flex: 1 1 auto; justify-content: center; }
.sharepop__note { margin: 0; font: var(--text-caption); color: var(--ink-3); }
.sharepop__remind { display: grid; gap: 6px; padding: 12px; border-radius: var(--radius-sm); background: var(--beyli-violet-soft); }
.sharepop__remind .btn { justify-self: start; }
.sharepop__remind-note { margin: 0; font: var(--text-caption); color: var(--ink-2); }
.sharepop__foot { display: flex; justify-content: flex-end; }

/* On a phone it is a bottom sheet, which is where a thumb already is. */
@media (max-width: 480px) {
  .sharepop { padding: 0; align-items: flex-end; }
  .sharepop__card {
    width: 100%;
    max-height: 92dvh;
    border-radius: 20px 20px 0 0;
    padding: 18px 16px calc(18px + env(safe-area-inset-bottom));
  }
}
