// mycards-modal.jsx — Final card details modal v2.
//
// Supersedes the provisional CardDetailsModal in card-v2.jsx. Per-type content
// (Player / Null / Fan / Coach) plus three responsive layout variants:
//   • 'sheet'       — bottom sheet, mobile-natural
//   • 'sideBySide'  — centered dialog with compact card on one side, info on the other
//   • 'minimal'     — slim centered dialog, just card + stats, no meta
//
// Loaded as a portal to escape any parent transform/scale.

// ─── icons ──────────────────────────────────────────────────────────────────
function MCIcon({ kind, size = 18 }) {
  const s = size;
  switch (kind) {
    case 'close':
      return (<svg width={s} height={s} viewBox="0 0 18 18" fill="none" aria-hidden="true">
        <path d="M4 4 L14 14 M14 4 L4 14" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"/>
      </svg>);
    case 'share':
      return (<svg width={s} height={s} viewBox="0 0 18 18" fill="none" aria-hidden="true">
        <circle cx="13.5" cy="4" r="2"   stroke="currentColor" strokeWidth="1.4"/>
        <circle cx="4.5"  cy="9" r="2"   stroke="currentColor" strokeWidth="1.4"/>
        <circle cx="13.5" cy="14" r="2"  stroke="currentColor" strokeWidth="1.4"/>
        <path d="M6.3 8 L11.7 5  M6.3 10 L11.7 13" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/>
      </svg>);
    case 'history':
      return (<svg width={s} height={s} viewBox="0 0 18 18" fill="none" aria-hidden="true">
        <circle cx="9" cy="9" r="6.5" stroke="currentColor" strokeWidth="1.4"/>
        <path d="M9 5.5 V9 L11.4 10.4" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round"/>
      </svg>);
    case 'add':
      return (<svg width={s} height={s} viewBox="0 0 18 18" fill="none" aria-hidden="true">
        <path d="M9 4 V14 M4 9 H14" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"/>
      </svg>);
    case 'star':
      return (<svg width={s} height={s} viewBox="0 0 18 18" fill="none" aria-hidden="true">
        <path d="M9 2 L11 7 L16 7.6 L12.3 11 L13.3 16 L9 13.5 L4.7 16 L5.7 11 L2 7.6 L7 7 Z"
              fill="currentColor"/>
      </svg>);
    default:
      return null;
  }
}

// ─── Per-type accents — re-derived from card tokens for in-modal styling ───
// Player → tier color (field is tier-tinted)
// Null   → distinct purple (field uses NULL palette, not tier)
// Fan    → red
// Coach  → green
function modalAccent(item) {
  if (item.type === 'player') {
    const tt = TIER[item.card.tier];
    return { color: tt.base, glow: tt.glow, pillBg: tt.pillBg, pillStroke: tt.pillStroke,
             text: tt.overallColor, accentText: tt.accentText };
  }
  if (item.type === 'null') {
    // Null cards have a tier (gold/platinum) for their Overall pill, but the
    // *card's* identity color — field gradient, rim glow — is purple. The
    // modal's accent line + ambient glow should match the card's identity,
    // not the tier, so the rail fades from purple → transparent (the same
    // hue you see at the top of the hero).
    return { color: '#A88CFF', glow: 'rgba(168,130,255,0.55)',
             pillBg: 'linear-gradient(180deg, rgba(190,160,255,0.20) 0%, rgba(80,50,160,0.18) 100%)',
             pillStroke: 'rgba(190,160,255,0.45)',
             text: '#D7C8FF', accentText: '#B8A5FF' };
  }
  if (item.type === 'fan') {
    return { color: '#EF4444', glow: 'rgba(239,68,68,0.55)',
             pillBg: 'linear-gradient(180deg, rgba(255,140,140,0.24) 0%, rgba(140,30,30,0.18) 100%)',
             pillStroke: 'rgba(255,160,160,0.45)',
             text: '#FFB8B8', accentText: '#F08585' };
  }
  // coach
  return { color: '#22C55E', glow: 'rgba(34,197,94,0.55)',
           pillBg: 'linear-gradient(180deg, rgba(150,240,180,0.24) 0%, rgba(30,100,60,0.18) 100%)',
           pillStroke: 'rgba(160,240,190,0.48)',
           text: '#B8F2CD', accentText: '#85D8A6' };
}

