// Five card styles for QRJam. Each exports a single Card({track, side, size}) renderer
// that draws either the 'front' (artist + title + year) or 'back' (QR + scan instruction).
// Cards are square at any size; default 280px is the on-screen preview size for 70mm.

// Seeded PRNG for deterministic decoration (confetti positions etc).
function seededRand(seedStr) {
  let h = 2166136261;
  for (let i = 0; i < seedStr.length; i++) {
    h ^= seedStr.charCodeAt(i);
    h = Math.imul(h, 16777619);
  }
  return () => {
    h = Math.imul(h ^ (h >>> 15), 2246822507);
    h = Math.imul(h ^ (h >>> 13), 3266489909);
    h ^= h >>> 16;
    return ((h >>> 0) / 4294967296);
  };
}

// ─────────────────────────────────────────────────────────────────────
// Shared bits
// ─────────────────────────────────────────────────────────────────────

const CardFrame = ({ children, size = 280, bg, style = {}, className = "" }) => (
  <div
    className={`qr-card ${className}`}
    style={{
      width: size, height: size, position: "relative",
      borderRadius: size * 0.045,
      background: bg, overflow: "hidden",
      boxShadow: "0 1px 0 rgba(255,255,255,.08) inset, 0 18px 40px -18px rgba(0,0,0,.35), 0 2px 6px rgba(0,0,0,.12)",
      ...style,
    }}>
    {children}
  </div>
);

const ScanBadge = ({ color = "#fff", bg = "rgba(0,0,0,.6)", text = "Scan met je camera" }) => (
  <div style={{
    display: "inline-flex", alignItems: "center", gap: 6,
    background: bg, color: color, padding: "6px 10px",
    borderRadius: 999, fontSize: 11, fontWeight: 600, letterSpacing: ".02em",
  }}>
    <svg width="11" height="11" viewBox="0 0 12 12" fill="none">
      <path d="M2 4V2h2M10 4V2H8M2 8v2h2M10 8v2H8" stroke={color} strokeWidth="1.4" strokeLinecap="round"/>
      <path d="M3.5 6h5" stroke={color} strokeWidth="1.2" strokeLinecap="round" opacity=".6"/>
    </svg>
    {text}
  </div>
);

// ─────────────────────────────────────────────────────────────────────
// 1. RETRO (70s warm)
// ─────────────────────────────────────────────────────────────────────

const RetroFront = ({ track, size }) => (
  <CardFrame size={size} bg="#e9d8b6"
    style={{
      backgroundImage: `
        radial-gradient(circle at 15% 20%, rgba(0,0,0,.05) 0, transparent 60%),
        radial-gradient(circle at 90% 90%, rgba(168,87,28,.18) 0, transparent 60%),
        repeating-linear-gradient(135deg, rgba(60,30,10,.02) 0 2px, transparent 2px 6px)
      `,
    }}>
    {/* Rounded inner frame */}
    <div style={{
      position: "absolute", inset: size * 0.05,
      border: `2px solid #6b3a14`, borderRadius: size * 0.025,
      display: "flex", flexDirection: "column", justifyContent: "space-between",
      padding: size * 0.07,
    }}>
      <div style={{
        fontFamily: '"DM Serif Display", Georgia, serif',
        fontSize: size * 0.058, color: "#6b3a14",
        letterSpacing: ".15em", textTransform: "uppercase", textAlign: "center",
      }}>
        ★ Side A ★
      </div>

      <div style={{ textAlign: "center" }}>
        <div style={{
          fontFamily: '"DM Serif Display", Georgia, serif',
          fontSize: size * 0.21, color: "#3d1a08",
          lineHeight: 1, letterSpacing: "-.02em", fontWeight: 400,
          fontVariantNumeric: "tabular-nums",
        }}>
          {track.year}
        </div>
        <div style={{ fontSize: size * 0.045, color: "#8a4a1c", marginTop: size * 0.02,
          fontFamily: '"Space Mono", monospace', letterSpacing: ".18em", textTransform: "uppercase" }}>
          A.D.
        </div>
      </div>

      <div style={{ textAlign: "center" }}>
        <div style={{
          fontFamily: '"DM Serif Display", Georgia, serif',
          fontSize: size * 0.073, color: "#3d1a08",
          fontWeight: 400, lineHeight: 1.05, textWrap: "balance",
        }}>
          {track.title}
        </div>
        <div style={{
          fontSize: size * 0.046, color: "#6b3a14", marginTop: size * 0.025,
          fontFamily: '"Space Mono", monospace', letterSpacing: ".12em", textTransform: "uppercase",
        }}>
          {track.artist}
        </div>
      </div>
    </div>
  </CardFrame>
);

