@jjlmoya/utils-babies 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,150 +1,6 @@
1
- export interface Milestone {
2
- analogies: { fruits: string; geek: string; sweets: string };
3
- size: string;
4
- biolook: string;
5
- mom: string;
6
- partner: string;
7
- symptoms: string[];
8
- alerts: string[];
9
- wonder: string;
10
- }
11
-
12
- export const milestones: Record<number, Milestone> = {
13
- 4: {
14
- analogies: { fruits: 'Semilla de amapola', geek: 'Píxel solitario', sweets: 'Granito de azúcar' },
15
- size: '~1 mm',
16
- biolook: 'El blastocisto se implanta en el endometrio. Aparecen las tres capas embrionarias que formarán todos tus órganos.',
17
- mom: 'Tu cuerpo aún no sabe que está embarazada, pero ya produce hCG. Quizá sientas un leve sangrado de implantación.',
18
- partner: 'Puede que ella esté más cansada de lo habitual sin saber por qué. Es buen momento para cocinar su cena favorita.',
19
- symptoms: ['Fatiga temprana', 'Tensión en el pecho', 'Ligero sangrado de implantación'],
20
- alerts: ['Sangrado abundante rojo', 'Dolor pélvico agudo', 'Fiebre mayor de 38 grados'],
21
- wonder: 'En este momento se decide si habrá uno o dos bebés.'
22
- },
23
- 6: {
24
- analogies: { fruits: 'Lenteja', geek: 'LED parpadeante', sweets: 'Lacasito' },
25
- size: '~6 mm',
26
- biolook: 'El corazón empieza a latir de forma irregular pero visible en ecografía. El tubo neural se cierra.',
27
- mom: 'Las náuseas matutinas pueden comenzar ahora.',
28
- partner: 'El olfato de ella se vuelve sobrehumano. Evita perfumes fuertes y cocinar pescado en casa.',
29
- symptoms: ['Náuseas', 'Hipersensibilidad al olfato', 'Somnolencia extrema'],
30
- alerts: ['Ausencia de latido en eco vaginal', 'Manchado oscuro persistente', 'Vómitos sin retención'],
31
- wonder: 'Ya tiene la misma estructura cerebral básica que tendrá de adulto.'
32
- },
33
- 8: {
34
- analogies: { fruits: 'Frambuesa', geek: 'Memoria USB nano', sweets: 'Gominola de oso' },
35
- size: '~18 mm',
36
- biolook: 'Los dedos de manos y pies se separan. Los párpados cubren los ojos. El corazón late 150-170 veces por minuto.',
37
- mom: 'Tu útero tiene el tamaño de un pomelo. Las náuseas están en su punto álgido.',
38
- partner: 'Ella puede necesitar dormir 10-12 horas. Asume las tareas del hogar.',
39
- symptoms: ['Náuseas intensas', 'Aumento de saliva', 'Micción muy frecuente'],
40
- alerts: ['Vómitos incoercibles', 'Manchado oscuro', 'Dolor lumbar intenso'],
41
- wonder: 'Si tocas la barriga de ella, él ya reacciona y se mueve.'
42
- },
43
- 10: {
44
- analogies: { fruits: 'Kumquat', geek: 'AirPod', sweets: 'Macarón' },
45
- size: '~31 mm',
46
- biolook: 'Técnicamente ya es un feto. Todos los órganos existen en forma rudimentaria. Las uñas empiezan a crecer.',
47
- mom: 'Tu cintura empieza a ensancharse.',
48
- partner: 'Acompáñala a la primera consulta del primer trimestre.',
49
- symptoms: ['Hinchazón abdominal', 'Cambios bruscos de humor', 'Piel más sensible al sol'],
50
- alerts: ['Pérdida de líquido claro', 'Calambres uterinos intensos', 'Fiebre sin causa aparente'],
51
- wonder: 'Ya tiene su propio tipo de sangre, diferente al de su madre.'
52
- },
53
- 12: {
54
- analogies: { fruits: 'Ciruela', geek: 'Ratón inalámbrico', sweets: 'Mochi de fresa' },
55
- size: '~55 mm',
56
- biolook: 'Los órganos principales están formados. El bebé empieza a practicar movimientos de deglución.',
57
- mom: 'El riesgo de aborto espontáneo cae drásticamente.',
58
- partner: 'El segundo trimestre se acerca. El cansancio de ella mejorará pronto.',
59
- symptoms: ['Reducción de las náuseas', 'Piel más luminosa o con manchas', 'Dolores de cabeza'],
60
- alerts: ['Pérdida de líquido', 'Calambres fuertes', 'Fiebre persistente'],
61
- wonder: 'Sus reflejos ya funcionan: si le tocas la palma de la mano, cierra el puño.'
62
- },
63
- 16: {
64
- analogies: { fruits: 'Aguacate', geek: 'Mando de PS5', sweets: 'Berlina de chocolate' },
65
- size: '~12 cm',
66
- biolook: 'Las orejas están en su posición final. El esqueleto de cartílago se convierte en hueso.',
67
- mom: 'Muchas mamás sienten los primeros movimientos. La energía vuelve.',
68
- partner: 'Puede que empiece a sentir pequeños movimientos.',
69
- symptoms: ['Ardor de estómago leve', 'Congestión nasal', 'Sueños muy vividos'],
70
- alerts: ['Ausencia total de movimientos fetales', 'Tensión alta', 'Sangrado vaginal'],
71
- wonder: 'Si es niña, ya tiene 6 millones de óvulos en sus ovarios.'
72
- },
73
- 20: {
74
- analogies: { fruits: 'Plátano', geek: 'Consola de juegos portátil', sweets: 'Tarta de queso entera' },
75
- size: '~25 cm',
76
- biolook: 'El bebé ya oye tu voz con claridad. Se chupa el dedo.',
77
- mom: 'Semana 20, ecuador. Es el momento de la ecografía morfológica.',
78
- partner: 'Habla al bebé. Ya te oye.',
79
- symptoms: ['Ardor de estómago frecuente', 'Hinchazón de pies al final del día', 'Picor en la piel del abdomen'],
80
- alerts: ['Falta de movimientos fetales', 'Tensión arterial alta sostenida', 'Visión borrosa o con destellos'],
81
- wonder: 'Sus huellas dactilares ya están completas y son únicas en el universo.'
82
- },
83
- 24: {
84
- analogies: { fruits: 'Mazorca de maíz', geek: 'Teclado mecánico', sweets: 'Donut gigante' },
85
- size: '~30 cm',
86
- biolook: 'Los pulmones producen surfactante. Los ojos empiezan a abrirse.',
87
- mom: 'Tu útero llega al ombligo. La espalda puede empezar a resentirse.',
88
- partner: 'Aprende los signos de parto pretérmino.',
89
- symptoms: ['Dolor de espalda baja', 'Calambres en las piernas por la noche', 'Línea negra en el abdomen'],
90
- alerts: ['Contracciones regulares antes de la semana 37', 'Pérdida de líquido amniótico', 'Sangrado vaginal'],
91
- wonder: 'Si nace ahora, con cuidados intensivos, tiene posibilidades de sobrevivir.'
92
- },
93
- 28: {
94
- analogies: { fruits: 'Berenjena', geek: 'Tableta gráfica', sweets: 'Tarta de tres pisos' },
95
- size: '~37 cm',
96
- biolook: 'El bebé abre y cierra los ojos. Tiene ciclos de sueño y vigilia.',
97
- mom: 'Empieza el tercer trimestre. La prueba de glucosa es ahora.',
98
- partner: 'El insomnio puede agotarla. Acompáñala.',
99
- symptoms: ['Insomnio y dificultad para encontrar postura', 'Hinchazón de manos y pies', 'Contracciones Braxton Hicks esporádicas'],
100
- alerts: ['Reducción marcada de movimientos fetales', 'Dolor de cabeza intenso que no cede', 'Visión con luces o moscas'],
101
- wonder: 'Ya tiene un sabor favorito.'
102
- },
103
- 32: {
104
- analogies: { fruits: 'Calabaza mediana', geek: 'Teclado de 60%', sweets: 'Caja de bombones entera' },
105
- size: '~42 cm',
106
- biolook: 'Practica la respiración. Sus pulmones están casi listos para el mundo exterior.',
107
- mom: 'Recuperar el aliento es difícil. El bebé presiona el diafragma.',
108
- partner: 'Prepara la bolsa del hospital.',
109
- symptoms: ['Falta de aliento al esfuerzo mínimo', 'Hemorroides', 'Pérdida de orina al reír o toser'],
110
- alerts: ['Picor intenso en palmas de manos y plantas de pies', 'Contracciones regulares', 'Dolor en boca del estómago con náuseas'],
111
- wonder: 'Ya gira la cabeza hacia la luz que atraviesa la barriga de su madre.'
112
- },
113
- 36: {
114
- analogies: { fruits: 'Melón cantalupo', geek: 'Portátil de 15 pulgadas', sweets: 'Tarta de cumpleaños' },
115
- size: '~47 cm',
116
- biolook: 'El bebé suele encajarse cabeza abajo. Sus pulmones están casi maduros.',
117
- mom: 'Sientes presión en la pelvis al encajarse el bebé.',
118
- partner: 'El plan de parto debería estar listo.',
119
- symptoms: ['Presión pélvica intensa', 'Vuelta del ardor de estómago', 'Ansiedad anticipatoria'],
120
- alerts: ['Rotura de bolsa', 'Sangrado vaginal rojo', 'Ausencia de movimientos fetales'],
121
- wonder: 'Ya tiene las uñas tan largas que podría arañarse al nacer.'
122
- },
123
- 40: {
124
- analogies: { fruits: 'Sandía', geek: 'PC de sobremesa completo', sweets: 'Tarta nupcial de 3 pisos' },
125
- size: '~50 cm',
126
- biolook: 'Todo está listo. Sus reflejos están coordinados, sus pulmones maduros, su cerebro activo.',
127
- mom: 'El cansancio es máximo. Eres increíble.',
128
- partner: 'Hoy puede que sea el día. Mantén el teléfono cargado y la calma que ella necesita de ti.',
129
- symptoms: ['Presión pélvica muy intensa', 'Pérdida del tapón mucoso', 'Contracciones irregulares'],
130
- alerts: ['Contracciones regulares cada 5 minutos durante 1 hora', 'Rotura de bolsa', 'Ausencia de movimientos'],
131
- wonder: 'Su cerebro ha creado 100 mil millones de neuronas durante estos 9 meses.'
132
- }
133
- };
134
-
135
- export const timelineLabels: Record<number, string> = {
136
- 4: 'Implantación', 5: 'Latido', 6: 'Corazón late', 7: 'Ojos y oídos',
137
- 8: 'Dedos', 9: 'Uñas', 10: 'Ya es feto', 11: 'Movimientos',
138
- 12: 'Triple Screening', 13: '2º Trimestre', 14: 'Cuello de útero', 15: 'Patadas',
139
- 16: 'Oye voces', 17: 'Grasa corporal', 18: 'Genitales visibles', 19: 'Vérnix',
140
- 20: 'Eco morfológica', 21: 'Huellas dactilares', 22: 'Labios formados', 23: 'Párpados',
141
- 24: 'Ojos se abren', 25: 'Capilares', 26: 'Reflejos', 27: 'Cerebro activo',
142
- 28: '3er Trimestre', 29: 'Huesos fuertes', 30: 'Médula ósea', 31: 'Surfactante',
143
- 32: 'Practica respirar', 33: 'Inmunidad', 34: 'Sistema nervioso', 35: 'Encajamiento',
144
- 36: 'Pulmones maduros', 37: 'A término', 38: 'Preparado', 39: 'Esperando', 40: 'Llegó el día'
145
- };
1
+ import type { MilestoneI18n } from './index';
146
2
 
