File size: 20,467 Bytes
a95f844
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
<!doctype html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Mapa íbero · Estilo Neoíbero</title>
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
  <style>
    /* === Paleta y estilo inspirados en la UI (Neoíbero) === */
    @font-face {
      font-family: 'IberiaGeorgeos';
      src: local('IberiaGeorgeos'), local('Georgia');
      font-weight: normal; font-style: normal;
    }
    :root {
      --iberian-clay:#8B4513;
      --iberian-ochre:#CC7722;
      --iberian-stone:#5C5C5C;
      --iberian-sand:#D2B48C;
      --iberian-rust:#A0522D;
      --iberian-bronze:#CD7F32;
      --bg-grad-1:#f4e8d8;
      --bg-grad-2:#e8d5c4;
      --bg-grad-3:#d4c4b0;
    }
    *{box-sizing:border-box}
    body{
      margin:0;
      background:linear-gradient(135deg,#f4e8d8 0%,#e8d5c4 50%,#d4c4b0 100%)!important;
      color:var(--iberian-stone);
      font-family: Georgia, 'Times New Roman', serif;
    }
    .wrap{max-width:1100px;margin:0 auto;padding:24px}
    h1,h2,h3{
      color:var(--iberian-clay);
      text-shadow:2px 2px 4px rgba(139,69,19,.15);
      letter-spacing:.5px;
    }
    h1{
      font-size:clamp(22px,2.6vw,32px);
      margin:0 0 8px;
      border-bottom:3px solid var(--iberian-bronze);
      padding-bottom:.5rem;
    }
    .lead{margin:0 0 16px;color:#5a534c}
    .grid{display:grid;gap:12px;grid-template-columns:1fr}
    @media (min-width: 1024px){ .grid{grid-template-columns:2fr 1fr 1fr} }
    .card{
      background:linear-gradient(to bottom,#f9f6f0,#ede6dc);
      border:2px solid var(--iberian-sand);
      border-radius:10px;
      box-shadow:0 4px 12px rgba(139,69,19,.18), inset 0 1px 0 rgba(255,255,255,.5);
      padding:14px;
    }
    label, input, button{font-size:14px}
    input[type="text"]{
      width:100%;padding:8px 10px;border:2px solid var(--iberian-sand);
      background:linear-gradient(to bottom,#faf8f3,#f5f0e8);
      border-radius:8px;outline:none;color:var(--iberian-stone);
      box-shadow:inset 2px 2px 4px rgba(139,69,19,.1);
    }
    input[type="text"]:focus{border-color:var(--iberian-bronze); box-shadow:inset 2px 2px 4px rgba(139,69,19,.1), 0 0 8px rgba(205,127,50,.3)}
    input[type="range"]{width:100%}
    .hint{font-size:12px;color:#6f655c;margin-top:6px}
    .btn{
      display:inline-block;background:linear-gradient(145deg,var(--iberian-bronze),var(--iberian-rust));
      color:#fff;border:2px solid var(--iberian-clay);border-radius:8px;padding:.6rem 1rem;
      text-decoration:none;font-weight:600;box-shadow:0 4px 8px rgba(139,69,19,.3), inset 0 1px 0 rgba(255,255,255,.2);
    }
    .btn:hover{background:linear-gradient(145deg,var(--iberian-rust),var(--iberian-bronze));transform:translateY(-1px)}
    .ticks{display:flex;justify-content:space-between;font-size:11px;color:#6b7280;user-select:none;margin-top:6px}
    .map-wrap{position:relative}
    #map{
      height:clamp(420px, 70vh, 1000px);
      width:100%;
      border-radius:10px;
      border:2px solid var(--iberian-sand);
      box-shadow:0 4px 12px rgba(139,69,19,.25)
    }
    .legend{
      position:absolute;right:12px;top:12px;background:rgba(255,255,255,.9);
      border:2px solid var(--iberian-sand);border-radius:10px;padding:8px 10px;font-size:12px;
      box-shadow:2px 2px 6px rgba(0,0,0,.15)
    }
    .dot{display:inline-block;width:9px;height:9px;border-radius:999px;margin-right:6px;vertical-align:middle}
    .dot-oppida{background:var(--iberian-clay)}
    .dot-contact{background:var(--iberian-bronze)}
    .dot-tribe{background:transparent;border:2px solid var(--iberian-rust)}
    .side{display:grid;grid-template-columns:1fr;gap:12px;margin-top:12px}
    @media(min-width:768px){ .side{grid-template-columns:1fr 2fr} }
    ul.clean{list-style:none;padding:0;margin:0}
    ul.clean li{display:flex;align-items:center;gap:8px;padding:3px 0;border-bottom:1px dashed #e2d4c4}
    ul.clean li:last-child{border-bottom:0}
    .prose{line-height:1.7}
    .prose h2{margin-top:22px}
    code{background:#f3f4f6;border:1px solid #e5e7eb;border-radius:6px;padding:1px 4px}
    pre{background:#0b1220;color:#e2e8f0;border-radius:10px;padding:12px;overflow:auto}
  </style>
</head>
<body>
  <header class="wrap">
    <h1>Mapa interactivo · Pueblos íberos (estilo Neoíbero)</h1>
    <p class="lead">Explora tribus, <em>oppida</em> y contactos mediterráneos (c. 800&nbsp;a. C. – 100&nbsp;d. C.). Paleta y estética adaptadas a la UI del traductor Neoíbero.</p>
    <div class="grid">
      <div class="card">
        <div style="display:flex;align-items:center;justify-content:space-between;gap:12px">
          <label for="year"><strong>Año:</strong> <span id="yearLabel"></span></label>
          <label style="font-size:12px"><input type="checkbox" id="showAllYears"> Mostrar todo</label>
        </div>
        <input id="year" type="range" min="-800" max="100" step="5">
        <div class="ticks" id="ticks"></div>
      </div>

      <div class="card">
        <div><strong>Capas</strong></div>
        <label><input type="checkbox" id="layerTribes" checked> Áreas tribales</label><br>
        <label><input type="checkbox" id="layerSites" checked> Oppida / santuarios</label><br>
        <label><input type="checkbox" id="layerContacts" checked> Colonias y contactos</label>
      </div>

      <div class="card">
        <label for="filter"><strong>Filtro por nombre</strong></label>
        <input id="filter" type="text" placeholder="Escribe 'Sagunto', 'Gadir', 'Baza'…">
        <div class="hint">El filtro aplica a todas las capas visibles.</div>
      </div>
    </div>
  </header>

  <main class="wrap">
    <div class="map-wrap card" aria-label="Mapa de España con capas históricas (paleta Neoíbero)">
      <div id="map"></div>
      <div class="legend">
        <div style="font-weight:600;margin-bottom:4px">Leyenda</div>
        <div><span class="dot dot-oppida"></span>Oppida / santuarios</div>
        <div><span class="dot dot-contact"></span>Colonias / contactos</div>
        <div><span class="dot dot-tribe"></span>Áreas tribales (aprox.)</div>
      </div>
    </div>

    <div class="side">
      <div class="card">
        <h3 style="margin:0 0 6px">Elementos visibles <span id="visibleScope"></span></h3>
        <ul class="clean" id="visibleList"></ul>
      </div>
      <div class="card">
        <h3 style="margin:0 0 6px">Cronología clave</h3>
        <ol id="timelineList" style="margin:0 0 0 18px"></ol>
      </div>
    </div>

    <article class="card prose" style="margin-top:12px">
      <h2>Notas y compatibilidad</h2>
      <p>Este archivo sigue la <strong>paleta</strong> y el <strong>estilo</strong> del proyecto Neoíbero: arcilla, ocre, bronce y arena, con tipografía serif y tarjetas con bordes texturizados. Puedes integrarlo como página independiente o incrustarlo en tu sitio.</p>
      <h3>Cómo incrustar</h3>
      <pre>&lt;iframe src="mapa_iberos_neoibero.html" width="100%" height="720" style="border:0"&gt;&lt;/iframe&gt;</pre>
      <p class="hint">Teselas de <em>OpenStreetMap</em> con atribución incluida.</p>
    </article>
  </main>

  <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
  <script>
  // ===== Utilidades formato/año =====
  const fmtYear = (y) => (y < 0 ? Math.abs(y) + " a. C." : y + " d. C.");
  const YEAR_MIN = -800, YEAR_MAX = 100;

  // ===== Colores desde CSS =====
  function cssVar(name){ return getComputedStyle(document.documentElement).getPropertyValue(name).trim(); }
  const COLORS = {
    oppida:  cssVar('--iberian-clay')    || '#8B4513',
    contact: cssVar('--iberian-bronze')  || '#CD7F32',
    tribe:   cssVar('--iberian-rust')    || '#A0522D',
    tribeFill: cssVar('--iberian-ochre') || '#CC7722',
  };

  // ===== Datos (resumen didáctico) =====
  const TRIBES = [
    { name: "Turdetanos", center: [37.4, -5.9], radius: 120000, start: -500, end: -100, note: "Herederos en parte de Tarteso; Guadalquivir bajo y medio." },
    { name: "Bastetanos", center: [37.5, -2.7], radius: 90000, start: -500, end: -50, note: "Sureste: Basti (Baza), Acci (Guadix)." },
    { name: "Contestanos", center: [38.6, -0.8], radius: 85000, start: -500, end: -50, note: "Sur de Valencia y norte de Alicante." },
    { name: "Edetanos", center: [39.6, -0.5], radius: 70000, start: -500, end: -50, note: "Hinterland de Valencia; Edeta (Llíria)." },
    { name: "Ilergetes", center: [41.6, 0.6], radius: 70000, start: -500, end: -100, note: "Llanos del Segre-Cinca (Lleida)." },
    { name: "Ilercavones", center: [40.8, 0.5], radius: 70000, start: -500, end: -50, note: "Bajo Ebro y norte de Castellón." },
    { name: "Oretanos (ibéricos)", center: [38.5, -3.3], radius: 80000, start: -500, end: -50, note: "Alto Guadalquivir; Cástulo." },
    { name: "Lacetanos", center: [41.8, 1.8], radius: 60000, start: -450, end: -50, note: "Interior de Cataluña central." },
    { name: "Layetanos", center: [41.4, 2.1], radius: 40000, start: -500, end: -50, note: "Costa de Barcelona; contactos griegos y fenicios." },
    { name: "Indiketes", center: [42.1, 3.1], radius: 40000, start: -575, end: -50, note: "Empordà; entorno de Emporion." },
    { name: "Ausetanos", center: [41.9, 2.3], radius: 50000, start: -450, end: -50, note: "Zona de Vic (Osona)." },
    { name: "Cessetanos", center: [41.2, 1.2], radius: 50000, start: -450, end: -50, note: "Penedès y entorno de Tarraco." },
    { name: "Sedetanos (frontera)", center: [41.6, -0.9], radius: 65000, start: -400, end: -50, note: "Valle medio del Ebro (área de Zaragoza)." },
    { name: "Mastienos", center: [37.8, -1.1], radius: 60000, start: -500, end: -200, note: "Entorno de Mastia/Carthago Nova (Murcia-Cartagena)." }
  ];
  const IBERIAN_SITES = [
    { name: "Ullastret (Indika)", coords: [42.0, 3.0], start: -550, end: -200, note: "Oppidum con murallas monumentales." },
    { name: "La Bastida de les Alcusses (Moixent)", coords: [38.85, -0.81], start: -500, end: -350, note: "‘Guerrero de Moixent’." },
    { name: "La Alcudia – Ilici (Elche)", coords: [38.25, -0.72], start: -500, end: 100, note: "Centro ibérico y romano; contexto de la Dama de Elche." },
    { name: "Basti (Baza)", coords: [37.49, -2.77], start: -400, end: -50, note: "Necrópolis con esculturas (Dama de Baza)." },
    { name: "Cástulo (Linares)", coords: [38.11, -3.63], start: -500, end: 50, note: "Nodo minero-comercial del alto Guadalquivir." },
    { name: "Arse / Saguntum (Sagunto)", coords: [39.68, -0.28], start: -500, end: 100, note: "Asedio cartaginés en 219 a. C." },
    { name: "Castellar de Meca (Ayora-Alpera)", coords: [38.96, -1.13], start: -400, end: -50, note: "Oppidum rupestre con cisternas talladas." },
    { name: "Tossal de Sant Miquel – Edeta (Llíria)", coords: [39.63, -0.58], start: -500, end: -50, note: "Capital edetana; epigrafía íbera." },
    { name: "La Serreta (Alcoi)", coords: [38.72, -0.45], start: -375, end: -50, note: "Santuario con exvotos y cerámica pintada." },
    { name: "Puig Castellar (Sta. Coloma de Gramenet)", coords: [41.46, 2.23], start: -350, end: -50, note: "Asentamiento layetano en altura." },
    { name: "Lucentum – Tossal de Manises (Alicante)", coords: [38.37, -0.42], start: -300, end: 100, note: "Centro ibérico romanizado (Lucentum)." },
    { name: "Cabezo Lucero (Guardamar del Segura)", coords: [38.08, -0.61], start: -400, end: -200, note: "Necrópolis; Dama de Guardamar." },
    { name: "El Cigarralejo (Mula)", coords: [38.05, -1.49], start: -350, end: -50, note: "Necrópolis ibérica con rico ajuar." },
    { name: "Cerro de los Santos (Montealegre del Castillo)", coords: [38.73, -1.23], start: -350, end: -50, note: "Santuario con esculturas oferentes." },
    { name: "Porcuna – Cerrillo Blanco (Obulco)", coords: [37.88, -4.19], start: -400, end: -50, note: "Conjunto escultórico ibérico excepcional." },
    { name: "Puente Tablas (Jaén)", coords: [37.81, -3.71], start: -600, end: -300, note: "Oppidum con puerta-santuario y urbanismo planificado." }
  ];
  const CONTACT_SITES = [
    { name: "Emporion (Empúries, griega)", coords: [42.13, 3.12], start: -575, end: 100, note: "Fundación focea; puerta de bienes e ideas griegas." },
    { name: "Gadir (Cádiz, fenicia)", coords: [36.53, -6.29], start: -800, end: 100, note: "Larga tradición fenicia; comercio atlántico." },
    { name: "Qart Hadasht / Carthago Nova (Cartagena)", coords: [37.6, -0.99], start: -228, end: 100, note: "Fundada por Asdrúbal; tomada en 209 a. C." },
    { name: "Malaka (Málaga, fenicia)", coords: [36.72, -4.42], start: -700, end: 100, note: "Factoría fenicia; salazones y comercio." },
    { name: "Sexi (Almuñécar, fenicia)", coords: [36.73, -3.69], start: -700, end: 100, note: "Red fenicia en la costa granadina." },
    { name: "Abdera (Adra, fenicia)", coords: [36.75, -3.02], start: -700, end: 100, note: "Punto nodal del sudeste." }
  ];
  const TIMELINE = [
    { year: -800, label: "Primeras factorías fenicias en el sur (Tiro–Gadir)." },
    { year: -650, label: "Intensificación de intercambios fenicio-púnicos." },
    { year: -575, label: "Fundación de Emporion (Empúries) por foceos." },
    { year: -500, label: "Consolidación de oppida ibéricos y redes regionales." },
    { year: -450, label: "Difusión de escrituras ibéricas (meridional y nororiental)." },
    { year: -350, label: "Apogeo urbano; cerámicas pintadas y santuarios monumentales." },
    { year: -300, label: "Primeras acuñaciones locales y monetización creciente." },
    { year: -219, label: "Asedio de Arse (Sagunto); preludio de la II Guerra Púnica." },
    { year: -209, label: "Escipión toma Qart Hadasht (Cartagena)." },
    { year: -197, label: "Hispania Citerior y Ulterior: organización provincial romana." },
    { year: -154, label: "Inicio de las guerras celtibéricas (contexto peninsular)." },
    { year: -133, label: "Fin de las guerras celtibéricas." },
    { year: -27, label: "Reforma de Augusto y municipalización." },
    { year: -19, label: "Fin de las guerras cántabras; integración peninsular completada." },
    { year: 14, label: "Muerte de Augusto; romanización avanzada." }
  ];

  // ===== DOM helpers / estado =====
  const $ = (id) => document.getElementById(id);
  const state = { year:-250, showAllYears:false, layerTribes:true, layerSites:true, layerContacts:true, filter:"" };
  const withinYear = (item) => state.showAllYears ? true : (state.year >= item.start && state.year <= item.end);
  const nameMatches = (item) => item.name.toLowerCase().includes(state.filter.trim().toLowerCase());

  // ===== Leaflet =====
  const map = L.map('map', { zoomControl:true, scrollWheelZoom:true }).setView([39.8, -3.7], 6);
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    maxZoom: 18,
    attribution: '&copy; OpenStreetMap contributors'
  }).addTo(map);
  const gTribes = L.layerGroup().addTo(map), gSites = L.layerGroup().addTo(map), gContacts = L.layerGroup().addTo(map);

  function renderLayers(){
    gTribes.clearLayers(); gSites.clearLayers(); gContacts.clearLayers();
    if(state.layerTribes){
      TRIBES.filter(x=>withinYear(x) && nameMatches(x)).forEach(t => {
        L.circle(t.center, {radius:t.radius,color:COLORS.tribe,weight:2,fillOpacity:.08,fillColor:COLORS.tribeFill})
         .bindPopup(`<div style="font-size:13px"><div style="font-weight:600">${t.name}</div><div style="color:#5a534c">${fmtYear(t.start)}${fmtYear(t.end)}</div><p style="margin:.3em 0 0">${t.note}</p><p style="margin:.3em 0 0;color:#6b7280;font-size:11px">Área aproximada (fines didácticos).</p></div>`)
         .addTo(gTribes);
      });
    }
    if(state.layerSites){
      IBERIAN_SITES.filter(x=>withinYear(x) && nameMatches(x)).forEach(s => {
        L.circleMarker(s.coords, {radius:6,color:COLORS.oppida,weight:2,fillOpacity:.9,fillColor:COLORS.oppida})
         .bindPopup(`<div style="font-size:13px"><div style="font-weight:600">${s.name}</div><div style="color:#5a534c">${fmtYear(s.start)}${fmtYear(s.end)}</div><p style="margin:.3em 0 0">${s.note}</p></div>`)
         .addTo(gSites);
      });
    }
    if(state.layerContacts){
      CONTACT_SITES.filter(x=>withinYear(x) && nameMatches(x)).forEach(c => {
        L.circleMarker(c.coords, {radius:5,color:COLORS.contact,weight:2,fillOpacity:.7,fillColor:COLORS.contact})
         .bindPopup(`<div style="font-size:13px"><div style="font-weight:600">${c.name}</div><div style="color:#5a534c">${fmtYear(c.start)}${fmtYear(c.end)}</div><p style="margin:.3em 0 0">${c.note}</p></div>`)
         .addTo(gContacts);
      });
    }
  }
  function updateControlsUI(){
    $('year').disabled = state.showAllYears;
    $('year').value = state.year;
    $('yearLabel').textContent = state.showAllYears ? 'todos los años' : fmtYear(state.year);
    $('visibleScope').textContent = state.showAllYears ? '(todos los años)' : 'en ' + fmtYear(state.year);
  }
  function updateVisibleList(){
    const visible = [];
    if(state.layerTribes) TRIBES.filter(x=>withinYear(x) && nameMatches(x)).forEach(t => visible.push({k:'T', name:t.name}));
    if(state.layerSites) IBERIAN_SITES.filter(x=>withinYear(x) && nameMatches(x)).forEach(s => visible.push({k:'S', name:s.name}));
    if(state.layerContacts) CONTACT_SITES.filter(x=>withinYear(x) && nameMatches(x)).forEach(c => visible.push({k:'C', name:c.name}));
    const ul = $('visibleList'); ul.innerHTML = '';
    if(visible.length === 0){ ul.innerHTML = '<li style="color:#6b7280">No hay elementos con estos filtros.</li>'; return; }
    visible.forEach(v => {
      const li = document.createElement('li');
      const dot = document.createElement('span');
      dot.className = 'dot ' + (v.k==='T' ? 'dot-tribe' : v.k==='S' ? 'dot-oppida' : 'dot-contact');
      li.appendChild(dot);
      li.appendChild(document.createTextNode(v.name));
      ul.appendChild(li);
    });
  }
  function renderTimeline(){
    const ticks = $('ticks'); ticks.innerHTML = '';
    TIMELINE.forEach(t => {
      const span = document.createElement('span');
      span.textContent = t.year; span.title = fmtYear(t.year) + ' · ' + t.label; ticks.appendChild(span);
    });
    const ol = $('timelineList'); ol.innerHTML='';
    TIMELINE.forEach(t => {
      const li = document.createElement('li');
      li.innerHTML = '<strong>'+fmtYear(t.year)+':</strong> '+t.label; ol.appendChild(li);
    });
  }
  function refresh(){ renderLayers(); updateControlsUI(); updateVisibleList(); }

  // ===== Eventos UI =====
  $('year').addEventListener('input', e => { state.year = parseInt(e.target.value,10); refresh(); ensureSize(); });
  $('showAllYears').addEventListener('change', e => { state.showAllYears = e.target.checked; refresh(); ensureSize(); });
  $('layerTribes').addEventListener('change', e => { state.layerTribes = e.target.checked; refresh(); });
  $('layerSites').addEventListener('change', e => { state.layerSites = e.target.checked; refresh(); });
  $('layerContacts').addEventListener('change', e => { state.layerContacts = e.target.checked; refresh(); });
  $('filter').addEventListener('input', e => { state.filter = e.target.value; refresh(); });

  renderTimeline(); refresh();

  // ===== Robustez en contenedores ocultos (HF Spaces / pestañas Gradio) =====
  function ensureSize(){ try{ map.invalidateSize({animate:false}); }catch(e){} }

  // al cargar y tras pequeños retrasos (layout async)
  window.addEventListener('load', ()=>{ ensureSize(); setTimeout(ensureSize,200); setTimeout(ensureSize,900); });
  // al redimensionar ventana
  window.addEventListener('resize', ()=>{ ensureSize(); });
  // cuando la página vuelve a ser visible
  document.addEventListener('visibilitychange', ()=>{ if(!document.hidden){ setTimeout(ensureSize,60); }});
  // si el padre avisa que la pestaña se mostró
  window.addEventListener('message', (e)=>{
    try{
      if(e && e.data && e.data.type==='neoibero-tab-shown'){ setTimeout(ensureSize,50); }
    }catch(_){}
  });

  // ===== Comunicar altura al padre para iframes autoajustables =====
  function postHeight(){
    try{
      const h = document.body.scrollHeight;
      parent && parent.postMessage && parent.postMessage({type:'neoibero-map-height', height:h}, '*');
    }catch(e){}
  }
  new ResizeObserver(postHeight).observe(document.body);
  setTimeout(postHeight, 500);
  </script>
</body>
</html>