const RetroBack = ({ track, size }) => (
  <CardFrame size={size} bg="#e9d8b6"
    style={{
      backgroundImage: `repeating-linear-gradient(135deg, rgba(60,30,10,.04) 0 2px, transparent 2px 6px)`,
    }}>
    <div style={{
      position: "absolute", inset: size * 0.05,
      border: `2px solid #6b3a14`, borderRadius: size * 0.025,
      display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "space-between",
      padding: size * 0.07,
    }}>
      <div style={{
        fontFamily: '"DM Serif Display", Georgia, serif',
        fontSize: size * 0.05, color: "#6b3a14",
        letterSpacing: ".25em", textTransform: "uppercase",
      }}>
        ✶ QRJam ✶
      </div>
      <div style={{ background: "#fff8ec", padding: size * 0.025, borderRadius: 4 }}>
        <PseudoQR track={track} size={size * 0.55} fg="#3d1a08" bg="#fff8ec" />
      </div>
      <div style={{
        fontSize: size * 0.04, color: "#6b3a14",
        fontFamily: '"Space Mono", monospace', letterSpacing: ".18em", textTransform: "uppercase",
      }}>
        ▸ Scan & Play ◂
      </div>
    </div>
  </CardFrame>
);

// ─────────────────────────────────────────────────────────────────────
// 2. NEON
// ─────────────────────────────────────────────────────────────────────

const NeonFront = ({ track, size }) => (
  <CardFrame size={size} bg="#0a0613"
    style={{
      backgroundImage: `
        radial-gradient(ellipse at 20% 10%, rgba(255,43,209,.18) 0, transparent 50%),
        radial-gradient(ellipse at 90% 100%, rgba(0,255,229,.18) 0, transparent 55%)
      `,
    }}>
    {/* Grid lines */}
    <svg width={size} height={size} style={{ position: "absolute", inset: 0, opacity: .25 }}>
      <defs>
        <pattern id={`ngrid-${track.id}`} width={size/10} height={size/10} patternUnits="userSpaceOnUse">
          <path d={`M ${size/10} 0 L 0 0 0 ${size/10}`} fill="none" stroke="#ff2bd1" strokeWidth=".5"/>
        </pattern>
      </defs>
      <rect width={size} height={size} fill={`url(#ngrid-${track.id})`}/>
    </svg>

    <div style={{
      position: "absolute", inset: 0, padding: size * 0.09,
      display: "flex", flexDirection: "column", justifyContent: "space-between",
    }}>
      <div style={{
        fontFamily: '"Space Mono", monospace', fontSize: size * 0.04,
        color: "#00ffe5", letterSpacing: ".3em", textTransform: "uppercase",
        textShadow: "0 0 8px rgba(0,255,229,.8)",
      }}>
        ◢◤ TRACK
      </div>

      <div>
        <div style={{
          fontFamily: '"Audiowide", "Space Grotesk", sans-serif',
          fontSize: size * 0.22, color: "#fff",
          lineHeight: 1, fontWeight: 700,
          textShadow: "0 0 10px rgba(255,43,209,.9), 0 0 22px rgba(255,43,209,.5), 0 0 40px rgba(255,43,209,.3)",
          letterSpacing: ".02em", fontVariantNumeric: "tabular-nums",
        }}>
          {track.year}
        </div>
      </div>

      <div>
        <div style={{
          fontFamily: '"Space Grotesk", sans-serif',
          fontSize: size * 0.078, color: "#fff",
          fontWeight: 700, lineHeight: 1.05, letterSpacing: "-.01em",
          textWrap: "balance",
          textShadow: "0 0 6px rgba(255,255,255,.4)",
        }}>
          {track.title}
        </div>
        <div style={{
          fontFamily: '"Space Mono", monospace', fontSize: size * 0.045,
          color: "#00ffe5", marginTop: size * 0.025, letterSpacing: ".15em",
          textTransform: "uppercase",
          textShadow: "0 0 6px rgba(0,255,229,.7)",
        }}>
          {track.artist}
        </div>
      </div>
    </div>
  </CardFrame>
);

