jeisaimporters-WEBSITE/.od-skills/luxury-botanical-6d7799632e/open-design.json

122 lines
45 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"$schema": "https://open-design.ai/schemas/plugin.v1.json",
"specVersion": "1.0.0",
"name": "example-luxury-botanical",
"title": "Luxury Botanical — Beyond The Collection",
"title_i18n": {
"zh-CN": "奢华植物香氛 — Beyond The Collection",
"zh-TW": "奢華植物香氛 — Beyond The Collection",
"ja": "Luxury Botanical — Beyond The Collection",
"ko": "Luxury Botanical — Beyond The Collection",
"de": "Luxury Botanical — Beyond The Collection",
"fr": "Luxury Botanical — Beyond The Collection",
"ru": "Luxury Botanical — Beyond The Collection",
"es": "Luxury Botanical — Beyond The Collection",
"pt-BR": "Luxury Botanical — Beyond The Collection",
"it": "Luxury Botanical — Beyond The Collection",
"vi": "Luxury Botanical — Beyond The Collection",
"pl": "Luxury Botanical — Beyond The Collection",
"id": "Luxury Botanical — Beyond The Collection",
"nl": "Luxury Botanical — Beyond The Collection",
"ar": "Luxury Botanical — Beyond The Collection",
"tr": "Luxury Botanical — Beyond The Collection",
"uk": "Luxury Botanical — Beyond The Collection",
"en": "Luxury Botanical — Beyond The Collection"
},
"version": "0.1.0",
"description": "Cinematic luxury-fragrance landing page: a fullscreen video hero, a scroll-driven elliptical clip-path reveal, an orbiting carousel of perfume bottles that scales up at a focal point, a newsletter section, and a warm parchment footer.",
"description_i18n": {
"zh-CN": "电影感奢华香氛落地页:全屏视频英雄区、滚动驱动的椭圆裁切揭示、在焦点处放大的香水瓶环绕轮播、订阅区与暖色羊皮纸页脚。",
"zh-TW": "電影感奢華香氛落地頁:全螢幕影片英雄區、捲動驅動的橢圓裁切揭示、在焦點處放大的香水瓶環繞輪播、訂閱區與暖色羊皮紙頁尾。",
"ja": "映画的なラグジュアリーフレグランスのランディングページ。全画面の動画ヒーロー、スクロール連動の楕円クリップパスによる演出、焦点で拡大する香水瓶の周回カルーセル、ニュースレターセクション、そして温かみのある羊皮紙調のフッターを備えます。",
"ko": "영화적인 럭셔리 향수 랜딩 페이지: 전체 화면 영상 히어로, 스크롤 기반의 타원형 클립패스 공개 효과, 초점에서 확대되는 향수병 궤도 캐러셀, 뉴스레터 섹션, 그리고 따뜻한 양피지 풋터로 구성됩니다.",
"de": "Cineastische Luxus-Duft-Landingpage: ein Vollbild-Video-Hero, eine scrollgesteuerte elliptische Clip-Path-Enthüllung, ein umlaufendes Karussell aus Parfümflakons, die an einem Fokuspunkt größer werden, ein Newsletter-Abschnitt und ein warmer Pergament-Footer.",
"fr": "Landing cinématique pour parfum de luxe : un hero vidéo plein écran, une révélation par clip-path elliptique pilotée au défilement, un carrousel de flacons de parfum en orbite qui s'agrandissent à un point focal, une section newsletter et un pied de page en parchemin chaud.",
"ru": "Кинематографичный лендинг люксового парфюма: полноэкранный видео-hero, эллиптическое раскрытие через clip-path, управляемое прокруткой, орбитальная карусель флаконов духов, увеличивающихся в фокусной точке, секция подписки на рассылку и тёплый пергаментный футер.",
"es": "Landing cinematográfica de fragancia de lujo: un hero de vídeo a pantalla completa, una revelación elíptica con clip-path impulsada por scroll, un carrusel orbital de frascos de perfume que se agrandan en un punto focal, una sección de newsletter y un pie de página de pergamino cálido.",
"pt-BR": "Landing cinematográfica de fragrância de luxo: um hero de vídeo em tela cheia, uma revelação elíptica com clip-path guiada por rolagem, um carrossel orbital de frascos de perfume que aumentam num ponto focal, uma seção de newsletter e um rodapé de pergaminho quente.",
"it": "Landing cinematografica per una fragranza di lusso: un hero video a schermo intero, una rivelazione ellittica con clip-path guidata dallo scroll, un carosello orbitante di flaconi di profumo che si ingrandiscono in un punto focale, una sezione newsletter e un footer in pergamena calda.",
"vi": "Trang đích nước hoa cao cấp đậm chất điện ảnh: hero video toàn màn hình, hiệu ứng hé lộ clip-path hình elip điều khiển bằng cuộn, băng chuyền các chai nước hoa xoay quanh quỹ đạo phóng to tại điểm tiêu cự, phần đăng ký nhận bản tin và chân trang giấy da tông ấm.",
"pl": "Filmowy landing luksusowych perfum: pełnoekranowy hero wideo, sterowane przewijaniem eliptyczne odsłanianie clip-path, orbitujący karuzela flakonów perfum powiększających się w punkcie ogniskowym, sekcja newslettera oraz ciepła pergaminowa stopka.",
"id": "Landing parfum mewah bernuansa sinematik: hero video layar penuh, reveal clip-path elips yang digerakkan oleh gulir, korsel orbit botol parfum yang membesar pada titik fokus, bagian newsletter, dan footer perkamen hangat.",
"nl": "Cinematische landingpagina voor luxe parfum: een fullscreen video-hero, een scrollgestuurde elliptische clip-path-onthulling, een orbitale carrousel van parfumflesjes die op een focuspunt groter worden, een nieuwsbriefsectie en een warme perkamenten footer.",
"ar": "صفحة هبوط سينمائية لعطر فاخر: قسم رئيسي بفيديو ملء الشاشة، وكشف بيضاوي عبر clip-path مدفوع بالتمرير، ودوّارة مدارية لزجاجات عطر تتكبّر عند نقطة التركيز، وقسم نشرة بريدية، وتذييل بلون رقّ دافئ.",
"tr": "Lüks bir parfüm için sinematik açılış sayfası: tam ekran video hero, kaydırmayla yönetilen eliptik clip-path açığa çıkarması, bir odak noktasında büyüyen yörüngede dönen parfüm şişeleri karuseli, bülten bölümü ve sıcak parşömen footer.",
"uk": "Кінематографічний лендинг люксового парфуму: повноекранний відео-hero, еліптичне розкриття через clip-path, кероване прокручуванням, орбітальна карусель флаконів парфумів, що збільшуються у фокусній точці, секція підписки на розсилку та теплий пергаментний футер.",
"en": "Cinematic luxury-fragrance landing page: a fullscreen video hero, a scroll-driven elliptical clip-path reveal, an orbiting carousel of perfume bottles that scales up at a focal point, a newsletter section, and a warm parchment footer."
},
"license": "CC-BY-4.0",
"author": {
"name": "Eli"
},
"homepage": "https://motionsites.ai/sections",
"tags": [
"example",
"first-party",
"prototype",
"design",
"web",
"desktop",
"luxury",
"fragrance",
"botanical",
"scroll-animation"
],
"compat": {
"agentSkills": [
{
"path": "./SKILL.md"
}
]
},
"od": {
"kind": "scenario",
"taskKind": "new-generation",
"mode": "prototype",
"platform": "desktop",
"scenario": "design",
"surface": "web",
"preview": {
"type": "html",
"entry": "./example.html"
},
"useCase": {
"query": {
"en": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n<title>Bentley — Beyond The Collection</title>\n\n<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin />\n<link\n href=\"https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Manrope:wght@300;400;500;600&family=Great+Vibes&display=swap\"\n rel=\"stylesheet\"\n/>\n\n<script src=\"https://cdn.tailwindcss.com\"></script>\n<script>\n tailwind.config = {\n theme: {\n extend: {\n fontFamily: {\n serif: ['\"Instrument Serif\"', \"serif\"],\n sans: [\"Manrope\", \"sans-serif\"],\n script: ['\"Great Vibes\"', \"cursive\"],\n },\n },\n },\n };\n</script>\n\n<style>\n *, *::before, *::after { box-sizing: border-box; }\n html, body { margin: 0; padding: 0; background: #000; }\n body {\n font-family: \"Manrope\", ui-sans-serif, system-ui, sans-serif;\n -webkit-font-smoothing: antialiased;\n }\n\n .orbit-container {\n position: relative;\n margin-left: auto;\n margin-right: auto;\n }\n .orbit-scaling-container {\n width: 100%;\n height: 100%;\n position: relative;\n }\n .orbit-scaling-container--responsive {\n position: absolute;\n left: 50%;\n top: 50%;\n transform-origin: center center;\n }\n .orbit-rotation-wrapper {\n width: 100%;\n height: 100%;\n transform-origin: center center;\n position: relative;\n }\n .orbit-path-svg {\n position: absolute;\n inset: 0;\n pointer-events: none;\n }\n .orbit-item {\n position: absolute;\n will-change: transform;\n user-select: none;\n }\n .orbit-center-content {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n }\n .orbit-image {\n width: 100%;\n height: 100%;\n object-fit: contain;\n border-radius: 50%;\n }\n\n @keyframes scrollArrow {\n 0% { transform: translateY(-6px); opacity: 0; }\n 40% { opacity: 1; }\n 100% { transform: translateY(10px); opacity: 0; }\n }\n .scroll-arrow {\n animation: scrollArrow 1.6s ease-in-out infinite;\n }\n</style>\n\n<script src=\"https://unpkg.com/@babel/standalone@7.29.0/babel.min.js\" integrity=\"sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y\" crossorigin=\"anonymous\"></script>\n<script type=\"module\">\n import React from \"https://esm.sh/react@18.3.1\";\n import * as ReactDOMClient from \"https://esm.sh/react-dom@18.3.1/client?deps=react@18.3.1\";\n import * as FM from \"https://esm.sh/framer-motion@11.18.2?deps=react@18.3.1,react-dom@18.3.1\";\n window.React = React;\n window.ReactDOM = ReactDOMClient;\n window.FM = FM;\n window.__depsReady = true;\n window.dispatchEvent(new Event(\"deps-ready\"));\n</script>\n</head>\n<body>\n <div id=\"root\"></div>\n\n <script type=\"text/babel\" data-presets=\"react\">\n(function () {\n const start = () => {\n const { useRef, useState, useEffect, useMemo } = React;\n const { createRoot } = ReactDOM;\n const {\n motion,\n useScroll,\n useTransform,\n useMotionTemplate,\n useMotionValue,\n useAnimationFrame,\n animate\n } = window.FM;\n\n /* ============================================================\n OrbitImages\n ============================================================ */\n\n function generateEllipsePath(cx, cy, rx, ry) {\n return `M ${cx - rx} ${cy} A ${rx} ${ry} 0 1 0 ${cx + rx} ${cy} A ${rx} ${ry} 0 1 0 ${cx - rx} ${cy}`;\n }\n\n function OrbitItem({\n item,\n title,\n desc,\n index,\n totalItems,\n pathValue,\n itemSizeValue,\n rotationValue,\n progress,\n fill,\n scaleStrength,\n focalPoint = 50\n }) {\n const itemOffset = fill ? index / totalItems * 100 : 0;\n\n const offsetPercentage = useTransform(progress, (p) => {\n return ((p + itemOffset) % 100 + 100) % 100;\n });\n\n const offsetDistance = useTransform(offsetPercentage, (p) => `${p}%`);\n\n const itemScale = useTransform(() => {\n const rawPos = offsetPercentage.get();\n const strength = scaleStrength ? scaleStrength.get() : 0;\n let dist = Math.abs(rawPos - focalPoint);\n if (dist > 50) dist = 100 - dist;\n let targetScale = 1;\n if (dist < 20) {\n const ratio = dist / 20;\n const cosCurve = (Math.cos(ratio * Math.PI) + 1) / 2;\n targetScale = 0.4 + cosCurve * 0.6;\n } else {\n targetScale = 0.4;\n }\n return 1 - strength * (1 - targetScale);\n });\n\n const offsetPath = useMotionTemplate`path(\"${pathValue}\")`;\n const zIndexMV = useTransform(itemScale, (s) => Math.round(s * 100));\n const counterRotate = useTransform(rotationValue, (r) => `rotate(${-r}deg)`);\n const labelOpacity = useTransform(scaleStrength || useMotionValue(0), (s) => s);\n\n return (\n <motion.div\n className=\"orbit-item\"\n style={{\n width: itemSizeValue,\n height: itemSizeValue,\n offsetPath,\n offsetRotate: \"0deg\",\n offsetAnchor: \"center center\",\n offsetDistance,\n scale: itemScale,\n zIndex: zIndexMV,\n pointerEvents: \"auto\"\n }}>\n <motion.div style={{ transform: counterRotate, width: \"100%\", height: \"100%\", position: \"relative\" }}>\n {item}\n {(title || desc) &&\n <motion.div\n style={{\n position: \"absolute\",\n left: \"115%\",\n top: \"50%\",\n transform: \"translateY(-50%)\",\n width: \"min(360px, 95%)\",\n color: \"#000\",\n opacity: labelOpacity,\n pointerEvents: \"none\",\n fontFamily: \"Manrope, ui-sans-serif, system-ui, sans-serif\"\n }}>\n {title &&\n <div style={{\n fontFamily: \"'Instrument Serif', serif\",\n fontSize: \"clamp(26px, 3vw, 40px)\",\n lineHeight: 1.05,\n letterSpacing: \"-0.01em\",\n marginBottom: \"14px\",\n whiteSpace: \"normal\"\n }}>\n {title}\n </div>\n }\n {desc &&\n <div style={{\n fontFamily: \"Manrope, ui-sans-serif, system-ui, sans-serif\",\n fontWeight: 400,\n fontSize: \"clamp(13px, 1vw, 15px)\",\n lineHeight: 1.5,\n color: \"rgba(0,0,0,0.72)\"\n }}>\n {desc}\n </div>\n }\n </motion.div>\n }\n </motion.div>\n </motion.div>\n );\n }\n\n function OrbitImages({\n images = [],\n altPrefix = \"Orbiting image\",\n baseWidth = 1400,\n radiusX = 700,\n radiusY = 170,\n duration = 40,\n itemSize = 64,\n direction = \"normal\",\n fill = true,\n width = 100,\n height = 100,\n className = \"\",\n showPath = false,\n pathColor = \"rgba(0,0,0,0.1)\",\n pathWidth = 2,\n easing = \"linear\",\n paused = false,\n centerContent,\n responsive = false,\n progressOverride,\n radiusXOverride,\n radiusYOverride,\n itemSizeOverride,\n rotationOverride,\n translateXOverride,\n focusStrength\n }) {\n const containerRef = useRef(null);\n const [scale, setScale] = useState(1);\n\n const designCenterX = baseWidth / 2;\n const designCenterY = baseWidth / 2;\n\n const currentRadiusX = radiusXOverride || useMotionValue(radiusX);\n const currentRadiusY = radiusYOverride || useMotionValue(radiusY);\n const currentItemSize = itemSizeOverride || useMotionValue(itemSize);\n const currentRotation = rotationOverride || useMotionValue(-8);\n const currentTranslateX = translateXOverride || useMotionValue(0);\n\n const pathValue = useTransform([currentRadiusX, currentRadiusY], ([rx, ry]) => {\n return generateEllipsePath(designCenterX, designCenterY, rx, ry);\n });\n\n useEffect(() => {\n if (!responsive || !containerRef.current) return;\n const updateScale = () => {\n if (!containerRef.current) return;\n setScale(containerRef.current.clientWidth / baseWidth);\n };\n updateScale();\n const observer = new ResizeObserver(updateScale);\n observer.observe(containerRef.current);\n return () => observer.disconnect();\n }, [responsive, baseWidth]);\n\n const internalProgress = useMotionValue(0);\n\n useEffect(() => {\n if (paused || progressOverride) return;\n const controls = animate(internalProgress, direction === \"reverse\" ? -100 : 100, {\n duration,\n ease: easing,\n repeat: Infinity,\n repeatType: \"loop\"\n });\n return () => controls.stop();\n }, [internalProgress, duration, easing, direction, paused, progressOverride]);\n\n const activeProgress = progressOverride || internalProgress;\n const containerWidth = responsive ? \"100%\" : typeof width === \"number\" ? width : \"100%\";\n const containerHeight = responsive ? \"auto\" : typeof height === \"number\" ? height : typeof width === \"number\" ? width : \"auto\";\n\n const items = images.map((entry, index) => {\n const src = typeof entry === \"string\" ? entry : entry.src;\n return (\n <motion.img\n key={src}\n src={src}\n alt={`${altPrefix} ${index + 1}`}\n draggable={false}\n className=\"orbit-image\"\n whileHover={{ scale: 1.2 }}\n transition={{ duration: 0.3 }}\n style={{ cursor: \"pointer\", pointerEvents: \"auto\" }}\n />\n );\n });\n\n return (\n <div\n ref={containerRef}\n className={`orbit-container ${className}`}\n style={{\n width: containerWidth,\n height: containerHeight,\n aspectRatio: responsive ? \"1 / 1\" : undefined\n }}\n aria-hidden=\"true\">\n <div\n className={responsive ? \"orbit-scaling-container orbit-scaling-container--responsive\" : \"orbit-scaling-container\"}\n style={{\n width: responsive ? baseWidth : \"100%\",\n height: responsive ? baseWidth : \"100%\",\n transform: responsive ? `translate(-50%, -50%) scale(${scale})` : undefined\n }}>\n <motion.div className=\"orbit-rotation-wrapper\" style={{ rotate: currentRotation, x: currentTranslateX }}>\n {showPath &&\n <svg width=\"100%\" height=\"100%\" viewBox={`0 0 ${baseWidth} ${baseWidth}`} className=\"orbit-path-svg\">\n <path d={pathValue.get()} fill=\"none\" stroke={pathColor} strokeWidth={pathWidth / scale} />\n </svg>\n }\n {items.map((item, index) => {\n const entry = images[index];\n const title = typeof entry === \"object\" ? entry.title : null;\n const desc = typeof entry === \"object\" ? entry.desc : null;\n return (\n <OrbitItem\n key={index}\n item={item}\n title={title}\n desc={desc}\n index={index}\n totalItems={items.length}\n pathValue={pathValue}\n itemSizeValue={currentItemSize}\n rotationValue={currentRotation}\n progress={activeProgress}\n fill={fill}\n scaleStrength={focusStrength}\n focalPoint={50}\n />\n );\n })}\n </motion.div>\n </div>\n {centerContent && <div className=\"orbit-center-content\">{centerContent}</div>}\n </div>\n );\n }\n\n /* ============================================================\n StaySection\n ============================================================ */\n\n function StaySection() {\n const blurUp = {\n initial: { opacity: 0, y: 40, filter: \"blur(20px)\" },\n whileInView: { opacity: 1, y: 0, filter: \"blur(0px)\" },\n viewport: { once: true, amount: 0.3 },\n transition: { duration: 1, ease: \"easeOut\" }\n };\n\n return (\n <section\n className=\"relative w-full overflow-hidden\"\n style={{ minHeight: \"100vh\", backgroundColor: \"#ffffff\" }}>\n <img\n src=\"https://plugin-assets.open-design.ai/plugins/luxury-botanical/pasted-1779282335552-1_gmztyi-eccf42.webp\"\n alt=\"\"\n aria-hidden=\"true\"\n className=\"absolute inset-x-0 bottom-0 w-full pointer-events-none select-none\"\n style={{ objectFit: \"cover\", objectPosition: \"center bottom\" }}\n />\n\n <div className=\"relative max-w-[1480px] mx-auto px-8 md:px-16 pt-20 md:pt-24 pb-20 md:pb-24 min-h-screen flex flex-col\" style={{ gap: \"32px\" }}>\n <motion.div {...blurUp}>\n <div style={{\n fontFamily: \"'Instrument Serif', serif\",\n fontSize: \"clamp(60px, 11vw, 160px)\",\n lineHeight: 0.95,\n letterSpacing: \"-0.01em\",\n color: \"#000\"\n }}>\n Stay <span style={{ fontStyle: \"italic\" }}>in</span>\n </div>\n <div style={{\n fontFamily: \"Manrope, ui-sans-serif, sans-serif\",\n fontWeight: 400,\n lineHeight: 0.95,\n letterSpacing: \"-0.02em\",\n color: \"#000\",\n fontSize: \"64px\"\n }}>\n the collection\n </div>\n </motion.div>\n\n <motion.div\n {...blurUp}\n transition={{ duration: 1, ease: \"easeOut\", delay: 0.2 }}\n className=\"max-w-md\">\n <p className=\"mb-6\" style={{\n fontFamily: \"Manrope, ui-sans-serif, sans-serif\",\n fontSize: \"15px\",\n lineHeight: 1.55,\n color: \"rgba(0,0,0,0.78)\"\n }}>\n Editions and invitations from the Bentley fragrance studio, sent twice a season.\n </p>\n <form\n className=\"flex items-center border-b border-black/40 pb-2 gap-3\"\n onSubmit={(e) => e.preventDefault()}>\n <input\n type=\"email\"\n placeholder=\"your@email.com\"\n className=\"bg-transparent flex-1 outline-none\"\n style={{ fontFamily: \"Manrope, ui-sans-serif, sans-serif\", fontSize: \"15px\", color: \"#000\" }}\n />\n <button type=\"submit\" style={{\n fontFamily: \"Manrope, ui-sans-serif, sans-serif\",\n fontSize: \"11px\",\n fontWeight: 500,\n letterSpacing: \"0.25em\",\n textTransform: \"uppercase\",\n color: \"#000\"\n }}>\n Subscribe →\n </button>\n </form>\n </motion.div>\n </div>\n </section>\n );\n }\n\n /* ============================================================\n Footer\n ============================================================ */\n\n function Footer() {\n const blurUp = {\n initial: { opacity: 0, y: 40, filter: \"blur(20px)\" },\n whileInView: { opacity: 1, y: 0, filter: \"blur(0px)\" },\n viewport: { once: true, amount: 0.3 },\n transition: { duration: 1, ease: \"easeOut\" }\n };\n\n const Column = ({ heading, items }) => (\n <div>\n <div className=\"mb-5 text-black/55\" style={{\n fontFamily: \"Manrope, ui-sans-serif, sans-serif\",\n fontSize: \"11px\",\n fontWeight: 500,\n letterSpacing: \"0.3em\",\n textTransform: \"uppercase\"\n }}>\n {heading}\n </div>\n <ul className=\"space-y-3\">\n {items.map((label) => (\n <li key={label}>\n <a href=\"#\" className=\"hover:underline\" style={{\n fontFamily: \"Manrope, ui-sans-serif, sans-serif\",\n fontSize: \"15px\",\n fontWeight: 400,\n color: \"rgba(0,0,0,0.85)\"\n }}>\n {label}\n </a>\n </li>\n ))}\n </ul>\n </div>\n );\n\n return (\n <footer className=\"relative w-full text-black overflow-hidden\" style={{ backgroundColor: \"#f4ecdc\" }}>\n <div className=\"relative max-w-[1480px] mx-auto px-8 md:px-16 pt-12 md:pt-14 pb-12\">\n <motion.div\n {...blurUp}\n transition={{ duration: 1, ease: \"easeOut\", delay: 0.15 }}\n className=\"grid grid-cols-2 md:grid-cols-4 gap-12 md:gap-10 mb-20 md:mb-24\">\n <Column heading=\"Discover\" items={[\"All fragrances\", \"The bottle\", \"Sustainability\", \"Editions\"]} />\n <Column heading=\"Studio\" items={[\"Our story\", \"Perfumers\", \"Atelier visits\", \"Press\"]} />\n <Column heading=\"Contact\" items={[\"Boutiques\", \"Concierge\", \"Returns\", \"Care guide\"]} />\n\n <div>\n <div className=\"mb-5 text-black/55\" style={{\n fontFamily: \"Manrope, ui-sans-serif, sans-serif\",\n fontSize: \"11px\",\n fontWeight: 500,\n letterSpacing: \"0.3em\",\n textTransform: \"uppercase\"\n }}>\n Newsletter\n </div>\n <p className=\"mb-5 text-black/65\" style={{\n fontFamily: \"Manrope, ui-sans-serif, sans-serif\",\n fontSize: \"14px\",\n lineHeight: 1.5\n }}>\n Editions and invitations, sent twice a season.\n </p>\n <form\n className=\"flex items-center border-b border-black/30 pb-2 gap-3\"\n onSubmit={(e) => e.preventDefault()}>\n <input\n type=\"email\"\n placeholder=\"your@email.com\"\n className=\"bg-transparent flex-1 outline-none\"\n style={{ fontFamily: \"Manrope, ui-sans-serif, sans-serif\", fontSize: \"14px\", color: \"#000\" }}\n />\n <button type=\"submit\" style={{\n fontFamily: \"Manrope, ui-sans-serif, sans-serif\",\n fontSize: \"11px\",\n fontWeight: 500,\n letterSpacing: \"0.25em\",\n textTransform: \"uppercase\"\n }}>\n Subscribe →\n </button>\n </form>\n </div>\n </motion.div>\n\n <motion.div\n {...blurUp}\n transition={{ duration: 0.9, ease: \"easeOut\", delay: 0.25 }}\n className=\"flex flex-col md:flex-row items-start md:items-center justify-between gap-6 pt-8 border-t border-black/15\">\n <div className=\"text-black/55\" style={{\n fontFamily: \"Manrope, ui-sans-serif, sans-serif\",\n fontSize: \"11px\",\n fontWeight: 500,\n letterSpacing: \"0.3em\",\n textTransform: \"uppercase\"\n }}>\n © 2026 Beyond The Collection\n </div>\n <div className=\"flex items-center gap-5\" style={{\n fontFamily: \"Manrope, ui-sans-serif, sans-serif\",\n fontSize: \"11px\",\n fontWeight: 500,\n letterSpacing: \"0.28em\",\n textTransform: \"uppercase\"\n }}>\n <a href=\"#\" className=\"hover:underline\">Instagram</a>\n <span className=\"text-black/30\">·</span>\n <a href=\"#\" className=\"hover:underline\">TikTok</a>\n <span className=\"text-black/30\">·</span>\n <a href=\"#\" className=\"hover:underline\">Spotify</a>\n </div>\n <div className=\"text-black/55\" style={{\n fontFamily: \"Manrope, ui-sans-serif, sans-serif\",\n fontSize: \"11px\",\n fontWeight: 500,\n letterSpacing: \"0.3em\",\n textTransform: \"uppercase\"\n }}>\n EN · USD\n </div>\n </motion.div>\n </div>\n </footer>\n );\n }\n\n /* ============================================================\n App\n ============================================================ */\n\n const orbitImagesData = [\n {\n src: \"https://plugin-assets.open-design.ai/plugins/luxury-botanical/BL1996-Beyond_wild_vetiver_Flakon_100ml_300dpi_a55ie5-ad7edb.webp\",\n title: \"Wild Vetiver\",\n desc: \"Smoky vetiver wrapped in saffron and leather — a grounded, untamed signature.\"\n },\n {\n src: \"https://plugin-assets.open-design.ai/plugins/luxury-botanical/BL2156_BEYOND_RADIANT_OSMANTHUS_hoc3up-259f05.webp\",\n title: \"Radiant Osmanthus\",\n desc: \"Apricot-tinged osmanthus over soft musks. Quietly luminous, never loud.\"\n },\n {\n src: \"https://plugin-assets.open-design.ai/plugins/luxury-botanical/BL2157_BEYOND_VIBRANT_HIBISCUS_pgiehq-8c4cbc.webp\",\n title: \"Vibrant Hibiscus\",\n desc: \"Bright hibiscus and pink pepper resting on creamy sandalwood.\"\n },\n {\n src: \"https://plugin-assets.open-design.ai/plugins/luxury-botanical/BL2158_BEYOND_MELLOW_HELIOTROPE_agqych-36c451.webp\",\n title: \"Mellow Heliotrope\",\n desc: \"Almond, vanilla and heliotrope petals — a powdery, hushed warmth.\"\n },\n {\n src: \"https://plugin-assets.open-design.ai/plugins/luxury-botanical/BL2371-BL2372-BL2373-Magnetic-Amber_web_2_dbmtpy-e68423.webp\",\n title: \"Magnetic Amber\",\n desc: \"Resinous amber, oud and rich woods. The collection's deepest note.\"\n },\n {\n src: \"https://plugin-assets.open-design.ai/plugins/luxury-botanical/BL2156_BEYOND_RADIANT_OSMANTHUS_1_hlc4v1-572755.webp\",\n title: \"Crystal Edition\",\n desc: \"A limited cut of the bottle — etched facets, lavender pour, leather collar.\"\n }\n ];\n\n const VIDEO_SRC = \"https://plugin-assets.open-design.ai/plugins/luxury-botanical/hf_20260520_114550_b72cc2b7-2267-4d9e-b19f-f3bb4b0c7084-e5c560.mp4\";\n const TARGET_RADIUS = 650;\n\n function App() {\n const containerRef = useRef(null);\n\n const { scrollYProgress } = useScroll({\n target: containerRef,\n offset: [\"start start\", \"end end\"]\n });\n\n const rx = useTransform(scrollYProgress, [0, 0.08, 1], [\"0%\", \"55%\", \"55%\"]);\n const ry = useTransform(scrollYProgress, [0, 0.08, 1], [\"0%\", \"55%\", \"55%\"]);\n const clipPath = useMotionTemplate`ellipse(${rx} ${ry} at 50% 50%)`;\n\n const textOpacity = useTransform(\n scrollYProgress,\n [0.03, 0.08, 0.15, 0.22, 0.90, 0.98, 1],\n [0, 1, 1, 0, 0, 1, 1]\n );\n const textBlurVal = useTransform(\n scrollYProgress,\n [0.03, 0.08, 0.15, 0.22, 0.90, 0.98, 1],\n [15, 0, 0, 15, 15, 0, 0]\n );\n const filterText = useMotionTemplate`blur(${textBlurVal}px)`;\n const yElement = useTransform(\n scrollYProgress,\n [0.03, 0.08, 0.15, 0.22, 0.90, 0.98, 1],\n [20, 0, 0, 20, 20, 0, 0]\n );\n\n const scrollHintOpacity = useTransform(scrollYProgress, [0, 0.03, 0.08], [1, 1, 0]);\n\n const orbitItemSize = useTransform(scrollYProgress, [0.15, 0.25, 0.85, 0.95, 1], [80, 360, 360, 80, 80]);\n const orbitRx = useTransform(scrollYProgress, [0.15, 0.25, 0.85, 0.95, 1], [330, TARGET_RADIUS, TARGET_RADIUS, 330, 330]);\n const orbitRy = useTransform(scrollYProgress, [0.15, 0.25, 0.85, 0.95, 1], [140, TARGET_RADIUS, TARGET_RADIUS, 140, 140]);\n const orbitRotation = useTransform(scrollYProgress, [0.15, 0.25, 0.85, 0.95, 1], [-15, 0, 0, -15, -15]);\n const orbitTx = useTransform(\n scrollYProgress,\n [0.15, 0.25, 0.85, 0.95, 1],\n [0, -(TARGET_RADIUS + 200), -(TARGET_RADIUS + 200), 0, 0]\n );\n const focusStrength = useTransform(scrollYProgress, [0.15, 0.25, 0.85, 0.95, 1], [0, 1, 1, 0, 0]);\n\n const orbitProgress = useMotionValue(0);\n const prevScroll = useRef(0);\n\n useAnimationFrame((time, delta) => {\n const pos = scrollYProgress.get();\n const scrollDelta = pos - prevScroll.current;\n prevScroll.current = pos;\n\n let frameSpeed = 0;\n if (pos > 0.15 && pos < 0.85) {\n frameSpeed = scrollDelta * 200;\n } else {\n frameSpeed = delta / 1000 * 2.5;\n }\n\n orbitProgress.set(orbitProgress.get() + frameSpeed);\n });\n\n return (\n <>\n <div ref={containerRef} className=\"relative w-full h-[600vh] bg-black\">\n <div className=\"sticky top-0 w-full h-screen overflow-hidden text-white\">\n\n {/* Video background */}\n <video autoPlay loop muted playsInline className=\"absolute inset-0 w-full h-full object-cover z-0\">\n <source src={VIDEO_SRC} type=\"video/mp4\" />\n </video>\n\n {/* Top-left logo text */}\n <div\n className=\"absolute z-10 flex flex-col items-start text-left text-black select-none leading-[0.95]\"\n style={{ top: \"120px\", left: \"96px\" }}>\n <div className=\"flex items-baseline\">\n <span style={{ fontFamily: \"'Instrument Serif', serif\", fontSize: \"clamp(32px, 5vw, 64px)\" }}>\n Beyond\n </span>\n <span style={{\n fontFamily: \"'Instrument Serif', serif\",\n fontStyle: \"italic\",\n fontSize: \"clamp(32px, 5vw, 64px)\",\n marginLeft: \"0.05em\"\n }}>\n The\n </span>\n </div>\n <span style={{\n fontFamily: \"Manrope, ui-sans-serif, system-ui, sans-serif\",\n fontWeight: 400,\n fontSize: \"clamp(28px, 4.4vw, 56px)\",\n letterSpacing: \"-0.01em\",\n marginTop: \"0.05em\"\n }}>\n Collection\n </span>\n </div>\n\n {/* Scroll hint arrow */}\n <motion.div\n className=\"absolute z-10 left-1/2 -translate-x-1/2 flex flex-col items-center text-white select-none pointer-events-none\"\n style={{ bottom: \"40px\", opacity: scrollHintOpacity }}>\n <div className=\"relative w-[20px] h-[34px] overflow-hidden\">\n <svg\n className=\"scroll-arrow absolute inset-0\"\n width=\"20\" height=\"34\" viewBox=\"0 0 20 34\" fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M10 4 V28 M3 21 L10 28 L17 21\" stroke=\"#ffffff\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </div>\n </motion.div>\n\n {/* Clip-path reveal with orbit */}\n <motion.div\n className=\"absolute z-20 flex items-center justify-center overflow-hidden\"\n style={{\n clipPath,\n rotate: -15,\n width: \"150vw\",\n height: \"150vh\",\n left: \"-25vw\",\n top: \"-25vh\"\n }}>\n <div className=\"absolute inset-0 bg-white\" />\n <div\n className=\"relative flex flex-col items-center justify-center\"\n style={{ width: \"100vw\", height: \"100vh\", transform: \"rotate(15deg)\" }}>\n <motion.div className=\"w-[90vw] max-w-[1200px] aspect-square relative z-0\">\n <OrbitImages\n images={orbitImagesData}\n shape=\"ellipse\"\n direction=\"normal\"\n duration={40}\n fill={true}\n showPath={false}\n responsive={true}\n baseWidth={800}\n progressOverride={orbitProgress}\n radiusXOverride={orbitRx}\n radiusYOverride={orbitRy}\n itemSizeOverride={orbitItemSize}\n rotationOverride={orbitRotation}\n translateXOverride={orbitTx}\n focusStrength={focusStrength}\n />\n </motion.div>\n </div>\n </motion.div>\n\n {/* Text overlays */}\n <div className=\"absolute inset-0 z-[60] pointer-events-none\">\n\n {/* Center brand text */}\n <div className=\"absolute top-[48%] left-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none z-50\">\n <motion.div\n className=\"flex flex-col items-center whitespace-nowrap pointer-events-auto\"\n style={{\n filter: filterText,\n opacity: textOpacity,\n WebkitFontSmoothing: \"antialiased\",\n WebkitBackfaceVisibility: \"hidden\",\n transform: \"translateZ(0)\"\n }}>\n <div className=\"flex items-baseline text-black leading-none mb-1\">\n <span className=\"font-serif text-[45px] md:text-[55px] tracking-tight text-black\">Beyond </span>\n <span className=\"font-serif text-[45px] md:text-[55px] italic tracking-tight text-black\">The</span>\n </div>\n <span className=\"font-sans text-[28px] md:text-[36px] tracking-tight text-black mt-[-5px]\">Collection</span>\n </motion.div>\n </div>\n\n {/* Top-right info */}\n <motion.div\n className=\"absolute top-32 right-[calc(6vw+150px)] md:right-[214px] flex flex-col items-start text-left pointer-events-auto cursor-text\"\n style={{ y: yElement, filter: filterText, opacity: textOpacity }}>\n <span className=\"font-serif text-[40px] leading-none mb-3 text-black\">2K26</span>\n <span className=\"font-serif text-[16px] uppercase tracking-widest text-black leading-[20px] text-left\">\n JOIN AN EXCLUSIVE<br />COMMUNITY\n </span>\n </motion.div>\n\n {/* Bottom-left number */}\n <motion.div\n className=\"absolute bottom-8 left-8 md:bottom-16 md:left-16 flex flex-col items-start text-black pointer-events-auto cursor-text\"\n style={{ y: yElement, filter: filterText, opacity: textOpacity }}>\n <span className=\"font-serif text-[40px] leading-none mb-1 text-black\">0651</span>\n <span className=\"font-serif text-[16px] uppercase tracking-widest text-black\">COLLECTION</span>\n </motion.div>\n\n {/* Bottom-right CTA */}\n <div className=\"absolute bottom-16 right-[6vw] md:right-[10vw] flex flex-col items-start z-10 pointer-events-auto\">\n <motion.p\n className=\"font-serif text-[16px] uppercase tracking-widest text-black leading-[20px] mb-6 text-left w-[240px] cursor-text\"\n style={{ y: yElement, filter: filterText, opacity: textOpacity }}>\n JOIN AN EXCLUSIVE COMMUNITY OF SAILORS. WHETHER YOU CRAVE THE THRILL OF THE OPEN\n </motion.p>\n <motion.div\n className=\"flex gap-0 pointer-events-auto items-center\"\n style={{ y: yElement, filter: filterText, opacity: textOpacity }}>\n <button className=\"bg-black hover:bg-black/90 transition-colors text-white rounded-[40px] px-8 py-3.5 font-serif tracking-[0.1em] uppercase text-[12px] md:text-[14px] z-10\">\n BUY COLLECTION\n </button>\n <button className=\"bg-black hover:bg-black/90 transition-colors w-[46px] h-[46px] flex items-center justify-center rounded-[50%] text-white -ml-2 z-0\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className=\"ml-1\">\n <path d=\"M5 12h14M12 5l7 7-7 7\" />\n </svg>\n </button>\n </motion.div>\n </div>\n </div>\n\n {/* Header */}\n <motion.header\n className=\"absolute top-0 left-0 w-full px-6 md:px-12 py-5 md:py-6 flex justify-between items-center z-[100] pointer-events-none\"\n style={{ opacity: scrollHintOpacity }}>\n <a href=\"#\" className=\"flex items-center gap-3 text-black select-none pointer-events-auto\" aria-label=\"Bentley\">\n <svg width=\"54\" height=\"40\" viewBox=\"0 0 84 60\" fill=\"none\" aria-hidden=\"true\">\n <g fill=\"currentColor\">\n <path d=\"M42 22 C30 22 19 16 4 12 C9 26 18 33 30 33 L42 33 Z\" />\n <path d=\"M42 22 C54 22 65 16 80 12 C75 26 66 33 54 33 L42 33 Z\" />\n <path d=\"M34 25 C36 28 39 30 42 30 C45 30 48 28 50 25 L42 22 Z\" opacity=\"0.7\" />\n </g>\n <text x=\"42\" y=\"52\" textAnchor=\"middle\" fontFamily=\"'Instrument Serif', serif\" fontSize=\"22\" fontStyle=\"italic\" fill=\"currentColor\">B</text>\n </svg>\n <span style={{\n fontFamily: \"Manrope, ui-sans-serif, sans-serif\",\n fontWeight: 600,\n fontSize: \"14px\",\n letterSpacing: \"0.42em\",\n textTransform: \"uppercase\"\n }}>\n Bentley\n </span>\n </a>\n\n <a\n href=\"#\"\n className=\"pointer-events-auto inline-flex items-center gap-2 bg-black text-white rounded-full pl-5 pr-2 py-2 hover:bg-black/85 transition-colors\"\n style={{\n fontFamily: \"Manrope, ui-sans-serif, sans-serif\",\n fontSize: \"11px\",\n fontWeight: 500,\n letterSpacing: \"0.22em\",\n textTransform: \"uppercase\"\n }}>\n <span className=\"hidden sm:inline\">Shop the collection</span>\n <span className=\"sm:hidden\">Shop</span>\n <span className=\"inline-flex w-7 h-7 items-center justify-center rounded-full bg-white/15\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M5 12h14M12 5l7 7-7 7\" />\n </svg>\n </span>\n </a>\n </motion.header>\n\n </div>\n </div>\n\n <StaySection />\n <Footer />\n </>\n );\n }\n\n createRoot(document.getElementById(\"root\")).render(<App />);\n };\n\n if (window.__depsReady) start();\n else window.addEventListener(\"deps-ready\", start, { once: true });\n})();\n </script>\n</body>\n</html>\n",
"zh-CN": "构建一个电影感的奢华香氛落地页(Bentley — Beyond The Collection):全屏视频英雄区、滚动驱动的椭圆 clip-path 揭示、香水瓶环绕椭圆轨道轮播(焦点处放大并显示标签)、Stay in the collection 订阅区与暖色羊皮纸页脚。完整规格以 en 字段为准。"
},
"exampleOutputs": [
{
"path": "./example.html",
"title": "Luxury Botanical — Beyond The Collection"
}
]
},
"context": {
"skills": [
{
"path": "./SKILL.md"
}
],
"assets": [
"./example.html"
]
},
"pipeline": {
"stages": [
{
"id": "generate",
"atoms": [
"file-write",
"live-artifact"
]
}
]
},
"capabilities": [
"prompt:inject",
"fs:write",
"network"
]
}
}