{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "bidproposal", "type": "registry:component", "title": "BidProposal", "description": "Componente Concorde BidProposal — un solo archivo, BidProposal.tsx.", "dependencies": [ "react" ], "registryDependencies": [], "files": [ { "path": "components/BidProposal.tsx", "type": "registry:file", "target": "components/BidProposal.tsx", "content": "\"use client\";\n\n/**\n * BidProposal — Generado por Concorde\n * Fuente: Figma VOYAGER · \"PropuestaBid v2 / Default\" (3300:12845)\n *\n * Propuesta de bid (glassmorphic): card 278×78 (radio 20) con relleno white 8% +\n * backdrop-blur(5px), borde gradiente y sombra morada. Monto grande blanco con\n * glow morado y caption lila.\n *\n * Animación de \"nuevo bid\" (prop `flash`), con los colores `flashColors` y 4\n * modos `flashMode` (taxonomía de animación de slots):\n * · \"bulb\" → Luminiscente: se prende y apaga como una bombilla (sin giro).\n * · \"spin\" → Rotatoria: un haz/cometa de luz que gira una vez.\n * · \"explode\" → Explosiva: estallido de chispas/partículas hacia afuera.\n * · \"pulse\" → Anticipación/Expansiva: escala elástica + borde neón intermitente.\n * · \"combo\" → Celebración: estallido (explode) + anticipación (pulse) juntos.\n * · \"shine\" → Como \"combo\" pero SIN chispas (solo latido + luz + neón).\n * En todos, el nuevo monto aparece al apagarse/asentarse la luz.\n */\n\nimport { useEffect, useRef, useState } from \"react\";\nimport type { JSX, CSSProperties } from \"react\";\n\nexport type BidProposalFlashMode = \"bulb\" | \"spin\" | \"explode\" | \"pulse\" | \"combo\" | \"shine\";\n\nexport interface BidProposalProps {\n /** Monto grande (blanco) — default \"US$ 6,559\" */\n amount?: string;\n /** Caption inferior (degradado lila) — default \"ENVIADO POR ZAE389\" */\n label?: string;\n /** Contador: al cambiar, dispara la animación de nuevo bid */\n flash?: number;\n /** Colores del efecto de luz (editable). Default: primary (naranja→lila→blanco). */\n flashColors?: string[];\n /** Tipo de efecto. Default \"bulb\". */\n flashMode?: BidProposalFlashMode;\n className?: string;\n}\n\n// Primary (VYStrokes1): naranja-500 → vault-500 → blanco\nconst DEFAULT_FLASH_COLORS = [\"#F4AC59\", \"#8460E5\", \"#ffffff\"];\nconst SPARKS = 12;\n\nconst STYLE_ID = \"concorde-bidproposal-styles\";\n\nconst BIDPROPOSAL_STYLES = `\n@property --pbid-spin { syntax: \"\"; inherits: false; initial-value: 0deg; }\n\n.pbid {\n position: relative;\n box-sizing: border-box;\n width: 278px;\n max-width: 100%;\n height: 78px;\n border-radius: 20px;\n background: rgba(255,255,255,0.08);\n -webkit-backdrop-filter: blur(5px);\n backdrop-filter: blur(5px);\n box-shadow: rgba(20,0,69,0.3) 0px 8px 24px -2px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 2px;\n padding: 8px 16px;\n font-family: var(--vmc-font-display, \"Plus Jakarta Sans\", -apple-system, sans-serif);\n}\n/* Borde gradiente base (anillo 1.5px, centro recortado con mask) */\n.pbid::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n padding: 1.5px;\n background: linear-gradient(135deg, #ffffff 0%, #F4AC59 22%, #8460E5 74.5%, #ffffff 100%);\n -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);\n -webkit-mask-composite: xor;\n mask-composite: exclude;\n pointer-events: none;\n z-index: 2;\n}\n/* Luz del borde (común): el fondo/animación dependen del modo */\n.pbid::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n padding: 1.5px;\n -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);\n -webkit-mask-composite: xor;\n mask-composite: exclude;\n pointer-events: none;\n opacity: 0;\n z-index: 3;\n filter: drop-shadow(0 0 5px var(--pbid-c3, #ffffff)) drop-shadow(0 0 12px var(--pbid-c2, #8460E5)) drop-shadow(0 0 20px var(--pbid-c1, #F4AC59));\n}\n\n/* Luz interna (común): forma/animación por modo */\n.pbid__lightwrap {\n position: absolute; inset: 0; border-radius: inherit;\n overflow: hidden; pointer-events: none; z-index: 0;\n}\n.pbid__light {\n position: absolute; inset: -15%;\n filter: blur(22px) saturate(1.7) brightness(1.6);\n mix-blend-mode: screen;\n opacity: 0;\n}\n\n/* ===== Modo BULB (mancha horizontal, se prende y apaga sin giro) ===== */\n.pbid--bulb .pbid__light {\n background: linear-gradient(95deg, var(--pbid-c1, #F4AC59) 0%, var(--pbid-c2, #8460E5) 52%, var(--pbid-c3, #ffffff) 100%);\n}\n.pbid--bulb.pbid--flash .pbid__light { animation: pbid-bulb 600ms ease-in-out; }\n.pbid--bulb::after {\n background: linear-gradient(135deg, var(--pbid-c1, #F4AC59) 0%, var(--pbid-c2, #8460E5) 50%, var(--pbid-c3, #ffffff) 100%);\n}\n.pbid--bulb.pbid--flash::after { animation: pbid-onoff 600ms ease-in-out; }\n@keyframes pbid-bulb { 0% { opacity: 0; } 28% { opacity: 1; } 100% { opacity: 0; } }\n@keyframes pbid-onoff { 0% { opacity: 0; } 28% { opacity: 1; } 100% { opacity: 0; } }\n\n/* ===== Modo SPIN (haz/cometa que gira alrededor del borde) ===== */\n.pbid--spin .pbid__light {\n inset: -45%;\n background: conic-gradient(from 0deg, transparent 0deg, var(--pbid-c1, #F4AC59) 60deg, var(--pbid-c2, #8460E5) 150deg, var(--pbid-c3, #ffffff) 230deg, transparent 330deg);\n}\n.pbid--spin.pbid--flash .pbid__light { animation: pbid-spin-light 900ms ease-in-out; }\n.pbid--spin::after {\n background: conic-gradient(from var(--pbid-spin), transparent 0 270deg, var(--pbid-c1, #F4AC59) 315deg, var(--pbid-c2, #8460E5) 340deg, var(--pbid-c3, #ffffff) 356deg, transparent 360deg);\n}\n.pbid--spin.pbid--flash::after { animation: pbid-arc 950ms ease-in-out; }\n@keyframes pbid-spin-light {\n 0% { opacity: 0; transform: rotate(0deg) scale(1.2); }\n 16% { opacity: 1; }\n 70% { opacity: 1; }\n 100% { opacity: 0; transform: rotate(540deg) scale(1.2); }\n}\n@keyframes pbid-arc {\n 0% { opacity: 0; --pbid-spin: 0deg; }\n 12% { opacity: 1; }\n 88% { opacity: 1; }\n 100% { opacity: 0; --pbid-spin: 540deg; }\n}\n\n/* ===== Modo EXPLODE (estallido: flash central + chispas hacia afuera) ===== */\n.pbid--explode .pbid__light {\n background: radial-gradient(circle at 50% 50%, var(--pbid-c3, #ffffff) 0%, var(--pbid-c2, #8460E5) 38%, var(--pbid-c1, #F4AC59) 65%, transparent 80%);\n}\n.pbid--explode.pbid--flash .pbid__light { animation: pbid-burst 500ms ease-out; }\n.pbid--explode::after {\n background: linear-gradient(135deg, var(--pbid-c1, #F4AC59) 0%, var(--pbid-c2, #8460E5) 50%, var(--pbid-c3, #ffffff) 100%);\n}\n.pbid--explode.pbid--flash::after { animation: pbid-onoff 500ms ease-out; }\n@keyframes pbid-burst {\n 0% { opacity: 0; transform: scale(0.3); }\n 16% { opacity: 1; transform: scale(1); }\n 100% { opacity: 0; transform: scale(1.35); }\n}\n.pbid__sparks { position: absolute; inset: 0; pointer-events: none; z-index: 4; }\n.pbid__spark {\n position: absolute; left: 50%; top: 50%; width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px;\n border-radius: 50%;\n background: radial-gradient(circle, var(--pbid-c3, #ffffff) 0%, var(--pbid-c1, #F4AC59) 70%, transparent 100%);\n filter: drop-shadow(0 0 4px var(--pbid-c2, #8460E5));\n opacity: 0;\n}\n.pbid--explode.pbid--flash .pbid__spark { animation: pbid-spark 600ms cubic-bezier(0.2,0.7,0.3,1); }\n@keyframes pbid-spark {\n 0% { opacity: 0; transform: rotate(var(--ang)) translateX(6px) scale(0.4); }\n 22% { opacity: 1; }\n 100% { opacity: 0; transform: rotate(var(--ang)) translateX(66px) scale(1); }\n}\n\n/* ===== Modo PULSE (anticipación/expansiva: escala elástica + neón intermitente) ===== */\n.pbid--pulse.pbid--flash { animation: pbid-pulse 700ms cubic-bezier(0.34,1.56,0.64,1); }\n@keyframes pbid-pulse {\n 0% { transform: scale(1); }\n 40% { transform: scale(1.05); }\n 100% { transform: scale(1); }\n}\n.pbid--pulse .pbid__light {\n background: linear-gradient(95deg, var(--pbid-c1, #F4AC59) 0%, var(--pbid-c2, #8460E5) 52%, var(--pbid-c3, #ffffff) 100%);\n}\n.pbid--pulse.pbid--flash .pbid__light { animation: pbid-pulse-light 700ms ease-in-out; }\n.pbid--pulse::after {\n background: linear-gradient(135deg, var(--pbid-c1, #F4AC59) 0%, var(--pbid-c2, #8460E5) 50%, var(--pbid-c3, #ffffff) 100%);\n}\n.pbid--pulse.pbid--flash::after { animation: pbid-flicker 700ms steps(1, end); }\n@keyframes pbid-pulse-light {\n 0% { opacity: 0; } 25% { opacity: 0.9; } 50% { opacity: 0.2; } 75% { opacity: 0.9; } 100% { opacity: 0; }\n}\n@keyframes pbid-flicker {\n 0% { opacity: 0; } 12% { opacity: 1; } 28% { opacity: 0.15; } 44% { opacity: 1; } 60% { opacity: 0.15; } 78% { opacity: 1; } 100% { opacity: 0; }\n}\n\n/* ===== Modos COMBO (estalla + anticipa) y SHINE (combo sin chispas) ===== */\n.pbid--combo.pbid--flash, .pbid--shine.pbid--flash { animation: pbid-combo 800ms cubic-bezier(0.34,1.56,0.64,1); }\n@keyframes pbid-combo {\n 0% { transform: scale(1); box-shadow: rgba(20,0,69,0.3) 0px 8px 24px -2px; }\n 18% {\n transform: scale(1.06);\n box-shadow:\n rgba(20,0,69,0.3) 0px 8px 24px -2px,\n 0 0 20px var(--pbid-c3, #ffffff),\n 0 0 38px var(--pbid-c2, #8460E5),\n 0 0 58px var(--pbid-c1, #F4AC59);\n }\n 40% { transform: scale(1.005); box-shadow: rgba(20,0,69,0.3) 0px 8px 24px -2px; }\n 62% {\n transform: scale(1.045);\n box-shadow:\n rgba(20,0,69,0.3) 0px 8px 24px -2px,\n 0 0 18px var(--pbid-c3, #ffffff),\n 0 0 34px var(--pbid-c2, #8460E5),\n 0 0 52px var(--pbid-c1, #F4AC59);\n }\n 100% { transform: scale(1); box-shadow: rgba(20,0,69,0.3) 0px 8px 24px -2px; }\n}\n.pbid--combo .pbid__light, .pbid--shine .pbid__light {\n background: radial-gradient(circle at 50% 50%, var(--pbid-c3, #ffffff) 0%, var(--pbid-c2, #8460E5) 38%, var(--pbid-c1, #F4AC59) 65%, transparent 80%);\n}\n/* luz interna intermitente (como Anticipa) en vez de un solo estallido */\n.pbid--combo.pbid--flash .pbid__light, .pbid--shine.pbid--flash .pbid__light { animation: pbid-pulse-light 800ms ease-in-out; }\n.pbid--combo::after, .pbid--shine::after {\n background: linear-gradient(135deg, var(--pbid-c1, #F4AC59) 0%, var(--pbid-c2, #8460E5) 50%, var(--pbid-c3, #ffffff) 100%);\n}\n.pbid--combo.pbid--flash::after, .pbid--shine.pbid--flash::after { animation: pbid-flicker 800ms steps(1, end); }\n/* solo \"combo\" lanza chispas */\n.pbid--combo.pbid--flash .pbid__spark { animation: pbid-spark 650ms cubic-bezier(0.2,0.7,0.3,1); }\n\n/* Glow externo (común salvo pulse/combo): la card \"irradia\" al prenderse */\n.pbid--bulb.pbid--flash, .pbid--spin.pbid--flash, .pbid--explode.pbid--flash {\n animation: pbid-glow 600ms ease-in-out;\n}\n@keyframes pbid-glow {\n 0%, 100% { box-shadow: rgba(20,0,69,0.3) 0px 8px 24px -2px; }\n 28% {\n box-shadow:\n rgba(20,0,69,0.3) 0px 8px 24px -2px,\n 0 0 20px var(--pbid-c3, #ffffff),\n 0 0 38px var(--pbid-c2, #8460E5),\n 0 0 58px var(--pbid-c1, #F4AC59);\n }\n}\n\n/* El nuevo monto/label aparecen cuando la luz se apaga (común) */\n.pbid--flash .pbid__amount, .pbid--flash .pbid__label { animation: pbid-appear 600ms ease-out; }\n@keyframes pbid-appear {\n 0%, 38% { opacity: 0; transform: scale(0.94); }\n 100% { opacity: 1; transform: scale(1); }\n}\n\n.pbid__amount {\n position: relative;\n z-index: 1;\n font-size: 36px;\n font-weight: 800;\n line-height: 1;\n letter-spacing: 0.04em;\n color: #ffffff;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n text-shadow:\n 0 0 7px rgba(174,166,255,0.7),\n 0 0 16px rgba(107,85,222,0.6),\n 0 0 32px rgba(82,52,189,0.5),\n 0 0 53px rgba(49,0,138,0.4);\n}\n.pbid__label {\n position: relative;\n z-index: 1;\n font-size: 13px;\n font-weight: 700;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n line-height: 1;\n white-space: nowrap;\n background: linear-gradient(160deg, #CFBAFF 0%, #ffffff 35%, #AE8EFF 65%, #CFBAFF 100%);\n -webkit-background-clip: text;\n background-clip: text;\n color: transparent;\n -webkit-text-fill-color: transparent;\n filter: drop-shadow(0 0 6px rgba(239,133,46,0.5));\n}\n@media (prefers-reduced-motion: reduce) {\n .pbid--flash, .pbid--flash::after, .pbid--flash .pbid__light, .pbid--flash .pbid__spark,\n .pbid--flash .pbid__amount, .pbid--flash .pbid__label { animation: none; }\n}\n`;\n\nlet _stylesInjected = false;\n\nexport default function BidProposal({\n amount = \"US$ 6,559\",\n label = \"ENVIADO POR ZAE389\",\n flash = 0,\n flashColors = DEFAULT_FLASH_COLORS,\n flashMode = \"bulb\",\n className = \"\",\n}: BidProposalProps): JSX.Element {\n const [flashing, setFlashing] = useState(false);\n const prevFlash = useRef(flash);\n\n const [c1, c2, c3] = flashColors;\n const colorVars = {\n \"--pbid-c1\": c1 ?? DEFAULT_FLASH_COLORS[0],\n \"--pbid-c2\": c2 ?? DEFAULT_FLASH_COLORS[1],\n \"--pbid-c3\": c3 ?? DEFAULT_FLASH_COLORS[2],\n } as CSSProperties;\n\n if (typeof document !== \"undefined\" && !_stylesInjected) {\n if (!document.getElementById(STYLE_ID)) {\n const el = document.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = BIDPROPOSAL_STYLES;\n document.head.appendChild(el);\n }\n _stylesInjected = true;\n }\n\n // Duración de cada modo (debe ser >= la animación CSS más larga de ese modo,\n // o la clase \"pbid--flash\" se quita a medio ciclo y el efecto se corta).\n const FLASH_MS: Record = {\n bulb: 650,\n spin: 1000,\n explode: 650,\n pulse: 750,\n combo: 850,\n shine: 850,\n };\n\n useEffect(\n function onFlash() {\n if (flash === prevFlash.current) return;\n prevFlash.current = flash;\n // reinicia la animación (quita y vuelve a poner la clase)\n setFlashing(false);\n const raf = requestAnimationFrame(() => requestAnimationFrame(() => setFlashing(true)));\n const t = setTimeout(() => setFlashing(false), FLASH_MS[flashMode]);\n return () => {\n cancelAnimationFrame(raf);\n clearTimeout(t);\n };\n },\n [flash, flashMode],\n );\n\n return (\n <>\n