const NeonBack = ({ track, size }) => (
  <CardFrame size={size} bg="#0a0613"
    style={{
      backgroundImage: `
        radial-gradient(ellipse at 50% 50%, rgba(255,43,209,.25) 0, transparent 60%)
      `,
    }}>
    <div style={{
      position: "absolute", inset: 0, padding: size * 0.08,
      display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "space-between",
    }}>
      <div style={{
        fontFamily: '"Audiowide", "Space Grotesk", sans-serif',
        fontSize: size * 0.07, color: "#ff2bd1", letterSpacing: ".15em",
        textShadow: "0 0 8px rgba(255,43,209,.9), 0 0 16px rgba(255,43,209,.5)",
      }}>
        QRJAM
      </div>
      <div style={{
        padding: size * 0.025, background: "#fff", borderRadius: 4,
        boxShadow: "0 0 20px rgba(0,255,229,.5), 0 0 40px rgba(255,43,209,.4)",
      }}>
        <PseudoQR track={track} size={size * 0.58} fg="#0a0613" bg="#fff" />
      </div>
      <div style={{
        fontFamily: '"Space Mono", monospace', fontSize: size * 0.04,
        color: "#00ffe5", letterSpacing: ".2em", textTransform: "uppercase",
        textShadow: "0 0 6px rgba(0,255,229,.7)",
      }}>
        ▸ SCAN ▸ PLAY ▸
      </div>
    </div>
  </CardFrame>
);

// ─────────────────────────────────────────────────────────────────────
// 3. 80s MEMPHIS
// ─────────────────────────────────────────────────────────────────────

const MemphisDecorations = ({ size, seed }) => {
  // deterministic positions but mostly fixed for consistent look
  return (
    <svg width={size} height={size} style={{ position: "absolute", inset: 0 }} viewBox={`0 0 100 100`}>
      {/* Squiggle top-right */}
      <path d="M 75 8 Q 80 4, 85 8 T 95 8" fill="none" stroke="#00b8a3" strokeWidth="2" strokeLinecap="round"/>
      {/* Dots cluster top-left */}
      <circle cx="10" cy="10" r="2" fill="#ff5fa2"/>
      <circle cx="16" cy="13" r="2" fill="#ff5fa2"/>
      <circle cx="10" cy="16" r="2" fill="#ff5fa2"/>
      <circle cx="16" cy="19" r="2" fill="#ff5fa2"/>
      {/* Triangle bottom-left */}
      <path d="M 8 92 L 18 92 L 13 82 Z" fill="#fdc52b"/>
      {/* Zigzag bottom-right */}
      <path d="M 78 88 L 82 84 L 86 88 L 90 84 L 94 88" fill="none" stroke="#3a5cf6" strokeWidth="1.8" strokeLinecap="round"/>
      {/* Big shape mid-left */}
      <circle cx="6" cy="55" r="6" fill="#3a5cf6" opacity=".9"/>
      <rect x="93" y="40" width="6" height="14" fill="#ff5fa2" transform="rotate(15 96 47)"/>
    </svg>
  );
};

