← back to CelebritySignatures

public/index.html

823 lines

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Celebrity Signatures — authentic signatures of history's icons</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;1,400;1,600&family=Dancing+Script:wght@600&display=swap" rel="stylesheet">
<style>
  :root { --cols: 5; --ink:#1a1a1a; --muted:#6b6b6b; --line:#e6e3dc; --bg:#f7f5f0; --card:#fff; }
  * { box-sizing: border-box; }
  body { margin:0; font:15px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; color:var(--ink); background:var(--bg); }
  /* masthead: Playfair wordmark + global nav across the three surfaces */
  .masthead { position:sticky; top:0; z-index:30; display:flex; align-items:center; gap:22px; height:56px; padding:0 24px; background:#fff; border-bottom:1px solid var(--line); }
  .wordmark { font:italic 600 24px/1 'Playfair Display', Georgia, serif; color:var(--ink); text-decoration:none; letter-spacing:.01em; }
  .gnav { margin-left:auto; display:flex; gap:20px; }
  .gnav a { font-size:13.5px; color:var(--muted); text-decoration:none; padding:4px 2px; border-bottom:2px solid transparent; }
  .gnav a.on, .gnav a:hover { color:var(--ink); border-bottom-color:var(--ink); }
  header { padding:18px 28px 14px; border-bottom:1px solid var(--line); background:#fff; }
  h1 { margin:0; font:700 26px/1.2 'Playfair Display', Georgia, serif; letter-spacing:.01em; }
  h1 span { color:var(--muted); font:400 14px -apple-system,BlinkMacSystemFont,sans-serif; margin-left:8px; }
  .valueprop { margin:6px 0 0; color:var(--muted); font-size:14px; }
  .valueprop a { color:var(--green, #1d7a36); }
  .search { margin-top:14px; }
  .search input { width:min(420px,100%); padding:9px 14px; border:1px solid var(--line); border-radius:999px; font:inherit; background:#fffef9; }
  .chip-count { background:rgba(0,0,0,.07); border-radius:9px; padding:0 6px; font-size:11px; margin-left:5px; }
  .chip.on .chip-count { background:rgba(255,255,255,.25); }
  .controls { display:flex; flex-wrap:wrap; gap:18px 22px; align-items:center; margin-top:14px; }
  .controls label { font-size:12px; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); margin-right:8px; }
  select, input[type=range] { font:inherit; }
  select { padding:6px 10px; border:1px solid var(--line); border-radius:7px; background:#fff; }
  .chips { display:flex; flex-wrap:wrap; gap:7px; }
  .chip { padding:5px 12px; border:1px solid var(--line); border-radius:999px; background:#fff; cursor:pointer; font-size:13px; color:var(--muted); }
  .chip.on { background:var(--ink); color:#fff; border-color:var(--ink); }
  .density { display:flex; align-items:center; gap:10px; }
  main { padding:22px 28px 60px; }
  .grid { display:grid; grid-template-columns:repeat(var(--cols), 1fr); gap:10px; }
  .card { background:#fffef9; border:1px solid #ece8df; border-radius:4px; overflow:hidden; display:flex; flex-direction:column; box-shadow:0 1px 3px rgba(0,0,0,.06), 0 4px 16px rgba(0,0,0,.04); }
  .sig { height:var(--sig-h,88px); display:flex; align-items:center; justify-content:center; padding:8px; background:#fff; border-bottom:1px solid var(--line); cursor:pointer; }
  .sig img { max-width:100%; max-height:100%; object-fit:contain; }
  .meta { padding:6px 8px 8px; display:flex; flex-direction:column; gap:3px; flex:1; }
  .name { font-weight:600; font-size:13px; line-height:1.25; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .row { display:flex; align-items:center; gap:6px; flex-wrap:wrap; min-width:0; }
  .tag { font-size:11px; padding:2px 8px; border-radius:999px; background:#eee; color:#444; max-width:100%; overflow-wrap:break-word; word-break:break-word; }
  .badge { font-size:11px; padding:2px 8px; border-radius:999px; font-weight:600; }
  .b-low { background:#e3f3e6; color:#1d7a36; } .b-medium { background:#fdf1d8; color:#9a6b00; } .b-high { background:#fae0df; color:#b3261e; }
  .use-yes { background:#1d7a36; color:#fff; } .use-perm { background:#9a6b00; color:#fff; } .use-review { background:#777; color:#fff; }
  .sub { font-size:11.5px; color:var(--muted); margin-top:auto; }
  .sub a { color:var(--muted); }
  .empty { color:var(--muted); padding:40px; text-align:center; }
  .tabs { display:flex; gap:4px; margin-top:14px; }
  .tab { padding:7px 18px; border:1px solid var(--line); border-bottom:none; border-radius:9px 9px 0 0; background:#faf9f6; cursor:pointer; font-size:14px; color:var(--muted); }
  .tab.on { background:var(--ink); color:#fff; border-color:var(--ink); }
  .collages { display:flex; flex-direction:column; gap:30px; max-width:1100px; }
  .collage { border:1px solid var(--line); border-radius:14px; overflow:hidden; background:#fff; }
  .collage .ch { display:flex; align-items:baseline; gap:12px; padding:14px 18px; border-bottom:1px solid var(--line); }
  .collage .ch b { font-size:17px; }
  .collage .ch .c { color:var(--muted); font-size:13px; }
  .collage .ch a { margin-left:auto; font-size:13px; color:#1d7a36; text-decoration:none; }
  .collage img { width:100%; display:block; }
  .collage .note { padding:24px 18px; color:var(--muted); font-size:14px; }
  /* signature-evolution strip (rendered inside the details popup) */
  .evo-strip { display:flex; flex-wrap:wrap; gap:10px; padding:6px 0 2px; }
  .evo-cell { text-align:center; }
  .evo-cell img { max-height:44px; max-width:140px; object-fit:contain; background:#fff; border:1px solid var(--line); border-radius:6px; padding:3px; }
  .evo-cell .y { font-size:10px; color:var(--muted); }
  /* minimal card: details chip — hidden on load; revealed only when the card /
     signature is hovered (or keyboard-focused). Hover the signature to surface it,
     then click to open the details popup. */
  .info-chip { display:inline-block; margin-top:2px; font-size:11px; color:var(--muted); border:1px solid var(--line); border-radius:999px; padding:2px 9px; cursor:pointer; text-decoration:none;
    opacity:0; transform:translateY(2px); pointer-events:none; transition:opacity .16s ease, transform .16s ease; }
  .card:hover .info-chip, .card:focus-within .info-chip, .info-chip:focus-visible { opacity:1; transform:none; pointer-events:auto; }
  .info-chip:hover { background:var(--ink); color:#fff; border-color:var(--ink); }
  .chip-face { width:16px; height:16px; border-radius:50%; object-fit:cover; vertical-align:-3px; margin-right:5px; }
  .ap-portrait { width:56px; height:56px; border-radius:50%; object-fit:cover; border:2px solid var(--line); margin-right:14px; vertical-align:middle; }
  .ap-figure { text-align:center; padding:6px 0 2px; }
  .ap-figure img { max-width:340px; max-height:420px; width:100%; object-fit:contain; border-radius:8px; border:1px solid var(--line); }
  .ap-figure .ap-sub { margin-top:6px; }
  /* touch / no-hover devices can't reveal on hover — keep the chip visible there */
  @media (hover: none) { .info-chip { opacity:1; transform:none; pointer-events:auto; } }
  /* ── 6 shopping layouts — signature only; ALL info reveals on hover ──
     switched via body[data-layout]; persisted in prefs.layout */
  /* L1 gallery (refined default): name always visible, shown BELOW the details chip */
  body[data-layout] .meta .name { transition:opacity .18s ease, transform .18s ease; }
  body[data-layout="gallery"] .meta { flex-direction:column; align-items:center; text-align:center; }
  body[data-layout="gallery"] .meta .info-chip { order:1; align-self:center; }
  body[data-layout="gallery"] .meta .name { order:2; margin-top:4px; text-align:center; }
  body[data-layout="float"] .meta .name { opacity:0; transform:translateY(3px); }
  body[data-layout="float"] .card:hover .name, body[data-layout="float"] .card:focus-within .name { opacity:1; transform:none; }
  /* L2 float: no chrome at all — ink floating on cream */
  body[data-layout="float"] .card { background:transparent; border:none; box-shadow:none; }
  body[data-layout="float"] .sig { background:transparent; border:none; }
  body[data-layout="float"] .meta { padding:6px 4px 10px; text-align:center; }
  body[data-layout="float"] .card:hover .sig { background:#fff; border-radius:8px; }
  /* L3 plaque: dark plate slides up over the signature on hover */
  body[data-layout="plaque"] .card { position:relative; }
  body[data-layout="plaque"] .sig { height:150px; border-bottom:none; }
  body[data-layout="plaque"] .meta { position:absolute; left:0; right:0; bottom:0; background:rgba(20,18,14,.88); color:#fff;
    transform:translateY(101%); transition:transform .22s ease; padding:10px 13px 12px; }
  body[data-layout="plaque"] .meta .name { color:#fff; opacity:1; transform:none; }
  body[data-layout="plaque"] .card:hover .meta, body[data-layout="plaque"] .card:focus-within .meta { transform:none; }
  body[data-layout="plaque"] .info-chip { color:#e8e4dc; border-color:rgba(255,255,255,.4); }
  /* L4 ledger: auction-catalog rows; details column fades in on hover */
  body[data-layout="ledger"] .grid { grid-template-columns:1fr !important; gap:0; max-width:880px; }
  body[data-layout="ledger"] .card { flex-direction:row; align-items:center; border:none; border-bottom:1px solid var(--line);
    border-radius:0; box-shadow:none; background:transparent; }
  body[data-layout="ledger"] .sig { height:84px; width:300px; flex:none; background:transparent; border:none; padding:10px 16px; justify-content:flex-start; }
  body[data-layout="ledger"] .meta { flex-direction:row; align-items:center; gap:16px; opacity:0; transition:opacity .18s; }
  body[data-layout="ledger"] .card:hover .meta, body[data-layout="ledger"] .card:focus-within .meta { opacity:1; }
  body[data-layout="ledger"] .meta .name { opacity:1; transform:none; margin:0; }
  body[data-layout="ledger"] .card:hover { background:#fff; }
  /* L5 vitrine: large airy tiles, caption fades over glass */
  body[data-layout="vitrine"] .grid { grid-template-columns:repeat(3,1fr) !important; gap:26px; }
  body[data-layout="vitrine"] .card { position:relative; border-radius:6px; }
  body[data-layout="vitrine"] .sig { height:230px; border-bottom:none; transition:transform .25s ease; }
  body[data-layout="vitrine"] .card:hover .sig { transform:scale(1.05); }
  body[data-layout="vitrine"] .meta { position:absolute; left:0; right:0; bottom:0; padding:12px 15px;
    background:linear-gradient(transparent, rgba(255,255,254,.96) 45%); opacity:0; transition:opacity .2s; }
  body[data-layout="vitrine"] .card:hover .meta, body[data-layout="vitrine"] .card:focus-within .meta { opacity:1; }
  body[data-layout="vitrine"] .meta .name { opacity:1; transform:none; font:600 17px/1.2 'Playfair Display', Georgia, serif; }
  /* L6 wall: dense texture of pure signatures; hovered one lifts with a floating name plate */
  body[data-layout="wall"] .grid { grid-template-columns:repeat(8,1fr) !important; gap:8px; }
  body[data-layout="wall"] .card { border:none; box-shadow:none; background:transparent; overflow:visible; }
  body[data-layout="wall"] .sig { height:74px; padding:8px; background:transparent; border:none; transition:transform .15s; }
  body[data-layout="wall"] .card:hover { z-index:5; }
  body[data-layout="wall"] .card:hover .sig { transform:scale(1.6); background:#fff; border-radius:8px; box-shadow:0 10px 30px rgba(0,0,0,.18); }
  body[data-layout="wall"] .meta { position:absolute; left:50%; transform:translateX(-50%) translateY(4px); top:100%;
    background:#fff; border:1px solid var(--line); border-radius:8px; padding:7px 12px; box-shadow:0 8px 24px rgba(0,0,0,.15);
    opacity:0; pointer-events:none; transition:opacity .15s; white-space:nowrap; z-index:6; }
  body[data-layout="wall"] .card:hover .meta, body[data-layout="wall"] .card:focus-within .meta { opacity:1; pointer-events:auto; }
  body[data-layout="wall"] .meta .name { opacity:1; transform:none; font-size:13px; }
  /* touch devices: no hover — keep info visible in every layout */
  @media (hover: none) {
    body[data-layout] .meta { opacity:1 !important; transform:none !important; position:static !important; }
    body[data-layout] .meta .name { opacity:1 !important; transform:none !important; }
  }
  .layout-chips .chip { font-size:12px; padding:4px 11px; }
  /* ── elegant front page: topbar logo · hero · left drawer ── */
  .masthead .logo img { height:30px; display:block; mix-blend-mode:multiply; }
  .topsearch { flex:1; max-width:440px; margin:0 8px; }
  .topsearch input { width:100%; padding:8px 14px; border:1px solid var(--line); border-radius:999px; font:inherit; background:#faf9f6; }
  .burger { width:40px; height:40px; border:1px solid var(--line); border-radius:10px; background:#fff; cursor:pointer; display:flex; flex-direction:column; justify-content:center; align-items:center; gap:4px; }
  .burger span { display:block; width:18px; height:2px; background:var(--ink); border-radius:2px; }
  .burger:hover { border-color:var(--ink); }
  .hero { position:relative; overflow:hidden; border-bottom:1px solid var(--line); background:#fbfaf7; }
  .hero-bg { position:absolute; inset:-2%; background:url('/output/collage-masters-of-the-met.png') center 30%/cover no-repeat; opacity:.16; filter:saturate(.7); }
  .hero-inner { position:relative; max-width:860px; margin:0 auto; padding:64px 24px 58px; text-align:center; }
  .hero-logo { max-width:min(560px,86%); mix-blend-mode:multiply; animation:heroLogoFloat 16s ease-in-out infinite; will-change:transform; }
  @keyframes heroLogoFloat { 0%{transform:translateY(0) scale(1)} 50%{transform:translateY(-12px) scale(1.015)} 100%{transform:translateY(0) scale(1)} }
  @media (prefers-reduced-motion:reduce){ .hero-logo{ animation:none } }
  .hero-tag { font:italic 400 18px/1.6 'Playfair Display', Georgia, serif; color:#4a463f; max-width:560px; margin:14px auto 22px; }
  .hero-ctas { display:flex; gap:12px; justify-content:center; flex-wrap:wrap; }
  .hbtn { padding:11px 26px; border-radius:999px; background:var(--ink); color:#fff; text-decoration:none; font-size:14.5px; border:1px solid var(--ink); }
  .hbtn.ghost { background:rgba(255,255,255,.85); color:var(--ink); }
  .hbtn:hover { background:#000; color:#fff; }
  /* drawer */
  .dback { position:fixed; inset:0; background:rgba(20,18,14,.45); z-index:60; }
  .dback[hidden] { display:none; }
  .drawer { position:fixed; top:0; left:0; bottom:0; width:min(320px,88vw); background:#fff; z-index:61; box-shadow:8px 0 40px rgba(0,0,0,.18);
    transform:translateX(-103%); transition:transform .22s ease; overflow-y:auto; padding:16px 18px 40px; }
  .drawer.open { transform:none; }
  .d-head { display:flex; align-items:center; justify-content:space-between; padding-bottom:10px; border-bottom:1px solid var(--line); margin-bottom:12px; }
  .d-head b { font:600 18px 'Playfair Display', Georgia, serif; }
  .d-x { border:none; background:none; font-size:26px; cursor:pointer; color:var(--muted); line-height:1; }
  .d-sec { margin-bottom:14px; }
  .d-lab { font-size:11px; text-transform:uppercase; letter-spacing:.07em; color:var(--muted); margin:0 0 8px; }
  .d-links { display:flex; flex-direction:column; gap:7px; margin-top:10px; }
  .d-links a { color:var(--ink); text-decoration:none; font-size:14px; padding:6px 2px; border-bottom:1px dashed var(--line); }
  .d-links a:hover { color:var(--green, #1d7a36); }
  .drawer .tabs { margin-top:0; }
  .drawer .controls { flex-direction:column; align-items:stretch; gap:14px; margin-top:8px; }
  .drawer .controls > div { width:100%; }
  .drawer .controls select { width:100%; }
  .drawer .chips { gap:6px; }
  .drawer .density { display:flex !important; }
  .section-head { display:flex; align-items:center; gap:14px; margin:6px 0 16px; }
  .section-head h2 { margin:0; font:600 22px 'Playfair Display', Georgia, serif; }
  .section-head h2 span { color:var(--muted); font:400 13px -apple-system,sans-serif; }
  .filters-hint { margin-left:auto; border:1px solid var(--line); background:#fff; border-radius:999px; padding:7px 16px; font:inherit; font-size:13px; color:var(--muted); cursor:pointer; }
  .filters-hint:hover { border-color:var(--ink); color:var(--ink); }
  @media (max-width:700px){
    .masthead .logo img { height:22px; }
    .topsearch { order:5; max-width:none; margin:0; width:100%; }
    .hero-inner { padding:34px 16px 32px; }
    .hero-tag { font-size:15px; }
  }
  /* ── mobile ── */
  @media (max-width:700px) {
    .masthead { height:auto; flex-wrap:wrap; padding:10px 14px; gap:8px 14px; }
    .wordmark { font-size:19px; }
    .gnav { margin-left:0; width:100%; gap:14px; overflow-x:auto; -webkit-overflow-scrolling:touch; }
    .gnav a { font-size:12.5px; white-space:nowrap; }
    header { padding:14px 16px 12px; }
    h1 { font-size:20px; }
    .valueprop { font-size:12.5px; }
    main { padding:14px 12px 60px; }
    .grid { grid-template-columns:repeat(2,1fr) !important; gap:10px; }
    body[data-layout="ledger"] .grid { grid-template-columns:1fr !important; }
    body[data-layout="ledger"] .sig { width:44%; }
    body[data-layout="wall"] .grid { grid-template-columns:repeat(4,1fr) !important; }
    body[data-layout="vitrine"] .grid { grid-template-columns:1fr !important; }
    .sig { height:var(--sig-h,96px); padding:10px; }
    .controls { gap:10px 14px; }
    .search input { width:100%; }
    .density { display:none; } /* columns are fixed on phones */
    .ap-card { width:96vw; max-height:92vh; border-radius:10px; }
    .ap-inner { padding:16px 16px 84px; }
    .ap-name { font-size:22px; }
    .works { grid-template-columns:repeat(2,1fr); }
    .ap-cta { padding:10px 14px; }
    .ap-cta a { width:100%; }
  }
  /* artist details popup */
  #artistPop { position:fixed; inset:0; z-index:50; display:flex; align-items:center; justify-content:center; }
  #artistPop[hidden] { display:none; }
  .ap-back { position:absolute; inset:0; background:rgba(20,18,14,.55); }
  .ap-card { position:relative; background:#fff; border-radius:10px; max-width:820px; width:min(94vw,820px); max-height:88vh; overflow:auto; padding:0 0 0; box-shadow:0 24px 80px rgba(0,0,0,.35); }
  .ap-inner { padding:22px 28px 84px; }
  .ap-x { position:absolute; top:12px; right:14px; border:none; background:none; font-size:26px; line-height:1; cursor:pointer; color:var(--muted); z-index:2; }
  .ap-x:hover { color:var(--ink); }
  .ap-sig { text-align:center; padding:16px 0 14px; border-bottom:1px solid var(--line); margin-bottom:12px; }
  .ap-sig img { max-height:110px; max-width:92%; object-fit:contain; }
  .ap-name { font:400 28px/1.15 'Playfair Display', Georgia, serif; margin:6px 0 2px; text-align:center; }
  .ap-life { text-align:center; }
  .ap-rowline { justify-content:center; }
  .ap-cta { position:sticky; bottom:0; background:#fff; border-top:1px solid var(--line); padding:12px 28px; text-align:center; }
  .ap-cta a { display:inline-block; padding:10px 26px; border-radius:999px; background:var(--ink); color:#fff; text-decoration:none; font-size:14.5px; }
  .ap-cta a:hover { background:#000; }
  .ap-life { color:var(--muted); font-size:13px; margin-bottom:10px; }
  .ap-rowline { display:flex; flex-wrap:wrap; gap:6px; margin:8px 0; }
  .ap-sec { font-size:12px; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); margin:16px 0 6px; }
  .ap-sub { font-size:13px; color:var(--muted); }
  .works { display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:10px; }
  .works a { display:block; border:1px solid var(--line); border-radius:10px; overflow:hidden; background:#faf9f6; }
  .works img { width:100%; height:150px; object-fit:cover; display:block; }
  .works .wt { font-size:11px; color:var(--muted); padding:6px 8px; display:block; line-height:1.3; }
  .shop-actions { display:flex; flex-wrap:wrap; gap:7px; justify-content:center; padding:8px; border-top:1px solid var(--line); margin-top:auto; position:relative; background:var(--card); }
  .shop-actions a { flex:1; min-width:68px; display:flex; align-items:center; justify-content:center; min-height:40px; border:1px solid var(--line); border-radius:999px; padding:6px 10px; font-size:12px; color:var(--ink); text-decoration:none; background:#fff; }
  .shop-actions a:hover, .shop-actions a:focus-visible { border-color:var(--ink); background:var(--ink); color:#fff; }
  body[data-layout="ledger"] .shop-actions { margin:0 10px 0 auto; flex-shrink:0; border-top:0; }
  .ap-shop { display:flex; flex-wrap:wrap; justify-content:center; gap:10px; margin:16px 0; }
  .ap-shop a { display:inline-block; padding:11px 24px; border:1px solid var(--ink); border-radius:999px; text-decoration:none; background:var(--ink); color:#fff; }
  .ap-shop a + a { background:#fff; color:var(--ink); }
  .ap-cta { display:flex; flex-wrap:wrap; justify-content:center; gap:10px; }
  @media(max-width:700px){ .shop-actions a { min-height:44px; } .ap-shop a { flex:1; text-align:center; } }
  /* ── Gucci-style editorial "Signature Edit" hero + campaign accent (front page) ── */
  .hero--edit .hero-inner { max-width:1080px; padding:56px 24px 44px; text-align:center; position:relative; z-index:2; }
  .hero--edit .hero-bg { opacity:.07; filter:saturate(.6); }
  .hero--edit::after { content:""; position:absolute; inset:0; z-index:1; pointer-events:none;
    background:radial-gradient(ellipse 62% 58% at 50% 42%, rgba(251,250,247,.95), rgba(251,250,247,.55) 60%, rgba(251,250,247,0) 82%); }
  .edit-kicker { font-size:12px; letter-spacing:.28em; text-transform:uppercase; color:var(--muted); }
  .edit-head { font:400 clamp(34px,6vw,74px)/1.02 'Playfair Display',Georgia,serif; letter-spacing:-.01em; margin:14px 0 0; }
  .edit-head em { font-style:italic; }
  .edit-sub { font:italic 400 18px/1.6 'Playfair Display',Georgia,serif; color:#4a463f; max-width:600px; margin:14px auto 24px; }
  .edit-sig { display:flex; flex-direction:column; align-items:center; gap:9px; margin:4px auto 30px; }
  .edit-sig img { max-width:min(440px,82%); height:auto; }
  .edit-sig .attr { font-size:12px; color:var(--muted); }
  .edit-sig .attr a { color:var(--green,#1d7a36); text-decoration:none; }
  .edit-trio { display:grid; grid-template-columns:repeat(3,1fr); gap:clamp(10px,2vw,24px); max-width:820px; margin:0 auto 28px; }
  .edit-piece { display:block; text-decoration:none; color:inherit; }
  .edit-piece .ph { aspect-ratio:1/1; background:#fff; border:1px solid var(--line); border-radius:10px; overflow:hidden; }
  .edit-piece .ph img { width:100%; height:100%; object-fit:cover; transition:transform .5s ease; }
  .edit-piece:hover .ph img { transform:scale(1.045); }
  .edit-piece .pl { margin-top:9px; font:600 15px 'Playfair Display',Georgia,serif; }
  .edit-piece .pn { font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:var(--muted); margin-top:2px; }
  @media(max-width:640px){ .edit-trio{ grid-template-columns:1fr; max-width:320px; } }
  /* full-bleed campaign image with the text OVERLAID in the left open area */
  .campaign { position:relative; background:#cdbb90; border-bottom:1px solid var(--line); }
  .campaign .cimg img { width:100%; height:auto; display:block; }
  .campaign .ctxt { position:absolute; top:0; left:0; bottom:0; width:min(48%,640px);
    display:flex; flex-direction:column; justify-content:center; gap:14px;
    padding:clamp(24px,4vw,72px); color:#241d15;
    background:linear-gradient(90deg, rgba(247,245,240,.62) 0%, rgba(247,245,240,.30) 55%, rgba(247,245,240,0) 82%); }
  .campaign .ck { font-size:12px; letter-spacing:.26em; text-transform:uppercase; color:#5c5140; }
  .campaign .ch2 { font:400 clamp(24px,3.4vw,46px)/1.04 'Playfair Display',Georgia,serif; margin:0; color:#1a1410; }
  .campaign .ch2 em { font-style:italic; }
  .campaign p { line-height:1.7; max-width:34ch; font-size:14px; margin:0; color:#3a3226; }
  .campaign a.clink { width:max-content; font-size:12px; letter-spacing:.18em; text-transform:uppercase; color:#1a1410; border-bottom:1px solid rgba(26,20,16,.55); padding-bottom:5px; text-decoration:none; }
  .campaign a.clink:hover { border-bottom-color:#1a1410; }
  .campaign .rendering { font-size:11px; letter-spacing:.02em; color:#6b5f4c; margin:0; max-width:34ch; }
  /* mobile: overlay would crowd — fall back to image on top, dark text band below */
  @media(max-width:760px){
    .campaign { background:#141210; }
    .campaign .ctxt { position:static; width:auto; background:none; color:#f4f1ea; padding:clamp(28px,7vw,44px); }
    .campaign .ck { color:rgba(244,241,234,.6); }
    .campaign .ch2 { color:#f4f1ea; }
    .campaign p { color:rgba(244,241,234,.78); }
    .campaign a.clink { color:#f4f1ea; border-bottom-color:rgba(244,241,234,.5); }
    .campaign .rendering { color:rgba(244,241,234,.5); }
  }
</style>
<link rel="stylesheet" href="/assets/signature-preview.css">
<!-- Google Analytics 4 (G-2HEVP6TD0J) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-2HEVP6TD0J"></script>
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-2HEVP6TD0J');</script>
<script src="/assets/ga-events.js" defer></script>
<link rel="canonical" href="https://celebsignatures.com/">
<meta name="description" content="Browse 7,000+ authentic signatures of history’s icons — artists, authors, presidents, founders — each traced to a real archive. Order any as a wall mural.">
<link rel="apple-touch-icon" href="/assets/favicon.png">
<link rel="icon" type="image/png" href="/assets/favicon.png">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Celebrity Signatures">
<meta property="og:title" content="Celebrity Signatures — the signatures of history">
<meta property="og:description" content="Browse 7,000+ authentic signatures of history’s icons — artists, authors, presidents, founders — each traced to a real archive. Order any as a wall mural.">
<meta property="og:url" content="https://celebsignatures.com/">
<meta property="og:image" content="https://celebsignatures.com/assets/og-share.jpg">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Celebrity Signatures — the signatures of history">
<meta name="twitter:description" content="Browse 7,000+ authentic signatures of history’s icons — artists, authors, presidents, founders — each traced to a real archive. Order any as a wall mural.">
<meta name="twitter:image" content="https://celebsignatures.com/assets/og-share.jpg">
</head>
<body>
<div class="masthead">
  <a class="logo" href="/"><img src="/assets/logo-celebsig.png" alt="Celebrity Signatures"></a>
  <div class="search topsearch"><input type="search" id="q" placeholder="Search any name — Monet, Lincoln, Kahlo…" aria-label="Search signatures by name"></div>
  <nav class="gnav">
    <a href="/wallpaper">Wallpaper</a>
    <a href="/wear">Clothing</a>
    <a href="/game">Games</a>
    <a href="/upload">Upload yours</a>
  </nav>
  <button class="burger" id="burger" aria-label="Open menu" aria-expanded="false"><span></span><span></span><span></span></button>
</div>

<!-- CAMPAIGN — clearly-labeled artistic rendering (AI), not a photograph -->
<section class="campaign">
  <div class="cimg"><img src="/assets/campaign-lincoln-seated.jpg" width="880" height="1184" alt="Abraham Lincoln seated in the full Signature Edit — bucket hat, tee and socks, each carrying his authentic 1862 autograph" loading="lazy"></div>
  <div class="ctxt">
    <span class="ck">The Campaign · № 001</span>
    <h2 class="ch2">Signed, <em>Mr. Lincoln.</em></h2>
    <p>Our first icon, styled in the full Signature Edit — the bucket hat, the tee, the socks. His authentic 1862 autograph on every piece.</p>
    <a class="clink" href="/signature-edit">Shop the Lincoln edit →</a>
    <p class="rendering">Digitally composed — the Signature Edit on a public-domain portrait of Abraham Lincoln (d. 1865). Not a photograph.</p>
  </div>
</section>

<!-- HERO -->
<section class="hero hero--edit">
  <div class="hero-bg" aria-hidden="true"></div>
  <div class="hero-inner">
    <span class="edit-kicker">The Signature Edit № 001 · Abraham Lincoln</span>
    <h1 class="edit-head">Wear a piece <em>of history.</em></h1>
    <p class="edit-sub">Every piece carries an authentic signature, traced to a real archive — never invented. First: the hand of Abraham Lincoln.</p>
    <div class="edit-sig">
      <img src="https://upload.wikimedia.org/wikipedia/commons/c/c0/Abraham_Lincoln_1862_signature.svg" alt="Authentic 1862 signature of Abraham Lincoln" loading="lazy">
      <span class="attr">Abraham Lincoln · d. 1865 · public domain, Wikimedia Commons · <a href="/a/Q91">view the source →</a></span>
    </div>
    <div class="edit-trio">
      <a class="edit-piece" href="/wear" aria-label="Shop the Tee">
        <div class="ph"><img src="/assets/garments/classic-tee-lincoln.jpg" alt="The Signature Tee with Abraham Lincoln's autograph" loading="lazy"></div>
        <div class="pl">The Tee</div><div class="pn">Signature Ready-to-Wear</div>
      </a>
      <a class="edit-piece" href="/wear" aria-label="Shop the Hat">
        <div class="ph"><img src="/assets/garments/bucket-hat-lincoln.jpg" alt="The Signature Hat with Abraham Lincoln's autograph" loading="lazy"></div>
        <div class="pl">The Hat</div><div class="pn">Signature Headwear</div>
      </a>
      <a class="edit-piece" href="/wear" aria-label="Shop the Socks">
        <div class="ph"><img src="/assets/garments/crew-socks-lincoln.jpg" alt="The Signature Socks with Abraham Lincoln's autograph" loading="lazy"></div>
        <div class="pl">The Socks</div><div class="pn">Signature Legwear</div>
      </a>
    </div>
    <div class="hero-ctas">
      <a class="hbtn" href="#collection">Browse the collection</a>
      <a class="hbtn ghost" href="/signature-edit">Shop the Lincoln edit</a>
      <a class="hbtn ghost" href="/wallpaper?qid=Q91">Create wallpaper</a>
      <a class="hbtn ghost" href="/game">Play the games</a>
    </div>
  </div>
</section>

<!-- LEFT DRAWER (all browse controls live here) -->
<div class="dback" id="dback" hidden></div>
<aside class="drawer" id="drawer" aria-label="Browse options">
  <div class="d-head"><b>Browse</b><button class="d-x" id="drawerX" aria-label="Close menu">×</button></div>
  <div class="d-sec">
    <div class="d-lab">Explore</div>
    <div class="tabs">
      <span class="tab on" data-v="grid">Grid</span>
      <span class="tab" data-v="collages">Collages</span>
    </div>
    <nav class="d-links">
      <a href="/wallpaper">Signature wallpaper</a>
      <a href="/wear">Signature clothing</a>
      <a href="/game">The Signature Games</a>
      <a href="/upload">Upload your signature</a>
    </nav>
  </div>
  <div class="controls" id="controls">
    <div><label>Category</label></div>
    <div class="chips" id="catChips"></div>
    <div><label>Use</label>
      <select id="useFilter">
        <option value="all">All signatures</option>
        <option value="yes">Free to use</option>
        <option value="permission-needed">Permission needed</option>
      </select>
    </div>
    <div><label>Sort</label>
      <select id="sort">
        <optgroup label="Category">
          <option value="cat">Category, then rank</option>
        </optgroup>
        <optgroup label="Name">
          <option value="name">Name A→Z</option>
          <option value="name-desc">Name Z→A</option>
        </optgroup>
        <optgroup label="Rank / notability">
          <option value="rank">Rank (1 → n)</option>
          <option value="notability">Notability ↓</option>
        </optgroup>
        <optgroup label="Usage & risk">
          <option value="usable">Usable first</option>
          <option value="risk">Risk low → high</option>
          <option value="risk-desc">Risk high → low</option>
          <option value="license">License A→Z</option>
        </optgroup>
        <optgroup label="Life">
          <option value="living">Living first</option>
          <option value="deceased">Deceased first</option>
          <option value="death">Death date (earliest → latest)</option>
        </optgroup>
        <optgroup label="Holdings & signatures">
          <option value="museums">Museums held (most first)</option>
          <option value="sigcount">Signatures over time (most first)</option>
        </optgroup>
      </select>
    </div>
    <div class="density"><label>Density</label>
      <input id="density" type="range" min="3" max="9" value="5">
    </div>
    <div class="density"><label>Signature size</label>
      <input id="sigSize" type="range" min="56" max="220" step="4" value="88" aria-label="Signature size across the gallery">
    </div>
    <div><label>View</label></div>
    <div class="chips layout-chips" id="layoutChips"></div>
  </div>
</aside>

<main id="collection">
  <div class="section-head">
    <h2>The Collection <span id="count"></span></h2>
    <button class="filters-hint" id="filtersHint">☰ Categories, sort &amp; views</button>
  </div>
  <div class="ad-zone" data-ad-zone="gallery"></div>
  <div id="gridView"><div class="grid" id="grid"></div><div class="empty" id="empty" hidden>No signatures match these filters.</div></div>
  <div id="collagesView" class="collages" hidden></div>
</main>
<div id="artistPop" hidden>
  <div class="ap-back"></div>
  <div class="ap-card"><button class="ap-x" aria-label="Close">×</button><div id="apBody"></div></div>
</div>

<script>
const signaturePreview = import('/assets/signature-preview.js');
const LS = 'celebsig.prefs';
const prefs = Object.assign({ cat:'All', use:'all', sort:'cat', density:5, view:'grid', gridSigH:null, apSigH:null }, JSON.parse(localStorage.getItem(LS)||'{}'));
let DATA = [];
const $ = (s) => document.querySelector(s);
const notability = (r) => { const m=(r.reason_for_ranking||'').match(/(\d+) language/); return m?+m[1]:0; };
const riskRank = { low:0, medium:1, high:2 };
// field extractors used by the "sort by any field" comparators
const qidOf = (r) => (r.wikidata||'').split('/').pop();
const evoCount = (r) => (EVO[qidOf(r)]?.sigs?.length) || 0;
const museumCount = (r) => Array.isArray(r.museums) ? r.museums.length : 0;
const deathYear = (r) => { const m=(r.death_date||'').match(/-?\d{3,4}/); return m ? +m[0] : Infinity; }; // undated / living sort last

function save() { localStorage.setItem(LS, JSON.stringify(prefs)); }
// ── href-drill: filter state (category / usability) is URL-addressable (shareable + deep-link + back-button) ──
const esc=s=>String(s==null?'':s).replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
function qstr(over){ const c=(over&&'cat'in over)?over.cat:prefs.cat, u=(over&&'use'in over)?over.use:prefs.use; const p=[]; if(c&&c!=='All')p.push('cat='+encodeURIComponent(c)); if(u&&u!=='all')p.push('use='+encodeURIComponent(u)); return p.length?('?'+p.join('&')):location.pathname; }
function drill(dim,val,label){ if(val==null||val==='')return esc(label!=null?label:val); return `<a class="drill" data-filter="${dim}" data-v="${esc(val)}" href="${esc(qstr({[dim]:String(val)}))}" title="Filter to ${esc(val)}">${esc(label!=null?label:val)}</a>`; }
function readURL(){ const u=new URLSearchParams(location.search); if(u.has('cat'))prefs.cat=u.get('cat')||'All'; if(u.has('use'))prefs.use=u.get('use')||'all'; }
function writeURL(push){ history[push?'pushState':'replaceState']({},'',qstr()); }

function signatureShopLinks(r, compact=false){
  const params = new URLSearchParams({ qid:qidOf(r), name:r.full_name });
  return `<a class="shop-wallpaper" href="/wallpaper?${esc(params.toString())}" aria-label="Design wallpaper with ${esc(r.full_name)}">${compact?'Wallpaper':'Design wallpaper'}</a>
    <a class="shop-clothing" href="/wear?${esc(params.toString())}" aria-label="Shop clothing for ${esc(r.full_name)}">${compact?'Clothing':'Shop clothing'}</a>`;
}

function render() {
  document.documentElement.style.setProperty('--cols', prefs.density);
  $('#density').value = prefs.density;
  $('#useFilter').value = prefs.use;
  $('#sort').value = prefs.sort;

  const q = (prefs.q||'').toLowerCase().trim();
  let rows = DATA.filter(r => (prefs.cat==='All' || r.category===prefs.cat)
                           && (prefs.use==='all' || r.usable_in_commercial_collage===prefs.use)
                           && (!q || r.full_name.toLowerCase().includes(q)));
  const cats = ['Oldest Signatures','Declaration of Independence','Politics','Artists','Authors'];
  const sorters = {
    cat: (a,b) => cats.indexOf(a.category)-cats.indexOf(b.category) || a.rank-b.rank,
    name: (a,b) => a.full_name.localeCompare(b.full_name),
    'name-desc': (a,b) => b.full_name.localeCompare(a.full_name),
    rank: (a,b) => a.rank-b.rank,
    notability: (a,b) => notability(b)-notability(a) || a.rank-b.rank,
    usable: (a,b) => (a.usable_in_commercial_collage==='yes'?0:1)-(b.usable_in_commercial_collage==='yes'?0:1) || a.rank-b.rank,
    risk: (a,b) => (riskRank[a.risk_level]??9)-(riskRank[b.risk_level]??9) || a.rank-b.rank,
    'risk-desc': (a,b) => (riskRank[b.risk_level]??-1)-(riskRank[a.risk_level]??-1) || a.rank-b.rank,
    license: (a,b) => (a.image_license||'~').localeCompare(b.image_license||'~') || a.rank-b.rank,
    living: (a,b) => (a.deceased==='yes'?1:0)-(b.deceased==='yes'?1:0) || a.rank-b.rank,
    deceased: (a,b) => (a.deceased==='yes'?0:1)-(b.deceased==='yes'?0:1) || a.rank-b.rank,
    death: (a,b) => deathYear(a)-deathYear(b) || a.rank-b.rank,
    museums: (a,b) => museumCount(b)-museumCount(a) || a.rank-b.rank,
    sigcount: (a,b) => evoCount(b)-evoCount(a) || a.rank-b.rank,
  };
  rows.sort(sorters[prefs.sort] || sorters.cat);

  $('#count').textContent = `· ${rows.length} of ${DATA.length}`;
  $('#empty').hidden = rows.length>0;
  if (rows.length===0){
    const q=(prefs.q||'').trim();
    $('#empty').innerHTML = q
      ? `No match for “${esc(q)}”.<br><span style="font-size:13px">Celebrity Signatures is an archive of <b>history's</b> hands — figures from antiquity through the early 20th century, every signature traced to a real archive. Searching for someone living? They're not here — yet.</span>`
      : 'No signatures match these filters.';
  }
  // Minimal card: signature + name only. Everything else lives in the
  // details popup (chip), which also loads the artist's works from the Met.
  BY_QID = {}; rows.forEach(r => { BY_QID[(r.wikidata||'').split('/').pop()] = r; });
  $('#grid').innerHTML = rows.map(r => {
    const qid = (r.wikidata||'').split('/').pop();
    const evoN = (EVO[qid]?.sigs?.length) || 0;
    return `<div class="card" data-qid="${qid}">
      <div class="sig" role="button" tabindex="0" aria-label="Open details for ${esc(r.full_name)}"><span class="signature-preview" data-signature-src="${esc(r.signature_image_url)}" data-signature-label="Signature of ${esc(r.full_name)}" data-signature-tint-key="${esc(qidOf(r)||r.full_name)}"></span></div>
      <div class="meta">
        <div class="name" title="${esc(r.full_name)}">${r.full_name}</div>
        <a class="info-chip" data-qid="${qid}" href="/a/${qid}">${PORTRAITS[qid]?`<img class="chip-face" loading="lazy" referrerpolicy="no-referrer" src="${esc(PORTRAITS[qid])}" alt="" onerror="this.remove()">`:''}Details${evoN>1?` · ${evoN} signatures`:''}</a>
      </div><div class="shop-actions">${signatureShopLinks(r,true)}</div></div>`;
  }).join('');
}

function buildChips() {
  const cats = ['All','Oldest Signatures','Declaration of Independence','Politics','Artists','Authors'];
  const counts = {}; DATA.forEach(r => { counts[r.category]=(counts[r.category]||0)+1; });
  counts['All'] = DATA.length;
  $('#catChips').innerHTML = cats.map(c => `<span class="chip${c===prefs.cat?' on':''}" data-c="${c}">${c}<span class="chip-count">${counts[c]||0}</span></span>`).join('');
  $('#catChips').onclick = (e) => { const chip=e.target.closest('.chip'); const c=chip&&chip.dataset.c; if(!c) return; prefs.cat=c; save(); writeURL(true);
    document.querySelectorAll('.chip').forEach(x=>x.classList.toggle('on', x.dataset.c===c)); render(); };
}
$('#density').oninput = (e) => { prefs.density=+e.target.value; save(); render(); };
// User-adjustable signature size: grid tiles (--sig-h) and the popup preview
// (--ap-sig-h). Both persist in prefs; a null value keeps the responsive default.
function applySigSizes() {
  const root=document.documentElement.style;
  if(prefs.gridSigH){ root.setProperty('--sig-h', prefs.gridSigH+'px'); } else { root.removeProperty('--sig-h'); }
  if(prefs.apSigH){ root.setProperty('--ap-sig-h', prefs.apSigH+'px'); } else { root.removeProperty('--ap-sig-h'); }
  const s=$('#sigSize'); if(s) s.value=prefs.gridSigH||88;
}
$('#sigSize').oninput = (e) => { prefs.gridSigH=+e.target.value; save(); document.documentElement.style.setProperty('--sig-h', prefs.gridSigH+'px'); };
const AP_SIG_DEFAULT = () => matchMedia('(max-width:700px)').matches?185:230;
function apSizeStep(dir){
  if(dir===0){ prefs.apSigH=null; document.documentElement.style.removeProperty('--ap-sig-h'); }
  else { const base=prefs.apSigH||AP_SIG_DEFAULT(); const v=Math.min(520,Math.max(120,base+dir*30)); prefs.apSigH=v; document.documentElement.style.setProperty('--ap-sig-h', v+'px'); }
  save(); updateApSizeLabel();
}
function updateApSizeLabel(){ const el=$('#apSizeVal'); if(el) el.textContent=(prefs.apSigH||AP_SIG_DEFAULT())+'px'; }
applySigSizes();
// ── left drawer (Amazon-style browse panel) ──
function openDrawer(){ $('#drawer').classList.add('open'); $('#dback').hidden=false; $('#burger').setAttribute('aria-expanded','true'); }
function closeDrawer(){ $('#drawer').classList.remove('open'); $('#dback').hidden=true; $('#burger').setAttribute('aria-expanded','false'); }
$('#burger').onclick = openDrawer;
$('#filtersHint').onclick = openDrawer;
$('#drawerX').onclick = closeDrawer;
$('#dback').onclick = closeDrawer;
document.addEventListener('keydown', e => { if(e.key==='Escape') closeDrawer(); });
// ── 6 switchable shopping layouts (signature-only; info on hover) ──
const LAYOUTS = [
  ['gallery','Gallery'], ['float','Float'], ['plaque','Plaque'],
  ['ledger','Ledger'], ['vitrine','Vitrine'], ['wall','Wall'],
];
function applyLayout(){
  document.body.dataset.layout = prefs.layout || 'gallery';
  document.querySelectorAll('#layoutChips .chip').forEach(x=>x.classList.toggle('on', x.dataset.l===(prefs.layout||'gallery')));
}
$('#layoutChips').innerHTML = LAYOUTS.map(([k,label]) => `<span class="chip" data-l="${k}">${label}</span>`).join('');
$('#layoutChips').onclick = (e) => { const c=e.target.closest('.chip'); if(!c) return; prefs.layout=c.dataset.l; save(); applyLayout(); };
applyLayout();
// name search — debounced, transient (not persisted)
let qTimer=null;
$('#q').oninput = (e) => { clearTimeout(qTimer); qTimer=setTimeout(()=>{ prefs.q=e.target.value; render(); },150); };
$('#useFilter').onchange = (e) => { prefs.use=e.target.value; save(); writeURL(true); render(); };
// a card data point (category / usability) is an href-drill atom → filter in place + reflect into the URL
document.addEventListener('click', (e) => { const d=e.target.closest('a.drill[data-filter]'); if(!d) return; if(e.metaKey||e.ctrlKey||e.shiftKey||e.button===1) return; e.preventDefault(); e.stopPropagation();
  closeArtist(false); // a drill inside the popup filters the grid behind it
  const dim=d.dataset.filter, v=d.dataset.v, dflt=dim==='cat'?'All':'all';
  prefs[dim] = (prefs[dim]===v?dflt:v); save(); writeURL(true);
  document.querySelectorAll('.chip').forEach(x=>x.classList.toggle('on', x.dataset.c===prefs.cat)); if($('#useFilter')) $('#useFilter').value=prefs.use; render(); });
addEventListener('popstate', () => { const u=new URLSearchParams(location.search); prefs.cat=u.get('cat')||'All'; prefs.use=u.get('use')||'all';
  document.querySelectorAll('.chip').forEach(x=>x.classList.toggle('on', x.dataset.c===prefs.cat)); if($('#useFilter')) $('#useFilter').value=prefs.use; render(); });
$('#sort').onchange = (e) => { prefs.sort=e.target.value; save(); render(); };

const slug = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-|-$/g,'');
let collagesBuilt = false;
function buildCollages() {
  if (collagesBuilt) return; collagesBuilt = true;
  const order = ['Oldest Signatures','Declaration of Independence','Politics','Artists','Authors'];
  const usable = {}; DATA.forEach(r => { if(r.usable_in_commercial_collage==='yes') usable[r.category]=(usable[r.category]||0)+1; });
  const cats = [...new Set([...order, ...Object.keys(usable)])].filter(c => order.includes(c) || usable[c]);
  $('#collagesView').innerHTML = cats.map(c => {
    const n = usable[c]||0; const src = `/output/collage-${slug(c)}.png`;
    if (!n) return `<div class="collage"><div class="ch"><b>${c}</b><span class="c">0 collage-usable signatures</span></div>
      <div class="note">No collage — every entry in this category is a living celebrity (flagged <em>permission-needed</em> for right of publicity).</div></div>`;
    return `<div class="collage">
      <div class="ch"><b>${c}</b><span class="c">${n} collage-usable signatures · public domain / openly licensed</span>
        <a href="${src}" download>↓ download PNG</a></div>
      <img loading="lazy" src="${src}" alt="${c} signatures collage"
        onerror="this.closest('.collage').querySelector('.ch .c').textContent='collage not built yet — run scripts/build-collages.py'; this.remove();"></div>`;
  }).join('');
}
function setView(v) {
  prefs.view = v; save();
  document.querySelectorAll('.tab').forEach(t => t.classList.toggle('on', t.dataset.v===v));
  $('#controls').style.display = v==='grid' ? '' : 'none';
  $('#gridView').hidden = v!=='grid';
  $('#collagesView').hidden = v!=='collages';
  $('#count').style.display = v==='grid' ? '' : 'none';
  if (v==='collages') buildCollages();
}
document.querySelector('.tabs').onclick = (e) => { const v=e.target.dataset.v; if(v) setView(v); };

// ── artist details popup: all metadata + signature evolution + works from the
// Met Open Access API (free, PD images, CORS-open) loaded on demand ──
let EVO = {}, BY_QID = {}, WORKS_CACHE = {}, PORTRAITS = {};
let AP_ACTIVE_QID=null, AP_SELECTION={}, AP_REQUEST=0;
document.addEventListener('signaturepreviewchange',event=>{
  if(event.target.id!=='apSignature')return;
  const mode=event.target.dataset.phase;
  $('#apPreviewStatus').textContent=mode==='clean'?'Signature ink on white':mode==='fallback'?'High-contrast source preview · automatic cleanup unavailable':'Source preview unavailable. Try again or open the original source.';
});
function signatureVariants(qid) {
  const r=DATA.find(x=>qidOf(x)===qid); if(!r)return [];
  return [{url:r.signature_image_url,file:'Main signature',license:r.image_license,primary:true,sourceURL:r.backup_source},
    ...(EVO[qid]?.sigs||[]).map(s=>({...s,sourceURL:s.url}))];
}
function evoStripHTML(qid){
  const variants=signatureVariants(qid);
  const person=EVO[qid]?.name||DATA.find(r=>qidOf(r)===qid)?.full_name||qid;
  return `<div class="ap-sec">Choose a signature (${variants.length})</div><p class="ap-picker-help">Select any version to see it above. Archival pages remain complete; original sources are preserved.</p><div class="evo-strip" aria-label="Signature versions">${variants.map((s,i)=>
    `<button class="evo-cell" type="button" data-variant="${i}" aria-pressed="false" aria-controls="apSignature" aria-label="Select signature ${i+1}: ${esc(s.file)}" title="${esc(s.file)}"><span class="signature-preview" data-signature-src="${esc(s.url)}" data-signature-label="${esc(s.file)}" data-signature-tint-key="${esc(qid||person)}"></span><span class="y">${s.primary?'Main signature':`Version ${i}${s.year?' · file label '+esc(s.year):' · undated'}`}</span></button>`).join('')}</div>`;
}
async function selectSignature(index,{scroll=true,retry=false}={}) {
  const qid=AP_ACTIVE_QID,variants=signatureVariants(qid),s=variants[index];if(!s)return;
  const request=++AP_REQUEST;AP_SELECTION[qid]=s.url;
  const target=$('#apSignature');if(!target)return;
  target.dataset.variant=String(index);
  document.querySelectorAll('.evo-cell').forEach(b=>b.setAttribute('aria-pressed',String(+b.dataset.variant===index)));
  $('#apSelectedCaption').textContent=`${s.primary?'Main signature':`Version ${index}`} · ${index+1} of ${variants.length}`;
  $('#apSelectedLicense').textContent=s.license||'License not recorded for this source';
  $('#apCatalogUse').hidden=!s.primary;
  const source=$('#apSourceLink');
  try {const u=new URL(s.sourceURL||s.url,location.href);source.href=['http:','https:'].includes(u.protocol)?u.href:'#';} catch {source.removeAttribute('href');}
  source.title=s.file;
  $('#apPreviewStatus').textContent='Loading selected signature…';
  if(scroll)document.querySelector('.ap-card').scrollTo({top:0,behavior:'instant'});
  const renderer=await signaturePreview;
  if(request!==AP_REQUEST || qid!==AP_ACTIVE_QID)return;
  const person=EVO[qid]?.name||DATA.find(r=>qidOf(r)===qid)?.full_name||qid;
  const mode=await renderer.renderInto(target,s.url,{priority:true,retry,label:`${person} — ${s.file}`,tintKey:qid||person});
  if(request!==AP_REQUEST || qid!==AP_ACTIVE_QID)return;
  $('#apPreviewStatus').textContent=mode==='clean'?'Signature ink on white':mode==='fallback'?'High-contrast source preview · automatic cleanup unavailable':'Source preview unavailable. Try again or open the original source.';
  const thumb=document.querySelector(`.evo-cell[data-variant="${index}"] .signature-preview`);
  if(mode==='clean'&&thumb?.dataset.phase==='error')renderer.renderInto(thumb,s.url,{label:s.file,tintKey:qid||person});
}
function refreshSignaturePicker() {
  if(!AP_ACTIVE_QID||$('#artistPop').hidden)return;
  $('#apVariants').innerHTML=evoStripHTML(AP_ACTIVE_QID);
  const variants=signatureVariants(AP_ACTIVE_QID),index=variants.findIndex(s=>s.url===AP_SELECTION[AP_ACTIVE_QID]);
  selectSignature(Math.max(0,index),{scroll:false});
}
// Works loader — free open-museum APIs, tried in order until we have 6:
// The Met (Open Access) → Art Institute of Chicago (PD only) → Cleveland
// Museum of Art. All keyless + CORS-open; each tile links to the museum page.
async function loadWorks(qid, name){
  if (WORKS_CACHE[qid]) return WORKS_CACHE[qid];
  const works = [];
  const last = name.toLowerCase().split(' ').pop();
  const MAX = 6;
  // 1) The Met
  try {
    const MET='https://collectionapi.metmuseum.org/public/collection/v1';
    const s = await (await fetch(`${MET}/search?hasImages=true&artistOrCulture=true&q=${encodeURIComponent(name)}`)).json();
    for (const id of (s.objectIDs||[]).slice(0,14)) {
      if (works.length >= MAX) break;
      try {
        const o = await (await fetch(`${MET}/objects/${id}`)).json();
        if (!o.primaryImageSmall) continue;
        if (!(o.artistDisplayName||'').toLowerCase().includes(last)) continue;
        works.push({ img:o.primaryImageSmall, title:o.title||'Untitled', date:o.objectDate||'', url:o.objectURL||('https://www.metmuseum.org/art/collection/search/'+id), src:'The Met' });
      } catch {}
    }
  } catch {}
  // 2) Art Institute of Chicago
  if (works.length < MAX) try {
    const j = await (await fetch(`https://api.artic.edu/api/v1/artworks/search?q=${encodeURIComponent(name)}&query[term][is_public_domain]=true&fields=id,title,image_id,artist_title,date_display&limit=12`)).json();
    for (const a of (j.data||[])) {
      if (works.length >= MAX) break;
      if (!a.image_id || !(a.artist_title||'').toLowerCase().includes(last)) continue;
      works.push({ img:`https://www.artic.edu/iiif/2/${a.image_id}/full/400,/0/default.jpg`, title:a.title||'Untitled', date:a.date_display||'', url:`https://www.artic.edu/artworks/${a.id}`, src:'Art Institute of Chicago' });
    }
  } catch {}
  // 3) Cleveland Museum of Art
  if (works.length < MAX) try {
    const j = await (await fetch(`https://openaccess-api.clevelandart.org/api/artworks/?artists=${encodeURIComponent(name)}&has_image=1&limit=10`)).json();
    for (const a of (j.data||[])) {
      if (works.length >= MAX) break;
      const img = a.images?.web?.url; if (!img) continue;
      if (!(a.creators||[]).some(c=>(c.description||'').toLowerCase().includes(last))) continue;
      works.push({ img, title:a.title||'Untitled', date:a.creation_date||'', url:a.url||'https://www.clevelandart.org/art/collection/search', src:'Cleveland Museum of Art' });
    }
  } catch {}
  // 4) Wikidata works-by-QID (P170 creator + P18 image) — QID-exact, keyless,
  //    CORS-open WDQS; surfaces Rijksmuseum/Uffizi/Prado CC0 images via Commons
  //    (the old rijksmuseum.nl API is retired — 410 — so Commons carries them).
  if (works.length < MAX) try {
    const q = `SELECT ?w ?wLabel ?img ?date WHERE { ?w wdt:P170 wd:${qid} ; wdt:P18 ?img . OPTIONAL { ?w wdt:P571 ?date } SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } } LIMIT ${MAX*2}`;
    const j = await (await fetch('https://query.wikidata.org/sparql?format=json&query='+encodeURIComponent(q), { headers:{ Accept:'application/sparql-results+json' } })).json();
    for (const b of (j.results?.bindings||[])) {
      if (works.length >= MAX) break;
      const m = (b.img?.value||'').match(/Special:FilePath\/(.+)$/); if (!m) continue;
      works.push({ img:`https://commons.wikimedia.org/wiki/Special:FilePath/${m[1]}?width=400`,
        title:b.wLabel?.value||'Untitled', date:(b.date?.value||'').slice(0,4),
        url:b.w?.value||'#', src:'Wikimedia Commons' });
    }
  } catch {}
  WORKS_CACHE[qid]=works; return works;
}
// Books loader — Open Library (keyless, CORS-open) for the non-artist hands
// (authors, presidents, founders): cover tiles that link to the book page.
const BOOKS_CACHE = {};
async function loadBooks(qid, name){
  if (BOOKS_CACHE[qid]) return BOOKS_CACHE[qid];
  const books = [];
  try {
    const j = await (await fetch(`https://openlibrary.org/search.json?author=${encodeURIComponent(name)}&sort=editions&limit=10&fields=title,cover_i,first_publish_year,key`)).json();
    for (const d of (j.docs||[])) {
      if (books.length >= 6) break;
      if (!d.cover_i) continue;
      books.push({ img:`https://covers.openlibrary.org/b/id/${d.cover_i}-M.jpg`, title:d.title||'Untitled',
        date:d.first_publish_year?String(d.first_publish_year):'', url:d.key?`https://openlibrary.org${d.key}`:'https://openlibrary.org', src:'Open Library' });
    }
  } catch {}
  BOOKS_CACHE[qid]=books; return books;
}
function openArtist(qid, push=true){
  const r = BY_QID[qid] || DATA.find(x=>(x.wikidata||'').endsWith('/'+qid));
  if (!r) return;
  AP_ACTIVE_QID=qid;
  const ub=r.usable_in_commercial_collage;
  const uClass=ub==='yes'?'use-yes':ub==='permission-needed'?'use-perm':'use-review';
  const uLabel=ub==='yes'?'Free to use':ub==='permission-needed'?'Permission needed':'Verify first';
  const isArtist = r.category==='Artists';
  const portrait = PORTRAITS[qid];
  $('#apBody').innerHTML = `<div class="ap-inner">
    <div class="ap-sig">${portrait?`<img class="ap-portrait" referrerpolicy="no-referrer" src="${esc(portrait)}" alt="Portrait of ${esc(r.full_name)}" onerror="this.remove()">`:''}<div class="signature-preview ap-sig-main" id="apSignature"></div></div>
    <div class="ap-sig-size" role="group" aria-label="Signature size"><button type="button" class="ap-sz" data-sz="-1" aria-label="Decrease signature size">−</button><span class="ap-sz-val" id="apSizeVal" aria-live="polite"></span><button type="button" class="ap-sz" data-sz="1" aria-label="Increase signature size">+</button><button type="button" class="ap-sz" data-sz="0" aria-label="Reset signature size">Reset</button></div>
    <div class="ap-selected-caption" id="apSelectedCaption"></div><div class="ap-preview-status" id="apPreviewStatus" role="status" aria-live="polite"></div>
    <div class="ap-name">${esc(r.full_name)}</div>
    <div class="ap-shop">${signatureShopLinks(r)}</div>
    <div class="ap-life">${r.deceased==='yes'?'† '+esc(r.death_date||''):'● living'} · ${esc((r.reason_for_ranking||'').replace('Cross-wiki notability: ',''))}</div>
    <div class="ap-rowline"><span class="tag">${drill('cat',r.category)}</span><span id="apCatalogUse" class="badge ${uClass}">${drill('use',ub,uLabel)}</span></div>
    ${Array.isArray(r.museums)&&r.museums.length?`<div class="ap-sec">In the collections of</div><div class="ap-sub">${esc(r.museums.join(' · '))}</div>`:''}
    <div class="ap-sec">License & sources</div>
    <div class="ap-sub"><span id="apSelectedLicense"></span> · <a href="${esc(r.wikidata)}" target="_blank" rel="noopener noreferrer">Wikidata</a> · <a id="apSourceLink" target="_blank" rel="noopener noreferrer">Open original source</a></div>
    <div id="apVariants"></div>
    ${isArtist
      ? `<div class="ap-sec">Works — open museum collections</div><div class="works" id="apWorks"><span class="ap-sub">loading works…</span></div>`
      : `${portrait ? `<div class="ap-sec">Portrait</div><div class="ap-figure"><img referrerpolicy="no-referrer" src="${esc(portrait.replace('width=120','width=520'))}" alt="Portrait of ${esc(r.full_name)}" onerror="this.closest('.ap-figure').remove()"><div class="ap-sub">Public-domain / CC portrait via Wikimedia Commons</div></div>` : ''}<div class="ap-sec">Books</div><div class="works" id="apWorks"><span class="ap-sub">loading books…</span></div>`}
  </div>
  <div class="ap-cta">${signatureShopLinks(r)}</div>`;
  const pop=$('#artistPop');
  pop.hidden = false;
  document.querySelector('.ap-card').scrollTop=0;
  updateApSizeLabel();
  refreshSignaturePicker();
  pop.setAttribute('role','dialog'); pop.setAttribute('aria-modal','true');
  pop.setAttribute('aria-label', r.full_name + ' — signature details');
  POP_RETURN_FOCUS = document.activeElement;
  document.querySelector('.ap-x').focus();
  document.body.style.overflow = 'hidden';
  if (push) { const u=new URLSearchParams(location.search); u.set('artist',qid); history.pushState({},'', '?'+u.toString()); }
  const renderTiles = (ws, emptyHtml) => {
    const el=$('#apWorks'); if(!el||AP_ACTIVE_QID!==qid) return;
    el.innerHTML = ws.length ? ws.map(w=>`<a href="${esc(w.url)}" target="_blank" rel="noopener noreferrer"><img loading="lazy" referrerpolicy="no-referrer" src="${esc(w.img)}" alt="${esc(w.title)}" onerror="this.closest('a').remove()"><span class="wt">${esc(w.title)}${w.date?' · '+esc(w.date):''}${w.src?' · '+esc(w.src):''}</span></a>`).join('')
      : emptyHtml;
  };
  if (isArtist) loadWorks(qid, r.full_name).then(ws=>renderTiles(ws,
    `<span class="ap-sub">no openly-licensed works found in the open museum APIs — <a href="${esc(r.wikidata)}" target="_blank" rel="noopener noreferrer">see Wikidata</a></span>`));
  else loadBooks(qid, r.full_name).then(bs=>renderTiles(bs,
    `<span class="ap-sub">no books found on Open Library — <a href="${esc(r.wikidata)}" target="_blank" rel="noopener noreferrer">see Wikidata</a></span>`));
}
let POP_RETURN_FOCUS = null;
function closeArtist(push=true){
  if ($('#artistPop').hidden) return;
  $('#artistPop').hidden = true;
  AP_ACTIVE_QID=null;AP_REQUEST++;
  document.body.style.overflow = '';
  if (POP_RETURN_FOCUS && POP_RETURN_FOCUS.focus) { POP_RETURN_FOCUS.focus(); POP_RETURN_FOCUS = null; }
  if (push) { const u=new URLSearchParams(location.search); u.delete('artist'); const q=u.toString(); history.pushState({},'', q?('?'+q):location.pathname); }
}
document.addEventListener('click', (e)=>{
  const sz=e.target.closest('.ap-sz'); if(sz){ apSizeStep(+sz.dataset.sz); return; }
  const variant=e.target.closest('.evo-cell');if(variant){selectSignature(+variant.dataset.variant,{retry:variant.querySelector('[data-phase=error]')!==null});return;}
  const c=e.target.closest('a.info-chip'); if(c){ e.preventDefault(); openArtist(c.dataset.qid); return; }
  // clicking the signature itself opens the same details popup (artwork/books)
  const s=e.target.closest('.card .sig'); if(s){ const card=s.closest('.card'); if(card&&card.dataset.qid){ openArtist(card.dataset.qid); return; } }
  if (e.target.closest('.ap-x')||e.target.classList.contains('ap-back')) closeArtist();
});
document.addEventListener('keydown', (e)=>{
  if(e.key==='Escape') closeArtist();
  if((e.key==='Enter'||e.key===' ') && e.target.classList?.contains('sig')){ const card=e.target.closest('.card'); if(card&&card.dataset.qid){ e.preventDefault(); openArtist(card.dataset.qid); } }
});
function maybeOpenFromURL(){ const q=new URLSearchParams(location.search).get('artist'); if(q&&DATA.length) openArtist(q,false); else if(!q) closeArtist(false); }

fetch('/api/signatures').then(r=>r.json()).then(d => { DATA=d; readURL(); buildChips(); render(); setView(prefs.view); maybeOpenFromURL(); });
fetch('/api/signature-evolution').then(r=>r.ok?r.json():{}).then(e=>{ EVO=e||{}; if(DATA.length) render(); refreshSignaturePicker(); }).catch(()=>{});
fetch('/api/portraits').then(r=>r.ok?r.json():{}).then(p=>{ PORTRAITS=p||{}; if(DATA.length) render(); }).catch(()=>{});
addEventListener('popstate', maybeOpenFromURL);
</script>
</body>
</html>