// templates-light.jsx — light-mode variants of the 5 16°S social post templates.
// Same brand DNA, inverted palette: cream surface, navy type, cyan accents
// used sparingly (period dots, hairlines, the parallel line). Photo regions
// still use dark scrims under cream text so any photo reads cleanly.

// ──────────────────────────────────────────────────────────────────────────
// useDraggable + ratioKey
// These also live in templates.jsx — defined here too so the light builder
// works when loaded standalone (templates-light.jsx is loaded on its own,
// not alongside templates.jsx). If templates.jsx is also loaded, its globals
// stay intact because we only assign to window if the slot is empty.
// ──────────────────────────────────────────────────────────────────────────
if (!window.useDraggable) {
  function useDraggable(storageKey, defaultPos = { x: 30, y: 50 }) {
    const [pos, setPos] = React.useState(() => {
      if (!storageKey || typeof localStorage === 'undefined') return defaultPos;
      try {
        const v = localStorage.getItem(storageKey);
        if (v) return JSON.parse(v);
      } catch (e) {}
      return defaultPos;
    });
    React.useEffect(() => {
      if (!storageKey) return;
      try { localStorage.setItem(storageKey, JSON.stringify(pos)); } catch (e) {}
    }, [pos, storageKey]);
    React.useEffect(() => {
      if (!storageKey) return;
      try {
        const v = localStorage.getItem(storageKey);
        setPos(v ? JSON.parse(v) : defaultPos);
      } catch (e) { setPos(defaultPos); }
    }, [storageKey]);
    const [dragging, setDragging] = React.useState(false);

    const onPointerDown = (e) => {
      if (e.button != null && e.button !== 0) return;
      e.preventDefault();
      e.stopPropagation();
      const handle = e.currentTarget;
      const container = handle.offsetParent || handle.parentElement;
      if (!container) return;
      const containerRect = container.getBoundingClientRect();
      const handleRect = handle.getBoundingClientRect();
      const offsetFromCenterX = e.clientX - (handleRect.left + handleRect.width / 2);
      const offsetFromCenterY = e.clientY - (handleRect.top  + handleRect.height / 2);
      setDragging(true);
      const onMove = (me) => {
        const cx = me.clientX - offsetFromCenterX - containerRect.left;
        const cy = me.clientY - offsetFromCenterY - containerRect.top;
        const halfWPct = (handleRect.width  / 2) / containerRect.width  * 100;
        const halfHPct = (handleRect.height / 2) / containerRect.height * 100;
        const xPct = (cx / containerRect.width)  * 100;
        const yPct = (cy / containerRect.height) * 100;
        setPos({
          x: Math.max(halfWPct, Math.min(100 - halfWPct, xPct)),
          y: Math.max(halfHPct, Math.min(100 - halfHPct, yPct)),
        });
      };
      const onUp = () => {
        setDragging(false);
        window.removeEventListener('pointermove', onMove);
        window.removeEventListener('pointerup', onUp);
      };
      window.addEventListener('pointermove', onMove);
      window.addEventListener('pointerup', onUp);
    };

    return { pos, dragging, onPointerDown };
  }
  function ratioKey(w, h) {
    const r = w / h;
    if (Math.abs(r - 1)    < 0.05) return '1x1';
    if (Math.abs(r - 4/5)  < 0.05) return '4x5';
    if (Math.abs(r - 9/16) < 0.05) return '9x16';
    if (Math.abs(r - 16/9) < 0.05) return '16x9';
    return r > 1 ? 'wide' : 'tall';
  }
  window.useDraggable = useDraggable;
  window.ratioKey = ratioKey;
}

// Pull MediaSlot from window (declared in media-slot.jsx, loaded before
// this file). Each Babel <script> gets its own scope, so we have to alias.
const MediaSlot = window.MediaSlot;
const Rise = window.Rise;
const CompassArrow = window.CompassArrow;
const Scramble = window.Scramble;
const AutoFitText = window.AutoFitText;
const ResizeHandle = window.ResizeHandle;

const LIGHT_SAMPLES = {
  coords: {
    location: 'Valencia',
    region: 'Valencian Community',
    lat: '39°28′12″N',
    lng: '0°22′04″W',
    client: 'Mercedes-AMG',
    project: 'F1 Pre-Season Test',
    date: 'May 2026',
    frame: 'A_047',
  },
  callsheet: {
    location: 'Porto Cervo',
    region: 'Sardinia',
    lat: '41°08′25″N',
    lng: '9°31′58″E',
    client: 'Oyster Yachts',
    project: 'Regatta',
    date: 'Sep 2025',
    day: 'Day 03',
    frame: 'A_0214',
  },
  parallel: {
    location: 'Cape Town',
    lat: '33°55′S',
    lng: '18°25′E',
    client: 'Volvo Ocean Race',
    project: 'Leg 3',
    headline: 'Open\nsea.',
    date: '2025',
  },
  bigtype: {
    line1: 'Built',
    line2: 'for the',
    accent: 'chase.',
    client: 'Porsche Motorsport',
    location: 'Le Mans',
    coords: '47°57′N · 0°12′E',
    date: '24h · 2026',
  },
  weather: {
    location: 'Southern Ocean',
    lat: '50°02′S',
    lng: '30°14′W',
    wind: '38',
    dir: 'NNW',
    sea: '6.2m',
    event: 'Clipper Round-the-World',
    day: 'Day 47',
    client: 'Clipper Race',
    project: 'No Going Back',
    date: '2024',
  },
};