const MemphisFront = ({ track, size }) => (
  <CardFrame size={size} bg="#fef6ea">
    <MemphisDecorations size={size} seed={track.id}/>
    <div style={{
      position: "absolute", inset: 0, padding: size * 0.13,
      display: "flex", flexDirection: "column", justifyContent: "space-between",
    }}>
      <div style={{
        fontFamily: '"Space Grotesk", sans-serif',
        fontSize: size * 0.038, fontWeight: 700,
        color: "#1c1c2e", letterSpacing: ".2em", textTransform: "uppercase",
      }}>
        ✦ Hit Card ✦
      </div>

      <div style={{ textAlign: "center" }}>
        <div style={{
          fontFamily: '"Bricolage Grotesque", "Space Grotesk", sans-serif',
          fontSize: size * 0.26, color: "#1c1c2e",
          lineHeight: 1, fontWeight: 800, letterSpacing: "-.04em",
          fontVariantNumeric: "tabular-nums",
          textShadow: `4px 4px 0 #fdc52b`,
        }}>
          {track.year}
        </div>
      </div>

      <div>
        <div style={{
          fontFamily: '"Space Grotesk", sans-serif',
          fontSize: size * 0.072, color: "#1c1c2e",
          fontWeight: 700, lineHeight: 1.05, textWrap: "balance",
        }}>
          {track.title}
        </div>
        <div style={{
          fontFamily: '"Space Grotesk", sans-serif',
          fontSize: size * 0.046, color: "#3a5cf6",
          fontWeight: 600, marginTop: size * 0.02,
          letterSpacing: ".02em",
        }}>
          {track.artist}
        </div>
      </div>
    </div>
  </CardFrame>
);

const MemphisBack = ({ track, size }) => (
  <CardFrame size={size} bg="#fef6ea">
    <MemphisDecorations size={size} seed={track.id}/>
    <div style={{
      position: "absolute", inset: 0, padding: size * 0.1,
      display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "space-between",
    }}>
      <div style={{
        fontFamily: '"Bricolage Grotesque", "Space Grotesk", sans-serif',
        fontSize: size * 0.075, color: "#1c1c2e", fontWeight: 800,
        letterSpacing: "-.02em",
      }}>
        QR<span style={{color:"#3a5cf6"}}>Jam</span>
      </div>
      <div style={{
        padding: size * 0.03, background: "#fff",
        border: `3px solid #1c1c2e`,
        boxShadow: `6px 6px 0 #fdc52b`,
      }}>
        <PseudoQR track={track} size={size * 0.52} fg="#1c1c2e" bg="#fff" />
      </div>
      <div style={{
        fontFamily: '"Space Grotesk", sans-serif',
        fontSize: size * 0.044, color: "#1c1c2e", fontWeight: 700,
        letterSpacing: ".05em", textTransform: "uppercase",
      }}>
        ▶ Scan & Play
      </div>
    </div>
  </CardFrame>
);

// ─────────────────────────────────────────────────────────────────────
// 4. PARTY
// ─────────────────────────────────────────────────────────────────────

const Confetti = ({ size, seed }) => {
  const r = seededRand(seed + "c");
  const bits = [];
  const colors = ["#ff3d8a", "#ffd83d", "#3dffaa", "#3d8aff", "#ff6b3d", "#c93dff"];
  for (let i = 0; i < 26; i++) {
    bits.push({
      x: r() * 100, y: r() * 100,
      rot: r() * 360,
      color: colors[Math.floor(r() * colors.length)],
      type: r() > 0.5 ? "rect" : "circle",
      w: 1.5 + r() * 2,
    });
  }
  return (
    <svg width={size} height={size} viewBox="0 0 100 100" style={{ position: "absolute", inset: 0 }}>
      {bits.map((b, i) =>
        b.type === "rect" ? (
          <rect key={i} x={b.x} y={b.y} width={b.w * 1.6} height={b.w * 0.7}
            fill={b.color} transform={`rotate(${b.rot} ${b.x} ${b.y})`} />
        ) : (
          <circle key={i} cx={b.x} cy={b.y} r={b.w * 0.6} fill={b.color} />
        )
      )}
    </svg>
  );
};

