/* ============================================================
   ALERTAS FISCALES — Centro fiscal interactivo
   (Solo este módulo. Mantiene el sistema de diseño de Lexly.)
   ============================================================ */

const MONTHS_ES = ['Ene','Feb','Mar','Abr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic'];
const MONTHS_LONG = ['enero','febrero','marzo','abril','mayo','junio','julio','agosto','septiembre','octubre','noviembre','diciembre'];

const CONTRIB_LABEL = { autonomo: 'Autónomo', pyme: 'Pyme', particular: 'Particular' };

const AEAT = {
  iva:        'https://sede.agenciatributaria.gob.es/Sede/iva.html',
  irpf:       'https://sede.agenciatributaria.gob.es/Sede/irpf.html',
  sociedades: 'https://sede.agenciatributaria.gob.es/Sede/impuesto-sociedades.html',
  censos:     'https://sede.agenciatributaria.gob.es/Sede/censos-nif-domicilio-fiscal.html',
};

/* Catálogo de modelos e impuestos.
   kind: 'q' trimestral · 'a' anual · 'event' según actividad (sin plazo fijo) */
const FISCAL_MODELS = [
  { code: '303', name: 'Modelo 303', title: 'IVA trimestral', tax: 'IVA', kind: 'q',
    dates: [[3,20],[6,20],[9,20],[0,30]], for: ['autonomo','pyme'], aeat: AEAT.iva,
    desc: 'Autoliquidación del IVA repercutido y soportado de cada trimestre.' },
  { code: '130', name: 'Modelo 130', title: 'IRPF · pago fraccionado', tax: 'IRPF', kind: 'q',
    dates: [[3,20],[6,20],[9,20],[0,30]], for: ['autonomo'], aeat: AEAT.irpf,
    desc: 'Pago fraccionado del IRPF para autónomos en estimación directa.' },
  { code: '111', name: 'Modelo 111', title: 'Retenciones de trabajo y profesionales', tax: 'Retenciones', kind: 'q',
    dates: [[3,20],[6,20],[9,20],[0,20]], for: ['autonomo','pyme'], aeat: AEAT.irpf,
    desc: 'Retenciones e ingresos a cuenta de rendimientos del trabajo y profesionales.' },
  { code: '115', name: 'Modelo 115', title: 'Retenciones por alquileres', tax: 'Retenciones', kind: 'q',
    dates: [[3,20],[6,20],[9,20],[0,20]], for: ['autonomo','pyme'], aeat: AEAT.irpf,
    desc: 'Retenciones por el arrendamiento de inmuebles urbanos.' },
  { code: '390', name: 'Modelo 390', title: 'Resumen anual de IVA', tax: 'IVA', kind: 'a',
    date: [0,30], for: ['autonomo','pyme'], aeat: AEAT.iva,
    desc: 'Declaración informativa que resume todas las operaciones de IVA del año.' },
  { code: '100', name: 'Modelo 100', title: 'IRPF · Declaración de la renta', tax: 'IRPF', kind: 'a',
    date: [5,30], for: ['autonomo','particular'], aeat: AEAT.irpf,
    desc: 'Declaración anual del IRPF. Campaña de Renta del ejercicio anterior.' },
  { code: '200', name: 'Modelo 200', title: 'Impuesto sobre Sociedades', tax: 'Sociedades', kind: 'a',
    date: [6,25], for: ['pyme'], aeat: AEAT.sociedades,
    desc: 'Declaración anual del Impuesto sobre Sociedades para personas jurídicas.' },
  { code: '036/037', name: 'Modelo 036 / 037', title: 'Alta y modificación censal', tax: 'Censos', kind: 'event',
    for: ['autonomo','pyme'], aeat: AEAT.censos,
    desc: 'Alta, baja o modificación en el Censo de Empresarios, Profesionales y Retenedores.' },
];

/* ---------- helpers de fechas ---------- */
function nextOccurrence(monthDay, today) {
  const [m, d] = monthDay;
  let cand = new Date(today.getFullYear(), m, d, 23, 59, 59);
  if (cand < today) cand = new Date(today.getFullYear() + 1, m, d, 23, 59, 59);
  return cand;
}
function nextQuarterly(dates, today) {
  const cands = [];
  for (let y = 0; y <= 1; y++) dates.forEach(([m, d]) => cands.push(new Date(today.getFullYear() + y, m, d, 23, 59, 59)));
  cands.sort((a, b) => a - b);
  return cands.find(c => c >= today);
}
function daysBetween(a, b) { return Math.ceil((b - a) / 86400000); }
function statusOf(days) {
  if (days < 10) return { key: 'red', c: 'var(--red)', bg: 'var(--red-dim)', line: 'rgba(248,113,113,0.3)', label: 'Urgente' };
  if (days <= 30) return { key: 'amber', c: 'var(--amber)', bg: 'var(--amber-dim)', line: 'rgba(251,191,36,0.3)', label: 'Próximo' };
  return { key: 'green', c: 'var(--green)', bg: 'var(--green-dim)', line: 'rgba(52,211,153,0.28)', label: 'A tiempo' };
}
function barWidth(days) { return Math.max(6, Math.min(100, 100 - (days / 90) * 100)) + '%'; }

