{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"hero-15","description":"Sera series — editorial monochrome hero with indexed meta row, oversized headline, dual CTAs, and hairline stat strip.","dependencies":["motion"],"registryDependencies":["@nusaiba/foundation","button","@nusaiba/theme-sera"],"files":[{"path":"registry/blocks/sera/sera-motion.tsx","content":"\"use client\";\n\nimport { useRef, type ReactNode } from \"react\";\nimport { AnimatePresence, motion, useInView, useReducedMotion } from \"motion/react\";\nimport { motionStagger, motionViewport } from \"@/lib/motion-tokens\";\nimport { cn } from \"@/lib/utils\";\n\nconst spring = { type: \"spring\" as const, duration: 0.3, bounce: 0 };\n\nexport function useSeraItemVariants() {\n\tconst reduceMotion = useReducedMotion();\n\n\treturn reduceMotion\n\t\t? { hidden: { opacity: 1 }, visible: { opacity: 1 } }\n\t\t: {\n\t\t\t\thidden: { opacity: 0, filter: \"blur(4px)\", y: 12 },\n\t\t\t\tvisible: {\n\t\t\t\t\topacity: 1,\n\t\t\t\t\tfilter: \"blur(0px)\",\n\t\t\t\t\ty: 0,\n\t\t\t\t\ttransition: spring,\n\t\t\t\t},\n\t\t\t};\n}\n\nexport function SeraStagger({\n\tchildren,\n\tclassName,\n}: {\n\tchildren: ReactNode;\n\tclassName?: string;\n}) {\n\tconst ref = useRef<HTMLDivElement>(null);\n\tconst inView = useInView(ref, motionViewport);\n\n\treturn (\n\t\t<motion.div\n\t\t\tanimate={inView ? \"visible\" : \"hidden\"}\n\t\t\tclassName={className}\n\t\t\tinitial=\"hidden\"\n\t\t\tref={ref}\n\t\t\tvariants={{\n\t\t\t\thidden: {},\n\t\t\t\tvisible: {\n\t\t\t\t\ttransition: { staggerChildren: motionStagger.item, delayChildren: 0.04 },\n\t\t\t\t},\n\t\t\t}}\n\t\t>\n\t\t\t{children}\n\t\t</motion.div>\n\t);\n}\n\nexport function SeraStaggerItem({\n\tchildren,\n\tclassName,\n}: {\n\tchildren: ReactNode;\n\tclassName?: string;\n}) {\n\tconst itemVariants = useSeraItemVariants();\n\n\treturn (\n\t\t<motion.div className={className} variants={itemVariants}>\n\t\t\t{children}\n\t\t</motion.div>\n\t);\n}\n\n/** Word reveal — signature text motion for Sera hero/headlines. */\nexport function SeraRevealWord({\n\tword,\n\tindex,\n\tclassName,\n}: {\n\tword: string;\n\tindex: number;\n\tclassName?: string;\n}) {\n\tconst reduceMotion = useReducedMotion();\n\n\treturn (\n\t\t<span className=\"inline-flex overflow-hidden pb-[0.08em]\">\n\t\t\t<motion.span\n\t\t\t\tclassName={className}\n\t\t\t\tinitial={\n\t\t\t\t\treduceMotion ? false : { opacity: 0, y: \"100%\", filter: \"blur(4px)\" }\n\t\t\t\t}\n\t\t\t\ttransition={{\n\t\t\t\t\t...spring,\n\t\t\t\t\tdelay: reduceMotion ? 0 : 0.08 + index * 0.06,\n\t\t\t\t}}\n\t\t\t\tviewport={{ once: true, margin: \"-5%\" }}\n\t\t\t\twhileInView={\n\t\t\t\t\treduceMotion ? undefined : { opacity: 1, y: 0, filter: \"blur(0px)\" }\n\t\t\t\t}\n\t\t\t>\n\t\t\t\t{word}\n\t\t\t</motion.span>\n\t\t</span>\n\t);\n}\n\nexport function SeraWordReveal({\n\ttext,\n\tclassName,\n}: {\n\ttext: string;\n\tclassName?: string;\n}) {\n\tconst words = text.split(\" \");\n\n\treturn (\n\t\t<span className={cn(\"flex flex-wrap gap-x-[0.28em]\", className)}>\n\t\t\t{words.map((word, index) => (\n\t\t\t\t<SeraRevealWord index={index} key={`${word}-${index}`} word={word} />\n\t\t\t))}\n\t\t</span>\n\t);\n}\n\n/** Subtle ledger cell lift on hover. */\nexport function SeraHoverLift({\n\tchildren,\n\tclassName,\n}: {\n\tchildren: ReactNode;\n\tclassName?: string;\n}) {\n\tconst reduceMotion = useReducedMotion();\n\n\treturn (\n\t\t<motion.div\n\t\t\tclassName={className}\n\t\t\ttransition={spring}\n\t\t\twhileHover={reduceMotion ? undefined : { y: -2 }}\n\t\t>\n\t\t\t{children}\n\t\t</motion.div>\n\t);\n}\n\n/** Monochrome infinite marquee — quotes, wordmarks, rows. */\nexport function SeraMarquee({\n\tchildren,\n\tclassName,\n\tduration = 42,\n}: {\n\tchildren: ReactNode;\n\tclassName?: string;\n\tduration?: number;\n}) {\n\tconst reduceMotion = useReducedMotion();\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"overflow-hidden [mask-image:linear-gradient(to_right,transparent,black_6%,black_94%,transparent)]\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t<motion.div\n\t\t\t\tanimate={reduceMotion ? undefined : { x: [\"0%\", \"-50%\"] }}\n\t\t\t\tclassName=\"flex w-max\"\n\t\t\t\ttransition={{ duration, ease: \"linear\", repeat: Number.POSITIVE_INFINITY }}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t\t{children}\n\t\t\t</motion.div>\n\t\t</div>\n\t);\n}\n\n/** Blur settle reveal for inverse strips and panels. */\nexport function SeraBlurReveal({\n\tchildren,\n\tclassName,\n}: {\n\tchildren: ReactNode;\n\tclassName?: string;\n}) {\n\tconst ref = useRef<HTMLDivElement>(null);\n\tconst inView = useInView(ref, motionViewport);\n\tconst reduceMotion = useReducedMotion();\n\n\treturn (\n\t\t<motion.div\n\t\t\tanimate={\n\t\t\t\tinView && !reduceMotion\n\t\t\t\t\t? { opacity: 1, y: 0, filter: \"blur(0px)\" }\n\t\t\t\t\t: undefined\n\t\t\t}\n\t\t\tclassName={className}\n\t\t\tinitial={reduceMotion ? false : { opacity: 0, y: 8, filter: \"blur(4px)\" }}\n\t\t\tref={ref}\n\t\t\ttransition={spring}\n\t\t>\n\t\t\t{children}\n\t\t</motion.div>\n\t);\n}\n\n/** Hairline rule draws in from the left. */\nexport function SeraMaskRule({ className }: { className?: string }) {\n\tconst ref = useRef<HTMLDivElement>(null);\n\tconst inView = useInView(ref, motionViewport);\n\tconst reduceMotion = useReducedMotion();\n\n\treturn (\n\t\t<motion.div\n\t\t\tanimate={inView ? { scaleX: 1 } : reduceMotion ? { scaleX: 1 } : { scaleX: 0 }}\n\t\t\taria-hidden\n\t\t\tclassName={cn(\"h-px w-full origin-left bg-border\", className)}\n\t\t\tinitial={reduceMotion ? false : { scaleX: 0 }}\n\t\t\tref={ref}\n\t\t\ttransition={spring}\n\t\t/>\n\t);\n}\n\n/** Sharp-corner billing toggle for Sera pricing. */\nexport function SeraBillingToggle({\n\tperiod,\n\tonChange,\n\tlayoutId = \"sera-billing-pill\",\n}: {\n\tperiod: \"monthly\" | \"annual\";\n\tonChange: (period: \"monthly\" | \"annual\") => void;\n\tlayoutId?: string;\n}) {\n\tconst reduceMotion = useReducedMotion();\n\n\treturn (\n\t\t<div\n\t\t\taria-label=\"Billing period\"\n\t\t\tclassName=\"inline-flex border border-border p-0.5\"\n\t\t\trole=\"group\"\n\t\t>\n\t\t\t{([\"monthly\", \"annual\"] as const).map((option) => {\n\t\t\t\tconst isActive = period === option;\n\t\t\t\treturn (\n\t\t\t\t\t<button\n\t\t\t\t\t\taria-pressed={isActive}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"relative px-4 py-2 font-medium text-[11px] uppercase tracking-[0.2em] transition-colors active:scale-[0.96]\",\n\t\t\t\t\t\t\tisActive ? \"text-foreground\" : \"text-muted-foreground\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t\tkey={option}\n\t\t\t\t\t\tonClick={() => onChange(option)}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{isActive ? (\n\t\t\t\t\t\t\treduceMotion ? (\n\t\t\t\t\t\t\t\t<span className=\"absolute inset-0 -z-10 bg-muted\" />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<motion.span\n\t\t\t\t\t\t\t\t\tclassName=\"absolute inset-0 -z-10 bg-muted\"\n\t\t\t\t\t\t\t\t\tlayoutId={layoutId}\n\t\t\t\t\t\t\t\t\ttransition={spring}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{option === \"monthly\" ? \"Monthly\" : \"Annual\"}\n\t\t\t\t\t</button>\n\t\t\t\t);\n\t\t\t})}\n\t\t</div>\n\t);\n}\n\n/** Cross-fade price when billing period changes. */\nexport function SeraPriceReveal({\n\tamount,\n\tclassName,\n}: {\n\tamount: string;\n\tclassName?: string;\n}) {\n\tconst reduceMotion = useReducedMotion();\n\n\treturn (\n\t\t<AnimatePresence initial={false} mode=\"popLayout\">\n\t\t\t<motion.span\n\t\t\t\tanimate={reduceMotion ? undefined : { opacity: 1, y: 0, filter: \"blur(0px)\" }}\n\t\t\t\tclassName={cn(\"font-medium tabular-nums tracking-tight\", className)}\n\t\t\t\texit={\n\t\t\t\t\treduceMotion\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\t\ty: -6,\n\t\t\t\t\t\t\t\tfilter: \"blur(4px)\",\n\t\t\t\t\t\t\t\ttransition: { duration: 0.15, ease: \"easeIn\" },\n\t\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tinitial={reduceMotion ? false : { opacity: 0, y: 8, filter: \"blur(4px)\" }}\n\t\t\t\tkey={amount}\n\t\t\t\ttransition={spring}\n\t\t\t>\n\t\t\t\t{amount}\n\t\t\t</motion.span>\n\t\t</AnimatePresence>\n\t);\n}\n\n/** Menu / close icon cross-fade for Sera header mobile toggle. */\nexport function SeraIconToggle({\n\topen,\n\tchildren,\n}: {\n\topen: boolean;\n\tchildren: [ReactNode, ReactNode];\n}) {\n\tconst reduceMotion = useReducedMotion();\n\tconst transition = reduceMotion ? { duration: 0 } : spring;\n\n\treturn (\n\t\t<span className=\"relative inline-flex size-4 items-center justify-center\">\n\t\t\t<motion.span\n\t\t\t\tanimate={\n\t\t\t\t\topen\n\t\t\t\t\t\t? { opacity: 0, scale: 0.25, filter: \"blur(4px)\" }\n\t\t\t\t\t\t: { opacity: 1, scale: 1, filter: \"blur(0px)\" }\n\t\t\t\t}\n\t\t\t\taria-hidden={open}\n\t\t\t\tclassName=\"absolute inset-0 inline-flex items-center justify-center\"\n\t\t\t\ttransition={transition}\n\t\t\t>\n\t\t\t\t{children[0]}\n\t\t\t</motion.span>\n\t\t\t<motion.span\n\t\t\t\tanimate={\n\t\t\t\t\topen\n\t\t\t\t\t\t? { opacity: 1, scale: 1, filter: \"blur(0px)\" }\n\t\t\t\t\t\t: { opacity: 0, scale: 0.25, filter: \"blur(4px)\" }\n\t\t\t\t}\n\t\t\t\taria-hidden={!open}\n\t\t\t\tclassName=\"absolute inset-0 inline-flex items-center justify-center\"\n\t\t\t\ttransition={transition}\n\t\t\t>\n\t\t\t\t{children[1]}\n\t\t\t</motion.span>\n\t\t</span>\n\t);\n}\n\n/** Stagger children when mobile drawer opens. */\nexport function SeraDrawerStagger({\n\topen,\n\tchildren,\n\tclassName,\n}: {\n\topen: boolean;\n\tchildren: ReactNode;\n\tclassName?: string;\n}) {\n\tconst reduceMotion = useReducedMotion();\n\n\treturn (\n\t\t<motion.div\n\t\t\tanimate={open ? \"visible\" : \"hidden\"}\n\t\t\tclassName={className}\n\t\t\tinitial=\"hidden\"\n\t\t\tvariants={{\n\t\t\t\thidden: {},\n\t\t\t\tvisible: {\n\t\t\t\t\ttransition: {\n\t\t\t\t\t\tstaggerChildren: reduceMotion ? 0 : motionStagger.item,\n\t\t\t\t\t\tdelayChildren: 0.06,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}}\n\t\t>\n\t\t\t{children}\n\t\t</motion.div>\n\t);\n}\n\nexport function SeraDrawerItem({\n\tchildren,\n\tclassName,\n}: {\n\tchildren: ReactNode;\n\tclassName?: string;\n}) {\n\tconst reduceMotion = useReducedMotion();\n\n\treturn (\n\t\t<motion.div\n\t\t\tclassName={className}\n\t\t\tvariants={\n\t\t\t\treduceMotion\n\t\t\t\t\t? { hidden: { opacity: 1, x: 0 }, visible: { opacity: 1, x: 0 } }\n\t\t\t\t\t: {\n\t\t\t\t\t\t\thidden: { opacity: 0, x: -8, filter: \"blur(4px)\" },\n\t\t\t\t\t\t\tvisible: {\n\t\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\t\tx: 0,\n\t\t\t\t\t\t\t\tfilter: \"blur(0px)\",\n\t\t\t\t\t\t\t\ttransition: spring,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\t\t\t}\n\t\t>\n\t\t\t{children}\n\t\t</motion.div>\n\t);\n}\n","type":"registry:lib"},{"path":"registry/blocks/hero/15/hero-section.tsx","content":"\"use client\";\n\nimport Link from \"next/link\";\nimport { ArrowRightIcon } from \"lucide-react\";\nimport { ContentRail, SectionShell } from \"@/components/layout-contract\";\nimport { buttonVariants } from \"@/components/ui/button\";\nimport { SeraStagger, SeraStaggerItem, SeraWordReveal } from \"../../sera/sera-motion\";\n\nconst heroStats = [\n\t{ label: \"Teams on Sera\", value: \"2,400+\" },\n\t{ label: \"Workflows tracked\", value: \"18k\" },\n\t{ label: \"Median setup\", value: \"4 min\" },\n] as const;\n\nexport function HeroSeraSection() {\n\treturn (\n\t\t<SectionShell spacingMode=\"section\">\n\t\t\t<ContentRail maxWidth=\"max-w-5xl\">\n\t\t\t\t<SeraStagger>\n\t\t\t\t\t<SeraStaggerItem>\n\t\t\t\t\t\t<div className=\"flex items-center justify-between gap-4 border-border border-b pb-4\">\n\t\t\t\t\t\t\t<p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n\t\t\t\t\t\t\t\tSera — Monochrome workspace\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<span className=\"font-mono text-[11px] text-muted-foreground\">01</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</SeraStaggerItem>\n\n\t\t\t\t\t<SeraStaggerItem>\n\t\t\t\t\t\t<h1 className=\"mt-10 max-w-3xl font-medium text-4xl tracking-tight md:text-6xl\">\n\t\t\t\t\t\t\t<SeraWordReveal text=\"Every workflow, measured in monochrome.\" />\n\t\t\t\t\t\t</h1>\n\t\t\t\t\t</SeraStaggerItem>\n\n\t\t\t\t\t<SeraStaggerItem>\n\t\t\t\t\t\t<p className=\"mt-5 max-w-xl text-pretty text-muted-foreground text-sm leading-relaxed md:text-base\">\n\t\t\t\t\t\t\tSera folds analytics, hand-offs, and reporting into one flat workspace. No\n\t\t\t\t\t\t\tdashboards to decorate — just the numbers your team ships against.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</SeraStaggerItem>\n\n\t\t\t\t\t<SeraStaggerItem>\n\t\t\t\t\t\t<div className=\"mt-8 flex flex-col gap-3 sm:flex-row\">\n\t\t\t\t\t\t\t<Link className={buttonVariants()} href=\"/signup\">\n\t\t\t\t\t\t\t\tStart free\n\t\t\t\t\t\t\t\t<ArrowRightIcon data-icon=\"inline-end\" />\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t<Link className={buttonVariants({ variant: \"outline\" })} href=\"/demo\">\n\t\t\t\t\t\t\t\tView demo\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</SeraStaggerItem>\n\n\t\t\t\t\t<SeraStaggerItem>\n\t\t\t\t\t\t<dl className=\"mt-14 grid grid-cols-1 divide-y divide-border border border-border sm:grid-cols-3 sm:divide-x sm:divide-y-0\">\n\t\t\t\t\t\t\t{heroStats.map((stat, index) => (\n\t\t\t\t\t\t\t\t<div className=\"min-w-0 p-6\" key={stat.label}>\n\t\t\t\t\t\t\t\t\t<dt className=\"flex items-baseline justify-between gap-3 text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n\t\t\t\t\t\t\t\t\t\t{stat.label}\n\t\t\t\t\t\t\t\t\t\t<span className=\"font-mono normal-case tracking-normal\">\n\t\t\t\t\t\t\t\t\t\t\t{String(index + 1).padStart(2, \"0\")}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</dt>\n\t\t\t\t\t\t\t\t\t<dd className=\"mt-3 font-medium text-2xl tracking-tight tabular-nums md:text-3xl\">\n\t\t\t\t\t\t\t\t\t\t{stat.value}\n\t\t\t\t\t\t\t\t\t</dd>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</dl>\n\t\t\t\t\t</SeraStaggerItem>\n\t\t\t\t</SeraStagger>\n\t\t\t</ContentRail>\n\t\t</SectionShell>\n\t);\n}\n","type":"registry:component"}],"categories":["hero"],"type":"registry:block","layoutMode":"contained","spacingMode":"section","dividerMode":"none"}