Showing
8 changed files
with
3793 additions
and
323 deletions
| ... | @@ -20,7 +20,6 @@ | ... | @@ -20,7 +20,6 @@ |
| 20 | let mode = $state('archivo'); | 20 | let mode = $state('archivo'); |
| 21 | let searchFocused = $state(false); | 21 | let searchFocused = $state(false); |
| 22 | let hoveredStory = $state(null); | 22 | let hoveredStory = $state(null); |
| 23 | - let mousePos = $state({ x: 0, y: 0 }); | ||
| 24 | let activeToggle = $state(null); | 23 | let activeToggle = $state(null); |
| 25 | let searchInput = $state(null); | 24 | let searchInput = $state(null); |
| 26 | let debounceTimer; | 25 | let debounceTimer; |
| ... | @@ -40,12 +39,12 @@ | ... | @@ -40,12 +39,12 @@ |
| 40 | 39 | ||
| 41 | const MODES = { | 40 | const MODES = { |
| 42 | archivo: { | 41 | archivo: { |
| 43 | - placeholder: 'YPFB, Publicidad, Sueldos...', | 42 | + placeholder: 'Ej: Ministerio de Salud, YPFB, sueldos...', |
| 44 | label: 'Serie histórica', | 43 | label: 'Serie histórica', |
| 45 | color: COLORS.accent, | 44 | color: COLORS.accent, |
| 46 | }, | 45 | }, |
| 47 | hoy: { | 46 | hoy: { |
| 48 | - placeholder: 'Tu municipio, Salud, Obras...', | 47 | + placeholder: 'Ej: Tu municipio, hospitales, obras...', |
| 49 | label: 'Actualización diaria', | 48 | label: 'Actualización diaria', |
| 50 | color: COLORS.green, | 49 | color: COLORS.green, |
| 51 | }, | 50 | }, |
| ... | @@ -175,10 +174,7 @@ | ... | @@ -175,10 +174,7 @@ |
| 175 | }); | 174 | }); |
| 176 | observer.observe(document.documentElement, { attributes: true }); | 175 | observer.observe(document.documentElement, { attributes: true }); |
| 177 | 176 | ||
| 178 | - const handleMouse = (e) => { mousePos = { x: e.clientX, y: e.clientY }; }; | ||
| 179 | - window.addEventListener('mousemove', handleMouse); | ||
| 180 | return () => { | 177 | return () => { |
| 181 | - window.removeEventListener('mousemove', handleMouse); | ||
| 182 | observer.disconnect(); | 178 | observer.disconnect(); |
| 183 | }; | 179 | }; |
| 184 | }); | 180 | }); |
| ... | @@ -189,9 +185,6 @@ | ... | @@ -189,9 +185,6 @@ |
| 189 | </svelte:head> | 185 | </svelte:head> |
| 190 | 186 | ||
| 191 | <div class="page"> | 187 | <div class="page"> |
| 192 | - <!-- Grain overlay --> | ||
| 193 | - <div class="grain"></div> | ||
| 194 | - | ||
| 195 | <!-- Header con Logo y Controles --> | 188 | <!-- Header con Logo y Controles --> |
| 196 | <header class="page-header"> | 189 | <header class="page-header"> |
| 197 | {#if isDarkMode} | 190 | {#if isDarkMode} |
| ... | @@ -262,11 +255,6 @@ | ... | @@ -262,11 +255,6 @@ |
| 262 | 255 | ||
| 263 | <!-- Hero --> | 256 | <!-- Hero --> |
| 264 | <section class="hero"> | 257 | <section class="hero"> |
| 265 | - <div | ||
| 266 | - class="mouse-glow" | ||
| 267 | - style="background: radial-gradient(800px circle at {mousePos.x}px {mousePos.y}px, rgba(201, 167, 81, 0.03), transparent 60%)" | ||
| 268 | - ></div> | ||
| 269 | - | ||
| 270 | <div class="hero-content"> | 258 | <div class="hero-content"> |
| 271 | <!-- Main title --> | 259 | <!-- Main title --> |
| 272 | <h1 class="hero-title fade-in" style="animation-delay: 0.3s"> | 260 | <h1 class="hero-title fade-in" style="animation-delay: 0.3s"> |
| ... | @@ -276,11 +264,12 @@ | ... | @@ -276,11 +264,12 @@ |
| 276 | <!-- Subtitle - the scale --> | 264 | <!-- Subtitle - the scale --> |
| 277 | <p class="hero-subtitle fade-in" style="animation-delay: 0.5s"> | 265 | <p class="hero-subtitle fade-in" style="animation-delay: 0.5s"> |
| 278 | Todos los ingresos y gastos del Estado boliviano.<br /> | 266 | Todos los ingresos y gastos del Estado boliviano.<br /> |
| 279 | - <span class="subtitle-stats">600+ entidades · 500+ rubros · 21 años · 9M de registros · actualización diaria</span> | 267 | + <span class="subtitle-stats">21 años · actualización diaria · 600+ entidades · 9M de registros</span> |
| 280 | </p> | 268 | </p> |
| 281 | 269 | ||
| 282 | <!-- Search - main CTA --> | 270 | <!-- Search - main CTA --> |
| 283 | <div class="search-wrapper fade-in" style="animation-delay: 0.6s"> | 271 | <div class="search-wrapper fade-in" style="animation-delay: 0.6s"> |
| 272 | + <p class="search-instruction">Busca cualquier entidad pública o tipo de gasto</p> | ||
| 284 | <div | 273 | <div |
| 285 | class="search-bar" | 274 | class="search-bar" |
| 286 | class:search-focused={searchFocused} | 275 | class:search-focused={searchFocused} |
| ... | @@ -352,9 +341,12 @@ | ... | @@ -352,9 +341,12 @@ |
| 352 | {/if} | 341 | {/if} |
| 353 | 342 | ||
| 354 | {#if !searchFocused || ($results.length === 0 && $query.length < 2)} | 343 | {#if !searchFocused || ($results.length === 0 && $query.length < 2)} |
| 344 | + <div class="browse-section"> | ||
| 345 | + <span class="browse-hint">¿No sabes qué buscar?</span> | ||
| 355 | <a href="/clasificadores" class="browse-link"> | 346 | <a href="/clasificadores" class="browse-link"> |
| 356 | - o explora por clasificadores <span class="browse-arrow">→</span> | 347 | + Explora cómo Bolivia organiza sus ingresos y gastos <span class="browse-arrow">→</span> |
| 357 | </a> | 348 | </a> |
| 349 | + </div> | ||
| 358 | {/if} | 350 | {/if} |
| 359 | </div> | 351 | </div> |
| 360 | 352 | ||
| ... | @@ -448,7 +440,6 @@ | ... | @@ -448,7 +440,6 @@ |
| 448 | .page::-webkit-scrollbar-track { background: var(--theme-body); } | 440 | .page::-webkit-scrollbar-track { background: var(--theme-body); } |
| 449 | .page::-webkit-scrollbar-thumb { background: var(--theme-texto); border-radius: 3px; } | 441 | .page::-webkit-scrollbar-thumb { background: var(--theme-texto); border-radius: 3px; } |
| 450 | 442 | ||
| 451 | - @keyframes grain { 0%,100%{transform:translate(0,0)}10%{transform:translate(-2%,-3%)}30%{transform:translate(-1%,3%)}50%{transform:translate(-3%,1%)}70%{transform:translate(-2%,2%)}90%{transform:translate(-1%,2%)} } | ||
| 452 | @keyframes fadeInUp { from{opacity:0;transform:translateY(30px)}to{opacity:1;transform:translateY(0)} } | 443 | @keyframes fadeInUp { from{opacity:0;transform:translateY(30px)}to{opacity:1;transform:translateY(0)} } |
| 453 | 444 | ||
| 454 | .page { | 445 | .page { |
| ... | @@ -460,15 +451,6 @@ | ... | @@ -460,15 +451,6 @@ |
| 460 | transition: background-color 0.3s, color 0.3s; | 451 | transition: background-color 0.3s, color 0.3s; |
| 461 | } | 452 | } |
| 462 | 453 | ||
| 463 | - /* Grain - only visible in dark mode */ | ||
| 464 | - .grain { | ||
| 465 | - position: fixed; top: -50%; left: -50%; width: 200%; height: 200%; | ||
| 466 | - pointer-events: none; z-index: 9999; opacity: 0.03; | ||
| 467 | - background: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); | ||
| 468 | - animation: grain 0.5s steps(1) infinite; | ||
| 469 | - } | ||
| 470 | - :global(:root:not(.dark)) .grain { display: none; } | ||
| 471 | - | ||
| 472 | /* Header with MEFP logo */ | 454 | /* Header with MEFP logo */ |
| 473 | .page-header { | 455 | .page-header { |
| 474 | position: fixed; top: 0; left: 0; right: 0; z-index: 100; | 456 | position: fixed; top: 0; left: 0; right: 0; z-index: 100; |
| ... | @@ -495,11 +477,6 @@ | ... | @@ -495,11 +477,6 @@ |
| 495 | min-height: 100vh; display: flex; flex-direction: column; justify-content: center; | 477 | min-height: 100vh; display: flex; flex-direction: column; justify-content: center; |
| 496 | padding: 80px 40px 60px; position: relative; max-width: 1200px; margin: 0 auto; | 478 | padding: 80px 40px 60px; position: relative; max-width: 1200px; margin: 0 auto; |
| 497 | } | 479 | } |
| 498 | - .mouse-glow { | ||
| 499 | - position: fixed; top: 0; left: 0; right: 0; bottom: 0; | ||
| 500 | - pointer-events: none; z-index: 0; transition: background 0.6s; | ||
| 501 | - } | ||
| 502 | - :global(:root:not(.dark)) .mouse-glow { display: none; } | ||
| 503 | .hero-content { | 480 | .hero-content { |
| 504 | position: relative; z-index: 1; | 481 | position: relative; z-index: 1; |
| 505 | display: flex; flex-direction: column; align-items: center; | 482 | display: flex; flex-direction: column; align-items: center; |
| ... | @@ -600,11 +577,24 @@ | ... | @@ -600,11 +577,24 @@ |
| 600 | width: 1px; height: 36px; background: var(--theme-borde); | 577 | width: 1px; height: 36px; background: var(--theme-borde); |
| 601 | } | 578 | } |
| 602 | 579 | ||
| 580 | + .browse-section { | ||
| 581 | + display: flex; | ||
| 582 | + flex-direction: column; | ||
| 583 | + align-items: center; | ||
| 584 | + margin-top: 20px; | ||
| 585 | + gap: 6px; | ||
| 586 | + } | ||
| 587 | + .browse-hint { | ||
| 588 | + font-family: 'DM Sans', sans-serif; | ||
| 589 | + font-size: 12px; | ||
| 590 | + color: var(--theme-texto); | ||
| 591 | + opacity: 0.7; | ||
| 592 | + } | ||
| 603 | .browse-link { | 593 | .browse-link { |
| 604 | display: block; | 594 | display: block; |
| 605 | - margin-top: 14px; | ||
| 606 | font-family: 'DM Sans', sans-serif; | 595 | font-family: 'DM Sans', sans-serif; |
| 607 | - font-size: 12px; color: var(--theme-texto); | 596 | + font-size: 13px; |
| 597 | + color: var(--theme-texto); | ||
| 608 | text-decoration: none; | 598 | text-decoration: none; |
| 609 | text-align: center; | 599 | text-align: center; |
| 610 | transition: color 0.2s; | 600 | transition: color 0.2s; |
| ... | @@ -618,6 +608,13 @@ | ... | @@ -618,6 +608,13 @@ |
| 618 | 608 | ||
| 619 | /* Search - main CTA */ | 609 | /* Search - main CTA */ |
| 620 | .search-wrapper { margin-top: 28px; width: 100%; max-width: 520px; position: relative; z-index: 60; } | 610 | .search-wrapper { margin-top: 28px; width: 100%; max-width: 520px; position: relative; z-index: 60; } |
| 611 | + .search-instruction { | ||
| 612 | + font-family: 'DM Sans', sans-serif; | ||
| 613 | + font-size: 15px; | ||
| 614 | + color: var(--theme-titulo); | ||
| 615 | + margin-bottom: 12px; | ||
| 616 | + text-align: center; | ||
| 617 | + } | ||
| 621 | .search-bar { | 618 | .search-bar { |
| 622 | display: flex; align-items: center; | 619 | display: flex; align-items: center; |
| 623 | background: var(--theme-surface); border: 1px solid var(--theme-borde); | 620 | background: var(--theme-surface); border: 1px solid var(--theme-borde); | ... | ... |
| 1 | <script> | 1 | <script> |
| 2 | - const clasificadores = [ | 2 | + import { onMount } from 'svelte'; |
| 3 | - { | 3 | + import { fade, scale } from 'svelte/transition'; |
| 4 | - slug: "institucional", | 4 | + import { cubicOut } from 'svelte/easing'; |
| 5 | - ruta: "/clasificadores/institucional", | 5 | + |
| 6 | - numero: "01", | 6 | + let hovered = null; |
| 7 | - nombre: "Institucional", | 7 | + let activeModal = null; |
| 8 | - titulo: "Quién", | 8 | + let mounted = false; |
| 9 | - desc: "Las instituciones que gastan y administran recursos públicos", | 9 | + |
| 10 | - ejemplo: "Ministerios, municipios, YPFB…" | 10 | + onMount(() => { setTimeout(() => { mounted = true; }, 80); }); |
| 11 | + | ||
| 12 | + const MODALS = { | ||
| 13 | + quien: { | ||
| 14 | + title: '¿Quién gasta o recibe?', tech: 'CLASIFICADOR INSTITUCIONAL', | ||
| 15 | + everyday: 'Es como preguntar "¿quién en la familia maneja plata?". Papá tiene su sueldo, mamá maneja el negocio, el abuelo cobra su renta. Cada uno es una "entidad" distinta con su propio presupuesto.', | ||
| 16 | + budget: 'El clasificador institucional cataloga a todas las entidades del sector público: ministerios, gobernaciones, municipios, empresas públicas, universidades. Cada una tiene un código único y un presupuesto asignado.', | ||
| 17 | + example: 'Ejemplo: El Ministerio de Educación (código 040) y el Gobierno Autónomo de La Paz (código 126) son entidades distintas, cada una con sus propios ingresos y gastos.', | ||
| 18 | + color: 'var(--theme-titulo)', | ||
| 11 | }, | 19 | }, |
| 12 | - { | 20 | + rubros: { |
| 13 | - slug: "objeto-gasto", | 21 | + title: '¿Qué tipo de ingreso es?', tech: 'CLASIFICADOR DE RECURSOS POR RUBROS', |
| 14 | - ruta: "/clasificadores/objeto-gasto", | 22 | + everyday: 'María gana un sueldo de su trabajo, cobra alquiler de un cuartito, y un amigo le prestó plata. Son tres tipos distintos de ingreso: laboral, patrimonial, y crédito.', |
| 15 | - numero: "02", | 23 | + budget: 'En el presupuesto, el Estado recibe ingresos tributarios (impuestos = el "sueldo"), regalías por explotación de recursos (= el "alquiler"), y créditos internacionales (= el "préstamo"). El rubro te dice qué naturaleza tiene cada ingreso.', |
| 16 | - nombre: "Objeto del Gasto", | 24 | + example: 'Ejemplo: Los Bs 2.400M que Bolivia recibe por IDH son un rubro distinto a los Bs 800M de crédito del BID, aunque ambos financien al mismo ministerio.', |
| 17 | - titulo: "En qué", | 25 | + color: 'var(--theme-accent)', |
| 18 | - desc: "La naturaleza del gasto: sueldos, obras, compras", | ||
| 19 | - ejemplo: "Servicios personales, bienes, inversión…" | ||
| 20 | }, | 26 | }, |
| 21 | - { | 27 | + fuentes: { |
| 22 | - slug: "rubros", | 28 | + title: '¿Quién pone la plata?', tech: 'CLASIFICADOR DE FUENTES DE FINANCIAMIENTO', |
| 23 | - ruta: "/clasificadores/rubros", | 29 | + everyday: 'María sabe que su sueldo se lo paga la empresa TechCorp, el alquiler se lo paga su inquilino Juan, y el préstamo se lo hizo el Banco Nacional. Son las fuentes: quién firma el cheque.', |
| 24 | - numero: "03", | 30 | + budget: 'Las fuentes son los bolsillos específicos: el TGN (Tesoro General de la Nación), recursos propios de cada entidad, crédito externo del BID o Banco Mundial, o donaciones de cooperación.', |
| 25 | - nombre: "Recursos por Rubros", | 31 | + example: 'Ejemplo: Un crédito del BID y uno del Banco Mundial son el mismo rubro (crédito externo) pero distinta fuente. La fuente dice quién exactamente pone la plata.', |
| 26 | - titulo: "Cuánto ingresa", | 32 | + color: 'var(--theme-accent)', |
| 27 | - desc: "Los ingresos del Estado por tipo de recaudación", | ||
| 28 | - ejemplo: "Impuestos, regalías, tasas…" | ||
| 29 | }, | 33 | }, |
| 30 | - { | 34 | + objeto: { |
| 31 | - slug: "funcional", | 35 | + title: '¿En qué se gasta concretamente?', tech: 'CLASIFICADOR POR OBJETO DEL GASTO', |
| 32 | - ruta: "/clasificadores/funcional", | 36 | + everyday: 'Cuando María gasta, compra cosas concretas: arroz, paga la luz, le paga al albañil. Cada compra es un "objeto" distinto — algo tangible que se puede listar.', |
| 33 | - numero: "04", | 37 | + budget: 'El Objeto del Gasto es exactamente eso: sueldos, consultorías, materiales de escritorio, combustible, medicamentos. Es la lista del supermercado del Estado.', |
| 34 | - nombre: "Finalidad y Función", | 38 | + example: 'Ejemplo: El grupo 10000 "Servicios Personales" incluye sueldos y aguinaldos. El 20000 "Servicios No Personales" incluye consultorías y alquileres.', |
| 35 | - titulo: "Para qué", | 39 | + color: '#4A8B6E', |
| 36 | - desc: "El propósito del gasto según funciones del Estado", | ||
| 37 | - ejemplo: "Salud, Educación, Defensa…" | ||
| 38 | }, | 40 | }, |
| 39 | - { | 41 | + sector: { |
| 40 | - slug: "fuente", | 42 | + title: '¿A qué sector de la economía va?', tech: 'CLASIFICADOR POR SECTORES ECONÓMICOS', |
| 41 | - ruta: "/clasificadores/fuente", | 43 | + everyday: 'Si María contrata un albañil, esa plata va al sector construcción. Si compra medicina, va al sector salud. No importa qué compró — importa qué parte de la economía mueve.', |
| 42 | - numero: "05", | 44 | + budget: 'Este clasificador mide el impacto económico: cuánto va a infraestructura, cuánto a servicios, cuánto a industria. Permite evaluar cómo las decisiones fiscales afectan a cada sector.', |
| 43 | - nombre: "Fuente de Financiamiento", | 45 | + example: 'Ejemplo: Un hospital nuevo es "construcción" en Objeto del Gasto pero "salud" en Sector Económico. Misma plata, dos lentes distintos.', |
| 44 | - titulo: "De dónde", | 46 | + color: '#4A8B6E', |
| 45 | - desc: "El origen del dinero que financia cada gasto", | ||
| 46 | - ejemplo: "Tesoro, IDH, créditos externos…" | ||
| 47 | }, | 47 | }, |
| 48 | - { | 48 | + finalidad: { |
| 49 | - slug: "organismo", | 49 | + title: '¿Para qué se gasta?', tech: 'CLASIFICADOR POR FINALIDAD Y FUNCIÓN', |
| 50 | - ruta: "/clasificadores/organismo", | 50 | + everyday: 'María compró arroz (objeto), pero ¿para qué? Para alimentar a su familia. Compró cemento: para tener casa. El "para qué" es el objetivo final, no el insumo.', |
| 51 | - numero: "06", | 51 | + budget: 'Este clasificador agrupa por propósito: educación, defensa, salud, protección social. No importa si se gastó en sueldos o en cemento — lo que importa es el fin.', |
| 52 | - nombre: "Organismo Financiador", | 52 | + example: 'Ejemplo: El sueldo de un profesor y la construcción de una escuela son objetos de gasto distintos, pero ambos tienen la misma finalidad: Educación.', |
| 53 | - titulo: "Quién financia", | 53 | + color: '#4A8B6E', |
| 54 | - desc: "Los organismos detrás de los créditos y donaciones", | ||
| 55 | - ejemplo: "BID, CAF, Banco Mundial…" | ||
| 56 | }, | 54 | }, |
| 57 | - { | 55 | + donde: { |
| 58 | - slug: "sectores", | 56 | + title: '¿Dónde se gasta?', tech: 'CLASIFICADOR GEOGRÁFICO', |
| 59 | - ruta: "/clasificadores/sectores", | 57 | + everyday: 'María manda plata a su mamá en Cochabamba y paga el alquiler en La Paz. No es lo mismo gastar en la ciudad que en el área rural, en Santa Cruz que en Potosí.', |
| 60 | - numero: "07", | 58 | + budget: 'La dimensión geográfica permite ver cómo se distribuyen los recursos en el territorio: por departamento, municipio o región. Revela las prioridades territoriales del Estado.', |
| 61 | - nombre: "Sector Económico", | 59 | + example: 'Ejemplo: Del presupuesto de Educación, ¿cuánto va a La Paz vs. Beni? Este clasificador responde esa pregunta.', |
| 62 | - titulo: "Qué sector", | 60 | + color: '#4A8B6E', |
| 63 | - desc: "Los sectores productivos que activa el gasto", | ||
| 64 | - ejemplo: "Hidrocarburos, agro, transporte…" | ||
| 65 | }, | 61 | }, |
| 62 | + }; | ||
| 63 | + | ||
| 64 | + const WIDGETS_INGRESOS = [ | ||
| 65 | + { id: 'rubros', q: '¿Qué tipo de ingreso?', d: 'Impuestos, regalías, ventas, créditos — la naturaleza de cada ingreso', t: 'Recursos por rubros', href: '/clasificadores/rubros' }, | ||
| 66 | + { id: 'fuentes', q: '¿Quién pone la plata?', d: 'TGN, crédito externo, donaciones — de qué bolsillo sale', t: 'Fuentes de financiamiento', href: '/clasificadores/fuente' }, | ||
| 66 | ]; | 67 | ]; |
| 67 | 68 | ||
| 68 | - </script> | 69 | + const WIDGETS_EN_QUE = [ |
| 70 | + { id: 'objeto', q: 'Objeto del gasto', d: 'Sueldos, consultorías, materiales — la lista concreta', t: 'Objeto del gasto', href: '/clasificadores/objeto-gasto' }, | ||
| 71 | + { id: 'sector', q: 'Sector económico', d: 'Industria, construcción, servicios — a qué sector va', t: 'Sectores económicos', href: '/clasificadores/sectores' }, | ||
| 72 | + ]; | ||
| 73 | + | ||
| 74 | + const WIDGETS_BOTTOM = [ | ||
| 75 | + { id: 'finalidad', q: '¿Para qué se gasta?', d: 'Educación, salud, defensa — el objetivo final', t: 'Finalidad y función', href: '/clasificadores/funcional' }, | ||
| 76 | + { id: 'donde', q: '¿Dónde se gasta?', d: 'Departamentos, municipios — distribución territorial', t: 'Geográfico', href: '/clasificadores/geografico' }, | ||
| 77 | + ]; | ||
| 78 | + | ||
| 79 | + $: modal = activeModal ? MODALS[activeModal] : null; | ||
| 80 | + | ||
| 81 | + function openModal(e, id) { | ||
| 82 | + e.preventDefault(); | ||
| 83 | + e.stopPropagation(); | ||
| 84 | + activeModal = id; | ||
| 85 | + } | ||
| 86 | + function closeModal() { activeModal = null; } | ||
| 87 | +</script> | ||
| 69 | 88 | ||
| 70 | <svelte:head> | 89 | <svelte:head> |
| 71 | - <title>Explorar los datos — Presupuesto Bolivia</title> | 90 | + <title>Clasificadores — Presupuesto Bolivia</title> |
| 72 | <link rel="preconnect" href="https://fonts.googleapis.com" /> | 91 | <link rel="preconnect" href="https://fonts.googleapis.com" /> |
| 73 | <link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@400;500&family=Instrument+Sans:wght@400;500;600&display=swap" rel="stylesheet" /> | 92 | <link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@400;500&family=Instrument+Sans:wght@400;500;600&display=swap" rel="stylesheet" /> |
| 74 | </svelte:head> | 93 | </svelte:head> |
| 75 | 94 | ||
| 76 | -<main class="clasificadores-page"> | 95 | +<div class="page" class:mounted> |
| 77 | - <!-- Header de la página --> | 96 | + <!-- Header --> |
| 78 | <header class="page-header"> | 97 | <header class="page-header"> |
| 79 | - <!-- Breadcrumb --> | ||
| 80 | <nav class="breadcrumb"> | 98 | <nav class="breadcrumb"> |
| 81 | - <a href="/" class="breadcrumb-link">← Inicio</a> | 99 | + <a href="/" class="breadcrumb-link sm-hidden">← Inicio</a> |
| 100 | + <div class="breadcrumb-full"> | ||
| 101 | + <a href="/">Inicio</a> | ||
| 102 | + <span class="breadcrumb-sep">/</span> | ||
| 103 | + <span class="breadcrumb-current">Clasificadores</span> | ||
| 104 | + </div> | ||
| 82 | </nav> | 105 | </nav> |
| 83 | - <div class="header-content"> | 106 | + |
| 84 | - <h1>Formas de leer el presupuesto</h1> | 107 | + <div class="header-content anim" style="--d:0ms"> |
| 108 | + <h1>Elige cómo explorar el presupuesto</h1> | ||
| 85 | <p class="subtitulo"> | 109 | <p class="subtitulo"> |
| 86 | - Cada ingreso y cada gasto del Estado se registra bajo distintas clasificaciones. | 110 | + Cada clasificador es una forma distinta de ver la misma información. |
| 87 | - Aquí puedes explorar los datos según cada una de ellas. | 111 | + Haz clic en <span class="help-inline">?</span> para entender qué es cada uno. |
| 88 | </p> | 112 | </p> |
| 89 | </div> | 113 | </div> |
| 90 | </header> | 114 | </header> |
| 91 | 115 | ||
| 92 | - <!-- Grid de clasificadores --> | 116 | + <main> |
| 93 | - <section class="grid-clasificadores"> | 117 | + <!-- ═══ ¿QUIÉN? ═══ --> |
| 94 | - {#each clasificadores as c} | 118 | + <div class="anim" style="--d:100ms"> |
| 95 | - <a href={c.ruta} class="card"> | 119 | + <div class="group"> |
| 96 | - <span class="card-meta"> | 120 | + <div class="group-accent" style="--c: var(--theme-titulo)"></div> |
| 97 | - <span class="card-numero">{c.numero}</span> | 121 | + <div class="group-header"> |
| 98 | - <span class="card-nombre">{c.nombre}</span> | 122 | + <div class="group-icon" style="--c: var(--theme-titulo)"> |
| 99 | - </span> | 123 | + <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg> |
| 100 | - <h2 class="card-titulo">{c.titulo}</h2> | 124 | + </div> |
| 101 | - <p class="card-desc">{c.desc}</p> | 125 | + <div> |
| 102 | - <p class="card-ejemplo">{c.ejemplo}</p> | 126 | + <div class="group-label">¿QUIÉN?</div> |
| 127 | + <div class="group-sub">La entidad que gasta o recibe</div> | ||
| 128 | + </div> | ||
| 129 | + </div> | ||
| 130 | + | ||
| 131 | + <a | ||
| 132 | + href="/clasificadores/institucional" | ||
| 133 | + class="widget-card widget-h" | ||
| 134 | + class:wh={hovered === 'quien'} | ||
| 135 | + on:mouseenter={() => hovered = 'quien'} | ||
| 136 | + on:mouseleave={() => hovered = null} | ||
| 137 | + > | ||
| 138 | + <div class="widget-h-left"> | ||
| 139 | + <div class="widget-q">Instituciones públicas</div> | ||
| 140 | + <div class="widget-desc">Ministerios, gobernaciones, municipios, empresas públicas, universidades</div> | ||
| 141 | + </div> | ||
| 142 | + <div class="widget-h-right"> | ||
| 143 | + <span class="widget-tech">INSTITUCIONAL</span> | ||
| 144 | + <button class="help-btn help-sm" on:click={(e) => openModal(e, 'quien')}>?</button> | ||
| 145 | + <span class="widget-arrow" class:arrow-show={hovered === 'quien'}>→</span> | ||
| 146 | + </div> | ||
| 147 | + </a> | ||
| 148 | + </div> | ||
| 149 | + </div> | ||
| 150 | + | ||
| 151 | + <!-- ═══ INGRESOS + GASTOS ═══ --> | ||
| 152 | + <div class="main-grid"> | ||
| 153 | + | ||
| 154 | + <!-- INGRESOS --> | ||
| 155 | + <div class="anim col-stretch" style="--d:200ms"> | ||
| 156 | + <div class="group group-stretch"> | ||
| 157 | + <div class="group-accent" style="--c: var(--theme-accent)"></div> | ||
| 158 | + <div class="group-header"> | ||
| 159 | + <div class="group-icon" style="--c: var(--theme-accent)"> | ||
| 160 | + <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 19V5M5 12l7-7 7 7"/></svg> | ||
| 161 | + </div> | ||
| 162 | + <div> | ||
| 163 | + <div class="group-label" style="color: var(--theme-accent)">INGRESOS</div> | ||
| 164 | + <div class="group-sub">¿De dónde viene la plata?</div> | ||
| 165 | + </div> | ||
| 166 | + </div> | ||
| 167 | + | ||
| 168 | + <div class="widgets-stretch"> | ||
| 169 | + {#each WIDGETS_INGRESOS as w} | ||
| 170 | + <a | ||
| 171 | + href={w.href} | ||
| 172 | + class="widget-card widget-grow" | ||
| 173 | + class:wh={hovered === w.id} | ||
| 174 | + on:mouseenter={() => hovered = w.id} | ||
| 175 | + on:mouseleave={() => hovered = null} | ||
| 176 | + > | ||
| 177 | + <div class="widget-top"> | ||
| 178 | + <h4 class="widget-q">{w.q}</h4> | ||
| 179 | + <button class="help-btn" on:click={(e) => openModal(e, w.id)}>?</button> | ||
| 180 | + </div> | ||
| 181 | + <p class="widget-desc widget-desc-grow">{w.d}</p> | ||
| 182 | + <div class="widget-footer"> | ||
| 183 | + <span class="widget-tech">{w.t}</span> | ||
| 184 | + <span class="widget-arrow" class:arrow-show={hovered === w.id}>→</span> | ||
| 185 | + </div> | ||
| 186 | + <div class="widget-glow widget-glow-accent"></div> | ||
| 103 | </a> | 187 | </a> |
| 104 | {/each} | 188 | {/each} |
| 105 | - </section> | 189 | + </div> |
| 106 | - | 190 | + </div> |
| 107 | - <!-- Footer contextual --> | 191 | + </div> |
| 108 | - <footer class="page-footer"> | 192 | + |
| 109 | - <p class="footnote"> | 193 | + <!-- GASTOS --> |
| 110 | - Fuente: SIGMA (Sistema Integrado de Gestión y Modernización Administrativa) y | 194 | + <div class="anim col-stretch" style="--d:320ms"> |
| 111 | - SIGEP (Sistema de Gestión Pública) — Ministerio de Economía y Finanzas Públicas. | 195 | + <div class="group"> |
| 112 | - Datos desde 2005. | 196 | + <div class="group-accent" style="--c: #4A8B6E"></div> |
| 113 | - </p> | 197 | + <div class="group-header"> |
| 114 | - </footer> | 198 | + <div class="group-icon" style="--c: #4A8B6E"> |
| 115 | -</main> | 199 | + <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 5v14M5 12l7 7 7-7"/></svg> |
| 200 | + </div> | ||
| 201 | + <div> | ||
| 202 | + <div class="group-label" style="color: #4A8B6E">GASTOS</div> | ||
| 203 | + <div class="group-sub">¿Cómo se gasta la plata?</div> | ||
| 204 | + </div> | ||
| 205 | + </div> | ||
| 206 | + | ||
| 207 | + <div class="group-widgets"> | ||
| 208 | + <!-- ¿En qué? sub-group --> | ||
| 209 | + <div class="sub-group"> | ||
| 210 | + <div class="sub-group-accent" style="--c: #4A8B6E"></div> | ||
| 211 | + <div class="sub-group-header"> | ||
| 212 | + <div class="sub-group-left"> | ||
| 213 | + <div class="sub-group-icon" style="--c: #4A8B6E"> | ||
| 214 | + <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg> | ||
| 215 | + </div> | ||
| 216 | + <div> | ||
| 217 | + <div class="sub-group-label">¿En qué se gasta?</div> | ||
| 218 | + <div class="sub-group-tag">DOS CLASIFICADORES</div> | ||
| 219 | + </div> | ||
| 220 | + </div> | ||
| 221 | + </div> | ||
| 222 | + | ||
| 223 | + <div class="sub-grid"> | ||
| 224 | + {#each WIDGETS_EN_QUE as w} | ||
| 225 | + <a | ||
| 226 | + href={w.href} | ||
| 227 | + class="widget-card sub-card" | ||
| 228 | + class:wh={hovered === w.id} | ||
| 229 | + on:mouseenter={() => hovered = w.id} | ||
| 230 | + on:mouseleave={() => hovered = null} | ||
| 231 | + > | ||
| 232 | + <div class="widget-top"> | ||
| 233 | + <h4 class="widget-q sub-q">{w.q}</h4> | ||
| 234 | + <button class="help-btn help-xs" on:click={(e) => openModal(e, w.id)}>?</button> | ||
| 235 | + </div> | ||
| 236 | + <p class="widget-desc sub-desc">{w.d}</p> | ||
| 237 | + <div class="widget-footer sub-footer"> | ||
| 238 | + <span class="widget-tech sub-tech">{w.t}</span> | ||
| 239 | + <span class="widget-arrow" class:arrow-show={hovered === w.id}>→</span> | ||
| 240 | + </div> | ||
| 241 | + <div class="widget-glow widget-glow-green"></div> | ||
| 242 | + </a> | ||
| 243 | + {/each} | ||
| 244 | + </div> | ||
| 245 | + </div> | ||
| 246 | + | ||
| 247 | + <!-- Bottom row --> | ||
| 248 | + <div class="bottom-grid"> | ||
| 249 | + {#each WIDGETS_BOTTOM as w} | ||
| 250 | + <a | ||
| 251 | + href={w.href} | ||
| 252 | + class="widget-card" | ||
| 253 | + class:wh={hovered === w.id} | ||
| 254 | + on:mouseenter={() => hovered = w.id} | ||
| 255 | + on:mouseleave={() => hovered = null} | ||
| 256 | + > | ||
| 257 | + <div class="widget-top"> | ||
| 258 | + <h4 class="widget-q">{w.q}</h4> | ||
| 259 | + <button class="help-btn" on:click={(e) => openModal(e, w.id)}>?</button> | ||
| 260 | + </div> | ||
| 261 | + <p class="widget-desc">{w.d}</p> | ||
| 262 | + <div class="widget-footer"> | ||
| 263 | + <span class="widget-tech">{w.t}</span> | ||
| 264 | + <span class="widget-arrow" class:arrow-show={hovered === w.id}>→</span> | ||
| 265 | + </div> | ||
| 266 | + <div class="widget-glow widget-glow-green"></div> | ||
| 267 | + </a> | ||
| 268 | + {/each} | ||
| 269 | + </div> | ||
| 270 | + </div> | ||
| 271 | + </div> | ||
| 272 | + </div> | ||
| 273 | + </div> | ||
| 274 | + </main> | ||
| 275 | +</div> | ||
| 276 | + | ||
| 277 | +<!-- ═══ MODAL ═══ --> | ||
| 278 | +{#if modal} | ||
| 279 | + <div | ||
| 280 | + class="modal-overlay" | ||
| 281 | + on:click={closeModal} | ||
| 282 | + on:keydown={(e) => e.key === 'Escape' && closeModal()} | ||
| 283 | + role="dialog" | ||
| 284 | + aria-modal="true" | ||
| 285 | + transition:fade={{ duration: 150 }} | ||
| 286 | + > | ||
| 287 | + <div | ||
| 288 | + class="modal-box" | ||
| 289 | + on:click|stopPropagation | ||
| 290 | + role="document" | ||
| 291 | + transition:scale={{ duration: 200, start: 0.95, easing: cubicOut }} | ||
| 292 | + > | ||
| 293 | + <button class="modal-close" on:click={closeModal}> | ||
| 294 | + <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6L6 18M6 6l12 12"/></svg> | ||
| 295 | + </button> | ||
| 296 | + | ||
| 297 | + <div class="modal-badge">{modal.tech}</div> | ||
| 298 | + <h2 class="modal-title">{modal.title}</h2> | ||
| 299 | + | ||
| 300 | + <div class="modal-section"> | ||
| 301 | + <div class="modal-icon">👤</div> | ||
| 302 | + <div> | ||
| 303 | + <div class="modal-label">EN LA VIDA COTIDIANA</div> | ||
| 304 | + <p class="modal-text">{modal.everyday}</p> | ||
| 305 | + </div> | ||
| 306 | + </div> | ||
| 307 | + | ||
| 308 | + <div class="modal-divider"></div> | ||
| 309 | + | ||
| 310 | + <div class="modal-section"> | ||
| 311 | + <div class="modal-icon">🏛️</div> | ||
| 312 | + <div> | ||
| 313 | + <div class="modal-label">EN EL PRESUPUESTO</div> | ||
| 314 | + <p class="modal-text">{modal.budget}</p> | ||
| 315 | + </div> | ||
| 316 | + </div> | ||
| 317 | + | ||
| 318 | + <div class="modal-example"> | ||
| 319 | + <p>{modal.example}</p> | ||
| 320 | + </div> | ||
| 321 | + </div> | ||
| 322 | + </div> | ||
| 323 | +{/if} | ||
| 324 | + | ||
| 116 | 325 | ||
| 117 | <style> | 326 | <style> |
| 118 | - .clasificadores-page { | 327 | + /* ── Base ── */ |
| 328 | + .page { | ||
| 119 | background: var(--theme-body); | 329 | background: var(--theme-body); |
| 120 | color: var(--theme-titulo); | 330 | color: var(--theme-titulo); |
| 121 | - font-family: 'Instrument Sans', sans-serif; | ||
| 122 | min-height: 100vh; | 331 | min-height: 100vh; |
| 123 | - max-width: 1000px; | 332 | + font-family: 'Instrument Sans', sans-serif; |
| 124 | - margin: 0 auto; | ||
| 125 | - padding: 0 2rem; | ||
| 126 | - display: flex; | ||
| 127 | - flex-direction: column; | ||
| 128 | - justify-content: center; | ||
| 129 | transition: background-color 0.2s, color 0.2s; | 333 | transition: background-color 0.2s, color 0.2s; |
| 130 | } | 334 | } |
| 131 | 335 | ||
| 336 | + /* ── Entrance animations ── */ | ||
| 337 | + .anim { | ||
| 338 | + opacity: 0; | ||
| 339 | + transform: translateY(24px); | ||
| 340 | + transition: all 0.7s cubic-bezier(0.16,1,0.3,1); | ||
| 341 | + transition-delay: var(--d, 0ms); | ||
| 342 | + } | ||
| 343 | + .mounted .anim { | ||
| 344 | + opacity: 1; | ||
| 345 | + transform: translateY(0); | ||
| 346 | + } | ||
| 347 | + | ||
| 132 | /* ── Header ── */ | 348 | /* ── Header ── */ |
| 133 | .page-header { | 349 | .page-header { |
| 134 | - padding: 1.25rem 0 1.5rem; | 350 | + border-bottom: 1px solid var(--theme-borde); |
| 135 | - margin-bottom: 1.25rem; | 351 | + padding: 1.5rem 2rem 2rem; |
| 352 | + max-width: 1120px; | ||
| 353 | + margin: 0 auto; | ||
| 136 | } | 354 | } |
| 137 | 355 | ||
| 138 | .breadcrumb { | 356 | .breadcrumb { |
| 139 | - margin-bottom: 0.75rem; | 357 | + margin-bottom: 1rem; |
| 358 | + font-family: 'DM Mono', monospace; | ||
| 359 | + font-size: 0.75rem; | ||
| 140 | } | 360 | } |
| 141 | 361 | ||
| 142 | .breadcrumb-link { | 362 | .breadcrumb-link { |
| 143 | - display: inline-block; | ||
| 144 | color: var(--theme-texto); | 363 | color: var(--theme-texto); |
| 145 | text-decoration: none; | 364 | text-decoration: none; |
| 146 | - font-family: 'DM Mono', monospace; | ||
| 147 | - font-size: 0.75rem; | ||
| 148 | - letter-spacing: 0.05em; | ||
| 149 | transition: color 0.2s; | 365 | transition: color 0.2s; |
| 150 | } | 366 | } |
| 151 | .breadcrumb-link:hover { | 367 | .breadcrumb-link:hover { |
| 152 | color: var(--theme-titulo); | 368 | color: var(--theme-titulo); |
| 153 | } | 369 | } |
| 154 | 370 | ||
| 155 | - .label-top { | 371 | + .sm-hidden { display: block; } |
| 156 | - display: block; | 372 | + .breadcrumb-full { display: none; } |
| 157 | - font-family: 'DM Mono', monospace; | 373 | + |
| 158 | - font-size: 0.68rem; | 374 | + @media (min-width: 640px) { |
| 159 | - letter-spacing: 0.15em; | 375 | + .sm-hidden { display: none; } |
| 160 | - text-transform: uppercase; | 376 | + .breadcrumb-full { |
| 377 | + display: flex; | ||
| 378 | + align-items: center; | ||
| 379 | + gap: 0.5rem; | ||
| 161 | color: var(--theme-texto); | 380 | color: var(--theme-texto); |
| 162 | - margin-bottom: 0.5rem; | 381 | + } |
| 382 | + .breadcrumb-full a { | ||
| 383 | + color: var(--theme-texto); | ||
| 384 | + text-decoration: none; | ||
| 385 | + transition: color 0.2s; | ||
| 386 | + } | ||
| 387 | + .breadcrumb-full a:hover { | ||
| 388 | + color: var(--theme-titulo); | ||
| 389 | + } | ||
| 390 | + .breadcrumb-sep { | ||
| 391 | + opacity: 0.5; | ||
| 392 | + } | ||
| 393 | + .breadcrumb-current { | ||
| 394 | + color: var(--theme-titulo); | ||
| 395 | + } | ||
| 163 | } | 396 | } |
| 164 | 397 | ||
| 165 | - h1 { | 398 | + .header-content h1 { |
| 166 | font-family: 'DM Serif Display', serif; | 399 | font-family: 'DM Serif Display', serif; |
| 167 | - font-size: clamp(1.8rem, 4vw, 2.5rem); | 400 | + font-size: clamp(1.75rem, 4vw, 2rem); |
| 168 | line-height: 1.1; | 401 | line-height: 1.1; |
| 169 | letter-spacing: -0.02em; | 402 | letter-spacing: -0.02em; |
| 170 | - margin-bottom: 0.75rem; | 403 | + margin-bottom: 0.5rem; |
| 171 | - color: var(--theme-titulo); | ||
| 172 | } | 404 | } |
| 173 | 405 | ||
| 174 | .subtitulo { | 406 | .subtitulo { |
| 175 | - max-width: 520px; | ||
| 176 | font-size: 0.95rem; | 407 | font-size: 0.95rem; |
| 177 | line-height: 1.5; | 408 | line-height: 1.5; |
| 178 | color: var(--theme-texto); | 409 | color: var(--theme-texto); |
| 410 | + max-width: 600px; | ||
| 179 | } | 411 | } |
| 180 | 412 | ||
| 181 | - /* ── Masonry Grid ── */ | 413 | + .help-inline { |
| 182 | - .grid-clasificadores { | 414 | + font-family: 'DM Serif Display', serif; |
| 183 | - column-count: 3; | 415 | + font-weight: 700; |
| 184 | - column-gap: 0.875rem; | 416 | + color: var(--theme-texto); |
| 185 | - margin-bottom: 1.5rem; | 417 | + border: 1px solid var(--theme-borde); |
| 418 | + border-radius: 50%; | ||
| 419 | + padding: 0 5px; | ||
| 420 | + font-size: 0.7rem; | ||
| 186 | } | 421 | } |
| 187 | 422 | ||
| 188 | - .card { | 423 | + main { |
| 189 | - display: block; | 424 | + max-width: 1120px; |
| 190 | - break-inside: avoid; | 425 | + margin: 0 auto; |
| 191 | - margin-bottom: 0.875rem; | 426 | + padding: 2rem 2rem 4rem; |
| 192 | - padding: 1.25rem 1.35rem; | 427 | + } |
| 428 | + | ||
| 429 | + /* ── Group (level 1) ── */ | ||
| 430 | + .group { | ||
| 193 | background: var(--theme-surface); | 431 | background: var(--theme-surface); |
| 194 | - border-radius: 6px; | ||
| 195 | - text-decoration: none; | ||
| 196 | - color: var(--theme-titulo); | ||
| 197 | - transition: all 0.15s ease; | ||
| 198 | - box-shadow: 0 1px 2px rgba(0,0,0,0.04); | ||
| 199 | border: 1px solid var(--theme-borde); | 432 | border: 1px solid var(--theme-borde); |
| 433 | + border-radius: 1.5rem; | ||
| 434 | + padding: 1.25rem 1.125rem 1.125rem; | ||
| 435 | + position: relative; | ||
| 436 | + overflow: hidden; | ||
| 437 | + } | ||
| 438 | + .group-stretch { | ||
| 439 | + display: flex; | ||
| 440 | + flex-direction: column; | ||
| 441 | + height: 100%; | ||
| 442 | + } | ||
| 443 | + .group-accent { | ||
| 444 | + position: absolute; | ||
| 445 | + top: 0; | ||
| 446 | + left: 0; | ||
| 447 | + right: 0; | ||
| 448 | + height: 2px; | ||
| 449 | + background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--c) 30%, transparent), transparent); | ||
| 450 | + border-radius: 1.5rem 1.5rem 0 0; | ||
| 451 | + } | ||
| 452 | + .group-header { | ||
| 453 | + display: flex; | ||
| 454 | + align-items: center; | ||
| 455 | + gap: 0.625rem; | ||
| 456 | + margin-bottom: 0.875rem; | ||
| 457 | + padding: 0 0.25rem; | ||
| 458 | + } | ||
| 459 | + .group-icon { | ||
| 460 | + width: 2rem; | ||
| 461 | + height: 2rem; | ||
| 462 | + border-radius: 0.625rem; | ||
| 463 | + background: color-mix(in srgb, var(--c) 10%, transparent); | ||
| 464 | + border: 1px solid color-mix(in srgb, var(--c) 20%, transparent); | ||
| 465 | + display: flex; | ||
| 466 | + align-items: center; | ||
| 467 | + justify-content: center; | ||
| 468 | + flex-shrink: 0; | ||
| 469 | + color: var(--c); | ||
| 470 | + } | ||
| 471 | + .group-label { | ||
| 472 | + font-family: 'DM Mono', monospace; | ||
| 473 | + font-size: 0.75rem; | ||
| 474 | + letter-spacing: 0.15em; | ||
| 475 | + font-weight: 600; | ||
| 476 | + line-height: 1; | ||
| 477 | + color: var(--theme-titulo); | ||
| 478 | + } | ||
| 479 | + .group-sub { | ||
| 480 | + font-family: 'Instrument Sans', sans-serif; | ||
| 481 | + font-size: 0.688rem; | ||
| 482 | + color: var(--theme-texto); | ||
| 483 | + margin-top: 0.25rem; | ||
| 484 | + opacity: 0.8; | ||
| 200 | } | 485 | } |
| 201 | 486 | ||
| 202 | - .card:hover { | 487 | + .group-widgets { |
| 203 | - box-shadow: 0 2px 8px rgba(0,0,0,0.08); | 488 | + display: flex; |
| 204 | - border-color: var(--theme-accent); | 489 | + flex-direction: column; |
| 490 | + gap: 0.625rem; | ||
| 491 | + } | ||
| 492 | + .widgets-stretch { | ||
| 493 | + display: flex; | ||
| 494 | + flex-direction: column; | ||
| 495 | + gap: 0.625rem; | ||
| 496 | + flex: 1; | ||
| 205 | } | 497 | } |
| 206 | 498 | ||
| 207 | - .card:nth-child(1), | 499 | + /* ── Widget card (level 2) ── */ |
| 208 | - .card:nth-child(5) { | 500 | + .widget-card { |
| 209 | - padding: 1.5rem 1.35rem; | 501 | + display: block; |
| 502 | + background: var(--theme-fill); | ||
| 503 | + border: 1px solid var(--theme-borde); | ||
| 504 | + border-radius: 1rem; | ||
| 505 | + padding: 1.375rem; | ||
| 506 | + position: relative; | ||
| 507 | + overflow: hidden; | ||
| 508 | + cursor: pointer; | ||
| 509 | + text-decoration: none; | ||
| 510 | + color: var(--theme-titulo); | ||
| 511 | + transition: all 0.4s cubic-bezier(0.16,1,0.3,1); | ||
| 512 | + } | ||
| 513 | + .wh { | ||
| 514 | + background: color-mix(in srgb, var(--theme-fill) 80%, var(--theme-titulo) 5%); | ||
| 515 | + border-color: color-mix(in srgb, var(--theme-borde) 50%, var(--theme-texto) 20%); | ||
| 516 | + transform: translateY(-1px); | ||
| 517 | + } | ||
| 518 | + .widget-grow { | ||
| 519 | + flex: 1; | ||
| 520 | + display: flex; | ||
| 521 | + flex-direction: column; | ||
| 210 | } | 522 | } |
| 211 | 523 | ||
| 212 | - .card-meta { | 524 | + /* Horizontal variant */ |
| 525 | + .widget-h { | ||
| 213 | display: flex; | 526 | display: flex; |
| 527 | + justify-content: space-between; | ||
| 214 | align-items: center; | 528 | align-items: center; |
| 215 | - gap: 0.4rem; | ||
| 216 | - margin-bottom: 0.6rem; | ||
| 217 | } | 529 | } |
| 218 | - | 530 | + .widget-h-left { flex: 1; } |
| 219 | - .card-numero { | 531 | + .widget-h-right { |
| 220 | - font-family: 'DM Mono', monospace; | 532 | + display: flex; |
| 221 | - font-size: 0.7rem; | 533 | + align-items: center; |
| 222 | - font-weight: 500; | 534 | + gap: 0.625rem; |
| 223 | - color: var(--theme-accent); | 535 | + flex-shrink: 0; |
| 224 | - letter-spacing: 0.05em; | ||
| 225 | } | 536 | } |
| 226 | 537 | ||
| 227 | - .card-nombre { | 538 | + .widget-top { |
| 539 | + display: flex; | ||
| 540 | + justify-content: space-between; | ||
| 541 | + align-items: flex-start; | ||
| 542 | + gap: 0.625rem; | ||
| 543 | + margin-bottom: 0.5rem; | ||
| 544 | + position: relative; | ||
| 545 | + z-index: 1; | ||
| 546 | + } | ||
| 547 | + .widget-q { | ||
| 548 | + font-size: 1rem; | ||
| 549 | + font-weight: 600; | ||
| 550 | + letter-spacing: -0.01em; | ||
| 551 | + line-height: 1.2; | ||
| 552 | + font-family: 'Instrument Sans', sans-serif; | ||
| 553 | + color: var(--theme-titulo); | ||
| 554 | + } | ||
| 555 | + .widget-desc { | ||
| 556 | + font-family: 'Instrument Sans', sans-serif; | ||
| 557 | + font-size: 0.75rem; | ||
| 558 | + color: var(--theme-texto); | ||
| 559 | + line-height: 1.5; | ||
| 560 | + position: relative; | ||
| 561 | + z-index: 1; | ||
| 562 | + } | ||
| 563 | + .widget-desc-grow { flex: 1; } | ||
| 564 | + .widget-footer { | ||
| 565 | + display: flex; | ||
| 566 | + justify-content: space-between; | ||
| 567 | + align-items: center; | ||
| 568 | + margin-top: 1rem; | ||
| 569 | + position: relative; | ||
| 570 | + z-index: 1; | ||
| 571 | + } | ||
| 572 | + .widget-tech { | ||
| 228 | font-family: 'DM Mono', monospace; | 573 | font-family: 'DM Mono', monospace; |
| 229 | - font-size: 0.6rem; | 574 | + font-size: 0.5rem; |
| 575 | + letter-spacing: 0.12em; | ||
| 230 | color: var(--theme-texto); | 576 | color: var(--theme-texto); |
| 231 | - letter-spacing: 0.02em; | 577 | + text-transform: uppercase; |
| 232 | - padding-left: 0.4rem; | ||
| 233 | - border-left: 1px solid var(--theme-borde); | ||
| 234 | } | 578 | } |
| 235 | - | 579 | + .widget-arrow { |
| 236 | - .card-titulo { | 580 | + font-family: 'Instrument Sans', sans-serif; |
| 237 | - font-family: 'DM Serif Display', serif; | 581 | + font-size: 0.813rem; |
| 238 | - font-size: 1.35rem; | 582 | + color: transparent; |
| 239 | - font-weight: 400; | 583 | + transform: translateX(-6px); |
| 240 | - line-height: 1.15; | 584 | + transition: all 0.35s cubic-bezier(0.16,1,0.3,1); |
| 241 | - letter-spacing: -0.015em; | 585 | + } |
| 242 | - margin-bottom: 0.35rem; | 586 | + .arrow-show { |
| 243 | color: var(--theme-titulo); | 587 | color: var(--theme-titulo); |
| 244 | - transition: color 0.15s; | 588 | + transform: translateX(0); |
| 245 | } | 589 | } |
| 246 | - | 590 | + .widget-glow { |
| 247 | - .card:hover .card-titulo { | 591 | + position: absolute; |
| 248 | - color: var(--theme-accent); | 592 | + inset: 0; |
| 593 | + pointer-events: none; | ||
| 594 | + opacity: 0; | ||
| 595 | + transition: opacity 0.4s; | ||
| 249 | } | 596 | } |
| 250 | - | 597 | + .widget-glow-accent { |
| 251 | - .card-desc { | 598 | + background: radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--theme-accent) 6%, transparent), transparent 70%); |
| 252 | - font-size: 0.78rem; | 599 | + } |
| 253 | - color: var(--theme-texto); | 600 | + .widget-glow-green { |
| 254 | - line-height: 1.45; | 601 | + background: radial-gradient(circle at 50% 50%, rgba(74,139,110,0.06), transparent 70%); |
| 255 | - margin-bottom: 0.5rem; | ||
| 256 | } | 602 | } |
| 603 | + .wh > .widget-glow, | ||
| 604 | + .wh .widget-glow { opacity: 1; } | ||
| 257 | 605 | ||
| 258 | - .card-ejemplo { | 606 | + /* ── Sub-group (¿En qué?) ── */ |
| 259 | - font-size: 0.68rem; | 607 | + .sub-group { |
| 608 | + background: color-mix(in srgb, var(--theme-surface) 60%, var(--theme-body) 40%); | ||
| 609 | + border: 1px solid var(--theme-borde); | ||
| 610 | + border-radius: 1.125rem; | ||
| 611 | + padding: 1rem 0.875rem 0.875rem; | ||
| 612 | + position: relative; | ||
| 613 | + overflow: hidden; | ||
| 614 | + } | ||
| 615 | + .sub-group-accent { | ||
| 616 | + position: absolute; | ||
| 617 | + top: 0; | ||
| 618 | + left: 15%; | ||
| 619 | + right: 15%; | ||
| 620 | + height: 1px; | ||
| 621 | + background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--c) 25%, transparent), transparent); | ||
| 622 | + } | ||
| 623 | + .sub-group-header { | ||
| 624 | + display: flex; | ||
| 625 | + align-items: center; | ||
| 626 | + justify-content: space-between; | ||
| 627 | + margin-bottom: 0.75rem; | ||
| 628 | + padding: 0 0.25rem; | ||
| 629 | + } | ||
| 630 | + .sub-group-left { | ||
| 631 | + display: flex; | ||
| 632 | + align-items: center; | ||
| 633 | + gap: 0.5rem; | ||
| 634 | + } | ||
| 635 | + .sub-group-icon { | ||
| 636 | + width: 1.5rem; | ||
| 637 | + height: 1.5rem; | ||
| 638 | + border-radius: 0.375rem; | ||
| 639 | + background: color-mix(in srgb, var(--c) 10%, transparent); | ||
| 640 | + border: 1px solid color-mix(in srgb, var(--c) 18%, transparent); | ||
| 641 | + display: flex; | ||
| 642 | + align-items: center; | ||
| 643 | + justify-content: center; | ||
| 644 | + flex-shrink: 0; | ||
| 645 | + color: var(--c); | ||
| 646 | + } | ||
| 647 | + .sub-group-label { | ||
| 648 | + font-family: 'Instrument Sans', sans-serif; | ||
| 649 | + font-size: 0.75rem; | ||
| 650 | + font-weight: 600; | ||
| 260 | color: var(--theme-texto); | 651 | color: var(--theme-texto); |
| 652 | + line-height: 1.1; | ||
| 653 | + } | ||
| 654 | + .sub-group-tag { | ||
| 655 | + font-family: 'DM Mono', monospace; | ||
| 656 | + font-size: 0.438rem; | ||
| 657 | + letter-spacing: 0.1em; | ||
| 658 | + margin-top: 0.125rem; | ||
| 659 | + color: #4A8B6E; | ||
| 261 | opacity: 0.7; | 660 | opacity: 0.7; |
| 262 | - line-height: 1.4; | ||
| 263 | } | 661 | } |
| 264 | 662 | ||
| 265 | - /* ── Footer ── */ | 663 | + .sub-grid { |
| 266 | - .page-footer { | 664 | + display: grid; |
| 267 | - padding-top: 1rem; | 665 | + grid-template-columns: 1fr 1fr; |
| 666 | + gap: 0.5rem; | ||
| 667 | + } | ||
| 668 | + | ||
| 669 | + /* ── Sub-widget (level 3) ── */ | ||
| 670 | + .sub-card { | ||
| 671 | + background: var(--theme-fill); | ||
| 672 | + border-color: var(--theme-borde); | ||
| 673 | + padding: 1rem; | ||
| 674 | + border-radius: 0.75rem; | ||
| 675 | + } | ||
| 676 | + .sub-card.wh { | ||
| 677 | + background: color-mix(in srgb, var(--theme-fill) 80%, var(--theme-titulo) 5%); | ||
| 678 | + border-color: color-mix(in srgb, var(--theme-borde) 50%, var(--theme-texto) 20%); | ||
| 679 | + } | ||
| 680 | + .sub-q { | ||
| 681 | + font-size: 0.813rem; | ||
| 682 | + font-weight: 500; | ||
| 683 | + color: var(--theme-titulo); | ||
| 684 | + } | ||
| 685 | + .sub-desc { | ||
| 686 | + font-size: 0.688rem; | ||
| 687 | + opacity: 0.9; | ||
| 688 | + } | ||
| 689 | + .sub-footer { margin-top: 0.75rem; } | ||
| 690 | + .sub-tech { font-size: 0.375rem; } | ||
| 691 | + | ||
| 692 | + .bottom-grid { | ||
| 693 | + display: grid; | ||
| 694 | + grid-template-columns: 1fr 1fr; | ||
| 695 | + gap: 0.625rem; | ||
| 696 | + } | ||
| 697 | + | ||
| 698 | + /* ── Main grid ── */ | ||
| 699 | + .main-grid { | ||
| 700 | + display: grid; | ||
| 701 | + grid-template-columns: 1fr 2fr; | ||
| 702 | + gap: 1rem; | ||
| 703 | + margin-top: 1rem; | ||
| 704 | + align-items: stretch; | ||
| 705 | + } | ||
| 706 | + .col-stretch { | ||
| 268 | display: flex; | 707 | display: flex; |
| 269 | flex-direction: column; | 708 | flex-direction: column; |
| 270 | - gap: 0.35rem; | ||
| 271 | } | 709 | } |
| 272 | 710 | ||
| 273 | - .page-footer p { | 711 | + /* ── Help button ── */ |
| 274 | - font-size: 0.75rem; | 712 | + .help-btn { |
| 713 | + width: 1.625rem; | ||
| 714 | + height: 1.625rem; | ||
| 715 | + border-radius: 50%; | ||
| 716 | + border: 1px solid var(--theme-borde); | ||
| 717 | + background: transparent; | ||
| 275 | color: var(--theme-texto); | 718 | color: var(--theme-texto); |
| 719 | + font-family: 'DM Serif Display', serif; | ||
| 720 | + font-size: 0.75rem; | ||
| 721 | + font-weight: 600; | ||
| 722 | + cursor: pointer; | ||
| 723 | + transition: all 0.3s; | ||
| 724 | + display: flex; | ||
| 725 | + align-items: center; | ||
| 726 | + justify-content: center; | ||
| 727 | + flex-shrink: 0; | ||
| 276 | } | 728 | } |
| 729 | + .help-btn:hover { | ||
| 730 | + border-color: var(--theme-accent); | ||
| 731 | + color: var(--theme-accent); | ||
| 732 | + background: color-mix(in srgb, var(--theme-accent) 8%, transparent); | ||
| 733 | + } | ||
| 734 | + .help-sm { width: 1.375rem; height: 1.375rem; font-size: 0.625rem; } | ||
| 735 | + .help-xs { width: 1.25rem; height: 1.25rem; font-size: 0.563rem; } | ||
| 277 | 736 | ||
| 278 | - .page-footer a { | 737 | + /* ── Modal ── */ |
| 738 | + .modal-overlay { | ||
| 739 | + position: fixed; | ||
| 740 | + inset: 0; | ||
| 741 | + background: rgba(0,0,0,0.6); | ||
| 742 | + backdrop-filter: blur(12px); | ||
| 743 | + display: flex; | ||
| 744 | + align-items: center; | ||
| 745 | + justify-content: center; | ||
| 746 | + z-index: 1000; | ||
| 747 | + padding: 2.5rem; | ||
| 748 | + } | ||
| 749 | + .modal-box { | ||
| 750 | + background: var(--theme-surface); | ||
| 751 | + border: 1px solid var(--theme-borde); | ||
| 752 | + border-radius: 1.5rem; | ||
| 753 | + max-width: 520px; | ||
| 754 | + width: 100%; | ||
| 755 | + padding: 2rem 2.25rem; | ||
| 756 | + position: relative; | ||
| 757 | + max-height: 85vh; | ||
| 758 | + overflow-y: auto; | ||
| 759 | + } | ||
| 760 | + .modal-close { | ||
| 761 | + position: absolute; | ||
| 762 | + top: 1rem; | ||
| 763 | + right: 1rem; | ||
| 764 | + background: transparent; | ||
| 765 | + border: 1px solid var(--theme-borde); | ||
| 766 | + border-radius: 50%; | ||
| 767 | + width: 2rem; | ||
| 768 | + height: 2rem; | ||
| 769 | + display: flex; | ||
| 770 | + align-items: center; | ||
| 771 | + justify-content: center; | ||
| 279 | color: var(--theme-texto); | 772 | color: var(--theme-texto); |
| 280 | - text-decoration: none; | 773 | + cursor: pointer; |
| 281 | - border-bottom: 1px solid var(--theme-borde); | 774 | + transition: all 0.2s; |
| 282 | - transition: border-color 0.2s, color 0.2s; | ||
| 283 | } | 775 | } |
| 284 | - .page-footer a:hover { | 776 | + .modal-close:hover { |
| 285 | - border-color: var(--theme-accent); | 777 | + border-color: var(--theme-texto); |
| 286 | color: var(--theme-titulo); | 778 | color: var(--theme-titulo); |
| 287 | } | 779 | } |
| 288 | - | 780 | + .modal-badge { |
| 289 | - .footnote { | ||
| 290 | font-family: 'DM Mono', monospace; | 781 | font-family: 'DM Mono', monospace; |
| 291 | - font-size: 0.58rem; | 782 | + font-size: 0.563rem; |
| 783 | + letter-spacing: 0.15em; | ||
| 784 | + border-radius: 0.375rem; | ||
| 785 | + padding: 0.25rem 0.563rem; | ||
| 786 | + display: inline-block; | ||
| 787 | + margin-bottom: 0.875rem; | ||
| 788 | + background: color-mix(in srgb, var(--theme-accent) 12%, transparent); | ||
| 789 | + color: var(--theme-accent); | ||
| 790 | + } | ||
| 791 | + .modal-title { | ||
| 792 | + font-family: 'DM Serif Display', serif; | ||
| 793 | + font-size: 1.5rem; | ||
| 794 | + font-weight: 400; | ||
| 795 | + letter-spacing: -0.02em; | ||
| 796 | + line-height: 1.15; | ||
| 797 | + margin-bottom: 1.5rem; | ||
| 798 | + color: var(--theme-titulo); | ||
| 799 | + } | ||
| 800 | + .modal-section { | ||
| 801 | + display: flex; | ||
| 802 | + gap: 0.875rem; | ||
| 803 | + } | ||
| 804 | + .modal-icon { | ||
| 805 | + font-size: 1.125rem; | ||
| 806 | + width: 2.25rem; | ||
| 807 | + height: 2.25rem; | ||
| 808 | + background: var(--theme-fill); | ||
| 809 | + border-radius: 0.625rem; | ||
| 810 | + display: flex; | ||
| 811 | + align-items: center; | ||
| 812 | + justify-content: center; | ||
| 813 | + flex-shrink: 0; | ||
| 814 | + border: 1px solid var(--theme-borde); | ||
| 815 | + } | ||
| 816 | + .modal-label { | ||
| 817 | + font-family: 'DM Mono', monospace; | ||
| 818 | + font-size: 0.563rem; | ||
| 819 | + letter-spacing: 0.15em; | ||
| 292 | color: var(--theme-texto); | 820 | color: var(--theme-texto); |
| 293 | - opacity: 0.7; | 821 | + margin-bottom: 0.313rem; |
| 294 | - line-height: 1.5; | 822 | + } |
| 823 | + .modal-text { | ||
| 824 | + font-family: 'Instrument Sans', sans-serif; | ||
| 825 | + font-size: 0.813rem; | ||
| 826 | + line-height: 1.65; | ||
| 827 | + color: var(--theme-texto); | ||
| 828 | + } | ||
| 829 | + .modal-divider { | ||
| 830 | + height: 1px; | ||
| 831 | + background: var(--theme-borde); | ||
| 832 | + margin: 1.125rem 0; | ||
| 833 | + margin-left: 3.125rem; | ||
| 834 | + } | ||
| 835 | + .modal-example { | ||
| 836 | + margin-top: 1.25rem; | ||
| 837 | + margin-left: 3.125rem; | ||
| 838 | + padding: 0.875rem 1.125rem; | ||
| 839 | + border-left: 3px solid var(--theme-accent); | ||
| 840 | + background: color-mix(in srgb, var(--theme-accent) 5%, transparent); | ||
| 841 | + border-radius: 0 0.625rem 0.625rem 0; | ||
| 842 | + } | ||
| 843 | + .modal-example p { | ||
| 844 | + font-family: 'Instrument Sans', sans-serif; | ||
| 845 | + font-size: 0.75rem; | ||
| 846 | + line-height: 1.6; | ||
| 847 | + color: var(--theme-texto); | ||
| 848 | + font-style: italic; | ||
| 295 | } | 849 | } |
| 296 | 850 | ||
| 851 | + /* ── Responsive ── */ | ||
| 297 | @media (max-width: 900px) { | 852 | @media (max-width: 900px) { |
| 298 | - .grid-clasificadores { | 853 | + .main-grid { |
| 299 | - column-count: 2; | 854 | + grid-template-columns: 1fr; |
| 300 | } | 855 | } |
| 301 | - .clasificadores-page { | 856 | + .page-header, |
| 302 | - justify-content: flex-start; | 857 | + main { |
| 303 | - padding-top: 1rem; | 858 | + padding-left: 1.5rem; |
| 859 | + padding-right: 1.5rem; | ||
| 304 | } | 860 | } |
| 305 | } | 861 | } |
| 306 | 862 | ||
| 307 | - @media (max-width: 540px) { | 863 | + @media (max-width: 640px) { |
| 308 | - .clasificadores-page { | 864 | + .page-header, |
| 309 | - padding: 1rem 1rem 1.5rem; | 865 | + main { |
| 310 | - justify-content: flex-start; | 866 | + padding-left: 1rem; |
| 867 | + padding-right: 1rem; | ||
| 868 | + } | ||
| 869 | + .sub-grid, | ||
| 870 | + .bottom-grid { | ||
| 871 | + grid-template-columns: 1fr; | ||
| 872 | + } | ||
| 873 | + .widget-h { | ||
| 874 | + flex-direction: column; | ||
| 875 | + align-items: flex-start; | ||
| 876 | + gap: 0.75rem; | ||
| 311 | } | 877 | } |
| 312 | - .grid-clasificadores { | 878 | + .widget-h-right { |
| 313 | - column-count: 1; | 879 | + width: 100%; |
| 880 | + justify-content: space-between; | ||
| 314 | } | 881 | } |
| 315 | - .card { | 882 | + .modal-overlay { |
| 316 | - padding: 1rem 1.15rem; | 883 | + padding: 1rem; |
| 317 | } | 884 | } |
| 318 | - .card:nth-child(1), | 885 | + .modal-box { |
| 319 | - .card:nth-child(5) { | 886 | + padding: 1.5rem; |
| 320 | - padding: 1rem 1.15rem; | ||
| 321 | } | 887 | } |
| 322 | - .card-pregunta { | 888 | + .modal-example, |
| 323 | - font-size: 1.1rem; | 889 | + .modal-divider { |
| 890 | + margin-left: 0; | ||
| 324 | } | 891 | } |
| 325 | - h1 { font-size: 1.4rem; } | ||
| 326 | } | 892 | } |
| 327 | </style> | 893 | </style> | ... | ... |
| ... | @@ -836,7 +836,7 @@ | ... | @@ -836,7 +836,7 @@ |
| 836 | <div class="min-h-screen" style="font-family: 'Instrument Sans', sans-serif; background-color: var(--theme-body); color: var(--theme-titulo); transition: background-color 0.2s, color 0.2s;"> | 836 | <div class="min-h-screen" style="font-family: 'Instrument Sans', sans-serif; background-color: var(--theme-body); color: var(--theme-titulo); transition: background-color 0.2s, color 0.2s;"> |
| 837 | <!-- Header pedagógico --> | 837 | <!-- Header pedagógico --> |
| 838 | <header class="border-b" style="border-color: var(--theme-borde); background-color: var(--theme-body);"> | 838 | <header class="border-b" style="border-color: var(--theme-borde); background-color: var(--theme-body);"> |
| 839 | - <div class="max-w-screen-xl mx-auto px-4 sm:px-6 py-6"> | 839 | + <div class="max-w-screen-xl mx-auto px-4 sm:px-6 {viewMode === 'mapa' ? 'py-3' : 'py-6'}"> |
| 840 | <!-- Breadcrumb: responsive --> | 840 | <!-- Breadcrumb: responsive --> |
| 841 | <nav class="mb-4" style="font-family: 'DM Mono', monospace; font-size: 0.75rem;"> | 841 | <nav class="mb-4" style="font-family: 'DM Mono', monospace; font-size: 0.75rem;"> |
| 842 | <!-- Móvil: solo padre --> | 842 | <!-- Móvil: solo padre --> |
| ... | @@ -855,9 +855,11 @@ | ... | @@ -855,9 +855,11 @@ |
| 855 | <p class="text-xs uppercase tracking-widest mb-2" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);"> | 855 | <p class="text-xs uppercase tracking-widest mb-2" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);"> |
| 856 | Clasificador 03 | 856 | Clasificador 03 |
| 857 | </p> | 857 | </p> |
| 858 | - <h1 class="text-3xl mb-3" style="font-family: 'DM Serif Display', serif; color: var(--theme-titulo);"> | 858 | + <h1 class="{viewMode === 'mapa' ? 'text-2xl mb-2' : 'text-3xl mb-3'}" style="font-family: 'DM Serif Display', serif; color: var(--theme-titulo);"> |
| 859 | ¿En qué se gasta? | 859 | ¿En qué se gasta? |
| 860 | </h1> | 860 | </h1> |
| 861 | + | ||
| 862 | + {#if viewMode !== 'mapa'} | ||
| 861 | <p class="leading-relaxed mb-3" style="color: var(--theme-texto);"> | 863 | <p class="leading-relaxed mb-3" style="color: var(--theme-texto);"> |
| 862 | Ordena el gasto según <strong style="color: var(--theme-titulo);">qué se compra o paga</strong>: | 864 | Ordena el gasto según <strong style="color: var(--theme-titulo);">qué se compra o paga</strong>: |
| 863 | sueldos, alquileres, combustible, medicamentos, construcciones, deudas, transferencias. | 865 | sueldos, alquileres, combustible, medicamentos, construcciones, deudas, transferencias. |
| ... | @@ -893,10 +895,11 @@ | ... | @@ -893,10 +895,11 @@ |
| 893 | <p class="sm:hidden text-sm mb-4" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);"> | 895 | <p class="sm:hidden text-sm mb-4" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);"> |
| 894 | Jerarquía: Grupo → Subgrupo → Partida → Subpartida | 896 | Jerarquía: Grupo → Subgrupo → Partida → Subpartida |
| 895 | </p> | 897 | </p> |
| 898 | + {/if} | ||
| 896 | </div> | 899 | </div> |
| 897 | 900 | ||
| 898 | <!-- Controles de visualización (fuera del max-w-3xl para usar todo el ancho) --> | 901 | <!-- Controles de visualización (fuera del max-w-3xl para usar todo el ancho) --> |
| 899 | - <div class="mt-8"> | 902 | + <div class="{viewMode === 'mapa' ? 'mt-3' : 'mt-8'}"> |
| 900 | <!-- Fila principal: Toggle + Guía + Filtros --> | 903 | <!-- Fila principal: Toggle + Guía + Filtros --> |
| 901 | <div class="flex flex-col md:flex-row gap-4 md:items-center md:justify-between"> | 904 | <div class="flex flex-col md:flex-row gap-4 md:items-center md:justify-between"> |
| 902 | <!-- Toggle + Guía inline --> | 905 | <!-- Toggle + Guía inline --> | ... | ... |
| 1 | <script> | 1 | <script> |
| 2 | import { supabase } from '$lib/supabase'; | 2 | import { supabase } from '$lib/supabase'; |
| 3 | import { onMount } from 'svelte'; | 3 | import { onMount } from 'svelte'; |
| 4 | + import { fade, scale } from 'svelte/transition'; | ||
| 5 | + import { cubicOut } from 'svelte/easing'; | ||
| 4 | 6 | ||
| 5 | let loading = $state(true); | 7 | let loading = $state(true); |
| 6 | let searchQuery = $state(''); | 8 | let searchQuery = $state(''); |
| ... | @@ -9,6 +11,7 @@ | ... | @@ -9,6 +11,7 @@ |
| 9 | let selectedTipo = $state(null); | 11 | let selectedTipo = $state(null); |
| 10 | let selectedItem = $state(null); | 12 | let selectedItem = $state(null); |
| 11 | let highlightedItem = $state(null); | 13 | let highlightedItem = $state(null); |
| 14 | + let sidebarOpen = $state(false); | ||
| 12 | 15 | ||
| 13 | onMount(async () => { | 16 | onMount(async () => { |
| 14 | const { data, error } = await supabase | 17 | const { data, error } = await supabase |
| ... | @@ -154,6 +157,7 @@ | ... | @@ -154,6 +157,7 @@ |
| 154 | selectedItem = null; | 157 | selectedItem = null; |
| 155 | searchQuery = ''; | 158 | searchQuery = ''; |
| 156 | highlightedItem = null; | 159 | highlightedItem = null; |
| 160 | + sidebarOpen = false; | ||
| 157 | } | 161 | } |
| 158 | 162 | ||
| 159 | function openDetail(item) { | 163 | function openDetail(item) { |
| ... | @@ -172,6 +176,7 @@ | ... | @@ -172,6 +176,7 @@ |
| 172 | selectedTipo = targetTipo; | 176 | selectedTipo = targetTipo; |
| 173 | highlightedItem = item.rubro; | 177 | highlightedItem = item.rubro; |
| 174 | searchQuery = ''; | 178 | searchQuery = ''; |
| 179 | + sidebarOpen = false; | ||
| 175 | 180 | ||
| 176 | setTimeout(() => { | 181 | setTimeout(() => { |
| 177 | const prefix = item.nivel === 'clase' ? 'cl' : item.nivel === 'cuenta' ? 'cu' : 'sc'; | 182 | const prefix = item.nivel === 'clase' ? 'cl' : item.nivel === 'cuenta' ? 'cu' : 'sc'; |
| ... | @@ -180,6 +185,11 @@ | ... | @@ -180,6 +185,11 @@ |
| 180 | element.scrollIntoView({ behavior: 'smooth', block: 'center' }); | 185 | element.scrollIntoView({ behavior: 'smooth', block: 'center' }); |
| 181 | } | 186 | } |
| 182 | }, 100); | 187 | }, 100); |
| 188 | + | ||
| 189 | + // Limpiar highlight después de la animación (2 pulsos de 0.8s = 1.6s + margen) | ||
| 190 | + setTimeout(() => { | ||
| 191 | + highlightedItem = null; | ||
| 192 | + }, 2000); | ||
| 183 | } | 193 | } |
| 184 | } | 194 | } |
| 185 | 195 | ||
| ... | @@ -200,85 +210,192 @@ | ... | @@ -200,85 +210,192 @@ |
| 200 | let tipoContent = $derived(selectedTipo ? getItemsForTipo(selectedTipo.rubro) : { clases: [] }); | 210 | let tipoContent = $derived(selectedTipo ? getItemsForTipo(selectedTipo.rubro) : { clases: [] }); |
| 201 | let searchResults = $derived(searchGlobal(searchQuery)); | 211 | let searchResults = $derived(searchGlobal(searchQuery)); |
| 202 | let isSearching = $derived(searchQuery.length >= 2); | 212 | let isSearching = $derived(searchQuery.length >= 2); |
| 213 | + | ||
| 214 | + // Contadores | ||
| 215 | + let totalItems = $derived(allItems.length); | ||
| 216 | + let countByNivel = $derived({ | ||
| 217 | + tipos: allItems.filter(i => i.nivel === 'tipo').length, | ||
| 218 | + clases: allItems.filter(i => i.nivel === 'clase').length, | ||
| 219 | + cuentas: allItems.filter(i => i.nivel === 'cuenta').length, | ||
| 220 | + subcuentas: allItems.filter(i => i.nivel === 'sub_cuenta').length | ||
| 221 | + }); | ||
| 203 | </script> | 222 | </script> |
| 204 | 223 | ||
| 205 | <svelte:head> | 224 | <svelte:head> |
| 206 | <title>Rubros | Presupuesto Público</title> | 225 | <title>Rubros | Presupuesto Público</title> |
| 226 | + <link rel="preconnect" href="https://fonts.googleapis.com" /> | ||
| 227 | + <link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@400;500&family=Instrument+Sans:wght@400;500;600&display=swap" rel="stylesheet" /> | ||
| 207 | </svelte:head> | 228 | </svelte:head> |
| 208 | 229 | ||
| 209 | -<div class="min-h-screen bg-white"> | 230 | +<div class="min-h-screen" style="font-family: 'Instrument Sans', sans-serif; background-color: var(--theme-body); color: var(--theme-titulo); transition: background-color 0.2s, color 0.2s;"> |
| 210 | - <!-- Header --> | 231 | + <!-- Header pedagógico --> |
| 211 | - <header class="border-b bg-slate-50"> | 232 | + <header class="border-b" style="border-color: var(--theme-borde); background-color: var(--theme-body);"> |
| 212 | - <div class="max-w-screen-2xl mx-auto px-6 py-4"> | 233 | + <div class="max-w-screen-xl mx-auto px-4 sm:px-6 py-6"> |
| 213 | - <nav class="text-sm text-slate-500 mb-2"> | 234 | + <!-- Breadcrumb: responsive --> |
| 214 | - <a href="/" class="hover:text-slate-700">Inicio</a> | 235 | + <nav class="mb-4" style="font-family: 'DM Mono', monospace; font-size: 0.75rem;"> |
| 215 | - <span class="mx-2">/</span> | 236 | + <!-- Móvil: solo padre --> |
| 216 | - <a href="/clasificadores" class="hover:text-slate-700">Clasificadores</a> | 237 | + <a href="/clasificadores" class="sm:hidden transition-colors" style="color: var(--theme-texto);"> |
| 217 | - <span class="mx-2">/</span> | 238 | + ← Clasificadores |
| 218 | - <span class="text-slate-900">Rubros</span> | 239 | + </a> |
| 240 | + <!-- Desktop: ruta completa --> | ||
| 241 | + <div class="hidden sm:flex items-center gap-2" style="color: var(--theme-texto);"> | ||
| 242 | + <a href="/" class="transition-colors hover:opacity-80">Inicio</a> | ||
| 243 | + <span style="opacity: 0.5;">/</span> | ||
| 244 | + <a href="/clasificadores" class="transition-colors hover:opacity-80">Clasificadores</a> | ||
| 245 | + </div> | ||
| 219 | </nav> | 246 | </nav> |
| 220 | - <div class="flex items-center justify-between"> | 247 | + |
| 221 | - <h1 class="text-2xl font-light text-slate-900">Clasificador por Rubros de Ingresos</h1> | 248 | + <div class="max-w-3xl"> |
| 249 | + <p class="text-xs uppercase tracking-widest mb-2" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);"> | ||
| 250 | + Clasificador 01 | ||
| 251 | + </p> | ||
| 252 | + <h1 class="text-3xl mb-3" style="font-family: 'DM Serif Display', serif; color: var(--theme-titulo);"> | ||
| 253 | + ¿De dónde vienen los ingresos? | ||
| 254 | + </h1> | ||
| 255 | + | ||
| 256 | + <p class="leading-relaxed mb-3" style="color: var(--theme-texto);"> | ||
| 257 | + Organiza los ingresos del Estado según su <strong style="color: var(--theme-titulo);">origen económico</strong>: | ||
| 258 | + impuestos, ventas de bienes, regalías, transferencias, créditos. | ||
| 259 | + Es la forma de entender cómo se financia el presupuesto público. | ||
| 260 | + </p> | ||
| 261 | + | ||
| 262 | + {#if totalItems > 0} | ||
| 263 | + <p class="text-sm mb-5" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);"> | ||
| 264 | + <span style="color: var(--theme-titulo); font-weight: 500;">{totalItems}</span> categorías de ingreso: | ||
| 265 | + {countByNivel.tipos} tipos, {countByNivel.clases} clases, {countByNivel.cuentas} cuentas, {countByNivel.subcuentas} subcuentas | ||
| 266 | + </p> | ||
| 267 | + {/if} | ||
| 268 | + | ||
| 269 | + <!-- Jerarquía --> | ||
| 270 | + <div class="hidden sm:flex flex-wrap items-center gap-2 sm:gap-3 text-xs mb-6" style="font-family: 'DM Mono', monospace;"> | ||
| 271 | + <span class="px-2.5 py-1 rounded-full font-medium" style="background-color: var(--theme-accent); color: var(--theme-body); opacity: 0.9;"> | ||
| 272 | + Tipo | ||
| 273 | + </span> | ||
| 274 | + <span style="color: var(--theme-texto);">→</span> | ||
| 275 | + <span class="px-2.5 py-1 rounded-full" style="background-color: var(--theme-fill); color: var(--theme-titulo); border: 1px solid var(--theme-borde);"> | ||
| 276 | + Clase | ||
| 277 | + </span> | ||
| 278 | + <span style="color: var(--theme-texto);">→</span> | ||
| 279 | + <span class="px-2.5 py-1 rounded-full" style="background-color: var(--theme-fill); color: var(--theme-titulo); border: 1px solid var(--theme-borde);"> | ||
| 280 | + Cuenta | ||
| 281 | + </span> | ||
| 282 | + <span style="color: var(--theme-texto);">→</span> | ||
| 283 | + <span class="px-2.5 py-1 rounded-full" style="background-color: var(--theme-fill); color: var(--theme-titulo); border: 1px solid var(--theme-borde);"> | ||
| 284 | + Subcuenta | ||
| 285 | + </span> | ||
| 286 | + </div> | ||
| 287 | + <!-- Versión móvil simplificada --> | ||
| 288 | + <p class="sm:hidden text-sm mb-4" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);"> | ||
| 289 | + Jerarquía: Tipo → Clase → Cuenta → Subcuenta | ||
| 290 | + </p> | ||
| 222 | </div> | 291 | </div> |
| 223 | </div> | 292 | </div> |
| 224 | </header> | 293 | </header> |
| 225 | 294 | ||
| 226 | {#if loading} | 295 | {#if loading} |
| 227 | <div class="flex items-center justify-center py-20"> | 296 | <div class="flex items-center justify-center py-20"> |
| 228 | - <p class="text-slate-500">Cargando clasificador...</p> | 297 | + <p style="color: var(--theme-texto);">Cargando clasificador...</p> |
| 229 | </div> | 298 | </div> |
| 230 | {:else} | 299 | {:else} |
| 231 | - <div class="max-w-screen-2xl mx-auto flex"> | 300 | + <!-- Botón móvil para abrir sidebar --> |
| 301 | + <div class="lg:hidden px-4 py-3 border-b" style="border-color: var(--theme-borde); background-color: var(--theme-fill);"> | ||
| 302 | + <button | ||
| 303 | + onclick={() => sidebarOpen = !sidebarOpen} | ||
| 304 | + class="flex items-center gap-2 text-sm" | ||
| 305 | + style="color: var(--theme-texto);" | ||
| 306 | + > | ||
| 307 | + <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | ||
| 308 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" /> | ||
| 309 | + </svg> | ||
| 310 | + <span class="font-medium">{selectedTipo ? getDescripcion(selectedTipo) : 'Seleccionar tipo'}</span> | ||
| 311 | + <svg class="w-4 h-4 ml-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | ||
| 312 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> | ||
| 313 | + </svg> | ||
| 314 | + </button> | ||
| 315 | + </div> | ||
| 316 | + | ||
| 317 | + <div class="max-w-screen-xl mx-auto flex px-4"> | ||
| 232 | <!-- Sidebar izquierda: Tipos --> | 318 | <!-- Sidebar izquierda: Tipos --> |
| 233 | - <aside class="w-64 flex-shrink-0 border-r bg-slate-50/50"> | 319 | + <!-- En móvil: overlay, en desktop: sidebar fijo --> |
| 234 | - <div class="sticky top-0 h-screen overflow-y-auto p-4"> | 320 | + {#if sidebarOpen} |
| 321 | + <div | ||
| 322 | + transition:fade={{ duration: 150 }} | ||
| 323 | + class="fixed inset-0 bg-black/30 z-40 lg:hidden" | ||
| 324 | + onclick={() => sidebarOpen = false} | ||
| 325 | + ></div> | ||
| 326 | + {/if} | ||
| 327 | + <aside class=" | ||
| 328 | + {sidebarOpen ? 'translate-x-0' : '-translate-x-full'} | ||
| 329 | + lg:translate-x-0 | ||
| 330 | + fixed lg:relative | ||
| 331 | + inset-y-0 left-0 | ||
| 332 | + w-72 lg:w-64 | ||
| 333 | + z-50 lg:z-auto | ||
| 334 | + transition-transform duration-200 ease-in-out | ||
| 335 | + lg:flex-shrink-0 | ||
| 336 | + shadow-xl lg:shadow-none | ||
| 337 | + sidebar-left | ||
| 338 | + "> | ||
| 339 | + <div class="h-full lg:h-screen lg:sticky lg:top-0 overflow-y-auto py-6 px-4 lg:px-0 lg:pr-6"> | ||
| 340 | + <!-- Cerrar en móvil --> | ||
| 341 | + <div class="flex justify-between items-center mb-4 lg:hidden"> | ||
| 342 | + <span class="text-sm font-medium" style="color: var(--theme-titulo);">Tipos de ingreso</span> | ||
| 343 | + <button onclick={() => sidebarOpen = false} style="color: var(--theme-texto);"> | ||
| 344 | + <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | ||
| 345 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> | ||
| 346 | + </svg> | ||
| 347 | + </button> | ||
| 348 | + </div> | ||
| 349 | + | ||
| 235 | <!-- Buscador --> | 350 | <!-- Buscador --> |
| 236 | <div class="mb-6"> | 351 | <div class="mb-6"> |
| 237 | <input | 352 | <input |
| 238 | type="text" | 353 | type="text" |
| 239 | bind:value={searchQuery} | 354 | bind:value={searchQuery} |
| 240 | placeholder="Buscar..." | 355 | placeholder="Buscar..." |
| 241 | - class="w-full px-3 py-2 text-sm border border-slate-200 rounded-md bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" | 356 | + class="w-full px-3 py-2 text-sm rounded-md focus:outline-none focus:ring-2" |
| 357 | + style="border: 1px solid var(--theme-borde); background-color: var(--theme-surface); color: var(--theme-titulo);" | ||
| 242 | /> | 358 | /> |
| 243 | </div> | 359 | </div> |
| 244 | 360 | ||
| 245 | <!-- Resultados de búsqueda --> | 361 | <!-- Resultados de búsqueda --> |
| 246 | {#if isSearching} | 362 | {#if isSearching} |
| 247 | <div class="mb-4"> | 363 | <div class="mb-4"> |
| 248 | - <p class="text-xs text-slate-500 uppercase tracking-wide mb-2"> | 364 | + <p class="text-xs uppercase tracking-wide mb-2" style="color: var(--theme-texto);"> |
| 249 | {searchResults.length} resultados | 365 | {searchResults.length} resultados |
| 250 | </p> | 366 | </p> |
| 251 | <div class="space-y-1"> | 367 | <div class="space-y-1"> |
| 252 | {#each searchResults as result} | 368 | {#each searchResults as result} |
| 253 | <button | 369 | <button |
| 254 | - class="w-full text-left px-2 py-2 text-sm rounded hover:bg-white hover:shadow-sm transition-all" | 370 | + class="w-full text-left px-2 py-2 text-sm rounded transition-all" |
| 371 | + style="color: var(--theme-titulo);" | ||
| 255 | onclick={() => goToSearchResult(result)} | 372 | onclick={() => goToSearchResult(result)} |
| 256 | > | 373 | > |
| 257 | - <span class="text-xs text-slate-400 block">{getNivelLabel(result.nivel)}</span> | 374 | + <span class="text-xs block" style="color: var(--theme-texto);">{getNivelLabel(result.nivel)}</span> |
| 258 | - <span class="font-mono text-xs text-blue-600">{result.rubro}</span> | 375 | + <span class="font-mono text-xs font-medium" style="color: var(--theme-accent);">{result.rubro}</span> |
| 259 | - <span class="text-slate-700 ml-1">{getDescripcion(result)}</span> | 376 | + <span class="ml-1">{getDescripcion(result)}</span> |
| 260 | </button> | 377 | </button> |
| 261 | {/each} | 378 | {/each} |
| 262 | {#if searchResults.length === 0} | 379 | {#if searchResults.length === 0} |
| 263 | - <p class="text-sm text-slate-400 px-2">Sin resultados</p> | 380 | + <p class="text-sm px-2" style="color: var(--theme-texto);">Sin resultados</p> |
| 264 | {/if} | 381 | {/if} |
| 265 | </div> | 382 | </div> |
| 266 | </div> | 383 | </div> |
| 267 | {:else} | 384 | {:else} |
| 268 | <!-- Lista de tipos --> | 385 | <!-- Lista de tipos --> |
| 269 | <nav> | 386 | <nav> |
| 270 | - <p class="text-xs text-slate-500 uppercase tracking-wide mb-3 px-2">Tipos</p> | 387 | + <p class="text-xs uppercase tracking-wide mb-3 px-2 hidden lg:block" style="color: var(--theme-texto);">Tipos</p> |
| 271 | <ul class="space-y-1"> | 388 | <ul class="space-y-1"> |
| 272 | {#each tipos as tipo} | 389 | {#each tipos as tipo} |
| 273 | <li> | 390 | <li> |
| 274 | <button | 391 | <button |
| 275 | - class="w-full text-left px-3 py-2 rounded-md text-sm transition-all | 392 | + class="w-full text-left px-3 py-2 rounded-md text-sm transition-all" |
| 276 | - {selectedTipo?.rubro === tipo.rubro | 393 | + style="{selectedTipo?.rubro === tipo.rubro |
| 277 | - ? 'bg-blue-50 text-blue-900 font-medium border-l-2 border-blue-500' | 394 | + ? `background-color: color-mix(in srgb, var(--theme-accent) 15%, transparent); color: var(--theme-titulo); font-weight: 500; border-left: 2px solid var(--theme-accent);` |
| 278 | - : 'text-slate-600 hover:bg-white hover:text-slate-900'}" | 395 | + : `color: var(--theme-texto);`}" |
| 279 | onclick={() => selectTipo(tipo)} | 396 | onclick={() => selectTipo(tipo)} |
| 280 | > | 397 | > |
| 281 | - <span class="font-mono text-xs text-slate-400 block">{tipo.rubro}</span> | 398 | + <span class="font-mono text-xs block" style="color: var(--theme-texto);">{tipo.rubro}</span> |
| 282 | {getDescripcion(tipo)} | 399 | {getDescripcion(tipo)} |
| 283 | </button> | 400 | </button> |
| 284 | </li> | 401 | </li> |
| ... | @@ -290,17 +407,20 @@ | ... | @@ -290,17 +407,20 @@ |
| 290 | </aside> | 407 | </aside> |
| 291 | 408 | ||
| 292 | <!-- Contenido principal --> | 409 | <!-- Contenido principal --> |
| 293 | - <main class="flex-1 min-w-0"> | 410 | + <main class="flex-1 min-w-0 lg:border-l xl:border-r" style="border-color: var(--theme-borde);"> |
| 294 | - <div class="px-8 py-6"> | 411 | + <div class="px-4 sm:px-6 lg:px-10 py-6 lg:py-8"> |
| 295 | {#if selectedTipo} | 412 | {#if selectedTipo} |
| 413 | + {#key selectedTipo.rubro} | ||
| 414 | + <div in:fade={{ duration: 200, delay: 50 }}> | ||
| 296 | <!-- Título del tipo --> | 415 | <!-- Título del tipo --> |
| 297 | - <div class="mb-8 pb-6 border-b"> | 416 | + <div class="mb-8 lg:mb-10 pb-6 lg:pb-8 border-b" style="border-color: var(--theme-borde);"> |
| 298 | - <p class="text-sm font-mono text-slate-400 mb-1">{selectedTipo.rubro}</p> | 417 | + <p class="text-sm font-mono mb-1" style="color: var(--theme-texto);">{selectedTipo.rubro}</p> |
| 299 | - <h2 class="text-xl font-medium text-slate-900 mb-2 flex items-center gap-3"> | 418 | + <h2 class="text-lg sm:text-xl font-medium mb-2 flex items-center gap-2 sm:gap-3 flex-wrap" style="color: var(--theme-titulo);"> |
| 300 | {getDescripcion(selectedTipo)} | 419 | {getDescripcion(selectedTipo)} |
| 301 | <a | 420 | <a |
| 302 | href="/rubro/{selectedTipo.rubro}" | 421 | href="/rubro/{selectedTipo.rubro}" |
| 303 | - class="text-slate-300 hover:text-blue-500 transition-colors" | 422 | + class="transition-colors hover:text-[var(--theme-accent)]" |
| 423 | + style="color: var(--theme-texto);" | ||
| 304 | title="Ver detalle" | 424 | title="Ver detalle" |
| 305 | > | 425 | > |
| 306 | <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | 426 | <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| ... | @@ -311,7 +431,7 @@ | ... | @@ -311,7 +431,7 @@ |
| 311 | {#if selectedTipo.descripciones} | 431 | {#if selectedTipo.descripciones} |
| 312 | {@const tipoDescs = parseDescripciones(selectedTipo.descripciones)} | 432 | {@const tipoDescs = parseDescripciones(selectedTipo.descripciones)} |
| 313 | {#if tipoDescs.length > 0} | 433 | {#if tipoDescs.length > 0} |
| 314 | - <p class="text-slate-600 leading-relaxed">{tipoDescs[0].descripcion}</p> | 434 | + <p class="leading-relaxed" style="color: var(--theme-texto);">{tipoDescs[0].descripcion}</p> |
| 315 | {#if selectedTipo.n_variaciones > 1} | 435 | {#if selectedTipo.n_variaciones > 1} |
| 316 | <button | 436 | <button |
| 317 | class="text-sm text-orange-500 hover:text-orange-700 mt-2 transition-colors underline decoration-dotted decoration-orange-300 hover:decoration-orange-500 cursor-pointer" | 437 | class="text-sm text-orange-500 hover:text-orange-700 mt-2 transition-colors underline decoration-dotted decoration-orange-300 hover:decoration-orange-500 cursor-pointer" |
| ... | @@ -325,17 +445,17 @@ | ... | @@ -325,17 +445,17 @@ |
| 325 | </div> | 445 | </div> |
| 326 | 446 | ||
| 327 | <!-- Clases --> | 447 | <!-- Clases --> |
| 328 | - <div class="space-y-8"> | 448 | + <div class="space-y-12"> |
| 329 | {#each tipoContent.clases as clase} | 449 | {#each tipoContent.clases as clase} |
| 330 | {@const claseDescs = parseDescripciones(clase.descripciones)} | 450 | {@const claseDescs = parseDescripciones(clase.descripciones)} |
| 331 | <section | 451 | <section |
| 332 | id="cl-{clase.rubro}" | 452 | id="cl-{clase.rubro}" |
| 333 | - class="scroll-mt-4 {highlightedItem === clase.rubro ? 'ring-2 ring-blue-200 rounded-lg' : ''}" | 453 | + class="scroll-mt-4 {highlightedItem === clase.rubro ? 'highlighted' : ''}" |
| 334 | > | 454 | > |
| 335 | - <div class="flex items-start gap-4 mb-3"> | 455 | + <div class="flex items-start gap-2 sm:gap-4 mb-3"> |
| 336 | - <span class="font-mono text-sm text-slate-400 pt-1">{clase.rubro}</span> | 456 | + <span class="font-mono text-xs sm:text-sm pt-1" style="color: var(--theme-texto);">{clase.rubro}</span> |
| 337 | <div class="flex-1"> | 457 | <div class="flex-1"> |
| 338 | - <h3 class="text-lg font-medium text-slate-900 flex items-center gap-2"> | 458 | + <h3 class="text-base sm:text-lg font-medium flex items-center gap-2 flex-wrap" style="color: var(--theme-titulo);"> |
| 339 | <span class="text-left">{getDescripcion(clase)}</span> | 459 | <span class="text-left">{getDescripcion(clase)}</span> |
| 340 | {#if clase.n_variaciones > 1} | 460 | {#if clase.n_variaciones > 1} |
| 341 | <button | 461 | <button |
| ... | @@ -348,7 +468,8 @@ | ... | @@ -348,7 +468,8 @@ |
| 348 | {/if} | 468 | {/if} |
| 349 | <a | 469 | <a |
| 350 | href="/rubro/{clase.rubro}" | 470 | href="/rubro/{clase.rubro}" |
| 351 | - class="text-slate-300 hover:text-blue-500 transition-colors" | 471 | + class="transition-colors hover:text-[var(--theme-accent)]" |
| 472 | + style="color: var(--theme-texto);" | ||
| 352 | title="Ver página de detalle" | 473 | title="Ver página de detalle" |
| 353 | > | 474 | > |
| 354 | <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | 475 | <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| ... | @@ -357,24 +478,24 @@ | ... | @@ -357,24 +478,24 @@ |
| 357 | </a> | 478 | </a> |
| 358 | </h3> | 479 | </h3> |
| 359 | {#if claseDescs.length > 0} | 480 | {#if claseDescs.length > 0} |
| 360 | - <p class="text-sm text-slate-600 mt-1 leading-relaxed">{claseDescs[0].descripcion}</p> | 481 | + <p class="text-sm mt-1 leading-relaxed" style="color: var(--theme-texto);">{claseDescs[0].descripcion}</p> |
| 361 | {/if} | 482 | {/if} |
| 362 | </div> | 483 | </div> |
| 363 | </div> | 484 | </div> |
| 364 | 485 | ||
| 365 | <!-- Cuentas --> | 486 | <!-- Cuentas --> |
| 366 | {#if clase.cuentas?.length > 0} | 487 | {#if clase.cuentas?.length > 0} |
| 367 | - <div class="ml-16 space-y-4 border-l-2 border-slate-100 pl-6"> | 488 | + <div class="ml-4 sm:ml-8 lg:ml-16 space-y-5 border-l pl-4 sm:pl-6 lg:pl-8" style="border-color: var(--theme-borde);"> |
| 368 | {#each clase.cuentas as cuenta} | 489 | {#each clase.cuentas as cuenta} |
| 369 | {@const cuentaDescs = parseDescripciones(cuenta.descripciones)} | 490 | {@const cuentaDescs = parseDescripciones(cuenta.descripciones)} |
| 370 | <div | 491 | <div |
| 371 | id="cu-{cuenta.rubro}" | 492 | id="cu-{cuenta.rubro}" |
| 372 | - class="scroll-mt-4 {highlightedItem === cuenta.rubro ? 'ring-2 ring-blue-200 rounded-lg p-2 -ml-2' : ''}" | 493 | + class="scroll-mt-4 {highlightedItem === cuenta.rubro ? 'highlighted-md' : ''}" |
| 373 | > | 494 | > |
| 374 | - <div class="flex items-start gap-3"> | 495 | + <div class="flex items-start gap-2 sm:gap-3"> |
| 375 | - <span class="font-mono text-xs text-slate-400 pt-0.5">{cuenta.rubro}</span> | 496 | + <span class="font-mono text-xs pt-0.5" style="color: var(--theme-texto);">{cuenta.rubro}</span> |
| 376 | <div class="flex-1"> | 497 | <div class="flex-1"> |
| 377 | - <h4 class="text-sm font-medium text-slate-800 flex items-center gap-2"> | 498 | + <h4 class="text-sm font-medium flex items-center gap-2 flex-wrap" style="color: var(--theme-titulo);"> |
| 378 | <span class="text-left">{getDescripcion(cuenta)}</span> | 499 | <span class="text-left">{getDescripcion(cuenta)}</span> |
| 379 | {#if cuenta.n_variaciones > 1} | 500 | {#if cuenta.n_variaciones > 1} |
| 380 | <button | 501 | <button |
| ... | @@ -387,7 +508,8 @@ | ... | @@ -387,7 +508,8 @@ |
| 387 | {/if} | 508 | {/if} |
| 388 | <a | 509 | <a |
| 389 | href="/rubro/{cuenta.rubro}" | 510 | href="/rubro/{cuenta.rubro}" |
| 390 | - class="text-slate-300 hover:text-blue-500 transition-colors" | 511 | + class="transition-colors hover:text-[var(--theme-accent)]" |
| 512 | + style="color: var(--theme-texto);" | ||
| 391 | title="Ver página de detalle" | 513 | title="Ver página de detalle" |
| 392 | > | 514 | > |
| 393 | <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | 515 | <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| ... | @@ -396,25 +518,25 @@ | ... | @@ -396,25 +518,25 @@ |
| 396 | </a> | 518 | </a> |
| 397 | </h4> | 519 | </h4> |
| 398 | {#if cuentaDescs.length > 0} | 520 | {#if cuentaDescs.length > 0} |
| 399 | - <p class="text-xs text-slate-500 mt-1 leading-relaxed">{cuentaDescs[0].descripcion}</p> | 521 | + <p class="text-xs mt-1 leading-relaxed" style="color: var(--theme-texto);">{cuentaDescs[0].descripcion}</p> |
| 400 | {/if} | 522 | {/if} |
| 401 | </div> | 523 | </div> |
| 402 | </div> | 524 | </div> |
| 403 | 525 | ||
| 404 | <!-- Subcuentas --> | 526 | <!-- Subcuentas --> |
| 405 | {#if cuenta.subcuentas?.length > 0} | 527 | {#if cuenta.subcuentas?.length > 0} |
| 406 | - <div class="ml-12 mt-3 space-y-2 border-l border-slate-100 pl-4"> | 528 | + <div class="ml-8 sm:ml-12 mt-3 space-y-2 border-l pl-4" style="border-color: var(--theme-borde);"> |
| 407 | {#each cuenta.subcuentas as subcuenta} | 529 | {#each cuenta.subcuentas as subcuenta} |
| 408 | {@const subcuentaDescs = parseDescripciones(subcuenta.descripciones)} | 530 | {@const subcuentaDescs = parseDescripciones(subcuenta.descripciones)} |
| 409 | <div | 531 | <div |
| 410 | id="sc-{subcuenta.rubro}" | 532 | id="sc-{subcuenta.rubro}" |
| 411 | - class="scroll-mt-4 {highlightedItem === subcuenta.rubro ? 'ring-2 ring-blue-200 rounded p-1 -ml-1' : ''}" | 533 | + class="scroll-mt-4 {highlightedItem === subcuenta.rubro ? 'highlighted-sm' : ''}" |
| 412 | > | 534 | > |
| 413 | <div class="flex items-start gap-2"> | 535 | <div class="flex items-start gap-2"> |
| 414 | - <span class="font-mono text-xs text-slate-300">{subcuenta.rubro}</span> | 536 | + <span class="font-mono text-xs" style="color: var(--theme-texto); opacity: 0.7;">{subcuenta.rubro}</span> |
| 415 | <div class="flex-1"> | 537 | <div class="flex-1"> |
| 416 | - <span class="flex items-center gap-2"> | 538 | + <span class="flex items-center gap-2 flex-wrap"> |
| 417 | - <span class="text-xs text-slate-700">{getDescripcion(subcuenta)}</span> | 539 | + <span class="text-xs" style="color: var(--theme-titulo);">{getDescripcion(subcuenta)}</span> |
| 418 | {#if subcuenta.n_variaciones > 1} | 540 | {#if subcuenta.n_variaciones > 1} |
| 419 | <button | 541 | <button |
| 420 | class="text-xs text-orange-500 hover:text-orange-700 transition-colors underline decoration-dotted decoration-orange-300 hover:decoration-orange-500 cursor-pointer" | 542 | class="text-xs text-orange-500 hover:text-orange-700 transition-colors underline decoration-dotted decoration-orange-300 hover:decoration-orange-500 cursor-pointer" |
| ... | @@ -426,7 +548,8 @@ | ... | @@ -426,7 +548,8 @@ |
| 426 | {/if} | 548 | {/if} |
| 427 | <a | 549 | <a |
| 428 | href="/rubro/{subcuenta.rubro}" | 550 | href="/rubro/{subcuenta.rubro}" |
| 429 | - class="text-slate-300 hover:text-blue-500 transition-colors" | 551 | + class="transition-colors hover:text-[var(--theme-accent)]" |
| 552 | + style="color: var(--theme-texto);" | ||
| 430 | title="Ver página de detalle" | 553 | title="Ver página de detalle" |
| 431 | > | 554 | > |
| 432 | <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | 555 | <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| ... | @@ -435,7 +558,7 @@ | ... | @@ -435,7 +558,7 @@ |
| 435 | </a> | 558 | </a> |
| 436 | </span> | 559 | </span> |
| 437 | {#if subcuentaDescs.length > 0} | 560 | {#if subcuentaDescs.length > 0} |
| 438 | - <p class="text-xs text-slate-400 mt-0.5">{subcuentaDescs[0].descripcion}</p> | 561 | + <p class="text-xs mt-0.5" style="color: var(--theme-texto); opacity: 0.8;">{subcuentaDescs[0].descripcion}</p> |
| 439 | {/if} | 562 | {/if} |
| 440 | </div> | 563 | </div> |
| 441 | </div> | 564 | </div> |
| ... | @@ -450,38 +573,42 @@ | ... | @@ -450,38 +573,42 @@ |
| 450 | </section> | 573 | </section> |
| 451 | {/each} | 574 | {/each} |
| 452 | </div> | 575 | </div> |
| 576 | + </div> | ||
| 577 | + {/key} | ||
| 453 | {/if} | 578 | {/if} |
| 454 | </div> | 579 | </div> |
| 455 | </main> | 580 | </main> |
| 456 | 581 | ||
| 457 | <!-- Sidebar derecha: En esta página --> | 582 | <!-- Sidebar derecha: En esta página --> |
| 458 | <aside class="w-56 flex-shrink-0 hidden xl:block"> | 583 | <aside class="w-56 flex-shrink-0 hidden xl:block"> |
| 459 | - <div class="sticky top-0 h-screen overflow-y-auto p-4 border-l"> | 584 | + <div class="sticky top-0 h-screen overflow-y-auto p-4 border-l" style="border-color: var(--theme-borde);"> |
| 460 | - <p class="text-xs text-slate-500 uppercase tracking-wide mb-3">En esta página</p> | 585 | + <p class="text-xs uppercase tracking-wide mb-3" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);">En esta página</p> |
| 461 | {#if selectedTipo && !isSearching} | 586 | {#if selectedTipo && !isSearching} |
| 462 | <nav class="space-y-2"> | 587 | <nav class="space-y-2"> |
| 463 | {#each tipoContent.clases as clase} | 588 | {#each tipoContent.clases as clase} |
| 464 | <div> | 589 | <div> |
| 465 | <a | 590 | <a |
| 466 | href="#cl-{clase.rubro}" | 591 | href="#cl-{clase.rubro}" |
| 467 | - class="block text-sm text-slate-600 hover:text-blue-600 truncate" | 592 | + class="block text-sm truncate transition-colors hover:text-[var(--theme-accent)]" |
| 593 | + style="color: var(--theme-texto);" | ||
| 468 | title="{getDescripcion(clase)}" | 594 | title="{getDescripcion(clase)}" |
| 469 | > | 595 | > |
| 470 | {getDescripcion(clase)} | 596 | {getDescripcion(clase)} |
| 471 | </a> | 597 | </a> |
| 472 | {#if clase.cuentas?.length > 0} | 598 | {#if clase.cuentas?.length > 0} |
| 473 | - <div class="ml-3 mt-1 space-y-1 border-l border-slate-100 pl-2"> | 599 | + <div class="ml-3 mt-1 space-y-1 border-l pl-2" style="border-color: var(--theme-borde);"> |
| 474 | {#each clase.cuentas.slice(0, 5) as cuenta} | 600 | {#each clase.cuentas.slice(0, 5) as cuenta} |
| 475 | <a | 601 | <a |
| 476 | href="#cu-{cuenta.rubro}" | 602 | href="#cu-{cuenta.rubro}" |
| 477 | - class="block text-xs text-slate-400 hover:text-blue-600 truncate" | 603 | + class="block text-xs truncate transition-colors hover:text-[var(--theme-accent)]" |
| 604 | + style="color: var(--theme-texto); opacity: 0.7;" | ||
| 478 | title="{getDescripcion(cuenta)}" | 605 | title="{getDescripcion(cuenta)}" |
| 479 | > | 606 | > |
| 480 | {getDescripcion(cuenta)} | 607 | {getDescripcion(cuenta)} |
| 481 | </a> | 608 | </a> |
| 482 | {/each} | 609 | {/each} |
| 483 | {#if clase.cuentas.length > 5} | 610 | {#if clase.cuentas.length > 5} |
| 484 | - <span class="text-xs text-slate-300">+{clase.cuentas.length - 5} más</span> | 611 | + <span class="text-xs" style="color: var(--theme-texto); opacity: 0.5;">+{clase.cuentas.length - 5} más</span> |
| 485 | {/if} | 612 | {/if} |
| 486 | </div> | 613 | </div> |
| 487 | {/if} | 614 | {/if} |
| ... | @@ -497,30 +624,37 @@ | ... | @@ -497,30 +624,37 @@ |
| 497 | 624 | ||
| 498 | <!-- Modal de detalle --> | 625 | <!-- Modal de detalle --> |
| 499 | {#if selectedItem} | 626 | {#if selectedItem} |
| 500 | - <div class="fixed inset-0 bg-black/40 flex items-center justify-center p-4 z-50" onclick={closeDetail}> | ||
| 501 | <div | 627 | <div |
| 502 | - class="bg-white rounded-xl shadow-2xl max-w-2xl w-full max-h-[85vh] overflow-hidden flex flex-col" | 628 | + transition:fade={{ duration: 150 }} |
| 629 | + class="fixed inset-0 bg-black/40 flex items-center justify-center p-4 z-50" | ||
| 630 | + onclick={closeDetail} | ||
| 631 | + > | ||
| 632 | + <div | ||
| 633 | + transition:scale={{ duration: 200, start: 0.95, easing: cubicOut }} | ||
| 634 | + class="rounded-xl shadow-2xl max-w-2xl w-full max-h-[85vh] overflow-hidden flex flex-col" | ||
| 635 | + style="background-color: var(--theme-surface);" | ||
| 503 | onclick={(e) => e.stopPropagation()} | 636 | onclick={(e) => e.stopPropagation()} |
| 504 | > | 637 | > |
| 505 | - <div class="px-6 py-4 border-b bg-slate-50 flex justify-between items-start"> | 638 | + <div class="px-6 py-4 border-b flex justify-between items-start" style="background-color: var(--theme-fill); border-color: var(--theme-borde);"> |
| 506 | <div> | 639 | <div> |
| 507 | - <p class="text-xs text-slate-500 uppercase tracking-wide">{getNivelLabel(selectedItem.nivel)}</p> | 640 | + <p class="text-xs uppercase tracking-wide font-medium" style="color: var(--theme-texto);">{getNivelLabel(selectedItem.nivel)}</p> |
| 508 | - <h3 class="text-lg font-medium text-slate-900 mt-1"> | 641 | + <h3 class="text-lg font-medium mt-1" style="color: var(--theme-titulo);"> |
| 509 | - <span class="font-mono text-slate-400">{selectedItem.rubro}</span> | 642 | + <span class="font-mono" style="color: var(--theme-texto);">{selectedItem.rubro}</span> |
| 510 | - <span class="mx-2">·</span> | 643 | + <span class="mx-2" style="color: var(--theme-texto); opacity: 0.5;">·</span> |
| 511 | {getDescripcion(selectedItem)} | 644 | {getDescripcion(selectedItem)} |
| 512 | </h3> | 645 | </h3> |
| 513 | </div> | 646 | </div> |
| 514 | <button | 647 | <button |
| 515 | onclick={closeDetail} | 648 | onclick={closeDetail} |
| 516 | - class="text-slate-400 hover:text-slate-600 text-2xl leading-none" | 649 | + class="text-2xl leading-none" |
| 650 | + style="color: var(--theme-texto);" | ||
| 517 | > | 651 | > |
| 518 | × | 652 | × |
| 519 | </button> | 653 | </button> |
| 520 | </div> | 654 | </div> |
| 521 | 655 | ||
| 522 | <div class="p-6 overflow-y-auto flex-1"> | 656 | <div class="p-6 overflow-y-auto flex-1"> |
| 523 | - <h4 class="text-sm font-medium text-slate-700 mb-4"> | 657 | + <h4 class="text-sm font-medium mb-4" style="color: var(--theme-titulo);"> |
| 524 | {#if selectedItem.n_variaciones > 1} | 658 | {#if selectedItem.n_variaciones > 1} |
| 525 | Descripciones ({selectedItem.n_variaciones} variaciones) | 659 | Descripciones ({selectedItem.n_variaciones} variaciones) |
| 526 | {:else} | 660 | {:else} |
| ... | @@ -530,15 +664,15 @@ | ... | @@ -530,15 +664,15 @@ |
| 530 | 664 | ||
| 531 | <div class="space-y-4"> | 665 | <div class="space-y-4"> |
| 532 | {#each parseDescripciones(selectedItem.descripciones) as desc, i} | 666 | {#each parseDescripciones(selectedItem.descripciones) as desc, i} |
| 533 | - <div class="border-l-2 {i === 0 ? 'border-blue-500 bg-blue-50/50' : 'border-slate-200'} pl-4 py-2 rounded-r"> | 667 | + <div class="border-l-2 pl-4 py-3 rounded-r" style="{i === 0 ? `border-color: var(--theme-accent); background-color: color-mix(in srgb, var(--theme-accent) 10%, transparent);` : `border-color: var(--theme-borde);`}"> |
| 534 | - <p class="text-xs text-slate-500 mb-2"> | 668 | + <p class="text-sm mb-2" style="color: var(--theme-texto);"> |
| 535 | {#if i === 0 && selectedItem.n_variaciones > 1} | 669 | {#if i === 0 && selectedItem.n_variaciones > 1} |
| 536 | - <span class="text-blue-600 font-medium">Vigente</span> | 670 | + <span class="font-medium" style="color: var(--theme-accent);">Vigente</span> |
| 537 | - <span class="mx-1">·</span> | 671 | + <span class="mx-1" style="opacity: 0.5;">·</span> |
| 538 | {/if} | 672 | {/if} |
| 539 | - {desc.rangos} | 673 | + <span class="font-mono">{desc.rangos}</span> |
| 540 | </p> | 674 | </p> |
| 541 | - <p class="text-sm text-slate-700 leading-relaxed">{desc.descripcion}</p> | 675 | + <p class="text-base leading-relaxed" style="color: var(--theme-titulo);">{desc.descripcion}</p> |
| 542 | </div> | 676 | </div> |
| 543 | {/each} | 677 | {/each} |
| 544 | </div> | 678 | </div> |
| ... | @@ -546,3 +680,57 @@ | ... | @@ -546,3 +680,57 @@ |
| 546 | </div> | 680 | </div> |
| 547 | </div> | 681 | </div> |
| 548 | {/if} | 682 | {/if} |
| 683 | + | ||
| 684 | +<style> | ||
| 685 | + /* Sidebar izquierdo: con fondo en móvil, transparente en desktop */ | ||
| 686 | + .sidebar-left { | ||
| 687 | + background-color: var(--theme-surface); | ||
| 688 | + } | ||
| 689 | + @media (min-width: 1024px) { | ||
| 690 | + .sidebar-left { | ||
| 691 | + background-color: transparent; | ||
| 692 | + } | ||
| 693 | + } | ||
| 694 | + | ||
| 695 | + /* Highlight pulse animation para resultados de búsqueda */ | ||
| 696 | + @keyframes highlight-pulse { | ||
| 697 | + 0%, 100% { | ||
| 698 | + box-shadow: 0 0 0 2px color-mix(in srgb, var(--theme-accent) 30%, transparent); | ||
| 699 | + } | ||
| 700 | + 50% { | ||
| 701 | + box-shadow: 0 0 0 4px color-mix(in srgb, var(--theme-accent) 50%, transparent); | ||
| 702 | + } | ||
| 703 | + } | ||
| 704 | + | ||
| 705 | + .highlighted { | ||
| 706 | + animation: highlight-pulse 0.8s ease-in-out 2; | ||
| 707 | + border-radius: 0.5rem; | ||
| 708 | + } | ||
| 709 | + | ||
| 710 | + .highlighted-sm { | ||
| 711 | + animation: highlight-pulse 0.8s ease-in-out 2; | ||
| 712 | + border-radius: 0.25rem; | ||
| 713 | + padding: 0.25rem; | ||
| 714 | + margin-left: -0.25rem; | ||
| 715 | + } | ||
| 716 | + | ||
| 717 | + .highlighted-md { | ||
| 718 | + animation: highlight-pulse 0.8s ease-in-out 2; | ||
| 719 | + border-radius: 0.5rem; | ||
| 720 | + padding: 0.5rem; | ||
| 721 | + margin-left: -0.5rem; | ||
| 722 | + } | ||
| 723 | + | ||
| 724 | + /* Transiciones estandarizadas */ | ||
| 725 | + :global(.transition-fast) { | ||
| 726 | + transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1); | ||
| 727 | + } | ||
| 728 | + | ||
| 729 | + :global(.transition-normal) { | ||
| 730 | + transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1); | ||
| 731 | + } | ||
| 732 | + | ||
| 733 | + :global(.transition-slow) { | ||
| 734 | + transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1); | ||
| 735 | + } | ||
| 736 | +</style> | ... | ... |
src/routes/test/+page.svelte
0 → 100644
| 1 | +<script> | ||
| 2 | + import { onMount } from 'svelte'; | ||
| 3 | + import { tweened } from 'svelte/motion'; | ||
| 4 | + import { cubicOut } from 'svelte/easing'; | ||
| 5 | + | ||
| 6 | + let mounted = false; | ||
| 7 | + let hoveredYear = null; | ||
| 8 | + let hoveredYearComparar = null; | ||
| 9 | + let showInfo = false; | ||
| 10 | + let vista = 'agregado'; // 'agregado' | 'comparar' | ||
| 11 | + let isDark = true; | ||
| 12 | + let entidadA = 0; | ||
| 13 | + let entidadB = 1; | ||
| 14 | + | ||
| 15 | + onMount(() => { | ||
| 16 | + setTimeout(() => { mounted = true; }, 50); | ||
| 17 | + | ||
| 18 | + // Leer estado del tema | ||
| 19 | + isDark = document.documentElement.classList.contains('dark'); | ||
| 20 | + | ||
| 21 | + // Observar cambios de tema | ||
| 22 | + const observer = new MutationObserver(() => { | ||
| 23 | + isDark = document.documentElement.classList.contains('dark'); | ||
| 24 | + }); | ||
| 25 | + observer.observe(document.documentElement, { | ||
| 26 | + attributes: true, | ||
| 27 | + attributeFilter: ['class'] | ||
| 28 | + }); | ||
| 29 | + | ||
| 30 | + return () => observer.disconnect(); | ||
| 31 | + }); | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + // Dummy data | ||
| 35 | + const objeto = { | ||
| 36 | + codigo: '10000', | ||
| 37 | + nombre: 'Servicios Personales', | ||
| 38 | + nivel: 'Grupo', | ||
| 39 | + años: '2005-2024', | ||
| 40 | + descripcion: 'Gastos por concepto de servicios prestados por el personal permanente y no permanente del sector público.', | ||
| 41 | + variaciones: [ | ||
| 42 | + { rango: '2012-2024', texto: 'Gastos por concepto de servicios prestados por el personal permanente y no permanente.' }, | ||
| 43 | + { rango: '2005-2011', texto: 'Retribuciones y complementos al personal permanente y eventual.' } | ||
| 44 | + ] | ||
| 45 | + }; | ||
| 46 | + | ||
| 47 | + // Dummy spending data (in millions Bs, population in millions) | ||
| 48 | + const gastoAnual = [ | ||
| 49 | + { año: 2005, monto: 8200, poblacion: 9.4 }, | ||
| 50 | + { año: 2006, monto: 8900, poblacion: 9.5 }, | ||
| 51 | + { año: 2007, monto: 10200, poblacion: 9.6 }, | ||
| 52 | + { año: 2008, monto: 12400, poblacion: 9.8 }, | ||
| 53 | + { año: 2009, monto: 13100, poblacion: 9.9 }, | ||
| 54 | + { año: 2010, monto: 14800, poblacion: 10.0 }, | ||
| 55 | + { año: 2011, monto: 17200, poblacion: 10.2 }, | ||
| 56 | + { año: 2012, monto: 19500, poblacion: 10.4 }, | ||
| 57 | + { año: 2013, monto: 22800, poblacion: 10.5 }, | ||
| 58 | + { año: 2014, monto: 28400, poblacion: 10.7 }, | ||
| 59 | + { año: 2015, monto: 31200, poblacion: 10.9 }, | ||
| 60 | + { año: 2016, monto: 33100, poblacion: 11.0 }, | ||
| 61 | + { año: 2017, monto: 35600, poblacion: 11.2 }, | ||
| 62 | + { año: 2018, monto: 38200, poblacion: 11.4 }, | ||
| 63 | + { año: 2019, monto: 41500, poblacion: 11.5 }, | ||
| 64 | + { año: 2020, monto: 39800, poblacion: 11.7 }, | ||
| 65 | + { año: 2021, monto: 42100, poblacion: 11.8 }, | ||
| 66 | + { año: 2022, monto: 45600, poblacion: 12.0 }, | ||
| 67 | + { año: 2023, monto: 48900, poblacion: 12.1 }, | ||
| 68 | + { año: 2024, monto: 52300, poblacion: 12.3 }, | ||
| 69 | + ]; | ||
| 70 | + | ||
| 71 | + // Calculate per capita for each year | ||
| 72 | + const gastoPerCapita = gastoAnual.map(g => ({ | ||
| 73 | + ...g, | ||
| 74 | + perCapita: Math.round(g.monto / g.poblacion) | ||
| 75 | + })); | ||
| 76 | + | ||
| 77 | + // Top entidades por año (dummy data) | ||
| 78 | + const topEntidadesPorAño = { | ||
| 79 | + 2020: [ | ||
| 80 | + { nombre: 'Ministerio de Educación', monto: 12500, porcentaje: 31.4 }, | ||
| 81 | + { nombre: 'Ministerio de Salud', monto: 7800, porcentaje: 19.6 }, | ||
| 82 | + { nombre: 'Policía Boliviana', monto: 4200, porcentaje: 10.6 }, | ||
| 83 | + ], | ||
| 84 | + 2021: [ | ||
| 85 | + { nombre: 'Ministerio de Educación', monto: 13200, porcentaje: 31.4 }, | ||
| 86 | + { nombre: 'Ministerio de Salud', monto: 8100, porcentaje: 19.2 }, | ||
| 87 | + { nombre: 'Policía Boliviana', monto: 4600, porcentaje: 10.9 }, | ||
| 88 | + ], | ||
| 89 | + 2022: [ | ||
| 90 | + { nombre: 'Ministerio de Educación', monto: 14800, porcentaje: 32.5 }, | ||
| 91 | + { nombre: 'Ministerio de Salud', monto: 8400, porcentaje: 18.4 }, | ||
| 92 | + { nombre: 'Policía Boliviana', monto: 5100, porcentaje: 11.2 }, | ||
| 93 | + ], | ||
| 94 | + 2023: [ | ||
| 95 | + { nombre: 'Ministerio de Educación', monto: 16200, porcentaje: 33.1 }, | ||
| 96 | + { nombre: 'Ministerio de Salud', monto: 8700, porcentaje: 17.8 }, | ||
| 97 | + { nombre: 'Policía Boliviana', monto: 5700, porcentaje: 11.7 }, | ||
| 98 | + ], | ||
| 99 | + 2024: [ | ||
| 100 | + { nombre: 'Ministerio de Educación', monto: 18200, porcentaje: 34.8 }, | ||
| 101 | + { nombre: 'Ministerio de Salud', monto: 8900, porcentaje: 17.0 }, | ||
| 102 | + { nombre: 'Policía Boliviana', monto: 6200, porcentaje: 11.9 }, | ||
| 103 | + ], | ||
| 104 | + }; | ||
| 105 | + | ||
| 106 | + // Total histórico (para cuando no hay hover) | ||
| 107 | + const topEntidadesTotal = [ | ||
| 108 | + { nombre: 'Ministerio de Educación', monto: 74900, porcentaje: 32.6 }, | ||
| 109 | + { nombre: 'Ministerio de Salud', monto: 41900, porcentaje: 18.4 }, | ||
| 110 | + { nombre: 'Policía Boliviana', monto: 25800, porcentaje: 11.3 }, | ||
| 111 | + ]; | ||
| 112 | + | ||
| 113 | + // Datos para comparación (dummy) | ||
| 114 | + const entidadesComparables = [ | ||
| 115 | + { | ||
| 116 | + id: 0, | ||
| 117 | + nombre: 'Ministerio de Educación', | ||
| 118 | + color: '#E8C547', | ||
| 119 | + datos: [ | ||
| 120 | + { año: 2020, monto: 12500, perCapita: 1068 }, | ||
| 121 | + { año: 2021, monto: 13200, perCapita: 1119 }, | ||
| 122 | + { año: 2022, monto: 14800, perCapita: 1233 }, | ||
| 123 | + { año: 2023, monto: 16200, perCapita: 1339 }, | ||
| 124 | + { año: 2024, monto: 18200, perCapita: 1480 }, | ||
| 125 | + ], | ||
| 126 | + total: 74900, | ||
| 127 | + promedioPerCapita: 1248, | ||
| 128 | + porcentajeObjeto: 34.8, | ||
| 129 | + ranking: 1 | ||
| 130 | + }, | ||
| 131 | + { | ||
| 132 | + id: 1, | ||
| 133 | + nombre: 'Ministerio de Salud', | ||
| 134 | + color: '#4A8B6E', | ||
| 135 | + datos: [ | ||
| 136 | + { año: 2020, monto: 6800, perCapita: 581 }, | ||
| 137 | + { año: 2021, monto: 7200, perCapita: 610 }, | ||
| 138 | + { año: 2022, monto: 7900, perCapita: 658 }, | ||
| 139 | + { año: 2023, monto: 8400, perCapita: 694 }, | ||
| 140 | + { año: 2024, monto: 8900, perCapita: 724 }, | ||
| 141 | + ], | ||
| 142 | + total: 39200, | ||
| 143 | + promedioPerCapita: 653, | ||
| 144 | + porcentajeObjeto: 17.0, | ||
| 145 | + ranking: 2 | ||
| 146 | + }, | ||
| 147 | + { | ||
| 148 | + id: 2, | ||
| 149 | + nombre: 'Policía Boliviana', | ||
| 150 | + color: '#8B5CF6', | ||
| 151 | + datos: [ | ||
| 152 | + { año: 2020, monto: 4200, perCapita: 359 }, | ||
| 153 | + { año: 2021, monto: 4600, perCapita: 390 }, | ||
| 154 | + { año: 2022, monto: 5100, perCapita: 425 }, | ||
| 155 | + { año: 2023, monto: 5700, perCapita: 471 }, | ||
| 156 | + { año: 2024, monto: 6200, perCapita: 504 }, | ||
| 157 | + ], | ||
| 158 | + total: 25800, | ||
| 159 | + promedioPerCapita: 430, | ||
| 160 | + porcentajeObjeto: 11.9, | ||
| 161 | + ranking: 3 | ||
| 162 | + }, | ||
| 163 | + { | ||
| 164 | + id: 3, | ||
| 165 | + nombre: 'Gobierno Autónomo de Santa Cruz', | ||
| 166 | + color: '#F97316', | ||
| 167 | + datos: [ | ||
| 168 | + { año: 2020, monto: 3100, perCapita: 265 }, | ||
| 169 | + { año: 2021, monto: 3400, perCapita: 288 }, | ||
| 170 | + { año: 2022, monto: 3800, perCapita: 317 }, | ||
| 171 | + { año: 2023, monto: 4100, perCapita: 339 }, | ||
| 172 | + { año: 2024, monto: 4500, perCapita: 366 }, | ||
| 173 | + ], | ||
| 174 | + total: 18900, | ||
| 175 | + promedioPerCapita: 315, | ||
| 176 | + porcentajeObjeto: 8.6, | ||
| 177 | + ranking: 4 | ||
| 178 | + } | ||
| 179 | + ]; | ||
| 180 | + | ||
| 181 | + // Colores fijos por posición - teal y naranja | ||
| 182 | + $: colorA = '#2a9d8f'; // teal | ||
| 183 | + $: colorB = '#f4a261'; // naranja | ||
| 184 | + | ||
| 185 | + $: entA = entidadesComparables[entidadA]; | ||
| 186 | + $: entB = entidadesComparables[entidadB]; | ||
| 187 | + $: maxComparacion = Math.max( | ||
| 188 | + ...entA.datos.map(d => d.perCapita), | ||
| 189 | + ...entB.datos.map(d => d.perCapita) | ||
| 190 | + ); | ||
| 191 | + $: años = entA.datos.map(d => d.año); | ||
| 192 | + | ||
| 193 | + // Datos de comparación según hover | ||
| 194 | + $: hoveredDataA = hoveredYearComparar | ||
| 195 | + ? entA.datos.find(d => d.año === hoveredYearComparar) | ||
| 196 | + : null; | ||
| 197 | + $: hoveredDataB = hoveredYearComparar | ||
| 198 | + ? entB.datos.find(d => d.año === hoveredYearComparar) | ||
| 199 | + : null; | ||
| 200 | + | ||
| 201 | + $: displayMontoA = hoveredDataA ? hoveredDataA.monto : entA.total; | ||
| 202 | + $: displayMontoLabelA = hoveredDataA ? `gasto en ${hoveredYearComparar}` : 'gasto 2020-2024'; | ||
| 203 | + $: displayPerCapitaA = hoveredDataA ? hoveredDataA.perCapita : entA.promedioPerCapita; | ||
| 204 | + $: displayPerCapitaLabelA = hoveredDataA ? `por persona en ${hoveredYearComparar}` : 'por persona (prom.)'; | ||
| 205 | + | ||
| 206 | + $: displayMontoB = hoveredDataB ? hoveredDataB.monto : entB.total; | ||
| 207 | + $: displayMontoLabelB = hoveredDataB ? `gasto en ${hoveredYearComparar}` : 'gasto 2020-2024'; | ||
| 208 | + $: displayPerCapitaB = hoveredDataB ? hoveredDataB.perCapita : entB.promedioPerCapita; | ||
| 209 | + $: displayPerCapitaLabelB = hoveredDataB ? `por persona en ${hoveredYearComparar}` : 'por persona (prom.)'; | ||
| 210 | + | ||
| 211 | + $: displayPeriodoComparar = hoveredYearComparar ? hoveredYearComparar : '2020-2024'; | ||
| 212 | + | ||
| 213 | + // Top entidades reactivo según hover | ||
| 214 | + $: displayTopEntidades = hoveredYear && topEntidadesPorAño[hoveredYear] | ||
| 215 | + ? topEntidadesPorAño[hoveredYear] | ||
| 216 | + : topEntidadesTotal; | ||
| 217 | + $: displayTopLabel = hoveredYear ? `en ${hoveredYear}` : '2005-2024'; | ||
| 218 | + | ||
| 219 | + // Context data | ||
| 220 | + const totalEntidades = 620; | ||
| 221 | + const totalGrupos = 9; // Total grupos in objeto del gasto | ||
| 222 | + | ||
| 223 | + // Calculations | ||
| 224 | + const maxPerCapita = Math.max(...gastoPerCapita.map(g => g.perCapita)); | ||
| 225 | + const minPerCapita = Math.min(...gastoPerCapita.map(g => g.perCapita)); | ||
| 226 | + const totalHistorico = gastoAnual.reduce((sum, g) => sum + g.monto, 0); | ||
| 227 | + const totalPoblacionAcum = gastoAnual.reduce((sum, g) => sum + g.poblacion, 0); | ||
| 228 | + const promedioPerCapita = Math.round(totalHistorico / totalPoblacionAcum); | ||
| 229 | + const totalPorcentaje = 14.2; | ||
| 230 | + const ranking = 1; | ||
| 231 | + | ||
| 232 | + // Current view data (changes on hover) | ||
| 233 | + $: currentData = hoveredYear | ||
| 234 | + ? gastoPerCapita.find(g => g.año === hoveredYear) | ||
| 235 | + : null; | ||
| 236 | + | ||
| 237 | + $: displayMonto = currentData ? currentData.monto : totalHistorico; | ||
| 238 | + $: displayMontoLabel = currentData ? `gasto en ${currentData.año}` : 'gasto 2005-2024'; | ||
| 239 | + $: displayPerCapita = currentData ? currentData.perCapita : promedioPerCapita; | ||
| 240 | + $: displayPerCapitaLabel = currentData ? `por persona en ${currentData.año}` : 'por persona (promedio)'; | ||
| 241 | + $: displayPorcentaje = currentData | ||
| 242 | + ? (totalPorcentaje * (currentData.monto / (totalHistorico / gastoAnual.length))).toFixed(1) | ||
| 243 | + : totalPorcentaje; | ||
| 244 | + $: displayPorcentajeLabel = currentData ? `del gasto ${currentData.año}` : 'del gasto total'; | ||
| 245 | + $: displayPeriodo = currentData ? currentData.año : '2005-2024'; | ||
| 246 | + $: displayRanking = `${ranking} de ${totalGrupos}`; | ||
| 247 | + $: displayRankingLabel = currentData ? `ranking en ${currentData.año}` : 'ranking entre grupos'; | ||
| 248 | + | ||
| 249 | + // Tweened values para transiciones suaves | ||
| 250 | + const tweenOptions = { duration: 300, easing: cubicOut }; | ||
| 251 | + | ||
| 252 | + // Agregado | ||
| 253 | + const twMonto = tweened(totalHistorico, tweenOptions); | ||
| 254 | + const twPerCapita = tweened(promedioPerCapita, tweenOptions); | ||
| 255 | + const twPorcentaje = tweened(totalPorcentaje, tweenOptions); | ||
| 256 | + | ||
| 257 | + // Comparación | ||
| 258 | + const twMontoA = tweened(0, tweenOptions); | ||
| 259 | + const twMontoB = tweened(0, tweenOptions); | ||
| 260 | + const twPerCapitaA = tweened(0, tweenOptions); | ||
| 261 | + const twPerCapitaB = tweened(0, tweenOptions); | ||
| 262 | + | ||
| 263 | + // Top entidades | ||
| 264 | + const twTop1 = tweened(0, tweenOptions); | ||
| 265 | + const twTop2 = tweened(0, tweenOptions); | ||
| 266 | + const twTop3 = tweened(0, tweenOptions); | ||
| 267 | + | ||
| 268 | + // Actualizar tweened cuando cambian los valores | ||
| 269 | + $: twMonto.set(displayMonto); | ||
| 270 | + $: twPerCapita.set(displayPerCapita); | ||
| 271 | + $: twPorcentaje.set(parseFloat(displayPorcentaje)); | ||
| 272 | + | ||
| 273 | + $: twMontoA.set(displayMontoA); | ||
| 274 | + $: twMontoB.set(displayMontoB); | ||
| 275 | + $: twPerCapitaA.set(displayPerCapitaA); | ||
| 276 | + $: twPerCapitaB.set(displayPerCapitaB); | ||
| 277 | + | ||
| 278 | + $: if (displayTopEntidades[0]) twTop1.set(displayTopEntidades[0].porcentaje); | ||
| 279 | + $: if (displayTopEntidades[1]) twTop2.set(displayTopEntidades[1].porcentaje); | ||
| 280 | + $: if (displayTopEntidades[2]) twTop3.set(displayTopEntidades[2].porcentaje); | ||
| 281 | + | ||
| 282 | + function formatMonto(m) { | ||
| 283 | + if (m >= 1000000) return (m / 1000000).toFixed(1) + 'B'; | ||
| 284 | + if (m >= 1000) return Math.round(m / 1000) + 'K'; | ||
| 285 | + return m.toLocaleString(); | ||
| 286 | + } | ||
| 287 | + | ||
| 288 | + function formatMontoLargo(m) { | ||
| 289 | + if (m >= 1000) return (m / 1000).toFixed(1) + 'MM'; | ||
| 290 | + return m.toLocaleString() + 'M'; | ||
| 291 | + } | ||
| 292 | +</script> | ||
| 293 | + | ||
| 294 | +<svelte:head> | ||
| 295 | + <title>Test — Objeto Detail</title> | ||
| 296 | + <link rel="preconnect" href="https://fonts.googleapis.com" /> | ||
| 297 | + <link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@400;500&family=Instrument+Sans:wght@400;500;600&display=swap" rel="stylesheet" /> | ||
| 298 | +</svelte:head> | ||
| 299 | + | ||
| 300 | +<div class="page" class:mounted class:tema-claro={!isDark}> | ||
| 301 | + | ||
| 302 | + <!-- Header compacto --> | ||
| 303 | + <header> | ||
| 304 | + <nav class="breadcrumb"> | ||
| 305 | + <a href="/clasificadores/objeto-gasto">← Objeto del Gasto</a> | ||
| 306 | + </nav> | ||
| 307 | + | ||
| 308 | + <div class="title-row"> | ||
| 309 | + <div class="title-main"> | ||
| 310 | + <span class="codigo">{objeto.codigo}</span> | ||
| 311 | + <h1> | ||
| 312 | + {objeto.nombre} | ||
| 313 | + <button class="help-icon" on:click={() => showInfo = !showInfo} class:active={showInfo}> | ||
| 314 | + <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | ||
| 315 | + <circle cx="12" cy="12" r="10"/> | ||
| 316 | + <path d="M9.5 9a3 3 0 1 1 3.5 2.95c-.5.17-1 .62-1 1.3V15"/> | ||
| 317 | + <circle cx="12" cy="18" r="0.5" fill="currentColor"/> | ||
| 318 | + </svg> | ||
| 319 | + </button> | ||
| 320 | + </h1> | ||
| 321 | + </div> | ||
| 322 | + </div> | ||
| 323 | + | ||
| 324 | + <div class="meta"> | ||
| 325 | + <span class="badge">{objeto.nivel}</span> | ||
| 326 | + <span class="years">{objeto.años}</span> | ||
| 327 | + </div> | ||
| 328 | + </header> | ||
| 329 | + | ||
| 330 | + <!-- Panel de info (colapsable) --> | ||
| 331 | + {#if showInfo} | ||
| 332 | + <div class="info-panel"> | ||
| 333 | + <div class="info-content"> | ||
| 334 | + <h4>Definición vigente</h4> | ||
| 335 | + <p>{objeto.descripcion}</p> | ||
| 336 | + | ||
| 337 | + {#if objeto.variaciones.length > 1} | ||
| 338 | + <h4>Variaciones históricas</h4> | ||
| 339 | + {#each objeto.variaciones as v} | ||
| 340 | + <div class="variacion"> | ||
| 341 | + <span class="variacion-rango">{v.rango}</span> | ||
| 342 | + <p>{v.texto}</p> | ||
| 343 | + </div> | ||
| 344 | + {/each} | ||
| 345 | + {/if} | ||
| 346 | + </div> | ||
| 347 | + </div> | ||
| 348 | + {/if} | ||
| 349 | + | ||
| 350 | + <!-- Dashboard principal --> | ||
| 351 | + <main> | ||
| 352 | + | ||
| 353 | + <!-- Toggle de vista --> | ||
| 354 | + <div class="vista-toggle"> | ||
| 355 | + <button | ||
| 356 | + class="toggle-btn" | ||
| 357 | + class:active={vista === 'agregado'} | ||
| 358 | + on:click={() => vista = 'agregado'} | ||
| 359 | + > | ||
| 360 | + Agregado | ||
| 361 | + </button> | ||
| 362 | + <button | ||
| 363 | + class="toggle-btn" | ||
| 364 | + class:active={vista === 'comparar'} | ||
| 365 | + on:click={() => vista = 'comparar'} | ||
| 366 | + > | ||
| 367 | + Comparar | ||
| 368 | + </button> | ||
| 369 | + </div> | ||
| 370 | + | ||
| 371 | + <!-- Selector de entidad (solo en vista agregado) --> | ||
| 372 | + {#if vista === 'agregado'} | ||
| 373 | + <div class="entity-selector"> | ||
| 374 | + <span class="selector-label">Viendo</span> | ||
| 375 | + <button class="selector-btn"> | ||
| 376 | + Todo el sector público | ||
| 377 | + <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | ||
| 378 | + <path d="M6 9l6 6 6-6"/> | ||
| 379 | + </svg> | ||
| 380 | + </button> | ||
| 381 | + </div> | ||
| 382 | + {/if} | ||
| 383 | + | ||
| 384 | + {#if vista === 'agregado'} | ||
| 385 | + <!-- VISTA AGREGADO --> | ||
| 386 | + | ||
| 387 | + <!-- KPIs arriba --> | ||
| 388 | + <div class="kpis"> | ||
| 389 | + <div class="kpi"> | ||
| 390 | + <span class="kpi-value">Bs {formatMontoLargo(Math.round($twMonto))}</span> | ||
| 391 | + <span class="kpi-label">{displayMontoLabel}</span> | ||
| 392 | + </div> | ||
| 393 | + | ||
| 394 | + <div class="kpi"> | ||
| 395 | + <span class="kpi-value">Bs {Math.round($twPerCapita).toLocaleString()}</span> | ||
| 396 | + <span class="kpi-label">{displayPerCapitaLabel}</span> | ||
| 397 | + </div> | ||
| 398 | + | ||
| 399 | + <div class="kpi"> | ||
| 400 | + <span class="kpi-value">{$twPorcentaje.toFixed(1)}%</span> | ||
| 401 | + <span class="kpi-label">{displayPorcentajeLabel}</span> | ||
| 402 | + </div> | ||
| 403 | + | ||
| 404 | + <div class="kpi"> | ||
| 405 | + <span class="kpi-value">{displayRanking}</span> | ||
| 406 | + <span class="kpi-label">{displayRankingLabel}</span> | ||
| 407 | + </div> | ||
| 408 | + </div> | ||
| 409 | + | ||
| 410 | + <!-- Gráfico de barras (per cápita) - protagonista al medio --> | ||
| 411 | + <div class="chart-section chart-protagonista"> | ||
| 412 | + <div class="chart-header"> | ||
| 413 | + <span class="chart-title">Gasto por persona</span> | ||
| 414 | + <span class="chart-period">{displayPeriodo}</span> | ||
| 415 | + </div> | ||
| 416 | + | ||
| 417 | + <div class="chart-wrapper"> | ||
| 418 | + <!-- Y-axis labels --> | ||
| 419 | + <div class="y-axis"> | ||
| 420 | + <span class="y-label">Bs {formatMonto(maxPerCapita)}</span> | ||
| 421 | + <span class="y-label">Bs {formatMonto(Math.round(maxPerCapita / 2))}</span> | ||
| 422 | + <span class="y-label">Bs 0</span> | ||
| 423 | + </div> | ||
| 424 | + | ||
| 425 | + <div | ||
| 426 | + class="chart" | ||
| 427 | + on:mouseleave={() => hoveredYear = null} | ||
| 428 | + role="group" | ||
| 429 | + > | ||
| 430 | + {#each gastoPerCapita as g} | ||
| 431 | + <div | ||
| 432 | + class="bar-container" | ||
| 433 | + on:mouseenter={() => hoveredYear = g.año} | ||
| 434 | + role="button" | ||
| 435 | + tabindex="0" | ||
| 436 | + > | ||
| 437 | + <div | ||
| 438 | + class="bar" | ||
| 439 | + class:hovered={hoveredYear === g.año} | ||
| 440 | + style="height: {(g.perCapita / maxPerCapita) * 100}%" | ||
| 441 | + ></div> | ||
| 442 | + <span class="bar-label" class:visible={hoveredYear === g.año || g.año % 5 === 0}> | ||
| 443 | + {g.año.toString().slice(-2)} | ||
| 444 | + </span> | ||
| 445 | + </div> | ||
| 446 | + {/each} | ||
| 447 | + </div> | ||
| 448 | + </div> | ||
| 449 | + </div> | ||
| 450 | + | ||
| 451 | + <!-- Top entidades --> | ||
| 452 | + <div class="top-section"> | ||
| 453 | + <div class="top-header"> | ||
| 454 | + <h3>Mayores ejecutores</h3> | ||
| 455 | + <span class="top-context">{totalEntidades} entidades · {displayTopLabel}</span> | ||
| 456 | + </div> | ||
| 457 | + | ||
| 458 | + <div class="top-list"> | ||
| 459 | + {#each displayTopEntidades as ent, i (ent.nombre)} | ||
| 460 | + <div class="top-item"> | ||
| 461 | + <span class="top-rank">#{i + 1}</span> | ||
| 462 | + <div class="top-info"> | ||
| 463 | + <span class="top-name">{ent.nombre}</span> | ||
| 464 | + <div class="top-bar-bg"> | ||
| 465 | + <div class="top-bar" style="width: {((i === 0 ? $twTop1 : i === 1 ? $twTop2 : $twTop3) / $twTop1) * 100}%"></div> | ||
| 466 | + </div> | ||
| 467 | + </div> | ||
| 468 | + <span class="top-pct">{(i === 0 ? $twTop1 : i === 1 ? $twTop2 : $twTop3).toFixed(1)}%</span> | ||
| 469 | + </div> | ||
| 470 | + {/each} | ||
| 471 | + </div> | ||
| 472 | + </div> | ||
| 473 | + | ||
| 474 | + {:else} | ||
| 475 | + <!-- VISTA COMPARAR --> | ||
| 476 | + | ||
| 477 | + <!-- Selectores de entidades --> | ||
| 478 | + <div class="comparador-selectors"> | ||
| 479 | + <div class="selector-group"> | ||
| 480 | + <span class="selector-dot" style="background: {colorA}"></span> | ||
| 481 | + <select bind:value={entidadA}> | ||
| 482 | + {#each entidadesComparables as ent} | ||
| 483 | + <option value={ent.id} disabled={ent.id === entidadB}>{ent.nombre}</option> | ||
| 484 | + {/each} | ||
| 485 | + </select> | ||
| 486 | + </div> | ||
| 487 | + <span class="vs">vs</span> | ||
| 488 | + <div class="selector-group"> | ||
| 489 | + <span class="selector-dot" style="background: {colorB}"></span> | ||
| 490 | + <select bind:value={entidadB}> | ||
| 491 | + {#each entidadesComparables as ent} | ||
| 492 | + <option value={ent.id} disabled={ent.id === entidadA}>{ent.nombre}</option> | ||
| 493 | + {/each} | ||
| 494 | + </select> | ||
| 495 | + </div> | ||
| 496 | + </div> | ||
| 497 | + | ||
| 498 | + <!-- Gráfico comparativo --> | ||
| 499 | + <div class="chart-section"> | ||
| 500 | + <div class="chart-header"> | ||
| 501 | + <span class="chart-title">Gasto por persona</span> | ||
| 502 | + <span class="chart-period">{displayPeriodoComparar}</span> | ||
| 503 | + </div> | ||
| 504 | + | ||
| 505 | + <div class="chart-wrapper"> | ||
| 506 | + <div class="y-axis"> | ||
| 507 | + <span class="y-label">Bs {formatMonto(maxComparacion)}</span> | ||
| 508 | + <span class="y-label">Bs {formatMonto(Math.round(maxComparacion / 2))}</span> | ||
| 509 | + <span class="y-label">Bs 0</span> | ||
| 510 | + </div> | ||
| 511 | + | ||
| 512 | + <div | ||
| 513 | + class="chart chart-comparar" | ||
| 514 | + on:mouseleave={() => hoveredYearComparar = null} | ||
| 515 | + role="group" | ||
| 516 | + > | ||
| 517 | + {#each años as año, i} | ||
| 518 | + <div | ||
| 519 | + class="bar-group" | ||
| 520 | + class:active={hoveredYearComparar === año} | ||
| 521 | + on:mouseenter={() => hoveredYearComparar = año} | ||
| 522 | + role="button" | ||
| 523 | + tabindex="0" | ||
| 524 | + > | ||
| 525 | + <div class="bars-pair"> | ||
| 526 | + <div | ||
| 527 | + class="bar bar-a" | ||
| 528 | + style="height: {(entA.datos[i].perCapita / maxComparacion) * 100}%; --bar-color: {colorA}" | ||
| 529 | + ></div> | ||
| 530 | + <div | ||
| 531 | + class="bar bar-b" | ||
| 532 | + style="height: {(entB.datos[i].perCapita / maxComparacion) * 100}%; --bar-color: {colorB}" | ||
| 533 | + ></div> | ||
| 534 | + </div> | ||
| 535 | + <span class="bar-label visible" class:highlight={hoveredYearComparar === año}>{año}</span> | ||
| 536 | + </div> | ||
| 537 | + {/each} | ||
| 538 | + </div> | ||
| 539 | + </div> | ||
| 540 | + | ||
| 541 | + <div class="chart-legend"> | ||
| 542 | + <span class="legend-item"><span class="legend-dot" style="background: {colorA}"></span>{entA.nombre}</span> | ||
| 543 | + <span class="legend-item"><span class="legend-dot" style="background: {colorB}"></span>{entB.nombre}</span> | ||
| 544 | + </div> | ||
| 545 | + </div> | ||
| 546 | + | ||
| 547 | + <!-- KPIs comparativos - tabla --> | ||
| 548 | + <div class="kpis-tabla"> | ||
| 549 | + <!-- Header --> | ||
| 550 | + <div class="tabla-header"> | ||
| 551 | + <span class="tabla-label"></span> | ||
| 552 | + <span class="tabla-val" style="color: {colorA}">{entA.nombre}</span> | ||
| 553 | + <span class="tabla-val" style="color: {colorB}">{entB.nombre}</span> | ||
| 554 | + </div> | ||
| 555 | + <!-- Gasto --> | ||
| 556 | + <div class="tabla-row"> | ||
| 557 | + <span class="tabla-label">{hoveredYearComparar ? `Gasto ${hoveredYearComparar}` : 'Gasto 2020-2024'}</span> | ||
| 558 | + <span class="tabla-val">Bs {formatMontoLargo(Math.round($twMontoA))}</span> | ||
| 559 | + <span class="tabla-val">Bs {formatMontoLargo(Math.round($twMontoB))}</span> | ||
| 560 | + </div> | ||
| 561 | + <!-- Per cápita --> | ||
| 562 | + <div class="tabla-row"> | ||
| 563 | + <span class="tabla-label">{hoveredYearComparar ? `Por persona ${hoveredYearComparar}` : 'Por persona (prom.)'}</span> | ||
| 564 | + <span class="tabla-val">Bs {Math.round($twPerCapitaA).toLocaleString()}</span> | ||
| 565 | + <span class="tabla-val">Bs {Math.round($twPerCapitaB).toLocaleString()}</span> | ||
| 566 | + </div> | ||
| 567 | + <!-- Porcentaje --> | ||
| 568 | + <div class="tabla-row"> | ||
| 569 | + <span class="tabla-label">Peso sobre total</span> | ||
| 570 | + <span class="tabla-val">{entA.porcentajeObjeto}%</span> | ||
| 571 | + <span class="tabla-val">{entB.porcentajeObjeto}%</span> | ||
| 572 | + </div> | ||
| 573 | + <!-- Ranking --> | ||
| 574 | + <div class="tabla-row"> | ||
| 575 | + <span class="tabla-label">Ranking</span> | ||
| 576 | + <span class="tabla-val">#{entA.ranking}</span> | ||
| 577 | + <span class="tabla-val">#{entB.ranking}</span> | ||
| 578 | + </div> | ||
| 579 | + </div> | ||
| 580 | + | ||
| 581 | + {/if} | ||
| 582 | + | ||
| 583 | + </main> | ||
| 584 | +</div> | ||
| 585 | + | ||
| 586 | + | ||
| 587 | + | ||
| 588 | +<style> | ||
| 589 | + .page { | ||
| 590 | + min-height: 100vh; | ||
| 591 | + background: #0A0A0A; | ||
| 592 | + color: #F5F0E8; | ||
| 593 | + font-family: 'Instrument Sans', -apple-system, sans-serif; | ||
| 594 | + opacity: 0; | ||
| 595 | + transition: opacity 0.4s ease; | ||
| 596 | + } | ||
| 597 | + .mounted { opacity: 1; } | ||
| 598 | + | ||
| 599 | + /* Header */ | ||
| 600 | + header { | ||
| 601 | + padding: 1rem 2rem; | ||
| 602 | + border-bottom: 1px solid #1E1E1C; | ||
| 603 | + } | ||
| 604 | + | ||
| 605 | + .breadcrumb a { | ||
| 606 | + font-family: 'DM Mono', monospace; | ||
| 607 | + font-size: 0.75rem; | ||
| 608 | + color: #5A5650; | ||
| 609 | + text-decoration: none; | ||
| 610 | + transition: color 0.2s; | ||
| 611 | + } | ||
| 612 | + .breadcrumb a:hover { color: #F5F0E8; } | ||
| 613 | + | ||
| 614 | + .title-row { | ||
| 615 | + display: flex; | ||
| 616 | + align-items: flex-start; | ||
| 617 | + justify-content: space-between; | ||
| 618 | + margin-top: 0.5rem; | ||
| 619 | + gap: 1rem; | ||
| 620 | + } | ||
| 621 | + | ||
| 622 | + .title-main { | ||
| 623 | + display: flex; | ||
| 624 | + align-items: baseline; | ||
| 625 | + gap: 1rem; | ||
| 626 | + flex-wrap: wrap; | ||
| 627 | + } | ||
| 628 | + | ||
| 629 | + .codigo { | ||
| 630 | + font-family: 'DM Mono', monospace; | ||
| 631 | + font-size: 1.5rem; | ||
| 632 | + color: #5A5650; | ||
| 633 | + font-weight: 400; | ||
| 634 | + } | ||
| 635 | + | ||
| 636 | + h1 { | ||
| 637 | + font-family: 'DM Serif Display', Georgia, serif; | ||
| 638 | + font-size: 1.5rem; | ||
| 639 | + font-weight: 400; | ||
| 640 | + color: #F5F0E8; | ||
| 641 | + margin: 0; | ||
| 642 | + display: inline-flex; | ||
| 643 | + align-items: center; | ||
| 644 | + gap: 0.5rem; | ||
| 645 | + } | ||
| 646 | + | ||
| 647 | + .help-icon { | ||
| 648 | + background: transparent; | ||
| 649 | + border: none; | ||
| 650 | + padding: 0; | ||
| 651 | + color: #5A5650; | ||
| 652 | + cursor: pointer; | ||
| 653 | + transition: all 0.2s; | ||
| 654 | + display: inline-flex; | ||
| 655 | + align-items: center; | ||
| 656 | + justify-content: center; | ||
| 657 | + vertical-align: middle; | ||
| 658 | + } | ||
| 659 | + .help-icon:hover, .help-icon.active { | ||
| 660 | + color: #E8C547; | ||
| 661 | + } | ||
| 662 | + | ||
| 663 | + .meta { | ||
| 664 | + display: flex; | ||
| 665 | + align-items: center; | ||
| 666 | + gap: 0.75rem; | ||
| 667 | + margin-top: 0.5rem; | ||
| 668 | + } | ||
| 669 | + | ||
| 670 | + .badge { | ||
| 671 | + font-family: 'DM Mono', monospace; | ||
| 672 | + font-size: 0.625rem; | ||
| 673 | + letter-spacing: 0.1em; | ||
| 674 | + text-transform: uppercase; | ||
| 675 | + padding: 0.25rem 0.5rem; | ||
| 676 | + background: #1A1A18; | ||
| 677 | + border: 1px solid #2E2E2C; | ||
| 678 | + border-radius: 4px; | ||
| 679 | + color: #8A8578; | ||
| 680 | + } | ||
| 681 | + | ||
| 682 | + .years { | ||
| 683 | + font-family: 'DM Mono', monospace; | ||
| 684 | + font-size: 0.75rem; | ||
| 685 | + color: #5A5650; | ||
| 686 | + } | ||
| 687 | + | ||
| 688 | + /* Info panel */ | ||
| 689 | + .info-panel { | ||
| 690 | + background: #111110; | ||
| 691 | + border-bottom: 1px solid #1E1E1C; | ||
| 692 | + padding: 1.5rem 2rem; | ||
| 693 | + } | ||
| 694 | + | ||
| 695 | + .info-content h4 { | ||
| 696 | + font-family: 'DM Mono', monospace; | ||
| 697 | + font-size: 0.625rem; | ||
| 698 | + letter-spacing: 0.15em; | ||
| 699 | + text-transform: uppercase; | ||
| 700 | + color: #5A5650; | ||
| 701 | + margin: 0 0 0.5rem 0; | ||
| 702 | + } | ||
| 703 | + | ||
| 704 | + .info-content p { | ||
| 705 | + font-size: 0.875rem; | ||
| 706 | + line-height: 1.6; | ||
| 707 | + color: #8A8578; | ||
| 708 | + margin: 0 0 1.25rem 0; | ||
| 709 | + } | ||
| 710 | + | ||
| 711 | + .variacion { | ||
| 712 | + padding-left: 1rem; | ||
| 713 | + border-left: 2px solid #2E2E2C; | ||
| 714 | + margin-bottom: 1rem; | ||
| 715 | + } | ||
| 716 | + | ||
| 717 | + .variacion-rango { | ||
| 718 | + font-family: 'DM Mono', monospace; | ||
| 719 | + font-size: 0.688rem; | ||
| 720 | + color: #5A5650; | ||
| 721 | + } | ||
| 722 | + | ||
| 723 | + .variacion p { | ||
| 724 | + margin-top: 0.25rem; | ||
| 725 | + font-size: 0.813rem; | ||
| 726 | + } | ||
| 727 | + | ||
| 728 | + /* Main */ | ||
| 729 | + main { | ||
| 730 | + padding: 1rem 2rem; | ||
| 731 | + max-width: 900px; | ||
| 732 | + margin: 0 auto; | ||
| 733 | + } | ||
| 734 | + | ||
| 735 | + /* Entity selector */ | ||
| 736 | + .entity-selector { | ||
| 737 | + display: flex; | ||
| 738 | + align-items: center; | ||
| 739 | + gap: 0.75rem; | ||
| 740 | + margin-bottom: 0.75rem; | ||
| 741 | + } | ||
| 742 | + | ||
| 743 | + .selector-label { | ||
| 744 | + font-size: 0.813rem; | ||
| 745 | + color: #5A5650; | ||
| 746 | + } | ||
| 747 | + | ||
| 748 | + .selector-btn { | ||
| 749 | + display: flex; | ||
| 750 | + align-items: center; | ||
| 751 | + gap: 0.5rem; | ||
| 752 | + padding: 0.5rem 1rem; | ||
| 753 | + background: #161614; | ||
| 754 | + border: 1px solid #2E2E2C; | ||
| 755 | + border-radius: 8px; | ||
| 756 | + color: #F5F0E8; | ||
| 757 | + font-size: 0.875rem; | ||
| 758 | + cursor: pointer; | ||
| 759 | + transition: all 0.2s; | ||
| 760 | + } | ||
| 761 | + .selector-btn:hover { | ||
| 762 | + border-color: #E8C547; | ||
| 763 | + } | ||
| 764 | + .selector-btn svg { | ||
| 765 | + color: #5A5650; | ||
| 766 | + } | ||
| 767 | + | ||
| 768 | + /* Chart */ | ||
| 769 | + .chart-section { | ||
| 770 | + margin-bottom: 1rem; | ||
| 771 | + } | ||
| 772 | + | ||
| 773 | + .chart-protagonista { | ||
| 774 | + margin-bottom: 1.25rem; | ||
| 775 | + } | ||
| 776 | + | ||
| 777 | + .chart-protagonista .chart { | ||
| 778 | + height: calc(100vh - 480px); | ||
| 779 | + min-height: 120px; | ||
| 780 | + max-height: 200px; | ||
| 781 | + } | ||
| 782 | + | ||
| 783 | + .chart-protagonista .y-axis { | ||
| 784 | + height: calc(100vh - 480px); | ||
| 785 | + min-height: 120px; | ||
| 786 | + max-height: 200px; | ||
| 787 | + } | ||
| 788 | + | ||
| 789 | + .chart-section:has(.chart-comparar) { | ||
| 790 | + margin-bottom: 0; | ||
| 791 | + } | ||
| 792 | + | ||
| 793 | + .chart-section:has(.chart-comparar) .y-axis { | ||
| 794 | + height: calc(100vh - 380px); | ||
| 795 | + min-height: 140px; | ||
| 796 | + max-height: 280px; | ||
| 797 | + } | ||
| 798 | + | ||
| 799 | + .chart-header { | ||
| 800 | + display: flex; | ||
| 801 | + justify-content: space-between; | ||
| 802 | + align-items: baseline; | ||
| 803 | + margin-bottom: 0.5rem; | ||
| 804 | + } | ||
| 805 | + | ||
| 806 | + .chart-title { | ||
| 807 | + font-size: 0.75rem; | ||
| 808 | + color: #5A5650; | ||
| 809 | + text-transform: uppercase; | ||
| 810 | + letter-spacing: 0.1em; | ||
| 811 | + } | ||
| 812 | + | ||
| 813 | + .chart-period { | ||
| 814 | + font-family: 'DM Mono', monospace; | ||
| 815 | + font-size: 0.875rem; | ||
| 816 | + color: #B0A89C; | ||
| 817 | + font-variant-numeric: tabular-nums; | ||
| 818 | + min-width: 5rem; | ||
| 819 | + text-align: right; | ||
| 820 | + } | ||
| 821 | + | ||
| 822 | + .chart-wrapper { | ||
| 823 | + display: flex; | ||
| 824 | + gap: 0.75rem; | ||
| 825 | + } | ||
| 826 | + | ||
| 827 | + .y-axis { | ||
| 828 | + display: flex; | ||
| 829 | + flex-direction: column; | ||
| 830 | + justify-content: space-between; | ||
| 831 | + align-items: flex-end; | ||
| 832 | + height: 160px; | ||
| 833 | + padding-bottom: 1.5rem; | ||
| 834 | + } | ||
| 835 | + | ||
| 836 | + .y-label { | ||
| 837 | + font-family: 'DM Mono', monospace; | ||
| 838 | + font-size: 0.625rem; | ||
| 839 | + color: #5A5650; | ||
| 840 | + } | ||
| 841 | + | ||
| 842 | + .chart { | ||
| 843 | + flex: 1; | ||
| 844 | + display: flex; | ||
| 845 | + align-items: flex-end; | ||
| 846 | + gap: 4px; | ||
| 847 | + height: 160px; | ||
| 848 | + padding: 0 0.5rem; | ||
| 849 | + border-left: 1px solid #2E2E2C; | ||
| 850 | + } | ||
| 851 | + | ||
| 852 | + .bar-container { | ||
| 853 | + flex: 1; | ||
| 854 | + display: flex; | ||
| 855 | + flex-direction: column; | ||
| 856 | + align-items: center; | ||
| 857 | + justify-content: flex-end; | ||
| 858 | + height: 100%; | ||
| 859 | + cursor: pointer; | ||
| 860 | + } | ||
| 861 | + | ||
| 862 | + .bar { | ||
| 863 | + width: 100%; | ||
| 864 | + background: #2a9d8f; | ||
| 865 | + border-radius: 4px 4px 0 0; | ||
| 866 | + transition: background 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); | ||
| 867 | + min-height: 4px; | ||
| 868 | + } | ||
| 869 | + | ||
| 870 | + .bar.hovered { | ||
| 871 | + background: #264653; | ||
| 872 | + transform: scaleX(1.1); | ||
| 873 | + } | ||
| 874 | + | ||
| 875 | + .bar-label { | ||
| 876 | + font-family: 'DM Mono', monospace; | ||
| 877 | + font-size: 0.625rem; | ||
| 878 | + color: #B0A89C; | ||
| 879 | + margin-top: 0.5rem; | ||
| 880 | + opacity: 0; | ||
| 881 | + transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1), color 0.25s ease; | ||
| 882 | + } | ||
| 883 | + .bar-label.visible { opacity: 1; } | ||
| 884 | + | ||
| 885 | + /* KPIs */ | ||
| 886 | + .kpis { | ||
| 887 | + display: grid; | ||
| 888 | + grid-template-columns: repeat(4, 1fr); | ||
| 889 | + gap: 0.5rem; | ||
| 890 | + margin-bottom: 1rem; | ||
| 891 | + } | ||
| 892 | + | ||
| 893 | + .kpi { | ||
| 894 | + background: #131312; | ||
| 895 | + padding: 0.75rem 0.75rem; | ||
| 896 | + text-align: center; | ||
| 897 | + border-radius: 8px; | ||
| 898 | + transition: background 0.3s ease; | ||
| 899 | + } | ||
| 900 | + | ||
| 901 | + .kpi-value { | ||
| 902 | + display: block; | ||
| 903 | + font-family: 'DM Mono', monospace; | ||
| 904 | + font-size: 1.125rem; | ||
| 905 | + font-weight: 500; | ||
| 906 | + color: #F5F0E8; | ||
| 907 | + margin-bottom: 0; | ||
| 908 | + transition: opacity 0.2s ease; | ||
| 909 | + font-variant-numeric: tabular-nums; | ||
| 910 | + min-height: 1.5rem; | ||
| 911 | + } | ||
| 912 | + | ||
| 913 | + .kpi-label { | ||
| 914 | + font-size: 0.5625rem; | ||
| 915 | + color: #5A5650; | ||
| 916 | + text-transform: uppercase; | ||
| 917 | + letter-spacing: 0.03em; | ||
| 918 | + line-height: 1.3; | ||
| 919 | + transition: opacity 0.2s ease; | ||
| 920 | + min-height: 1.25rem; | ||
| 921 | + display: flex; | ||
| 922 | + align-items: center; | ||
| 923 | + justify-content: center; | ||
| 924 | + } | ||
| 925 | + | ||
| 926 | + /* Top entidades */ | ||
| 927 | + .top-header { | ||
| 928 | + display: flex; | ||
| 929 | + justify-content: space-between; | ||
| 930 | + align-items: baseline; | ||
| 931 | + margin-bottom: 0.625rem; | ||
| 932 | + } | ||
| 933 | + | ||
| 934 | + .top-section h3 { | ||
| 935 | + font-family: 'DM Mono', monospace; | ||
| 936 | + font-size: 0.75rem; | ||
| 937 | + color: #5A5650; | ||
| 938 | + text-transform: uppercase; | ||
| 939 | + letter-spacing: 0.1em; | ||
| 940 | + margin: 0; | ||
| 941 | + } | ||
| 942 | + | ||
| 943 | + .top-context { | ||
| 944 | + font-family: 'DM Mono', monospace; | ||
| 945 | + font-size: 0.688rem; | ||
| 946 | + color: #5A5650; | ||
| 947 | + font-variant-numeric: tabular-nums; | ||
| 948 | + } | ||
| 949 | + | ||
| 950 | + .top-list { | ||
| 951 | + display: flex; | ||
| 952 | + flex-direction: column; | ||
| 953 | + gap: 0.5rem; | ||
| 954 | + } | ||
| 955 | + | ||
| 956 | + .top-item { | ||
| 957 | + display: flex; | ||
| 958 | + align-items: center; | ||
| 959 | + gap: 0.75rem; | ||
| 960 | + padding: 0.625rem 0.875rem; | ||
| 961 | + background: #131312; | ||
| 962 | + border-radius: 8px; | ||
| 963 | + } | ||
| 964 | + | ||
| 965 | + .top-rank { | ||
| 966 | + font-family: 'DM Mono', monospace; | ||
| 967 | + font-size: 0.875rem; | ||
| 968 | + color: #5A5650; | ||
| 969 | + width: 24px; | ||
| 970 | + } | ||
| 971 | + | ||
| 972 | + .top-info { | ||
| 973 | + flex: 1; | ||
| 974 | + } | ||
| 975 | + | ||
| 976 | + .top-name { | ||
| 977 | + display: block; | ||
| 978 | + font-size: 0.8125rem; | ||
| 979 | + color: #F5F0E8; | ||
| 980 | + margin-bottom: 0.375rem; | ||
| 981 | + } | ||
| 982 | + | ||
| 983 | + .top-bar-bg { | ||
| 984 | + height: 4px; | ||
| 985 | + background: #1E1E1C; | ||
| 986 | + border-radius: 2px; | ||
| 987 | + overflow: hidden; | ||
| 988 | + } | ||
| 989 | + | ||
| 990 | + .top-bar { | ||
| 991 | + height: 100%; | ||
| 992 | + background: #2a9d8f; | ||
| 993 | + border-radius: 2px; | ||
| 994 | + transition: width 0.35s cubic-bezier(0.4, 0, 0.2, 1); | ||
| 995 | + } | ||
| 996 | + | ||
| 997 | + .top-pct { | ||
| 998 | + font-family: 'DM Mono', monospace; | ||
| 999 | + font-size: 0.875rem; | ||
| 1000 | + color: #B0A89C; | ||
| 1001 | + min-width: 52px; | ||
| 1002 | + width: 52px; | ||
| 1003 | + text-align: right; | ||
| 1004 | + transition: opacity 0.2s ease; | ||
| 1005 | + font-variant-numeric: tabular-nums; | ||
| 1006 | + } | ||
| 1007 | + | ||
| 1008 | + .see-all { | ||
| 1009 | + background: transparent; | ||
| 1010 | + border: 1px solid #2E2E2C; | ||
| 1011 | + border-radius: 8px; | ||
| 1012 | + padding: 0.75rem 1rem; | ||
| 1013 | + color: #8A8578; | ||
| 1014 | + font-size: 0.813rem; | ||
| 1015 | + cursor: pointer; | ||
| 1016 | + transition: all 0.2s; | ||
| 1017 | + width: 100%; | ||
| 1018 | + margin-top: 1rem; | ||
| 1019 | + } | ||
| 1020 | + .see-all:hover { | ||
| 1021 | + border-color: #E8C547; | ||
| 1022 | + color: #E8C547; | ||
| 1023 | + } | ||
| 1024 | + | ||
| 1025 | + /* Vista Toggle */ | ||
| 1026 | + .vista-toggle { | ||
| 1027 | + display: inline-flex; | ||
| 1028 | + background: #131312; | ||
| 1029 | + border: 1px solid #2E2E2C; | ||
| 1030 | + border-radius: 10px; | ||
| 1031 | + padding: 4px; | ||
| 1032 | + margin-bottom: 0.75rem; | ||
| 1033 | + } | ||
| 1034 | + | ||
| 1035 | + .toggle-btn { | ||
| 1036 | + padding: 0.5rem 1.25rem; | ||
| 1037 | + font-size: 0.813rem; | ||
| 1038 | + font-weight: 500; | ||
| 1039 | + color: #5A5650; | ||
| 1040 | + background: transparent; | ||
| 1041 | + border: none; | ||
| 1042 | + border-radius: 7px; | ||
| 1043 | + cursor: pointer; | ||
| 1044 | + transition: all 0.2s; | ||
| 1045 | + } | ||
| 1046 | + | ||
| 1047 | + .toggle-btn:hover { | ||
| 1048 | + color: #8A8578; | ||
| 1049 | + } | ||
| 1050 | + | ||
| 1051 | + .toggle-btn.active { | ||
| 1052 | + background: #F5F0E8; | ||
| 1053 | + color: #0A0A0A; | ||
| 1054 | + } | ||
| 1055 | + | ||
| 1056 | + /* Comparador inline - ajustado para 100vh */ | ||
| 1057 | + .comparador-selectors { | ||
| 1058 | + display: flex; | ||
| 1059 | + align-items: center; | ||
| 1060 | + gap: 1rem; | ||
| 1061 | + margin-bottom: 0.75rem; | ||
| 1062 | + } | ||
| 1063 | + | ||
| 1064 | + .selector-group { | ||
| 1065 | + flex: 1; | ||
| 1066 | + display: flex; | ||
| 1067 | + align-items: center; | ||
| 1068 | + gap: 0.5rem; | ||
| 1069 | + } | ||
| 1070 | + | ||
| 1071 | + .selector-dot { | ||
| 1072 | + width: 10px; | ||
| 1073 | + height: 10px; | ||
| 1074 | + border-radius: 50%; | ||
| 1075 | + flex-shrink: 0; | ||
| 1076 | + } | ||
| 1077 | + | ||
| 1078 | + .selector-group select { | ||
| 1079 | + flex: 1; | ||
| 1080 | + background: #1A1A18; | ||
| 1081 | + border: 1px solid #2E2E2C; | ||
| 1082 | + border-radius: 8px; | ||
| 1083 | + padding: 0.5rem 0.75rem; | ||
| 1084 | + color: #F5F0E8; | ||
| 1085 | + font-size: 0.813rem; | ||
| 1086 | + font-family: 'Instrument Sans', sans-serif; | ||
| 1087 | + cursor: pointer; | ||
| 1088 | + } | ||
| 1089 | + .selector-group select:focus { | ||
| 1090 | + outline: none; | ||
| 1091 | + border-color: #E8C547; | ||
| 1092 | + } | ||
| 1093 | + | ||
| 1094 | + .vs { | ||
| 1095 | + font-family: 'DM Mono', monospace; | ||
| 1096 | + font-size: 0.75rem; | ||
| 1097 | + color: #5A5650; | ||
| 1098 | + flex-shrink: 0; | ||
| 1099 | + } | ||
| 1100 | + | ||
| 1101 | + .chart-comparar { | ||
| 1102 | + gap: 1.5rem; | ||
| 1103 | + justify-content: center; | ||
| 1104 | + height: calc(100vh - 380px); | ||
| 1105 | + min-height: 140px; | ||
| 1106 | + max-height: 280px; | ||
| 1107 | + } | ||
| 1108 | + | ||
| 1109 | + .chart-comparar + .chart-legend + .kpis-comparar { | ||
| 1110 | + margin-top: 0; | ||
| 1111 | + } | ||
| 1112 | + | ||
| 1113 | + .bar-group { | ||
| 1114 | + display: flex; | ||
| 1115 | + flex-direction: column; | ||
| 1116 | + align-items: center; | ||
| 1117 | + height: 100%; | ||
| 1118 | + } | ||
| 1119 | + | ||
| 1120 | + .bars-pair { | ||
| 1121 | + display: flex; | ||
| 1122 | + align-items: flex-end; | ||
| 1123 | + gap: 4px; | ||
| 1124 | + flex: 1; | ||
| 1125 | + } | ||
| 1126 | + | ||
| 1127 | + .bar-a, .bar-b { | ||
| 1128 | + width: 20px; | ||
| 1129 | + border-radius: 4px 4px 0 0; | ||
| 1130 | + min-height: 4px; | ||
| 1131 | + background: var(--bar-color); | ||
| 1132 | + transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), filter 0.25s ease; | ||
| 1133 | + } | ||
| 1134 | + | ||
| 1135 | + .bar-group.active .bar-a, | ||
| 1136 | + .bar-group.active .bar-b { | ||
| 1137 | + filter: brightness(1.15); | ||
| 1138 | + } | ||
| 1139 | + | ||
| 1140 | + .bar-group { | ||
| 1141 | + cursor: pointer; | ||
| 1142 | + padding: 0.5rem 0.35rem; | ||
| 1143 | + margin: -0.5rem -0.35rem; | ||
| 1144 | + border-radius: 6px; | ||
| 1145 | + transition: background 0.3s cubic-bezier(0.4, 0, 0.2, 1); | ||
| 1146 | + } | ||
| 1147 | + | ||
| 1148 | + .bar-group.active { | ||
| 1149 | + background: rgba(42, 157, 143, 0.15); | ||
| 1150 | + } | ||
| 1151 | + | ||
| 1152 | + .bar-label.highlight { | ||
| 1153 | + color: #F5F0E8; | ||
| 1154 | + font-weight: 500; | ||
| 1155 | + transition: color 0.25s ease; | ||
| 1156 | + } | ||
| 1157 | + | ||
| 1158 | + .chart-legend { | ||
| 1159 | + display: flex; | ||
| 1160 | + justify-content: center; | ||
| 1161 | + gap: 2rem; | ||
| 1162 | + margin-top: 0.5rem; | ||
| 1163 | + margin-bottom: 0.75rem; | ||
| 1164 | + } | ||
| 1165 | + | ||
| 1166 | + .legend-item { | ||
| 1167 | + display: flex; | ||
| 1168 | + align-items: center; | ||
| 1169 | + gap: 0.5rem; | ||
| 1170 | + font-size: 0.75rem; | ||
| 1171 | + color: #8A8578; | ||
| 1172 | + } | ||
| 1173 | + | ||
| 1174 | + .legend-dot { | ||
| 1175 | + width: 10px; | ||
| 1176 | + height: 10px; | ||
| 1177 | + border-radius: 3px; | ||
| 1178 | + } | ||
| 1179 | + | ||
| 1180 | + /* KPIs comparativos - tabla */ | ||
| 1181 | + .kpis-tabla { | ||
| 1182 | + background: #131312; | ||
| 1183 | + border-radius: 10px; | ||
| 1184 | + padding: 0.5rem 1rem; | ||
| 1185 | + } | ||
| 1186 | + | ||
| 1187 | + .tabla-header { | ||
| 1188 | + display: grid; | ||
| 1189 | + grid-template-columns: 1fr 1fr 1fr; | ||
| 1190 | + gap: 1rem; | ||
| 1191 | + padding-bottom: 0.375rem; | ||
| 1192 | + border-bottom: 1px solid #2E2E2C; | ||
| 1193 | + margin-bottom: 0.375rem; | ||
| 1194 | + } | ||
| 1195 | + | ||
| 1196 | + .tabla-header .tabla-val { | ||
| 1197 | + font-family: 'DM Mono', monospace; | ||
| 1198 | + font-size: 0.625rem; | ||
| 1199 | + letter-spacing: 0.05em; | ||
| 1200 | + text-transform: uppercase; | ||
| 1201 | + text-align: center; | ||
| 1202 | + } | ||
| 1203 | + | ||
| 1204 | + .tabla-row { | ||
| 1205 | + display: grid; | ||
| 1206 | + grid-template-columns: 1fr 1fr 1fr; | ||
| 1207 | + gap: 1rem; | ||
| 1208 | + padding: 0.25rem 0; | ||
| 1209 | + } | ||
| 1210 | + | ||
| 1211 | + .tabla-label { | ||
| 1212 | + font-family: 'DM Mono', monospace; | ||
| 1213 | + font-size: 0.75rem; | ||
| 1214 | + font-weight: 500; | ||
| 1215 | + color: #8A8578; | ||
| 1216 | + display: flex; | ||
| 1217 | + align-items: center; | ||
| 1218 | + text-transform: uppercase; | ||
| 1219 | + letter-spacing: 0.03em; | ||
| 1220 | + } | ||
| 1221 | + | ||
| 1222 | + .tabla-val { | ||
| 1223 | + font-family: 'DM Mono', monospace; | ||
| 1224 | + font-size: 0.9375rem; | ||
| 1225 | + font-weight: 500; | ||
| 1226 | + color: #F5F0E8; | ||
| 1227 | + text-align: center; | ||
| 1228 | + font-variant-numeric: tabular-nums; | ||
| 1229 | + transition: opacity 0.2s ease; | ||
| 1230 | + } | ||
| 1231 | + | ||
| 1232 | + /* Insight comparador */ | ||
| 1233 | + .comparador-insight { | ||
| 1234 | + text-align: center; | ||
| 1235 | + padding: 1rem 1.5rem; | ||
| 1236 | + background: linear-gradient(90deg, rgba(232,197,71,0.05), rgba(74,139,110,0.05)); | ||
| 1237 | + border-radius: 10px; | ||
| 1238 | + border: 1px solid #2E2E2C; | ||
| 1239 | + font-size: 0.875rem; | ||
| 1240 | + color: #8A8578; | ||
| 1241 | + line-height: 1.6; | ||
| 1242 | + } | ||
| 1243 | + | ||
| 1244 | + .comparador-insight strong { | ||
| 1245 | + color: #F5F0E8; | ||
| 1246 | + } | ||
| 1247 | + | ||
| 1248 | + .insight-year { | ||
| 1249 | + font-family: 'DM Mono', monospace; | ||
| 1250 | + font-size: 0.813rem; | ||
| 1251 | + color: #E8C547; | ||
| 1252 | + } | ||
| 1253 | + | ||
| 1254 | + /* Responsive */ | ||
| 1255 | + @media (max-width: 640px) { | ||
| 1256 | + header, main { | ||
| 1257 | + padding: 1.25rem 1rem; | ||
| 1258 | + } | ||
| 1259 | + | ||
| 1260 | + .title-main { | ||
| 1261 | + flex-direction: column; | ||
| 1262 | + gap: 0.25rem; | ||
| 1263 | + } | ||
| 1264 | + | ||
| 1265 | + .codigo { | ||
| 1266 | + font-size: 1.25rem; | ||
| 1267 | + } | ||
| 1268 | + | ||
| 1269 | + h1 { | ||
| 1270 | + font-size: 1.375rem; | ||
| 1271 | + } | ||
| 1272 | + | ||
| 1273 | + .y-axis { | ||
| 1274 | + display: none; | ||
| 1275 | + } | ||
| 1276 | + | ||
| 1277 | + .chart { | ||
| 1278 | + border-left: none; | ||
| 1279 | + height: 120px; | ||
| 1280 | + } | ||
| 1281 | + | ||
| 1282 | + .kpis { | ||
| 1283 | + grid-template-columns: repeat(2, 1fr); | ||
| 1284 | + } | ||
| 1285 | + | ||
| 1286 | + .kpi-value { | ||
| 1287 | + font-size: 1.125rem; | ||
| 1288 | + } | ||
| 1289 | + | ||
| 1290 | + .top-summary { | ||
| 1291 | + flex-direction: column; | ||
| 1292 | + gap: 0.25rem; | ||
| 1293 | + } | ||
| 1294 | + | ||
| 1295 | + .comparador-modal { | ||
| 1296 | + padding: 1.5rem; | ||
| 1297 | + } | ||
| 1298 | + | ||
| 1299 | + .comparador-selectors { | ||
| 1300 | + flex-direction: column; | ||
| 1301 | + gap: 0.75rem; | ||
| 1302 | + } | ||
| 1303 | + | ||
| 1304 | + .vs { | ||
| 1305 | + display: none; | ||
| 1306 | + } | ||
| 1307 | + | ||
| 1308 | + .kpi-values { | ||
| 1309 | + gap: 1rem; | ||
| 1310 | + } | ||
| 1311 | + | ||
| 1312 | + .kpi-val { | ||
| 1313 | + font-size: 0.75rem; | ||
| 1314 | + min-width: 60px; | ||
| 1315 | + } | ||
| 1316 | + | ||
| 1317 | + .comp-bar { | ||
| 1318 | + width: 12px; | ||
| 1319 | + } | ||
| 1320 | + | ||
| 1321 | + .grouped-chart { | ||
| 1322 | + gap: 0.5rem; | ||
| 1323 | + } | ||
| 1324 | + } | ||
| 1325 | + | ||
| 1326 | + /* ========== TEMA CLARO ========== */ | ||
| 1327 | + .tema-claro { | ||
| 1328 | + background: #FAFAF8; | ||
| 1329 | + color: #1A1A18; | ||
| 1330 | + } | ||
| 1331 | + | ||
| 1332 | + .tema-claro header { | ||
| 1333 | + border-bottom-color: #E5E5E0; | ||
| 1334 | + } | ||
| 1335 | + | ||
| 1336 | + .tema-claro .breadcrumb a { | ||
| 1337 | + color: #8A8578; | ||
| 1338 | + } | ||
| 1339 | + .tema-claro .breadcrumb a:hover { | ||
| 1340 | + color: #1A1A18; | ||
| 1341 | + } | ||
| 1342 | + | ||
| 1343 | + .tema-claro .codigo { | ||
| 1344 | + color: #8A8578; | ||
| 1345 | + } | ||
| 1346 | + | ||
| 1347 | + .tema-claro h1 { | ||
| 1348 | + color: #1A1A18; | ||
| 1349 | + } | ||
| 1350 | + | ||
| 1351 | + .tema-claro .help-icon { | ||
| 1352 | + color: #8A8578; | ||
| 1353 | + } | ||
| 1354 | + .tema-claro .help-icon:hover, | ||
| 1355 | + .tema-claro .help-icon.active { | ||
| 1356 | + color: #C9A227; | ||
| 1357 | + } | ||
| 1358 | + | ||
| 1359 | + .tema-claro .badge { | ||
| 1360 | + background: #F0F0EC; | ||
| 1361 | + border-color: #E5E5E0; | ||
| 1362 | + color: #5A5650; | ||
| 1363 | + } | ||
| 1364 | + | ||
| 1365 | + .tema-claro .years { | ||
| 1366 | + color: #8A8578; | ||
| 1367 | + } | ||
| 1368 | + | ||
| 1369 | + .tema-claro .info-panel { | ||
| 1370 | + background: #F5F5F3; | ||
| 1371 | + border-bottom-color: #E5E5E0; | ||
| 1372 | + } | ||
| 1373 | + | ||
| 1374 | + .tema-claro .info-content h4 { | ||
| 1375 | + color: #8A8578; | ||
| 1376 | + } | ||
| 1377 | + | ||
| 1378 | + .tema-claro .info-content p { | ||
| 1379 | + color: #5A5650; | ||
| 1380 | + } | ||
| 1381 | + | ||
| 1382 | + .tema-claro .variacion { | ||
| 1383 | + border-left-color: #D5D5D0; | ||
| 1384 | + } | ||
| 1385 | + | ||
| 1386 | + .tema-claro .variacion-rango { | ||
| 1387 | + color: #8A8578; | ||
| 1388 | + } | ||
| 1389 | + | ||
| 1390 | + .tema-claro .vista-toggle { | ||
| 1391 | + background: #F0F0EC; | ||
| 1392 | + border-color: #E5E5E0; | ||
| 1393 | + } | ||
| 1394 | + | ||
| 1395 | + .tema-claro .toggle-btn { | ||
| 1396 | + color: #8A8578; | ||
| 1397 | + } | ||
| 1398 | + .tema-claro .toggle-btn:hover { | ||
| 1399 | + color: #5A5650; | ||
| 1400 | + } | ||
| 1401 | + .tema-claro .toggle-btn.active { | ||
| 1402 | + background: #1A1A18; | ||
| 1403 | + color: #FAFAF8; | ||
| 1404 | + } | ||
| 1405 | + | ||
| 1406 | + .tema-claro .entity-selector .selector-label { | ||
| 1407 | + color: #8A8578; | ||
| 1408 | + } | ||
| 1409 | + | ||
| 1410 | + .tema-claro .selector-btn { | ||
| 1411 | + background: transparent; | ||
| 1412 | + border: none; | ||
| 1413 | + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08); | ||
| 1414 | + color: #1A1A18; | ||
| 1415 | + } | ||
| 1416 | + .tema-claro .selector-btn:hover { | ||
| 1417 | + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15); | ||
| 1418 | + } | ||
| 1419 | + .tema-claro .selector-btn svg { | ||
| 1420 | + color: #8A8578; | ||
| 1421 | + } | ||
| 1422 | + | ||
| 1423 | + .tema-claro .chart-title { | ||
| 1424 | + color: #8A8578; | ||
| 1425 | + } | ||
| 1426 | + | ||
| 1427 | + .tema-claro .chart-period { | ||
| 1428 | + color: #333333; | ||
| 1429 | + } | ||
| 1430 | + | ||
| 1431 | + .tema-claro .y-label { | ||
| 1432 | + color: #8A8578; | ||
| 1433 | + } | ||
| 1434 | + | ||
| 1435 | + .tema-claro .chart { | ||
| 1436 | + border-left-color: #E5E5E0; | ||
| 1437 | + } | ||
| 1438 | + | ||
| 1439 | + .tema-claro .bar-container .bar { | ||
| 1440 | + background: #2a9d8f; | ||
| 1441 | + } | ||
| 1442 | + | ||
| 1443 | + .tema-claro .bar-container .bar.hovered { | ||
| 1444 | + background: #264653; | ||
| 1445 | + } | ||
| 1446 | + | ||
| 1447 | + /* Las barras de comparación mantienen su --bar-color */ | ||
| 1448 | + .tema-claro .bar-a, | ||
| 1449 | + .tema-claro .bar-b { | ||
| 1450 | + background: var(--bar-color); | ||
| 1451 | + } | ||
| 1452 | + | ||
| 1453 | + .tema-claro .bar-label { | ||
| 1454 | + color: #333333; | ||
| 1455 | + } | ||
| 1456 | + | ||
| 1457 | + .tema-claro .kpis { | ||
| 1458 | + background: transparent; | ||
| 1459 | + } | ||
| 1460 | + | ||
| 1461 | + .tema-claro .kpi { | ||
| 1462 | + background: transparent; | ||
| 1463 | + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08); | ||
| 1464 | + } | ||
| 1465 | + | ||
| 1466 | + .tema-claro .kpi-value { | ||
| 1467 | + color: #1A1A18; | ||
| 1468 | + } | ||
| 1469 | + | ||
| 1470 | + .tema-claro .kpi-label { | ||
| 1471 | + color: #8A8578; | ||
| 1472 | + } | ||
| 1473 | + | ||
| 1474 | + .tema-claro .top-section h3 { | ||
| 1475 | + color: #8A8578; | ||
| 1476 | + } | ||
| 1477 | + | ||
| 1478 | + .tema-claro .top-context { | ||
| 1479 | + color: #8A8578; | ||
| 1480 | + } | ||
| 1481 | + | ||
| 1482 | + .tema-claro .top-item { | ||
| 1483 | + background: transparent; | ||
| 1484 | + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08); | ||
| 1485 | + } | ||
| 1486 | + | ||
| 1487 | + .tema-claro .top-rank { | ||
| 1488 | + color: #8A8578; | ||
| 1489 | + } | ||
| 1490 | + | ||
| 1491 | + .tema-claro .top-name { | ||
| 1492 | + color: #1A1A18; | ||
| 1493 | + } | ||
| 1494 | + | ||
| 1495 | + .tema-claro .top-bar-bg { | ||
| 1496 | + background: #F0F0EC; | ||
| 1497 | + } | ||
| 1498 | + | ||
| 1499 | + .tema-claro .top-bar { | ||
| 1500 | + background: #2a9d8f; | ||
| 1501 | + } | ||
| 1502 | + | ||
| 1503 | + .tema-claro .top-pct { | ||
| 1504 | + color: #333333; | ||
| 1505 | + } | ||
| 1506 | + | ||
| 1507 | + /* Comparador - tema claro */ | ||
| 1508 | + .tema-claro .comparador-selectors .selector-group select { | ||
| 1509 | + background: transparent; | ||
| 1510 | + border: none; | ||
| 1511 | + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08); | ||
| 1512 | + color: #1A1A18; | ||
| 1513 | + } | ||
| 1514 | + .tema-claro .comparador-selectors .selector-group select:focus { | ||
| 1515 | + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15); | ||
| 1516 | + outline: none; | ||
| 1517 | + } | ||
| 1518 | + | ||
| 1519 | + .tema-claro .vs { | ||
| 1520 | + color: #8A8578; | ||
| 1521 | + } | ||
| 1522 | + | ||
| 1523 | + .tema-claro .chart-legend .legend-item { | ||
| 1524 | + color: #5A5650; | ||
| 1525 | + } | ||
| 1526 | + | ||
| 1527 | + .tema-claro .kpis-tabla { | ||
| 1528 | + background: transparent; | ||
| 1529 | + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08); | ||
| 1530 | + } | ||
| 1531 | + | ||
| 1532 | + .tema-claro .tabla-header { | ||
| 1533 | + border-bottom-color: #E5E5E0; | ||
| 1534 | + } | ||
| 1535 | + | ||
| 1536 | + .tema-claro .tabla-label { | ||
| 1537 | + color: #5A5650; | ||
| 1538 | + } | ||
| 1539 | + | ||
| 1540 | + .tema-claro .tabla-val { | ||
| 1541 | + color: #1A1A18; | ||
| 1542 | + } | ||
| 1543 | + | ||
| 1544 | + .tema-claro .bar-group.active { | ||
| 1545 | + background: rgba(42, 157, 143, 0.12); | ||
| 1546 | + } | ||
| 1547 | + | ||
| 1548 | + .tema-claro .bar-label.highlight { | ||
| 1549 | + color: #1A1A18; | ||
| 1550 | + } | ||
| 1551 | +</style> |
static/Clasificadores.svelte
0 → 100644
| 1 | +<script> | ||
| 2 | + import { onMount } from 'svelte'; | ||
| 3 | + | ||
| 4 | + let hovered = null; | ||
| 5 | + let activeModal = null; | ||
| 6 | + let mounted = false; | ||
| 7 | + | ||
| 8 | + onMount(() => { setTimeout(() => { mounted = true; }, 80); }); | ||
| 9 | + | ||
| 10 | + const C = { | ||
| 11 | + bg: '#0A0A0A', bgAlt: '#111111', surface: '#161616', | ||
| 12 | + card: '#131313', cardHover: '#1A1A1A', | ||
| 13 | + accent: '#E8C547', green: '#4A8B6E', | ||
| 14 | + text: '#F5F0E8', muted: '#8A8578', dim: '#5A5650', | ||
| 15 | + border: '#222220', borderLight: '#2E2E2C', | ||
| 16 | + }; | ||
| 17 | + | ||
| 18 | + const MODALS = { | ||
| 19 | + quien: { | ||
| 20 | + title: '¿Quién gasta o recibe?', tech: 'CLASIFICADOR INSTITUCIONAL', | ||
| 21 | + everyday: 'Es como preguntar "¿quién en la familia maneja plata?". Papá tiene su sueldo, mamá maneja el negocio, el abuelo cobra su renta. Cada uno es una "entidad" distinta con su propio presupuesto.', | ||
| 22 | + budget: 'El clasificador institucional cataloga a todas las entidades del sector público: ministerios, gobernaciones, municipios, empresas públicas, universidades. Cada una tiene un código único y un presupuesto asignado.', | ||
| 23 | + example: 'Ejemplo: El Ministerio de Educación (código 040) y el Gobierno Autónomo de La Paz (código 126) son entidades distintas, cada una con sus propios ingresos y gastos.', | ||
| 24 | + color: '#F5F0E8', | ||
| 25 | + }, | ||
| 26 | + rubros: { | ||
| 27 | + title: '¿Qué tipo de ingreso es?', tech: 'CLASIFICADOR DE RECURSOS POR RUBROS', | ||
| 28 | + everyday: 'María gana un sueldo de su trabajo, cobra alquiler de un cuartito, y un amigo le prestó plata. Son tres tipos distintos de ingreso: laboral, patrimonial, y crédito.', | ||
| 29 | + budget: 'En el presupuesto, el Estado recibe ingresos tributarios (impuestos = el "sueldo"), regalías por explotación de recursos (= el "alquiler"), y créditos internacionales (= el "préstamo"). El rubro te dice qué naturaleza tiene cada ingreso.', | ||
| 30 | + example: 'Ejemplo: Los Bs 2.400M que Bolivia recibe por IDH son un rubro distinto a los Bs 800M de crédito del BID, aunque ambos financien al mismo ministerio.', | ||
| 31 | + color: '#E8C547', | ||
| 32 | + }, | ||
| 33 | + fuentes: { | ||
| 34 | + title: '¿Quién pone la plata?', tech: 'CLASIFICADOR DE FUENTES DE FINANCIAMIENTO', | ||
| 35 | + everyday: 'María sabe que su sueldo se lo paga la empresa TechCorp, el alquiler se lo paga su inquilino Juan, y el préstamo se lo hizo el Banco Nacional. Son las fuentes: quién firma el cheque.', | ||
| 36 | + budget: 'Las fuentes son los bolsillos específicos: el TGN (Tesoro General de la Nación), recursos propios de cada entidad, crédito externo del BID o Banco Mundial, o donaciones de cooperación.', | ||
| 37 | + example: 'Ejemplo: Un crédito del BID y uno del Banco Mundial son el mismo rubro (crédito externo) pero distinta fuente. La fuente dice quién exactamente pone la plata.', | ||
| 38 | + color: '#E8C547', | ||
| 39 | + }, | ||
| 40 | + objeto: { | ||
| 41 | + title: '¿En qué se gasta concretamente?', tech: 'CLASIFICADOR POR OBJETO DEL GASTO', | ||
| 42 | + everyday: 'Cuando María gasta, compra cosas concretas: arroz, paga la luz, le paga al albañil. Cada compra es un "objeto" distinto — algo tangible que se puede listar.', | ||
| 43 | + budget: 'El Objeto del Gasto es exactamente eso: sueldos, consultorías, materiales de escritorio, combustible, medicamentos. Es la lista del supermercado del Estado.', | ||
| 44 | + example: 'Ejemplo: El grupo 10000 "Servicios Personales" incluye sueldos y aguinaldos. El 20000 "Servicios No Personales" incluye consultorías y alquileres.', | ||
| 45 | + color: '#4A8B6E', | ||
| 46 | + }, | ||
| 47 | + sector: { | ||
| 48 | + title: '¿A qué sector de la economía va?', tech: 'CLASIFICADOR POR SECTORES ECONÓMICOS', | ||
| 49 | + everyday: 'Si María contrata un albañil, esa plata va al sector construcción. Si compra medicina, va al sector salud. No importa qué compró — importa qué parte de la economía mueve.', | ||
| 50 | + budget: 'Este clasificador mide el impacto económico: cuánto va a infraestructura, cuánto a servicios, cuánto a industria. Permite evaluar cómo las decisiones fiscales afectan a cada sector.', | ||
| 51 | + example: 'Ejemplo: Un hospital nuevo es "construcción" en Objeto del Gasto pero "salud" en Sector Económico. Misma plata, dos lentes distintos.', | ||
| 52 | + color: '#4A8B6E', | ||
| 53 | + }, | ||
| 54 | + finalidad: { | ||
| 55 | + title: '¿Para qué se gasta?', tech: 'CLASIFICADOR POR FINALIDAD Y FUNCIÓN', | ||
| 56 | + everyday: 'María compró arroz (objeto), pero ¿para qué? Para alimentar a su familia. Compró cemento: para tener casa. El "para qué" es el objetivo final, no el insumo.', | ||
| 57 | + budget: 'Este clasificador agrupa por propósito: educación, defensa, salud, protección social. No importa si se gastó en sueldos o en cemento — lo que importa es el fin.', | ||
| 58 | + example: 'Ejemplo: El sueldo de un profesor y la construcción de una escuela son objetos de gasto distintos, pero ambos tienen la misma finalidad: Educación.', | ||
| 59 | + color: '#4A8B6E', | ||
| 60 | + }, | ||
| 61 | + donde: { | ||
| 62 | + title: '¿Dónde se gasta?', tech: 'CLASIFICADOR GEOGRÁFICO', | ||
| 63 | + everyday: 'María manda plata a su mamá en Cochabamba y paga el alquiler en La Paz. No es lo mismo gastar en la ciudad que en el área rural, en Santa Cruz que en Potosí.', | ||
| 64 | + budget: 'La dimensión geográfica permite ver cómo se distribuyen los recursos en el territorio: por departamento, municipio o región. Revela las prioridades territoriales del Estado.', | ||
| 65 | + example: 'Ejemplo: Del presupuesto de Educación, ¿cuánto va a La Paz vs. Beni? Este clasificador responde esa pregunta.', | ||
| 66 | + color: '#4A8B6E', | ||
| 67 | + }, | ||
| 68 | + }; | ||
| 69 | + | ||
| 70 | + const WIDGETS_INGRESOS = [ | ||
| 71 | + { id: 'rubros', q: '¿Qué tipo de ingreso?', d: 'Impuestos, regalías, ventas, créditos — la naturaleza de cada ingreso', t: 'Recursos por rubros' }, | ||
| 72 | + { id: 'fuentes', q: '¿Quién pone la plata?', d: 'TGN, crédito externo, donaciones — de qué bolsillo sale', t: 'Fuentes de financiamiento' }, | ||
| 73 | + ]; | ||
| 74 | + | ||
| 75 | + const WIDGETS_EN_QUE = [ | ||
| 76 | + { id: 'objeto', q: 'Objeto del gasto', d: 'Sueldos, consultorías, materiales — la lista concreta', t: 'Objeto del gasto' }, | ||
| 77 | + { id: 'sector', q: 'Sector económico', d: 'Industria, construcción, servicios — a qué sector va', t: 'Sectores económicos' }, | ||
| 78 | + ]; | ||
| 79 | + | ||
| 80 | + const WIDGETS_BOTTOM = [ | ||
| 81 | + { id: 'finalidad', q: '¿Para qué se gasta?', d: 'Educación, salud, defensa — el objetivo final', t: 'Finalidad y función' }, | ||
| 82 | + { id: 'donde', q: '¿Dónde se gasta?', d: 'Departamentos, municipios — distribución territorial', t: 'Geográfico' }, | ||
| 83 | + ]; | ||
| 84 | + | ||
| 85 | + $: modal = activeModal ? MODALS[activeModal] : null; | ||
| 86 | + | ||
| 87 | + function openModal(e, id) { | ||
| 88 | + e.stopPropagation(); | ||
| 89 | + activeModal = id; | ||
| 90 | + } | ||
| 91 | + function closeModal() { activeModal = null; } | ||
| 92 | +</script> | ||
| 93 | + | ||
| 94 | +<div class="page" class:mounted> | ||
| 95 | + | ||
| 96 | + <!-- Header --> | ||
| 97 | + <header> | ||
| 98 | + <div class="header-left"> | ||
| 99 | + <a href="/" class="back-link"> | ||
| 100 | + <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M19 12H5M12 19l-7-7 7-7"/></svg> | ||
| 101 | + </a> | ||
| 102 | + <div class="live-dot"></div> | ||
| 103 | + <span class="header-brand">PRESUPUESTO ABIERTO</span> | ||
| 104 | + </div> | ||
| 105 | + <span class="header-right">Explorar</span> | ||
| 106 | + </header> | ||
| 107 | + | ||
| 108 | + <main> | ||
| 109 | + | ||
| 110 | + <!-- Title --> | ||
| 111 | + <div class="page-title anim" style="--d:0ms"> | ||
| 112 | + <h1>Elige cómo explorar el presupuesto</h1> | ||
| 113 | + <p>Cada clasificador es una forma distinta de ver la misma información. Haz clic en <span class="help-inline">?</span> para entender qué es cada uno.</p> | ||
| 114 | + </div> | ||
| 115 | + | ||
| 116 | + <!-- ═══ ¿QUIÉN? ═══ --> | ||
| 117 | + <div class="anim" style="--d:100ms"> | ||
| 118 | + <div class="group"> | ||
| 119 | + <div class="group-accent" style="--c: {C.text}"></div> | ||
| 120 | + <div class="group-header"> | ||
| 121 | + <div class="group-icon" style="--c: {C.text}"> | ||
| 122 | + <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={C.text} stroke-width="1.5" stroke-linecap="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg> | ||
| 123 | + </div> | ||
| 124 | + <div> | ||
| 125 | + <div class="group-label" style="color:{C.text}">¿QUIÉN?</div> | ||
| 126 | + <div class="group-sub">La entidad que gasta o recibe</div> | ||
| 127 | + </div> | ||
| 128 | + </div> | ||
| 129 | + | ||
| 130 | + <div | ||
| 131 | + class="widget-card widget-h" | ||
| 132 | + class:wh={hovered === 'quien'} | ||
| 133 | + on:mouseenter={() => hovered = 'quien'} | ||
| 134 | + on:mouseleave={() => hovered = null} | ||
| 135 | + role="button" tabindex="0" | ||
| 136 | + > | ||
| 137 | + <div class="widget-h-left"> | ||
| 138 | + <div class="widget-q" style="font-size:16px">Instituciones públicas</div> | ||
| 139 | + <div class="widget-desc">Ministerios, gobernaciones, municipios, empresas públicas, universidades</div> | ||
| 140 | + </div> | ||
| 141 | + <div class="widget-h-right"> | ||
| 142 | + <span class="widget-tech">INSTITUCIONAL</span> | ||
| 143 | + <button class="help-btn help-sm" on:click={(e) => openModal(e, 'quien')}>?</button> | ||
| 144 | + <span class="widget-arrow" class:arrow-show={hovered === 'quien'}>→</span> | ||
| 145 | + </div> | ||
| 146 | + </div> | ||
| 147 | + </div> | ||
| 148 | + </div> | ||
| 149 | + | ||
| 150 | + <!-- ═══ INGRESOS + GASTOS ═══ --> | ||
| 151 | + <div class="main-grid"> | ||
| 152 | + | ||
| 153 | + <!-- INGRESOS --> | ||
| 154 | + <div class="anim col-stretch" style="--d:200ms"> | ||
| 155 | + <div class="group group-stretch"> | ||
| 156 | + <div class="group-accent" style="--c: {C.accent}"></div> | ||
| 157 | + <div class="group-header"> | ||
| 158 | + <div class="group-icon" style="--c: {C.accent}"> | ||
| 159 | + <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke={C.accent} stroke-width="2" stroke-linecap="round"><path d="M12 19V5M5 12l7-7 7 7"/></svg> | ||
| 160 | + </div> | ||
| 161 | + <div> | ||
| 162 | + <div class="group-label" style="color:{C.accent}">INGRESOS</div> | ||
| 163 | + <div class="group-sub">¿De dónde viene la plata?</div> | ||
| 164 | + </div> | ||
| 165 | + </div> | ||
| 166 | + | ||
| 167 | + <div class="widgets-stretch"> | ||
| 168 | + {#each WIDGETS_INGRESOS as w} | ||
| 169 | + <div | ||
| 170 | + class="widget-card widget-grow" | ||
| 171 | + class:wh={hovered === w.id} | ||
| 172 | + on:mouseenter={() => hovered = w.id} | ||
| 173 | + on:mouseleave={() => hovered = null} | ||
| 174 | + role="button" tabindex="0" | ||
| 175 | + > | ||
| 176 | + <div class="widget-top"> | ||
| 177 | + <h4 class="widget-q">{w.q}</h4> | ||
| 178 | + <button class="help-btn" on:click={(e) => openModal(e, w.id)}>?</button> | ||
| 179 | + </div> | ||
| 180 | + <p class="widget-desc widget-desc-grow">{w.d}</p> | ||
| 181 | + <div class="widget-footer"> | ||
| 182 | + <span class="widget-tech">{w.t}</span> | ||
| 183 | + <span class="widget-arrow" class:arrow-show={hovered === w.id}>→</span> | ||
| 184 | + </div> | ||
| 185 | + <div class="widget-glow" style="background:radial-gradient(circle at 50% 50%,{C.accent}06,transparent 70%)"></div> | ||
| 186 | + </div> | ||
| 187 | + {/each} | ||
| 188 | + </div> | ||
| 189 | + </div> | ||
| 190 | + </div> | ||
| 191 | + | ||
| 192 | + <!-- GASTOS --> | ||
| 193 | + <div class="anim col-stretch" style="--d:320ms"> | ||
| 194 | + <div class="group"> | ||
| 195 | + <div class="group-accent" style="--c: {C.green}"></div> | ||
| 196 | + <div class="group-header"> | ||
| 197 | + <div class="group-icon" style="--c: {C.green}"> | ||
| 198 | + <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke={C.green} stroke-width="2" stroke-linecap="round"><path d="M12 5v14M5 12l7 7 7-7"/></svg> | ||
| 199 | + </div> | ||
| 200 | + <div> | ||
| 201 | + <div class="group-label" style="color:{C.green}">GASTOS</div> | ||
| 202 | + <div class="group-sub">¿Cómo se gasta la plata?</div> | ||
| 203 | + </div> | ||
| 204 | + </div> | ||
| 205 | + | ||
| 206 | + <div class="group-widgets"> | ||
| 207 | + | ||
| 208 | + <!-- ¿En qué? sub-group --> | ||
| 209 | + <div class="sub-group"> | ||
| 210 | + <div class="sub-group-accent" style="--c: {C.green}"></div> | ||
| 211 | + <div class="sub-group-header"> | ||
| 212 | + <div class="sub-group-left"> | ||
| 213 | + <div class="sub-group-icon" style="--c: {C.green}"> | ||
| 214 | + <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke={C.green} stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg> | ||
| 215 | + </div> | ||
| 216 | + <div> | ||
| 217 | + <div class="sub-group-label">¿En qué se gasta?</div> | ||
| 218 | + <div class="sub-group-tag" style="color:{C.green}90">DOS CLASIFICADORES</div> | ||
| 219 | + </div> | ||
| 220 | + </div> | ||
| 221 | + <button class="help-btn help-sm" on:click={(e) => openModal(e, 'objeto')}>?</button> | ||
| 222 | + </div> | ||
| 223 | + | ||
| 224 | + <div class="sub-grid"> | ||
| 225 | + {#each WIDGETS_EN_QUE as w} | ||
| 226 | + <div | ||
| 227 | + class="widget-card sub-card" | ||
| 228 | + class:wh={hovered === w.id} | ||
| 229 | + on:mouseenter={() => hovered = w.id} | ||
| 230 | + on:mouseleave={() => hovered = null} | ||
| 231 | + role="button" tabindex="0" | ||
| 232 | + > | ||
| 233 | + <div class="widget-top"> | ||
| 234 | + <h4 class="widget-q sub-q">{w.q}</h4> | ||
| 235 | + <button class="help-btn help-xs" on:click={(e) => openModal(e, w.id)}>?</button> | ||
| 236 | + </div> | ||
| 237 | + <p class="widget-desc sub-desc">{w.d}</p> | ||
| 238 | + <div class="widget-footer sub-footer"> | ||
| 239 | + <span class="widget-tech sub-tech">{w.t}</span> | ||
| 240 | + <span class="widget-arrow" class:arrow-show={hovered === w.id}>→</span> | ||
| 241 | + </div> | ||
| 242 | + <div class="widget-glow" style="background:radial-gradient(circle at 50% 50%,{C.green}06,transparent 70%)"></div> | ||
| 243 | + </div> | ||
| 244 | + {/each} | ||
| 245 | + </div> | ||
| 246 | + </div> | ||
| 247 | + | ||
| 248 | + <!-- Bottom row --> | ||
| 249 | + <div class="bottom-grid"> | ||
| 250 | + {#each WIDGETS_BOTTOM as w} | ||
| 251 | + <div | ||
| 252 | + class="widget-card" | ||
| 253 | + class:wh={hovered === w.id} | ||
| 254 | + on:mouseenter={() => hovered = w.id} | ||
| 255 | + on:mouseleave={() => hovered = null} | ||
| 256 | + role="button" tabindex="0" | ||
| 257 | + > | ||
| 258 | + <div class="widget-top"> | ||
| 259 | + <h4 class="widget-q">{w.q}</h4> | ||
| 260 | + <button class="help-btn" on:click={(e) => openModal(e, w.id)}>?</button> | ||
| 261 | + </div> | ||
| 262 | + <p class="widget-desc">{w.d}</p> | ||
| 263 | + <div class="widget-footer"> | ||
| 264 | + <span class="widget-tech">{w.t}</span> | ||
| 265 | + <span class="widget-arrow" class:arrow-show={hovered === w.id}>→</span> | ||
| 266 | + </div> | ||
| 267 | + <div class="widget-glow" style="background:radial-gradient(circle at 50% 50%,{C.green}06,transparent 70%)"></div> | ||
| 268 | + </div> | ||
| 269 | + {/each} | ||
| 270 | + </div> | ||
| 271 | + </div> | ||
| 272 | + </div> | ||
| 273 | + </div> | ||
| 274 | + </div> | ||
| 275 | + | ||
| 276 | + </main> | ||
| 277 | +</div> | ||
| 278 | + | ||
| 279 | +<!-- ═══ MODAL ═══ --> | ||
| 280 | +{#if modal} | ||
| 281 | + <!-- svelte-ignore a11y-click-events-have-key-events --> | ||
| 282 | + <div class="modal-overlay" on:click={closeModal} role="dialog" aria-modal="true"> | ||
| 283 | + <!-- svelte-ignore a11y-click-events-have-key-events --> | ||
| 284 | + <div class="modal-box" on:click|stopPropagation role="document"> | ||
| 285 | + <button class="modal-close" on:click={closeModal}> | ||
| 286 | + <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6L6 18M6 6l12 12"/></svg> | ||
| 287 | + </button> | ||
| 288 | + | ||
| 289 | + <div class="modal-badge" style="background:{modal.color}12;color:{modal.color}">{modal.tech}</div> | ||
| 290 | + <h2 class="modal-title">{modal.title}</h2> | ||
| 291 | + | ||
| 292 | + <div class="modal-section"> | ||
| 293 | + <div class="modal-icon">👤</div> | ||
| 294 | + <div> | ||
| 295 | + <div class="modal-label">EN LA VIDA COTIDIANA</div> | ||
| 296 | + <p class="modal-text">{modal.everyday}</p> | ||
| 297 | + </div> | ||
| 298 | + </div> | ||
| 299 | + | ||
| 300 | + <div class="modal-divider"></div> | ||
| 301 | + | ||
| 302 | + <div class="modal-section"> | ||
| 303 | + <div class="modal-icon">🏛️</div> | ||
| 304 | + <div> | ||
| 305 | + <div class="modal-label">EN EL PRESUPUESTO</div> | ||
| 306 | + <p class="modal-text">{modal.budget}</p> | ||
| 307 | + </div> | ||
| 308 | + </div> | ||
| 309 | + | ||
| 310 | + <div class="modal-example" style="border-left-color:{modal.color}"> | ||
| 311 | + <p>{modal.example}</p> | ||
| 312 | + </div> | ||
| 313 | + </div> | ||
| 314 | + </div> | ||
| 315 | +{/if} | ||
| 316 | + | ||
| 317 | + | ||
| 318 | +<style> | ||
| 319 | + @import url('https://fonts.googleapis.com/css2?family=Source+Serif+4:opsz,wght@8..60,300;8..60,400;8..60,600;8..60,700;8..60,900&family=JetBrains+Mono:wght@300;400;500&family=DM+Sans:wght@400;500;600;700&display=swap'); | ||
| 320 | + | ||
| 321 | + :global(*) { margin: 0; padding: 0; box-sizing: border-box; } | ||
| 322 | + :global(::selection) { background: #E8C547; color: #0A0A0A; } | ||
| 323 | + :global(::-webkit-scrollbar) { width: 5px; } | ||
| 324 | + :global(::-webkit-scrollbar-track) { background: #0A0A0A; } | ||
| 325 | + :global(::-webkit-scrollbar-thumb) { background: #5A5650; border-radius: 3px; } | ||
| 326 | + | ||
| 327 | + @keyframes pulseGlow { 0%,100%{opacity:0.4}50%{opacity:0.9} } | ||
| 328 | + @keyframes mIn { from{opacity:0;transform:translateY(16px) scale(0.97)}to{opacity:1;transform:translateY(0) scale(1)} } | ||
| 329 | + | ||
| 330 | + .page { background: #0A0A0A; color: #F5F0E8; min-height: 100vh; font-family: 'Source Serif 4', Georgia, serif; } | ||
| 331 | + | ||
| 332 | + /* ── Entrance animations ── */ | ||
| 333 | + .anim { | ||
| 334 | + opacity: 0; transform: translateY(24px); | ||
| 335 | + transition: all 0.7s cubic-bezier(0.16,1,0.3,1); | ||
| 336 | + transition-delay: var(--d, 0ms); | ||
| 337 | + } | ||
| 338 | + .mounted .anim { opacity: 1; transform: translateY(0); } | ||
| 339 | + | ||
| 340 | + /* ── Header ── */ | ||
| 341 | + header { | ||
| 342 | + padding: 18px 40px; display: flex; justify-content: space-between; align-items: center; | ||
| 343 | + border-bottom: 1px solid #222220; position: sticky; top: 0; z-index: 50; | ||
| 344 | + background: rgba(10,10,10,0.93); backdrop-filter: blur(12px); | ||
| 345 | + } | ||
| 346 | + .header-left { display: flex; align-items: center; gap: 12px; } | ||
| 347 | + .back-link { color: #5A5650; display: flex; align-items: center; transition: color 0.3s; text-decoration: none; } | ||
| 348 | + .back-link:hover { color: #F5F0E8; } | ||
| 349 | + .live-dot { | ||
| 350 | + width: 7px; height: 7px; border-radius: 50%; background: #4A8B6E; | ||
| 351 | + animation: pulseGlow 2s ease-in-out infinite; | ||
| 352 | + box-shadow: 0 0 10px rgba(74,139,110,0.37); | ||
| 353 | + } | ||
| 354 | + .header-brand { font-family: 'JetBrains Mono', monospace; font-size: 11px; letter-spacing: 0.2em; color: #8A8578; font-weight: 500; } | ||
| 355 | + .header-right { font-family: 'DM Sans', sans-serif; font-size: 14px; font-weight: 600; color: #5A5650; letter-spacing: 0.04em; } | ||
| 356 | + | ||
| 357 | + main { max-width: 1120px; margin: 0 auto; padding: 36px 36px 100px; } | ||
| 358 | + | ||
| 359 | + /* ── Page title ── */ | ||
| 360 | + .page-title { margin-bottom: 28px; padding: 0 2px; } | ||
| 361 | + .page-title h1 { font-size: 28px; font-weight: 900; letter-spacing: -0.02em; line-height: 1.1; } | ||
| 362 | + .page-title p { font-family: 'DM Sans', sans-serif; font-size: 14px; color: #5A5650; margin-top: 6px; line-height: 1.5; } | ||
| 363 | + .help-inline { | ||
| 364 | + font-family: 'Source Serif 4', serif; font-weight: 700; color: #8A8578; | ||
| 365 | + border: 1px solid #222220; border-radius: 50%; padding: 0 5px; font-size: 11px; | ||
| 366 | + } | ||
| 367 | + | ||
| 368 | + /* ── Group (level 1) ── */ | ||
| 369 | + .group { | ||
| 370 | + background: #111111; border: 1px solid #222220; border-radius: 24px; | ||
| 371 | + padding: 20px 18px 18px; position: relative; overflow: hidden; | ||
| 372 | + } | ||
| 373 | + .group-stretch { | ||
| 374 | + display: flex; flex-direction: column; height: 100%; | ||
| 375 | + } | ||
| 376 | + .group-accent { | ||
| 377 | + position: absolute; top: 0; left: 0; right: 0; height: 2px; | ||
| 378 | + background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--c) 30%, transparent), transparent); | ||
| 379 | + border-radius: 24px 24px 0 0; | ||
| 380 | + } | ||
| 381 | + .group-header { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; padding: 0 4px; } | ||
| 382 | + .group-icon { | ||
| 383 | + width: 32px; height: 32px; border-radius: 10px; | ||
| 384 | + background: color-mix(in srgb, var(--c) 10%, transparent); | ||
| 385 | + border: 1px solid color-mix(in srgb, var(--c) 20%, transparent); | ||
| 386 | + display: flex; align-items: center; justify-content: center; flex-shrink: 0; | ||
| 387 | + } | ||
| 388 | + .group-label { font-family: 'JetBrains Mono', monospace; font-size: 11px; letter-spacing: 0.18em; font-weight: 600; line-height: 1; } | ||
| 389 | + .group-sub { font-family: 'DM Sans', sans-serif; font-size: 12px; color: #5A5650; margin-top: 2px; } | ||
| 390 | + | ||
| 391 | + .group-widgets { display: flex; flex-direction: column; gap: 10px; } | ||
| 392 | + .widgets-stretch { display: flex; flex-direction: column; gap: 10px; flex: 1; } | ||
| 393 | + | ||
| 394 | + /* ── Widget card (level 2) ── */ | ||
| 395 | + .widget-card { | ||
| 396 | + background: #131313; border: 1px solid #222220; border-radius: 16px; | ||
| 397 | + padding: 22px; position: relative; overflow: hidden; cursor: pointer; | ||
| 398 | + transition: all 0.4s cubic-bezier(0.16,1,0.3,1); | ||
| 399 | + } | ||
| 400 | + .wh { | ||
| 401 | + background: #1A1A1A; border-color: #2E2E2C; transform: translateY(-1px); | ||
| 402 | + } | ||
| 403 | + .widget-grow { | ||
| 404 | + flex: 1; display: flex; flex-direction: column; | ||
| 405 | + } | ||
| 406 | + | ||
| 407 | + /* Horizontal variant (¿Quién?) */ | ||
| 408 | + .widget-h { | ||
| 409 | + display: flex; justify-content: space-between; align-items: center; | ||
| 410 | + } | ||
| 411 | + .widget-h-left { flex: 1; } | ||
| 412 | + .widget-h-right { display: flex; align-items: center; gap: 10px; flex-shrink: 0; } | ||
| 413 | + | ||
| 414 | + .widget-top { | ||
| 415 | + display: flex; justify-content: space-between; align-items: flex-start; | ||
| 416 | + gap: 10px; margin-bottom: 8px; position: relative; z-index: 1; | ||
| 417 | + } | ||
| 418 | + .widget-q { | ||
| 419 | + font-size: 17px; font-weight: 700; letter-spacing: -0.01em; | ||
| 420 | + line-height: 1.2; font-family: 'DM Sans', sans-serif; | ||
| 421 | + } | ||
| 422 | + .widget-desc { | ||
| 423 | + font-family: 'DM Sans', sans-serif; font-size: 12px; color: #8A8578; | ||
| 424 | + line-height: 1.5; position: relative; z-index: 1; | ||
| 425 | + } | ||
| 426 | + .widget-desc-grow { flex: 1; } | ||
| 427 | + .widget-footer { | ||
| 428 | + display: flex; justify-content: space-between; align-items: center; | ||
| 429 | + margin-top: 16px; position: relative; z-index: 1; | ||
| 430 | + } | ||
| 431 | + .widget-tech { | ||
| 432 | + font-family: 'JetBrains Mono', monospace; font-size: 8px; | ||
| 433 | + letter-spacing: 0.12em; color: #5A5650; text-transform: uppercase; | ||
| 434 | + } | ||
| 435 | + .widget-arrow { | ||
| 436 | + font-family: 'DM Sans', sans-serif; font-size: 13px; color: transparent; | ||
| 437 | + transform: translateX(-6px); | ||
| 438 | + transition: all 0.35s cubic-bezier(0.16,1,0.3,1); | ||
| 439 | + } | ||
| 440 | + .arrow-show { color: #F5F0E8; transform: translateX(0); } | ||
| 441 | + .widget-glow { | ||
| 442 | + position: absolute; inset: 0; pointer-events: none; | ||
| 443 | + opacity: 0; transition: opacity 0.4s; | ||
| 444 | + } | ||
| 445 | + .wh > .widget-glow, | ||
| 446 | + .wh .widget-glow { opacity: 1; } | ||
| 447 | + | ||
| 448 | + /* ── Sub-group (level 2 container — "¿En qué?") ── */ | ||
| 449 | + .sub-group { | ||
| 450 | + background: #0E0E0E; border: 1px solid #222220; border-radius: 18px; | ||
| 451 | + padding: 16px 14px 14px; position: relative; overflow: hidden; | ||
| 452 | + } | ||
| 453 | + .sub-group-accent { | ||
| 454 | + position: absolute; top: 0; left: 15%; right: 15%; height: 1px; | ||
| 455 | + background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--c) 25%, transparent), transparent); | ||
| 456 | + } | ||
| 457 | + .sub-group-header { | ||
| 458 | + display: flex; align-items: center; justify-content: space-between; | ||
| 459 | + margin-bottom: 12px; padding: 0 4px; | ||
| 460 | + } | ||
| 461 | + .sub-group-left { display: flex; align-items: center; gap: 8px; } | ||
| 462 | + .sub-group-icon { | ||
| 463 | + width: 26px; height: 26px; border-radius: 8px; | ||
| 464 | + background: color-mix(in srgb, var(--c) 10%, transparent); | ||
| 465 | + border: 1px solid color-mix(in srgb, var(--c) 18%, transparent); | ||
| 466 | + display: flex; align-items: center; justify-content: center; flex-shrink: 0; | ||
| 467 | + } | ||
| 468 | + .sub-group-label { | ||
| 469 | + font-family: 'DM Sans', sans-serif; font-size: 14px; | ||
| 470 | + font-weight: 700; color: #F5F0E8; line-height: 1.1; | ||
| 471 | + } | ||
| 472 | + .sub-group-tag { | ||
| 473 | + font-family: 'JetBrains Mono', monospace; font-size: 8px; | ||
| 474 | + letter-spacing: 0.12em; margin-top: 2px; | ||
| 475 | + } | ||
| 476 | + | ||
| 477 | + .sub-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; } | ||
| 478 | + | ||
| 479 | + /* ── Sub-widget (level 3) ── */ | ||
| 480 | + .sub-card { | ||
| 481 | + background: #111111; border-color: #1E1E1C; | ||
| 482 | + padding: 18px; border-radius: 14px; | ||
| 483 | + } | ||
| 484 | + .sub-card.wh { background: #161616; border-color: #2A2A28; } | ||
| 485 | + .sub-q { font-size: 14px; } | ||
| 486 | + .sub-desc { font-size: 11px; } | ||
| 487 | + .sub-footer { margin-top: 12px; } | ||
| 488 | + .sub-tech { font-size: 7px; } | ||
| 489 | + | ||
| 490 | + .bottom-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; } | ||
| 491 | + | ||
| 492 | + /* ── Main grid — equal height ── */ | ||
| 493 | + .main-grid { | ||
| 494 | + display: grid; grid-template-columns: 1fr 2fr; | ||
| 495 | + gap: 16px; margin-top: 16px; align-items: stretch; | ||
| 496 | + } | ||
| 497 | + .col-stretch { display: flex; flex-direction: column; } | ||
| 498 | + | ||
| 499 | + /* ── Help button ── */ | ||
| 500 | + .help-btn { | ||
| 501 | + width: 26px; height: 26px; border-radius: 50%; border: 1px solid #222220; | ||
| 502 | + background: transparent; color: #5A5650; | ||
| 503 | + font-family: 'Source Serif 4', serif; font-size: 12px; font-weight: 600; | ||
| 504 | + cursor: pointer; transition: all 0.3s; | ||
| 505 | + display: flex; align-items: center; justify-content: center; flex-shrink: 0; | ||
| 506 | + } | ||
| 507 | + .help-btn:hover { border-color: #E8C547; color: #E8C547; background: rgba(232,197,71,0.08); } | ||
| 508 | + .help-sm { width: 22px; height: 22px; font-size: 10px; } | ||
| 509 | + .help-xs { width: 20px; height: 20px; font-size: 9px; } | ||
| 510 | + | ||
| 511 | + /* ── Modal ── */ | ||
| 512 | + .modal-overlay { | ||
| 513 | + position: fixed; inset: 0; background: rgba(0,0,0,0.8); | ||
| 514 | + backdrop-filter: blur(12px); | ||
| 515 | + display: flex; align-items: center; justify-content: center; | ||
| 516 | + z-index: 1000; padding: 40px; | ||
| 517 | + } | ||
| 518 | + .modal-box { | ||
| 519 | + background: #141414; border: 1px solid #222220; border-radius: 24px; | ||
| 520 | + max-width: 520px; width: 100%; padding: 32px 36px; position: relative; | ||
| 521 | + animation: mIn 0.3s cubic-bezier(0.16,1,0.3,1); | ||
| 522 | + max-height: 85vh; overflow-y: auto; | ||
| 523 | + } | ||
| 524 | + .modal-close { | ||
| 525 | + position: absolute; top: 16px; right: 16px; | ||
| 526 | + background: transparent; border: 1px solid #222220; border-radius: 50%; | ||
| 527 | + width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; | ||
| 528 | + color: #5A5650; cursor: pointer; transition: all 0.2s; | ||
| 529 | + } | ||
| 530 | + .modal-close:hover { border-color: #5A5650; color: #F5F0E8; } | ||
| 531 | + .modal-badge { | ||
| 532 | + font-family: 'JetBrains Mono', monospace; font-size: 9px; letter-spacing: 0.15em; | ||
| 533 | + border-radius: 6px; padding: 4px 9px; display: inline-block; margin-bottom: 14px; | ||
| 534 | + } | ||
| 535 | + .modal-title { font-size: 24px; font-weight: 900; letter-spacing: -0.02em; line-height: 1.15; margin-bottom: 24px; } | ||
| 536 | + .modal-section { display: flex; gap: 14px; } | ||
| 537 | + .modal-icon { | ||
| 538 | + font-size: 18px; width: 36px; height: 36px; background: #161616; border-radius: 10px; | ||
| 539 | + display: flex; align-items: center; justify-content: center; flex-shrink: 0; | ||
| 540 | + border: 1px solid #222220; | ||
| 541 | + } | ||
| 542 | + .modal-label { | ||
| 543 | + font-family: 'JetBrains Mono', monospace; font-size: 9px; | ||
| 544 | + letter-spacing: 0.15em; color: #5A5650; margin-bottom: 5px; | ||
| 545 | + } | ||
| 546 | + .modal-text { font-family: 'DM Sans', sans-serif; font-size: 13px; line-height: 1.65; color: #8A8578; } | ||
| 547 | + .modal-divider { height: 1px; background: #222220; margin: 18px 0; margin-left: 50px; } | ||
| 548 | + .modal-example { | ||
| 549 | + margin-top: 20px; margin-left: 50px; padding: 14px 18px; | ||
| 550 | + border-left: 3px solid; background: rgba(232,197,71,0.03); | ||
| 551 | + border-radius: 0 10px 10px 0; | ||
| 552 | + } | ||
| 553 | + .modal-example p { | ||
| 554 | + font-family: 'DM Sans', sans-serif; font-size: 12px; | ||
| 555 | + line-height: 1.6; color: #8A8578; font-style: italic; | ||
| 556 | + } | ||
| 557 | +</style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
static/ObjetoDetail.svelte
0 → 100644
| 1 | +<script> | ||
| 2 | + import { onMount } from 'svelte'; | ||
| 3 | + | ||
| 4 | + let mounted = false; | ||
| 5 | + let hoveredYear = null; | ||
| 6 | + let showInfo = false; | ||
| 7 | + | ||
| 8 | + onMount(() => { setTimeout(() => { mounted = true; }, 50); }); | ||
| 9 | + | ||
| 10 | + // Dummy data | ||
| 11 | + const objeto = { | ||
| 12 | + codigo: '10000', | ||
| 13 | + nombre: 'Servicios Personales', | ||
| 14 | + nivel: 'Grupo', | ||
| 15 | + años: '2005-2024', | ||
| 16 | + descripcion: 'Gastos por concepto de servicios prestados por el personal permanente y no permanente del sector público.', | ||
| 17 | + variaciones: [ | ||
| 18 | + { rango: '2012-2024', texto: 'Gastos por concepto de servicios prestados por el personal permanente y no permanente.' }, | ||
| 19 | + { rango: '2005-2011', texto: 'Retribuciones y complementos al personal permanente y eventual.' } | ||
| 20 | + ] | ||
| 21 | + }; | ||
| 22 | + | ||
| 23 | + // Dummy spending data (in millions Bs) | ||
| 24 | + const gastoAnual = [ | ||
| 25 | + { año: 2005, monto: 8200, poblacion: 9.4 }, | ||
| 26 | + { año: 2006, monto: 8900, poblacion: 9.5 }, | ||
| 27 | + { año: 2007, monto: 10200, poblacion: 9.6 }, | ||
| 28 | + { año: 2008, monto: 12400, poblacion: 9.8 }, | ||
| 29 | + { año: 2009, monto: 13100, poblacion: 9.9 }, | ||
| 30 | + { año: 2010, monto: 14800, poblacion: 10.0 }, | ||
| 31 | + { año: 2011, monto: 17200, poblacion: 10.2 }, | ||
| 32 | + { año: 2012, monto: 19500, poblacion: 10.4 }, | ||
| 33 | + { año: 2013, monto: 22800, poblacion: 10.5 }, | ||
| 34 | + { año: 2014, monto: 28400, poblacion: 10.7 }, | ||
| 35 | + { año: 2015, monto: 31200, poblacion: 10.9 }, | ||
| 36 | + { año: 2016, monto: 33100, poblacion: 11.0 }, | ||
| 37 | + { año: 2017, monto: 35600, poblacion: 11.2 }, | ||
| 38 | + { año: 2018, monto: 38200, poblacion: 11.4 }, | ||
| 39 | + { año: 2019, monto: 41500, poblacion: 11.5 }, | ||
| 40 | + { año: 2020, monto: 39800, poblacion: 11.7 }, | ||
| 41 | + { año: 2021, monto: 42100, poblacion: 11.8 }, | ||
| 42 | + { año: 2022, monto: 45600, poblacion: 12.0 }, | ||
| 43 | + { año: 2023, monto: 48900, poblacion: 12.1 }, | ||
| 44 | + { año: 2024, monto: 52300, poblacion: 12.3 }, | ||
| 45 | + ]; | ||
| 46 | + | ||
| 47 | + const topEntidades = [ | ||
| 48 | + { nombre: 'Ministerio de Educación', monto: 18200, porcentaje: 34.8 }, | ||
| 49 | + { nombre: 'Ministerio de Salud', monto: 8900, porcentaje: 17.0 }, | ||
| 50 | + { nombre: 'Policía Boliviana', monto: 6200, porcentaje: 11.9 }, | ||
| 51 | + ]; | ||
| 52 | + | ||
| 53 | + // Calculations | ||
| 54 | + const maxMonto = Math.max(...gastoAnual.map(g => g.monto)); | ||
| 55 | + const totalHistorico = gastoAnual.reduce((sum, g) => sum + g.monto, 0); | ||
| 56 | + const totalPorcentaje = 14.2; // dummy: % del presupuesto total | ||
| 57 | + const ranking = 1; // dummy: #1 en gasto | ||
| 58 | + | ||
| 59 | + // Current view data (changes on hover) | ||
| 60 | + $: currentData = hoveredYear | ||
| 61 | + ? gastoAnual.find(g => g.año === hoveredYear) | ||
| 62 | + : null; | ||
| 63 | + | ||
| 64 | + $: displayMonto = currentData ? currentData.monto : totalHistorico; | ||
| 65 | + $: displayPerCapita = currentData | ||
| 66 | + ? Math.round(currentData.monto / currentData.poblacion) | ||
| 67 | + : Math.round(totalHistorico / gastoAnual.reduce((sum, g) => sum + g.poblacion, 0) * gastoAnual.length); | ||
| 68 | + $: displayPorcentaje = currentData ? (totalPorcentaje * (currentData.monto / (totalHistorico / gastoAnual.length)) / 1).toFixed(1) : totalPorcentaje; | ||
| 69 | + $: displayLabel = currentData ? currentData.año : '2005-2024'; | ||
| 70 | + $: displayRanking = currentData ? `#${ranking} en ${currentData.año}` : `#${ranking}`; | ||
| 71 | + | ||
| 72 | + function formatMonto(m) { | ||
| 73 | + if (m >= 1000000) return (m / 1000000).toFixed(1) + 'B'; | ||
| 74 | + if (m >= 1000) return (m / 1000).toFixed(1) + 'M'; | ||
| 75 | + return m.toString(); | ||
| 76 | + } | ||
| 77 | +</script> | ||
| 78 | + | ||
| 79 | +<div class="page" class:mounted> | ||
| 80 | + | ||
| 81 | + <!-- Header compacto --> | ||
| 82 | + <header> | ||
| 83 | + <nav class="breadcrumb"> | ||
| 84 | + <a href="/clasificadores/objeto-gasto">← Objeto del Gasto</a> | ||
| 85 | + </nav> | ||
| 86 | + | ||
| 87 | + <div class="title-row"> | ||
| 88 | + <div class="title-main"> | ||
| 89 | + <span class="codigo">{objeto.codigo}</span> | ||
| 90 | + <h1>{objeto.nombre}</h1> | ||
| 91 | + </div> | ||
| 92 | + | ||
| 93 | + <button class="info-btn" on:click={() => showInfo = !showInfo} class:active={showInfo}> | ||
| 94 | + <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | ||
| 95 | + <circle cx="12" cy="12" r="10"/> | ||
| 96 | + <path d="M12 16v-4M12 8h.01"/> | ||
| 97 | + </svg> | ||
| 98 | + </button> | ||
| 99 | + </div> | ||
| 100 | + | ||
| 101 | + <div class="meta"> | ||
| 102 | + <span class="badge">{objeto.nivel}</span> | ||
| 103 | + <span class="years">{objeto.años}</span> | ||
| 104 | + </div> | ||
| 105 | + </header> | ||
| 106 | + | ||
| 107 | + <!-- Panel de info (colapsable) --> | ||
| 108 | + {#if showInfo} | ||
| 109 | + <div class="info-panel"> | ||
| 110 | + <div class="info-content"> | ||
| 111 | + <h4>Definición vigente</h4> | ||
| 112 | + <p>{objeto.descripcion}</p> | ||
| 113 | + | ||
| 114 | + {#if objeto.variaciones.length > 1} | ||
| 115 | + <h4>Variaciones históricas</h4> | ||
| 116 | + {#each objeto.variaciones as v} | ||
| 117 | + <div class="variacion"> | ||
| 118 | + <span class="variacion-rango">{v.rango}</span> | ||
| 119 | + <p>{v.texto}</p> | ||
| 120 | + </div> | ||
| 121 | + {/each} | ||
| 122 | + {/if} | ||
| 123 | + </div> | ||
| 124 | + </div> | ||
| 125 | + {/if} | ||
| 126 | + | ||
| 127 | + <!-- Dashboard principal --> | ||
| 128 | + <main> | ||
| 129 | + | ||
| 130 | + <!-- Selector de entidad --> | ||
| 131 | + <div class="entity-selector"> | ||
| 132 | + <span class="selector-label">Viendo</span> | ||
| 133 | + <button class="selector-btn"> | ||
| 134 | + Todo el sector público | ||
| 135 | + <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | ||
| 136 | + <path d="M6 9l6 6 6-6"/> | ||
| 137 | + </svg> | ||
| 138 | + </button> | ||
| 139 | + </div> | ||
| 140 | + | ||
| 141 | + <!-- Gráfico de barras --> | ||
| 142 | + <div class="chart-section"> | ||
| 143 | + <div class="chart-header"> | ||
| 144 | + <span class="chart-title">Ejecución presupuestaria</span> | ||
| 145 | + <span class="chart-period">{displayLabel}</span> | ||
| 146 | + </div> | ||
| 147 | + | ||
| 148 | + <div class="chart"> | ||
| 149 | + {#each gastoAnual as g} | ||
| 150 | + <div | ||
| 151 | + class="bar-container" | ||
| 152 | + on:mouseenter={() => hoveredYear = g.año} | ||
| 153 | + on:mouseleave={() => hoveredYear = null} | ||
| 154 | + > | ||
| 155 | + <div | ||
| 156 | + class="bar" | ||
| 157 | + class:hovered={hoveredYear === g.año} | ||
| 158 | + style="height: {(g.monto / maxMonto) * 100}%" | ||
| 159 | + ></div> | ||
| 160 | + <span class="bar-label" class:visible={hoveredYear === g.año || g.año % 5 === 0}> | ||
| 161 | + {g.año.toString().slice(-2)} | ||
| 162 | + </span> | ||
| 163 | + </div> | ||
| 164 | + {/each} | ||
| 165 | + </div> | ||
| 166 | + </div> | ||
| 167 | + | ||
| 168 | + <!-- KPIs --> | ||
| 169 | + <div class="kpis"> | ||
| 170 | + <div class="kpi"> | ||
| 171 | + <span class="kpi-value">Bs {formatMonto(displayMonto)}</span> | ||
| 172 | + <span class="kpi-label">{currentData ? 'ese año' : 'acumulado'}</span> | ||
| 173 | + </div> | ||
| 174 | + | ||
| 175 | + <div class="kpi"> | ||
| 176 | + <span class="kpi-value">Bs {displayPerCapita.toLocaleString()}</span> | ||
| 177 | + <span class="kpi-label">por persona</span> | ||
| 178 | + </div> | ||
| 179 | + | ||
| 180 | + <div class="kpi"> | ||
| 181 | + <span class="kpi-value">{displayPorcentaje}%</span> | ||
| 182 | + <span class="kpi-label">del total</span> | ||
| 183 | + </div> | ||
| 184 | + | ||
| 185 | + <div class="kpi"> | ||
| 186 | + <span class="kpi-value">{displayRanking}</span> | ||
| 187 | + <span class="kpi-label">en gasto</span> | ||
| 188 | + </div> | ||
| 189 | + </div> | ||
| 190 | + | ||
| 191 | + <!-- Top entidades --> | ||
| 192 | + <div class="top-section"> | ||
| 193 | + <h3>Quién más gasta en esto</h3> | ||
| 194 | + | ||
| 195 | + <div class="top-list"> | ||
| 196 | + {#each topEntidades as ent, i} | ||
| 197 | + <div class="top-item"> | ||
| 198 | + <span class="top-rank">#{i + 1}</span> | ||
| 199 | + <div class="top-info"> | ||
| 200 | + <span class="top-name">{ent.nombre}</span> | ||
| 201 | + <div class="top-bar-bg"> | ||
| 202 | + <div class="top-bar" style="width: {ent.porcentaje}%"></div> | ||
| 203 | + </div> | ||
| 204 | + </div> | ||
| 205 | + <span class="top-pct">{ent.porcentaje}%</span> | ||
| 206 | + </div> | ||
| 207 | + {/each} | ||
| 208 | + </div> | ||
| 209 | + | ||
| 210 | + <button class="see-all">Ver todas las entidades →</button> | ||
| 211 | + </div> | ||
| 212 | + | ||
| 213 | + </main> | ||
| 214 | +</div> | ||
| 215 | + | ||
| 216 | + | ||
| 217 | +<style> | ||
| 218 | + .page { | ||
| 219 | + min-height: 100vh; | ||
| 220 | + background: #0A0A0A; | ||
| 221 | + color: #F5F0E8; | ||
| 222 | + font-family: 'Instrument Sans', -apple-system, sans-serif; | ||
| 223 | + opacity: 0; | ||
| 224 | + transition: opacity 0.4s ease; | ||
| 225 | + } | ||
| 226 | + .mounted { opacity: 1; } | ||
| 227 | + | ||
| 228 | + /* Header */ | ||
| 229 | + header { | ||
| 230 | + padding: 1.5rem 2rem; | ||
| 231 | + border-bottom: 1px solid #1E1E1C; | ||
| 232 | + } | ||
| 233 | + | ||
| 234 | + .breadcrumb a { | ||
| 235 | + font-family: 'DM Mono', monospace; | ||
| 236 | + font-size: 0.75rem; | ||
| 237 | + color: #5A5650; | ||
| 238 | + text-decoration: none; | ||
| 239 | + transition: color 0.2s; | ||
| 240 | + } | ||
| 241 | + .breadcrumb a:hover { color: #F5F0E8; } | ||
| 242 | + | ||
| 243 | + .title-row { | ||
| 244 | + display: flex; | ||
| 245 | + align-items: flex-start; | ||
| 246 | + justify-content: space-between; | ||
| 247 | + margin-top: 1rem; | ||
| 248 | + gap: 1rem; | ||
| 249 | + } | ||
| 250 | + | ||
| 251 | + .title-main { | ||
| 252 | + display: flex; | ||
| 253 | + align-items: baseline; | ||
| 254 | + gap: 1rem; | ||
| 255 | + flex-wrap: wrap; | ||
| 256 | + } | ||
| 257 | + | ||
| 258 | + .codigo { | ||
| 259 | + font-family: 'DM Mono', monospace; | ||
| 260 | + font-size: 1.75rem; | ||
| 261 | + color: #5A5650; | ||
| 262 | + font-weight: 400; | ||
| 263 | + } | ||
| 264 | + | ||
| 265 | + h1 { | ||
| 266 | + font-family: 'DM Serif Display', Georgia, serif; | ||
| 267 | + font-size: 1.75rem; | ||
| 268 | + font-weight: 400; | ||
| 269 | + color: #F5F0E8; | ||
| 270 | + margin: 0; | ||
| 271 | + } | ||
| 272 | + | ||
| 273 | + .info-btn { | ||
| 274 | + background: transparent; | ||
| 275 | + border: 1px solid #2E2E2C; | ||
| 276 | + border-radius: 50%; | ||
| 277 | + width: 36px; | ||
| 278 | + height: 36px; | ||
| 279 | + display: flex; | ||
| 280 | + align-items: center; | ||
| 281 | + justify-content: center; | ||
| 282 | + color: #5A5650; | ||
| 283 | + cursor: pointer; | ||
| 284 | + transition: all 0.2s; | ||
| 285 | + flex-shrink: 0; | ||
| 286 | + } | ||
| 287 | + .info-btn:hover, .info-btn.active { | ||
| 288 | + border-color: #E8C547; | ||
| 289 | + color: #E8C547; | ||
| 290 | + } | ||
| 291 | + | ||
| 292 | + .meta { | ||
| 293 | + display: flex; | ||
| 294 | + align-items: center; | ||
| 295 | + gap: 0.75rem; | ||
| 296 | + margin-top: 0.75rem; | ||
| 297 | + } | ||
| 298 | + | ||
| 299 | + .badge { | ||
| 300 | + font-family: 'DM Mono', monospace; | ||
| 301 | + font-size: 0.625rem; | ||
| 302 | + letter-spacing: 0.1em; | ||
| 303 | + text-transform: uppercase; | ||
| 304 | + padding: 0.25rem 0.5rem; | ||
| 305 | + background: #1A1A18; | ||
| 306 | + border: 1px solid #2E2E2C; | ||
| 307 | + border-radius: 4px; | ||
| 308 | + color: #8A8578; | ||
| 309 | + } | ||
| 310 | + | ||
| 311 | + .years { | ||
| 312 | + font-family: 'DM Mono', monospace; | ||
| 313 | + font-size: 0.75rem; | ||
| 314 | + color: #5A5650; | ||
| 315 | + } | ||
| 316 | + | ||
| 317 | + /* Info panel */ | ||
| 318 | + .info-panel { | ||
| 319 | + background: #111110; | ||
| 320 | + border-bottom: 1px solid #1E1E1C; | ||
| 321 | + padding: 1.5rem 2rem; | ||
| 322 | + } | ||
| 323 | + | ||
| 324 | + .info-content h4 { | ||
| 325 | + font-family: 'DM Mono', monospace; | ||
| 326 | + font-size: 0.625rem; | ||
| 327 | + letter-spacing: 0.15em; | ||
| 328 | + text-transform: uppercase; | ||
| 329 | + color: #5A5650; | ||
| 330 | + margin: 0 0 0.5rem 0; | ||
| 331 | + } | ||
| 332 | + | ||
| 333 | + .info-content p { | ||
| 334 | + font-size: 0.875rem; | ||
| 335 | + line-height: 1.6; | ||
| 336 | + color: #8A8578; | ||
| 337 | + margin: 0 0 1.25rem 0; | ||
| 338 | + } | ||
| 339 | + | ||
| 340 | + .variacion { | ||
| 341 | + padding-left: 1rem; | ||
| 342 | + border-left: 2px solid #2E2E2C; | ||
| 343 | + margin-bottom: 1rem; | ||
| 344 | + } | ||
| 345 | + | ||
| 346 | + .variacion-rango { | ||
| 347 | + font-family: 'DM Mono', monospace; | ||
| 348 | + font-size: 0.688rem; | ||
| 349 | + color: #5A5650; | ||
| 350 | + } | ||
| 351 | + | ||
| 352 | + .variacion p { | ||
| 353 | + margin-top: 0.25rem; | ||
| 354 | + font-size: 0.813rem; | ||
| 355 | + } | ||
| 356 | + | ||
| 357 | + /* Main */ | ||
| 358 | + main { | ||
| 359 | + padding: 2rem; | ||
| 360 | + max-width: 900px; | ||
| 361 | + margin: 0 auto; | ||
| 362 | + } | ||
| 363 | + | ||
| 364 | + /* Entity selector */ | ||
| 365 | + .entity-selector { | ||
| 366 | + display: flex; | ||
| 367 | + align-items: center; | ||
| 368 | + gap: 0.75rem; | ||
| 369 | + margin-bottom: 2rem; | ||
| 370 | + } | ||
| 371 | + | ||
| 372 | + .selector-label { | ||
| 373 | + font-size: 0.813rem; | ||
| 374 | + color: #5A5650; | ||
| 375 | + } | ||
| 376 | + | ||
| 377 | + .selector-btn { | ||
| 378 | + display: flex; | ||
| 379 | + align-items: center; | ||
| 380 | + gap: 0.5rem; | ||
| 381 | + padding: 0.5rem 1rem; | ||
| 382 | + background: #161614; | ||
| 383 | + border: 1px solid #2E2E2C; | ||
| 384 | + border-radius: 8px; | ||
| 385 | + color: #F5F0E8; | ||
| 386 | + font-size: 0.875rem; | ||
| 387 | + cursor: pointer; | ||
| 388 | + transition: all 0.2s; | ||
| 389 | + } | ||
| 390 | + .selector-btn:hover { | ||
| 391 | + border-color: #E8C547; | ||
| 392 | + } | ||
| 393 | + .selector-btn svg { | ||
| 394 | + color: #5A5650; | ||
| 395 | + } | ||
| 396 | + | ||
| 397 | + /* Chart */ | ||
| 398 | + .chart-section { | ||
| 399 | + margin-bottom: 2rem; | ||
| 400 | + } | ||
| 401 | + | ||
| 402 | + .chart-header { | ||
| 403 | + display: flex; | ||
| 404 | + justify-content: space-between; | ||
| 405 | + align-items: baseline; | ||
| 406 | + margin-bottom: 1rem; | ||
| 407 | + } | ||
| 408 | + | ||
| 409 | + .chart-title { | ||
| 410 | + font-size: 0.75rem; | ||
| 411 | + color: #5A5650; | ||
| 412 | + text-transform: uppercase; | ||
| 413 | + letter-spacing: 0.1em; | ||
| 414 | + } | ||
| 415 | + | ||
| 416 | + .chart-period { | ||
| 417 | + font-family: 'DM Mono', monospace; | ||
| 418 | + font-size: 0.875rem; | ||
| 419 | + color: #E8C547; | ||
| 420 | + } | ||
| 421 | + | ||
| 422 | + .chart { | ||
| 423 | + display: flex; | ||
| 424 | + align-items: flex-end; | ||
| 425 | + gap: 4px; | ||
| 426 | + height: 160px; | ||
| 427 | + padding: 0 0.5rem; | ||
| 428 | + } | ||
| 429 | + | ||
| 430 | + .bar-container { | ||
| 431 | + flex: 1; | ||
| 432 | + display: flex; | ||
| 433 | + flex-direction: column; | ||
| 434 | + align-items: center; | ||
| 435 | + height: 100%; | ||
| 436 | + cursor: pointer; | ||
| 437 | + } | ||
| 438 | + | ||
| 439 | + .bar { | ||
| 440 | + width: 100%; | ||
| 441 | + background: linear-gradient(to top, #2A5A45, #4A8B6E); | ||
| 442 | + border-radius: 4px 4px 0 0; | ||
| 443 | + transition: all 0.2s ease; | ||
| 444 | + min-height: 4px; | ||
| 445 | + } | ||
| 446 | + | ||
| 447 | + .bar.hovered { | ||
| 448 | + background: linear-gradient(to top, #E8C547, #F5D76E); | ||
| 449 | + transform: scaleX(1.1); | ||
| 450 | + } | ||
| 451 | + | ||
| 452 | + .bar-label { | ||
| 453 | + font-family: 'DM Mono', monospace; | ||
| 454 | + font-size: 0.625rem; | ||
| 455 | + color: #5A5650; | ||
| 456 | + margin-top: 0.5rem; | ||
| 457 | + opacity: 0; | ||
| 458 | + transition: opacity 0.2s; | ||
| 459 | + } | ||
| 460 | + .bar-label.visible { opacity: 1; } | ||
| 461 | + | ||
| 462 | + /* KPIs */ | ||
| 463 | + .kpis { | ||
| 464 | + display: grid; | ||
| 465 | + grid-template-columns: repeat(4, 1fr); | ||
| 466 | + gap: 1px; | ||
| 467 | + background: #2E2E2C; | ||
| 468 | + border-radius: 12px; | ||
| 469 | + overflow: hidden; | ||
| 470 | + margin-bottom: 2.5rem; | ||
| 471 | + } | ||
| 472 | + | ||
| 473 | + .kpi { | ||
| 474 | + background: #131312; | ||
| 475 | + padding: 1.25rem 1rem; | ||
| 476 | + text-align: center; | ||
| 477 | + } | ||
| 478 | + | ||
| 479 | + .kpi-value { | ||
| 480 | + display: block; | ||
| 481 | + font-family: 'DM Serif Display', Georgia, serif; | ||
| 482 | + font-size: 1.375rem; | ||
| 483 | + color: #F5F0E8; | ||
| 484 | + margin-bottom: 0.25rem; | ||
| 485 | + } | ||
| 486 | + | ||
| 487 | + .kpi-label { | ||
| 488 | + font-size: 0.688rem; | ||
| 489 | + color: #5A5650; | ||
| 490 | + text-transform: uppercase; | ||
| 491 | + letter-spacing: 0.05em; | ||
| 492 | + } | ||
| 493 | + | ||
| 494 | + /* Top entidades */ | ||
| 495 | + .top-section h3 { | ||
| 496 | + font-family: 'DM Mono', monospace; | ||
| 497 | + font-size: 0.75rem; | ||
| 498 | + color: #5A5650; | ||
| 499 | + text-transform: uppercase; | ||
| 500 | + letter-spacing: 0.1em; | ||
| 501 | + margin: 0 0 1rem 0; | ||
| 502 | + } | ||
| 503 | + | ||
| 504 | + .top-list { | ||
| 505 | + display: flex; | ||
| 506 | + flex-direction: column; | ||
| 507 | + gap: 0.75rem; | ||
| 508 | + } | ||
| 509 | + | ||
| 510 | + .top-item { | ||
| 511 | + display: flex; | ||
| 512 | + align-items: center; | ||
| 513 | + gap: 1rem; | ||
| 514 | + padding: 0.875rem 1rem; | ||
| 515 | + background: #131312; | ||
| 516 | + border-radius: 10px; | ||
| 517 | + border: 1px solid #1E1E1C; | ||
| 518 | + transition: border-color 0.2s; | ||
| 519 | + } | ||
| 520 | + .top-item:hover { | ||
| 521 | + border-color: #2E2E2C; | ||
| 522 | + } | ||
| 523 | + | ||
| 524 | + .top-rank { | ||
| 525 | + font-family: 'DM Mono', monospace; | ||
| 526 | + font-size: 0.875rem; | ||
| 527 | + color: #5A5650; | ||
| 528 | + width: 24px; | ||
| 529 | + } | ||
| 530 | + | ||
| 531 | + .top-info { | ||
| 532 | + flex: 1; | ||
| 533 | + } | ||
| 534 | + | ||
| 535 | + .top-name { | ||
| 536 | + display: block; | ||
| 537 | + font-size: 0.875rem; | ||
| 538 | + color: #F5F0E8; | ||
| 539 | + margin-bottom: 0.5rem; | ||
| 540 | + } | ||
| 541 | + | ||
| 542 | + .top-bar-bg { | ||
| 543 | + height: 4px; | ||
| 544 | + background: #1E1E1C; | ||
| 545 | + border-radius: 2px; | ||
| 546 | + overflow: hidden; | ||
| 547 | + } | ||
| 548 | + | ||
| 549 | + .top-bar { | ||
| 550 | + height: 100%; | ||
| 551 | + background: linear-gradient(to right, #4A8B6E, #6BAF8E); | ||
| 552 | + border-radius: 2px; | ||
| 553 | + } | ||
| 554 | + | ||
| 555 | + .top-pct { | ||
| 556 | + font-family: 'DM Mono', monospace; | ||
| 557 | + font-size: 0.875rem; | ||
| 558 | + color: #E8C547; | ||
| 559 | + } | ||
| 560 | + | ||
| 561 | + .see-all { | ||
| 562 | + background: transparent; | ||
| 563 | + border: 1px solid #2E2E2C; | ||
| 564 | + border-radius: 8px; | ||
| 565 | + padding: 0.75rem 1rem; | ||
| 566 | + color: #8A8578; | ||
| 567 | + font-size: 0.813rem; | ||
| 568 | + cursor: pointer; | ||
| 569 | + transition: all 0.2s; | ||
| 570 | + width: 100%; | ||
| 571 | + margin-top: 1rem; | ||
| 572 | + } | ||
| 573 | + .see-all:hover { | ||
| 574 | + border-color: #E8C547; | ||
| 575 | + color: #E8C547; | ||
| 576 | + } | ||
| 577 | + | ||
| 578 | + /* Responsive */ | ||
| 579 | + @media (max-width: 640px) { | ||
| 580 | + header, main { | ||
| 581 | + padding: 1.25rem 1rem; | ||
| 582 | + } | ||
| 583 | + | ||
| 584 | + .title-main { | ||
| 585 | + flex-direction: column; | ||
| 586 | + gap: 0.25rem; | ||
| 587 | + } | ||
| 588 | + | ||
| 589 | + .codigo { | ||
| 590 | + font-size: 1.25rem; | ||
| 591 | + } | ||
| 592 | + | ||
| 593 | + h1 { | ||
| 594 | + font-size: 1.375rem; | ||
| 595 | + } | ||
| 596 | + | ||
| 597 | + .kpis { | ||
| 598 | + grid-template-columns: repeat(2, 1fr); | ||
| 599 | + } | ||
| 600 | + | ||
| 601 | + .chart { | ||
| 602 | + height: 120px; | ||
| 603 | + } | ||
| 604 | + } | ||
| 605 | +</style> |
-
Please register or login to post a comment