/* Shared primitives + fake data + icons */
const { useState, useEffect, useRef, useMemo, useLayoutEffect, useCallback } = React;

// ---------- Icon ----------
function Icon({ name, size = 20, fill = false, className = '', style = {} }) {
  return (
    <span
      className={`ms ${fill ? 'fill' : ''} ${className}`}
      style={{ fontSize: size, ...style }}
    >
      {name}
    </span>
  );
}

// ---------- Channel glyph (native social-platform mark) ----------
function ChannelGlyph({ ch, size = 14, style = {} }) {
  const bgClass = {
    ig: 'channel-ig', tt: 'channel-tt', yt: 'channel-yt',
    li: 'channel-li', x: 'channel-x',  fb: 'channel-fb',
  }[ch] || 'channel-ig';

  const dim = size + 6;
  const inner = size + 1;

  const Svg = (() => {
    switch (ch) {
      case 'ig':
        return (
          <svg width={inner} height={inner} viewBox="0 0 24 24" fill="none"
               stroke="white" strokeWidth="2.1" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
            <rect x="3" y="3" width="18" height="18" rx="5" />
            <circle cx="12" cy="12" r="4" />
            <circle cx="17.4" cy="6.6" r="0.9" fill="white" stroke="none" />
          </svg>
        );
      case 'tt':
        return (
          <svg width={inner} height={inner} viewBox="0 0 24 24" fill="white" aria-hidden="true">
            <path d="M19.59 6.69a4.83 4.83 0 0 1-3.77-4.25V2h-3.45v13.67a2.89 2.89 0 0 1-5.2 1.74 2.89 2.89 0 0 1 2.31-4.64 2.93 2.93 0 0 1 .88.13V9.4a6.84 6.84 0 0 0-1-.05A6.33 6.33 0 0 0 5.8 20.1a6.34 6.34 0 0 0 10.86-4.43V8.85a8.16 8.16 0 0 0 4.77 1.52v-3.4a4.85 4.85 0 0 1-1.84-.28z" />
          </svg>
        );
      case 'yt':
        return (
          <svg width={inner} height={inner} viewBox="0 0 24 24" fill="white" aria-hidden="true">
            <polygon points="9.5,7.8 16.5,12 9.5,16.2" />
          </svg>
        );
      case 'li':
        return (
          <svg width={inner} height={inner} viewBox="0 0 24 24" fill="white" aria-hidden="true">
            <path d="M4.98 3.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5zM3 9h4v12H3V9zm7 0h3.8v1.7h.05c.53-1 1.83-2.05 3.77-2.05 4.03 0 4.78 2.65 4.78 6.1V21H18.6v-5.55c0-1.32-.02-3.02-1.84-3.02-1.84 0-2.12 1.44-2.12 2.92V21H10V9z" />
          </svg>
        );
      case 'x':
        return (
          <svg width={inner} height={inner} viewBox="0 0 24 24" fill="white" aria-hidden="true">
            <path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231 5.451-6.231zm-1.161 17.52h1.833L7.084 4.126H5.117l11.966 15.644z" />
          </svg>
        );
      case 'fb':
        return (
          <svg width={inner} height={inner} viewBox="0 0 24 24" fill="white" aria-hidden="true">
            <path d="M13.5 21v-8.4h2.82l.42-3.27H13.5V7.24c0-.94.26-1.58 1.62-1.58h1.73V2.73c-.3-.04-1.33-.13-2.52-.13-2.5 0-4.2 1.52-4.2 4.32v2.41H7.3v3.27h2.83V21h3.37z" />
          </svg>
        );
      default:
        return null;
    }
  })();

  return (
    <span
      className={bgClass}
      style={{
        width: dim, height: dim,
        borderRadius: 6,
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        flexShrink: 0, lineHeight: 0,
        ...style,
      }}
      aria-label={ch}
    >
      {Svg}
    </span>
  );
}

// ---------- Flag (emoji wrap) ----------
function Flag({ code, size = 16 }) {
  const map = { de: '🇩🇪', uk: '🇬🇧', pl: '🇵🇱', ee: '🇪🇪', lt: '🇱🇹', lv: '🇱🇻' };
  return <span style={{ fontSize: size }}>{map[code] || '🏳️'}</span>;
}

