( ′∀`)σ≡σ☆))Д′)レ(゚∀゚;)ヘ=З=З=Зε≡(ノ´_ゝ`)ノ HEX
HEX
Server: Apache/2.4.58 (Ubuntu)
System: Linux mail.thebrand.ai 6.8.0-107-generic #107-Ubuntu SMP PREEMPT_DYNAMIC Fri Mar 13 19:51:50 UTC 2026 x86_64
User: www-data (33)
PHP: 8.3.6
Disabled: NONE
Upload Files
File: /var/www/html/tmpr/..//tmpr/../tmpr/../tmpr/../wowX/polished design.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<title>Atelier — a canvas studio</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=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,400;0,9..144,500;0,9..144,600;0,9..144,700;1,9..144,300;1,9..144,400;1,9..144,600&display=swap" rel="stylesheet">
<link href="https://api.fontshare.com/v2/css?f[]=general-sans@400,500,600,700&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
<style>
  :root {
    --ink: #0E0E14;
    --ink-2: #1A1A23;
    --paper: #F7F5F0;
    --paper-2: #EFECE4;
    --paper-3: #E5E1D6;
    --line: rgba(14, 14, 20, 0.08);
    --line-2: rgba(14, 14, 20, 0.14);

    --accent: #FF5B2E;
    --accent-2: #E8C547;
    --accent-3: #5B8DEF;
    --accent-4: #7AC74F;
    --accent-5: #D946EF;

    --serif: 'Fraunces', Georgia, serif;
    --sans: 'General Sans', -apple-system, BlinkMacSystemFont, sans-serif;

    --bar-h: 60px;
    --safe-top: 40px;
    --safe-bottom: 22px;
    --tap: 44px;
    --sheet-h: 360px;
    --radius-lg: 24px;
    --radius-md: 14px;
    --radius-sm: 10px;

    --shadow-sm: 0 1px 2px rgba(14,14,20,0.06), 0 1px 3px rgba(14,14,20,0.04);
    --shadow-md: 0 4px 12px rgba(14,14,20,0.08), 0 2px 4px rgba(14,14,20,0.04);
    --shadow-lg: 0 12px 40px rgba(14,14,20,0.14), 0 4px 12px rgba(14,14,20,0.06);
    --shadow-xl: 0 24px 60px rgba(14,14,20,0.20), 0 8px 20px rgba(14,14,20,0.08);

    --spring: cubic-bezier(0.34, 1.4, 0.5, 1);
    --ease-out: cubic-bezier(0.2, 0.8, 0.3, 1);
    --ease: cubic-bezier(0.4, 0, 0.2, 1);
    --dur: 260ms;
    --dur-fast: 160ms;

    --z-canvas: 1;
    --z-floating: 50;
    --z-bar: 100;
    --z-scrim: 150;
    --z-sheet: 200;
  }

  * { box-sizing: border-box; -webkit-tap-highlight-color: transparent; margin: 0; padding: 0; }
  html, body {
    font-family: var(--sans);
    background: #0a0a0f;
    color: var(--ink);
    height: 100%;
    overflow: hidden;
    -webkit-font-smoothing: antialiased;
    touch-action: manipulation;
    letter-spacing: -0.003em;
  }
  button { font-family: inherit; border: none; background: none; color: inherit; cursor: pointer; }
  input { font-family: inherit; }

  /* ============ STAGE & DEVICE ============ */
  .stage {
    width: 100vw; height: 100vh; height: 100dvh;
    display: flex; align-items: center; justify-content: center;
    background:
      radial-gradient(ellipse 80% 60% at 50% 0%, #1a1a28 0%, transparent 60%),
      radial-gradient(ellipse 60% 40% at 80% 100%, #241621 0%, transparent 50%),
      radial-gradient(ellipse 60% 40% at 20% 100%, #16202a 0%, transparent 50%),
      #06060a;
    overflow: hidden;
    position: relative;
  }
  .stage::before {
    content: '';
    position: absolute; inset: 0;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.08 0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
    mix-blend-mode: overlay;
    pointer-events: none;
    opacity: 0.5;
  }
  .device {
    width: 390px; height: 844px;
    max-width: 100vw; max-height: 100vh; max-height: 100dvh;
    background: var(--paper);
    position: relative;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    border-radius: 48px;
    box-shadow:
      0 0 0 2px #0a0a0f,
      0 0 0 12px #1a1a24,
      0 0 0 13px #2a2a38,
      0 40px 80px -20px rgba(0,0,0,0.7),
      0 20px 40px -10px rgba(0,0,0,0.5);
  }
  .notch {
    position: absolute; top: 10px; left: 50%;
    transform: translateX(-50%);
    width: 110px; height: 28px;
    background: #000;
    border-radius: 20px;
    z-index: 500;
    pointer-events: none;
  }
  .notch::after {
    content: '';
    position: absolute; right: 14px; top: 50%;
    transform: translateY(-50%);
    width: 8px; height: 8px;
    border-radius: 50%;
    background: radial-gradient(circle at 30% 30%, #2a3540, #0a0f15);
  }
  .home-bar {
    position: absolute; bottom: 8px; left: 50%;
    transform: translateX(-50%);
    width: 120px; height: 5px;
    background: rgba(14,14,20,0.85);
    border-radius: 3px;
    z-index: 500;
    pointer-events: none;
  }
  @media (max-width: 430px) {
    .stage { background: var(--paper); }
    .stage::before { display: none; }
    .device {
      width: 100vw; height: 100vh; height: 100dvh;
      border-radius: 0;
      box-shadow: none;
    }
    .notch, .home-bar { display: none; }
  }

  /* ============ TOP BAR ============ */
  .top-bar {
    height: calc(var(--bar-h) + var(--safe-top));
    padding: var(--safe-top) 10px 0;
    display: flex;
    align-items: center;
    gap: 2px;
    z-index: var(--z-bar);
    flex-shrink: 0;
    background: linear-gradient(to bottom, var(--paper) 0%, var(--paper) 70%, transparent 100%);
    position: relative;
  }
  .top-bar::after {
    content: '';
    position: absolute;
    left: 16px; right: 16px; bottom: 0;
    height: 1px;
    background: linear-gradient(to right, transparent, var(--line-2), transparent);
  }

  .icon-btn {
    width: var(--tap); height: var(--tap);
    display: flex; align-items: center; justify-content: center;
    color: var(--ink);
    border-radius: 12px;
    position: relative;
    transition: background var(--dur) var(--ease), transform var(--dur-fast) var(--ease-out);
  }
  .icon-btn svg { width: 20px; height: 20px; stroke-width: 1.75; }
  .icon-btn:hover { background: rgba(14,14,20,0.05); }
  .icon-btn:active { background: rgba(14,14,20,0.09); transform: scale(0.92); }
  .icon-btn:disabled { opacity: 0.25; cursor: not-allowed; }
  .icon-btn:disabled:hover { background: none; }

  .doc-title-wrap {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 6px;
    min-width: 0;
  }
  .doc-title {
    background: transparent;
    border: none;
    color: var(--ink);
    font-family: var(--serif);
    font-size: 19px;
    font-weight: 400;
    font-style: italic;
    text-align: center;
    padding: 6px 12px;
    border-radius: 8px;
    min-width: 0;
    max-width: 100%;
    outline: none;
    letter-spacing: -0.01em;
    transition: background var(--dur) var(--ease);
  }
  .doc-title:focus { background: rgba(14,14,20,0.05); }
  .doc-title:hover { background: rgba(14,14,20,0.03); }

  .share-btn {
    background: var(--ink);
    color: var(--paper);
    padding: 0 18px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 600;
    height: 36px;
    display: flex; align-items: center;
    margin-right: 4px;
    letter-spacing: -0.01em;
    transition: transform var(--dur-fast) var(--spring);
    position: relative;
    overflow: hidden;
  }
  .share-btn::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(135deg, var(--accent) 0%, var(--accent-5) 100%);
    opacity: 0;
    transition: opacity var(--dur) var(--ease);
  }
  .share-btn span { position: relative; z-index: 1; }
  .share-btn:hover::before { opacity: 1; }
  .share-btn:active { transform: scale(0.95); }

  /* ============ CANVAS ZONE ============ */
  .canvas-zone {
    flex: 1;
    position: relative;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    touch-action: none;
    background:
      radial-gradient(ellipse 100% 70% at 50% 50%, rgba(255,255,255,0.4) 0%, transparent 70%),
      var(--paper);
  }
  .canvas-zone::before {
    content: '';
    position: absolute; inset: 0;
    background-image: radial-gradient(circle, rgba(14,14,20,0.08) 1px, transparent 1px);
    background-size: 20px 20px;
    pointer-events: none;
    mask-image: radial-gradient(ellipse 100% 70% at 50% 50%, black 0%, transparent 80%);
    -webkit-mask-image: radial-gradient(ellipse 100% 70% at 50% 50%, black 0%, transparent 80%);
  }

  .canvas-host {
    position: relative;
    border-radius: 6px;
    background: #fff;
    box-shadow:
      0 0 0 1px rgba(14,14,20,0.06),
      0 2px 4px rgba(14,14,20,0.04),
      0 12px 40px rgba(14,14,20,0.12),
      0 24px 60px rgba(14,14,20,0.08);
  }
  .canvas-host canvas { display: block; border-radius: 6px; }

  /* Zoom pill */
  .zoom-pill {
    position: absolute;
    bottom: 16px; right: 16px;
    background: rgba(14,14,20,0.92);
    backdrop-filter: blur(20px) saturate(180%);
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    color: var(--paper);
    border-radius: 999px;
    display: flex; align-items: center;
    padding: 3px;
    z-index: var(--z-floating);
    box-shadow: var(--shadow-lg), inset 0 0 0 1px rgba(255,255,255,0.08);
  }
  .zoom-pill button {
    width: 30px; height: 30px;
    display: flex; align-items: center; justify-content: center;
    color: rgba(255,255,255,0.7);
    border-radius: 50%;
    transition: all var(--dur-fast) var(--ease-out);
  }
  .zoom-pill button:hover { color: white; background: rgba(255,255,255,0.12); }
  .zoom-pill button:active { transform: scale(0.9); }
  .zoom-pill .val {
    font-size: 11px;
    font-weight: 600;
    min-width: 38px;
    text-align: center;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.02em;
  }

  /* Floating object toolbar */
  .obj-toolbar {
    position: absolute;
    background: rgba(14,14,20,0.92);
    backdrop-filter: blur(24px) saturate(180%);
    -webkit-backdrop-filter: blur(24px) saturate(180%);
    border-radius: 14px;
    padding: 5px;
    display: none;
    align-items: center;
    gap: 1px;
    z-index: var(--z-floating);
    box-shadow: var(--shadow-xl), inset 0 0 0 1px rgba(255,255,255,0.1);
    will-change: transform;
    transform: translate(-50%, 0);
  }
  .obj-toolbar.visible {
    display: flex;
    animation: toolbar-in 320ms var(--spring);
  }
  @keyframes toolbar-in {
    from { opacity: 0; transform: translate(-50%, 8px) scale(0.94); }
    to { opacity: 1; transform: translate(-50%, 0) scale(1); }
  }
  .obj-toolbar button {
    width: 34px; height: 34px;
    display: flex; align-items: center; justify-content: center;
    color: rgba(255,255,255,0.75);
    border-radius: 8px;
    transition: all var(--dur-fast) var(--ease-out);
  }
  .obj-toolbar button:hover { color: white; background: rgba(255,255,255,0.12); }
  .obj-toolbar button:active { transform: scale(0.92); }
  .obj-toolbar button svg { width: 17px; height: 17px; stroke-width: 1.75; }
  .obj-toolbar .sep { width: 1px; height: 18px; background: rgba(255,255,255,0.12); margin: 0 3px; }
  .obj-toolbar .fill-swatch {
    width: 22px; height: 22px;
    border-radius: 50%;
    border: 2px solid rgba(255,255,255,0.9);
    box-shadow: inset 0 0 0 1px rgba(0,0,0,0.1), 0 2px 4px rgba(0,0,0,0.2);
    transition: transform var(--dur-fast) var(--spring);
  }
  .obj-toolbar .fill-swatch:hover { transform: scale(1.1); }
  .obj-toolbar .opacity-wrap {
    display: flex; align-items: center;
    padding: 0 10px 0 6px;
    gap: 8px;
    color: rgba(255,255,255,0.7);
  }
  .obj-toolbar input[type=range] {
    width: 68px;
    -webkit-appearance: none;
    appearance: none;
    height: 3px;
    background: rgba(255,255,255,0.2);
    border-radius: 2px;
    outline: none;
  }
  .obj-toolbar input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 14px; height: 14px;
    border-radius: 50%;
    background: white;
    box-shadow: 0 2px 4px rgba(0,0,0,0.3);
    cursor: pointer;
  }
  .obj-toolbar input[type=range]::-moz-range-thumb {
    width: 14px; height: 14px;
    border-radius: 50%;
    background: white;
    border: none;
    cursor: pointer;
  }
  .hidden-color { position: absolute; opacity: 0; pointer-events: none; width: 0; height: 0; }

  /* ============ BOTTOM TOOLBAR ============ */
  .bottom-bar {
    height: calc(var(--bar-h) + var(--safe-bottom));
    padding: 0 6px var(--safe-bottom);
    display: flex;
    align-items: center;
    justify-content: space-around;
    z-index: var(--z-bar);
    flex-shrink: 0;
    background: linear-gradient(to top, var(--paper) 0%, var(--paper) 70%, transparent 100%);
    position: relative;
  }
  .bottom-bar::before {
    content: '';
    position: absolute;
    left: 16px; right: 16px; top: 0;
    height: 1px;
    background: linear-gradient(to right, transparent, var(--line-2), transparent);
  }

  .tab-btn {
    flex: 1;
    height: 52px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    color: rgba(14,14,20,0.45);
    position: relative;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.01em;
    transition: color var(--dur) var(--ease);
  }
  .tab-btn svg { width: 20px; height: 20px; stroke-width: 1.75; transition: transform var(--dur) var(--spring); }
  .tab-btn:active svg { transform: scale(0.88); }
  .tab-btn.active { color: var(--ink); }
  .tab-btn.active svg { transform: scale(1.05); }
  .tab-btn.active::after {
    content: '';
    position: absolute;
    bottom: 4px;
    left: 50%;
    width: 4px; height: 4px;
    border-radius: 50%;
    background: var(--accent);
    transform: translateX(-50%);
    animation: dot-in 400ms var(--spring);
  }
  @keyframes dot-in {
    from { transform: translateX(-50%) scale(0); }
    to { transform: translateX(-50%) scale(1); }
  }

  /* ============ BOTTOM SHEET ============ */
  .scrim {
    position: absolute;
    top: 0; left: 0; right: 0;
    bottom: calc(var(--bar-h) + var(--safe-bottom) + var(--sheet-h) - 20px);
    background: rgba(14,14,20,0.25);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--dur) var(--ease);
    z-index: var(--z-scrim);
  }
  .scrim.open { opacity: 1; pointer-events: auto; }

  .sheet {
    position: absolute;
    left: 0; right: 0;
    bottom: calc(var(--bar-h) + var(--safe-bottom));
    height: var(--sheet-h);
    max-height: calc(100% - var(--bar-h) - var(--safe-bottom) - var(--safe-top) - 40px);
    background: var(--paper);
    border-radius: 28px 28px 0 0;
    transform: translateY(calc(100% + 100px));
    transition: transform 480ms var(--spring);
    z-index: var(--z-sheet);
    display: flex;
    flex-direction: column;
    will-change: transform;
    box-shadow:
      0 -20px 60px rgba(14,14,20,0.2),
      0 -4px 12px rgba(14,14,20,0.06),
      inset 0 1px 0 rgba(255,255,255,0.8);
    overflow: hidden;
  }
  .sheet.open { transform: translateY(0); }
  .sheet::before {
    content: '';
    position: absolute;
    inset: 0 0 auto 0;
    height: 140px;
    background: linear-gradient(to bottom, rgba(255,255,255,0.5), transparent);
    pointer-events: none;
  }

  .sheet-handle {
    width: 40px; height: 4px;
    background: var(--line-2);
    border-radius: 2px;
    margin: 10px auto 4px;
    flex-shrink: 0;
    position: relative;
    z-index: 1;
  }
  .sheet-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    padding: 10px 22px 14px;
    flex-shrink: 0;
    position: relative;
    z-index: 1;
  }
  .sheet-title {
    font-family: var(--serif);
    font-size: 26px;
    font-weight: 500;
    letter-spacing: -0.02em;
    color: var(--ink);
  }
  .sheet-title em {
    font-style: italic;
    font-weight: 300;
    color: rgba(14,14,20,0.45);
    font-size: 14px;
    margin-left: 8px;
    letter-spacing: 0;
  }
  .sheet-close {
    width: 32px; height: 32px;
    display: flex; align-items: center; justify-content: center;
    color: rgba(14,14,20,0.5);
    border-radius: 50%;
    transition: all var(--dur-fast) var(--ease-out);
  }
  .sheet-close:hover { background: rgba(14,14,20,0.06); color: var(--ink); }
  .sheet-close:active { transform: scale(0.9); }

  .sheet-body {
    flex: 1;
    overflow-y: auto;
    padding: 4px 22px 28px;
    -webkit-overflow-scrolling: touch;
    position: relative;
    z-index: 1;
    scrollbar-width: thin;
    scrollbar-color: var(--line-2) transparent;
  }
  .sheet-body::-webkit-scrollbar { width: 4px; }
  .sheet-body::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 2px; }

  /* ============ PANELS ============ */
  .panel { display: none; }
  .panel.active {
    display: block;
    animation: panel-in 400ms var(--ease-out);
  }
  @keyframes panel-in {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: translateY(0); }
  }

  .grid-3 {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
  }

  .tile {
    aspect-ratio: 1;
    background: #fff;
    border-radius: var(--radius-md);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--ink);
    box-shadow: inset 0 0 0 1px var(--line), var(--shadow-sm);
    transition: all var(--dur) var(--spring);
    position: relative;
    overflow: hidden;
  }
  .tile::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(135deg, transparent 40%, rgba(255,91,46,0.06) 100%);
    opacity: 0;
    transition: opacity var(--dur) var(--ease);
  }
  .tile:hover {
    transform: translateY(-2px);
    box-shadow: inset 0 0 0 1px var(--line-2), var(--shadow-md);
  }
  .tile:hover::before { opacity: 1; }
  .tile:active { transform: translateY(0) scale(0.96); }
  .tile svg { width: 42px; height: 42px; position: relative; z-index: 1; }

  /* Staggered panel entrance for tiles */
  .panel.active .grid-3 > *:nth-child(1),
  .panel.active > *:nth-child(1) { animation-delay: 40ms; }
  .panel.active .grid-3 > *:nth-child(2),
  .panel.active > *:nth-child(2) { animation-delay: 80ms; }
  .panel.active .grid-3 > *:nth-child(3),
  .panel.active > *:nth-child(3) { animation-delay: 120ms; }
  .panel.active .grid-3 > *:nth-child(4),
  .panel.active > *:nth-child(4) { animation-delay: 160ms; }
  .panel.active .grid-3 > *:nth-child(5),
  .panel.active > *:nth-child(5) { animation-delay: 200ms; }
  .panel.active .grid-3 > *:nth-child(6),
  .panel.active > *:nth-child(6) { animation-delay: 240ms; }
  .panel.active .grid-3 > *,
  .panel.active > .text-preset {
    animation: item-in 500ms var(--spring) both;
  }
  @keyframes item-in {
    from { opacity: 0; transform: translateY(14px) scale(0.96); }
    to { opacity: 1; transform: translateY(0) scale(1); }
  }

  /* Text panel */
  .text-preset {
    display: block;
    width: 100%;
    padding: 22px 22px;
    background: #fff;
    border-radius: var(--radius-md);
    margin-bottom: 12px;
    text-align: left;
    color: var(--ink);
    box-shadow: inset 0 0 0 1px var(--line), var(--shadow-sm);
    transition: all var(--dur) var(--spring);
    font-family: var(--serif);
    line-height: 1;
  }
  .text-preset:hover {
    transform: translateY(-2px);
    box-shadow: inset 0 0 0 1px var(--line-2), var(--shadow-md);
  }
  .text-preset:active { transform: translateY(0) scale(0.99); }
  .text-preset.h { font-size: 30px; font-weight: 600; letter-spacing: -0.03em; }
  .text-preset.sh { font-size: 22px; font-weight: 400; font-style: italic; letter-spacing: -0.02em; }
  .text-preset.body { font-size: 15px; font-weight: 400; font-family: var(--sans); letter-spacing: -0.01em; color: rgba(14,14,20,0.7); line-height: 1.4; }

  /* Draw panel */
  .draw-toggle {
    width: 100%;
    padding: 18px;
    background: #fff;
    border-radius: var(--radius-md);
    font-size: 14px;
    font-weight: 600;
    margin-bottom: 20px;
    color: var(--ink);
    box-shadow: inset 0 0 0 1px var(--line), var(--shadow-sm);
    transition: all var(--dur) var(--spring);
    display: flex;
    align-items: center;
    justify-content: space-between;
    letter-spacing: -0.01em;
  }
  .draw-toggle:hover { transform: translateY(-1px); box-shadow: inset 0 0 0 1px var(--line-2), var(--shadow-md); }
  .draw-toggle .pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    border-radius: 999px;
    background: var(--paper-2);
    font-size: 11px;
    font-weight: 600;
    color: rgba(14,14,20,0.6);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    transition: all var(--dur) var(--ease);
  }
  .draw-toggle .pill::before {
    content: '';
    width: 6px; height: 6px; border-radius: 50%;
    background: rgba(14,14,20,0.3);
    transition: all var(--dur) var(--ease);
  }
  .draw-toggle.on { background: var(--ink); color: var(--paper); box-shadow: var(--shadow-lg); }
  .draw-toggle.on .pill { background: rgba(255,255,255,0.12); color: var(--accent); }
  .draw-toggle.on .pill::before { background: var(--accent); box-shadow: 0 0 8px var(--accent); }

  .draw-label {
    font-family: var(--serif);
    font-style: italic;
    font-size: 13px;
    color: rgba(14,14,20,0.55);
    margin-bottom: 10px;
    margin-top: 4px;
  }
  .draw-label b { font-style: normal; font-weight: 500; color: var(--ink); font-family: var(--sans); }

  .color-row {
    display: flex;
    gap: 10px;
    margin-bottom: 22px;
    flex-wrap: wrap;
  }
  .color-swatch {
    width: 34px; height: 34px;
    border-radius: 50%;
    box-shadow: inset 0 0 0 2px #fff, inset 0 0 0 3px var(--line-2), 0 1px 3px rgba(0,0,0,0.08);
    transition: transform var(--dur) var(--spring);
    position: relative;
  }
  .color-swatch:hover { transform: scale(1.12) rotate(8deg); }
  .color-swatch.selected {
    transform: scale(1.15);
    box-shadow: inset 0 0 0 2px #fff, inset 0 0 0 3px var(--ink), 0 4px 12px rgba(0,0,0,0.15);
  }

  .width-slider {
    width: 100%;
    -webkit-appearance: none;
    appearance: none;
    height: 6px;
    background: var(--paper-2);
    border-radius: 3px;
    outline: none;
    box-shadow: inset 0 1px 2px rgba(0,0,0,0.06);
  }
  .width-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 22px; height: 22px;
    border-radius: 50%;
    background: var(--ink);
    cursor: pointer;
    box-shadow: 0 2px 8px rgba(0,0,0,0.2);
    border: 2px solid #fff;
    transition: transform var(--dur-fast) var(--spring);
  }
  .width-slider::-webkit-slider-thumb:hover { transform: scale(1.15); }
  .width-slider::-moz-range-thumb {
    width: 22px; height: 22px;
    border-radius: 50%;
    background: var(--ink);
    cursor: pointer;
    border: 2px solid #fff;
  }

  /* Layers */
  .layer-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border-radius: var(--radius-md);
    background: #fff;
    margin-bottom: 8px;
    transition: all var(--dur) var(--spring);
    box-shadow: inset 0 0 0 1px var(--line), var(--shadow-sm);
    cursor: pointer;
  }
  .layer-row:hover { transform: translateX(2px); box-shadow: inset 0 0 0 1px var(--line-2), var(--shadow-md); }
  .layer-row.active {
    box-shadow: inset 0 0 0 1.5px var(--ink), var(--shadow-md);
    background: var(--paper);
  }
  .drag-handle {
    color: rgba(14,14,20,0.25);
    display: flex; align-items: center;
    cursor: grab;
  }
  .drag-handle svg { width: 14px; height: 14px; }
  .layer-thumb {
    width: 36px; height: 36px;
    border-radius: 8px;
    background: var(--paper-2);
    border: 1px solid var(--line);
    flex-shrink: 0;
    overflow: hidden;
    display: flex; align-items: center; justify-content: center;
  }
  .layer-thumb img { max-width: 80%; max-height: 80%; }
  .layer-label {
    flex: 1;
    font-size: 13px;
    font-weight: 500;
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    letter-spacing: -0.01em;
  }
  .layer-label .type {
    font-family: var(--serif);
    font-style: italic;
    font-size: 11px;
    color: rgba(14,14,20,0.4);
    font-weight: 400;
    display: block;
    margin-top: 1px;
  }
  .layer-row .mini-btn {
    width: 30px; height: 30px;
    display: flex; align-items: center; justify-content: center;
    color: rgba(14,14,20,0.45);
    border-radius: 8px;
    transition: all var(--dur-fast) var(--ease-out);
  }
  .layer-row .mini-btn:hover { background: var(--paper-2); color: var(--ink); }
  .layer-row .mini-btn:active { transform: scale(0.9); }
  .layer-row .mini-btn svg { width: 15px; height: 15px; stroke-width: 1.75; }
  .layer-empty {
    text-align: center;
    padding: 50px 20px;
    color: rgba(14,14,20,0.4);
  }
  .layer-empty .mark {
    font-family: var(--serif);
    font-style: italic;
    font-size: 56px;
    color: var(--line-2);
    line-height: 1;
    margin-bottom: 14px;
  }
  .layer-empty .msg {
    font-family: var(--serif);
    font-size: 15px;
    font-style: italic;
  }

  /* Photos */
  .photo-tile {
    aspect-ratio: 1;
    border-radius: var(--radius-md);
    display: flex;
    align-items: flex-end;
    padding: 12px;
    color: rgba(255,255,255,0.95);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 0.02em;
    transition: all var(--dur) var(--spring);
    box-shadow: var(--shadow-sm);
    position: relative;
    overflow: hidden;
    text-shadow: 0 1px 2px rgba(0,0,0,0.3);
  }
  .photo-tile:hover { transform: translateY(-2px) scale(1.02); box-shadow: var(--shadow-lg); }
  .photo-tile:active { transform: translateY(0) scale(0.97); }

  /* Templates */
  .template-tile {
    aspect-ratio: 3/4;
    background: #fff;
    border-radius: var(--radius-md);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 0 0 10px;
    color: rgba(14,14,20,0.5);
    box-shadow: inset 0 0 0 1px var(--line), var(--shadow-sm);
    transition: all var(--dur) var(--spring);
    position: relative;
    overflow: hidden;
  }
  .template-tile:hover { transform: translateY(-2px); box-shadow: inset 0 0 0 1px var(--line-2), var(--shadow-md); }
  .template-tile:active { transform: translateY(0) scale(0.97); }
  .template-tile .preview {
    width: 100%;
    flex: 1;
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    position: relative;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    padding: 10px;
  }
  .template-tile .num {
    font-family: var(--serif);
    font-style: italic;
    font-size: 13px;
    color: var(--ink);
    margin-top: 2px;
  }