147
- export function getMilestone(week: number): Milestone {
3
+ export function getMilestone(week: number, milestones: Record<number, MilestoneI18n>): MilestoneI18n {
148
4
  const keys = Object.keys(milestones).map(Number).sort((a, b) => b - a);
149
5
  for (const k of keys) {
150
6
  if (week >= k) return milestones[k]!;
@@ -1,5 +1,4 @@
1
1
  ---
2
- import './style.css';
3
2
  import type { VaccinationCalendarUI } from './index';
4
3
  interface Props { ui: VaccinationCalendarUI; }
5
4
  const { ui } = Astro.props;
@@ -64,7 +63,7 @@ const { ui } = Astro.props;
64
63
  import type { DoseGroup } from './logic';
65
64
 
66
65
  const root = document.getElementById('vc-root') as HTMLElement;
67
- const ui = JSON.parse(root.dataset.ui as string) as Record<string, string>;
66
+ const ui = root.dataset.ui ? JSON.parse(root.dataset.ui) : {} as Record<string, string>;
68
67
 
69
68
  const ddInput = document.getElementById('vc-dd') as HTMLInputElement;
70
69
  const mmInput = document.getElementById('vc-mm') as HTMLInputElement;
@@ -81,7 +80,7 @@ const { ui } = Astro.props;
81
80
  const btnReminder = document.getElementById('vc-btn-reminder') as HTMLButtonElement;
82
81
  const shareLink = document.getElementById('vc-share-link') as HTMLAnchorElement;
83
82
 
84
- const doseGroups = buildDoseGroups();
83
+ const doseGroups = buildDoseGroups(ui);
85
84
  let currentBirthDate: Date | null = null;
86
85
 
87
86
  function parseInputDate(): Date | null {
@@ -100,7 +99,8 @@ const { ui } = Astro.props;
100
99
  }
101
100
 
102
101
  function formatDate(date: Date): string {
103
- return new Intl.DateTimeFormat('es-ES', { day: '2-digit', month: 'long', year: 'numeric' }).format(date);
102
+ const locale = document.documentElement.lang || 'es-ES';
103
+ return new Intl.DateTimeFormat(locale, { day: '2-digit', month: 'long', year: 'numeric' }).format(date);
104
104
  }
105
105
 
106
106
  function buildTimelineRow(group: DoseGroup, _birthDate: Date, isPast: boolean): HTMLElement {
@@ -109,7 +109,7 @@ const { ui } = Astro.props;
109
109
 
110
110
  const ageEl = document.createElement('span');
111
111
  ageEl.className = 'vc-timeline-age';
112
- ageEl.textContent = getAgeLabel(group.months);
112
+ ageEl.textContent = getAgeLabel(group.months, ui);
113
113
 
114
114
  const vacEl = document.createElement('div');
115
115
  vacEl.className = 'vc-timeline-vac';
@@ -174,7 +174,7 @@ const { ui } = Astro.props;
174
174
  root.classList.toggle('vc-is-today', isToday);
175
175
  nextDateEl.textContent = isToday
176
176
  ? (ui['btnToday'] ?? '¡Hoy!')
177
- : `${getAgeLabel(next.months)} — ${formatDate(target)}`;
177
+ : `${getAgeLabel(next.months, ui)} — ${formatDate(target)}`;
178
178
 
179
179
  next.vaccines.forEach((name, idx) => {
180
180
  const item = document.createElement('div');
@@ -193,7 +193,7 @@ const { ui } = Astro.props;
193
193
  function updateDisplay(birthDate: Date): void {
194
194
  const today = new Date();
195
195
  today.setHours(0, 0, 0, 0);
196
- ageBadge.textContent = calculateAge(birthDate, today);
196
+ ageBadge.textContent = calculateAge(birthDate, today, ui);
197
197
  ageBadge.classList.add('vc-age-visible');
198
198
  root.classList.add('vc-active');
199
199
  emptyEl.style.display = 'none';
@@ -249,7 +249,7 @@ const { ui } = Astro.props;
249
249
 
250
250
  btnReminder?.addEventListener('click', () => {
251
251
  if (!currentBirthDate) return;
252
- const ics = buildIcsContent(currentBirthDate, doseGroups);
252
+ const ics = buildIcsContent(currentBirthDate, doseGroups, ui);
253
253
  const blob = new Blob([ics], { type: 'text/calendar' });
254
254
  const url = URL.createObjectURL(blob);
255
255
  const a = document.createElement('a');
@@ -280,3 +280,402 @@ const { ui } = Astro.props;
280
280
 
281
281
  clearDisplay();
282
282
  </script>
283
+
284
+ <style>
285
+ .vc-card {
286
+ --primary: #4f46e5;
287
+ --primary-soft: #eef2ff;
288
+ --success: #10b981;
289
+ --warning: #f59e0b;
290
+ --text-main: #1e293b;
291
+ --text-muted: #64748b;
292
+ --bg-card: #fff;
293
+ --border: #e2e8f0;
294
+ --shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
295
+
296
+ max-width: 600px;
297
+ margin: 0 auto;
298
+ background: var(--bg-card);
299
+ border-radius: 28px;
300
+ border: 1px solid var(--border);
301
+ box-shadow: var(--shadow);
302
+ overflow: hidden;
303
+ color: var(--text-main);
304
+ transition: transform 0.3s ease;
305
+ }
306
+
307
+ :global(.theme-dark) .vc-card {
308
+ --bg-card: #1e293b;
309
+ --border: rgba(255, 255, 255, 0.1);
310
+ --text-main: #f1f5f9;
311
+ --text-muted: #94a3b8;
312
+ --primary: #a5b4fc;
313
+ --primary-soft: rgba(79, 70, 229, 0.15);
314
+ }
315
+
316
+ .vc-header {
317
+ padding: 2rem 1.5rem;
318
+ background: linear-gradient(to bottom, var(--primary-soft), transparent);
319
+ border-bottom: 1px solid var(--border);
320
+ display: flex;
321
+ flex-direction: column;
322
+ align-items: center;
323
+ gap: 1.5rem;
324
+ }
325
+
326
+ .vc-inputs {
327
+ width: 100%;
328
+ display: flex;
329
+ justify-content: center;
330
+ }
331
+
332
+ .vc-field {
333
+ display: flex;
334
+ flex-direction: column;
335
+ align-items: center;
336
+ gap: 0.75rem;
337
+ width: 100%;
338
+ }
339
+
340
+ .vc-field label {
341
+ font-size: 0.8rem;
342
+ font-weight: 800;
343
+ text-transform: uppercase;
344
+ color: var(--text-muted);
345
+ letter-spacing: 0.05em;
346
+ }
347
+
348
+ .vc-triple-input {
349
+ display: flex;
350
+ align-items: center;
351
+ gap: 0.5rem;
352
+ background: var(--bg-card);
353
+ border: 2px solid var(--border);
354
+ border-radius: 16px;
355
+ padding: 0.25rem 1rem;
356
+ transition: all 0.2s ease;
357
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
358
+ }
359
+
360
+ .vc-triple-input:focus-within {
361
+ border-color: var(--primary);
362
+ box-shadow: 0 0 0 4px var(--primary-soft);
363
+ transform: translateY(-1px);
364
+ }
365
+
366
+ .vc-triple-input.has-error {
367
+ border-color: #ef4444;
368
+ background: #fef2f2;
369
+ }
370
+
371
+ :global(.theme-dark) .vc-triple-input.has-error {
372
+ background: rgba(239, 68, 68, 0.05);
373
+ }
374
+
375
+ .vc-triple-input.has-error:focus-within {
376
+ box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.15);
377
+ }
378
+
379
+ .vc-segment {
380
+ border: none;
381
+ padding: 0.8rem 0;
382
+ width: 3rem;
383
+ text-align: center;
384
+ background: transparent;
385
+ font-size: 1.1rem;
386
+ font-weight: 700;
387
+ color: inherit;
388
+ outline: none;
389
+ }
390
+
391
+ .vc-segment-year {
392
+ width: 4.5rem;
393
+ }
394
+
395
+ .vc-sep {
396
+ color: var(--text-muted);
397
+ font-weight: 700;
398
+ font-size: 1.2rem;
399
+ opacity: 0.3;
400
+ pointer-events: none;
401
+ }
402
+
403
+ .vc-age-badge {
404
+ margin-top: 0.25rem;
405
+ font-size: 0.85rem;
406
+ font-weight: 700;
407
+ color: var(--text-muted);
408
+ opacity: 0;
409
+ transform: translateY(-5px);
410
+ transition: all 0.3s ease;
411
+ }
412
+
413
+ .vc-age-badge.vc-age-visible {
414
+ opacity: 1;
415
+ transform: translateY(0);
416
+ }
417
+
418
+ .vc-empty-state {
419
+ padding: 3rem 1.5rem;
420
+ text-align: center;
421
+ color: var(--text-muted);
422
+ }
423
+
424
+ .vc-main-context {
425
+ padding: 2rem 1.5rem;
426
+ text-align: center;
427
+ border-bottom: 1px solid var(--border);
428
+ display: none;
429
+ }
430
+
431
+ .vc-active .vc-main-context {
432
+ display: block;
433
+ animation: vc-slide-up 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
434
+ }
435
+
436
+ @keyframes vc-slide-up {
437
+ from {
438
+ opacity: 0;
439
+ transform: translateY(20px);
440
+ }
441
+
442
+ to {
443
+ opacity: 1;
444
+ transform: translateY(0);
445
+ }
446
+ }
447
+
448
+ .vc-next-title {
449
+ font-size: 0.9rem;
450
+ font-weight: 700;
451
+ color: var(--primary);
452
+ text-transform: uppercase;
453
+ margin: 0 0 0.5rem;
454
+ }
455
+
456
+ .vc-next-date {
457
+ font-size: 1.75rem;
458
+ font-weight: 900;
459
+ margin-bottom: 1.5rem;
460
+ }
461
+
462
+ .vc-is-today .vc-next-date {
463
+ color: #ef4444;
464
+ animation: vc-pulse-today 2s infinite;
465
+ }
466
+
467
+ @keyframes vc-pulse-today {
468
+ 0% { transform: scale(1); }
469
+ 50% { transform: scale(1.02); }
470
+ 100% { transform: scale(1); }
471
+ }
472
+
473
+ .vc-is-today .vc-btn-primary {
474
+ background: #ef4444;
475
+ box-shadow: 0 4px 15px rgba(239, 68, 68, 0.4);
476
+ }
477
+
478
+ .vc-vac-list {
479
+ display: flex;
480
+ flex-direction: column;
481
+ gap: 0.75rem;
482
+ margin-bottom: 2rem;
483
+ }
484
+
485
+ :global(.vc-vac-item) {
486
+ display: flex;
487
+ align-items: center;
488
+ gap: 1rem;
489
+ padding: 1rem;
490
+ background: var(--primary-soft);
491
+ border-radius: 16px;
492
+ text-align: left;
493
+ border: 1px solid transparent;
494
+ transition: 0.2s;
495
+ }
496
+
497
+ :global(.vc-vac-icon) {
498
+ width: 48px;
499
+ height: 48px;
500
+ display: flex;
501
+ align-items: center;
502
+ justify-content: center;
503
+ background: #fff;
504
+ border-radius: 12px;
505
+ font-weight: 700;
506
+ color: var(--primary);
507
+ border: 1px solid var(--border);
508
+ flex-shrink: 0;
509
+ }
510
+
511
+ :global(.theme-dark) :global(.vc-vac-icon) {
512
+ background: #0f172a;
513
+ }
514
+
515
+ :global(.vc-vac-info) {
516
+ flex: 1;
517
+ }
518
+
519
+ :global(.vc-vac-name) {
520
+ display: block;
521
+ font-weight: 700;
522
+ font-size: 1rem;
523
+ }
524
+
525
+ .vc-btn-primary {
526
+ display: inline-flex;
527
+ align-items: center;
528
+ gap: 0.5rem;
529
+ padding: 0.8rem 1.5rem;
530
+ background: var(--primary);
531
+ color: #fff;
532
+ border-radius: 12px;
533
+ font-weight: 700;
534
+ border: none;
535
+ cursor: pointer;
536
+ width: 100%;
537
+ justify-content: center;
538
+ }
539
+
540
+ .vc-sections {
541
+ display: none;
542
+ }
543
+
544
+ .vc-active .vc-sections {
545
+ display: block;
546
+ }
547
+
548
+ .vc-accordion-item {
549
+ border-bottom: 1px solid var(--border);
550
+ }
551
+
552
+ .vc-accordion-item:last-child {
553
+ border-bottom: none;
554
+ }
555
+
556
+ .vc-accordion-trigger {
557
+ width: 100%;
558
+ padding: 1.25rem 1.5rem;
559
+ display: flex;
560
+ justify-content: space-between;
561
+ align-items: center;
562
+ background: none;
563
+ border: none;
564
+ color: inherit;
565
+ font-weight: 700;
566
+ cursor: pointer;
567
+ font-size: 1rem;
568
+ }
569
+
570
+ .vc-accordion-trigger:hover {
571
+ background: var(--primary-soft);
572
+ }
573
+
574
+ .vc-accordion-content {
575
+ max-height: 0;
576
+ overflow: hidden;
577
+ transition: max-height 0.3s ease;
578
+ }
579
+
580
+ .vc-accordion-item.vc-open .vc-accordion-content {
581
+ max-height: 800px;
582
+ }
583
+
584
+ .vc-timeline-compact {
585
+ padding: 0 1.5rem 1.5rem;
586
+ display: flex;
587
+ flex-direction: column;
588
+ gap: 0.5rem;
589
+ }
590
+
591
+ :global(.vc-timeline-row) {
592
+ display: flex;
593
+ justify-content: space-between;
594
+ align-items: center;
595
+ padding: 0.6rem 0;
596
+ border-bottom: 1px dashed var(--border);
597
+ font-size: 0.9rem;
598
+ }
599
+
600
+ :global(.vc-timeline-row:last-child) {
601
+ border-bottom: none;
602
+ }
603
+
604
+ :global(.vc-timeline-age) {
605
+ font-weight: 800;
606
+ color: var(--primary);
607
+ width: 6.5rem;
608
+ flex-shrink: 0;
609
+ font-size: 0.8rem;
610
+ text-transform: uppercase;
611
+ letter-spacing: 0.02em;
612
+ }
613
+
614
+ :global(.vc-timeline-vac) {
615
+ flex: 1;
616
+ font-weight: 600;
617
+ color: var(--text-main);
618
+ display: flex;
619
+ flex-wrap: wrap;
620
+ gap: 0.4rem;
621
+ padding: 0 0.5rem;
622
+ }
623
+
624
+ :global(.vc-vac-pill) {
625
+ padding: 0.15rem 0.5rem;
626
+ background: var(--primary-soft);
627
+ border-radius: 6px;
628
+ font-size: 0.8rem;
629
+ color: var(--primary);
630
+ border: 1px solid rgba(79, 70, 229, 0.1);
631
+ }
632
+
633
+ :global(.theme-dark) .vc-vac-pill {
634
+ background: var(--primary-soft);
635
+ color: var(--primary);
636
+ border: 1px solid var(--primary-soft);
637
+ }
638
+
639
+ :global(.vc-timeline-status) {
640
+ font-size: 0.65rem;
641
+ font-weight: 900;
642
+ text-transform: uppercase;
643
+ width: 3.5rem;
644
+ flex-shrink: 0;
645
+ text-align: right;
646
+ letter-spacing: 0.05em;
647
+ }
648
+
649
+ :global(.vc-check) {
650
+ color: var(--success);
651
+ }
652
+
653
+ :global(.vc-clock) {
654
+ color: var(--warning);
655
+ }
656
+
657
+ .vc-footer {
658
+ padding: 1rem;
659
+ text-align: center;
660
+ font-size: 0.75rem;
661
+ color: var(--text-muted);
662
+ background: var(--primary-soft);
663
+ }
664
+
665
+ .vc-share-link {
666
+ display: none;
667
+ color: var(--primary);
668
+ font-weight: 700;
669
+ text-decoration: none;
670
+ }
671
+
672
+ .vc-active .vc-share-link {
673
+ display: inline;
674
+ }
675
+
676
+ @media (max-width: 480px) {
677
+ .vc-timeline-age {
678
+ width: 5rem;
679
+ }
680
+ }
681
+ </style>
@@ -95,6 +95,26 @@ export const content: VaccinationCalendarLocaleContent = {
95
95
  labelShare: 'Share these dates',
96
96
  faqTitle: 'Frequently asked questions',
97
97
  bibliographyTitle: 'References',
98
+ labelMonth: 'month',
99
+ labelMonths: 'months',
100
+ labelYear: 'year',
101
+ labelYears: 'years',
102
+ labelDay: 'day',
103
+ labelDays: 'days',
104
+ labelAnd: 'and',
105
+ labelVaccination: 'Vaccination',
106
+ labelAppointment: 'Vaccination appointment',
107
+ vac_hexavalente: 'Hexavalent',
108
+ vac_neumococo: 'Pneumococcal (PCV15/20)',
109
+ vac_meningococo_b: 'Meningococcal B (Bexsero)',
110
+ vac_rotavirus: 'Rotavirus',
111
+ vac_meningococo_acwy: 'Meningococcal ACWY',
112
+ vac_triple_virica: 'MMR (Measles, Mumps, Rubella)',
113
+ vac_varicela: 'Varicella (Chickenpox)',
114
+ vac_gripe: 'Flu (Seasonal)',
115
+ vac_vph: 'HPV (Papillomavirus)',
116
+ vac_tdpa: 'Tdap (Tetanus, Diphtheria, Pertussis)',
117
+ vac_polio_booster: 'Polio (Booster)',
98
118
  },
99
119
  seo: [
100
120
  { type: 'title', text: "Baby Vaccination Calculator: When Is My Child's Next Dose?", level: 2 },
@@ -99,6 +99,26 @@ export const content: VaccinationCalendarLocaleContent = {
99
99
  labelShare: 'Compartir estas fechas',
100
100
  faqTitle: 'Preguntas frecuentes',
101
101
  bibliographyTitle: 'Referencias',
102
+ labelMonth: 'mes',
103
+ labelMonths: 'meses',
104
+ labelYear: 'año',
105
+ labelYears: 'años',
106
+ labelDay: 'día',
107
+ labelDays: 'días',
108
+ labelAnd: 'y',
109
+ labelVaccination: 'Vacunación',
110
+ labelAppointment: 'Cita de vacunación',
111
+ vac_hexavalente: 'Hexavalente',
112
+ vac_neumococo: 'Neumococo (VCN15/20)',
113
+ vac_meningococo_b: 'Meningococo B (Bexsero)',
114
+ vac_rotavirus: 'Rotavirus',
115
+ vac_meningococo_acwy: 'Meningococo ACWY',
116
+ vac_triple_virica: 'Triple Vírica (SRP)',
117
+ vac_varicela: 'Varicela',
118
+ vac_gripe: 'Gripe (Estacional)',
119
+ vac_vph: 'VPH (Papiloma)',
120
+ vac_tdpa: 'Tdpa (Tétanos, Difteria, Tosferina)',
121
+ vac_polio_booster: 'Polio (Refuerzo)',
102
122
  },
103
123
  seo: [
104
124
  { type: 'title', text: 'Calculadora de Vacunas: ¿Cuándo le toca la próxima a mi hijo?', level: 2 },
@@ -95,6 +95,26 @@ export const content: VaccinationCalendarLocaleContent = {
95
95
  labelShare: "Partager ces dates",
96
96
  faqTitle: "Questions fréquentes",
97
97
  bibliographyTitle: "Références",
98
+ labelMonth: "mois",
99
+ labelMonths: "mois",
100
+ labelYear: "an",
101
+ labelYears: "ans",
102
+ labelDay: "jour",
103
+ labelDays: "jours",
104
+ labelAnd: "et",
105
+ labelVaccination: "Vaccination",
106
+ labelAppointment: "Rendez-vous de vaccination",
107
+ vac_hexavalente: "Hexavalent",
108
+ vac_neumococo: "Pneumocoque (VCN15/20)",
109
+ vac_meningococo_b: "Méningocoque B (Bexsero)",
110
+ vac_rotavirus: "Rotavirus",
111
+ vac_meningococo_acwy: "Méningocoque ACWY",
112
+ vac_triple_virica: "ROR (Rougeole, Oreillons, Rubéole)",
113
+ vac_varicela: "Varicelle",
114
+ vac_gripe: "Grippe (Saisonnière)",
115
+ vac_vph: "HPV (Papillomavirus)",
116
+ vac_tdpa: "dTca (Diphthérie, Tétanos, Coqueluche)",
117
+ vac_polio_booster: "Polio (Rappel)",
98
118
  },
99
119
  seo: [
100
120
  { type: 'title', text: "Calculateur de vaccins : quand est le prochain rendez-vous de mon enfant ?", level: 2 },