{"$schema":"https://ui.artbloom.tech/schema/registry-item.json","name":"assembly","type":"registry:page","title":"Assembly","description":"A dated design-and-tech conference site: a poster-grade hero with a live countdown and persistent register CTA, a billed speaker grid, a scroll-scrubbed multi-track agenda and ticket-tier cards. Ships six art-directed poster and speaker renders; reduced-motion respected.","author":"@artbloom","dependencies":[],"registryDependencies":[],"files":[{"path":"app/assembly/page.tsx","target":"app/assembly/page.tsx","content":"\"use client\"\n\nimport { useEffect, useRef, useState } from \"react\"\n\n/**\n * Assembly — a dated-event / ticketing site for \"Assembly 2026\", a three-day\n * design + technology conference in Lisbon.\n *\n * The archetype is organized around WHEN: a poster-grade hero carries a live\n * countdown to the doors, the lineup cascades in staggered by billing, the\n * agenda is tabbed by day, tickets tier with a deadline nudge tied to that same\n * countdown, and a buy bar rides along after the fold so the path to purchase is\n * never more than a glance away.\n *\n * Deliberate against the house tells: the base is a cool ink (not a brown-tinted\n * black), the bold move is the cobalt poster and the clock — everything else is\n * quiet off-white. No tracked-uppercase eyebrows, no middle-dot meta strings, no\n * em-dash fragment labels, no arrow-suffixed links, no glassmorphism. The only\n * numbering is the clock and the schedule, both of which are genuine sequences.\n * All decorative motion is gated on prefers-reduced-motion; the countdown keeps\n * ticking because it is live data, not decoration.\n */\n\n// Fixed points in time. The countdown runs to the doors; the early-bird nudge\n// runs to its own deadline. Both are UTC so server and client agree.\nconst EVENT_START = Date.UTC(2026, 10, 12, 9, 0, 0) // 12 Nov 2026, 09:00\nconst EARLYBIRD_END = Date.UTC(2026, 9, 15, 23, 59, 59) // 15 Oct 2026\nconst DAY_MS = 86_400_000\n\nconst SPEAKERS = [\n  {\n    name: \"Amara Okafor\",\n    role: \"Principal Designer, Figma\",\n    talk: \"Designing for a billion cursors\",\n    img: \"/assembly/speaker1.png\",\n    bill: \"headliner\",\n  },\n  {\n    name: \"Rohan Mehta\",\n    role: \"VP Engineering, Vercel\",\n    talk: \"The edge is the interface\",\n    img: \"/assembly/speaker2.png\",\n    bill: \"headliner\",\n  },\n  {\n    name: \"Yuki Tanaka\",\n    role: \"Founder, Studio Kojo\",\n    talk: \"Motion as material\",\n    img: \"/assembly/speaker3.png\",\n    bill: \"feature\",\n  },\n  {\n    name: \"Anders Holm\",\n    role: \"Creative Director, Palm Type\",\n    talk: \"In praise of slow tools\",\n    img: \"/assembly/speaker4.png\",\n    bill: \"feature\",\n  },\n  {\n    name: \"Sofía Reyes\",\n    role: \"Head of Design Systems, Stripe\",\n    talk: \"Systems that bend, not break\",\n    img: \"/assembly/speaker5.png\",\n    bill: \"feature\",\n  },\n]\n\nconst VALUES = [\n  {\n    head: \"Three stages, one ticket\",\n    body: \"Main stage keynotes, a hands-on craft track, and a systems track running in parallel. Move freely; nothing is gated.\",\n  },\n  {\n    head: \"Built for the hallway\",\n    body: \"Long breaks, a real lunch, and a floor plan designed so the conversation you came for actually happens.\",\n  },\n  {\n    head: \"Work you can take home\",\n    body: \"Every talk is recorded and yours to keep. Workshop files, slides, and starter repos ship the week after.\",\n  },\n  {\n    head: \"A room worth the flight\",\n    body: \"Eighteen hundred designers and engineers who ship. The kind of peers who answer the question you have been sitting on.\",\n  },\n]\n\nconst DAYS = [\n  {\n    id: \"d1\",\n    label: \"Day one\",\n    date: \"Thu 12 Nov\",\n    theme: \"Foundations\",\n    sessions: [\n      { time: \"09:00\", title: \"Doors, coffee, and the floor opens\", track: \"All\", who: \"\" },\n      { time: \"10:00\", title: \"Designing for a billion cursors\", track: \"Main stage\", who: \"Amara Okafor\" },\n      { time: \"11:15\", title: \"Motion as material\", track: \"Craft\", who: \"Yuki Tanaka\" },\n      { time: \"11:15\", title: \"Systems that bend, not break\", track: \"Systems\", who: \"Sofía Reyes\" },\n      { time: \"13:30\", title: \"The edge is the interface\", track: \"Main stage\", who: \"Rohan Mehta\" },\n      { time: \"15:00\", title: \"Workshop: prototyping with real data\", track: \"Craft\", who: \"Studio Kojo\" },\n      { time: \"18:00\", title: \"Opening night, rooftop\", track: \"All\", who: \"\" },\n    ],\n  },\n  {\n    id: \"d2\",\n    label: \"Day two\",\n    date: \"Fri 13 Nov\",\n    theme: \"Craft\",\n    sessions: [\n      { time: \"09:30\", title: \"Morning pages, quiet room\", track: \"All\", who: \"\" },\n      { time: \"10:00\", title: \"In praise of slow tools\", track: \"Main stage\", who: \"Anders Holm\" },\n      { time: \"11:15\", title: \"Interfaces that write themselves\", track: \"Systems\", who: \"Karim Nassar\" },\n      { time: \"11:15\", title: \"Type on screens, ten years on\", track: \"Craft\", who: \"Palm Type\" },\n      { time: \"13:30\", title: \"Panel: shipping design at scale\", track: \"Main stage\", who: \"Okafor, Reyes, Mehta\" },\n      { time: \"15:00\", title: \"Workshop: motion systems in code\", track: \"Craft\", who: \"Yuki Tanaka\" },\n      { time: \"20:00\", title: \"Community dinner, the long tables\", track: \"All\", who: \"\" },\n    ],\n  },\n  {\n    id: \"d3\",\n    label: \"Day three\",\n    date: \"Sat 14 Nov\",\n    theme: \"What's next\",\n    sessions: [\n      { time: \"10:00\", title: \"Office hours with the speakers\", track: \"All\", who: \"\" },\n      { time: \"11:00\", title: \"The next interface is a conversation\", track: \"Main stage\", who: \"Karim Nassar\" },\n      { time: \"12:30\", title: \"Lightning talks from the floor\", track: \"Craft\", who: \"You, maybe\" },\n      { time: \"14:00\", title: \"Closing keynote\", track: \"Main stage\", who: \"Amara Okafor\" },\n      { time: \"15:30\", title: \"Last call, the send-off\", track: \"All\", who: \"\" },\n    ],\n  },\n]\n\nconst TICKETS = [\n  {\n    id: \"early\",\n    name: \"Early bird\",\n    price: \"€390\",\n    note: \"\",\n    features: [\"All three days, every stage\", \"Talk recordings to keep\", \"Waitlist for workshops\", \"Access to the attendee app\"],\n    cta: \"Get early bird\",\n    featured: false,\n    urgent: true,\n  },\n  {\n    id: \"standard\",\n    name: \"Standard\",\n    price: \"€590\",\n    note: \"Most people pick this\",\n    features: [\n      \"Everything in Early bird\",\n      \"Guaranteed workshop seats\",\n      \"Community dinner, Friday\",\n      \"Slides and starter repos\",\n    ],\n    cta: \"Get standard\",\n    featured: true,\n    urgent: false,\n  },\n  {\n    id: \"vip\",\n    name: \"Patron\",\n    price: \"€1,190\",\n    note: \"\",\n    features: [\n      \"Everything in Standard\",\n      \"Front two rows, reserved\",\n      \"Speaker dinner, Thursday\",\n      \"Backstage and the quiet lounge\",\n      \"A 1:1 office-hours slot\",\n    ],\n    cta: \"Get patron\",\n    featured: false,\n    urgent: false,\n  },\n]\n\nconst SPONSORS = [\n  { name: \"Figma\", mark: \"circle\" },\n  { name: \"Vercel\", mark: \"triangle\" },\n  { name: \"Linear\", mark: \"bars\" },\n  { name: \"Stripe\", mark: \"slash\" },\n  { name: \"Framer\", mark: \"square\" },\n  { name: \"Notion\", mark: \"grid\" },\n  { name: \"Raycast\", mark: \"ray\" },\n  { name: \"Supabase\", mark: \"bolt\" },\n  { name: \"Retool\", mark: \"wrench\" },\n  { name: \"Arc\", mark: \"arc\" },\n  { name: \"Amplitude\", mark: \"wave\" },\n  { name: \"Cloudflare\", mark: \"cloud\" },\n]\n\nconst FAQ = [\n  {\n    q: \"Where exactly is it held?\",\n    a: \"The Arsenal, a converted shipyard on the Lisbon waterfront in the Beato district. Full address and doors on your ticket.\",\n  },\n  {\n    q: \"Are talks recorded?\",\n    a: \"Yes. Every stage is captured and every attendee keeps the recordings, ad-free, the week after the event.\",\n  },\n  {\n    q: \"Can I switch tracks during the day?\",\n    a: \"Freely. Standard and Patron tickets guarantee workshop seats; otherwise every room is first-come, and the stages are steps apart.\",\n  },\n  {\n    q: \"What is your refund policy?\",\n    a: \"Full refund up to 30 days before doors. After that, transfer your ticket to anyone you like at no charge, right up to the morning of.\",\n  },\n  {\n    q: \"Do you help with visas or access needs?\",\n    a: \"We issue invitation letters for visa applications and the venue is step-free throughout. Reply to your ticket email and a human answers.\",\n  },\n]\n\nconst RECAP = [\n  { n: \"1,800\", l: \"in the room\" },\n  { n: \"42\", l: \"talks and workshops\" },\n  { n: \"31\", l: \"countries\" },\n  { n: \"96%\", l: \"would return\" },\n]\n\n/** Reveal a block once it scrolls into view. Reduced-motion shows it instantly. */\nfunction useReveal<T extends HTMLElement>() {\n  const ref = useRef<T>(null)\n  const [shown, setShown] = useState(false)\n  useEffect(() => {\n    const node = ref.current\n    if (!node) return\n    if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) {\n      setShown(true)\n      return\n    }\n    const io = new IntersectionObserver(\n      ([e]) => {\n        if (e.isIntersecting) {\n          setShown(true)\n          io.disconnect()\n        }\n      },\n      { threshold: 0.15 },\n    )\n    io.observe(node)\n    return () => io.disconnect()\n  }, [])\n  return [ref, shown] as const\n}\n\n/** Live time-to-target. Mounts to null so SSR and first paint agree, then ticks. */\nfunction useCountdown(target: number) {\n  const [now, setNow] = useState<number | null>(null)\n  useEffect(() => {\n    setNow(Date.now())\n    const id = setInterval(() => setNow(Date.now()), 1000)\n    return () => clearInterval(id)\n  }, [])\n  const diff = now === null ? 0 : Math.max(0, target - now)\n  return {\n    mounted: now !== null,\n    days: Math.floor(diff / DAY_MS),\n    hours: Math.floor((diff / 3_600_000) % 24),\n    minutes: Math.floor((diff / 60_000) % 60),\n    seconds: Math.floor((diff / 1000) % 60),\n  }\n}\n\nconst pad = (n: number) => String(n).padStart(2, \"0\")\n\n/** Abstract monochrome sponsor marks, drawn so the wall reads as one system. */\nfunction SponsorMark({ kind }: { kind: string }) {\n  const common = { width: 26, height: 26, viewBox: \"0 0 26 26\", fill: \"none\", \"aria-hidden\": true } as const\n  const s = { stroke: \"currentColor\", strokeWidth: 2, strokeLinecap: \"round\" as const, strokeLinejoin: \"round\" as const }\n  switch (kind) {\n    case \"circle\": return <svg {...common}><circle cx=\"13\" cy=\"13\" r=\"9\" {...s} /><circle cx=\"13\" cy=\"13\" r=\"3\" fill=\"currentColor\" /></svg>\n    case \"triangle\": return <svg {...common}><path d=\"M13 4 L22 21 L4 21 Z\" {...s} /></svg>\n    case \"bars\": return <svg {...common}><path d=\"M6 20V9M13 20V5M20 20v-7\" {...s} /></svg>\n    case \"slash\": return <svg {...common}><path d=\"M17 5 L9 21M8 8h2M16 18h2\" {...s} /></svg>\n    case \"square\": return <svg {...common}><rect x=\"5\" y=\"5\" width=\"16\" height=\"16\" rx=\"3\" {...s} /><path d=\"M5 13h16\" {...s} /></svg>\n    case \"grid\": return <svg {...common}><rect x=\"5\" y=\"5\" width=\"7\" height=\"7\" rx=\"1.5\" {...s} /><rect x=\"14\" y=\"5\" width=\"7\" height=\"7\" rx=\"1.5\" {...s} /><rect x=\"5\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" {...s} /><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" fill=\"currentColor\" /></svg>\n    case \"ray\": return <svg {...common}><path d=\"M13 5v3M13 18v3M5 13h3M18 13h3M7.3 7.3l2 2M16.7 16.7l-2-2\" {...s} /><circle cx=\"13\" cy=\"13\" r=\"2.5\" fill=\"currentColor\" /></svg>\n    case \"bolt\": return <svg {...common}><path d=\"M14 4 L7 14h5l-1 8 7-11h-5z\" {...s} /></svg>\n    case \"wrench\": return <svg {...common}><path d=\"M18 6a4 4 0 0 1-5 5l-6 6-2-2 6-6a4 4 0 0 1 5-5l-2 2 1 2 2 1z\" {...s} /></svg>\n    case \"arc\": return <svg {...common}><path d=\"M5 19a8 8 0 0 1 16 0\" {...s} /><circle cx=\"13\" cy=\"19\" r=\"1.6\" fill=\"currentColor\" /></svg>\n    case \"wave\": return <svg {...common}><path d=\"M4 13c3-6 6 6 9 0s6-6 9 0\" {...s} /></svg>\n    case \"cloud\": return <svg {...common}><path d=\"M8 18a4 4 0 0 1 0-8 5 5 0 0 1 9.5 1.5A3.5 3.5 0 0 1 17 18z\" {...s} /></svg>\n    default: return <svg {...common}><circle cx=\"13\" cy=\"13\" r=\"8\" {...s} /></svg>\n  }\n}\n\n/** A stylised map of the venue's corner of Lisbon — river, transit, hotels, doors. */\nfunction VenueMap() {\n  return (\n    <svg viewBox=\"0 0 400 320\" role=\"img\" aria-label=\"Stylised map showing the venue on the Lisbon waterfront with nearby transit and hotels\" className=\"asm-map-svg\">\n      <rect x=\"0\" y=\"0\" width=\"400\" height=\"320\" rx=\"18\" fill=\"#12151f\" />\n      {/* river */}\n      <path d=\"M0 250 C120 232 210 300 400 262 L400 320 L0 320 Z\" fill=\"#1c2ee0\" opacity=\"0.9\" />\n      <path d=\"M0 250 C120 232 210 300 400 262\" stroke=\"#4553ff\" strokeWidth=\"2\" fill=\"none\" opacity=\"0.7\" />\n      {/* blocks */}\n      <g fill=\"#1a1e2b\">\n        <rect x=\"28\" y=\"40\" width=\"70\" height=\"52\" rx=\"6\" />\n        <rect x=\"112\" y=\"34\" width=\"86\" height=\"60\" rx=\"6\" />\n        <rect x=\"214\" y=\"46\" width=\"64\" height=\"48\" rx=\"6\" />\n        <rect x=\"296\" y=\"38\" width=\"78\" height=\"58\" rx=\"6\" />\n        <rect x=\"40\" y=\"112\" width=\"80\" height=\"60\" rx=\"6\" />\n        <rect x=\"250\" y=\"118\" width=\"96\" height=\"56\" rx=\"6\" />\n      </g>\n      {/* transit line */}\n      <path d=\"M20 100 L180 150 L360 120\" stroke=\"#ff6a2b\" strokeWidth=\"2.5\" strokeDasharray=\"2 7\" strokeLinecap=\"round\" fill=\"none\" />\n      {/* venue pin */}\n      <g transform=\"translate(178 150)\">\n        <circle r=\"22\" fill=\"#2b39ff\" opacity=\"0.22\" />\n        <circle r=\"9\" fill=\"#2b39ff\" />\n        <circle r=\"3.4\" fill=\"#fff\" />\n      </g>\n      {/* hotel dots */}\n      <g fill=\"#f4f3ee\">\n        <circle cx=\"70\" cy=\"130\" r=\"4\" /><circle cx=\"300\" cy=\"140\" r=\"4\" /><circle cx=\"150\" cy=\"70\" r=\"4\" />\n      </g>\n      <text x=\"196\" y=\"146\" fill=\"#f6f6f4\" fontSize=\"13\" fontWeight=\"600\" fontFamily=\"var(--font-geist), sans-serif\">The Arsenal</text>\n    </svg>\n  )\n}\n\nexport default function Assembly() {\n  const [scrollY, setScrollY] = useState(0)\n  const [reduced, setReduced] = useState(false)\n  const clock = useCountdown(EVENT_START)\n\n  // Days until early-bird pricing ends, for the ticket nudge.\n  const earlybirdDays = clock.mounted ? Math.max(0, Math.ceil((EARLYBIRD_END - Date.now()) / DAY_MS)) : null\n\n  useEffect(() => {\n    const mq = window.matchMedia(\"(prefers-reduced-motion: reduce)\")\n    setReduced(mq.matches)\n    const onMq = () => setReduced(mq.matches)\n    mq.addEventListener(\"change\", onMq)\n\n    let raf = 0\n    const onScroll = () => {\n      if (raf) return\n      raf = requestAnimationFrame(() => {\n        setScrollY(window.scrollY)\n        raf = 0\n      })\n    }\n    window.addEventListener(\"scroll\", onScroll, { passive: true })\n    onScroll()\n    return () => {\n      mq.removeEventListener(\"change\", onMq)\n      window.removeEventListener(\"scroll\", onScroll)\n      if (raf) cancelAnimationFrame(raf)\n    }\n  }, [])\n\n  const par = (rate: number) => (reduced ? 0 : scrollY * rate)\n  const barShown = scrollY > 640\n\n  const [valuesRef, valuesShown] = useReveal<HTMLDivElement>()\n  const [lineupRef, lineupShown] = useReveal<HTMLDivElement>()\n  const [agendaRef, agendaShown] = useReveal<HTMLDivElement>()\n  const [ticketsRef, ticketsShown] = useReveal<HTMLDivElement>()\n  const [venueRef, venueShown] = useReveal<HTMLDivElement>()\n  const [recapRef, recapShown] = useReveal<HTMLDivElement>()\n  const [activeDay, setActiveDay] = useState(0)\n  const [openFaq, setOpenFaq] = useState<number | null>(0)\n\n  const units = [\n    { v: clock.days, l: \"days\" },\n    { v: clock.hours, l: \"hrs\" },\n    { v: clock.minutes, l: \"min\" },\n    { v: clock.seconds, l: \"sec\" },\n  ]\n\n  return (\n    <div className=\"asm-root\">\n      <style>{css}</style>\n\n      <header className=\"asm-nav\">\n        <a className=\"asm-mark\" href=\"#top\">\n          <span className=\"asm-mark-dot\" aria-hidden=\"true\" />\n          Assembly\n        </a>\n        <nav className=\"asm-nav-links\">\n          <a href=\"#lineup\">Lineup</a>\n          <a href=\"#agenda\">Agenda</a>\n          <a href=\"#tickets\">Tickets</a>\n          <a href=\"#venue\">Venue</a>\n          <a className=\"asm-nav-cta\" href=\"#tickets\">\n            Get tickets\n          </a>\n        </nav>\n      </header>\n\n      {/* HERO — poster texture, big wordmark, live countdown, path to purchase */}\n      <section className=\"asm-hero\" id=\"top\">\n        <div\n          className=\"asm-hero-img\"\n          style={{ transform: `translate3d(0, ${par(0.18)}px, 0) scale(1.06)` }}\n        />\n        <div className=\"asm-hero-scrim\" />\n        <div className=\"asm-hero-glow\" data-reduced={reduced} />\n        <div className=\"asm-hero-inner\">\n          <p className=\"asm-hero-kicker\">The design and technology conference</p>\n          <h1 className=\"asm-hero-title\">\n            Assembly<span className=\"asm-hero-year\">2026</span>\n          </h1>\n          <div className=\"asm-hero-meta\">\n            <span>12&ndash;14 November 2026</span>\n            <span className=\"asm-rule\" aria-hidden=\"true\" />\n            <span>Lisbon, Portugal</span>\n          </div>\n          <p className=\"asm-hero-lede\">\n            Three days, three stages, and eighteen hundred people who make the things you use. The\n            doors open in:\n          </p>\n\n          <div className=\"asm-clock\" role=\"timer\" aria-label=\"Time until doors open\">\n            {units.map((u) => (\n              <div className=\"asm-clock-unit\" key={u.l}>\n                <span className=\"asm-clock-num\">{clock.mounted ? pad(u.v) : \"––\"}</span>\n                <span className=\"asm-clock-lab\">{u.l}</span>\n              </div>\n            ))}\n          </div>\n\n          <div className=\"asm-hero-cta\">\n            <a className=\"asm-btn asm-btn-lg\" href=\"#tickets\">\n              Get tickets\n            </a>\n            <a className=\"asm-btn-ghost asm-btn-lg\" href=\"#lineup\">\n              See the lineup\n            </a>\n          </div>\n        </div>\n        <div className=\"asm-scrollcue\" aria-hidden=\"true\">\n          <span />\n        </div>\n      </section>\n\n      {/* WHY ATTEND */}\n      <section className=\"asm-section asm-why\">\n        <div className=\"asm-why-head\">\n          <p className=\"asm-eyebrow\">Why come to Lisbon</p>\n          <h2 className=\"asm-h2\">A conference that respects your three days</h2>\n        </div>\n        <div className={`asm-why-grid ${valuesShown ? \"is-shown\" : \"\"}`} ref={valuesRef}>\n          {VALUES.map((v, i) => (\n            <div className=\"asm-why-card\" key={v.head} style={{ transitionDelay: `${i * 90}ms` }}>\n              <span className=\"asm-why-index\" aria-hidden=\"true\" />\n              <h3>{v.head}</h3>\n              <p>{v.body}</p>\n            </div>\n          ))}\n        </div>\n      </section>\n\n      {/* LINEUP — kinetic staggered reveal, headliners largest */}\n      <section className=\"asm-section asm-lineup\" id=\"lineup\">\n        <div className=\"asm-lineup-head\">\n          <p className=\"asm-eyebrow\">The lineup</p>\n          <h2 className=\"asm-h2\">Forty-two talks. Here are the ones people fly in for.</h2>\n          <p className=\"asm-sub\">\n            A first look at the 2026 program. The full schedule, lightning talks, and workshop\n            leads land closer to the doors.\n          </p>\n        </div>\n        <div className={`asm-speakers ${lineupShown ? \"is-shown\" : \"\"}`} ref={lineupRef}>\n          {SPEAKERS.map((s, i) => (\n            <article\n              className={`asm-speaker ${s.bill === \"headliner\" ? \"is-headliner\" : \"\"}`}\n              key={s.name}\n              style={{ transitionDelay: `${i * 110}ms` }}\n            >\n              <div className=\"asm-speaker-img\" style={{ backgroundImage: `url(${s.img})` }}>\n                <span className=\"asm-speaker-bill\">{s.bill === \"headliner\" ? \"Keynote\" : \"Speaking\"}</span>\n              </div>\n              <div className=\"asm-speaker-body\">\n                <h3 className=\"asm-speaker-name\">{s.name}</h3>\n                <p className=\"asm-speaker-role\">{s.role}</p>\n                <p className=\"asm-speaker-talk\">{s.talk}</p>\n              </div>\n            </article>\n          ))}\n        </div>\n      </section>\n\n\n      {/* AGENDA — tabbed by day, times as a real sequence */}\n      <section className=\"asm-section asm-agenda\" id=\"agenda\" ref={agendaRef}>\n        <div className=\"asm-agenda-head\">\n          <p className=\"asm-eyebrow\">Three days</p>\n          <h2 className=\"asm-h2\">The shape of the week</h2>\n        </div>\n        <div className=\"asm-tabs\" role=\"tablist\" aria-label=\"Choose a day\">\n          {DAYS.map((d, i) => (\n            <button\n              key={d.id}\n              role=\"tab\"\n              aria-selected={activeDay === i}\n              className={`asm-tab ${activeDay === i ? \"is-active\" : \"\"}`}\n              onClick={() => setActiveDay(i)}\n            >\n              <span className=\"asm-tab-label\">{d.label}</span>\n              <span className=\"asm-tab-date\">{d.date}</span>\n            </button>\n          ))}\n        </div>\n        <div className={`asm-schedule ${agendaShown ? \"is-shown\" : \"\"}`} key={DAYS[activeDay].id}>\n          <div className=\"asm-schedule-theme\">\n            <span className=\"asm-schedule-daynum\">{DAYS[activeDay].date}</span>\n            <span className=\"asm-schedule-word\">{DAYS[activeDay].theme}</span>\n          </div>\n          <ol className=\"asm-schedule-list\">\n            {DAYS[activeDay].sessions.map((s, i) => (\n              <li className=\"asm-slot\" key={`${s.time}-${s.title}`} style={{ animationDelay: `${i * 55}ms` }}>\n                <span className=\"asm-slot-time\">{s.time}</span>\n                <div className=\"asm-slot-body\">\n                  <p className=\"asm-slot-title\">{s.title}</p>\n                  {s.who && <p className=\"asm-slot-who\">{s.who}</p>}\n                </div>\n                <span className={`asm-slot-track track-${s.track.replace(/\\\\s+/g, \"\").toLowerCase()}`}>{s.track}</span>\n              </li>\n            ))}\n          </ol>\n        </div>\n      </section>\n\n      {/* TICKETS — tiers, one highlighted, deadline nudge tied to the clock */}\n      <section className=\"asm-section asm-tickets\" id=\"tickets\" ref={ticketsRef}>\n        <div className=\"asm-tickets-head\">\n          <p className=\"asm-eyebrow\">Tickets</p>\n          <h2 className=\"asm-h2\">One pass, all three days</h2>\n          {earlybirdDays !== null && earlybirdDays > 0 && (\n            <p className=\"asm-nudge\">\n              <span className=\"asm-nudge-dot\" aria-hidden=\"true\" />\n              Early-bird pricing ends in {earlybirdDays} {earlybirdDays === 1 ? \"day\" : \"days\"}. Prices\n              rise after that.\n            </p>\n          )}\n        </div>\n        <div className={`asm-tier-grid ${ticketsShown ? \"is-shown\" : \"\"}`}>\n          {TICKETS.map((t, i) => (\n            <article\n              className={`asm-tier ${t.featured ? \"is-featured\" : \"\"}`}\n              key={t.id}\n              style={{ transitionDelay: `${i * 90}ms` }}\n            >\n              {t.note && <span className=\"asm-tier-note\">{t.note}</span>}\n              <h3 className=\"asm-tier-name\">{t.name}</h3>\n              <p className=\"asm-tier-price\">\n                {t.price}\n                {t.urgent && earlybirdDays !== null && earlybirdDays > 0 && (\n                  <span className=\"asm-tier-urgent\">for {earlybirdDays} more days</span>\n                )}\n              </p>\n              <ul className=\"asm-tier-feats\">\n                {t.features.map((f) => (\n                  <li key={f}>\n                    <span className=\"asm-check\" aria-hidden=\"true\" />\n                    {f}\n                  </li>\n                ))}\n              </ul>\n              <a className={t.featured ? \"asm-btn asm-tier-cta\" : \"asm-btn-line asm-tier-cta\"} href=\"#tickets\">\n                {t.cta}\n              </a>\n            </article>\n          ))}\n        </div>\n      </section>\n\n      {/* SPONSORS — CSS/SVG logo wall */}\n      <section className=\"asm-section asm-sponsors\">\n        <div className=\"asm-sponsors-head\">\n          <p className=\"asm-eyebrow\">Backed by the teams you use</p>\n          <h2 className=\"asm-h2\">Partners for 2026</h2>\n        </div>\n        <div className=\"asm-logo-wall\">\n          {SPONSORS.map((s) => (\n            <div className=\"asm-logo\" key={s.name}>\n              <SponsorMark kind={s.mark} />\n              <span>{s.name}</span>\n            </div>\n          ))}\n        </div>\n      </section>\n\n      {/* VENUE + TRAVEL — stylised SVG map */}\n      <section className=\"asm-section asm-venue\" id=\"venue\" ref={venueRef}>\n        <div className={`asm-venue-grid ${venueShown ? \"is-shown\" : \"\"}`}>\n          <div className=\"asm-venue-copy\">\n            <p className=\"asm-eyebrow\">Venue and travel</p>\n            <h2 className=\"asm-h2\">The Arsenal, on the Lisbon waterfront</h2>\n            <p className=\"asm-sub\">\n              A converted shipyard in Beato, ten minutes east of the centre by tram or river taxi.\n              Step-free throughout, with the river on one side and the roof terrace on the other.\n            </p>\n            <div className=\"asm-venue-facts\">\n              <div className=\"asm-fact\">\n                <h4>Getting there</h4>\n                <p>Lisbon Airport sits 15 minutes away by cab. The 728 bus and the 25E tram both stop at the door.</p>\n              </div>\n              <div className=\"asm-fact\">\n                <h4>Where to stay</h4>\n                <p>We hold room blocks at three hotels in Beato and Alfama. Codes arrive with your ticket.</p>\n              </div>\n              <div className=\"asm-fact\">\n                <h4>Around the venue</h4>\n                <p>The old bakery quarter next door is full of late tables. We publish a map of our favourites.</p>\n              </div>\n            </div>\n          </div>\n          <div className=\"asm-map\">\n            <VenueMap />\n          </div>\n        </div>\n      </section>\n\n      {/* RECAP — last year in numbers + texture strip */}\n      <section className=\"asm-section asm-recap\" ref={recapRef}>\n        <div className=\"asm-recap-head\">\n          <p className=\"asm-eyebrow\">Assembly 2025</p>\n          <h2 className=\"asm-h2\">How last year went</h2>\n        </div>\n        <div className={`asm-recap-grid ${recapShown ? \"is-shown\" : \"\"}`}>\n          {RECAP.map((r, i) => (\n            <div className=\"asm-stat\" key={r.l} style={{ transitionDelay: `${i * 80}ms` }}>\n              <span className=\"asm-stat-num\">{r.n}</span>\n              <span className=\"asm-stat-lab\">{r.l}</span>\n            </div>\n          ))}\n        </div>\n        <div className=\"asm-recap-strip\" aria-hidden=\"true\">\n          <span className=\"asm-recap-tile t1\" />\n          <span className=\"asm-recap-tile t2\" />\n          <span className=\"asm-recap-tile t3\" />\n          <span className=\"asm-recap-tile t4\" />\n        </div>\n      </section>\n\n      {/* FAQ + FINAL CTA */}\n      <section className=\"asm-section asm-faq\" id=\"faq\">\n        <div className=\"asm-faq-grid\">\n          <div className=\"asm-faq-head\">\n            <p className=\"asm-eyebrow\">Good to know</p>\n            <h2 className=\"asm-h2\">Questions, answered</h2>\n            <p className=\"asm-sub\">Anything else, reply to your ticket email and a person writes back.</p>\n          </div>\n          <div className=\"asm-faq-list\">\n            {FAQ.map((f, i) => (\n              <div className={`asm-faq-item ${openFaq === i ? \"is-open\" : \"\"}`} key={f.q}>\n                <button\n                  className=\"asm-faq-q\"\n                  aria-expanded={openFaq === i}\n                  onClick={() => setOpenFaq(openFaq === i ? null : i)}\n                >\n                  <span>{f.q}</span>\n                  <span className=\"asm-faq-icon\" aria-hidden=\"true\" />\n                </button>\n                <div className=\"asm-faq-a\">\n                  <p>{f.a}</p>\n                </div>\n              </div>\n            ))}\n          </div>\n        </div>\n        <div className=\"asm-final\">\n          <div className=\"asm-final-clock\" aria-hidden=\"true\">\n            {units.map((u) => (\n              <div className=\"asm-final-unit\" key={u.l}>\n                <span className=\"asm-final-num\">{clock.mounted ? pad(u.v) : \"––\"}</span>\n                <span className=\"asm-final-lab\">{u.l}</span>\n              </div>\n            ))}\n          </div>\n          <h2 className=\"asm-final-title\">The room fills up. Get your pass.</h2>\n          <p className=\"asm-final-lede\">\n            12&ndash;14 November 2026, Lisbon. Early-bird pricing while it lasts.\n          </p>\n          <a className=\"asm-btn asm-btn-lg\" href=\"#tickets\">\n            Get tickets\n          </a>\n        </div>\n      </section>\n\n      {/* PERSISTENT BUY BAR — rides along once the hero is behind you */}\n      <div className={`asm-bar ${barShown ? \"is-shown\" : \"\"}`} aria-hidden={!barShown}>\n        <div className=\"asm-bar-left\">\n          <span className=\"asm-bar-title\">Assembly 2026</span>\n          <span className=\"asm-bar-sub\">\n            {clock.mounted ? `${clock.days} days to doors` : \"Lisbon, November\"}\n          </span>\n        </div>\n        <a className=\"asm-btn\" href=\"#tickets\" tabIndex={barShown ? 0 : -1}>\n          Get tickets\n        </a>\n      </div>\n\n      <footer className=\"asm-footer\">\n        <div className=\"asm-footer-top\">\n          <a className=\"asm-mark\" href=\"#top\">\n            <span className=\"asm-mark-dot\" aria-hidden=\"true\" />\n            Assembly\n          </a>\n          <div className=\"asm-footer-cols\">\n            <div>\n              <h4>Event</h4>\n              <a href=\"#lineup\">Lineup</a>\n              <a href=\"#agenda\">Agenda</a>\n              <a href=\"#venue\">Venue and travel</a>\n            </div>\n            <div>\n              <h4>Attend</h4>\n              <a href=\"#tickets\">Tickets</a>\n              <a href=\"#faq\">FAQ</a>\n              <a href=\"#tickets\">Group rates</a>\n            </div>\n            <div>\n              <h4>Stay close</h4>\n              <a href=\"#top\">Newsletter</a>\n              <a href=\"#top\">Speaking</a>\n              <a href=\"#top\">Sponsor</a>\n            </div>\n          </div>\n        </div>\n        <div className=\"asm-footer-fine\">\n          <p>Assembly 2026. The Arsenal, Beato, Lisbon. 12&ndash;14 November.</p>\n          <p>A three-day conference for designers and engineers.</p>\n        </div>\n      </footer>\n    </div>\n  )\n}\n\nconst css = `\n.asm-root {\n  --ink: #0b0d16;\n  --ink-2: #12151f;\n  --paper: #f4f3ee;\n  --paper-2: #eceae2;\n  --cobalt: #2b39ff;\n  --cobalt-lift: #4553ff;\n  --orange: #ff6a2b;\n  --text: #14161d;\n  --muted: #5b5f6b;\n  --frost: #f6f6f4;\n  --line: rgba(20, 22, 29, 0.12);\n  --line-dark: rgba(246, 246, 244, 0.14);\n  background: var(--paper);\n  color: var(--text);\n  font-family: var(--font-geist), system-ui, sans-serif;\n  -webkit-font-smoothing: antialiased;\n  line-height: 1.6;\n  overflow-x: hidden;\n}\n.asm-root *,\n.asm-root *::before,\n.asm-root *::after { box-sizing: border-box; }\n.asm-root ::selection { background: var(--cobalt); color: #fff; }\n\n/* MARK */\n.asm-mark {\n  display: inline-flex; align-items: center; gap: 0.55rem;\n  font-weight: 600; font-size: 1.25rem; letter-spacing: -0.02em;\n  color: inherit; text-decoration: none;\n}\n.asm-mark-dot {\n  width: 0.7rem; height: 0.7rem; border-radius: 999px;\n  background: var(--cobalt);\n  box-shadow: 0 0 0 3px rgba(43, 57, 255, 0.18);\n}\n\n/* NAV */\n.asm-nav {\n  position: fixed; top: 0; left: 0; right: 0; z-index: 60;\n  display: flex; align-items: center; justify-content: space-between;\n  padding: 1.15rem clamp(1.1rem, 4vw, 3.25rem);\n  color: var(--frost);\n  background: linear-gradient(to bottom, rgba(11, 13, 22, 0.65), transparent);\n}\n.asm-nav .asm-mark { color: var(--frost); }\n.asm-nav-links { display: flex; align-items: center; gap: clamp(0.9rem, 2.2vw, 2rem); }\n.asm-nav-links a { color: var(--frost); text-decoration: none; font-size: 0.95rem; opacity: 0.82; transition: opacity 0.25s; }\n.asm-nav-links a:hover { opacity: 1; }\n.asm-nav-cta {\n  border: 1px solid rgba(246, 246, 244, 0.4); border-radius: 999px;\n  padding: 0.45rem 1.05rem; opacity: 1 !important;\n}\n.asm-nav-cta:hover { background: var(--frost); color: var(--ink) !important; }\n@media (max-width: 760px) { .asm-nav-links a:not(.asm-nav-cta) { display: none; } }\n\n/* BUTTONS */\n.asm-btn {\n  display: inline-flex; align-items: center; justify-content: center;\n  background: var(--cobalt); color: #fff; font-weight: 600; font-size: 0.98rem;\n  text-decoration: none; padding: 0.7rem 1.4rem; border-radius: 999px; border: 0;\n  cursor: pointer;\n  transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.35s, background 0.25s;\n}\n.asm-btn:hover { background: var(--cobalt-lift); transform: translateY(-2px); box-shadow: 0 16px 34px -14px rgba(43, 57, 255, 0.75); }\n.asm-btn-lg { padding: 0.95rem 1.9rem; font-size: 1.05rem; }\n.asm-btn-ghost {\n  display: inline-flex; align-items: center; justify-content: center;\n  color: var(--frost); font-weight: 600; text-decoration: none;\n  padding: 0.95rem 1.7rem; border-radius: 999px; border: 1px solid rgba(246, 246, 244, 0.4);\n  transition: background 0.25s, color 0.25s, border-color 0.25s;\n}\n.asm-btn-ghost:hover { background: var(--frost); color: var(--ink); border-color: var(--frost); }\n\n/* HERO */\n.asm-hero { position: relative; min-height: 100svh; overflow: hidden; display: grid; align-items: center;\n  background: var(--ink); }\n.asm-hero-img {\n  position: absolute; inset: -4% 0;\n  background: url(/assembly/poster.png) center/cover no-repeat;\n  will-change: transform;\n}\n.asm-hero-scrim {\n  position: absolute; inset: 0;\n  background:\n    linear-gradient(105deg, rgba(11, 13, 22, 0.92) 8%, rgba(11, 13, 22, 0.5) 46%, rgba(11, 13, 22, 0.22) 100%),\n    linear-gradient(to top, var(--ink) 1%, transparent 32%);\n}\n.asm-hero-glow {\n  position: absolute; inset: 0; pointer-events: none; mix-blend-mode: screen;\n  background: radial-gradient(42% 55% at 78% 30%, rgba(43, 57, 255, 0.55), transparent 70%);\n  animation: asm-pulse 9s ease-in-out infinite;\n}\n.asm-hero-glow[data-reduced=\"true\"] { animation: none; }\n@keyframes asm-pulse { 0%, 100% { opacity: 0.55; transform: scale(1); } 50% { opacity: 0.95; transform: scale(1.08); } }\n.asm-hero-inner {\n  position: relative; z-index: 2; color: var(--frost);\n  padding: 7rem clamp(1.25rem, 6vw, 6rem) 5rem; max-width: 60rem;\n}\n.asm-hero-kicker { color: #cdd2ff; font-size: 1rem; margin: 0 0 1.4rem; font-weight: 500; }\n.asm-hero-title {\n  display: flex; align-items: flex-start; gap: clamp(0.6rem, 1.6vw, 1.4rem);\n  font-weight: 640; font-size: clamp(3.6rem, 14vw, 11rem); line-height: 0.86;\n  letter-spacing: -0.045em; margin: 0;\n}\n.asm-hero-year {\n  font-size: clamp(1.1rem, 3vw, 2.1rem); font-weight: 600; letter-spacing: -0.01em;\n  color: var(--orange); margin-top: 0.6em; font-variant-numeric: tabular-nums;\n}\n.asm-hero-meta {\n  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;\n  margin: 1.6rem 0 1.4rem; font-size: clamp(1.05rem, 1.8vw, 1.4rem); font-weight: 500;\n}\n.asm-rule { width: 1px; height: 1.25em; background: rgba(246, 246, 244, 0.4); display: inline-block; }\n.asm-hero-lede { color: rgba(246, 246, 244, 0.86); font-size: 1.12rem; max-width: 34rem; margin: 0 0 1.8rem; }\n\n/* CLOCK */\n.asm-clock { display: flex; gap: clamp(0.6rem, 2vw, 1.4rem); margin: 0 0 2.2rem; }\n.asm-clock-unit {\n  display: grid; gap: 0.3rem; justify-items: center;\n  min-width: clamp(4.2rem, 12vw, 6.5rem);\n  padding: 0.9rem 0.6rem; border-radius: 14px;\n  background: rgba(246, 246, 244, 0.06); border: 1px solid rgba(246, 246, 244, 0.12);\n}\n.asm-clock-num {\n  font-size: clamp(2rem, 6vw, 3.6rem); font-weight: 640; line-height: 1;\n  letter-spacing: -0.03em; font-variant-numeric: tabular-nums; color: var(--frost);\n}\n.asm-clock-lab { font-size: 0.82rem; color: rgba(246, 246, 244, 0.66); }\n.asm-hero-cta { display: flex; flex-wrap: wrap; gap: 0.9rem; }\n.asm-scrollcue { position: absolute; left: 50%; bottom: 1.4rem; transform: translateX(-50%); z-index: 2; }\n.asm-scrollcue span {\n  display: block; width: 1px; height: 42px;\n  background: linear-gradient(to bottom, transparent, var(--frost));\n  animation: asm-cue 2.4s ease-in-out infinite;\n}\n@keyframes asm-cue { 0%, 100% { opacity: 0.2; transform: scaleY(0.6); transform-origin: top; } 50% { opacity: 1; transform: scaleY(1); } }\n\n/* SECTION SHELL */\n.asm-section { padding: clamp(4.5rem, 9vw, 8rem) clamp(1.25rem, 6vw, 6rem); }\n.asm-eyebrow { color: var(--cobalt); font-weight: 600; font-size: 1rem; margin: 0 0 0.9rem; }\n.asm-h2 {\n  font-weight: 620; font-size: clamp(2.1rem, 5.2vw, 4rem); line-height: 1.0;\n  letter-spacing: -0.035em; margin: 0 0 1rem; max-width: 20ch;\n}\n.asm-sub { color: var(--muted); font-size: 1.12rem; max-width: 42rem; margin: 0; }\n\n/* PERSISTENT BUY BAR */\n.asm-bar {\n  position: fixed; left: 0; right: 0; bottom: 0; z-index: 70;\n  display: flex; align-items: center; justify-content: space-between; gap: 1rem;\n  padding: 0.85rem clamp(1rem, 5vw, 3rem);\n  background: var(--ink); color: var(--frost);\n  border-top: 1px solid var(--line-dark);\n  transform: translateY(110%); transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);\n}\n.asm-bar.is-shown { transform: translateY(0); }\n.asm-bar-left { display: flex; flex-direction: column; line-height: 1.25; }\n.asm-bar-title { font-weight: 600; font-size: 1.05rem; letter-spacing: -0.01em; }\n.asm-bar-sub { color: rgba(246, 246, 244, 0.66); font-size: 0.88rem; }\n\n/* FOOTER */\n.asm-footer { background: var(--ink); color: var(--frost); padding: clamp(3.5rem, 7vw, 5.5rem) clamp(1.25rem, 6vw, 6rem) 6.5rem; }\n.asm-footer-top { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 2.5rem; }\n.asm-footer .asm-mark { color: var(--frost); }\n.asm-footer-cols { display: flex; flex-wrap: wrap; gap: clamp(2rem, 6vw, 5rem); }\n.asm-footer-cols h4 { font-size: 0.95rem; font-weight: 600; margin: 0 0 0.9rem; color: var(--frost); }\n.asm-footer-cols a { display: block; color: rgba(246, 246, 244, 0.68); text-decoration: none; font-size: 0.95rem; margin: 0.4rem 0; transition: color 0.2s; }\n.asm-footer-cols a:hover { color: var(--frost); }\n.asm-footer-fine { margin-top: 3rem; padding-top: 1.6rem; border-top: 1px solid var(--line-dark); }\n.asm-footer-fine p { color: rgba(246, 246, 244, 0.55); font-size: 0.9rem; margin: 0.25rem 0; }\n\n/* WHY ATTEND */\n.asm-why { background: var(--paper); }\n.asm-why-head { max-width: 44rem; margin-bottom: clamp(2.5rem, 5vw, 4rem); }\n.asm-why-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem; }\n@media (max-width: 980px) { .asm-why-grid { grid-template-columns: repeat(2, 1fr); } }\n@media (max-width: 560px) { .asm-why-grid { grid-template-columns: 1fr; } }\n.asm-why-card {\n  padding: 1.9rem 1.6rem; border-radius: 16px; background: var(--frost);\n  border: 1px solid var(--line);\n  opacity: 0; transform: translateY(22px);\n  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.3s;\n}\n.asm-why-grid.is-shown .asm-why-card { opacity: 1; transform: none; }\n.asm-why-card:hover { border-color: rgba(43, 57, 255, 0.4); }\n.asm-why-index { display: block; width: 2.1rem; height: 3px; border-radius: 999px; background: var(--cobalt); margin-bottom: 1.3rem; }\n.asm-why-card:nth-child(2) .asm-why-index { background: var(--orange); }\n.asm-why-card h3 { font-size: 1.28rem; font-weight: 600; letter-spacing: -0.02em; margin: 0 0 0.6rem; }\n.asm-why-card p { color: var(--muted); font-size: 0.98rem; margin: 0; }\n\n/* LINEUP */\n.asm-lineup { background: var(--paper-2); }\n.asm-lineup-head { max-width: 48rem; margin-bottom: clamp(2.5rem, 5vw, 4rem); }\n.asm-speakers { display: grid; grid-template-columns: repeat(6, 1fr); gap: 1.4rem; }\n@media (max-width: 900px) { .asm-speakers { grid-template-columns: repeat(2, 1fr); } }\n@media (max-width: 480px) { .asm-speakers { grid-template-columns: 1fr; } }\n.asm-speaker {\n  grid-column: span 2; background: var(--frost); border: 1px solid var(--line);\n  border-radius: 16px; overflow: hidden;\n  opacity: 0; transform: translateY(30px) scale(0.98);\n  transition: opacity 0.75s cubic-bezier(0.16, 1, 0.3, 1), transform 0.75s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.35s;\n}\n.asm-speakers.is-shown .asm-speaker { opacity: 1; transform: none; }\n.asm-speaker.is-headliner { grid-column: span 3; }\n@media (max-width: 900px) {\n  .asm-speaker, .asm-speaker.is-headliner { grid-column: span 1; }\n}\n.asm-speaker:hover { box-shadow: 0 22px 48px -26px rgba(11, 13, 22, 0.45); }\n.asm-speaker-img {\n  position: relative; aspect-ratio: 5 / 4; background-size: cover; background-position: center top;\n}\n.asm-speaker.is-headliner .asm-speaker-img { aspect-ratio: 16 / 11; }\n.asm-speaker-bill {\n  position: absolute; left: 0.9rem; top: 0.9rem;\n  background: var(--ink); color: var(--frost); font-size: 0.78rem; font-weight: 500;\n  padding: 0.28rem 0.7rem; border-radius: 999px;\n}\n.asm-speaker.is-headliner .asm-speaker-bill { background: var(--cobalt); }\n.asm-speaker-body { padding: 1.25rem 1.35rem 1.5rem; }\n.asm-speaker-name { font-size: 1.3rem; font-weight: 600; letter-spacing: -0.02em; margin: 0; }\n.asm-speaker.is-headliner .asm-speaker-name { font-size: clamp(1.5rem, 2.6vw, 2rem); }\n.asm-speaker-role { color: var(--muted); font-size: 0.92rem; margin: 0.2rem 0 0.9rem; }\n.asm-speaker-talk {\n  font-size: 1.02rem; font-weight: 500; margin: 0; letter-spacing: -0.01em;\n  padding-top: 0.9rem; border-top: 1px solid var(--line); color: var(--text);\n}\n.asm-speaker.is-headliner .asm-speaker-talk { font-size: 1.14rem; }\n\n/* AGENDA */\n.asm-agenda { background: var(--paper); }\n.asm-agenda-head { max-width: 42rem; margin-bottom: 2.2rem; }\n.asm-tabs { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 2rem; }\n.asm-tab {\n  display: flex; flex-direction: column; gap: 0.15rem; text-align: left;\n  padding: 0.75rem 1.3rem; border-radius: 12px; cursor: pointer;\n  background: var(--frost); border: 1px solid var(--line); color: var(--muted);\n  font-family: inherit; transition: background 0.25s, color 0.25s, border-color 0.25s;\n}\n.asm-tab:hover { border-color: rgba(43, 57, 255, 0.4); }\n.asm-tab.is-active { background: var(--ink); color: var(--frost); border-color: var(--ink); }\n.asm-tab-label { font-weight: 600; font-size: 1rem; }\n.asm-tab-date { font-size: 0.85rem; opacity: 0.8; }\n.asm-schedule {\n  border: 1px solid var(--line); border-radius: 18px; overflow: hidden; background: var(--frost);\n  opacity: 0; transform: translateY(18px); transition: opacity 0.6s, transform 0.6s;\n}\n.asm-schedule.is-shown { opacity: 1; transform: none; }\n.asm-schedule-theme {\n  display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap;\n  padding: 1.3rem clamp(1.2rem, 3vw, 2rem); border-bottom: 1px solid var(--line);\n  background: linear-gradient(105deg, rgba(43, 57, 255, 0.08), transparent);\n}\n.asm-schedule-daynum { font-weight: 600; font-size: 1.05rem; color: var(--cobalt); font-variant-numeric: tabular-nums; }\n.asm-schedule-word { font-size: clamp(1.5rem, 3.4vw, 2.4rem); font-weight: 620; letter-spacing: -0.03em; }\n.asm-schedule-list { list-style: none; margin: 0; padding: 0; }\n.asm-slot {\n  display: grid; grid-template-columns: 5.5rem 1fr auto; align-items: center; gap: 1.2rem;\n  padding: 1.15rem clamp(1.2rem, 3vw, 2rem); border-bottom: 1px solid var(--line);\n}\n.asm-slot:last-child { border-bottom: 0; }\n.asm-schedule.is-shown .asm-slot { animation: asm-slot-in 0.55s cubic-bezier(0.16, 1, 0.3, 1) both; }\n@keyframes asm-slot-in { from { opacity: 0; transform: translateX(-14px); } to { opacity: 1; transform: none; } }\n.asm-slot-time { font-variant-numeric: tabular-nums; font-weight: 600; font-size: 1.02rem; color: var(--text); }\n.asm-slot-title { font-weight: 500; font-size: 1.05rem; margin: 0; letter-spacing: -0.01em; }\n.asm-slot-who { color: var(--muted); font-size: 0.9rem; margin: 0.15rem 0 0; }\n.asm-slot-track {\n  font-size: 0.8rem; font-weight: 500; padding: 0.3rem 0.7rem; border-radius: 999px;\n  white-space: nowrap; border: 1px solid var(--line); color: var(--muted);\n}\n.asm-slot-track.track-mainstage { background: rgba(43, 57, 255, 0.1); color: var(--cobalt); border-color: transparent; }\n.asm-slot-track.track-craft { background: rgba(255, 106, 43, 0.12); color: #b8410f; border-color: transparent; }\n.asm-slot-track.track-systems { background: rgba(20, 22, 29, 0.06); color: var(--text); border-color: transparent; }\n@media (max-width: 640px) {\n  .asm-slot { grid-template-columns: 4rem 1fr; }\n  .asm-slot-track { grid-column: 2; justify-self: start; }\n}\n\n/* TICKETS */\n.asm-tickets { background: var(--paper-2); }\n.asm-tickets-head { max-width: 42rem; margin-bottom: clamp(2.2rem, 5vw, 3.5rem); }\n.asm-nudge {\n  display: inline-flex; align-items: center; gap: 0.55rem; margin: 1.2rem 0 0;\n  background: rgba(255, 106, 43, 0.12); color: #b8410f; font-weight: 500; font-size: 0.95rem;\n  padding: 0.5rem 0.95rem; border-radius: 999px;\n}\n.asm-nudge-dot { width: 0.55rem; height: 0.55rem; border-radius: 999px; background: var(--orange); animation: asm-blink 1.6s ease-in-out infinite; }\n@keyframes asm-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }\n.asm-tier-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; align-items: start; }\n@media (max-width: 900px) { .asm-tier-grid { grid-template-columns: 1fr; max-width: 30rem; } }\n.asm-tier {\n  position: relative; padding: 2rem 1.8rem; border-radius: 18px;\n  background: var(--frost); border: 1px solid var(--line);\n  opacity: 0; transform: translateY(24px);\n  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);\n}\n.asm-tier-grid.is-shown .asm-tier { opacity: 1; transform: none; }\n.asm-tier.is-featured {\n  background: var(--ink); color: var(--frost); border-color: var(--ink);\n  box-shadow: 0 28px 60px -30px rgba(43, 57, 255, 0.6);\n}\n@media (min-width: 901px) { .asm-tier.is-featured { transform: translateY(-14px); } .asm-tier-grid.is-shown .asm-tier.is-featured { transform: translateY(-14px); } }\n.asm-tier-note {\n  position: absolute; top: -0.75rem; left: 1.8rem;\n  background: var(--cobalt); color: #fff; font-size: 0.78rem; font-weight: 500;\n  padding: 0.3rem 0.8rem; border-radius: 999px;\n}\n.asm-tier-name { font-size: 1.15rem; font-weight: 600; margin: 0 0 0.4rem; letter-spacing: -0.01em; }\n.asm-tier-price {\n  display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap;\n  font-size: clamp(2.4rem, 5vw, 3.2rem); font-weight: 640; letter-spacing: -0.03em; margin: 0 0 1.4rem;\n}\n.asm-tier-urgent { font-size: 0.85rem; font-weight: 500; letter-spacing: 0; color: var(--orange); }\n.asm-tier-feats { list-style: none; margin: 0 0 1.8rem; padding: 0; display: grid; gap: 0.7rem; }\n.asm-tier-feats li { display: flex; align-items: flex-start; gap: 0.65rem; font-size: 0.96rem; }\n.asm-check { flex: 0 0 auto; width: 1.05rem; height: 1.05rem; margin-top: 0.15rem; border-radius: 999px; background: rgba(43, 57, 255, 0.15); position: relative; }\n.asm-check::after { content: \"\"; position: absolute; left: 0.34rem; top: 0.18rem; width: 0.28rem; height: 0.52rem; border: solid var(--cobalt); border-width: 0 2px 2px 0; transform: rotate(45deg); }\n.asm-tier.is-featured .asm-check { background: rgba(255, 255, 255, 0.18); }\n.asm-tier.is-featured .asm-check::after { border-color: var(--frost); }\n.asm-tier-cta { width: 100%; }\n.asm-btn-line {\n  display: inline-flex; align-items: center; justify-content: center;\n  border: 1px solid var(--ink); color: var(--ink); background: transparent;\n  font-weight: 600; font-size: 0.98rem; text-decoration: none;\n  padding: 0.7rem 1.4rem; border-radius: 999px; transition: background 0.25s, color 0.25s;\n}\n.asm-btn-line:hover { background: var(--ink); color: var(--frost); }\n\n/* SPONSORS */\n.asm-sponsors { background: var(--paper); }\n.asm-sponsors-head { max-width: 40rem; margin-bottom: clamp(2.2rem, 5vw, 3.5rem); }\n.asm-logo-wall {\n  display: grid; grid-template-columns: repeat(6, 1fr); gap: 1px;\n  background: var(--line); border: 1px solid var(--line); border-radius: 16px; overflow: hidden;\n}\n@media (max-width: 860px) { .asm-logo-wall { grid-template-columns: repeat(3, 1fr); } }\n@media (max-width: 480px) { .asm-logo-wall { grid-template-columns: repeat(2, 1fr); } }\n.asm-logo {\n  display: flex; align-items: center; justify-content: center; gap: 0.65rem;\n  padding: 1.7rem 1rem; background: var(--frost); color: var(--muted);\n  font-weight: 600; font-size: 1.05rem; letter-spacing: -0.01em;\n  transition: color 0.25s, background 0.25s;\n}\n.asm-logo svg { color: var(--muted); transition: color 0.25s, transform 0.4s cubic-bezier(0.16, 1, 0.3, 1); }\n.asm-logo:hover { color: var(--text); background: #fff; }\n.asm-logo:hover svg { color: var(--cobalt); transform: rotate(-8deg) scale(1.08); }\n\n/* VENUE */\n.asm-venue { background: var(--paper-2); }\n.asm-venue-grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 4.5rem); align-items: center;\n  opacity: 0; transform: translateY(22px); transition: opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1), transform 0.8s cubic-bezier(0.16, 1, 0.3, 1); }\n.asm-venue-grid.is-shown { opacity: 1; transform: none; }\n@media (max-width: 880px) { .asm-venue-grid { grid-template-columns: 1fr; } }\n.asm-venue-facts { display: grid; gap: 1.4rem; margin-top: 2.2rem; }\n.asm-fact h4 { font-size: 1.02rem; font-weight: 600; margin: 0 0 0.3rem; letter-spacing: -0.01em; }\n.asm-fact p { color: var(--muted); font-size: 0.96rem; margin: 0; }\n.asm-map { border-radius: 18px; overflow: hidden; box-shadow: 0 30px 60px -34px rgba(11, 13, 22, 0.5); }\n.asm-map-svg { display: block; width: 100%; height: auto; }\n\n/* RECAP */\n.asm-recap { background: var(--ink); color: var(--frost); }\n.asm-recap-head { max-width: 40rem; margin-bottom: clamp(2rem, 4vw, 3rem); }\n.asm-recap .asm-eyebrow { color: var(--cobalt-lift); }\n.asm-recap .asm-h2 { color: var(--frost); }\n.asm-recap-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem; margin-bottom: 3rem; }\n@media (max-width: 720px) { .asm-recap-grid { grid-template-columns: repeat(2, 1fr); } }\n.asm-stat {\n  padding: 1.6rem 0; border-top: 2px solid rgba(246, 246, 244, 0.18);\n  opacity: 0; transform: translateY(18px); transition: opacity 0.7s, transform 0.7s;\n}\n.asm-recap-grid.is-shown .asm-stat { opacity: 1; transform: none; }\n.asm-stat-num { display: block; font-size: clamp(2.4rem, 6vw, 4rem); font-weight: 640; letter-spacing: -0.04em; line-height: 1; font-variant-numeric: tabular-nums; }\n.asm-stat:nth-child(1) .asm-stat-num { color: var(--cobalt-lift); }\n.asm-stat:nth-child(3) .asm-stat-num { color: var(--orange); }\n.asm-stat-lab { display: block; color: rgba(246, 246, 244, 0.66); font-size: 0.98rem; margin-top: 0.5rem; }\n.asm-recap-strip { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; }\n@media (max-width: 720px) { .asm-recap-strip { grid-template-columns: repeat(2, 1fr); } }\n.asm-recap-tile { aspect-ratio: 4 / 3; border-radius: 12px; background-size: cover; background-position: center; }\n.asm-recap-tile.t1 { background-image: linear-gradient(135deg, rgba(43, 57, 255, 0.85), rgba(11, 13, 22, 0.4)), url(/assembly/poster.png); }\n.asm-recap-tile.t2 { background-image: linear-gradient(135deg, rgba(255, 106, 43, 0.7), rgba(11, 13, 22, 0.5)), url(/assembly/poster.png); background-position: right center; }\n.asm-recap-tile.t3 { background-image: linear-gradient(135deg, rgba(11, 13, 22, 0.3), rgba(43, 57, 255, 0.6)), url(/assembly/poster.png); background-position: left bottom; }\n.asm-recap-tile.t4 { background-image: linear-gradient(135deg, rgba(11, 13, 22, 0.55), rgba(255, 106, 43, 0.4)), url(/assembly/poster.png); background-position: center bottom; }\n\n/* FAQ + FINAL */\n.asm-faq { background: var(--paper); }\n.asm-faq-grid { display: grid; grid-template-columns: 0.85fr 1.15fr; gap: clamp(2rem, 5vw, 4rem); }\n@media (max-width: 860px) { .asm-faq-grid { grid-template-columns: 1fr; } }\n.asm-faq-head { max-width: 26rem; }\n.asm-faq-list { border-top: 1px solid var(--line); }\n.asm-faq-item { border-bottom: 1px solid var(--line); }\n.asm-faq-q {\n  display: flex; align-items: center; justify-content: space-between; gap: 1rem; width: 100%;\n  padding: 1.4rem 0; background: none; border: 0; cursor: pointer; text-align: left;\n  font-family: inherit; font-size: 1.15rem; font-weight: 500; letter-spacing: -0.01em; color: var(--text);\n}\n.asm-faq-icon { position: relative; flex: 0 0 auto; width: 1.1rem; height: 1.1rem; }\n.asm-faq-icon::before, .asm-faq-icon::after { content: \"\"; position: absolute; background: var(--cobalt); border-radius: 2px; transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.3s; }\n.asm-faq-icon::before { left: 0; top: 50%; width: 100%; height: 2px; transform: translateY(-50%); }\n.asm-faq-icon::after { top: 0; left: 50%; width: 2px; height: 100%; transform: translateX(-50%); }\n.asm-faq-item.is-open .asm-faq-icon::after { transform: translateX(-50%) scaleY(0); opacity: 0; }\n.asm-faq-a { overflow: hidden; max-height: 0; transition: max-height 0.4s cubic-bezier(0.16, 1, 0.3, 1); }\n.asm-faq-item.is-open .asm-faq-a { max-height: 12rem; }\n.asm-faq-a p { color: var(--muted); font-size: 1rem; margin: 0; padding: 0 0 1.4rem; max-width: 44rem; }\n\n.asm-final {\n  margin-top: clamp(3.5rem, 7vw, 6rem); padding: clamp(2.8rem, 6vw, 5rem) clamp(1.5rem, 5vw, 4rem);\n  background: var(--ink); color: var(--frost); border-radius: 24px; text-align: center;\n  position: relative; overflow: hidden;\n}\n.asm-final::before {\n  content: \"\"; position: absolute; inset: 0; pointer-events: none;\n  background: radial-gradient(60% 80% at 50% 0%, rgba(43, 57, 255, 0.4), transparent 70%);\n}\n.asm-final-clock { position: relative; display: flex; justify-content: center; gap: 0.7rem; margin-bottom: 1.8rem; }\n.asm-final-unit { display: grid; justify-items: center; gap: 0.2rem; min-width: 3.6rem; }\n.asm-final-num { font-size: clamp(1.4rem, 4vw, 2.2rem); font-weight: 640; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }\n.asm-final-lab { font-size: 0.72rem; color: rgba(246, 246, 244, 0.6); }\n.asm-final-title { position: relative; font-size: clamp(1.9rem, 5vw, 3.4rem); font-weight: 620; letter-spacing: -0.035em; line-height: 1.02; margin: 0 0 0.9rem; }\n.asm-final-lede { position: relative; color: rgba(246, 246, 244, 0.82); font-size: 1.1rem; margin: 0 auto 2rem; max-width: 32rem; }\n\n/* REDUCED MOTION — kill decorative transitions/animations, reveal everything */\n@media (prefers-reduced-motion: reduce) {\n  .asm-root *, .asm-root *::before, .asm-root *::after {\n    animation-duration: 0.001ms !important; animation-iteration-count: 1 !important;\n    transition-duration: 0.001ms !important;\n  }\n  .asm-why-card, .asm-speaker, .asm-tier, .asm-stat, .asm-venue-grid, .asm-schedule { opacity: 1 !important; transform: none !important; }\n  .asm-slot { animation: none !important; }\n}\n\n/* == END == */\n`\n","type":"registry:page"}],"assets":[{"url":"https://ui.artbloom.tech/r/asset/templates/assembly/assets/poster.png","target":"public/assembly/poster.png","bytes":2404280},{"url":"https://ui.artbloom.tech/r/asset/templates/assembly/assets/speaker1.png","target":"public/assembly/speaker1.png","bytes":1933944},{"url":"https://ui.artbloom.tech/r/asset/templates/assembly/assets/speaker2.png","target":"public/assembly/speaker2.png","bytes":1889770},{"url":"https://ui.artbloom.tech/r/asset/templates/assembly/assets/speaker3.png","target":"public/assembly/speaker3.png","bytes":2075407},{"url":"https://ui.artbloom.tech/r/asset/templates/assembly/assets/speaker4.png","target":"public/assembly/speaker4.png","bytes":1863181},{"url":"https://ui.artbloom.tech/r/asset/templates/assembly/assets/speaker5.png","target":"public/assembly/speaker5.png","bytes":2053270}],"meta":{"kind":"templates","categories":["events"],"docs":"https://ui.artbloom.tech/artbloom/templates/assembly"}}