<script>
  import { onMount } from 'svelte';
  import { getJSON } from '$lib/api.js';

  // Virtual frames the viewer can switch between — remembered locally, no account needed.
  const FRAMES = [
    { id: 'walnut', label: 'Walnut' },
    { id: 'oak', label: 'Oak' },
    { id: 'mahogany', label: 'Mahogany' },
    { id: 'gold', label: 'Gold' },
    { id: 'silver', label: 'Silver' },
    { id: 'black', label: 'Black' },
    { id: 'none', label: 'No frame' },
  ];

  let art = $state(null);
  let state = $state('loading');   // loading | ready | empty
  let zoom = $state(false);
  let frame = $state('walnut');
  let thickness = $state(1);       // frame-scale multiplier, 0.7–1.9 (mat caps at 1.5)
  let lightboxEl = $state(null);

  function onKey(e) {
    if (e.key === 'Escape' && zoom) zoom = false;
  }
  // Move focus to the lightbox when it opens, so Escape/Enter work and focus is trapped sanely.
  $effect(() => { if (zoom && lightboxEl) lightboxEl.focus(); });

  let who = $derived(
    art ? [art.artist || 'Unknown artist', art.date_text].filter(Boolean) : []
  );
  // Woods are built from four real mitered rails (grain turns at the corners); metals/none aren't.
  let isWood = $derived(['walnut', 'oak', 'mahogany'].includes(frame));

  onMount(async () => {
    try {
      const saved = localStorage.getItem('ub_art_frame');
      if (saved && FRAMES.some((f) => f.id === saved)) frame = saved;
      const t = parseFloat(localStorage.getItem('ub_art_thickness'));
      if (t >= 0.7 && t <= 1.9) thickness = t;
    } catch { /* private mode — defaults are fine */ }
    try {
      art = await getJSON('/api/art/today');
      state = art?.image_url ? 'ready' : 'empty';
    } catch {
      state = 'empty';
    }
  });

  function setFrame(id) {
    frame = id;
    try { localStorage.setItem('ub_art_frame', id); } catch { /* ignore */ }
  }

  function saveThickness() {
    try { localStorage.setItem('ub_art_thickness', String(thickness)); } catch { /* ignore */ }
  }
</script>

<svelte:window onkeydown={onKey} />

<svelte:head>
  <title>Daily Art · upbeatBytes</title>
  <meta name="description" content="A masterwork a day from the world's open museum collections — one piece, beautifully framed, on upbeatBytes." />
</svelte:head>