const PartyFront = ({ track, size }) => (
  <CardFrame size={size} bg="#ff3d8a"
    style={{
      backgroundImage: `linear-gradient(135deg, #ff3d8a 0%, #c93dff 60%, #3d8aff 100%)`,
    }}>
    <Confetti size={size} seed={track.id} />
    <div style={{
      position: "absolute", inset: 0, padding: size * 0.085,
      display: "flex", flexDirection: "column", justifyContent: "space-between",
    }}>
      <div style={{
        display: "inline-flex", alignSelf: "flex-start",
        background: "#fff", color: "#ff3d8a", padding: `${size*0.018}px ${size*0.04}px`,
        borderRadius: 999, fontSize: size * 0.04, fontWeight: 800,
        letterSpacing: ".1em", textTransform: "uppercase",
        fontFamily: '"Space Grotesk", sans-serif',
      }}>
        🎉 PARTY HIT
      </div>

      <div style={{ textAlign: "center" }}>
        <div style={{
          fontFamily: '"Fraunces", "DM Serif Display", serif',
          fontSize: size * 0.3, color: "#fff",
          lineHeight: .95, fontWeight: 900, letterSpacing: "-.04em",
          fontVariantNumeric: "tabular-nums", fontStyle: "italic",
          textShadow: "0 4px 0 rgba(0,0,0,.18)",
        }}>
          {track.year}
        </div>
      </div>

      <div style={{
        background: "rgba(0,0,0,.18)", borderRadius: size * 0.04,
        padding: `${size*0.04}px ${size*0.05}px`,
        backdropFilter: "blur(4px)",
      }}>
        <div style={{
          fontFamily: '"Space Grotesk", sans-serif',
          fontSize: size * 0.075, color: "#fff",
          fontWeight: 800, lineHeight: 1.05, letterSpacing: "-.01em",
          textWrap: "balance",
        }}>
          {track.title}
        </div>
        <div style={{
          fontFamily: '"Space Grotesk", sans-serif',
          fontSize: size * 0.044, color: "#fff",
          fontWeight: 600, marginTop: size * 0.012,
          opacity: .9,
        }}>
          {track.artist}
        </div>
      </div>
    </div>
  </CardFrame>
);

const PartyBack = ({ track, size }) => (
  <CardFrame size={size} bg="#ff3d8a"
    style={{
      backgroundImage: `linear-gradient(135deg, #ff3d8a 0%, #c93dff 60%, #3d8aff 100%)`,
    }}>
    <Confetti size={size} seed={track.id + "b"} />
    <div style={{
      position: "absolute", inset: 0, padding: size * 0.08,
      display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "space-between",
    }}>
      <div style={{
        fontFamily: '"Fraunces", serif', fontStyle: "italic", fontWeight: 900,
        fontSize: size * 0.085, color: "#fff", letterSpacing: "-.02em",
        textShadow: "0 3px 0 rgba(0,0,0,.18)",
      }}>
        QRJam!
      </div>
      <div style={{
        padding: size * 0.03, background: "#fff", borderRadius: size * 0.04,
        boxShadow: "0 12px 30px rgba(0,0,0,.25)",
      }}>
        <PseudoQR track={track} size={size * 0.54} fg="#1c1c2e" bg="#fff" />
      </div>
      <div style={{
        display: "inline-flex", alignItems: "center", gap: 6,
        background: "#fff", color: "#ff3d8a",
        padding: `${size*0.022}px ${size*0.045}px`,
        borderRadius: 999, fontSize: size * 0.042, fontWeight: 800,
        letterSpacing: ".08em", textTransform: "uppercase",
        fontFamily: '"Space Grotesk", sans-serif',
      }}>
        📱 Scan & Dans
      </div>
    </div>
  </CardFrame>
);

// ─────────────────────────────────────────────────────────────────────
// 5. VINYL (record label)
// ─────────────────────────────────────────────────────────────────────