// ─── Hero block (REDESIGNED) ────────────────────────────────────────────────
// Used to render the entire compact card scaled down; that turned out to
// repeat info (name / tier badge / stat pill) we're already showing in the
// title block + meta grid. The redesign extracts only what's *visual*:
//
//   1. tier-tinted radial halo (the card's field gradient, no chrome)
//   2. item image (player / null jersey / fan / coach) — bigger
//   3. glass "Overall" disc with the headline value (OVR, +bonus, ×mult)
//
// This is the design language the user asked for: "no full card, just image +
// halo + glass disc".
function ModalHeroCard({ item, locale, scale = 1 }) {
  // Pick the visual material per type.
  let field, rim, image, imageStyle, pillBg, pillStroke, glowColor;
  let pillValue, pillLabel, pillValueColor, pillLabelColor;

  if (item.type === 'player') {
    const tt = TIER[item.card.tier];
    field = tt.field;
    rim   = tt.rim;
    glowColor = tt.glow;
    image = item.card.image;
    imageStyle = { filter: tt.imageFilter };
    pillBg = tt.pillBg; pillStroke = tt.pillStroke;
    pillValue = toLocaleDigits(item.card.overall, locale);
    pillLabel = I18N[locale].overall;
    pillValueColor = tt.overallColor; pillLabelColor = tt.accentText;
  } else if (item.type === 'null') {
    const tt = TIER[item.card.tier];
    field = 'radial-gradient(ellipse 90% 70% at 50% 42%, #3D2980 0%, #1F1455 45%, #0E0830 80%, #06051A 100%)';
    rim   = 'rgba(168,130,255,0.45)';
    glowColor = 'rgba(168,130,255,0.55)';
    image = 'assets/null.webp';
    imageStyle = {};
    pillBg = tt.pillBg; pillStroke = tt.pillStroke;
    pillValue = toLocaleDigits(item.card.overall, locale);
    pillLabel = I18N[locale].overall;
    pillValueColor = tt.overallColor; pillLabelColor = tt.accentText;
  } else if (item.type === 'fan') {
    field = 'radial-gradient(ellipse 90% 70% at 50% 42%, #7A1C1C 0%, #3D0E0E 45%, #1A0606 80%, #0A0303 100%)';
    rim   = 'rgba(239,68,68,0.45)';
    glowColor = 'rgba(239,68,68,0.55)';
    image = 'assets/fan.webp';
    imageStyle = {};
    pillBg = 'linear-gradient(180deg, rgba(255,140,140,0.24) 0%, rgba(140,30,30,0.18) 100%)';
    pillStroke = 'rgba(255,160,160,0.45)';
    pillValue = '+' + toLocaleDigits(item.card.bonus, locale);
    pillLabel = SPECIAL_I18N[locale].fanBonus;
    pillValueColor = '#FFB8B8'; pillLabelColor = '#F08585';
  } else {
    // coach
    field = 'radial-gradient(ellipse 90% 70% at 50% 42%, #2D5A3E 0%, #1A3326 45%, #0C1812 80%, #060A07 100%)';
    rim   = 'rgba(34,197,94,0.45)';
    glowColor = 'rgba(34,197,94,0.55)';
    image = 'assets/coach.webp';
    imageStyle = {};
    pillBg = 'linear-gradient(180deg, rgba(150,240,180,0.24) 0%, rgba(30,100,60,0.18) 100%)';
    pillStroke = 'rgba(160,240,190,0.48)';
    pillValue = '×' + toLocaleDigits(String(item.card.multiplier), locale);
    pillLabel = SPECIAL_I18N[locale].coachMult;
    pillValueColor = '#B8F2CD'; pillLabelColor = '#85D8A6';
  }

  return (
    <div className="mc-hero" style={{
      transform: `scale(${scale})`,
      transformOrigin: 'top center',
      // Use tier rim color for the outer glow ring + halo.
      '--hero-rim':  rim,
      '--hero-glow': glowColor,
    }}>
      <div className="mc-hero-field" style={{ background: field }}></div>
      <div className="mc-hero-vignette" aria-hidden="true"></div>
      <img className="mc-hero-img"
           src={image}
           alt=""
           style={imageStyle}
           onError={(e) => { e.target.style.opacity = '0.25'; }}/>
      <div className="mc-hero-pill" style={{
        backgroundImage: pillBg,
        borderColor: pillStroke,
        boxShadow: `0 8px 24px ${glowColor.replace('0.55','0.30')}, inset 0 1px 0 rgba(255,255,255,0.18)`,
      }}>
        <div className="mc-hero-pill-glow" style={{
          background: `radial-gradient(ellipse 70% 65% at 50% 60%, ${glowColor} 0%, transparent 70%)`,
        }}></div>
        <span className="mc-hero-pill-value" style={{ color: pillValueColor }}>{pillValue}</span>
        <span className="mc-hero-pill-label" style={{ color: pillLabelColor }}>{pillLabel}</span>
      </div>
    </div>
  );
}

// ─── Position scores grid (player only) ────────────────────────────────────
function PositionScoresGrid({ item, locale }) {
  const ll = MC_I18N[locale];
  const llBase = I18N[locale];
  const p = item.card;
  const scores = playerPositionScores(p);
  const main = p.position;
  const positions = ['GK', 'DEF', 'MID', 'FWD'];
  return (
    <div className="mc-pos-scores">
      <div className="mc-pos-scores-head">
        <span className="mc-section-label">{ll.posScoresTitle}</span>
        <span className="mc-section-meta">
          {ll.mainPos}: <strong>{llBase.posShort[main]}</strong>
        </span>
      </div>
      <div className="mc-pos-scores-grid">
        {positions.map((pos) => {
          const isMain = pos === main;
          const val = scores[pos];
          return (
            <div key={pos} className={`mc-pos-cell ${isMain ? 'is-main' : ''}`}>
              <div className="mc-pos-cell-label">{llBase.pos[pos]}</div>
              <div className="mc-pos-cell-val">{toLocaleDigits(Math.round(val), locale)}</div>
              <div className="mc-pos-cell-key">
                {isMain ? <MCIcon kind="star" size={9} /> : null}
                <span>{llBase.posShort[pos]}</span>
              </div>
            </div>
          );
        })}
      </div>
      <div className="mc-section-hint">{ll.posScoresHint}</div>
    </div>
  );
}

// ─── Stat triplet (coach/fan) ──────────────────────────────────────────────
function StatTriplet({ items }) {
  return (
    <div className="mc-stat-triplet">
      {items.map((s, i) => (
        <div key={i} className="mc-stat-cell">
          <div className="mc-stat-label">{s.label}</div>
          <div className="mc-stat-value">{s.value}</div>
        </div>
      ))}
    </div>
  );
}

// ─── Meta row (acquired / source / last used / status) ─────────────────────
function MetaRow({ item, locale }) {
  const ll = MC_I18N[locale];
  const src = SOURCES[item.source_key]?.[locale] || item.source_key;
  const acquired = relativeDate(item.acquired_days_ago, locale);
  const lastUsed = (item.last_used_days_ago != null)
    ? relativeDate(item.last_used_days_ago, locale)
    : ll.metaNeverUsed;
  const statusLabel = (
    item.status === 'reserved' ? ll.statusReserved :
    item.status === 'expired'  ? ll.statusExpired  :
    ll.statusAvailable
  );
  const fields = [
    { label: ll.metaAcquired, value: acquired },
    { label: ll.metaSource,   value: src },
    { label: ll.metaLastUsed, value: lastUsed },
    { label: ll.metaStatus,   value: statusLabel, status: item.status },
  ];
  return (
    <div className="mc-meta-grid">
      {fields.map((f, i) => (
        <div key={i} className="mc-meta-cell">
          <div className="mc-meta-label">{f.label}</div>
          <div className={`mc-meta-value ${f.status ? 'mc-meta-status mc-meta-status--' + f.status : ''}`}>
            {f.value}
          </div>
        </div>
      ))}
    </div>
  );
}