function computeItem(m, today) {
  if (m.kind === 'event') return { ...m, due: null, days: null, status: null };
  const due = m.kind === 'q' ? nextQuarterly(m.dates, today) : nextOccurrence(m.date, today);
  const days = daysBetween(today, due);
  return { ...m, due, days, status: statusOf(days) };
}

/* ============================================================
   Ficha fiscal — generación IA (callLexly) + caché (store.jsx)
   ============================================================ */
const FISCAL_SYSTEM = 'Eres un experto en fiscalidad española. Genera información clara, estructurada y precisa sobre el modelo o impuesto solicitado. Cubre todas las secciones pedidas. Cita la normativa concreta (ley y artículo). Cuando menciones plazos, sanciones o cuantías, recuerda que pueden variar y que debe verificarse en la fuente oficial. Usa lenguaje claro. Termina cada ficha indicando la fecha aproximada de la información y recomendando verificar en la sede electrónica de la AEAT.';

const FICHE_SECTIONS = [
  { key: 'QUE_ES',         label: 'Qué es',                            icon: 'file' },
  { key: 'OBLIGADOS',      label: 'Quién está obligado',               icon: 'user' },
  { key: 'CUANDO',         label: 'Cuándo se presenta',                icon: 'calendar' },
  { key: 'PLAZOS',         label: 'Plazos y calendario',               icon: 'clock',     tone: 'amber' },
  { key: 'BASE_LEGAL',     label: 'Base legal aplicable',              icon: 'scale' },
  { key: 'INCUMPLIMIENTO', label: 'Consecuencias del incumplimiento',  icon: 'warn',      tone: 'red' },
  { key: 'SANCIONES',      label: 'Sanciones',                         icon: 'warn',      tone: 'red' },
  { key: 'CASOS',          label: 'Casos prácticos',                   icon: 'sparkle' },
  { key: 'FAQ',            label: 'Preguntas frecuentes',              icon: 'message' },
  { key: 'JURISPRUDENCIA', label: 'Jurisprudencia relevante',          icon: 'building' },
  { key: 'RELACION',       label: 'Relación con otros modelos',        icon: 'book' },
];

function parseFiche(raw) {
  const map = {};
  if (!raw) return map;
  const re = /\[\[([A-Z_]+)\]\]/g;
  const parts = [];
  let m;
  while ((m = re.exec(raw))) parts.push({ key: m[1], start: m.index, end: re.lastIndex });
  for (let i = 0; i < parts.length; i++) {
    const p = parts[i];
    if (p.key === 'FIN') continue;
    const next = parts[i + 1] ? parts[i + 1].start : raw.length;
    map[p.key] = raw.slice(p.end, next).replace(/\[\[FIN\]\]/g, '').trim();
  }
  return map;
}

/* Genera la ficha vía callLexly. El runtime limita cada respuesta, así que
   pedimos contenido extenso (objetivo ~3000 tokens) y completamos por tramos
   hasta el marcador [[FIN]]. */
async function generateFiche(model) {
  const forLabel = model.for.map(f => CONTRIB_LABEL[f].toLowerCase()).join(' y ');
  const userPrompt = `Genera la ficha fiscal completa del ${model.name} (${model.title}) en España, orientada a ${forLabel}. Sé extenso y detallado (objetivo: hasta 3000 tokens).

Escribe el contenido usando EXACTAMENTE estos marcadores, cada uno en su propia línea y seguido de su contenido:
[[QUE_ES]]
[[OBLIGADOS]]
[[CUANDO]]
[[PLAZOS]]
[[BASE_LEGAL]]
[[INCUMPLIMIENTO]]
[[SANCIONES]]
[[CASOS]]
[[FAQ]]
[[JURISPRUDENCIA]]
[[RELACION]]

Reglas: contenido claro y CONCISO (2 a 5 viñetas o frases breves por sección, prioriza lo esencial); usa viñetas con "-" y **negritas** para lo importante; cita normativa concreta (ley y artículo); es IMPRESCINDIBLE incluir las 11 secciones, incluidas [[JURISPRUDENCIA]] y [[RELACION]]. Cuando termines TODA la ficha, escribe en una línea final [[FIN]].`;

  const messages = [{ role: 'user', content: userPrompt }];
  let full = '';
  for (let seg = 0; seg < 8; seg++) {
    const chunk = await callLexly(messages, FISCAL_SYSTEM);
    const done = /\[\[FIN\]\]/.test(chunk);
    const clean = chunk.replace(/\[\[FIN\]\]/g, '').replace(/\s+$/, '');
    full += (full ? '\n' : '') + clean;
    if (done) break;
    if (clean.length < 400) break;
    messages.push({ role: 'assistant', content: full });
    messages.push({ role: 'user', content: 'Continúa la ficha EXACTAMENTE donde lo dejaste, sin repetir nada anterior ni reescribir marcadores ya completados. Asegúrate de incluir las secciones que falten, incluidas [[JURISPRUDENCIA]] y [[RELACION]]. Cuando termines, añade [[FIN]].' });
  }
  if (!full.trim()) throw new Error('empty');
  return full;
}

