// Voxel office — isometric CSS-3D scene with department zones and walking avatars. // Built with pure transforms; modular avatars made of 4 cubes (legs/torso/head + accessory). const VOX_STYLE = ` .vox-wrap{ position:absolute; inset:0; perspective: 1400px; } .vox-scene{ position:absolute; left:50%; top:50%; transform-style: preserve-3d; transform: translate(-50%, -50%) rotateX(54deg) rotateZ(45deg); width: 0; height: 0; } .vox-cube, .vox-tile{ position:absolute; transform-style: preserve-3d; width: var(--s, 28px); height: var(--s, 28px); } /* Standard cube — top is the visible face, left/right are sides for height */ .vox-cube .top, .vox-cube .left, .vox-cube .right{ position:absolute; left:0; top:0; width: var(--s, 28px); height: var(--s, 28px); background: var(--c, #fff); } .vox-cube .top { transform: translateZ(var(--h, 28px)); filter: brightness(1.08); } .vox-cube .left { transform-origin: left top; transform: rotateY(-90deg) translateX(0) translateZ(0); width: var(--h, 28px); filter: brightness(.78); } .vox-cube .right { transform-origin: left top; transform: rotateX(90deg) translateY(0) translateZ(0); height: var(--h, 28px); filter: brightness(.9); } /* Flat floor tile */ .vox-tile .face{ position:absolute; inset:0; background: var(--c, #DCE6F2); border: 1px solid rgba(0,0,0,.04); } .vox-label{ position:absolute; transform: rotateZ(-45deg) rotateX(-54deg); transform-origin: 0 0; font: 600 10px/1 'Inter', sans-serif; color: rgba(15,30,60,.7); letter-spacing:.04em; text-transform: uppercase; white-space:nowrap; pointer-events: none; text-shadow: 0 1px 0 rgba(255,255,255,.6); } /* Floating bubble (chat preview) above a worker */ .vox-bubble{ position:absolute; transform: rotateZ(-45deg) rotateX(-54deg); transform-origin: center; background: #fff; border-radius: 10px; padding: 5px 9px; font: 600 10px/1.2 'Inter', sans-serif; color: #1F2937; box-shadow: 0 6px 14px rgba(8,20,40,.25); white-space: nowrap; pointer-events:none; } .vox-bubble .who{ color: var(--brand-secondary); font-weight:700; font-size: 9px; text-transform: uppercase; letter-spacing: .05em } .vox-bubble .dot{ width:6px;height:6px;border-radius:999px;background:#16A34A;display:inline-block;margin-right:4px;vertical-align:middle} @keyframes voxBob { 0%,100%{ transform: translateZ(0) } 50%{ transform: translateZ(4px) } } @keyframes voxWalk { from{ transform: translateZ(0) } 50%{ transform: translateZ(3px) } to{ transform: translateZ(0) } } .vox-worker .bob{ animation: voxBob 1.4s ease-in-out infinite; transform-style: preserve-3d; position:absolute; inset:0; } `; // One voxel cube function Cube({ x = 0, y = 0, z = 0, w = 28, h = 28, depth, color = '#cccccc', style }) { const s = w; // assume square footprint const tall = h; return (
); } function Tile({ x = 0, y = 0, w = 28, color = '#E5EDF7', children, style }) { return (
{children}
); } // Voxel worker — modular: legs (small), torso (color), head (skin) function Worker({ x, y, color = '#1E6BB0', skin = '#F3D1A2', anim = true, message, role }) { return (
{/* Legs (dark) */} {/* Torso (brand color) */} {/* Head (skin) */} {/* Hair patch */}
{message && (
{role &&
{role}
}
{message}
)}
); } // The full office scene function VoxelOffice({ brandPrimary = '#1B3A6B', brandSecondary = '#1E6BB0', brandAccent = '#E8620A' }) { const [hoverDept, setHoverDept] = React.useState(null); // Workers animate by cycling positions through a small loop const [tick, setTick] = React.useState(0); React.useEffect(() => { const i = setInterval(() => setTick(t => t + 1), 2200); return () => clearInterval(i); }, []); // ────────────────────────────────────────────────────────── // GRID — using "voxel coords" where one unit = 28px tile. // We compose tile positions and building blocks below. // Center is (0,0); coords flow outward. // ────────────────────────────────────────────────────────── const U = 28; // unit // Floor tiles per department (zone color) const zones = [ { id: 'recepcion', name: 'Recepción', tiles: [[ -1, 1],[-1,2],[0,1],[0,2]], color: '#FFE6CF', border: brandAccent }, { id: 'marketing', name: 'Marketing', tiles: [[-3,-2],[-3,-1],[-2,-2],[-2,-1],[-3,-3],[-2,-3]], color: '#E4D7F7' }, { id: 'ventas', name: 'Ventas / Soporte', tiles: [[1,-2],[1,-1],[2,-2],[2,-1],[1,-3],[2,-3]], color: '#CFE8E0' }, { id: 'almacen', name: 'Almacén', tiles: [[-3,2],[-3,3],[-2,2],[-2,3]], color: '#F6E1B8' }, { id: 'estudio', name: 'Estudio Creativo', tiles: [[1,2],[1,3],[2,2],[2,3]], color: '#D5E7FF' }, { id: 'ti', name: 'Oficina TI', tiles: [[-1,-2],[-1,-1],[0,-2],[0,-1]], color: '#DDE3EB' }, ]; // Building elements (walls / planters / dividers — short cubes) const blocks = [ // outer walls — perimeter at half-height ...Array.from({length: 8}, (_,i)=> ({ x:i-3.5, y:-3.5, h: 6, color:'#F4F8FC' })), ...Array.from({length: 8}, (_,i)=> ({ x:i-3.5, y: 3.5, h: 6, color:'#F4F8FC' })), ...Array.from({length: 7}, (_,i)=> ({ x:-3.5, y:i-2.5, h: 6, color:'#F4F8FC' })), ...Array.from({length: 7}, (_,i)=> ({ x: 3.5, y:i-2.5, h: 6, color:'#F4F8FC' })), // dividers between rooms { x: 0.5, y:-2.5, h: 14, color:'#E5EDF7' }, { x: 0.5, y:-1.5, h: 14, color:'#E5EDF7' }, { x: 0.5, y: 2.5, h: 14, color:'#E5EDF7' }, { x:-2.5, y: 0.5, h: 14, color:'#E5EDF7' }, { x: 2.5, y: 0.5, h: 14, color:'#E5EDF7' }, // Reception desk (accent) { x:-0.5, y: 1.5, h: 10, color: brandAccent }, // plants — short green cubes { x:-3, y: 0, h: 12, color:'#7CB37A' }, { x: 3, y: 0, h: 12, color:'#7CB37A' }, ]; // Workers — one per zone, plus a director near recepción const seats = [ { id:'mkt', zone:'marketing', role:'Marketing', color:'#7C3AED', x:-2.6, y:-2.0, msg:'Redactando post…' }, { id:'ana', zone:'marketing', role:'Analítica', color:'#0EA5E9', x:-2.0, y:-2.7, msg:'Calculando ROAS' }, { id:'sop', zone:'ventas', role:'Atención', color:'#14B8A6', x: 1.6, y:-2.0, msg:'CSAT 4.9' }, { id:'inv', zone:'almacen', role:'Inventario', color:'#A16207', x:-2.6, y: 2.6, msg:'2 SKUs sin SEO' }, { id:'log', zone:'almacen', role:'Logística', color: brandAccent, x:-2.0, y: 2.0, msg:'Entrega #142' }, { id:'dis', zone:'estudio', role:'Diseño Web', color:'#DB2777', x: 1.6, y: 2.6, msg:'Editando landing' }, { id:'sec', zone:'ti', role:'Seguridad', color:'#475569', x:-0.7, y:-1.6, msg:'Monitor activo' }, { id:'dir', zone:'recepcion', role:'Director', color: brandPrimary, x:-0.7, y: 1.6, msg:'@dueño aprobación', tag: true }, ]; // Pulse the active worker (rotates each tick) const activeIdx = tick % seats.length; return (
{/* Base platform — slight raised slab */} {/* Zone floor tiles */} {zones.map(z => z.tiles.map(([gx, gy], i) => ( )))} {/* Walls / planters / desks */} {blocks.map((b, i) => ( ))} {/* Workers */} {seats.map((w, i) => ( ))} {/* Zone labels positioned at zone centroid */} {zones.map(z => { const cx = z.tiles.reduce((a,t)=>a+t[0],0)/z.tiles.length; const cy = z.tiles.reduce((a,t)=>a+t[1],0)/z.tiles.length; return (
{z.name}
); })}
{/* Overlay legend */}
Vista isométrica — Tu oficina IA · 8 colaboradores activos
{['Aérea','Planta'].map((v,i)=>( ))}
); } Object.assign(window, { VoxelOffice });