const VinylFront = ({ track, size }) => {
  const cx = size / 2;
  const cy = size / 2;
  const id = `vinyl-${track.id}`;
  return (
    <CardFrame size={size} bg="#1a1612">
      {/* outer paper border */}
      <div style={{
        position: "absolute", inset: size * 0.04,
        background: "#f4ead8", borderRadius: size * 0.03,
      }}/>
      <svg width={size} height={size} style={{ position: "absolute", inset: 0 }}>
        <defs>
          {/* curved text path */}
          <path id={`${id}-top`} d={`M ${cx - size*0.36} ${cy} A ${size*0.36} ${size*0.36} 0 0 1 ${cx + size*0.36} ${cy}`} />
          <path id={`${id}-bot`} d={`M ${cx - size*0.36} ${cy} A ${size*0.36} ${size*0.36} 0 0 0 ${cx + size*0.36} ${cy}`} />
        </defs>
        {/* vinyl disc */}
        <circle cx={cx} cy={cy} r={size * 0.44} fill="#1a1612"/>
        {/* grooves */}
        {[0.42, 0.40, 0.38, 0.36, 0.34, 0.32].map((r, i) => (
          <circle key={i} cx={cx} cy={cy} r={size * r} fill="none" stroke="#3a3128" strokeWidth=".5" opacity=".6"/>
        ))}
        {/* label */}
        <circle cx={cx} cy={cy} r={size * 0.3} fill="#c1392b"/>
        <circle cx={cx} cy={cy} r={size * 0.29} fill="none" stroke="#7a1f15" strokeWidth=".8"/>
        {/* center hole */}
        <circle cx={cx} cy={cy} r={size * 0.018} fill="#1a1612"/>
        {/* curved title around top */}
        <text fontSize={size * 0.055}
              fontFamily='"DM Serif Display", Georgia, serif'
              fill="#fff" letterSpacing=".05em">
          <textPath href={`#${id}-top`} startOffset="50%" textAnchor="middle">
            {track.title.length > 28 ? track.title.slice(0, 26) + "…" : track.title}
          </textPath>
        </text>
        {/* curved year around bottom */}
        <text fontSize={size * 0.04}
              fontFamily='"Space Mono", monospace'
              fill="#fff" letterSpacing=".25em">
          <textPath href={`#${id}-bot`} startOffset="50%" textAnchor="middle" side="right">
            ★ ★ ★ {track.year} ★ ★ ★
          </textPath>
        </text>
        {/* center artist */}
        <text x={cx} y={cy - size * 0.005} textAnchor="middle"
              fontSize={size * 0.05}
              fontFamily='"Space Mono", monospace'
              fill="#fff" letterSpacing=".15em">
          {track.artist.length > 18 ? track.artist.slice(0, 16) + "…" : track.artist.toUpperCase()}
        </text>
        <text x={cx} y={cy + size * 0.055} textAnchor="middle"
              fontSize={size * 0.032}
              fontFamily='"DM Serif Display", Georgia, serif'
              fontStyle="italic"
              fill="#fff" opacity=".85">
          a QRJam single
        </text>
      </svg>
    </CardFrame>
  );
};

const VinylBack = ({ track, size }) => (
  <CardFrame size={size} bg="#1a1612">
    <div style={{
      position: "absolute", inset: size * 0.04,
      background: "#f4ead8", borderRadius: size * 0.03,
      display: "flex", flexDirection: "column",
      alignItems: "center", justifyContent: "space-between",
      padding: size * 0.07,
    }}>
      <div style={{
        fontFamily: '"DM Serif Display", Georgia, serif',
        fontSize: size * 0.055, color: "#1a1612",
        letterSpacing: ".2em", textTransform: "uppercase",
      }}>
        Side B
      </div>
      <div style={{
        background: "#fff",
        padding: size * 0.025,
        border: `1px solid #1a1612`,
      }}>
        <PseudoQR track={track} size={size * 0.55} fg="#1a1612" bg="#fff" />
      </div>
      <div style={{
        fontFamily: '"Space Mono", monospace',
        fontSize: size * 0.04, color: "#1a1612",
        letterSpacing: ".18em", textTransform: "uppercase",
      }}>
        ▸ Drop the Needle ◂
      </div>
    </div>
  </CardFrame>
);