/* ============================================================
   Panel deslizante con la ficha
   ============================================================ */
function FichePanel({ model, onClose }) {
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [sections, setSections] = useState(null);
  const [meta, setMeta] = useState(null);
  const [open, setOpen] = useState({ QUE_ES: true });

  const todayLabel = () => {
    const d = new Date();
    return `${d.getDate()} de ${MONTHS_LONG[d.getMonth()]} de ${d.getFullYear()}`;
  };

  const load = async (force) => {
    setLoading(true); setError(null);
    if (!force) {
      const cached = lxFicheGet(model.code);
      if (cached && cached.sections) {
        setSections(cached.sections);
        setMeta({ date: cached.date, cached: true });
        setLoading(false);
        return;
      }
    }
    try {
      const raw = await generateFiche(model);
      const parsed = parseFiche(raw);
      if (!Object.keys(parsed).length) throw new Error('parse');
      const date = todayLabel();
      setSections(parsed);
      setMeta({ date, cached: false });
      lxFicheSet(model.code, { sections: parsed, date, generatedAt: Date.now() });
    } catch (e) {
      setError(e.message === 'NO_API'
        ? 'No se ha podido conectar con el servicio. Revisa tu conexión e inténtalo de nuevo.'
        : 'No se ha podido generar la ficha. Inténtalo de nuevo.');
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => { load(false); /* eslint-disable-next-line */ }, [model.code]);
  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, []);

  const available = FICHE_SECTIONS.filter(s => sections && sections[s.key]);

  return (
    <div className="fiche-overlay" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="fiche-panel" role="dialog" aria-modal="true">
        {/* Header */}
        <div className="fiche-head">
          <div className="fiche-head-top">
            <div className="fiche-tax" style={{ '--tc': 'var(--violet-soft)' }}>{model.tax}</div>
            <button className="fiche-close" onClick={onClose} aria-label="Cerrar"><Icon name="x" size={18} /></button>
          </div>
          <div className="fiche-title">
            <div className="fiche-code">{model.code}</div>
            <div>
              <h2>{model.name}</h2>
              <p>{model.title}</p>
            </div>
          </div>
          <div className="fiche-chips">
            {model.for.map(f => <span key={f} className="fiche-chip">{CONTRIB_LABEL[f]}</span>)}
          </div>
        </div>

        {/* Transparencia (obligatoria) */}
        <div className="fiche-transparency">
          <div className="ft-warn">
            <Icon name="warn" size={15} />
            <span>Información <b>orientativa</b> generada por IA. Verifica siempre en la fuente oficial antes de presentar o tomar decisiones.</span>
          </div>
          <div className="ft-meta">
            <a className="ft-aeat" href={model.aeat} target="_blank" rel="noopener noreferrer">
              <Icon name="shield" size={13} /> Sede electrónica AEAT
              <Icon name="arrowRight" size={12} />
            </a>
            {meta?.date && <span className="ft-date"><Icon name="clock" size={12} /> Información de {meta.date}</span>}
          </div>
        </div>

        {/* Body */}
        <div className="fiche-body">
          {loading && <FicheSkeleton model={model} />}

          {error && !loading && (
            <div className="fiche-error">
              <Icon name="warn" size={22} />
              <p>{error}</p>
              <button className="btn btn-primary" onClick={() => load(true)}><Icon name="sparkle" size={15} /> Reintentar</button>
            </div>
          )}

          {!loading && !error && sections && (
            <div className="fiche-sections">
              {available.map(s => (
                <FicheSection
                  key={s.key}
                  section={s}
                  content={sections[s.key]}
                  open={!!open[s.key]}
                  onToggle={() => setOpen(o => ({ ...o, [s.key]: !o[s.key] }))}
                />
              ))}
              <div className="fiche-regen">
                <button className="btn btn-ghost" onClick={() => load(true)}><Icon name="sparkle" size={14} /> Regenerar ficha</button>
              </div>
            </div>
          )}
        </div>
      </div>

      <style>{`
        .fiche-overlay { position: fixed; inset: 0; z-index: 80; background: rgba(5,5,9,0.62); backdrop-filter: blur(4px); display: flex; justify-content: flex-end; animation: ficheFade .2s ease; }
        @keyframes ficheFade { from { opacity: 0; } }
        .fiche-panel { width: 600px; max-width: 100%; height: 100%; background: var(--bg-2); border-left: 1px solid var(--line-2); box-shadow: var(--shadow-lg); display: flex; flex-direction: column; animation: ficheSlide .32s cubic-bezier(.2,.8,.3,1); }
        @keyframes ficheSlide { from { transform: translateX(40px); opacity: .4; } }

        .fiche-head { padding: 20px 24px 16px; border-bottom: 1px solid var(--line); flex-shrink: 0; background: linear-gradient(180deg, var(--violet-dim), transparent); }
        .fiche-head-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
        .fiche-tax { font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--violet-soft); background: var(--violet-dim); border: 1px solid var(--violet-line); padding: 4px 11px; border-radius: 20px; }
        .fiche-close { width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center; color: var(--text-3); border: 1px solid var(--line); transition: .15s; }
        .fiche-close:hover { color: var(--text); background: var(--surface-2); border-color: var(--line-2); }
        .fiche-title { display: flex; align-items: center; gap: 15px; }
        .fiche-code { font-size: 26px; font-weight: 700; letter-spacing: -0.03em; color: var(--white); background: var(--surface); border: 1px solid var(--line-2); border-radius: 12px; padding: 8px 14px; font-variant-numeric: tabular-nums; white-space: nowrap; }
        .fiche-title h2 { font-size: 19px; font-weight: 600; color: var(--white); letter-spacing: -0.01em; }
        .fiche-title p { font-size: 13.5px; color: var(--text-2); margin-top: 2px; }
        .fiche-chips { display: flex; gap: 7px; margin-top: 14px; }
        .fiche-chip { font-size: 11.5px; font-weight: 500; color: var(--text-2); background: var(--surface); border: 1px solid var(--line); border-radius: 20px; padding: 3px 11px; }

        .fiche-transparency { padding: 14px 24px; border-bottom: 1px solid var(--line); flex-shrink: 0; background: var(--bg); }
        .ft-warn { display: flex; gap: 9px; font-size: 12px; color: var(--amber); line-height: 1.45; }
        .ft-warn .ic { flex-shrink: 0; margin-top: 1px; }
        .ft-warn b { color: #ffd87a; font-weight: 700; }
        .ft-meta { display: flex; align-items: center; gap: 16px; margin-top: 11px; flex-wrap: wrap; }
        .ft-aeat { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; font-weight: 600; color: var(--violet-soft); background: var(--violet-dim); border: 1px solid var(--violet-line); padding: 6px 12px; border-radius: 9px; transition: .15s; }
        .ft-aeat:hover { background: rgba(124,58,237,0.22); }
        .ft-date { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--text-3); }

        .fiche-body { flex: 1; min-height: 0; overflow-y: auto; padding: 18px 24px 32px; }
        .fiche-sections { display: flex; flex-direction: column; gap: 10px; }

        .fiche-error { text-align: center; padding: 50px 20px; display: flex; flex-direction: column; align-items: center; gap: 14px; color: var(--text-2); }
        .fiche-error .ic { color: var(--red); }
        .fiche-error p { font-size: 13.5px; max-width: 320px; line-height: 1.5; }
        .fiche-regen { margin-top: 8px; display: flex; justify-content: center; }

        @media (max-width: 640px) {
          .fiche-panel { width: 100%; border-left: none; }
          .fiche-head, .fiche-transparency, .fiche-body { padding-left: 18px; padding-right: 18px; }
        }
      `}</style>
    </div>
  );
}