// ─── Type-specific content block (between hero and meta) ──────────────────
function TypeBody({ item, locale, accent }) {
  const ll = MC_I18N[locale];
  if (item.type === 'player') {
    return <PositionScoresGrid item={item} locale={locale} />;
  }
  if (item.type === 'null') {
    return (
      <div className="mc-null-body">
        <div className="mc-section-label">{ll.tabNull}</div>
        <p className="mc-null-text">
          {locale === 'fa'
            ? 'این کارت در هر پست قابل استفاده است و امتیاز پایه آن از تیر کارت محاسبه می‌شود.'
            : locale === 'ar'
            ? 'يمكن استخدام هذه البطاقة في أي مركز ويتم احتساب نقاطها من مستوى البطاقة.'
            : 'A wildcard usable in any position. Base score is its tier overall.'}
        </p>
      </div>
    );
  }
  if (item.type === 'fan') {
    return (
      <React.Fragment>
        <StatTriplet items={[
          { label: ll.fanUseTimes,     value: toLocaleDigits(item.use_count || 0, locale) },
          { label: ll.fanTotalBonus,   value: '+' + toLocaleDigits(item.total_bonus || 0, locale) },
          { label: ll.fanRelatedCount, value: toLocaleDigits(item.related_count || 0, locale) },
        ]}/>
        <div className="mc-callout" style={{ borderColor: 'rgba(239,68,68,0.18)' }}>
          <span className="mc-callout-label">{ll.fanSource}</span>
          <span className="mc-callout-value">{SOURCES[item.source_key]?.[locale] || item.source_key}</span>
        </div>
      </React.Fragment>
    );
  }
  // coach
  return (
    <React.Fragment>
      <StatTriplet items={[
        { label: ll.coachUseTimes,  value: toLocaleDigits(item.use_count || 0, locale) + (locale==='fa' ? ' بار' : '') },
        { label: ll.coachAvgScore,  value: toLocaleDigits(item.avg_score || 0, locale) },
        { label: ll.coachBestScore, value: toLocaleDigits(item.best_score || 0, locale) },
      ]}/>
      <div className="mc-callout" style={{ borderColor: 'rgba(34,197,94,0.18)' }}>
        <span className="mc-callout-label">{ll.coachUnlockedAt}</span>
        <span className="mc-callout-value">{item.card.condition[locale]}</span>
      </div>
    </React.Fragment>
  );
}

// ─── Modal title block ─────────────────────────────────────────────────────
function ModalTitleBlock({ item, locale, accent, layout }) {
  const llBase = I18N[locale];
  let primary, secondary, tertiary;
  if (item.type === 'player') {
    primary   = item.card.name[locale];
    secondary = `${item.card.team[locale]} · ${llBase.pos[item.card.position]}`;
    tertiary  = `${llBase.tier}: ${TIER[item.card.tier].name[locale]}`;
  } else if (item.type === 'null') {
    primary   = MC_I18N[locale].tabNull;
    secondary = SPECIAL_I18N[locale].nullType;
    tertiary  = `${llBase.tier}: ${TIER[item.card.tier].name[locale]}`;
  } else if (item.type === 'fan') {
    primary   = `${SPECIAL_I18N[locale].fanName} · ${item.card.team[locale]}`;
    secondary = SPECIAL_I18N[locale].fanType;
    tertiary  = '+' + toLocaleDigits(item.card.bonus, locale) + ' ' + MC_I18N[locale].fanBonusLabel;
  } else {
    primary   = item.card.name[locale];
    secondary = SPECIAL_I18N[locale].coachType;
    tertiary  = '×' + toLocaleDigits(String(item.card.multiplier), locale) + ' ' + MC_I18N[locale].multiplierLabel;
  }
  return (
    <div className="mc-title-block">
      <h2 className="mc-title">{primary}</h2>
      <div className="mc-title-sub">{secondary}</div>
      <div className="mc-title-tert" style={{ color: accent.accentText }}>{tertiary}</div>
    </div>
  );
}

// ─── Actions ───────────────────────────────────────────────────────────────
// `primaryCta` semantics:
//   • undefined → fall back to the default "Add to Lineup" CTA
//   • null      → hide the primary CTA entirely (used by standalone previews)
//   • object    → render with the given { label, icon, onClick, disabled }
function ModalActions({ item, locale, primaryCta, onHistory, onShare, hideHistory = false }) {
  const ll = MC_I18N[locale];
  const showHistory = !hideHistory && (
    (item.use_count && item.use_count > 0) ||
    item.status === 'expired' || item.status === 'reserved'
  );

  const cta = (primaryCta === undefined)
    ? {
        label: ll.actAddToLineup,
        icon: 'add',
        onClick: undefined,
        disabled: item.status === 'reserved' || item.status === 'expired',
      }
    : primaryCta;   // null → no CTA; object → use it as-is

  return (
    <div className="mc-actions">
      {showHistory && (
        <button className="mc-btn mc-btn--secondary" onClick={onHistory} type="button">
          <MCIcon kind="history" />
          <span>{ll.actViewHistory}</span>
        </button>
      )}
      <button className="mc-btn mc-btn--icon-ghost" onClick={onShare} type="button" aria-label={ll.actShare}>
        <MCIcon kind="share" />
      </button>
      {cta && (
        <button className={`mc-btn mc-btn--primary ${cta.disabled ? 'is-disabled' : ''}`}
                onClick={cta.onClick}
                disabled={!!cta.disabled}
                type="button">
          {cta.icon && <MCIcon kind={cta.icon} />}
          <span>{cta.label}</span>
        </button>
      )}
    </div>
  );
}

