{"$schema":"https://ui.artbloom.tech/schema/registry-item.json","name":"flux","type":"registry:page","title":"Flux","description":"A generative-AI creative showcase where the output is the hero: a full-bleed autoplaying render reel, a hover-to-scrub output gallery, prompt-to-result pairs and a style-explorer. Ships six art-directed generative stills; motion respects reduced-motion.","author":"@artbloom","dependencies":[],"registryDependencies":[],"files":[{"path":"app/flux/page.tsx","target":"app/flux/page.tsx","content":"\"use client\"\n\nimport { useEffect, useRef, useState } from \"react\"\n\n/**\n * Flux — a generative image & video model, sold the only honest way for a tool\n * whose whole pitch is \"look what it makes\": the model's own output is the hero.\n *\n * The base is a true neutral near-black (#08080a, not brown-tinted) so the\n * generated frames carry every bit of colour. Chrome is quiet frost-on-black\n * with a single electric-violet accent that mostly echoes the violet already\n * living inside the imagery. No tracked-caps eyebrows, no middle-dot metas, no\n * em-dash fragment labels, no arrow-suffixed links, no glass blur. Monospace is\n * used only where the text is a literal prompt. The one loud move is the\n * full-bleed generative reel; everything else stays out of its way. All motion\n * — the crossfade reel, hover-scrub, prompt→output morph — is gated on\n * prefers-reduced-motion.\n */\n\nconst HERO_REEL = [\"/flux/g1.png\", \"/flux/g5.png\", \"/flux/g4.png\", \"/flux/g2.png\"]\n\n/**\n * The output gallery. Each tile is one generated frame; `frames` holds a couple\n * of palette-kin outputs so hovering scrubs through them like a short reel, and\n * `prompt` is the line that made the primary one. `w`/`h` seed the aspect ratio\n * so the masonry is stable before any image loads.\n */\ntype Shot = { name: string; w: number; h: number; label: string; prompt: string; frames: string[] }\nconst GALLERY: Shot[] = [\n  {\n    name: \"g1\", w: 1536, h: 1024, label: \"Dreamscape\",\n    prompt: \"endless mirror-calm sea of liquid mercury, three floating stone islands pouring light, a lone figure at the shore\",\n    frames: [\"/flux/g1.png\", \"/flux/g5.png\"],\n  },\n  {\n    name: \"g3\", w: 1024, h: 1536, label: \"Portrait\",\n    prompt: \"explorer with braided hair woven with glowing filaments, half cyan light half warm amber, painterly concept-art finish\",\n    frames: [\"/flux/g3.png\", \"/flux/g6.png\"],\n  },\n  {\n    name: \"g2\", w: 1024, h: 1024, label: \"Product\",\n    prompt: \"frosted-glass perfume bottle sculpted like a river stone, faint violet liquid, wet black reflective surface\",\n    frames: [\"/flux/g2.png\", \"/flux/g4.png\"],\n  },\n  {\n    name: \"g5\", w: 1536, h: 1024, label: \"Architecture\",\n    prompt: \"monumental brutalist megastructure carved into a canyon at blue hour, a single glowing violet atrium at its heart\",\n    frames: [\"/flux/g5.png\", \"/flux/g1.png\"],\n  },\n  {\n    name: \"g6\", w: 1024, h: 1536, label: \"Macro\",\n    prompt: \"jewel-toned beetle with iridescent violet carapace on a dew-covered fern, droplets acting as tiny lenses at dawn\",\n    frames: [\"/flux/g6.png\", \"/flux/g4.png\"],\n  },\n  {\n    name: \"g7\", w: 1536, h: 1024, label: \"Retro-future\",\n    prompt: \"lone chrome concept car on a wet desert highway facing a giant sun, airbrushed synthwave sky of magenta and teal\",\n    frames: [\"/flux/g2.png\", \"/flux/g5.png\"],\n  },\n  {\n    name: \"g4\", w: 1024, h: 1024, label: \"Abstract 3D\",\n    prompt: \"collision of glossy iridescent chrome blobs and matte violet spheres in a black void, oil-slick rainbow highlights\",\n    frames: [\"/flux/g4.png\", \"/flux/g2.png\"],\n  },\n  {\n    name: \"g9\", w: 1024, h: 1536, label: \"Cityscape\",\n    prompt: \"rain-soaked back alley at night, stacked signage glowing violet and cyan reflected in deep puddles, a figure with an umbrella\",\n    frames: [\"/flux/g5.png\", \"/flux/g2.png\"],\n  },\n  {\n    name: \"g8\", w: 1536, h: 1024, label: \"Painterly\",\n    prompt: \"rolling northern mountains under swirling twilight clouds, thick impasto oil strokes, warm ochre light on the peaks\",\n    frames: [\"/flux/g4.png\", \"/flux/g5.png\"],\n  },\n  {\n    name: \"g10\", w: 1024, h: 1024, label: \"Claymation\",\n    prompt: \"round handmade clay creature with big curious eyes on a mossy diorama, visible fingerprints, cozy stop-motion still\",\n    frames: [\"/flux/g6.png\", \"/flux/g3.png\"],\n  },\n]\n\n/** Model lineage — a real sequence, so the years carry the order (no 01/02/03). */\nconst LINEAGE = [\n  { year: \"2023\", name: \"Flux Alpha\", note: \"The first internal model. 512px, painterly, and slow — but the first time a text line came back as something we wanted to keep.\" },\n  { year: \"2024\", name: \"Flux 1\", note: \"Public preview. Coherent 1K stills, real material response, and a prompt language people could actually reason about.\" },\n  { year: \"2025\", name: \"Flux 2 Pro\", note: \"Photoreal quality at native 1.5K, consistent characters across a set, and the style system that powers the presets above.\" },\n  { year: \"2026\", name: \"Flux Motion\", note: \"Stills learned to move. Short generative clips that hold their world frame to frame — the reel at the top of this page is Motion.\" },\n]\n\n/** Community creators + the output each is known for. */\nconst CREATORS = [\n  { handle: \"nyx.render\", craft: \"Product & still-life\", img: \"/flux/g2.png\", grad: \"linear-gradient(135deg, #8b5cf6, #4c1d95)\" },\n  { handle: \"atlas.frames\", craft: \"Cinematic worlds\", img: \"/flux/g5.png\", grad: \"linear-gradient(135deg, #06b6d4, #1e3a8a)\" },\n  { handle: \"kiln.studio\", craft: \"Painterly landscapes\", img: \"/flux/g4.png\", grad: \"linear-gradient(135deg, #f59e0b, #7c2d12)\" },\n  { handle: \"vero.macro\", craft: \"Nature macro\", img: \"/flux/g6.png\", grad: \"linear-gradient(135deg, #10b981, #064e3b)\" },\n  { handle: \"sable.co\", craft: \"Character design\", img: \"/flux/g3.png\", grad: \"linear-gradient(135deg, #ec4899, #831843)\" },\n  { handle: \"form.void\", craft: \"Abstract 3D\", img: \"/flux/g4.png\", grad: \"linear-gradient(135deg, #a78bfa, #312e81)\" },\n]\n\n/** Prompt→output pairs for the morph demo; loops through these on reveal. */\nconst MORPHS = [\n  { prompt: \"a lone chrome concept car on a wet desert highway, facing a giant setting sun, synthwave sky\", img: \"/flux/g2.png\" },\n  { prompt: \"rain-soaked alley at night, stacked neon signage in violet and cyan mirrored in the puddles\", img: \"/flux/g5.png\" },\n  { prompt: \"a jewel-toned beetle on a dew-covered fern at dawn, droplets acting as tiny lenses\", img: \"/flux/g6.png\" },\n  { prompt: \"a brutalist megastructure carved into a canyon at blue hour, one glowing violet atrium\", img: \"/flux/g5.png\" },\n]\n\n/** Style presets for the deep-dive: each chip swaps the featured render. */\nconst STYLES = [\n  { id: \"photoreal\", label: \"Photoreal\", img: \"/flux/g2.png\", blurb: \"Studio-grade product and still-life renders — accurate materials, real reflections, controllable light.\" },\n  { id: \"cinematic\", label: \"Cinematic\", img: \"/flux/g5.png\", blurb: \"Anamorphic depth, volumetric haze and colour grades pulled straight from film — a frame that could open a title sequence.\" },\n  { id: \"painterly\", label: \"Painterly\", img: \"/flux/g4.png\", blurb: \"Visible brushwork and canvas grain, from loose impasto to fine glazing, without ever tipping into a filter.\" },\n  { id: \"render3d\", label: \"3D render\", img: \"/flux/g4.png\", blurb: \"Clean product-viz geometry — subsurface glow, oil-slick chrome and soft studio bounce, no scene to build.\" },\n  { id: \"portrait\", label: \"Portrait\", img: \"/flux/g3.png\", blurb: \"Character work with real skin texture and directable lighting, holding a consistent face across a whole set.\" },\n]\n\n/**\n * One masonry tile. Owns its hover state: while hovered (and motion allowed) it\n * crossfades through its `frames` on a fast timer — a simulated scrub — and lifts\n * the prompt into view. Reduced motion just shows the still and reveals the prompt\n * on hover with no cycling.\n */\nfunction FluxTile({ shot, reduced }: { shot: Shot; reduced: boolean }) {\n  const [hover, setHover] = useState(false)\n  const [frame, setFrame] = useState(0)\n\n  useEffect(() => {\n    if (!hover || reduced || shot.frames.length < 2) return\n    const id = window.setInterval(() => {\n      setFrame((f) => (f + 1) % shot.frames.length)\n    }, 780)\n    return () => window.clearInterval(id)\n  }, [hover, reduced, shot.frames.length])\n\n  useEffect(() => {\n    if (!hover) setFrame(0)\n  }, [hover])\n\n  return (\n    <figure\n      className={`fx-tile ${hover ? \"is-hover\" : \"\"}`}\n      style={{ aspectRatio: `${shot.w} / ${shot.h}` }}\n      onMouseEnter={() => setHover(true)}\n      onMouseLeave={() => setHover(false)}\n      onFocus={() => setHover(true)}\n      onBlur={() => setHover(false)}\n      tabIndex={0}\n    >\n      <div className=\"fx-tile-frames\" aria-hidden=\"true\">\n        {shot.frames.map((src, i) => (\n          <div\n            key={src}\n            className={`fx-tile-img ${i === (reduced ? 0 : frame) ? \"is-on\" : \"\"}`}\n            style={{ backgroundImage: `url(${src})` }}\n          />\n        ))}\n      </div>\n      <figcaption className=\"fx-tile-cap\">\n        <span className=\"fx-tile-label\">{shot.label}</span>\n        <span className=\"fx-tile-prompt\">{shot.prompt}</span>\n      </figcaption>\n      <span className=\"fx-tile-scrub\" aria-hidden=\"true\">\n        {shot.frames.map((src, i) => (\n          <span key={src} className={i === (reduced ? 0 : frame) ? \"is-on\" : \"\"} />\n        ))}\n      </span>\n    </figure>\n  )\n}\n\n/**\n * Prompt→output demo. Once on screen it loops the pairs: the prompt writes in on\n * the left, then the output wipes open on the right, holds, and advances. Under\n * reduced motion it just shows the first pair fully resolved, no loop, no wipe.\n */\nfunction MorphDemo({ reduced }: { reduced: boolean }) {\n  const [ref, shown] = useReveal<HTMLDivElement>()\n  const [i, setI] = useState(0)\n  const [phase, setPhase] = useState<\"typing\" | \"output\">(\"output\")\n\n  useEffect(() => {\n    if (!shown || reduced) return\n    // Kick the first cycle in, then run the loop.\n    setPhase(\"typing\")\n    const toOutput = window.setTimeout(() => setPhase(\"output\"), 2200)\n    const next = window.setTimeout(() => {\n      setI((n) => (n + 1) % MORPHS.length)\n      setPhase(\"typing\")\n    }, 5200)\n    return () => {\n      window.clearTimeout(toOutput)\n      window.clearTimeout(next)\n    }\n  }, [shown, reduced, i])\n\n  const cur = MORPHS[i]\n  const resolved = reduced || phase === \"output\"\n\n  return (\n    <div className={`fx-morph ${shown ? \"is-shown\" : \"\"}`} ref={ref}>\n      <div className=\"fx-morph-in\">\n        <span className=\"fx-morph-tag\">Prompt</span>\n        <p className=\"fx-morph-prompt\" key={`p-${i}`}>\n          {cur.prompt}\n          {!reduced && <span className=\"fx-caret\" aria-hidden=\"true\" />}\n        </p>\n      </div>\n      <div className=\"fx-morph-arrow\" aria-hidden=\"true\">\n        <span />\n      </div>\n      <div className=\"fx-morph-out\">\n        <span className=\"fx-morph-tag\">Output</span>\n        <div className=\"fx-morph-frame\">\n          {MORPHS.map((m, n) => (\n            <div\n              key={m.img}\n              className={`fx-morph-img ${n === i && resolved ? \"is-on\" : \"\"}`}\n              style={{ backgroundImage: `url(${m.img})` }}\n              aria-hidden=\"true\"\n            />\n          ))}\n          <span className={`fx-morph-wipe ${resolved ? \"is-open\" : \"\"}`} aria-hidden=\"true\" />\n        </div>\n      </div>\n    </div>\n  )\n}\n\n/** True when the visitor has asked for reduced motion; kept live on change. */\nfunction useReducedMotion() {\n  const [reduced, setReduced] = useState(false)\n  useEffect(() => {\n    const mq = window.matchMedia(\"(prefers-reduced-motion: reduce)\")\n    setReduced(mq.matches)\n    const on = () => setReduced(mq.matches)\n    mq.addEventListener(\"change\", on)\n    return () => mq.removeEventListener(\"change\", on)\n  }, [])\n  return reduced\n}\n\n/** Reveal a block once when it scrolls into view. Reduced-motion shows 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.18 },\n    )\n    io.observe(node)\n    return () => io.disconnect()\n  }, [])\n  return [ref, shown] as const\n}\n\nexport default function Flux() {\n  const reduced = useReducedMotion()\n  const [slide, setSlide] = useState(0)\n  const [scrollY, setScrollY] = useState(0)\n  const [styleId, setStyleId] = useState(STYLES[0].id)\n  const activeStyle = STYLES.find((s) => s.id === styleId) ?? STYLES[0]\n\n  // Hero reel: slow crossfade between the strongest landscape outputs.\n  useEffect(() => {\n    if (reduced) return\n    const id = window.setInterval(() => {\n      setSlide((s) => (s + 1) % HERO_REEL.length)\n    }, 5200)\n    return () => window.clearInterval(id)\n  }, [reduced])\n\n  // Gentle hero parallax for depth (skipped entirely under reduced motion).\n  useEffect(() => {\n    if (reduced) return\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      window.removeEventListener(\"scroll\", onScroll)\n      if (raf) cancelAnimationFrame(raf)\n    }\n  }, [reduced])\n\n  const par = reduced ? 0 : scrollY\n\n  return (\n    <div className={`flux-root ${reduced ? \"is-still\" : \"\"}`}>\n      <style>{css}</style>\n\n      <header className=\"fx-nav\">\n        <a className=\"fx-mark\" href=\"#top\" aria-label=\"Flux home\">\n          <span className=\"fx-mark-dot\" aria-hidden=\"true\" />\n          Flux\n        </a>\n        <nav className=\"fx-links\">\n          <a href=\"#gallery\">Gallery</a>\n          <a href=\"#styles\">Styles</a>\n          <a href=\"#models\">Models</a>\n          <a href=\"#start\" className=\"fx-nav-cta\">\n            Start creating\n          </a>\n        </nav>\n      </header>\n\n      {/* HERO — full-bleed crossfading generative reel behind an oversized line */}\n      <section className=\"fx-hero\" id=\"top\">\n        <div className=\"fx-reel\" aria-hidden=\"true\">\n          {HERO_REEL.map((src, i) => (\n            <div\n              key={src}\n              className={`fx-reel-slide ${i === slide ? \"is-active\" : \"\"}`}\n              style={{ backgroundImage: `url(${src})` }}\n            />\n          ))}\n          <div className=\"fx-reel-scrim\" />\n        </div>\n\n        <div className=\"fx-hero-inner\" style={{ transform: `translate3d(0, ${par * -0.08}px, 0)` }}>\n          <p className=\"fx-hero-kicker\">Generative image and video model</p>\n          <h1 className=\"fx-hero-title\">\n            Describe it.\n            <br />\n            Flux draws the rest.\n          </h1>\n          <p className=\"fx-hero-lede\">\n            Type a scene, a style, a mood. Flux renders it as a finished image or a moving shot in\n            seconds — and every frame is yours to use.\n          </p>\n          <div className=\"fx-hero-actions\">\n            <a className=\"fx-btn fx-btn-primary\" href=\"#start\">\n              Start creating\n            </a>\n            <a className=\"fx-btn fx-btn-ghost\" href=\"#gallery\">\n              See what it makes\n            </a>\n          </div>\n        </div>\n\n        <div className=\"fx-reel-dots\" role=\"presentation\">\n          {HERO_REEL.map((src, i) => (\n            <span key={src} className={i === slide ? \"is-active\" : \"\"} />\n          ))}\n        </div>\n      </section>\n\n      {/* GALLERY — masonry of outputs; hover a tile to scrub frames + read the prompt */}\n      <section className=\"fx-section fx-gallery\" id=\"gallery\">\n        <div className=\"fx-gallery-head\">\n          <div>\n            <p className=\"fx-eyebrow\">Made with Flux</p>\n            <h2 className=\"fx-h2\">Ten prompts, ten worlds</h2>\n          </div>\n          <p className=\"fx-sub\">\n            Everything here came out of a single line of text. Hover any frame to scrub through the\n            takes and see the prompt behind it.\n          </p>\n        </div>\n        <div className=\"fx-masonry\">\n          {GALLERY.map((shot) => (\n            <FluxTile key={shot.name} shot={shot} reduced={reduced} />\n          ))}\n        </div>\n      </section>\n\n      {/* STYLES — chips swap the featured render, proving range from one control */}\n      <section className=\"fx-section fx-styles\" id=\"styles\">\n        <div className=\"fx-styles-copy\">\n          <p className=\"fx-eyebrow\">One model, any look</p>\n          <h2 className=\"fx-h2\">Steer the style with a word</h2>\n          <p className=\"fx-sub\">\n            Flux isn't tuned for a single aesthetic. Name the look you're after and the whole render\n            shifts to match. Try a few.\n          </p>\n          <div className=\"fx-chips\" role=\"tablist\" aria-label=\"Style presets\">\n            {STYLES.map((s) => (\n              <button\n                key={s.id}\n                role=\"tab\"\n                aria-selected={s.id === styleId}\n                className={`fx-chip ${s.id === styleId ? \"is-active\" : \"\"}`}\n                onClick={() => setStyleId(s.id)}\n              >\n                {s.label}\n              </button>\n            ))}\n          </div>\n          <p className=\"fx-style-blurb\" key={activeStyle.id}>{activeStyle.blurb}</p>\n        </div>\n        <div className=\"fx-styles-stage\">\n          {STYLES.map((s) => (\n            <div\n              key={s.id}\n              className={`fx-style-img ${s.id === styleId ? \"is-on\" : \"\"}`}\n              style={{ backgroundImage: `url(${s.img})` }}\n              aria-hidden={s.id !== styleId}\n            />\n          ))}\n          <span className=\"fx-style-tag\">{activeStyle.label}</span>\n        </div>\n      </section>\n\n      {/* PROMPT → OUTPUT — a line of text wipes open into the render it produced */}\n      <section className=\"fx-section fx-morph-section\">\n        <div className=\"fx-morph-head\">\n          <p className=\"fx-eyebrow\">How it feels</p>\n          <h2 className=\"fx-h2\">From a sentence to a frame</h2>\n          <p className=\"fx-sub\">\n            No dials to learn. You write what you want, Flux resolves it. Here it is, on repeat.\n          </p>\n        </div>\n        <MorphDemo reduced={reduced} />\n      </section>\n\n      {/* MODELS — lineage timeline */}\n      <section className=\"fx-section fx-lineage\" id=\"models\">\n        <div className=\"fx-lineage-head\">\n          <p className=\"fx-eyebrow\">The models behind it</p>\n          <h2 className=\"fx-h2\">Three years, four models</h2>\n          <p className=\"fx-sub\">\n            Every release traded a limitation for a capability. Here's the line that led to the one\n            you're using now.\n          </p>\n        </div>\n        <ol className=\"fx-timeline\">\n          {LINEAGE.map((m) => (\n            <li className=\"fx-tl-item\" key={m.year}>\n              <span className=\"fx-tl-year\">{m.year}</span>\n              <span className=\"fx-tl-node\" aria-hidden=\"true\" />\n              <div className=\"fx-tl-body\">\n                <h3 className=\"fx-tl-name\">{m.name}</h3>\n                <p className=\"fx-tl-note\">{m.note}</p>\n              </div>\n            </li>\n          ))}\n        </ol>\n      </section>\n\n      {/* COMMUNITY — creators + their work */}\n      <section className=\"fx-section fx-community\">\n        <div className=\"fx-community-head\">\n          <p className=\"fx-eyebrow\">In the wild</p>\n          <h2 className=\"fx-h2\">What people are making</h2>\n          <p className=\"fx-sub\">\n            Two million creators have generated with Flux this month. A few whose work we keep coming\n            back to.\n          </p>\n        </div>\n        <div className=\"fx-creators\">\n          {CREATORS.map((c) => (\n            <article className=\"fx-creator\" key={c.handle}>\n              <div className=\"fx-creator-img\" style={{ backgroundImage: `url(${c.img})` }} />\n              <div className=\"fx-creator-row\">\n                <span className=\"fx-creator-avatar\" style={{ background: c.grad }} aria-hidden=\"true\">\n                  {c.handle.slice(0, 1).toUpperCase()}\n                </span>\n                <div>\n                  <p className=\"fx-creator-handle\">{c.handle}</p>\n                  <p className=\"fx-creator-craft\">{c.craft}</p>\n                </div>\n              </div>\n            </article>\n          ))}\n        </div>\n      </section>\n\n      {/* START — final CTA over a quiet output */}\n      <section className=\"fx-section fx-start\" id=\"start\">\n        <div className=\"fx-start-bg\" style={{ backgroundImage: \"url(/flux/g1.png)\" }} aria-hidden=\"true\" />\n        <div className=\"fx-start-scrim\" />\n        <div className=\"fx-start-inner\">\n          <h2 className=\"fx-start-title\">The next image is a sentence away</h2>\n          <p className=\"fx-start-lede\">\n            Start free with fifty generations a day. No card, no watermark, and every frame you make\n            is yours to keep.\n          </p>\n          <div className=\"fx-hero-actions fx-start-actions\">\n            <a className=\"fx-btn fx-btn-primary\" href=\"#top\">\n              Start creating\n            </a>\n            <a className=\"fx-btn fx-btn-ghost\" href=\"#models\">\n              Read the model card\n            </a>\n          </div>\n        </div>\n      </section>\n\n      <footer className=\"fx-footer\" id=\"start-footer\">\n        <div className=\"fx-footer-top\">\n          <a className=\"fx-mark\" href=\"#top\">\n            <span className=\"fx-mark-dot\" aria-hidden=\"true\" />\n            Flux\n          </a>\n          <p className=\"fx-footer-tag\">A generative model for images and video. Made for people who make things.</p>\n        </div>\n        <div className=\"fx-footer-cols\">\n          <div>\n            <h4>Product</h4>\n            <a href=\"#gallery\">Gallery</a>\n            <a href=\"#styles\">Styles</a>\n            <a href=\"#models\">Models</a>\n          </div>\n          <div>\n            <h4>Company</h4>\n            <a href=\"#top\">About</a>\n            <a href=\"#top\">Research</a>\n            <a href=\"#top\">Careers</a>\n          </div>\n          <div>\n            <h4>Use</h4>\n            <a href=\"#top\">Licence</a>\n            <a href=\"#top\">Guidelines</a>\n            <a href=\"#top\">API</a>\n          </div>\n        </div>\n        <p className=\"fx-footer-fine\">\n          Every image on this page was generated by Flux. Outputs are yours to use, commercially or otherwise.\n        </p>\n      </footer>\n    </div>\n  )\n}\n\nconst css = `\n.flux-root {\n  --bg: #08080a;\n  --bg-2: #0e0e13;\n  --panel: #121218;\n  --frost: #f4f4f7;\n  --mist: #9a9aa8;\n  --faint: #6a6a78;\n  --line: rgba(244, 244, 247, 0.09);\n  --line-2: rgba(244, 244, 247, 0.16);\n  --violet: #8b5cf6;\n  --violet-lo: rgba(139, 92, 246, 0.16);\n  --violet-glow: rgba(139, 92, 246, 0.45);\n  background: var(--bg);\n  color: var(--frost);\n  font-family: var(--font-geist), system-ui, sans-serif;\n  -webkit-font-smoothing: antialiased;\n  line-height: 1.55;\n  overflow-x: hidden;\n}\n.flux-root *,\n.flux-root *::before,\n.flux-root *::after { box-sizing: border-box; }\n.flux-root ::selection { background: var(--violet); color: #fff; }\n\n.fx-mark {\n  display: inline-flex; align-items: center; gap: 0.5rem;\n  font-weight: 600; font-size: 1.2rem; letter-spacing: -0.02em;\n  color: var(--frost); text-decoration: none;\n}\n.fx-mark-dot {\n  width: 0.62rem; height: 0.62rem; border-radius: 50%;\n  background: var(--violet);\n  box-shadow: 0 0 14px var(--violet-glow);\n}\n\n/* NAV */\n.fx-nav {\n  position: fixed; inset: 0 0 auto 0; z-index: 60;\n  display: flex; align-items: center; justify-content: space-between;\n  padding: 1.15rem clamp(1.1rem, 4vw, 3rem);\n  background: linear-gradient(to bottom, rgba(8, 8, 10, 0.72), rgba(8, 8, 10, 0));\n}\n.fx-links { display: flex; align-items: center; gap: clamp(1rem, 2.2vw, 2rem); }\n.fx-links a { color: var(--mist); text-decoration: none; font-size: 0.94rem; transition: color 0.25s; }\n.fx-links a:hover { color: var(--frost); }\n.fx-nav-cta {\n  color: var(--frost) !important; font-weight: 550;\n  border: 1px solid var(--line-2); border-radius: 999px; padding: 0.5rem 1.05rem;\n  transition: border-color 0.25s, background 0.25s;\n}\n.fx-nav-cta:hover { border-color: var(--violet); background: var(--violet-lo); }\n@media (max-width: 620px) { .fx-links a:not(.fx-nav-cta) { display: none; } }\n\n/* BUTTONS */\n.fx-btn {\n  display: inline-flex; align-items: center; justify-content: center;\n  font-weight: 550; font-size: 1rem; text-decoration: none;\n  padding: 0.9rem 1.7rem; border-radius: 999px;\n  transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.35s, background 0.25s, border-color 0.25s;\n}\n.fx-btn-primary { background: var(--violet); color: #fff; }\n.fx-btn-primary:hover { transform: translateY(-2px); box-shadow: 0 16px 38px -12px var(--violet-glow); }\n.fx-btn-ghost { color: var(--frost); border: 1px solid var(--line-2); }\n.fx-btn-ghost:hover { border-color: var(--frost); transform: translateY(-2px); }\n\n/* HERO */\n.fx-hero { position: relative; height: 100svh; min-height: 640px; overflow: hidden; }\n.fx-reel { position: absolute; inset: 0; }\n.fx-reel-slide {\n  position: absolute; inset: 0;\n  background-size: cover; background-position: center;\n  opacity: 0; transform: scale(1.06);\n  transition: opacity 1.6s ease-in-out;\n  will-change: opacity, transform;\n}\n.fx-reel-slide.is-active { opacity: 1; animation: fxKen 7s ease-out forwards; }\n@keyframes fxKen {\n  from { transform: scale(1.05) translate3d(0, 0, 0); }\n  to { transform: scale(1.15) translate3d(-1.4%, -1.6%, 0); }\n}\n.fx-reel-scrim {\n  position: absolute; inset: 0;\n  background:\n    linear-gradient(to top, var(--bg) 3%, rgba(8, 8, 10, 0.2) 46%, rgba(8, 8, 10, 0.55) 100%),\n    linear-gradient(to right, rgba(8, 8, 10, 0.72) 0%, rgba(8, 8, 10, 0.1) 60%, transparent 100%);\n}\n.fx-hero-inner {\n  position: absolute; left: clamp(1.1rem, 6vw, 6rem); right: clamp(1.1rem, 6vw, 6rem);\n  bottom: clamp(3rem, 12vh, 7.5rem); max-width: 52rem; will-change: transform;\n}\n.fx-hero-kicker { color: var(--violet); font-size: 0.98rem; font-weight: 500; margin: 0 0 1.1rem; }\n.fx-hero-title {\n  font-weight: 620; font-size: clamp(2.9rem, 8.6vw, 7rem); line-height: 0.96;\n  letter-spacing: -0.035em; margin: 0;\n}\n.fx-hero-lede {\n  color: var(--frost); opacity: 0.9; font-size: clamp(1.05rem, 1.5vw, 1.28rem);\n  max-width: 36rem; margin: 1.5rem 0 2rem;\n}\n.fx-hero-actions { display: flex; flex-wrap: wrap; gap: 0.9rem; }\n.fx-reel-dots {\n  position: absolute; right: clamp(1.1rem, 6vw, 6rem); bottom: clamp(3rem, 12vh, 7.5rem);\n  display: flex; gap: 0.5rem;\n}\n.fx-reel-dots span { width: 22px; height: 3px; border-radius: 999px; background: var(--line-2); transition: background 0.4s; }\n.fx-reel-dots span.is-active { background: var(--violet); }\n@media (max-width: 720px) { .fx-reel-dots { display: none; } }\n\n/* SHARED SECTION SCAFFOLD */\n.fx-section { padding: clamp(4.5rem, 10vw, 8rem) clamp(1.1rem, 6vw, 6rem); }\n.fx-eyebrow { color: var(--violet); font-size: 0.95rem; font-weight: 550; margin: 0 0 0.9rem; }\n.fx-h2 {\n  font-weight: 600; font-size: clamp(2rem, 5vw, 3.5rem); line-height: 1.02;\n  letter-spacing: -0.03em; margin: 0;\n}\n.fx-sub { color: var(--mist); font-size: 1.08rem; max-width: 40rem; margin: 1rem 0 0; }\n\n/* FOOTER */\n.fx-footer { background: var(--bg-2); border-top: 1px solid var(--line); padding: clamp(3.5rem, 7vw, 5.5rem) clamp(1.1rem, 6vw, 6rem) 2.5rem; }\n.fx-footer-top { display: flex; flex-wrap: wrap; gap: 1.2rem 3rem; align-items: baseline; justify-content: space-between; padding-bottom: 2.5rem; border-bottom: 1px solid var(--line); }\n.fx-footer-tag { color: var(--mist); max-width: 30rem; margin: 0; }\n.fx-footer-cols { display: flex; flex-wrap: wrap; gap: 2.5rem 4rem; padding: 2.5rem 0; }\n.fx-footer-cols h4 { font-size: 0.95rem; font-weight: 600; margin: 0 0 0.9rem; }\n.fx-footer-cols a { display: block; color: var(--mist); text-decoration: none; font-size: 0.95rem; margin-bottom: 0.55rem; transition: color 0.25s; }\n.fx-footer-cols a:hover { color: var(--frost); }\n.fx-footer-fine { color: var(--faint); font-size: 0.88rem; margin: 0; }\n\n/* Reduced-motion: kill every non-essential animation. */\n@media (prefers-reduced-motion: reduce) {\n  .flux-root * { animation: none !important; transition: none !important; }\n  .fx-reel-slide { transform: none !important; }\n}\n/* GALLERY */\n.fx-gallery-head { display: flex; flex-wrap: wrap; gap: 1.2rem 3rem; align-items: end; justify-content: space-between; margin-bottom: clamp(2.2rem, 4vw, 3.2rem); }\n.fx-gallery-head .fx-sub { margin-top: 0; }\n.fx-masonry { columns: 3 320px; column-gap: 1rem; }\n@media (max-width: 900px) { .fx-masonry { columns: 2 240px; } }\n@media (max-width: 560px) { .fx-masonry { columns: 1; } }\n\n.fx-tile {\n  position: relative; margin: 0 0 1rem; break-inside: avoid;\n  border-radius: 14px; overflow: hidden; background: var(--panel);\n  border: 1px solid var(--line); cursor: pointer; outline: none;\n  transition: border-color 0.35s, box-shadow 0.4s, transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);\n}\n.fx-tile.is-hover { border-color: var(--violet); box-shadow: 0 24px 60px -24px var(--violet-glow); transform: translateY(-4px); }\n.fx-tile:focus-visible { border-color: var(--violet); }\n.fx-tile-frames { position: absolute; inset: 0; }\n.fx-tile-img {\n  position: absolute; inset: 0; background-size: cover; background-position: center;\n  opacity: 0; transition: opacity 0.55s ease, transform 4s ease-out;\n}\n.fx-tile-img.is-on { opacity: 1; }\n.fx-tile.is-hover .fx-tile-img.is-on { transform: scale(1.08); }\n.fx-tile-cap {\n  position: absolute; inset: auto 0 0 0; z-index: 2;\n  padding: 1.5rem 1.1rem 1rem;\n  background: linear-gradient(to top, rgba(8, 8, 10, 0.92), rgba(8, 8, 10, 0.55) 60%, transparent);\n  transform: translateY(0);\n}\n.fx-tile-label {\n  display: inline-block; font-size: 0.82rem; font-weight: 600; color: var(--frost);\n  background: var(--violet); padding: 0.18rem 0.6rem; border-radius: 999px; margin-bottom: 0.55rem;\n}\n.fx-tile-prompt {\n  display: block; font-family: var(--font-geist-mono), ui-monospace, monospace;\n  font-size: 0.82rem; line-height: 1.45; color: var(--mist);\n  max-height: 0; opacity: 0; overflow: hidden;\n  transition: max-height 0.5s ease, opacity 0.4s ease;\n}\n.fx-tile.is-hover .fx-tile-prompt { max-height: 8rem; opacity: 1; }\n.fx-tile-scrub { position: absolute; top: 0.8rem; left: 0.8rem; z-index: 2; display: flex; gap: 0.3rem; opacity: 0; transition: opacity 0.3s; }\n.fx-tile.is-hover .fx-tile-scrub { opacity: 1; }\n.fx-tile-scrub span { width: 14px; height: 3px; border-radius: 999px; background: rgba(244,244,247,0.35); transition: background 0.3s; }\n.fx-tile-scrub span.is-on { background: var(--violet); }\n\n/* STYLES */\n.fx-styles { display: grid; grid-template-columns: 1fr 1.05fr; gap: clamp(2rem, 5vw, 4.5rem); align-items: center; background: var(--bg-2); }\n@media (max-width: 860px) { .fx-styles { grid-template-columns: 1fr; } }\n.fx-styles-copy .fx-sub { margin-bottom: 1.8rem; }\n.fx-chips { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-bottom: 1.5rem; }\n.fx-chip {\n  font-family: inherit; font-size: 0.95rem; font-weight: 500; color: var(--mist);\n  background: transparent; border: 1px solid var(--line-2); border-radius: 999px;\n  padding: 0.55rem 1.1rem; cursor: pointer;\n  transition: color 0.25s, border-color 0.25s, background 0.25s;\n}\n.fx-chip:hover { color: var(--frost); border-color: var(--frost); }\n.fx-chip.is-active { color: #fff; background: var(--violet); border-color: var(--violet); }\n.fx-style-blurb { color: var(--frost); opacity: 0.9; font-size: 1.05rem; max-width: 34rem; margin: 0; min-height: 3.4rem; animation: fxFade 0.5s ease; }\n@keyframes fxFade { from { opacity: 0; transform: translateY(6px); } to { opacity: 0.9; transform: none; } }\n.fx-styles-stage {\n  position: relative; aspect-ratio: 4 / 3; border-radius: 16px; overflow: hidden;\n  border: 1px solid var(--line); box-shadow: 0 30px 80px -40px rgba(0,0,0,0.9);\n}\n.fx-style-img { position: absolute; inset: 0; background-size: cover; background-position: center; opacity: 0; transform: scale(1.04); transition: opacity 0.7s ease, transform 0.9s ease; }\n.fx-style-img.is-on { opacity: 1; transform: scale(1); }\n.fx-style-tag { position: absolute; left: 1rem; bottom: 1rem; font-size: 0.85rem; font-weight: 600; color: #fff; background: rgba(8,8,10,0.7); border: 1px solid var(--line-2); padding: 0.35rem 0.85rem; border-radius: 999px; }\n\n/* PROMPT → OUTPUT MORPH */\n.fx-morph-head { max-width: 40rem; margin-bottom: clamp(2.2rem, 4vw, 3.2rem); }\n.fx-morph-head .fx-sub { margin-top: 1rem; }\n.fx-morph {\n  display: grid; grid-template-columns: 1fr auto 1.1fr; gap: clamp(1.2rem, 3vw, 2.5rem); align-items: center;\n  opacity: 0; transform: translateY(20px);\n  transition: opacity 0.9s cubic-bezier(0.16,1,0.3,1), transform 0.9s cubic-bezier(0.16,1,0.3,1);\n}\n.fx-morph.is-shown { opacity: 1; transform: none; }\n@media (max-width: 780px) { .fx-morph { grid-template-columns: 1fr; } .fx-morph-arrow { transform: rotate(90deg); justify-self: center; } }\n.fx-morph-tag { display: block; font-size: 0.8rem; font-weight: 600; color: var(--violet); margin-bottom: 0.8rem; }\n.fx-morph-in {\n  background: var(--panel); border: 1px solid var(--line); border-radius: 14px;\n  padding: 1.5rem 1.6rem; min-height: 11rem; display: flex; flex-direction: column; justify-content: center;\n}\n.fx-morph-prompt {\n  font-family: var(--font-geist-mono), ui-monospace, monospace;\n  font-size: clamp(1rem, 1.7vw, 1.3rem); line-height: 1.5; color: var(--frost); margin: 0;\n}\n.fx-caret { display: inline-block; width: 0.6ch; height: 1.1em; vertical-align: -0.15em; margin-left: 0.15rem; background: var(--violet); animation: fxBlink 1s steps(2) infinite; }\n@keyframes fxBlink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }\n.fx-morph-arrow { display: grid; place-items: center; }\n.fx-morph-arrow span { position: relative; width: 2.4rem; height: 1px; background: var(--line-2); }\n.fx-morph-arrow span::after { content: \"\"; position: absolute; right: 0; top: 50%; width: 7px; height: 7px; border-top: 1px solid var(--violet); border-right: 1px solid var(--violet); transform: translateY(-50%) rotate(45deg); }\n.fx-morph-out .fx-morph-tag { text-align: left; }\n.fx-morph-frame {\n  position: relative; aspect-ratio: 3 / 2; border-radius: 14px; overflow: hidden;\n  border: 1px solid var(--line); background: var(--panel);\n}\n.fx-morph-img { position: absolute; inset: 0; background-size: cover; background-position: center; opacity: 0; transform: scale(1.05); transition: opacity 0.8s ease, transform 1.2s ease; }\n.fx-morph-img.is-on { opacity: 1; transform: scale(1); }\n.fx-morph-wipe {\n  position: absolute; inset: 0; z-index: 2; background: var(--bg);\n  clip-path: inset(0 0 0 0);\n  transition: clip-path 1.1s cubic-bezier(0.76, 0, 0.24, 1);\n}\n.fx-morph-wipe.is-open { clip-path: inset(0 0 0 100%); }\n\n/* LINEAGE TIMELINE */\n.fx-lineage { background: var(--bg-2); }\n.fx-lineage-head { max-width: 40rem; margin-bottom: clamp(2.5rem, 5vw, 4rem); }\n.fx-lineage-head .fx-sub { margin-top: 1rem; }\n.fx-timeline { list-style: none; margin: 0; padding: 0; position: relative; }\n.fx-timeline::before { content: \"\"; position: absolute; left: 6.5rem; top: 0.4rem; bottom: 0.4rem; width: 1px; background: var(--line); }\n@media (max-width: 620px) { .fx-timeline::before { left: 0.3rem; } }\n.fx-tl-item { position: relative; display: grid; grid-template-columns: 6.5rem 1fr; gap: 0 2rem; padding: 0 0 clamp(2rem, 4vw, 3rem); }\n.fx-tl-item:last-child { padding-bottom: 0; }\n@media (max-width: 620px) { .fx-tl-item { grid-template-columns: 1fr; gap: 0.4rem; padding-left: 1.6rem; } }\n.fx-tl-year { font-variant-numeric: tabular-nums; font-weight: 600; font-size: 1.05rem; color: var(--violet); padding-top: 0.05rem; }\n@media (max-width: 620px) { .fx-tl-year { grid-row: 1; } }\n.fx-tl-node { position: absolute; left: 6.5rem; top: 0.5rem; width: 11px; height: 11px; border-radius: 50%; background: var(--violet); transform: translateX(-5px); box-shadow: 0 0 0 4px var(--bg-2), 0 0 14px var(--violet-glow); }\n@media (max-width: 620px) { .fx-tl-node { left: 0.3rem; } }\n.fx-tl-name { font-weight: 600; font-size: clamp(1.2rem, 2.4vw, 1.6rem); letter-spacing: -0.02em; margin: 0 0 0.5rem; }\n.fx-tl-note { color: var(--mist); font-size: 1.02rem; margin: 0; max-width: 42rem; }\n\n/* COMMUNITY */\n.fx-community-head { max-width: 40rem; margin-bottom: clamp(2.2rem, 4vw, 3.2rem); }\n.fx-community-head .fx-sub { margin-top: 1rem; }\n.fx-creators { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }\n@media (max-width: 820px) { .fx-creators { grid-template-columns: repeat(2, 1fr); } }\n@media (max-width: 520px) { .fx-creators { grid-template-columns: 1fr; } }\n.fx-creator { border: 1px solid var(--line); border-radius: 14px; overflow: hidden; background: var(--panel); transition: border-color 0.35s, transform 0.5s cubic-bezier(0.16,1,0.3,1); }\n.fx-creator:hover { border-color: var(--line-2); transform: translateY(-4px); }\n.fx-creator-img { aspect-ratio: 4 / 3; background-size: cover; background-position: center; }\n.fx-creator-row { display: flex; align-items: center; gap: 0.75rem; padding: 0.9rem 1rem 1.05rem; }\n.fx-creator-avatar { display: grid; place-items: center; width: 2.1rem; height: 2.1rem; border-radius: 50%; color: #fff; font-weight: 600; font-size: 0.95rem; flex: 0 0 auto; }\n.fx-creator-handle { font-weight: 550; font-size: 0.98rem; margin: 0; }\n.fx-creator-craft { color: var(--faint); font-size: 0.86rem; margin: 0.1rem 0 0; }\n\n/* START CTA */\n.fx-start { position: relative; text-align: center; display: grid; place-items: center; min-height: 82vh; overflow: hidden; }\n.fx-start-bg { position: absolute; inset: -4% 0; background-size: cover; background-position: center; }\n.fx-start-scrim { position: absolute; inset: 0; background: radial-gradient(ellipse at center, rgba(8,8,10,0.55), rgba(8,8,10,0.9)); }\n.fx-start-inner { position: relative; max-width: 42rem; padding: 0 1rem; }\n.fx-start-title { font-weight: 620; font-size: clamp(2.1rem, 6vw, 4.2rem); line-height: 1.0; letter-spacing: -0.035em; margin: 0 0 1.2rem; }\n.fx-start-lede { color: var(--frost); opacity: 0.9; font-size: 1.15rem; max-width: 34rem; margin: 0 auto 2rem; }\n.fx-start-actions { justify-content: center; }\n\n/* fx-css-end */\n`\n","type":"registry:page"}],"assets":[{"url":"https://ui.artbloom.tech/r/asset/templates/flux/assets/g1.png","target":"public/flux/g1.png","bytes":2488802},{"url":"https://ui.artbloom.tech/r/asset/templates/flux/assets/g2.png","target":"public/flux/g2.png","bytes":2049428},{"url":"https://ui.artbloom.tech/r/asset/templates/flux/assets/g3.png","target":"public/flux/g3.png","bytes":2364357},{"url":"https://ui.artbloom.tech/r/asset/templates/flux/assets/g4.png","target":"public/flux/g4.png","bytes":1691269},{"url":"https://ui.artbloom.tech/r/asset/templates/flux/assets/g5.png","target":"public/flux/g5.png","bytes":2214789},{"url":"https://ui.artbloom.tech/r/asset/templates/flux/assets/g6.png","target":"public/flux/g6.png","bytes":2484856}],"meta":{"kind":"templates","categories":["landing"],"docs":"https://ui.artbloom.tech/artbloom/templates/flux"}}