function FicheSection({ section, content, open, onToggle }) {
  const toneColor = section.tone === 'red' ? 'var(--red)' : section.tone === 'amber' ? 'var(--amber)' : 'var(--violet-soft)';
  const toneBg = section.tone === 'red' ? 'var(--red-dim)' : section.tone === 'amber' ? 'var(--amber-dim)' : 'var(--violet-dim)';
  return (
    <div className={'fsec' + (open ? ' open' : '') + (section.tone ? ' tone-' + section.tone : '')}>
      <button className="fsec-head" onClick={onToggle} aria-expanded={open}>
        <span className="fsec-ic" style={{ color: toneColor, background: toneBg }}><Icon name={section.icon} size={15} /></span>
        <span className="fsec-label">{section.label}</span>
        <Icon name="arrowRight" size={15} className="fsec-arrow" />
      </button>
      {open && (
        <div className="fsec-body">
          <Markdown text={content} />
        </div>
      )}
      <style>{`
        .fsec { border: 1px solid var(--line); border-radius: 12px; background: var(--surface); overflow: hidden; transition: border-color .15s; }
        .fsec.open { border-color: var(--line-2); }
        .fsec.tone-red.open { border-color: rgba(248,113,113,0.3); }
        .fsec.tone-amber.open { border-color: rgba(251,191,36,0.3); }
        .fsec-head { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; padding: 14px 16px; transition: background .15s; }
        .fsec-head:hover { background: var(--surface-2); }
        .fsec-ic { width: 30px; height: 30px; border-radius: 8px; display: grid; place-items: center; flex-shrink: 0; }
        .fsec-label { flex: 1; font-size: 14px; font-weight: 600; color: var(--text); }
        .fsec-arrow { color: var(--text-4); transition: transform .2s; transform: rotate(90deg); }
        .fsec.open .fsec-arrow { transform: rotate(-90deg); }
        .fsec-body { padding: 2px 18px 18px 18px; }
        .fsec-body .md { font-size: 13.5px; }
      `}</style>
    </div>
  );
}