// ─────────────────────────────────────────────────────────────────────
// 6. CUSTOM — fully user-defined via a config object
// ─────────────────────────────────────────────────────────────────────

const CUSTOM_FONTS = {
  serif:  '"Fraunces", "DM Serif Display", Georgia, serif',
  sans:   '"Space Grotesk", "Inter", system-ui, sans-serif',
  bold:   '"Bricolage Grotesque", "Space Grotesk", sans-serif',
  mono:   '"Space Mono", monospace',
};

function isLightHex(hex) {
  const h = String(hex).replace("#", "");
  const x = h.length === 3 ? h.replace(/./g, (c) => c + c) : h.padEnd(6, "0");
  const n = parseInt(x.slice(0, 6), 16);
  if (Number.isNaN(n)) return true;
  const r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255;
  return r * 299 + g * 587 + b * 114 > 148000;
}

const CustomPattern = ({ pattern, size, color, isLight }) => {
  const tint = isLight ? "rgba(0,0,0,.06)" : "rgba(255,255,255,.08)";
  if (pattern === "none") return null;
  if (pattern === "dots") return (
    <svg width={size} height={size} style={{ position: "absolute", inset: 0, opacity: .8 }}>
      <defs>
        <pattern id={`p-d-${color.replace("#","")}`} width="14" height="14" patternUnits="userSpaceOnUse">
          <circle cx="7" cy="7" r="1.2" fill={tint}/>
        </pattern>
      </defs>
      <rect width="100%" height="100%" fill={`url(#p-d-${color.replace("#","")})`}/>
    </svg>
  );
  if (pattern === "lines") return (
    <div style={{ position:"absolute", inset:0,
      backgroundImage: `repeating-linear-gradient(45deg, ${tint} 0 1.5px, transparent 1.5px 9px)` }}/>
  );
  if (pattern === "grain") return (
    <div style={{ position:"absolute", inset:0,
      backgroundImage: `radial-gradient(${tint} 1px, transparent 1.5px)`,
      backgroundSize: "4px 4px", opacity: .5 }}/>
  );
  if (pattern === "corner-blob") return (
    <svg width={size} height={size} style={{ position:"absolute", inset:0 }}>
      <circle cx={size*0.95} cy={size*0.95} r={size*0.35} fill={color} opacity=".25"/>
      <circle cx={size*0.05} cy={size*0.1} r={size*0.18} fill={color} opacity=".15"/>
    </svg>
  );
  if (pattern === "stripes") return (
    <div style={{ position:"absolute", inset:0,
      backgroundImage: `repeating-linear-gradient(0deg, ${color}22 0 8px, transparent 8px 24px)` }}/>
  );
  return null;
};