// ---------- Animated counter ----------
function Counter({ to, duration = 700, format = (n) => Math.round(n) }) {
  const [v, setV] = useState(0);
  useEffect(() => {
    const start = performance.now();
    let raf;
    const tick = (t) => {
      const p = Math.min(1, (t - start) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setV(to * eased);
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [to, duration]);
  return <span className="num">{format(v)}</span>;
}

// ---------- Fake data ----------
const MARKETS = [
  { code: 'de', name: 'Germany' },
  { code: 'uk', name: 'United Kingdom' },
  { code: 'pl', name: 'Poland' },
  { code: 'ee', name: 'Estonia' },
  { code: 'lt', name: 'Lithuania' },
  { code: 'lv', name: 'Latvia' },
];
const CHANNELS = [
  { code: 'ig', name: 'Instagram' },
  { code: 'fb', name: 'Facebook' },
  { code: 'tt', name: 'TikTok' },
  { code: 'yt', name: 'YouTube' },
];
// Channels we still consider "active" — used to filter out stale posts from
// previous app versions (e.g. localStorage seeded with li / x posts).
const ALLOWED_CH = new Set(CHANNELS.map(c => c.code));

// Channel-specific post formats — cross-checked against each platform's
// official content-publishing API (Meta Graph, TikTok Content Posting,
// YouTube Data API v3). Only formats that can be scheduled / published
// programmatically are listed. Live broadcasts are real-time events with a
// separate flow (Live Producer / YouTube Live Studio) so they're not part of
// the scheduling tool.
const KINDS_BY_CHANNEL = {
  ig: [
    { id: 'photo',    label: 'Photo',    icon: 'photo' },
    { id: 'video',    label: 'Video',    icon: 'movie' },
    { id: 'reel',     label: 'Reel',     icon: 'motion_photos_on' },
    { id: 'story',    label: 'Story',    icon: 'auto_stories' },
    { id: 'carousel', label: 'Carousel', icon: 'view_carousel' },
  ],
  fb: [
    { id: 'photo',    label: 'Photo',    icon: 'photo' },
    { id: 'video',    label: 'Video',    icon: 'movie' },
    { id: 'reel',     label: 'Reel',     icon: 'motion_photos_on' },
    { id: 'story',    label: 'Story',    icon: 'auto_stories' },
    { id: 'carousel', label: 'Carousel', icon: 'view_carousel' },
    { id: 'link',     label: 'Link',     icon: 'link' },
  ],
  tt: [
    { id: 'video',    label: 'Video',    icon: 'movie' },
    { id: 'photo',    label: 'Photo',    icon: 'photo' },
  ],
  yt: [
    { id: 'video',    label: 'Long-form', icon: 'movie' },
    { id: 'short',    label: 'Short',     icon: 'smartphone' },
    { id: 'premiere', label: 'Premiere',  icon: 'live_tv' },
  ],
};

// Per-format publishing specs (aspect ratio, max duration, caption + cover
// hints). Drives the Format panel hint card and the thumbnail uploader.
// Values track the public docs as of 2026.
const KIND_SPECS = {
  ig: {
    photo:    { aspect: '1:1 · 4:5', duration: null,    cover: false, caption: 2200, note: 'JPG/PNG up to 8 MB' },
    video:    { aspect: '4:5 · 9:16', duration: 'up to 60 min', cover: true, caption: 2200, note: 'Auto-cross-posts to Reels' },
    reel:     { aspect: '9:16',      duration: 'up to 3 min',  cover: true,  caption: 2200, note: 'Trending sounds OK' },
    story:    { aspect: '9:16',      duration: 'up to 60 s',   cover: false, caption: null, note: 'Stickers added after publish' },
    carousel: { aspect: '1:1 · 4:5', duration: null,           cover: false, caption: 2200, note: '2–10 images / videos' },
  },
  fb: {
    photo:    { aspect: 'flex',  duration: null,            cover: false, caption: 63206, note: 'JPG/PNG up to 30 MB' },
    video:    { aspect: '16:9 · 9:16', duration: 'up to 240 min', cover: true, caption: 63206, note: 'HD recommended' },
    reel:     { aspect: '9:16',  duration: 'up to 90 s',    cover: true,  caption: 2200,  note: 'Vertical only' },
    story:    { aspect: '9:16',  duration: 'up to 20 s',    cover: false, caption: null,  note: '24-hour lifetime' },
    carousel: { aspect: '1:1',   duration: null,            cover: false, caption: 63206, note: '2–10 cards' },
    link:     { aspect: '1.91:1', duration: null,           cover: false, caption: 63206, note: 'Auto-fetches OG image' },
  },
  tt: {
    video: { aspect: '9:16', duration: 'up to 10 min', cover: true,  caption: 2200, note: 'Vertical, sound on' },
    photo: { aspect: '9:16', duration: null,           cover: false, caption: 2200, note: '2–35 image slideshow' },
  },
  yt: {
    video:    { aspect: '16:9', duration: 'up to 12 h',    cover: true, caption: 5000, note: 'Custom thumbnail recommended' },
    short:    { aspect: '9:16', duration: 'up to 60 s',    cover: true, caption: 100,  note: 'Vertical only' },
    premiere: { aspect: '16:9', duration: 'up to 12 h',    cover: true, caption: 5000, note: 'Countdown + live chat' },
  },
};

// Kinds that allow a custom thumbnail / cover image upload. Cross-checked
// against each platform's content-publishing API — IG Reel & Video, FB Reel
// & Video, every TikTok video, and every YouTube format support a cover.
function kindAllowsThumbnail(ch, kindId) {
  const spec = (KIND_SPECS[ch] || {})[kindId];
  return !!(spec && spec.cover);
}

function kindSpec(ch, kindId) {
  return (KIND_SPECS[ch] || {})[kindId] || null;
}

function kindsForChannel(ch) {
  return KINDS_BY_CHANNEL[ch] || KINDS_BY_CHANNEL.ig;
}
function defaultKindForChannel(ch) {
  return kindsForChannel(ch)[0].id;
}
function kindMeta(ch, kindId) {
  const list = kindsForChannel(ch);
  return list.find(k => k.id === kindId) || list[0];
}

// ---------- Planning sign ----------
// Visual urgency signal for the calendar. Encodes "is this composed and ready,
// or does it still need work, and how soon is the slot?"
//
// Rules:
//   live                                            → green  (already posted)
//   scheduled + has media                           → green  (composed & ready)
//   idea, ≤3 days away                              → red    (no time)
//   idea, >3 days away                              → yellow (time to compose)
//   anything else (draft / scheduled-no-media):
//        ≤1 day away                                → red
//        otherwise                                  → yellow
function planningSign(post, today = TODAY) {
  if (post.status === 'live') {
    return { level: 'live',  color: 'var(--good)', label: 'Live' };
  }
  if (post.status === 'scheduled' && post.media) {
    return { level: 'ready', color: 'var(--good)', label: 'Ready · composed' };
  }

  const today0   = new Date(today.getFullYear(), today.getMonth(), today.getDate());
  const postDate = new Date(
    post.y ?? today.getFullYear(),
    post.m ?? today.getMonth(),
    post.d,
  );
  const daysAway = Math.round((postDate - today0) / 86400000);
  const threshold = post.status === 'idea' ? 3 : 1;

  const whenLabel =
    daysAway < 0  ? 'Overdue' :
    daysAway === 0 ? 'today' :
    daysAway === 1 ? 'tomorrow' :
    `in ${daysAway} d`;

  if (daysAway <= threshold) {
    return {
      level: 'urgent',
      color: 'var(--bad)',
      label: post.status === 'idea' ? `Idea · ${whenLabel}` : `Needs composing · ${whenLabel}`,
    };
  }
  return {
    level: 'planned',
    color: 'var(--warn)',
    label: post.status === 'idea' ? `Idea · ${whenLabel}` : `Planned · compose ${whenLabel}`,
  };
}

// Small visual indicator used in calendar post chips.
// Idea posts get a lightbulb icon (so they pop visually); everything else uses
// a colored dot. Color encodes urgency (planningSign).
function PlanningSignDot({ post, size = 6 }) {
  const sign = planningSign(post);
  const isIdea = post.status === 'idea';
  if (isIdea) {
    return (
      <span title={sign.label} aria-label={sign.label} style={{
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        flexShrink: 0,
      }}>
        <Icon name="lightbulb" size={size + 5} fill style={{ color: sign.color }} />
      </span>
    );
  }
  return (
    <span title={sign.label} aria-label={sign.label} style={{
      width: size, height: size, borderRadius: 99,
      background: sign.color, flexShrink: 0,
      boxShadow: sign.level === 'urgent' ? `0 0 0 2px rgba(255,138,126,.18)` : undefined,
    }} />
  );
}

// May 2026 — calendar uses this as "current"
const TODAY = new Date(2026, 4, 14); // May 14 2026

// Every non-idea post is "compose-complete": campaign + media populated so the
// test data reflects what a real scheduled queue looks like. Ideas are left
// without media/campaign on purpose — that's what "idea" means in this product.
const POSTS = [
  // [day, hour, channel, market, title, kind, status, campaign, media]
  { d: 4,  h: 9,  ch: 'ig', mk: 'de', title: 'Neue Materialien — Carbon-Edition', kind: 'photo', status: 'live',      campaign: 'always-on-ig',  media: 'product_macro_001.jpg' },
  { d: 4,  h: 14, ch: 'tt', mk: 'de', title: 'Wie wir Aluminium recyceln',         kind: 'video', status: 'live',      campaign: 'sustainability', media: 'recycling_loop.mp4' },
  { d: 6,  h: 10, ch: 'ig', mk: 'uk', title: 'Engineering Tour · Pt. 02',          kind: 'video', status: 'scheduled', campaign: 'sustainability', media: 'engineer_clip_a.mp4' },
  { d: 7,  h: 17, ch: 'ig', mk: 'pl', title: 'Premiera kolekcji wiosna',           kind: 'photo', status: 'scheduled', campaign: 'spring-popup',  media: 'carousel_frame_01.jpg' },
  { d: 8,  h: 12, ch: 'tt', mk: 'uk', title: 'Behind the lens',                    kind: 'video', status: 'draft',     campaign: 'always-on-ig',  media: 'btsphoto_studio.jpg' },
  { d: 11, h: 9,  ch: 'ig', mk: 'de', title: 'Stories: Pop-Up Berlin',             kind: 'story', status: 'scheduled', campaign: 'spring-popup',  media: 'designer_interview_clip.mov' },
  { d: 11, h: 16, ch: 'ig', mk: 'de', title: 'Sustainability Report 2026',         kind: 'carousel', status: 'scheduled', campaign: 'sustainability', media: 'sustainability_q2_pg1.jpg' },
  { d: 13, h: 11, ch: 'ig', mk: 'lt', title: 'Pavasario kampanija',                kind: 'photo', status: 'scheduled', campaign: 'spring-popup',  media: 'carousel_frame_02.jpg' },
  { d: 14, h: 8,  ch: 'tt', mk: 'de', title: 'Morning Reveal Drop',                kind: 'video', status: 'scheduled', campaign: 'spring-popup',  media: 'reveal_anchor_v3.mp4' },
  { d: 14, h: 15, ch: 'ig', mk: 'uk', title: 'Spring Pop-Up — London',             kind: 'photo', status: 'scheduled', campaign: 'spring-popup',  media: 'london_storefront.jpg' },
  { d: 14, h: 19, ch: 'ig', mk: 'de', title: 'Late carousel — 6 frames',           kind: 'photo', status: 'idea' },
  { d: 15, h: 10, ch: 'yt', mk: 'de', title: 'Engineering Deep Dive — Episode 4',  kind: 'video', status: 'scheduled', campaign: 'sustainability', media: 'recycling_loop.mp4' },
  { d: 17, h: 12, ch: 'tt', mk: 'pl', title: 'Spring Pop-Up Trailer',              kind: 'video', status: 'scheduled', campaign: 'spring-popup',  media: 'london_lineup.mp4' },
  { d: 18, h: 9,  ch: 'ig', mk: 'ee', title: 'Tallinn Studio Tour',                kind: 'photo', status: 'idea' },
  { d: 20, h: 16, ch: 'ig', mk: 'lv', title: 'Riga reveal',                        kind: 'photo', status: 'idea' },
  { d: 21, h: 11, ch: 'yt', mk: 'uk', title: 'Q2 retrospective',                   kind: 'video', status: 'draft',     campaign: 'sustainability', media: 'engineer_clip_a.mp4' },
  { d: 22, h: 14, ch: 'ig', mk: 'de', title: 'Kollektion Sommer — Teaser',         kind: 'video', status: 'idea' },
  { d: 25, h: 9,  ch: 'tt', mk: 'de', title: 'Day-in-the-life: Designer',          kind: 'video', status: 'scheduled', campaign: 'always-on-ig',  media: 'btsphoto_studio.jpg' },
  { d: 28, h: 17, ch: 'ig', mk: 'uk', title: 'June launch — countdown',            kind: 'photo', status: 'idea' },
  // Facebook — community-anchored long-form content
  { d: 5,  h: 11, ch: 'fb', mk: 'de', title: 'Materialgeschichte — Album',         kind: 'carousel', status: 'live',      campaign: 'always-on-ig',  media: 'product_macro_002.jpg' },
  { d: 12, h: 18, ch: 'fb', mk: 'uk', title: 'Spring Pop-Up event RSVP',           kind: 'link',     status: 'scheduled', campaign: 'spring-popup',  media: 'london_lineup.mp4' },
  { d: 16, h: 13, ch: 'fb', mk: 'de', title: 'Nachhaltigkeitsbericht Q2',          kind: 'link',     status: 'draft',     campaign: 'sustainability', media: 'sustainability_q2_pg2.jpg' },
  { d: 23, h: 19, ch: 'fb', mk: 'pl', title: 'Atelier Q&A — recap',                kind: 'video',    status: 'idea' },
];

const CAMPAIGNS = [
  {
    id: 'spring-popup',
    name: 'Spring Pop-Up · 2026',
    color: '#7aa7ff',
    status: 'active',
    start: 'May 04', end: 'Jun 02',
    posts: 24, scheduled: 9, live: 6, drafts: 3,
    markets: ['de','uk','pl','lt'],
    creatives: 36,
    source: 'email',
    summary: 'In-store reveal week across DE / UK / PL / LT — anchor video + 6-frame carousel + interview reel.',
  },
  {
    id: 'sustainability',
    name: 'Sustainability Report Q2',
    color: '#79d2a3',
    status: 'planning',
    start: 'May 18', end: 'Jun 30',
    posts: 12, scheduled: 4, live: 0, drafts: 6,
    markets: ['de','uk'],
    creatives: 18,
    source: 'manual',
    summary: 'Long-form report excerpts and engineer-led explainers for IG carousel + YouTube long-form.',
  },
  {
    id: 'always-on-ig',
    name: 'Always-On · IG',
    color: '#c8c6c5',
    status: 'active',
    start: 'Jan 01', end: 'Dec 31',
    posts: 86, scheduled: 14, live: 60, drafts: 8,
    markets: ['de','uk','pl','ee','lt','lv'],
    creatives: 142,
    source: 'manual',
    summary: 'Weekly cadence per market — product close-ups, BTS, brand voice consistency.',
  },
  {
    id: 'summer-launch',
    name: 'Summer Launch · 2026',
    color: '#f1c054',
    status: 'idea',
    start: 'Jun 15', end: 'Jul 30',
    posts: 0, scheduled: 0, live: 0, drafts: 0,
    markets: ['de','uk','pl'],
    creatives: 4,
    source: 'email',
    summary: 'Brief just landed via campaigns@hookline.io — auto-structured by the bot, awaiting review.',
  },
];

const INBOX_THREADS = [
  { id: 't1', who: '@maria.k', name: 'Maria K.', ch: 'ig', mk: 'de', last: 'Wann ist der Pop-Up in Berlin? 🙌', unread: 2, time: '2 min', kind: 'dm', tag: 'lead' },
  { id: 't2', who: '@runners.club',  name: 'Runners Club London', ch: 'ig', mk: 'uk', last: 'Quick collab idea — open to chat?', unread: 1, time: '14 min', kind: 'dm', tag: 'collab' },
  { id: 't3', who: '@piotr92',  name: 'Piotr', ch: 'tt', mk: 'pl', last: 'Comment on "Premiera kolekcji wiosna"', unread: 0, time: '38 min', kind: 'comment' },
  { id: 't4', who: '@team_riga',  name: 'Riga Studio', ch: 'ig', mk: 'lv', last: 'Sent the new BTS reel for review.', unread: 4, time: '1 h', kind: 'dm', tag: 'team' },
  { id: 't5', who: '@vilnius_creative', name: 'Vilnius Creative', ch: 'ig', mk: 'lt', last: 'Loved the reel — can I repost?', unread: 0, time: '2 h', kind: 'mention' },
  { id: 't6', who: '@henna_eu', name: 'Henna', ch: 'tt', mk: 'ee', last: 'Comment: "Need this in Tallinn ASAP"', unread: 0, time: '3 h', kind: 'comment' },
  { id: 't7', who: '@max.engineer', name: 'Max', ch: 'yt', mk: 'de', last: 'Replying to your sustainability post', unread: 0, time: '5 h', kind: 'comment' },
  { id: 't8', who: '@studiozero', name: 'Studio Zero', ch: 'ig', mk: 'uk', last: 'DM sent — proposal attached.', unread: 1, time: '1 d', kind: 'dm', tag: 'collab' },
];

// External inspirations — posts the team saved from Instagram / TikTok /
// YouTube / Facebook to use as a reference when planning content. Unlike
// MEDIA_ASSETS these aren't OUR creatives; they're someone else's posts we
// liked. The calendar "Idea" status points at one of these via post.idea
// so the planning slot says "make something inspired by this".
const IDEA_LIBRARY = [
  // { id, title, source, author, mk, kind, ph, dur? }
  { id: 'inspo_001', title: 'Carbon weave macro · static',     source: 'ig', author: '@material.lab',    mk: 'de', kind: 'photo', ph: 'ph-cool' },
  { id: 'inspo_002', title: 'Recycling loop · BTS reel',       source: 'tt', author: '@circular.studio', mk: 'de', kind: 'video', ph: 'ph-green', dur: '0:22' },
  { id: 'inspo_003', title: 'Workshop voice-over · founder',   source: 'yt', author: '@craft.minute',    mk: 'uk', kind: 'video', ph: 'ph-warm',  dur: '0:48' },
  { id: 'inspo_004', title: 'Colour-story carousel · 6 frames',source: 'ig', author: '@hue.heap',        mk: 'pl', kind: 'photo', ph: 'ph-rose' },
  { id: 'inspo_005', title: 'Editorial product still · flatlay',source:'ig', author: '@studio.flat',     mk: 'uk', kind: 'photo', ph: 'ph-photo' },
  { id: 'inspo_006', title: 'Stop-motion reveal · 15s',        source: 'tt', author: '@looplab',         mk: 'de', kind: 'video', ph: 'ph-cool',  dur: '0:15' },
  { id: 'inspo_007', title: 'Day-in-the-life · designer cut',  source: 'ig', author: '@designer.daily',  mk: 'lt', kind: 'video', ph: 'ph-warm',  dur: '0:35' },
  { id: 'inspo_008', title: 'Factory walkthrough · long form', source: 'yt', author: '@made.here',       mk: 'de', kind: 'video', ph: 'ph-green', dur: '2:14' },
  { id: 'inspo_009', title: 'Worn-tool detail overlay',        source: 'ig', author: '@worn.tools',      mk: 'ee', kind: 'photo', ph: 'ph-rose' },
  { id: 'inspo_010', title: 'Atelier silhouette · album',      source: 'fb', author: '@atelier.notes',   mk: 'lv', kind: 'photo', ph: 'ph-photo' },
  { id: 'inspo_011', title: 'Pop-up storefront · documentary', source: 'ig', author: '@street.archive',  mk: 'uk', kind: 'video', ph: 'ph-warm',  dur: '0:54' },
  { id: 'inspo_012', title: 'Tonal product grid · 9-up',       source: 'ig', author: '@quiet.grid',      mk: 'de', kind: 'photo', ph: 'ph-cool' },
];

// Assets are platform-agnostic — same creative can be reused on any channel.
// They're categorised by format (kind: photo|video), campaign, and market (mk).
const MEDIA_ASSETS = [
  // {name, kind, mk, campaign, ph, dur?}
  { name: 'reveal_anchor_v3.mp4',     kind: 'video', mk: 'de', campaign: 'spring-popup', ph: 'ph-cool',  dur: '0:32' },
  { name: 'carousel_frame_01.jpg',    kind: 'photo', mk: 'de', campaign: 'spring-popup', ph: 'ph-warm' },
  { name: 'carousel_frame_02.jpg',    kind: 'photo', mk: 'de', campaign: 'spring-popup', ph: 'ph-rose' },
  { name: 'carousel_frame_03.jpg',    kind: 'photo', mk: 'de', campaign: 'spring-popup', ph: 'ph-photo' },
  { name: 'designer_interview_clip.mov', kind: 'video', mk: 'de', campaign: 'spring-popup', ph: 'ph-green', dur: '0:48' },
  { name: 'london_storefront.jpg',    kind: 'photo', mk: 'uk', campaign: 'spring-popup', ph: 'ph-cool' },
  { name: 'london_lineup.mp4',        kind: 'video', mk: 'uk', campaign: 'spring-popup', ph: 'ph-warm', dur: '0:21' },
  { name: 'sustainability_q2_pg1.jpg', kind: 'photo', mk: 'de', campaign: 'sustainability', ph: 'ph-green' },
  { name: 'sustainability_q2_pg2.jpg', kind: 'photo', mk: 'de', campaign: 'sustainability', ph: 'ph-green' },
  { name: 'recycling_loop.mp4',       kind: 'video', mk: 'de', campaign: 'sustainability', ph: 'ph-green', dur: '0:18' },
  { name: 'engineer_clip_a.mp4',      kind: 'video', mk: 'uk', campaign: 'sustainability', ph: 'ph-photo', dur: '1:04' },
  { name: 'product_macro_001.jpg',    kind: 'photo', mk: 'de', campaign: 'always-on-ig', ph: 'ph-warm' },
  { name: 'btsphoto_studio.jpg',      kind: 'photo', mk: 'pl', campaign: 'always-on-ig', ph: 'ph-rose' },
  { name: 'product_macro_002.jpg',    kind: 'photo', mk: 'lt', campaign: 'always-on-ig', ph: 'ph-cool' },
];

// ---------- Skeleton image with placeholder ----------
function PhotoTile({ ph = 'ph-photo', label, video = false, dur, style = {}, className = '' }) {
  return (
    <div className={`placeholder ${ph} ${className}`} style={{ position: 'relative', overflow: 'hidden', ...style }}>
      {label && <span style={{ opacity: .6 }}>{label}</span>}
      {video && (
        <span style={{
          position: 'absolute', left: 8, bottom: 8,
          background: 'rgba(0,0,0,.55)', color: '#fff',
          padding: '3px 7px', borderRadius: 6,
          fontFamily: 'JetBrains Mono', fontSize: 10, letterSpacing: 0.04,
          display: 'inline-flex', gap: 4, alignItems: 'center',
        }}>
          <Icon name="play_arrow" size={11} fill /> {dur || '0:30'}
        </span>
      )}
    </div>
  );
}

/* Status — animated concentric-ring indicator. tones: live | warn | muted | accent */
function Status({ tone = 'live', label, compact, count }) {
  return (
    <span className={'status ' + tone} aria-live="polite">
      <span className="ring"><i /></span>
      {!compact && <span className="label">{label}</span>}
      {count != null && <span className="mono" style={{ color: 'var(--fg-3)', fontSize: 10.5 }}>{count}</span>}
    </span>
  );
}

/* Tick — bracketed monospace ticker, e.g. [ READY ]  [ SYNCING ] */
function Tick({ tone = 'idle', children, bars }) {
  return (
    <span className={'tick ' + tone}>
      <svg width="6" height="10" viewBox="0 0 6 10" fill="none">
        <path d="M5 0H1V1H0V9H1V10H5V9H1V1H5V0Z" fill="currentColor" />
      </svg>
      {bars && (
        <span className="bars" aria-hidden="true">
          <i /><i /><i /><i />
        </span>
      )}
      <span>{children}</span>
      <svg width="6" height="10" viewBox="0 0 6 10" fill="none" style={{ transform: 'scaleX(-1)' }}>
        <path d="M5 0H1V1H0V9H1V10H5V9H1V1H5V0Z" fill="currentColor" />
      </svg>
    </span>
  );
}

Object.assign(window, {
  Icon, ChannelGlyph, Flag, Counter, PhotoTile, Status, Tick,
  MARKETS, CHANNELS, ALLOWED_CH, TODAY, POSTS, CAMPAIGNS, INBOX_THREADS, MEDIA_ASSETS, IDEA_LIBRARY,
  KINDS_BY_CHANNEL, KIND_SPECS, kindsForChannel, defaultKindForChannel, kindMeta, kindAllowsThumbnail, kindSpec,
  planningSign, PlanningSignDot,
});