function FicheSkeleton({ model }) {
  return (
    <div className="fiche-skel">
      <div className="fsk-head">
        <LexAvatar size={36} />
        <div>
          <strong>Lexly está preparando la ficha del {model.name}…</strong>
          <span>Generando información fiscal estructurada</span>
        </div>
      </div>
      {[0,1,2].map(i => (
        <div key={i} className="fsk-block">
          <div className="fsk-bar w40"></div>
          <div className="fsk-line"></div>
          <div className="fsk-line w80"></div>
          <div className="fsk-line w60"></div>
        </div>
      ))}
      <style>{`
        .fiche-skel { display: flex; flex-direction: column; gap: 20px; }
        .fsk-head { display: flex; align-items: center; gap: 13px; padding: 6px 2px 8px; }
        .fsk-head strong { display: block; font-size: 14px; color: var(--white); font-weight: 600; }
        .fsk-head span { font-size: 12.5px; color: var(--text-3); }
        .fsk-block { display: flex; flex-direction: column; gap: 9px; padding: 16px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); }
        .fsk-bar, .fsk-line { height: 10px; border-radius: 5px; background: linear-gradient(90deg, var(--surface-2), var(--surface-3), var(--surface-2)); background-size: 200% 100%; animation: fskShimmer 1.4s infinite; }
        .fsk-bar { height: 13px; } .w40 { width: 40%; } .w80 { width: 80%; } .w60 { width: 60%; }
        @keyframes fskShimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
      `}</style>
    </div>
  );
}

/* ============================================================
   Sección principal
   ============================================================ */