</style>
</head>
<body>
<div class="stage">
  <div class="device" id="device">
    <div class="notch"></div>

    <!-- TOP BAR -->
    <div class="top-bar" role="toolbar" aria-label="Document toolbar">
      <button class="icon-btn" aria-label="Back">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
      </button>
      <div class="doc-title-wrap">
        <input class="doc-title" id="docTitle" value="Untitled composition" aria-label="Document title" />
      </div>
      <button class="icon-btn" id="undoBtn" aria-label="Undo">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M9 14L4 9l5-5"/><path d="M4 9h10.5a5.5 5.5 0 015.5 5.5v0a5.5 5.5 0 01-5.5 5.5H11"/></svg>
      </button>
      <button class="icon-btn" id="redoBtn" aria-label="Redo">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M15 14l5-5-5-5"/><path d="M20 9H9.5A5.5 5.5 0 004 14.5v0A5.5 5.5 0 009.5 20H13"/></svg>
      </button>
      <button class="share-btn" aria-label="Share"><span>Share</span></button>
    </div>

    <!-- CANVAS ZONE -->
    <div class="canvas-zone" id="canvasZone">
      <div class="canvas-host" id="canvasHost">
        <canvas id="fabricCanvas"></canvas>
      </div>

      <div class="obj-toolbar" id="objToolbar" role="toolbar" aria-label="Object actions">
        <button id="btnFront" aria-label="Bring to front">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><rect x="8" y="8" width="12" height="12" rx="2"/><path d="M4 16V6a2 2 0 012-2h10"/></svg>
        </button>
        <button id="btnDup" aria-label="Duplicate">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
        </button>
        <button id="btnDel" aria-label="Delete">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/></svg>
        </button>
        <div class="sep"></div>
        <button id="btnFill" aria-label="Fill color" style="padding: 0 8px;">
          <div class="fill-swatch" id="fillSwatch" style="background:#FF5B2E;"></div>
        </button>
        <input type="color" id="fillPicker" class="hidden-color" aria-label="Color picker" />
        <div class="sep"></div>
        <div class="opacity-wrap">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 3v18"/></svg>
          <input type="range" id="opacitySlider" min="0" max="100" value="100" aria-label="Opacity" />
        </div>
      </div>

      <div class="zoom-pill" role="group" aria-label="Zoom">
        <button id="zoomOut" aria-label="Zoom out">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round"><path d="M5 12h14"/></svg>
        </button>
        <div class="val" id="zoomVal">100%</div>
        <button id="zoomIn" aria-label="Zoom in">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round"><path d="M12 5v14M5 12h14"/></svg>
        </button>
      </div>
    </div>

    <!-- BOTTOM TOOLBAR -->
    <div class="bottom-bar" role="tablist" aria-label="Tools">
      <button class="tab-btn" data-tab="elements" role="tab" aria-label="Shapes">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7" rx="1.5"/><circle cx="17" cy="6.5" r="3.5"/><path d="M3 14h7v7H3z"/><path d="M17 13.5l4.5 7.5h-9z"/></svg>
        <span>Shapes</span>
      </button>
      <button class="tab-btn" data-tab="text" role="tab" aria-label="Type">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M5 6V4h14v2"/><path d="M9 20h6"/><path d="M12 4v16"/></svg>
        <span>Type</span>
      </button>
      <button class="tab-btn" data-tab="photos" role="tab" aria-label="Photos">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="1.5"/><path d="M21 16l-5-5L5 21"/></svg>
        <span>Photos</span>
      </button>
      <button class="tab-btn" data-tab="templates" role="tab" aria-label="Layouts">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="9" rx="1.5"/><rect x="14" y="3" width="7" height="5" rx="1.5"/><rect x="14" y="12" width="7" height="9" rx="1.5"/><rect x="3" y="16" width="7" height="5" rx="1.5"/></svg>
        <span>Layouts</span>
      </button>
      <button class="tab-btn" data-tab="draw" role="tab" aria-label="Draw">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21l3.5-1 13-13-2.5-2.5-13 13z"/><path d="M14 6l2.5 2.5"/><path d="M18 2l4 4"/></svg>
        <span>Draw</span>
      </button>
      <button class="tab-btn" data-tab="layers" role="tab" aria-label="Layers">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L2 8l10 6 10-6-10-6z"/><path d="M2 16l10 6 10-6"/><path d="M2 12l10 6 10-6"/></svg>
        <span>Layers</span>
      </button>
    </div>

    <div class="scrim" id="scrim"></div>

    <!-- BOTTOM SHEET -->
    <div class="sheet" id="sheet" role="dialog" aria-modal="true" aria-labelledby="sheetTitle">
      <div class="sheet-handle"></div>
      <div class="sheet-header">
        <div class="sheet-title" id="sheetTitle">Shapes<em>— pick &amp; place</em></div>
        <button class="sheet-close" id="sheetClose" aria-label="Close panel">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round"><path d="M18 6L6 18M6 6l12 12"/></svg>
        </button>
      </div>
      <div class="sheet-body">
        <div class="panel" data-panel="elements">
          <div class="grid-3" id="elementsGrid"></div>
        </div>
        <div class="panel" data-panel="text">
          <button class="text-preset h" data-size="96" data-weight="600" data-family="serif" data-italic="false">Add a headline</button>
          <button class="text-preset sh" data-size="48" data-weight="400" data-family="serif" data-italic="true">a subtitle, softly</button>
          <button class="text-preset body" data-size="28" data-weight="400" data-family="sans" data-italic="false">Body copy for the rest of your story.</button>
        </div>
        <div class="panel" data-panel="photos">
          <div class="grid-3" id="photosGrid"></div>
        </div>
        <div class="panel" data-panel="templates">
          <div class="grid-3" id="templatesGrid"></div>
        </div>
        <div class="panel" data-panel="draw">
          <button class="draw-toggle" id="drawToggle">
            <span>Free-hand pencil</span>
            <span class="pill">Off</span>
          </button>
          <div class="draw-label">Ink <b>color</b></div>
          <div class="color-row" id="colorRow"></div>
          <div class="draw-label">Brush <b>weight</b> · <span id="widthVal">8</span>px</div>
          <input type="range" class="width-slider" id="widthSlider" min="4" max="32" value="8" aria-label="Brush width" />
        </div>
        <div class="panel" data-panel="layers">
          <div id="layersList"></div>
        </div>
      </div>
    </div>

    <div class="home-bar"></div>
  </div>