const CustomFront = ({ track, size, config }) => {
  const c = { bg:"#1a3a5c", ink:"#ffffff", accent:"#ffd83d", font:"bold", pattern:"dots", ...config };
  const light = isLightHex(c.bg);
  return (
    <CardFrame size={size} bg={c.bg}>
      <CustomPattern pattern={c.pattern} size={size} color={c.accent} isLight={light}/>
      <div style={{
        position: "absolute", inset: 0, padding: size * 0.09,
        display: "flex", flexDirection: "column", justifyContent: "space-between",
      }}>
        <div style={{
          fontFamily: CUSTOM_FONTS.mono, fontSize: size * 0.04,
          color: c.accent, letterSpacing: ".22em", textTransform: "uppercase",
          fontWeight: 700,
        }}>
          ◆ Track
        </div>

        <div>
          <div style={{
            fontFamily: CUSTOM_FONTS[c.font] || CUSTOM_FONTS.bold,
            fontSize: size * 0.24, color: c.accent,
            lineHeight: 1, fontWeight: c.font === "serif" ? 500 : 800,
            letterSpacing: c.font === "serif" ? "-.02em" : "-.03em",
            fontVariantNumeric: "tabular-nums",
            fontStyle: c.font === "serif" ? "italic" : "normal",
          }}>
            {track.year}
          </div>
        </div>

        <div>
          <div style={{
            fontFamily: CUSTOM_FONTS[c.font] || CUSTOM_FONTS.bold,
            fontSize: size * 0.075, color: c.ink,
            fontWeight: c.font === "serif" ? 500 : 700,
            lineHeight: 1.05, letterSpacing: "-.01em",
            textWrap: "balance",
          }}>
            {track.title}
          </div>
          <div style={{
            fontFamily: CUSTOM_FONTS.mono, fontSize: size * 0.044,
            color: c.ink, opacity: .7, marginTop: size * 0.02,
            letterSpacing: ".08em", textTransform: "uppercase",
          }}>
            {track.artist}
          </div>
        </div>
      </div>
    </CardFrame>
  );
};

const CustomBack = ({ track, size, config }) => {
  const c = { bg:"#1a3a5c", ink:"#ffffff", accent:"#ffd83d", font:"bold", pattern:"dots", ...config };
  const light = isLightHex(c.bg);
  return (
    <CardFrame size={size} bg={c.bg}>
      <CustomPattern pattern={c.pattern} size={size} color={c.accent} isLight={light}/>
      <div style={{
        position: "absolute", inset: 0, padding: size * 0.08,
        display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "space-between",
      }}>
        <div style={{
          fontFamily: CUSTOM_FONTS[c.font] || CUSTOM_FONTS.bold,
          fontSize: size * 0.07, color: c.accent,
          fontWeight: c.font === "serif" ? 600 : 800,
          letterSpacing: c.font === "serif" ? ".05em" : "-.01em",
          fontStyle: c.font === "serif" ? "italic" : "normal",
        }}>
          QRJam
        </div>
        <div style={{
          padding: size * 0.025, background: "#fff", borderRadius: 4,
          boxShadow: `0 0 0 ${size*0.012}px ${c.accent}`,
        }}>
          <PseudoQR track={track} size={size * 0.55} fg="#000" bg="#fff" />
        </div>
        <div style={{
          fontFamily: CUSTOM_FONTS.mono, fontSize: size * 0.04,
          color: c.ink, opacity: .8, letterSpacing: ".18em", textTransform: "uppercase",
        }}>
          ▸ Scan & Play ◂
        </div>
      </div>
    </CardFrame>
  );
};

// ─────────────────────────────────────────────────────────────────────
// Style registry
// ─────────────────────────────────────────────────────────────────────

const CARD_STYLES = {
  retro:   { label: "Retro",       hint: "Jaren 70 — warme tinten", Front: RetroFront,   Back: RetroBack   },
  neon:    { label: "Neon",        hint: "Donker met felle glow",   Front: NeonFront,    Back: NeonBack    },
  memphis: { label: "80's Memphis",hint: "Geometrisch & speels",    Front: MemphisFront, Back: MemphisBack },
  party:   { label: "Party",       hint: "Confetti & knal-kleuren", Front: PartyFront,   Back: PartyBack   },
  vinyl:   { label: "Vinyl",       hint: "Plaat-label stijl",       Front: VinylFront,   Back: VinylBack   },
  custom:  { label: "Custom",      hint: "Mix je eigen kleuren",    Front: CustomFront,  Back: CustomBack, custom: true },
};

function Card({ track, style = "retro", side = "front", size = 280, customConfig }) {
  const S = CARD_STYLES[style] || CARD_STYLES.retro;
  const Comp = side === "back" ? S.Back : S.Front;
  return <Comp track={track} size={size} config={customConfig} />;
}

Object.assign(window, { Card, CARD_STYLES, CUSTOM_FONTS });