function AlertasFiscales() {
  const today = new Date();
  today.setHours(0, 0, 0, 0);

  const profile = useProfile();
  const profileType = (typeof lxProfileActivityType === 'function') ? lxProfileActivityType() : null;
  const ccaa = (profile.consent.region && profile.data.region && profile.data.region.ccaa) || null;

  const [query, setQuery] = useState('');
  const [filter, setFilter] = useState('todos');
  const [openModel, setOpenModel] = useState(null);

  const allItems = FISCAL_MODELS.map(m => computeItem(m, today));
  const periodic = allItems.filter(i => i.due).sort((a, b) => a.days - b.days);
  const nextUp = periodic[0];

  const q = query.trim().toLowerCase();
  const items = allItems.filter(i => {
    if (filter !== 'todos' && !i.for.includes(filter)) return false;
    if (!q) return true;
    return (i.code + ' ' + i.name + ' ' + i.title + ' ' + i.tax + ' ' + i.desc).toLowerCase().includes(q);
  }).map(i => ({ ...i, recommended: profileType ? i.for.includes(profileType) : false }));
  // sort: recomendados primero, luego periódicos por días, luego eventuales
  items.sort((a, b) => {
    if (a.recommended !== b.recommended) return a.recommended ? -1 : 1;
    if (a.due && b.due) return a.days - b.days;
    if (a.due) return -1;
    if (b.due) return 1;
    return 0;
  });

  const TIPO_LABEL = { autonomo: 'autónomo', pyme: 'pyme', particular: 'particular' };

  const FILTERS = [
    { id: 'todos', label: 'Todos' },
    { id: 'autonomo', label: 'Autónomo' },
    { id: 'pyme', label: 'Pyme' },
    { id: 'particular', label: 'Particular' },
  ];

  return (
    <div className="section-pad fade-up">
      <div className="eyebrow">Centro fiscal</div>
      <h1 className="h-title">Alertas fiscales</h1>
      <div className="alert-subrow">
        <p className="h-sub">Tus obligaciones con Hacienda, ordenadas por urgencia. Pulsa cualquier modelo para abrir su ficha detallada.</p>
        <TerritoryBadge />
      </div>

      {/* Próximo vencimiento */}
      {nextUp && (
        <div className="alert-hero card" style={{ borderColor: nextUp.status.line }}>
          <div className="ah-left">
            <div className="ah-badge" style={{ color: nextUp.status.c, background: nextUp.status.bg }}>
              <Icon name="clock" size={13} /> {nextUp.status.label}
            </div>
            <h3>Lo próximo: {nextUp.name} · {nextUp.title}</h3>
            <p>{nextUp.desc}</p>
            <button className="ah-open" onClick={() => setOpenModel(nextUp)}>Ver ficha detallada <Icon name="arrowRight" size={14} /></button>
          </div>
          <div className="ah-count" style={{ color: nextUp.status.c }}>
            <div className="ah-num">{nextUp.days}</div>
            <div className="ah-unit">{nextUp.days === 1 ? 'día' : 'días'}</div>
          </div>
        </div>
      )}

      {/* Calendario anual */}
      <YearStrip today={today} items={periodic} />

      {ccaa && <ForalNotice ccaa={ccaa} />}

      {profileType && (
        <div className="fiscal-perfil">
          <Icon name="sparkle" size={14} />
          <span>Hemos destacado los modelos relevantes para tu perfil de <b>{TIPO_LABEL[profileType] || profileType}</b>. Cámbialo en <b>Privacidad y mis datos</b>.</span>
        </div>
      )}

      {/* Toolbar: buscador + filtros */}
      <div className="fiscal-toolbar">
        <div className="fiscal-search">
          <Icon name="search" size={16} />
          <input placeholder="Buscar modelo o impuesto…" value={query} onChange={(e) => setQuery(e.target.value)} />
          {query && <button className="fs-clear" onClick={() => setQuery('')}><Icon name="x" size={14} /></button>}
        </div>
        <div className="fiscal-filters">
          {FILTERS.map(f => (
            <button key={f.id} className={'ffilter' + (filter === f.id ? ' on' : '')} onClick={() => setFilter(f.id)}>{f.label}</button>
          ))}
        </div>
      </div>

      {/* Cuadrícula de modelos */}
      {items.length === 0 ? (
        <div className="fiscal-empty">
          <div className="fe-ic"><Icon name="search" size={22} /></div>
          <h3>Sin resultados</h3>
          <p>No encontramos modelos para esa búsqueda o filtro. Prueba con otro término.</p>
        </div>
      ) : (
        <div className="alert-grid">
          {items.map((it, i) => (
            <button key={i} className={'alert-card card' + (it.recommended ? ' reco' : '')} onClick={() => setOpenModel(it)}>
              <div className="acard-top">
                <div className="acard-code"><span className="acode-num">{it.code}</span></div>
                {it.status ? (
                  <div className="acard-status" style={{ color: it.status.c, background: it.status.bg }}>
                    <span className="acard-dot" style={{ background: it.status.c }}></span>{it.status.label}
                  </div>
                ) : (
                  <div className="acard-status neutral"><span className="acard-dot" style={{ background: 'var(--text-3)' }}></span>Según actividad</div>
                )}
              </div>
              <div className="acard-body">
                <strong>{it.name}</strong>
                <span className="acard-desc">{it.title}</span>
                {it.recommended && <span className="acard-reco"><Icon name="sparkle" size={11} /> Para tu perfil</span>}
              </div>
              <div className="acard-foot">
                {it.due ? (
                  <>
                    <div className="acard-date"><Icon name="calendar" size={13} />{it.due.getDate()} {MONTHS_LONG[it.due.getMonth()]} {it.due.getFullYear()}</div>
                    <div className="acard-days" style={{ color: it.status.c }}>{it.days} {it.days === 1 ? 'día' : 'días'}</div>
                  </>
                ) : (
                  <div className="acard-date"><Icon name="file" size={13} />Sin plazo periódico fijo</div>
                )}
              </div>
              <div className="acard-open"><Icon name="arrowRight" size={15} /></div>
              {it.status && <div className="acard-bar"><div style={{ width: barWidth(it.days), background: it.status.c }}></div></div>}
            </button>
          ))}
        </div>
      )}

      {ccaa && <TerritorialTaxes ccaa={ccaa} />}

      <div className="alert-legend">
        <span><i style={{ background: 'var(--green)' }}></i> Más de 30 días</span>
        <span><i style={{ background: 'var(--amber)' }}></i> Entre 10 y 30 días</span>
        <span><i style={{ background: 'var(--red)' }}></i> Menos de 10 días</span>
      </div>

      {openModel && <FichePanel model={openModel} onClose={() => setOpenModel(null)} />}

      <style>{`
        .alert-hero { display: flex; align-items: center; gap: 24px; padding: 22px 26px; margin: 24px 0 26px; }
        .ah-left { flex: 1; }
        .ah-badge { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; font-weight: 600; padding: 4px 11px; border-radius: 20px; margin-bottom: 12px; }
        .ah-left h3 { font-size: 18px; font-weight: 600; color: var(--white); letter-spacing: -0.01em; margin-bottom: 6px; }
        .ah-left p { font-size: 13px; color: var(--text-2); line-height: 1.5; max-width: 520px; }
        .ah-open { display: inline-flex; align-items: center; gap: 6px; margin-top: 12px; font-size: 12.5px; font-weight: 600; color: var(--violet-soft); transition: gap .15s; white-space: nowrap; }
        .ah-open:hover { gap: 9px; }
        .ah-count { text-align: center; flex-shrink: 0; line-height: 1; }
        .ah-num { font-size: 52px; font-weight: 700; letter-spacing: -0.04em; font-variant-numeric: tabular-nums; }
        .ah-unit { font-size: 13px; color: var(--text-3); margin-top: 4px; }

        .fiscal-perfil { display: flex; align-items: center; gap: 9px; margin-top: 18px; padding: 11px 15px; background: var(--violet-dim); border: 1px solid var(--violet-line); border-radius: 11px; font-size: 12.5px; color: var(--text-2); }
        .alert-subrow { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
        .alert-subrow .h-sub { flex: 1; min-width: 240px; margin-top: 0; }
        .alert-subrow .terr-wrap { margin-top: 4px; flex-shrink: 0; }
        .fiscal-perfil .ic { color: var(--violet-soft); flex-shrink: 0; }
        .fiscal-perfil b { color: var(--violet-soft); font-weight: 600; }
        .alert-card.reco { border-color: var(--violet-line); background: linear-gradient(180deg, var(--violet-dim), transparent); }
        .acard-reco { display: inline-flex; align-items: center; gap: 5px; align-self: flex-start; margin-top: 7px; font-size: 10.5px; font-weight: 600; color: var(--violet-soft); background: var(--violet-dim); border: 1px solid var(--violet-line); padding: 2px 9px; border-radius: 20px; }
        .acard-reco .ic { color: var(--violet-soft); }
        .fiscal-toolbar { display: flex; align-items: center; gap: 14px; margin: 26px 0 18px; flex-wrap: wrap; }
        .fiscal-search { flex: 1; min-width: 220px; display: flex; align-items: center; gap: 10px; background: var(--bg-2); border: 1px solid var(--line-2); border-radius: 11px; padding: 0 13px; transition: border-color .15s, box-shadow .15s; }
        .fiscal-search:focus-within { border-color: var(--violet-line); box-shadow: 0 0 0 3px var(--violet-dim); }
        .fiscal-search > .ic { color: var(--text-3); flex-shrink: 0; }
        .fiscal-search input { flex: 1; background: transparent; border: none; outline: none; padding: 11px 0; font-size: 13.5px; color: var(--text); }
        .fiscal-search input::placeholder { color: var(--text-4); }
        .fs-clear { width: 26px; height: 26px; border-radius: 7px; display: grid; place-items: center; color: var(--text-3); transition: .15s; }
        .fs-clear:hover { color: var(--text); background: var(--surface-3); }
        .fiscal-filters { display: flex; gap: 6px; background: var(--surface); border: 1px solid var(--line); border-radius: 11px; padding: 4px; }
        .ffilter { padding: 7px 13px; border-radius: 8px; font-size: 12.5px; font-weight: 500; color: var(--text-3); transition: color .15s, background .15s; }
        .ffilter:hover { color: var(--text); }
        .ffilter.on { background: var(--violet-dim); color: var(--violet-soft); }

        .alert-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; }
        .alert-card { text-align: left; padding: 18px; display: flex; flex-direction: column; gap: 13px; position: relative; overflow: hidden; transition: border-color .15s, transform .12s, background .15s; }
        .alert-card:hover { transform: translateY(-2px); border-color: var(--violet-line); background: var(--surface-2); }
        .acard-top { display: flex; align-items: center; justify-content: space-between; }
        .acard-code { display: flex; align-items: baseline; gap: 5px; }
        .acode-num { font-size: 23px; font-weight: 700; letter-spacing: -0.03em; color: var(--white); font-variant-numeric: tabular-nums; }
        .acard-code::before { content: 'Modelo'; font-size: 10.5px; color: var(--text-3); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 600; align-self: center; }
        .acard-status { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600; padding: 4px 10px; border-radius: 20px; }
        .acard-status.neutral { color: var(--text-3); background: rgba(255,255,255,0.05); }
        .acard-dot { width: 6px; height: 6px; border-radius: 50%; }
        .acard-body { display: flex; flex-direction: column; gap: 2px; }
        .acard-body strong { font-size: 14.5px; font-weight: 600; color: var(--text); }
        .acard-desc { font-size: 12.5px; color: var(--text-3); }
        .acard-foot { display: flex; align-items: center; justify-content: space-between; padding-top: 12px; border-top: 1px solid var(--line); }
        .acard-date { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--text-2); }
        .acard-date .ic { color: var(--text-3); }
        .acard-days { font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums; }
        .acard-open { position: absolute; top: 16px; right: 16px; color: var(--text-4); opacity: 0; transform: translateX(-4px); transition: opacity .15s, transform .15s; }
        .alert-card:hover .acard-open { opacity: 1; transform: none; color: var(--violet-soft); }
        .acard-bar { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; background: rgba(255,255,255,0.04); }
        .acard-bar > div { height: 100%; transition: width .4s; }

        .fiscal-empty { text-align: center; max-width: 380px; margin: 40px auto; display: flex; flex-direction: column; align-items: center; }
        .fe-ic { width: 52px; height: 52px; border-radius: 14px; background: var(--surface); border: 1px solid var(--line); display: grid; place-items: center; color: var(--text-3); margin-bottom: 14px; }
        .fiscal-empty h3 { font-size: 16px; font-weight: 600; color: var(--white); margin-bottom: 6px; }
        .fiscal-empty p { font-size: 13px; color: var(--text-3); line-height: 1.5; }

        .alert-legend { display: flex; gap: 20px; flex-wrap: wrap; margin-top: 22px; padding-top: 16px; border-top: 1px solid var(--line); }
        .alert-legend span { display: inline-flex; align-items: center; gap: 7px; font-size: 11.5px; color: var(--text-3); }
        .alert-legend i { width: 8px; height: 8px; border-radius: 50%; }

        @media (max-width: 860px) {
          .alert-hero { flex-direction: column; align-items: flex-start; gap: 14px; }
          .ah-num { font-size: 42px; }
          .alert-grid { grid-template-columns: 1fr; }
          .fiscal-filters { width: 100%; overflow-x: auto; }
        }
      `}</style>
    </div>
  );
}

