Showing
9 changed files
with
682 additions
and
579 deletions
| ... | @@ -111,35 +111,35 @@ | ... | @@ -111,35 +111,35 @@ |
| 111 | ============================================ */ | 111 | ============================================ */ |
| 112 | 112 | ||
| 113 | :root { | 113 | :root { |
| 114 | - /* Modo claro (default) - Slate/White */ | 114 | + /* Modo claro (default) - Estilo Apple, limpio y con contraste */ |
| 115 | - --theme-body: #fafafa; | 115 | + --theme-body: #ffffff; |
| 116 | - --theme-background: #f8fafc; | 116 | + --theme-background: #ffffff; |
| 117 | - --theme-titulo: #1a1a1a; | 117 | + --theme-titulo: #1d1d1f; |
| 118 | - --theme-texto: #64748b; | 118 | + --theme-texto: #424245; |
| 119 | - --theme-fill: #f1f5f9; | 119 | + --theme-fill: #ffffff; |
| 120 | - --theme-borde: rgba(0, 0, 0, 0.08); | 120 | + --theme-borde: rgba(0, 0, 0, 0.04); |
| 121 | - --theme-accent: #c9a751; | 121 | + --theme-accent: #9A8550; |
| 122 | - --theme-accent-hover: #b8963f; | 122 | + --theme-accent-hover: #857040; |
| 123 | 123 | ||
| 124 | /* Superficies alternativas */ | 124 | /* Superficies alternativas */ |
| 125 | --theme-surface: #ffffff; | 125 | --theme-surface: #ffffff; |
| 126 | - --theme-surface-hover: #f8fafc; | 126 | + --theme-surface-hover: #f5f5f7; |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | :root.dark { | 129 | :root.dark { |
| 130 | - /* Modo oscuro */ | 130 | + /* Modo oscuro - Cálido como el landing (#1C1C1A base) */ |
| 131 | - --theme-body: #0d0d0d; | 131 | + --theme-body: #1C1C1A; |
| 132 | - --theme-background: #1a1a1a; | 132 | + --theme-background: #242422; |
| 133 | - --theme-titulo: #e8e4d8; | 133 | + --theme-titulo: #F5F0E8; |
| 134 | - --theme-texto: #b8b3a3; | 134 | + --theme-texto: #B8B5AD; |
| 135 | - --theme-fill: #2d2d2d; | 135 | + --theme-fill: #2A2A27; |
| 136 | - --theme-borde: rgba(255, 255, 255, 0.1); | 136 | + --theme-borde: rgba(255, 255, 255, 0.08); |
| 137 | - --theme-accent: #c9a751; | 137 | + --theme-accent: #C9A751; |
| 138 | - --theme-accent-hover: #d4b76a; | 138 | + --theme-accent-hover: #D4B76A; |
| 139 | 139 | ||
| 140 | /* Superficies alternativas */ | 140 | /* Superficies alternativas */ |
| 141 | - --theme-surface: #1a1a1a; | 141 | + --theme-surface: #232321; |
| 142 | - --theme-surface-hover: #2d2d2d; | 142 | + --theme-surface-hover: #2A2A27; |
| 143 | } | 143 | } |
| 144 | 144 | ||
| 145 | /* ============================================ | 145 | /* ============================================ | ... | ... |
| ... | @@ -49,6 +49,7 @@ | ... | @@ -49,6 +49,7 @@ |
| 49 | <div class="relative group/home"> | 49 | <div class="relative group/home"> |
| 50 | <a | 50 | <a |
| 51 | href="/" | 51 | href="/" |
| 52 | + data-sveltekit-preload-data="off" | ||
| 52 | class="nav-icon p-2 rounded-full transition-all block" | 53 | class="nav-icon p-2 rounded-full transition-all block" |
| 53 | > | 54 | > |
| 54 | <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"> | 55 | <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"> | ... | ... |
| ... | @@ -35,8 +35,8 @@ const createClasificadorCache = () => { | ... | @@ -35,8 +35,8 @@ const createClasificadorCache = () => { |
| 35 | const cache = currentCache.objetoGasto; | 35 | const cache = currentCache.objetoGasto; |
| 36 | if (!cache.lastFetched) return null; | 36 | if (!cache.lastFetched) return null; |
| 37 | 37 | ||
| 38 | - // Cache valid for 5 minutes | 38 | + // Cache valid for 30 minutes |
| 39 | - const isValid = Date.now() - cache.lastFetched < 5 * 60 * 1000; | 39 | + const isValid = Date.now() - cache.lastFetched < 30 * 60 * 1000; |
| 40 | if (!isValid) return null; | 40 | if (!isValid) return null; |
| 41 | 41 | ||
| 42 | return cache; | 42 | return cache; | ... | ... |
src/lib/stores/navigating.js
0 → 100644
| 1 | <script> | 1 | <script> |
| 2 | import '../app.css'; | 2 | import '../app.css'; |
| 3 | - import { page } from '$app/stores'; | 3 | + import { page, navigating } from '$app/stores'; |
| 4 | import favicon from '$lib/assets/favicon.svg'; | 4 | import favicon from '$lib/assets/favicon.svg'; |
| 5 | import Navbar from '$lib/components/ui/Navbar.svelte'; | 5 | import Navbar from '$lib/components/ui/Navbar.svelte'; |
| 6 | import NavigationDrawer from '$lib/components/layout/NavigationDrawer.svelte'; | 6 | import NavigationDrawer from '$lib/components/layout/NavigationDrawer.svelte'; |
| 7 | import { openDrawer } from '$lib/stores/drawer.js'; | 7 | import { openDrawer } from '$lib/stores/drawer.js'; |
| 8 | + import Spinner from '$lib/components/ui/Spinner.svelte'; | ||
| 8 | 9 | ||
| 9 | let { children } = $props(); | 10 | let { children } = $props(); |
| 10 | 11 | ||
| 11 | // Don't show navbar on landing page | 12 | // Don't show navbar on landing page |
| 12 | let isLanding = $derived($page.url.pathname === '/'); | 13 | let isLanding = $derived($page.url.pathname === '/'); |
| 14 | + | ||
| 15 | + // Navigation loading state | ||
| 16 | + let showLoader = $state(false); | ||
| 17 | + let loaderTimeout = null; | ||
| 18 | + let navStartTime = null; | ||
| 19 | + | ||
| 20 | + // Show loader after 150ms delay (avoid flash for fast navigations) | ||
| 21 | + $effect(() => { | ||
| 22 | + if ($navigating) { | ||
| 23 | + navStartTime = performance.now(); | ||
| 24 | + console.log(`[NAV] Navigation started → ${$navigating.to?.url?.pathname}`); | ||
| 25 | + loaderTimeout = setTimeout(() => { | ||
| 26 | + showLoader = true; | ||
| 27 | + }, 150); | ||
| 28 | + } else { | ||
| 29 | + if (loaderTimeout) clearTimeout(loaderTimeout); | ||
| 30 | + if (navStartTime) { | ||
| 31 | + console.log(`[NAV] Navigation complete: ${(performance.now() - navStartTime).toFixed(0)}ms`); | ||
| 32 | + navStartTime = null; | ||
| 33 | + } | ||
| 34 | + showLoader = false; | ||
| 35 | + } | ||
| 36 | + }); | ||
| 13 | </script> | 37 | </script> |
| 14 | 38 | ||
| 15 | <svelte:head> | 39 | <svelte:head> |
| ... | @@ -26,8 +50,55 @@ | ... | @@ -26,8 +50,55 @@ |
| 26 | <Navbar onOpenDrawer={openDrawer} /> | 50 | <Navbar onOpenDrawer={openDrawer} /> |
| 27 | {/if} | 51 | {/if} |
| 28 | 52 | ||
| 53 | +<!-- Navigation loading overlay --> | ||
| 54 | +{#if showLoader} | ||
| 55 | + <div class="nav-loader"> | ||
| 56 | + <div class="nav-loader-content"> | ||
| 57 | + <Spinner size={32} color="#C9A751" /> | ||
| 58 | + <span class="nav-loader-text">Cargando...</span> | ||
| 59 | + </div> | ||
| 60 | + </div> | ||
| 61 | +{/if} | ||
| 62 | + | ||
| 29 | <div class="min-h-screen" style="background-color: var(--theme-body);"> | 63 | <div class="min-h-screen" style="background-color: var(--theme-body);"> |
| 30 | <main> | 64 | <main> |
| 31 | {@render children()} | 65 | {@render children()} |
| 32 | </main> | 66 | </main> |
| 33 | </div> | 67 | </div> |
| 68 | + | ||
| 69 | +<style> | ||
| 70 | + .nav-loader { | ||
| 71 | + position: fixed; | ||
| 72 | + top: 0; | ||
| 73 | + left: 0; | ||
| 74 | + right: 0; | ||
| 75 | + bottom: 0; | ||
| 76 | + background: rgba(28, 28, 26, 0.85); | ||
| 77 | + backdrop-filter: blur(8px); | ||
| 78 | + -webkit-backdrop-filter: blur(8px); | ||
| 79 | + z-index: 9999; | ||
| 80 | + display: flex; | ||
| 81 | + align-items: center; | ||
| 82 | + justify-content: center; | ||
| 83 | + animation: fadeIn 0.15s ease-out; | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + .nav-loader-content { | ||
| 87 | + display: flex; | ||
| 88 | + flex-direction: column; | ||
| 89 | + align-items: center; | ||
| 90 | + gap: 16px; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + .nav-loader-text { | ||
| 94 | + font-family: 'DM Mono', monospace; | ||
| 95 | + font-size: 13px; | ||
| 96 | + color: #B8B5AD; | ||
| 97 | + letter-spacing: 0.05em; | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + @keyframes fadeIn { | ||
| 101 | + from { opacity: 0; } | ||
| 102 | + to { opacity: 1; } | ||
| 103 | + } | ||
| 104 | +</style> | ... | ... |
| 1 | <script> | 1 | <script> |
| 2 | import { onMount } from 'svelte'; | 2 | import { onMount } from 'svelte'; |
| 3 | import { goto } from '$app/navigation'; | 3 | import { goto } from '$app/navigation'; |
| 4 | + import { supabase } from '$lib/supabase'; | ||
| 4 | import { | 5 | import { |
| 5 | query, | 6 | query, |
| 6 | results, | 7 | results, |
| ... | @@ -15,6 +16,13 @@ | ... | @@ -15,6 +16,13 @@ |
| 15 | navigateResults | 16 | navigateResults |
| 16 | } from '$lib/stores/searchStore'; | 17 | } from '$lib/stores/searchStore'; |
| 17 | 18 | ||
| 19 | + // Pre-warm Supabase connection (evita cold start al navegar) | ||
| 20 | + async function warmupSupabase() { | ||
| 21 | + console.time('[WARMUP] Supabase'); | ||
| 22 | + await supabase.schema('ppto').from('vista_objeto_estado').select('objeto').limit(1); | ||
| 23 | + console.timeEnd('[WARMUP] Supabase'); | ||
| 24 | + } | ||
| 25 | + | ||
| 18 | let mounted = false; | 26 | let mounted = false; |
| 19 | let mode = 'archivo'; | 27 | let mode = 'archivo'; |
| 20 | let searchFocused = false; | 28 | let searchFocused = false; |
| ... | @@ -56,6 +64,8 @@ | ... | @@ -56,6 +64,8 @@ |
| 56 | onMount(() => { | 64 | onMount(() => { |
| 57 | setTimeout(() => { mounted = true; }, 80); | 65 | setTimeout(() => { mounted = true; }, 80); |
| 58 | initIndex(); | 66 | initIndex(); |
| 67 | + // Pre-calentar Supabase en background para evitar cold start | ||
| 68 | + warmupSupabase(); | ||
| 59 | }); | 69 | }); |
| 60 | 70 | ||
| 61 | function toggleMobileSettings() { | 71 | function toggleMobileSettings() { |
| ... | @@ -791,7 +801,7 @@ | ... | @@ -791,7 +801,7 @@ |
| 791 | <div class="descargas-formats"> | 801 | <div class="descargas-formats"> |
| 792 | <span class="format-pill">.csv</span> | 802 | <span class="format-pill">.csv</span> |
| 793 | <span class="format-pill">.parquet</span> | 803 | <span class="format-pill">.parquet</span> |
| 794 | - <span class="format-pill">.json</span> | 804 | + <span class="format-pill">.gpkg</span> |
| 795 | </div> | 805 | </div> |
| 796 | <a href="/descargas" class="descargas-link"> | 806 | <a href="/descargas" class="descargas-link"> |
| 797 | <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> | 807 | <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> |
| ... | @@ -872,7 +882,7 @@ | ... | @@ -872,7 +882,7 @@ |
| 872 | .hero-search{display:flex;justify-content:center;position:relative;z-index:50} | 882 | .hero-search{display:flex;justify-content:center;position:relative;z-index:50} |
| 873 | .hero-tag{font-family:var(--mono);font-size:12px;letter-spacing:0.25em;color:#B8B5AD} | 883 | .hero-tag{font-family:var(--mono);font-size:12px;letter-spacing:0.25em;color:#B8B5AD} |
| 874 | .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} | 884 | .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} |
| 875 | - .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} | 885 | + .hero-sub{font-family:var(--sans);font-size:clamp(16px,1.6vw,18px);color:#A5A29A;max-width:100%;margin-top:14px;line-height:1.6} |
| 876 | .hero-stats{font-family:var(--mono);font-size:13px;letter-spacing:0.04em;color:#B8B5AD;margin-top:16px;line-height:1} | 886 | .hero-stats{font-family:var(--mono);font-size:13px;letter-spacing:0.04em;color:#B8B5AD;margin-top:16px;line-height:1} |
| 877 | .stat-num{color:#F5F0E8} | 887 | .stat-num{color:#F5F0E8} |
| 878 | .stat-dot{margin:0 10px;opacity:0.3} | 888 | .stat-dot{margin:0 10px;opacity:0.3} | ... | ... |
| ... | @@ -16,6 +16,19 @@ | ... | @@ -16,6 +16,19 @@ |
| 16 | let highlightedItem = $state(null); | 16 | let highlightedItem = $state(null); |
| 17 | let sidebarOpen = $state(false); | 17 | let sidebarOpen = $state(false); |
| 18 | 18 | ||
| 19 | + // Modo embed (cuando está dentro de un iframe en /objeto/[codigo]) | ||
| 20 | + let isEmbed = $derived($page.url.searchParams.get('embed') === 'true'); | ||
| 21 | + let initialObjeto = $derived($page.url.searchParams.get('objeto')); | ||
| 22 | + | ||
| 23 | + // Función para navegar a un objeto (en modo embed envía mensaje al padre) | ||
| 24 | + function navigateToObjeto(codigo) { | ||
| 25 | + if (isEmbed && window.parent !== window) { | ||
| 26 | + window.parent.postMessage({ type: 'navigate-objeto', codigo }, '*'); | ||
| 27 | + } else { | ||
| 28 | + goto(`/objeto/${codigo}`); | ||
| 29 | + } | ||
| 30 | + } | ||
| 31 | + | ||
| 19 | // Modo de visualización desde URL | 32 | // Modo de visualización desde URL |
| 20 | let viewMode = $derived($page.url.searchParams.get('modo') || 'lista'); | 33 | let viewMode = $derived($page.url.searchParams.get('modo') || 'lista'); |
| 21 | 34 | ||
| ... | @@ -300,6 +313,11 @@ | ... | @@ -300,6 +313,11 @@ |
| 300 | return; | 313 | return; |
| 301 | } | 314 | } |
| 302 | 315 | ||
| 316 | + // No calcular si las dimensiones no están listas | ||
| 317 | + if (treemapWidth <= 0 || treemapHeight <= 0) { | ||
| 318 | + return; | ||
| 319 | + } | ||
| 320 | + | ||
| 303 | const treemap = d3.treemap() | 321 | const treemap = d3.treemap() |
| 304 | .size([treemapWidth, treemapHeight]) | 322 | .size([treemapWidth, treemapHeight]) |
| 305 | .paddingOuter(4) | 323 | .paddingOuter(4) |
| ... | @@ -467,55 +485,59 @@ | ... | @@ -467,55 +485,59 @@ |
| 467 | } | 485 | } |
| 468 | } | 486 | } |
| 469 | 487 | ||
| 470 | - // Manejar resize del contenedor (solo una vez al montar) | 488 | + // Manejar resize del contenedor - usando onMount para evitar conflictos de efectos |
| 471 | let resizeObserver = null; | 489 | let resizeObserver = null; |
| 472 | - let scrollHandler = null; | ||
| 473 | 490 | ||
| 474 | - $effect(() => { | 491 | + // Función para actualizar dimensiones del treemap |
| 475 | - if (treemapContainer && viewMode === 'mapa') { | 492 | + function updateTreemapDimensions() { |
| 476 | - // Función para actualizar dimensiones y recalcular layout | 493 | + if (!treemapContainer || viewMode !== 'mapa') return; |
| 477 | - const updateDimensions = () => { | 494 | + |
| 478 | const rect = treemapContainer.getBoundingClientRect(); | 495 | const rect = treemapContainer.getBoundingClientRect(); |
| 479 | if (rect.width > 0) { | 496 | if (rect.width > 0) { |
| 480 | const newWidth = Math.floor(rect.width); | 497 | const newWidth = Math.floor(rect.width); |
| 481 | - // Calcular altura disponible para que quepa en viewport | ||
| 482 | const viewportHeight = window.innerHeight; | 498 | const viewportHeight = window.innerHeight; |
| 483 | const paddingBottom = 24; | 499 | const paddingBottom = 24; |
| 484 | - | ||
| 485 | const effectiveTop = Math.max(rect.top, 0); | 500 | const effectiveTop = Math.max(rect.top, 0); |
| 486 | const availableHeight = viewportHeight - effectiveTop - paddingBottom; | 501 | const availableHeight = viewportHeight - effectiveTop - paddingBottom; |
| 487 | const newHeight = Math.max(250, Math.floor(availableHeight)); | 502 | const newHeight = Math.max(250, Math.floor(availableHeight)); |
| 488 | 503 | ||
| 489 | - if (newWidth !== treemapWidth || newHeight !== treemapHeight) { | 504 | + const dimensionsChanged = newWidth !== treemapWidth || newHeight !== treemapHeight; |
| 505 | + | ||
| 506 | + if (dimensionsChanged) { | ||
| 490 | treemapWidth = newWidth; | 507 | treemapWidth = newWidth; |
| 491 | treemapHeight = newHeight; | 508 | treemapHeight = newHeight; |
| 509 | + } | ||
| 510 | + | ||
| 511 | + // Recalcular si hay datos y dimensiones válidas | ||
| 512 | + if (currentTreemapNode && currentTreemapNode.children && treemapWidth > 0 && treemapHeight > 0) { | ||
| 513 | + if (dimensionsChanged || treemapNodes.length === 0) { | ||
| 492 | recalculateCurrentLayout(); | 514 | recalculateCurrentLayout(); |
| 493 | } | 515 | } |
| 494 | } | 516 | } |
| 495 | - }; | 517 | + } |
| 518 | + } | ||
| 496 | 519 | ||
| 497 | - // Calcular dimensiones iniciales con pequeño delay para asegurar render | 520 | + // Efecto simple para setup/cleanup de ResizeObserver |
| 498 | - setTimeout(updateDimensions, 50); | 521 | + $effect(() => { |
| 522 | + const container = treemapContainer; | ||
| 523 | + const mode = viewMode; | ||
| 499 | 524 | ||
| 500 | - // Usar ResizeObserver para cambios de tamaño | 525 | + if (container && mode === 'mapa') { |
| 501 | - resizeObserver = new ResizeObserver(() => { | 526 | + // Calcular dimensiones iniciales |
| 502 | - updateDimensions(); | 527 | + requestAnimationFrame(() => { |
| 528 | + updateTreemapDimensions(); | ||
| 503 | }); | 529 | }); |
| 504 | - resizeObserver.observe(treemapContainer); | ||
| 505 | 530 | ||
| 506 | - // También escuchar scroll para recalcular cuando el usuario hace scroll | 531 | + // Setup ResizeObserver |
| 507 | - scrollHandler = () => { | 532 | + const observer = new ResizeObserver(() => { |
| 508 | - updateDimensions(); | 533 | + requestAnimationFrame(updateTreemapDimensions); |
| 509 | - }; | 534 | + }); |
| 510 | - window.addEventListener('scroll', scrollHandler, { passive: true }); | 535 | + observer.observe(container); |
| 536 | + resizeObserver = observer; | ||
| 511 | 537 | ||
| 512 | return () => { | 538 | return () => { |
| 513 | - if (resizeObserver) { | 539 | + observer.disconnect(); |
| 514 | - resizeObserver.disconnect(); | 540 | + resizeObserver = null; |
| 515 | - } | ||
| 516 | - if (scrollHandler) { | ||
| 517 | - window.removeEventListener('scroll', scrollHandler); | ||
| 518 | - } | ||
| 519 | }; | 541 | }; |
| 520 | } | 542 | } |
| 521 | }); | 543 | }); |
| ... | @@ -876,6 +898,45 @@ | ... | @@ -876,6 +898,45 @@ |
| 876 | return Math.max(...years.map(y => parseInt(y))); | 898 | return Math.max(...years.map(y => parseInt(y))); |
| 877 | } | 899 | } |
| 878 | 900 | ||
| 901 | + /** | ||
| 902 | + * Converts a string with years into compact ranges | ||
| 903 | + * Example: "2006, 2007, 2008, 2009, 2015, 2016" → "2006-2009, 2015-2016" | ||
| 904 | + * Single years: "2006, 2008, 2010" → "2006, 2008, 2010" | ||
| 905 | + */ | ||
| 906 | + function formatYearsAsRanges(rangosStr) { | ||
| 907 | + if (!rangosStr) return ''; | ||
| 908 | + | ||
| 909 | + // Extract all 4-digit years | ||
| 910 | + const yearsMatch = rangosStr.match(/\d{4}/g); | ||
| 911 | + if (!yearsMatch || yearsMatch.length === 0) return rangosStr; | ||
| 912 | + | ||
| 913 | + // Convert to numbers and sort | ||
| 914 | + const years = [...new Set(yearsMatch.map(y => parseInt(y)))].sort((a, b) => a - b); | ||
| 915 | + | ||
| 916 | + if (years.length === 1) return String(years[0]); | ||
| 917 | + | ||
| 918 | + // Group consecutive years into ranges | ||
| 919 | + const ranges = []; | ||
| 920 | + let rangeStart = years[0]; | ||
| 921 | + let rangeEnd = years[0]; | ||
| 922 | + | ||
| 923 | + for (let i = 1; i < years.length; i++) { | ||
| 924 | + if (years[i] === rangeEnd + 1) { | ||
| 925 | + // Consecutive year, extend range | ||
| 926 | + rangeEnd = years[i]; | ||
| 927 | + } else { | ||
| 928 | + // Gap found, save current range and start new one | ||
| 929 | + ranges.push(rangeStart === rangeEnd ? String(rangeStart) : `${rangeStart}-${rangeEnd}`); | ||
| 930 | + rangeStart = years[i]; | ||
| 931 | + rangeEnd = years[i]; | ||
| 932 | + } | ||
| 933 | + } | ||
| 934 | + // Add the last range | ||
| 935 | + ranges.push(rangeStart === rangeEnd ? String(rangeStart) : `${rangeStart}-${rangeEnd}`); | ||
| 936 | + | ||
| 937 | + return ranges.join(', '); | ||
| 938 | + } | ||
| 939 | + | ||
| 879 | function selectGrupo(grupo) { | 940 | function selectGrupo(grupo) { |
| 880 | selectedGrupo = grupo; | 941 | selectedGrupo = grupo; |
| 881 | selectedItem = null; | 942 | selectedItem = null; |
| ... | @@ -953,48 +1014,48 @@ | ... | @@ -953,48 +1014,48 @@ |
| 953 | 1014 | ||
| 954 | <svelte:window onclick={handleClickOutsideEntity} /> | 1015 | <svelte:window onclick={handleClickOutsideEntity} /> |
| 955 | 1016 | ||
| 956 | -<div class="min-h-screen" style="font-family: 'Instrument Sans', sans-serif; background-color: var(--theme-body); color: var(--theme-titulo); transition: background-color 0.2s, color 0.2s;"> | 1017 | +<div class="min-h-screen" style="font-family: var(--font-sans); background-color: var(--theme-body); color: var(--theme-titulo);"> |
| 957 | <!-- Header pedagógico --> | 1018 | <!-- Header pedagógico --> |
| 958 | <header class="border-b" style="border-color: var(--theme-borde); background-color: var(--theme-body);"> | 1019 | <header class="border-b" style="border-color: var(--theme-borde); background-color: var(--theme-body);"> |
| 959 | <div class="max-w-screen-xl mx-auto px-4 sm:px-6 {viewMode === 'mapa' ? 'py-3' : 'py-6'}"> | 1020 | <div class="max-w-screen-xl mx-auto px-4 sm:px-6 {viewMode === 'mapa' ? 'py-3' : 'py-6'}"> |
| 960 | <!-- Breadcrumb: responsive --> | 1021 | <!-- Breadcrumb: responsive --> |
| 961 | - <nav class="mb-4" style="font-family: 'DM Mono', monospace; font-size: 0.75rem;"> | 1022 | + <nav class="mb-4" style="font-family: var(--font-sans); font-variant-numeric: tabular-nums; font-size: 0.75rem;"> |
| 962 | <!-- Móvil: solo padre --> | 1023 | <!-- Móvil: solo padre --> |
| 963 | <a href="/clasificadores" class="sm:hidden transition-colors" style="color: var(--theme-texto);"> | 1024 | <a href="/clasificadores" class="sm:hidden transition-colors" style="color: var(--theme-texto);"> |
| 964 | ← Clasificadores | 1025 | ← Clasificadores |
| 965 | </a> | 1026 | </a> |
| 966 | <!-- Desktop: ruta completa --> | 1027 | <!-- Desktop: ruta completa --> |
| 967 | <div class="hidden sm:flex items-center gap-2" style="color: var(--theme-texto);"> | 1028 | <div class="hidden sm:flex items-center gap-2" style="color: var(--theme-texto);"> |
| 968 | - <a href="/" class="transition-colors hover:opacity-80">Inicio</a> | 1029 | + <a href="/" data-sveltekit-preload-data="off" class="hover:opacity-100" style="opacity: 0.7;">Inicio</a> |
| 969 | <span style="opacity: 0.5;">/</span> | 1030 | <span style="opacity: 0.5;">/</span> |
| 970 | - <a href="/clasificadores" class="transition-colors hover:opacity-80">Clasificadores</a> | 1031 | + <a href="/clasificadores" data-sveltekit-preload-data="off" class="hover:opacity-100" style="opacity: 0.7;">Clasificadores</a> |
| 971 | </div> | 1032 | </div> |
| 972 | </nav> | 1033 | </nav> |
| 973 | 1034 | ||
| 974 | <div class="max-w-3xl"> | 1035 | <div class="max-w-3xl"> |
| 975 | - <p class="text-xs uppercase tracking-widest mb-2" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);"> | 1036 | + <p class="text-sm uppercase tracking-widest mb-2 font-semibold" style="font-family: var(--font-sans); color: var(--theme-accent);"> |
| 976 | - Clasificador 03 | 1037 | + Clasificador de Objeto del Gasto |
| 977 | </p> | 1038 | </p> |
| 978 | - <h1 class="{viewMode === 'mapa' ? 'text-2xl mb-2' : 'text-3xl mb-3'}" style="font-family: 'DM Serif Display', serif; color: var(--theme-titulo);"> | 1039 | + <h1 class="{viewMode === 'mapa' ? 'text-2xl mb-2' : 'text-3xl mb-3'}" style="font-family: var(--font-display); color: var(--theme-titulo);"> |
| 979 | ¿En qué se gasta? | 1040 | ¿En qué se gasta? |
| 980 | </h1> | 1041 | </h1> |
| 981 | 1042 | ||
| 982 | {#if viewMode !== 'mapa'} | 1043 | {#if viewMode !== 'mapa'} |
| 983 | - <p class="leading-relaxed mb-3" style="color: var(--theme-texto);"> | 1044 | + <p class="text-lg leading-relaxed mb-4" style="color: var(--theme-texto);"> |
| 984 | Ordena el gasto según <strong style="color: var(--theme-titulo);">qué se compra o paga</strong>: | 1045 | Ordena el gasto según <strong style="color: var(--theme-titulo);">qué se compra o paga</strong>: |
| 985 | sueldos, alquileres, combustible, medicamentos, construcciones, deudas, transferencias. | 1046 | sueldos, alquileres, combustible, medicamentos, construcciones, deudas, transferencias. |
| 986 | Es la forma más directa de entender en qué se usa el dinero público. | 1047 | Es la forma más directa de entender en qué se usa el dinero público. |
| 987 | </p> | 1048 | </p> |
| 988 | 1049 | ||
| 989 | {#if totalItems > 0} | 1050 | {#if totalItems > 0} |
| 990 | - <p class="text-sm mb-5" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);"> | 1051 | + <p class="text-base mb-5" style="font-family: var(--font-sans); font-variant-numeric: tabular-nums; color: var(--theme-texto);"> |
| 991 | - <span style="color: var(--theme-titulo); font-weight: 500;">{totalItems}</span> categorías de gasto: | 1052 | + <span style="color: var(--theme-accent); font-weight: 600;">{totalItems}</span> categorías de gasto: |
| 992 | {countByNivel.grupos} grupos, {countByNivel.subgrupos} subgrupos, {countByNivel.partidas} partidas, {countByNivel.subpartidas} subpartidas | 1053 | {countByNivel.grupos} grupos, {countByNivel.subgrupos} subgrupos, {countByNivel.partidas} partidas, {countByNivel.subpartidas} subpartidas |
| 993 | </p> | 1054 | </p> |
| 994 | {/if} | 1055 | {/if} |
| 995 | 1056 | ||
| 996 | <!-- Jerarquía --> | 1057 | <!-- Jerarquía --> |
| 997 | - <div class="hidden sm:flex flex-wrap items-center gap-2 sm:gap-3 text-xs mb-6" style="font-family: 'DM Mono', monospace;"> | 1058 | + <div class="hidden sm:flex flex-wrap items-center gap-2 sm:gap-3 text-sm mb-6" style="font-family: var(--font-sans); font-variant-numeric: tabular-nums;"> |
| 998 | <span class="px-2.5 py-1 rounded-full font-medium" style="background-color: var(--theme-accent); color: var(--theme-body); opacity: 0.9;"> | 1059 | <span class="px-2.5 py-1 rounded-full font-medium" style="background-color: var(--theme-accent); color: var(--theme-body); opacity: 0.9;"> |
| 999 | Grupo | 1060 | Grupo |
| 1000 | </span> | 1061 | </span> |
| ... | @@ -1012,7 +1073,7 @@ | ... | @@ -1012,7 +1073,7 @@ |
| 1012 | </span> | 1073 | </span> |
| 1013 | </div> | 1074 | </div> |
| 1014 | <!-- Versión móvil simplificada --> | 1075 | <!-- Versión móvil simplificada --> |
| 1015 | - <p class="sm:hidden text-sm mb-4" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);"> | 1076 | + <p class="sm:hidden text-sm mb-4" style="font-family: var(--font-sans); font-variant-numeric: tabular-nums; color: var(--theme-texto);"> |
| 1016 | Jerarquía: Grupo → Subgrupo → Partida → Subpartida | 1077 | Jerarquía: Grupo → Subgrupo → Partida → Subpartida |
| 1017 | </p> | 1078 | </p> |
| 1018 | {/if} | 1079 | {/if} |
| ... | @@ -1085,12 +1146,40 @@ | ... | @@ -1085,12 +1146,40 @@ |
| 1085 | </div> | 1146 | </div> |
| 1086 | </div> | 1147 | </div> |
| 1087 | 1148 | ||
| 1088 | - <!-- Filtros (solo en modo mapa) --> | 1149 | + <!-- Buscador + Filtros --> |
| 1089 | - {#if viewMode === 'mapa'} | ||
| 1090 | <div class="flex flex-wrap items-center gap-3 flex-shrink-0"> | 1150 | <div class="flex flex-wrap items-center gap-3 flex-shrink-0"> |
| 1151 | + <!-- Buscador inline (estilo similar al toggle) --> | ||
| 1152 | + <div class="inline-flex items-center rounded-xl border-2 p-1 shadow-sm" style="border-color: var(--theme-titulo); background-color: var(--theme-surface);"> | ||
| 1153 | + <div class="flex items-center px-3" style="color: var(--theme-texto);"> | ||
| 1154 | + <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | ||
| 1155 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /> | ||
| 1156 | + </svg> | ||
| 1157 | + </div> | ||
| 1158 | + <input | ||
| 1159 | + type="text" | ||
| 1160 | + bind:value={searchQuery} | ||
| 1161 | + placeholder="Buscar código o nombre..." | ||
| 1162 | + class="px-2 py-1.5 text-sm font-medium bg-transparent focus:outline-none w-48 sm:w-56" | ||
| 1163 | + style="color: var(--theme-titulo);" | ||
| 1164 | + /> | ||
| 1165 | + {#if searchQuery} | ||
| 1166 | + <button | ||
| 1167 | + class="px-2 py-1.5 rounded-lg transition-all hover:bg-black/5" | ||
| 1168 | + onclick={() => searchQuery = ''} | ||
| 1169 | + style="color: var(--theme-texto);" | ||
| 1170 | + title="Limpiar búsqueda" | ||
| 1171 | + > | ||
| 1172 | + <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | ||
| 1173 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> | ||
| 1174 | + </svg> | ||
| 1175 | + </button> | ||
| 1176 | + {/if} | ||
| 1177 | + </div> | ||
| 1178 | + | ||
| 1179 | + {#if viewMode === 'mapa'} | ||
| 1091 | <!-- Selector de nivel --> | 1180 | <!-- Selector de nivel --> |
| 1092 | <div class="flex items-center gap-2 level-dropdown-container"> | 1181 | <div class="flex items-center gap-2 level-dropdown-container"> |
| 1093 | - <label class="text-xs uppercase tracking-wide" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);">Vista</label> | 1182 | + <label class="text-xs uppercase tracking-wide" style="font-family: var(--font-sans); font-variant-numeric: tabular-nums; color: var(--theme-texto);">Vista</label> |
| 1094 | <div class="relative"> | 1183 | <div class="relative"> |
| 1095 | <button | 1184 | <button |
| 1096 | class="dropdown-control px-3 py-1.5 text-sm rounded-lg focus:outline-none min-w-[140px] text-left flex items-center justify-between gap-2" | 1185 | class="dropdown-control px-3 py-1.5 text-sm rounded-lg focus:outline-none min-w-[140px] text-left flex items-center justify-between gap-2" |
| ... | @@ -1119,7 +1208,7 @@ | ... | @@ -1119,7 +1208,7 @@ |
| 1119 | </div> | 1208 | </div> |
| 1120 | 1209 | ||
| 1121 | <div class="flex items-center gap-2 year-dropdown-container"> | 1210 | <div class="flex items-center gap-2 year-dropdown-container"> |
| 1122 | - <label class="text-xs uppercase tracking-wide" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);">Año</label> | 1211 | + <label class="text-xs uppercase tracking-wide" style="font-family: var(--font-sans); font-variant-numeric: tabular-nums; color: var(--theme-texto);">Año</label> |
| 1123 | <div class="relative"> | 1212 | <div class="relative"> |
| 1124 | <button | 1213 | <button |
| 1125 | class="dropdown-control px-3 py-1.5 text-sm rounded-lg focus:outline-none min-w-[80px] text-left flex items-center justify-between gap-2" | 1214 | class="dropdown-control px-3 py-1.5 text-sm rounded-lg focus:outline-none min-w-[80px] text-left flex items-center justify-between gap-2" |
| ... | @@ -1147,7 +1236,7 @@ | ... | @@ -1147,7 +1236,7 @@ |
| 1147 | </div> | 1236 | </div> |
| 1148 | 1237 | ||
| 1149 | <div class="flex items-center gap-2 entity-dropdown-container"> | 1238 | <div class="flex items-center gap-2 entity-dropdown-container"> |
| 1150 | - <label class="text-xs uppercase tracking-wide" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);">Entidad</label> | 1239 | + <label class="text-xs uppercase tracking-wide" style="font-family: var(--font-sans); font-variant-numeric: tabular-nums; color: var(--theme-texto);">Entidad</label> |
| 1151 | <div class="relative"> | 1240 | <div class="relative"> |
| 1152 | <button | 1241 | <button |
| 1153 | class="dropdown-control px-3 py-1.5 text-sm rounded-lg focus:outline-none min-w-[180px] text-left flex items-center justify-between gap-2" | 1242 | class="dropdown-control px-3 py-1.5 text-sm rounded-lg focus:outline-none min-w-[180px] text-left flex items-center justify-between gap-2" |
| ... | @@ -1210,9 +1299,9 @@ | ... | @@ -1210,9 +1299,9 @@ |
| 1210 | {/if} | 1299 | {/if} |
| 1211 | </div> | 1300 | </div> |
| 1212 | </div> | 1301 | </div> |
| 1213 | - </div> | ||
| 1214 | {/if} | 1302 | {/if} |
| 1215 | </div> | 1303 | </div> |
| 1304 | + </div> | ||
| 1216 | 1305 | ||
| 1217 | <!-- Guía móvil (solo visible en móvil, no en comparar) --> | 1306 | <!-- Guía móvil (solo visible en móvil, no en comparar) --> |
| 1218 | {#if viewMode !== 'comparar'} | 1307 | {#if viewMode !== 'comparar'} |
| ... | @@ -1275,7 +1364,7 @@ | ... | @@ -1275,7 +1364,7 @@ |
| 1275 | </button> | 1364 | </button> |
| 1276 | </div> | 1365 | </div> |
| 1277 | 1366 | ||
| 1278 | - <div class="max-w-screen-xl mx-auto flex px-4"> | 1367 | + <div class="max-w-screen-xl mx-auto flex px-4 lg:px-6"> |
| 1279 | <!-- Sidebar izquierda: Grupos --> | 1368 | <!-- Sidebar izquierda: Grupos --> |
| 1280 | <!-- En móvil: overlay, en desktop: sidebar fijo --> | 1369 | <!-- En móvil: overlay, en desktop: sidebar fijo --> |
| 1281 | {#if sidebarOpen} | 1370 | {#if sidebarOpen} |
| ... | @@ -1286,7 +1375,7 @@ | ... | @@ -1286,7 +1375,7 @@ |
| 1286 | lg:translate-x-0 | 1375 | lg:translate-x-0 |
| 1287 | fixed lg:relative | 1376 | fixed lg:relative |
| 1288 | inset-y-0 left-0 | 1377 | inset-y-0 left-0 |
| 1289 | - w-72 lg:w-64 | 1378 | + w-80 lg:w-72 xl:w-80 |
| 1290 | z-50 lg:z-auto | 1379 | z-50 lg:z-auto |
| 1291 | transition-transform duration-200 ease-in-out | 1380 | transition-transform duration-200 ease-in-out |
| 1292 | lg:flex-shrink-0 | 1381 | lg:flex-shrink-0 |
| ... | @@ -1304,32 +1393,21 @@ | ... | @@ -1304,32 +1393,21 @@ |
| 1304 | </button> | 1393 | </button> |
| 1305 | </div> | 1394 | </div> |
| 1306 | 1395 | ||
| 1307 | - <!-- Buscador --> | 1396 | + <!-- Resultados de búsqueda (el buscador está en el header) --> |
| 1308 | - <div class="mb-6"> | ||
| 1309 | - <input | ||
| 1310 | - type="text" | ||
| 1311 | - bind:value={searchQuery} | ||
| 1312 | - placeholder="Buscar..." | ||
| 1313 | - class="w-full px-3 py-2 text-sm rounded-md focus:outline-none focus:ring-2" | ||
| 1314 | - style="border: 1px solid var(--theme-borde); background-color: var(--theme-surface); color: var(--theme-titulo);" | ||
| 1315 | - /> | ||
| 1316 | - </div> | ||
| 1317 | - | ||
| 1318 | - <!-- Resultados de búsqueda --> | ||
| 1319 | {#if isSearching} | 1397 | {#if isSearching} |
| 1320 | <div class="mb-4"> | 1398 | <div class="mb-4"> |
| 1321 | - <p class="text-xs uppercase tracking-wide mb-2" style="color: var(--theme-texto);"> | 1399 | + <p class="text-sm uppercase tracking-wide mb-2" style="color: var(--theme-texto);"> |
| 1322 | {searchResults.length} resultados | 1400 | {searchResults.length} resultados |
| 1323 | </p> | 1401 | </p> |
| 1324 | - <div class="space-y-1"> | 1402 | + <div class="space-y-1.5"> |
| 1325 | {#each searchResults as result} | 1403 | {#each searchResults as result} |
| 1326 | <button | 1404 | <button |
| 1327 | - class="search-result-item w-full text-left px-3 py-3 text-sm rounded-lg transition-all cursor-pointer" | 1405 | + class="search-result-item w-full text-left px-3 py-3 text-base rounded-lg transition-all cursor-pointer" |
| 1328 | onclick={() => goToSearchResult(result)} | 1406 | onclick={() => goToSearchResult(result)} |
| 1329 | > | 1407 | > |
| 1330 | - <span class="text-xs block mb-0.5" style="color: var(--theme-texto);">{getNivelLabel(result.nivel)}</span> | 1408 | + <span class="text-sm block mb-0.5" style="color: var(--theme-texto);">{getNivelLabel(result.nivel)}</span> |
| 1331 | <div class="flex items-baseline gap-2"> | 1409 | <div class="flex items-baseline gap-2"> |
| 1332 | - <span class="font-mono text-xs font-medium" style="color: var(--theme-accent);">{result.objeto}</span> | 1410 | + <span class="font-mono text-sm font-medium" style="color: var(--theme-accent);">{result.objeto}</span> |
| 1333 | <span class="flex-1" style="color: var(--theme-titulo);">{result.desc_objeto}</span> | 1411 | <span class="flex-1" style="color: var(--theme-titulo);">{result.desc_objeto}</span> |
| 1334 | <svg class="w-4 h-4 flex-shrink-0 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | 1412 | <svg class="w-4 h-4 flex-shrink-0 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 1335 | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /> | 1413 | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /> |
| ... | @@ -1345,19 +1423,19 @@ | ... | @@ -1345,19 +1423,19 @@ |
| 1345 | {:else} | 1423 | {:else} |
| 1346 | <!-- Lista de grupos --> | 1424 | <!-- Lista de grupos --> |
| 1347 | <nav> | 1425 | <nav> |
| 1348 | - <p class="text-xs uppercase tracking-wide mb-3 px-2 hidden lg:block" style="color: var(--theme-texto);">Grupos</p> | 1426 | + <p class="text-base uppercase tracking-wide mb-4 px-2 hidden lg:block font-medium" style="color: var(--theme-texto);">Índice</p> |
| 1349 | <ul class="space-y-1"> | 1427 | <ul class="space-y-1"> |
| 1350 | {#each grupos as grupo} | 1428 | {#each grupos as grupo} |
| 1351 | <li> | 1429 | <li> |
| 1352 | <button | 1430 | <button |
| 1353 | - class="w-full text-left px-3 py-2 rounded-md text-sm transition-all" | 1431 | + class="w-full text-left px-3 py-3 rounded-md text-base transition-all" |
| 1354 | style="{selectedGrupo?.objeto === grupo.objeto | 1432 | style="{selectedGrupo?.objeto === grupo.objeto |
| 1355 | - ? `background-color: color-mix(in srgb, var(--theme-accent) 15%, transparent); color: var(--theme-titulo); font-weight: 500; border-left: 2px solid var(--theme-accent);` | 1433 | + ? `background-color: color-mix(in srgb, var(--theme-accent) 15%, transparent); color: var(--theme-titulo); font-weight: 500; border-left: 3px solid var(--theme-accent);` |
| 1356 | : `color: var(--theme-texto);`}" | 1434 | : `color: var(--theme-texto);`}" |
| 1357 | onclick={() => selectGrupo(grupo)} | 1435 | onclick={() => selectGrupo(grupo)} |
| 1358 | > | 1436 | > |
| 1359 | - <span class="font-mono text-xs block" style="color: var(--theme-texto);">{grupo.objeto}</span> | 1437 | + <span class="font-mono text-base block mb-0.5" style="color: var(--theme-accent);">{grupo.objeto}</span> |
| 1360 | - {grupo.desc_objeto} | 1438 | + <span class="leading-snug">{grupo.desc_objeto}</span> |
| 1361 | </button> | 1439 | </button> |
| 1362 | </li> | 1440 | </li> |
| 1363 | {/each} | 1441 | {/each} |
| ... | @@ -1373,14 +1451,15 @@ | ... | @@ -1373,14 +1451,15 @@ |
| 1373 | {#if selectedGrupo} | 1451 | {#if selectedGrupo} |
| 1374 | <!-- Título del grupo --> | 1452 | <!-- Título del grupo --> |
| 1375 | <div class="mb-8 lg:mb-10 pb-6 lg:pb-8 border-b" style="border-color: var(--theme-borde);"> | 1453 | <div class="mb-8 lg:mb-10 pb-6 lg:pb-8 border-b" style="border-color: var(--theme-borde);"> |
| 1376 | - <p class="text-sm font-mono mb-1" style="color: var(--theme-texto);">{selectedGrupo.objeto}</p> | 1454 | + <p class="text-base font-mono mb-1" style="color: var(--theme-texto);">{selectedGrupo.objeto}</p> |
| 1377 | - <h2 class="text-lg sm:text-xl font-medium mb-2 flex items-center gap-2 sm:gap-3 flex-wrap" style="color: var(--theme-titulo);"> | 1455 | + <h2 class="text-xl sm:text-2xl font-semibold mb-2 flex items-center gap-2 sm:gap-3 flex-wrap" style="color: var(--theme-titulo);"> |
| 1378 | {selectedGrupo.desc_objeto} | 1456 | {selectedGrupo.desc_objeto} |
| 1379 | <a | 1457 | <a |
| 1380 | href="/objeto/{selectedGrupo.objeto}" | 1458 | href="/objeto/{selectedGrupo.objeto}" |
| 1381 | class="transition-colors hover:text-[var(--theme-accent)]" | 1459 | class="transition-colors hover:text-[var(--theme-accent)]" |
| 1382 | style="color: var(--theme-texto);" | 1460 | style="color: var(--theme-texto);" |
| 1383 | title="Ver detalle" | 1461 | title="Ver detalle" |
| 1462 | + onclick={(e) => { if (isEmbed) { e.preventDefault(); navigateToObjeto(selectedGrupo.objeto); }}} | ||
| 1384 | > | 1463 | > |
| 1385 | <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | 1464 | <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 1386 | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> | 1465 | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> |
| ... | @@ -1412,14 +1491,14 @@ | ... | @@ -1412,14 +1491,14 @@ |
| 1412 | class="scroll-mt-4" | 1491 | class="scroll-mt-4" |
| 1413 | style="{highlightedItem === subgrupo.objeto ? `box-shadow: 0 0 0 2px color-mix(in srgb, var(--theme-accent) 30%, transparent); border-radius: 0.5rem;` : ''}" | 1492 | style="{highlightedItem === subgrupo.objeto ? `box-shadow: 0 0 0 2px color-mix(in srgb, var(--theme-accent) 30%, transparent); border-radius: 0.5rem;` : ''}" |
| 1414 | > | 1493 | > |
| 1415 | - <div class="flex items-start gap-2 sm:gap-4 mb-3"> | 1494 | + <div class="flex items-start gap-3 sm:gap-4 mb-3"> |
| 1416 | - <span class="font-mono text-xs sm:text-sm pt-1" style="color: var(--theme-texto);">{subgrupo.objeto}</span> | 1495 | + <span class="font-mono text-base pt-0.5" style="color: var(--theme-accent); font-weight: 500;">{subgrupo.objeto}</span> |
| 1417 | <div class="flex-1"> | 1496 | <div class="flex-1"> |
| 1418 | - <h3 class="text-base sm:text-lg font-medium flex items-center gap-2 flex-wrap" style="color: var(--theme-titulo);"> | 1497 | + <h3 class="text-lg font-semibold flex items-center gap-2 flex-wrap" style="color: var(--theme-titulo);"> |
| 1419 | <span class="text-left">{subgrupo.desc_objeto}</span> | 1498 | <span class="text-left">{subgrupo.desc_objeto}</span> |
| 1420 | {#if subgrupo.n_variaciones > 1} | 1499 | {#if subgrupo.n_variaciones > 1} |
| 1421 | <button | 1500 | <button |
| 1422 | - class="text-xs font-normal text-orange-500 hover:text-orange-700 transition-colors underline decoration-dotted decoration-orange-300 hover:decoration-orange-500 cursor-pointer" | 1501 | + class="text-sm font-normal text-orange-500 hover:text-orange-700 transition-colors underline decoration-dotted decoration-orange-300 hover:decoration-orange-500 cursor-pointer" |
| 1423 | onclick={() => openDetail(subgrupo)} | 1502 | onclick={() => openDetail(subgrupo)} |
| 1424 | title="Ver variaciones de descripción" | 1503 | title="Ver variaciones de descripción" |
| 1425 | > | 1504 | > |
| ... | @@ -1431,6 +1510,7 @@ | ... | @@ -1431,6 +1510,7 @@ |
| 1431 | class="transition-colors hover:text-[var(--theme-accent)]" | 1510 | class="transition-colors hover:text-[var(--theme-accent)]" |
| 1432 | style="color: var(--theme-texto);" | 1511 | style="color: var(--theme-texto);" |
| 1433 | title="Ver página de detalle" | 1512 | title="Ver página de detalle" |
| 1513 | + onclick={(e) => { if (isEmbed) { e.preventDefault(); navigateToObjeto(subgrupo.objeto); }}} | ||
| 1434 | > | 1514 | > |
| 1435 | <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | 1515 | <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 1436 | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> | 1516 | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> |
| ... | @@ -1438,7 +1518,7 @@ | ... | @@ -1438,7 +1518,7 @@ |
| 1438 | </a> | 1518 | </a> |
| 1439 | </h3> | 1519 | </h3> |
| 1440 | {#if subgrupoDescs.length > 0} | 1520 | {#if subgrupoDescs.length > 0} |
| 1441 | - <p class="text-sm mt-1 leading-relaxed" style="color: var(--theme-texto);">{subgrupoDescs[0].descripcion}</p> | 1521 | + <p class="text-base mt-1 leading-relaxed" style="color: var(--theme-texto);">{subgrupoDescs[0].descripcion}</p> |
| 1442 | {/if} | 1522 | {/if} |
| 1443 | </div> | 1523 | </div> |
| 1444 | </div> | 1524 | </div> |
| ... | @@ -1453,10 +1533,10 @@ | ... | @@ -1453,10 +1533,10 @@ |
| 1453 | class="scroll-mt-4" | 1533 | class="scroll-mt-4" |
| 1454 | style="{highlightedItem === partida.objeto ? `box-shadow: 0 0 0 2px color-mix(in srgb, var(--theme-accent) 30%, transparent); border-radius: 0.5rem; padding: 0.5rem; margin-left: -0.5rem;` : ''}" | 1534 | style="{highlightedItem === partida.objeto ? `box-shadow: 0 0 0 2px color-mix(in srgb, var(--theme-accent) 30%, transparent); border-radius: 0.5rem; padding: 0.5rem; margin-left: -0.5rem;` : ''}" |
| 1455 | > | 1535 | > |
| 1456 | - <div class="flex items-start gap-2 sm:gap-3"> | 1536 | + <div class="flex items-start gap-3 sm:gap-4"> |
| 1457 | - <span class="font-mono text-xs pt-0.5 shrink-0" style="color: var(--theme-texto); {partida._sintetica ? 'opacity: 0.5;' : ''}">{partida.objeto}</span> | 1537 | + <span class="font-mono text-base pt-0.5 shrink-0" style="color: var(--theme-texto); {partida._sintetica ? 'opacity: 0.5;' : ''}">{partida.objeto}</span> |
| 1458 | <div class="flex-1 min-w-0"> | 1538 | <div class="flex-1 min-w-0"> |
| 1459 | - <h4 class="text-sm font-medium flex items-center gap-2 flex-wrap" style="color: var(--theme-titulo);"> | 1539 | + <h4 class="text-base font-medium flex items-center gap-2 flex-wrap" style="color: var(--theme-titulo);"> |
| 1460 | {#if partida._sintetica} | 1540 | {#if partida._sintetica} |
| 1461 | <span class="text-left opacity-60 italic">(Partida inferida)</span> | 1541 | <span class="text-left opacity-60 italic">(Partida inferida)</span> |
| 1462 | {:else} | 1542 | {:else} |
| ... | @@ -1464,7 +1544,7 @@ | ... | @@ -1464,7 +1544,7 @@ |
| 1464 | {/if} | 1544 | {/if} |
| 1465 | {#if partida.n_variaciones > 1} | 1545 | {#if partida.n_variaciones > 1} |
| 1466 | <button | 1546 | <button |
| 1467 | - class="text-xs font-normal text-orange-500 hover:text-orange-700 transition-colors underline decoration-dotted decoration-orange-300 hover:decoration-orange-500 cursor-pointer" | 1547 | + class="text-sm font-normal text-orange-500 hover:text-orange-700 transition-colors underline decoration-dotted decoration-orange-300 hover:decoration-orange-500 cursor-pointer" |
| 1468 | onclick={() => openDetail(partida)} | 1548 | onclick={() => openDetail(partida)} |
| 1469 | title="Ver variaciones de descripción" | 1549 | title="Ver variaciones de descripción" |
| 1470 | > | 1550 | > |
| ... | @@ -1477,15 +1557,16 @@ | ... | @@ -1477,15 +1557,16 @@ |
| 1477 | class="transition-colors hover:text-[var(--theme-accent)]" | 1557 | class="transition-colors hover:text-[var(--theme-accent)]" |
| 1478 | style="color: var(--theme-texto);" | 1558 | style="color: var(--theme-texto);" |
| 1479 | title="Ver página de detalle" | 1559 | title="Ver página de detalle" |
| 1560 | + onclick={(e) => { if (isEmbed) { e.preventDefault(); navigateToObjeto(partida.objeto); }}} | ||
| 1480 | > | 1561 | > |
| 1481 | - <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | 1562 | + <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 1482 | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> | 1563 | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> |
| 1483 | </svg> | 1564 | </svg> |
| 1484 | </a> | 1565 | </a> |
| 1485 | {/if} | 1566 | {/if} |
| 1486 | </h4> | 1567 | </h4> |
| 1487 | {#if partidaDescs.length > 0 && !partida._sintetica} | 1568 | {#if partidaDescs.length > 0 && !partida._sintetica} |
| 1488 | - <p class="text-sm mt-1 leading-relaxed" style="color: var(--theme-texto);">{partidaDescs[0].descripcion}</p> | 1569 | + <p class="text-base mt-1 leading-relaxed" style="color: var(--theme-texto);">{partidaDescs[0].descripcion}</p> |
| 1489 | {/if} | 1570 | {/if} |
| 1490 | </div> | 1571 | </div> |
| 1491 | </div> | 1572 | </div> |
| ... | @@ -1500,14 +1581,14 @@ | ... | @@ -1500,14 +1581,14 @@ |
| 1500 | class="scroll-mt-4" | 1581 | class="scroll-mt-4" |
| 1501 | style="{highlightedItem === subpartida.objeto ? `box-shadow: 0 0 0 2px color-mix(in srgb, var(--theme-accent) 30%, transparent); border-radius: 0.25rem; padding: 0.25rem; margin-left: -0.25rem;` : ''}" | 1582 | style="{highlightedItem === subpartida.objeto ? `box-shadow: 0 0 0 2px color-mix(in srgb, var(--theme-accent) 30%, transparent); border-radius: 0.25rem; padding: 0.25rem; margin-left: -0.25rem;` : ''}" |
| 1502 | > | 1583 | > |
| 1503 | - <div class="flex items-start gap-2"> | 1584 | + <div class="flex items-start gap-3"> |
| 1504 | - <span class="font-mono text-xs" style="color: var(--theme-texto);">{subpartida.objeto}</span> | 1585 | + <span class="font-mono text-base" style="color: var(--theme-texto);">{subpartida.objeto}</span> |
| 1505 | <div class="flex-1"> | 1586 | <div class="flex-1"> |
| 1506 | <span class="flex items-center gap-2"> | 1587 | <span class="flex items-center gap-2"> |
| 1507 | - <span class="text-sm" style="color: var(--theme-titulo);">{subpartida.desc_objeto}</span> | 1588 | + <span class="text-base" style="color: var(--theme-titulo);">{subpartida.desc_objeto}</span> |
| 1508 | {#if subpartida.n_variaciones > 1} | 1589 | {#if subpartida.n_variaciones > 1} |
| 1509 | <button | 1590 | <button |
| 1510 | - class="text-xs text-orange-500 hover:text-orange-700 transition-colors underline decoration-dotted decoration-orange-300 hover:decoration-orange-500 cursor-pointer" | 1591 | + class="text-sm text-orange-500 hover:text-orange-700 transition-colors underline decoration-dotted decoration-orange-300 hover:decoration-orange-500 cursor-pointer" |
| 1511 | onclick={() => openDetail(subpartida)} | 1592 | onclick={() => openDetail(subpartida)} |
| 1512 | title="Ver variaciones de descripción" | 1593 | title="Ver variaciones de descripción" |
| 1513 | > | 1594 | > |
| ... | @@ -1519,14 +1600,15 @@ | ... | @@ -1519,14 +1600,15 @@ |
| 1519 | class="transition-colors hover:text-[var(--theme-accent)]" | 1600 | class="transition-colors hover:text-[var(--theme-accent)]" |
| 1520 | style="color: var(--theme-texto);" | 1601 | style="color: var(--theme-texto);" |
| 1521 | title="Ver página de detalle" | 1602 | title="Ver página de detalle" |
| 1603 | + onclick={(e) => { if (isEmbed) { e.preventDefault(); navigateToObjeto(subpartida.objeto); }}} | ||
| 1522 | > | 1604 | > |
| 1523 | - <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | 1605 | + <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 1524 | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> | 1606 | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> |
| 1525 | </svg> | 1607 | </svg> |
| 1526 | </a> | 1608 | </a> |
| 1527 | </span> | 1609 | </span> |
| 1528 | {#if subpartidaDescs.length > 0} | 1610 | {#if subpartidaDescs.length > 0} |
| 1529 | - <p class="text-sm mt-1" style="color: var(--theme-texto);">{subpartidaDescs[0].descripcion}</p> | 1611 | + <p class="text-base mt-1" style="color: var(--theme-texto);">{subpartidaDescs[0].descripcion}</p> |
| 1530 | {/if} | 1612 | {/if} |
| 1531 | </div> | 1613 | </div> |
| 1532 | </div> | 1614 | </div> |
| ... | @@ -1545,36 +1627,36 @@ | ... | @@ -1545,36 +1627,36 @@ |
| 1545 | </div> | 1627 | </div> |
| 1546 | </main> | 1628 | </main> |
| 1547 | 1629 | ||
| 1548 | - <!-- Sidebar derecha: En esta página --> | 1630 | + <!-- Sidebar derecha: Navegación rápida --> |
| 1549 | - <aside class="w-48 flex-shrink-0 hidden xl:block"> | 1631 | + <aside class="w-56 flex-shrink-0 hidden xl:block"> |
| 1550 | - <div class="sticky top-0 h-screen overflow-y-auto py-8 pl-8 border-l" style="border-color: var(--theme-borde);"> | 1632 | + <div class="sticky top-0 h-screen overflow-y-auto py-8 pl-6 border-l" style="border-color: var(--theme-borde);"> |
| 1551 | - <p class="text-xs uppercase tracking-wide mb-3" style="color: var(--theme-texto);">En esta página</p> | 1633 | + <p class="text-sm uppercase tracking-wide mb-4 font-medium" style="color: var(--theme-texto);">Navegación</p> |
| 1552 | {#if selectedGrupo && !isSearching} | 1634 | {#if selectedGrupo && !isSearching} |
| 1553 | - <nav class="space-y-2"> | 1635 | + <nav class="space-y-3"> |
| 1554 | {#each grupoContent.subgrupos as subgrupo} | 1636 | {#each grupoContent.subgrupos as subgrupo} |
| 1555 | <div> | 1637 | <div> |
| 1556 | <a | 1638 | <a |
| 1557 | href="#sg-{subgrupo.objeto}" | 1639 | href="#sg-{subgrupo.objeto}" |
| 1558 | - class="block text-sm truncate transition-colors hover:opacity-80" | 1640 | + class="block text-base truncate transition-colors hover:text-[var(--theme-accent)]" |
| 1559 | style="color: var(--theme-titulo);" | 1641 | style="color: var(--theme-titulo);" |
| 1560 | title="{subgrupo.desc_objeto}" | 1642 | title="{subgrupo.desc_objeto}" |
| 1561 | > | 1643 | > |
| 1562 | {subgrupo.desc_objeto} | 1644 | {subgrupo.desc_objeto} |
| 1563 | </a> | 1645 | </a> |
| 1564 | {#if subgrupo.partidas?.length > 0} | 1646 | {#if subgrupo.partidas?.length > 0} |
| 1565 | - <div class="ml-3 mt-1 space-y-1 border-l pl-2" style="border-color: color-mix(in srgb, var(--theme-borde) 50%, transparent);"> | 1647 | + <div class="ml-3 mt-2 space-y-1.5 border-l pl-3" style="border-color: color-mix(in srgb, var(--theme-borde) 50%, transparent);"> |
| 1566 | - {#each subgrupo.partidas.slice(0, 5) as partida} | 1648 | + {#each subgrupo.partidas.slice(0, 4) as partida} |
| 1567 | <a | 1649 | <a |
| 1568 | href="#p-{partida.objeto}" | 1650 | href="#p-{partida.objeto}" |
| 1569 | - class="block text-xs truncate transition-colors hover:opacity-80" | 1651 | + class="block text-sm truncate transition-colors hover:text-[var(--theme-accent)]" |
| 1570 | style="color: var(--theme-texto);" | 1652 | style="color: var(--theme-texto);" |
| 1571 | title="{partida.desc_objeto}" | 1653 | title="{partida.desc_objeto}" |
| 1572 | > | 1654 | > |
| 1573 | {partida.desc_objeto} | 1655 | {partida.desc_objeto} |
| 1574 | </a> | 1656 | </a> |
| 1575 | {/each} | 1657 | {/each} |
| 1576 | - {#if subgrupo.partidas.length > 5} | 1658 | + {#if subgrupo.partidas.length > 4} |
| 1577 | - <span class="text-xs" style="color: var(--theme-texto);">+{subgrupo.partidas.length - 5} más</span> | 1659 | + <span class="text-sm" style="color: var(--theme-texto); opacity: 0.7;">+{subgrupo.partidas.length - 4} más</span> |
| 1578 | {/if} | 1660 | {/if} |
| 1579 | </div> | 1661 | </div> |
| 1580 | {/if} | 1662 | {/if} |
| ... | @@ -1597,7 +1679,7 @@ | ... | @@ -1597,7 +1679,7 @@ |
| 1597 | <div class="flex items-center justify-between mb-2"> | 1679 | <div class="flex items-center justify-between mb-2"> |
| 1598 | <!-- Breadcrumb de navegación (solo en modo jerárquico) --> | 1680 | <!-- Breadcrumb de navegación (solo en modo jerárquico) --> |
| 1599 | {#if treemapViewLevel === 'jerarquico' && treemapBreadcrumb.length > 0} | 1681 | {#if treemapViewLevel === 'jerarquico' && treemapBreadcrumb.length > 0} |
| 1600 | - <nav class="flex items-center gap-2 flex-wrap" style="font-family: 'DM Mono', monospace; font-size: 0.75rem;"> | 1682 | + <nav class="flex items-center gap-2 flex-wrap" style="font-family: var(--font-sans); font-variant-numeric: tabular-nums; font-size: 0.75rem;"> |
| 1601 | {#each treemapBreadcrumb as crumb, i} | 1683 | {#each treemapBreadcrumb as crumb, i} |
| 1602 | {#if i > 0} | 1684 | {#if i > 0} |
| 1603 | <span style="color: var(--theme-texto); opacity: 0.5;">›</span> | 1685 | <span style="color: var(--theme-texto); opacity: 0.5;">›</span> |
| ... | @@ -1615,7 +1697,7 @@ | ... | @@ -1615,7 +1697,7 @@ |
| 1615 | </nav> | 1697 | </nav> |
| 1616 | {:else if treemapViewLevel !== 'jerarquico'} | 1698 | {:else if treemapViewLevel !== 'jerarquico'} |
| 1617 | <!-- Info de nivel aplanado --> | 1699 | <!-- Info de nivel aplanado --> |
| 1618 | - <div class="flex items-center gap-3" style="font-family: 'DM Mono', monospace; font-size: 0.75rem;"> | 1700 | + <div class="flex items-center gap-3" style="font-family: var(--font-sans); font-variant-numeric: tabular-nums; font-size: 0.75rem;"> |
| 1619 | <span class="px-2 py-1 rounded" style="background-color: var(--theme-fill); color: var(--theme-titulo);"> | 1701 | <span class="px-2 py-1 rounded" style="background-color: var(--theme-fill); color: var(--theme-titulo);"> |
| 1620 | {NIVEL_OPTIONS.find(o => o.value === treemapViewLevel)?.label || treemapViewLevel} | 1702 | {NIVEL_OPTIONS.find(o => o.value === treemapViewLevel)?.label || treemapViewLevel} |
| 1621 | </span> | 1703 | </span> |
| ... | @@ -1683,7 +1765,7 @@ | ... | @@ -1683,7 +1765,7 @@ |
| 1683 | 1765 | ||
| 1684 | {#if width > 60 && height > 40} | 1766 | {#if width > 60 && height > 40} |
| 1685 | <foreignObject x="6" y="6" width={width - 12} height={height - 12}> | 1767 | <foreignObject x="6" y="6" width={width - 12} height={height - 12}> |
| 1686 | - <div class="h-full flex flex-col justify-between overflow-hidden" style="font-family: 'Instrument Sans', sans-serif;"> | 1768 | + <div class="h-full flex flex-col justify-between overflow-hidden" style="font-family: var(--font-sans);"> |
| 1687 | <div class="font-medium leading-tight" style="font-size: {Math.min(14, Math.max(10, width / 12))}px; color: {textColor};"> | 1769 | <div class="font-medium leading-tight" style="font-size: {Math.min(14, Math.max(10, width / 12))}px; color: {textColor};"> |
| 1688 | {#if width > 100} | 1770 | {#if width > 100} |
| 1689 | {label} | 1771 | {label} |
| ... | @@ -1692,7 +1774,7 @@ | ... | @@ -1692,7 +1774,7 @@ |
| 1692 | {/if} | 1774 | {/if} |
| 1693 | </div> | 1775 | </div> |
| 1694 | {#if height > 55} | 1776 | {#if height > 55} |
| 1695 | - <div style="font-family: 'DM Mono', monospace; font-size: {Math.min(12, Math.max(9, width / 14))}px; color: {textColorSecondary};"> | 1777 | + <div style="font-family: var(--font-sans); font-variant-numeric: tabular-nums; font-size: {Math.min(12, Math.max(9, width / 14))}px; color: {textColorSecondary};"> |
| 1696 | {formatMoney(node.value)} | 1778 | {formatMoney(node.value)} |
| 1697 | {#if width > 90 && height > 70} | 1779 | {#if width > 90 && height > 70} |
| 1698 | <span style="opacity: 0.7;"> · {formatPerCapita(node.value)}</span> | 1780 | <span style="opacity: 0.7;"> · {formatPerCapita(node.value)}</span> |
| ... | @@ -1753,7 +1835,7 @@ | ... | @@ -1753,7 +1835,7 @@ |
| 1753 | fill={getNodeColor(grupoId, 1)} | 1835 | fill={getNodeColor(grupoId, 1)} |
| 1754 | font-size="10" | 1836 | font-size="10" |
| 1755 | font-weight="600" | 1837 | font-weight="600" |
| 1756 | - style="font-family: 'DM Mono', monospace; text-transform: uppercase; letter-spacing: 0.5px;" | 1838 | + style="font-family: var(--font-sans); font-variant-numeric: tabular-nums; text-transform: uppercase; letter-spacing: 0.5px;" |
| 1757 | > | 1839 | > |
| 1758 | {#if width > 180} | 1840 | {#if width > 180} |
| 1759 | {node.name} | 1841 | {node.name} |
| ... | @@ -1787,7 +1869,7 @@ | ... | @@ -1787,7 +1869,7 @@ |
| 1787 | 1869 | ||
| 1788 | {#if width > 40 && height > 25} | 1870 | {#if width > 40 && height > 25} |
| 1789 | <foreignObject x="4" y="4" width={width - 8} height={height - 8}> | 1871 | <foreignObject x="4" y="4" width={width - 8} height={height - 8}> |
| 1790 | - <div class="h-full flex flex-col justify-between overflow-hidden" style="font-family: 'Instrument Sans', sans-serif;"> | 1872 | + <div class="h-full flex flex-col justify-between overflow-hidden" style="font-family: var(--font-sans);"> |
| 1791 | <div class="font-medium leading-tight" style="font-size: {Math.min(12, Math.max(9, width / 14))}px; color: {textColor};"> | 1873 | <div class="font-medium leading-tight" style="font-size: {Math.min(12, Math.max(9, width / 14))}px; color: {textColor};"> |
| 1792 | {#if width > 80} | 1874 | {#if width > 80} |
| 1793 | {(node.name || node.id).slice(0, Math.floor(width / 6))}{(node.name || node.id).length > Math.floor(width / 6) ? '…' : ''} | 1875 | {(node.name || node.id).slice(0, Math.floor(width / 6))}{(node.name || node.id).length > Math.floor(width / 6) ? '…' : ''} |
| ... | @@ -1796,7 +1878,7 @@ | ... | @@ -1796,7 +1878,7 @@ |
| 1796 | {/if} | 1878 | {/if} |
| 1797 | </div> | 1879 | </div> |
| 1798 | {#if height > 40 && width > 50} | 1880 | {#if height > 40 && width > 50} |
| 1799 | - <div style="font-family: 'DM Mono', monospace; font-size: {Math.min(10, Math.max(8, width / 16))}px; color: {textColorSecondary};"> | 1881 | + <div style="font-family: var(--font-sans); font-variant-numeric: tabular-nums; font-size: {Math.min(10, Math.max(8, width / 16))}px; color: {textColorSecondary};"> |
| 1800 | {formatMoney(node.value)} | 1882 | {formatMoney(node.value)} |
| 1801 | {#if width > 80 && height > 55} | 1883 | {#if width > 80 && height > 55} |
| 1802 | <span style="opacity: 0.7;"> · {formatPerCapita(node.value)}</span> | 1884 | <span style="opacity: 0.7;"> · {formatPerCapita(node.value)}</span> |
| ... | @@ -1822,31 +1904,31 @@ | ... | @@ -1822,31 +1904,31 @@ |
| 1822 | top: {Math.min(hoveredNode.y0 + 10, treemapHeight - 100)}px; | 1904 | top: {Math.min(hoveredNode.y0 + 10, treemapHeight - 100)}px; |
| 1823 | background-color: var(--theme-titulo); | 1905 | background-color: var(--theme-titulo); |
| 1824 | color: var(--theme-body); | 1906 | color: var(--theme-body); |
| 1825 | - font-family: 'Instrument Sans', sans-serif; | 1907 | + font-family: var(--font-sans); |
| 1826 | " | 1908 | " |
| 1827 | > | 1909 | > |
| 1828 | {#if treemapViewLevel !== 'jerarquico'} | 1910 | {#if treemapViewLevel !== 'jerarquico'} |
| 1829 | <!-- Vista aplanada: mostrar contexto del grupo --> | 1911 | <!-- Vista aplanada: mostrar contexto del grupo --> |
| 1830 | - <div class="text-xs opacity-60 mb-1" style="font-family: 'DM Mono', monospace;"> | 1912 | + <div class="text-xs opacity-60 mb-1" style="font-family: var(--font-sans); font-variant-numeric: tabular-nums;"> |
| 1831 | {hoveredNode.id} | 1913 | {hoveredNode.id} |
| 1832 | </div> | 1914 | </div> |
| 1833 | <div class="font-medium text-sm">{hoveredNode.name || hoveredNode.id}</div> | 1915 | <div class="font-medium text-sm">{hoveredNode.name || hoveredNode.id}</div> |
| 1834 | - <div class="text-xs opacity-80 mt-1" style="font-family: 'DM Mono', monospace;"> | 1916 | + <div class="text-xs opacity-80 mt-1" style="font-family: var(--font-sans); font-variant-numeric: tabular-nums;"> |
| 1835 | {formatMoney(hoveredNode.value)} · {((hoveredNode.value / totalValue) * 100).toFixed(1)}% | 1917 | {formatMoney(hoveredNode.value)} · {((hoveredNode.value / totalValue) * 100).toFixed(1)}% |
| 1836 | </div> | 1918 | </div> |
| 1837 | - <div class="text-xs opacity-60 mt-0.5" style="font-family: 'DM Mono', monospace;"> | 1919 | + <div class="text-xs opacity-60 mt-0.5" style="font-family: var(--font-sans); font-variant-numeric: tabular-nums;"> |
| 1838 | {formatPerCapita(hoveredNode.value)} | 1920 | {formatPerCapita(hoveredNode.value)} |
| 1839 | </div> | 1921 | </div> |
| 1840 | {:else} | 1922 | {:else} |
| 1841 | <!-- Vista jerárquica --> | 1923 | <!-- Vista jerárquica --> |
| 1842 | <div class="font-medium text-sm">{hoveredNode.data?.desc_objeto || hoveredNode.name || hoveredNode.id}</div> | 1924 | <div class="font-medium text-sm">{hoveredNode.data?.desc_objeto || hoveredNode.name || hoveredNode.id}</div> |
| 1843 | - <div class="text-xs opacity-80 mt-1" style="font-family: 'DM Mono', monospace;"> | 1925 | + <div class="text-xs opacity-80 mt-1" style="font-family: var(--font-sans); font-variant-numeric: tabular-nums;"> |
| 1844 | {formatMoney(hoveredNode.value)} | 1926 | {formatMoney(hoveredNode.value)} |
| 1845 | {#if currentTreemapNode && currentTreemapNode.value} | 1927 | {#if currentTreemapNode && currentTreemapNode.value} |
| 1846 | · {((hoveredNode.value / currentTreemapNode.value) * 100).toFixed(1)}% | 1928 | · {((hoveredNode.value / currentTreemapNode.value) * 100).toFixed(1)}% |
| 1847 | {/if} | 1929 | {/if} |
| 1848 | </div> | 1930 | </div> |
| 1849 | - <div class="text-xs opacity-60 mt-0.5" style="font-family: 'DM Mono', monospace;"> | 1931 | + <div class="text-xs opacity-60 mt-0.5" style="font-family: var(--font-sans); font-variant-numeric: tabular-nums;"> |
| 1850 | {formatPerCapita(hoveredNode.value)} | 1932 | {formatPerCapita(hoveredNode.value)} |
| 1851 | </div> | 1933 | </div> |
| 1852 | {#if hoveredNode._originalNode?.children && hoveredNode._originalNode.children.length > 0} | 1934 | {#if hoveredNode._originalNode?.children && hoveredNode._originalNode.children.length > 0} |
| ... | @@ -2070,7 +2152,7 @@ | ... | @@ -2070,7 +2152,7 @@ |
| 2070 | 2152 | ||
| 2071 | <!-- Resumen de diferencias (placeholder) --> | 2153 | <!-- Resumen de diferencias (placeholder) --> |
| 2072 | <div class="border rounded-xl p-6" style="background-color: var(--theme-fill); border-color: var(--theme-borde);"> | 2154 | <div class="border rounded-xl p-6" style="background-color: var(--theme-fill); border-color: var(--theme-borde);"> |
| 2073 | - <h3 class="text-sm font-medium uppercase tracking-wide mb-4" style="font-family: 'DM Mono', monospace; color: var(--theme-titulo);"> | 2155 | + <h3 class="text-sm font-medium uppercase tracking-wide mb-4" style="font-family: var(--font-sans); font-variant-numeric: tabular-nums; color: var(--theme-titulo);"> |
| 2074 | Diferencias principales | 2156 | Diferencias principales |
| 2075 | </h3> | 2157 | </h3> |
| 2076 | <div class="grid sm:grid-cols-3 gap-4 text-center"> | 2158 | <div class="grid sm:grid-cols-3 gap-4 text-center"> |
| ... | @@ -2136,7 +2218,7 @@ | ... | @@ -2136,7 +2218,7 @@ |
| 2136 | <span class="font-medium" style="color: var(--theme-accent);">Vigente</span> | 2218 | <span class="font-medium" style="color: var(--theme-accent);">Vigente</span> |
| 2137 | <span class="mx-1" style="opacity: 0.5;">·</span> | 2219 | <span class="mx-1" style="opacity: 0.5;">·</span> |
| 2138 | {/if} | 2220 | {/if} |
| 2139 | - <span class="font-mono">{desc.rangos}</span> | 2221 | + <span class="font-mono">{formatYearsAsRanges(desc.rangos)}</span> |
| 2140 | </p> | 2222 | </p> |
| 2141 | <p class="text-base leading-relaxed" style="color: var(--theme-titulo);">{desc.descripcion}</p> | 2223 | <p class="text-base leading-relaxed" style="color: var(--theme-titulo);">{desc.descripcion}</p> |
| 2142 | </div> | 2224 | </div> |
| ... | @@ -2262,28 +2344,28 @@ | ... | @@ -2262,28 +2344,28 @@ |
| 2262 | box-shadow: 0 0 0 2px var(--theme-accent); | 2344 | box-shadow: 0 0 0 2px var(--theme-accent); |
| 2263 | } | 2345 | } |
| 2264 | 2346 | ||
| 2265 | - /* Tema claro: transparente + ring sutil */ | 2347 | + /* Tema claro: fondo blanco con sombra sutil */ |
| 2266 | :global(.light) .dropdown-control, | 2348 | :global(.light) .dropdown-control, |
| 2267 | :global(html:not(.dark)) .dropdown-control { | 2349 | :global(html:not(.dark)) .dropdown-control { |
| 2268 | - background: transparent; | 2350 | + background: #ffffff; |
| 2269 | border: none; | 2351 | border: none; |
| 2270 | - box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08); | 2352 | + box-shadow: 0 1px 3px rgba(28,28,26,0.06), inset 0 0 0 1px rgba(28,28,26,0.06); |
| 2271 | } | 2353 | } |
| 2272 | :global(.light) .dropdown-control:focus, | 2354 | :global(.light) .dropdown-control:focus, |
| 2273 | :global(html:not(.dark)) .dropdown-control:focus { | 2355 | :global(html:not(.dark)) .dropdown-control:focus { |
| 2274 | - box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15); | 2356 | + box-shadow: 0 1px 3px rgba(28,28,26,0.08), inset 0 0 0 1px rgba(28,28,26,0.12); |
| 2275 | } | 2357 | } |
| 2276 | :global(.light) .dropdown-control:hover, | 2358 | :global(.light) .dropdown-control:hover, |
| 2277 | :global(html:not(.dark)) .dropdown-control:hover { | 2359 | :global(html:not(.dark)) .dropdown-control:hover { |
| 2278 | - box-shadow: inset 0 0 0 1px rgba(0,0,0,0.12); | 2360 | + box-shadow: 0 2px 6px rgba(28,28,26,0.08), inset 0 0 0 1px rgba(28,28,26,0.08); |
| 2279 | } | 2361 | } |
| 2280 | 2362 | ||
| 2281 | /* Dropdown panels en tema claro */ | 2363 | /* Dropdown panels en tema claro */ |
| 2282 | :global(.light) .dropdown-panel, | 2364 | :global(.light) .dropdown-panel, |
| 2283 | :global(html:not(.dark)) .dropdown-panel { | 2365 | :global(html:not(.dark)) .dropdown-panel { |
| 2284 | - background: #FAFAF8 !important; | 2366 | + background: #ffffff !important; |
| 2285 | border: none !important; | 2367 | border: none !important; |
| 2286 | - box-shadow: 0 4px 20px rgba(0,0,0,0.08), inset 0 0 0 1px rgba(0,0,0,0.08) !important; | 2368 | + box-shadow: 0 4px 24px rgba(28,28,26,0.12), inset 0 0 0 1px rgba(28,28,26,0.06) !important; |
| 2287 | } | 2369 | } |
| 2288 | 2370 | ||
| 2289 | /* Sidebar izquierdo: con fondo en móvil, transparente en desktop */ | 2371 | /* Sidebar izquierdo: con fondo en móvil, transparente en desktop */ |
| ... | @@ -2329,13 +2411,13 @@ | ... | @@ -2329,13 +2411,13 @@ |
| 2329 | /* Tema claro */ | 2411 | /* Tema claro */ |
| 2330 | :global(.light) .search-result-item, | 2412 | :global(.light) .search-result-item, |
| 2331 | :global(html:not(.dark)) .search-result-item { | 2413 | :global(html:not(.dark)) .search-result-item { |
| 2332 | - background: transparent; | 2414 | + background: #ffffff; |
| 2333 | border: none; | 2415 | border: none; |
| 2334 | - box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06); | 2416 | + box-shadow: 0 1px 3px rgba(28,28,26,0.04), inset 0 0 0 1px rgba(28,28,26,0.05); |
| 2335 | } | 2417 | } |
| 2336 | :global(.light) .search-result-item:hover, | 2418 | :global(.light) .search-result-item:hover, |
| 2337 | :global(html:not(.dark)) .search-result-item:hover { | 2419 | :global(html:not(.dark)) .search-result-item:hover { |
| 2338 | - background: rgba(0,0,0,0.03); | 2420 | + background: #ffffff; |
| 2339 | - box-shadow: inset 0 0 0 1px var(--theme-accent); | 2421 | + box-shadow: 0 2px 8px rgba(28,28,26,0.08), inset 0 0 0 1px var(--theme-accent); |
| 2340 | } | 2422 | } |
| 2341 | </style> | 2423 | </style> | ... | ... |
| ... | @@ -2,13 +2,16 @@ import { supabase } from '$lib/supabase'; | ... | @@ -2,13 +2,16 @@ import { supabase } from '$lib/supabase'; |
| 2 | import { error } from '@sveltejs/kit'; | 2 | import { error } from '@sveltejs/kit'; |
| 3 | 3 | ||
| 4 | export async function load({ params }) { | 4 | export async function load({ params }) { |
| 5 | + console.time('[SERVER] Total load objeto'); | ||
| 5 | const { codigo } = params; | 6 | const { codigo } = params; |
| 6 | 7 | ||
| 8 | + console.time('[SERVER] Query clas_objetos'); | ||
| 7 | const { data, error: dbError } = await supabase | 9 | const { data, error: dbError } = await supabase |
| 8 | .schema('ppto') | 10 | .schema('ppto') |
| 9 | .from('clas_objetos') | 11 | .from('clas_objetos') |
| 10 | .select('*') | 12 | .select('*') |
| 11 | .eq('objeto', codigo); | 13 | .eq('objeto', codigo); |
| 14 | + console.timeEnd('[SERVER] Query clas_objetos'); | ||
| 12 | 15 | ||
| 13 | if (dbError || !data || data.length === 0) { | 16 | if (dbError || !data || data.length === 0) { |
| 14 | throw error(404, 'Objeto no encontrado'); | 17 | throw error(404, 'Objeto no encontrado'); |
| ... | @@ -21,6 +24,7 @@ export async function load({ params }) { | ... | @@ -21,6 +24,7 @@ export async function load({ params }) { |
| 21 | let padres = []; | 24 | let padres = []; |
| 22 | let hijos = []; | 25 | let hijos = []; |
| 23 | 26 | ||
| 27 | + console.time('[SERVER] Query padres'); | ||
| 24 | // Buscar padre según nivel | 28 | // Buscar padre según nivel |
| 25 | if (objeto.nivel === 'subpartida') { | 29 | if (objeto.nivel === 'subpartida') { |
| 26 | // Padre es partida | 30 | // Padre es partida |
| ... | @@ -58,7 +62,9 @@ export async function load({ params }) { | ... | @@ -58,7 +62,9 @@ export async function load({ params }) { |
| 58 | .eq('objeto', grupoCodigo); | 62 | .eq('objeto', grupoCodigo); |
| 59 | if (padreData?.length) padres.unshift(padreData[0]); | 63 | if (padreData?.length) padres.unshift(padreData[0]); |
| 60 | } | 64 | } |
| 65 | + console.timeEnd('[SERVER] Query padres'); | ||
| 61 | 66 | ||
| 67 | + console.time('[SERVER] Query hijos'); | ||
| 62 | // Buscar hijos según nivel | 68 | // Buscar hijos según nivel |
| 63 | if (objeto.nivel === 'grupo') { | 69 | if (objeto.nivel === 'grupo') { |
| 64 | const { data: hijosData } = await supabase | 70 | const { data: hijosData } = await supabase |
| ... | @@ -100,7 +106,9 @@ export async function load({ params }) { | ... | @@ -100,7 +106,9 @@ export async function load({ params }) { |
| 100 | return acc; | 106 | return acc; |
| 101 | }, []) || []; | 107 | }, []) || []; |
| 102 | } | 108 | } |
| 109 | + console.timeEnd('[SERVER] Query hijos'); | ||
| 103 | 110 | ||
| 111 | + console.timeEnd('[SERVER] Total load objeto'); | ||
| 104 | return { | 112 | return { |
| 105 | objeto, | 113 | objeto, |
| 106 | padres, | 114 | padres, | ... | ... |
| ... | @@ -3,6 +3,7 @@ | ... | @@ -3,6 +3,7 @@ |
| 3 | import { tweened } from 'svelte/motion'; | 3 | import { tweened } from 'svelte/motion'; |
| 4 | import { cubicOut } from 'svelte/easing'; | 4 | import { cubicOut } from 'svelte/easing'; |
| 5 | import { supabase } from '$lib/supabase'; | 5 | import { supabase } from '$lib/supabase'; |
| 6 | + import { goto } from '$app/navigation'; | ||
| 6 | import Spinner from '$lib/components/ui/Spinner.svelte'; | 7 | import Spinner from '$lib/components/ui/Spinner.svelte'; |
| 7 | 8 | ||
| 8 | // Datos del servidor (reactivos) | 9 | // Datos del servidor (reactivos) |
| ... | @@ -47,6 +48,35 @@ | ... | @@ -47,6 +48,35 @@ |
| 47 | return Math.max(...years.map(y => parseInt(y))); | 48 | return Math.max(...years.map(y => parseInt(y))); |
| 48 | } | 49 | } |
| 49 | 50 | ||
| 51 | + /** | ||
| 52 | + * Converts a string with years into compact ranges | ||
| 53 | + * Example: "2006, 2007, 2008, 2009, 2015, 2016" → "2006-2009, 2015-2016" | ||
| 54 | + */ | ||
| 55 | + function formatYearsAsRanges(rangosStr) { | ||
| 56 | + if (!rangosStr) return ''; | ||
| 57 | + const yearsMatch = rangosStr.match(/\d{4}/g); | ||
| 58 | + if (!yearsMatch || yearsMatch.length === 0) return rangosStr; | ||
| 59 | + | ||
| 60 | + const years = [...new Set(yearsMatch.map(y => parseInt(y)))].sort((a, b) => a - b); | ||
| 61 | + if (years.length === 1) return String(years[0]); | ||
| 62 | + | ||
| 63 | + const ranges = []; | ||
| 64 | + let rangeStart = years[0]; | ||
| 65 | + let rangeEnd = years[0]; | ||
| 66 | + | ||
| 67 | + for (let i = 1; i < years.length; i++) { | ||
| 68 | + if (years[i] === rangeEnd + 1) { | ||
| 69 | + rangeEnd = years[i]; | ||
| 70 | + } else { | ||
| 71 | + ranges.push(rangeStart === rangeEnd ? String(rangeStart) : `${rangeStart}-${rangeEnd}`); | ||
| 72 | + rangeStart = years[i]; | ||
| 73 | + rangeEnd = years[i]; | ||
| 74 | + } | ||
| 75 | + } | ||
| 76 | + ranges.push(rangeStart === rangeEnd ? String(rangeStart) : `${rangeStart}-${rangeEnd}`); | ||
| 77 | + return ranges.join(', '); | ||
| 78 | + } | ||
| 79 | + | ||
| 50 | function getNivelLabel(nivel) { | 80 | function getNivelLabel(nivel) { |
| 51 | const labels = { grupo: 'Grupo', subgrupo: 'Subgrupo', partida: 'Partida', subpartida: 'Subpartida' }; | 81 | const labels = { grupo: 'Grupo', subgrupo: 'Subgrupo', partida: 'Partida', subpartida: 'Subpartida' }; |
| 52 | return labels[nivel] || nivel; | 82 | return labels[nivel] || nivel; |
| ... | @@ -123,6 +153,31 @@ | ... | @@ -123,6 +153,31 @@ |
| 123 | } | 153 | } |
| 124 | 154 | ||
| 125 | // ══════════════════════════════════════════════════════════════ | 155 | // ══════════════════════════════════════════════════════════════ |
| 156 | + // SPLIT VIEW - CLASIFICADOR LADO A LADO | ||
| 157 | + // ══════════════════════════════════════════════════════════════ | ||
| 158 | + let splitOpen = $state(false); | ||
| 159 | + let splitWidth = $state(45); // porcentaje del ancho para el clasificador | ||
| 160 | + let isResizing = $state(false); | ||
| 161 | + | ||
| 162 | + function startResize(e) { | ||
| 163 | + isResizing = true; | ||
| 164 | + document.body.style.cursor = 'col-resize'; | ||
| 165 | + document.body.style.userSelect = 'none'; | ||
| 166 | + } | ||
| 167 | + | ||
| 168 | + function handleResize(e) { | ||
| 169 | + if (!isResizing) return; | ||
| 170 | + const newWidth = (e.clientX / window.innerWidth) * 100; | ||
| 171 | + splitWidth = Math.max(25, Math.min(65, newWidth)); // entre 25% y 65% | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + function stopResize() { | ||
| 175 | + isResizing = false; | ||
| 176 | + document.body.style.cursor = ''; | ||
| 177 | + document.body.style.userSelect = ''; | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + // ══════════════════════════════════════════════════════════════ | ||
| 126 | // DATOS DE SUPABASE | 181 | // DATOS DE SUPABASE |
| 127 | // ══════════════════════════════════════════════════════════════ | 182 | // ══════════════════════════════════════════════════════════════ |
| 128 | let datosAnuales = $state([]); // Datos por año | 183 | let datosAnuales = $state([]); // Datos por año |
| ... | @@ -131,10 +186,14 @@ | ... | @@ -131,10 +186,14 @@ |
| 131 | 186 | ||
| 132 | // Cargar datos según selección | 187 | // Cargar datos según selección |
| 133 | async function loadData() { | 188 | async function loadData() { |
| 189 | + const loadId = Date.now(); | ||
| 190 | + console.log(`[CLIENT] loadData started (${loadId})`); | ||
| 191 | + const startTime = performance.now(); | ||
| 134 | loading = true; | 192 | loading = true; |
| 135 | 193 | ||
| 136 | if (selectedEntity) { | 194 | if (selectedEntity) { |
| 137 | // Cargar desde vista_objeto_entidad | 195 | // Cargar desde vista_objeto_entidad |
| 196 | + const queryStart = performance.now(); | ||
| 138 | const { data, error } = await supabase | 197 | const { data, error } = await supabase |
| 139 | .schema('ppto') | 198 | .schema('ppto') |
| 140 | .from('vista_objeto_entidad') | 199 | .from('vista_objeto_entidad') |
| ... | @@ -144,6 +203,7 @@ | ... | @@ -144,6 +203,7 @@ |
| 144 | .eq('entidad', selectedEntity.entidad) | 203 | .eq('entidad', selectedEntity.entidad) |
| 145 | .order('gestion'); | 204 | .order('gestion'); |
| 146 | 205 | ||
| 206 | + console.log(`[CLIENT] Query vista_objeto_entidad: ${(performance.now() - queryStart).toFixed(0)}ms`); | ||
| 147 | if (data) { | 207 | if (data) { |
| 148 | datosHistorico = data.find(d => d.gestion === 0) || null; | 208 | datosHistorico = data.find(d => d.gestion === 0) || null; |
| 149 | datosAnuales = data.filter(d => d.gestion > 0); | 209 | datosAnuales = data.filter(d => d.gestion > 0); |
| ... | @@ -151,6 +211,7 @@ | ... | @@ -151,6 +211,7 @@ |
| 151 | } | 211 | } |
| 152 | } else { | 212 | } else { |
| 153 | // Cargar desde vista_objeto_estado (Todo el Estado) | 213 | // Cargar desde vista_objeto_estado (Todo el Estado) |
| 214 | + const queryStart = performance.now(); | ||
| 154 | const { data, error } = await supabase | 215 | const { data, error } = await supabase |
| 155 | .schema('ppto') | 216 | .schema('ppto') |
| 156 | .from('vista_objeto_estado') | 217 | .from('vista_objeto_estado') |
| ... | @@ -158,6 +219,7 @@ | ... | @@ -158,6 +219,7 @@ |
| 158 | .eq('objeto', objetoCodigo) | 219 | .eq('objeto', objetoCodigo) |
| 159 | .order('gestion'); | 220 | .order('gestion'); |
| 160 | 221 | ||
| 222 | + console.log(`[CLIENT] Query vista_objeto_estado: ${(performance.now() - queryStart).toFixed(0)}ms`); | ||
| 161 | if (data) { | 223 | if (data) { |
| 162 | datosHistorico = data.find(d => d.gestion === 0) || null; | 224 | datosHistorico = data.find(d => d.gestion === 0) || null; |
| 163 | datosAnuales = data.filter(d => d.gestion > 0); | 225 | datosAnuales = data.filter(d => d.gestion > 0); |
| ... | @@ -175,10 +237,12 @@ | ... | @@ -175,10 +237,12 @@ |
| 175 | } | 237 | } |
| 176 | 238 | ||
| 177 | loading = false; | 239 | loading = false; |
| 240 | + console.log(`[CLIENT] loadData total: ${(performance.now() - startTime).toFixed(0)}ms (${loadId})`); | ||
| 178 | } | 241 | } |
| 179 | 242 | ||
| 180 | // Cargar lista de entidades disponibles para este objeto | 243 | // Cargar lista de entidades disponibles para este objeto |
| 181 | async function loadEntidades() { | 244 | async function loadEntidades() { |
| 245 | + const startTime = performance.now(); | ||
| 182 | const { data, error } = await supabase | 246 | const { data, error } = await supabase |
| 183 | .schema('ppto') | 247 | .schema('ppto') |
| 184 | .from('entidades_por_objeto') | 248 | .from('entidades_por_objeto') |
| ... | @@ -190,6 +254,7 @@ | ... | @@ -190,6 +254,7 @@ |
| 190 | if (data) { | 254 | if (data) { |
| 191 | entidades = data; | 255 | entidades = data; |
| 192 | } | 256 | } |
| 257 | + console.log(`[CLIENT] loadEntidades: ${(performance.now() - startTime).toFixed(0)}ms`); | ||
| 193 | } | 258 | } |
| 194 | 259 | ||
| 195 | // ══════════════════════════════════════════════════════════════ | 260 | // ══════════════════════════════════════════════════════════════ |
| ... | @@ -574,6 +639,7 @@ | ... | @@ -574,6 +639,7 @@ |
| 574 | // MOUNT | 639 | // MOUNT |
| 575 | // ══════════════════════════════════════════════════════════════ | 640 | // ══════════════════════════════════════════════════════════════ |
| 576 | onMount(async () => { | 641 | onMount(async () => { |
| 642 | + const mountStart = performance.now(); | ||
| 577 | setTimeout(() => { mounted = true; }, 50); | 643 | setTimeout(() => { mounted = true; }, 50); |
| 578 | 644 | ||
| 579 | // Leer estado del tema | 645 | // Leer estado del tema |
| ... | @@ -589,7 +655,10 @@ | ... | @@ -589,7 +655,10 @@ |
| 589 | }); | 655 | }); |
| 590 | 656 | ||
| 591 | // Cargar datos | 657 | // Cargar datos |
| 658 | + const loadStart = performance.now(); | ||
| 592 | await Promise.all([loadData(), loadEntidades()]); | 659 | await Promise.all([loadData(), loadEntidades()]); |
| 660 | + console.log(`[CLIENT] Promise.all: ${(performance.now() - loadStart).toFixed(0)}ms`); | ||
| 661 | + console.log(`[CLIENT] onMount total: ${(performance.now() - mountStart).toFixed(0)}ms`); | ||
| 593 | 662 | ||
| 594 | // Cerrar dropdown al hacer click fuera | 663 | // Cerrar dropdown al hacer click fuera |
| 595 | const handleClickOutside = (e) => { | 664 | const handleClickOutside = (e) => { |
| ... | @@ -606,9 +675,24 @@ | ... | @@ -606,9 +675,24 @@ |
| 606 | }; | 675 | }; |
| 607 | document.addEventListener('click', handleClickOutside); | 676 | document.addEventListener('click', handleClickOutside); |
| 608 | 677 | ||
| 678 | + // Resize handlers para split view | ||
| 679 | + document.addEventListener('mousemove', handleResize); | ||
| 680 | + document.addEventListener('mouseup', stopResize); | ||
| 681 | + | ||
| 682 | + // Escuchar mensajes del iframe (clasificador en modo embed) | ||
| 683 | + const handleIframeMessage = (e) => { | ||
| 684 | + if (e.data?.type === 'navigate-objeto' && e.data?.codigo) { | ||
| 685 | + goto(`/objeto/${e.data.codigo}`); | ||
| 686 | + } | ||
| 687 | + }; | ||
| 688 | + window.addEventListener('message', handleIframeMessage); | ||
| 689 | + | ||
| 609 | return () => { | 690 | return () => { |
| 610 | observer.disconnect(); | 691 | observer.disconnect(); |
| 611 | document.removeEventListener('click', handleClickOutside); | 692 | document.removeEventListener('click', handleClickOutside); |
| 693 | + document.removeEventListener('mousemove', handleResize); | ||
| 694 | + document.removeEventListener('mouseup', stopResize); | ||
| 695 | + window.removeEventListener('message', handleIframeMessage); | ||
| 612 | }; | 696 | }; |
| 613 | }); | 697 | }); |
| 614 | 698 | ||
| ... | @@ -660,8 +744,36 @@ | ... | @@ -660,8 +744,36 @@ |
| 660 | <link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@400;500&family=Instrument+Sans:wght@400;500;600&display=swap" rel="stylesheet" /> | 744 | <link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@400;500&family=Instrument+Sans:wght@400;500;600&display=swap" rel="stylesheet" /> |
| 661 | </svelte:head> | 745 | </svelte:head> |
| 662 | 746 | ||
| 663 | -<div class="page" class:mounted class:tema-claro={!isDark}> | 747 | +<!-- Contenedor split view --> |
| 748 | +<div class="split-container" class:split-active={splitOpen}> | ||
| 749 | + <!-- Panel izquierdo: Clasificador --> | ||
| 750 | + {#if splitOpen} | ||
| 751 | + <div class="split-left" style="width: {splitWidth}%"> | ||
| 752 | + <div class="split-header"> | ||
| 753 | + <span>Clasificador por Objeto</span> | ||
| 754 | + <button class="split-close" onclick={() => splitOpen = false}> | ||
| 755 | + <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | ||
| 756 | + <path d="M18 6L6 18M6 6l12 12"/> | ||
| 757 | + </svg> | ||
| 758 | + </button> | ||
| 759 | + </div> | ||
| 760 | + <iframe | ||
| 761 | + src="/clasificadores/objeto-gasto?embed=true&objeto={objetoCodigo}" | ||
| 762 | + title="Clasificador" | ||
| 763 | + class="split-iframe" | ||
| 764 | + ></iframe> | ||
| 765 | + </div> | ||
| 766 | + | ||
| 767 | + <!-- Resizer --> | ||
| 768 | + <div | ||
| 769 | + class="split-resizer" | ||
| 770 | + onmousedown={startResize} | ||
| 771 | + role="separator" | ||
| 772 | + ></div> | ||
| 773 | + {/if} | ||
| 664 | 774 | ||
| 775 | + <!-- Panel derecho: Contenido principal --> | ||
| 776 | + <div class="split-right" class:mounted> | ||
| 665 | <!-- Header compacto --> | 777 | <!-- Header compacto --> |
| 666 | <header> | 778 | <header> |
| 667 | <nav class="breadcrumb"> | 779 | <nav class="breadcrumb"> |
| ... | @@ -690,7 +802,7 @@ | ... | @@ -690,7 +802,7 @@ |
| 690 | 802 | ||
| 691 | <div class="meta"> | 803 | <div class="meta"> |
| 692 | <span class="badge">{objeto.nivel}</span> | 804 | <span class="badge">{objeto.nivel}</span> |
| 693 | - <span class="years">{objeto.años}</span> | 805 | + <span class="years">{formatYearsAsRanges(objeto.años)}</span> |
| 694 | </div> | 806 | </div> |
| 695 | </header> | 807 | </header> |
| 696 | 808 | ||
| ... | @@ -705,7 +817,7 @@ | ... | @@ -705,7 +817,7 @@ |
| 705 | <h4>Variaciones históricas</h4> | 817 | <h4>Variaciones históricas</h4> |
| 706 | {#each objeto.variaciones as v} | 818 | {#each objeto.variaciones as v} |
| 707 | <div class="variacion"> | 819 | <div class="variacion"> |
| 708 | - <span class="variacion-rango">{v.rango}</span> | 820 | + <span class="variacion-rango">{formatYearsAsRanges(v.rango)}</span> |
| 709 | <p>{v.texto}</p> | 821 | <p>{v.texto}</p> |
| 710 | </div> | 822 | </div> |
| 711 | {/each} | 823 | {/each} |
| ... | @@ -1112,25 +1224,56 @@ | ... | @@ -1112,25 +1224,56 @@ |
| 1112 | {/if} | 1224 | {/if} |
| 1113 | 1225 | ||
| 1114 | </main> | 1226 | </main> |
| 1115 | -</div> | 1227 | + </div><!-- end split-right --> |
| 1116 | - | ||
| 1117 | 1228 | ||
| 1229 | + <!-- Botón para abrir split view --> | ||
| 1230 | + <button | ||
| 1231 | + class="split-toggle" | ||
| 1232 | + onclick={() => splitOpen = !splitOpen} | ||
| 1233 | + title={splitOpen ? "Cerrar clasificador" : "Abrir clasificador"} | ||
| 1234 | + class:active={splitOpen} | ||
| 1235 | + > | ||
| 1236 | + <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | ||
| 1237 | + <rect x="3" y="3" width="7" height="18" rx="1"/> | ||
| 1238 | + <rect x="14" y="3" width="7" height="18" rx="1"/> | ||
| 1239 | + </svg> | ||
| 1240 | + <span class="split-toggle-label">{splitOpen ? 'Cerrar' : 'Clasificador'}</span> | ||
| 1241 | + </button> | ||
| 1242 | +</div><!-- end split-container --> | ||
| 1118 | 1243 | ||
| 1119 | <style> | 1244 | <style> |
| 1120 | - .page { | 1245 | + /* Split container */ |
| 1246 | + .split-container { | ||
| 1247 | + display: flex; | ||
| 1121 | min-height: 100vh; | 1248 | min-height: 100vh; |
| 1122 | - background: #0A0A0A; | 1249 | + background: var(--theme-body); |
| 1123 | - color: #F5F0E8; | 1250 | + } |
| 1124 | - font-family: 'Instrument Sans', -apple-system, sans-serif; | 1251 | + |
| 1252 | + .split-left { | ||
| 1253 | + flex-shrink: 0; | ||
| 1254 | + display: flex; | ||
| 1255 | + flex-direction: column; | ||
| 1256 | + height: 100vh; | ||
| 1257 | + position: sticky; | ||
| 1258 | + top: 0; | ||
| 1259 | + border-right: 1px solid var(--theme-borde); | ||
| 1260 | + } | ||
| 1261 | + | ||
| 1262 | + .split-right { | ||
| 1263 | + flex: 1; | ||
| 1264 | + min-width: 0; | ||
| 1265 | + background: var(--theme-body); | ||
| 1266 | + color: var(--theme-titulo); | ||
| 1267 | + font-family: var(--font-sans), -apple-system, sans-serif; | ||
| 1125 | opacity: 0; | 1268 | opacity: 0; |
| 1126 | transition: opacity 0.4s ease; | 1269 | transition: opacity 0.4s ease; |
| 1127 | } | 1270 | } |
| 1128 | - .mounted { opacity: 1; } | 1271 | + .split-right.mounted { opacity: 1; } |
| 1129 | 1272 | ||
| 1130 | /* Header */ | 1273 | /* Header */ |
| 1131 | header { | 1274 | header { |
| 1132 | padding: 1rem 2rem; | 1275 | padding: 1rem 2rem; |
| 1133 | - border-bottom: 1px solid #1E1E1C; | 1276 | + border-bottom: 1px solid var(--theme-borde); |
| 1134 | } | 1277 | } |
| 1135 | 1278 | ||
| 1136 | .breadcrumb { | 1279 | .breadcrumb { |
| ... | @@ -1143,14 +1286,14 @@ | ... | @@ -1143,14 +1286,14 @@ |
| 1143 | .breadcrumb a { | 1286 | .breadcrumb a { |
| 1144 | font-family: 'DM Mono', monospace; | 1287 | font-family: 'DM Mono', monospace; |
| 1145 | font-size: 0.75rem; | 1288 | font-size: 0.75rem; |
| 1146 | - color: #5A5650; | 1289 | + color: var(--theme-texto); |
| 1147 | text-decoration: none; | 1290 | text-decoration: none; |
| 1148 | transition: color 0.2s; | 1291 | transition: color 0.2s; |
| 1149 | } | 1292 | } |
| 1150 | - .breadcrumb a:hover { color: #F5F0E8; } | 1293 | + .breadcrumb a:hover { color: var(--theme-titulo); } |
| 1151 | 1294 | ||
| 1152 | .breadcrumb-sep { | 1295 | .breadcrumb-sep { |
| 1153 | - color: #3A3A38; | 1296 | + color: var(--theme-borde); |
| 1154 | font-size: 0.75rem; | 1297 | font-size: 0.75rem; |
| 1155 | } | 1298 | } |
| 1156 | 1299 | ||
| ... | @@ -1179,15 +1322,15 @@ | ... | @@ -1179,15 +1322,15 @@ |
| 1179 | .codigo { | 1322 | .codigo { |
| 1180 | font-family: 'DM Mono', monospace; | 1323 | font-family: 'DM Mono', monospace; |
| 1181 | font-size: 1.5rem; | 1324 | font-size: 1.5rem; |
| 1182 | - color: #5A5650; | 1325 | + color: var(--theme-texto); |
| 1183 | font-weight: 400; | 1326 | font-weight: 400; |
| 1184 | } | 1327 | } |
| 1185 | 1328 | ||
| 1186 | h1 { | 1329 | h1 { |
| 1187 | - font-family: 'DM Serif Display', Georgia, serif; | 1330 | + font-family: var(--font-display), Georgia, serif; |
| 1188 | font-size: 1.5rem; | 1331 | font-size: 1.5rem; |
| 1189 | font-weight: 400; | 1332 | font-weight: 400; |
| 1190 | - color: #F5F0E8; | 1333 | + color: var(--theme-titulo); |
| 1191 | margin: 0; | 1334 | margin: 0; |
| 1192 | display: inline-flex; | 1335 | display: inline-flex; |
| 1193 | align-items: center; | 1336 | align-items: center; |
| ... | @@ -1198,7 +1341,7 @@ | ... | @@ -1198,7 +1341,7 @@ |
| 1198 | background: transparent; | 1341 | background: transparent; |
| 1199 | border: none; | 1342 | border: none; |
| 1200 | padding: 0; | 1343 | padding: 0; |
| 1201 | - color: #5A5650; | 1344 | + color: var(--theme-texto); |
| 1202 | cursor: pointer; | 1345 | cursor: pointer; |
| 1203 | transition: all 0.2s; | 1346 | transition: all 0.2s; |
| 1204 | display: inline-flex; | 1347 | display: inline-flex; |
| ... | @@ -1207,7 +1350,7 @@ | ... | @@ -1207,7 +1350,7 @@ |
| 1207 | vertical-align: middle; | 1350 | vertical-align: middle; |
| 1208 | } | 1351 | } |
| 1209 | .help-icon:hover, .help-icon.active { | 1352 | .help-icon:hover, .help-icon.active { |
| 1210 | - color: #E8C547; | 1353 | + color: var(--theme-accent); |
| 1211 | } | 1354 | } |
| 1212 | 1355 | ||
| 1213 | .meta { | 1356 | .meta { |
| ... | @@ -1223,22 +1366,137 @@ | ... | @@ -1223,22 +1366,137 @@ |
| 1223 | letter-spacing: 0.1em; | 1366 | letter-spacing: 0.1em; |
| 1224 | text-transform: uppercase; | 1367 | text-transform: uppercase; |
| 1225 | padding: 0.25rem 0.5rem; | 1368 | padding: 0.25rem 0.5rem; |
| 1226 | - background: #1A1A18; | 1369 | + background: var(--theme-surface); |
| 1227 | - border: 1px solid #2E2E2C; | 1370 | + border: 1px solid var(--theme-borde); |
| 1228 | border-radius: 4px; | 1371 | border-radius: 4px; |
| 1229 | - color: #8A8578; | 1372 | + color: var(--theme-texto); |
| 1230 | } | 1373 | } |
| 1231 | 1374 | ||
| 1232 | .years { | 1375 | .years { |
| 1233 | font-family: 'DM Mono', monospace; | 1376 | font-family: 'DM Mono', monospace; |
| 1234 | font-size: 0.75rem; | 1377 | font-size: 0.75rem; |
| 1235 | - color: #5A5650; | 1378 | + color: var(--theme-texto); |
| 1379 | + } | ||
| 1380 | + | ||
| 1381 | + /* Split View - Header y controles */ | ||
| 1382 | + .split-header { | ||
| 1383 | + display: flex; | ||
| 1384 | + align-items: center; | ||
| 1385 | + justify-content: space-between; | ||
| 1386 | + padding: 0.75rem 1rem; | ||
| 1387 | + background: var(--theme-surface); | ||
| 1388 | + border-bottom: 1px solid var(--theme-borde); | ||
| 1389 | + font-size: 0.75rem; | ||
| 1390 | + font-weight: 600; | ||
| 1391 | + color: var(--theme-titulo); | ||
| 1392 | + } | ||
| 1393 | + | ||
| 1394 | + .split-close { | ||
| 1395 | + padding: 0.25rem; | ||
| 1396 | + border: none; | ||
| 1397 | + background: transparent; | ||
| 1398 | + color: var(--theme-texto); | ||
| 1399 | + cursor: pointer; | ||
| 1400 | + border-radius: 4px; | ||
| 1401 | + display: flex; | ||
| 1402 | + align-items: center; | ||
| 1403 | + justify-content: center; | ||
| 1404 | + } | ||
| 1405 | + | ||
| 1406 | + .split-close:hover { | ||
| 1407 | + background: var(--theme-borde); | ||
| 1408 | + color: var(--theme-titulo); | ||
| 1409 | + } | ||
| 1410 | + | ||
| 1411 | + .split-iframe { | ||
| 1412 | + flex: 1; | ||
| 1413 | + width: 100%; | ||
| 1414 | + border: none; | ||
| 1415 | + background: var(--theme-body); | ||
| 1416 | + } | ||
| 1417 | + | ||
| 1418 | + .split-resizer { | ||
| 1419 | + width: 8px; | ||
| 1420 | + background: var(--theme-borde); | ||
| 1421 | + cursor: col-resize; | ||
| 1422 | + flex-shrink: 0; | ||
| 1423 | + position: relative; | ||
| 1424 | + transition: background 0.2s; | ||
| 1425 | + } | ||
| 1426 | + | ||
| 1427 | + .split-resizer::after { | ||
| 1428 | + content: ''; | ||
| 1429 | + position: absolute; | ||
| 1430 | + left: 50%; | ||
| 1431 | + top: 50%; | ||
| 1432 | + transform: translate(-50%, -50%); | ||
| 1433 | + width: 4px; | ||
| 1434 | + height: 48px; | ||
| 1435 | + background: var(--theme-texto); | ||
| 1436 | + border-radius: 4px; | ||
| 1437 | + opacity: 0.3; | ||
| 1438 | + transition: opacity 0.2s, height 0.2s; | ||
| 1439 | + } | ||
| 1440 | + | ||
| 1441 | + .split-resizer:hover { | ||
| 1442 | + background: var(--theme-accent); | ||
| 1443 | + } | ||
| 1444 | + | ||
| 1445 | + .split-resizer:hover::after { | ||
| 1446 | + opacity: 0.8; | ||
| 1447 | + height: 64px; | ||
| 1448 | + } | ||
| 1449 | + | ||
| 1450 | + /* Botón para abrir/cerrar split view */ | ||
| 1451 | + .split-toggle { | ||
| 1452 | + position: fixed; | ||
| 1453 | + left: 1rem; | ||
| 1454 | + bottom: 1.5rem; | ||
| 1455 | + display: none; | ||
| 1456 | + align-items: center; | ||
| 1457 | + gap: 0.5rem; | ||
| 1458 | + padding: 0.625rem 1rem; | ||
| 1459 | + background: var(--theme-surface); | ||
| 1460 | + border: 1px solid var(--theme-borde); | ||
| 1461 | + border-radius: 8px; | ||
| 1462 | + color: var(--theme-texto); | ||
| 1463 | + font-size: 0.75rem; | ||
| 1464 | + cursor: pointer; | ||
| 1465 | + z-index: 60; | ||
| 1466 | + transition: all 0.2s, left 0.3s ease; | ||
| 1467 | + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | ||
| 1468 | + } | ||
| 1469 | + | ||
| 1470 | + .split-active .split-toggle { | ||
| 1471 | + /* Se ajusta automáticamente con margin-left del contenedor */ | ||
| 1472 | + } | ||
| 1473 | + | ||
| 1474 | + @media (min-width: 1024px) { | ||
| 1475 | + .split-toggle { | ||
| 1476 | + display: flex; | ||
| 1477 | + } | ||
| 1478 | + } | ||
| 1479 | + | ||
| 1480 | + .split-toggle:hover { | ||
| 1481 | + background: var(--theme-fill); | ||
| 1482 | + color: var(--theme-titulo); | ||
| 1483 | + border-color: var(--theme-accent); | ||
| 1484 | + } | ||
| 1485 | + | ||
| 1486 | + .split-toggle.active { | ||
| 1487 | + background: var(--theme-accent); | ||
| 1488 | + color: white; | ||
| 1489 | + border-color: var(--theme-accent); | ||
| 1490 | + } | ||
| 1491 | + | ||
| 1492 | + .split-toggle-label { | ||
| 1493 | + font-weight: 500; | ||
| 1236 | } | 1494 | } |
| 1237 | 1495 | ||
| 1238 | /* Info panel */ | 1496 | /* Info panel */ |
| 1239 | .info-panel { | 1497 | .info-panel { |
| 1240 | - background: #111110; | 1498 | + background: var(--theme-fill); |
| 1241 | - border-bottom: 1px solid #1E1E1C; | 1499 | + border-bottom: 1px solid var(--theme-borde); |
| 1242 | padding: 1.5rem 2rem; | 1500 | padding: 1.5rem 2rem; |
| 1243 | } | 1501 | } |
| 1244 | 1502 | ||
| ... | @@ -1247,27 +1505,27 @@ | ... | @@ -1247,27 +1505,27 @@ |
| 1247 | font-size: 0.625rem; | 1505 | font-size: 0.625rem; |
| 1248 | letter-spacing: 0.15em; | 1506 | letter-spacing: 0.15em; |
| 1249 | text-transform: uppercase; | 1507 | text-transform: uppercase; |
| 1250 | - color: #5A5650; | 1508 | + color: var(--theme-texto); |
| 1251 | margin: 0 0 0.5rem 0; | 1509 | margin: 0 0 0.5rem 0; |
| 1252 | } | 1510 | } |
| 1253 | 1511 | ||
| 1254 | .info-content p { | 1512 | .info-content p { |
| 1255 | font-size: 0.875rem; | 1513 | font-size: 0.875rem; |
| 1256 | line-height: 1.6; | 1514 | line-height: 1.6; |
| 1257 | - color: #8A8578; | 1515 | + color: var(--theme-texto); |
| 1258 | margin: 0 0 1.25rem 0; | 1516 | margin: 0 0 1.25rem 0; |
| 1259 | } | 1517 | } |
| 1260 | 1518 | ||
| 1261 | .variacion { | 1519 | .variacion { |
| 1262 | padding-left: 1rem; | 1520 | padding-left: 1rem; |
| 1263 | - border-left: 2px solid #2E2E2C; | 1521 | + border-left: 2px solid var(--theme-borde); |
| 1264 | margin-bottom: 1rem; | 1522 | margin-bottom: 1rem; |
| 1265 | } | 1523 | } |
| 1266 | 1524 | ||
| 1267 | .variacion-rango { | 1525 | .variacion-rango { |
| 1268 | font-family: 'DM Mono', monospace; | 1526 | font-family: 'DM Mono', monospace; |
| 1269 | font-size: 0.688rem; | 1527 | font-size: 0.688rem; |
| 1270 | - color: #5A5650; | 1528 | + color: var(--theme-texto); |
| 1271 | } | 1529 | } |
| 1272 | 1530 | ||
| 1273 | .variacion p { | 1531 | .variacion p { |
| ... | @@ -1294,23 +1552,23 @@ | ... | @@ -1294,23 +1552,23 @@ |
| 1294 | } | 1552 | } |
| 1295 | 1553 | ||
| 1296 | .hijo-link:hover { | 1554 | .hijo-link:hover { |
| 1297 | - background: rgba(255, 255, 255, 0.05); | 1555 | + background: var(--theme-fill); |
| 1298 | } | 1556 | } |
| 1299 | 1557 | ||
| 1300 | .hijo-codigo { | 1558 | .hijo-codigo { |
| 1301 | font-family: 'DM Mono', monospace; | 1559 | font-family: 'DM Mono', monospace; |
| 1302 | font-size: 0.688rem; | 1560 | font-size: 0.688rem; |
| 1303 | - color: #5A5650; | 1561 | + color: var(--theme-texto); |
| 1304 | min-width: 3rem; | 1562 | min-width: 3rem; |
| 1305 | } | 1563 | } |
| 1306 | 1564 | ||
| 1307 | .hijo-nombre { | 1565 | .hijo-nombre { |
| 1308 | font-size: 0.813rem; | 1566 | font-size: 0.813rem; |
| 1309 | - color: #B0A89C; | 1567 | + color: var(--theme-texto); |
| 1310 | } | 1568 | } |
| 1311 | 1569 | ||
| 1312 | .hijo-link:hover .hijo-nombre { | 1570 | .hijo-link:hover .hijo-nombre { |
| 1313 | - color: #F5F0E8; | 1571 | + color: var(--theme-titulo); |
| 1314 | } | 1572 | } |
| 1315 | 1573 | ||
| 1316 | /* Main */ | 1574 | /* Main */ |
| ... | @@ -1330,9 +1588,8 @@ | ... | @@ -1330,9 +1588,8 @@ |
| 1330 | } | 1588 | } |
| 1331 | 1589 | ||
| 1332 | .interaction-hint { | 1590 | .interaction-hint { |
| 1333 | - font-size: 0.688rem; | 1591 | + font-size: 0.75rem; |
| 1334 | - color: #5A5650; | 1592 | + color: var(--theme-texto); |
| 1335 | - font-style: italic; | ||
| 1336 | margin-left: auto; | 1593 | margin-left: auto; |
| 1337 | } | 1594 | } |
| 1338 | 1595 | ||
| ... | @@ -1355,7 +1612,7 @@ | ... | @@ -1355,7 +1612,7 @@ |
| 1355 | 1612 | ||
| 1356 | .selector-label { | 1613 | .selector-label { |
| 1357 | font-size: 0.813rem; | 1614 | font-size: 0.813rem; |
| 1358 | - color: #5A5650; | 1615 | + color: var(--theme-texto); |
| 1359 | } | 1616 | } |
| 1360 | 1617 | ||
| 1361 | .selector-btn { | 1618 | .selector-btn { |
| ... | @@ -1365,18 +1622,18 @@ | ... | @@ -1365,18 +1622,18 @@ |
| 1365 | padding: 0.5rem 1rem; | 1622 | padding: 0.5rem 1rem; |
| 1366 | background: transparent; | 1623 | background: transparent; |
| 1367 | border: none; | 1624 | border: none; |
| 1368 | - box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08); | 1625 | + box-shadow: inset 0 0 0 1px var(--theme-borde); |
| 1369 | border-radius: 8px; | 1626 | border-radius: 8px; |
| 1370 | - color: #F5F0E8; | 1627 | + color: var(--theme-titulo); |
| 1371 | font-size: 0.875rem; | 1628 | font-size: 0.875rem; |
| 1372 | cursor: pointer; | 1629 | cursor: pointer; |
| 1373 | transition: all 0.2s; | 1630 | transition: all 0.2s; |
| 1374 | } | 1631 | } |
| 1375 | .selector-btn:hover { | 1632 | .selector-btn:hover { |
| 1376 | - box-shadow: inset 0 0 0 1px rgba(255,255,255,0.15); | 1633 | + box-shadow: inset 0 0 0 1px var(--theme-accent); |
| 1377 | } | 1634 | } |
| 1378 | .selector-btn svg { | 1635 | .selector-btn svg { |
| 1379 | - color: #5A5650; | 1636 | + color: var(--theme-texto); |
| 1380 | } | 1637 | } |
| 1381 | 1638 | ||
| 1382 | .relative { | 1639 | .relative { |
| ... | @@ -1391,8 +1648,8 @@ | ... | @@ -1391,8 +1648,8 @@ |
| 1391 | width: min(500px, 90vw); | 1648 | width: min(500px, 90vw); |
| 1392 | max-height: 360px; | 1649 | max-height: 360px; |
| 1393 | overflow: hidden; | 1650 | overflow: hidden; |
| 1394 | - background: #1A1A18; | 1651 | + background: var(--theme-surface); |
| 1395 | - border: 1px solid #2E2E2C; | 1652 | + border: 1px solid var(--theme-borde); |
| 1396 | border-radius: 8px; | 1653 | border-radius: 8px; |
| 1397 | box-shadow: 0 10px 40px rgba(0,0,0,0.4); | 1654 | box-shadow: 0 10px 40px rgba(0,0,0,0.4); |
| 1398 | z-index: 50; | 1655 | z-index: 50; |
| ... | @@ -1400,24 +1657,24 @@ | ... | @@ -1400,24 +1657,24 @@ |
| 1400 | 1657 | ||
| 1401 | .dropdown-search { | 1658 | .dropdown-search { |
| 1402 | padding: 0.5rem; | 1659 | padding: 0.5rem; |
| 1403 | - border-bottom: 1px solid #2E2E2C; | 1660 | + border-bottom: 1px solid var(--theme-borde); |
| 1404 | } | 1661 | } |
| 1405 | 1662 | ||
| 1406 | .search-input { | 1663 | .search-input { |
| 1407 | width: 100%; | 1664 | width: 100%; |
| 1408 | padding: 0.5rem 0.75rem; | 1665 | padding: 0.5rem 0.75rem; |
| 1409 | - background: #111110; | 1666 | + background: var(--theme-fill); |
| 1410 | - border: 1px solid #2E2E2C; | 1667 | + border: 1px solid var(--theme-borde); |
| 1411 | border-radius: 6px; | 1668 | border-radius: 6px; |
| 1412 | - color: #F5F0E8; | 1669 | + color: var(--theme-titulo); |
| 1413 | font-size: 0.875rem; | 1670 | font-size: 0.875rem; |
| 1414 | } | 1671 | } |
| 1415 | .search-input:focus { | 1672 | .search-input:focus { |
| 1416 | outline: none; | 1673 | outline: none; |
| 1417 | - border-color: #E8C547; | 1674 | + border-color: var(--theme-accent); |
| 1418 | } | 1675 | } |
| 1419 | .search-input::placeholder { | 1676 | .search-input::placeholder { |
| 1420 | - color: #5A5650; | 1677 | + color: var(--theme-texto); |
| 1421 | } | 1678 | } |
| 1422 | 1679 | ||
| 1423 | .dropdown-item { | 1680 | .dropdown-item { |
| ... | @@ -1428,7 +1685,7 @@ | ... | @@ -1428,7 +1685,7 @@ |
| 1428 | padding: 0.625rem 0.75rem; | 1685 | padding: 0.625rem 0.75rem; |
| 1429 | background: transparent; | 1686 | background: transparent; |
| 1430 | border: none; | 1687 | border: none; |
| 1431 | - color: #F5F0E8; | 1688 | + color: var(--theme-titulo); |
| 1432 | font-size: 0.813rem; | 1689 | font-size: 0.813rem; |
| 1433 | text-align: left; | 1690 | text-align: left; |
| 1434 | cursor: pointer; | 1691 | cursor: pointer; |
| ... | @@ -1449,7 +1706,7 @@ | ... | @@ -1449,7 +1706,7 @@ |
| 1449 | .entity-code { | 1706 | .entity-code { |
| 1450 | font-family: 'DM Mono', monospace; | 1707 | font-family: 'DM Mono', monospace; |
| 1451 | font-size: 0.688rem; | 1708 | font-size: 0.688rem; |
| 1452 | - color: #5A5650; | 1709 | + color: var(--theme-texto); |
| 1453 | min-width: 2.5rem; | 1710 | min-width: 2.5rem; |
| 1454 | } | 1711 | } |
| 1455 | 1712 | ||
| ... | @@ -1499,7 +1756,7 @@ | ... | @@ -1499,7 +1756,7 @@ |
| 1499 | 1756 | ||
| 1500 | .chart-title { | 1757 | .chart-title { |
| 1501 | font-size: 0.75rem; | 1758 | font-size: 0.75rem; |
| 1502 | - color: #5A5650; | 1759 | + color: var(--theme-texto); |
| 1503 | text-transform: uppercase; | 1760 | text-transform: uppercase; |
| 1504 | letter-spacing: 0.1em; | 1761 | letter-spacing: 0.1em; |
| 1505 | } | 1762 | } |
| ... | @@ -1507,7 +1764,7 @@ | ... | @@ -1507,7 +1764,7 @@ |
| 1507 | .chart-period { | 1764 | .chart-period { |
| 1508 | font-family: 'DM Mono', monospace; | 1765 | font-family: 'DM Mono', monospace; |
| 1509 | font-size: 0.875rem; | 1766 | font-size: 0.875rem; |
| 1510 | - color: #B0A89C; | 1767 | + color: var(--theme-texto); |
| 1511 | font-variant-numeric: tabular-nums; | 1768 | font-variant-numeric: tabular-nums; |
| 1512 | min-width: 5rem; | 1769 | min-width: 5rem; |
| 1513 | text-align: right; | 1770 | text-align: right; |
| ... | @@ -1522,7 +1779,7 @@ | ... | @@ -1522,7 +1779,7 @@ |
| 1522 | overflow-x: auto; | 1779 | overflow-x: auto; |
| 1523 | overflow-y: visible; | 1780 | overflow-y: visible; |
| 1524 | scrollbar-width: thin; | 1781 | scrollbar-width: thin; |
| 1525 | - scrollbar-color: #2E2E2C transparent; | 1782 | + scrollbar-color: var(--theme-borde) transparent; |
| 1526 | } | 1783 | } |
| 1527 | 1784 | ||
| 1528 | .chart-wrapper-scroll::-webkit-scrollbar { | 1785 | .chart-wrapper-scroll::-webkit-scrollbar { |
| ... | @@ -1534,7 +1791,7 @@ | ... | @@ -1534,7 +1791,7 @@ |
| 1534 | } | 1791 | } |
| 1535 | 1792 | ||
| 1536 | .chart-wrapper-scroll::-webkit-scrollbar-thumb { | 1793 | .chart-wrapper-scroll::-webkit-scrollbar-thumb { |
| 1537 | - background: #2E2E2C; | 1794 | + background: var(--theme-borde); |
| 1538 | border-radius: 3px; | 1795 | border-radius: 3px; |
| 1539 | } | 1796 | } |
| 1540 | 1797 | ||
| ... | @@ -1542,7 +1799,7 @@ | ... | @@ -1542,7 +1799,7 @@ |
| 1542 | position: sticky; | 1799 | position: sticky; |
| 1543 | left: 0; | 1800 | left: 0; |
| 1544 | z-index: 10; | 1801 | z-index: 10; |
| 1545 | - background: #0A0A0A; | 1802 | + background: var(--theme-body); |
| 1546 | padding-right: 0.5rem; | 1803 | padding-right: 0.5rem; |
| 1547 | margin-right: -0.5rem; | 1804 | margin-right: -0.5rem; |
| 1548 | } | 1805 | } |
| ... | @@ -1567,7 +1824,7 @@ | ... | @@ -1567,7 +1824,7 @@ |
| 1567 | .y-label { | 1824 | .y-label { |
| 1568 | font-family: 'DM Mono', monospace; | 1825 | font-family: 'DM Mono', monospace; |
| 1569 | font-size: 0.625rem; | 1826 | font-size: 0.625rem; |
| 1570 | - color: #5A5650; | 1827 | + color: var(--theme-texto); |
| 1571 | } | 1828 | } |
| 1572 | 1829 | ||
| 1573 | .chart { | 1830 | .chart { |
| ... | @@ -1577,7 +1834,7 @@ | ... | @@ -1577,7 +1834,7 @@ |
| 1577 | gap: 4px; | 1834 | gap: 4px; |
| 1578 | height: 160px; | 1835 | height: 160px; |
| 1579 | padding: 0 0.5rem; | 1836 | padding: 0 0.5rem; |
| 1580 | - border-left: 1px solid #2E2E2C; | 1837 | + border-left: 1px solid var(--theme-borde); |
| 1581 | } | 1838 | } |
| 1582 | 1839 | ||
| 1583 | .bar-container { | 1840 | .bar-container { |
| ... | @@ -1599,14 +1856,14 @@ | ... | @@ -1599,14 +1856,14 @@ |
| 1599 | } | 1856 | } |
| 1600 | 1857 | ||
| 1601 | .bar.hovered { | 1858 | .bar.hovered { |
| 1602 | - background: #E8C547; | 1859 | + background: var(--theme-accent); |
| 1603 | transform: scaleX(1.1); | 1860 | transform: scaleX(1.1); |
| 1604 | } | 1861 | } |
| 1605 | 1862 | ||
| 1606 | .bar-label { | 1863 | .bar-label { |
| 1607 | font-family: 'DM Mono', monospace; | 1864 | font-family: 'DM Mono', monospace; |
| 1608 | font-size: 0.625rem; | 1865 | font-size: 0.625rem; |
| 1609 | - color: #B0A89C; | 1866 | + color: var(--theme-texto); |
| 1610 | margin-top: 0.5rem; | 1867 | margin-top: 0.5rem; |
| 1611 | opacity: 0; | 1868 | opacity: 0; |
| 1612 | transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1), color 0.25s ease; | 1869 | transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1), color 0.25s ease; |
| ... | @@ -1635,7 +1892,7 @@ | ... | @@ -1635,7 +1892,7 @@ |
| 1635 | font-family: 'DM Mono', monospace; | 1892 | font-family: 'DM Mono', monospace; |
| 1636 | font-size: 1.125rem; | 1893 | font-size: 1.125rem; |
| 1637 | font-weight: 500; | 1894 | font-weight: 500; |
| 1638 | - color: #F5F0E8; | 1895 | + color: var(--theme-titulo); |
| 1639 | margin-bottom: 0; | 1896 | margin-bottom: 0; |
| 1640 | transition: opacity 0.2s ease; | 1897 | transition: opacity 0.2s ease; |
| 1641 | font-variant-numeric: tabular-nums; | 1898 | font-variant-numeric: tabular-nums; |
| ... | @@ -1644,7 +1901,7 @@ | ... | @@ -1644,7 +1901,7 @@ |
| 1644 | 1901 | ||
| 1645 | .kpi-label { | 1902 | .kpi-label { |
| 1646 | font-size: 0.5625rem; | 1903 | font-size: 0.5625rem; |
| 1647 | - color: #5A5650; | 1904 | + color: var(--theme-texto); |
| 1648 | text-transform: uppercase; | 1905 | text-transform: uppercase; |
| 1649 | letter-spacing: 0.03em; | 1906 | letter-spacing: 0.03em; |
| 1650 | line-height: 1.3; | 1907 | line-height: 1.3; |
| ... | @@ -1666,7 +1923,7 @@ | ... | @@ -1666,7 +1923,7 @@ |
| 1666 | .top-section h3 { | 1923 | .top-section h3 { |
| 1667 | font-family: 'DM Mono', monospace; | 1924 | font-family: 'DM Mono', monospace; |
| 1668 | font-size: 0.75rem; | 1925 | font-size: 0.75rem; |
| 1669 | - color: #5A5650; | 1926 | + color: var(--theme-texto); |
| 1670 | text-transform: uppercase; | 1927 | text-transform: uppercase; |
| 1671 | letter-spacing: 0.1em; | 1928 | letter-spacing: 0.1em; |
| 1672 | margin: 0; | 1929 | margin: 0; |
| ... | @@ -1675,7 +1932,7 @@ | ... | @@ -1675,7 +1932,7 @@ |
| 1675 | .top-context { | 1932 | .top-context { |
| 1676 | font-family: 'DM Mono', monospace; | 1933 | font-family: 'DM Mono', monospace; |
| 1677 | font-size: 0.688rem; | 1934 | font-size: 0.688rem; |
| 1678 | - color: #5A5650; | 1935 | + color: var(--theme-texto); |
| 1679 | font-variant-numeric: tabular-nums; | 1936 | font-variant-numeric: tabular-nums; |
| 1680 | } | 1937 | } |
| 1681 | 1938 | ||
| ... | @@ -1703,7 +1960,7 @@ | ... | @@ -1703,7 +1960,7 @@ |
| 1703 | .top-rank { | 1960 | .top-rank { |
| 1704 | font-family: 'DM Mono', monospace; | 1961 | font-family: 'DM Mono', monospace; |
| 1705 | font-size: 0.875rem; | 1962 | font-size: 0.875rem; |
| 1706 | - color: #5A5650; | 1963 | + color: var(--theme-texto); |
| 1707 | width: 24px; | 1964 | width: 24px; |
| 1708 | } | 1965 | } |
| 1709 | 1966 | ||
| ... | @@ -1714,32 +1971,32 @@ | ... | @@ -1714,32 +1971,32 @@ |
| 1714 | .top-name { | 1971 | .top-name { |
| 1715 | display: block; | 1972 | display: block; |
| 1716 | font-size: 0.8125rem; | 1973 | font-size: 0.8125rem; |
| 1717 | - color: #F5F0E8; | 1974 | + color: var(--theme-titulo); |
| 1718 | margin-bottom: 0.375rem; | 1975 | margin-bottom: 0.375rem; |
| 1719 | } | 1976 | } |
| 1720 | 1977 | ||
| 1721 | .top-bar-bg { | 1978 | .top-bar-bg { |
| 1722 | height: 4px; | 1979 | height: 4px; |
| 1723 | - background: #1E1E1C; | 1980 | + background: var(--theme-surface); |
| 1724 | border-radius: 2px; | 1981 | border-radius: 2px; |
| 1725 | overflow: hidden; | 1982 | overflow: hidden; |
| 1726 | } | 1983 | } |
| 1727 | 1984 | ||
| 1728 | .top-bar { | 1985 | .top-bar { |
| 1729 | height: 100%; | 1986 | height: 100%; |
| 1730 | - background: #C9A751; | 1987 | + background: var(--theme-accent); |
| 1731 | border-radius: 2px; | 1988 | border-radius: 2px; |
| 1732 | transition: width 0.35s cubic-bezier(0.4, 0, 0.2, 1), background 0.25s ease; | 1989 | transition: width 0.35s cubic-bezier(0.4, 0, 0.2, 1), background 0.25s ease; |
| 1733 | } | 1990 | } |
| 1734 | 1991 | ||
| 1735 | .top-item:hover .top-bar { | 1992 | .top-item:hover .top-bar { |
| 1736 | - background: #E8C547; | 1993 | + background: var(--theme-accent); |
| 1737 | } | 1994 | } |
| 1738 | 1995 | ||
| 1739 | .top-pct { | 1996 | .top-pct { |
| 1740 | font-family: 'DM Mono', monospace; | 1997 | font-family: 'DM Mono', monospace; |
| 1741 | font-size: 0.875rem; | 1998 | font-size: 0.875rem; |
| 1742 | - color: #B0A89C; | 1999 | + color: var(--theme-texto); |
| 1743 | min-width: 52px; | 2000 | min-width: 52px; |
| 1744 | width: 52px; | 2001 | width: 52px; |
| 1745 | text-align: right; | 2002 | text-align: right; |
| ... | @@ -1749,10 +2006,10 @@ | ... | @@ -1749,10 +2006,10 @@ |
| 1749 | 2006 | ||
| 1750 | .see-all { | 2007 | .see-all { |
| 1751 | background: transparent; | 2008 | background: transparent; |
| 1752 | - border: 1px solid #2E2E2C; | 2009 | + border: 1px solid var(--theme-borde); |
| 1753 | border-radius: 8px; | 2010 | border-radius: 8px; |
| 1754 | padding: 0.75rem 1rem; | 2011 | padding: 0.75rem 1rem; |
| 1755 | - color: #8A8578; | 2012 | + color: var(--theme-texto); |
| 1756 | font-size: 0.813rem; | 2013 | font-size: 0.813rem; |
| 1757 | cursor: pointer; | 2014 | cursor: pointer; |
| 1758 | transition: all 0.2s; | 2015 | transition: all 0.2s; |
| ... | @@ -1760,8 +2017,8 @@ | ... | @@ -1760,8 +2017,8 @@ |
| 1760 | margin-top: 1rem; | 2017 | margin-top: 1rem; |
| 1761 | } | 2018 | } |
| 1762 | .see-all:hover { | 2019 | .see-all:hover { |
| 1763 | - border-color: #E8C547; | 2020 | + border-color: var(--theme-accent); |
| 1764 | - color: #E8C547; | 2021 | + color: var(--theme-accent); |
| 1765 | } | 2022 | } |
| 1766 | 2023 | ||
| 1767 | /* Vista Toggle */ | 2024 | /* Vista Toggle */ |
| ... | @@ -1778,7 +2035,7 @@ | ... | @@ -1778,7 +2035,7 @@ |
| 1778 | padding: 0.5rem 1.25rem; | 2035 | padding: 0.5rem 1.25rem; |
| 1779 | font-size: 0.813rem; | 2036 | font-size: 0.813rem; |
| 1780 | font-weight: 500; | 2037 | font-weight: 500; |
| 1781 | - color: #5A5650; | 2038 | + color: var(--theme-texto); |
| 1782 | background: transparent; | 2039 | background: transparent; |
| 1783 | border: none; | 2040 | border: none; |
| 1784 | border-radius: 7px; | 2041 | border-radius: 7px; |
| ... | @@ -1787,12 +2044,12 @@ | ... | @@ -1787,12 +2044,12 @@ |
| 1787 | } | 2044 | } |
| 1788 | 2045 | ||
| 1789 | .toggle-btn:hover { | 2046 | .toggle-btn:hover { |
| 1790 | - color: #8A8578; | 2047 | + color: var(--theme-texto); |
| 1791 | } | 2048 | } |
| 1792 | 2049 | ||
| 1793 | .toggle-btn.active { | 2050 | .toggle-btn.active { |
| 1794 | - background: #F5F0E8; | 2051 | + background: var(--theme-titulo); |
| 1795 | - color: #0A0A0A; | 2052 | + color: var(--theme-body); |
| 1796 | } | 2053 | } |
| 1797 | 2054 | ||
| 1798 | /* Comparador inline - ajustado para 100vh */ | 2055 | /* Comparador inline - ajustado para 100vh */ |
| ... | @@ -1823,20 +2080,20 @@ | ... | @@ -1823,20 +2080,20 @@ |
| 1823 | justify-content: center; | 2080 | justify-content: center; |
| 1824 | min-height: 200px; | 2081 | min-height: 200px; |
| 1825 | background: rgba(255,255,255,0.02); | 2082 | background: rgba(255,255,255,0.02); |
| 1826 | - border: 1px dashed #2E2E2C; | 2083 | + border: 1px dashed var(--theme-borde); |
| 1827 | border-radius: 12px; | 2084 | border-radius: 12px; |
| 1828 | padding: 2rem; | 2085 | padding: 2rem; |
| 1829 | text-align: center; | 2086 | text-align: center; |
| 1830 | } | 2087 | } |
| 1831 | 2088 | ||
| 1832 | .comparador-placeholder p { | 2089 | .comparador-placeholder p { |
| 1833 | - color: #5A5650; | 2090 | + color: var(--theme-texto); |
| 1834 | font-size: 0.875rem; | 2091 | font-size: 0.875rem; |
| 1835 | margin: 0; | 2092 | margin: 0; |
| 1836 | } | 2093 | } |
| 1837 | 2094 | ||
| 1838 | .comparador-placeholder strong { | 2095 | .comparador-placeholder strong { |
| 1839 | - color: #8A8578; | 2096 | + color: var(--theme-texto); |
| 1840 | } | 2097 | } |
| 1841 | 2098 | ||
| 1842 | .rango-selector { | 2099 | .rango-selector { |
| ... | @@ -1848,7 +2105,7 @@ | ... | @@ -1848,7 +2105,7 @@ |
| 1848 | 2105 | ||
| 1849 | .rango-label { | 2106 | .rango-label { |
| 1850 | font-size: 0.75rem; | 2107 | font-size: 0.75rem; |
| 1851 | - color: #5A5650; | 2108 | + color: var(--theme-texto); |
| 1852 | } | 2109 | } |
| 1853 | 2110 | ||
| 1854 | .rango-options { | 2111 | .rango-options { |
| ... | @@ -1860,7 +2117,7 @@ | ... | @@ -1860,7 +2117,7 @@ |
| 1860 | padding: 0.375rem 0.75rem; | 2117 | padding: 0.375rem 0.75rem; |
| 1861 | font-size: 0.6875rem; | 2118 | font-size: 0.6875rem; |
| 1862 | font-family: 'DM Mono', monospace; | 2119 | font-family: 'DM Mono', monospace; |
| 1863 | - color: #5A5650; | 2120 | + color: var(--theme-texto); |
| 1864 | background: transparent; | 2121 | background: transparent; |
| 1865 | border: none; | 2122 | border: none; |
| 1866 | border-radius: 6px; | 2123 | border-radius: 6px; |
| ... | @@ -1869,12 +2126,12 @@ | ... | @@ -1869,12 +2126,12 @@ |
| 1869 | } | 2126 | } |
| 1870 | 2127 | ||
| 1871 | .rango-btn:hover { | 2128 | .rango-btn:hover { |
| 1872 | - color: #8A8578; | 2129 | + color: var(--theme-texto); |
| 1873 | background: rgba(255,255,255,0.05); | 2130 | background: rgba(255,255,255,0.05); |
| 1874 | } | 2131 | } |
| 1875 | 2132 | ||
| 1876 | .rango-btn.active { | 2133 | .rango-btn.active { |
| 1877 | - color: #F5F0E8; | 2134 | + color: var(--theme-titulo); |
| 1878 | background: rgba(255,255,255,0.1); | 2135 | background: rgba(255,255,255,0.1); |
| 1879 | } | 2136 | } |
| 1880 | 2137 | ||
| ... | @@ -1904,20 +2161,20 @@ | ... | @@ -1904,20 +2161,20 @@ |
| 1904 | box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08); | 2161 | box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08); |
| 1905 | border-radius: 8px; | 2162 | border-radius: 8px; |
| 1906 | padding: 0.5rem 0.75rem; | 2163 | padding: 0.5rem 0.75rem; |
| 1907 | - color: #F5F0E8; | 2164 | + color: var(--theme-titulo); |
| 1908 | font-size: 0.813rem; | 2165 | font-size: 0.813rem; |
| 1909 | font-family: 'Instrument Sans', sans-serif; | 2166 | font-family: 'Instrument Sans', sans-serif; |
| 1910 | cursor: pointer; | 2167 | cursor: pointer; |
| 1911 | } | 2168 | } |
| 1912 | .selector-group select:focus { | 2169 | .selector-group select:focus { |
| 1913 | outline: none; | 2170 | outline: none; |
| 1914 | - border-color: #E8C547; | 2171 | + border-color: var(--theme-accent); |
| 1915 | } | 2172 | } |
| 1916 | 2173 | ||
| 1917 | .vs { | 2174 | .vs { |
| 1918 | font-family: 'DM Mono', monospace; | 2175 | font-family: 'DM Mono', monospace; |
| 1919 | font-size: 0.75rem; | 2176 | font-size: 0.75rem; |
| 1920 | - color: #5A5650; | 2177 | + color: var(--theme-texto); |
| 1921 | flex-shrink: 0; | 2178 | flex-shrink: 0; |
| 1922 | } | 2179 | } |
| 1923 | 2180 | ||
| ... | @@ -1973,7 +2230,7 @@ | ... | @@ -1973,7 +2230,7 @@ |
| 1973 | } | 2230 | } |
| 1974 | 2231 | ||
| 1975 | .bar-label.highlight { | 2232 | .bar-label.highlight { |
| 1976 | - color: #F5F0E8; | 2233 | + color: var(--theme-titulo); |
| 1977 | font-weight: 500; | 2234 | font-weight: 500; |
| 1978 | transition: color 0.25s ease; | 2235 | transition: color 0.25s ease; |
| 1979 | } | 2236 | } |
| ... | @@ -1991,7 +2248,7 @@ | ... | @@ -1991,7 +2248,7 @@ |
| 1991 | align-items: center; | 2248 | align-items: center; |
| 1992 | gap: 0.5rem; | 2249 | gap: 0.5rem; |
| 1993 | font-size: 0.75rem; | 2250 | font-size: 0.75rem; |
| 1994 | - color: #8A8578; | 2251 | + color: var(--theme-texto); |
| 1995 | } | 2252 | } |
| 1996 | 2253 | ||
| 1997 | .legend-dot { | 2254 | .legend-dot { |
| ... | @@ -2013,7 +2270,7 @@ | ... | @@ -2013,7 +2270,7 @@ |
| 2013 | grid-template-columns: 1fr 1fr 1fr; | 2270 | grid-template-columns: 1fr 1fr 1fr; |
| 2014 | gap: 1rem; | 2271 | gap: 1rem; |
| 2015 | padding-bottom: 0.375rem; | 2272 | padding-bottom: 0.375rem; |
| 2016 | - border-bottom: 1px solid #2E2E2C; | 2273 | + border-bottom: 1px solid var(--theme-borde); |
| 2017 | margin-bottom: 0.375rem; | 2274 | margin-bottom: 0.375rem; |
| 2018 | } | 2275 | } |
| 2019 | 2276 | ||
| ... | @@ -2036,7 +2293,7 @@ | ... | @@ -2036,7 +2293,7 @@ |
| 2036 | font-family: 'DM Mono', monospace; | 2293 | font-family: 'DM Mono', monospace; |
| 2037 | font-size: 0.75rem; | 2294 | font-size: 0.75rem; |
| 2038 | font-weight: 500; | 2295 | font-weight: 500; |
| 2039 | - color: #8A8578; | 2296 | + color: var(--theme-texto); |
| 2040 | display: flex; | 2297 | display: flex; |
| 2041 | align-items: center; | 2298 | align-items: center; |
| 2042 | text-transform: uppercase; | 2299 | text-transform: uppercase; |
| ... | @@ -2047,7 +2304,7 @@ | ... | @@ -2047,7 +2304,7 @@ |
| 2047 | font-family: 'DM Mono', monospace; | 2304 | font-family: 'DM Mono', monospace; |
| 2048 | font-size: 0.9375rem; | 2305 | font-size: 0.9375rem; |
| 2049 | font-weight: 500; | 2306 | font-weight: 500; |
| 2050 | - color: #F5F0E8; | 2307 | + color: var(--theme-titulo); |
| 2051 | text-align: center; | 2308 | text-align: center; |
| 2052 | font-variant-numeric: tabular-nums; | 2309 | font-variant-numeric: tabular-nums; |
| 2053 | transition: opacity 0.2s ease; | 2310 | transition: opacity 0.2s ease; |
| ... | @@ -2059,20 +2316,20 @@ | ... | @@ -2059,20 +2316,20 @@ |
| 2059 | padding: 1rem 1.5rem; | 2316 | padding: 1rem 1.5rem; |
| 2060 | background: linear-gradient(90deg, rgba(232,197,71,0.05), rgba(74,139,110,0.05)); | 2317 | background: linear-gradient(90deg, rgba(232,197,71,0.05), rgba(74,139,110,0.05)); |
| 2061 | border-radius: 10px; | 2318 | border-radius: 10px; |
| 2062 | - border: 1px solid #2E2E2C; | 2319 | + border: 1px solid var(--theme-borde); |
| 2063 | font-size: 0.875rem; | 2320 | font-size: 0.875rem; |
| 2064 | - color: #8A8578; | 2321 | + color: var(--theme-texto); |
| 2065 | line-height: 1.6; | 2322 | line-height: 1.6; |
| 2066 | } | 2323 | } |
| 2067 | 2324 | ||
| 2068 | .comparador-insight strong { | 2325 | .comparador-insight strong { |
| 2069 | - color: #F5F0E8; | 2326 | + color: var(--theme-titulo); |
| 2070 | } | 2327 | } |
| 2071 | 2328 | ||
| 2072 | .insight-year { | 2329 | .insight-year { |
| 2073 | font-family: 'DM Mono', monospace; | 2330 | font-family: 'DM Mono', monospace; |
| 2074 | font-size: 0.813rem; | 2331 | font-size: 0.813rem; |
| 2075 | - color: #E8C547; | 2332 | + color: var(--theme-accent); |
| 2076 | } | 2333 | } |
| 2077 | 2334 | ||
| 2078 | /* Responsive */ | 2335 | /* Responsive */ |
| ... | @@ -2147,335 +2404,5 @@ | ... | @@ -2147,335 +2404,5 @@ |
| 2147 | } | 2404 | } |
| 2148 | } | 2405 | } |
| 2149 | 2406 | ||
| 2150 | - /* ========== TEMA CLARO ========== */ | 2407 | + /* Tema claro: las variables CSS cambian automáticamente via :root.dark */ |
| 2151 | - .tema-claro { | ||
| 2152 | - background: #FAFAF8; | ||
| 2153 | - color: #1A1A18; | ||
| 2154 | - } | ||
| 2155 | - | ||
| 2156 | - .tema-claro header { | ||
| 2157 | - border-bottom-color: #E5E5E0; | ||
| 2158 | - } | ||
| 2159 | - | ||
| 2160 | - .tema-claro .breadcrumb a { | ||
| 2161 | - color: #8A8578; | ||
| 2162 | - } | ||
| 2163 | - .tema-claro .breadcrumb a:hover { | ||
| 2164 | - color: #1A1A18; | ||
| 2165 | - } | ||
| 2166 | - | ||
| 2167 | - .tema-claro .breadcrumb-sep { | ||
| 2168 | - color: #C5C5C0; | ||
| 2169 | - } | ||
| 2170 | - | ||
| 2171 | - .tema-claro .codigo { | ||
| 2172 | - color: #8A8578; | ||
| 2173 | - } | ||
| 2174 | - | ||
| 2175 | - .tema-claro h1 { | ||
| 2176 | - color: #1A1A18; | ||
| 2177 | - } | ||
| 2178 | - | ||
| 2179 | - .tema-claro .help-icon { | ||
| 2180 | - color: #8A8578; | ||
| 2181 | - } | ||
| 2182 | - .tema-claro .help-icon:hover, | ||
| 2183 | - .tema-claro .help-icon.active { | ||
| 2184 | - color: #C9A227; | ||
| 2185 | - } | ||
| 2186 | - | ||
| 2187 | - .tema-claro .badge { | ||
| 2188 | - background: #F0F0EC; | ||
| 2189 | - border-color: #E5E5E0; | ||
| 2190 | - color: #5A5650; | ||
| 2191 | - } | ||
| 2192 | - | ||
| 2193 | - .tema-claro .years { | ||
| 2194 | - color: #8A8578; | ||
| 2195 | - } | ||
| 2196 | - | ||
| 2197 | - .tema-claro .info-panel { | ||
| 2198 | - background: #F5F5F3; | ||
| 2199 | - border-bottom-color: #E5E5E0; | ||
| 2200 | - } | ||
| 2201 | - | ||
| 2202 | - .tema-claro .info-content h4 { | ||
| 2203 | - color: #8A8578; | ||
| 2204 | - } | ||
| 2205 | - | ||
| 2206 | - .tema-claro .info-content p { | ||
| 2207 | - color: #5A5650; | ||
| 2208 | - } | ||
| 2209 | - | ||
| 2210 | - .tema-claro .variacion { | ||
| 2211 | - border-left-color: #D5D5D0; | ||
| 2212 | - } | ||
| 2213 | - | ||
| 2214 | - .tema-claro .variacion-rango { | ||
| 2215 | - color: #8A8578; | ||
| 2216 | - } | ||
| 2217 | - | ||
| 2218 | - .tema-claro .hijo-link:hover { | ||
| 2219 | - background: rgba(0, 0, 0, 0.03); | ||
| 2220 | - } | ||
| 2221 | - | ||
| 2222 | - .tema-claro .hijo-codigo { | ||
| 2223 | - color: #8A8578; | ||
| 2224 | - } | ||
| 2225 | - | ||
| 2226 | - .tema-claro .hijo-nombre { | ||
| 2227 | - color: #5A5650; | ||
| 2228 | - } | ||
| 2229 | - | ||
| 2230 | - .tema-claro .hijo-link:hover .hijo-nombre { | ||
| 2231 | - color: #1A1A18; | ||
| 2232 | - } | ||
| 2233 | - | ||
| 2234 | - .tema-claro .vista-toggle { | ||
| 2235 | - background: transparent; | ||
| 2236 | - box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08); | ||
| 2237 | - } | ||
| 2238 | - | ||
| 2239 | - .tema-claro .toggle-btn { | ||
| 2240 | - color: #8A8578; | ||
| 2241 | - } | ||
| 2242 | - .tema-claro .toggle-btn:hover { | ||
| 2243 | - color: #5A5650; | ||
| 2244 | - } | ||
| 2245 | - .tema-claro .toggle-btn.active { | ||
| 2246 | - background: #1A1A18; | ||
| 2247 | - color: #FAFAF8; | ||
| 2248 | - } | ||
| 2249 | - | ||
| 2250 | - .tema-claro .entity-selector .selector-label { | ||
| 2251 | - color: #8A8578; | ||
| 2252 | - } | ||
| 2253 | - | ||
| 2254 | - .tema-claro .interaction-hint { | ||
| 2255 | - color: #8A8578; | ||
| 2256 | - } | ||
| 2257 | - | ||
| 2258 | - .tema-claro .selector-btn { | ||
| 2259 | - background: transparent; | ||
| 2260 | - border: none; | ||
| 2261 | - box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08); | ||
| 2262 | - color: #1A1A18; | ||
| 2263 | - } | ||
| 2264 | - .tema-claro .selector-btn:hover { | ||
| 2265 | - box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15); | ||
| 2266 | - } | ||
| 2267 | - .tema-claro .selector-btn svg { | ||
| 2268 | - color: #8A8578; | ||
| 2269 | - } | ||
| 2270 | - | ||
| 2271 | - .tema-claro .dropdown-panel { | ||
| 2272 | - background: #FAFAF8; | ||
| 2273 | - border-color: #E5E5E0; | ||
| 2274 | - } | ||
| 2275 | - | ||
| 2276 | - .tema-claro .dropdown-search { | ||
| 2277 | - border-bottom-color: #E5E5E0; | ||
| 2278 | - } | ||
| 2279 | - | ||
| 2280 | - .tema-claro .search-input { | ||
| 2281 | - background: #F5F5F3; | ||
| 2282 | - border-color: #E5E5E0; | ||
| 2283 | - color: #1A1A18; | ||
| 2284 | - } | ||
| 2285 | - .tema-claro .search-input:focus { | ||
| 2286 | - border-color: #C9A227; | ||
| 2287 | - } | ||
| 2288 | - .tema-claro .search-input::placeholder { | ||
| 2289 | - color: #8A8578; | ||
| 2290 | - } | ||
| 2291 | - | ||
| 2292 | - .tema-claro .dropdown-item { | ||
| 2293 | - color: #1A1A18; | ||
| 2294 | - } | ||
| 2295 | - .tema-claro .dropdown-item:hover { | ||
| 2296 | - background: rgba(0,0,0,0.03); | ||
| 2297 | - } | ||
| 2298 | - .tema-claro .dropdown-item.active { | ||
| 2299 | - background: rgba(201,162,39,0.12); | ||
| 2300 | - } | ||
| 2301 | - | ||
| 2302 | - .tema-claro .entity-code { | ||
| 2303 | - color: #8A8578; | ||
| 2304 | - } | ||
| 2305 | - | ||
| 2306 | - .tema-claro .chart-title { | ||
| 2307 | - color: #8A8578; | ||
| 2308 | - } | ||
| 2309 | - | ||
| 2310 | - .tema-claro .chart-period { | ||
| 2311 | - color: #333333; | ||
| 2312 | - } | ||
| 2313 | - | ||
| 2314 | - .tema-claro .y-label { | ||
| 2315 | - color: #8A8578; | ||
| 2316 | - } | ||
| 2317 | - | ||
| 2318 | - .tema-claro .y-axis-sticky { | ||
| 2319 | - background: #FAFAF8; | ||
| 2320 | - } | ||
| 2321 | - | ||
| 2322 | - .tema-claro .chart-wrapper-scroll::-webkit-scrollbar-thumb { | ||
| 2323 | - background: #D5D5D0; | ||
| 2324 | - } | ||
| 2325 | - | ||
| 2326 | - .tema-claro .chart { | ||
| 2327 | - border-left-color: #E5E5E0; | ||
| 2328 | - } | ||
| 2329 | - | ||
| 2330 | - .tema-claro .bar-container .bar { | ||
| 2331 | - background: #2a9d8f; | ||
| 2332 | - } | ||
| 2333 | - | ||
| 2334 | - .tema-claro .bar-container .bar.hovered { | ||
| 2335 | - background: #C9A751; | ||
| 2336 | - } | ||
| 2337 | - | ||
| 2338 | - /* Las barras de comparación mantienen su --bar-color */ | ||
| 2339 | - .tema-claro .bar-a, | ||
| 2340 | - .tema-claro .bar-b { | ||
| 2341 | - background: var(--bar-color); | ||
| 2342 | - } | ||
| 2343 | - | ||
| 2344 | - .tema-claro .bar-label { | ||
| 2345 | - color: #333333; | ||
| 2346 | - } | ||
| 2347 | - | ||
| 2348 | - .tema-claro .kpis { | ||
| 2349 | - background: transparent; | ||
| 2350 | - } | ||
| 2351 | - | ||
| 2352 | - .tema-claro .kpi { | ||
| 2353 | - background: transparent; | ||
| 2354 | - box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08); | ||
| 2355 | - } | ||
| 2356 | - | ||
| 2357 | - .tema-claro .kpi-value { | ||
| 2358 | - color: #1A1A18; | ||
| 2359 | - } | ||
| 2360 | - | ||
| 2361 | - .tema-claro .kpi-label { | ||
| 2362 | - color: #8A8578; | ||
| 2363 | - } | ||
| 2364 | - | ||
| 2365 | - .tema-claro .top-section h3 { | ||
| 2366 | - color: #8A8578; | ||
| 2367 | - } | ||
| 2368 | - | ||
| 2369 | - .tema-claro .top-context { | ||
| 2370 | - color: #8A8578; | ||
| 2371 | - } | ||
| 2372 | - | ||
| 2373 | - .tema-claro .top-item { | ||
| 2374 | - background: transparent; | ||
| 2375 | - box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08); | ||
| 2376 | - } | ||
| 2377 | - | ||
| 2378 | - .tema-claro .top-rank { | ||
| 2379 | - color: #8A8578; | ||
| 2380 | - } | ||
| 2381 | - | ||
| 2382 | - .tema-claro .top-name { | ||
| 2383 | - color: #1A1A18; | ||
| 2384 | - } | ||
| 2385 | - | ||
| 2386 | - .tema-claro .top-bar-bg { | ||
| 2387 | - background: #F0F0EC; | ||
| 2388 | - } | ||
| 2389 | - | ||
| 2390 | - .tema-claro .top-bar { | ||
| 2391 | - background: #C9A751; | ||
| 2392 | - } | ||
| 2393 | - | ||
| 2394 | - .tema-claro .top-item:hover { | ||
| 2395 | - box-shadow: inset 0 0 0 1px rgba(201, 167, 81, 0.5); | ||
| 2396 | - } | ||
| 2397 | - | ||
| 2398 | - .tema-claro .top-item:hover .top-bar { | ||
| 2399 | - background: #B8963F; | ||
| 2400 | - } | ||
| 2401 | - | ||
| 2402 | - .tema-claro .top-pct { | ||
| 2403 | - color: #333333; | ||
| 2404 | - } | ||
| 2405 | - | ||
| 2406 | - /* Comparador - tema claro */ | ||
| 2407 | - .tema-claro .comparador-selectors .selector-group select { | ||
| 2408 | - background: transparent; | ||
| 2409 | - border: none; | ||
| 2410 | - box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08); | ||
| 2411 | - color: #1A1A18; | ||
| 2412 | - } | ||
| 2413 | - .tema-claro .comparador-selectors .selector-group select:focus { | ||
| 2414 | - box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15); | ||
| 2415 | - outline: none; | ||
| 2416 | - } | ||
| 2417 | - | ||
| 2418 | - .tema-claro .comparador-placeholder { | ||
| 2419 | - background: rgba(0,0,0,0.02); | ||
| 2420 | - border-color: #E5E5E0; | ||
| 2421 | - } | ||
| 2422 | - | ||
| 2423 | - .tema-claro .comparador-placeholder p { | ||
| 2424 | - color: #8A8578; | ||
| 2425 | - } | ||
| 2426 | - | ||
| 2427 | - .tema-claro .comparador-placeholder strong { | ||
| 2428 | - color: #5A5650; | ||
| 2429 | - } | ||
| 2430 | - | ||
| 2431 | - .tema-claro .rango-label { | ||
| 2432 | - color: #8A8578; | ||
| 2433 | - } | ||
| 2434 | - | ||
| 2435 | - .tema-claro .rango-btn { | ||
| 2436 | - color: #8A8578; | ||
| 2437 | - } | ||
| 2438 | - | ||
| 2439 | - .tema-claro .rango-btn:hover { | ||
| 2440 | - color: #5A5650; | ||
| 2441 | - background: rgba(0,0,0,0.05); | ||
| 2442 | - } | ||
| 2443 | - | ||
| 2444 | - .tema-claro .rango-btn.active { | ||
| 2445 | - color: #1A1A18; | ||
| 2446 | - background: rgba(0,0,0,0.08); | ||
| 2447 | - } | ||
| 2448 | - | ||
| 2449 | - .tema-claro .vs { | ||
| 2450 | - color: #8A8578; | ||
| 2451 | - } | ||
| 2452 | - | ||
| 2453 | - .tema-claro .chart-legend .legend-item { | ||
| 2454 | - color: #5A5650; | ||
| 2455 | - } | ||
| 2456 | - | ||
| 2457 | - .tema-claro .kpis-tabla { | ||
| 2458 | - background: transparent; | ||
| 2459 | - box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08); | ||
| 2460 | - } | ||
| 2461 | - | ||
| 2462 | - .tema-claro .tabla-header { | ||
| 2463 | - border-bottom-color: #E5E5E0; | ||
| 2464 | - } | ||
| 2465 | - | ||
| 2466 | - .tema-claro .tabla-label { | ||
| 2467 | - color: #5A5650; | ||
| 2468 | - } | ||
| 2469 | - | ||
| 2470 | - .tema-claro .tabla-val { | ||
| 2471 | - color: #1A1A18; | ||
| 2472 | - } | ||
| 2473 | - | ||
| 2474 | - .tema-claro .bar-group.active { | ||
| 2475 | - background: rgba(42, 157, 143, 0.12); | ||
| 2476 | - } | ||
| 2477 | - | ||
| 2478 | - .tema-claro .bar-label.highlight { | ||
| 2479 | - color: #1A1A18; | ||
| 2480 | - } | ||
| 2481 | </style> | 2408 | </style> | ... | ... |
-
Please register or login to post a comment