// ─── SheetCustomShell — generic bottom sheet with header/body/footer slots ──
// Internal: rendered by MyCardsDetailModal when layout === 'sheetCustom'.
// Reuses the canonical .mc-mb backdrop + .mc-mb-sheet-card chrome (handle,
// rounded corners, blur, slide-up animation, swipe-to-dismiss), but instead
// of card-driven content (item / hero / meta) it accepts three React-node
// slots — header (sticky), body (scrollable), footer (sticky) — so any
// caller can host their own form / picker / detail panel.
// Used by Prediction Hub (PredictionMatchSheet, §14.0 of the Design Log).
function SheetCustomShell({ dir, locale, inline, onClose,
                            header, body, footer,
                            mobileHeight, accent, swipeToDismiss = true }) {
  const cardRef = React.useRef(null);
  const dragRef = React.useRef({ active: false, startY: 0, dy: 0 });

  const startDrag = (e) => {
    if (!swipeToDismiss) return;
    const t = e.touches ? e.touches[0] : e;
    dragRef.current.active = true;
    dragRef.current.startY = t.clientY;
    dragRef.current.dy = 0;
  };
  const moveDrag = (e) => {
    if (!dragRef.current.active) return;
    const t = e.touches ? e.touches[0] : e;
    const dy = Math.max(0, t.clientY - dragRef.current.startY);
    dragRef.current.dy = dy;
    if (cardRef.current) {
      cardRef.current.style.transform = dy > 0 ? `translateY(${dy}px)` : '';
      cardRef.current.style.transition = 'none';
    }
  };
  const endDrag = () => {
    if (!dragRef.current.active) return;
    const dy = dragRef.current.dy;
    dragRef.current.active = false;
    if (cardRef.current) {
      cardRef.current.style.transition = '';
      cardRef.current.style.transform = '';
    }
    if (dy > 120) onClose && onClose();
  };

  return (
    <div className={`mc-mb mc-mb--sheet ${inline ? 'mc-mb--inline' : ''}`}
         dir={dir} lang={locale}
         onClick={onClose}
         style={{ '--accent': accent || 'var(--accent-primary)' }}>
      <div className="mc-mb-sheet-card mc-mb-sheet-card--custom"
           ref={cardRef}
           onClick={(e) => e.stopPropagation()}
           style={mobileHeight ? { height: mobileHeight } : undefined}>
        <div className="mc-mb-handle-zone"
             onTouchStart={startDrag} onTouchMove={moveDrag} onTouchEnd={endDrag}
             onMouseDown={startDrag} onMouseMove={moveDrag} onMouseUp={endDrag} onMouseLeave={endDrag}>
          <div className="mc-mb-handle"></div>
        </div>
        {header && (
          <header className="mc-mb-sheet-header"
                  onTouchStart={startDrag} onTouchMove={moveDrag} onTouchEnd={endDrag}>
            <button className="mc-mb-close mc-mb-close--header"
                    onClick={onClose} type="button" aria-label="Close">
              <MCIcon kind="close" size={16} />
            </button>
            {header}
          </header>
        )}
        {body && (
          <div className="mc-mb-sheet-body mc-mb-sheet-body--custom">
            {body}
          </div>
        )}
        {footer && (
          <div className="mc-mb-sheet-foot mc-mb-sheet-foot--custom">
            {footer}
          </div>
        )}
      </div>
    </div>
  );
}

// ─── Main modal — renders different shells per layout ─────────────────────
// New props (additive, all optional) for cross-screen reuse:
//   • primaryCta   — { label, icon, onClick, disabled }. Overrides the default
//                     "Add to Lineup" action. Pass null to omit the primary.
//   • hideMeta     — drop the Acquired/Source/Last-used/Status row. Use when
//                     this modal is shown over a card that has no real
//                     inventory metadata (Lineup picker, standalone preview).
//   • hideHistory  — never show the secondary "Deck history" button.
//
// v1.3 sheetCustom additions (slot-driven generic bottom sheet for Prediction
// Hub and any future host that needs a 90vh form-style modal):
//   • header / body / footer  — React nodes (sticky/scroll/sticky slots).
//   • mobileHeight  — CSS height applied inline. Default 92vh. For inline-
//                     mode artboards pass '90%' so it sizes against the
//                     artboard, not the browser viewport.
//   • swipeToDismiss — touch-drag the handle-zone or header to close. Default true.
//   • accent  — override the --accent css var on the sheet (for theme-tinted host pages).
function MyCardsDetailModal({
  open, item, locale = 'fa', layout = 'auto',
  onClose, onAddToLineup, onViewHistory, onShare,
  containerWidth = 420,        // hint for auto layout
  inline = false,              // render in-place (artboard preview) — no portal, absolute backdrop
  primaryCta,                  // optional override
  hideMeta = false,
  hideHistory = false,
  // sheetCustom slot props (v1.3) — only used when layout="sheetCustom"
  header, body, footer,
  mobileHeight,
  swipeToDismiss = true,
  accent: accentOverride,
}) {
  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === 'Escape') onClose && onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [open, onClose]);
  if (!open) return null;

  // Resolve layout — 'auto' chooses sideBySide when wide enough.
  const effLayout = (layout === 'auto')
    ? (containerWidth >= 760 ? 'sideBySide' : 'sheet')
    : layout;

  const dir = locale === 'en' ? 'ltr' : 'rtl';

  // ── sheetCustom: slot-driven generic bottom sheet, no `item` required.
  // Render path is intentionally before the !item early-return so callers
  // (e.g. PredictionMatchSheet) can host arbitrary content. (Design Log §14.0)
  if (effLayout === 'sheetCustom') {
    const node = (
      <SheetCustomShell dir={dir} locale={locale} inline={inline} onClose={onClose}
                        header={header} body={body} footer={footer}
                        mobileHeight={mobileHeight}
                        swipeToDismiss={swipeToDismiss}
                        accent={accentOverride}/>
    );
    return inline ? node : ReactDOM.createPortal(node, document.body);
  }

  // ── dialog: slot-driven small CENTERED modal, no `item` required.
  // Added v1.7 for Profile LogoutConfirmModal (§S39). Uses the existing
  // mc-mb--dialog backdrop positioning (align-items:center). Accepts the
  // same header/body/footer slot props as sheetCustom.
  if (effLayout === 'dialog') {
    const node = (
      <div className={`mc-mb mc-mb--dialog ${inline ? 'mc-mb--inline' : ''}`}
           dir={dir} lang={locale}
           onClick={onClose}>
        <div className="mc-mb-dialog-slot" onClick={(e) => e.stopPropagation()}>
          <button className="mc-mb-close" onClick={onClose} type="button" aria-label="Close">
            <MCIcon kind="close" size={16}/>
          </button>
          {header && <div className="mc-mb-dialog-slot-header">{header}</div>}
          {body   && <div className="mc-mb-dialog-slot-body">{body}</div>}
          {footer && <div className="mc-mb-dialog-slot-footer">{footer}</div>}
        </div>
      </div>
    );
    return inline ? node : ReactDOM.createPortal(node, document.body);
  }

  // From here on, the rest of the shells (sheet / minimal / sideBySide) need
  // a real `item` to render hero card / meta / actions.
  if (!item) return null;
  const accent = modalAccent(item);

  const handleAdd = () => { onAddToLineup && onAddToLineup(item); };
  const handleHist = () => { onViewHistory && onViewHistory(item); };
  const handleShare = () => { onShare && onShare(item); };

  // Resolve the active primary CTA. If `primaryCta` prop given, use it as-is.
  // Otherwise build the default "Add to Lineup" action wired to onAddToLineup.
  const resolvedCta = (primaryCta !== undefined) ? primaryCta : {
    label: MC_I18N[locale].actAddToLineup,
    icon: 'add',
    onClick: handleAdd,
    disabled: item.status === 'reserved' || item.status === 'expired',
  };

  // — sheet (mobile): grabber + scrollable body
  if (effLayout === 'sheet') {
    const node = (
      <div className={`mc-mb mc-mb--sheet ${inline ? 'mc-mb--inline' : ''}`} dir={dir} lang={locale}
           onClick={onClose}
           style={{ '--accent': accent.color, '--accent-glow': accent.glow }}>
        <div className="mc-mb-sheet-card" onClick={(e) => e.stopPropagation()}>
          <div className="mc-mb-handle"></div>
          <button className="mc-mb-close" onClick={onClose} type="button" aria-label="Close">
            <MCIcon kind="close" size={16} />
          </button>
          <div className="mc-mb-accent-line" style={{ background: accent.color }}></div>

          <div className="mc-mb-sheet-body">
            <div className="mc-mb-sheet-hero">
              <ModalHeroCard item={item} locale={locale} />
            </div>
            <div className="mc-mb-sheet-info">
              <ModalTitleBlock item={item} locale={locale} accent={accent} layout="sheet" />
              <TypeBody item={item} locale={locale} accent={accent} />
              {!hideMeta && <MetaRow item={item} locale={locale} />}
            </div>
          </div>
          <div className="mc-mb-sheet-foot">
            <ModalActions item={item} locale={locale}
              primaryCta={resolvedCta} onHistory={handleHist} onShare={handleShare}
              hideHistory={hideHistory}/>
          </div>
        </div>
      </div>
    );
    return inline ? node : ReactDOM.createPortal(node, document.body);
  }

  // — minimal: card + a single stat row, no meta
  if (effLayout === 'minimal') {
    const node = (
      <div className={`mc-mb mc-mb--dialog ${inline ? 'mc-mb--inline' : ''}`} dir={dir} lang={locale}
           onClick={onClose}
           style={{ '--accent': accent.color, '--accent-glow': accent.glow }}>
        <div className="mc-mb-dialog-card mc-mb-dialog-card--narrow" onClick={(e) => e.stopPropagation()}>
          <button className="mc-mb-close" onClick={onClose} type="button" aria-label="Close">
            <MCIcon kind="close" size={16} />
          </button>
          <div className="mc-mb-accent-line" style={{ background: accent.color }}></div>
          <div className="mc-mb-dialog-narrow-hero">
            <ModalHeroCard item={item} locale={locale} />
          </div>
          <ModalTitleBlock item={item} locale={locale} accent={accent} layout="minimal" />
          <div className="mc-mb-actions-wrap">
            <ModalActions item={item} locale={locale}
              primaryCta={resolvedCta} onHistory={handleHist} onShare={handleShare}
              hideHistory={hideHistory}/>
          </div>
        </div>
      </div>
    );
    return inline ? node : ReactDOM.createPortal(node, document.body);
  }

  // — sideBySide: dialog with hero card on right, info on left
  const node = (
    <div className={`mc-mb mc-mb--dialog ${inline ? 'mc-mb--inline' : ''}`} dir={dir} lang={locale}
         onClick={onClose}
         style={{ '--accent': accent.color, '--accent-glow': accent.glow }}>
      <div className="mc-mb-dialog-card mc-mb-dialog-card--wide" onClick={(e) => e.stopPropagation()}>
        <button className="mc-mb-close" onClick={onClose} type="button" aria-label="Close">
          <MCIcon kind="close" size={16} />
        </button>
        <div className="mc-mb-accent-line" style={{ background: accent.color }}></div>
        <div className="mc-mb-side-grid">
          <div className="mc-mb-side-card">
            <ModalHeroCard item={item} locale={locale} />
          </div>
          <div className="mc-mb-side-info">
            <ModalTitleBlock item={item} locale={locale} accent={accent} layout="sideBySide" />
            <TypeBody item={item} locale={locale} accent={accent} />
            {!hideMeta && <MetaRow item={item} locale={locale} />}
            <div className="mc-mb-actions-wrap">
              <ModalActions item={item} locale={locale}
                primaryCta={resolvedCta} onHistory={handleHist} onShare={handleShare}
                hideHistory={hideHistory}/>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
  return inline ? node : ReactDOM.createPortal(node, document.body);
}

