Showing
10 changed files
with
1520 additions
and
1272 deletions
| ... | @@ -10,7 +10,8 @@ | ... | @@ -10,7 +10,8 @@ |
| 10 | "dependencies": { | 10 | "dependencies": { |
| 11 | "@supabase/supabase-js": "^2.98.0", | 11 | "@supabase/supabase-js": "^2.98.0", |
| 12 | "d3": "^7.9.0", | 12 | "d3": "^7.9.0", |
| 13 | - "fuse.js": "^7.1.0" | 13 | + "fuse.js": "^7.1.0", |
| 14 | + "marked": "^17.0.4" | ||
| 14 | }, | 15 | }, |
| 15 | "devDependencies": { | 16 | "devDependencies": { |
| 16 | "@sveltejs/adapter-auto": "^7.0.0", | 17 | "@sveltejs/adapter-auto": "^7.0.0", |
| ... | @@ -2335,6 +2336,18 @@ | ... | @@ -2335,6 +2336,18 @@ |
| 2335 | "@jridgewell/sourcemap-codec": "^1.5.5" | 2336 | "@jridgewell/sourcemap-codec": "^1.5.5" |
| 2336 | } | 2337 | } |
| 2337 | }, | 2338 | }, |
| 2339 | + "node_modules/marked": { | ||
| 2340 | + "version": "17.0.4", | ||
| 2341 | + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.4.tgz", | ||
| 2342 | + "integrity": "sha512-NOmVMM+KAokHMvjWmC5N/ZOvgmSWuqJB8FoYI019j4ogb/PeRMKoKIjReZ2w3376kkA8dSJIP8uD993Kxc0iRQ==", | ||
| 2343 | + "license": "MIT", | ||
| 2344 | + "bin": { | ||
| 2345 | + "marked": "bin/marked.js" | ||
| 2346 | + }, | ||
| 2347 | + "engines": { | ||
| 2348 | + "node": ">= 20" | ||
| 2349 | + } | ||
| 2350 | + }, | ||
| 2338 | "node_modules/mrmime": { | 2351 | "node_modules/mrmime": { |
| 2339 | "version": "2.0.1", | 2352 | "version": "2.0.1", |
| 2340 | "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", | 2353 | "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", | ... | ... |
| ... | @@ -21,6 +21,7 @@ | ... | @@ -21,6 +21,7 @@ |
| 21 | "dependencies": { | 21 | "dependencies": { |
| 22 | "@supabase/supabase-js": "^2.98.0", | 22 | "@supabase/supabase-js": "^2.98.0", |
| 23 | "d3": "^7.9.0", | 23 | "d3": "^7.9.0", |
| 24 | - "fuse.js": "^7.1.0" | 24 | + "fuse.js": "^7.1.0", |
| 25 | + "marked": "^17.0.4" | ||
| 25 | } | 26 | } |
| 26 | } | 27 | } | ... | ... |
| ... | @@ -58,23 +58,32 @@ | ... | @@ -58,23 +58,32 @@ |
| 58 | gearMenuOpen = !gearMenuOpen; | 58 | gearMenuOpen = !gearMenuOpen; |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | - function closeGearMenu() { | ||
| 62 | - setTimeout(() => { gearMenuOpen = false; }, 150); | ||
| 63 | - } | ||
| 64 | - | ||
| 65 | onMount(() => { | 61 | onMount(() => { |
| 66 | setTimeout(() => { mounted = true; }, 80); | 62 | setTimeout(() => { mounted = true; }, 80); |
| 67 | initIndex(); | 63 | initIndex(); |
| 68 | // Pre-calentar Supabase en background para evitar cold start | 64 | // Pre-calentar Supabase en background para evitar cold start |
| 69 | warmupSupabase(); | 65 | warmupSupabase(); |
| 66 | + | ||
| 67 | + // Cerrar gear menu al hacer click fuera | ||
| 68 | + const handleClickOutside = (e) => { | ||
| 69 | + if (gearMenuOpen && !e.target.closest('.gear-wrap')) { | ||
| 70 | + gearMenuOpen = false; | ||
| 71 | + } | ||
| 72 | + }; | ||
| 73 | + document.addEventListener('click', handleClickOutside); | ||
| 74 | + return () => document.removeEventListener('click', handleClickOutside); | ||
| 70 | }); | 75 | }); |
| 71 | 76 | ||
| 72 | - // Auto-focus en el buscador cuando el índice esté listo (solo una vez) | 77 | + // Auto-focus en el buscador cuando el índice esté listo (solo desktop) |
| 73 | $: if ($indexLoaded && searchInput && !autoFocused) { | 78 | $: if ($indexLoaded && searchInput && !autoFocused) { |
| 74 | autoFocused = true; | 79 | autoFocused = true; |
| 75 | - setTimeout(() => { | 80 | + // No auto-focus en móvil para evitar que aparezca el teclado |
| 76 | - searchInput?.focus(); | 81 | + const isMobile = typeof window !== 'undefined' && window.innerWidth < 768; |
| 77 | - }, 50); | 82 | + if (!isMobile) { |
| 83 | + setTimeout(() => { | ||
| 84 | + searchInput?.focus(); | ||
| 85 | + }, 50); | ||
| 86 | + } | ||
| 78 | } | 87 | } |
| 79 | 88 | ||
| 80 | function toggleMobileSettings() { | 89 | function toggleMobileSettings() { |
| ... | @@ -85,7 +94,7 @@ | ... | @@ -85,7 +94,7 @@ |
| 85 | $: accent = isD ? '#4A8B6E' : '#C9A751'; | 94 | $: accent = isD ? '#4A8B6E' : '#C9A751'; |
| 86 | $: placeholder = isD | 95 | $: placeholder = isD |
| 87 | ? 'Busca una entidad, un ministerio, un municipio...' | 96 | ? 'Busca una entidad, un ministerio, un municipio...' |
| 88 | - : 'Busca en 7 clasificadores: partidas, rubros, fuentes...'; | 97 | + : 'Busca instituciones, gastos, ingresos, sectores...'; |
| 89 | $: examples = isD | 98 | $: examples = isD |
| 90 | ? [ | 99 | ? [ |
| 91 | { label: 'MEFP', bg: 'rgba(140,170,150,0.10)', color: '#9ab8a8' }, | 100 | { label: 'MEFP', bg: 'rgba(140,170,150,0.10)', color: '#9ab8a8' }, |
| ... | @@ -93,11 +102,11 @@ | ... | @@ -93,11 +102,11 @@ |
| 93 | { label: 'GOB. La Paz', bg: 'rgba(180,130,100,0.10)', color: '#b8947a' }, | 102 | { label: 'GOB. La Paz', bg: 'rgba(180,130,100,0.10)', color: '#b8947a' }, |
| 94 | ] | 103 | ] |
| 95 | : [ | 104 | : [ |
| 96 | - { label: 'Sueldos', bg: 'rgba(201,167,81,0.12)', color: '#d4b962' }, | 105 | + { label: 'Sueldos', bg: 'rgba(201,167,81,0.12)', color: '#d4b962', href: '/objeto/11700' }, |
| 97 | - { label: 'MEFP', bg: 'rgba(140,170,150,0.10)', color: '#9ab8a8' }, | 106 | + { label: 'Regalías', bg: 'rgba(160,150,170,0.09)', color: '#aaa0b0', href: '/rubro/14000' }, |
| 98 | { label: 'BOA', bg: 'rgba(150,155,175,0.10)', color: '#a0a5b8' }, | 107 | { label: 'BOA', bg: 'rgba(150,155,175,0.10)', color: '#a0a5b8' }, |
| 99 | - { label: 'Consultorías', bg: 'rgba(180,130,100,0.10)', color: '#b8947a' }, | 108 | + { label: 'MEFP', bg: 'rgba(140,170,150,0.10)', color: '#9ab8a8' }, |
| 100 | - { label: 'Regalías', bg: 'rgba(160,150,170,0.09)', color: '#aaa0b0' }, | 109 | + { label: 'Publicidad', bg: 'rgba(180,130,100,0.10)', color: '#b8947a' }, |
| 101 | ]; | 110 | ]; |
| 102 | 111 | ||
| 103 | function switchMode(m) { | 112 | function switchMode(m) { |
| ... | @@ -205,9 +214,9 @@ | ... | @@ -205,9 +214,9 @@ |
| 205 | ]; | 214 | ]; |
| 206 | 215 | ||
| 207 | const VISUALIZACIONES = [ | 216 | const VISUALIZACIONES = [ |
| 208 | - { color: '#4A8B6E', title: 'Evolución del gasto en salud', desc: 'Cómo cambiaron las prioridades en salud a lo largo de dos décadas.', type: 'Serie temporal' }, | 217 | + { color: '#4A8B6E', title: 'Evolución del gasto en salud', desc: 'Cómo cambiaron las prioridades en salud a lo largo de dos décadas.', type: 'Serie temporal', href: null }, |
| 209 | - { color: '#C9A751', title: 'Treemap del presupuesto 2025', desc: 'Explora cada peso del presupuesto actual.', type: 'Treemap' }, | 218 | + { color: '#C9A751', title: '¿En qué se gasta?', desc: 'Explora el presupuesto por objeto de gasto.', type: 'Treemap', href: '/clasificadores/objeto-gasto?modo=mapa' }, |
| 210 | - { color: '#8B6E9B', title: 'Mapa de inversión por departamento', desc: 'Distribución geográfica de la inversión pública.', type: 'Mapa' }, | 219 | + { color: '#8B6E9B', title: 'Mapa de inversión por departamento', desc: 'Distribución geográfica de la inversión pública.', type: 'Mapa', href: null }, |
| 211 | ]; | 220 | ]; |
| 212 | 221 | ||
| 213 | /* Reveal action */ | 222 | /* Reveal action */ |
| ... | @@ -295,7 +304,7 @@ | ... | @@ -295,7 +304,7 @@ |
| 295 | <div class="hero-search anim" style="--d:520ms"> | 304 | <div class="hero-search anim" style="--d:520ms"> |
| 296 | <div class="search-wrap"> | 305 | <div class="search-wrap"> |
| 297 | <!-- Search bar unificado --> | 306 | <!-- Search bar unificado --> |
| 298 | - <div class="search-bar" class:search-focused={searchFocused} class:search-has-results={searchFocused && ($results.length > 0 || ($query.length >= 2 && $results.length === 0) || $isLoading)} style="--accent:{accent}"> | 307 | + <div class="search-bar" class:search-focused={searchFocused} class:search-has-results={searchFocused && $query.length >= 2 && ($results.length > 0 || $results.length === 0 || $isLoading)} style="--accent:{accent}"> |
| 299 | <!-- Toggle integrado (desktop) --> | 308 | <!-- Toggle integrado (desktop) --> |
| 300 | <div class="toggle-bar toggle-desktop"> | 309 | <div class="toggle-bar toggle-desktop"> |
| 301 | <button class="toggle-btn" class:toggle-active={mode === 'archivo'} on:click={() => switchMode('archivo')}> | 310 | <button class="toggle-btn" class:toggle-active={mode === 'archivo'} on:click={() => switchMode('archivo')}> |
| ... | @@ -338,7 +347,7 @@ | ... | @@ -338,7 +347,7 @@ |
| 338 | /> | 347 | /> |
| 339 | {#if !searchVal} | 348 | {#if !searchVal} |
| 340 | <div class="search-placeholder"> | 349 | <div class="search-placeholder"> |
| 341 | - <span class="blink-cursor" style="background:{accent}"></span> | 350 | + <span class="blink-cursor" class:cursor-diario={isD} class:cursor-archivo={!isD}></span> |
| 342 | <span class="placeholder-text placeholder-desktop">{placeholder}</span> | 351 | <span class="placeholder-text placeholder-desktop">{placeholder}</span> |
| 343 | <span class="placeholder-text placeholder-mobile">{isD ? 'MEFP, Min. Salud, GAM...' : 'BOA, Publicidad, IDH...'}</span> | 352 | <span class="placeholder-text placeholder-mobile">{isD ? 'MEFP, Min. Salud, GAM...' : 'BOA, Publicidad, IDH...'}</span> |
| 344 | </div> | 353 | </div> |
| ... | @@ -350,8 +359,8 @@ | ... | @@ -350,8 +359,8 @@ |
| 350 | {/if} | 359 | {/if} |
| 351 | </div> | 360 | </div> |
| 352 | <!-- Gear button with filter dropdown (desktop) --> | 361 | <!-- Gear button with filter dropdown (desktop) --> |
| 353 | - <div class="gear-wrap gear-desktop"> | 362 | + <div class="gear-wrap gear-desktop" on:click|stopPropagation> |
| 354 | - <button class="gear-btn" class:gear-active={gearMenuOpen} on:click={toggleGearMenu} on:blur={closeGearMenu}> | 363 | + <button class="gear-btn" class:gear-active={gearMenuOpen} on:click={toggleGearMenu}> |
| 355 | <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={gearMenuOpen ? accent : '#B8B5AD'} stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"> | 364 | <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={gearMenuOpen ? accent : '#B8B5AD'} stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"> |
| 356 | <circle cx="12" cy="12" r="3"/> | 365 | <circle cx="12" cy="12" r="3"/> |
| 357 | <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/> | 366 | <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/> |
| ... | @@ -359,21 +368,42 @@ | ... | @@ -359,21 +368,42 @@ |
| 359 | </button> | 368 | </button> |
| 360 | {#if gearMenuOpen} | 369 | {#if gearMenuOpen} |
| 361 | <div class="gear-menu"> | 370 | <div class="gear-menu"> |
| 362 | - <div class="gear-menu-title">Buscar en:</div> | 371 | + <div class="gear-menu-title">Ajusta tu búsqueda</div> |
| 363 | - <button class="gear-filter-btn" class:filter-active={searchFilters.entidad} on:mousedown={() => toggleFilter('entidad')}> | 372 | + <button class="gear-row" on:click={() => toggleFilter('entidad')}> |
| 364 | - <span class="filter-dot" style="background:#4A8B6E"></span> | 373 | + <div class="gear-check" class:gear-check-on={searchFilters.entidad}> |
| 365 | - <span>Entidades</span> | 374 | + {#if searchFilters.entidad} |
| 366 | - <span class="filter-check">{searchFilters.entidad ? '✓' : ''}</span> | 375 | + <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> |
| 376 | + {/if} | ||
| 377 | + </div> | ||
| 378 | + <div class="gear-row-content"> | ||
| 379 | + <span class="gear-row-question">¿Quién?</span> | ||
| 380 | + <span class="gear-row-label" class:gear-row-label-on={searchFilters.entidad}>Instituciones</span> | ||
| 381 | + </div> | ||
| 382 | + <span class="gear-row-count">756</span> | ||
| 367 | </button> | 383 | </button> |
| 368 | - <button class="gear-filter-btn" class:filter-active={searchFilters.objeto_gasto} on:mousedown={() => toggleFilter('objeto_gasto')}> | 384 | + <button class="gear-row" on:click={() => toggleFilter('objeto_gasto')}> |
| 369 | - <span class="filter-dot" style="background:#C9A751"></span> | 385 | + <div class="gear-check" class:gear-check-on={searchFilters.objeto_gasto}> |
| 370 | - <span>Objetos de gasto</span> | 386 | + {#if searchFilters.objeto_gasto} |
| 371 | - <span class="filter-check">{searchFilters.objeto_gasto ? '✓' : ''}</span> | 387 | + <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> |
| 388 | + {/if} | ||
| 389 | + </div> | ||
| 390 | + <div class="gear-row-content"> | ||
| 391 | + <span class="gear-row-question">¿En qué?</span> | ||
| 392 | + <span class="gear-row-label" class:gear-row-label-on={searchFilters.objeto_gasto}>Objetos de gasto</span> | ||
| 393 | + </div> | ||
| 394 | + <span class="gear-row-count">324</span> | ||
| 372 | </button> | 395 | </button> |
| 373 | - <button class="gear-filter-btn" class:filter-active={searchFilters.rubro} on:mousedown={() => toggleFilter('rubro')}> | 396 | + <button class="gear-row" on:click={() => toggleFilter('rubro')}> |
| 374 | - <span class="filter-dot" style="background:#8B6E9B"></span> | 397 | + <div class="gear-check" class:gear-check-on={searchFilters.rubro}> |
| 375 | - <span>Rubros</span> | 398 | + {#if searchFilters.rubro} |
| 376 | - <span class="filter-check">{searchFilters.rubro ? '✓' : ''}</span> | 399 | + <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> |
| 400 | + {/if} | ||
| 401 | + </div> | ||
| 402 | + <div class="gear-row-content"> | ||
| 403 | + <span class="gear-row-question">¿De dónde?</span> | ||
| 404 | + <span class="gear-row-label" class:gear-row-label-on={searchFilters.rubro}>Rubros de ingreso</span> | ||
| 405 | + </div> | ||
| 406 | + <span class="gear-row-count">324</span> | ||
| 377 | </button> | 407 | </button> |
| 378 | </div> | 408 | </div> |
| 379 | {/if} | 409 | {/if} |
| ... | @@ -381,7 +411,7 @@ | ... | @@ -381,7 +411,7 @@ |
| 381 | </div> | 411 | </div> |
| 382 | 412 | ||
| 383 | <!-- Search Results Dropdown --> | 413 | <!-- Search Results Dropdown --> |
| 384 | - {#if $isLoading} | 414 | + {#if $isLoading && $query.length >= 2} |
| 385 | <div class="search-results"> | 415 | <div class="search-results"> |
| 386 | <div class="search-loading">Buscando...</div> | 416 | <div class="search-loading">Buscando...</div> |
| 387 | </div> | 417 | </div> |
| ... | @@ -438,19 +468,19 @@ | ... | @@ -438,19 +468,19 @@ |
| 438 | </button> | 468 | </button> |
| 439 | </div> | 469 | </div> |
| 440 | <div class="mobile-filter-section"> | 470 | <div class="mobile-filter-section"> |
| 441 | - <div class="mobile-filter-title">Buscar en:</div> | 471 | + <div class="mobile-filter-title">Buscar</div> |
| 442 | <div class="mobile-filter-bar"> | 472 | <div class="mobile-filter-bar"> |
| 443 | <button class="mobile-filter-chip" class:filter-chip-active={searchFilters.entidad} on:click={() => toggleFilter('entidad')}> | 473 | <button class="mobile-filter-chip" class:filter-chip-active={searchFilters.entidad} on:click={() => toggleFilter('entidad')}> |
| 444 | <span class="filter-chip-dot" style="background:#4A8B6E"></span> | 474 | <span class="filter-chip-dot" style="background:#4A8B6E"></span> |
| 445 | - Entidades | 475 | + Instituciones |
| 446 | </button> | 476 | </button> |
| 447 | <button class="mobile-filter-chip" class:filter-chip-active={searchFilters.objeto_gasto} on:click={() => toggleFilter('objeto_gasto')}> | 477 | <button class="mobile-filter-chip" class:filter-chip-active={searchFilters.objeto_gasto} on:click={() => toggleFilter('objeto_gasto')}> |
| 448 | <span class="filter-chip-dot" style="background:#C9A751"></span> | 478 | <span class="filter-chip-dot" style="background:#C9A751"></span> |
| 449 | - Objetos | 479 | + Gastos |
| 450 | </button> | 480 | </button> |
| 451 | <button class="mobile-filter-chip" class:filter-chip-active={searchFilters.rubro} on:click={() => toggleFilter('rubro')}> | 481 | <button class="mobile-filter-chip" class:filter-chip-active={searchFilters.rubro} on:click={() => toggleFilter('rubro')}> |
| 452 | <span class="filter-chip-dot" style="background:#8B6E9B"></span> | 482 | <span class="filter-chip-dot" style="background:#8B6E9B"></span> |
| 453 | - Rubros | 483 | + Ingresos |
| 454 | </button> | 484 | </button> |
| 455 | </div> | 485 | </div> |
| 456 | </div> | 486 | </div> |
| ... | @@ -460,7 +490,11 @@ | ... | @@ -460,7 +490,11 @@ |
| 460 | <div class="pills"> | 490 | <div class="pills"> |
| 461 | <span class="pills-label">Ej:</span> | 491 | <span class="pills-label">Ej:</span> |
| 462 | {#each examples as ex} | 492 | {#each examples as ex} |
| 463 | - <button class="pill" style="color:{ex.color};background:{ex.bg}" on:click={() => searchVal = ex.label}>{ex.label}</button> | 493 | + {#if ex.href} |
| 494 | + <a class="pill" style="color:{ex.color};background:{ex.bg}" href={ex.href}>{ex.label}</a> | ||
| 495 | + {:else} | ||
| 496 | + <button class="pill" style="color:{ex.color};background:{ex.bg}" on:click={() => searchVal = ex.label}>{ex.label}</button> | ||
| 497 | + {/if} | ||
| 464 | {/each} | 498 | {/each} |
| 465 | </div> | 499 | </div> |
| 466 | </div> | 500 | </div> |
| ... | @@ -778,17 +812,31 @@ | ... | @@ -778,17 +812,31 @@ |
| 778 | <div class="vis-grid-simple"> | 812 | <div class="vis-grid-simple"> |
| 779 | {#each VISUALIZACIONES as v, i} | 813 | {#each VISUALIZACIONES as v, i} |
| 780 | <div use:reveal={{ id: `vis-${i}`, delay: 60 + i * 80 }} class="reveal" class:revealed={revealStates[`vis-${i}`]}> | 814 | <div use:reveal={{ id: `vis-${i}`, delay: 60 + i * 80 }} class="reveal" class:revealed={revealStates[`vis-${i}`]}> |
| 781 | - <div class="vis-card" class:vis-card-hovered={hVis === i} on:mouseenter={() => hVis = i} on:mouseleave={() => hVis = null} role="button" tabindex="0"> | 815 | + {#if v.href} |
| 782 | - <div class="vis-accent" style="background:{v.color};transform:scaleX({hVis === i ? 1 : 0})"></div> | 816 | + <a href={v.href} class="vis-card vis-card-link" class:vis-card-hovered={hVis === i} on:mouseenter={() => hVis = i} on:mouseleave={() => hVis = null}> |
| 783 | - <div> | 817 | + <div class="vis-accent" style="background:{v.color};transform:scaleX({hVis === i ? 1 : 0})"></div> |
| 784 | - <span class="vis-type" style="background:color-mix(in srgb, {v.color} 12%, transparent);color:{v.color}">{v.type}</span> | 818 | + <div> |
| 785 | - <h4 class="vis-title">{v.title}</h4> | 819 | + <span class="vis-type" style="background:color-mix(in srgb, {v.color} 12%, transparent);color:{v.color}">{v.type}</span> |
| 786 | - <p class="vis-desc">{v.desc}</p> | 820 | + <h4 class="vis-title">{v.title}</h4> |
| 787 | - </div> | 821 | + <p class="vis-desc">{v.desc}</p> |
| 788 | - <div class="vis-cta" style="color:{hVis === i ? v.color : '#6B6860'}"> | 822 | + </div> |
| 789 | - Explorar <span class="vis-arrow" class:vis-arrow-moved={hVis === i}>→</span> | 823 | + <div class="vis-cta" style="color:{hVis === i ? v.color : '#6B6860'}"> |
| 824 | + Explorar <span class="vis-arrow" class:vis-arrow-moved={hVis === i}>→</span> | ||
| 825 | + </div> | ||
| 826 | + </a> | ||
| 827 | + {:else} | ||
| 828 | + <div class="vis-card vis-card-disabled" class:vis-card-hovered={hVis === i} on:mouseenter={() => hVis = i} on:mouseleave={() => hVis = null} role="button" tabindex="0"> | ||
| 829 | + <div class="vis-accent" style="background:{v.color};transform:scaleX({hVis === i ? 1 : 0})"></div> | ||
| 830 | + <div> | ||
| 831 | + <span class="vis-type" style="background:color-mix(in srgb, {v.color} 12%, transparent);color:{v.color}">{v.type}</span> | ||
| 832 | + <h4 class="vis-title">{v.title}</h4> | ||
| 833 | + <p class="vis-desc">{v.desc}</p> | ||
| 834 | + </div> | ||
| 835 | + <div class="vis-cta" style="color:#6B6860"> | ||
| 836 | + Próximamente | ||
| 837 | + </div> | ||
| 790 | </div> | 838 | </div> |
| 791 | - </div> | 839 | + {/if} |
| 792 | </div> | 840 | </div> |
| 793 | {/each} | 841 | {/each} |
| 794 | </div> | 842 | </div> |
| ... | @@ -913,24 +961,31 @@ | ... | @@ -913,24 +961,31 @@ |
| 913 | .search-divider{width:1px;height:32px;background:rgba(255,255,255,0.08);margin:0 16px;flex-shrink:0} | 961 | .search-divider{width:1px;height:32px;background:rgba(255,255,255,0.08);margin:0 16px;flex-shrink:0} |
| 914 | .search-icon{flex-shrink:0;transition:stroke 0.3s} | 962 | .search-icon{flex-shrink:0;transition:stroke 0.3s} |
| 915 | .search-input-wrap{flex:1;position:relative;display:flex;align-items:center} | 963 | .search-input-wrap{flex:1;position:relative;display:flex;align-items:center} |
| 916 | - .search-input-wrap input{width:100%;background:transparent;border:none;outline:none;color:#F5F0E8;font-size:16px;font-family:var(--sans);padding:12px 12px 12px 10px;caret-color:transparent} | 964 | + .search-input-wrap input{width:100%;background:transparent;border:none;outline:none;color:#F5F0E8;font-size:16px;font-family:var(--sans);padding:12px 12px 12px 10px;caret-color:#C9A751} |
| 917 | .search-placeholder{position:absolute;left:10px;top:50%;transform:translateY(-50%);display:flex;align-items:center;pointer-events:none} | 965 | .search-placeholder{position:absolute;left:10px;top:50%;transform:translateY(-50%);display:flex;align-items:center;pointer-events:none} |
| 918 | - .blink-cursor{width:1.5px;height:18px;margin-right:1px;animation:blink 1.1s step-end infinite;border-radius:1px;flex-shrink:0} | 966 | + .blink-cursor{width:1.5px;height:18px;margin-right:1px;animation:blink 1s step-end infinite;border-radius:1px;flex-shrink:0} |
| 967 | + .cursor-diario{background:#4A8B6E} | ||
| 968 | + .cursor-archivo{background:#C9A751} | ||
| 919 | .placeholder-text{font-family:var(--sans);font-size:16px;color:#B8B5AD} | 969 | .placeholder-text{font-family:var(--sans);font-size:16px;color:#B8B5AD} |
| 920 | .placeholder-mobile{display:none} | 970 | .placeholder-mobile{display:none} |
| 921 | .gear-btn{width:38px;height:38px;border-radius:10px;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,0.06);cursor:pointer;transition:all 0.3s;flex-shrink:0;margin-left:8px} | 971 | .gear-btn{width:38px;height:38px;border-radius:10px;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,0.06);cursor:pointer;transition:all 0.3s;flex-shrink:0;margin-left:8px} |
| 972 | + .gear-btn svg{transition:transform 0.3s ease} | ||
| 973 | + .gear-btn:hover svg{transform:rotate(45deg)} | ||
| 922 | .gear-btn:hover,.gear-active{background:rgba(255,255,255,0.12)} | 974 | .gear-btn:hover,.gear-active{background:rgba(255,255,255,0.12)} |
| 923 | .gear-wrap{position:relative} | 975 | .gear-wrap{position:relative} |
| 924 | .gear-desktop{display:block} | 976 | .gear-desktop{display:block} |
| 925 | - .gear-menu{position:absolute;top:calc(100% + 8px);right:0;background:rgba(28,28,26,0.98);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid rgba(255,255,255,0.12);border-radius:12px;padding:12px;min-width:180px;z-index:9999;box-shadow:0 8px 32px rgba(0,0,0,0.4)} | 977 | + .gear-menu{position:absolute;top:calc(100% + 6px);right:0;background:rgba(28,28,26,0.92);backdrop-filter:blur(32px) saturate(1.2);-webkit-backdrop-filter:blur(32px) saturate(1.2);border-radius:16px;min-width:320px;z-index:9999;box-shadow:0 12px 48px rgba(0,0,0,0.5);padding:4px;animation:gearFadeIn 0.15s ease-out} |
| 926 | - .gear-menu-title{font-family:var(--mono);font-size:10px;font-weight:600;letter-spacing:0.1em;color:#6B6860;text-transform:uppercase;margin-bottom:8px;padding-left:4px} | 978 | + @keyframes gearFadeIn{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}} |
| 927 | - .gear-filter-btn{width:100%;display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:8px;background:transparent;border:none;cursor:pointer;transition:all 0.2s;font-family:var(--sans);font-size:14px;color:#B8B5AD;text-align:left} | 979 | + .gear-menu-title{font-family:var(--sans);font-size:11px;font-weight:400;color:#6B6860;padding:10px 14px 6px;letter-spacing:0.02em} |
| 928 | - .gear-filter-btn:hover{background:rgba(255,255,255,0.06)} | 980 | + .gear-row{display:flex;align-items:center;gap:12px;width:100%;background:transparent;border:none;padding:12px 14px;border-radius:12px;cursor:pointer;transition:background 0.15s;text-align:left} |
| 929 | - .gear-filter-btn.filter-active{color:#F5F0E8} | 981 | + .gear-row:hover{background:rgba(255,255,255,0.04)} |
| 930 | - .filter-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;opacity:0.4;transition:opacity 0.2s} | 982 | + .gear-check{width:18px;height:18px;border-radius:6px;border:1.5px solid rgba(255,255,255,0.15);background:transparent;display:flex;align-items:center;justify-content:center;transition:all 0.2s;flex-shrink:0;color:transparent} |
| 931 | - .gear-filter-btn.filter-active .filter-dot{opacity:1} | 983 | + .gear-check-on{border-color:transparent;background:#C9A751;color:#1C1C1A} |
| 932 | - .filter-check{margin-left:auto;font-size:12px;color:#4A8B6E;opacity:0;transition:opacity 0.2s} | 984 | + .gear-row-content{flex:1;display:flex;align-items:baseline;gap:8px} |
| 933 | - .gear-filter-btn.filter-active .filter-check{opacity:1} | 985 | + .gear-row-question{font-family:var(--sans);font-size:12px;color:#5A5850;font-weight:400} |
| 986 | + .gear-row-label{font-family:var(--sans);font-size:14px;color:#8B8880;transition:color 0.15s} | ||
| 987 | + .gear-row-label-on{color:#F5F0E8} | ||
| 988 | + .gear-row-count{font-family:var(--mono);font-size:10px;color:#4A4840} | ||
| 934 | 989 | ||
| 935 | /* Mobile settings button (hidden on desktop) */ | 990 | /* Mobile settings button (hidden on desktop) */ |
| 936 | .mobile-settings-btn{display:none;width:40px;height:40px;border-radius:10px;background:rgba(255,255,255,0.06);border:none;cursor:pointer;align-items:center;justify-content:center;position:relative;transition:all 0.3s;flex-shrink:0} | 991 | .mobile-settings-btn{display:none;width:40px;height:40px;border-radius:10px;background:rgba(255,255,255,0.06);border:none;cursor:pointer;align-items:center;justify-content:center;position:relative;transition:all 0.3s;flex-shrink:0} |
| ... | @@ -976,7 +1031,7 @@ | ... | @@ -976,7 +1031,7 @@ |
| 976 | .result-type{font-family:var(--mono);font-size:9px;letter-spacing:0.1em;font-weight:600} | 1031 | .result-type{font-family:var(--mono);font-size:9px;letter-spacing:0.1em;font-weight:600} |
| 977 | .pills{display:flex;gap:8px;margin-top:16px;flex-wrap:wrap;align-items:center;justify-content:center} | 1032 | .pills{display:flex;gap:8px;margin-top:16px;flex-wrap:wrap;align-items:center;justify-content:center} |
| 978 | .pills-label{font-family:var(--mono);font-size:11px;letter-spacing:0.1em;color:#B8B5AD;margin-right:2px} | 1033 | .pills-label{font-family:var(--mono);font-size:11px;letter-spacing:0.1em;color:#B8B5AD;margin-right:2px} |
| 979 | - .pill{font-family:var(--mono);font-size:12px;font-weight:300;letter-spacing:0.03em;border:none;border-radius:6px;padding:5px 12px;cursor:pointer;transition:all 0.25s;line-height:1.4;position:relative;overflow:hidden} | 1034 | + .pill{font-family:var(--mono);font-size:12px;font-weight:300;letter-spacing:0.03em;border:none;border-radius:6px;padding:5px 12px;cursor:pointer;transition:all 0.25s;line-height:1.4;position:relative;overflow:hidden;text-decoration:none} |
| 980 | .pill::before{content:'';position:absolute;inset:0;background:rgba(255,255,255,0.12);opacity:0;transition:opacity 0.25s} | 1035 | .pill::before{content:'';position:absolute;inset:0;background:rgba(255,255,255,0.12);opacity:0;transition:opacity 0.25s} |
| 981 | .pill:hover::before{opacity:1} | 1036 | .pill:hover::before{opacity:1} |
| 982 | .pill:hover{transform:translateY(-1px)} | 1037 | .pill:hover{transform:translateY(-1px)} |
| ... | @@ -1131,6 +1186,9 @@ | ... | @@ -1131,6 +1186,9 @@ |
| 1131 | .vis-cta{font-family:var(--sans);font-size:14px;margin-top:20px;transition:color 0.3s;display:flex;align-items:center;gap:3px;color:#6B6860} | 1186 | .vis-cta{font-family:var(--sans);font-size:14px;margin-top:20px;transition:color 0.3s;display:flex;align-items:center;gap:3px;color:#6B6860} |
| 1132 | .vis-arrow{transition:transform 0.3s;display:inline-block} | 1187 | .vis-arrow{transition:transform 0.3s;display:inline-block} |
| 1133 | .vis-arrow-moved{transform:translateX(3px)} | 1188 | .vis-arrow-moved{transform:translateX(3px)} |
| 1189 | + .vis-card-link{text-decoration:none;color:inherit} | ||
| 1190 | + .vis-card-disabled{opacity:0.6;cursor:default} | ||
| 1191 | + .vis-card-disabled:hover{transform:none;box-shadow:none} | ||
| 1134 | 1192 | ||
| 1135 | /* ── MANIFIESTO ── */ | 1193 | /* ── MANIFIESTO ── */ |
| 1136 | .manifiesto{background:#1C1C1A;color:#F5F0E8;padding:96px 48px;position:relative;min-height:100vh;display:flex;flex-direction:column;justify-content:center;scroll-snap-align:start;scroll-snap-stop:always} | 1194 | .manifiesto{background:#1C1C1A;color:#F5F0E8;padding:96px 48px;position:relative;min-height:100vh;display:flex;flex-direction:column;justify-content:center;scroll-snap-align:start;scroll-snap-stop:always} | ... | ... |
This diff could not be displayed because it is too large.
src/routes/documentacion/+page.svelte
0 → 100644
| 1 | +<script> | ||
| 2 | + import { onMount } from 'svelte'; | ||
| 3 | + import { marked } from 'marked'; | ||
| 4 | + | ||
| 5 | + let content = $state(''); | ||
| 6 | + let loading = $state(true); | ||
| 7 | + let headings = $state([]); | ||
| 8 | + let activeId = $state(''); | ||
| 9 | + let sidebarOpen = $state(false); | ||
| 10 | + let expandedSections = $state(new Set()); | ||
| 11 | + | ||
| 12 | + // Build hierarchical structure from flat headings | ||
| 13 | + function buildHierarchy(flatHeadings) { | ||
| 14 | + const root = { children: [], level: 0 }; | ||
| 15 | + const stack = [root]; | ||
| 16 | + | ||
| 17 | + for (const heading of flatHeadings) { | ||
| 18 | + const node = { ...heading, children: [] }; | ||
| 19 | + | ||
| 20 | + // Pop stack until we find a parent with lower level | ||
| 21 | + while (stack.length > 1 && stack[stack.length - 1].level >= heading.level) { | ||
| 22 | + stack.pop(); | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + stack[stack.length - 1].children.push(node); | ||
| 26 | + stack.push(node); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + return root.children; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + // Extract headings from markdown and add IDs | ||
| 33 | + function processMarkdown(markdown) { | ||
| 34 | + const headingsList = []; | ||
| 35 | + let headingIndex = 0; | ||
| 36 | + | ||
| 37 | + // Custom renderer for marked v17+ | ||
| 38 | + const renderer = { | ||
| 39 | + heading({ tokens, depth }) { | ||
| 40 | + const text = tokens.map(t => t.text || t.raw || '').join(''); | ||
| 41 | + | ||
| 42 | + const slug = text | ||
| 43 | + .toLowerCase() | ||
| 44 | + .normalize('NFD') | ||
| 45 | + .replace(/[\u0300-\u036f]/g, '') | ||
| 46 | + .replace(/[^\w\s-]/g, '') | ||
| 47 | + .replace(/\s+/g, '-') | ||
| 48 | + .replace(/-+/g, '-') | ||
| 49 | + .trim(); | ||
| 50 | + | ||
| 51 | + const id = `${slug}-${headingIndex++}`; | ||
| 52 | + | ||
| 53 | + // Include up to level 4 | ||
| 54 | + if (depth <= 4) { | ||
| 55 | + headingsList.push({ | ||
| 56 | + id, | ||
| 57 | + text: text, | ||
| 58 | + level: depth | ||
| 59 | + }); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + // Add anchor link to each heading | ||
| 63 | + return `<h${depth} id="${id}" class="heading-anchor"><a href="#${id}" class="anchor-link">${text}</a></h${depth}>`; | ||
| 64 | + } | ||
| 65 | + }; | ||
| 66 | + | ||
| 67 | + marked.use({ renderer }); | ||
| 68 | + const html = marked.parse(markdown); | ||
| 69 | + | ||
| 70 | + return { html, headings: headingsList }; | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + onMount(async () => { | ||
| 74 | + try { | ||
| 75 | + const response = await fetch('/docs.md'); | ||
| 76 | + const markdown = await response.text(); | ||
| 77 | + const result = processMarkdown(markdown); | ||
| 78 | + content = result.html; | ||
| 79 | + headings = buildHierarchy(result.headings); | ||
| 80 | + | ||
| 81 | + // Expand level 1 by default | ||
| 82 | + const initialExpanded = new Set(); | ||
| 83 | + result.headings.filter(h => h.level === 1).forEach(h => initialExpanded.add(h.id)); | ||
| 84 | + expandedSections = initialExpanded; | ||
| 85 | + | ||
| 86 | + // Setup intersection observer for scroll tracking | ||
| 87 | + setTimeout(() => { | ||
| 88 | + setupScrollTracking(result.headings); | ||
| 89 | + | ||
| 90 | + // Handle URL hash on page load | ||
| 91 | + if (window.location.hash) { | ||
| 92 | + const id = window.location.hash.slice(1); | ||
| 93 | + const element = document.getElementById(id); | ||
| 94 | + if (element) { | ||
| 95 | + // Expand parents of this heading | ||
| 96 | + const parentIds = findParentIds(id, headings); | ||
| 97 | + if (parentIds && parentIds.length > 0) { | ||
| 98 | + const newExpanded = new Set(expandedSections); | ||
| 99 | + parentIds.forEach(pid => newExpanded.add(pid)); | ||
| 100 | + expandedSections = newExpanded; | ||
| 101 | + } | ||
| 102 | + // Scroll to element | ||
| 103 | + setTimeout(() => { | ||
| 104 | + element.scrollIntoView({ behavior: 'smooth', block: 'start' }); | ||
| 105 | + activeId = id; | ||
| 106 | + }, 50); | ||
| 107 | + } | ||
| 108 | + } | ||
| 109 | + }, 100); | ||
| 110 | + } catch (error) { | ||
| 111 | + console.error('Error loading documentation:', error); | ||
| 112 | + content = '<p>Error al cargar la documentación.</p>'; | ||
| 113 | + } finally { | ||
| 114 | + loading = false; | ||
| 115 | + } | ||
| 116 | + }); | ||
| 117 | + | ||
| 118 | + // Find all parent IDs for a given heading ID | ||
| 119 | + function findParentIds(targetId, nodes, parents = []) { | ||
| 120 | + for (const node of nodes) { | ||
| 121 | + if (node.id === targetId) { | ||
| 122 | + return parents; | ||
| 123 | + } | ||
| 124 | + if (node.children && node.children.length > 0) { | ||
| 125 | + const found = findParentIds(targetId, node.children, [...parents, node.id]); | ||
| 126 | + if (found) return found; | ||
| 127 | + } | ||
| 128 | + } | ||
| 129 | + return null; | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + function setupScrollTracking(flatHeadings) { | ||
| 133 | + const observer = new IntersectionObserver( | ||
| 134 | + (entries) => { | ||
| 135 | + entries.forEach((entry) => { | ||
| 136 | + if (entry.isIntersecting) { | ||
| 137 | + const newActiveId = entry.target.id; | ||
| 138 | + activeId = newActiveId; | ||
| 139 | + | ||
| 140 | + // Auto-expand parents of the active heading | ||
| 141 | + const parentIds = findParentIds(newActiveId, headings); | ||
| 142 | + if (parentIds && parentIds.length > 0) { | ||
| 143 | + const newExpanded = new Set(expandedSections); | ||
| 144 | + parentIds.forEach(id => newExpanded.add(id)); | ||
| 145 | + expandedSections = newExpanded; | ||
| 146 | + } | ||
| 147 | + } | ||
| 148 | + }); | ||
| 149 | + }, | ||
| 150 | + { | ||
| 151 | + rootMargin: '-100px 0px -70% 0px', | ||
| 152 | + threshold: 0 | ||
| 153 | + } | ||
| 154 | + ); | ||
| 155 | + | ||
| 156 | + flatHeadings.forEach((heading) => { | ||
| 157 | + const element = document.getElementById(heading.id); | ||
| 158 | + if (element) { | ||
| 159 | + observer.observe(element); | ||
| 160 | + } | ||
| 161 | + }); | ||
| 162 | + | ||
| 163 | + return () => observer.disconnect(); | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + function scrollToHeading(id) { | ||
| 167 | + const element = document.getElementById(id); | ||
| 168 | + if (element) { | ||
| 169 | + // Update URL hash without triggering scroll | ||
| 170 | + history.pushState(null, '', `#${id}`); | ||
| 171 | + element.scrollIntoView({ behavior: 'smooth', block: 'start' }); | ||
| 172 | + sidebarOpen = false; | ||
| 173 | + } | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + function toggleSection(id) { | ||
| 177 | + const newExpanded = new Set(expandedSections); | ||
| 178 | + if (newExpanded.has(id)) { | ||
| 179 | + newExpanded.delete(id); | ||
| 180 | + } else { | ||
| 181 | + newExpanded.add(id); | ||
| 182 | + } | ||
| 183 | + expandedSections = newExpanded; | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | + function toggleSidebar() { | ||
| 187 | + sidebarOpen = !sidebarOpen; | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + // Check if a heading or any of its children is active | ||
| 191 | + function isActiveOrHasActiveChild(node, currentActiveId) { | ||
| 192 | + if (node.id === currentActiveId) return true; | ||
| 193 | + if (node.children) { | ||
| 194 | + return node.children.some(child => isActiveOrHasActiveChild(child, currentActiveId)); | ||
| 195 | + } | ||
| 196 | + return false; | ||
| 197 | + } | ||
| 198 | +</script> | ||
| 199 | + | ||
| 200 | +<svelte:head> | ||
| 201 | + <title>Documentación | Presupuesto Público Bolivia</title> | ||
| 202 | +</svelte:head> | ||
| 203 | + | ||
| 204 | +<div class="docs-page"> | ||
| 205 | + <header class="docs-header"> | ||
| 206 | + <div class="docs-header-content"> | ||
| 207 | + <div class="docs-header-top"> | ||
| 208 | + <a href="/" class="docs-back"> | ||
| 209 | + <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | ||
| 210 | + <path d="M19 12H5M12 19l-7-7 7-7"/> | ||
| 211 | + </svg> | ||
| 212 | + <span>Inicio</span> | ||
| 213 | + </a> | ||
| 214 | + | ||
| 215 | + <!-- Mobile menu button --> | ||
| 216 | + <button class="docs-menu-btn" onclick={toggleSidebar}> | ||
| 217 | + <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | ||
| 218 | + <path d="M3 12h18M3 6h18M3 18h18"/> | ||
| 219 | + </svg> | ||
| 220 | + <span>Índice</span> | ||
| 221 | + </button> | ||
| 222 | + </div> | ||
| 223 | + <h1 class="docs-title">Documentación</h1> | ||
| 224 | + <p class="docs-subtitle">Estructura de las bases de datos del presupuesto público</p> | ||
| 225 | + </div> | ||
| 226 | + </header> | ||
| 227 | + | ||
| 228 | + <div class="docs-layout-wrapper"> | ||
| 229 | + <!-- Fixed Sidebar / Table of Contents --> | ||
| 230 | + <aside class="docs-sidebar" class:open={sidebarOpen}> | ||
| 231 | + <div class="docs-sidebar-content"> | ||
| 232 | + <div class="docs-sidebar-header"> | ||
| 233 | + <span class="docs-sidebar-title">Índice</span> | ||
| 234 | + <button class="docs-sidebar-close" onclick={() => sidebarOpen = false}> | ||
| 235 | + <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | ||
| 236 | + <path d="M18 6L6 18M6 6l12 12"/> | ||
| 237 | + </svg> | ||
| 238 | + </button> | ||
| 239 | + </div> | ||
| 240 | + | ||
| 241 | + <nav class="docs-toc"> | ||
| 242 | + {#snippet renderHeading(node, depth = 0)} | ||
| 243 | + <div class="toc-node"> | ||
| 244 | + <div class="toc-item-row"> | ||
| 245 | + {#if node.children && node.children.length > 0} | ||
| 246 | + <button | ||
| 247 | + class="toc-toggle" | ||
| 248 | + class:expanded={expandedSections.has(node.id)} | ||
| 249 | + onclick={() => toggleSection(node.id)} | ||
| 250 | + > | ||
| 251 | + <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | ||
| 252 | + <path d="M9 18l6-6-6-6"/> | ||
| 253 | + </svg> | ||
| 254 | + </button> | ||
| 255 | + {:else} | ||
| 256 | + <span class="toc-toggle-spacer"></span> | ||
| 257 | + {/if} | ||
| 258 | + <button | ||
| 259 | + class="docs-toc-item level-{node.level}" | ||
| 260 | + class:active={activeId === node.id} | ||
| 261 | + onclick={() => scrollToHeading(node.id)} | ||
| 262 | + > | ||
| 263 | + {node.text} | ||
| 264 | + </button> | ||
| 265 | + </div> | ||
| 266 | + {#if node.children && node.children.length > 0 && expandedSections.has(node.id)} | ||
| 267 | + <div class="toc-children"> | ||
| 268 | + {#each node.children as child} | ||
| 269 | + {@render renderHeading(child, depth + 1)} | ||
| 270 | + {/each} | ||
| 271 | + </div> | ||
| 272 | + {/if} | ||
| 273 | + </div> | ||
| 274 | + {/snippet} | ||
| 275 | + | ||
| 276 | + {#each headings as heading} | ||
| 277 | + {@render renderHeading(heading)} | ||
| 278 | + {/each} | ||
| 279 | + </nav> | ||
| 280 | + </div> | ||
| 281 | + </aside> | ||
| 282 | + | ||
| 283 | + <!-- Main content --> | ||
| 284 | + <main class="docs-main"> | ||
| 285 | + {#if loading} | ||
| 286 | + <div class="docs-loading"> | ||
| 287 | + <svg class="docs-loader" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> | ||
| 288 | + <rect x="1" y="1" rx="1" width="10" height="10"> | ||
| 289 | + <animate id="sq1" attributeName="fill-opacity" values="1;0.4;0.4;0.4;1" dur="1.6s" repeatCount="indefinite" begin="0s"/> | ||
| 290 | + </rect> | ||
| 291 | + <rect x="13" y="1" rx="1" width="10" height="10"> | ||
| 292 | + <animate id="sq2" attributeName="fill-opacity" values="0.4;1;0.4;0.4;0.4" dur="1.6s" repeatCount="indefinite" begin="0s"/> | ||
| 293 | + </rect> | ||
| 294 | + <rect x="1" y="13" rx="1" width="10" height="10"> | ||
| 295 | + <animate id="sq3" attributeName="fill-opacity" values="0.4;0.4;0.4;1;0.4" dur="1.6s" repeatCount="indefinite" begin="0s"/> | ||
| 296 | + </rect> | ||
| 297 | + <rect x="13" y="13" rx="1" width="10" height="10"> | ||
| 298 | + <animate id="sq4" attributeName="fill-opacity" values="0.4;0.4;1;0.4;0.4" dur="1.6s" repeatCount="indefinite" begin="0s"/> | ||
| 299 | + </rect> | ||
| 300 | + </svg> | ||
| 301 | + <p>Cargando documentación...</p> | ||
| 302 | + </div> | ||
| 303 | + {:else} | ||
| 304 | + <article class="docs-content"> | ||
| 305 | + {@html content} | ||
| 306 | + </article> | ||
| 307 | + {/if} | ||
| 308 | + </main> | ||
| 309 | + </div> | ||
| 310 | + | ||
| 311 | + <!-- Overlay for mobile --> | ||
| 312 | + {#if sidebarOpen} | ||
| 313 | + <button class="docs-overlay" onclick={() => sidebarOpen = false}></button> | ||
| 314 | + {/if} | ||
| 315 | +</div> | ||
| 316 | + | ||
| 317 | +<style> | ||
| 318 | + .docs-page { | ||
| 319 | + min-height: 100vh; | ||
| 320 | + background-color: var(--theme-body); | ||
| 321 | + font-family: var(--font-sans); | ||
| 322 | + } | ||
| 323 | + | ||
| 324 | + .docs-header { | ||
| 325 | + border-bottom: 1px solid var(--theme-borde); | ||
| 326 | + background-color: var(--theme-body); | ||
| 327 | + position: fixed; | ||
| 328 | + top: 0; | ||
| 329 | + left: 0; | ||
| 330 | + right: 0; | ||
| 331 | + z-index: 20; | ||
| 332 | + } | ||
| 333 | + | ||
| 334 | + .docs-header-content { | ||
| 335 | + max-width: 1000px; | ||
| 336 | + margin: 0 auto; | ||
| 337 | + padding: 1rem 1.5rem; | ||
| 338 | + } | ||
| 339 | + | ||
| 340 | + .docs-header-top { | ||
| 341 | + display: flex; | ||
| 342 | + justify-content: space-between; | ||
| 343 | + align-items: center; | ||
| 344 | + margin-bottom: 0.5rem; | ||
| 345 | + } | ||
| 346 | + | ||
| 347 | + .docs-back { | ||
| 348 | + display: inline-flex; | ||
| 349 | + align-items: center; | ||
| 350 | + gap: 0.5rem; | ||
| 351 | + color: var(--theme-texto); | ||
| 352 | + text-decoration: none; | ||
| 353 | + font-size: 0.875rem; | ||
| 354 | + transition: color 0.15s ease; | ||
| 355 | + } | ||
| 356 | + | ||
| 357 | + .docs-back:hover { | ||
| 358 | + color: var(--theme-accent); | ||
| 359 | + } | ||
| 360 | + | ||
| 361 | + .docs-menu-btn { | ||
| 362 | + display: flex; | ||
| 363 | + align-items: center; | ||
| 364 | + gap: 0.5rem; | ||
| 365 | + padding: 0.5rem 0.75rem; | ||
| 366 | + background: var(--theme-fill); | ||
| 367 | + border: 1px solid var(--theme-borde); | ||
| 368 | + border-radius: 8px; | ||
| 369 | + color: var(--theme-texto); | ||
| 370 | + font-size: 0.875rem; | ||
| 371 | + cursor: pointer; | ||
| 372 | + transition: all 0.15s ease; | ||
| 373 | + } | ||
| 374 | + | ||
| 375 | + .docs-menu-btn:hover { | ||
| 376 | + color: var(--theme-titulo); | ||
| 377 | + border-color: var(--theme-accent); | ||
| 378 | + } | ||
| 379 | + | ||
| 380 | + @media (min-width: 1100px) { | ||
| 381 | + .docs-menu-btn { | ||
| 382 | + display: none; | ||
| 383 | + } | ||
| 384 | + } | ||
| 385 | + | ||
| 386 | + .docs-title { | ||
| 387 | + font-family: var(--font-sans); | ||
| 388 | + font-size: 1.5rem; | ||
| 389 | + font-weight: 700; | ||
| 390 | + color: var(--theme-titulo); | ||
| 391 | + margin: 0 0 0.25rem; | ||
| 392 | + } | ||
| 393 | + | ||
| 394 | + .docs-subtitle { | ||
| 395 | + color: var(--theme-texto); | ||
| 396 | + font-size: 0.875rem; | ||
| 397 | + margin: 0; | ||
| 398 | + } | ||
| 399 | + | ||
| 400 | + /* Centered layout container */ | ||
| 401 | + .docs-layout-wrapper { | ||
| 402 | + max-width: 700px; | ||
| 403 | + margin: 0 auto; | ||
| 404 | + padding-top: 140px; | ||
| 405 | + padding-left: 1.5rem; | ||
| 406 | + padding-right: 1.5rem; | ||
| 407 | + } | ||
| 408 | + | ||
| 409 | + /* Sidebar */ | ||
| 410 | + .docs-sidebar { | ||
| 411 | + display: none; | ||
| 412 | + } | ||
| 413 | + | ||
| 414 | + /* Desktop: sidebar (200) + gap (50) + content (600) = 850px total centered */ | ||
| 415 | + @media (min-width: 1100px) { | ||
| 416 | + .docs-layout-wrapper { | ||
| 417 | + max-width: 600px; | ||
| 418 | + margin-left: calc(50% - 425px + 250px); | ||
| 419 | + margin-right: auto; | ||
| 420 | + padding-left: 2rem; | ||
| 421 | + padding-right: 2rem; | ||
| 422 | + } | ||
| 423 | + | ||
| 424 | + .docs-sidebar { | ||
| 425 | + display: block; | ||
| 426 | + position: fixed; | ||
| 427 | + top: 120px; | ||
| 428 | + left: calc(50% - 425px); | ||
| 429 | + width: 200px; | ||
| 430 | + max-height: calc(100vh - 140px); | ||
| 431 | + overflow-y: auto; | ||
| 432 | + overflow-x: hidden; | ||
| 433 | + padding: 0; | ||
| 434 | + z-index: 10; | ||
| 435 | + } | ||
| 436 | + | ||
| 437 | + .docs-sidebar-content { | ||
| 438 | + padding-right: 1rem; | ||
| 439 | + padding-top: 2rem; | ||
| 440 | + } | ||
| 441 | + } | ||
| 442 | + | ||
| 443 | + /* Larger screens: more space */ | ||
| 444 | + @media (min-width: 1300px) { | ||
| 445 | + .docs-layout-wrapper { | ||
| 446 | + max-width: 650px; | ||
| 447 | + margin-left: calc(50% - 475px + 280px); | ||
| 448 | + } | ||
| 449 | + | ||
| 450 | + .docs-sidebar { | ||
| 451 | + left: calc(50% - 475px); | ||
| 452 | + width: 230px; | ||
| 453 | + } | ||
| 454 | + } | ||
| 455 | + | ||
| 456 | + /* Mobile sidebar */ | ||
| 457 | + @media (max-width: 1099px) { | ||
| 458 | + .docs-sidebar { | ||
| 459 | + display: block; | ||
| 460 | + position: fixed; | ||
| 461 | + top: 0; | ||
| 462 | + left: 0; | ||
| 463 | + width: 320px; | ||
| 464 | + height: 100vh; | ||
| 465 | + background: var(--theme-body); | ||
| 466 | + z-index: 100; | ||
| 467 | + transform: translateX(-100%); | ||
| 468 | + transition: transform 0.25s ease; | ||
| 469 | + box-shadow: 4px 0 24px rgba(0, 0, 0, 0.15); | ||
| 470 | + } | ||
| 471 | + | ||
| 472 | + .docs-sidebar.open { | ||
| 473 | + transform: translateX(0); | ||
| 474 | + } | ||
| 475 | + | ||
| 476 | + .docs-sidebar-content { | ||
| 477 | + height: 100%; | ||
| 478 | + overflow-y: auto; | ||
| 479 | + padding: 1rem; | ||
| 480 | + } | ||
| 481 | + } | ||
| 482 | + | ||
| 483 | + .docs-sidebar-header { | ||
| 484 | + display: flex; | ||
| 485 | + justify-content: space-between; | ||
| 486 | + align-items: center; | ||
| 487 | + margin-bottom: 1rem; | ||
| 488 | + padding-bottom: 0.75rem; | ||
| 489 | + border-bottom: 1px solid var(--theme-borde); | ||
| 490 | + } | ||
| 491 | + | ||
| 492 | + @media (min-width: 1100px) { | ||
| 493 | + .docs-sidebar-header { | ||
| 494 | + border-bottom: none; | ||
| 495 | + padding-bottom: 0; | ||
| 496 | + margin-bottom: 1.25rem; | ||
| 497 | + } | ||
| 498 | + | ||
| 499 | + .docs-sidebar-close { | ||
| 500 | + display: none; | ||
| 501 | + } | ||
| 502 | + } | ||
| 503 | + | ||
| 504 | + .docs-sidebar-title { | ||
| 505 | + font-size: 0.8125rem; | ||
| 506 | + font-weight: 600; | ||
| 507 | + text-transform: uppercase; | ||
| 508 | + letter-spacing: 0.08em; | ||
| 509 | + color: var(--theme-texto); | ||
| 510 | + } | ||
| 511 | + | ||
| 512 | + .docs-sidebar-close { | ||
| 513 | + padding: 0.25rem; | ||
| 514 | + color: var(--theme-texto); | ||
| 515 | + background: none; | ||
| 516 | + border: none; | ||
| 517 | + cursor: pointer; | ||
| 518 | + border-radius: 4px; | ||
| 519 | + transition: color 0.15s ease; | ||
| 520 | + } | ||
| 521 | + | ||
| 522 | + .docs-sidebar-close:hover { | ||
| 523 | + color: var(--theme-titulo); | ||
| 524 | + } | ||
| 525 | + | ||
| 526 | + /* Table of Contents */ | ||
| 527 | + .docs-toc { | ||
| 528 | + display: flex; | ||
| 529 | + flex-direction: column; | ||
| 530 | + gap: 1px; | ||
| 531 | + } | ||
| 532 | + | ||
| 533 | + .toc-node { | ||
| 534 | + display: flex; | ||
| 535 | + flex-direction: column; | ||
| 536 | + } | ||
| 537 | + | ||
| 538 | + .toc-item-row { | ||
| 539 | + display: flex; | ||
| 540 | + align-items: flex-start; | ||
| 541 | + gap: 2px; | ||
| 542 | + } | ||
| 543 | + | ||
| 544 | + .toc-toggle { | ||
| 545 | + flex-shrink: 0; | ||
| 546 | + width: 20px; | ||
| 547 | + height: 24px; | ||
| 548 | + display: flex; | ||
| 549 | + align-items: center; | ||
| 550 | + justify-content: center; | ||
| 551 | + background: none; | ||
| 552 | + border: none; | ||
| 553 | + color: var(--theme-texto); | ||
| 554 | + cursor: pointer; | ||
| 555 | + opacity: 0.5; | ||
| 556 | + transition: all 0.15s ease; | ||
| 557 | + padding: 0; | ||
| 558 | + } | ||
| 559 | + | ||
| 560 | + .toc-toggle:hover { | ||
| 561 | + opacity: 1; | ||
| 562 | + color: var(--theme-accent); | ||
| 563 | + } | ||
| 564 | + | ||
| 565 | + .toc-toggle svg { | ||
| 566 | + transition: transform 0.15s ease; | ||
| 567 | + } | ||
| 568 | + | ||
| 569 | + .toc-toggle.expanded svg { | ||
| 570 | + transform: rotate(90deg); | ||
| 571 | + } | ||
| 572 | + | ||
| 573 | + .toc-toggle-spacer { | ||
| 574 | + width: 20px; | ||
| 575 | + flex-shrink: 0; | ||
| 576 | + } | ||
| 577 | + | ||
| 578 | + .toc-children { | ||
| 579 | + padding-left: 10px; | ||
| 580 | + border-left: 1px solid color-mix(in srgb, var(--theme-borde) 50%, transparent); | ||
| 581 | + margin-left: 9px; | ||
| 582 | + } | ||
| 583 | + | ||
| 584 | + .docs-toc-item { | ||
| 585 | + flex: 1; | ||
| 586 | + display: block; | ||
| 587 | + text-align: left; | ||
| 588 | + padding: 0.35rem 0.5rem; | ||
| 589 | + font-size: 0.8125rem; | ||
| 590 | + color: var(--theme-texto); | ||
| 591 | + background: none; | ||
| 592 | + border: none; | ||
| 593 | + border-radius: 4px; | ||
| 594 | + cursor: pointer; | ||
| 595 | + transition: all 0.15s ease; | ||
| 596 | + line-height: 1.3; | ||
| 597 | + } | ||
| 598 | + | ||
| 599 | + .docs-toc-item:hover { | ||
| 600 | + color: var(--theme-titulo); | ||
| 601 | + background: var(--theme-fill); | ||
| 602 | + } | ||
| 603 | + | ||
| 604 | + .docs-toc-item.active { | ||
| 605 | + color: var(--theme-accent); | ||
| 606 | + background: color-mix(in srgb, var(--theme-accent) 12%, transparent); | ||
| 607 | + font-weight: 500; | ||
| 608 | + } | ||
| 609 | + | ||
| 610 | + .docs-toc-item.level-1 { | ||
| 611 | + font-weight: 600; | ||
| 612 | + font-size: 0.875rem; | ||
| 613 | + color: var(--theme-titulo); | ||
| 614 | + } | ||
| 615 | + | ||
| 616 | + .docs-toc-item.level-2 { | ||
| 617 | + font-weight: 500; | ||
| 618 | + } | ||
| 619 | + | ||
| 620 | + .docs-toc-item.level-3 { | ||
| 621 | + font-size: 0.8125rem; | ||
| 622 | + } | ||
| 623 | + | ||
| 624 | + .docs-toc-item.level-4 { | ||
| 625 | + font-size: 0.75rem; | ||
| 626 | + font-family: monospace; | ||
| 627 | + color: color-mix(in srgb, var(--theme-texto) 80%, transparent); | ||
| 628 | + } | ||
| 629 | + | ||
| 630 | + .docs-toc-item.level-4.active { | ||
| 631 | + color: var(--theme-accent); | ||
| 632 | + } | ||
| 633 | + | ||
| 634 | + /* Overlay */ | ||
| 635 | + .docs-overlay { | ||
| 636 | + display: none; | ||
| 637 | + } | ||
| 638 | + | ||
| 639 | + @media (max-width: 1099px) { | ||
| 640 | + .docs-overlay { | ||
| 641 | + display: block; | ||
| 642 | + position: fixed; | ||
| 643 | + inset: 0; | ||
| 644 | + background: rgba(0, 0, 0, 0.4); | ||
| 645 | + z-index: 50; | ||
| 646 | + border: none; | ||
| 647 | + cursor: pointer; | ||
| 648 | + } | ||
| 649 | + } | ||
| 650 | + | ||
| 651 | + /* Main content */ | ||
| 652 | + .docs-main { | ||
| 653 | + flex: 1; | ||
| 654 | + min-width: 0; | ||
| 655 | + padding: 0 0 4rem 0; | ||
| 656 | + } | ||
| 657 | + | ||
| 658 | + @media (max-width: 1099px) { | ||
| 659 | + .docs-layout-wrapper { | ||
| 660 | + padding-top: 130px; | ||
| 661 | + padding-left: 1rem; | ||
| 662 | + padding-right: 1rem; | ||
| 663 | + } | ||
| 664 | + | ||
| 665 | + .docs-main { | ||
| 666 | + padding: 0 0 4rem 0; | ||
| 667 | + } | ||
| 668 | + } | ||
| 669 | + | ||
| 670 | + .docs-loading { | ||
| 671 | + display: flex; | ||
| 672 | + flex-direction: column; | ||
| 673 | + align-items: center; | ||
| 674 | + justify-content: center; | ||
| 675 | + padding: 4rem 2rem; | ||
| 676 | + gap: 1rem; | ||
| 677 | + color: var(--theme-texto); | ||
| 678 | + } | ||
| 679 | + | ||
| 680 | + .docs-loader { | ||
| 681 | + width: 32px; | ||
| 682 | + height: 32px; | ||
| 683 | + fill: var(--theme-texto); | ||
| 684 | + opacity: 0.6; | ||
| 685 | + } | ||
| 686 | + | ||
| 687 | + /* Markdown content styles */ | ||
| 688 | + .docs-content { | ||
| 689 | + color: var(--theme-titulo); | ||
| 690 | + line-height: 1.7; | ||
| 691 | + } | ||
| 692 | + | ||
| 693 | + .docs-content :global(h1) { | ||
| 694 | + font-family: var(--font-sans); | ||
| 695 | + font-size: 1.75rem; | ||
| 696 | + font-weight: 700; | ||
| 697 | + color: var(--theme-titulo); | ||
| 698 | + margin: 3rem 0 1.5rem; | ||
| 699 | + scroll-margin-top: 130px; | ||
| 700 | + } | ||
| 701 | + | ||
| 702 | + .docs-content :global(h1:first-child) { | ||
| 703 | + margin-top: 0; | ||
| 704 | + } | ||
| 705 | + | ||
| 706 | + .docs-content :global(h2) { | ||
| 707 | + font-family: var(--font-sans); | ||
| 708 | + font-size: 1.5rem; | ||
| 709 | + font-weight: 700; | ||
| 710 | + color: var(--theme-titulo); | ||
| 711 | + margin: 2.5rem 0 1rem; | ||
| 712 | + scroll-margin-top: 130px; | ||
| 713 | + } | ||
| 714 | + | ||
| 715 | + .docs-content :global(h3) { | ||
| 716 | + font-size: 1.25rem; | ||
| 717 | + font-weight: 600; | ||
| 718 | + color: var(--theme-titulo); | ||
| 719 | + margin: 2rem 0 0.75rem; | ||
| 720 | + scroll-margin-top: 130px; | ||
| 721 | + } | ||
| 722 | + | ||
| 723 | + .docs-content :global(h4) { | ||
| 724 | + font-size: 1rem; | ||
| 725 | + font-weight: 600; | ||
| 726 | + color: var(--theme-accent); | ||
| 727 | + margin: 1.5rem 0 0.5rem; | ||
| 728 | + font-family: monospace; | ||
| 729 | + scroll-margin-top: 130px; | ||
| 730 | + } | ||
| 731 | + | ||
| 732 | + .docs-content :global(p) { | ||
| 733 | + margin: 0 0 1rem; | ||
| 734 | + color: var(--theme-texto); | ||
| 735 | + } | ||
| 736 | + | ||
| 737 | + .docs-content :global(ul), | ||
| 738 | + .docs-content :global(ol) { | ||
| 739 | + margin: 0 0 1rem; | ||
| 740 | + padding-left: 1.5rem; | ||
| 741 | + color: var(--theme-texto); | ||
| 742 | + } | ||
| 743 | + | ||
| 744 | + .docs-content :global(li) { | ||
| 745 | + margin-bottom: 0.5rem; | ||
| 746 | + } | ||
| 747 | + | ||
| 748 | + .docs-content :global(strong) { | ||
| 749 | + font-weight: 600; | ||
| 750 | + color: var(--theme-titulo); | ||
| 751 | + } | ||
| 752 | + | ||
| 753 | + .docs-content :global(code) { | ||
| 754 | + font-family: 'SF Mono', 'Monaco', 'Consolas', monospace; | ||
| 755 | + font-size: 0.875em; | ||
| 756 | + background-color: var(--theme-fill); | ||
| 757 | + color: var(--theme-accent); | ||
| 758 | + padding: 0.2em 0.4em; | ||
| 759 | + border-radius: 4px; | ||
| 760 | + border: 1px solid var(--theme-borde); | ||
| 761 | + } | ||
| 762 | + | ||
| 763 | + .docs-content :global(pre) { | ||
| 764 | + background-color: var(--theme-fill); | ||
| 765 | + border: 1px solid var(--theme-borde); | ||
| 766 | + border-radius: 8px; | ||
| 767 | + padding: 1rem; | ||
| 768 | + margin: 1rem 0 1.5rem; | ||
| 769 | + overflow-x: auto; | ||
| 770 | + } | ||
| 771 | + | ||
| 772 | + .docs-content :global(pre code) { | ||
| 773 | + background: none; | ||
| 774 | + border: none; | ||
| 775 | + padding: 0; | ||
| 776 | + font-size: 0.875rem; | ||
| 777 | + color: var(--theme-titulo); | ||
| 778 | + } | ||
| 779 | + | ||
| 780 | + .docs-content :global(hr) { | ||
| 781 | + border: none; | ||
| 782 | + height: 1px; | ||
| 783 | + background: color-mix(in srgb, var(--theme-borde) 25%, transparent); | ||
| 784 | + margin: 2rem 0; | ||
| 785 | + opacity: 0.5; | ||
| 786 | + } | ||
| 787 | + | ||
| 788 | + .docs-content :global(blockquote) { | ||
| 789 | + border-left: 3px solid var(--theme-accent); | ||
| 790 | + margin: 1rem 0; | ||
| 791 | + padding: 0.5rem 1rem; | ||
| 792 | + background-color: var(--theme-fill); | ||
| 793 | + border-radius: 0 8px 8px 0; | ||
| 794 | + } | ||
| 795 | + | ||
| 796 | + .docs-content :global(blockquote p) { | ||
| 797 | + margin: 0; | ||
| 798 | + } | ||
| 799 | + | ||
| 800 | + /* Table of contents style for nested lists */ | ||
| 801 | + .docs-content :global(ul ul) { | ||
| 802 | + margin-top: 0.5rem; | ||
| 803 | + margin-bottom: 0.5rem; | ||
| 804 | + } | ||
| 805 | + | ||
| 806 | + /* Links */ | ||
| 807 | + .docs-content :global(a) { | ||
| 808 | + color: var(--theme-accent); | ||
| 809 | + text-decoration: underline; | ||
| 810 | + text-decoration-color: transparent; | ||
| 811 | + transition: text-decoration-color 0.15s ease; | ||
| 812 | + } | ||
| 813 | + | ||
| 814 | + .docs-content :global(a:hover) { | ||
| 815 | + text-decoration-color: var(--theme-accent); | ||
| 816 | + } | ||
| 817 | + | ||
| 818 | + /* Heading anchor links */ | ||
| 819 | + .docs-content :global(.heading-anchor) { | ||
| 820 | + position: relative; | ||
| 821 | + } | ||
| 822 | + | ||
| 823 | + .docs-content :global(.anchor-link) { | ||
| 824 | + color: inherit; | ||
| 825 | + text-decoration: none; | ||
| 826 | + } | ||
| 827 | + | ||
| 828 | + .docs-content :global(.anchor-link:hover) { | ||
| 829 | + text-decoration: none; | ||
| 830 | + } | ||
| 831 | + | ||
| 832 | + .docs-content :global(.heading-anchor:hover .anchor-link::before) { | ||
| 833 | + content: '#'; | ||
| 834 | + position: absolute; | ||
| 835 | + left: -1.25em; | ||
| 836 | + color: var(--theme-accent); | ||
| 837 | + opacity: 0.6; | ||
| 838 | + font-weight: 400; | ||
| 839 | + } | ||
| 840 | + | ||
| 841 | + .docs-content :global(h4.heading-anchor:hover .anchor-link::before) { | ||
| 842 | + left: -1em; | ||
| 843 | + font-size: 0.875em; | ||
| 844 | + } | ||
| 845 | + | ||
| 846 | + /* Responsive */ | ||
| 847 | + @media (max-width: 640px) { | ||
| 848 | + .docs-header-content { | ||
| 849 | + padding: 0.75rem 1rem; | ||
| 850 | + } | ||
| 851 | + | ||
| 852 | + .docs-title { | ||
| 853 | + font-size: 1.25rem; | ||
| 854 | + } | ||
| 855 | + | ||
| 856 | + .docs-content :global(h1) { | ||
| 857 | + font-size: 1.5rem; | ||
| 858 | + scroll-margin-top: 120px; | ||
| 859 | + } | ||
| 860 | + | ||
| 861 | + .docs-content :global(h2) { | ||
| 862 | + font-size: 1.25rem; | ||
| 863 | + scroll-margin-top: 120px; | ||
| 864 | + } | ||
| 865 | + | ||
| 866 | + .docs-content :global(h3) { | ||
| 867 | + font-size: 1.125rem; | ||
| 868 | + scroll-margin-top: 120px; | ||
| 869 | + } | ||
| 870 | + | ||
| 871 | + .docs-content :global(h4) { | ||
| 872 | + scroll-margin-top: 120px; | ||
| 873 | + } | ||
| 874 | + | ||
| 875 | + .docs-content :global(pre) { | ||
| 876 | + font-size: 0.8rem; | ||
| 877 | + padding: 0.75rem; | ||
| 878 | + } | ||
| 879 | + } | ||
| 880 | +</style> |
| 1 | <script> | 1 | <script> |
| 2 | import { onMount } from 'svelte'; | 2 | import { onMount } from 'svelte'; |
| 3 | - import { goto } from '$app/navigation'; | 3 | + import * as d3 from 'd3'; |
| 4 | - import { | 4 | + |
| 5 | - query, | 5 | + // Estado |
| 6 | - results, | 6 | + let streamData = $state([]); |
| 7 | - isLoading, | 7 | + let loading = $state(true); |
| 8 | - indexLoaded, | 8 | + let container = $state(null); |
| 9 | - error, | 9 | + let width = $state(0); |
| 10 | - selectedIndex, | 10 | + let height = $state(500); |
| 11 | - showResults, | 11 | + let tooltip = $state({ show: false, x: 0, y: 0, data: null }); |
| 12 | - initIndex, | 12 | + |
| 13 | - performSearch, | 13 | + // Drilldown |
| 14 | - clearSearch, | 14 | + let currentLevel = $state('tipo'); // tipo -> clase -> cuenta -> subcuenta |
| 15 | - navigateResults | 15 | + let breadcrumb = $state([{ level: 'tipo', codigo: null, descripcion: 'Todos los ingresos' }]); |
| 16 | - } from '$lib/stores/searchStore'; | 16 | + let currentParent = $state(null); |
| 17 | - | 17 | + |
| 18 | - let mounted = false; | 18 | + // Colores para las categorías |
| 19 | - let mode = 'archivo'; | 19 | + const colorScale = d3.scaleOrdinal() |
| 20 | - let searchFocused = false; | 20 | + .range([ |
| 21 | - let searchVal = ''; | 21 | + '#4A8B6E', '#C9A751', '#8B6E9B', '#6B9FD4', '#D4846B', |
| 22 | - let hDiario = false; | 22 | + '#5B8FA8', '#A8845B', '#7B9B6B', '#9B7B8B', '#6B8B9B', |
| 23 | - let hArchivo = false; | 23 | + '#B89B6B', '#6B7B9B', '#9B8B6B', '#7B8B7B', '#8B7B6B', |
| 24 | - let hVis = null; | 24 | + '#6B9B8B', '#9B6B7B', '#7B6B9B', '#8B9B6B', '#6B8B7B' |
| 25 | - let hHist = null; | 25 | + ]); |
| 26 | - let hWidget = null; | 26 | + |
| 27 | - let hGear = false; | 27 | + // Cargar datos |
| 28 | - let mobileSettingsOpen = false; | 28 | + async function loadData() { |
| 29 | - let searchInput; | 29 | + loading = true; |
| 30 | - let debounceTimer; | 30 | + try { |
| 31 | + const response = await fetch('/stream1.csv'); | ||
| 32 | + const text = await response.text(); | ||
| 33 | + const parsed = d3.csvParse(text, d => ({ | ||
| 34 | + entidad: +d.entidad, | ||
| 35 | + año: +d.año, | ||
| 36 | + nivel: d.nivel, | ||
| 37 | + codigo: +d.codigo, | ||
| 38 | + descripcion: d.descripcion, | ||
| 39 | + codigo_padre: d.codigo_padre ? +d.codigo_padre : null, | ||
| 40 | + desc_padre: d.desc_padre || null, | ||
| 41 | + devengado: +d.devengado | ||
| 42 | + })); | ||
| 43 | + | ||
| 44 | + // Filtrar solo entidad 0 (Todo el Estado) | ||
| 45 | + streamData = parsed.filter(d => d.entidad === 0); | ||
| 46 | + console.log('Stream data loaded:', streamData.length, 'rows'); | ||
| 47 | + } catch (err) { | ||
| 48 | + console.error('Error loading data:', err); | ||
| 49 | + } | ||
| 50 | + loading = false; | ||
| 51 | + } | ||
| 31 | 52 | ||
| 32 | - onMount(() => { | 53 | + // Obtener datos filtrados para el nivel actual |
| 33 | - setTimeout(() => { mounted = true; }, 80); | 54 | + function getFilteredData() { |
| 34 | - initIndex(); | 55 | + let filtered = streamData.filter(d => d.nivel === currentLevel); |
| 35 | - }); | ||
| 36 | 56 | ||
| 37 | - function toggleMobileSettings() { | 57 | + if (currentParent !== null) { |
| 38 | - mobileSettingsOpen = !mobileSettingsOpen; | 58 | + filtered = filtered.filter(d => d.codigo_padre === currentParent); |
| 39 | - } | 59 | + } |
| 40 | 60 | ||
| 41 | - $: isD = mode === 'diario'; | 61 | + return filtered; |
| 42 | - $: accent = isD ? '#4A8B6E' : '#C9A751'; | ||
| 43 | - $: placeholder = isD | ||
| 44 | - ? 'Busca una entidad, un ministerio, un municipio...' | ||
| 45 | - : 'Busca en 7 clasificadores: partidas, rubros, fuentes...'; | ||
| 46 | - $: examples = isD | ||
| 47 | - ? [ | ||
| 48 | - { label: 'MEFP', bg: 'rgba(140,170,150,0.10)', color: '#9ab8a8' }, | ||
| 49 | - { label: 'Min. Educación', bg: 'rgba(150,155,175,0.10)', color: '#a0a5b8' }, | ||
| 50 | - { label: 'GOB. La Paz', bg: 'rgba(180,130,100,0.10)', color: '#b8947a' }, | ||
| 51 | - ] | ||
| 52 | - : [ | ||
| 53 | - { label: 'Sueldos', bg: 'rgba(201,167,81,0.12)', color: '#d4b962' }, | ||
| 54 | - { label: 'MEFP', bg: 'rgba(140,170,150,0.10)', color: '#9ab8a8' }, | ||
| 55 | - { label: 'BOA', bg: 'rgba(150,155,175,0.10)', color: '#a0a5b8' }, | ||
| 56 | - { label: 'Consultorías', bg: 'rgba(180,130,100,0.10)', color: '#b8947a' }, | ||
| 57 | - { label: 'Regalías', bg: 'rgba(160,150,170,0.09)', color: '#aaa0b0' }, | ||
| 58 | - ]; | ||
| 59 | - | ||
| 60 | - function switchMode(m) { | ||
| 61 | - mode = m; | ||
| 62 | - searchVal = ''; | ||
| 63 | - clearSearch(); | ||
| 64 | - if (searchInput) searchInput.value = ''; | ||
| 65 | } | 62 | } |
| 66 | 63 | ||
| 67 | - function handleBlur() { | 64 | + // Preparar datos para el streamgraph |
| 68 | - setTimeout(() => { searchFocused = false; }, 200); | 65 | + function prepareStreamData() { |
| 69 | - } | 66 | + const filtered = getFilteredData(); |
| 67 | + if (!filtered.length) return { years: [], series: [], keys: [] }; | ||
| 68 | + | ||
| 69 | + // Agrupar por año | ||
| 70 | + const byYear = d3.group(filtered, d => d.año); | ||
| 71 | + const years = Array.from(byYear.keys()).sort((a, b) => a - b); | ||
| 72 | + | ||
| 73 | + // Obtener todas las categorías únicas | ||
| 74 | + const categories = [...new Set(filtered.map(d => d.codigo))]; | ||
| 75 | + | ||
| 76 | + // Crear estructura para stack | ||
| 77 | + const stackData = years.map(year => { | ||
| 78 | + const yearData = byYear.get(year) || []; | ||
| 79 | + const row = { año: year }; | ||
| 80 | + categories.forEach(cat => { | ||
| 81 | + const item = yearData.find(d => d.codigo === cat); | ||
| 82 | + row[cat] = item ? item.devengado : 0; | ||
| 83 | + }); | ||
| 84 | + return row; | ||
| 85 | + }); | ||
| 70 | 86 | ||
| 71 | - function handleInput(e) { | 87 | + // Crear el stack |
| 72 | - const value = e.target.value; | 88 | + const stack = d3.stack() |
| 73 | - searchVal = value; | 89 | + .keys(categories) |
| 74 | - clearTimeout(debounceTimer); | 90 | + .offset(d3.stackOffsetWiggle) |
| 75 | - debounceTimer = setTimeout(() => { | 91 | + .order(d3.stackOrderInsideOut); |
| 76 | - performSearch(value); | ||
| 77 | - }, 250); | ||
| 78 | - } | ||
| 79 | 92 | ||
| 80 | - function handleKeydown(e) { | 93 | + const series = stack(stackData); |
| 81 | - if (!$showResults) return; | 94 | + |
| 82 | - switch (e.key) { | 95 | + // Agregar descripciones a las series |
| 83 | - case 'ArrowDown': | 96 | + series.forEach(s => { |
| 84 | - e.preventDefault(); | 97 | + const item = filtered.find(d => d.codigo === +s.key); |
| 85 | - navigateResults('down', $results.length); | 98 | + s.descripcion = item ? item.descripcion : s.key; |
| 86 | - break; | 99 | + }); |
| 87 | - case 'ArrowUp': | 100 | + |
| 88 | - e.preventDefault(); | 101 | + return { years, series, keys: categories, stackData }; |
| 89 | - navigateResults('up', $results.length); | ||
| 90 | - break; | ||
| 91 | - case 'Enter': | ||
| 92 | - e.preventDefault(); | ||
| 93 | - if ($selectedIndex >= 0 && $results[$selectedIndex]) { | ||
| 94 | - handleSelect($results[$selectedIndex]); | ||
| 95 | - } | ||
| 96 | - break; | ||
| 97 | - case 'Escape': | ||
| 98 | - clearSearch(); | ||
| 99 | - searchVal = ''; | ||
| 100 | - if (searchInput) searchInput.value = ''; | ||
| 101 | - searchInput?.blur(); | ||
| 102 | - break; | ||
| 103 | - } | ||
| 104 | } | 102 | } |
| 105 | 103 | ||
| 106 | - function handleSelect(item) { | 104 | + // Formatear moneda |
| 107 | - if (item.tipo === 'entidad') { | 105 | + function formatMoney(value) { |
| 108 | - goto(`/entidad/${item.codigo}`); | 106 | + if (value >= 1e9) return `Bs ${(value / 1e9).toFixed(1)}B`; |
| 109 | - } else if (item.tipo === 'objeto_gasto') { | 107 | + if (value >= 1e6) return `Bs ${(value / 1e6).toFixed(1)}M`; |
| 110 | - goto(`/objeto/${item.codigo}`); | 108 | + if (value >= 1e3) return `Bs ${(value / 1e3).toFixed(0)}K`; |
| 111 | - } else if (item.tipo === 'rubro') { | 109 | + return `Bs ${value.toFixed(0)}`; |
| 112 | - goto(`/rubro/${item.codigo}`); | ||
| 113 | - } | ||
| 114 | - clearSearch(); | ||
| 115 | - searchVal = ''; | ||
| 116 | - if (searchInput) searchInput.value = ''; | ||
| 117 | } | 110 | } |
| 118 | 111 | ||
| 119 | - function getTypeLabel(tipo) { | 112 | + // Manejar clic para drilldown |
| 120 | - switch (tipo) { | 113 | + function handleClick(codigo, descripcion) { |
| 121 | - case 'entidad': return 'ENTIDAD'; | 114 | + const levels = ['tipo', 'clase', 'cuenta', 'subcuenta']; |
| 122 | - case 'objeto_gasto': return 'OBJETO'; | 115 | + const currentIndex = levels.indexOf(currentLevel); |
| 123 | - case 'rubro': return 'RUBRO'; | 116 | + |
| 124 | - default: return tipo?.toUpperCase() || ''; | 117 | + if (currentIndex < levels.length - 1) { |
| 118 | + const nextLevel = levels[currentIndex + 1]; | ||
| 119 | + | ||
| 120 | + // Verificar si hay datos en el siguiente nivel | ||
| 121 | + const hasChildren = streamData.some( | ||
| 122 | + d => d.nivel === nextLevel && d.codigo_padre === codigo | ||
| 123 | + ); | ||
| 124 | + | ||
| 125 | + if (hasChildren) { | ||
| 126 | + currentParent = codigo; | ||
| 127 | + currentLevel = nextLevel; | ||
| 128 | + breadcrumb = [...breadcrumb, { level: nextLevel, codigo, descripcion }]; | ||
| 129 | + } | ||
| 125 | } | 130 | } |
| 126 | } | 131 | } |
| 127 | 132 | ||
| 128 | - function getTypeColor(tipo) { | 133 | + // Navegar con breadcrumb |
| 129 | - switch (tipo) { | 134 | + function navigateTo(index) { |
| 130 | - case 'entidad': return '#4A8B6E'; | 135 | + if (index === 0) { |
| 131 | - case 'objeto_gasto': return '#C9A751'; | 136 | + currentLevel = 'tipo'; |
| 132 | - case 'rubro': return '#8B6E9B'; | 137 | + currentParent = null; |
| 133 | - default: return '#6B6860'; | 138 | + breadcrumb = [{ level: 'tipo', codigo: null, descripcion: 'Todos los ingresos' }]; |
| 139 | + } else { | ||
| 140 | + const target = breadcrumb[index]; | ||
| 141 | + currentLevel = target.level; | ||
| 142 | + currentParent = index > 0 ? breadcrumb[index - 1].codigo : null; | ||
| 143 | + | ||
| 144 | + // Si navegamos a un nivel intermedio, necesitamos el padre correcto | ||
| 145 | + if (index > 0) { | ||
| 146 | + currentParent = breadcrumb[index].codigo; | ||
| 147 | + // Buscar el nivel siguiente | ||
| 148 | + const levels = ['tipo', 'clase', 'cuenta', 'subcuenta']; | ||
| 149 | + const nextLevelIndex = levels.indexOf(target.level) + 1; | ||
| 150 | + if (nextLevelIndex < levels.length) { | ||
| 151 | + currentLevel = levels[nextLevelIndex]; | ||
| 152 | + } | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + breadcrumb = breadcrumb.slice(0, index + 1); | ||
| 134 | } | 156 | } |
| 135 | } | 157 | } |
| 136 | 158 | ||
| 137 | - const WIDGETS_INGRESOS = [ | 159 | + // Efecto para observar tamaño |
| 138 | - { id: 'rubros', label: 'tipos de ingreso', d: 'Impuestos, regalías, ventas, créditos', t: 'Recursos por rubros', n: '324', href: '/clasificadores/rubros' }, | 160 | + $effect(() => { |
| 139 | - { id: 'fuentes', label: 'fuentes de financiamiento', d: 'TGN, crédito externo, donaciones', t: 'Fuentes', n: '22', href: '/clasificadores/fuente' }, | 161 | + if (!container) return; |
| 140 | - { id: 'organismos', label: 'organismos financiadores', d: 'BID, Banco Mundial, cooperación', t: 'Organismos', n: '228', href: '/clasificadores/organismos' }, | 162 | + |
| 141 | - ]; | 163 | + const observer = new ResizeObserver(entries => { |
| 142 | - | 164 | + for (const entry of entries) { |
| 143 | - const WIDGETS_GASTOS_QUE = [ | 165 | + width = entry.contentRect.width; |
| 144 | - { id: 'objeto', label: 'objetos de gasto', d: 'Sueldos, consultorías, materiales', t: 'Objeto del gasto', n: '324', href: '/clasificadores/objeto-gasto' }, | ||
| 145 | - { id: 'sector', label: 'sectores económicos', d: 'Industria, construcción, servicios', t: 'Sectores', n: '488', href: '/clasificadores/sectores' }, | ||
| 146 | - ]; | ||
| 147 | - | ||
| 148 | - const WIDGETS_GASTOS_BOTTOM = [ | ||
| 149 | - { id: 'finalidad', label: 'finalidades y funciones', d: 'Educación, salud, defensa', t: 'Finalidad', n: '132', href: '/clasificadores/funcional' }, | ||
| 150 | - { id: 'donde', label: 'ubicaciones geográficas', d: 'Departamentos, municipios', t: 'Geográfico', n: '352', href: '/clasificadores/geografico' }, | ||
| 151 | - ]; | ||
| 152 | - | ||
| 153 | - const HISTORIAS = [ | ||
| 154 | - { color: '#C9A751', title: '¿Cuánto gasta Bolivia en consultorías cada año?', desc: 'Un análisis de 20 años revela patrones que todo ciudadano debería conocer. La evolución del gasto en servicios profesionales muestra tendencias inesperadas.', tag: 'INVESTIGACIÓN', tall: true }, | ||
| 155 | - { color: '#C44B3F', title: 'Salud vs. Defensa: 20 años', desc: 'Cómo se redistribuyó el presupuesto entre sectores.', tag: 'COMPARATIVA', tall: false }, | ||
| 156 | - { color: '#4A8B6E', title: 'El boom del gasto en infraestructura', desc: 'De 2006 a 2014 Bolivia multiplicó por 8 su inversión pública. Un recorrido por las grandes obras y su impacto fiscal.', tag: 'CRÓNICA', tall: true }, | ||
| 157 | - { color: '#6B8CA6', title: '¿Quién financia a Bolivia?', desc: 'Un mapa de los organismos internacionales que prestan al país.', tag: 'ANÁLISIS', tall: false }, | ||
| 158 | - ]; | ||
| 159 | - | ||
| 160 | - const VISUALIZACIONES = [ | ||
| 161 | - { color: '#4A8B6E', title: 'Evolución del gasto en salud', desc: 'Cómo cambiaron las prioridades en salud a lo largo de dos décadas.', type: 'Serie temporal' }, | ||
| 162 | - { color: '#C9A751', title: 'Treemap del presupuesto 2025', desc: 'Explora cada peso del presupuesto actual.', type: 'Treemap' }, | ||
| 163 | - { color: '#8B6E9B', title: 'Mapa de inversión por departamento', desc: 'Distribución geográfica de la inversión pública.', type: 'Mapa' }, | ||
| 164 | - ]; | ||
| 165 | - | ||
| 166 | - /* Reveal action */ | ||
| 167 | - let revealStates = {}; | ||
| 168 | - function reveal(node, { id, delay = 0 }) { | ||
| 169 | - revealStates[id] = false; | ||
| 170 | - revealStates = revealStates; | ||
| 171 | - const obs = new IntersectionObserver(([e]) => { | ||
| 172 | - if (e.isIntersecting) { | ||
| 173 | - setTimeout(() => { revealStates[id] = true; revealStates = revealStates; }, delay); | ||
| 174 | - obs.disconnect(); | ||
| 175 | } | 166 | } |
| 176 | - }, { threshold: 0.08 }); | 167 | + }); |
| 177 | - obs.observe(node); | ||
| 178 | - return { destroy() { obs.disconnect(); } }; | ||
| 179 | - } | ||
| 180 | 168 | ||
| 181 | - /* Section navigation */ | 169 | + observer.observe(container); |
| 182 | - const SECTIONS_NAV = [ | 170 | + return () => observer.disconnect(); |
| 183 | - { id: 'hero', label: 'Inicio' }, | 171 | + }); |
| 184 | - { id: 'datos', label: 'Datos' }, | ||
| 185 | - { id: 'clasificadores', label: 'Clasificadores' }, | ||
| 186 | - { id: 'historias', label: 'Historias' }, | ||
| 187 | - { id: 'visualizaciones', label: 'Visualizaciones' }, | ||
| 188 | - { id: 'manifiesto', label: 'Manifiesto' }, | ||
| 189 | - { id: 'descargas', label: 'Descargas' }, | ||
| 190 | - ]; | ||
| 191 | - let currentSection = 'hero'; | ||
| 192 | 172 | ||
| 193 | onMount(() => { | 173 | onMount(() => { |
| 194 | - const sections = document.querySelectorAll('[data-section]'); | 174 | + loadData(); |
| 195 | - const observer = new IntersectionObserver((entries) => { | ||
| 196 | - entries.forEach(entry => { | ||
| 197 | - if (entry.isIntersecting) { | ||
| 198 | - currentSection = entry.target.dataset.section; | ||
| 199 | - } | ||
| 200 | - }); | ||
| 201 | - }, { threshold: 0.5 }); | ||
| 202 | - sections.forEach(section => observer.observe(section)); | ||
| 203 | - return () => observer.disconnect(); | ||
| 204 | }); | 175 | }); |
| 205 | 176 | ||
| 206 | - function scrollToSection(id) { | 177 | + // Datos preparados reactivos |
| 207 | - const el = document.querySelector(`[data-section="${id}"]`); | 178 | + let prepared = $derived(prepareStreamData()); |
| 208 | - if (el) el.scrollIntoView({ behavior: 'smooth' }); | ||
| 209 | - } | ||
| 210 | -</script> | ||
| 211 | 179 | ||
| 212 | -<div class="page" class:mounted> | 180 | + // Escalas reactivas |
| 181 | + let xScale = $derived( | ||
| 182 | + d3.scaleLinear() | ||
| 183 | + .domain(d3.extent(prepared.years)) | ||
| 184 | + .range([60, width - 20]) | ||
| 185 | + ); | ||
| 213 | 186 | ||
| 214 | - <!-- Section Navigation Dots --> | 187 | + let yExtent = $derived(() => { |
| 215 | - <nav class="section-dots"> | 188 | + if (!prepared.series.length) return [0, 0]; |
| 216 | - {#each SECTIONS_NAV as s} | 189 | + const allValues = prepared.series.flatMap(s => s.flatMap(d => [d[0], d[1]])); |
| 190 | + return d3.extent(allValues); | ||
| 191 | + }); | ||
| 192 | + | ||
| 193 | + let yScale = $derived( | ||
| 194 | + d3.scaleLinear() | ||
| 195 | + .domain(yExtent()) | ||
| 196 | + .range([height - 40, 40]) | ||
| 197 | + ); | ||
| 198 | + | ||
| 199 | + // Generador de área | ||
| 200 | + let areaGenerator = $derived( | ||
| 201 | + d3.area() | ||
| 202 | + .x(d => xScale(d.data.año)) | ||
| 203 | + .y0(d => yScale(d[0])) | ||
| 204 | + .y1(d => yScale(d[1])) | ||
| 205 | + .curve(d3.curveBasis) | ||
| 206 | + ); | ||
| 207 | +</script> | ||
| 208 | + | ||
| 209 | +<svelte:head> | ||
| 210 | + <title>Streamgraph - Ingresos del Estado</title> | ||
| 211 | +</svelte:head> | ||
| 212 | + | ||
| 213 | +<div class="page"> | ||
| 214 | + <header> | ||
| 215 | + <h1>Ingresos del Estado Boliviano</h1> | ||
| 216 | + <p class="subtitle">Streamgraph interactivo con drilldown · 2005-2025</p> | ||
| 217 | + </header> | ||
| 218 | + | ||
| 219 | + <!-- Breadcrumb --> | ||
| 220 | + <nav class="breadcrumb"> | ||
| 221 | + {#each breadcrumb as crumb, i} | ||
| 222 | + {#if i > 0} | ||
| 223 | + <span class="separator">→</span> | ||
| 224 | + {/if} | ||
| 217 | <button | 225 | <button |
| 218 | - class="section-dot" | 226 | + class="crumb" |
| 219 | - class:section-dot-active={currentSection === s.id} | 227 | + class:active={i === breadcrumb.length - 1} |
| 220 | - on:click={() => scrollToSection(s.id)} | 228 | + onclick={() => navigateTo(i)} |
| 221 | - aria-label={s.label} | 229 | + disabled={i === breadcrumb.length - 1} |
| 222 | > | 230 | > |
| 223 | - <span class="section-dot-label">{s.label}</span> | 231 | + {crumb.descripcion} |
| 224 | </button> | 232 | </button> |
| 225 | {/each} | 233 | {/each} |
| 226 | </nav> | 234 | </nav> |
| 227 | 235 | ||
| 228 | - <!-- ═══ LANDING SCREEN (NAV + HERO) ═══ --> | 236 | + <!-- Nivel actual --> |
| 229 | - <section class="landing-screen" data-section="hero"> | 237 | + <div class="level-indicator"> |
| 230 | - <nav> | 238 | + Nivel: <strong>{currentLevel}</strong> |
| 231 | - <div class="nav-logo"><img src="/logos_oscuro/05_Imago MEFP horizontal espacio negativo.png" alt="MEFP" /></div> | 239 | + {#if currentLevel !== 'subcuenta'} |
| 232 | - </nav> | 240 | + <span class="hint">· Clic en una banda para ver detalle</span> |
| 233 | - | 241 | + {/if} |
| 234 | - <!-- ═══ HERO ═══ --> | 242 | + </div> |
| 235 | - <section class="hero"> | 243 | + |
| 236 | - <div class="dot-pattern"></div> | 244 | + <!-- Contenedor del gráfico --> |
| 237 | - <div class="hero-inner"> | 245 | + <div class="chart-container" bind:this={container}> |
| 238 | - <!-- Bloque 1: Identidad --> | 246 | + {#if loading} |
| 239 | - <div class="hero-identity"> | 247 | + <div class="loading">Cargando datos...</div> |
| 240 | - <h1 class="hero-title anim" style="--d:200ms">Presupuesto <span class="gold">Abierto</span></h1> | 248 | + {:else if width > 0 && prepared.series.length > 0} |
| 241 | - <p class="hero-sub anim" style="--d:340ms">Información diaria e histórica de todo el Estado Boliviano.</p> | 249 | + <svg {width} {height}> |
| 242 | - <div class="hero-stats anim" style="--d:420ms"> | 250 | + <!-- Eje X --> |
| 243 | - <span class="stat-num">21</span> años<span class="stat-dot">·</span><span class="stat-num">8.3M</span> registros<span class="stat-dot">·</span><span class="stat-num">647</span> instituciones<span class="stat-dot">·</span><span class="stat-num">7</span> clasificadores<span class="stat-dot">·</span><span class="stat-num">10</span> visualizaciones<span class="stat-dot">·</span><span class="stat-num">4</span> historias | 251 | + <g class="axis-x" transform="translate(0, {height - 35})"> |
| 244 | - </div> | 252 | + {#each prepared.years.filter((_, i) => i % 2 === 0) as year} |
| 245 | - </div> | 253 | + <text x={xScale(year)} y="20" text-anchor="middle" class="axis-label"> |
| 246 | - | 254 | + {year} |
| 247 | - <!-- Bloque 2: Búsqueda --> | 255 | + </text> |
| 248 | - <div class="hero-search anim" style="--d:520ms"> | 256 | + <line |
| 249 | - <div class="search-wrap"> | 257 | + x1={xScale(year)} |
| 250 | - <!-- Search bar unificado --> | 258 | + y1="-{height - 80}" |
| 251 | - <div class="search-bar" class:search-focused={searchFocused} class:search-has-results={searchFocused && ($results.length > 0 || ($query.length >= 2 && $results.length === 0) || $isLoading)} style="--accent:{accent}"> | 259 | + x2={xScale(year)} |
| 252 | - <!-- Toggle integrado (desktop) --> | 260 | + y2="0" |
| 253 | - <div class="toggle-bar toggle-desktop"> | 261 | + class="grid-line" |
| 254 | - <button class="toggle-btn" class:toggle-active={mode === 'archivo'} on:click={() => switchMode('archivo')}> | 262 | + /> |
| 255 | - <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke={mode === 'archivo' ? '#C9A751' : '#B8B5AD'} stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> | 263 | + {/each} |
| 256 | - <rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/> | 264 | + </g> |
| 257 | - </svg> | 265 | + |
| 258 | - 2005 a 2025 | 266 | + <!-- Streams --> |
| 259 | - </button> | 267 | + {#each prepared.series as serie, i} |
| 260 | - <button class="toggle-btn" class:toggle-active={mode === 'diario'} on:click={() => switchMode('diario')}> | 268 | + {@const color = colorScale(serie.key)} |
| 261 | - <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke={mode === 'diario' ? '#4A8B6E' : '#B8B5AD'} stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> | 269 | + <path |
| 262 | - <polyline points="3 12 7 12 9 6 12 18 15 9 17 12 21 12"/> | 270 | + d={areaGenerator(serie)} |
| 263 | - </svg> | 271 | + fill={color} |
| 264 | - Diario | 272 | + fill-opacity="0.85" |
| 265 | - </button> | 273 | + stroke={color} |
| 266 | - </div> | 274 | + stroke-width="0.5" |
| 267 | - <div class="search-divider"></div> | 275 | + class="stream-path" |
| 268 | - | 276 | + class:clickable={currentLevel !== 'subcuenta'} |
| 269 | - <!-- Mobile: mode indicator + settings button --> | 277 | + onmouseenter={(e) => { |
| 270 | - <button class="mobile-settings-btn" class:mobile-settings-open={mobileSettingsOpen} on:click={toggleMobileSettings}> | 278 | + tooltip = { |
| 271 | - <span class="mobile-mode-indicator" style="background:{accent}"></span> | 279 | + show: true, |
| 272 | - <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke={mobileSettingsOpen ? accent : '#B8B5AD'} stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> | 280 | + x: e.clientX, |
| 273 | - <circle cx="12" cy="12" r="3"/> | 281 | + y: e.clientY, |
| 274 | - <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/> | 282 | + data: { |
| 275 | - </svg> | 283 | + codigo: serie.key, |
| 276 | - </button> | 284 | + descripcion: serie.descripcion, |
| 277 | - | 285 | + color, |
| 278 | - <svg class="search-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke={searchFocused ? accent : '#B8B5AD'} stroke-width="2" stroke-linecap="round"> | 286 | + total: d3.sum(serie, d => d[1] - d[0]) |
| 279 | - <circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/> | 287 | + } |
| 280 | - </svg> | 288 | + }; |
| 281 | - <div class="search-input-wrap"> | 289 | + }} |
| 282 | - <input | 290 | + onmousemove={(e) => { |
| 283 | - type="text" | 291 | + tooltip.x = e.clientX; |
| 284 | - bind:this={searchInput} | 292 | + tooltip.y = e.clientY; |
| 285 | - bind:value={searchVal} | 293 | + }} |
| 286 | - on:input={handleInput} | 294 | + onmouseleave={() => tooltip.show = false} |
| 287 | - on:keydown={handleKeydown} | 295 | + onclick={() => handleClick(+serie.key, serie.descripcion)} |
| 288 | - on:focus={() => searchFocused = true} | 296 | + /> |
| 289 | - on:blur={handleBlur} | 297 | + {/each} |
| 290 | - disabled={!$indexLoaded} | ||
| 291 | - /> | ||
| 292 | - {#if !searchVal && !searchFocused} | ||
| 293 | - <div class="search-placeholder"> | ||
| 294 | - <span class="blink-cursor" style="background:{accent}"></span> | ||
| 295 | - <span class="placeholder-text placeholder-desktop">{$indexLoaded ? placeholder : 'Cargando...'}</span> | ||
| 296 | - <span class="placeholder-text placeholder-mobile">{$indexLoaded ? (isD ? 'MEFP, Min. Salud, GAM...' : 'BOA, Publicidad, IDH...') : 'Cargando...'}</span> | ||
| 297 | - </div> | ||
| 298 | - {/if} | ||
| 299 | - {#if searchVal && !$isLoading} | ||
| 300 | - <button class="search-clear" on:click={() => { clearSearch(); searchVal = ''; if (searchInput) searchInput.value = ''; }}> | ||
| 301 | - <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg> | ||
| 302 | - </button> | ||
| 303 | - {/if} | ||
| 304 | - </div> | ||
| 305 | - {#if !isD} | ||
| 306 | - <div class="gear-btn gear-desktop" class:gear-hover={hGear} on:mouseenter={() => hGear = true} on:mouseleave={() => hGear = false} role="button" tabindex="0"> | ||
| 307 | - <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#B8B5AD" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"> | ||
| 308 | - <circle cx="12" cy="12" r="3"/> | ||
| 309 | - <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/> | ||
| 310 | - </svg> | ||
| 311 | - </div> | ||
| 312 | - {/if} | ||
| 313 | - </div> | ||
| 314 | - | ||
| 315 | - <!-- Search Results Dropdown --> | ||
| 316 | - {#if $isLoading} | ||
| 317 | - <div class="search-results"> | ||
| 318 | - <div class="search-loading">Buscando...</div> | ||
| 319 | - </div> | ||
| 320 | - {:else if searchFocused && $results.length > 0} | ||
| 321 | - <div class="search-results"> | ||
| 322 | - {#each $results as item, i} | ||
| 323 | - <button | ||
| 324 | - type="button" | ||
| 325 | - class="search-result-item" | ||
| 326 | - class:result-selected={$selectedIndex === i} | ||
| 327 | - on:click={() => handleSelect(item)} | ||
| 328 | - > | ||
| 329 | - <div class="result-main"> | ||
| 330 | - <span class="result-name">{item.nombre}</span> | ||
| 331 | - {#if item.sigla} | ||
| 332 | - <span class="result-sigla">({item.sigla})</span> | ||
| 333 | - {/if} | ||
| 334 | - </div> | ||
| 335 | - <div class="result-meta"> | ||
| 336 | - {#if item.codigo} | ||
| 337 | - <span class="result-code">{item.codigo}</span> | ||
| 338 | - {/if} | ||
| 339 | - <span class="result-type" style="color:{getTypeColor(item.tipo)}">{getTypeLabel(item.tipo)}</span> | ||
| 340 | - </div> | ||
| 341 | - </button> | ||
| 342 | - {/each} | ||
| 343 | - </div> | ||
| 344 | - {:else if searchFocused && $query.length >= 2 && $results.length === 0} | ||
| 345 | - <div class="search-results"> | ||
| 346 | - <div class="search-no-results">Sin resultados para "{$query}"</div> | ||
| 347 | - </div> | ||
| 348 | - {/if} | ||
| 349 | - | ||
| 350 | - {#if $error} | ||
| 351 | - <div class="search-error">{$error}</div> | ||
| 352 | - {/if} | ||
| 353 | - | ||
| 354 | - <!-- Mobile settings panel --> | ||
| 355 | - <div class="mobile-settings-panel" class:mobile-settings-panel-open={mobileSettingsOpen}> | ||
| 356 | - <div class="mobile-toggle-bar"> | ||
| 357 | - <button class="mobile-toggle-btn" class:toggle-active={mode === 'archivo'} on:click={() => { switchMode('archivo'); mobileSettingsOpen = false; }}> | ||
| 358 | - <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={mode === 'archivo' ? '#C9A751' : '#B8B5AD'} stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> | ||
| 359 | - <rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/> | ||
| 360 | - </svg> | ||
| 361 | - <span>Histórico</span> | ||
| 362 | - <span class="mobile-toggle-sub">2005 — 2025</span> | ||
| 363 | - </button> | ||
| 364 | - <button class="mobile-toggle-btn" class:toggle-active={mode === 'diario'} on:click={() => { switchMode('diario'); mobileSettingsOpen = false; }}> | ||
| 365 | - <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={mode === 'diario' ? '#4A8B6E' : '#B8B5AD'} stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> | ||
| 366 | - <polyline points="3 12 7 12 9 6 12 18 15 9 17 12 21 12"/> | ||
| 367 | - </svg> | ||
| 368 | - <span>Diario</span> | ||
| 369 | - <span class="mobile-toggle-sub">En vivo</span> | ||
| 370 | - </button> | ||
| 371 | - </div> | ||
| 372 | - </div> | ||
| 373 | - | ||
| 374 | - <!-- Pills --> | ||
| 375 | - <div class="pills"> | ||
| 376 | - <span class="pills-label">Ej:</span> | ||
| 377 | - {#each examples as ex} | ||
| 378 | - <button class="pill" style="color:{ex.color};background:{ex.bg}" on:click={() => searchVal = ex.label}>{ex.label}</button> | ||
| 379 | - {/each} | ||
| 380 | - </div> | ||
| 381 | - </div> | ||
| 382 | - </div> | ||
| 383 | - </div> | ||
| 384 | - | ||
| 385 | - <!-- Scroll hint --> | ||
| 386 | - <div class="scroll-hint anim" style="--d:700ms" class:scroll-hint-hidden={searchFocused && $results.length > 0}> | ||
| 387 | - <svg width="18" height="28" viewBox="0 0 18 28" fill="none" stroke="#B8B5AD" stroke-width="1.2" stroke-linecap="round"> | ||
| 388 | - <rect x="1" y="1" width="16" height="26" rx="8"/><line x1="9" y1="7" x2="9" y2="12" opacity="0.7"/> | ||
| 389 | </svg> | 298 | </svg> |
| 390 | - <span>Explora el portal</span> | ||
| 391 | - </div> | ||
| 392 | - </section> | ||
| 393 | - </section> | ||
| 394 | - | ||
| 395 | - <!-- ═══ PORTAL DIRECTORY ═══ --> | ||
| 396 | - <section class="directory" data-section="datos"> | ||
| 397 | - <div class="directory-inner"> | ||
| 398 | - <!-- Left: Title --> | ||
| 399 | - <div class="directory-left"> | ||
| 400 | - <div use:reveal={{ id: 'dir-head' }} class="reveal" class:revealed={revealStates['dir-head']}> | ||
| 401 | - <p class="section-tag">LOS DATOS</p> | ||
| 402 | - <h2 class="section-title">Dos formas<br/>de ver el<br/>presupuesto.</h2> | ||
| 403 | - </div> | ||
| 404 | - </div> | ||
| 405 | - | ||
| 406 | - <!-- Right: Cards --> | ||
| 407 | - <div class="directory-right"> | ||
| 408 | - <!-- Diario --> | ||
| 409 | - <div use:reveal={{ id: 'door-d', delay: 60 }} class="reveal" class:revealed={revealStates['door-d']}> | ||
| 410 | - <div class="door door-warm" class:door-hovered={hDiario} on:mouseenter={() => hDiario = true} on:mouseleave={() => hDiario = false} role="button" tabindex="0"> | ||
| 411 | - <div class="door-content"> | ||
| 412 | - <div class="door-header"> | ||
| 413 | - <div class="door-icon" style="opacity:{hDiario ? 1 : 0.4}"> | ||
| 414 | - <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#4A8B6E" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 12 7 12 9 6 12 18 15 9 17 12 21 12"/></svg> | ||
| 415 | - </div> | ||
| 416 | - <div class="door-badge"> | ||
| 417 | - <div class="pulse-dot-sm"></div> | ||
| 418 | - <span class="door-badge-text" style="color:#4A8B6E">EN VIVO</span> | ||
| 419 | - </div> | ||
| 420 | - </div> | ||
| 421 | - <h3 class="door-title door-title-dark">Diariamente</h3> | ||
| 422 | - <div class="door-pills"> | ||
| 423 | - <span class="door-pill door-pill-green">¿Cuánto se ejecutó hoy?</span> | ||
| 424 | - <span class="door-pill door-pill-green">¿Quién gastó más?</span> | ||
| 425 | - <span class="door-pill door-pill-green">¿Qué cambió desde ayer?</span> | ||
| 426 | - <span class="door-pill door-pill-green">Panorama del presupuesto en curso</span> | ||
| 427 | - <span class="door-pill door-pill-green">Bitácora de movimientos</span> | ||
| 428 | - <span class="door-pill door-pill-green">Descarga de datos diaria</span> | ||
| 429 | - <span class="door-pill door-pill-green">Vista de todo el Estado</span> | ||
| 430 | - <span class="door-pill door-pill-green">Vista por Institución</span> | ||
| 431 | - </div> | ||
| 432 | - </div> | ||
| 433 | - <div class="door-footer"> | ||
| 434 | - <span class="door-meta">ACTUALIZADO HACE 2 HORAS</span> | ||
| 435 | - <span class="door-cta" style="color:{hDiario ? '#4A8B6E' : '#6B6860'}">Explorar →</span> | ||
| 436 | - </div> | ||
| 437 | - </div> | ||
| 438 | - </div> | ||
| 439 | - | ||
| 440 | - <!-- Archivo --> | ||
| 441 | - <div use:reveal={{ id: 'door-a', delay: 160 }} class="reveal" class:revealed={revealStates['door-a']}> | ||
| 442 | - <div class="door door-warm" class:door-hovered={hArchivo} on:mouseenter={() => hArchivo = true} on:mouseleave={() => hArchivo = false} role="button" tabindex="0"> | ||
| 443 | - <div class="door-content"> | ||
| 444 | - <div class="door-header"> | ||
| 445 | - <div class="door-icon" style="opacity:{hArchivo ? 1 : 0.4}"> | ||
| 446 | - <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#A68A3E" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="6" width="14" height="14" rx="2"/><path d="M7 6V4.5A1.5 1.5 0 0 1 8.5 3h7A1.5 1.5 0 0 1 17 4.5V6"/><line x1="8" y1="11" x2="14" y2="11"/><line x1="8" y1="14.5" x2="12" y2="14.5"/></svg> | ||
| 447 | - </div> | ||
| 448 | - <span class="door-badge-text" style="color:#A68A3E">2005 — 2025</span> | ||
| 449 | - </div> | ||
| 450 | - <h3 class="door-title door-title-dark">Históricamente</h3> | ||
| 451 | - <div class="door-pills"> | ||
| 452 | - <span class="door-pill door-pill-light">Tendencias</span> | ||
| 453 | - <span class="door-pill door-pill-light">Tipos de ingreso</span> | ||
| 454 | - <span class="door-pill door-pill-light">Tipos de gasto</span> | ||
| 455 | - <span class="door-pill door-pill-light">Por institución</span> | ||
| 456 | - <span class="door-pill door-pill-light">Por sector</span> | ||
| 457 | - <span class="door-pill door-pill-light">Por autonomías</span> | ||
| 458 | - <span class="door-pill door-pill-light">Comparar entidades</span> | ||
| 459 | - </div> | ||
| 460 | - </div> | ||
| 461 | - <div class="door-footer"> | ||
| 462 | - <span class="door-meta">8M REGISTROS · 7 CLASIFICADORES</span> | ||
| 463 | - <span class="door-cta" style="color:{hArchivo ? '#C9A751' : '#6B6860'}">Explorar →</span> | ||
| 464 | - </div> | ||
| 465 | - </div> | ||
| 466 | - </div> | ||
| 467 | - </div> | ||
| 468 | - </div> | ||
| 469 | - </section> | ||
| 470 | - | ||
| 471 | - <!-- ═══ CLASIFICADORES ═══ --> | ||
| 472 | - <section id="clasificadores" class="clasificadores" data-section="clasificadores"> | ||
| 473 | - <div class="clas-container"> | ||
| 474 | - <!-- Header --> | ||
| 475 | - <div use:reveal={{ id: 'clas-head' }} class="reveal clas-header-section" class:revealed={revealStates['clas-head']}> | ||
| 476 | - <div class="clas-header-row"> | ||
| 477 | - <div> | ||
| 478 | - <p class="section-tag">CLASIFICADORES</p> | ||
| 479 | - <h2 class="section-title">Elige cómo explorar el presupuesto</h2> | ||
| 480 | - <p class="clas-subtitle">Navega por cada una de las 2,626 piezas de los 8 clasificadores.</p> | ||
| 481 | - </div> | ||
| 482 | - <a href="/clasificadores" class="clas-ver-todos">Ver todos →</a> | ||
| 483 | - </div> | ||
| 484 | - </div> | ||
| 485 | 299 | ||
| 486 | - <!-- ¿Quién? --> | 300 | + <!-- Leyenda --> |
| 487 | - <div use:reveal={{ id: 'clas-quien', delay: 100 }} class="reveal" class:revealed={revealStates['clas-quien']}> | 301 | + <div class="legend"> |
| 488 | - <div class="clas-group-card"> | 302 | + {#each prepared.series as serie} |
| 489 | - <div class="clas-group-header"> | 303 | + {@const color = colorScale(serie.key)} |
| 490 | - <div class="clas-group-icon" style="--c:#1C1C1A"> | 304 | + <button |
| 491 | - <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> | 305 | + class="legend-item" |
| 492 | - </div> | 306 | + onclick={() => handleClick(+serie.key, serie.descripcion)} |
| 493 | - <div> | 307 | + > |
| 494 | - <div class="clas-group-label">¿QUIÉN?</div> | 308 | + <span class="legend-color" style="background: {color}"></span> |
| 495 | - <div class="clas-group-sub">La entidad que gasta o recibe</div> | 309 | + <span class="legend-label">{serie.descripcion}</span> |
| 496 | - </div> | 310 | + </button> |
| 497 | - </div> | ||
| 498 | - <a href="/clasificadores/institucional" class="widget-card widget-h" class:widget-hover={hWidget === 'quien'} on:mouseenter={() => hWidget = 'quien'} on:mouseleave={() => hWidget = null}> | ||
| 499 | - <div class="widget-left"> | ||
| 500 | - <div class="widget-headline"> | ||
| 501 | - <span class="widget-num-big">756</span> | ||
| 502 | - <span class="widget-label">instituciones públicas</span> | ||
| 503 | - </div> | ||
| 504 | - <div class="widget-desc">Ministerios, gobernaciones, municipios, empresas públicas, universidades</div> | ||
| 505 | - </div> | ||
| 506 | - <div class="widget-right"> | ||
| 507 | - <span class="widget-tech">INSTITUCIONAL</span> | ||
| 508 | - <span class="widget-arrow" class:arrow-show={hWidget === 'quien'}>→</span> | ||
| 509 | - </div> | ||
| 510 | - </a> | ||
| 511 | - </div> | ||
| 512 | - </div> | ||
| 513 | - | ||
| 514 | - <!-- Grid: Ingresos + Gastos --> | ||
| 515 | - <div class="clas-main-grid"> | ||
| 516 | - <!-- INGRESOS --> | ||
| 517 | - <div use:reveal={{ id: 'clas-ing', delay: 200 }} class="reveal clas-col" class:revealed={revealStates['clas-ing']}> | ||
| 518 | - <div class="clas-group-card"> | ||
| 519 | - <div class="clas-group-header"> | ||
| 520 | - <div class="clas-group-icon" style="--c:#C9A751"> | ||
| 521 | - <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> | ||
| 522 | - </div> | ||
| 523 | - <div> | ||
| 524 | - <div class="clas-group-label" style="color:#C9A751">INGRESOS</div> | ||
| 525 | - </div> | ||
| 526 | - </div> | ||
| 527 | - <div class="widgets-col"> | ||
| 528 | - {#each WIDGETS_INGRESOS as w} | ||
| 529 | - <a href={w.href} class="widget-card widget-grow" class:widget-hover={hWidget === w.id} on:mouseenter={() => hWidget = w.id} on:mouseleave={() => hWidget = null}> | ||
| 530 | - <div class="widget-headline"> | ||
| 531 | - <span class="widget-num-big widget-num-gold">{w.n}</span> | ||
| 532 | - <span class="widget-label">{w.label}</span> | ||
| 533 | - </div> | ||
| 534 | - <p class="widget-desc">{w.d}</p> | ||
| 535 | - <div class="widget-footer"> | ||
| 536 | - <span class="widget-tech">{w.t}</span> | ||
| 537 | - <span class="widget-arrow" class:arrow-show={hWidget === w.id}>→</span> | ||
| 538 | - </div> | ||
| 539 | - </a> | ||
| 540 | - {/each} | ||
| 541 | - </div> | ||
| 542 | - </div> | ||
| 543 | - </div> | ||
| 544 | - | ||
| 545 | - <!-- GASTOS --> | ||
| 546 | - <div use:reveal={{ id: 'clas-gas', delay: 300 }} class="reveal clas-col" class:revealed={revealStates['clas-gas']}> | ||
| 547 | - <div class="clas-group-card"> | ||
| 548 | - <div class="clas-group-header"> | ||
| 549 | - <div class="clas-group-icon" style="--c:#4A8B6E"> | ||
| 550 | - <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> | ||
| 551 | - </div> | ||
| 552 | - <div> | ||
| 553 | - <div class="clas-group-label" style="color:#4A8B6E">GASTOS</div> | ||
| 554 | - </div> | ||
| 555 | - </div> | ||
| 556 | - | ||
| 557 | - <!-- Gastos content wrapper --> | ||
| 558 | - <div class="gastos-content"> | ||
| 559 | - <!-- ¿En qué? sub-group --> | ||
| 560 | - <div class="clas-subgroup"> | ||
| 561 | - <div class="clas-subgroup-header"> | ||
| 562 | - <div class="clas-subgroup-icon" style="--c:#4A8B6E"> | ||
| 563 | - <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" 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"/></svg> | ||
| 564 | - </div> | ||
| 565 | - <div> | ||
| 566 | - <div class="clas-subgroup-label">¿EN QUÉ?</div> | ||
| 567 | - </div> | ||
| 568 | - </div> | ||
| 569 | - <div class="widgets-row"> | ||
| 570 | - {#each WIDGETS_GASTOS_QUE as w} | ||
| 571 | - <a href={w.href} class="widget-card widget-sm" class:widget-hover={hWidget === w.id} on:mouseenter={() => hWidget = w.id} on:mouseleave={() => hWidget = null}> | ||
| 572 | - <div class="widget-headline"> | ||
| 573 | - <span class="widget-num-big widget-num-green">{w.n}</span> | ||
| 574 | - <span class="widget-label">{w.label}</span> | ||
| 575 | - </div> | ||
| 576 | - <p class="widget-desc widget-desc-sm">{w.d}</p> | ||
| 577 | - <div class="widget-footer"> | ||
| 578 | - <span class="widget-tech">{w.t}</span> | ||
| 579 | - <span class="widget-arrow" class:arrow-show={hWidget === w.id}>→</span> | ||
| 580 | - </div> | ||
| 581 | - </a> | ||
| 582 | - {/each} | ||
| 583 | - </div> | ||
| 584 | - </div> | ||
| 585 | - | ||
| 586 | - <!-- Bottom: Para qué + Dónde --> | ||
| 587 | - <div class="widgets-row"> | ||
| 588 | - <!-- ¿Para qué? --> | ||
| 589 | - <div class="clas-subgroup"> | ||
| 590 | - <div class="clas-subgroup-header"> | ||
| 591 | - <div class="clas-subgroup-icon" style="--c:#4A8B6E"> | ||
| 592 | - <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="5"/><circle cx="12" cy="12" r="1.5" fill="currentColor"/></svg> | ||
| 593 | - </div> | ||
| 594 | - <div> | ||
| 595 | - <div class="clas-subgroup-label">¿PARA QUÉ?</div> | ||
| 596 | - </div> | ||
| 597 | - </div> | ||
| 598 | - <a href="/clasificadores/funcional" class="widget-card widget-full" class:widget-hover={hWidget === 'finalidad'} on:mouseenter={() => hWidget = 'finalidad'} on:mouseleave={() => hWidget = null}> | ||
| 599 | - <div class="widget-headline"> | ||
| 600 | - <span class="widget-num-big widget-num-green">132</span> | ||
| 601 | - <span class="widget-label">finalidades y funciones</span> | ||
| 602 | - </div> | ||
| 603 | - <p class="widget-desc">Educación, salud, defensa</p> | ||
| 604 | - <div class="widget-footer"> | ||
| 605 | - <span class="widget-tech">Finalidad</span> | ||
| 606 | - <span class="widget-arrow" class:arrow-show={hWidget === 'finalidad'}>→</span> | ||
| 607 | - </div> | ||
| 608 | - </a> | ||
| 609 | - </div> | ||
| 610 | - | ||
| 611 | - <!-- ¿Dónde? --> | ||
| 612 | - <div class="clas-subgroup"> | ||
| 613 | - <div class="clas-subgroup-header"> | ||
| 614 | - <div class="clas-subgroup-icon" style="--c:#4A8B6E"> | ||
| 615 | - <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z"/><circle cx="12" cy="9" r="2.5"/></svg> | ||
| 616 | - </div> | ||
| 617 | - <div> | ||
| 618 | - <div class="clas-subgroup-label">¿DÓNDE?</div> | ||
| 619 | - </div> | ||
| 620 | - </div> | ||
| 621 | - <a href="/clasificadores/geografico" class="widget-card widget-full" class:widget-hover={hWidget === 'donde'} on:mouseenter={() => hWidget = 'donde'} on:mouseleave={() => hWidget = null}> | ||
| 622 | - <div class="widget-headline"> | ||
| 623 | - <span class="widget-num-big widget-num-green">352</span> | ||
| 624 | - <span class="widget-label">ubicaciones geográficas</span> | ||
| 625 | - </div> | ||
| 626 | - <p class="widget-desc">Departamentos, municipios</p> | ||
| 627 | - <div class="widget-footer"> | ||
| 628 | - <span class="widget-tech">Geográfico</span> | ||
| 629 | - <span class="widget-arrow" class:arrow-show={hWidget === 'donde'}>→</span> | ||
| 630 | - </div> | ||
| 631 | - </a> | ||
| 632 | - </div> | ||
| 633 | - </div> | ||
| 634 | - </div> | ||
| 635 | - </div> | ||
| 636 | - </div> | ||
| 637 | - </div> | ||
| 638 | - </div> | ||
| 639 | - </section> | ||
| 640 | - | ||
| 641 | - <!-- ═══ HISTORIAS ═══ --> | ||
| 642 | - <section id="historias" class="historias" data-section="historias"> | ||
| 643 | - <div class="container"> | ||
| 644 | - <div use:reveal={{ id: 'hist-head' }} class="reveal" class:revealed={revealStates['hist-head']}> | ||
| 645 | - <div class="hist-header"> | ||
| 646 | - <div> | ||
| 647 | - <div class="hist-tag-row"> | ||
| 648 | - <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#6B6860" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg> | ||
| 649 | - <p class="section-tag">HISTORIAS</p> | ||
| 650 | - </div> | ||
| 651 | - <h2 class="section-title">Los datos, contados.</h2> | ||
| 652 | - <p class="hist-subtitle">Investigaciones y análisis narrativos sobre el presupuesto público.</p> | ||
| 653 | - </div> | ||
| 654 | - <a href="/historias" class="gold-link">Ver todas →</a> | ||
| 655 | - </div> | ||
| 656 | - </div> | ||
| 657 | - <div class="hist-grid"> | ||
| 658 | - {#each HISTORIAS as h, i} | ||
| 659 | - <div use:reveal={{ id: `hist-${i}`, delay: 60 + i * 80 }} class="reveal hist-cell" class:revealed={revealStates[`hist-${i}`]} class:hist-tall={h.tall}> | ||
| 660 | - <div class="hist-card" class:hist-card-hovered={hHist === i} on:mouseenter={() => hHist = i} on:mouseleave={() => hHist = null} role="button" tabindex="0" style="--card-color:{h.color}"> | ||
| 661 | - <div class="hist-accent" style="background:{h.color};transform:scaleX({hHist === i ? 1 : 0})"></div> | ||
| 662 | - <div class="hist-content"> | ||
| 663 | - <span class="hist-tag" style="color:{h.color}">{h.tag}</span> | ||
| 664 | - <h4 class="hist-title">{h.title}</h4> | ||
| 665 | - <p class="hist-desc">{h.desc}</p> | ||
| 666 | - </div> | ||
| 667 | - <div class="hist-cta" style="color:{hHist === i ? h.color : '#6B6860'}"> | ||
| 668 | - Leer historia <span class="hist-arrow" class:hist-arrow-moved={hHist === i}>→</span> | ||
| 669 | - </div> | ||
| 670 | - </div> | ||
| 671 | - </div> | ||
| 672 | {/each} | 311 | {/each} |
| 673 | </div> | 312 | </div> |
| 674 | - </div> | 313 | + {:else} |
| 675 | - </section> | 314 | + <div class="no-data">No hay datos para este nivel</div> |
| 676 | - | 315 | + {/if} |
| 677 | - <!-- ═══ VISUALIZACIONES ═══ --> | 316 | + </div> |
| 678 | - <section id="visualizaciones" class="visualizaciones" data-section="visualizaciones"> | 317 | + |
| 679 | - <div class="container"> | 318 | + <!-- Tooltip --> |
| 680 | - <div use:reveal={{ id: 'vis-head' }} class="reveal" class:revealed={revealStates['vis-head']}> | 319 | + {#if tooltip.show && tooltip.data} |
| 681 | - <div class="vis-header"> | 320 | + <div |
| 682 | - <div> | 321 | + class="tooltip" |
| 683 | - <div class="vis-tag-row"> | 322 | + style="left: {tooltip.x + 15}px; top: {tooltip.y - 10}px;" |
| 684 | - <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#6B6860" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg> | 323 | + > |
| 685 | - <p class="section-tag">VISUALIZACIONES</p> | 324 | + <div class="tooltip-header" style="border-color: {tooltip.data.color}"> |
| 686 | - </div> | 325 | + <span class="tooltip-code">{tooltip.data.codigo}</span> |
| 687 | - <h2 class="section-title">Explora los datos.</h2> | 326 | + <span class="tooltip-desc">{tooltip.data.descripcion}</span> |
| 688 | - <p class="vis-subtitle">Herramientas interactivas para navegar el presupuesto.</p> | ||
| 689 | - </div> | ||
| 690 | - <a href="/visualizaciones" class="gold-link">Ver todas →</a> | ||
| 691 | - </div> | ||
| 692 | </div> | 327 | </div> |
| 693 | - <div class="vis-grid-simple"> | 328 | + <div class="tooltip-value"> |
| 694 | - {#each VISUALIZACIONES as v, i} | 329 | + Total acumulado: {formatMoney(tooltip.data.total)} |
| 695 | - <div use:reveal={{ id: `vis-${i}`, delay: 60 + i * 80 }} class="reveal" class:revealed={revealStates[`vis-${i}`]}> | ||
| 696 | - <div class="vis-card" class:vis-card-hovered={hVis === i} on:mouseenter={() => hVis = i} on:mouseleave={() => hVis = null} role="button" tabindex="0"> | ||
| 697 | - <div class="vis-accent" style="background:{v.color};transform:scaleX({hVis === i ? 1 : 0})"></div> | ||
| 698 | - <div> | ||
| 699 | - <span class="vis-type" style="background:color-mix(in srgb, {v.color} 12%, transparent);color:{v.color}">{v.type}</span> | ||
| 700 | - <h4 class="vis-title">{v.title}</h4> | ||
| 701 | - <p class="vis-desc">{v.desc}</p> | ||
| 702 | - </div> | ||
| 703 | - <div class="vis-cta" style="color:{hVis === i ? v.color : '#6B6860'}"> | ||
| 704 | - Explorar <span class="vis-arrow" class:vis-arrow-moved={hVis === i}>→</span> | ||
| 705 | - </div> | ||
| 706 | - </div> | ||
| 707 | - </div> | ||
| 708 | - {/each} | ||
| 709 | </div> | 330 | </div> |
| 331 | + {#if currentLevel !== 'subcuenta'} | ||
| 332 | + <div class="tooltip-hint">Clic para ver detalle</div> | ||
| 333 | + {/if} | ||
| 710 | </div> | 334 | </div> |
| 711 | - </section> | 335 | + {/if} |
| 712 | - | ||
| 713 | - <!-- ═══ MANIFIESTO ═══ --> | ||
| 714 | - <section class="manifiesto" data-section="manifiesto"> | ||
| 715 | - <div class="dot-pattern"></div> | ||
| 716 | - <div class="manifiesto-inner"> | ||
| 717 | - <div use:reveal={{ id: 'man' }} class="reveal" class:revealed={revealStates['man']}> | ||
| 718 | - <p class="section-tag-light">POR QUÉ EXISTE ESTO</p> | ||
| 719 | - <h2 class="manifiesto-title">El presupuesto público no es de los gobiernos.<br/><span class="gold">Es de todos.</span></h2> | ||
| 720 | - </div> | ||
| 721 | - </div> | ||
| 722 | - </section> | ||
| 723 | - | ||
| 724 | - <!-- ═══ DESCARGAS ═══ --> | ||
| 725 | - <section id="descargas" class="descargas" data-section="descargas"> | ||
| 726 | - <div class="descargas-inner"> | ||
| 727 | - <div use:reveal={{ id: 'desc' }} class="reveal" class:revealed={revealStates['desc']}> | ||
| 728 | - <h2 class="descargas-title">Descarga los datos.</h2> | ||
| 729 | - <div class="descargas-formats"> | ||
| 730 | - <span class="format-pill">.csv</span> | ||
| 731 | - <span class="format-pill">.parquet</span> | ||
| 732 | - <span class="format-pill">.json</span> | ||
| 733 | - </div> | ||
| 734 | - <a href="/descargas" class="descargas-link"> | ||
| 735 | - <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 4v12"/><polyline points="8 12 12 16 16 12"/><path d="M4 18h16"/></svg> | ||
| 736 | - Ir a página de descargas | ||
| 737 | - </a> | ||
| 738 | - </div> | ||
| 739 | - </div> | ||
| 740 | - </section> | ||
| 741 | - | ||
| 742 | - <!-- ═══ FOOTER ═══ --> | ||
| 743 | - <footer> | ||
| 744 | - <div class="footer-inner"> | ||
| 745 | - <div class="footer-org">MINISTERIO DE ECONOMÍA Y FINANZAS PÚBLICAS</div> | ||
| 746 | - <div class="footer-right">DATOS ABIERTOS · 2005 — 2026</div> | ||
| 747 | - </div> | ||
| 748 | - </footer> | ||
| 749 | </div> | 336 | </div> |
| 750 | 337 | ||
| 751 | <style> | 338 | <style> |
| 752 | - @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500&display=swap'); | 339 | + .page { |
| 753 | - :global(*){margin:0;padding:0;box-sizing:border-box} | 340 | + min-height: 100vh; |
| 754 | - :global(html){width:100%;height:100%;margin:0;padding:0;overflow-x:hidden;background:#1C1C1A;scroll-behavior:smooth} | 341 | + background: #1a1a1a; |
| 755 | - :global(body){width:100%;min-height:100%;margin:0;padding:0;overflow-x:hidden;background:#1C1C1A} | 342 | + color: #f5f5f5; |
| 756 | - :global(::selection){background:#C9A751;color:#1C1C1A} | 343 | + padding: 2rem; |
| 757 | - :global(::-webkit-scrollbar){width:5px} | 344 | + font-family: system-ui, -apple-system, sans-serif; |
| 758 | - :global(::-webkit-scrollbar-track){background:#1C1C1A} | 345 | + } |
| 759 | - :global(::-webkit-scrollbar-thumb){background:#444;border-radius:3px} | 346 | + |
| 760 | - :global(a){color:inherit;text-decoration:none} | 347 | + header { |
| 761 | - | 348 | + text-align: center; |
| 762 | - @keyframes pulse{0%,100%{opacity:0.35}50%{opacity:1}} | 349 | + margin-bottom: 2rem; |
| 763 | - @keyframes blink{0%,100%{opacity:1}50%{opacity:0}} | 350 | + } |
| 764 | - @keyframes scrollHint{0%,100%{transform:translateY(0);opacity:0.6}50%{transform:translateY(5px);opacity:1}} | 351 | + |
| 765 | - @keyframes heartbeat{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:48}} | 352 | + h1 { |
| 766 | - | 353 | + font-size: 2rem; |
| 767 | - .page{--serif:'Qanelas',system-ui,sans-serif;--sans:'Qanelas',system-ui,sans-serif;--mono:'JetBrains Mono',monospace;font-family:var(--serif);color:#1C1C1A;width:100%;min-width:100%;overflow-x:hidden;scroll-snap-type:y mandatory;overflow-y:scroll;height:100vh} | 354 | + font-weight: 700; |
| 768 | - | 355 | + margin: 0; |
| 769 | - /* Section Navigation Dots */ | 356 | + color: #f5f5f5; |
| 770 | - .section-dots{position:fixed;right:24px;top:50%;transform:translateY(-50%);z-index:100;display:flex;flex-direction:column;gap:12px;background:rgba(255,255,255,0.1);backdrop-filter:blur(8px);padding:12px 8px;border-radius:20px} | 357 | + } |
| 771 | - .section-dot{width:10px;height:10px;border-radius:50%;border:1.5px solid rgba(100,100,100,0.4);background:rgba(150,150,150,0.2);cursor:pointer;transition:all 0.3s;padding:0;position:relative} | 358 | + |
| 772 | - .section-dot:hover{border-color:rgba(100,100,100,0.6);background:rgba(150,150,150,0.4);transform:scale(1.2)} | 359 | + .subtitle { |
| 773 | - .section-dot-active{background:#C9A751;border-color:#C9A751} | 360 | + color: #888; |
| 774 | - .section-dot-active:hover{transform:scale(1.2)} | 361 | + margin-top: 0.5rem; |
| 775 | - .section-dot-label{position:absolute;right:20px;top:50%;transform:translateY(-50%);font-family:var(--mono);font-size:10px;letter-spacing:0.05em;color:#6B6860;white-space:nowrap;opacity:0;transition:opacity 0.2s;pointer-events:none} | 362 | + } |
| 776 | - .section-dot:hover .section-dot-label{opacity:1} | 363 | + |
| 777 | - .gold{color:#C9A751} | 364 | + .breadcrumb { |
| 778 | - .container{max-width:1060px;margin:0 auto} | 365 | + display: flex; |
| 779 | - | 366 | + align-items: center; |
| 780 | - /* Reveal */ | 367 | + gap: 0.5rem; |
| 781 | - .reveal{opacity:0;transform:translateY(28px);transition:all 0.8s cubic-bezier(0.16,1,0.3,1)} | 368 | + margin-bottom: 1rem; |
| 782 | - .revealed{opacity:1!important;transform:translateY(0)!important} | 369 | + flex-wrap: wrap; |
| 783 | - .anim{opacity:0;transform:translateY(18px);transition:all 0.8s cubic-bezier(0.16,1,0.3,1);transition-delay:var(--d,0ms)} | 370 | + } |
| 784 | - .mounted .anim{opacity:1;transform:translateY(0)} | 371 | + |
| 785 | - | 372 | + .separator { |
| 786 | - /* ── NAV ── */ | 373 | + color: #555; |
| 787 | - nav{background:#1C1C1A;padding:12px 48px;display:flex;align-items:center;justify-content:space-between;flex-shrink:0} | 374 | + } |
| 788 | - .nav-left{display:flex;align-items:center} | 375 | + |
| 789 | - .nav-right{display:flex;align-items:center;gap:24px} | 376 | + .crumb { |
| 790 | - .nav-link{font-family:var(--sans);font-size:14px;color:#B8B5AD;letter-spacing:0.01em;font-weight:400;transition:color 0.2s} | 377 | + background: #2a2a2a; |
| 791 | - .nav-link:hover{color:#F5F0E8} | 378 | + border: 1px solid #333; |
| 792 | - .nav-link-icon{display:flex;align-items:center;gap:5px} | 379 | + color: #aaa; |
| 793 | - .heartbeat-line{animation:heartbeat 3s ease-in-out infinite} | 380 | + padding: 0.4rem 0.8rem; |
| 794 | - .nav-date{display:flex;align-items:center;gap:6px;font-family:var(--mono);font-size:9px;letter-spacing:0.06em;color:#B8B5AD;font-weight:400} | 381 | + border-radius: 6px; |
| 795 | - .date-label{color:#9B9890} | 382 | + cursor: pointer; |
| 796 | - .date-sep{margin:0 3px;opacity:0.3} | 383 | + font-size: 0.85rem; |
| 797 | - .pulse-dot{width:5px;height:5px;border-radius:50%;background:#4A8B6E;animation:pulse 2s ease-in-out infinite} | 384 | + transition: all 0.2s; |
| 798 | - .pulse-dot-sm{width:6px;height:6px;border-radius:50%;background:#4A8B6E;animation:pulse 2s ease-in-out infinite;box-shadow:0 0 8px rgba(74,139,110,0.37)} | 385 | + } |
| 799 | - .nav-sep{width:1px;height:16px;background:#2A2A27} | 386 | + |
| 800 | - .nav-logo{height:88px;display:flex;align-items:center;flex-shrink:0} | 387 | + .crumb:hover:not(:disabled) { |
| 801 | - .nav-logo img{height:100%;width:auto;object-fit:contain} | 388 | + background: #333; |
| 802 | - | 389 | + color: #fff; |
| 803 | - /* ── HERO ── */ | 390 | + } |
| 804 | - .landing-screen{height:100vh;background:#1C1C1A;display:flex;flex-direction:column;scroll-snap-align:start;scroll-snap-stop:always;overflow:hidden} | 391 | + |
| 805 | - .hero{background:#1C1C1A;color:#F5F0E8;flex:1;display:flex;flex-direction:column;justify-content:center;padding:40px 48px 32px;position:relative;overflow:hidden} | 392 | + .crumb.active { |
| 806 | - .dot-pattern{position:absolute;inset:0;opacity:0.02;background-image:radial-gradient(circle at 1px 1px,#B8B5AD 0.5px,transparent 0);background-size:40px 40px} | 393 | + background: #C9A751; |
| 807 | - .dot-pattern-sm{position:absolute;inset:0;opacity:0.015;background-image:radial-gradient(circle at 1px 1px,#fff 0.5px,transparent 0);background-size:32px 32px} | 394 | + color: #1a1a1a; |
| 808 | - .hero-inner{max-width:1060px;margin:0 auto;width:100%;position:relative;margin-top:-80px;text-align:center} | 395 | + font-weight: 600; |
| 809 | - .hero-identity{margin-bottom:20px} | 396 | + cursor: default; |
| 810 | - .hero-search{display:flex;justify-content:center;position:relative;z-index:50} | 397 | + } |
| 811 | - .hero-tag{font-family:var(--mono);font-size:12px;letter-spacing:0.25em;color:#B8B5AD} | 398 | + |
| 812 | - .hero-title{font-size:clamp(44px,5.5vw,72px);font-weight:900;line-height:1.02;letter-spacing:-0.035em;max-width:720px;margin:14px auto 0} | 399 | + .crumb:disabled { |
| 813 | - .hero-sub{font-family:var(--sans);font-size:clamp(16px,1.6vw,18px);color:#9B9890;max-width:100%;margin-top:14px;line-height:1.6} | 400 | + cursor: default; |
| 814 | - .hero-stats{font-family:var(--mono);font-size:13px;letter-spacing:0.04em;color:#B8B5AD;margin-top:16px;line-height:1} | 401 | + } |
| 815 | - .stat-num{color:#F5F0E8} | 402 | + |
| 816 | - .stat-dot{margin:0 10px;opacity:0.3} | 403 | + .level-indicator { |
| 817 | - | 404 | + color: #666; |
| 818 | - /* Search */ | 405 | + font-size: 0.85rem; |
| 819 | - .search-wrap{max-width:860px;width:100%;margin:0 auto;position:relative;z-index:9999} | 406 | + margin-bottom: 1rem; |
| 820 | - .search-bar{display:flex;align-items:center;background:rgba(255,255,255,0.04);border-radius:16px;padding:8px 14px 8px 8px;transition:all 0.4s cubic-bezier(0.16,1,0.3,1);box-shadow:0 0 0 1px rgba(201,167,81,0.12),0 8px 32px rgba(0,0,0,0.25);position:relative} | 407 | + } |
| 821 | - .search-focused{background:rgba(255,255,255,0.07);box-shadow:0 0 0 1.5px rgba(201,167,81,0.35),0 8px 40px rgba(0,0,0,0.3)} | 408 | + |
| 822 | - .search-has-results{border-radius:16px 16px 0 0} | 409 | + .level-indicator strong { |
| 823 | - .toggle-bar{display:flex;border-radius:10px;padding:2px;background:rgba(0,0,0,0.2);flex-shrink:0} | 410 | + color: #C9A751; |
| 824 | - .toggle-btn{background:transparent;border:none;border-radius:8px;padding:12px 16px;cursor:pointer;display:flex;align-items:center;gap:7px;transition:all 0.3s;font-family:var(--sans);font-size:14px;font-weight:400;color:#9B9890} | 411 | + text-transform: uppercase; |
| 825 | - .toggle-btn svg{display:block;flex-shrink:0} | 412 | + } |
| 826 | - .toggle-btn:hover{color:#B8B5AD} | 413 | + |
| 827 | - .toggle-active{background:rgba(255,255,255,0.1);font-weight:600;color:#F5F0E8} | 414 | + .hint { |
| 828 | - .search-divider{width:1px;height:32px;background:rgba(255,255,255,0.08);margin:0 16px;flex-shrink:0} | 415 | + color: #555; |
| 829 | - .search-icon{flex-shrink:0;transition:stroke 0.3s} | 416 | + } |
| 830 | - .search-input-wrap{flex:1;position:relative;display:flex;align-items:center} | 417 | + |
| 831 | - .search-input-wrap input{width:100%;background:transparent;border:none;outline:none;color:#F5F0E8;font-size:16px;font-family:var(--sans);padding:12px 12px 12px 10px;caret-color:transparent} | 418 | + .chart-container { |
| 832 | - .search-placeholder{position:absolute;left:10px;top:50%;transform:translateY(-50%);display:flex;align-items:center;pointer-events:none} | 419 | + background: #222; |
| 833 | - .blink-cursor{width:1.5px;height:18px;margin-right:1px;animation:blink 1.1s step-end infinite;border-radius:1px;flex-shrink:0} | 420 | + border-radius: 12px; |
| 834 | - .placeholder-text{font-family:var(--sans);font-size:16px;color:#B8B5AD} | 421 | + padding: 1rem; |
| 835 | - .placeholder-mobile{display:none} | 422 | + min-height: 500px; |
| 836 | - .gear-btn{width:38px;height:38px;border-radius:10px;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,0.06);cursor:pointer;transition:all 0.3s;flex-shrink:0;margin-left:8px} | 423 | + position: relative; |
| 837 | - .gear-hover{background:rgba(255,255,255,0.12)} | 424 | + } |
| 838 | - | 425 | + |
| 839 | - /* Mobile settings button (hidden on desktop) */ | 426 | + .loading, .no-data { |
| 840 | - .mobile-settings-btn{display:none;width:40px;height:40px;border-radius:10px;background:rgba(255,255,255,0.06);border:none;cursor:pointer;align-items:center;justify-content:center;position:relative;transition:all 0.3s;flex-shrink:0} | 427 | + display: flex; |
| 841 | - .mobile-settings-btn:hover,.mobile-settings-open{background:rgba(255,255,255,0.12)} | 428 | + align-items: center; |
| 842 | - .mobile-mode-indicator{position:absolute;top:6px;right:6px;width:6px;height:6px;border-radius:50%} | 429 | + justify-content: center; |
| 843 | - | 430 | + height: 400px; |
| 844 | - /* Mobile settings panel (hidden on desktop) */ | 431 | + color: #666; |
| 845 | - .mobile-settings-panel{display:none;overflow:hidden;max-height:0;opacity:0;transition:all 0.3s cubic-bezier(0.16,1,0.3,1)} | 432 | + } |
| 846 | - .mobile-settings-panel-open{max-height:200px;opacity:1;margin-top:12px} | 433 | + |
| 847 | - .mobile-toggle-bar{display:flex;gap:10px} | 434 | + svg { |
| 848 | - .mobile-toggle-btn{flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;padding:16px 12px;border-radius:12px;background:rgba(0,0,0,0.2);border:1px solid rgba(255,255,255,0.06);cursor:pointer;transition:all 0.3s} | 435 | + display: block; |
| 849 | - .mobile-toggle-btn.toggle-active{background:rgba(255,255,255,0.1);border-color:rgba(255,255,255,0.15)} | 436 | + } |
| 850 | - .mobile-toggle-btn span{font-family:var(--sans);font-size:14px;font-weight:600;color:#F5F0E8} | 437 | + |
| 851 | - .mobile-toggle-btn svg{margin-bottom:4px} | 438 | + .axis-label { |
| 852 | - .mobile-toggle-sub{font-size:11px!important;font-weight:400!important;color:#9B9890!important;font-family:var(--mono)!important;letter-spacing:0.05em} | 439 | + fill: #666; |
| 853 | - | 440 | + font-size: 12px; |
| 854 | - /* Search clear button */ | 441 | + } |
| 855 | - .search-clear{background:none;border:none;cursor:pointer;color:#6B6860;padding:4px;display:flex;align-items:center;justify-content:center;transition:color 0.2s;flex-shrink:0} | 442 | + |
| 856 | - .search-clear:hover{color:#F5F0E8} | 443 | + .grid-line { |
| 857 | - | 444 | + stroke: #333; |
| 858 | - /* Search Results Dropdown */ | 445 | + stroke-width: 0.5; |
| 859 | - .search-results{position:absolute;top:100%;left:0;right:0;background:rgba(28,28,26,0.98);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid rgba(255,255,255,0.1);border-top:none;border-radius:0 0 16px 16px;max-height:320px;overflow-y:auto;z-index:9999} | 446 | + stroke-dasharray: 2, 4; |
| 860 | - .search-loading,.search-no-results{padding:16px 20px;font-family:var(--sans);font-size:14px;color:#9B9890;text-align:center} | 447 | + } |
| 861 | - .search-error{margin-top:8px;font-family:var(--sans);font-size:13px;color:#C44B3F;text-align:center} | 448 | + |
| 862 | - | 449 | + .stream-path { |
| 863 | - .search-result-item{width:100%;text-align:left;background:none;border:none;border-top:1px solid rgba(255,255,255,0.06);padding:14px 20px;display:flex;justify-content:space-between;align-items:center;cursor:pointer;transition:background 0.2s} | 450 | + transition: fill-opacity 0.2s, transform 0.2s; |
| 864 | - .search-result-item:first-child{border-top:none} | 451 | + } |
| 865 | - .search-result-item:hover,.result-selected{background:rgba(255,255,255,0.06)} | 452 | + |
| 866 | - | 453 | + .stream-path:hover { |
| 867 | - .result-main{display:flex;align-items:center;gap:8px;flex:1;min-width:0} | 454 | + fill-opacity: 1; |
| 868 | - .result-name{font-family:var(--sans);font-size:14px;color:#F5F0E8;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} | 455 | + } |
| 869 | - .result-sigla{font-family:var(--sans);font-size:12px;color:#9B9890;flex-shrink:0} | 456 | + |
| 870 | - | 457 | + .stream-path.clickable { |
| 871 | - .result-meta{display:flex;align-items:center;gap:8px;flex-shrink:0;margin-left:12px} | 458 | + cursor: pointer; |
| 872 | - .result-code{font-family:var(--mono);font-size:10px;color:#9B9890;background:rgba(255,255,255,0.06);padding:2px 6px;border-radius:4px} | 459 | + } |
| 873 | - .result-type{font-family:var(--mono);font-size:9px;letter-spacing:0.1em;font-weight:600} | 460 | + |
| 874 | - .pills{display:flex;gap:8px;margin-top:16px;flex-wrap:wrap;align-items:center;justify-content:center} | 461 | + .stream-path.clickable:hover { |
| 875 | - .pills-label{font-family:var(--mono);font-size:11px;letter-spacing:0.1em;color:#B8B5AD;margin-right:2px} | 462 | + filter: brightness(1.1); |
| 876 | - .pill{font-family:var(--mono);font-size:12px;font-weight:300;letter-spacing:0.03em;border:none;border-radius:6px;padding:5px 12px;cursor:pointer;transition:all 0.25s;line-height:1.4;position:relative;overflow:hidden} | 463 | + } |
| 877 | - .pill::before{content:'';position:absolute;inset:0;background:rgba(255,255,255,0.12);opacity:0;transition:opacity 0.25s} | 464 | + |
| 878 | - .pill:hover::before{opacity:1} | 465 | + .legend { |
| 879 | - .pill:hover{transform:translateY(-1px)} | 466 | + display: flex; |
| 880 | - | 467 | + flex-wrap: wrap; |
| 881 | - .scroll-hint{position:absolute;bottom:28px;left:0;right:0;margin:0 auto;width:fit-content;display:flex;flex-direction:column;align-items:center;gap:10px;animation:scrollHint 2.5s ease-in-out infinite;z-index:1;transition:opacity 0.3s} | 468 | + gap: 0.5rem; |
| 882 | - .scroll-hint-hidden{opacity:0;pointer-events:none} | 469 | + margin-top: 1rem; |
| 883 | - .scroll-hint span{font-family:var(--mono);font-size:10px;letter-spacing:0.2em;color:#B8B5AD;opacity:0.7} | 470 | + padding-top: 1rem; |
| 884 | - | 471 | + border-top: 1px solid #333; |
| 885 | - /* ── DIRECTORY ── */ | 472 | + } |
| 886 | - .directory{background:#f5f5f7;min-height:100vh;display:flex;align-items:center;scroll-snap-align:start;scroll-snap-stop:always;padding:48px} | 473 | + |
| 887 | - .directory-inner{display:grid;grid-template-columns:280px 1fr;gap:48px;width:100%;max-width:1200px;margin:0 auto;align-items:center} | 474 | + .legend-item { |
| 888 | - .directory-left{} | 475 | + display: flex; |
| 889 | - .directory-right{display:flex;flex-direction:column;gap:20px} | 476 | + align-items: center; |
| 890 | - .section-tag{font-family:var(--mono);font-size:12px;letter-spacing:0.2em;color:#6B6860;margin-bottom:10px} | 477 | + gap: 0.4rem; |
| 891 | - .section-tag-light{font-family:var(--mono);font-size:12px;letter-spacing:0.2em;color:#B8B5AD} | 478 | + background: #2a2a2a; |
| 892 | - .section-title{font-size:clamp(40px,4.5vw,56px);font-weight:900;letter-spacing:-0.025em;line-height:1.05;color:#1C1C1A} | 479 | + border: 1px solid #333; |
| 893 | - .section-title-light{font-size:clamp(32px,4vw,46px);font-weight:900;letter-spacing:-0.02em;line-height:1.08;color:#F5F0E8} | 480 | + padding: 0.3rem 0.6rem; |
| 894 | - | 481 | + border-radius: 4px; |
| 895 | - /* Door cards */ | 482 | + cursor: pointer; |
| 896 | - .door{border-radius:24px;padding:44px 40px 40px;cursor:pointer;transition:all 0.5s cubic-bezier(0.16,1,0.3,1);position:relative;overflow:hidden;display:flex;flex-direction:column;justify-content:space-between;min-height:260px} | 483 | + transition: all 0.2s; |
| 897 | - .door-hovered{transform:translateY(-4px)} | 484 | + } |
| 898 | - .door-dark{background:#1C1C1A;color:#F5F0E8;box-shadow:none} | 485 | + |
| 899 | - .door-dark.door-hovered{box-shadow:0 24px 64px rgba(0,0,0,0.2)} | 486 | + .legend-item:hover { |
| 900 | - .door-warm{background:#ffffff;color:#1C1C1A;box-shadow:none} | 487 | + background: #333; |
| 901 | - .door-warm.door-hovered{box-shadow:0 24px 64px rgba(201,167,81,0.12)} | 488 | + } |
| 902 | - .door-accent{position:absolute;top:0;left:0;right:0;height:3px;transform-origin:left;transition:transform 0.6s cubic-bezier(0.16,1,0.3,1)} | 489 | + |
| 903 | - .door-content{position:relative} | 490 | + .legend-color { |
| 904 | - .door-header{display:flex;align-items:center;gap:10px;margin-bottom:16px} | 491 | + width: 12px; |
| 905 | - .door-icon{transition:opacity 0.3s} | 492 | + height: 12px; |
| 906 | - .door-badge{display:flex;align-items:center;gap:6px} | 493 | + border-radius: 2px; |
| 907 | - .door-badge-text{font-family:var(--mono);font-size:11px;letter-spacing:0.16em;font-weight:500} | 494 | + flex-shrink: 0; |
| 908 | - .door-title{font-size:clamp(28px,3vw,38px);font-weight:900;letter-spacing:-0.02em;line-height:1.08;color:#F5F0E8} | 495 | + } |
| 909 | - .door-title-dark{color:#1C1C1A} | 496 | + |
| 910 | - .door-desc{font-family:var(--sans);font-size:17px;color:#6B6860;line-height:1.55;margin-top:10px;max-width:480px} | 497 | + .legend-label { |
| 911 | - .door-desc-light{color:#9B9890} | 498 | + font-size: 0.75rem; |
| 912 | - .door-pills{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px} | 499 | + color: #aaa; |
| 913 | - .door-pill{font-family:var(--sans);font-size:16px;padding:10px 16px;border-radius:8px;line-height:1.3} | 500 | + max-width: 150px; |
| 914 | - .door-pill-dark{background:rgba(74,139,110,0.12);color:#8ac4a8;font-weight:500} | 501 | + overflow: hidden; |
| 915 | - .door-pill-light{background:rgba(166,138,62,0.1);color:#a68a3e;font-weight:500} | 502 | + text-overflow: ellipsis; |
| 916 | - .door-pill-green{background:rgba(74,139,110,0.1);color:#3d7a5e;font-weight:500} | 503 | + white-space: nowrap; |
| 917 | - .door-footer{position:relative;display:flex;justify-content:space-between;align-items:center;margin-top:24px} | 504 | + } |
| 918 | - .door-meta{font-family:var(--mono);font-size:11px;letter-spacing:0.1em;color:#4A4843} | 505 | + |
| 919 | - .door-meta-light{color:#B8B5AD} | 506 | + .tooltip { |
| 920 | - .door-cta{font-family:var(--sans);font-size:16px;font-weight:400;transition:color 0.3s} | 507 | + position: fixed; |
| 921 | - | 508 | + background: rgba(30, 30, 30, 0.95); |
| 922 | - /* ── CLASIFICADORES ── */ | 509 | + backdrop-filter: blur(8px); |
| 923 | - .clasificadores{background:#e8e8ea;color:#1C1C1A;padding:48px;height:100vh;display:flex;flex-direction:column;justify-content:center;scroll-snap-align:start;scroll-snap-stop:always;overflow:hidden} | 510 | + border: 1px solid #444; |
| 924 | - .clas-container{max-width:1100px;margin:0 auto;width:100%;max-height:calc(100vh - 96px);display:flex;flex-direction:column} | 511 | + border-radius: 8px; |
| 925 | - .clas-header-section{margin-bottom:24px;flex-shrink:0} | 512 | + padding: 0.75rem 1rem; |
| 926 | - .clas-header-row{display:flex;justify-content:space-between;align-items:flex-end} | 513 | + pointer-events: none; |
| 927 | - .clas-subtitle{font-family:var(--sans);font-size:17px;color:#6B6860;margin-top:10px} | 514 | + z-index: 1000; |
| 928 | - .clas-ver-todos{font-family:var(--sans);font-size:15px;font-weight:500;color:#1C1C1A;transition:color 0.2s} | 515 | + max-width: 300px; |
| 929 | - .clas-ver-todos:hover{color:#4A8B6E} | 516 | + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4); |
| 930 | - | 517 | + } |
| 931 | - /* Group cards */ | 518 | + |
| 932 | - .clas-group-card{background:#f2f2f4;border:1px solid rgba(0,0,0,0.04);border-radius:20px;padding:16px;margin-bottom:12px;height:100%;display:flex;flex-direction:column;transition:all 0.3s} | 519 | + .tooltip-header { |
| 933 | - /* Parent reacts to child hover */ | 520 | + border-left: 3px solid; |
| 934 | - .clas-group-card:has(.widget-card:hover){background:#efefef;border-color:rgba(0,0,0,0.06)} | 521 | + padding-left: 0.5rem; |
| 935 | - .clas-group-card:has(.widget-card:hover) > .clas-group-header .clas-group-label{transform:scale(1.05);transform-origin:left} | 522 | + margin-bottom: 0.5rem; |
| 936 | - /* Parent hover highlights children */ | 523 | + } |
| 937 | - .clas-group-card:hover .widget-card{background:#fdfdfd;border-color:rgba(0,0,0,0.05)} | 524 | + |
| 938 | - .clas-group-card:hover .clas-subgroup{background:#f5f5f7} | 525 | + .tooltip-code { |
| 939 | - .gastos-content{display:flex;flex-direction:column;flex:1;gap:10px} | 526 | + font-family: monospace; |
| 940 | - .gastos-content .clas-subgroup{flex:1;margin-bottom:0} | 527 | + font-size: 0.75rem; |
| 941 | - .gastos-content > .widgets-row{flex:1} | 528 | + color: #888; |
| 942 | - .clas-group-header{display:flex;align-items:center;gap:10px;margin-bottom:10px} | 529 | + display: block; |
| 943 | - .clas-group-icon{width:32px;height:32px;border-radius:10px;background:color-mix(in srgb, var(--c) 10%, transparent);border:1px solid color-mix(in srgb, var(--c) 15%, transparent);display:flex;align-items:center;justify-content:center;color:var(--c);flex-shrink:0} | 530 | + } |
| 944 | - .clas-group-label{font-family:var(--mono);font-size:12px;letter-spacing:0.15em;font-weight:600;color:#1C1C1A;transition:transform 0.3s cubic-bezier(0.16,1,0.3,1)} | 531 | + |
| 945 | - .clas-group-sub{font-family:var(--sans);font-size:13px;color:#6B6860;margin-top:2px} | 532 | + .tooltip-desc { |
| 946 | - | 533 | + font-size: 0.9rem; |
| 947 | - /* Subgroup */ | 534 | + color: #f5f5f5; |
| 948 | - .clas-subgroup{background:#f7f7f9;border:1px solid rgba(0,0,0,0.03);border-radius:14px;padding:12px;margin-bottom:10px;display:flex;flex-direction:column;transition:all 0.3s} | 535 | + font-weight: 500; |
| 949 | - /* Subgroup parent reacts to child hover */ | 536 | + } |
| 950 | - .clas-subgroup:has(.widget-card:hover){background:#f3f3f5;border-color:rgba(0,0,0,0.05)} | 537 | + |
| 951 | - .clas-subgroup:has(.widget-card:hover) > .clas-subgroup-header .clas-subgroup-label{transform:scale(1.05);transform-origin:left} | 538 | + .tooltip-value { |
| 952 | - /* Subgroup hover highlights children */ | 539 | + font-size: 0.85rem; |
| 953 | - .clas-subgroup:hover .widget-card{background:#fdfdfd;border-color:rgba(0,0,0,0.05)} | 540 | + color: #C9A751; |
| 954 | - .clas-subgroup .widgets-row{flex:1} | 541 | + font-weight: 600; |
| 955 | - .clas-subgroup-header{display:flex;align-items:center;gap:10px;margin-bottom:10px} | ||
| 956 | - .clas-subgroup-icon{width:32px;height:32px;border-radius:10px;background:color-mix(in srgb, var(--c) 10%, transparent);border:1px solid color-mix(in srgb, var(--c) 15%, transparent);display:flex;align-items:center;justify-content:center;color:var(--c);flex-shrink:0} | ||
| 957 | - .clas-subgroup-label{font-family:var(--mono);font-size:12px;letter-spacing:0.15em;font-weight:600;color:#1C1C1A;transition:transform 0.3s cubic-bezier(0.16,1,0.3,1)} | ||
| 958 | - .clas-subgroup-tag{font-family:var(--sans);font-size:13px;color:#6B6860;margin-top:2px} | ||
| 959 | - | ||
| 960 | - /* Main grid */ | ||
| 961 | - .clas-main-grid{display:grid;grid-template-columns:1fr 2fr;gap:12px;align-items:stretch;flex:1;min-height:0} | ||
| 962 | - .clas-col{display:flex;flex-direction:column} | ||
| 963 | - .clas-col .clas-group-card{flex:1} | ||
| 964 | - | ||
| 965 | - /* Widget cards */ | ||
| 966 | - .widget-card{display:block;background:#fafafa;border:1px solid rgba(0,0,0,0.04);border-radius:14px;padding:14px;cursor:pointer;text-decoration:none;color:#1C1C1A;transition:all 0.35s cubic-bezier(0.16,1,0.3,1);box-shadow:0 1px 3px rgba(0,0,0,0.02)} | ||
| 967 | - .widget-hover{background:#ffffff;border-color:rgba(0,0,0,0.06);transform:translateY(-2px);box-shadow:0 8px 24px rgba(0,0,0,0.05)} | ||
| 968 | - .widget-h{display:flex;justify-content:space-between;align-items:center} | ||
| 969 | - .widget-left{flex:1} | ||
| 970 | - .widget-right{display:flex;align-items:center;gap:12px;flex-shrink:0} | ||
| 971 | - .widget-grow{flex:1} | ||
| 972 | - .widget-sm{padding:12px} | ||
| 973 | - .widget-full{flex:1} | ||
| 974 | - .widgets-col{display:flex;flex-direction:column;gap:8px;flex:1} | ||
| 975 | - .widgets-col .widget-card{flex:1} | ||
| 976 | - .widgets-row{display:grid;grid-template-columns:1fr 1fr;gap:8px;flex:1} | ||
| 977 | - .widgets-row .widget-card{display:flex;flex-direction:column;justify-content:space-between} | ||
| 978 | - .widget-headline{display:flex;align-items:baseline;gap:8px;margin-bottom:6px} | ||
| 979 | - .widget-num-big{font-family:var(--mono);font-size:24px;font-weight:700;line-height:1} | ||
| 980 | - .widget-num-gold{color:#a68a3e} | ||
| 981 | - .widget-num-green{color:#4A8B6E} | ||
| 982 | - .widget-label{font-family:var(--sans);font-size:14px;font-weight:700;color:#1C1C1A;line-height:1.2} | ||
| 983 | - .widget-desc{font-family:var(--sans);font-size:14px;color:#6B6860;line-height:1.4;font-weight:400} | ||
| 984 | - .widget-desc-sm{font-size:13px} | ||
| 985 | - .widget-footer{display:flex;justify-content:space-between;align-items:center;margin-top:8px} | ||
| 986 | - .widget-tech{font-family:var(--mono);font-size:9px;letter-spacing:0.1em;color:#9B9890;text-transform:uppercase} | ||
| 987 | - .widget-arrow{font-family:var(--sans);font-size:14px;color:transparent;transform:translateX(-6px);transition:all 0.35s cubic-bezier(0.16,1,0.3,1)} | ||
| 988 | - .arrow-show{color:#1C1C1A;transform:translateX(0)} | ||
| 989 | - | ||
| 990 | - /* ── HISTORIAS ── */ | ||
| 991 | - .historias{background:#1C1C1A;padding:80px 48px;color:#F5F0E8;min-height:100vh;display:flex;flex-direction:column;justify-content:center;scroll-snap-align:start;scroll-snap-stop:always} | ||
| 992 | - .historias .container{width:100%;max-width:1060px;margin:0 auto} | ||
| 993 | - .hist-header{display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:36px} | ||
| 994 | - .hist-tag-row{display:flex;align-items:center;gap:8px;margin-bottom:8px} | ||
| 995 | - .hist-tag-row svg{stroke:#9B9890} | ||
| 996 | - .hist-tag-row .section-tag{color:#9B9890} | ||
| 997 | - .historias .section-title{color:#F5F0E8} | ||
| 998 | - .hist-subtitle{font-family:var(--sans);font-size:17px;color:#9B9890;margin-top:10px} | ||
| 999 | - .hist-grid{display:grid;grid-template-columns:repeat(3, 1fr);grid-auto-rows:minmax(120px, auto);gap:16px} | ||
| 1000 | - .hist-cell{grid-row:span 1} | ||
| 1001 | - .hist-tall{grid-row:span 2} | ||
| 1002 | - .hist-card{background:color-mix(in srgb, var(--card-color) 15%, #1C1C1A);border:1px solid color-mix(in srgb, var(--card-color) 25%, transparent);border-radius:18px;padding:28px 24px 24px;cursor:pointer;transition:all 0.5s cubic-bezier(0.16,1,0.3,1);position:relative;overflow:hidden;height:100%;display:flex;flex-direction:column;justify-content:space-between} | ||
| 1003 | - .hist-card-hovered{background:color-mix(in srgb, var(--card-color) 22%, #1C1C1A);border-color:color-mix(in srgb, var(--card-color) 35%, transparent);transform:translateY(-3px)} | ||
| 1004 | - .hist-accent{position:absolute;top:0;left:0;right:0;height:2px;transform-origin:left;transition:transform 0.5s cubic-bezier(0.16,1,0.3,1)} | ||
| 1005 | - .hist-content{flex:1} | ||
| 1006 | - .hist-tag{font-family:var(--mono);font-size:10px;letter-spacing:0.14em;font-weight:500} | ||
| 1007 | - .hist-title{font-family:var(--serif);font-size:18px;font-weight:800;margin-top:12px;line-height:1.25;letter-spacing:-0.01em;color:#F5F0E8} | ||
| 1008 | - .hist-tall .hist-title{font-size:22px} | ||
| 1009 | - .hist-desc{font-family:var(--sans);font-size:14px;color:#9B9890;line-height:1.5;margin-top:10px} | ||
| 1010 | - .hist-cta{font-family:var(--sans);font-size:14px;margin-top:20px;transition:color 0.3s;display:flex;align-items:center;gap:3px;color:#6B6860} | ||
| 1011 | - .hist-arrow{transition:transform 0.3s;display:inline-block} | ||
| 1012 | - .hist-arrow-moved{transform:translateX(3px)} | ||
| 1013 | - | ||
| 1014 | - /* ── VISUALIZACIONES ── */ | ||
| 1015 | - .visualizaciones{background:#f5f5f7;padding:80px 48px;color:#1C1C1A;min-height:100vh;display:flex;flex-direction:column;justify-content:center;scroll-snap-align:start;scroll-snap-stop:always} | ||
| 1016 | - .visualizaciones .container{width:100%;max-width:1060px;margin:0 auto} | ||
| 1017 | - .vis-header{display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:36px} | ||
| 1018 | - .vis-tag-row{display:flex;align-items:center;gap:8px;margin-bottom:8px} | ||
| 1019 | - .vis-subtitle{font-family:var(--sans);font-size:17px;color:#6B6860;margin-top:10px} | ||
| 1020 | - .gold-link{font-family:var(--sans);font-size:15px;color:#C9A751;font-weight:600} | ||
| 1021 | - .vis-grid-simple{display:grid;grid-template-columns:repeat(3, 1fr);gap:16px} | ||
| 1022 | - .vis-card{background:#fafafc;border-radius:18px;padding:28px 24px 24px;cursor:pointer;transition:all 0.5s cubic-bezier(0.16,1,0.3,1);position:relative;overflow:hidden;height:100%;display:flex;flex-direction:column;justify-content:space-between} | ||
| 1023 | - .vis-card-hovered{transform:translateY(-2px);box-shadow:0 16px 40px rgba(0,0,0,0.05)} | ||
| 1024 | - .vis-accent{position:absolute;top:0;left:0;right:0;height:2px;transform-origin:left;transition:transform 0.5s cubic-bezier(0.16,1,0.3,1)} | ||
| 1025 | - .vis-type{font-family:var(--mono);font-size:10px;letter-spacing:0.1em;font-weight:500;padding:5px 10px;border-radius:6px;display:inline-block} | ||
| 1026 | - .vis-title{font-family:var(--serif);font-size:18px;font-weight:800;margin-top:12px;line-height:1.25;letter-spacing:-0.01em;color:#1C1C1A} | ||
| 1027 | - .vis-desc{font-family:var(--sans);font-size:14px;color:#6B6860;line-height:1.5;margin-top:10px} | ||
| 1028 | - .vis-cta{font-family:var(--sans);font-size:14px;margin-top:20px;transition:color 0.3s;display:flex;align-items:center;gap:3px;color:#6B6860} | ||
| 1029 | - .vis-arrow{transition:transform 0.3s;display:inline-block} | ||
| 1030 | - .vis-arrow-moved{transform:translateX(3px)} | ||
| 1031 | - | ||
| 1032 | - /* ── MANIFIESTO ── */ | ||
| 1033 | - .manifiesto{background:#1C1C1A;color:#F5F0E8;padding:96px 48px;position:relative;min-height:100vh;display:flex;flex-direction:column;justify-content:center;scroll-snap-align:start;scroll-snap-stop:always} | ||
| 1034 | - .manifiesto-inner{max-width:640px;width:100%;margin:0 auto;position:relative;text-align:center} | ||
| 1035 | - .manifiesto-title{font-size:clamp(36px,4.5vw,56px);font-weight:900;line-height:1.08;letter-spacing:-0.025em;margin-top:18px} | ||
| 1036 | - .manifiesto-text{font-family:var(--sans);font-size:17px;color:#9B9890;margin-top:20px;line-height:1.7} | ||
| 1037 | - | ||
| 1038 | - /* ── DESCARGAS ── */ | ||
| 1039 | - .descargas{background:#1C1C1A;padding:80px 48px;color:#F5F0E8;min-height:100vh;display:flex;flex-direction:column;justify-content:center;scroll-snap-align:start;scroll-snap-stop:always} | ||
| 1040 | - .descargas-inner{max-width:640px;width:100%;margin:0 auto;text-align:center} | ||
| 1041 | - .descargas-title{font-size:clamp(32px,4vw,48px);font-weight:900;line-height:1.1;letter-spacing:-0.02em;color:#F5F0E8} | ||
| 1042 | - .descargas-formats{display:flex;justify-content:center;gap:12px;margin-top:24px} | ||
| 1043 | - .format-pill{font-family:var(--mono);font-size:14px;letter-spacing:0.05em;color:#9B9890;background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.08);padding:8px 16px;border-radius:8px} | ||
| 1044 | - .descargas-link{display:inline-flex;align-items:center;gap:8px;margin-top:32px;font-family:var(--sans);font-size:15px;color:#C9A751;transition:all 0.3s} | ||
| 1045 | - .descargas-link:hover{color:#d4b45a;gap:10px} | ||
| 1046 | - | ||
| 1047 | - /* ── FOOTER ── */ | ||
| 1048 | - footer{background:#1C1C1A;padding:40px 48px;border-top:1px solid #2A2A27} | ||
| 1049 | - .footer-inner{max-width:1060px;margin:0 auto;display:flex;justify-content:space-between;align-items:center} | ||
| 1050 | - .footer-org{font-family:var(--mono);font-size:12px;letter-spacing:0.12em;color:#6B6860} | ||
| 1051 | - .footer-unit{font-family:var(--sans);font-size:13px;color:#6B6860;margin-top:3px} | ||
| 1052 | - .footer-right{font-family:var(--mono);font-size:12px;letter-spacing:0.1em;color:#6B6860} | ||
| 1053 | - | ||
| 1054 | - /* ══════════════════════════════════════════════════════════════════════════ | ||
| 1055 | - RESPONSIVE - TABLET (768px) | ||
| 1056 | - ══════════════════════════════════════════════════════════════════════════ */ | ||
| 1057 | - @media(max-width:768px){ | ||
| 1058 | - .page{scroll-snap-type:y proximity} | ||
| 1059 | - .landing-screen,.directory,.clasificadores,.historias,.visualizaciones,.manifiesto,.descargas{scroll-snap-stop:normal} | ||
| 1060 | - | ||
| 1061 | - /* Section dots - bottom horizontal bar */ | ||
| 1062 | - .section-dots{position:fixed;right:auto;left:50%;top:auto;bottom:20px;transform:translateX(-50%);flex-direction:row;gap:10px;padding:12px 20px;border-radius:24px;background:rgba(28,28,26,0.7);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid rgba(255,255,255,0.1);z-index:1000} | ||
| 1063 | - .section-dot{width:8px;height:8px} | ||
| 1064 | - .section-dot-label{display:none} | ||
| 1065 | - | ||
| 1066 | - /* Nav */ | ||
| 1067 | - nav{padding:12px 24px} | ||
| 1068 | - .nav-logo{height:60px} | ||
| 1069 | - | ||
| 1070 | - /* Hero */ | ||
| 1071 | - .hero{padding:32px 24px} | ||
| 1072 | - .hero-inner{margin-top:0} | ||
| 1073 | - .hero-title{font-size:clamp(36px,8vw,48px)} | ||
| 1074 | - .hero-stats{font-size:11px;line-height:1.6} | ||
| 1075 | - .stat-dot{margin:0 6px} | ||
| 1076 | - | ||
| 1077 | - /* Search bar - mobile compact */ | ||
| 1078 | - .search-bar{flex-wrap:nowrap;padding:8px 12px;gap:8px} | ||
| 1079 | - .toggle-desktop{display:none} | ||
| 1080 | - .search-divider{display:none} | ||
| 1081 | - .gear-desktop{display:none} | ||
| 1082 | - .mobile-settings-btn{display:flex} | ||
| 1083 | - .mobile-settings-panel{display:block} | ||
| 1084 | - .search-icon{margin-left:0} | ||
| 1085 | - .search-input-wrap{order:1} | ||
| 1086 | - .search-input-wrap input{font-size:15px;padding:10px 8px} | ||
| 1087 | - .placeholder-desktop{display:none} | ||
| 1088 | - .placeholder-mobile{display:inline;font-size:14px} | ||
| 1089 | - | ||
| 1090 | - /* Search results mobile */ | ||
| 1091 | - .search-results{max-height:260px;border-radius:0 0 12px 12px} | ||
| 1092 | - .search-result-item{padding:12px 16px} | ||
| 1093 | - .result-name{font-size:13px} | ||
| 1094 | - .result-meta{flex-direction:column;align-items:flex-end;gap:4px} | ||
| 1095 | - .result-code{font-size:9px} | ||
| 1096 | - .result-type{font-size:8px} | ||
| 1097 | - .pills{gap:6px} | ||
| 1098 | - .pill{font-size:11px;padding:4px 10px} | ||
| 1099 | - .scroll-hint{bottom:20px} | ||
| 1100 | - | ||
| 1101 | - /* Directory */ | ||
| 1102 | - .directory{padding:48px 24px} | ||
| 1103 | - .directory-inner{grid-template-columns:1fr;gap:32px} | ||
| 1104 | - .directory-left{text-align:center} | ||
| 1105 | - .section-title{font-size:clamp(32px,7vw,42px)} | ||
| 1106 | - .door{padding:28px 24px 24px;min-height:auto} | ||
| 1107 | - .door-title{font-size:clamp(24px,5vw,32px)} | ||
| 1108 | - .door-pills{gap:6px} | ||
| 1109 | - .door-pill{font-size:14px;padding:8px 12px} | ||
| 1110 | - .door-meta{font-size:10px} | ||
| 1111 | - | ||
| 1112 | - /* Clasificadores */ | ||
| 1113 | - .clasificadores{padding:32px 20px;height:auto;min-height:100vh;overflow-y:auto} | ||
| 1114 | - .clas-container{max-height:none} | ||
| 1115 | - .clas-header-row{flex-direction:column;align-items:flex-start;gap:12px} | ||
| 1116 | - .clas-ver-todos{align-self:flex-start} | ||
| 1117 | - .clas-main-grid{grid-template-columns:1fr;gap:12px} | ||
| 1118 | - .widgets-row{grid-template-columns:1fr} | ||
| 1119 | - .widget-h{flex-direction:column;align-items:flex-start;gap:12px} | ||
| 1120 | - .widget-right{width:100%;justify-content:space-between} | ||
| 1121 | - .widget-num-big{font-size:22px} | ||
| 1122 | - .clas-group-icon,.clas-subgroup-icon{width:28px;height:28px} | ||
| 1123 | - | ||
| 1124 | - /* Historias */ | ||
| 1125 | - .historias{padding:60px 24px} | ||
| 1126 | - .hist-header{flex-direction:column;align-items:flex-start;gap:16px} | ||
| 1127 | - .hist-grid{grid-template-columns:1fr 1fr;grid-auto-rows:auto;gap:12px} | ||
| 1128 | - .hist-tall{grid-row:span 1} | ||
| 1129 | - .hist-card{padding:20px 18px 18px} | ||
| 1130 | - .hist-title{font-size:16px} | ||
| 1131 | - .hist-tall .hist-title{font-size:18px} | ||
| 1132 | - .hist-desc{font-size:13px} | ||
| 1133 | - | ||
| 1134 | - /* Visualizaciones */ | ||
| 1135 | - .visualizaciones{padding:60px 24px} | ||
| 1136 | - .vis-header{flex-direction:column;align-items:flex-start;gap:16px} | ||
| 1137 | - .vis-grid-simple{grid-template-columns:1fr 1fr;gap:12px} | ||
| 1138 | - .vis-card{padding:20px 18px 18px} | ||
| 1139 | - .vis-title{font-size:16px} | ||
| 1140 | - | ||
| 1141 | - /* Manifiesto */ | ||
| 1142 | - .manifiesto{padding:60px 24px} | ||
| 1143 | - .manifiesto-title{font-size:clamp(28px,6vw,40px)} | ||
| 1144 | - | ||
| 1145 | - /* Descargas */ | ||
| 1146 | - .descargas{padding:60px 24px} | ||
| 1147 | - .descargas-title{font-size:clamp(28px,6vw,40px)} | ||
| 1148 | - .descargas-formats{gap:8px} | ||
| 1149 | - .format-pill{font-size:12px;padding:6px 12px} | ||
| 1150 | - | ||
| 1151 | - /* Footer */ | ||
| 1152 | - footer{padding:32px 24px} | ||
| 1153 | - .footer-inner{flex-direction:column;gap:12px;text-align:center} | ||
| 1154 | - .footer-org,.footer-right{font-size:10px} | ||
| 1155 | } | 542 | } |
| 1156 | 543 | ||
| 1157 | - /* ══════════════════════════════════════════════════════════════════════════ | 544 | + .tooltip-hint { |
| 1158 | - RESPONSIVE - MOBILE (480px) | 545 | + font-size: 0.75rem; |
| 1159 | - ══════════════════════════════════════════════════════════════════════════ */ | 546 | + color: #666; |
| 1160 | - @media(max-width:480px){ | 547 | + margin-top: 0.3rem; |
| 1161 | - /* Section dots - bottom bar smaller */ | ||
| 1162 | - .section-dots{bottom:16px;gap:8px;padding:10px 16px;border-radius:20px} | ||
| 1163 | - .section-dot{width:7px;height:7px} | ||
| 1164 | - | ||
| 1165 | - /* Nav */ | ||
| 1166 | - nav{padding:10px 16px} | ||
| 1167 | - .nav-logo{height:48px} | ||
| 1168 | - | ||
| 1169 | - /* Hero */ | ||
| 1170 | - .hero{padding:24px 16px} | ||
| 1171 | - .hero-title{font-size:clamp(32px,9vw,40px)} | ||
| 1172 | - .hero-sub{font-size:15px} | ||
| 1173 | - .hero-stats{font-size:10px;display:flex;flex-wrap:wrap;justify-content:center;gap:4px 0} | ||
| 1174 | - .stat-dot{margin:0 4px} | ||
| 1175 | - | ||
| 1176 | - /* Search */ | ||
| 1177 | - .search-wrap{padding:0} | ||
| 1178 | - .mobile-settings-btn{width:36px;height:36px} | ||
| 1179 | - .mobile-toggle-btn{padding:14px 10px} | ||
| 1180 | - .mobile-toggle-btn span{font-size:13px} | ||
| 1181 | - .search-results{max-height:220px} | ||
| 1182 | - .search-result-item{padding:10px 14px} | ||
| 1183 | - .result-name{font-size:12px} | ||
| 1184 | - .result-sigla{font-size:10px} | ||
| 1185 | - .pills-label{display:none} | ||
| 1186 | - .pill{font-size:10px;padding:4px 8px} | ||
| 1187 | - .scroll-hint{display:none} | ||
| 1188 | - | ||
| 1189 | - /* Directory */ | ||
| 1190 | - .directory{padding:40px 16px} | ||
| 1191 | - .directory-inner{gap:24px} | ||
| 1192 | - .section-tag{font-size:10px} | ||
| 1193 | - .section-title{font-size:clamp(28px,8vw,36px)} | ||
| 1194 | - .door{padding:24px 20px 20px;border-radius:18px} | ||
| 1195 | - .door-icon svg{width:22px;height:22px} | ||
| 1196 | - .door-title{font-size:clamp(22px,6vw,28px)} | ||
| 1197 | - .door-pill{font-size:13px;padding:7px 10px} | ||
| 1198 | - .door-footer{flex-direction:column;align-items:flex-start;gap:8px} | ||
| 1199 | - .door-meta{font-size:9px} | ||
| 1200 | - .door-cta{font-size:14px} | ||
| 1201 | - | ||
| 1202 | - /* Clasificadores */ | ||
| 1203 | - .clasificadores{padding:24px 16px} | ||
| 1204 | - .clas-header-section{margin-bottom:16px} | ||
| 1205 | - .section-title{font-size:clamp(24px,6vw,32px)} | ||
| 1206 | - .clas-subtitle{font-size:14px} | ||
| 1207 | - .clas-group-card{padding:12px;border-radius:14px;margin-bottom:10px} | ||
| 1208 | - .clas-subgroup{padding:10px;border-radius:10px} | ||
| 1209 | - .clas-group-header,.clas-subgroup-header{margin-bottom:8px} | ||
| 1210 | - .clas-group-label,.clas-subgroup-label{font-size:10px} | ||
| 1211 | - .clas-group-sub{font-size:12px} | ||
| 1212 | - .widget-card{padding:12px;border-radius:10px} | ||
| 1213 | - .widget-num-big{font-size:20px} | ||
| 1214 | - .widget-label{font-size:13px} | ||
| 1215 | - .widget-desc{font-size:12px} | ||
| 1216 | - .widget-tech{font-size:8px} | ||
| 1217 | - | ||
| 1218 | - /* Historias */ | ||
| 1219 | - .historias{padding:48px 16px} | ||
| 1220 | - .hist-subtitle{font-size:14px} | ||
| 1221 | - .hist-grid{grid-template-columns:1fr;gap:10px} | ||
| 1222 | - .hist-card{padding:18px 16px 16px;border-radius:14px} | ||
| 1223 | - .hist-tag{font-size:9px} | ||
| 1224 | - .hist-title{font-size:16px} | ||
| 1225 | - .hist-desc{font-size:12px;margin-top:8px} | ||
| 1226 | - .hist-cta{font-size:13px;margin-top:14px} | ||
| 1227 | - | ||
| 1228 | - /* Visualizaciones */ | ||
| 1229 | - .visualizaciones{padding:48px 16px} | ||
| 1230 | - .vis-subtitle{font-size:14px} | ||
| 1231 | - .vis-grid-simple{grid-template-columns:1fr;gap:10px} | ||
| 1232 | - .vis-card{padding:18px 16px 16px;border-radius:14px} | ||
| 1233 | - .vis-type{font-size:9px;padding:4px 8px} | ||
| 1234 | - .vis-title{font-size:16px} | ||
| 1235 | - .vis-desc{font-size:12px} | ||
| 1236 | - .vis-cta{font-size:13px;margin-top:14px} | ||
| 1237 | - | ||
| 1238 | - /* Manifiesto */ | ||
| 1239 | - .manifiesto{padding:48px 16px} | ||
| 1240 | - .section-tag-light{font-size:10px} | ||
| 1241 | - .manifiesto-title{font-size:clamp(24px,7vw,32px)} | ||
| 1242 | - | ||
| 1243 | - /* Descargas */ | ||
| 1244 | - .descargas{padding:48px 16px} | ||
| 1245 | - .descargas-title{font-size:clamp(24px,7vw,32px)} | ||
| 1246 | - .descargas-formats{flex-wrap:wrap;gap:6px} | ||
| 1247 | - .format-pill{font-size:11px;padding:5px 10px} | ||
| 1248 | - .descargas-link{font-size:14px;margin-top:24px} | ||
| 1249 | - | ||
| 1250 | - /* Footer */ | ||
| 1251 | - footer{padding:24px 16px} | ||
| 1252 | - .footer-org{font-size:9px;letter-spacing:0.08em} | ||
| 1253 | - .footer-right{font-size:9px} | ||
| 1254 | } | 548 | } |
| 1255 | -</style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 549 | +</style> | ... | ... |
static/fail.png
deleted
100644 → 0
257 KB
static/problem.png
deleted
100644 → 0
71.8 KB
static/treemap_objeto_estado.csv
deleted
100644 → 0
This diff could not be displayed because it is too large.
-
Please register or login to post a comment