{#snippet woodRails()}
  <span class="rail rail--t"></span>
  <span class="rail rail--r"></span>
  <span class="rail rail--b"></span>
  <span class="rail rail--l"></span>
{/snippet}

<div class="room">
  <header class="bar">
    <a class="brand" href="/" aria-label="upbeatBytes home">
      <img src="/logo.svg" alt="upbeatBytes" width="586" height="196" />
    </a>
    <nav class="nav">
      <a href="/">News</a>
      <a href="/play">Games</a>
      <a href="/art" aria-current="page">Art</a>
      <a class="acct" href="/account" aria-label="Your account">
        <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor"
             stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
          <circle cx="12" cy="8" r="3.3" />
          <path d="M5.5 19.2a6.5 6.5 0 0 1 13 0" />
        </svg>
      </a>
    </nav>
  </header>

  <main class="gallery">
    <div class="intro">
      <h1>Daily Art</h1>
      <p>A masterwork a day, from the world's open collections.</p>
    </div>
    <div class="divider" aria-hidden="true"></div>

    {#if state === 'ready'}
      <figure class="piece">
        <button class="frame frame--{frame}" style="--frame-scale:{thickness}"
                onclick={() => (zoom = true)} aria-label="Expand artwork">
          {#if isWood}{@render woodRails()}{/if}
          <span class="mat">
            <img src={art.image_url} alt={art.title} />
            <span class="hint">
              <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor"
                   stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                <path d="M9 4H4v5M15 4h5v5M9 20H4v-5M15 20h5v-5" />
              </svg>
              Click to expand
            </span>
          </span>
        </button>

        <figcaption class="placard">
          <h2 class="title">{art.title}</h2>
          <p class="who">
            {#each who as part, i}{#if i > 0}<span class="sep">·</span>{/if}{part}{/each}
          </p>
          {#if art.medium}<p class="medium">{art.medium}</p>{/if}
          <p class="credit">
            from {art.museum}{#if art.license}<span class="sep">·</span>{art.license}{/if}
            {#if art.source_url}<a class="more" href={art.source_url} target="_blank" rel="noopener">View at {art.museum} →</a>{/if}
          </p>

          <div class="frames">
            <span class="frames-label">Frame</span>
            {#each FRAMES as f}
              <button class="swatch swatch--{f.id}" class:on={frame === f.id}
                      onclick={() => setFrame(f.id)} aria-pressed={frame === f.id} title={f.label}>
                <span class="sr">{f.label}</span>
              </button>
            {/each}
          </div>
          {#if frame !== 'none'}
            <div class="thickness">
              <span class="frames-label">Thickness</span>
              <input type="range" min="0.7" max="1.9" step="0.05"
                     bind:value={thickness} oninput={saveThickness} aria-label="Frame thickness" />
            </div>
          {/if}
        </figcaption>
      </figure>
    {:else if state === 'empty'}
      <p class="note">The gallery's resting — a new piece is hung each morning. Check back soon.</p>
    {:else}
      <p class="note">Hanging today's piece…</p>
    {/if}
  </main>

  <footer class="foot">upbeatBytes — no ads, no paywalls, no doomscrolling.</footer>
</div>

{#if zoom && art}
  <button class="lightbox" bind:this={lightboxEl} onclick={() => (zoom = false)} aria-label="Close artwork">
    <span class="frame frame--{frame} lb-frame" style="--frame-scale:{thickness}">
      {#if isWood}{@render woodRails()}{/if}
      <span class="mat"><img src={art.image_url_large || art.image_url} alt={art.title} /></span>
    </span>
    <span class="lb-cap">{art.title}{#if art.artist}<span class="sep">·</span>{art.artist}{/if}</span>
  </button>
{/if}

<style>
  /* --- look-overhaul testbed: bright, modern, calm. Scoped to /art for now. --- */
  .room {
    --canvas: #faf6ee;        /* warm cream, not brown */
    --surface: #ffffff;
    --ink: #232a31;           /* dark slate */
    --muted: #707b86;
    --line: #ece5d8;
    --accent: #0a93c0;        /* upbeatBytes blue, a touch brighter */
    --accent-deep: #066c8e;
    min-height: 100vh;
    background: var(--canvas);
    color: var(--ink);
    font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    display: flex;
    flex-direction: column;
  }

  .bar {
    display: flex; align-items: center; justify-content: space-between;
    padding: 14px clamp(16px, 5vw, 56px);
    max-width: 1100px; width: 100%; margin: 0 auto; box-sizing: border-box;
  }
  .brand { display: block; line-height: 0; }
  .brand img { height: 34px; width: auto; display: block; }
  .nav { display: flex; align-items: center; gap: clamp(12px, 2.5vw, 26px); }
  .nav a { color: var(--muted); text-decoration: none; font-weight: 600; font-size: 0.95rem; }
  .nav a:hover { color: var(--ink); }
  .nav a[aria-current="page"] { color: var(--accent); }
  .acct {
    display: inline-flex; align-items: center; justify-content: center;
    width: 38px; height: 38px; border-radius: 50%; color: var(--muted);
  }
  .acct:hover { color: var(--accent); background: #eef6f9; }

  .gallery {
    flex: 1; width: 100%; max-width: 1100px; margin: 0 auto;
    padding: clamp(6px, 1.5vw, 16px) clamp(20px, 5vw, 56px) clamp(20px, 5vw, 56px);
    box-sizing: border-box;
    display: flex; flex-direction: column; align-items: center;
  }
  .intro { text-align: center; margin-bottom: clamp(18px, 3.5vw, 30px); }
  .intro h1 {
    font-family: Georgia, "Iowan Old Style", "Times New Roman", serif;
    font-size: clamp(2rem, 5vw, 3rem); margin: 0; letter-spacing: -0.01em;
  }
  .intro p { color: var(--muted); margin: 10px 0 0; font-size: 1.05rem; }

  /* A quiet hairline between the title and the artwork, fading at both ends. */
  .divider {
    width: min(440px, 76%); height: 1px; margin: 0 auto clamp(24px, 4.5vw, 44px);
    background: linear-gradient(90deg, transparent, rgba(112, 123, 134, 0.30) 22%,
      rgba(112, 123, 134, 0.30) 78%, transparent);
  }

  .piece { margin: 0; display: flex; flex-direction: column; align-items: center; max-width: 860px; }

  /* The frame: a beveled moulding (wood/metal) around a cream mat around the art.
     --rail / --mat are the moulding and mat widths; both scale with --frame-scale (the
     thickness slider). EVERY variant — even "No frame" — reserves the same footprint, so
     switching frames never reflows the page. */
  .frame {
    box-sizing: border-box;
    /* Rail scales across the whole slider; the mat caps at scale 1.5 so the top of the
       slider thickens only the moulding (wood), holding the white border steady. */
    --rail: calc(clamp(11px, 2.3vw, 22px) * var(--frame-scale, 1));
    --mat: calc(clamp(10px, 2.4vw, 22px) * min(var(--frame-scale, 1), 1.5));
    border: none; cursor: zoom-in; background: none;
    position: relative; line-height: 0; display: inline-block; max-width: 100%;
    border-radius: 4px; padding: var(--rail);
    box-shadow: 0 24px 58px rgba(20, 30, 45, 0.20), 0 3px 10px rgba(20, 30, 45, 0.10);
  }
  /* The three woods share one real (CC0, Poly Haven) fine-grain texture, recolored per
     species with a CSS filter, with a soft bevel baked into the texture layer. Metals
     stay gradient. The texture sits on ::before (behind the mat); miters on ::after. */
  .frame--walnut, .frame--oak, .frame--mahogany {
    background: linear-gradient(135deg, #5a3a24, #34210f);   /* fallback if texture misses */
  }

  /* Four real mitered rails: the grain TURNS at every corner — horizontal moulding on the
     top/bottom rails (rotated texture), vertical on the sides — like four cut lengths of
     wood. Each rail is a trapezoid clipped to 45° at the joints; --rail drives size + cut.
     A directional bevel per rail (lit top-left) gives the moulding a rounded profile. */
  .rail {
    position: absolute; z-index: 0; pointer-events: none;
    background-position: center; background-size: cover; background-repeat: no-repeat;
  }
  .rail--t { top: 0; left: 0; right: 0; height: var(--rail);
    clip-path: polygon(0 0, 100% 0, calc(100% - var(--rail)) 100%, var(--rail) 100%);
    background-image: linear-gradient(to bottom, rgba(255,255,255,0.22), rgba(0,0,0,0.22)), url("/textures/wood-grain-h.jpg"); }
  .rail--b { bottom: 0; left: 0; right: 0; height: var(--rail);
    clip-path: polygon(var(--rail) 0, calc(100% - var(--rail)) 0, 100% 100%, 0 100%);
    background-image: linear-gradient(to top, rgba(255,255,255,0.05), rgba(0,0,0,0.36)), url("/textures/wood-grain-h.jpg"); }
  .rail--l { top: 0; bottom: 0; left: 0; width: var(--rail);
    clip-path: polygon(0 0, 100% var(--rail), 100% calc(100% - var(--rail)), 0 100%);
    background-image: linear-gradient(to right, rgba(255,255,255,0.20), rgba(0,0,0,0.22)), url("/textures/wood-grain.jpg"); }
  .rail--r { top: 0; bottom: 0; right: 0; width: var(--rail);
    clip-path: polygon(0 var(--rail), 100% 0, 100% 100%, 0 calc(100% - var(--rail)));
    background-image: linear-gradient(to left, rgba(255,255,255,0.05), rgba(0,0,0,0.36)), url("/textures/wood-grain.jpg"); }
  /* per-species recolor, now applied to the rails */
  .frame--walnut .rail { filter: brightness(1.08) saturate(1.05); }
  .frame--oak .rail { filter: brightness(1.72) contrast(0.92) saturate(1.12) sepia(0.22) hue-rotate(-6deg); }
  .frame--mahogany .rail { filter: brightness(0.98) contrast(1.06) saturate(1.55) sepia(0.4) hue-rotate(-16deg); }

  /* Metals: fine brushed striations over an anisotropic sheen. */
  .frame--gold {
    background:
      repeating-linear-gradient(180deg, rgba(255,255,255,0.08) 0 1px, rgba(120,80,10,0.07) 1px 2px),
      linear-gradient(135deg, #b88c3d, #ecd293 42%, #a9772f 60%, #dcbd71);
    box-shadow: 0 24px 58px rgba(20, 30, 45, 0.20),
      inset 0 0 0 1px rgba(255, 255, 255, 0.30),
      inset 0 2px 3px rgba(255, 249, 226, 0.60),
      inset 0 -3px 9px rgba(92, 62, 12, 0.48);
  }
  .frame--silver {
    background:
      repeating-linear-gradient(180deg, rgba(255,255,255,0.11) 0 1px, rgba(40,50,60,0.07) 1px 2px),
      linear-gradient(135deg, #a9b0ba, #edf0f3 45%, #98a0ab 62%, #d2d7de);
    box-shadow: 0 24px 58px rgba(20, 30, 45, 0.18),
      inset 0 0 0 1px rgba(255, 255, 255, 0.50),
      inset 0 2px 3px rgba(255, 255, 255, 0.75),
      inset 0 -3px 9px rgba(60, 70, 85, 0.42);
  }
  .frame--black {
    background: linear-gradient(135deg, #2a2c30, #15171a 45%, #25282c 60%, #101113);
    box-shadow: 0 24px 58px rgba(20, 30, 45, 0.30),
      inset 0 0 0 1px rgba(255, 255, 255, 0.08),
      inset 0 2px 3px rgba(255, 255, 255, 0.10),
      inset 0 -3px 9px rgba(0, 0, 0, 0.60);
  }

  /* Mitered (45°) corner joints for the woods — each seam runs from the outer corner to
     the mat, sized to the rail width so it tracks the thickness slider. */
  .frame--walnut::after, .frame--oak::after, .frame--mahogany::after {
    content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none; border-radius: inherit;
    background:
      linear-gradient(45deg, transparent calc(50% - 0.9px), rgba(28, 16, 6, 0.7) calc(50% - 0.9px), rgba(28, 16, 6, 0.7) calc(50% + 0.1px), rgba(255, 230, 190, 0.34) calc(50% + 0.1px), rgba(255, 230, 190, 0.34) calc(50% + 1px), transparent calc(50% + 1px)) top left / var(--rail) var(--rail) no-repeat,
      linear-gradient(135deg, transparent calc(50% - 0.9px), rgba(28, 16, 6, 0.7) calc(50% - 0.9px), rgba(28, 16, 6, 0.7) calc(50% + 0.1px), rgba(255, 230, 190, 0.34) calc(50% + 0.1px), rgba(255, 230, 190, 0.34) calc(50% + 1px), transparent calc(50% + 1px)) top right / var(--rail) var(--rail) no-repeat,
      linear-gradient(135deg, transparent calc(50% - 0.9px), rgba(28, 16, 6, 0.7) calc(50% - 0.9px), rgba(28, 16, 6, 0.7) calc(50% + 0.1px), rgba(255, 230, 190, 0.34) calc(50% + 0.1px), rgba(255, 230, 190, 0.34) calc(50% + 1px), transparent calc(50% + 1px)) bottom left / var(--rail) var(--rail) no-repeat,
      linear-gradient(45deg, transparent calc(50% - 0.9px), rgba(28, 16, 6, 0.7) calc(50% - 0.9px), rgba(28, 16, 6, 0.7) calc(50% + 0.1px), rgba(255, 230, 190, 0.34) calc(50% + 0.1px), rgba(255, 230, 190, 0.34) calc(50% + 1px), transparent calc(50% + 1px)) bottom right / var(--rail) var(--rail) no-repeat;
  }

  .mat {
    display: block; position: relative; z-index: 1; background: #fbf8f1; border-radius: 1px;
    padding: var(--mat);
    /* The mat sits recessed below the moulding: a crisp rabbet groove at the lip, the
       frame casting shadow onto the top/left of the mat, the bottom/right catching light
       — a small chiseled step instead of a flat transition. */
    box-shadow:
      inset 0 0 0 1px rgba(40, 28, 16, 0.18),
      inset 0 6px 9px -3px rgba(0, 0, 0, 0.34),
      inset 6px 0 9px -4px rgba(0, 0, 0, 0.24),
      inset 0 -3px 6px -2px rgba(255, 255, 255, 0.55),
      inset -3px 0 6px -2px rgba(255, 255, 255, 0.40);
  }
  .frame img { display: block; width: 100%; height: auto; max-height: 72vh; object-fit: contain; border-radius: 1px; }

  /* No frame: keep the footprint (so switching never shifts the layout), drop the
     moulding + mat, and let the bare art float with a soft drop shadow. */
  .frame--none { background: transparent; box-shadow: none; }
  .frame--none .mat { background: transparent; box-shadow: none; }
  .frame--none img {
    border-radius: 6px;
    box-shadow: 0 18px 44px rgba(20, 30, 45, 0.20), 0 4px 12px rgba(20, 30, 45, 0.10);
  }

  /* A quiet, always-there affordance — no hover wobble. */
  .hint {
    position: absolute; right: 12px; bottom: 12px;
    display: inline-flex; align-items: center; gap: 6px;
    font-family: ui-sans-serif, system-ui, sans-serif;
    font-size: 0.76rem; font-weight: 600; line-height: 1; color: #fff;
    background: rgba(20, 26, 33, 0.50); border-radius: 999px; padding: 7px 11px;
    backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
    pointer-events: none; transition: background 0.2s ease;
  }
  .frame:hover .hint, .frame:focus-visible .hint { background: rgba(20, 26, 33, 0.72); }

  .placard { text-align: center; margin-top: clamp(22px, 4vw, 36px); max-width: 640px; }
  .title {
    font-family: Georgia, "Iowan Old Style", "Times New Roman", serif;
    font-size: clamp(1.4rem, 3.5vw, 2rem); margin: 0; line-height: 1.2;
  }
  .who { margin: 8px 0 0; font-size: 1.05rem; color: var(--ink); }
  .medium { margin: 4px 0 0; color: var(--muted); font-size: 0.95rem; font-style: italic; }
  .credit { margin: 16px 0 0; color: var(--muted); font-size: 0.88rem; }
  .sep { display: inline-block; margin: 0 0.5em; color: var(--muted); }
  .more { display: inline-block; margin-left: 8px; color: var(--accent); font-weight: 600; text-decoration: none; }
  .more:hover { color: var(--accent-deep); }

  .frames { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 11px; margin-top: 24px; }
  .frames-label { font-size: 0.74rem; font-weight: 600; color: var(--muted); margin-right: 4px; text-transform: uppercase; letter-spacing: 0.06em; }
  /* Frame chips read as little beveled beads; selection is a clean offset ring. */
  .swatch {
    width: 30px; height: 30px; border-radius: 50%; border: none; cursor: pointer; padding: 0;
    box-shadow: inset 0 2px 3px rgba(255, 255, 255, 0.38),
      inset 0 -3px 5px rgba(0, 0, 0, 0.30), 0 1px 3px rgba(0, 0, 0, 0.22);
    outline: 2.5px solid transparent; outline-offset: 2px;
    transition: outline-color 0.15s ease, transform 0.15s ease;
  }
  .swatch:hover { transform: translateY(-1px); }
  .swatch.on { outline-color: var(--accent); }
  .swatch--walnut { background: linear-gradient(150deg, #6b4528, #8a5d31 55%, #4f3420); }
  .swatch--oak { background: linear-gradient(150deg, #b07f46, #cb9c5b 55%, #8c5d2f); }
  .swatch--mahogany { background: linear-gradient(150deg, #5a241a, #7e3826 55%, #3a160e); }
  .swatch--gold { background: linear-gradient(150deg, #c79a45, #ecd293 55%, #a9772f); }
  .swatch--silver { background: linear-gradient(150deg, #aab1bb, #edf0f3 55%, #98a0ab); }
  .swatch--black { background: linear-gradient(150deg, #34373c, #16181b 55%, #0c0d0f); }
  .swatch--none { background: linear-gradient(150deg, #ffffff, #ece6da); }
  .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

  .thickness { display: flex; align-items: center; justify-content: center; gap: 12px; margin-top: 16px; }
  .thickness input[type="range"] { width: min(220px, 60vw); accent-color: var(--accent); cursor: pointer; }

  .note { color: var(--muted); font-size: 1.05rem; margin-top: 40px; }

  .foot {
    text-align: center; color: var(--muted); font-size: 0.84rem;
    padding: 28px 16px 36px; border-top: 1px solid var(--line); margin-top: 24px;
  }

  .lightbox {
    position: fixed; inset: 0; z-index: 50; border: none; cursor: zoom-out;
    /* A soft, top-lit gallery wall — lighter than the page so every frame (Black
       included) reads, like a piece hung on a real wall. */
    background: linear-gradient(180deg, #efe9dd, #e2dbcc);
    display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: 12px; padding: 2.5vmin;
  }
  /* The full-screen view wears the same frame. Rail + mat are only a touch larger than the
     page (keeping the ~1:1 wood:white proportion), and the image is capped so the whole
     framed piece — including the bottom rail — always fits on screen. */
  .lb-frame {
    cursor: zoom-out; max-width: 92vw;
    --rail: calc(clamp(18px, 1.9vw, 36px) * var(--frame-scale, 1));
    --mat: calc(clamp(16px, 1.6vw, 30px) * min(var(--frame-scale, 1), 1.5));
  }
  .lb-frame.frame--none { --rail: 0px; --mat: 0px; }   /* bare art fills the screen */
  .lb-frame img { max-width: 86vw; max-height: 66vh; width: auto; height: auto; object-fit: contain; }
  .lb-frame.frame--none img { max-width: 96vw; max-height: 88vh; }   /* no frame → go big */
  .lb-cap { color: #5b636e; font-size: 0.9rem; }
  .lb-cap .sep { color: #a6acb4; }
</style>
