{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "testsala", "type": "registry:block", "title": "testsala", "description": "Bloque Concorde testsala — incluye sus 6 componentes.", "dependencies": [ "react" ], "registryDependencies": [], "files": [ { "path": "bloques/testsala/liveData.ts", "type": "registry:file", "target": "bloques/testsala/liveData.ts", "content": "/**\n * liveData — Modelo de la animación \"Ver live\" del bloque Sala · Mobile.\n *\n * Secuencia de pujas (montos coherentes que suben, quién puja), tiempos de cada\n * mensaje y lista estática (idle). Compartido por SalaMobile (motor) y\n * MobileChatPanel (render).\n */\n\nexport type Phase = \"idle\" | \"welcome\" | \"extended\" | \"streaming\" | \"processing\" | \"result\" | \"activity\" | \"reserve\" | \"improve\" | \"bestbid\";\n\nexport interface LiveMsg {\n kind: \"proposal\" | \"closes\" | \"vmc\";\n side: \"sent\" | \"received\";\n type: \"white\" | \"live\" | \"vault\";\n amount?: number; // proposal / closes\n bidder?: string; // proposal\n text?: string; // vmc\n slow?: boolean; // vmc → 2s\n mine?: boolean; // proposal sent (mi puja)\n}\n\nexport const ME = \"KAHTH4\"; // mis pujas (los \"live\" / sent)\nconst OTHERS = [\"ZAE389\", \"RAZER21\", \"MEND99\", \"LUC88\", \"VKR55\", \"TORO_7\"];\n\n// Luz del BidProposal cuando la puja es MÍA — naranja \"live\" (distinta a la de\n// los demás, que usan la paleta elegida en el visor).\nexport const MY_FLASH_COLORS = [\"#FFB566\", \"#FF6A00\", \"#FFFFFF\"];\nexport const BASE = 5000;\nexport const STEP = 79;\n\nexport function fmtMoney(n: number): string {\n return \"US$ \" + String(n).replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");\n}\n\n/** Formatea un número a 2 dígitos (para el contador \"INICIA EN\"). */\nexport function pad(n: number): string {\n return String(Math.max(0, n)).padStart(2, \"0\");\n}\n\ninterface RawSpec {\n kind: \"proposal\" | \"closes\" | \"vmc\";\n side: \"sent\" | \"received\";\n text?: string;\n slow?: boolean;\n}\n\nfunction build(raw: RawSpec[], startBid: number): LiveMsg[] {\n let bid = startBid;\n let oi = 0;\n return raw.map(function resolve(r): LiveMsg {\n if (r.kind === \"proposal\") {\n bid += STEP;\n const mine = r.side === \"sent\";\n const bidder = mine ? ME : OTHERS[oi++ % OTHERS.length];\n return { kind: \"proposal\", side: r.side, type: mine ? \"live\" : \"white\", amount: bid, bidder, mine };\n }\n if (r.kind === \"closes\") {\n return { kind: \"closes\", side: \"received\", type: \"vault\", amount: bid };\n }\n return { kind: \"vmc\", side: \"received\", type: \"vault\", text: r.text, slow: r.slow };\n });\n}\n\n// Secuencia live: pujas intercaladas con \"Cierra en\"; al final \"Cierra en\" sobre\n// la última puja y el remate \"A la una / dos / tres\".\nconst RAW: RawSpec[] = [\n { kind: \"proposal\", side: \"received\" },\n { kind: \"closes\", side: \"received\" },\n { kind: \"proposal\", side: \"sent\" },\n { kind: \"closes\", side: \"received\" },\n { kind: \"proposal\", side: \"received\" },\n { kind: \"proposal\", side: \"sent\" },\n { kind: \"closes\", side: \"received\" },\n { kind: \"proposal\", side: \"received\" },\n { kind: \"proposal\", side: \"sent\" },\n { kind: \"closes\", side: \"received\" },\n { kind: \"proposal\", side: \"received\" },\n { kind: \"proposal\", side: \"sent\" },\n { kind: \"closes\", side: \"received\" },\n { kind: \"proposal\", side: \"received\" },\n { kind: \"proposal\", side: \"sent\" },\n { kind: \"closes\", side: \"received\" },\n { kind: \"proposal\", side: \"received\" },\n { kind: \"proposal\", side: \"sent\" }, // ← mi puja gana: última puja antes del remate (naranja)\n { kind: \"closes\", side: \"received\" }, // cierra sobre mi puja antes del remate\n { kind: \"vmc\", side: \"received\", text: \"A la una\", slow: true },\n { kind: \"vmc\", side: \"received\", text: \"A las dos\", slow: true },\n { kind: \"vmc\", side: \"received\", text: \"A las tres\", slow: true },\n];\n\nexport const STREAM: LiveMsg[] = build(RAW, BASE);\n\n// Lista estática (idle) — coherente\nconst STATIC_RAW: RawSpec[] = [\n { kind: \"proposal\", side: \"received\" },\n { kind: \"closes\", side: \"received\" },\n { kind: \"vmc\", side: \"received\", text: \"A la una\" },\n { kind: \"proposal\", side: \"sent\" },\n { kind: \"closes\", side: \"received\" },\n { kind: \"vmc\", side: \"received\", text: \"A la una\" },\n { kind: \"proposal\", side: \"received\" },\n { kind: \"proposal\", side: \"sent\" },\n { kind: \"closes\", side: \"received\" },\n { kind: \"vmc\", side: \"received\", text: \"A la una\" },\n { kind: \"proposal\", side: \"received\" },\n { kind: \"closes\", side: \"received\" },\n { kind: \"vmc\", side: \"received\", text: \"A la una\" },\n];\nexport const STATIC: LiveMsg[] = build(STATIC_RAW, BASE);\n\n// ── Tiempos ──\nexport const REG_DELAY = 650; // ritmo moderado\nexport const VMC_DELAY = 2000; // \"a la una/dos/tres\" → 2s c/u\nexport function delayOf(m: LiveMsg): number {\n return m.slow ? VMC_DELAY : REG_DELAY;\n}\n\nlet cum = 0;\nexport const REVEAL_AT: number[] = STREAM.map(function reveal(m) {\n cum += delayOf(m);\n return cum;\n});\nexport const TOTAL_STREAM = cum;\nconst firstVmc = STREAM.findIndex((m) => m.slow);\nexport const VMC_START = REVEAL_AT[firstVmc]; // cuando aparece \"a la una\"\nexport const VMC_FILL = TOTAL_STREAM - VMC_START; // dura el remate (3·2s)\n\nexport const WELCOME_MS = 3000;\nexport const EXTENDED_MS = 3000;\nexport const RESTART_PAUSE = 1400;\nexport const START_COUNT = 3; // cada fase (recibiendo / extendido) dura 3s\nexport const PARTICIPANTS_TARGET = 18;\n\n// ── Final: \"Procesando\" → \"Tabla de posiciones\" → \"Actividad\" (gano yo) ──\nexport const PROCESSING_MS = 2200; // \"Un momento por favor\" antes de la tabla\nexport const RESULT_MS = 6500; // tabla de posiciones: reveal escalonado (2° → 1° con brillo) + lectura, luego \"Actividad\"\n\n// ── Con precio reserva: sub-flujo \"Mejor postor\" → (Ingresa monto → Confirmar) → 48 horas ──\nexport const RESERVE_MS = 5000; // reservado (transiciones internas)\nexport const IMPROVE_PLACEHOLDER = 19159; // monto sugerido (placeholder del input)\nexport const BESTBID_SECONDS = 23; // countdown de \"¿Deseas mejorarlo?\" — al llegar a 0 → 48 horas\n\n// Monto ganador = último proposal del stream (es MÍO). Las siguientes posiciones\n// bajan un paso por fila.\nconst WINNING_BID = (function last(): number {\n const props = STREAM.filter((m) => m.kind === \"proposal\");\n return props.length ? (props[props.length - 1].amount ?? BASE) : BASE;\n})();\n\nexport interface Standing {\n rank: number;\n user: string;\n amount: number;\n mine: boolean;\n note: string; // texto de la 3ª columna (ej. \"En consulta\")\n}\n\n// Podio: 1° YO (ganador, borde live + estrella llena + \"En consulta\");\n// 2° otro postor (borde normal, rango vault, estrella media, tiempo).\nexport const STANDINGS: Standing[] = [\n { rank: 1, user: ME, amount: WINNING_BID, mine: true, note: \"En consulta\" },\n { rank: 2, user: \"ZAE389\", amount: WINNING_BID - STEP, mine: false, note: \"02:27:12\" },\n];\n" }, { "path": "bloques/testsala/mobile/dimensions.ts", "type": "registry:file", "target": "bloques/testsala/mobile/dimensions.ts", "content": "/**\n * Dimensiones del bloque SalaMobile — módulo plano sin \"use client\".\n */\n\nexport const SALAMOBILE_WIDTH = 420;\nexport const SALAMOBILE_HEIGHT = 844;\nexport const SALAMOBILE_BG = \"linear-gradient(116deg, #5F3ED8 0%, #340091 50%, #140046 100%)\";\n" }, { "path": "bloques/testsala/mobile/MobileChatPanel.tsx", "type": "registry:file", "target": "bloques/testsala/mobile/MobileChatPanel.tsx", "content": "/**\n * MobileChatPanel — Sección del bloque Sala · Mobile (Voyager DS)\n * Fuente: Figma VOYAGER · Frame 1000023075 (panel glass del chat mobile)\n *\n * Presentacional: el motor de la animación vive en SalaMobile y le pasa el\n * estado por props. Renderiza:\n * · idle → lista estática de BidMessage + BidProposal + ProgressBar.\n * · welcome → \"Recibiendo participantes\" + dots + caja \"Inicia en\" (reloj).\n * · extended → + \"Inicio extendido\" + caja \"Inicia en\" (símbolo +).\n * · streaming → BidMessage entrando con fade+slide horizontal (received←izq,\n * sent←der) y subiendo; BidProposal con el bid actual.\n */\n\n\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { ChangeEvent, CSSProperties, JSX } from \"react\";\nimport ProgressBar, { type ProgressBarVariant } from \"../../../components/ProgressBar\";\nimport BidProposal from \"../../../components/BidProposal\";\nimport BidMessage from \"../../../components/BidMessage\";\nimport { STREAM, STATIC, STANDINGS, fmtMoney, IMPROVE_PLACEHOLDER, BESTBID_SECONDS, ME, MY_FLASH_COLORS, type LiveMsg, type Phase } from \"../liveData\";\nimport { useSpring } from \"../../../hooks/useSpring\";\nimport { useAuroraSpring } from \"../../../hooks/useAuroraSpring\";\n\nexport const MOBILECHATPANEL_WIDTH = 420;\n\nconst VMC_LOGO = \"VMC;\n\nfunction Bubble({ m }: { m: LiveMsg }): JSX.Element {\n if (m.kind === \"vmc\") {\n return (\n \n {m.text}\n \n );\n }\n if (m.kind === \"proposal\") {\n return (\n \n {m.bidder} ha propuesto {fmtMoney(m.amount ?? 0)}\n \n );\n }\n return (\n \n Cierra en {fmtMoney(m.amount ?? 0)}\n \n );\n}\n\n// Gradiente lila común de los íconos INICIA EN (paths exactos de los SVG)\nconst INICIA_GRAD = (\n \n \n \n \n \n \n);\n// Timer (SVG 46×51) — reloj con gradiente lila, render 40×40\nfunction ClockIcon(): JSX.Element {\n return (\n \n \n {INICIA_GRAD}\n \n );\n}\n// Plus (SVG 56×56) — \"+\" con gradiente lila, render 40×40\nfunction PlusIcon(): JSX.Element {\n return (\n \n \n {INICIA_GRAD}\n \n );\n}\n// Estrella del podio — paths + gradientes exactos de los SVG.\n// · full=true → relleno naranja→morado (star.svg, ganador)\n// · full=false → relleno medio: naranja abajo, transparente arriba (Posición 2)\nfunction StarIcon({ full = true }: { full?: boolean }): JSX.Element {\n const d =\n \"M3.55172 11.6155L4.78672 7.80647L1.53672 5.44047H5.55372L6.78872 1.61847L8.03672 5.44047H12.0537L8.80372 7.80647L10.0517 11.6155L6.78872 9.26247L3.55172 11.6155Z\";\n const fillId = full ? \"smstar-full\" : \"smstar-half\";\n return (\n \n \n \n \n \n \n \n \n {/* Vertical: naranja abajo → transparente arriba (paint4 de \"Posición 2\") */}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n );\n}\n\n// Bolsa de dinero ($) — paths exactos del SVG \"Frame 1000023386\" (glow por CSS)\nfunction MoneyBagIcon(): JSX.Element {\n return (\n \n \n \n \n \n \n \n );\n}\n\n// Mano con billete ($) — paths exactos del SVG \"Frame 1000023143\" (glow por CSS)\nfunction HandMoneyIcon(): JSX.Element {\n return (\n \n {/* dedos (filter0) */}\n \n \n \n \n {/* billete (filter1) */}\n \n {/* símbolo $ */}\n \n \n {/* mano/palma (filter2) */}\n \n \n );\n}\n\n// Reloj — paths exactos del SVG \"reloj.svg\" (círculo + manecillas). Glow por CSS.\nfunction ClockBigIcon(): JSX.Element {\n return (\n \n \n \n \n );\n}\n\n// Sobre / email — paths exactos del SVG \"Frame\" (glow por CSS)\nfunction EmailIcon(): JSX.Element {\n return (\n \n \n \n \n \n \n );\n}\n\nconst STYLE_ID = \"concorde-testsmchatpanel-styles\";\n\nconst SMCHATPANEL_STYLES = `\n/* ── Luz consistente (principio PBR): una sola fuente arriba-izquierda para todo\n el panel. --sm-glow-* son la intensidad \"reposo\"; los eventos (ganador, remate,\n flash) multiplican brillo/blur sin reorientar la luz — ver skill light-consistent. */\n.smchatpanel {\n --sm-light-angle: 135deg;\n --sm-glow-soft: 0 0 10px rgba(174,142,255,0.55), 0 0 24px rgba(82,52,189,0.4);\n --sm-glow-strong: 0 0 18px rgba(174,142,255,0.95), 0 0 38px rgba(82,52,189,0.7);\n --sm-glow-win: 0 0 20px rgba(255,180,102,0.9), 0 0 42px rgba(237,137,54,0.6);\n position: relative;\n width: ${MOBILECHATPANEL_WIDTH}px;\n border-radius: 0;\n background: rgba(255,255,255,0.03);\n backdrop-filter: blur(14px);\n -webkit-backdrop-filter: blur(14px);\n box-shadow: rgba(20,0,69,0.3) 0px 8px 24px -2px;\n overflow: hidden;\n font-family: var(--vmc-font-display, \"Plus Jakarta Sans\", -apple-system, sans-serif);\n}\n.smchatpanel::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n /* sin borde arriba: el panel se conecta con el header sin línea que corte */\n padding: 0 1px 1px 1px;\n background: linear-gradient(120deg, #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}\n/* ── Fondo \"flagship PBR\": aurora de blobs difusos flotando detrás del chat\n (mismo lenguaje que el fondo óptico de la referencia — glow orgánico, lento,\n paleta de marca). Elemento propio (.smchat__aurora, NO pseudo-elemento del\n panel): un ::after en .smchatpanel queda debajo del propio backdrop-filter\n del panel y el blur lo apaga casi por completo — con nodo propio se ve.\n El movimiento lo maneja useAuroraSpring (mass-spring-damper) escribiendo\n --mx/--my (grupo A: naranjas) y --mx2/--my2 (grupo B: lilas) directo al style. */\n.smchat__aurora {\n position: absolute;\n inset: -25%;\n z-index: 0;\n pointer-events: none;\n --mx: 0vmax; --my: 0vmax; --mx2: 0vmax; --my2: 0vmax;\n}\n.smchat__aurora-a, .smchat__aurora-b {\n position: absolute;\n inset: 0;\n filter: blur(52px) saturate(1.25);\n opacity: 0.85;\n will-change: transform;\n}\n/* Grupo A — blobs naranjas (esquinas opuestas), siguen --mx/--my */\n.smchat__aurora-a {\n background:\n radial-gradient(36% 28% at 16% 12%, rgba(255,158,66,0.55) 0%, transparent 68%),\n radial-gradient(32% 26% at 80% 85%, rgba(255,140,60,0.45) 0%, transparent 68%);\n transform: translate(var(--mx), var(--my));\n}\n/* Grupo B — blobs lilas (esquinas opuestas), siguen --mx2/--my2 (deriva distinta) */\n.smchat__aurora-b {\n background:\n radial-gradient(40% 32% at 88% 18%, rgba(150,110,255,0.6) 0%, transparent 70%),\n radial-gradient(44% 36% at 26% 90%, rgba(190,160,255,0.5) 0%, transparent 68%);\n transform: translate(var(--mx2), var(--my2));\n}\n/* ── \"Seda óptica\": grandes pliegues curvos con highlight especular (Fresnel) y\n sombra profunda, muy difuminados — el look sedoso/vidrio de la referencia, no\n ondas de línea duras. Gradientes cónicos = simulan una superficie 3D curva\n iluminada desde arriba-izquierda; blur fuerte los vuelve volumétricos. Cada\n capa deriva y respira lento (spring vía --sx/--sy + keyframe de rotación). */\n.smchat__silk {\n position: absolute;\n inset: -35%;\n z-index: 0;\n pointer-events: none;\n mix-blend-mode: screen;\n --sx: 0vmax; --sy: 0vmax;\n}\n.smchat__silk-fold {\n position: absolute;\n inset: 0;\n filter: blur(46px) saturate(1.35) contrast(1.05);\n will-change: transform;\n}\n/* Pliegue 1 — lila/vidrio: highlight arriba-izq, cae a sombra abajo-der */\n.smchat__silk-fold--1 {\n background:\n conic-gradient(from 210deg at 32% 30%,\n rgba(190,160,255,0) 0deg,\n rgba(150,110,255,0.30) 70deg,\n rgba(207,186,255,0.60) 140deg,\n rgba(120,80,220,0.15) 210deg,\n rgba(60,30,120,0) 300deg,\n rgba(190,160,255,0) 360deg);\n animation: smchat-silk-1 34s ease-in-out infinite alternate;\n}\n/* Pliegue 2 — naranja cálido: segundo foco de luz, cruza al primero */\n.smchat__silk-fold--2 {\n background:\n conic-gradient(from 40deg at 74% 68%,\n rgba(255,158,66,0) 0deg,\n rgba(255,140,60,0.22) 60deg,\n rgba(255,190,120,0.45) 130deg,\n rgba(200,90,30,0.12) 200deg,\n rgba(120,40,10,0) 290deg,\n rgba(255,158,66,0) 360deg);\n animation: smchat-silk-2 44s ease-in-out infinite alternate;\n}\n@keyframes smchat-silk-1 {\n 0% { transform: translate(var(--sx), var(--sy)) rotate(-8deg) scale(1.05); }\n 100% { transform: translate(var(--sx), var(--sy)) rotate(10deg) scale(1.18); }\n}\n@keyframes smchat-silk-2 {\n 0% { transform: translate(calc(var(--sx) * -0.6), calc(var(--sy) * -0.6)) rotate(6deg) scale(1.12); }\n 100% { transform: translate(calc(var(--sx) * -0.6), calc(var(--sy) * -0.6)) rotate(-9deg) scale(1.0); }\n}\n.smchat__list, .smchat__stream { z-index: 1; }\n@media (prefers-reduced-motion: reduce) {\n .smchat__silk-fold--1, .smchat__silk-fold--2 { animation: none; }\n}\n.smchat__list, .smchat__stream { scrollbar-width: none; -ms-overflow-style: none; }\n.smchat__list::-webkit-scrollbar, .smchat__stream::-webkit-scrollbar { width: 0; height: 0; display: none; }\n.smchat__list .pbidmsg, .smchat__stream .pbidmsg { font-size: 13px; }\n.smchat__list .pbidmsg--sent { align-self: flex-end; }\n/* ProgressBar del panel: sin esquinas redondeadas (override del componente) */\n.smchatpanel .pprogbar { border-radius: 0; }\n\n.smchat__stream {\n position: absolute; top: 0; left: 0; right: 0; bottom: 108px;\n overflow: hidden; padding: 16px 18px;\n display: flex; flex-direction: column; justify-content: flex-end; gap: 8px;\n}\n.smchat__msg { align-self: flex-start; }\n.smchat__msg--received { animation: smchat-in-left 150ms cubic-bezier(0.3,0,0,1) both; }\n.smchat__msg--sent { align-self: flex-end; animation: smchat-in-right 150ms cubic-bezier(0.3,0,0,1) both; }\n@keyframes smchat-in-left { from { opacity: 0; transform: translateX(-50px); } to { opacity: 1; transform: translateX(0); } }\n@keyframes smchat-in-right { from { opacity: 0; transform: translateX(50px); } to { opacity: 1; transform: translateX(0); } }\n\n.smchat__status {\n position: absolute; top: 0; left: 0; right: 0; bottom: 108px; z-index: 4;\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n gap: 22px; text-align: center; padding: 24px 28px;\n}\n/* Variante Actividad: el contenido arranca arriba (no centrado vertical) */\n.smchat__status--top { justify-content: flex-start; padding-top: 40px; }\n.smchat__rp {\n font-size: 30px; font-weight: 800; line-height: 1.12; letter-spacing: -0.01em;\n background: linear-gradient(135deg, #CFBAFF 0%, #ffffff 35%, #AE8EFF 65%, #CFBAFF 100%);\n -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent;\n filter: drop-shadow(0 0 10px rgba(132,96,229,0.55)) drop-shadow(0 0 24px rgba(82,52,189,0.4));\n}\n/* Variantes de tamaño (mismo gradiente + glow) */\n.smchat__rp--16 { font-size: 16px; }\n.smchat__rp--20 { font-size: 20px; }\n/* Bloque \"Procesando\" — dos líneas con tamaños distintos, poco gap */\n.smchat__proc-lines { display: flex; flex-direction: column; align-items: center; gap: 4px; }\n.smchat__rdots { display: flex; align-items: center; gap: 12px; }\n.smchat__rdots span {\n border-radius: 50%; background: #fff; box-shadow: 0 0 8px rgba(174,142,255,0.85);\n animation: smchat-dot 1.2s ease-in-out infinite both;\n}\n.smchat__rdots span:nth-child(1) { width: 10px; height: 10px; animation-delay: 0s; }\n.smchat__rdots span:nth-child(2) { width: 12px; height: 12px; animation-delay: 0.18s; }\n.smchat__rdots span:nth-child(3) { width: 8px; height: 8px; animation-delay: 0.36s; }\n@keyframes smchat-dot {\n 0%, 80%, 100% { opacity: 0.35; transform: scale(0.65); }\n 40% { opacity: 1; transform: scale(1.15); box-shadow: 0 0 14px rgba(174,142,255,1); }\n}\n.smchat__ext { font-size: 18px; font-weight: 700; color: #ffffff; }\n.smchat__box {\n position: relative; box-sizing: border-box; width: 278px; height: 78px; max-width: 86%; border-radius: 20px;\n background: rgba(255,255,255,0.08); -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px);\n display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; padding: 10px 24px;\n}\n.smchat__box::before {\n content: \"\"; position: absolute; inset: 0; border-radius: inherit; padding: 1.5px;\n background: linear-gradient(120deg, #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; mask-composite: exclude; pointer-events: none;\n}\n.smchat__box-label {\n font-size: 15px; font-weight: 700;\n background: linear-gradient(135deg, #CFBAFF 0%, #ffffff 35%, #AE8EFF 65%, #CFBAFF 100%);\n -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent;\n}\n.smchat__box-row { display: flex; align-items: center; gap: 12px; }\n.smchat__box-row svg {\n filter:\n drop-shadow(0 0 6px rgba(207,186,255,0.95))\n drop-shadow(0 0 14px rgba(174,167,255,0.85))\n drop-shadow(0 0 26px rgba(132,96,229,0.6));\n}\n.smchat__box-time {\n font-size: 30px; font-weight: 800; color: #ffffff; letter-spacing: 0.08em;\n font-variant-numeric: tabular-nums; text-shadow: 0 0 10px rgba(174,166,255,0.5);\n}\n\n/* ── Tabla de posiciones: filas del podio ── */\n.smchat__rows { display: flex; flex-direction: column; align-items: center; gap: 12px; width: 100%; }\n/* ── Fila de posición — réplica de los SVG \"Banner\"/\"Posición 2\" ── */\n.smchat__row {\n position: relative; box-sizing: border-box; width: 384px; max-width: 96%; height: 64px;\n display: grid; grid-template-columns: 58px minmax(0, 1fr) 96px; align-items: center;\n border-radius: 16px;\n /* Glass burbuja: fondo blanco + overlay vertical (paint3) para el brillo superior */\n background:\n linear-gradient(180deg, rgba(255,255,255,0.14) 0%, rgba(255,255,255,0.02) 55%, rgba(255,255,255,0) 100%),\n rgba(255,255,255,0.08);\n -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);\n box-shadow: rgba(20,0,69,0.3) 0px 6px 16px -2px, inset 0 1px 0 rgba(255,255,255,0.18);\n}\n/* Solo el GANADOR (1°) tiene borde: LIVE (naranja→morado) 2px.\n El resto (2°…) NO lleva borde — su burbuja es solo el glass. */\n.smchat__row--mine::after {\n content: \"\"; position: absolute; inset: 0; border-radius: inherit; padding: 2px;\n background: linear-gradient(120deg, #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; mask-composite: exclude; pointer-events: none;\n}\n/* Col A — rango grande. Base: vault gradient (lila). */\n.smchat__rank {\n display: flex; align-items: baseline; justify-content: center; gap: 1px;\n font-weight: 800;\n background: linear-gradient(120deg, #CFBAFF 0%, #ffffff 35%, #AE8EFF 65%, #CFBAFF 100%);\n -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent;\n filter: drop-shadow(0 0 8px rgba(174,142,255,0.5));\n}\n/* Ganador: tinte con naranja */\n.smchat__row--mine .smchat__rank {\n background: linear-gradient(135deg, #F6C98A 0%, #CFBAFF 55%, #AE8EFF 100%);\n -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent;\n}\n.smchat__rank b { font-size: 26px; line-height: 1; }\n.smchat__rank i { font-size: 13px; font-style: normal; }\n/* Divisor corto (34px, fade arriba/abajo) entre columnas — paint1/paint2 del SVG */\n.smchat__cell, .smchat__note {\n position: relative;\n}\n.smchat__cell::before, .smchat__note::before {\n content: \"\"; position: absolute; left: 0; top: 50%; transform: translateY(-50%);\n width: 1.2px; height: 34px; border-radius: 0.6px;\n background: linear-gradient(180deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.3) 50%, rgba(255,255,255,0) 100%);\n}\n/* Col B — monto (20px) + user·estrella (11px / 400) · letra pequeña para que\n quepan bien los 2 puestos también en el panel angosto de desktop */\n.smchat__cell {\n min-width: 0; align-self: stretch; padding: 0 8px 0 12px;\n display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 1px;\n}\n.smchat__amount {\n max-width: 100%; text-align: left;\n font-size: 20px; font-weight: 800; color: #ffffff; letter-spacing: 0.01em;\n font-variant-numeric: tabular-nums; line-height: 1.2;\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n.smchat__user {\n display: flex; align-items: center; gap: 5px; line-height: 1.2;\n font-size: 11px; font-weight: 400; color: rgba(255,255,255,0.75); letter-spacing: 0.02em;\n}\n.smchat__user svg { flex-shrink: 0; }\n/* Col C — nota (\"En consulta\") 11px / 400 */\n.smchat__note {\n align-self: stretch; padding: 0 10px;\n display: flex; align-items: center; justify-content: center;\n font-size: 11px; font-weight: 400; color: rgba(255,255,255,0.9); text-align: center;\n}\n/* Panel angosto (desktop): el monto se corta → letra más chica y columnas ajustadas */\n.smchatpanel--narrow .smchat__row { grid-template-columns: 48px minmax(0, 1fr) 84px; }\n.smchatpanel--narrow .smchat__rank b { font-size: 22px; }\n.smchatpanel--narrow .smchat__rank i { font-size: 11px; }\n.smchatpanel--narrow .smchat__cell { padding: 0 6px 0 10px; }\n.smchatpanel--narrow .smchat__amount { font-size: 16px; }\n.smchatpanel--narrow .smchat__user { font-size: 10px; }\n.smchatpanel--narrow .smchat__note { font-size: 10px; padding: 0 8px; }\n/* Ícono bolsa + mensaje de próximas ofertas — arriba de la barra (tabla de posiciones) */\n.smchat__next {\n position: absolute; left: 50%; bottom: 102px; z-index: 5;\n transform: translateX(-50%);\n box-sizing: border-box; width: 336px; max-width: 86%; padding-bottom: 12px;\n display: flex; flex-direction: column; align-items: center; gap: 10px;\n text-align: center; font-size: 12px; font-weight: 400; line-height: 1.35;\n color: rgba(255,255,255,0.9);\n}\n.smchat__next-icon {\n display: flex;\n filter:\n drop-shadow(0 0 4px rgba(174,167,255,0.7))\n drop-shadow(0 0 8px rgba(107,85,223,0.6))\n drop-shadow(0 0 16px rgba(49,0,186,0.4));\n}\n/* Actividad: ícono mano$ + felicitaciones + título + \"con una propuesta de\" + BidProposal */\n.smchat__act { display: flex; flex-direction: column; align-items: center; }\n.smchat__act-icon {\n display: flex; margin-bottom: 8px;\n filter:\n drop-shadow(0 0 4px rgba(174,167,255,0.7))\n drop-shadow(0 0 8px rgba(107,85,223,0.6))\n drop-shadow(0 0 16px rgba(49,0,186,0.4));\n}\n/* \"Felicitaciones {user} has\" — VYButton16 16/600 blanco; solo el user en gradiente */\n.smchat__act-congrats { font-size: 16px; font-weight: 600; line-height: 1.4; color: #ffffff; text-align: center; }\n.smchat__act-user {\n background: linear-gradient(120deg, #CFBAFF 0%, #ffffff 35%, #AE8EFF 65%, #CFBAFF 100%);\n -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent;\n}\n/* \"Ganado la Oferta\" — VYheading-xl: 20/28, ExtraBold, gradiente naranja→morado */\n.smchat__act-title {\n font-size: 20px; font-weight: 800; line-height: 28px; text-align: center; margin-top: 6px;\n background: linear-gradient(120deg, #ED8936 0%, #ED8936 40%, #8460E5 100%);\n -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent;\n filter: drop-shadow(0 0 53px rgba(49,0,138,0.4)) drop-shadow(0 0 32px rgba(82,52,188,0.5));\n}\n/* Mismo tipo de letra que \"Procesando\" pero blanco sólido (sin gradiente).\n Misma distancia (margin-top) que hay entre felicitaciones y \"Ganado la Oferta\". */\n.smchat__act-label { font-size: 16px; font-weight: 600; color: #ffffff; margin-top: 6px; margin-bottom: 48px; }\n/* Actividad: el caption del BidProposal (\"Comisión: US$ 0\") sin mayúsculas */\n.pbid--activity .pbid__label { text-transform: none; letter-spacing: 0.02em; }\n\n/* ── Pantalla \"Revisa tu historial / 48 horas\" (sin precio reserva) ── */\n.smchat__reserve {\n display: flex; flex-direction: column; align-items: center; gap: 22px; width: 100%;\n}\n/* Reloj — glow lila/morado como el resto de íconos */\n.smchat__reserve-icon {\n display: flex;\n filter:\n drop-shadow(0 0 4px rgba(174,167,255,0.7))\n drop-shadow(0 0 8px rgba(107,85,223,0.6))\n drop-shadow(0 0 16px rgba(49,0,186,0.4));\n}\n.smchat__reserve-copy {\n display: flex; flex-direction: column; align-items: center; gap: 4px; text-align: center;\n}\n/* Líneas superior/inferior — blanco 16/600 */\n.smchat__reserve-line { font-size: 16px; font-weight: 600; line-height: 1.35; color: #ffffff; }\n/* Desktop (panel angosto): reduce para que \"Revisa tu historial de participación\"\n quepa en una línea → el bloque superior queda en 2 filas exactas. */\n.smchatpanel--narrow .smchat__reserve-line { font-size: 14px; white-space: nowrap; }\n/* \"48 horas\" — gradiente lila VYStrokes3 (#CFBAFF→#fff→#AE8EFF→#CFBAFF) +\n drop shadow lila (#AEA7FF 70%, blur 7.11). ExtraBold grande. */\n.smchat__reserve-hours {\n font-size: 34px; font-weight: 800; line-height: 1.1; letter-spacing: -0.01em;\n background: linear-gradient(135deg, #CFBAFF 0%, #ffffff 35%, #AE8EFF 65%, #CFBAFF 100%);\n -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent;\n filter: drop-shadow(0 0 7.11px rgba(174,167,255,0.7));\n}\n/* Texto \"Recuerda que…\" — arriba de la barra, igual que .smchat__next */\n.smchat__reserve-note {\n position: absolute; left: 50%; bottom: 102px; z-index: 5;\n transform: translateX(-50%);\n box-sizing: border-box; width: 336px; max-width: 86%; padding-bottom: 12px;\n text-align: center; font-size: 12px; font-weight: 400; line-height: 1.35;\n color: rgba(255,255,255,0.9);\n}\n/* Desktop (panel angosto): reduce fuente y ensancha para que las 3 filas del\n
(\"…necesarias para que tu propuesta sea aceptada por el…\") no se re-partan. */\n.smchatpanel--narrow .smchat__reserve-note { font-size: 10px; width: 300px; max-width: 100%; }\n/* ── Pantalla \"Ingresa un monto superior\" (improve) ── */\n.smchat__improve {\n display: flex; flex-direction: column; align-items: center; gap: 20px; width: 100%;\n padding: 0 20px; box-sizing: border-box;\n}\n/* Título — VYheading-xl (20/28, ExtraBold) gradiente lila VYStrokes3 + glow morado */\n.smchat__improve-title {\n font-size: 20px; font-weight: 800; line-height: 28px; text-align: center;\n background: linear-gradient(135deg, #CFBAFF 0%, #ffffff 35%, #AE8EFF 65%, #CFBAFF 100%);\n -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent;\n filter: drop-shadow(0 0 53.31px rgba(49,0,138,0.4)) drop-shadow(0 0 31.98px rgba(82,52,188,0.5));\n}\n/* Input glass 278×72 — glass white 8% + borde gradiente VYStrokes1 (white→#F4AC59→\n #8460E5→white) radio 20, background blur, drop-shadow #140045 30%. */\n.smchat__improve-input {\n position: relative; box-sizing: border-box;\n width: 278px; max-width: 100%; height: 72px; border-radius: 20px;\n background: rgba(255,255,255,0.08);\n -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);\n box-shadow: rgba(20,0,69,0.3) 0 8px 24px -2px;\n display: flex; align-items: center; justify-content: flex-start; gap: 32px;\n padding: 0 22px 0 30px;\n}\n.smchat__improve-input::before {\n content: \"\"; position: absolute; inset: 0; border-radius: inherit; 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; mask-composite: exclude; pointer-events: none;\n}\n/* Prefijo \"US$\" — pegado a la izquierda, blanco sólido, ExtraBold */\n.smchat__improve-cur {\n font-size: 36px; font-weight: 800; color: #ffffff; letter-spacing: 0.01em;\n flex-shrink: 0;\n}\n/* Campo editable — ocupa el resto a la derecha del \"US$\" (como en Figma).\n Transparente, ExtraBold. */\n.smchat__improve-field {\n min-width: 0; flex: 1; border: none; outline: none; background: transparent;\n font-family: inherit; font-size: 36px; font-weight: 800; letter-spacing: 0.01em;\n color: #ffffff; font-variant-numeric: tabular-nums; text-align: left;\n}\n.smchat__improve-field::placeholder { color: rgba(255,255,255,0.4); font-weight: 800; }\n/* Panel angosto (desktop 313px): el contenedor pierde padding lateral y el input\n ocupa TODO el ancho disponible; el contenido se reduce para no cortarse. */\n.smchatpanel--narrow .smchat__improve,\n.smchatpanel--narrow .smchat__best { padding: 0 8px; }\n.smchatpanel--narrow .smchat__improve-input { width: 100%; max-width: 100%; gap: 16px; padding: 0 16px 0 18px; }\n.smchatpanel--narrow .smchat__improve-cur,\n.smchatpanel--narrow .smchat__improve-field { font-size: 28px; }\n/* Botones — el primary reusa .pvbtn (width 200). \"Volver\" texto subrayado que\n pasa a primary. */\n.smchat__improve-actions { display: flex; flex-direction: column; align-items: center; gap: 12px; }\n.smchat__improve-actions .pvbtn { width: 200px; padding: 0; }\n/* \"Volver\" — texto subrayado (se vuelve primary con .pvbtn width auto) */\n.smchat__improve-back {\n width: 50px; height: 28px; padding: 0; border: none; background: transparent;\n cursor: pointer; font-family: inherit; font-size: 16px; font-weight: 600;\n color: #ffffff; text-decoration: underline; text-underline-offset: 3px;\n}\n/* Texto explicativo — arriba de la barra, igual que .smchat__reserve-note.\n En MOBILE (ancho) los son renglones fijos (nowrap) = saltos del Figma.\n En DESKTOP (angosto) el texto FLUYE (los se vuelven inline) y parte solo. */\n.smchat__improve-note {\n position: absolute; left: 50%; bottom: 102px; z-index: 5;\n transform: translateX(-50%);\n box-sizing: border-box; width: 348px; max-width: 96%; padding-bottom: 12px;\n text-align: center; font-size: 11.5px; font-weight: 400; line-height: 1.5;\n color: rgba(255,255,255,0.9);\n}\n/* Mobile: cada renglón entero (los saltos del Figma). */\n.smchat__improve-note span { display: block; white-space: nowrap; }\n/* Desktop (panel angosto ~276px): el texto fluye libre en varias líneas. Los\n pasan a inline (con espacio) y el contenedor angosto define el wrap. */\n.smchatpanel--narrow .smchat__improve-note { font-size: 10px; width: 276px; max-width: 100%; }\n.smchatpanel--narrow .smchat__improve-note span { display: inline; white-space: normal; }\n.smchatpanel--narrow .smchat__improve-note span::after { content: \" \"; }\n\n/* ── Pantalla \"Mejor postor / ¿Deseas mejorarlo?\" (bestbid) ── */\n.smchat__best {\n display: flex; flex-direction: column; align-items: center; gap: 14px; width: 100%;\n padding: 0 20px; box-sizing: border-box;\n}\n/* Texto superior (3 líneas) — blanco 18/700; el user en línea 1 */\n.smchat__best-top {\n font-size: 18px; font-weight: 700; line-height: 1.35; text-align: center; color: #ffffff;\n}\n/* SOLO \"Mejor postor de la Oferta\" con degradado lila VYStrokes3 + glow */\n.smchat__best-hl {\n display: inline-block; white-space: nowrap;\n font-size: 20px; font-weight: 800; line-height: 1.4;\n background: linear-gradient(135deg, #CFBAFF 0%, #ffffff 35%, #AE8EFF 65%, #CFBAFF 100%);\n -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent;\n filter: drop-shadow(0 0 10px rgba(132,96,229,0.55));\n}\n/* Desktop (panel angosto): reduce el texto superior para que las 3 líneas quepan\n (en especial \"Mejor postor de la Oferta\" en una sola línea). */\n.smchatpanel--narrow .smchat__best-top { font-size: 16px; }\n.smchatpanel--narrow .smchat__best-hl { font-size: 17px; }\n/* Display del monto — mismo glass que el input pero no editable, centrado */\n.smchat__best-amount { justify-content: center; gap: 20px; padding: 0 24px; }\n.smchatpanel--narrow .smchat__best-amount { gap: 14px; padding: 0 16px; }\n.smchat__best-value { flex: 0 0 auto; }\n/* Texto medio (2 líneas) — blanco tenue */\n.smchat__best-sub {\n font-size: 13px; font-weight: 400; line-height: 1.35; text-align: center;\n color: rgba(255,255,255,0.85);\n}\n/* Desktop: cada renglón del texto medio entero (los saltos del Figma) */\n.smchatpanel--narrow .smchat__best-sub { font-size: 12px; white-space: nowrap; }\n/* \"¿Deseas mejorarlo?\" — degradado lila + glow */\n.smchat__best-ask {\n font-size: 18px; font-weight: 800; line-height: 1.2;\n background: linear-gradient(135deg, #CFBAFF 0%, #ffffff 35%, #AE8EFF 65%, #CFBAFF 100%);\n -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent;\n filter: drop-shadow(0 0 10px rgba(132,96,229,0.55));\n}\n/* Timer sobre la barra — texto + contador naranja */\n.smchat__best-timer {\n position: absolute; left: 50%; bottom: 100px; z-index: 5;\n transform: translateX(-50%);\n display: flex; align-items: center; gap: 8px; white-space: nowrap;\n font-size: 12px; font-weight: 400; color: rgba(255,255,255,0.9);\n}\n.smchat__best-count {\n font-size: 16px; font-weight: 800; color: #ED8936; font-variant-numeric: tabular-nums;\n text-shadow: 0 0 8px rgba(237,137,54,0.5);\n}\n.smchatpanel--narrow .smchat__best-timer { font-size: 10px; gap: 6px; }\n.smchatpanel--narrow .smchat__best-count { font-size: 13px; }\n\n/* ── Pantalla \"Confirmar monto\" (sub-vista de improve) ── */\n.smchat__confirm {\n display: flex; flex-direction: column; align-items: center; gap: 26px; width: 100%;\n padding: 0 20px; box-sizing: border-box;\n}\n/* Pregunta — 2 líneas, blanco 18/700. El user en bold (ya blanco). */\n.smchat__confirm-ask {\n font-size: 18px; font-weight: 700; line-height: 1.4; text-align: center; color: #ffffff;\n}\n.smchat__confirm-ask b { font-weight: 800; }\n/* SOLO \"el monto a US$XX?\" con degradado lila VYStrokes3 + glow morado */\n.smchat__confirm-amount {\n background: linear-gradient(135deg, #CFBAFF 0%, #ffffff 35%, #AE8EFF 65%, #CFBAFF 100%);\n -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent;\n filter: drop-shadow(0 0 10px rgba(132,96,229,0.55));\n}\n.smchat__confirm-actions { display: flex; flex-direction: column; align-items: center; gap: 12px; }\n\n/* Botón glass \"Confirmar monto\" 196×48 — glass burbuja + borde gradiente VYStrokes1\n + inner shadow blanco (0 1 6 #fff 45%). Animación \"gambler\" en hover/press. */\n.smchat__confirm-btn {\n position: relative; box-sizing: border-box;\n min-width: 196px; max-width: 100%; height: 48px; padding: 0 28px; border: none; border-radius: 9999px;\n cursor: pointer; overflow: hidden; isolation: isolate; white-space: nowrap;\n font-family: inherit; font-size: 16px; font-weight: 700; color: #ffffff;\n /* Glass burbuja: highlight superior + base translúcida */\n background:\n radial-gradient(120% 80% at 30% -10%, rgba(255,255,255,0.35) 0%, rgba(255,255,255,0) 55%),\n linear-gradient(180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.06) 45%, rgba(255,255,255,0.03) 100%);\n -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);\n /* Inner shadow blanco (Figma 0 1 6 #fff 45%) + leve sombra externa */\n box-shadow: inset 0 1px 6px rgba(255,255,255,0.45), 0 4px 14px -4px rgba(20,0,69,0.5);\n transition: transform 0.18s cubic-bezier(0.25,0.8,0.25,1), box-shadow 0.25s;\n}\n/* Borde gradiente VYStrokes1 (white→#F4AC59→#8460E5→white) 1.5px */\n.smchat__confirm-btn::before {\n content: \"\"; position: absolute; inset: 0; border-radius: inherit; 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; mask-composite: exclude; pointer-events: none; z-index: 2;\n}\n/* Shimmer diagonal \"gambler\" — banda de luz que recorre el botón. En default está\n FUERA a la izquierda (translateX -220%); solo entra en hover. */\n.smchat__confirm-btn::after {\n content: \"\"; position: absolute; top: 0; bottom: 0; left: 0;\n width: 55%; z-index: 1; pointer-events: none;\n background: linear-gradient(105deg, transparent 0%, rgba(255,255,255,0.55) 50%, transparent 100%);\n transform: translateX(-220%) skewX(-18deg);\n}\n.smchat__confirm-btn-label { position: relative; z-index: 3; text-shadow: 0 1px 3px rgba(0,0,0,0.28); }\n/* Panel angosto (desktop): fuente/padding menores para que el texto largo\n (\"Sí, quiero mejorar el monto\") entre en UNA línea. */\n.smchatpanel--narrow .smchat__confirm-btn { font-size: 14px; padding: 0 18px; min-width: 0; }\n/* Hover: la banda recorre + glow morado pulsante + leve levantada */\n.smchat__confirm-btn:hover {\n transform: translateY(-2px) scale(1.02);\n box-shadow:\n inset 0 1px 6px rgba(255,255,255,0.55),\n 0 0 18px rgba(174,142,255,0.55),\n 0 8px 22px -6px rgba(132,96,229,0.6);\n animation: gambler-glow 1.1s ease-in-out infinite;\n}\n.smchat__confirm-btn:hover::after { animation: gambler-sweep 1.1s ease-in-out infinite; }\n/* Press: \"punch\" — se hunde y destella */\n.smchat__confirm-btn:active {\n transform: scale(0.95) translateY(1px);\n box-shadow: inset 0 2px 8px rgba(0,0,0,0.25), inset 0 0 12px rgba(255,255,255,0.5);\n animation: none;\n}\n.smchat__confirm-btn:active::after {\n left: 0; width: 100%; transform: translateX(0) skewX(0deg);\n background: radial-gradient(60% 100% at 50% 50%, rgba(255,255,255,0.7) 0%, transparent 70%);\n animation: none;\n}\n.smchat__confirm-btn:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px #ffffff, 0 0 0 5px rgba(132,96,229,0.9);\n}\n/* Disabled — glass apagado (gris), sin burbuja ni gambler (Button primary disabled) */\n.smchat__confirm-btn:disabled {\n cursor: not-allowed;\n background: var(--vmc-color-background-disabled, #e1e3e2);\n color: var(--vmc-color-neutral-700, #99a1af);\n box-shadow: none;\n -webkit-backdrop-filter: none; backdrop-filter: none;\n transform: none; animation: none;\n}\n.smchat__confirm-btn:disabled::before { background: transparent; }\n.smchat__confirm-btn:disabled::after { display: none; }\n.smchat__confirm-btn:disabled .smchat__confirm-btn-label { text-shadow: none; }\n@keyframes gambler-sweep {\n 0% { transform: translateX(-220%) skewX(-18deg); }\n 60%, 100% { transform: translateX(400%) skewX(-18deg); }\n}\n@keyframes gambler-glow {\n 0%, 100% { box-shadow: inset 0 1px 6px rgba(255,255,255,0.55), 0 0 14px rgba(174,142,255,0.4), 0 8px 22px -6px rgba(132,96,229,0.55); }\n 50% { box-shadow: inset 0 1px 6px rgba(255,255,255,0.6), 0 0 26px rgba(207,186,255,0.75), 0 10px 26px -6px rgba(132,96,229,0.7); }\n}\n@media (prefers-reduced-motion: reduce) {\n .smchat__confirm-btn:hover, .smchat__confirm-btn:hover::after { animation: none; }\n}\n\n/* Pill \"Con Precio Reserva\" (VYSecondaryHover) dentro del panel (desktop) —\n pegado arriba, centrado, esquinas inferiores 16px. Gradiente #AE8EFF→#5A35C2 +\n sombra card (0 0 16 spread 4, #000 10%). Sin glow morado. */\n.smchat__reserve-pill {\n position: absolute; top: 0; left: 50%; z-index: 6;\n transform: translateX(-50%);\n box-sizing: border-box; height: 33px; padding: 0 22px;\n border-radius: 0 0 16px 16px;\n display: flex; align-items: center; justify-content: center; white-space: nowrap;\n font-size: 15px; font-weight: 600; color: #ffffff;\n background: linear-gradient(155deg, #5A35C2 0%, #AE8EFF 100%);\n}\n\n/* ═══════════════ GAMIFICACIÓN — animaciones \"gambler\" por fase ═══════════════ */\n\n/* Keyframes genéricos */\n@keyframes smchat-rise { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: translateY(0); } }\n/* Variante para elementos centrados con translateX(-50%) (notas/timer/next) */\n@keyframes smchat-rise-center { from { opacity: 0; transform: translate(-50%, 18px); } to { opacity: 1; transform: translate(-50%, 0); } }\n@keyframes smchat-tick { 0% { transform: scale(1); } 45% { transform: scale(1.14); } 100% { transform: scale(1); } }\n\n/* Cada pantalla de estado entra con fade + leve settle (antes era un fade plano;\n el resto del panel usa curvas con overshoot — esto lo alinea con ese lenguaje). */\n.smchat__status { animation: smchat-status-in 420ms cubic-bezier(0.22,1,0.36,1) both; }\n@keyframes smchat-status-in { from { opacity: 0; transform: scale(0.98); } to { opacity: 1; transform: none; } }\n\n/* ── Welcome / Inicio extendido ── */\n/* Título con glow \"respirando\" */\n.smchat__rp--breathe { animation: smchat-breathe 2.6s ease-in-out infinite; }\n@keyframes smchat-breathe {\n 0%, 100% { filter: drop-shadow(0 0 10px rgba(174,142,255,0.55)) drop-shadow(0 0 24px rgba(82,52,189,0.4)); }\n 50% { filter: drop-shadow(0 0 18px rgba(174,142,255,0.95)) drop-shadow(0 0 38px rgba(82,52,189,0.7)); }\n}\n/* Caja \"Inicia en\" entra con pop elástico */\n.smchat__box { animation: smchat-pop-in 550ms 200ms cubic-bezier(0.34,1.56,0.64,1) both; }\n@keyframes smchat-pop-in { from { opacity: 0; transform: scale(0.6); } to { opacity: 1; transform: scale(1); } }\n/* Ícono (reloj ↔ +) hace pop al cambiar de fase */\n.smchat__box-icon { display: inline-flex; animation: smchat-icon-in 450ms cubic-bezier(0.34,1.56,0.64,1) both; }\n@keyframes smchat-icon-in { from { opacity: 0; transform: scale(0.4) rotate(-90deg); } to { opacity: 1; transform: none; } }\n/* Dígitos del contador hacen tick cada segundo (re-monta con key={count}) */\n.smchat__box-time--tick { animation: smchat-tick 300ms cubic-bezier(0.34,1.56,0.64,1); }\n/* \"Inicio extendido\" cae como sello + latido de brillo */\n.smchat__ext {\n animation: smchat-ext-in 450ms cubic-bezier(0.34,1.56,0.64,1) both, smchat-ext-glow 1.6s 500ms ease-in-out infinite;\n}\n@keyframes smchat-ext-in { from { opacity: 0; transform: scale(1.6); } to { opacity: 1; transform: scale(1); } }\n@keyframes smchat-ext-glow { 0%, 100% { text-shadow: 0 0 8px rgba(255,255,255,0.25); } 50% { text-shadow: 0 0 18px rgba(255,255,255,0.75); } }\n\n/* ── Streaming: MI puja ≠ puja de otros ── */\n/* Mi burbuja live es MÁS GRANDE (solo en el stream) */\n.smchat__stream .smchat__msg--mine .pbidmsg { font-size: 15px; padding: 10px 20px; }\n/* Entrada \"slam\": llega grande desde la derecha, aplasta y deja un halo naranja */\n.smchat__msg--mine { transform-origin: right bottom; animation: smchat-slam 500ms cubic-bezier(0.22,1,0.36,1) both; }\n@keyframes smchat-slam {\n 0% { opacity: 0; transform: translateX(70px) scale(1.4); filter: drop-shadow(0 0 0 rgba(255,150,57,0)); }\n 55% { opacity: 1; transform: translateX(-4px) scale(1.06); filter: drop-shadow(0 0 18px rgba(255,150,57,0.9)); }\n 100% { opacity: 1; transform: none; filter: drop-shadow(0 0 6px rgba(255,150,57,0.35)); }\n}\n/* Banda de luz que barre mi burbuja al aterrizar */\n.smchat__msg--mine .pbidmsg { position: relative; overflow: hidden; }\n.smchat__msg--mine .pbidmsg::after {\n content: \"\"; position: absolute; top: 0; bottom: 0; left: 0; width: 60%; pointer-events: none;\n background: linear-gradient(105deg, transparent 0%, rgba(255,255,255,0.5) 50%, transparent 100%);\n transform: translateX(-180%) skewX(-18deg);\n animation: smchat-bubble-shine 700ms 350ms ease-out both;\n}\n@keyframes smchat-bubble-shine { from { transform: translateX(-180%) skewX(-18deg); } to { transform: translateX(320%) skewX(-18deg); } }\n/* Flash de pantalla (wash naranja desde mi esquina) cuando bideo YO */\n.smchat__myflash {\n position: absolute; top: 0; left: 0; right: 0; bottom: 108px; z-index: 2; pointer-events: none; opacity: 0;\n background: radial-gradient(120% 100% at 80% 92%, rgba(255,150,57,0.5) 0%, rgba(255,106,0,0.18) 40%, transparent 72%);\n mix-blend-mode: screen;\n animation: smchat-myflash 700ms ease-out both;\n}\n@keyframes smchat-myflash { 0% { opacity: 0; } 25% { opacity: 1; } 100% { opacity: 0; } }\n/* \"Cierra en\" entra con un wobble corto (tensión de remate) */\n.smchat__msg--closes { animation: smchat-in-wobble 480ms cubic-bezier(0.22,1,0.36,1) both; }\n@keyframes smchat-in-wobble {\n 0% { opacity: 0; transform: translateX(-60px) rotate(-3deg); }\n 60% { opacity: 1; transform: translateX(4px) rotate(1.5deg); }\n 100% { opacity: 1; transform: none; }\n}\n/* \"A la una / dos / tres\" — sello que cae, cada vez más fuerte; la tercera sacude */\n.smchat__msg--vmc { transform-origin: left bottom; }\n.smchat__msg--vmc1 { animation: smchat-stamp1 480ms cubic-bezier(0.22,1,0.36,1) both; }\n.smchat__msg--vmc2 { animation: smchat-stamp2 520ms cubic-bezier(0.22,1,0.36,1) both; }\n.smchat__msg--vmc3 { animation: smchat-stamp3 560ms cubic-bezier(0.22,1,0.36,1) both, smchat-shake 320ms 560ms linear; }\n@keyframes smchat-stamp1 {\n 0% { opacity: 0; transform: scale(1.45); filter: drop-shadow(0 0 0 rgba(174,142,255,0)); }\n 55% { opacity: 1; transform: scale(0.97); filter: drop-shadow(0 0 14px rgba(174,142,255,0.8)); }\n 100% { opacity: 1; transform: scale(1); filter: none; }\n}\n@keyframes smchat-stamp2 {\n 0% { opacity: 0; transform: scale(1.8); filter: drop-shadow(0 0 0 rgba(174,142,255,0)); }\n 55% { opacity: 1; transform: scale(0.95); filter: drop-shadow(0 0 20px rgba(174,142,255,0.95)); }\n 100% { opacity: 1; transform: scale(1); filter: none; }\n}\n@keyframes smchat-stamp3 {\n 0% { opacity: 0; transform: scale(2.2); filter: drop-shadow(0 0 0 rgba(255,150,57,0)); }\n 50% { opacity: 1; transform: scale(0.93); filter: drop-shadow(0 0 26px rgba(255,150,57,1)); }\n 100% { opacity: 1; transform: scale(1); filter: drop-shadow(0 0 10px rgba(255,150,57,0.45)); }\n}\n@keyframes smchat-shake {\n 0%, 100% { transform: translateX(0); }\n 20% { transform: translateX(-4px); } 40% { transform: translateX(4px); }\n 60% { transform: translateX(-3px); } 80% { transform: translateX(3px); }\n}\n\n/* ── Barra: chispa en el borde del relleno + modo \"urgente\" cerca del final ── */\n.smchat__barwrap { position: absolute; left: 0; right: 0; bottom: 80px; z-index: 5; }\n.smchat__spark {\n position: absolute; top: 50%; left: 0; width: 16px; height: 16px; margin: -8px 0 0 -8px;\n border-radius: 50%; pointer-events: none;\n background: radial-gradient(circle, #ffffff 0%, rgba(255,255,255,0.75) 35%, transparent 70%);\n filter: drop-shadow(0 0 6px #ffffff) drop-shadow(0 0 14px #CC00FF) drop-shadow(0 0 24px #FF0066);\n transition: left 150ms linear;\n animation: smchat-spark-idle 1.4s ease-in-out infinite;\n}\n@keyframes smchat-spark-idle { 0%, 100% { transform: scale(0.85); } 50% { transform: scale(1.15); } }\n.smchat__barwrap--hot .pprogbar__fill { animation: smchat-bar-throb 500ms ease-in-out infinite; }\n.smchat__barwrap--hot .smchat__spark { animation: smchat-spark-hot 300ms ease-in-out infinite; }\n@keyframes smchat-bar-throb { 0%, 100% { filter: brightness(1) saturate(1); } 50% { filter: brightness(1.55) saturate(1.4); } }\n@keyframes smchat-spark-hot { 0%, 100% { transform: scale(0.9); } 50% { transform: scale(1.55); } }\n\n/* ── Processing: shimmer que recorre el texto gradiente ── */\n.smchat__rp--shimmer { background-size: 220% 100%; animation: smchat-shimmer 1.6s linear infinite; }\n@keyframes smchat-shimmer { 0% { background-position: 0% 0; } 100% { background-position: -220% 0; } }\n\n/* ── Tabla de posiciones: reveal escalonado (título → 2° → 1° con fanfarria) ── */\n.smchat__result-title { animation: smchat-rise 500ms cubic-bezier(0.22,1,0.36,1) both; }\n.smchat__rows { position: relative; }\n.smchat__row--in2 { animation: smchat-row-in 550ms 500ms cubic-bezier(0.22,1,0.36,1) both; }\n@keyframes smchat-row-in { from { opacity: 0; transform: translateY(24px) scale(0.94); } to { opacity: 1; transform: none; } }\n/* El GANADOR cae desde arriba con rebote + ráfaga dorada; conserva un aura suave */\n.smchat__row--in1 { animation: smchat-win-in 720ms 1300ms cubic-bezier(0.34,1.56,0.64,1) both; }\n@keyframes smchat-win-in {\n 0% { opacity: 0; transform: translateY(-30px) scale(0.7); }\n 55% { opacity: 1; transform: translateY(4px) scale(1.07);\n box-shadow: rgba(20,0,69,0.3) 0px 6px 16px -2px, var(--sm-glow-win); }\n 100% { opacity: 1; transform: none;\n box-shadow: rgba(20,0,69,0.3) 0px 6px 16px -2px, inset 0 1px 0 rgba(255,255,255,0.18), 0 0 14px rgba(237,137,54,0.28); }\n}\n/* Brillo que barre la fila del ganador en loop (empieza al aterrizar) */\n.smchat__row--mine { overflow: hidden; }\n.smchat__row--mine::before {\n content: \"\"; position: absolute; top: 0; bottom: 0; left: 0; width: 55%; z-index: 1; pointer-events: none;\n background: linear-gradient(105deg, transparent 0%, rgba(255,255,255,0.45) 50%, transparent 100%);\n transform: translateX(-220%) skewX(-18deg);\n}\n.smchat__row--in1::before { animation: smchat-row-sheen 3s 2.2s ease-in-out infinite; }\n@keyframes smchat-row-sheen { 0% { transform: translateX(-220%) skewX(-18deg); } 30%, 100% { transform: translateX(420%) skewX(-18deg); } }\n/* La estrella llena del ganador hace pop girando (después de aterrizar) */\n.smchat__row--in1 .smchat__user svg { animation: smchat-star-pop 550ms 1950ms cubic-bezier(0.34,1.56,0.64,1) both; }\n@keyframes smchat-star-pop { 0% { opacity: 0; transform: scale(0) rotate(-120deg); } 100% { opacity: 1; transform: none; } }\n/* Chispas doradas que estallan desde la fila del ganador */\n.smchat__win-sparks { position: absolute; left: 50%; width: 0; height: 0; z-index: 2; pointer-events: none; }\n.smchat__win-spark {\n position: absolute; left: 0; top: 0; width: 8px; height: 8px; margin: -4px;\n border-radius: 50%; opacity: 0;\n background: radial-gradient(circle, #ffffff 0%, #FFB566 60%, transparent 100%);\n filter: drop-shadow(0 0 6px rgba(255,150,57,0.9));\n animation: smchat-win-spark 800ms 1700ms cubic-bezier(0.2,0.7,0.3,1) both;\n}\n@keyframes smchat-win-spark {\n 0% { opacity: 0; transform: rotate(var(--ang)) translateX(10px) scale(0.4); }\n 20% { opacity: 1; }\n 100% { opacity: 0; transform: rotate(var(--ang)) translateX(var(--dist, 120px)) scale(1); }\n}\n/* Nota \"próximas ofertas\": en la tabla espera a la fanfarria; en actividad entra antes */\n.smchat__next { animation: smchat-rise-center 550ms 700ms cubic-bezier(0.22,1,0.36,1) both; }\n.smchat__next--late { animation: smchat-rise-center 650ms 2400ms cubic-bezier(0.22,1,0.36,1) both; }\n\n/* ── Cascadas de entrada (actividad / 48 horas / bestbid / improve / confirm) ── */\n.smchat__act > *, .smchat__reserve > *, .smchat__best > *, .smchat__improve > *, .smchat__confirm > * {\n animation: smchat-rise 520ms cubic-bezier(0.22,1,0.36,1) both;\n}\n.smchat__act > *:nth-child(2), .smchat__reserve > *:nth-child(2), .smchat__best > *:nth-child(2),\n.smchat__improve > *:nth-child(2), .smchat__confirm > *:nth-child(2) { animation-delay: 160ms; }\n.smchat__act > *:nth-child(3), .smchat__best > *:nth-child(3), .smchat__improve > *:nth-child(3) { animation-delay: 320ms; }\n.smchat__act > *:nth-child(4), .smchat__best > *:nth-child(4) { animation-delay: 480ms; }\n.smchat__act > *:nth-child(5), .smchat__best > *:nth-child(5) { animation-delay: 640ms; }\n\n/* ── Actividad (Ganado la Oferta): título con burst + ícono flotando + card con aura ── */\n.smchat__act > .smchat__act-title { animation: smchat-title-in 700ms 320ms cubic-bezier(0.34,1.56,0.64,1) both; }\n@keyframes smchat-title-in {\n 0% { opacity: 0; transform: scale(0.5); }\n 60% { opacity: 1; transform: scale(1.12); filter: drop-shadow(0 0 24px rgba(237,137,54,0.9)) drop-shadow(0 0 48px rgba(132,96,229,0.7)); }\n 100% { opacity: 1; transform: scale(1); filter: drop-shadow(0 0 53px rgba(49,0,138,0.4)) drop-shadow(0 0 32px rgba(82,52,188,0.5)); }\n}\n.smchat__act-icon svg { animation: smchat-float 3.2s 1.1s ease-in-out infinite; }\n@keyframes smchat-float { 0%, 100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-7px) rotate(3deg); } }\n.smchat__act > .pbid {\n animation: smchat-rise 520ms 640ms cubic-bezier(0.22,1,0.36,1) both, smchat-won-glow 2.6s 1.7s ease-in-out infinite;\n}\n@keyframes smchat-won-glow {\n 0%, 100% { box-shadow: rgba(20,0,69,0.3) 0px 8px 24px -2px; }\n 50% { box-shadow: rgba(20,0,69,0.3) 0px 8px 24px -2px, 0 0 18px rgba(255,180,102,0.5), 0 0 36px rgba(132,96,229,0.45); }\n}\n\n/* ── 48 horas: reloj que hace tic-tac + \"48 horas\" late con glow ── */\n.smchat__reserve-icon svg { animation: smchat-clock-tick 2.2s 900ms ease-in-out infinite; transform-origin: 50% 55%; }\n@keyframes smchat-clock-tick { 0%, 100% { transform: rotate(-5deg); } 50% { transform: rotate(5deg); } }\n.smchat__reserve-hours {\n display: inline-block;\n animation: smchat-hours-in 650ms 420ms cubic-bezier(0.34,1.56,0.64,1) both, smchat-breathe 2.6s 1.3s ease-in-out infinite;\n}\n@keyframes smchat-hours-in { 0% { opacity: 0; transform: scale(0.5); } 60% { opacity: 1; transform: scale(1.12); } 100% { opacity: 1; transform: scale(1); } }\n\n/* ── Bestbid: \"¿Deseas mejorarlo?\" late + countdown con tick (rojo al final) ── */\n.smchat__best > .smchat__best-ask {\n animation: smchat-rise 520ms 480ms cubic-bezier(0.22,1,0.36,1) both, smchat-ask-pulse 1.8s 1.4s ease-in-out infinite;\n}\n@keyframes smchat-ask-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.07); } }\n.smchat__best-count { display: inline-block; }\n.smchat__best-count--tick { animation: smchat-tick 320ms cubic-bezier(0.34,1.56,0.64,1); }\n.smchat__best-count--hot {\n color: #FF3B5C; text-shadow: 0 0 10px rgba(255,59,92,0.8);\n animation: smchat-count-hot 320ms cubic-bezier(0.34,1.56,0.64,1);\n}\n@keyframes smchat-count-hot { 0% { transform: scale(1.6); } 100% { transform: scale(1); } }\n.smchat__best-timer--hot > span:first-child { animation: smchat-blink 700ms ease-in-out infinite; }\n@keyframes smchat-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }\n\n/* Notas y timer sobre la barra entran después del contenido */\n.smchat__reserve-note, .smchat__improve-note, .smchat__best-timer {\n animation: smchat-rise-center 550ms 700ms cubic-bezier(0.22,1,0.36,1) both;\n}\n\n/* Input de improve: glow al enfocar */\n.smchat__improve-input:focus-within {\n box-shadow: rgba(20,0,69,0.3) 0 8px 24px -2px, 0 0 16px rgba(174,142,255,0.55);\n}\n\n/* Shimmer \"atractor\" automático en los botones glass (sin esperar hover) */\n.smchat__confirm-btn::after { animation: gambler-idle 3.2s 1s ease-in-out infinite; }\n@keyframes gambler-idle {\n 0% { transform: translateX(-220%) skewX(-18deg); }\n 38% { transform: translateX(400%) skewX(-18deg); }\n 100% { transform: translateX(400%) skewX(-18deg); }\n}\n\n/* Cartelito \"Con Precio Reserva\" (versión panel): baja desde detrás del borde superior */\n.smchat__reserve-pill { animation: smchat-pill-drop 700ms 120ms cubic-bezier(0.34,1.56,0.64,1) both; }\n@keyframes smchat-pill-drop { from { transform: translate(-50%, -110%); } to { transform: translate(-50%, 0); } }\n\n@media (prefers-reduced-motion: reduce) {\n .smchatpanel *, .smchatpanel *::before, .smchatpanel *::after { animation: none !important; transition: none !important; }\n .smchat__myflash, .smchat__win-spark, .smchat__spark { display: none; }\n}\n`;\n\nlet _stylesInjected = false;\n\nfunction pad(n: number): string {\n return String(Math.max(0, n)).padStart(2, \"0\");\n}\n\n/** Nivel del remate para el efecto \"sello\": A la una (1) → A las dos (2) → A las tres (3). */\nfunction vmcLevel(text?: string): 1 | 2 | 3 {\n if (text?.includes(\"tres\")) return 3;\n if (text?.includes(\"dos\")) return 2;\n return 1;\n}\n\nconst WIN_SPARKS = 12;\n\nexport interface MobileChatPanelProps {\n height?: number;\n /** Ancho del panel. Default 420 (mobile). En desktop es menor: recorta a los\n lados sin achicar el contenido (centrado + overflow:hidden). */\n width?: number;\n className?: string;\n /** Estado de la animación (controlado por SalaMobile) */\n live?: boolean;\n phase?: Phase;\n shown?: number;\n progress?: number;\n progressVariant?: ProgressBarVariant;\n count?: number;\n /** Bid actual (para BidProposal) */\n bidAmount?: number;\n bidder?: string;\n /** Contador que dispara la animación de nuevo bid en BidProposal */\n flash?: number;\n /** Colores del efecto de luz (editable) */\n flashColors?: string[];\n /** Tipo de efecto de luz: \"bulb\" o \"spin\" */\n flashMode?: \"bulb\" | \"spin\" | \"explode\" | \"pulse\" | \"combo\" | \"shine\";\n /** Muestra el pill \"Con Precio Reserva\" pegado arriba del panel (solo desktop:\n en mobile el pill lo pone MobileHeader). Se ve en la fase \"reserve\". */\n reservePill?: boolean;\n}\n\nexport default function MobileChatPanel({\n height = 670,\n width = MOBILECHATPANEL_WIDTH,\n className = \"\",\n live = false,\n phase = \"idle\",\n shown = 0,\n progress = 40,\n progressVariant = \"rainbow\",\n count = 3,\n bidAmount = 6559,\n bidder = \"ZAE389\",\n flash = 0,\n flashColors,\n flashMode = \"bulb\",\n reservePill = false,\n}: MobileChatPanelProps): JSX.Element {\n // Fondo aurora + seda: física mass-spring-damper que escribe CSS vars vía ref.\n const { ref: auroraRef } = useAuroraSpring();\n const { ref: silkRef } = useAuroraSpring({ vars: [\"--sx\", \"--sy\", \"--sx2\", \"--sy2\"], amplitude: 20 });\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 = SMCHATPANEL_STYLES;\n document.head.appendChild(el);\n }\n _stylesInjected = true;\n }\n\n // Sub-flujo con precio reserva. La fase del motor entra en \"bestbid\"; a partir de\n // ahí el panel navega localmente entre estos pasos (interacciones del usuario):\n // bestbid → \"Mejor postor / ¿Deseas mejorarlo?\" (+ countdown 23s)\n // improve → \"Ingresa un monto superior\" (input)\n // confirm → \"¿estás seguro de mejorar el monto a US$XX?\"\n // reserve → \"Revisa tu historial / 48 horas\" (fin)\n type Step = \"bestbid\" | \"improve\" | \"confirm\" | \"reserve\";\n const [step, setStep] = useState(\"bestbid\");\n const [improveAmount, setImproveAmount] = useState(\"\");\n const [countdown, setCountdown] = useState(BESTBID_SECONDS);\n const improveDigits = improveAmount.replace(/\\D/g, \"\");\n const improveValid = improveDigits.length > 0 && Number(improveDigits) > bidAmount;\n function handleImproveInput(e: ChangeEvent): void {\n setImproveAmount(e.target.value.replace(/\\D/g, \"\"));\n }\n const improveShown = improveDigits ? Number(improveDigits).toLocaleString(\"en-US\") : \"\";\n\n // Al (re)entrar a la fase \"bestbid\" del motor, reinicia el sub-flujo desde el paso 1.\n useEffect(\n function resetOnEnter() {\n if (phase === \"bestbid\") {\n setStep(\"bestbid\");\n setImproveAmount(\"\");\n setCountdown(BESTBID_SECONDS);\n }\n },\n [phase],\n );\n\n // Countdown de \"¿Deseas mejorarlo?\": corre solo en el paso \"bestbid\". Al llegar a\n // 0 pasa a \"48 horas\" (reserve). Se pausa/cancela si el usuario navega a otro paso.\n const inBestbid = live && phase === \"bestbid\" && step === \"bestbid\";\n useEffect(\n function runCountdown() {\n if (!inBestbid) return;\n if (countdown <= 0) {\n setStep(\"reserve\");\n return;\n }\n const id = setTimeout(() => setCountdown((c) => c - 1), 1000);\n return () => clearTimeout(id);\n },\n [inBestbid, countdown],\n );\n\n const showStatus = live && (phase === \"welcome\" || phase === \"extended\");\n const showStream = live && phase === \"streaming\";\n const showProcessing = live && phase === \"processing\";\n const showResult = live && phase === \"result\"; // Tabla de posiciones (podio)\n const showActivity = live && phase === \"activity\"; // BidProposal ganador + email\n // Fase \"bestbid\" del motor → sub-flujo local (bestbid/improve/confirm/reserve)\n const inReserveFlow = live && phase === \"bestbid\";\n const showBestbid = inReserveFlow && step === \"bestbid\"; // \"Mejor postor / ¿Deseas mejorarlo?\"\n const showImprove = inReserveFlow && step === \"improve\"; // \"Ingresa un monto superior\"\n const showConfirm = inReserveFlow && step === \"confirm\"; // \"¿estás seguro de mejorar…?\"\n const showReserve = inReserveFlow && step === \"reserve\"; // \"Revisa tu historial / 48 horas\"\n const isActivity = showActivity;\n const showStatic = !live;\n const showProposal = showStatic || showStream;\n // Barra del countdown en \"bestbid\": baja de 100% (23s) a 0% (0s).\n const bestbidProg = (countdown / BESTBID_SECONDS) * 100;\n\n // ── Gamificación ──\n // ¿El último mensaje del stream es una puja MÍA? → dispara el flash de pantalla.\n const lastStreamMsg = shown > 0 ? STREAM[Math.min(shown, STREAM.length) - 1] : undefined;\n const lastMine = lastStreamMsg?.kind === \"proposal\" && lastStreamMsg.mine === true;\n // La luz del BidProposal cambia cuando la puja actual es MÍA (naranja + combo).\n const proposalMine = bidder === ME;\n // Valor único de la barra + estados de urgencia/chispa. Persigue su objetivo con\n // inercia (decaimiento exponencial): el relleno \"acelera y frena\" en vez de saltar\n // en los cambios de fase (p. ej. streaming→processing 100%→0%), y sigue el remate\n // con más peso físico que una transición CSS lineal.\n const barTarget = showBestbid ? bestbidProg : showResult || showActivity || showReserve || showImprove || showConfirm ? 100 : showProcessing ? 0 : progress;\n // Barra con spring subamortiguado (mass-spring-damper): \"acelera y frena\" con un\n // leve rebote al asentarse. Damping alto (ζ≈0.7) → rebote suave, sin pasarse feo.\n const barValue = Math.max(0, Math.min(100, useSpring(barTarget, { stiffness: 90, damping: 17 })));\n const barHot = (showStream && progress >= 70) || (showBestbid && countdown <= 5);\n const sparkOn = (showStream || showBestbid) && barValue > 1 && barValue < 100;\n\n return (\n <>\n