Object.assign(window, {
  MyCardsDetailModal, MCIcon, modalAccent,
  ModalHeroCard, PositionScoresGrid, MetaRow, TypeBody, ModalActions, ModalTitleBlock,
  SheetCustomShell,
});

// ─── styles ─────────────────────────────────────────────────────────────────
const MC_MODAL_CSS = `
  /* ───── BACKDROP (shared) ───── */
  .mc-mb {
    position: fixed; inset: 0;
    z-index: 99999;
    background: rgba(4,4,8, 0.72);
    backdrop-filter: blur(18px) saturate(140%);
    -webkit-backdrop-filter: blur(18px) saturate(140%);
    display: flex;
    align-items: flex-end;
    justify-content: center;
    font-family: var(--font-current);
    animation: mc-mb-fade .18s ease;
  }
  .mc-mb--inline {
    position: absolute;
    z-index: 1;
  }
  .mc-mb--dialog { align-items: center; padding: 24px; }

  /* ── dialog-slot (v1.7) — slot-driven centered panel (§S39 LogoutConfirm) ── */
  .mc-mb-dialog-slot {
    position: relative;
    width: 100%;
    max-width: 340px;
    background: var(--bg-elevated, #15151F);
    border: 1px solid rgba(255,255,255,0.10);
    border-radius: 20px;
    padding: 28px 24px 20px;
    box-shadow: 0 24px 60px rgba(0,0,0,0.55), 0 0 40px rgba(0,0,0,0.25);
    color: var(--text-primary);
    font-family: var(--font-current);
    display: flex;
    flex-direction: column;
    animation: mc-mb-pop .22s cubic-bezier(.2,.7,.3,1.1);
  }
  .mc-mb-dialog-slot-header { /* header slot — no margin needed; child manages spacing */ }
  .mc-mb-dialog-slot-body   { margin-block-start: 0; }
  .mc-mb-dialog-slot-footer { margin-block-start: 16px; }
  @keyframes mc-mb-fade { from { opacity: 0; } to { opacity: 1; } }
  @keyframes mc-mb-slide-up {
    from { transform: translateY(40px); opacity: 0; }
    to   { transform: none; opacity: 1; }
  }
  @keyframes mc-mb-pop {
    from { transform: scale(0.95); opacity: 0; }
    to   { transform: none; opacity: 1; }
  }

  /* ───── SHEET CARD ───── */
  .mc-mb-sheet-card {
    position: relative;
    width: 100%;
    max-width: 480px;
    max-height: 92vh;
    background: linear-gradient(180deg, rgba(22,22,32,0.98) 0%, rgba(10,10,16,0.98) 100%);
    border: 1px solid rgba(255,255,255,0.10);
    border-bottom: 0;
    border-radius: 28px 28px 0 0;
    box-shadow:
      0 -30px 80px rgba(0,0,0,0.6),
      0 0 80px var(--accent-glow);
    color: var(--text-primary);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    animation: mc-mb-slide-up .26s cubic-bezier(.2,.7,.3,1.1);
  }
  .mc-mb-handle {
    margin: 8px auto 4px;
    width: 36px; height: 4px;
    border-radius: 4px;
    background: rgba(255,255,255,0.18);
    flex-shrink: 0;
  }
  .mc-mb-close {
    position: absolute; top: 12px; inset-inline-end: 14px;
    width: 32px; height: 32px;
    border-radius: 50%;
    border: 1px solid rgba(255,255,255,0.10);
    background: rgba(255,255,255,0.04);
    color: var(--text-secondary);
    display: flex; align-items: center; justify-content: center;
    cursor: pointer;
    font-family: inherit !important;
    transition: background .12s, color .12s, transform .12s;
  }
  .mc-mb-close:hover { background: rgba(255,255,255,0.08); color: var(--text-primary); }
  .mc-mb-close:active { transform: scale(0.94); }
  .mc-mb-accent-line {
    position: absolute; top: 0; left: 0; right: 0;
    height: 2px;
    border-radius: 0;
    opacity: 0.85;
    /* Fade the colored bar into the modal background at both ends — solid in
       the middle, fully transparent at the corners. Mask runs in physical
       pixels so it works identically in RTL/LTR. */
    -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 22%, #000 78%, transparent 100%);
    mask-image: linear-gradient(90deg, transparent 0%, #000 22%, #000 78%, transparent 100%);
  }

  .mc-mb-sheet-body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 8px 20px 16px;
    display: flex;
    flex-direction: column;
    gap: 16px;
    scrollbar-width: thin;
  }
  .mc-mb-sheet-hero {
    display: flex;
    justify-content: center;
    padding: 4px 24px 0;
    pointer-events: none;
  }
  .mc-mb-sheet-hero .mc-hero {
    width: 100%;
    max-width: 240px;
  }
  .mc-mb-sheet-info {
    display: flex; flex-direction: column; gap: 16px;
  }
  .mc-mb-sheet-foot {
    padding: 12px 20px 18px;
    border-top: 1px solid rgba(255,255,255,0.05);
    background: rgba(8,8,14,0.4);
    flex-shrink: 0;
  }

  /* ───── DIALOG CARD (side-by-side & minimal) ───── */
  .mc-mb-dialog-card {
    position: relative;
    background: linear-gradient(180deg, rgba(22,22,32,0.98) 0%, rgba(10,10,16,0.98) 100%);
    border: 1px solid rgba(255,255,255,0.10);
    border-radius: 28px;
    box-shadow:
      0 30px 90px rgba(0,0,0,0.6),
      0 0 80px var(--accent-glow);
    color: var(--text-primary);
    overflow: hidden;
    animation: mc-mb-pop .22s cubic-bezier(.2,.7,.3,1.2);
    max-height: 92vh;
    display: flex;
    flex-direction: column;
  }
  .mc-mb-dialog-card--narrow { width: min(360px, 92vw); padding: 36px 24px 24px; }
  .mc-mb-dialog-card--wide   { width: min(820px, 94vw); padding: 28px; }

  .mc-mb-dialog-narrow-hero {
    display: flex; justify-content: center;
    margin-bottom: 18px;
    pointer-events: none;
  }
  .mc-mb-dialog-narrow-hero .mc-hero { width: 220px; }

  .mc-mb-side-grid {
    display: grid;
    grid-template-columns: 260px 1fr;
    gap: 28px;
    align-items: start;
    overflow: hidden;
  }
  html[dir="ltr"] .mc-mb-side-grid { grid-template-columns: 260px 1fr; }
  .mc-mb-side-card {
    display: flex; justify-content: center;
    padding-top: 4px;
    pointer-events: none;
  }
  .mc-mb-side-card .mc-hero { width: 260px; }
  .mc-mb-side-info {
    display: flex; flex-direction: column;
    gap: 18px;
    min-width: 0;
    overflow-y: auto;
    max-height: 78vh;
    padding-inline-end: 4px;
  }

  /* ───── HERO BLOCK (redesigned) ─────
     Just the visual essentials of the card: tier-tinted halo, the item image,
     and a glass overall/headline pill. No chrome (no tier badge, no name plate,
     no stat panel) — those live in the title block + meta grid beside it. */
  .mc-hero {
    position: relative;
    width: 100%;
    aspect-ratio: 4 / 5;             /* taller than wide */
    border-radius: 22px;
    overflow: hidden;
    isolation: isolate;
    box-shadow:
      0 1px 0 rgba(255,255,255,0.06) inset,
      0 0 0 1px var(--hero-rim, rgba(255,255,255,0.10)) inset,
      0 20px 60px -10px var(--hero-glow, rgba(0,0,0,0.4)),
      0 30px 70px -20px rgba(0,0,0,0.55);
  }
  .mc-hero-field {
    position: absolute; inset: 0;
    z-index: 0;
  }
  .mc-hero-vignette {
    position: absolute; inset: 0;
    z-index: 1;
    pointer-events: none;
    background:
      radial-gradient(ellipse 110% 50% at 50% 0%, rgba(0,0,0,0.40), transparent 55%),
      radial-gradient(ellipse 110% 55% at 50% 100%, rgba(0,0,0,0.55), transparent 50%);
  }
  .mc-hero-img {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    margin: 0 auto;
    z-index: 2;
    width: 86%;
    height: 96%;
    object-fit: contain;
    object-position: bottom center;
    transform-origin: bottom center;
    filter: drop-shadow(0 18px 28px rgba(0,0,0,0.55));
    user-select: none;
    pointer-events: none;
  }
  .mc-hero-pill {
    position: absolute;
    bottom: 14px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 3;
    min-width: 110px;
    height: 64px;
    padding: 4px 18px;
    border-radius: 18px;
    border: 1px solid;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0;
    overflow: hidden;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    background-color: rgba(0, 0, 0, 0.40);
  }
  .mc-hero-pill-glow {
    position: absolute;
    inset: -20%;
    filter: blur(4px);
    pointer-events: none;
  }
  .mc-hero-pill-value {
    position: relative;
    font-size: 30px;
    font-weight: 900;
    letter-spacing: -0.02em;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    text-shadow: 0 1px 6px rgba(0,0,0,0.55);
    font-family: 'JetBrains Mono', ui-monospace, monospace;
  }
  html[lang="fa"] .mc-hero-pill-value,
  html[lang="ar"] .mc-hero-pill-value { font-family: inherit; }
  .mc-hero-pill-label {
    position: relative;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.10em;
    text-transform: uppercase;
    margin-top: 2px;
    opacity: 0.92;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
  }
  html[lang="fa"] .mc-hero-pill-label,
  html[lang="ar"] .mc-hero-pill-label {
    font-family: inherit;
    letter-spacing: 0.02em;
    text-transform: none;
    font-size: 11px;
  }

  /* ───── TITLE BLOCK ───── */
  .mc-title-block { display: flex; flex-direction: column; gap: 2px; }
  .mc-title {
    margin: 0;
    font-size: 26px;
    font-weight: 800;
    line-height: 1.15;
    letter-spacing: -0.01em;
    color: var(--text-primary);
    text-wrap: pretty;
  }
  .mc-title-sub {
    font-size: 13px;
    color: var(--text-secondary);
    line-height: 1.4;
  }
  .mc-title-tert {
    margin-top: 4px;
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.01em;
  }

  /* ───── SECTION ───── */
  .mc-section-label {
    font-size: 11px;
    font-weight: 700;
    color: var(--text-tertiary);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
  }
  html[lang="fa"] .mc-section-label,
  html[lang="ar"] .mc-section-label {
    font-family: inherit;
    text-transform: none;
    letter-spacing: 0;
    font-size: 12px;
    color: var(--text-secondary);
  }
  .mc-section-meta {
    font-size: 11px;
    color: var(--text-secondary);
    font-family: 'JetBrains Mono', ui-monospace, monospace;
  }
  html[lang="fa"] .mc-section-meta,
  html[lang="ar"] .mc-section-meta { font-family: inherit; font-size: 12px; }
  .mc-section-meta strong {
    color: var(--text-primary);
    font-weight: 700;
  }
  .mc-section-hint {
    font-size: 11px;
    color: var(--text-tertiary);
    line-height: 1.45;
    margin-top: 8px;
  }
  html[lang="fa"] .mc-section-hint,
  html[lang="ar"] .mc-section-hint { font-size: 12px; }

  /* ───── Position scores grid (player) ───── */
  .mc-pos-scores { display: flex; flex-direction: column; gap: 10px; }
  .mc-pos-scores-head {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
  }
  .mc-pos-scores-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 8px;
  }
  .mc-pos-cell {
    border: 1px solid rgba(255,255,255,0.06);
    background: rgba(255,255,255,0.02);
    border-radius: 12px;
    padding: 10px 8px 8px;
    text-align: center;
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    color: var(--text-tertiary);
    transition: background .12s, border-color .12s;
  }
  .mc-pos-cell.is-main {
    border-color: var(--accent);
    background: linear-gradient(180deg, rgba(113,99,217,0.16) 0%, rgba(113,99,217,0.05) 100%);
    color: var(--text-primary);
    box-shadow: 0 8px 20px rgba(113,99,217,0.18);
  }
  .mc-pos-cell-label {
    font-size: 10px;
    font-weight: 600;
    opacity: 0.85;
    line-height: 1.1;
  }
  .mc-pos-cell-val {
    font-size: 22px;
    font-weight: 800;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
  }
  .mc-pos-cell.is-main .mc-pos-cell-val { color: #C8C0FF; }
  html[lang="fa"] .mc-pos-cell-val,
  html[lang="ar"] .mc-pos-cell-val { font-family: inherit; font-size: 24px; }
  .mc-pos-cell-key {
    font-size: 9px;
    font-weight: 600;
    letter-spacing: 0.06em;
    opacity: 0.7;
    display: inline-flex; align-items: center; gap: 3px;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
  }
  html[lang="fa"] .mc-pos-cell-key,
  html[lang="ar"] .mc-pos-cell-key { font-family: inherit; letter-spacing: 0; font-size: 10px; }

  /* ───── Stat triplet (coach/fan) ───── */
  .mc-stat-triplet {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
  }
  .mc-stat-cell {
    border: 1px solid rgba(255,255,255,0.05);
    background: rgba(255,255,255,0.025);
    border-radius: 12px;
    padding: 10px 8px;
    display: flex; flex-direction: column; gap: 2px;
    align-items: center;
    text-align: center;
  }
  .mc-stat-label {
    font-size: 10px;
    color: var(--text-tertiary);
    font-weight: 600;
    line-height: 1.1;
  }
  html[lang="fa"] .mc-stat-label,
  html[lang="ar"] .mc-stat-label { font-size: 11px; }
  .mc-stat-value {
    font-size: 18px;
    font-weight: 800;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
    color: var(--text-primary);
    font-family: 'JetBrains Mono', ui-monospace, monospace;
  }
  html[lang="fa"] .mc-stat-value,
  html[lang="ar"] .mc-stat-value { font-family: inherit; }

  /* ───── Callout ───── */
  .mc-callout {
    display: flex;
    align-items: flex-start;
    flex-direction: column;
    gap: 4px;
    padding: 12px 14px;
    border-radius: 12px;
    background: rgba(255,255,255,0.025);
    border: 1px solid rgba(255,255,255,0.05);
  }
  .mc-callout-label {
    font-size: 10px;
    color: var(--text-tertiary);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
  }
  html[lang="fa"] .mc-callout-label,
  html[lang="ar"] .mc-callout-label {
    font-family: inherit;
    text-transform: none;
    letter-spacing: 0;
    font-size: 11px;
  }
  .mc-callout-value {
    font-size: 13px;
    color: var(--text-primary);
    font-weight: 600;
    line-height: 1.4;
  }

  /* ───── Null body text ───── */
  .mc-null-body { display: flex; flex-direction: column; gap: 10px; }
  .mc-null-text {
    margin: 0;
    font-size: 13px;
    color: var(--text-secondary);
    line-height: 1.55;
  }

  /* ───── Meta grid ───── */
  .mc-meta-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px 16px;
    padding-top: 4px;
    border-top: 1px solid rgba(255,255,255,0.05);
    padding: 12px 0 0;
  }
  .mc-meta-cell { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
  .mc-meta-label {
    font-size: 10px;
    color: var(--text-tertiary);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  html[lang="fa"] .mc-meta-label,
  html[lang="ar"] .mc-meta-label {
    font-family: inherit;
    text-transform: none;
    letter-spacing: 0;
    font-size: 11px;
  }
  .mc-meta-value {
    font-size: 13px;
    color: var(--text-primary);
    font-weight: 600;
    line-height: 1.35;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .mc-meta-status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 8px;
    border-radius: 999px;
    width: fit-content;
    border: 1px solid rgba(255,255,255,0.08);
    font-size: 11px;
  }
  .mc-meta-status--available {
    color: var(--success);
    border-color: rgba(52,211,153,0.22);
    background: rgba(52,211,153,0.08);
  }
  .mc-meta-status--reserved {
    color: var(--accent);
    border-color: rgba(113,99,217,0.30);
    background: rgba(113,99,217,0.10);
  }
  .mc-meta-status--expired {
    color: var(--text-tertiary);
    border-color: rgba(255,255,255,0.08);
    background: rgba(255,255,255,0.02);
  }

  /* ───── Actions ───── */
  .mc-mb-actions-wrap { padding-top: 8px; }
  .mc-actions {
    display: flex;
    align-items: stretch;
    gap: 8px;
  }
  .mc-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 12px 18px;
    border-radius: 999px;
    border: 1px solid;
    font-family: inherit !important;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    transition: background .14s, border-color .14s, transform .12s, box-shadow .15s, opacity .14s;
    line-height: 1;
    white-space: nowrap;
  }
  .mc-btn:active:not(.is-disabled):not(:disabled) { transform: translateY(0) scale(0.97); }

  /* PRIMARY — clean glass pill, same design language as Lineup's Submit CTA.
     No purple gradient; the lift comes from translucency + edge highlights. */
  .mc-btn--primary {
    flex: 1 1 auto;
    color: #FFFFFF;
    border-color: rgba(255,255,255,0.22);
    background:
      linear-gradient(180deg,
        rgba(255,255,255,0.10) 0%,
        rgba(255,255,255,0.04) 100%),
      rgba(0, 0, 0, 0.35);
    backdrop-filter: blur(10px) saturate(140%);
    -webkit-backdrop-filter: blur(10px) saturate(140%);
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.18),
      inset 0 -1px 0 rgba(0,0,0,0.20),
      0 4px 14px rgba(0,0,0,0.30);
    text-shadow: 0 1px 2px rgba(0,0,0,0.45);
  }
  .mc-btn--primary:hover:not(:disabled) {
    transform: translateY(-1px);
    border-color: rgba(255,255,255,0.34);
    background:
      linear-gradient(180deg,
        rgba(255,255,255,0.14) 0%,
        rgba(255,255,255,0.06) 100%),
      rgba(0, 0, 0, 0.30);
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.24),
      inset 0 -1px 0 rgba(0,0,0,0.20),
      0 6px 18px rgba(0,0,0,0.40);
  }
  .mc-btn--primary.is-disabled,
  .mc-btn--primary:disabled {
    opacity: 0.40;
    cursor: not-allowed;
    border-color: rgba(255,255,255,0.08);
    background: rgba(255,255,255,0.03);
    text-shadow: none;
    box-shadow: none;
    color: var(--text-tertiary);
  }
  .mc-btn--secondary {
    background: rgba(255,255,255,0.04);
    color: var(--text-secondary);
    border-color: rgba(255,255,255,0.10);
  }
  .mc-btn--secondary:hover { background: rgba(255,255,255,0.08); color: var(--text-primary); }
  .mc-btn--icon-ghost {
    flex: 0 0 auto;
    width: 44px;
    padding: 0;
    background: rgba(255,255,255,0.03);
    border-color: rgba(255,255,255,0.08);
    color: var(--text-secondary);
  }
  .mc-btn--icon-ghost:hover { background: rgba(255,255,255,0.08); color: var(--text-primary); }
  html[dir="rtl"] .mc-btn svg { transform: scaleX(-1); }

  /* ───── Responsive: dialog → fall back to sheet on narrow ───── */
  @media (max-width: 660px) {
    .mc-mb-side-grid {
      grid-template-columns: 1fr;
      gap: 18px;
    }
    .mc-mb-dialog-card--wide { width: min(560px, 94vw); padding: 24px; }
  }

  /* ─────────────────────────────────────────────────────────────────────
   * SHEET CUSTOM — generic slot-driven sheet (v1.3, Prediction Hub refactor)
   * Adds:
   *   • sticky header (with crests / title / close)
   *   • scrollable body (forms, lists)
   *   • sticky footer (submit CTA)
   *   • swipeable handle zone (touch-drag the .mc-mb-handle-zone or header
   *     downward → translateY; >120px → dismiss)
   *   • mobileHeight override (90vh for Prediction, 92vh default)
   * Reuses the canonical .mc-mb backdrop + .mc-mb-sheet-card chrome.
   * ───────────────────────────────────────────────────────────────────── */
  .mc-mb-sheet-card--custom {
    padding: 0;
    /* Height is applied inline from the mobileHeight prop. */
  }
  .mc-mb-handle-zone {
    padding: 6px 0 4px;
    flex-shrink: 0;
    touch-action: none;
    cursor: grab;
  }
  .mc-mb-handle-zone:active { cursor: grabbing; }
  .mc-mb-sheet-card--custom .mc-mb-handle {
    margin: 0 auto;
  }
  .mc-mb-sheet-header {
    position: relative;
    flex-shrink: 0;
    padding: 6px 20px 14px;
    border-bottom: 1px solid rgba(255,255,255,0.06);
    background: linear-gradient(180deg, rgba(22,22,32,0.96) 0%, rgba(18,18,28,0.94) 100%);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    touch-action: none;
  }
  .mc-mb-close--header { top: 8px; }
  html[dir="rtl"] .mc-mb-close--header { inset-inline-end: 16px; }
  .mc-mb-sheet-body--custom {
    padding: 16px 16px 12px;
    gap: 14px;
  }
  .mc-mb-sheet-foot--custom {
    padding: 12px 16px calc(14px + env(safe-area-inset-bottom, 0px));
  }
`;

const __mcModalStyle = document.createElement('style');
__mcModalStyle.textContent = MC_MODAL_CSS;
document.head.appendChild(__mcModalStyle);