// Light color tokens — reference the same CSS vars so they stay brand-locked.
const LC = {
  surface:  'var(--cream)',        // #F2EFEA
  surface2: '#E8E4DC',              // 1 step darker for inset chips
  ink:      'var(--navy-deep)',    // #02102B — primary type
  inkSoft:  'rgba(2,16,43,0.62)',  // secondary type
  inkLine:  'rgba(2,16,43,0.10)',  // hairline borders
  accent:   'var(--blue-light)',   // #61D4F2 — used sparingly
  accentInk:'var(--teal)',         // #0F6D96 — cyan-ish but reads on cream
};

// ── Shared helpers ────────────────────────────────────────────────────────

function LightTopoSvg({ opacity = 0.10, color = '#02102B' }) {
  // Topo lines drawn in low-opacity navy so they read on cream as a texture.
  return (
    <svg
      viewBox="0 0 600 600"
      preserveAspectRatio="xMidYMid slice"
      style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', opacity, pointerEvents: 'none' }}
      aria-hidden="true"
    >
      <g fill="none" stroke={color} strokeWidth="1">
        <circle cx="300" cy="300" r="40" />
        <circle cx="300" cy="300" r="80" />
        <circle cx="300" cy="300" r="130" />
        <circle cx="300" cy="300" r="190" />
        <circle cx="300" cy="300" r="260" />
        <path d="M -50 100 Q 100 60 220 110 T 480 120 T 700 90" />
        <path d="M -50 170 Q 120 130 250 180 T 500 190 T 700 160" />
        <path d="M -50 470 Q 100 430 220 480 T 480 490 T 700 460" />
        <path d="M -50 540 Q 120 500 250 550 T 500 560 T 700 530" />
      </g>
    </svg>
  );
}

function LightPhotoSlot({ id, placeholder = 'Drop a photo', mediaKind = 'photo' }) {
  // Same image-slot; the empty-state gradient stays navy so the cream
  // surround frames it like a print. When mediaKind === 'video', swap to a
  // <media-slot> drop zone (auto-loop muted preview).
  if (mediaKind === 'video') {
    return (
      <MediaSlot
        id={`${id}--vid`}
        placeholder="Drop a video (mp4 / mov / webm)"
        style={{ background: 'transparent' }}
      />
    );
  }
  return (
    <image-slot
      id={id}
      shape="rect"
      placeholder={placeholder}
      style={{
        position: 'absolute',
        inset: 0,
        width: '100%',
        height: '100%',
        background: 'linear-gradient(135deg, #02102B 0%, #0F6D96 60%, #001172 100%)',
        '--is-bg': 'transparent',
      }}
    ></image-slot>
  );
}

// Bottom scrim for cream-on-photo headlines. Darkens the photo without
// muddying everything — we still rely on it because cream type on a bright
// photo otherwise floats.
function BottomScrim({ from = 0.35, opacity = 0.85 }) {
  return (
    <div style={{
      position: 'absolute', inset: 0,
      background: `linear-gradient(180deg, rgba(2,16,43,0) ${from * 100}%, rgba(2,16,43,${opacity}) 100%)`,
      pointerEvents: 'none',
    }} />
  );
}