function YearStrip({ today, items }) {
  const curMonth = today.getMonth();
  const byMonth = {};
  items.forEach(it => {
    if (!it.due) return;
    const m = it.due.getMonth();
    (byMonth[m] = byMonth[m] || []).push(it);
  });
  return (
    <div className="card year-strip">
      <div className="ys-head"><Icon name="calendar" size={14} /><span>Calendario {today.getFullYear()}</span></div>
      <div className="ys-track">
        {MONTHS_ES.map((m, i) => {
          const deadlines = byMonth[i] || [];
          const isCur = i === curMonth;
          return (
            <div key={i} className={'ys-month' + (isCur ? ' cur' : '')}>
              <div className="ys-dots">
                {deadlines.map((d, j) => (
                  <span key={j} className="ys-dot" style={{ background: d.status.c }} title={`${d.name} · ${d.title}`}></span>
                ))}
              </div>
              <div className="ys-label">{m}</div>
              {isCur && <div className="ys-now">Hoy</div>}
            </div>
          );
        })}
      </div>
      <style>{`
        .year-strip { padding: 18px 20px 14px; margin-bottom: 0; }
        .ys-head { display: flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 600; color: var(--text-2); margin-bottom: 16px; }
        .ys-head .ic { color: var(--text-3); }
        .ys-track { display: grid; grid-template-columns: repeat(12, 1fr); gap: 4px; }
        .ys-month { position: relative; text-align: center; padding: 6px 2px 4px; border-radius: 8px; transition: background .15s; }
        .ys-month.cur { background: var(--violet-dim); }
        .ys-dots { height: 18px; display: flex; align-items: center; justify-content: center; gap: 3px; flex-wrap: wrap; }
        .ys-dot { width: 7px; height: 7px; border-radius: 50%; box-shadow: 0 0 8px currentColor; }
        .ys-label { font-size: 11px; color: var(--text-3); font-weight: 500; margin-top: 2px; }
        .ys-month.cur .ys-label { color: var(--violet-soft); font-weight: 600; }
        .ys-now { font-size: 9px; color: var(--violet-soft); font-weight: 600; margin-top: 1px; }
        @media (max-width: 860px) { .ys-track { grid-template-columns: repeat(6, 1fr); gap: 6px; row-gap: 10px; } }
      `}</style>
    </div>
  );
}

Object.assign(window, { AlertasFiscales });