</div>

<script>
(() => {
  'use strict';

  const PALETTE = ['#FF5B2E','#E8C547','#5B8DEF','#7AC74F','#D946EF','#0E0E14','#FF8FA3','#2DD4BF','#A78BFA','#FB923C','#14B8A6','#F472B6'];
  const ARTBOARD_W = 1080;
  const ARTBOARD_H = 1080;
  const state = {
    history: [], cursor: -1, suppressHistory: false,
    zoom: 1, baseZoom: 1,
    activeTab: null,
    drawColor: '#FF5B2E', drawWidth: 8, drawOn: false,
  };

  const canvasEl = document.getElementById('fabricCanvas');
  const canvas = new fabric.Canvas(canvasEl, {
    preserveObjectStacking: true,
    stopContextMenu: true,
    fireRightClick: false,
    enableRetinaScaling: true,
    backgroundColor: '#FFFFFF',
    selection: true,
  });
  fabric.Object.prototype.set({
    borderColor: '#0E0E14',
    cornerColor: '#FFFFFF',
    cornerStrokeColor: '#0E0E14',
    cornerSize: 10,
    cornerStyle: 'circle',
    transparentCorners: false,
    borderScaleFactor: 1.5,
    padding: 4,
  });

  const artboard = new fabric.Rect({
    left: 0, top: 0, width: ARTBOARD_W, height: ARTBOARD_H,
    fill: '#FFFFFF', selectable: false, evented: false, hoverCursor: 'default',
  });
  artboard.isArtboard = true;
  canvas.add(artboard);

  function resizeCanvas() {
    const zone = document.getElementById('canvasZone');
    const host = document.getElementById('canvasHost');
    const pad = 32;
    const maxW = zone.clientWidth - pad * 2;
    const maxH = zone.clientHeight - pad * 2;
    const size = Math.max(140, Math.min(maxW, maxH));
    host.style.width = size + 'px';
    host.style.height = size + 'px';
    canvas.setDimensions({ width: size, height: size });
    const zoom = size / ARTBOARD_W;
    state.baseZoom = zoom;
    state.zoom = zoom;
    canvas.setZoom(zoom);
    canvas.absolutePan({ x: 0, y: 0 });
    updateZoomLabel();
    updateFloatingToolbar();
  }
  window.addEventListener('resize', resizeCanvas);

  function updateZoomLabel() {
    const rel = state.baseZoom > 0 ? state.zoom / state.baseZoom : 1;
    document.getElementById('zoomVal').textContent = Math.round(rel * 100) + '%';
  }

  function snapshot() {
    if (state.suppressHistory) return;
    const json = canvas.toJSON(['isArtboard']);
    state.history = state.history.slice(0, state.cursor + 1);
    state.history.push(json);
    if (state.history.length > 30) state.history.shift();
    state.cursor = state.history.length - 1;
    updateUndoRedo();
  }
  function updateUndoRedo() {
    document.getElementById('undoBtn').disabled = state.cursor <= 0;
    document.getElementById('redoBtn').disabled = state.cursor >= state.history.length - 1;
  }
  function undo() {
    if (state.cursor <= 0) return;
    state.cursor--;
    loadSnapshot(state.history[state.cursor]);
  }
  function redo() {
    if (state.cursor >= state.history.length - 1) return;
    state.cursor++;
    loadSnapshot(state.history[state.cursor]);
  }
  function loadSnapshot(json) {
    state.suppressHistory = true;
    canvas.loadFromJSON(json, () => {
      canvas.renderAll();
      state.suppressHistory = false;
      updateUndoRedo();
      renderLayers();
    });
  }
  canvas.on('object:added', snapshot);
  canvas.on('object:modified', snapshot);
  canvas.on('object:removed', snapshot);
  document.getElementById('undoBtn').addEventListener('click', undo);
  document.getElementById('redoBtn').addEventListener('click', redo);

  const objToolbar = document.getElementById('objToolbar');
  function updateFloatingToolbar() {
    const obj = canvas.getActiveObject();
    if (!obj || obj.isArtboard) { objToolbar.classList.remove('visible'); return; }
    const rect = obj.getBoundingRect(true, true);
    const host = document.getElementById('canvasHost');
    const hostRect = host.getBoundingClientRect();
    const zoneRect = document.getElementById('canvasZone').getBoundingClientRect();
    const centerX = hostRect.left - zoneRect.left + rect.left + rect.width / 2;
    let topY = hostRect.top - zoneRect.top + rect.top - 54;
    if (topY < 8) topY = hostRect.top - zoneRect.top + rect.top + rect.height + 12;
    const tbHalfW = 180;
    const clampedX = Math.max(tbHalfW + 10, Math.min(zoneRect.width - tbHalfW - 10, centerX));
    objToolbar.style.left = clampedX + 'px';
    objToolbar.style.top = topY + 'px';
    objToolbar.classList.add('visible');

    const fill = (typeof obj.fill === 'string' && obj.fill) || obj.stroke || '#FF5B2E';
    if (typeof fill === 'string') {
      document.getElementById('fillSwatch').style.background = fill;
      document.getElementById('fillPicker').value = toHex(fill);
    }
    document.getElementById('opacitySlider').value = Math.round((obj.opacity ?? 1) * 100);
  }
  function toHex(c) {
    if (!c) return '#000000';
    if (c.startsWith('#')) return c.length === 7 ? c : '#000000';
    const m = c.match(/\d+/g);
    if (!m) return '#000000';
    return '#' + m.slice(0,3).map(n => (+n).toString(16).padStart(2,'0')).join('');
  }
  canvas.on('selection:created', updateFloatingToolbar);
  canvas.on('selection:updated', updateFloatingToolbar);
  canvas.on('selection:cleared', () => objToolbar.classList.remove('visible'));
  canvas.on('object:moving', updateFloatingToolbar);
  canvas.on('object:scaling', updateFloatingToolbar);
  canvas.on('object:rotating', updateFloatingToolbar);
  canvas.on('after:render', () => { if (canvas.getActiveObject()) updateFloatingToolbar(); });

  document.getElementById('btnDel').addEventListener('click', () => {
    const obj = canvas.getActiveObject();
    if (obj && !obj.isArtboard) { canvas.remove(obj); canvas.discardActiveObject(); canvas.renderAll(); renderLayers(); }
  });
  document.getElementById('btnDup').addEventListener('click', () => {
    const obj = canvas.getActiveObject();
    if (!obj || obj.isArtboard) return;
    obj.clone((clone) => {
      clone.set({ left: (obj.left || 0) + 20, top: (obj.top || 0) + 20 });
      canvas.add(clone);
      canvas.setActiveObject(clone);
      canvas.renderAll();
      renderLayers();
    });
  });
  document.getElementById('btnFront').addEventListener('click', () => {
    const obj = canvas.getActiveObject();
    if (obj && !obj.isArtboard) { canvas.bringToFront(obj); canvas.renderAll(); renderLayers(); snapshot(); }
  });
  document.getElementById('btnFill').addEventListener('click', () => document.getElementById('fillPicker').click());
  document.getElementById('fillPicker').addEventListener('input', (e) => {
    const obj = canvas.getActiveObject();
    if (obj && !obj.isArtboard) {
      if (obj.type === 'line' || obj.type === 'path') obj.set('stroke', e.target.value);
      else obj.set('fill', e.target.value);
      document.getElementById('fillSwatch').style.background = e.target.value;
      canvas.renderAll();
    }
  });
  document.getElementById('fillPicker').addEventListener('change', snapshot);
  const opSlider = document.getElementById('opacitySlider');
  opSlider.addEventListener('input', (e) => {
    const obj = canvas.getActiveObject();
    if (obj && !obj.isArtboard) { obj.set('opacity', e.target.value / 100); canvas.renderAll(); }
  });
  opSlider.addEventListener('change', snapshot);

  const sheet = document.getElementById('sheet');
  const scrim = document.getElementById('scrim');
  const tabs = document.querySelectorAll('.tab-btn');
  const TAB_META = {
    elements: { title: 'Shapes',  sub: '— pick & place' },
    text:     { title: 'Type',    sub: '— set the tone' },
    photos:   { title: 'Photos',  sub: '— drop them in' },
    templates:{ title: 'Layouts', sub: '— start strong' },
    draw:     { title: 'Draw',    sub: '— make your mark' },
    layers:   { title: 'Layers',  sub: '— arrange & edit' },
  };
  tabs.forEach(tab => {
    tab.addEventListener('click', () => {
      const name = tab.dataset.tab;
      if (state.activeTab === name) { closeSheet(); return; }
      openSheet(name);
    });
  });
  function openSheet(name) {
    state.activeTab = name;
    tabs.forEach(t => t.classList.toggle('active', t.dataset.tab === name));
    document.querySelectorAll('.panel').forEach(p => {
      p.classList.remove('active');
      if (p.dataset.panel === name) {
        void p.offsetWidth;
        p.classList.add('active');
      }
    });
    const meta = TAB_META[name];
    document.getElementById('sheetTitle').innerHTML = meta.title + '<em>' + meta.sub + '</em>';
    sheet.classList.add('open');
    scrim.classList.add('open');
    if (name === 'layers') renderLayers();
  }
  function closeSheet() {
    state.activeTab = null;
    tabs.forEach(t => t.classList.remove('active'));
    sheet.classList.remove('open');
    scrim.classList.remove('open');
  }
  document.getElementById('sheetClose').addEventListener('click', closeSheet);
  scrim.addEventListener('click', closeSheet);

  // ============ ELEMENTS ============
  const SHAPES = [
    { name: 'Rectangle', svg: '<rect x="6" y="12" width="32" height="20" rx="3" fill="#FF5B2E"/>' },
    { name: 'Circle',    svg: '<circle cx="22" cy="22" r="14" fill="#5B8DEF"/>' },
    { name: 'Triangle',  svg: '<path d="M22 7 L38 36 L6 36 Z" fill="#E8C547"/>' },
    { name: 'Star',      svg: '<path d="M22 5 L26.5 17 L39 17.5 L29 25.5 L32.5 38 L22 31 L11.5 38 L15 25.5 L5 17.5 L17.5 17 Z" fill="#D946EF"/>' },
    { name: 'Line',      svg: '<rect x="6" y="20" width="32" height="4" rx="2" fill="#0E0E14"/>' },
    { name: 'Arrow',     svg: '<path d="M6 22 L32 22 M24 13 L34 22 L24 31" stroke="#7AC74F" stroke-width="4" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' },
  ];
  const elGrid = document.getElementById('elementsGrid');
  SHAPES.forEach(s => {
    const btn = document.createElement('button');
    btn.className = 'tile';
    btn.setAttribute('aria-label', `Add ${s.name}`);
    btn.innerHTML = `<svg viewBox="0 0 44 44">${s.svg}</svg>`;
    btn.addEventListener('click', () => addShape(s.name));
    elGrid.appendChild(btn);
  });

  function randomColor() { return PALETTE[Math.floor(Math.random() * PALETTE.length)]; }
  function addShape(type) {
    const color = randomColor();
    const center = { left: ARTBOARD_W/2, top: ARTBOARD_H/2 };
    const common = { originX: 'center', originY: 'center', fill: color, ...center };
    let obj;
    switch(type) {
      case 'Rectangle': obj = new fabric.Rect({ ...common, width: 340, height: 240, rx: 8, ry: 8 }); break;
      case 'Circle':    obj = new fabric.Circle({ ...common, radius: 140 }); break;
      case 'Triangle':  obj = new fabric.Triangle({ ...common, width: 300, height: 280 }); break;
      case 'Star': {
        const pts = []; const spikes = 5; const outer = 160, inner = 70;
        for (let i = 0; i < spikes * 2; i++) {
          const r = i % 2 === 0 ? outer : inner;
          const a = (Math.PI / spikes) * i - Math.PI / 2;
          pts.push({ x: Math.cos(a) * r, y: Math.sin(a) * r });
        }
        obj = new fabric.Polygon(pts, { ...common });
        break;
      }
      case 'Line':
        obj = new fabric.Line([-170, 0, 170, 0], { ...common, stroke: color, strokeWidth: 12, strokeLineCap: 'round', fill: undefined });
        break;
      case 'Arrow': {
        const path = 'M 0 0 L 220 0 M 180 -34 L 220 0 L 180 34';
        obj = new fabric.Path(path, { ...common, fill: '', stroke: color, strokeWidth: 16, strokeLineCap: 'round', strokeLineJoin: 'round' });
        break;
      }
    }
    if (obj) {
      canvas.add(obj);
      canvas.setActiveObject(obj);
      canvas.renderAll();
      renderLayers();
    }
  }

  // ============ TEXT ============
  document.querySelectorAll('.text-preset').forEach(btn => {
    btn.addEventListener('click', () => {
      const size = parseInt(btn.dataset.size, 10);
      const weight = btn.dataset.weight;
      const family = btn.dataset.family === 'serif'
        ? '"Fraunces", Georgia, serif'
        : '"General Sans", -apple-system, sans-serif';
      const italic = btn.dataset.italic === 'true';
      const text = btn.textContent.trim();
      const it = new fabric.IText(text, {
        left: ARTBOARD_W/2, top: ARTBOARD_H/2,
        originX: 'center', originY: 'center',
        fontFamily: family,
        fontSize: size,
        fontWeight: weight,
        fontStyle: italic ? 'italic' : 'normal',
        fill: '#0E0E14',
        charSpacing: -20,
      });
      canvas.add(it);
      canvas.setActiveObject(it);
      it.enterEditing();
      it.selectAll();
      canvas.renderAll();
      renderLayers();
      closeSheet();
    });
  });

  // ============ PHOTOS ============
  const photosGrid = document.getElementById('photosGrid');
  const photoGradients = [
    { from: '#FF5B2E', to: '#FFB088', label: 'Ember' },
    { from: '#5B8DEF', to: '#A5C8FF', label: 'Cerulean' },
    { from: '#0E0E14', to: '#4A4A5C', label: 'Obsidian' },
    { from: '#E8C547', to: '#F4E08A', label: 'Saffron' },
    { from: '#7AC74F', to: '#B9E89B', label: 'Moss' },
    { from: '#D946EF', to: '#F0A5FA', label: 'Orchid' },
  ];
  photoGradients.forEach((p) => {
    const b = document.createElement('button');
    b.className = 'photo-tile';
    b.style.background = `linear-gradient(135deg, ${p.from} 0%, ${p.to} 100%)`;
    b.innerHTML = `<span>${p.label}</span>`;
    b.setAttribute('aria-label', `Add ${p.label}`);
    b.addEventListener('click', () => {
      const rect = new fabric.Rect({
        left: ARTBOARD_W/2, top: ARTBOARD_H/2,
        originX: 'center', originY: 'center',
        width: 500, height: 500, rx: 16, ry: 16,
      });
      rect.set('fill', new fabric.Gradient({
        type: 'linear',
        coords: { x1: 0, y1: 0, x2: 500, y2: 500 },
        colorStops: [{ offset: 0, color: p.from }, { offset: 1, color: p.to }],
      }));
      canvas.add(rect); canvas.setActiveObject(rect); canvas.renderAll(); renderLayers();
      closeSheet();
    });
    photosGrid.appendChild(b);
  });

  // ============ TEMPLATES ============
  const tplGrid = document.getElementById('templatesGrid');
  const TEMPLATES = [
    { n: 'i',   bg: '#FF5B2E', accent: '#0E0E14' },
    { n: 'ii',  bg: '#0E0E14', accent: '#E8C547' },
    { n: 'iii', bg: '#F7F5F0', accent: '#FF5B2E' },
    { n: 'iv',  bg: '#5B8DEF', accent: '#F7F5F0' },
    { n: 'v',   bg: '#E8C547', accent: '#0E0E14' },
    { n: 'vi',  bg: '#D946EF', accent: '#F7F5F0' },
  ];
  TEMPLATES.forEach(t => {
    const b = document.createElement('button');
    b.className = 'template-tile';
    b.setAttribute('aria-label', `Template ${t.n}`);
    b.innerHTML = `
      <div class="preview" style="background:${t.bg};">
        <div style="height:3px;width:60%;background:${t.accent};border-radius:2px;margin-bottom:4px;opacity:0.9;"></div>
        <div style="height:2px;width:40%;background:${t.accent};border-radius:2px;opacity:0.6;"></div>
      </div>
      <div class="num">${t.n}</div>
    `;
    b.addEventListener('click', () => applyTemplate(t));
    tplGrid.appendChild(b);
  });
  function applyTemplate(t) {
    canvas.getObjects().slice().forEach(o => { if (!o.isArtboard) canvas.remove(o); });
    artboard.set('fill', t.bg);
    const title = new fabric.IText('Composition', {
      left: ARTBOARD_W/2, top: ARTBOARD_H/2 - 60,
      originX: 'center', originY: 'center',
      fontFamily: '"Fraunces", Georgia, serif',
      fontSize: 130, fontWeight: '600', fontStyle: 'italic',
      fill: t.accent, charSpacing: -40,
    });
    const bar = new fabric.Rect({
      left: ARTBOARD_W/2, top: ARTBOARD_H/2 + 20,
      originX: 'center', originY: 'center',
      width: 80, height: 3, fill: t.accent, opacity: 0.8,
    });
    const sub = new fabric.IText('NO. ' + t.n.toUpperCase(), {
      left: ARTBOARD_W/2, top: ARTBOARD_H/2 + 70,
      originX: 'center', originY: 'center',
      fontFamily: '"General Sans", sans-serif',
      fontSize: 28, fontWeight: '500',
      fill: t.accent, charSpacing: 400, opacity: 0.7,
    });
    canvas.add(title, bar, sub);
    canvas.renderAll();
    renderLayers();
    closeSheet();
  }

  // ============ DRAW ============
  const drawToggle = document.getElementById('drawToggle');
  drawToggle.addEventListener('click', () => {
    state.drawOn = !state.drawOn;
    canvas.isDrawingMode = state.drawOn;
    if (state.drawOn) {
      canvas.freeDrawingBrush = new fabric.PencilBrush(canvas);
      canvas.freeDrawingBrush.color = state.drawColor;
      canvas.freeDrawingBrush.width = state.drawWidth;
    }
    drawToggle.querySelector('.pill').textContent = state.drawOn ? 'On' : 'Off';
    drawToggle.classList.toggle('on', state.drawOn);
  });
  const colorRow = document.getElementById('colorRow');
  const DRAW_COLORS = ['#0E0E14','#FF5B2E','#E8C547','#5B8DEF','#7AC74F','#D946EF','#FB923C','#FFFFFF'];
  DRAW_COLORS.forEach((c, idx) => {
    const sw = document.createElement('button');
    sw.className = 'color-swatch' + (idx === 1 ? ' selected' : '');
    sw.style.background = c;
    sw.setAttribute('aria-label', `Color ${c}`);
    sw.addEventListener('click', () => {
      state.drawColor = c;
      document.querySelectorAll('.color-swatch').forEach(s => s.classList.remove('selected'));
      sw.classList.add('selected');
      if (canvas.freeDrawingBrush) canvas.freeDrawingBrush.color = c;
    });
    colorRow.appendChild(sw);
  });
  const widthSlider = document.getElementById('widthSlider');
  widthSlider.addEventListener('input', (e) => {
    state.drawWidth = +e.target.value;
    document.getElementById('widthVal').textContent = state.drawWidth;
    if (canvas.freeDrawingBrush) canvas.freeDrawingBrush.width = state.drawWidth;
  });

  // ============ LAYERS ============
  function renderLayers() {
    const list = document.getElementById('layersList');
    list.innerHTML = '';
    const objs = canvas.getObjects().filter(o => !o.isArtboard).slice().reverse();
    if (!objs.length) {
      list.innerHTML = `<div class="layer-empty"><div class="mark">∅</div><div class="msg">No layers — yet</div></div>`;
      return;
    }
    const active = canvas.getActiveObject();
    objs.forEach((obj) => {
      const row = document.createElement('div');
      row.className = 'layer-row' + (obj === active ? ' active' : '');
      const drag = document.createElement('div');
      drag.className = 'drag-handle';
      drag.innerHTML = '<svg viewBox="0 0 24 24" fill="currentColor"><circle cx="9" cy="6" r="1.2"/><circle cx="9" cy="12" r="1.2"/><circle cx="9" cy="18" r="1.2"/><circle cx="15" cy="6" r="1.2"/><circle cx="15" cy="12" r="1.2"/><circle cx="15" cy="18" r="1.2"/></svg>';

      const thumb = document.createElement('div');
      thumb.className = 'layer-thumb';
      try {
        const data = obj.toDataURL({ format: 'png', multiplier: 0.1 });
        const img = document.createElement('img');
        img.src = data;
        thumb.appendChild(img);
      } catch(e) {}

      const label = document.createElement('div');
      label.className = 'layer-label';
      label.innerHTML = `${escapeHtml(labelFor(obj))}<span class="type">${typeLabel(obj)}</span>`;

      const vis = document.createElement('button');
      vis.className = 'mini-btn';
      vis.setAttribute('aria-label', 'Toggle visibility');
      vis.innerHTML = obj.visible !== false
        ? '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>'
        : '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94"/><path d="M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19"/><path d="M1 1l22 22"/></svg>';
      vis.addEventListener('click', (e) => {
        e.stopPropagation();
        obj.visible = obj.visible === false ? true : false;
        canvas.renderAll();
        renderLayers();
      });

      const del = document.createElement('button');
      del.className = 'mini-btn';
      del.setAttribute('aria-label', 'Delete layer');
      del.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/></svg>';
      del.addEventListener('click', (e) => {
        e.stopPropagation();
        canvas.remove(obj); canvas.renderAll(); renderLayers();
      });

      row.appendChild(drag);
      row.appendChild(thumb);
      row.appendChild(label);
      row.appendChild(vis);
      row.appendChild(del);
      row.addEventListener('click', () => {
        canvas.setActiveObject(obj); canvas.renderAll(); renderLayers();
      });
      list.appendChild(row);
    });
  }
  function escapeHtml(s) { return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); }
  function labelFor(obj) {
    if (obj.type === 'i-text' || obj.type === 'text' || obj.type === 'textbox') {
      return (obj.text || 'Text').substring(0, 22);
    }
    return typeLabel(obj);
  }
  function typeLabel(obj) {
    const map = { rect: 'Rectangle', circle: 'Circle', triangle: 'Triangle', polygon: 'Polygon', line: 'Line', path: 'Drawing', image: 'Image', group: 'Group', 'i-text': 'Type', text: 'Type', textbox: 'Type' };
    return map[obj.type] || obj.type;
  }

  // ============ ZOOM ============
  document.getElementById('zoomIn').addEventListener('click', () => zoomBy(1.2));
  document.getElementById('zoomOut').addEventListener('click', () => zoomBy(1/1.2));
  function zoomBy(factor) {
    const host = document.getElementById('canvasHost');
    const w = host.clientWidth, h = host.clientHeight;
    const point = new fabric.Point(w/2, h/2);
    let newZoom = state.zoom * factor;
    newZoom = Math.max(state.baseZoom * 0.5, Math.min(state.baseZoom * 5, newZoom));
    canvas.zoomToPoint(point, newZoom);
    state.zoom = newZoom;
    updateZoomLabel();
    updateFloatingToolbar();
  }

  // ============ PINCH / PAN ============
  const zone = document.getElementById('canvasZone');
  const pointers = new Map();
  let pinchStart = null;
  let panLast = null;
  let rafPending = false;
  let pendingAction = null;

  zone.addEventListener('pointerdown', (e) => {
    pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
    if (pointers.size === 2) {
      const pts = [...pointers.values()];
      pinchStart = {
        dist: Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y),
        zoom: state.zoom,
      };
      canvas.discardActiveObject();
      canvas.renderAll();
    }
  }, { passive: true });

  zone.addEventListener('pointermove', (e) => {
    if (!pointers.has(e.pointerId)) return;
    pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });

    if (pointers.size === 2 && pinchStart) {
      const pts = [...pointers.values()];
      const dist = Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);
      const factor = dist / pinchStart.dist;
      const host = document.getElementById('canvasHost');
      const hostRect = host.getBoundingClientRect();
      const centerX = (pts[0].x + pts[1].x)/2 - hostRect.left;
      const centerY = (pts[0].y + pts[1].y)/2 - hostRect.top;
      pendingAction = () => {
        let newZoom = pinchStart.zoom * factor;
        newZoom = Math.max(state.baseZoom * 0.5, Math.min(state.baseZoom * 5, newZoom));
        canvas.zoomToPoint(new fabric.Point(centerX, centerY), newZoom);
        state.zoom = newZoom;
        updateZoomLabel();
      };
      scheduleRaf();
    } else if (pointers.size === 1 && !canvas.getActiveObject() && !canvas.isDrawingMode) {
      if (!panLast) { panLast = { x: e.clientX, y: e.clientY }; return; }
      const dx = e.clientX - panLast.x;
      const dy = e.clientY - panLast.y;
      panLast = { x: e.clientX, y: e.clientY };
      pendingAction = () => { canvas.relativePan(new fabric.Point(dx, dy)); };
      scheduleRaf();
    }
  }, { passive: true });

  function endPointer(e) {
    pointers.delete(e.pointerId);
    if (pointers.size < 2) pinchStart = null;
    if (pointers.size === 0) panLast = null;
  }
  zone.addEventListener('pointerup', endPointer);
  zone.addEventListener('pointercancel', endPointer);
  zone.addEventListener('pointerleave', endPointer);

  function scheduleRaf() {
    if (rafPending) return;
    rafPending = true;
    requestAnimationFrame(() => {
      rafPending = false;
      if (pendingAction) { pendingAction(); pendingAction = null; }
    });
  }

  // ============ SEED ============
  function seed() {
    state.suppressHistory = true;
    const kicker = new fabric.IText('ISSUE Nº 01', {
      left: ARTBOARD_W/2, top: 280,
      originX: 'center', originY: 'center',
      fontFamily: '"General Sans", sans-serif',
      fontSize: 26, fontWeight: '600',
      fill: '#FF5B2E', charSpacing: 600,
    });
    const heading = new fabric.IText('Design,\nslowly.', {
      left: ARTBOARD_W/2, top: 500,
      originX: 'center', originY: 'center',
      fontFamily: '"Fraunces", Georgia, serif',
      fontSize: 180, fontWeight: '600', fontStyle: 'italic',
      fill: '#0E0E14', charSpacing: -60, textAlign: 'center', lineHeight: 0.9,
    });
    const rule = new fabric.Rect({
      left: ARTBOARD_W/2, top: 720,
      originX: 'center', originY: 'center',
      width: 60, height: 2, fill: '#0E0E14',
    });
    const sub = new fabric.IText('a canvas for considered work', {
      left: ARTBOARD_W/2, top: 780,
      originX: 'center', originY: 'center',
      fontFamily: '"Fraunces", Georgia, serif',
      fontSize: 36, fontWeight: '400', fontStyle: 'italic',
      fill: 'rgba(14,14,20,0.6)', charSpacing: 20,
    });
    const dot = new fabric.Circle({
      left: ARTBOARD_W - 200, top: 200,
      originX: 'center', originY: 'center',
      radius: 60, fill: '#FF5B2E',
    });
    canvas.add(kicker, heading, rule, sub, dot);
    canvas.renderAll();
    state.suppressHistory = false;
    snapshot();
  }

  resizeCanvas();
  seed();
  updateUndoRedo();

  document.addEventListener('keydown', (e) => {
    if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
    if ((e.metaKey || e.ctrlKey) && e.key === 'z' && !e.shiftKey) { e.preventDefault(); undo(); }
    else if ((e.metaKey || e.ctrlKey) && (e.key === 'y' || (e.key === 'z' && e.shiftKey))) { e.preventDefault(); redo(); }
    else if (e.key === 'Delete' || e.key === 'Backspace') {
      const obj = canvas.getActiveObject();
      if (obj && !obj.isArtboard && !obj.isEditing) { canvas.remove(obj); canvas.renderAll(); renderLayers(); }
    } else if (e.key === 'Escape' && sheet.classList.contains('open')) {
      closeSheet();
    }
  });
})();
</script>
</body>
</html>