diff --git a/package-lock.json b/package-lock.json index 146f2be..dc3ae5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@designcodeio/threeui", - "version": "1.2.0", + "version": "1.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@designcodeio/threeui", - "version": "1.2.0", + "version": "1.3.0", "license": "MIT", "dependencies": { "three128": "npm:three@0.128.0", diff --git a/package.json b/package.json index a2fdca0..e34ef77 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@designcodeio/threeui", - "version": "1.2.0", + "version": "1.3.0", "description": "Open-source React components and interactive templates from ThreeUI Community.", "license": "MIT", "type": "module", diff --git a/public/community-sync-report.json b/public/community-sync-report.json index cfb00ed..cbdb7a9 100644 --- a/public/community-sync-report.json +++ b/public/community-sync-report.json @@ -1,11 +1,11 @@ { "schemaVersion": 1, - "generatedAt": "2026-08-31T16:05:46.996Z", - "sourceCommit": "6b65f2dbec22ac58c56474f028aec5590e62d3c9", - "communityParents": 43, - "communityRoutes": 104, - "communityVariants": 163, - "excludedProParents": 108, + "generatedAt": "2026-09-03T05:35:30.118Z", + "sourceCommit": "65f7995b00f0f64323b217b353a529de99ec7fac", + "communityParents": 46, + "communityRoutes": 107, + "communityVariants": 166, + "excludedProParents": 109, "excludedBetaParents": 23, "layoutStylesSha256": "cee8ebe47ad857149f52f12bb12324a83856fc403ea1431f238aec4ecc385760", "rendererStylesSha256": "dc36aeb914df4c554164304292d75738a770e454b1e2710bac1ce4169d5fee2a", @@ -395,7 +395,8 @@ "induction-button", "plasma-button", "tactile-button", - "thinking-button" + "thinking-button", + "raking-light-pill" ], "controlKeys": [ "mode", @@ -409,7 +410,8 @@ "induction-button": [], "plasma-button": [], "tactile-button": [], - "thinking-button": [] + "thinking-button": [], + "raking-light-pill": [] } }, { @@ -436,7 +438,9 @@ "meridian-keycap-secondary", "halvorsen-arrow-pill", "aster-glass-access", - "aster-glass-arrow" + "aster-glass-arrow", + "ember-keycap", + "bloom-outline-button" ], "controlKeys": [ "mode", @@ -521,6 +525,18 @@ "hue", "saturation", "brightness" + ], + "ember-keycap": [ + "mode", + "hue", + "saturation", + "brightness" + ], + "bloom-outline-button": [ + "mode", + "hue", + "saturation", + "brightness" ] } }, @@ -594,6 +610,24 @@ ], "variantControlKeys": {} }, + { + "id": "orb-gallery", + "variantIds": [], + "controlKeys": [], + "variantControlKeys": {} + }, + { + "id": "3d-paper", + "variantIds": [], + "controlKeys": [], + "variantControlKeys": {} + }, + { + "id": "ashen-press", + "variantIds": [], + "controlKeys": [], + "variantControlKeys": {} + }, { "id": "sylva-living-world", "variantIds": [ diff --git a/public/source-code.json b/public/source-code.json index 2b54eee..b9919ea 100644 --- a/public/source-code.json +++ b/public/source-code.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "generatedAt": "2026-08-31T16:05:46.446Z", + "generatedAt": "2026-09-03T05:35:29.358Z", "readyIds": [ "kage-landing-page", "complete-shelf-landing-page", @@ -23,6 +23,9 @@ "liquid-metal-button", "character-carousel", "gallery", + "orb-gallery", + "3d-paper", + "ashen-press", "sylva-living-world", "temple-night", "landscape", @@ -65,6 +68,15 @@ "sha256": "7810e7163c027f654235032fb1eed48846b68bf80f5b1c1c4292e01694b71f3d", "code": "\n\n\n\n\nTowers\n\">\n\n\n\n
\n
\n \n
\n
\n
\n\n
\n
TOWERS
\n
CONSTRUCTION STUDY / 01
\n
\n \n \n \n \n \n
\n
\n\n
\n
FRAME / 365
\n
普請
\n
TENSHU GROWTH STUDY
\n
\n
“The viewpoint never moves.
\n
Stone, timber and tile converge
\n
from scattered heaps into place,
\n
assembling from the ground up.”
\n
\n
0105
\n
\n\n
\n
\n \n \n \n
\n
天守生長
\n
(Tsuku · 築)
\n
\n
ORIGINAL
RECONSTRUCTION
\n\n
準備 / READY
\n
0%
\n\n
\n \n
\n
0.00 / 4.40
\n
\n
\n\n\n\n\n\n" }, + { + "path": "src/shaders/landing-pages/LandingPageFrame.tsx", + "language": "tsx", + "role": "frame-component", + "bytes": 6756, + "lines": 178, + "sha256": "61de2cc50888aac4ac5557420b07fa47ed3543bb57c1e0055fafdefa53dbaa78", + "code": "import { useEffect, useRef, useState, type CSSProperties } from \"react\";\n\nimport {\n applyPageCustomization,\n postPageCustomization,\n type LandingPageCustomization,\n} from \"./pageTypography\";\n\nexport type LandingPageFrameProps = {\n /**\n * CSS selector for the authored visual layer when a complete page is reused\n * as a scene-only Background. The document stays untouched on disk; its UI\n * is suppressed only inside this catalog frame.\n */\n backgroundCanvasSelector?: string;\n /** Extra authored atmosphere layers (scrims, veils, vignettes) to retain. */\n backgroundVisualSelector?: string;\n className?: string;\n /** Loaded directly when the packaged document is byte-exact. */\n sourceUrl: string;\n /** Set only for derived variants; the frame renders this instead of fetching sourceUrl. */\n srcDoc?: string;\n style?: CSSProperties;\n title: string;\n /**\n * Typography and colour overrides, appended to the loaded document's own\n * head. The packaged file is never rewritten, so it stays byte-exact.\n */\n customization?: LandingPageCustomization;\n /**\n * Runs against the live frame on every load and whenever the callback's own\n * identity changes, which is how a page that exposes a scene API of its own\n * receives slider values. Memoize it on the values it reads.\n */\n applyScene?: (frame: HTMLIFrameElement) => void;\n};\n\nexport type LandingPageProps = Omit<\n LandingPageFrameProps,\n \"sourceUrl\" | \"title\" | \"customization\" | \"backgroundCanvasSelector\" | \"backgroundVisualSelector\"\n>;\n\nconst URL_FRAME_SANDBOX = \"allow-downloads allow-forms allow-modals allow-popups allow-same-origin allow-scripts\";\nconst SRCDOC_FRAME_SANDBOX = \"allow-downloads allow-forms allow-modals allow-popups allow-scripts\";\n\nconst BACKGROUND_PRESENTATION_STYLE_ID = \"threeui-background-presentation\";\n\nexport function applyBackgroundPresentation(\n frame: HTMLIFrameElement | null,\n backgroundCanvasSelector?: string,\n backgroundVisualSelector?: string,\n) {\n const document = frame?.contentDocument;\n if (!document) return;\n\n document.getElementById(BACKGROUND_PRESENTATION_STYLE_ID)?.remove();\n document.querySelectorAll(\"[data-threeui-background-layer]\").forEach((element) => {\n element.removeAttribute(\"data-threeui-background-layer\");\n element.removeAttribute(\"data-threeui-background-fill\");\n });\n\n if (!backgroundCanvasSelector) {\n document.documentElement.removeAttribute(\"data-threeui-presentation\");\n return;\n }\n\n const canvas = document.querySelector(backgroundCanvasSelector);\n if (!canvas) return;\n\n canvas.setAttribute(\"data-threeui-background-layer\", \"\");\n canvas.setAttribute(\"data-threeui-background-fill\", \"\");\n if (backgroundVisualSelector) {\n document.querySelectorAll(backgroundVisualSelector).forEach((element) => {\n element.setAttribute(\"data-threeui-background-layer\", \"\");\n });\n }\n\n document.documentElement.setAttribute(\"data-threeui-presentation\", \"background\");\n const presentationStyle = document.createElement(\"style\");\n presentationStyle.id = BACKGROUND_PRESENTATION_STYLE_ID;\n presentationStyle.textContent = `\n html[data-threeui-presentation=\"background\"],\n html[data-threeui-presentation=\"background\"] body {\n width: 100% !important;\n height: 100% !important;\n min-height: 100% !important;\n overflow: hidden !important;\n }\n html[data-threeui-presentation=\"background\"] body * {\n visibility: hidden !important;\n pointer-events: none !important;\n }\n html[data-threeui-presentation=\"background\"] [data-threeui-background-layer],\n html[data-threeui-presentation=\"background\"] [data-threeui-background-layer] * {\n visibility: visible !important;\n }\n html[data-threeui-presentation=\"background\"] [data-threeui-background-fill] {\n position: fixed !important;\n inset: 0 !important;\n width: 100vw !important;\n height: 100vh !important;\n max-width: none !important;\n max-height: none !important;\n margin: 0 !important;\n transform: none !important;\n }\n `;\n document.head.appendChild(presentationStyle);\n\n // The original renderer may have measured a split hero or section-sized\n // canvas. Re-run its authored resize path after the layer becomes full-frame.\n frame.contentWindow?.requestAnimationFrame(() => {\n frame.contentWindow?.dispatchEvent(new Event(\"resize\"));\n });\n}\n\nexport function LandingPageFrame({\n applyScene,\n backgroundCanvasSelector,\n backgroundVisualSelector,\n className = \"\",\n customization,\n sourceUrl,\n srcDoc,\n style,\n title,\n}: LandingPageFrameProps) {\n const [ready, setReady] = useState(false);\n const frameRef = useRef(null);\n\n // Re-applied on every change; the load handler covers the first paint and\n // any navigation the page does inside its own frame.\n useEffect(() => {\n applyPageCustomization(frameRef.current, customization);\n postPageCustomization(frameRef.current, customization);\n applyBackgroundPresentation(frameRef.current, backgroundCanvasSelector, backgroundVisualSelector);\n if (frameRef.current) applyScene?.(frameRef.current);\n }, [applyScene, backgroundCanvasSelector, backgroundVisualSelector, customization]);\n\n return (\n \n {\n applyPageCustomization(event.currentTarget, customization);\n postPageCustomization(event.currentTarget, customization);\n applyBackgroundPresentation(event.currentTarget, backgroundCanvasSelector, backgroundVisualSelector);\n applyScene?.(event.currentTarget);\n setReady(true);\n }}\n style={{\n position: \"absolute\",\n inset: 0,\n display: \"block\",\n width: \"100%\",\n height: \"100%\",\n border: 0,\n background: \"#080808\",\n // A background presentation begins life as the complete source page.\n // Keep that page visually sealed until onLoad has installed the\n // scene-only CSS, otherwise its authored navigation/copy can flash for\n // one paint before the canvas is isolated.\n opacity: backgroundCanvasSelector && !ready ? 0 : 1,\n pointerEvents: backgroundCanvasSelector && !ready ? \"none\" : \"auto\",\n }}\n />\n \n );\n}\n" + }, { "path": "src/shaders/landing-pages/LandingPages.tsx", "language": "tsx", @@ -130,6 +142,7 @@ "src/shaders/landing-pages/LandingPages.tsx", "src/shaders/landing-pages/pageTypography.ts", "src/shaders/landing-pages/pageRecipes.ts", + "src/shaders/landing-pages/LandingPageFrame.tsx", "src/shaders/community.css" ], "files": [ @@ -255,6 +268,7 @@ "runtime": "Full HTML + DOM/CSS + Three.js r165", "sharedFilePaths": [ "src/shaders/landing-pages/LandingPages.tsx", + "src/shaders/landing-pages/LandingPageFrame.tsx", "src/shaders/community.css" ], "files": [ @@ -277,6 +291,7 @@ "runtime": "Full HTML + DOM/CSS + embedded media", "sharedFilePaths": [ "src/shaders/landing-pages/LandingPages.tsx", + "src/shaders/landing-pages/LandingPageFrame.tsx", "src/shaders/community.css" ], "files": [ @@ -301,6 +316,7 @@ "src/shaders/landing-pages/LandingPages.tsx", "src/shaders/landing-pages/pageTypography.ts", "src/shaders/landing-pages/pageRecipes.ts", + "src/shaders/landing-pages/LandingPageFrame.tsx", "src/shaders/community.css" ], "files": [ @@ -353,6 +369,7 @@ "src/shaders/landing-pages/LandingPages.tsx", "src/shaders/landing-pages/pageTypography.ts", "src/shaders/landing-pages/pageRecipes.ts", + "src/shaders/landing-pages/LandingPageFrame.tsx", "src/shaders/community.css" ], "files": [ @@ -947,7 +964,7 @@ { "id": "star-portal", "exportName": "ShaderButtons", - "sourceCommit": "SHA-256 002199215e12", + "sourceCommit": "SHA-256 6f56c4f91814", "runtime": "Raw WebGL + Canvas 2D + CSS", "sharedFilePaths": [ "src/shaders/neuform-isolated/NeuformIsolatedEffects.tsx", @@ -958,10 +975,19 @@ "path": "src/shaders/shader-buttons/ShaderButtons.tsx", "language": "tsx", "role": "component", - "bytes": 2217, - "lines": 57, - "sha256": "b2599e35ee59ce4acec0fe868fbe34501b4eb1096a3fab80c6ede5d6876fece5", - "code": "import { lazy, Suspense, type ComponentType, type LazyExoticComponent } from \"react\";\n\nimport type { NeuformIsolatedEffectProps } from \"../neuform-isolated/NeuformIsolatedEffects\";\n\nexport type ShaderButtonVariant =\n | \"star-portal\"\n | \"ignition-button\"\n | \"induction-button\"\n | \"plasma-button\"\n | \"tactile-button\"\n | \"thinking-button\";\n\n/** @deprecated Variant names that shipped before a rename. Use {@link ShaderButtonVariant}. */\nexport type LegacyShaderButtonVariant = \"uploading-button\";\n\nexport type ShaderButtonsProps = NeuformIsolatedEffectProps & {\n variant?: ShaderButtonVariant | LegacyShaderButtonVariant;\n};\n\nconst LEGACY_SHADER_BUTTON_VARIANTS = {\n \"uploading-button\": \"thinking-button\",\n} satisfies Record;\n\nconst SHADER_BUTTON_VARIANTS = {\n \"star-portal\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.StarPortal })),\n ),\n \"ignition-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.IgnitionButton })),\n ),\n \"induction-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.InductionButton })),\n ),\n \"plasma-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.PlasmaButton })),\n ),\n \"tactile-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.TactileButton })),\n ),\n \"thinking-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.ThinkingButton })),\n ),\n} satisfies Record>>;\n\nexport function ShaderButtons({ variant = \"star-portal\", ...props }: ShaderButtonsProps) {\n const resolved = variant in LEGACY_SHADER_BUTTON_VARIANTS\n ? LEGACY_SHADER_BUTTON_VARIANTS[variant as LegacyShaderButtonVariant]\n : variant as ShaderButtonVariant;\n const Variant = SHADER_BUTTON_VARIANTS[resolved];\n\n return (\n \n \n \n );\n}\n" + "bytes": 2381, + "lines": 61, + "sha256": "6f56c4f9181492f60d642edc5317a635f0b462e509b34713d532e32d660638b0", + "code": "import { lazy, Suspense, type ComponentType, type LazyExoticComponent } from \"react\";\n\nimport type { NeuformIsolatedEffectProps } from \"../neuform-isolated/NeuformIsolatedEffects\";\n\nexport type ShaderButtonVariant =\n | \"star-portal\"\n | \"ignition-button\"\n | \"induction-button\"\n | \"plasma-button\"\n | \"tactile-button\"\n | \"thinking-button\"\n | \"raking-light-pill\";\n\n/** @deprecated Variant names that shipped before a rename. Use {@link ShaderButtonVariant}. */\nexport type LegacyShaderButtonVariant = \"uploading-button\";\n\nexport type ShaderButtonsProps = NeuformIsolatedEffectProps & {\n variant?: ShaderButtonVariant | LegacyShaderButtonVariant;\n};\n\nconst LEGACY_SHADER_BUTTON_VARIANTS = {\n \"uploading-button\": \"thinking-button\",\n} satisfies Record;\n\nconst SHADER_BUTTON_VARIANTS = {\n \"star-portal\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.StarPortal })),\n ),\n \"ignition-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.IgnitionButton })),\n ),\n \"induction-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.InductionButton })),\n ),\n \"plasma-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.PlasmaButton })),\n ),\n \"tactile-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.TactileButton })),\n ),\n \"thinking-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.ThinkingButton })),\n ),\n \"raking-light-pill\": lazy(() =>\n import(\"./RakingLightPillButton\").then((module) => ({ default: module.RakingLightPillButton })),\n ),\n} satisfies Record>>;\n\nexport function ShaderButtons({ variant = \"star-portal\", ...props }: ShaderButtonsProps) {\n const resolved = variant in LEGACY_SHADER_BUTTON_VARIANTS\n ? LEGACY_SHADER_BUTTON_VARIANTS[variant as LegacyShaderButtonVariant]\n : variant as ShaderButtonVariant;\n const Variant = SHADER_BUTTON_VARIANTS[resolved];\n\n return (\n \n \n \n );\n}\n" + }, + { + "path": "src/shaders/shader-buttons/RakingLightPillButton.tsx", + "language": "tsx", + "role": "variant-component", + "bytes": 10022, + "lines": 298, + "sha256": "1069b29edc532f9bffcdfce7ca072387096fae3b99368ccab04deb6190b1ad94", + "code": "import { useEffect, useRef } from \"react\";\n\nimport type { NeuformIsolatedEffectProps } from \"../neuform-isolated/NeuformIsolatedEffects\";\n\nconst VERTEX_SHADER = `\n attribute vec2 aPosition;\n varying vec2 vUv;\n\n void main() {\n vUv = aPosition * 0.5 + 0.5;\n gl_Position = vec4(aPosition, 0.0, 1.0);\n }\n`;\n\nconst FRAGMENT_SHADER = `\n precision highp float;\n\n varying vec2 vUv;\n uniform float uTime;\n uniform float uHover;\n uniform float uLight;\n uniform vec2 uSize;\n\n float hash21(vec2 p) {\n p = fract(p * vec2(443.897, 441.423));\n p += dot(p, p + 19.19);\n return fract(p.x * p.y);\n }\n\n float boxSD(vec2 p, vec2 b, float r) {\n vec2 d = abs(p) - b + r;\n return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0) - r;\n }\n\n void main() {\n vec2 p = (vUv - 0.5) * uSize;\n float radius = uSize.y * 0.5;\n float sd = boxSD(p, uSize * 0.5, radius);\n float mask = 1.0 - smoothstep(-1.2, 0.6, sd);\n if (mask < 0.004) discard;\n\n float t = uTime;\n vec2 q = p / uSize.y;\n float aspect = uSize.x / uSize.y;\n\n vec3 darkPlate = mix(vec3(0.028, 0.022, 0.028), vec3(0.068, 0.050, 0.056), vUv.y);\n vec3 lightPlate = mix(vec3(0.930, 0.884, 0.842), vec3(0.985, 0.957, 0.925), vUv.y);\n vec3 col = mix(darkPlate, lightPlate, uLight);\n\n float s = q.x + q.y * 1.15;\n float rays = pow(max(0.0, sin(s * 3.6 - t * 0.50)), 1.7) * 0.62\n + pow(max(0.0, sin(s * 8.1 + t * 0.29 + 1.3)), 2.2) * 0.38;\n float head = (fract(t * 0.11) * 2.4 - 1.2) * aspect * 0.5;\n rays *= 0.22 + 1.15 * exp(-pow((q.x - head) * 1.30, 2.0));\n vec3 rayInk = mix(vec3(1.00, 0.82, 0.56), vec3(0.48, 0.25, 0.24), uLight);\n col += rayInk * rays * (0.20 + 0.42 * uHover);\n\n float motes = 0.0;\n for (int i = 0; i < 2; i++) {\n float fi = float(i);\n vec2 g = q * (4.2 + fi * 2.4) + vec2(fi * 7.3, -t * (0.26 + fi * 0.15) + fi * 3.1);\n vec2 cell = floor(g);\n vec2 f = fract(g) - 0.5;\n float hs = hash21(cell + fi * 17.0);\n if (hs < 0.45) continue;\n vec2 offset = (vec2(hs, fract(hs * 31.7)) - 0.5) * 0.62;\n float twinkle = 0.30 + 0.70 * sin(t * (1.3 + hs * 2.2) + hs * 40.0);\n motes += smoothstep(0.20, 0.02, length(f - offset)) * max(twinkle, 0.0) * (0.30 + 0.70 * hs);\n }\n vec3 moteInk = mix(vec3(1.00, 0.95, 0.86), vec3(0.34, 0.18, 0.20), uLight);\n col += moteInk * motes * (0.34 + 0.60 * uHover);\n\n float alpha = mask * (0.88 + 0.12 * uHover);\n gl_FragColor = vec4(col * alpha, alpha);\n }\n`;\n\nconst STYLES = `\n .threeui-raking-light-pill-stage {\n --threeui-raking-light-pill-ink: #fff;\n --threeui-raking-light-pill-edge: rgba(255, 255, 255, .26);\n position: relative;\n display: grid;\n width: 100%;\n height: 100%;\n min-height: 240px;\n place-items: center;\n overflow: hidden;\n isolation: isolate;\n background: #160d0c;\n }\n .threeui-raking-light-pill-stage::before {\n content: \"\";\n position: absolute;\n inset: 0;\n z-index: -1;\n background:\n radial-gradient(circle at 68% 36%, rgba(126, 66, 43, .18), transparent 31%),\n radial-gradient(circle at 28% 74%, rgba(72, 25, 18, .28), transparent 38%),\n linear-gradient(132deg, #120b0a 0%, #21100d 52%, #0f0909 100%);\n }\n .threeui-raking-light-pill {\n position: relative;\n display: inline-flex;\n height: 45.9px;\n align-items: center;\n justify-content: center;\n padding: 0 23.4px;\n overflow: hidden;\n border: 1px solid var(--threeui-raking-light-pill-edge);\n border-radius: 999px;\n background: transparent;\n color: var(--threeui-raking-light-pill-ink);\n font: 400 18px/1 \"Switzer\", \"Neue Montreal\", -apple-system, \"Helvetica Neue\", Arial, sans-serif;\n white-space: nowrap;\n cursor: pointer;\n transition: border-color .45s cubic-bezier(.2, .75, .25, 1), transform .6s cubic-bezier(.2, .75, .25, 1);\n -webkit-font-smoothing: antialiased;\n }\n .threeui-raking-light-pill canvas {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n border-radius: inherit;\n pointer-events: none;\n }\n .threeui-raking-light-pill span {\n position: relative;\n z-index: 1;\n }\n .threeui-raking-light-pill:hover {\n border-color: rgba(255, 255, 255, .5);\n }\n .threeui-raking-light-pill:focus-visible {\n outline: 2px solid var(--threeui-raking-light-pill-ink);\n outline-offset: 5px;\n }\n .threeui-raking-light-pill-stage[data-mode=\"light\"] {\n --threeui-raking-light-pill-ink: #3f2c33;\n --threeui-raking-light-pill-edge: rgba(63, 44, 51, .28);\n background: #efe6dc;\n }\n .threeui-raking-light-pill-stage[data-mode=\"light\"]::before {\n background:\n radial-gradient(circle at 68% 36%, rgba(162, 102, 78, .13), transparent 31%),\n radial-gradient(circle at 28% 74%, rgba(102, 53, 49, .08), transparent 38%),\n linear-gradient(132deg, #f4ece3 0%, #e7d9cd 52%, #f8f2eb 100%);\n }\n .threeui-raking-light-pill-stage[data-mode=\"light\"] .threeui-raking-light-pill:hover {\n border-color: rgba(63, 44, 51, .5);\n }\n @media (prefers-reduced-motion: reduce) {\n .threeui-raking-light-pill { transition-duration: .01ms; }\n }\n`;\n\nfunction createShader(gl: WebGLRenderingContext, type: number, source: string) {\n const shader = gl.createShader(type);\n if (!shader) return null;\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n gl.deleteShader(shader);\n return null;\n }\n return shader;\n}\n\nexport function RakingLightPillButton({\n mode = \"dark\",\n hue = 0,\n saturation = 1,\n brightness = 1,\n className = \"\",\n style,\n}: NeuformIsolatedEffectProps) {\n const stageRef = useRef(null);\n const canvasRef = useRef(null);\n const hoverTargetRef = useRef(0);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n const stage = stageRef.current;\n if (!canvas || !stage) return undefined;\n\n const gl = canvas.getContext(\"webgl\", {\n alpha: true,\n antialias: true,\n depth: false,\n premultipliedAlpha: true,\n });\n if (!gl) return undefined;\n\n const vertexShader = createShader(gl, gl.VERTEX_SHADER, VERTEX_SHADER);\n const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, FRAGMENT_SHADER);\n if (!vertexShader || !fragmentShader) return undefined;\n\n const program = gl.createProgram();\n if (!program) return undefined;\n gl.attachShader(program, vertexShader);\n gl.attachShader(program, fragmentShader);\n gl.linkProgram(program);\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) return undefined;\n\n const positionBuffer = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);\n\n const position = gl.getAttribLocation(program, \"aPosition\");\n const timeUniform = gl.getUniformLocation(program, \"uTime\");\n const hoverUniform = gl.getUniformLocation(program, \"uHover\");\n const lightUniform = gl.getUniformLocation(program, \"uLight\");\n const sizeUniform = gl.getUniformLocation(program, \"uSize\");\n gl.useProgram(program);\n gl.enableVertexAttribArray(position);\n gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n\n let width = 1;\n let height = 1;\n const resize = () => {\n const rect = canvas.getBoundingClientRect();\n const dpr = Math.min(window.devicePixelRatio || 1, 2);\n width = Math.max(1, rect.width);\n height = Math.max(1, rect.height);\n canvas.width = Math.max(1, Math.round(width * dpr));\n canvas.height = Math.max(1, Math.round(height * dpr));\n gl.viewport(0, 0, canvas.width, canvas.height);\n };\n resize();\n\n const resizeObserver = new ResizeObserver(resize);\n resizeObserver.observe(canvas);\n\n const motionQuery = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n let visible = true;\n const visibilityObserver = new IntersectionObserver(([entry]) => {\n visible = entry?.isIntersecting ?? true;\n }, { rootMargin: \"80px\" });\n visibilityObserver.observe(stage);\n\n const startedAt = performance.now();\n let hover = 0;\n let animationFrame = 0;\n const render = (now: number) => {\n hover += (hoverTargetRef.current - hover) * 0.085;\n if (visible && !document.hidden) {\n gl.clearColor(0, 0, 0, 0);\n gl.clear(gl.COLOR_BUFFER_BIT);\n gl.useProgram(program);\n gl.uniform1f(timeUniform, motionQuery.matches ? 2.4 : (now - startedAt) / 1000);\n gl.uniform1f(hoverUniform, hover);\n gl.uniform1f(lightUniform, mode === \"light\" ? 1 : 0);\n gl.uniform2f(sizeUniform, width, height);\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n }\n animationFrame = requestAnimationFrame(render);\n };\n animationFrame = requestAnimationFrame(render);\n\n return () => {\n cancelAnimationFrame(animationFrame);\n visibilityObserver.disconnect();\n resizeObserver.disconnect();\n gl.deleteBuffer(positionBuffer);\n gl.deleteProgram(program);\n gl.deleteShader(vertexShader);\n gl.deleteShader(fragmentShader);\n };\n }, [mode]);\n\n return (\n \n \n { hoverTargetRef.current = 1; }}\n onPointerLeave={() => { hoverTargetRef.current = 0; }}\n onBlur={() => { hoverTargetRef.current = 0; }}\n onFocus={() => { hoverTargetRef.current = 1; }}\n >\n \n Field Notes 2026\n \n \n );\n}\n" }, { "path": "src/shaders/neuform-isolated/sources/imaginie-starfield.html", @@ -1023,7 +1049,7 @@ { "id": "rectangle-buttons", "exportName": "RectangleButtons", - "sourceCommit": "SHA-256 f4f3dd8422cd", + "sourceCommit": "SHA-256 ff30e28c2781", "runtime": "DOM + CSS", "sharedFilePaths": [ "src/shaders/neuform-isolated/NeuformIsolatedEffects.tsx", @@ -1034,10 +1060,10 @@ "path": "src/shaders/rectangle-buttons/RectangleButtons.tsx", "language": "tsx", "role": "component", - "bytes": 29427, - "lines": 688, - "sha256": "f4f3dd8422cd20a464d3d41bbdb9595aeed6beee1efd504754f20a8c2a7de1b0", - "code": "import { lazy, Suspense, type ComponentType, type LazyExoticComponent, type ReactNode } from \"react\";\n\nimport {\n DarkGlassButton as DarkGlassRectangle,\n type SectionCompositionProps,\n} from \"../section-elements/SectionElements\";\nimport type { LumenCtaProps } from \"../lumen-cta/LumenCta\";\nimport type { NeuformIsolatedEffectProps } from \"../neuform-isolated/NeuformIsolatedEffects\";\n\nexport type RectangleButtonVariant =\n | \"dark-pill\"\n | \"launch-button\"\n | \"dot-border-button\"\n | \"floating-dots-cta\"\n | \"sliding-text-cta\"\n | \"gradient-beam-cta\"\n | \"gradient-pill-button\"\n | \"generate-button\"\n | \"glassmorphism-cta\"\n | \"spinning-border-button\"\n | \"gradient-cta\"\n | \"lumen-cta\"\n | \"lumen-cta-ghost\"\n | \"trochil-signal\"\n | \"attune-thermal\"\n | \"tideform-outline\"\n | \"understory-arrow-pill\"\n | \"meridian-keycap-primary\"\n | \"meridian-keycap-secondary\"\n | \"halvorsen-arrow-pill\"\n | \"aster-glass-access\"\n | \"aster-glass-arrow\";\n\ntype SelectedPageButtonVariant = Extract<\n RectangleButtonVariant,\n | \"trochil-signal\"\n | \"attune-thermal\"\n | \"tideform-outline\"\n | \"understory-arrow-pill\"\n | \"meridian-keycap-primary\"\n | \"meridian-keycap-secondary\"\n | \"halvorsen-arrow-pill\"\n | \"aster-glass-access\"\n | \"aster-glass-arrow\"\n>;\n\ntype IsolatedRectangleVariant = Exclude<\n RectangleButtonVariant,\n \"dark-pill\" | \"lumen-cta\" | \"lumen-cta-ghost\" | SelectedPageButtonVariant\n>;\n\nexport type RectangleButtonsProps = SectionCompositionProps &\n NeuformIsolatedEffectProps &\n Omit & {\n variant?: RectangleButtonVariant;\n };\n\nconst LumenCta = lazy(() =>\n import(\"../lumen-cta/LumenCta\").then((module) => ({ default: module.LumenCta })),\n);\n\nconst RECTANGLE_VARIANTS = {\n \"launch-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.LaunchButton })),\n ),\n \"dot-border-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.DotBorderButton })),\n ),\n \"floating-dots-cta\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.FloatingDotsCta })),\n ),\n \"sliding-text-cta\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.SlidingTextCta })),\n ),\n \"gradient-beam-cta\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.GradientBeamCta })),\n ),\n \"gradient-pill-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.GradientPillButton })),\n ),\n \"generate-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.GenerateButton })),\n ),\n \"glassmorphism-cta\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.GlassmorphismCta })),\n ),\n \"spinning-border-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.SpinningBorderButton })),\n ),\n \"gradient-cta\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.GradientCta })),\n ),\n} satisfies Record>>;\n\nconst SELECTED_PAGE_BUTTON_VARIANTS = new Set([\n \"trochil-signal\",\n \"attune-thermal\",\n \"tideform-outline\",\n \"understory-arrow-pill\",\n \"meridian-keycap-primary\",\n \"meridian-keycap-secondary\",\n \"halvorsen-arrow-pill\",\n \"aster-glass-access\",\n \"aster-glass-arrow\",\n]);\n\nfunction isSelectedPageButtonVariant(variant: RectangleButtonVariant): variant is SelectedPageButtonVariant {\n return SELECTED_PAGE_BUTTON_VARIANTS.has(variant);\n}\n\nconst SELECTED_PAGE_BUTTON_STYLES = `\n.threeui-page-button-stage {\n --threeui-page-ink: #fff;\n position: relative;\n display: grid;\n width: 100%;\n height: 100%;\n min-height: 240px;\n place-items: center;\n overflow: hidden;\n isolation: isolate;\n background: #050505;\n color: var(--threeui-page-ink);\n font-family: Inter, \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n}\n.threeui-page-button-stage::before {\n content: \"\";\n position: absolute;\n inset: 0;\n z-index: -1;\n pointer-events: none;\n}\n.threeui-page-button {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 0;\n font: inherit;\n text-decoration: none;\n white-space: nowrap;\n cursor: pointer;\n -webkit-font-smoothing: antialiased;\n}\n.threeui-page-button:focus-visible {\n outline: 2px solid var(--threeui-page-ink);\n outline-offset: 5px;\n}\n.threeui-page-button-stage--trochil {\n background: #030303;\n}\n.threeui-page-button-stage--trochil::before {\n background: radial-gradient(circle at 71% 49%, rgba(251, 215, 54, .18), transparent 31%);\n opacity: .72;\n}\n.threeui-page-button--trochil {\n height: 52px;\n padding: 0 30px;\n overflow: hidden;\n border: 1px solid rgba(255, 255, 255, .27);\n background: linear-gradient(180deg, rgba(255, 255, 255, .07), rgba(0, 0, 0, .36));\n box-shadow: 0 15px 36px rgba(0, 0, 0, .48), inset 0 1px rgba(255, 255, 255, .08);\n color: rgba(255, 255, 255, .82);\n font-size: 14px;\n font-weight: 500;\n transition: border-color .28s ease, color .28s ease, transform .28s ease, box-shadow .28s ease;\n}\n.threeui-page-button--trochil::before {\n content: \"\";\n position: absolute;\n inset: -1px;\n background: linear-gradient(105deg, transparent 17%, rgba(251, 215, 54, .25) 48%, transparent 76%);\n transform: translateX(-125%);\n transition: transform .65s cubic-bezier(.22, .61, .36, 1);\n}\n.threeui-page-button--trochil span { position: relative; }\n.threeui-page-button--trochil:hover {\n border-color: rgba(251, 215, 54, .58);\n color: #fff;\n transform: translateY(-2px);\n box-shadow: 0 18px 42px rgba(0, 0, 0, .58), 0 0 24px rgba(251, 215, 54, .12);\n}\n.threeui-page-button--trochil:hover::before { transform: translateX(125%); }\n.threeui-page-button-stage--attune {\n background: #070709;\n}\n.threeui-page-button-stage--attune::before {\n background: radial-gradient(circle at 50% 50%, rgba(255, 122, 20, .12), transparent 32%);\n}\n.threeui-page-button--attune {\n gap: 10px;\n height: 52px;\n padding: 0 22px;\n border-radius: 11px;\n background: linear-gradient(180deg, #ffa347 0%, #ff7a14 48%, #f2610a 100%);\n box-shadow: inset 0 1px rgba(255, 255, 255, .26), 0 8px 28px rgba(255, 122, 20, .28);\n color: #1a0e04;\n font-size: 14px;\n font-weight: 650;\n letter-spacing: .012em;\n transition: filter .18s ease, transform .18s ease, box-shadow .18s ease;\n}\n.threeui-page-button--attune svg { width: 8px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.7; }\n.threeui-page-button--attune:hover { filter: brightness(1.09); transform: translateY(-2px); box-shadow: inset 0 1px rgba(255, 255, 255, .34), 0 12px 34px rgba(255, 122, 20, .36); }\n.threeui-page-button--attune:active { transform: translateY(1px); }\n.threeui-page-button-stage--tideform {\n background: #16181a;\n font-family: \"Roboto Mono\", \"SFMono-Regular\", Consolas, monospace;\n}\n.threeui-page-button-stage--tideform::before {\n background: radial-gradient(ellipse at 76% 44%, rgba(255, 122, 24, .12), transparent 34%);\n}\n.threeui-page-button--tideform {\n gap: 18px;\n padding: 17px 24px;\n border: 1px solid rgba(255, 255, 255, .22);\n background: rgba(255, 255, 255, .055);\n box-shadow: inset 0 1px rgba(255, 255, 255, .035);\n color: #fff;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: .16em;\n text-transform: uppercase;\n transition: border-color .25s ease, background-color .25s ease, color .25s ease;\n}\n.threeui-page-button--tideform svg { width: 21px; height: 9px; fill: none; stroke: currentColor; stroke-width: 1.25; transition: transform .25s cubic-bezier(.2, .8, .2, 1); }\n.threeui-page-button--tideform:hover { border-color: #ff7a18; color: #ff7a18; background: rgba(255, 122, 24, .14); }\n.threeui-page-button--tideform:hover svg { transform: translateX(4px); }\n.threeui-page-button-stage--understory {\n --threeui-page-ink: #27222d;\n background: #f2efe7;\n color: #27222d;\n}\n.threeui-page-button-stage--understory::before {\n background: none;\n}\n.threeui-page-button--arrow-pill {\n gap: 18px;\n height: 54px;\n padding: 0 11px 0 36px;\n border-radius: 999px;\n font-size: 17px;\n letter-spacing: -.005em;\n transition: background .3s ease, transform .3s ease, color .3s ease;\n}\n.threeui-page-button--arrow-pill .threeui-page-button__disc {\n display: grid;\n width: 32px;\n height: 32px;\n flex: none;\n place-items: center;\n border-radius: 50%;\n}\n.threeui-page-button--arrow-pill svg { width: 11px; height: 11px; fill: none; stroke: currentColor; stroke-width: 2; transition: transform .3s ease; }\n.threeui-page-button--arrow-pill:hover { transform: translateY(-2px); }\n.threeui-page-button--arrow-pill:hover svg { transform: translateX(2px); }\n.threeui-page-button--understory {\n background: #9b78d0;\n color: #fff;\n box-shadow: 0 14px 30px rgba(82, 53, 112, .18);\n}\n.threeui-page-button--understory .threeui-page-button__disc { background: #f6f3ed; color: #9b78d0; }\n.threeui-page-button--understory:hover { background: #8d69c5; }\n.threeui-page-button-stage--halvorsen {\n background: #111113;\n}\n.threeui-page-button-stage--halvorsen::before {\n background: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, .06), transparent 35%);\n}\n.threeui-page-button--halvorsen {\n background: #f0eee7;\n color: #111113;\n box-shadow: 0 14px 34px rgba(0, 0, 0, .32);\n}\n.threeui-page-button--halvorsen .threeui-page-button__disc { background: #111113; color: #f0eee7; }\n.threeui-page-button--halvorsen:hover { background: #fff; }\n.threeui-page-button-stage--meridian {\n background: #04070d;\n}\n.threeui-page-button-stage--meridian::before {\n background: radial-gradient(circle at 50% 44%, rgba(77, 163, 255, .13), transparent 34%);\n}\n.threeui-page-button--meridian {\n height: 62px;\n padding: 0 30px;\n border-radius: 15px;\n flex-shrink: 0;\n background: #0c1017;\n box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .07);\n}\n.threeui-page-button--meridian::before {\n content: \"\";\n position: absolute;\n inset: 3px 4px 9px;\n border-radius: 12px;\n background: linear-gradient(180deg, #2a3142, #1c2230);\n box-shadow: 0 5px 0 #080b11, 0 9px 15px -2px rgba(0, 0, 0, .62), inset 0 1px rgba(255, 255, 255, .11);\n transition: transform .13s cubic-bezier(.22, .61, .36, 1), box-shadow .13s cubic-bezier(.22, .61, .36, 1);\n}\n.threeui-page-button--meridian > span:not(.threeui-page-button__led) {\n position: relative;\n z-index: 1;\n color: #dde5f2;\n font-size: 14.5px;\n font-weight: 600;\n letter-spacing: -.005em;\n}\n.threeui-page-button--meridian .threeui-page-button__led {\n position: absolute;\n z-index: 1;\n top: 12px;\n left: 50%;\n width: 3px;\n height: 3px;\n border-radius: 50%;\n background: rgba(255, 255, 255, .16);\n transform: translateX(-50%);\n transition: background .22s ease, box-shadow .22s ease;\n}\n.threeui-page-button--meridian:hover::before { transform: translateY(4px); box-shadow: 0 1px 0 #080b11, 0 3px 8px -2px rgba(0, 0, 0, .55), inset 0 1px rgba(255, 255, 255, .11); }\n.threeui-page-button--meridian:hover .threeui-page-button__led { background: #4da3ff; box-shadow: 0 0 7px #4da3ff; }\n.threeui-page-button--meridian-primary { background: #050f1c; box-shadow: inset 0 0 0 1px rgba(77, 163, 255, .22); }\n.threeui-page-button--meridian-primary::before { background: linear-gradient(180deg, #6db6ff, #2e85e8); box-shadow: 0 5px 0 #0b4a86, 0 9px 18px -2px rgba(21, 88, 158, .5), inset 0 1px rgba(255, 255, 255, .5); }\n.threeui-page-button--meridian-primary > span:not(.threeui-page-button__led) { color: #04182f; }\n.threeui-page-button--meridian-primary .threeui-page-button__led { background: rgba(4, 24, 47, .3); }\n.threeui-page-button--meridian-primary:hover::before { box-shadow: 0 1px 0 #0b4a86, 0 3px 10px -2px rgba(21, 88, 158, .45), inset 0 1px rgba(255, 255, 255, .5); }\n.threeui-page-button--meridian-primary:hover .threeui-page-button__led { background: #04182f; box-shadow: 0 0 6px rgba(4, 24, 47, .55); }\n.threeui-page-button-stage--aster {\n background: #101010;\n font-family: \"Roboto Flex\", \"Arial Narrow\", Inter, \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n}\n.threeui-page-button-stage--aster::before {\n background:\n radial-gradient(circle at 64% 42%, rgba(255, 255, 255, .07), transparent 21%),\n radial-gradient(circle at 38% 62%, rgba(255, 255, 255, .035), transparent 27%);\n}\n.threeui-page-button--aster {\n height: 52px;\n border-radius: 12px;\n isolation: isolate;\n background: rgba(255, 255, 255, .055);\n color: #f1f1f1;\n font-size: 17px;\n font-variation-settings: \"wdth\" 100, \"opsz\" 14;\n letter-spacing: -.004em;\n box-shadow: 0 12px 34px -22px rgba(255, 255, 255, .35);\n backdrop-filter: blur(16px) saturate(1.25);\n -webkit-backdrop-filter: blur(16px) saturate(1.25);\n transition: transform .5s cubic-bezier(.22, 1, .36, 1), background .4s ease, color .4s ease, box-shadow .5s cubic-bezier(.22, 1, .36, 1);\n}\n.threeui-page-button--aster::before {\n content: \"\";\n position: absolute;\n inset: 0;\n z-index: -1;\n border-radius: inherit;\n padding: 1px;\n background: linear-gradient(148deg, rgba(255, 255, 255, .72), rgba(255, 255, 255, .16) 34%, rgba(255, 255, 255, .05) 58%, rgba(255, 255, 255, .34));\n mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);\n mask-composite: exclude;\n -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);\n -webkit-mask-composite: xor;\n pointer-events: none;\n transition: filter .45s cubic-bezier(.22, 1, .36, 1);\n}\n.threeui-page-button--aster::after {\n content: \"\";\n position: absolute;\n inset: 0;\n z-index: -1;\n border-radius: inherit;\n background: linear-gradient(180deg, rgba(255, 255, 255, .13), transparent 46%);\n pointer-events: none;\n}\n.threeui-page-button--aster > span { position: relative; z-index: 1; }\n.threeui-page-button--aster:hover {\n transform: translateY(-1px);\n background: rgba(255, 255, 255, .15);\n color: #fff;\n box-shadow: 0 8px 26px -12px rgba(255, 255, 255, .42);\n}\n.threeui-page-button--aster:hover::before { filter: brightness(1.55); }\n.threeui-page-button--aster:active { transform: translateY(0); }\n.threeui-page-button--aster-access { padding: 0 31px; }\n.threeui-page-button--aster-arrow { gap: 17px; padding: 0 7px 0 22px; }\n.threeui-page-button--aster-arrow .threeui-page-button__chip {\n display: grid;\n width: 34px;\n height: 34px;\n flex: none;\n place-items: center;\n border-radius: 9px;\n background: rgba(255, 255, 255, .10);\n}\n.threeui-page-button--aster-arrow .threeui-page-button__chip::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n padding: 1px;\n background: linear-gradient(150deg, rgba(255, 255, 255, .75), rgba(255, 255, 255, .10) 62%, rgba(255, 255, 255, .42));\n mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);\n mask-composite: exclude;\n -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);\n -webkit-mask-composite: xor;\n pointer-events: none;\n}\n.threeui-page-button--aster-arrow svg {\n width: 15px;\n height: 15px;\n fill: none;\n stroke: currentColor;\n stroke-width: 1.4;\n stroke-linecap: round;\n stroke-linejoin: round;\n transition: transform .5s cubic-bezier(.22, 1, .36, 1);\n}\n.threeui-page-button--aster-arrow:hover svg { transform: translateX(2px); }\n\n/* ---------------------------------------------------------------- *\n The other ground\n\n Eight of these treatments were drawn on their page's dark ground and\n Understory on its paper one, so the opposite mode is a re-tone rather\n than a wash: the stage takes the other ground, and every value that\n ground would swallow — edge, ink, sheen, cast shadow — is restated at\n the weight it was authored to read at. Geometry and motion never move.\n * ---------------------------------------------------------------- */\n.threeui-page-button-stage[data-mode=\"light\"] { --threeui-page-ink: #14161a; }\n\n/* Trochil — the amber signal sheen, now over warm paper */\n.threeui-page-button-stage--trochil[data-mode=\"light\"] { background: #f5f3ee; }\n.threeui-page-button-stage--trochil[data-mode=\"light\"]::before {\n background: radial-gradient(circle at 71% 49%, rgba(196, 142, 6, .22), transparent 31%);\n opacity: .9;\n}\n.threeui-page-button-stage--trochil[data-mode=\"light\"] .threeui-page-button--trochil {\n border-color: rgba(20, 19, 14, .22);\n background: linear-gradient(180deg, rgba(255, 255, 255, .95), rgba(20, 19, 14, .05));\n box-shadow: 0 14px 30px rgba(20, 19, 14, .13), inset 0 1px rgba(255, 255, 255, .9);\n color: rgba(20, 19, 14, .78);\n}\n.threeui-page-button-stage--trochil[data-mode=\"light\"] .threeui-page-button--trochil::before {\n background: linear-gradient(105deg, transparent 17%, rgba(196, 142, 6, .3) 48%, transparent 76%);\n}\n.threeui-page-button-stage--trochil[data-mode=\"light\"] .threeui-page-button--trochil:hover {\n border-color: rgba(176, 126, 4, .62);\n color: #14130e;\n box-shadow: 0 18px 38px rgba(20, 19, 14, .17), 0 0 22px rgba(196, 142, 6, .2);\n}\n\n/* Attune — the thermal cap already carries its own ink, so only the ground\n and the cast heat change */\n.threeui-page-button-stage--attune[data-mode=\"light\"] { background: #faf4ee; }\n.threeui-page-button-stage--attune[data-mode=\"light\"]::before {\n background: radial-gradient(circle at 50% 50%, rgba(255, 122, 20, .18), transparent 32%);\n}\n.threeui-page-button-stage--attune[data-mode=\"light\"] .threeui-page-button--attune {\n box-shadow: inset 0 1px rgba(255, 255, 255, .5), 0 8px 24px rgba(226, 92, 6, .28);\n}\n.threeui-page-button-stage--attune[data-mode=\"light\"] .threeui-page-button--attune:hover {\n box-shadow: inset 0 1px rgba(255, 255, 255, .58), 0 12px 30px rgba(226, 92, 6, .34);\n}\n\n/* Tideform — the outline drops to graphite and the hot state to a deeper\n orange, which is where it clears text contrast on paper */\n.threeui-page-button-stage--tideform[data-mode=\"light\"] { background: #eeece7; --threeui-page-ink: #16181a; }\n.threeui-page-button-stage--tideform[data-mode=\"light\"]::before {\n background: radial-gradient(ellipse at 76% 44%, rgba(214, 96, 12, .16), transparent 34%);\n}\n.threeui-page-button-stage--tideform[data-mode=\"light\"] .threeui-page-button--tideform {\n border-color: rgba(22, 24, 26, .26);\n background: rgba(22, 24, 26, .035);\n box-shadow: inset 0 1px rgba(255, 255, 255, .7);\n color: #16181a;\n}\n.threeui-page-button-stage--tideform[data-mode=\"light\"] .threeui-page-button--tideform:hover {\n border-color: #c25a0c;\n background: rgba(194, 90, 12, .1);\n color: #b4530b;\n}\n\n/* Understory runs the other way: the violet capsule is the constant, and\n dark mode is the ground it was never given */\n.threeui-page-button-stage--understory[data-mode=\"dark\"] {\n --threeui-page-ink: #ece7e0;\n background: #17141b;\n color: #ece7e0;\n}\n.threeui-page-button-stage--understory[data-mode=\"dark\"]::before {\n background: radial-gradient(circle at 50% 50%, rgba(155, 120, 208, .16), transparent 34%);\n}\n.threeui-page-button-stage--understory[data-mode=\"dark\"] .threeui-page-button--understory {\n background: #a482d9;\n box-shadow: 0 16px 34px rgba(0, 0, 0, .5);\n}\n.threeui-page-button-stage--understory[data-mode=\"dark\"] .threeui-page-button--understory:hover { background: #b08fe2; }\n\n/* Halvorsen is a maximum-contrast pill, so on paper it inverts rather than\n fades — the capsule takes the ink and the endcap takes the paper */\n.threeui-page-button-stage--halvorsen[data-mode=\"light\"] { background: #eeece5; }\n.threeui-page-button-stage--halvorsen[data-mode=\"light\"]::before {\n background: radial-gradient(circle at 50% 50%, rgba(17, 17, 19, .05), transparent 35%);\n}\n.threeui-page-button-stage--halvorsen[data-mode=\"light\"] .threeui-page-button--halvorsen {\n background: #111113;\n box-shadow: 0 14px 30px rgba(17, 17, 19, .22);\n color: #f0eee7;\n}\n.threeui-page-button-stage--halvorsen[data-mode=\"light\"] .threeui-page-button--halvorsen .threeui-page-button__disc {\n background: #f0eee7;\n color: #111113;\n}\n.threeui-page-button-stage--halvorsen[data-mode=\"light\"] .threeui-page-button--halvorsen:hover { background: #000; }\n\n/* Meridian — the keycap keeps its travel and its hard base edge; only the\n housing, the cap face, and the legend follow the light ground */\n.threeui-page-button-stage--meridian[data-mode=\"light\"] { background: #e9eef5; --threeui-page-ink: #04070d; }\n.threeui-page-button-stage--meridian[data-mode=\"light\"]::before {\n background: radial-gradient(circle at 50% 44%, rgba(46, 133, 232, .2), transparent 34%);\n}\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian {\n background: #dfe5ee;\n box-shadow: inset 0 0 0 1px rgba(4, 7, 13, .09);\n}\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian::before {\n background: linear-gradient(180deg, #fbfcfe, #e3e8f1);\n box-shadow: 0 5px 0 #bcc5d3, 0 9px 15px -2px rgba(4, 7, 13, .2), inset 0 1px #fff;\n}\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian > span:not(.threeui-page-button__led) { color: #1d2532; }\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian .threeui-page-button__led { background: rgba(4, 7, 13, .18); }\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian:hover::before { box-shadow: 0 1px 0 #bcc5d3, 0 3px 8px -2px rgba(4, 7, 13, .18), inset 0 1px #fff; }\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian:hover .threeui-page-button__led { background: #2e85e8; box-shadow: 0 0 7px #2e85e8; }\n/* the primary cap stays blue on both grounds, so it is restated after the\n secondary block rather than inheriting it */\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian-primary {\n background: #dbe7f6;\n box-shadow: inset 0 0 0 1px rgba(46, 133, 232, .3);\n}\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian-primary::before {\n background: linear-gradient(180deg, #6db6ff, #2e85e8);\n box-shadow: 0 5px 0 #1d6dc4, 0 9px 18px -2px rgba(21, 88, 158, .32), inset 0 1px rgba(255, 255, 255, .5);\n}\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian-primary > span:not(.threeui-page-button__led) { color: #04182f; }\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian-primary .threeui-page-button__led { background: rgba(4, 24, 47, .3); }\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian-primary:hover::before { box-shadow: 0 1px 0 #1d6dc4, 0 3px 10px -2px rgba(21, 88, 158, .3), inset 0 1px rgba(255, 255, 255, .5); }\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian-primary:hover .threeui-page-button__led { background: #04182f; box-shadow: 0 0 6px rgba(4, 24, 47, .55); }\n\n/* Aster — the glass is a white edge lit from one corner, so on paper the\n whole build flips to graphite and the hover brightening becomes darkening */\n.threeui-page-button-stage--aster[data-mode=\"light\"] { background: #eceae7; --threeui-page-ink: #101010; }\n.threeui-page-button-stage--aster[data-mode=\"light\"]::before {\n background:\n radial-gradient(circle at 64% 42%, rgba(16, 16, 16, .06), transparent 21%),\n radial-gradient(circle at 38% 62%, rgba(16, 16, 16, .035), transparent 27%);\n}\n.threeui-page-button-stage--aster[data-mode=\"light\"] .threeui-page-button--aster {\n background: rgba(16, 16, 16, .05);\n box-shadow: 0 14px 34px -22px rgba(16, 16, 16, .55);\n color: #16181a;\n}\n.threeui-page-button-stage--aster[data-mode=\"light\"] .threeui-page-button--aster::before {\n background: linear-gradient(148deg, rgba(16, 16, 16, .45), rgba(16, 16, 16, .12) 34%, rgba(16, 16, 16, .05) 58%, rgba(16, 16, 16, .26));\n}\n.threeui-page-button-stage--aster[data-mode=\"light\"] .threeui-page-button--aster::after {\n background: linear-gradient(180deg, rgba(255, 255, 255, .8), transparent 46%);\n}\n.threeui-page-button-stage--aster[data-mode=\"light\"] .threeui-page-button--aster:hover {\n background: rgba(16, 16, 16, .1);\n box-shadow: 0 8px 26px -12px rgba(16, 16, 16, .3);\n color: #000;\n}\n.threeui-page-button-stage--aster[data-mode=\"light\"] .threeui-page-button--aster:hover::before { filter: brightness(.68); }\n.threeui-page-button-stage--aster[data-mode=\"light\"] .threeui-page-button--aster-arrow .threeui-page-button__chip { background: rgba(16, 16, 16, .07); }\n.threeui-page-button-stage--aster[data-mode=\"light\"] .threeui-page-button--aster-arrow .threeui-page-button__chip::before {\n background: linear-gradient(150deg, rgba(16, 16, 16, .5), rgba(16, 16, 16, .1) 62%, rgba(16, 16, 16, .3));\n}\n@media (prefers-reduced-motion: reduce) {\n .threeui-page-button, .threeui-page-button::before, .threeui-page-button svg { transition-duration: .01ms !important; }\n}\n`;\n\ntype SelectedPageButtonProps = Pick & {\n variant: SelectedPageButtonVariant;\n};\n\nfunction ArrowIcon({ long = false }: { long?: boolean }) {\n return long ? (\n \n ) : (\n \n );\n}\n\nfunction SelectedPageButton({ variant, mode = \"dark\", className = \"\", style }: SelectedPageButtonProps) {\n let theme = \"trochil\";\n let button: ReactNode = null;\n\n switch (variant) {\n case \"trochil-signal\":\n button = ;\n break;\n case \"attune-thermal\":\n theme = \"attune\";\n button = ;\n break;\n case \"tideform-outline\":\n theme = \"tideform\";\n button = ;\n break;\n case \"understory-arrow-pill\":\n theme = \"understory\";\n button = ;\n break;\n case \"meridian-keycap-primary\":\n theme = \"meridian\";\n button = ;\n break;\n case \"meridian-keycap-secondary\":\n theme = \"meridian\";\n button = ;\n break;\n case \"halvorsen-arrow-pill\":\n theme = \"halvorsen\";\n button = ;\n break;\n case \"aster-glass-access\":\n theme = \"aster\";\n button = ;\n break;\n case \"aster-glass-arrow\":\n theme = \"aster\";\n button = ;\n break;\n }\n\n return (\n \n \n {button}\n \n );\n}\n\nexport function RectangleButtons({ variant = \"dark-pill\", ...props }: RectangleButtonsProps) {\n if (variant === \"dark-pill\") {\n return ;\n }\n\n if (variant === \"lumen-cta\" || variant === \"lumen-cta-ghost\") {\n return (\n \n \n \n );\n }\n\n if (isSelectedPageButtonVariant(variant)) {\n return (\n \n );\n }\n\n const Variant = RECTANGLE_VARIANTS[variant];\n return (\n \n \n \n );\n}\n\n/** @deprecated Use RectangleButtons. */\nexport const DarkGlassButton = RectangleButtons;\n/** @deprecated Use RectangleButtonsProps. */\nexport type DarkGlassButtonProps = RectangleButtonsProps;\n/** @deprecated Use RectangleButtonVariant. */\nexport type DarkGlassButtonVariant = RectangleButtonVariant;\n" + "bytes": 39715, + "lines": 950, + "sha256": "ff30e28c278193a90f63c93aba77bc6c5c6a8a8f01ec8882392957851146f083", + "code": "import { lazy, Suspense, type ComponentType, type LazyExoticComponent, type ReactNode } from \"react\";\n\nimport {\n DarkGlassButton as DarkGlassRectangle,\n type SectionCompositionProps,\n} from \"../section-elements/SectionElements\";\nimport type { LumenCtaProps } from \"../lumen-cta/LumenCta\";\nimport type { NeuformIsolatedEffectProps } from \"../neuform-isolated/NeuformIsolatedEffects\";\n\nexport type RectangleButtonVariant =\n | \"dark-pill\"\n | \"launch-button\"\n | \"dot-border-button\"\n | \"floating-dots-cta\"\n | \"sliding-text-cta\"\n | \"gradient-beam-cta\"\n | \"gradient-pill-button\"\n | \"generate-button\"\n | \"glassmorphism-cta\"\n | \"spinning-border-button\"\n | \"gradient-cta\"\n | \"lumen-cta\"\n | \"lumen-cta-ghost\"\n | \"trochil-signal\"\n | \"attune-thermal\"\n | \"tideform-outline\"\n | \"understory-arrow-pill\"\n | \"meridian-keycap-primary\"\n | \"meridian-keycap-secondary\"\n | \"halvorsen-arrow-pill\"\n | \"aster-glass-access\"\n | \"aster-glass-arrow\"\n | \"ember-keycap\"\n | \"bloom-outline-button\";\n\ntype SelectedPageButtonVariant = Extract<\n RectangleButtonVariant,\n | \"trochil-signal\"\n | \"attune-thermal\"\n | \"tideform-outline\"\n | \"understory-arrow-pill\"\n | \"meridian-keycap-primary\"\n | \"meridian-keycap-secondary\"\n | \"halvorsen-arrow-pill\"\n | \"aster-glass-access\"\n | \"aster-glass-arrow\"\n | \"ember-keycap\"\n | \"bloom-outline-button\"\n>;\n\ntype IsolatedRectangleVariant = Exclude<\n RectangleButtonVariant,\n \"dark-pill\" | \"lumen-cta\" | \"lumen-cta-ghost\" | SelectedPageButtonVariant\n>;\n\nexport type RectangleButtonsProps = SectionCompositionProps &\n NeuformIsolatedEffectProps &\n Omit & {\n variant?: RectangleButtonVariant;\n };\n\nconst LumenCta = lazy(() =>\n import(\"../lumen-cta/LumenCta\").then((module) => ({ default: module.LumenCta })),\n);\n\nconst RECTANGLE_VARIANTS = {\n \"launch-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.LaunchButton })),\n ),\n \"dot-border-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.DotBorderButton })),\n ),\n \"floating-dots-cta\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.FloatingDotsCta })),\n ),\n \"sliding-text-cta\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.SlidingTextCta })),\n ),\n \"gradient-beam-cta\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.GradientBeamCta })),\n ),\n \"gradient-pill-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.GradientPillButton })),\n ),\n \"generate-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.GenerateButton })),\n ),\n \"glassmorphism-cta\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.GlassmorphismCta })),\n ),\n \"spinning-border-button\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.SpinningBorderButton })),\n ),\n \"gradient-cta\": lazy(() =>\n import(\"../neuform-isolated/NeuformIsolatedEffects\").then((module) => ({ default: module.GradientCta })),\n ),\n} satisfies Record>>;\n\nconst SELECTED_PAGE_BUTTON_VARIANTS = new Set([\n \"trochil-signal\",\n \"attune-thermal\",\n \"tideform-outline\",\n \"understory-arrow-pill\",\n \"meridian-keycap-primary\",\n \"meridian-keycap-secondary\",\n \"halvorsen-arrow-pill\",\n \"aster-glass-access\",\n \"aster-glass-arrow\",\n \"ember-keycap\",\n \"bloom-outline-button\",\n]);\n\nfunction isSelectedPageButtonVariant(variant: RectangleButtonVariant): variant is SelectedPageButtonVariant {\n return SELECTED_PAGE_BUTTON_VARIANTS.has(variant);\n}\n\nconst SELECTED_PAGE_BUTTON_STYLES = `\n.threeui-page-button-stage {\n --threeui-page-ink: #fff;\n position: relative;\n display: grid;\n width: 100%;\n height: 100%;\n min-height: 240px;\n place-items: center;\n overflow: hidden;\n isolation: isolate;\n background: #050505;\n color: var(--threeui-page-ink);\n font-family: Inter, \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n}\n.threeui-page-button-stage::before {\n content: \"\";\n position: absolute;\n inset: 0;\n z-index: -1;\n pointer-events: none;\n}\n.threeui-page-button {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 0;\n font: inherit;\n text-decoration: none;\n white-space: nowrap;\n cursor: pointer;\n -webkit-font-smoothing: antialiased;\n}\n.threeui-page-button:focus-visible {\n outline: 2px solid var(--threeui-page-ink);\n outline-offset: 5px;\n}\n.threeui-page-button-stage--trochil {\n background: #030303;\n}\n.threeui-page-button-stage--trochil::before {\n background: radial-gradient(circle at 71% 49%, rgba(251, 215, 54, .18), transparent 31%);\n opacity: .72;\n}\n.threeui-page-button--trochil {\n height: 52px;\n padding: 0 30px;\n overflow: hidden;\n border: 1px solid rgba(255, 255, 255, .27);\n background: linear-gradient(180deg, rgba(255, 255, 255, .07), rgba(0, 0, 0, .36));\n box-shadow: 0 15px 36px rgba(0, 0, 0, .48), inset 0 1px rgba(255, 255, 255, .08);\n color: rgba(255, 255, 255, .82);\n font-size: 14px;\n font-weight: 500;\n transition: border-color .28s ease, color .28s ease, transform .28s ease, box-shadow .28s ease;\n}\n.threeui-page-button--trochil::before {\n content: \"\";\n position: absolute;\n inset: -1px;\n background: linear-gradient(105deg, transparent 17%, rgba(251, 215, 54, .25) 48%, transparent 76%);\n transform: translateX(-125%);\n transition: transform .65s cubic-bezier(.22, .61, .36, 1);\n}\n.threeui-page-button--trochil span { position: relative; }\n.threeui-page-button--trochil:hover {\n border-color: rgba(251, 215, 54, .58);\n color: #fff;\n transform: translateY(-2px);\n box-shadow: 0 18px 42px rgba(0, 0, 0, .58), 0 0 24px rgba(251, 215, 54, .12);\n}\n.threeui-page-button--trochil:hover::before { transform: translateX(125%); }\n.threeui-page-button-stage--attune {\n background: #070709;\n}\n.threeui-page-button-stage--attune::before {\n background: radial-gradient(circle at 50% 50%, rgba(255, 122, 20, .12), transparent 32%);\n}\n.threeui-page-button--attune {\n gap: 10px;\n height: 52px;\n padding: 0 22px;\n border-radius: 11px;\n background: linear-gradient(180deg, #ffa347 0%, #ff7a14 48%, #f2610a 100%);\n box-shadow: inset 0 1px rgba(255, 255, 255, .26), 0 8px 28px rgba(255, 122, 20, .28);\n color: #1a0e04;\n font-size: 14px;\n font-weight: 650;\n letter-spacing: .012em;\n transition: filter .18s ease, transform .18s ease, box-shadow .18s ease;\n}\n.threeui-page-button--attune svg { width: 8px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.7; }\n.threeui-page-button--attune:hover { filter: brightness(1.09); transform: translateY(-2px); box-shadow: inset 0 1px rgba(255, 255, 255, .34), 0 12px 34px rgba(255, 122, 20, .36); }\n.threeui-page-button--attune:active { transform: translateY(1px); }\n.threeui-page-button-stage--tideform {\n background: #16181a;\n font-family: \"Roboto Mono\", \"SFMono-Regular\", Consolas, monospace;\n}\n.threeui-page-button-stage--tideform::before {\n background: radial-gradient(ellipse at 76% 44%, rgba(255, 122, 24, .12), transparent 34%);\n}\n.threeui-page-button--tideform {\n gap: 18px;\n padding: 17px 24px;\n border: 1px solid rgba(255, 255, 255, .22);\n background: rgba(255, 255, 255, .055);\n box-shadow: inset 0 1px rgba(255, 255, 255, .035);\n color: #fff;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: .16em;\n text-transform: uppercase;\n transition: border-color .25s ease, background-color .25s ease, color .25s ease;\n}\n.threeui-page-button--tideform svg { width: 21px; height: 9px; fill: none; stroke: currentColor; stroke-width: 1.25; transition: transform .25s cubic-bezier(.2, .8, .2, 1); }\n.threeui-page-button--tideform:hover { border-color: #ff7a18; color: #ff7a18; background: rgba(255, 122, 24, .14); }\n.threeui-page-button--tideform:hover svg { transform: translateX(4px); }\n.threeui-page-button-stage--understory {\n --threeui-page-ink: #27222d;\n background: #f2efe7;\n color: #27222d;\n}\n.threeui-page-button-stage--understory::before {\n background: none;\n}\n.threeui-page-button--arrow-pill {\n gap: 18px;\n height: 54px;\n padding: 0 11px 0 36px;\n border-radius: 999px;\n font-size: 17px;\n letter-spacing: -.005em;\n transition: background .3s ease, transform .3s ease, color .3s ease;\n}\n.threeui-page-button--arrow-pill .threeui-page-button__disc {\n display: grid;\n width: 32px;\n height: 32px;\n flex: none;\n place-items: center;\n border-radius: 50%;\n}\n.threeui-page-button--arrow-pill svg { width: 11px; height: 11px; fill: none; stroke: currentColor; stroke-width: 2; transition: transform .3s ease; }\n.threeui-page-button--arrow-pill:hover { transform: translateY(-2px); }\n.threeui-page-button--arrow-pill:hover svg { transform: translateX(2px); }\n.threeui-page-button--understory {\n background: #9b78d0;\n color: #fff;\n box-shadow: 0 14px 30px rgba(82, 53, 112, .18);\n}\n.threeui-page-button--understory .threeui-page-button__disc { background: #f6f3ed; color: #9b78d0; }\n.threeui-page-button--understory:hover { background: #8d69c5; }\n.threeui-page-button-stage--halvorsen {\n background: #111113;\n}\n.threeui-page-button-stage--halvorsen::before {\n background: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, .06), transparent 35%);\n}\n.threeui-page-button--halvorsen {\n background: #f0eee7;\n color: #111113;\n box-shadow: 0 14px 34px rgba(0, 0, 0, .32);\n}\n.threeui-page-button--halvorsen .threeui-page-button__disc { background: #111113; color: #f0eee7; }\n.threeui-page-button--halvorsen:hover { background: #fff; }\n.threeui-page-button-stage--meridian {\n background: #04070d;\n}\n.threeui-page-button-stage--meridian::before {\n background: radial-gradient(circle at 50% 44%, rgba(77, 163, 255, .13), transparent 34%);\n}\n.threeui-page-button--meridian {\n height: 62px;\n padding: 0 30px;\n border-radius: 15px;\n flex-shrink: 0;\n background: #0c1017;\n box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .07);\n}\n.threeui-page-button--meridian::before {\n content: \"\";\n position: absolute;\n inset: 3px 4px 9px;\n border-radius: 12px;\n background: linear-gradient(180deg, #2a3142, #1c2230);\n box-shadow: 0 5px 0 #080b11, 0 9px 15px -2px rgba(0, 0, 0, .62), inset 0 1px rgba(255, 255, 255, .11);\n transition: transform .13s cubic-bezier(.22, .61, .36, 1), box-shadow .13s cubic-bezier(.22, .61, .36, 1);\n}\n.threeui-page-button--meridian > span:not(.threeui-page-button__led) {\n position: relative;\n z-index: 1;\n color: #dde5f2;\n font-size: 14.5px;\n font-weight: 600;\n letter-spacing: -.005em;\n}\n.threeui-page-button--meridian .threeui-page-button__led {\n position: absolute;\n z-index: 1;\n top: 12px;\n left: 50%;\n width: 3px;\n height: 3px;\n border-radius: 50%;\n background: rgba(255, 255, 255, .16);\n transform: translateX(-50%);\n transition: background .22s ease, box-shadow .22s ease;\n}\n.threeui-page-button--meridian:hover::before { transform: translateY(4px); box-shadow: 0 1px 0 #080b11, 0 3px 8px -2px rgba(0, 0, 0, .55), inset 0 1px rgba(255, 255, 255, .11); }\n.threeui-page-button--meridian:hover .threeui-page-button__led { background: #4da3ff; box-shadow: 0 0 7px #4da3ff; }\n.threeui-page-button--meridian-primary { background: #050f1c; box-shadow: inset 0 0 0 1px rgba(77, 163, 255, .22); }\n.threeui-page-button--meridian-primary::before { background: linear-gradient(180deg, #6db6ff, #2e85e8); box-shadow: 0 5px 0 #0b4a86, 0 9px 18px -2px rgba(21, 88, 158, .5), inset 0 1px rgba(255, 255, 255, .5); }\n.threeui-page-button--meridian-primary > span:not(.threeui-page-button__led) { color: #04182f; }\n.threeui-page-button--meridian-primary .threeui-page-button__led { background: rgba(4, 24, 47, .3); }\n.threeui-page-button--meridian-primary:hover::before { box-shadow: 0 1px 0 #0b4a86, 0 3px 10px -2px rgba(21, 88, 158, .45), inset 0 1px rgba(255, 255, 255, .5); }\n.threeui-page-button--meridian-primary:hover .threeui-page-button__led { background: #04182f; box-shadow: 0 0 6px rgba(4, 24, 47, .55); }\n.threeui-page-button-stage--aster {\n background: #101010;\n font-family: \"Roboto Flex\", \"Arial Narrow\", Inter, \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n}\n.threeui-page-button-stage--aster::before {\n background:\n radial-gradient(circle at 64% 42%, rgba(255, 255, 255, .07), transparent 21%),\n radial-gradient(circle at 38% 62%, rgba(255, 255, 255, .035), transparent 27%);\n}\n.threeui-page-button--aster {\n height: 52px;\n border-radius: 12px;\n isolation: isolate;\n background: rgba(255, 255, 255, .055);\n color: #f1f1f1;\n font-size: 17px;\n font-variation-settings: \"wdth\" 100, \"opsz\" 14;\n letter-spacing: -.004em;\n box-shadow: 0 12px 34px -22px rgba(255, 255, 255, .35);\n backdrop-filter: blur(16px) saturate(1.25);\n -webkit-backdrop-filter: blur(16px) saturate(1.25);\n transition: transform .5s cubic-bezier(.22, 1, .36, 1), background .4s ease, color .4s ease, box-shadow .5s cubic-bezier(.22, 1, .36, 1);\n}\n.threeui-page-button--aster::before {\n content: \"\";\n position: absolute;\n inset: 0;\n z-index: -1;\n border-radius: inherit;\n padding: 1px;\n background: linear-gradient(148deg, rgba(255, 255, 255, .72), rgba(255, 255, 255, .16) 34%, rgba(255, 255, 255, .05) 58%, rgba(255, 255, 255, .34));\n mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);\n mask-composite: exclude;\n -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);\n -webkit-mask-composite: xor;\n pointer-events: none;\n transition: filter .45s cubic-bezier(.22, 1, .36, 1);\n}\n.threeui-page-button--aster::after {\n content: \"\";\n position: absolute;\n inset: 0;\n z-index: -1;\n border-radius: inherit;\n background: linear-gradient(180deg, rgba(255, 255, 255, .13), transparent 46%);\n pointer-events: none;\n}\n.threeui-page-button--aster > span { position: relative; z-index: 1; }\n.threeui-page-button--aster:hover {\n transform: translateY(-1px);\n background: rgba(255, 255, 255, .15);\n color: #fff;\n box-shadow: 0 8px 26px -12px rgba(255, 255, 255, .42);\n}\n.threeui-page-button--aster:hover::before { filter: brightness(1.55); }\n.threeui-page-button--aster:active { transform: translateY(0); }\n.threeui-page-button--aster-access { padding: 0 31px; }\n.threeui-page-button--aster-arrow { gap: 17px; padding: 0 7px 0 22px; }\n.threeui-page-button--aster-arrow .threeui-page-button__chip {\n display: grid;\n width: 34px;\n height: 34px;\n flex: none;\n place-items: center;\n border-radius: 9px;\n background: rgba(255, 255, 255, .10);\n}\n.threeui-page-button--aster-arrow .threeui-page-button__chip::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n padding: 1px;\n background: linear-gradient(150deg, rgba(255, 255, 255, .75), rgba(255, 255, 255, .10) 62%, rgba(255, 255, 255, .42));\n mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);\n mask-composite: exclude;\n -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);\n -webkit-mask-composite: xor;\n pointer-events: none;\n}\n.threeui-page-button--aster-arrow svg {\n width: 15px;\n height: 15px;\n fill: none;\n stroke: currentColor;\n stroke-width: 1.4;\n stroke-linecap: round;\n stroke-linejoin: round;\n transition: transform .5s cubic-bezier(.22, 1, .36, 1);\n}\n.threeui-page-button--aster-arrow:hover svg { transform: translateX(2px); }\n.threeui-page-button-stage--ember-keycap {\n background: #080604;\n font-family: \"JetBrains Mono\", \"SFMono-Regular\", Menlo, Consolas, monospace;\n}\n.threeui-page-button-stage--ember-keycap::before {\n background:\n radial-gradient(ellipse at 50% 56%, rgba(255, 111, 35, .20), transparent 30%),\n linear-gradient(180deg, rgba(255, 119, 45, .035), transparent 58%);\n}\n.threeui-page-button-ember-wrap {\n position: relative;\n display: inline-block;\n}\n.threeui-page-button-ember-glow,\n.threeui-page-button-ember-bloom {\n position: absolute;\n pointer-events: none;\n border-radius: 50%;\n mix-blend-mode: plus-lighter;\n transition: opacity .16s ease, transform .16s ease;\n}\n.threeui-page-button-ember-glow {\n left: 6%;\n right: 6%;\n top: -9px;\n height: 16px;\n background: radial-gradient(closest-side, rgba(255, 222, 182, .95), rgba(255, 138, 54, .62) 50%, rgba(255, 96, 24, 0) 100%);\n filter: blur(4px);\n opacity: 1;\n}\n.threeui-page-button-ember-bloom {\n left: -10%;\n right: -10%;\n top: -31px;\n height: 58px;\n background: radial-gradient(closest-side, rgba(255, 128, 50, .55), rgba(255, 92, 24, .18) 58%, rgba(255, 72, 14, 0) 100%);\n filter: blur(12px);\n opacity: .85;\n}\n.threeui-page-button--ember-keycap {\n gap: .85em;\n padding: 1.05em 1.6em 1.1em;\n border-radius: 9px;\n background: linear-gradient(178deg, #4a4d54 0%, #34373d 18%, #23262b 62%, #1a1c20 100%);\n box-shadow:\n inset 0 1.5px 0 rgba(255, 238, 225, .22),\n inset 0 -1px 0 rgba(0, 0, 0, .75),\n 0 7px 0 -1px #101215,\n 0 12px 26px rgba(0, 0, 0, .62);\n color: #fff1e2;\n font-size: 14px;\n font-weight: 500;\n letter-spacing: .13em;\n text-transform: uppercase;\n transform: translateY(0);\n transition: transform .13s cubic-bezier(.3, .7, .3, 1), box-shadow .13s ease;\n}\n.threeui-page-button--ember-keycap .threeui-page-button__spark {\n width: 1.1em;\n height: 1.1em;\n flex: none;\n fill: #ff8a3d;\n filter: drop-shadow(0 0 .55em rgba(255, 120, 50, .9));\n}\n.threeui-page-button--ember-keycap .threeui-page-button__price { color: #b9ada3; }\n.threeui-page-button-ember-wrap:hover .threeui-page-button--ember-keycap,\n.threeui-page-button--ember-keycap:focus-visible {\n transform: translateY(5px);\n box-shadow:\n inset 0 1.5px 0 rgba(255, 238, 225, .26),\n inset 0 -1px 0 rgba(0, 0, 0, .75),\n 0 2px 0 -1px #101215,\n 0 5px 14px rgba(0, 0, 0, .6);\n}\n.threeui-page-button--ember-keycap:focus-visible {\n outline-color: #ff9a59;\n}\n.threeui-page-button-ember-wrap:hover .threeui-page-button-ember-glow,\n.threeui-page-button-ember-wrap:focus-within .threeui-page-button-ember-glow {\n transform: translateY(5px) scaleX(1.05);\n}\n.threeui-page-button-ember-wrap:hover .threeui-page-button-ember-bloom,\n.threeui-page-button-ember-wrap:focus-within .threeui-page-button-ember-bloom {\n opacity: 1;\n transform: translateY(5px) scale(1.05);\n}\n.threeui-page-button--ember-keycap:active { transform: translateY(6px); }\n.threeui-page-button-stage--bloom-outline {\n --threeui-bloom-outline-edge: #f5ece6;\n --threeui-bloom-outline-fill: #f5ece6;\n --threeui-bloom-outline-fg: #3f2c33;\n --threeui-page-ink: #f5ece6;\n background: #b5808e;\n font-family: \"General Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n}\n.threeui-page-button-stage--bloom-outline::before {\n background:\n radial-gradient(ellipse at 29% 24%, rgba(245, 236, 230, .2), transparent 34%),\n radial-gradient(ellipse at 75% 72%, rgba(63, 44, 51, .15), transparent 36%),\n repeating-radial-gradient(circle at 44% 48%, rgba(245, 236, 230, .1) 0 1px, transparent 1px 4px);\n opacity: .7;\n mix-blend-mode: soft-light;\n}\n.threeui-page-button--bloom-outline {\n --bloom-outline-x: 50%;\n --bloom-outline-y: 50%;\n --bloom-outline-diameter: 220px;\n gap: 13.6px;\n overflow: hidden;\n padding: 14.4px 23.2px;\n border: 1px solid currentColor;\n border-radius: 2px;\n background: transparent;\n color: var(--threeui-bloom-outline-edge);\n font-size: 14.72px;\n font-weight: 400;\n letter-spacing: .045em;\n line-height: 1;\n text-transform: uppercase;\n will-change: transform;\n transition:\n color .38s cubic-bezier(.22, .61, .36, 1),\n border-color .38s cubic-bezier(.22, .61, .36, 1),\n transform .55s cubic-bezier(.19, 1, .22, 1);\n}\n.threeui-page-button--bloom-outline::before {\n content: \"\";\n position: absolute;\n z-index: 0;\n left: var(--bloom-outline-x);\n top: var(--bloom-outline-y);\n width: var(--bloom-outline-diameter);\n height: var(--bloom-outline-diameter);\n border-radius: 50%;\n background: var(--threeui-bloom-outline-fill);\n transform: translate(-50%, -50%) scale(0);\n transition: transform .58s cubic-bezier(.19, 1, .22, 1);\n}\n.threeui-page-button--bloom-outline > * {\n position: relative;\n z-index: 1;\n}\n.threeui-page-button--bloom-outline:hover,\n.threeui-page-button--bloom-outline:focus-visible {\n border-color: var(--threeui-bloom-outline-fg);\n color: var(--threeui-bloom-outline-fg);\n}\n.threeui-page-button--bloom-outline:hover::before,\n.threeui-page-button--bloom-outline:focus-visible::before { transform: translate(-50%, -50%) scale(1); }\n.threeui-page-button--bloom-outline .threeui-page-button__bloom-label {\n display: inline-block;\n overflow: hidden;\n vertical-align: middle;\n}\n.threeui-page-button--bloom-outline .threeui-page-button__bloom-label > span {\n display: block;\n transition: transform .5s cubic-bezier(.19, 1, .22, 1);\n}\n.threeui-page-button--bloom-outline .threeui-page-button__bloom-label > span + span {\n position: absolute;\n left: 0;\n top: 0;\n transform: translateY(115%);\n}\n.threeui-page-button--bloom-outline:hover .threeui-page-button__bloom-label > span:first-child,\n.threeui-page-button--bloom-outline:focus-visible .threeui-page-button__bloom-label > span:first-child { transform: translateY(-115%); }\n.threeui-page-button--bloom-outline:hover .threeui-page-button__bloom-label > span + span,\n.threeui-page-button--bloom-outline:focus-visible .threeui-page-button__bloom-label > span + span { transform: translateY(0); }\n.threeui-page-button__bloom-dot {\n width: 4.8px;\n height: 4.8px;\n flex: none;\n border-radius: 50%;\n background: currentColor;\n transition: transform .55s cubic-bezier(.19, 1, .22, 1), opacity .4s;\n}\n.threeui-page-button--bloom-outline:hover .threeui-page-button__bloom-dot:first-child,\n.threeui-page-button--bloom-outline:focus-visible .threeui-page-button__bloom-dot:first-child {\n opacity: .5;\n transform: translateX(5.44px) scale(.6);\n}\n.threeui-page-button--bloom-outline:hover .threeui-page-button__bloom-dot:last-child,\n.threeui-page-button--bloom-outline:focus-visible .threeui-page-button__bloom-dot:last-child {\n transform: translateX(-5.44px) scale(1.7);\n}\n\n/* ---------------------------------------------------------------- *\n The other ground\n\n Eight of these treatments were drawn on their page's dark ground and\n Understory on its paper one, so the opposite mode is a re-tone rather\n than a wash: the stage takes the other ground, and every value that\n ground would swallow — edge, ink, sheen, cast shadow — is restated at\n the weight it was authored to read at. Geometry and motion never move.\n * ---------------------------------------------------------------- */\n.threeui-page-button-stage[data-mode=\"light\"] { --threeui-page-ink: #14161a; }\n\n/* Trochil — the amber signal sheen, now over warm paper */\n.threeui-page-button-stage--trochil[data-mode=\"light\"] { background: #f5f3ee; }\n.threeui-page-button-stage--trochil[data-mode=\"light\"]::before {\n background: radial-gradient(circle at 71% 49%, rgba(196, 142, 6, .22), transparent 31%);\n opacity: .9;\n}\n.threeui-page-button-stage--trochil[data-mode=\"light\"] .threeui-page-button--trochil {\n border-color: rgba(20, 19, 14, .22);\n background: linear-gradient(180deg, rgba(255, 255, 255, .95), rgba(20, 19, 14, .05));\n box-shadow: 0 14px 30px rgba(20, 19, 14, .13), inset 0 1px rgba(255, 255, 255, .9);\n color: rgba(20, 19, 14, .78);\n}\n.threeui-page-button-stage--trochil[data-mode=\"light\"] .threeui-page-button--trochil::before {\n background: linear-gradient(105deg, transparent 17%, rgba(196, 142, 6, .3) 48%, transparent 76%);\n}\n.threeui-page-button-stage--trochil[data-mode=\"light\"] .threeui-page-button--trochil:hover {\n border-color: rgba(176, 126, 4, .62);\n color: #14130e;\n box-shadow: 0 18px 38px rgba(20, 19, 14, .17), 0 0 22px rgba(196, 142, 6, .2);\n}\n\n/* Attune — the thermal cap already carries its own ink, so only the ground\n and the cast heat change */\n.threeui-page-button-stage--attune[data-mode=\"light\"] { background: #faf4ee; }\n.threeui-page-button-stage--attune[data-mode=\"light\"]::before {\n background: radial-gradient(circle at 50% 50%, rgba(255, 122, 20, .18), transparent 32%);\n}\n.threeui-page-button-stage--attune[data-mode=\"light\"] .threeui-page-button--attune {\n box-shadow: inset 0 1px rgba(255, 255, 255, .5), 0 8px 24px rgba(226, 92, 6, .28);\n}\n.threeui-page-button-stage--attune[data-mode=\"light\"] .threeui-page-button--attune:hover {\n box-shadow: inset 0 1px rgba(255, 255, 255, .58), 0 12px 30px rgba(226, 92, 6, .34);\n}\n\n/* Tideform — the outline drops to graphite and the hot state to a deeper\n orange, which is where it clears text contrast on paper */\n.threeui-page-button-stage--tideform[data-mode=\"light\"] { background: #eeece7; --threeui-page-ink: #16181a; }\n.threeui-page-button-stage--tideform[data-mode=\"light\"]::before {\n background: radial-gradient(ellipse at 76% 44%, rgba(214, 96, 12, .16), transparent 34%);\n}\n.threeui-page-button-stage--tideform[data-mode=\"light\"] .threeui-page-button--tideform {\n border-color: rgba(22, 24, 26, .26);\n background: rgba(22, 24, 26, .035);\n box-shadow: inset 0 1px rgba(255, 255, 255, .7);\n color: #16181a;\n}\n.threeui-page-button-stage--tideform[data-mode=\"light\"] .threeui-page-button--tideform:hover {\n border-color: #c25a0c;\n background: rgba(194, 90, 12, .1);\n color: #b4530b;\n}\n\n/* Understory runs the other way: the violet capsule is the constant, and\n dark mode is the ground it was never given */\n.threeui-page-button-stage--understory[data-mode=\"dark\"] {\n --threeui-page-ink: #ece7e0;\n background: #17141b;\n color: #ece7e0;\n}\n.threeui-page-button-stage--understory[data-mode=\"dark\"]::before {\n background: radial-gradient(circle at 50% 50%, rgba(155, 120, 208, .16), transparent 34%);\n}\n.threeui-page-button-stage--understory[data-mode=\"dark\"] .threeui-page-button--understory {\n background: #a482d9;\n box-shadow: 0 16px 34px rgba(0, 0, 0, .5);\n}\n.threeui-page-button-stage--understory[data-mode=\"dark\"] .threeui-page-button--understory:hover { background: #b08fe2; }\n\n/* Halvorsen is a maximum-contrast pill, so on paper it inverts rather than\n fades — the capsule takes the ink and the endcap takes the paper */\n.threeui-page-button-stage--halvorsen[data-mode=\"light\"] { background: #eeece5; }\n.threeui-page-button-stage--halvorsen[data-mode=\"light\"]::before {\n background: radial-gradient(circle at 50% 50%, rgba(17, 17, 19, .05), transparent 35%);\n}\n.threeui-page-button-stage--halvorsen[data-mode=\"light\"] .threeui-page-button--halvorsen {\n background: #111113;\n box-shadow: 0 14px 30px rgba(17, 17, 19, .22);\n color: #f0eee7;\n}\n.threeui-page-button-stage--halvorsen[data-mode=\"light\"] .threeui-page-button--halvorsen .threeui-page-button__disc {\n background: #f0eee7;\n color: #111113;\n}\n.threeui-page-button-stage--halvorsen[data-mode=\"light\"] .threeui-page-button--halvorsen:hover { background: #000; }\n\n/* Meridian — the keycap keeps its travel and its hard base edge; only the\n housing, the cap face, and the legend follow the light ground */\n.threeui-page-button-stage--meridian[data-mode=\"light\"] { background: #e9eef5; --threeui-page-ink: #04070d; }\n.threeui-page-button-stage--meridian[data-mode=\"light\"]::before {\n background: radial-gradient(circle at 50% 44%, rgba(46, 133, 232, .2), transparent 34%);\n}\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian {\n background: #dfe5ee;\n box-shadow: inset 0 0 0 1px rgba(4, 7, 13, .09);\n}\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian::before {\n background: linear-gradient(180deg, #fbfcfe, #e3e8f1);\n box-shadow: 0 5px 0 #bcc5d3, 0 9px 15px -2px rgba(4, 7, 13, .2), inset 0 1px #fff;\n}\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian > span:not(.threeui-page-button__led) { color: #1d2532; }\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian .threeui-page-button__led { background: rgba(4, 7, 13, .18); }\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian:hover::before { box-shadow: 0 1px 0 #bcc5d3, 0 3px 8px -2px rgba(4, 7, 13, .18), inset 0 1px #fff; }\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian:hover .threeui-page-button__led { background: #2e85e8; box-shadow: 0 0 7px #2e85e8; }\n/* the primary cap stays blue on both grounds, so it is restated after the\n secondary block rather than inheriting it */\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian-primary {\n background: #dbe7f6;\n box-shadow: inset 0 0 0 1px rgba(46, 133, 232, .3);\n}\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian-primary::before {\n background: linear-gradient(180deg, #6db6ff, #2e85e8);\n box-shadow: 0 5px 0 #1d6dc4, 0 9px 18px -2px rgba(21, 88, 158, .32), inset 0 1px rgba(255, 255, 255, .5);\n}\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian-primary > span:not(.threeui-page-button__led) { color: #04182f; }\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian-primary .threeui-page-button__led { background: rgba(4, 24, 47, .3); }\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian-primary:hover::before { box-shadow: 0 1px 0 #1d6dc4, 0 3px 10px -2px rgba(21, 88, 158, .3), inset 0 1px rgba(255, 255, 255, .5); }\n.threeui-page-button-stage--meridian[data-mode=\"light\"] .threeui-page-button--meridian-primary:hover .threeui-page-button__led { background: #04182f; box-shadow: 0 0 6px rgba(4, 24, 47, .55); }\n\n/* Aster — the glass is a white edge lit from one corner, so on paper the\n whole build flips to graphite and the hover brightening becomes darkening */\n.threeui-page-button-stage--aster[data-mode=\"light\"] { background: #eceae7; --threeui-page-ink: #101010; }\n.threeui-page-button-stage--aster[data-mode=\"light\"]::before {\n background:\n radial-gradient(circle at 64% 42%, rgba(16, 16, 16, .06), transparent 21%),\n radial-gradient(circle at 38% 62%, rgba(16, 16, 16, .035), transparent 27%);\n}\n.threeui-page-button-stage--aster[data-mode=\"light\"] .threeui-page-button--aster {\n background: rgba(16, 16, 16, .05);\n box-shadow: 0 14px 34px -22px rgba(16, 16, 16, .55);\n color: #16181a;\n}\n.threeui-page-button-stage--aster[data-mode=\"light\"] .threeui-page-button--aster::before {\n background: linear-gradient(148deg, rgba(16, 16, 16, .45), rgba(16, 16, 16, .12) 34%, rgba(16, 16, 16, .05) 58%, rgba(16, 16, 16, .26));\n}\n.threeui-page-button-stage--aster[data-mode=\"light\"] .threeui-page-button--aster::after {\n background: linear-gradient(180deg, rgba(255, 255, 255, .8), transparent 46%);\n}\n.threeui-page-button-stage--aster[data-mode=\"light\"] .threeui-page-button--aster:hover {\n background: rgba(16, 16, 16, .1);\n box-shadow: 0 8px 26px -12px rgba(16, 16, 16, .3);\n color: #000;\n}\n.threeui-page-button-stage--aster[data-mode=\"light\"] .threeui-page-button--aster:hover::before { filter: brightness(.68); }\n.threeui-page-button-stage--aster[data-mode=\"light\"] .threeui-page-button--aster-arrow .threeui-page-button__chip { background: rgba(16, 16, 16, .07); }\n.threeui-page-button-stage--aster[data-mode=\"light\"] .threeui-page-button--aster-arrow .threeui-page-button__chip::before {\n background: linear-gradient(150deg, rgba(16, 16, 16, .5), rgba(16, 16, 16, .1) 62%, rgba(16, 16, 16, .3));\n}\n.threeui-page-button-stage--ember-keycap[data-mode=\"light\"] {\n background: #f2ece6;\n --threeui-page-ink: #1a1511;\n}\n.threeui-page-button-stage--ember-keycap[data-mode=\"light\"]::before {\n background: radial-gradient(ellipse at 50% 56%, rgba(221, 84, 12, .20), transparent 30%);\n}\n.threeui-page-button-stage--ember-keycap[data-mode=\"light\"] .threeui-page-button-ember-bloom {\n opacity: .66;\n}\n.threeui-page-button-stage--bloom-outline[data-mode=\"light\"] {\n --threeui-bloom-outline-edge: #3f2c33;\n --threeui-bloom-outline-fill: #3f2c33;\n --threeui-bloom-outline-fg: #f5ece6;\n --threeui-page-ink: #3f2c33;\n background: #f5ece6;\n}\n.threeui-page-button-stage--bloom-outline[data-mode=\"light\"]::before {\n background:\n radial-gradient(ellipse at 29% 24%, rgba(181, 128, 142, .2), transparent 34%),\n radial-gradient(ellipse at 75% 72%, rgba(63, 44, 51, .08), transparent 36%),\n repeating-radial-gradient(circle at 44% 48%, rgba(138, 95, 108, .08) 0 1px, transparent 1px 4px);\n mix-blend-mode: multiply;\n}\n@media (prefers-reduced-motion: reduce) {\n .threeui-page-button, .threeui-page-button::before, .threeui-page-button svg,\n .threeui-page-button-ember-glow, .threeui-page-button-ember-bloom { transition-duration: .01ms !important; }\n .threeui-page-button--bloom-outline { transform: none !important; }\n}\n`;\n\ntype SelectedPageButtonProps = Pick & {\n variant: SelectedPageButtonVariant;\n};\n\nfunction ArrowIcon({ long = false }: { long?: boolean }) {\n return long ? (\n \n ) : (\n \n );\n}\n\nfunction SelectedPageButton({ variant, mode = \"dark\", className = \"\", style }: SelectedPageButtonProps) {\n let theme = \"trochil\";\n let button: ReactNode = null;\n\n switch (variant) {\n case \"trochil-signal\":\n button = ;\n break;\n case \"attune-thermal\":\n theme = \"attune\";\n button = ;\n break;\n case \"tideform-outline\":\n theme = \"tideform\";\n button = ;\n break;\n case \"understory-arrow-pill\":\n theme = \"understory\";\n button = ;\n break;\n case \"meridian-keycap-primary\":\n theme = \"meridian\";\n button = ;\n break;\n case \"meridian-keycap-secondary\":\n theme = \"meridian\";\n button = ;\n break;\n case \"halvorsen-arrow-pill\":\n theme = \"halvorsen\";\n button = ;\n break;\n case \"aster-glass-access\":\n theme = \"aster\";\n button = ;\n break;\n case \"aster-glass-arrow\":\n theme = \"aster\";\n button = ;\n break;\n case \"ember-keycap\":\n theme = \"ember-keycap\";\n button = (\n \n \n \n \n \n );\n break;\n case \"bloom-outline-button\":\n theme = \"bloom-outline\";\n button = (\n {\n const rect = event.currentTarget.getBoundingClientRect();\n const x = event.clientX - rect.left;\n const y = event.clientY - rect.top;\n const diameter = 2 * Math.hypot(Math.max(x, rect.width - x), Math.max(y, rect.height - y));\n event.currentTarget.style.setProperty(\"--bloom-outline-x\", `${x.toFixed(1)}px`);\n event.currentTarget.style.setProperty(\"--bloom-outline-y\", `${y.toFixed(1)}px`);\n event.currentTarget.style.setProperty(\"--bloom-outline-diameter\", `${diameter.toFixed(1)}px`);\n if (!window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) {\n const dx = (x - rect.width / 2) / rect.width;\n const dy = (y - rect.height / 2) / rect.height;\n event.currentTarget.style.transform = `translate3d(${(dx * 14).toFixed(1)}px, ${(dy * 9).toFixed(1)}px, 0)`;\n }\n }}\n onPointerLeave={(event) => { event.currentTarget.style.transform = \"\"; }}\n >\n \n \n See the season\n See the season\n \n \n \n );\n break;\n }\n\n return (\n \n \n {button}\n \n );\n}\n\nexport function RectangleButtons({ variant = \"dark-pill\", ...props }: RectangleButtonsProps) {\n if (variant === \"dark-pill\") {\n return ;\n }\n\n if (variant === \"lumen-cta\" || variant === \"lumen-cta-ghost\") {\n return (\n \n \n \n );\n }\n\n if (isSelectedPageButtonVariant(variant)) {\n return (\n \n );\n }\n\n const Variant = RECTANGLE_VARIANTS[variant];\n return (\n \n \n \n );\n}\n\n/** @deprecated Use RectangleButtons. */\nexport const DarkGlassButton = RectangleButtons;\n/** @deprecated Use RectangleButtonsProps. */\nexport type DarkGlassButtonProps = RectangleButtonsProps;\n/** @deprecated Use RectangleButtonVariant. */\nexport type DarkGlassButtonVariant = RectangleButtonVariant;\n" }, { "path": "src/shaders/section-elements/SectionElements.tsx", @@ -1337,6 +1363,96 @@ } ] }, + { + "id": "orb-gallery", + "exportName": "OrbGallery", + "sourceCommit": "SHA-256 fff1d451248b", + "runtime": "Three.js r149 + embedded WebP atlas + DOM/CSS", + "sharedFilePaths": [ + "src/shaders/community.css" + ], + "files": [ + { + "path": "src/shaders/orb-gallery/sources/orb-gallery.html", + "language": "html", + "role": "canonical-source", + "bytes": 929062, + "lines": 606, + "sha256": "fff1d451248bdcaea452045d3b6606161d677824326f46b95e8c2a52c32e8e55", + "code": "\n\n\n\n\norb.gallery\n\n\n\n\n\n\n\n\n
\n \n
\n\n
\n \n \n \n \n \n orb.gallery\n \n \n \n
\n\n
\n \n \n \n \n Drag to spin · hover a screen\n
\n\n
\n

Every interface
worth keeping.

\n\n
\n 1,428 interfaces indexed this month\n

\n Product screens, components and flows — captured from the apps you\n admire, tagged, and ready for your next file.\n

\n \n
\n
\"\"\"\"\"\"\"\"\"\"
\n
\n
★★★★★
\n
Joined by 12,000+ product designers
\n
\n
\n
\n
\n\n\n\n\n\n" + }, + { + "path": "src/shaders/orb-gallery/OrbGallery.tsx", + "language": "tsx", + "role": "component", + "bytes": 2389, + "lines": 80, + "sha256": "228699f09aa4b0e51ec38c641007b9286abfcd811a23dc42942c9ff42d800164", + "code": "import { useEffect, useRef, useState, type CSSProperties } from \"react\";\n\nimport orbGallerySource from \"./sources/orb-gallery.html?raw\";\n\nexport type OrbGalleryProps = {\n className?: string;\n style?: CSSProperties;\n};\n\nexport function OrbGallery({ className = \"\", style }: OrbGalleryProps) {\n const hostRef = useRef(null);\n const [documentVisible, setDocumentVisible] = useState(() => (\n typeof document === \"undefined\" || !document.hidden\n ));\n const [hostVisible, setHostVisible] = useState(true);\n const [ready, setReady] = useState(false);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host || typeof IntersectionObserver === \"undefined\") return undefined;\n const observer = new IntersectionObserver(([entry]) => {\n setHostVisible(entry?.isIntersecting ?? true);\n }, { rootMargin: \"80px\" });\n observer.observe(host);\n return () => observer.disconnect();\n }, []);\n\n useEffect(() => {\n if (typeof document === \"undefined\") return undefined;\n const update = () => setDocumentVisible(!document.hidden);\n document.addEventListener(\"visibilitychange\", update);\n return () => document.removeEventListener(\"visibilitychange\", update);\n }, []);\n\n const mounted = hostVisible && documentVisible;\n\n useEffect(() => {\n setReady(false);\n }, [mounted]);\n\n return (\n \n {mounted ? (\n setReady(true)}\n style={{\n position: \"absolute\",\n inset: 0,\n display: \"block\",\n width: \"100%\",\n height: \"100%\",\n border: 0,\n background: \"#1f1f21\",\n opacity: ready ? 1 : 0,\n pointerEvents: ready ? \"auto\" : \"none\",\n transition: \"opacity 240ms ease-out\",\n }}\n />\n ) : null}\n \n );\n}\n" + } + ], + "assets": [] + }, + { + "id": "3d-paper", + "exportName": "ThreeDPaper", + "sourceCommit": "SHA-256 8ec1b71c0dbc", + "runtime": "Bundled Three.js r149 + custom GLSL + CanvasTexture", + "sharedFilePaths": [ + "src/shaders/community.css" + ], + "files": [ + { + "path": "src/shaders/3d-paper/sources/3d-paper.html", + "language": "html", + "role": "canonical-source", + "bytes": 630847, + "lines": 549, + "sha256": "8ec1b71c0dbcafbadf908100ae2a08045d0a1087c00a09d28245ef19366c7353", + "code": "\n\n3D Paper\n\n\n\n\n\n\n

NOCTURNE

\n\n
\n\n
\n
\n
\n
Drag to turn it  ·  Hover to light it
\n\n\n\n" + }, + { + "path": "src/shaders/3d-paper/ThreeDPaper.tsx", + "language": "tsx", + "role": "component", + "bytes": 2389, + "lines": 80, + "sha256": "dbdc440527cc650411ea381349fe4dc8d592aa9c88871e221ff4296248222e66", + "code": "import { useEffect, useRef, useState, type CSSProperties } from \"react\";\n\nimport threeDPaperSource from \"./sources/3d-paper.html?raw\";\n\nexport type ThreeDPaperProps = {\n className?: string;\n style?: CSSProperties;\n};\n\nexport function ThreeDPaper({ className = \"\", style }: ThreeDPaperProps) {\n const hostRef = useRef(null);\n const [documentVisible, setDocumentVisible] = useState(() => (\n typeof document === \"undefined\" || !document.hidden\n ));\n const [hostVisible, setHostVisible] = useState(true);\n const [ready, setReady] = useState(false);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host || typeof IntersectionObserver === \"undefined\") return undefined;\n const observer = new IntersectionObserver(([entry]) => {\n setHostVisible(entry?.isIntersecting ?? true);\n }, { rootMargin: \"80px\" });\n observer.observe(host);\n return () => observer.disconnect();\n }, []);\n\n useEffect(() => {\n if (typeof document === \"undefined\") return undefined;\n const update = () => setDocumentVisible(!document.hidden);\n document.addEventListener(\"visibilitychange\", update);\n return () => document.removeEventListener(\"visibilitychange\", update);\n }, []);\n\n const mounted = hostVisible && documentVisible;\n\n useEffect(() => {\n setReady(false);\n }, [mounted]);\n\n return (\n \n {mounted ? (\n setReady(true)}\n style={{\n position: \"absolute\",\n inset: 0,\n display: \"block\",\n width: \"100%\",\n height: \"100%\",\n border: 0,\n background: \"#08080a\",\n opacity: ready ? 1 : 0,\n pointerEvents: ready ? \"auto\" : \"none\",\n transition: \"opacity 240ms ease-out\",\n }}\n />\n ) : null}\n \n );\n}\n" + } + ], + "assets": [] + }, + { + "id": "ashen-press", + "exportName": "AshenPress", + "sourceCommit": "SHA-256 5fe2554e578a", + "runtime": "Three.js r181 + custom GLSL + CanvasTexture", + "sharedFilePaths": [ + "src/shaders/community.css" + ], + "files": [ + { + "path": "src/shaders/ashen-press/sources/ashen-press.html", + "language": "html", + "role": "canonical-source", + "bytes": 1156000, + "lines": 1694, + "sha256": "5fe2554e578acac5d55cb466a9564440e7767e38797981f8e829dfd2de0bc90f", + "code": "\n\nAshen Press — The Art Book Shelf\n\n\n\n\n\n\n\n\n
\n
\n \n Ashen Press\n
\n \n
\n\n\n
\n

\n

\n

\n
\n \n \n
\n
\n\n
Drag to turn
\n\n
\n\n
\n

\n
\n \n \n
\n
\n\n\n\n
\n\n\n\n\n" + }, + { + "path": "src/shaders/ashen-press/AshenPress.tsx", + "language": "tsx", + "role": "component", + "bytes": 2405, + "lines": 80, + "sha256": "36b365f7a69223f1c3347bfcb3f2d1aa1ab20d30f2c31e5efb248e62ab4ddc90", + "code": "import { useEffect, useRef, useState, type CSSProperties } from \"react\";\n\nimport ashenPressSource from \"./sources/ashen-press.html?raw\";\n\nexport type AshenPressProps = {\n className?: string;\n style?: CSSProperties;\n};\n\nexport function AshenPress({ className = \"\", style }: AshenPressProps) {\n const hostRef = useRef(null);\n const [documentVisible, setDocumentVisible] = useState(() => (\n typeof document === \"undefined\" || !document.hidden\n ));\n const [hostVisible, setHostVisible] = useState(true);\n const [ready, setReady] = useState(false);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host || typeof IntersectionObserver === \"undefined\") return undefined;\n const observer = new IntersectionObserver(([entry]) => {\n setHostVisible(entry?.isIntersecting ?? true);\n }, { rootMargin: \"80px\" });\n observer.observe(host);\n return () => observer.disconnect();\n }, []);\n\n useEffect(() => {\n if (typeof document === \"undefined\") return undefined;\n const update = () => setDocumentVisible(!document.hidden);\n document.addEventListener(\"visibilitychange\", update);\n return () => document.removeEventListener(\"visibilitychange\", update);\n }, []);\n\n const mounted = hostVisible && documentVisible;\n\n useEffect(() => {\n setReady(false);\n }, [mounted]);\n\n return (\n \n {mounted ? (\n setReady(true)}\n style={{\n position: \"absolute\",\n inset: 0,\n display: \"block\",\n width: \"100%\",\n height: \"100%\",\n border: 0,\n background: \"#c6ae8e\",\n opacity: ready ? 1 : 0,\n pointerEvents: ready ? \"auto\" : \"none\",\n transition: \"opacity 240ms ease-out\",\n }}\n />\n ) : null}\n \n );\n}\n" + } + ], + "assets": [] + }, { "id": "sylva-living-world", "exportName": "SylvaLivingWorldScene", diff --git a/src/components/buildSkillMarkdown.js b/src/components/buildSkillMarkdown.js index f910552..9a32e4e 100644 --- a/src/components/buildSkillMarkdown.js +++ b/src/components/buildSkillMarkdown.js @@ -14,12 +14,15 @@ const SKILLS = { "semantic-bloom": "---\nname: add-semantic-bloom\ndescription: \"Build Semantic Bloom from its verified authored source using Canvas 2D + DOM/CSS, including the complete renderer, interactions, and required assets. Use when Codex needs to implement, port, or adapt this effect without requiring the ThreeUI package or reconstructing the visual from an approximation.\"\n---\n\n# Build Semantic Bloom\n\n## Description\n\nA customizable Codex wordmark that draws a viscous particle organism toward its letters, illuminating the text as the network searches and reconnects.\n\nRecreate the authored behavior from the verified source, not from screenshots or the abbreviated orchestration sample in this skill. The implementation may live directly in the target project and does not require `@designcodeio/threeui`.\n\n## Technologies\n\n- React and TypeScript visibility-aware iframe host\n- Byte-exact Organic Semantic Explorer HTML source\n- Canvas 2D particle network with SVG goo and displacement filters\n- Customizable DOM wordmark synchronized through postMessage\n- Dark/light surfaces, pointer attraction, and reduced-motion handling\n\n## Verified source material\n\n- `design-f0ebbe02-7d8a-41fd-9041-a1124185c27b.html — complete Organic Semantic Explorer source`\n- `src/shaders/semantic-bloom/sources/design-f0ebbe02-7d8a-41fd-9041-a1124185c27b.html`\n- `src/shaders/semantic-bloom/SemanticBloom.tsx`\n\nSource revision: `SHA-256 0e48ec9ed2c3`\n\n## Implementation steps\n\n1. Open every verified source file listed above and identify the renderer, host lifecycle, styles, and assets before editing.\n2. Keep the complete selected HTML document byte-for-byte as the canonical provenance source.\n3. Inject presentation-only CSS that hides the journal chrome and centers the editable semantic text without rewriting its particle engine.\n4. Replace only the source particle and connection colors when light mode is selected; retain the authored dark values as exact defaults.\n5. Send text, scale, and pause state through postMessage so interactive controls do not remount the iframe or reset the organism.\n6. Pause animation when the host is offscreen or the document is hidden, and render a still frame when reduced motion is requested.\n7. Keep the iframe allow-scripts-only so unmounting releases the document, canvas, listeners, filters, and animation frame together.\n8. Give the local component a sized, overflow-controlled parent and verify desktop, mobile, reduced-motion, and context-loss behavior.\n\nAsset handling: This effect has no required external assets.\n\n## Local component example\n\nImport the copied local component rather than a package entrypoint:\n\n```tsx\nimport { SemanticBloom } from \"./effects/semantic-bloom/SemanticBloom\";\nimport \"./effects/semantic-bloom/styles.css\";\n\nexport function Scene() {\n return
;\n}\n```\n\n## Core renderer pattern\n\nThis excerpt documents orchestration only. Copy the exact shader, geometry, pass, and interaction code from the verified source files.\n\n```tsx\n\n```\n\n## Behavior contract\n\n- Runtime: Canvas 2D + DOM/CSS\n- Passes: 1 filtered Canvas 2D particle network over a DOM wordmark\n- Interaction: Pointer attraction, live text and size controls, synchronized light/dark mode, visibility pausing, and reduced-motion still frame\n- Assets: No external assets\n- **source** (fixed): Exact owner-selected HTML\n- **focus** (host): Centered semantic wordmark\n- **text** (optional): Codex\n- **mode** (optional): dark | light\n- **size** (optional): 0.55–1.6\n- **motion** (adaptive): Pointer + reduced-motion still\n- **assets** (fixed): None\n\n## Verification\n\n1. Compare the rendered composition, animation timing, pointer behavior, and state transitions with the source implementation.\n2. Exercise resize, high-DPI, mobile/coarse-pointer, reduced-motion, tab visibility, and WebGL context-loss paths where applicable.\n3. Confirm every animation frame, observer, listener, geometry, buffer, texture, framebuffer, material, and renderer is released on teardown.\n4. Check the browser console and confirm the effect renders at native-or-better backing resolution.\n\n## Guardrails\n\n- Do not substitute a visually similar package, demo, shader, or runtime.\n- Do not approximate, reconstruct, or simplify the authored GLSL, render passes, geometry, interaction state, or assets.\n- Keep exact source and asset hashes under regression tests when the source project provides them.\n- Adapt only the surrounding host boundary needed by the target project; keep renderer behavior intact.\n", "globe-study": "---\nname: add-globe-study\ndescription: \"Build Text Path Studies from its verified authored source using Canvas 2D, including the complete renderer, interactions, and required assets. Use when Codex needs to implement, port, or adapt this effect without requiring the ThreeUI package or reconstructing the visual from an approximation.\"\n---\n\n# Build Text Path Studies\n\n## Description\n\nSix interactive Canvas 2D typography studies spanning a globe, flowing outlines, morphing glyphs, cloth physics, ripples, and a particle sphere.\n\nRecreate the authored behavior from the verified source, not from screenshots or the abbreviated orchestration sample in this skill. The implementation may live directly in the target project and does not require `@designcodeio/threeui`.\n\n## Technologies\n\n- React variant host\n- Two exact authored Canvas 2D documents\n- Six interactive text-path studies\n- Sandboxed source isolation\n- Light and dark palette controls\n\n## Verified source material\n\n- `text-on-a-path-ii.html — FIG 06 Globe, FIG 07 Outline Typeflow, FIG 08 Morphing Glyph Cloud, and FIG 11 Cloth`\n- `text-on-a-path.html — FIG 04 Ripple and FIG 05 Ball`\n- `src/shaders/text-path-studies/TextPathStudies.tsx`\n\nSource revision: `SHA-256 2e21ae3b77c3 + e5d01ff0fc47`\n\n## Implementation steps\n\n1. Open every verified source file listed above and identify the renderer, host lifecycle, styles, and assets before editing.\n2. Keep TextPathStudies as the public entry point and select globe-study, outline-typeflow, morphing-glyph-cloud, cloth-study, ripple-study, or ball-study with the variant prop.\n3. Retain both complete authored Text on a Path documents and focus only the selected figure inside an allow-scripts-only sandbox.\n4. Preserve each study's own drag, zoom, hover, click, morph, cloth, ripple, or particle interaction instead of flattening them into one renderer.\n5. Expose shared mode, scale, opacity, and palette controls at the collection boundary.\n6. Mount only the selected source document so inactive studies do not allocate canvases or animation loops.\n7. Give the local component a sized, overflow-controlled parent and verify desktop, mobile, reduced-motion, and context-loss behavior.\n\nAsset handling: This effect has no required external assets.\n\n## Local component example\n\nImport the copied local component rather than a package entrypoint:\n\n```tsx\nimport { TextPathStudies } from \"./effects/globe-study/TextPathStudies\";\nimport \"./effects/globe-study/styles.css\";\n\nexport function Scene() {\n return
;\n}\n```\n\n## Core renderer pattern\n\nThis excerpt documents orchestration only. Copy the exact shader, geometry, pass, and interaction code from the verified source files.\n\n```tsx\n\n```\n\n## Behavior contract\n\n- Runtime: Canvas 2D\n- Passes: 1 selected sandboxed Canvas 2D study\n- Interaction: Variant-specific drag, zoom, hover, click, morph, cloth, ripple, and particle interactions\n- Assets: No external assets\n- **source** (fixed): Six exact authored figures across two documents\n- **renderer** (sandbox): Canvas 2D\n- **mode** (optional): dark | light\n- **variant** (optional): Six text-path studies\n- **pixelRatio** (adaptive): ≤ 2.5\n- **assets** (fixed): None\n\n## Verification\n\n1. Compare the rendered composition, animation timing, pointer behavior, and state transitions with the source implementation.\n2. Exercise resize, high-DPI, mobile/coarse-pointer, reduced-motion, tab visibility, and WebGL context-loss paths where applicable.\n3. Confirm every animation frame, observer, listener, geometry, buffer, texture, framebuffer, material, and renderer is released on teardown.\n4. Check the browser console and confirm the effect renders at native-or-better backing resolution.\n\n## Guardrails\n\n- Do not substitute a visually similar package, demo, shader, or runtime.\n- Do not approximate, reconstruct, or simplify the authored GLSL, render passes, geometry, interaction state, or assets.\n- Keep exact source and asset hashes under regression tests when the source project provides them.\n- Adapt only the surrounding host boundary needed by the target project; keep renderer behavior intact.\n", "gallery-heading": "---\nname: add-gallery-heading\ndescription: \"Build Gallery Heading from its verified authored source using Canvas 2D, including the complete renderer, interactions, and required assets. Use when Codex needs to implement, port, or adapt this effect without requiring the ThreeUI package or reconstructing the visual from an approximation.\"\n---\n\n# Build Gallery Heading\n\n## Description\n\nAn oversized headline ringed by twelve 4:3 plates — one flat colour each, shaded by a procedural noise field rather than a gradient — that hold still until the pointer arrives, then orbit. Four galleries, each with its own field, typography, and direction.\n\nRecreate the authored behavior from the verified source, not from screenshots or the abbreviated orchestration sample in this skill. The implementation may live directly in the target project and does not require `@designcodeio/threeui`.\n\n## Technologies\n\n- React component with a sandboxed `srcDoc` effect boundary\n- Canvas 2D copied from the byte-exact Neuform export\n- A post-load focus adapter that keeps only the authored shader, button, canvas, or visual targets visible\n- Optional outer-frame hue, saturation, and brightness grading with source-exact defaults\n\n## Verified source material\n\n- `src/shaders/neuform-isolated/sources/gallery-heading.html`\n- `src/shaders/neuform-isolated/NeuformIsolatedEffects.tsx`\n\nSource revision: `SHA-256 8e42d2d5b497`\n\n## Implementation steps\n\n1. Open every verified source file listed above and identify the renderer, host lifecycle, styles, and assets before editing.\n2. Copy the complete canonical HTML source byte-for-byte so its shader strings, materials, DOM, timing, and initialization order remain auditable.\n3. After the source load event, retain only the `#stage` twelve-tile 4:3 gradient ring and its hover-driven serif headline; do not rewrite the renderer or approximate the composition.\n4. Force retained background targets to the sandbox viewport and center retained buttons without changing their internal pointer or shader state.\n5. Dispatch one resize event after reparenting so the exact source renderer recalculates its backing resolution.\n6. Apply optional hue, saturation, and brightness only to the outer iframe; omit the filter at 0/1/1 so source color remains exact.\n7. Keep the sandbox isolated with `allow-scripts` only; removing the iframe must release its document, listeners, frames, and graphics contexts together.\n8. Give the local component a sized, overflow-controlled parent and verify desktop, mobile, reduced-motion, and context-loss behavior.\n\nAsset handling: This effect has no required external assets.\n\n## Local component example\n\nImport the copied local component rather than a package entrypoint:\n\n```tsx\nimport { GalleryHeading } from \"./effects/gallery-heading/GalleryHeading\";\nimport \"./effects/gallery-heading/styles.css\";\n\nexport function Scene() {\n return
;\n}\n```\n\n## Core renderer pattern\n\nThis excerpt documents orchestration only. Copy the exact shader, geometry, pass, and interaction code from the verified source files.\n\n```tsx\nconst focusedSource = canonicalHtml\n .replace(\"\", focusStyles + \"\")\n .replace(\"\", focusAfterLoadScript + \"\");\n\nreturn