function LightCard({ w, h, children, style }) {
  const u = Math.min(w, h) / 100;
  return (
    <div
      style={{
        position: 'relative',
        width: w,
        height: h,
        background: LC.surface,
        color: LC.ink,
        overflow: 'hidden',
        '--u': `${u}px`,
        fontFamily: 'var(--font-body)',
        ...style,
      }}
    >
      {children}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// TEMPLATE 01 — COORDINATES (LIGHT)
// Cream frame around an inset photo (print/polaroid feel). All metadata
// lives on the cream surround in navy.
// ─────────────────────────────────────────────────────────────────────────
function TplCoordinatesLight({ w, h, slotId = 'tpl-coords-light', data }) {
  const d = { ...LIGHT_SAMPLES.coords, ...(data || {}) };
  const u = Math.min(w, h) / 100;
  const isWide = w / h > 1.2;
  const isTall = h / w > 1.4;        // 9:16 only — stacks coords under headline
  // Stack vs side-by-side. 1:1 + 4:5 have enough horizontal room for the
  // coordinates block to sit beside the headline; 9:16 is too narrow so we
  // stack and give it more vertical room.
  const stackCoords = isTall;
  // Insets around the photo. Top has a small allowance for the 16°S monogram
  // (Heavitas has chunky line-box so we need extra space). Bottom holds the
  // location stack + coords block.
  const inset = {
    top:    u * (isTall ? 11 : 10),
    left:   u * (isWide ? 6 : 5),
    right:  u * (isWide ? 6 : 5),
    bottom: u * (isTall ? 38 : isWide ? 22 : 28),
  };
  return (
    <LightCard w={w} h={h}>
      {/* Photo inset */}
      <div style={{
        position: 'absolute',
        top: inset.top, left: inset.left, right: inset.right, bottom: inset.bottom,
      }}>
        <LightPhotoSlot id={slotId} placeholder="Drop a photo" mediaKind={d.mediaKind} />
        {/* Topo overlay on photo */}
        <div style={{ position: 'absolute', inset: 0, mixBlendMode: 'screen', opacity: 0.45, pointerEvents: 'none' }}>
          <LightTopoSvg opacity={0.6} color="#61D4F2" />
        </div>
      </div>
      {/* Top edge — monogram + client */}
      <div style={{
        position: 'absolute', top: u * 3, left: inset.left, right: inset.right,
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        zIndex: 2,
      }}>
        <Rise enabled={d.mediaKind === 'video'} delay={0} style={{
          fontFamily: 'var(--font-display)', color: LC.ink,
          fontSize: u * 3.5, letterSpacing: '-0.02em', lineHeight: 1.15,
          paddingBottom: u * 0.4,
        }}>16°S</Rise>
        <Rise enabled={d.mediaKind === 'video'} delay={0.15} style={{
          fontFamily: 'var(--font-brand)', color: LC.inkSoft,
          fontSize: u * 1.9, fontWeight: 600, letterSpacing: '0.20em',
          textTransform: 'uppercase',
        }}>// {d.client}</Rise>
      </div>
      {/* Bottom surround — location + meta */}
      <div style={{
        position: 'absolute',
        left: inset.left, right: inset.right,
        top: `calc(100% - ${inset.bottom}px + ${u * 2.5}px)`,
        bottom: u * 3,
        zIndex: 2,
        display: 'grid',
        gridTemplateColumns: stackCoords ? '1fr' : '1fr auto',
        gap: u * 2,
        alignItems: 'end',
      }}>
        <div>
          <Rise enabled={d.mediaKind === 'video'} delay={0.25} style={{
            fontFamily: 'var(--font-brand)', color: LC.inkSoft,
            fontSize: u * 1.8, fontWeight: 600, letterSpacing: '0.20em',
            textTransform: 'uppercase', marginBottom: u * 1.2,
            display: 'block',
          }}>// {d.project} · {d.date}</Rise>
          <Rise enabled={d.mediaKind === 'video'} delay={0.5} style={{
            fontFamily: 'var(--font-display)', color: LC.ink,
            lineHeight: 0.92,
            letterSpacing: '-0.02em', textTransform: 'uppercase',
            display: 'block',
          }}>
            <AutoFitText baseSize={isWide ? u * 9 : stackCoords ? u * 11 : u * 9.5}>
              {d.location}<span style={{ color: LC.accent }}>.</span>
            </AutoFitText>
          </Rise>
          <Rise enabled={d.mediaKind === 'video'} delay={0.8} style={{
            fontFamily: 'var(--font-brand)', color: LC.inkSoft,
            fontSize: u * 1.9, fontWeight: 500, letterSpacing: '0.16em',
            textTransform: 'uppercase', marginTop: u * 1.2,
            display: 'block',
          }}>{d.region}</Rise>
        </div>
        <div style={{
          textAlign: stackCoords ? 'left' : 'right',
          fontFamily: 'var(--font-brand)', color: LC.ink,
          fontSize: u * 2, fontWeight: 500, letterSpacing: '0.08em',
          lineHeight: 1.5,
          paddingTop: stackCoords ? u * 2 : 0,
          marginTop: stackCoords ? u * 2 : 0,
          borderTop: stackCoords ? `1px solid ${LC.inkLine}` : 'none',
        }}>
          <Rise enabled={d.mediaKind === 'video'} delay={0.35} style={{ color: LC.inkSoft, fontSize: u * 1.6, marginBottom: u * 0.4, letterSpacing: '0.20em', fontWeight: 600, display: 'block' }}>// COORDINATES</Rise>
          <div><Scramble enabled={d.mediaKind === 'video'} value={d.lat} delay={0.55} duration={0.9} /></div>
          <div><Scramble enabled={d.mediaKind === 'video'} value={d.lng} delay={0.75} duration={0.9} /></div>
        </div>
      </div>
    </LightCard>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// TEMPLATE 02 — CALL SHEET (LIGHT)
// Inverted strip: cream metadata block with navy type.
// ─────────────────────────────────────────────────────────────────────────
function TplCallSheetLight({ w, h, slotId = 'tpl-callsheet-light', data }) {
  const d = { ...LIGHT_SAMPLES.callsheet, ...(data || {}) };
  const u = Math.min(w, h) / 100;
  const ratio = w / h;
  const stripOnRight = ratio > 1.3;
  const stripSize = stripOnRight ? '40%' : ratio > 0.9 ? '34%' : '32%';
  return (
    <LightCard w={w} h={h}>
      {/* Photo area */}
      <div style={{
        position: 'absolute',
        top: 0, left: 0,
        right: stripOnRight ? stripSize : 0,
        bottom: stripOnRight ? 0 : stripSize,
      }}>
        <LightPhotoSlot id={slotId} placeholder="Drop a frame" mediaKind={d.mediaKind} />
        {/* Topo overlay on photo */}
        <div style={{ position: 'absolute', inset: 0, mixBlendMode: 'screen', opacity: 0.45, pointerEvents: 'none' }}>
          <LightTopoSvg opacity={0.6} color="#61D4F2" />
        </div>
      </div>
      {/* Cream strip */}
      <div style={{
        position: 'absolute',
        ...(stripOnRight
          ? { top: 0, right: 0, bottom: 0, width: stripSize }
          : { left: 0, right: 0, bottom: 0, height: stripSize }),
        background: LC.surface,
        overflow: 'hidden',
      }}>
        <LightTopoSvg opacity={0.05} />
        <div style={{
          position: 'absolute', inset: 0, padding: u * 4,
          display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
          zIndex: 2,
        }}>
          {/* Top row */}
          <div style={{
            display: 'flex', alignItems: 'center', justifyContent: 'space-between',
            fontFamily: 'var(--font-brand)', fontWeight: 600,
            fontSize: u * 1.8, letterSpacing: '0.20em', textTransform: 'uppercase',
          }}>
            <Rise enabled={d.mediaKind === 'video'} delay={0} style={{ color: LC.ink }}>// 16°S CONTENT FACTORY</Rise>
            <Rise enabled={d.mediaKind === 'video'} delay={0.1} style={{ color: LC.inkSoft }}>{d.frame}</Rise>
          </div>
          {/* Middle — big location */}
          <div style={{ marginTop: u * 2 }}>
            <Rise enabled={d.mediaKind === 'video'} delay={0.2} style={{
              fontFamily: 'var(--font-brand)', color: LC.inkSoft,
              fontSize: u * 1.8, fontWeight: 600, letterSpacing: '0.20em',
              textTransform: 'uppercase', marginBottom: u * 1.5,
              display: 'block',
            }}>// LOCATION</Rise>
            <Rise enabled={d.mediaKind === 'video'} delay={0.3} style={{
              fontFamily: 'var(--font-display)', color: LC.ink,
              lineHeight: 0.95,
              letterSpacing: '-0.02em', textTransform: 'uppercase',
              display: 'block',
            }}>
              <AutoFitText baseSize={stripOnRight ? u * 6.5 : u * 8}>
                {d.location}<span style={{ color: LC.accent }}>.</span>
              </AutoFitText>
            </Rise>
            <Rise enabled={d.mediaKind === 'video'} delay={0.4} style={{
              fontFamily: 'var(--font-brand)', color: LC.inkSoft,
              fontSize: u * 2, fontWeight: 500, letterSpacing: '0.14em',
              textTransform: 'uppercase', marginTop: u * 1.2,
              display: 'block',
            }}>{d.region}</Rise>
          </div>
          {/* Bottom — metadata grid */}
          <div style={{
            display: 'grid',
            gridTemplateColumns: stripOnRight ? '1fr' : '1fr 1fr',
            gap: u * 2.5,
            paddingTop: u * 3,
            borderTop: `1px solid ${LC.inkLine}`,
          }}>
            <LightMeta label="// CLIENT" value={d.client} u={u} animate={d.mediaKind === 'video'} delay={0.5} />
            <LightMeta label="// COORDINATES" value={`${d.lat}\n${d.lng}`} u={u} mono animate={d.mediaKind === 'video'} delay={0.65} />
            <LightMeta label="// PROJECT" value={`${d.project} · ${d.day}`} u={u} animate={d.mediaKind === 'video'} delay={0.8} />
            <LightMeta label="// DATE" value={d.date} u={u} animate={d.mediaKind === 'video'} delay={0.95} />
          </div>
        </div>
      </div>
    </LightCard>
  );
}

function LightMeta({ label, value, u, mono, animate, delay = 0 }) {
  return (
    <div>
      <Rise enabled={animate} delay={delay} style={{
        fontFamily: 'var(--font-brand)', color: LC.inkSoft,
        fontSize: u * 1.5, fontWeight: 600, letterSpacing: '0.20em',
        textTransform: 'uppercase', marginBottom: u * 0.6,
        display: 'block',
      }}>{label}</Rise>
      <Rise enabled={animate} delay={delay + 0.1} style={{
        fontFamily: 'var(--font-brand)', color: LC.ink,
        fontSize: u * 2, fontWeight: 500,
        letterSpacing: mono ? '0.06em' : '0.04em',
        textTransform: 'uppercase', whiteSpace: 'pre-line', lineHeight: 1.35,
        display: 'block',
      }}>{value}</Rise>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// TEMPLATE 03 — PARALLEL (LIGHT)
// Photo top half, cream bottom half. Dashed line straddles the seam.
// ─────────────────────────────────────────────────────────────────────────
function TplParallelLight({ w, h, slotId = 'tpl-parallel-light', data }) {
  const d = { ...LIGHT_SAMPLES.parallel, ...(data || {}) };
  const u = Math.min(w, h) / 100;
  const isWide = w / h > 1.2;
  const seam = isWide ? 0.62 : 0.55; // photo height fraction
  return (
    <LightCard w={w} h={h}>
      {/* Photo area */}
      <div style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: `${(1 - seam) * 100}%` }}>
        <LightPhotoSlot id={slotId} placeholder="Drop a photo" mediaKind={d.mediaKind} />
        {/* Topo overlay on photo */}
        <div style={{ position: 'absolute', inset: 0, mixBlendMode: 'screen', opacity: 0.45, pointerEvents: 'none' }}>
          <LightTopoSvg opacity={0.6} color="#61D4F2" />
        </div>
        {/* Top scrim for eyebrow legibility on any photo */}
        <div style={{
          position: 'absolute', inset: 0,
          background: 'linear-gradient(180deg, rgba(2,16,43,0.55) 0%, rgba(2,16,43,0) 35%)',
          pointerEvents: 'none',
        }} />
        {/* Top eyebrow over photo */}
        <div style={{
          position: 'absolute', top: u * 4, left: u * 5, right: u * 5,
          display: 'flex', justifyContent: 'space-between', zIndex: 2,
          fontFamily: 'var(--font-brand)', fontWeight: 600,
          fontSize: u * 2, letterSpacing: '0.20em', textTransform: 'uppercase',
        }}>
          <Rise enabled={d.mediaKind === 'video'} delay={0} style={{ color: LC.accent }}>// {d.client}</Rise>
          <Rise enabled={d.mediaKind === 'video'} delay={0.1} style={{ color: 'var(--cream)' }}>{d.project} · {d.date}</Rise>
        </div>
      </div>
      {/* Dashed line on the seam */}
      <div style={{
        position: 'absolute', left: 0, right: 0, top: `${seam * 100}%`,
        height: 1, borderTop: '1.5px dashed var(--blue-light)',
        opacity: 1, zIndex: 3,
      }} />
      {/* Lat chip left */}
      <Rise enabled={d.mediaKind === 'video'} delay={0.35} style={{
        position: 'absolute', left: u * 4, top: `calc(${seam * 100}% - ${u * 2}px)`,
        transform: 'translateY(-50%)',
        fontFamily: 'var(--font-brand)', color: LC.surface,
        background: LC.ink,
        fontSize: u * 1.9, fontWeight: 600, letterSpacing: '0.16em',
        textTransform: 'uppercase', zIndex: 4,
        padding: `${u * 0.7}px ${u * 1.3}px`,
      }}><Scramble enabled={d.mediaKind === 'video'} value={d.lat} delay={0.5} duration={0.9} /></Rise>
      {/* Long chip right */}
      <Rise enabled={d.mediaKind === 'video'} delay={0.45} style={{
        position: 'absolute', right: u * 4, top: `calc(${seam * 100}% - ${u * 2}px)`,
        transform: 'translateY(-50%)',
        fontFamily: 'var(--font-brand)', color: LC.surface,
        background: LC.ink,
        fontSize: u * 1.9, fontWeight: 600, letterSpacing: '0.16em',
        textTransform: 'uppercase', zIndex: 4,
        padding: `${u * 0.7}px ${u * 1.3}px`,
      }}><Scramble enabled={d.mediaKind === 'video'} value={d.lng} delay={0.6} duration={0.9} /></Rise>
      {/* Parallel tick center */}
      <Rise enabled={d.mediaKind === 'video'} delay={0.7} style={{
        position: 'absolute', left: '50%', top: `${seam * 100}%`,
        transform: 'translate(-50%, -50%)',
        fontFamily: 'var(--font-brand)', color: LC.ink,
        background: LC.accent,
        fontSize: u * 1.7, fontWeight: 700, letterSpacing: '0.20em',
        textTransform: 'uppercase', padding: `${u * 0.5}px ${u * 1.2}px`,
        zIndex: 5,
      }}>{d.lat.replace(/′.*/, '′')} PARALLEL</Rise>
      {/* Cream bottom — headline */}
      <div style={{
        position: 'absolute', left: u * 5, right: u * 5, top: `${seam * 100}%`,
        paddingTop: u * 6, paddingBottom: u * 5,
        zIndex: 2,
      }}>
        <Rise enabled={d.mediaKind === 'video'} delay={0.85} as="div" style={{
          fontFamily: 'var(--font-display)', color: LC.ink,
          lineHeight: 0.92,
          letterSpacing: '-0.02em', textTransform: 'uppercase',
          display: 'block',
        }}>
          {(() => {
            const lines = (d.headline || '').split('\n');
            const main = lines.slice(0, -1);
            const accent = lines[lines.length - 1] || '';
            const baseSize = isWide ? u * 11 : u * 14;
            return (
              <>
                {main.map((line, i) => line ? (
                  <AutoFitText key={i} baseSize={baseSize}>{line}</AutoFitText>
                ) : null)}
                {accent ? (
                  <AutoFitText baseSize={baseSize} style={{ color: LC.accentInk }}>{accent}</AutoFitText>
                ) : null}
              </>
            );
          })()}
        </Rise>
        <Rise enabled={d.mediaKind === 'video'} delay={1.05} style={{
          marginTop: u * 2,
          fontFamily: 'var(--font-brand)', color: LC.inkSoft,
          fontSize: u * 2, fontWeight: 500, letterSpacing: '0.16em',
          textTransform: 'uppercase', display: 'block',
        }}>{d.location}</Rise>
      </div>
    </LightCard>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// TEMPLATE 04 — BIG TYPE (LIGHT)
// Cream type panel (was navy) on one side, photo on the other.
// ─────────────────────────────────────────────────────────────────────────
function TplBigTypeLight({ w, h, slotId = 'tpl-bigtype-light', data }) {
  const d = { ...LIGHT_SAMPLES.bigtype, ...(data || {}) };
  const u = Math.min(w, h) / 100;
  const ratio = w / h;
  const layout = ratio > 1.2 ? 'horiz' : 'vert';
  const photoFrac = layout === 'horiz' ? 0.55 : 0.5;
  return (
    <LightCard w={w} h={h}>
      {/* Photo half */}
      <div style={{
        position: 'absolute',
        ...(layout === 'horiz'
          ? { right: 0, top: 0, bottom: 0, width: `${photoFrac * 100}%` }
          : { top: 0, left: 0, right: 0, height: `${photoFrac * 100}%` }),
      }}>
        <LightPhotoSlot id={slotId} placeholder="Drop a photo" mediaKind={d.mediaKind} />
      </div>
      {/* Type half — cream */}
      <div style={{
        position: 'absolute',
        ...(layout === 'horiz'
          ? { left: 0, top: 0, bottom: 0, width: `${(1 - photoFrac) * 100}%` }
          : { bottom: 0, left: 0, right: 0, height: `${(1 - photoFrac) * 100}%` }),
        background: LC.surface,
        overflow: 'hidden',
      }}>
        <LightTopoSvg opacity={0.08} />
        <div style={{
          position: 'absolute', inset: 0, padding: u * 5,
          display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
          zIndex: 2,
        }}>
          {/* Top eyebrow */}
          <Rise enabled={d.mediaKind === 'video'} delay={0} style={{
            fontFamily: 'var(--font-brand)', color: LC.inkSoft,
            fontSize: u * 2, fontWeight: 600, letterSpacing: '0.20em',
            textTransform: 'uppercase', display: 'block',
          }}>// {d.client}</Rise>
          {/* Stacked headline */}
          <div style={{
            fontFamily: 'var(--font-display)', color: LC.ink,
            lineHeight: 0.92,
            letterSpacing: '-0.025em', textTransform: 'uppercase',
          }}>
            {d.line1 ? (
              <Rise enabled={d.mediaKind === 'video'} delay={0.2} as="div">
                <AutoFitText baseSize={layout === 'horiz' ? u * 13 : u * 16}>{d.line1}</AutoFitText>
              </Rise>
            ) : null}
            {d.line2 ? (
              <Rise enabled={d.mediaKind === 'video'} delay={0.35} as="div">
                <AutoFitText baseSize={layout === 'horiz' ? u * 13 : u * 16}>{d.line2}</AutoFitText>
              </Rise>
            ) : null}
            {d.accent ? (
              <Rise enabled={d.mediaKind === 'video'} delay={0.5} as="div" style={{ color: LC.accentInk }}>
                <AutoFitText baseSize={layout === 'horiz' ? u * 13 : u * 16}>{d.accent}</AutoFitText>
              </Rise>
            ) : null}
          </div>
          {/* Bottom meta */}
          <div style={{
            display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end',
            gap: u * 2,
            paddingTop: u * 2, borderTop: `1px solid ${LC.inkLine}`,
          }}>
            <div>
              <Rise enabled={d.mediaKind === 'video'} delay={0.75} style={{
                fontFamily: 'var(--font-brand)', color: LC.inkSoft,
                fontSize: u * 1.5, fontWeight: 600, letterSpacing: '0.20em',
                textTransform: 'uppercase', marginBottom: u * 0.6,
                display: 'block',
              }}>// LOCATION</Rise>
              <Rise enabled={d.mediaKind === 'video'} delay={0.85} style={{
                fontFamily: 'var(--font-brand)', color: LC.ink,
                fontSize: u * 2, fontWeight: 500, letterSpacing: '0.10em',
                textTransform: 'uppercase', display: 'block',
              }}>{d.location}</Rise>
            </div>
            <div style={{ textAlign: 'right' }}>
              <Rise enabled={d.mediaKind === 'video'} delay={0.9} style={{
                fontFamily: 'var(--font-brand)', color: LC.inkSoft,
                fontSize: u * 1.5, fontWeight: 600, letterSpacing: '0.20em',
                textTransform: 'uppercase', marginBottom: u * 0.6,
                display: 'block',
              }}>// FRAME</Rise>
              <Rise enabled={d.mediaKind === 'video'} delay={1.0} style={{
                fontFamily: 'var(--font-brand)', color: LC.ink,
                fontSize: u * 2, fontWeight: 500, letterSpacing: '0.10em',
                textTransform: 'uppercase', display: 'block',
              }}>{d.date}</Rise>
            </div>
          </div>
        </div>
      </div>
    </LightCard>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// TEMPLATE 05 — WEATHER SLATE (LIGHT)
// Photo full-bleed with a cream "instrument card" overlay — readouts on
// cream, compass needle in teal. Cream surround at top for the eyebrow.
// ─────────────────────────────────────────────────────────────────────────
function TplWeatherLight({ w, h, slotId = 'tpl-weather-light', data, draggable = true }) {
  const d = { ...LIGHT_SAMPLES.weather, ...(data || {}) };
  const u = Math.min(w, h) / 100;
  const isWide = w / h > 1.2;
  const isTall = h / w > 1.4;
  // Default position: center-left (matches the old fixed layout).
  const drag = window.useDraggable(
    draggable ? `16ds-weather-pos-light-${window.ratioKey(w, h)}` : null,
    { x: 32, y: 50 },
  );
  const resize = window.useResizable(
    draggable ? `16ds-weather-scale-light-${window.ratioKey(w, h)}` : null,
    1, 0.5, 2.2,
  );
  return (
    <LightCard w={w} h={h}>
      {/* Top cream eyebrow strip */}
      <div style={{
        position: 'absolute', top: 0, left: 0, right: 0,
        height: u * 8, background: LC.surface, zIndex: 2,
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        padding: `0 ${u * 4}px`,
        borderBottom: `1px solid ${LC.inkLine}`,
        fontFamily: 'var(--font-brand)', fontWeight: 600,
        fontSize: u * 1.9, letterSpacing: '0.20em', textTransform: 'uppercase',
      }}>
        <Rise enabled={d.mediaKind === 'video'} delay={0} style={{ color: LC.ink }}>// {d.client}</Rise>
        <Rise enabled={d.mediaKind === 'video'} delay={0.1} style={{ color: LC.inkSoft }}>{d.project}</Rise>
      </div>
      {/* Photo */}
      <div style={{ position: 'absolute', top: u * 8, left: 0, right: 0, bottom: u * 8 }}>
        <LightPhotoSlot id={slotId} placeholder="Drop a photo" mediaKind={d.mediaKind} />
        {/* Topo overlay on photo — cyan isobars + concentric rings, screen-
            blended so they read as light texture over any photo. Same
            treatment as the dark Weather Slate. pointer-events:none so it
            doesn't block the photo slot's drop/click handlers. */}
        <div style={{ position: 'absolute', inset: 0, mixBlendMode: 'screen', opacity: 0.45, pointerEvents: 'none' }}>
          <LightTopoSvg opacity={0.6} color="#61D4F2" />
        </div>
        {/* Subtle dark scrim only at the bottom for location overlay legibility */}
        <BottomScrim from={0.55} opacity={0.75} />
      </div>
      {/* Bottom cream meta strip */}
      <div style={{
        position: 'absolute', bottom: 0, left: 0, right: 0,
        height: u * 8, background: LC.surface, zIndex: 2,
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        padding: `0 ${u * 4}px`,
        borderTop: `1px solid ${LC.inkLine}`,
        fontFamily: 'var(--font-brand)', fontWeight: 600,
        fontSize: u * 1.7, letterSpacing: '0.16em', textTransform: 'uppercase',
        color: LC.ink, gap: u * 2,
      }}>
        <span><span style={{ color: LC.inkSoft }}>// LOCATION ·</span> <Rise enabled={d.mediaKind === 'video'} delay={0.9}>{d.location}</Rise></span>
        <Rise enabled={d.mediaKind === 'video'} delay={1.1} style={{ color: LC.inkSoft }}>
          <Scramble enabled={d.mediaKind === 'video'} value={d.lat} delay={1.2} duration={0.8} /> · <Scramble enabled={d.mediaKind === 'video'} value={d.lng} delay={1.4} duration={0.8} />
        </Rise>
      </div>
      {/* Floating instrument card on the photo — DRAGGABLE + RESIZABLE. Size reduced 25%, white-w/-opacity bg. */}
      <div
        data-resizable-card="weather-light"
        onPointerDown={draggable ? drag.onPointerDown : undefined}
        style={{
          position: 'absolute',
          left: `${drag.pos.x}%`,
          top:  `${drag.pos.y}%`,
          transform: `translate(-50%, -50%) scale(${resize.scale})`,
          transformOrigin: 'center center',
          background: 'rgba(255, 255, 255, 0.5)',
          backdropFilter: 'blur(8px)',
          WebkitBackdropFilter: 'blur(8px)',
          padding: `${u * 2.25}px ${u * 3}px`,
          zIndex: 3,
          display: 'flex', alignItems: 'center', gap: u * 2.25,
          boxShadow: `0 ${u * 0.75}px ${u * 2.25}px rgba(2,16,43,0.18)`,
          maxWidth: isTall ? `${w - u * 10}px` : isWide ? '60%' : '70%',
          cursor: draggable ? (drag.dragging ? 'grabbing' : 'grab') : 'default',
          touchAction: draggable ? 'none' : 'auto',
          userSelect: 'none',
          outline: (drag.dragging || resize.resizing) ? `1px dashed ${LC.accent}` : 'none',
          outlineOffset: '4px',
        }}
      >
        <CompassArrow dir={d.dir} size={u * 8.25} navy="#02102B" accent="#0F6D96" />
        <div>
          <Rise enabled={d.mediaKind === 'video'} delay={0.2} style={{
            fontFamily: 'var(--font-brand)', color: LC.inkSoft,
            fontSize: u * 1.2, fontWeight: 600, letterSpacing: '0.20em',
            textTransform: 'uppercase', marginBottom: u * 0.225,
            display: 'block',
          }}>// WIND</Rise>
          <Rise enabled={d.mediaKind === 'video'} delay={0.3} style={{
            fontFamily: 'var(--font-display)', color: LC.ink,
            fontSize: u * 5.25, lineHeight: 0.9, letterSpacing: '-0.02em',
            textTransform: 'uppercase', display: 'block',
          }}><Scramble enabled={d.mediaKind === 'video'} value={d.wind} delay={0.45} duration={0.7} /><span style={{ fontSize: u * 2.25, color: LC.accentInk }}>KTS</span></Rise>
          <Rise enabled={d.mediaKind === 'video'} delay={0.5} style={{
            fontFamily: 'var(--font-brand)', color: LC.ink,
            fontSize: u * 1.275, fontWeight: 600, letterSpacing: '0.14em',
            textTransform: 'uppercase', marginTop: u * 0.3,
            display: 'block',
          }}>{d.dir} · sea {d.sea}</Rise>
          {/* Event + Day line */}
          {(d.event || d.day) && (
            <Rise enabled={d.mediaKind === 'video'} delay={0.7} style={{
              marginTop: u * 0.9,
              paddingTop: u * 0.75,
              borderTop: `1px solid ${LC.inkLine}`,
              display: 'flex', flexWrap: 'wrap', alignItems: 'baseline', gap: `${u * 0.45}px ${u * 1.05}px`,
              fontFamily: 'var(--font-brand)', fontWeight: 600,
              fontSize: u * 1.125, letterSpacing: '0.16em', textTransform: 'uppercase',
              color: LC.ink,
            }}>
              {d.event && <span><span style={{ color: LC.inkSoft }}>// EVENT</span> &nbsp;{d.event}</span>}
              {d.day   && <span><span style={{ color: LC.inkSoft }}>// </span>{d.day}</span>}
            </Rise>
          )}
        </div>
        <ResizeHandle onPointerDown={resize.onPointerDown} color="#02102B" bg="rgba(2,16,43,0.15)" />
      </div>
    </LightCard>
  );
}

// Expose globally
Object.assign(window, {
  TplCoordinatesLight, TplCallSheetLight, TplParallelLight,
  TplBigTypeLight, TplWeatherLight,
});
