const { useState, useEffect, useRef } = React;

const ESCUDO = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAYAAAA5ZDbSAABb5ElEQVR42u19d3hVVdb+u0+5PT2BhAQIvQoivQoIoth7H9vY64hjmVHBcfSzISqiAjaKIMVCld57LyGkkd5zez9tr98fuTcGR2fG+fQbZ37u58mTJzf3nnPufvda611l7wX8Nv4rBhEJsd/diIhT8+DCb1Pz3z1+A/g3gH8bvwH82/gN4N/GbwD/Nn4D+J92GdjUqVMFIhKWLl0qEpFIROLWrVulrVu3SvG/Y/8Tpk6dKgBgvy2HXymYMaAkIhIFQfhfXSt2HSl2TfYfNA8/6AdL/4GYstjECwAMxhgBMFq/ISMjw7Fq1arkjIyMVEmS2nTo0AEArIcPH+7BOcfgwYMLAUROnjzJNE1rqK2tdV122WVOxpgCQP/+xG3btk3Ytm0bf/HFF/l/3GT9h61QQRAEnYhaXn/99dcz77zzzp6MseGyLJ/LOe8JID0ajaZEIhGroihQVRWapoExBiICYwyyLMNsNsNkMsFsNkcdDkeDYRgNjLFCzvkhRVH23X777SUbNmxwt5bwbdu2ib9GsIlIYIxxIuoGoDCG7a9eBbEYsDwmqQCQtHr16v6jRo0ZwxhdpOt6b7fbnVJZWYm8vDxeWFjoPX36tKO6ulp1u92mcDjMVVVVOedmIhIBkCAIAmMsZDKZDLvdbsnIyEBubm6of//+kc6dO7fv06cPsrKyYLfbXaIoHguHw+uKiorWTpgwIb812AAYY4z/BvC/CKwgCEZcWg8fPtyrffv2twG4IRqNdC4qKsLmzVuwZcsW7eTJk5FwOKxbrVZkZ2cHcnNz2+Xm5vKcnBxKTU0Vk5OTeVJSEldV1SIIAiMiBAIB+Hw+vampSauoqDBXVFTolZWVjXV1dQmqqnqTk5NTBwwYkDBhwgSMHTsWXbp0UdLS0g56vd5vlixZsvzhhx+uiAO9bNky4frrr+cA6DeA//GDiq2AlfLz8y/PzMx8OBKJjMnPzxdXrvy6dtmy5VRf38hSU1PtAwcOFEeOHCmdf/754U6dOiW0a9dOlmX5X7q33+9HbW0tlZeXO3ft2sV27drlO3jwYGI4HLb27Nmz8corr8i64oorrLm5uZFwOLIzLy9v5hVXXLEazbYDhmEI/y6J/jGAf1VsuBVrlauqqn7n9/uPlZSU0PTp07VevXr5ARRnZbWtvvPO2wPLli2hqqoq3ooxEhFxl8ulc85J0zRSFIXWrFmjbdy4qWHz5s18/vz5bqfTGdV1nVRVJU3TSNM00nW95fOct74c8eLiYpo3b17gxhuvL0lMTMgDsH306JEln3zyCVVWVlI0Gt184MDhywDI8QX672DfP8aify3qWIz/UVhYeFVjY+PpoqJCeuqpP0aSk5OPAMi78MILI4sXLw46nU1GawRqamr4wYMH/WvWrNE3btwY/vLLL/0ul4viQL388l+1Z555uikYDNKsWbMab7nlljLDMM5aGJxzCoVCtGnTJmXLli2B1atXN5WUlIRj12h5X21tdei992ZGhgwZ5ARQ1rFjx1PTp0+PlJeXkcfj2b59+/YxrTXR//cAExGL+61Tp04dX11dvbOyspKeffYZstvNR00m8eQ999xTs2/fvmj8of1+P5WWllIkEiHOOW3ZsiV04YUXVvl8PoNzTpxzikQipGkaERHNmjWTXnjhOSIiCgQCdNlll6mRSITHgY2BTdu3b68/depUkIiovLxc2bNnj9swDPJ4PLRx40ZveXl5i5hHo1Fjy5YtTdddd10xgJXZ2Vmls2bNjNbX1xvl5eVfXn755QP+r6X5Vwdwqy/uqK2tner1esMLFy705+Tk7AFw7O677/QcOXKoRVoNwyBd1+nQoUPK5s2bI/HXI5EITZo0Sa2vr28t2KSqKiciWrBgXsNf//qXqnA4TDt37tSWL1+uxK9HRKTrOicievfdd+t27tzpPUs/c07BYJCPGjWq4NixY1HODYpEIlxV1Zb3HDx4sO7KK68oBVB8zjl93OvWrTMCgYDzwIEDD8a/69KlS8X/nwBm77zzjhkAXnnlxcG1tbV5BQUFdMUVVxwDsOvyyy8/cPTo4UYiMmIAUOvfNTU14cOHD/uJiDRN40REd9xxh3vVqlWBxsZGbe/evWppaWmLZC5fvqzx4YcfrNi+fasxadKFFeFwmBuG0QKwYRjEOadTp06FBw0atOPjjz92tr72pk0bI3fffefJ+GIwDIPKysrUt99+293U1BSfR1q6dInWr19fD4Bj9957b11NTQ01NDSsGjduXEcAuPfee22/ZNzhVwEwETFRbF7MmzdvfqCpqdFYtmxptdks78jIyDg9d+5cXyQSaZn49evXK1ddddUpl8vVInUVFRV806ZNnIhIURRO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79J46ODBSDxoEf9faWlp9IorLi+58MLxRXv27GxhumVlZXzVqlU85gZxIqKZM9+p+8MfHqtsxfKprq6Oz5o1yxkKhbT4+4iIFixYEElLSytNSUnZumHDBgoEApUffPBB/9YL/r8K4Fj2yQIAn332WR+n01m6b99e6t6962EA61555ZXaYDCotor8xCJVzdLwwAP3nrzvvnt8Ho/HiLtX8QWg6zqfPPmiSFFRgRFXnTEp40Scjhw55J027fnQlClPtARS4sPpdBpbtmxW9+zZrY8dO3rHs88+44lnmz7//PPW0Szu93vVCRPG7SgvLw2oqkrHjx83vvrqq3BFRUXLompqago+/vjjpwAcGT9+/JnS0lIKBALfTp48ObO1h/FfCXDrMJzZbM5tbGxc2NBQT4899kgpgMPDhw8/sXv37rrW2Zq4nX7rren+6dOnB76XxSEiopKSEn7xxRf5NU014gIcB7+pqZEvXfpF+MyZEho1amSRz+dVW0eVDMOgL75YHCEiHgwG+COPPOw+//zza4qLi/1btmwhp9N5Vuboo4/mNAwYMKBg+fJlyuHDh1ueT1UVmjfvM61r165rABx888036/1+v1peXv4yANPP4Qr9RwDcivEBAPbt23dXMBh0rly5kvfq1WsXgKK77rqr4vjx4y1sxTAMqqmpIb8/EAOuRXKJiOibb76h3/3u9rNi0/HfmzZtpiVLltKpU6foxhuvb1q0aEEoToZ0vfk9K1asoJgUUnOw4svouHHjTt94443+6upqrbVWOHPmDN+yZUukdTpwy5bN6iWXXFwGoGDChAn7Tpw4GfL7/cULFiy4CgBEUcT/JojxHwdwPKwZt8tTp07tUFNTs7Kuro6mTp1KNpvtgM1mq54yZUpZUVFRgIiMH0lWcCKiP/7xj9633367qfVrnHOKRqO0cOHCFrA3blyvX331FaF4Zir++p49e4xjx44ZnHOKA+d0OqM33HBD1ebNm/3xLuTfz0vv2LFDu+WWWxsB7GzXrm3FwoULnPX19XT69Om5aWlpCa3sLfu/EJpfFcDfV9kAsGHDhusCgcCRgoICevzxx10mk+mA1Wotu/XWW/Xt27dHWldicG6QpmlGKBSiGTNm1G/bts0Tt7tx4A4dOmRs27aNfyfthnLRRZNKjx49YrTWAJWVldHVq1e7459vdR+jrq7urGyYokQjq1at9F5zzdUlAI6mpqbUvfbaq87q6mrd6XRuXbly5eD49/klVfJ/DMCtS39if2Zu2rTpz16v90x+fj49+eSTSnp6eg2AglGjRpZ/9NGcyvz8PP69ogjSNI1UVSU9lgFSFIV/8803Hrfbrcd8a05ENH36dOebb77pjL+Pc05Op1PZuXNnbTQaPSt79B2oinHy5En/K6/8talv317FAI506dJp3yeffJRfWVmu+ny+A9XV1Zd+b9Gy/2uz96sFOD5mz54tt7JVjurq6rv8fv+RyspKdc6cOTRq1MgGAEdMJqn4/PPHhN94442mAwcOBJqamvj3y2B+aMRdpEgk8o/eyuvq6mjbtm3eV199tXDQoEEuQRAaTCaxcvLki7Rly5ZSdVW1UVtbv3/v3r034rvDzP7ldN8vBTDDr3B8r1zFXFBQMK59+/Z3apo2sbT0TMqOHTucS5d+YT906OgZVVUz2rVrl9CnTx8MGjRI69q1a1KPHj3U9u3bU2Jiomyz2YQf2WJChmFoHo/H5HK5UFNTo5WVldGpU6eix44f9Z84fsLhcrlVh8MRGD58eJerrroKY8eej8TExCDAviktLf1wzJgxu4Hm/URffPHFT24c+UvM2f9pLPpnKCAQBEEw4jXWn332WXb//v1vzsnJuUQUxb6NjY1peXl52LFjB504caIkLy9PdrvdDs65bjabeXJycigx0dEuMTFRtlgsYZvNJuq67ohEIlzTjEafzxf0+XyZfr/foihKkyiKprZt20i9evWUhw0bZh8xYiR69eqNlJSU+sTExM0nTpzYdM899+w4dOhQaVxiAcQXI/0ahOI/CeAWoJcuXSpcd911aL1945133smYPHnyyJycnNEAhiqK0tXlcrX1+/2ora1FZWUlampq0NTUAI/HA1VVw+FwWGKMyWazhdlsdqSlpSEjIwM5OTno0KEDsrKykJaWFrXbHS5RFIoMw9hdWVm587nnnjuwYsUK7/dsLP2UorjfAP4nXatt27aJY8eO5d+f3GeeeSbld7/7XSdRFM9JS0vLTUtLy2hsbOyVmGi3WCx2cjobOyqKYgKAlJQUj83mcAaDQWY2m2sZYw2MsRKn01mxZ8+emueee64+Pz+/8vtsf9u2bWzbtm3877Va/7UBjP/UESM0IhFJ/2SZixlAQuxH/CevLy1dulT8V8tofg0kS/pPBTi22dxoDQjOOl4WQKvuYIIgKESkxD4Lznmc7bZ+P8V/YtfX8R8+JPyXjBggf08lsVaLAb8m+/kbwD+TFsP/h0PAb+M3gH8b/8Eq+t8VWvtt/PzCGgsIncX4pf9fyMb/B4PHCKTyfQnO+W1u/jsciRiR7HDWi4ZhRH+bm/8uoAVBaMmuMPpnuxz+Nv5j/eDfAP7vVNctALPf5uM3P/i38R86/h+eXunHMi/8HAAAAABJRU5ErkJggg==";

const STYLE = `
  @import url('https://fonts.googleapis.com/css2?family=PT+Serif:ital,wght@0,400;0,700;1,400&family=Lato:wght@300;400;700;900&display=swap');
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
  :root {
    /* Paleta oficial Unisant */
    --rojo:       #8B1A1A;
    --rojo-vivo:  #C8102E;
    --rojo-deep:  #5C0A0A;
    --rojo-ink:   #3D0000;
    --dorado:     #F5A800;
    --dorado-dk:  #C8860A;
    --dorado-lt:  #F5A80022;
    --gris:       #D4D0C8;
    --gris-lt:    #F2F0EC;
    --blanco:     #FFFFFF;
    --papel:      #FAF8F5;
    --ink:        #1A0A0A;
    --muted:      #7A6060;
    --border:     #D4C8C8;
    /* Semánticos */
    --danger:     #C8102E;
    --danger-lt:  #C8102E22;
    --warn:       #F5A800;
    --warn-lt:    #F5A80022;
    --ok:         #2D6A2D;
    --ok-lt:      #2D6A2D20;
    --shadow:     0 2px 12px #3D000018;
    --shadow-lg:  0 8px 40px #3D000028;
  }
  body { background:var(--papel); color:var(--ink); font-family:'Lato',sans-serif; }
  .app { height:100vh; display:flex; flex-direction:column; overflow:hidden; }

  /* ── Header ── */
  .header {
    background: linear-gradient(135deg, var(--rojo-ink) 0%, var(--rojo-deep) 40%, var(--rojo) 100%);
    color:var(--blanco); padding:0 32px;
    display:flex; align-items:stretch; justify-content:space-between;
    border-bottom:3px solid var(--dorado);
    box-shadow: 0 2px 20px #3D000044;
  }
  .header-brand {
    display:flex; align-items:center; gap:16px; padding:12px 0;
  }
  .header-escudo {
    width:52px; height:52px; object-fit:contain;
    filter: drop-shadow(0 2px 4px #00000044);
  }
  .header-text { display:flex; flex-direction:column; gap:1px; }
  .header-univ {
    font-family:'PT Serif',serif; font-size:11px; font-weight:700;
    letter-spacing:2px; text-transform:uppercase; color:var(--dorado);
    line-height:1;
  }
  .header-title {
    font-family:'PT Serif',serif; font-size:20px; font-weight:700;
    letter-spacing:-.3px; color:var(--blanco); line-height:1.1;
  }
  .header-sub {
    font-family:'Lato',sans-serif; font-size:10px; color:#C8A8A8;
    letter-spacing:1.5px; text-transform:uppercase; line-height:1;
  }
  .header-right { display:flex; align-items:center; gap:14px; }
  .avatar {
    width:34px; height:34px; border-radius:50%;
    color:#fff; display:flex; align-items:center; justify-content:center;
    font-weight:900; font-size:13px; flex-shrink:0;
    border:2px solid var(--dorado);
  }
  .avatar-name { font-size:13px; font-weight:700; color:var(--blanco); }
  .avatar-role { font-size:10px; font-family:'Lato',sans-serif; color:#C8A8A8; text-transform:uppercase; letter-spacing:.8px; }
  .btn-logout {
    background:none; border:1px solid #C8A8A855; color:#C8A8A8;
    border-radius:3px; padding:5px 12px; font-size:11px; cursor:pointer;
    font-family:'Lato',sans-serif; font-weight:700; letter-spacing:.5px;
    text-transform:uppercase; transition:all .15s;
  }
  .btn-logout:hover { border-color:var(--dorado); color:var(--dorado); }

  /* ── Sidebar ── */
  .body-wrap { display:flex; flex:1; overflow:hidden; }

  .sidebar {
    background: linear-gradient(180deg, var(--rojo-ink) 0%, var(--rojo-deep) 100%);
    display:flex; flex-direction:column;
    transition: width .25s cubic-bezier(.4,0,.2,1);
    overflow:hidden; flex-shrink:0;
    border-right:1px solid #ffffff11;
    box-shadow: 2px 0 12px #3D000033;
    position:relative; z-index:10;
  }
  .sidebar.expanded { width:220px; }
  .sidebar.collapsed { width:56px; }

  .sidebar-toggle {
    display:flex; align-items:center; justify-content:flex-end;
    padding:10px 12px; border-bottom:1px solid #ffffff11;
    flex-shrink:0;
  }
  .sidebar-toggle-btn {
    background:none; border:none; cursor:pointer;
    color:#C8A8A8; width:28px; height:28px;
    display:flex; align-items:center; justify-content:center;
    border-radius:3px; transition:all .15s; font-size:16px;
  }
  .sidebar-toggle-btn:hover { background:#ffffff11; color:var(--dorado); }

  .nav-btn {
    background:none; border:none; cursor:pointer;
    font-family:'Lato',sans-serif; font-size:11px; font-weight:700;
    color:#C8A8A8; padding:0; width:100%;
    border-left:3px solid transparent;
    transition:all .18s; letter-spacing:.6px; text-transform:uppercase;
    display:flex; align-items:center; gap:12px;
    white-space:nowrap; overflow:hidden;
    min-height:46px; flex-shrink:0;
  }
  .nav-btn:hover { color:var(--dorado); background:#ffffff08; border-left-color:#ffffff22; }
  .nav-btn.active { color:var(--dorado); background:#ffffff12; border-left-color:var(--dorado); }

  .nav-icon {
    width:56px; height:46px; display:flex; align-items:center; justify-content:center;
    flex-shrink:0; font-size:17px;
  }
  .nav-label { font-size:11px; overflow:hidden; white-space:nowrap; }

  .sidebar-footer {
    margin-top:auto; border-top:1px solid #ffffff11; padding:12px 0;
  }

  /* ── Layout ── */
  .main { flex:1; padding:28px 32px; overflow-y:auto; }
  .stack { display:flex; flex-direction:column; gap:16px; }
  .grid-2 { display:grid; grid-template-columns:1fr 1fr; gap:20px; }
  .grid-4 { display:grid; grid-template-columns:repeat(4,1fr); gap:16px; }

  /* ── Cards ── */
  .card {
    background:var(--blanco); border:1px solid var(--border);
    border-radius:4px; padding:24px; box-shadow:var(--shadow);
  }
  .card-title {
    font-family:'PT Serif',serif; font-size:17px; font-weight:700;
    margin-bottom:20px; padding-bottom:12px;
    border-bottom:2px solid var(--dorado);
    color:var(--rojo-deep); display:flex; align-items:center; gap:10px;
  }

  /* ── Stats ── */
  .stat-box {
    background:var(--blanco); border:1px solid var(--border);
    border-radius:4px; padding:20px 24px;
    display:flex; flex-direction:column; gap:4px;
    box-shadow:var(--shadow);
  }
  .stat-num { font-family:'PT Serif',serif; font-size:38px; line-height:1; font-weight:700; }
  .stat-label { font-size:10px; color:var(--muted); text-transform:uppercase; letter-spacing:1px; font-family:'Lato',sans-serif; font-weight:700; }
  .stat-box.rust { border-left:4px solid var(--danger); }
  .stat-box.amber { border-left:4px solid var(--warn); }
  .stat-box.sage { border-left:4px solid var(--ok); }
  .stat-box.ink { border-left:4px solid var(--rojo); }

  /* ── Forms ── */
  .form-group { display:flex; flex-direction:column; gap:6px; }
  .form-label {
    font-size:11px; font-weight:700; color:var(--muted);
    text-transform:uppercase; letter-spacing:.8px; font-family:'Lato',sans-serif;
  }
  .form-input,.form-select,.form-textarea {
    background:var(--gris-lt); border:1px solid var(--border);
    border-radius:3px; padding:10px 12px;
    font-family:'Lato',sans-serif; font-size:14px; color:var(--ink);
    transition:border-color .15s;
  }
  .form-input:focus,.form-select:focus,.form-textarea:focus {
    outline:none; border-color:var(--rojo); background:var(--blanco);
    box-shadow: 0 0 0 2px var(--danger-lt);
  }
  .form-textarea { resize:vertical; min-height:80px; }
  .form-row { display:grid; grid-template-columns:1fr 1fr; gap:16px; }
  .form-row-3 { display:grid; grid-template-columns:1fr 1fr 1fr; gap:16px; }

  /* ── Buttons ── */
  .btn {
    font-family:'Lato',sans-serif; font-size:12px; font-weight:700;
    padding:10px 22px; border-radius:3px; border:none; cursor:pointer;
    transition:all .15s; letter-spacing:.6px; text-transform:uppercase;
  }
  .btn-primary { background:var(--rojo); color:#fff; }
  .btn-primary:hover { background:var(--rojo-deep); }
  .btn-secondary { background:var(--gris-lt); color:var(--ink); border:1px solid var(--border); }
  .btn-secondary:hover { background:var(--gris); }
  .btn-sage { background:var(--ok); color:#fff; }
  .btn-sage:hover { background:#1f4d1f; }
  .btn-gold { background:var(--dorado); color:var(--rojo-ink); }
  .btn-gold:hover { background:var(--dorado-dk); }
  .btn-sm { padding:5px 12px; font-size:11px; }
  .btn-group { display:flex; gap:10px; margin-top:8px; flex-wrap:wrap; }
  .btn:disabled { opacity:.45; cursor:not-allowed; }

  /* ── Badges ── */
  .badge {
    display:inline-flex; align-items:center; font-size:10px; font-weight:700;
    font-family:'Lato',sans-serif; padding:3px 8px; border-radius:2px;
    text-transform:uppercase; letter-spacing:.6px;
  }
  .badge-rust { background:var(--danger-lt); color:var(--danger); }
  .badge-amber { background:var(--warn-lt); color:#8B6000; }
  .badge-sage { background:var(--ok-lt); color:var(--ok); }
  .badge-ink { background:#3D000011; color:var(--rojo-deep); }
  .badge-blue { background:#1d4ed820; color:#1d4ed8; }
  .risk-follow { color:#1d4ed8; }

  /* ── Table ── */
  .table-wrap { overflow-x:auto; }
  table { width:100%; border-collapse:collapse; font-size:13px; }
  thead tr { background:var(--rojo-ink); }
  th {
    padding:10px 14px; text-align:left;
    font-family:'Lato',sans-serif; font-size:10px; text-transform:uppercase;
    letter-spacing:.8px; color:var(--dorado); font-weight:700;
    border-bottom:2px solid var(--dorado);
  }
  td { padding:11px 14px; border-bottom:1px solid var(--border); vertical-align:top; }
  tr:last-child td { border-bottom:none; }
  tr:hover td { background:var(--gris-lt); }

  /* ── Persona cards ── */
  .persona-card {
    background:var(--blanco); border:1px solid var(--border);
    border-left:4px solid var(--gris); border-radius:4px; padding:20px;
    cursor:pointer; transition:all .15s;
  }
  .persona-card:hover { border-left-color:var(--dorado); box-shadow:var(--shadow); }
  .persona-stats { display:flex; gap:16px; margin-top:12px; }
  .persona-stat { display:flex; flex-direction:column; align-items:center; }
  .persona-stat-n { font-family:'PT Serif',serif; font-size:24px; line-height:1; font-weight:700; }
  .persona-stat-l { font-size:9px; color:var(--muted); text-transform:uppercase; letter-spacing:.6px; font-family:'Lato',sans-serif; font-weight:700; }
  .risk-high { color:var(--danger); }
  .risk-mid { color:var(--warn); }
  .risk-low { color:var(--ok); }

  /* ── Timeline ── */
  .tl-item { display:flex; gap:16px; }
  .tl-line { display:flex; flex-direction:column; align-items:center; }
  .tl-dot { width:12px; height:12px; border-radius:50%; flex-shrink:0; margin-top:4px; }
  .tl-dot-cap { background:var(--ok); }
  .tl-dot-inc { background:var(--danger); }
  .tl-dot-aviso { background:var(--warn); }
  .tl-bar { width:2px; flex:1; background:var(--border); margin:4px 0; }
  .tl-content { padding-bottom:20px; flex:1; }
  .tl-date { font-family:'Lato',monospace; font-size:11px; color:var(--muted); font-weight:700; letter-spacing:.3px; }
  .tl-title { font-weight:700; font-size:14px; margin:2px 0; }
  .tl-desc { font-size:13px; color:var(--muted); }

  /* ── Modal ── */
  .modal-overlay { position:fixed; inset:0; background:#3D000088; display:flex; align-items:center; justify-content:center; z-index:100; padding:20px; }
  .modal {
    background:var(--blanco); border-radius:4px;
    border-top:4px solid var(--dorado);
    padding:32px; width:100%; max-width:640px; max-height:90vh; overflow-y:auto;
    box-shadow:var(--shadow-lg); animation:slideUp .2s ease;
  }
  @keyframes slideUp { from{transform:translateY(16px);opacity:0} to{transform:translateY(0);opacity:1} }
  .modal-title { font-family:'PT Serif',serif; font-size:22px; font-weight:700; margin-bottom:24px; color:var(--rojo-deep); }
  .modal-close { float:right; background:none; border:none; font-size:20px; cursor:pointer; color:var(--muted); margin-top:-4px; }
  .modal-close:hover { color:var(--danger); }

  /* ── Alert ── */
  .alert { padding:12px 16px; border-radius:3px; font-size:13px; margin-bottom:16px; font-weight:600; }
  .alert-success { background:var(--ok-lt); color:var(--ok); border:1px solid var(--ok); }
  .alert-error { background:var(--danger-lt); color:var(--danger); border:1px solid var(--danger); }

  /* ── Empty ── */
  .empty { text-align:center; padding:48px; color:var(--muted); font-family:'Lato',sans-serif; font-size:12px; text-transform:uppercase; letter-spacing:1px; }

  /* ── Divider ── */
  .divider { height:1px; background:var(--border); margin:24px 0; }

  /* ── Scope banner ── */
  .scope-banner {
    background:linear-gradient(90deg,var(--rojo-ink),var(--rojo-deep));
    border-radius:3px; padding:10px 16px; font-size:11px;
    color:var(--dorado); font-family:'Lato',sans-serif; font-weight:700;
    letter-spacing:.5px; text-transform:uppercase; margin-bottom:4px;
  }

  /* ── User card ── */
  .user-card {
    background:var(--blanco); border:1px solid var(--border);
    border-radius:4px; padding:16px 20px; display:flex; align-items:center; gap:14px;
  }
  .user-card-info { flex:1; }
  .user-card-name { font-weight:700; font-size:14px; }
  .user-card-email { font-size:12px; color:var(--muted); font-family:'Lato',sans-serif; }

  /* ── Login ── */
  .login-wrap {
    min-height:100vh; display:flex; align-items:center; justify-content:center;
    background: linear-gradient(135deg, var(--rojo-ink) 0%, var(--rojo-deep) 60%, var(--rojo) 100%);
  }
  .login-card {
    background:var(--blanco); border-radius:6px;
    border-top:5px solid var(--dorado);
    padding:48px 40px; width:100%; max-width:440px;
    box-shadow:var(--shadow-lg); text-align:center;
  }
  .login-escudo { width:80px; height:80px; object-fit:contain; margin-bottom:12px; }
  .login-univ { font-family:'PT Serif',serif; font-size:11px; font-weight:700; letter-spacing:2.5px; text-transform:uppercase; color:var(--dorado-dk); margin-bottom:2px; }
  .login-logo { font-family:'PT Serif',serif; font-size:26px; font-weight:700; color:var(--rojo-deep); margin-bottom:2px; }
  .login-sub { font-family:'Lato',sans-serif; font-size:10px; color:var(--muted); text-transform:uppercase; letter-spacing:1.5px; margin-bottom:6px; }
  .login-inst { font-size:13px; color:var(--muted); margin-bottom:32px; line-height:1.5; }
  .google-btn {
    display:flex; align-items:center; justify-content:center; gap:12px;
    background:#fff; border:1.5px solid var(--border); border-radius:4px;
    padding:12px 20px; font-size:13px; font-weight:700; cursor:pointer; width:100%;
    transition:all .15s; font-family:'Lato',sans-serif; text-transform:uppercase; letter-spacing:.5px;
  }
  .google-btn:hover { border-color:var(--rojo); box-shadow:var(--shadow); color:var(--rojo); }
  .user-option {
    display:flex; align-items:center; gap:12px; padding:12px 14px;
    border:1px solid var(--border); border-radius:4px; cursor:pointer;
    margin-bottom:8px; transition:all .15s; background:var(--blanco); text-align:left; width:100%;
  }
  .user-option:hover { border-color:var(--dorado); background:var(--gris-lt); }
  .hint-box {
    background:var(--gris-lt); border:1px solid var(--border);
    border-left:3px solid var(--dorado);
    border-radius:3px; padding:10px 14px; font-size:12px; color:var(--muted);
    font-family:'Lato',sans-serif;
  }

  @media(max-width:700px){
    .main{padding:16px;}
    .grid-2,.grid-4,.form-row,.form-row-3{grid-template-columns:1fr;}
    .header{padding:0 16px;}
    .nav{padding:0 16px;}
  }
`;

const TIPOS_INC = [
  {value:"olvido",label:"Olvido recurrente",badge:"badge-rust"},
  {value:"no_consulto",label:"No consultó recursos",badge:"badge-amber"},
  {value:"error_proceso",label:"Error de proceso",badge:"badge-rust"},
  {value:"escalacion",label:"Escalación innecesaria",badge:"badge-amber"},
  {value:"incumplimiento",label:"Incumplimiento de instrucción",badge:"badge-rust"},
  {value:"retraso",label:"Retraso injustificado",badge:"badge-ink"},
];
const ROLES = {superadmin:"Super Admin",capacitador:"Capacitador",observador:"Observador"};
const ROLE_BADGES = {superadmin:"badge-rust",capacitador:"badge-sage",observador:"badge-blue"};
const ROLE_COLORS = {superadmin:"var(--danger)",capacitador:"var(--ok)",observador:"var(--blue)"};
const CATEGORIAS = ["TI","Servicios Escolares","Vinculación Docente","Finanzas","Internacionalización","Administración","Otro"];
const PAISES = ["México","El Salvador","Ecuador","Perú","Colombia","Guatemala","Otro"];
const EVIDENCIAS = ["Sin evidencia","Correo electrónico","Captura de pantalla","Videollamada en vivo","Videollamada grabada","Documento firmado","Manual entregado"];

const fmtDate = d => new Date(d).toLocaleDateString("es-MX",{day:"2-digit",month:"short",year:"numeric"});
const today = () => new Date().toISOString().slice(0,10);
const uid = () => Math.random().toString(36).slice(2,9);
const initials = n => n.split(" ").slice(0,2).map(w=>w[0]).join("").toUpperCase();
// ── Sistema de puntaje ponderado ─────────────────────────────────────────────
const DEFAULT_PESOS = {
  olvido:1, no_consulto:1, error_proceso:2, escalacion:1,
  incumplimiento:3, retraso:1, aviso_formal:2, amonestacion:4
};
// Competencias por defecto (editables desde Admin)
const DEFAULT_COMPETENCIAS = [
  {id:"c1", nombre:"Seguimiento de instrucciones", descripcion:"Ejecuta las instrucciones recibidas correctamente y en tiempo"},
  {id:"c2", nombre:"Comunicación",                 descripcion:"Se comunica de forma clara, oportuna y profesional"},
  {id:"c3", nombre:"Puntualidad en procesos",      descripcion:"Cumple con los tiempos establecidos para cada proceso"},
  {id:"c4", nombre:"Trabajo en equipo",            descripcion:"Colabora efectivamente con su equipo y otras áreas"},
  {id:"c5", nombre:"Iniciativa",                   descripcion:"Propone soluciones y actúa proactivamente ante situaciones"},
  {id:"c6", nombre:"Adaptabilidad",                descripcion:"Se adapta a cambios, nuevas herramientas y procesos"},
];
const ESCALA_COMP = [
  {valor:1, label:"Deficiente",  color:"var(--danger)"},
  {valor:2, label:"Regular",     color:"var(--warn)"},
  {valor:3, label:"Aceptable",   color:"#ca8a04"},
  {valor:4, label:"Bueno",       color:"#16a34a"},
  {valor:5, label:"Excelente",   color:"var(--ok)"},
];
const TIPOS_LOGRO = [
  {value:"cumplimiento",  label:"Cumplimiento puntual",       badge:"badge-sage",  puntos:1},
  {value:"mejora",        label:"Mejora demostrada",          badge:"badge-sage",  puntos:2},
  {value:"sin_incidencia",label:"Periodo sin incidencias",    badge:"badge-blue",  puntos:1},
  {value:"capacitacion",  label:"Capacitación aplicada",      badge:"badge-blue",  puntos:1},
  {value:"destacado",     label:"Desempeño destacado",        badge:"badge-sage",  puntos:3},
];

const UMBRALES = [
  {min:0,  max:2,  label:"NORMAL",      cls:"risk-low",  badge:"badge-sage",  color:"var(--ok)"},
  {min:3,  max:5,  label:"SEGUIMIENTO", cls:"risk-follow",badge:"badge-blue", color:"#1d4ed8"},
  {min:6,  max:10, label:"ALERTA",      cls:"risk-mid",  badge:"badge-amber", color:"var(--warn)"},
  {min:11, max:999,label:"CRÍTICO",     cls:"risk-high", badge:"badge-rust",  color:"var(--danger)"},
];
const PERIODOS = [
  {value:7,   label:"Últimos 7 días"},
  {value:30,  label:"Último mes"},
  {value:90,  label:"Últimos 3 meses"},
  {value:180, label:"Últimos 6 meses"},
];

function calcScore(incs=[], avisos=[], pesos=DEFAULT_PESOS, dias=7){
  const safeIncs = Array.isArray(incs) ? incs : [];
  const safeAvisos = Array.isArray(avisos) ? avisos : [];
  const cutoff = new Date();
  cutoff.setDate(cutoff.getDate() - dias);
  const filtrarFecha = f => new Date(f) >= cutoff;
  const incsFilt = safeIncs.filter(i => filtrarFecha(i.fecha) && !i.sinPuntos && !i.fromAviso);
  const avisosFilt = safeAvisos.filter(a => filtrarFecha(a.fecha) && (a.nivel==="formal"||a.nivel==="howler"));
  let score = 0;
  const desglose = [];
  incsFilt.forEach(i => {
    const p = pesos[i.tipo] || 1;
    score += p;
    desglose.push({tipo:i.tipo, peso:p, fecha:i.fecha});
  });
  avisosFilt.forEach(a => {
    const p = a.nivel==="howler" ? pesos.amonestacion : pesos.aviso_formal;
    score += p;
    desglose.push({tipo:a.nivel==="howler"?"amonestacion":"aviso_formal", peso:p, fecha:a.fecha});
  });
  return {score, desglose, totalIncs:incsFilt.length, totalAvisos:avisosFilt.length};
}

function riskLevel(score){
  const u = UMBRALES.find(u => score>=u.min && score<=u.max) || UMBRALES[0];
  return u;
}

// Mantener compatibilidad con llamadas antiguas que pasaban count
function riskLevelCount(n){ return riskLevel(n<=1?n:n>=4?11:n>=2?6:n); }

// Score integrado global: combina última evaluación de competencias + penalización por incidencias
// Si no hay evaluación de competencias, usa solo el score de incidencias como indicador parcial
function calcScoreIntegrado(incs=[], avisos=[], evals=[], pesos=DEFAULT_PESOS, dias=30){
  const {score:incScore} = calcScore(incs, avisos, pesos, dias);
  const evalsOrdenadas = [...evals].sort((a,b)=>b.createdAt.localeCompare(a.createdAt));
  const ultimaEval = evalsOrdenadas[0];
  if(!ultimaEval || !ultimaEval.calificaciones || Object.keys(ultimaEval.calificaciones).length===0){
    // Sin evaluación: score parcial basado solo en incidencias (invertido: 0 inc = 100, penaliza)
    const parcial = Math.max(0, 100 - (incScore * 5));
    return { score: parcial, tienEval: false, incScore, compScore: null };
  }
  const vals = Object.values(ultimaEval.calificaciones).filter(v=>v>0);
  const promComp = vals.length ? vals.reduce((s,v)=>s+v,0)/vals.length : 0;
  const compScore = Math.round((promComp/5)*100);
  const penalizacion = Math.min(incScore*3, 40);
  const total = Math.max(0, compScore - penalizacion);
  return { score: total, tieneEval: true, incScore, compScore, penalizacion };
}

function nivelDesempeno(score, tieneEval=true){
  if(!tieneEval) return {label:"Sin evaluar", badge:"badge-ink", color:"var(--muted)", parcial:true};
  if(score>=85) return {label:"Sobresaliente", badge:"badge-sage",  color:"var(--ok)"};
  if(score>=70) return {label:"Satisfactorio", badge:"badge-blue",  color:"#1d4ed8"};
  if(score>=50) return {label:"En desarrollo", badge:"badge-amber", color:"var(--warn)"};
  return         {label:"Req. atención",  badge:"badge-rust",  color:"var(--danger)"};
}

// Similitud de nombres (Levenshtein normalizado)
function levenshtein(a,b){
  const m=a.length,n=b.length;
  const dp=Array.from({length:m+1},(_,i)=>Array.from({length:n+1},(_,j)=>i===0?j:j===0?i:0));
  for(let i=1;i<=m;i++) for(let j=1;j<=n;j++) dp[i][j]=a[i-1]===b[j-1]?dp[i-1][j-1]:1+Math.min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1]);
  return dp[m][n];
}
function similaridad(a,b){
  const norm=s=>s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/[^a-z0-9 ]/g,"").trim();
  const na=norm(a),nb=norm(b);
  if(!na||!nb)return 0;
  const dist=levenshtein(na,nb);
  return 1-(dist/Math.max(na.length,nb.length));
}
function buscarDuplicados(nombre,personas,umbral=0.7){
  return personas.map(p=>({...p,sim:similaridad(nombre,p.nombre)})).filter(p=>p.sim>=umbral).sort((a,b)=>b.sim-a.sim);
}

// ── STORAGE (backend-backed, replaces window.storage) ──
// "srio:session" is deliberately kept in this browser's localStorage only, never sent to
// the shared backend — otherwise logging in on one computer would silently log in every
// other computer pointed at the same deployment as the same person.
const ACCESS_CODE_KEY = "srio-access-code";
const getAccessCode = () => { try { return localStorage.getItem(ACCESS_CODE_KEY) || ""; } catch (e) { return ""; } };
const setAccessCodeStored = (code) => { try { localStorage.setItem(ACCESS_CODE_KEY, code); } catch (e) {} };
const clearAccessCodeStored = () => { try { localStorage.removeItem(ACCESS_CODE_KEY); } catch (e) {} };

const verifyAccessCode = async (code) => {
  try {
    const res = await fetch("/api/kv/srio:usuarios", { headers: { "x-srio-access": code } });
    return res.ok || res.status === 404;
  } catch (e) { return false; }
};

async function load(key, fb) {
  if (key === "srio:session") {
    try { const raw = localStorage.getItem("srio:session"); return raw ? JSON.parse(raw) : fb; } catch (e) { return fb; }
  }
  const code = getAccessCode();
  const res = await fetch("/api/kv/" + encodeURIComponent(key), { headers: { "x-srio-access": code } });
  if (res.status === 401) { clearAccessCodeStored(); throw { unauthorized: true }; }
  if (res.status === 404) return fb;
  if (!res.ok) return fb;
  try { const body = await res.json(); return body.value; } catch (e) { return fb; }
}
async function save(key, val) {
  if (key === "srio:session") {
    try { localStorage.setItem("srio:session", JSON.stringify(val)); } catch (e) {}
    return;
  }
  const code = getAccessCode();
  try {
    const res = await fetch("/api/kv/" + encodeURIComponent(key), {
      method: "PUT",
      headers: { "Content-Type": "application/json", "x-srio-access": code },
      body: JSON.stringify({ value: val }),
    });
    if (res.status === 401) clearAccessCodeStored();
  } catch (e) {}
}

const DEFAULT_USERS = [{
  id:"sa001",nombre:"Dra. Gabriela Barranco",email:"g.barranco@usantander.edu.mx",
  rol:"superadmin",categoria:"TI",activo:true,createdAt:today()
}];

// ══════════════════════════════════════════════════════════════════════════════
function SrioApp({ onUnauthorized }){
  const [loadError,setLoadError] = useState(null);
  const [session,setSession]   = useState(null);
  const [usuarios,setUsuarios] = useState([]);
  const [personas,setPersonas] = useState([]);
  const [caps,setCaps]         = useState([]);
  const [incs,setIncs]         = useState([]);
  const [avisos,setAvisos]     = useState([]);
  const [pesos,setPesos]           = useState(DEFAULT_PESOS);
  const [periodo,setPeriodo]       = useState(7);
  const [logros,setLogros]         = useState([]);
  const [evaluaciones,setEvals]    = useState([]);
  const [competencias,setComps]    = useState(DEFAULT_COMPETENCIAS);
  const [mLogro,setMLogro]         = useState(false);
  const [mEval,setMEval]           = useState(false);
  const [loaded,setLoaded]     = useState(false);
  const [toast,setToast]       = useState(null);
  const [tab,setTab]           = useState("dashboard");
  const [mPersona,setMPersona]       = useState(false);
  const [mEditPersona,setMEditPersona] = useState(null); // persona a editar
  const [mCap,setMCap]         = useState(false);
  const [mInc,setMInc]         = useState(false);
  const [mUsuario,setMUsuario] = useState(false);
  const [mAviso,setMAviso]     = useState(false);
  const [perfilPersona,setPerfilPersona] = useState(null);
  const [sidebarOpen,setSidebarOpen]     = useState(true);

  useEffect(()=>{ (async()=>{
    let u,p,c,i,av,pw,lg,ev,cp,s;
    try {
      u=await load("srio:usuarios",DEFAULT_USERS);
      p=await load("srio:personas",[]);
      c=await load("srio:caps",[]);
      i=await load("srio:incs",[]);
      av=await load("srio:avisos",[]);
      pw=await load("srio:pesos",DEFAULT_PESOS);
      lg=await load("srio:logros",[]);
      ev=await load("srio:evals",[]);
      cp=await load("srio:comps",DEFAULT_COMPETENCIAS);
      s=await load("srio:session",null);
    } catch (e) {
      if (e && e.unauthorized) { onUnauthorized(); return; }
      console.error(e);
      setLoadError("No se pudo conectar con el servidor. Intenta recargar la página.");
      return;
    }
    setUsuarios(u); setPersonas(p); setCaps(c); setIncs(i); setAvisos(av); setPesos(pw); setLogros(lg); setEvals(ev); setComps(cp);
    if(s){ const still=u.find(x=>x.id===s.id&&x.activo); if(still) setSession(still); }
    setLoaded(true);
  })(); },[]);

  const notify=(msg,type="success")=>{ setToast({msg,type}); setTimeout(()=>setToast(null),3500); };

  // ── Seed de Karla Bianchi (demo) ─────────────────────────────────────────
  useEffect(()=>{
    (async()=>{
      if(!loaded) return;
      const existing = await load("srio:personas",[]);
      if(existing.find(p=>p.id==="karla001")) return; // ya existe
      const karla = {"id": "karla001", "nombre": "Karla Bianchi", "sede": "Campus Monterrey Norte", "pais": "México", "area": "Servicios Escolares", "cargo": "Coordinadora de Inscripciones", "createdAt": "2025-07-13"};
      const newPersonas = [...existing, karla];
      await save("srio:personas", newPersonas);
      setPersonas(newPersonas);
      const existingCaps = await load("srio:caps",[]);
      await save("srio:caps", [...existingCaps, ...[{"id": "44i1wwl", "personaId": "karla001", "tema": "Proceso de inscripción en plataforma SIIA", "fecha": "2025-08-02", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Videollamada grabada", "notas": "Se explicó el flujo completo de inscripción, carga de documentos y validación de pagos.", "linkGrabacion": "https://drive.google.com/ejemplo", "imagenes": [], "createdAt": "2025-08-02"}, {"id": "6moat1t", "personaId": "karla001", "tema": "Manejo de incidencias de pago y reembolsos", "fecha": "2025-10-11", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Correo electrónico", "notas": "Procedimiento para reverso de cargos y escalación a finanzas.", "linkGrabacion": "", "imagenes": [], "createdAt": "2025-10-11"}, {"id": "ihyhdud", "personaId": "karla001", "tema": "Actualización de expedientes digitales", "fecha": "2025-12-10", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Documento firmado", "notas": "Nuevo flujo de digitalización y nomenclatura de archivos.", "linkGrabacion": "", "imagenes": [], "createdAt": "2025-12-10"}, {"id": "51cazpz", "personaId": "karla001", "tema": "Uso del módulo de reportes en plataforma", "fecha": "2026-02-08", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Videollamada en vivo", "notas": "Generación de reportes de inscripción, filtros y exportación.", "linkGrabacion": "", "imagenes": [], "createdAt": "2026-02-08"}]]);
      setCaps(c=>[...c, ...[{"id": "44i1wwl", "personaId": "karla001", "tema": "Proceso de inscripción en plataforma SIIA", "fecha": "2025-08-02", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Videollamada grabada", "notas": "Se explicó el flujo completo de inscripción, carga de documentos y validación de pagos.", "linkGrabacion": "https://drive.google.com/ejemplo", "imagenes": [], "createdAt": "2025-08-02"}, {"id": "6moat1t", "personaId": "karla001", "tema": "Manejo de incidencias de pago y reembolsos", "fecha": "2025-10-11", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Correo electrónico", "notas": "Procedimiento para reverso de cargos y escalación a finanzas.", "linkGrabacion": "", "imagenes": [], "createdAt": "2025-10-11"}, {"id": "ihyhdud", "personaId": "karla001", "tema": "Actualización de expedientes digitales", "fecha": "2025-12-10", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Documento firmado", "notas": "Nuevo flujo de digitalización y nomenclatura de archivos.", "linkGrabacion": "", "imagenes": [], "createdAt": "2025-12-10"}, {"id": "51cazpz", "personaId": "karla001", "tema": "Uso del módulo de reportes en plataforma", "fecha": "2026-02-08", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Videollamada en vivo", "notas": "Generación de reportes de inscripción, filtros y exportación.", "linkGrabacion": "", "imagenes": [], "createdAt": "2026-02-08"}]]);
      const existingIncs = await load("srio:incs",[]);
      await save("srio:incs", [...existingIncs, ...[{"id": "4axv7dk", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "olvido", "fecha": "2025-09-01", "descripcion": "Registró a un alumno con folio incorrecto en SIIA ignorando el procedimiento explicado en capacitación.", "capacitacionId": "44i1wwl", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-09-01"}, {"id": "2o2fyb3", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "escalacion", "fecha": "2025-09-21", "descripcion": "Escaló a Rectoría una solicitud de cambio de grupo que podía resolver directamente en plataforma.", "capacitacionId": "", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-09-21"}, {"id": "hp29sed", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "no_consulto", "fecha": "2025-10-31", "descripcion": "Preguntó por el procedimiento de reembolso sin revisar el manual entregado en capacitación.", "capacitacionId": "6moat1t", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-10-31"}, {"id": "d5ykw5j", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "retraso", "fecha": "2025-11-30", "descripcion": "Entregó el reporte mensual de inscripciones con 4 días de retraso sin justificación.", "capacitacionId": "", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-11-30"}, {"id": "u9uxutv", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "error_proceso", "fecha": "2025-12-30", "descripcion": "Subió 12 expedientes con nomenclatura incorrecta tras capacitación específica sobre este proceso.", "capacitacionId": "ihyhdud", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-12-30"}, {"id": "twr1b8t", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "olvido", "fecha": "2026-03-25", "descripcion": "Generó reporte sin aplicar el filtro de periodo correcto. Corrigió al momento.", "capacitacionId": "51cazpz", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2026-03-25"}, {"id": "u0jy4nv", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "olvido", "fecha": "2025-09-16", "descripcion": "[Auto — Friendly Reminder] Recordarle que el manual de procesos está disponible en el portal antes de escalar dudas.", "registradaPor": "Dra. Gabriela Barranco", "capacitacionId": "", "fromAviso": "nqm5lgf", "sinPuntos": true, "createdAt": "2025-09-16"}, {"id": "nhuqvty", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "error_proceso", "fecha": "2026-01-01", "descripcion": "[Auto — Aviso Formal] Nomenclatura incorrecta en expedientes a pesar de capacitación documentada.", "registradaPor": "Dra. Gabriela Barranco", "capacitacionId": "ihyhdud", "fromAviso": "owya6l9", "sinPuntos": false, "createdAt": "2026-01-01"}]]);
      setIncs(i=>[...i, ...[{"id": "4axv7dk", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "olvido", "fecha": "2025-09-01", "descripcion": "Registró a un alumno con folio incorrecto en SIIA ignorando el procedimiento explicado en capacitación.", "capacitacionId": "44i1wwl", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-09-01"}, {"id": "2o2fyb3", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "escalacion", "fecha": "2025-09-21", "descripcion": "Escaló a Rectoría una solicitud de cambio de grupo que podía resolver directamente en plataforma.", "capacitacionId": "", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-09-21"}, {"id": "hp29sed", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "no_consulto", "fecha": "2025-10-31", "descripcion": "Preguntó por el procedimiento de reembolso sin revisar el manual entregado en capacitación.", "capacitacionId": "6moat1t", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-10-31"}, {"id": "d5ykw5j", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "retraso", "fecha": "2025-11-30", "descripcion": "Entregó el reporte mensual de inscripciones con 4 días de retraso sin justificación.", "capacitacionId": "", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-11-30"}, {"id": "u9uxutv", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "error_proceso", "fecha": "2025-12-30", "descripcion": "Subió 12 expedientes con nomenclatura incorrecta tras capacitación específica sobre este proceso.", "capacitacionId": "ihyhdud", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-12-30"}, {"id": "twr1b8t", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "olvido", "fecha": "2026-03-25", "descripcion": "Generó reporte sin aplicar el filtro de periodo correcto. Corrigió al momento.", "capacitacionId": "51cazpz", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2026-03-25"}, {"id": "u0jy4nv", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "olvido", "fecha": "2025-09-16", "descripcion": "[Auto — Friendly Reminder] Recordarle que el manual de procesos está disponible en el portal antes de escalar dudas.", "registradaPor": "Dra. Gabriela Barranco", "capacitacionId": "", "fromAviso": "nqm5lgf", "sinPuntos": true, "createdAt": "2025-09-16"}, {"id": "nhuqvty", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "error_proceso", "fecha": "2026-01-01", "descripcion": "[Auto — Aviso Formal] Nomenclatura incorrecta en expedientes a pesar de capacitación documentada.", "registradaPor": "Dra. Gabriela Barranco", "capacitacionId": "ihyhdud", "fromAviso": "owya6l9", "sinPuntos": false, "createdAt": "2026-01-01"}]]);
      const existingAvisos = await load("srio:avisos",[]);
      await save("srio:avisos", [...existingAvisos, ...[{"id": "nqm5lgf", "personaId": "karla001", "personaNombre": "Karla Bianchi", "nivel": "reminder", "fecha": "2025-09-16", "motivo": "Recordarle que el manual de procesos está disponible en el portal antes de escalar dudas.", "enviadoPor": "Dra. Gabriela Barranco", "textoGenerado": "Hola Karla, te recuerdo que el manual de procesos actualizado está disponible en el portal interno. Antes de escalar consultas, te pido revisar ahí primero. Quedo disponible si tienes dudas después de consultarlo.", "capacitacionId": "", "createdAt": "2025-09-16"}, {"id": "owya6l9", "personaId": "karla001", "personaNombre": "Karla Bianchi", "nivel": "formal", "fecha": "2026-01-01", "motivo": "Nomenclatura incorrecta en expedientes a pesar de capacitación documentada el 2025-12-10.", "enviadoPor": "Dra. Gabriela Barranco", "textoGenerado": "Karla, el pasado 2025-12-10 recibiste capacitación específica sobre el proceso de digitalización y nomenclatura de expedientes. Sin embargo, se detectaron 12 archivos subidos con nomenclatura incorrecta. Este tipo de errores afecta directamente la operación del área. Queda registro formal de esta situación y espero no vuelva a ocurrir.", "capacitacionId": "ihyhdud", "createdAt": "2026-01-01"}]]);
      setAvisos(a=>[...a, ...[{"id": "nqm5lgf", "personaId": "karla001", "personaNombre": "Karla Bianchi", "nivel": "reminder", "fecha": "2025-09-16", "motivo": "Recordarle que el manual de procesos está disponible en el portal antes de escalar dudas.", "enviadoPor": "Dra. Gabriela Barranco", "textoGenerado": "Hola Karla, te recuerdo que el manual de procesos actualizado está disponible en el portal interno. Antes de escalar consultas, te pido revisar ahí primero. Quedo disponible si tienes dudas después de consultarlo.", "capacitacionId": "", "createdAt": "2025-09-16"}, {"id": "owya6l9", "personaId": "karla001", "personaNombre": "Karla Bianchi", "nivel": "formal", "fecha": "2026-01-01", "motivo": "Nomenclatura incorrecta en expedientes a pesar de capacitación documentada el 2025-12-10.", "enviadoPor": "Dra. Gabriela Barranco", "textoGenerado": "Karla, el pasado 2025-12-10 recibiste capacitación específica sobre el proceso de digitalización y nomenclatura de expedientes. Sin embargo, se detectaron 12 archivos subidos con nomenclatura incorrecta. Este tipo de errores afecta directamente la operación del área. Queda registro formal de esta situación y espero no vuelva a ocurrir.", "capacitacionId": "ihyhdud", "createdAt": "2026-01-01"}]]);
      const existingLogros = await load("srio:logros",[]);
      await save("srio:logros", [...existingLogros, ...[{"id": "69knsus", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "cumplimiento", "fecha": "2025-10-21", "descripcion": "Procesó 87 inscripciones del periodo sin errores en tiempo récord durante semana de cierre.", "registradoPor": "Dra. Gabriela Barranco", "createdAt": "2025-10-21"}, {"id": "vtlvxdh", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "capacitacion", "fecha": "2025-12-20", "descripcion": "Completó capacitación de actualización de expedientes y aplicó correctamente el nuevo flujo durante 3 semanas consecutivas.", "registradoPor": "Dra. Gabriela Barranco", "createdAt": "2025-12-20"}, {"id": "pieaim8", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "mejora", "fecha": "2026-03-10", "descripcion": "Reducción notable de errores en los últimos 2 meses: de 5 incidencias en Q1 a 1 en Q3. Tendencia positiva clara.", "registradoPor": "Dra. Gabriela Barranco", "createdAt": "2026-03-10"}, {"id": "ijirhu8", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "sin_incidencia", "fecha": "2026-04-19", "descripcion": "Último mes sin incidencias registradas. Manejo correcto de todos los procesos.", "registradoPor": "Dra. Gabriela Barranco", "createdAt": "2026-04-19"}]]);
      setLogros(l=>[...l, ...[{"id": "69knsus", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "cumplimiento", "fecha": "2025-10-21", "descripcion": "Procesó 87 inscripciones del periodo sin errores en tiempo récord durante semana de cierre.", "registradoPor": "Dra. Gabriela Barranco", "createdAt": "2025-10-21"}, {"id": "vtlvxdh", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "capacitacion", "fecha": "2025-12-20", "descripcion": "Completó capacitación de actualización de expedientes y aplicó correctamente el nuevo flujo durante 3 semanas consecutivas.", "registradoPor": "Dra. Gabriela Barranco", "createdAt": "2025-12-20"}, {"id": "pieaim8", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "mejora", "fecha": "2026-03-10", "descripcion": "Reducción notable de errores en los últimos 2 meses: de 5 incidencias en Q1 a 1 en Q3. Tendencia positiva clara.", "registradoPor": "Dra. Gabriela Barranco", "createdAt": "2026-03-10"}, {"id": "ijirhu8", "personaId": "karla001", "personaNombre": "Karla Bianchi", "tipo": "sin_incidencia", "fecha": "2026-04-19", "descripcion": "Último mes sin incidencias registradas. Manejo correcto de todos los procesos.", "registradoPor": "Dra. Gabriela Barranco", "createdAt": "2026-04-19"}]]);
    })();
  },[loaded]);
  // ── Fin seed ──────────────────────────────────────────────────────────────

  // ── Seed Rodrigo y Verónica ───────────────────────────────────────────────
  useEffect(()=>{
    (async()=>{
      if(!loaded) return;
      const existing = await load("srio:personas",[]);
      if(existing.find(p=>p.id==="rodrigo001"||p.id==="veronica001")) return;

      const newPersonas=[...existing, {"id": "rodrigo001", "nombre": "Rodrigo Castellanos", "sede": "Campus San Salvador", "pais": "El Salvador", "area": "TI", "cargo": "Coordinador de Sistemas", "createdAt": "2025-05-24"}, {"id": "veronica001", "nombre": "Verónica Espinoza", "sede": "Campus Guadalajara Vallarta", "pais": "México", "area": "Servicios Escolares", "cargo": "Auxiliar de Inscripciones", "createdAt": "2025-06-23"}];
      setPersonas(newPersonas); await save("srio:personas",newPersonas);

      const nc=[...await load("srio:caps",[]),...[{"id": "37bm4lv", "personaId": "rodrigo001", "tema": "Administración de plataforma SIIA", "fecha": "2025-06-03", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Videollamada grabada", "notas": "Flujo completo de administración, permisos y reportes.", "linkGrabacion": "", "imagenes": [], "createdAt": "2025-06-03"}, {"id": "whhtf9o", "personaId": "rodrigo001", "tema": "Protocolo de soporte a sedes internacionales", "fecha": "2025-08-12", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Documento firmado", "notas": "Escalación correcta, tiempos de respuesta y documentación.", "linkGrabacion": "", "imagenes": [], "createdAt": "2025-08-12"}, {"id": "me89ayi", "personaId": "rodrigo001", "tema": "Seguridad de datos y respaldos", "fecha": "2025-11-10", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Videollamada grabada", "notas": "Procedimientos de respaldo, cifrado y recuperación.", "linkGrabacion": "", "imagenes": [], "createdAt": "2025-11-10"}, {"id": "d51ifwn", "personaId": "rodrigo001", "tema": "Módulo de reportes avanzados", "fecha": "2026-02-03", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Videollamada en vivo", "notas": "Reportes personalizados, dashboards y exportación masiva.", "linkGrabacion": "", "imagenes": [], "createdAt": "2026-02-03"}],...[{"id": "njsjg8r", "personaId": "veronica001", "tema": "Proceso de inscripción en plataforma SIIA", "fecha": "2025-07-13", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Videollamada grabada", "notas": "Se explicó flujo completo. Confirmó comprensión.", "linkGrabacion": "", "imagenes": [], "createdAt": "2025-07-13"}, {"id": "y4y9icc", "personaId": "veronica001", "tema": "Proceso de inscripción en plataforma SIIA — 2da sesión", "fecha": "2025-10-01", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Correo electrónico", "notas": "Segunda capacitación por errores recurrentes. Se entregó manual impreso.", "linkGrabacion": "", "imagenes": [], "createdAt": "2025-10-01"}, {"id": "oobehs8", "personaId": "veronica001", "tema": "Manejo de expedientes y documentación", "fecha": "2025-11-30", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Documento firmado", "notas": "Proceso de digitalización y nomenclatura. Firmó de recibido.", "linkGrabacion": "", "imagenes": [], "createdAt": "2025-11-30"}, {"id": "svjep9y", "personaId": "veronica001", "tema": "Proceso de inscripción — 3ra sesión reforzamiento", "fecha": "2026-02-18", "impartidaPorId": "sa001", "impartidaPor": "Dra. Gabriela Barranco", "categoria": "TI", "evidencia": "Videollamada en vivo", "notas": "Tercera sesión del mismo tema. Patrón de no aplicación persiste.", "linkGrabacion": "", "imagenes": [], "createdAt": "2026-02-18"}]];
      setCaps(nc); await save("srio:caps",nc);

      const ni=[...await load("srio:incs",[]),...[{"id": "irwgd52", "personaId": "rodrigo001", "personaNombre": "Rodrigo Castellanos", "tipo": "retraso", "fecha": "2025-07-03", "descripcion": "Entregó reporte semanal con 1 día de retraso por ajuste de zona horaria. Situación resuelta y no se repitió.", "capacitacionId": "", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-07-03"}],...[{"id": "4d850km", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "tipo": "olvido", "fecha": "2025-07-28", "descripcion": "Registró 3 alumnos con folio duplicado en SIIA.", "capacitacionId": "njsjg8r", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-07-28"}, {"id": "i4apy4l", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "tipo": "error_proceso", "fecha": "2025-08-07", "descripcion": "Asignó materias incorrectas a 5 alumnos de nuevo ingreso.", "capacitacionId": "njsjg8r", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-08-07"}, {"id": "wfz2l61", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "tipo": "olvido", "fecha": "2025-08-17", "descripcion": "[Auto — Friendly Reminder] Se le recuerda revisar el manual antes de registrar alumnos.", "capacitacionId": "njsjg8r", "registradaPor": "Dra. Gabriela Barranco", "fromAviso": "wrywnlf", "sinPuntos": true, "createdAt": "2025-08-17"}, {"id": "wcgsb76", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "tipo": "no_consulto", "fecha": "2025-09-01", "descripcion": "Preguntó por procedimiento de baja ya explicado en capacitación y documentado en manual.", "capacitacionId": "njsjg8r", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-09-01"}, {"id": "uzb2irq", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "tipo": "olvido", "fecha": "2025-09-16", "descripcion": "Cuarto error en folios: número de control sin prefijo de sede.", "capacitacionId": "njsjg8r", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-09-16"}, {"id": "iqks4ev", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "tipo": "escalacion", "fecha": "2025-10-11", "descripcion": "Escaló solicitud de cambio de turno a Rectoría sin intentar resolverlo en plataforma primero.", "capacitacionId": "", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-10-11"}, {"id": "dlse3i3", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "tipo": "error_proceso", "fecha": "2025-10-31", "descripcion": "[Auto — Aviso Formal] Cuarto error consecutivo en folios de inscripción.", "capacitacionId": "y4y9icc", "registradaPor": "Dra. Gabriela Barranco", "fromAviso": "f1cdh1v", "sinPuntos": false, "createdAt": "2025-10-31"}, {"id": "mkfemyc", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "tipo": "retraso", "fecha": "2025-11-20", "descripcion": "Entregó lista de inscripción del periodo con 6 días de retraso.", "capacitacionId": "", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-11-20"}, {"id": "diqu6xw", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "tipo": "no_consulto", "fecha": "2025-12-20", "descripcion": "Solicitó explicación sobre nomenclatura de expedientes ya cubierta en cap del 2025-11-30.", "capacitacionId": "oobehs8", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2025-12-20"}, {"id": "55u6741", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "tipo": "incumplimiento", "fecha": "2026-01-19", "descripcion": "No actualizó expedientes en plazo establecido pese a instrucción explícita.", "capacitacionId": "oobehs8", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2026-01-19"}, {"id": "72yt8r7", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "tipo": "olvido", "fecha": "2026-02-23", "descripcion": "Volvió a usar nomenclatura antigua en 18 expedientes digitalizados.", "capacitacionId": "oobehs8", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2026-02-23"}, {"id": "b31wcn4", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "tipo": "incumplimiento", "fecha": "2026-03-15", "descripcion": "[Auto — Amonestación] Error en 18 expedientes. Mismo error documentado hace 5 meses.", "capacitacionId": "oobehs8", "registradaPor": "Dra. Gabriela Barranco", "fromAviso": "17ypbh9", "sinPuntos": false, "createdAt": "2026-03-15"}, {"id": "0vdc348", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "tipo": "error_proceso", "fecha": "2026-04-14", "descripcion": "Error en asignación de grupo en 2 alumnos. Mismo error del inicio del año.", "capacitacionId": "svjep9y", "registradaPor": "Dra. Gabriela Barranco", "createdAt": "2026-04-14"}]];
      setIncs(ni); await save("srio:incs",ni);

      const na=[...await load("srio:avisos",[]),...[{"id": "wrywnlf", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "nivel": "reminder", "fecha": "2025-08-17", "motivo": "Se le recuerda revisar el manual antes de registrar alumnos en el sistema.", "enviadoPor": "Dra. Gabriela Barranco", "textoGenerado": "Verónica, antes de registrar alumnos en el sistema por favor revisa el manual de procesos. Esto evitará errores que retrasan la operación.", "capacitacionId": "njsjg8r", "createdAt": "2025-08-17"}, {"id": "f1cdh1v", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "nivel": "formal", "fecha": "2025-10-31", "motivo": "Cuarto error consecutivo en folios de inscripción a pesar de dos capacitaciones recibidas.", "enviadoPor": "Dra. Gabriela Barranco", "textoGenerado": "Verónica, has recibido capacitación sobre este proceso en dos ocasiones y sin embargo seguimos registrando los mismos errores. Esto está generando reprocesos que afectan la operación del área. Queda registro formal.", "capacitacionId": "y4y9icc", "createdAt": "2025-10-31"}, {"id": "17ypbh9", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "nivel": "howler", "fecha": "2026-03-15", "motivo": "Error en 18 expedientes digitalizados con nomenclatura incorrecta. Mismo error documentado hace 5 meses. Tercera capacitación no fue aplicada.", "enviadoPor": "Dra. Gabriela Barranco", "textoGenerado": "Verónica, este es un error que ya fue documentado, capacitado en tres ocasiones y del que existe aviso formal previo. 18 expedientes con nomenclatura incorrecta es inaceptable en este punto. Esto tiene consecuencias directas en el expediente académico de los alumnos. La situación queda registrada como amonestación formal.", "capacitacionId": "oobehs8", "createdAt": "2026-03-15"}]];
      setAvisos(na); await save("srio:avisos",na);

      const nl=[...await load("srio:logros",[]),...[{"id": "3jnk905", "personaId": "rodrigo001", "personaNombre": "Rodrigo Castellanos", "tipo": "destacado", "fecha": "2025-07-18", "descripcion": "Detectó y resolvió proactivamente una vulnerabilidad en el sistema de acceso antes de que fuera reportada. Evitó incidente mayor.", "registradoPor": "Dra. Gabriela Barranco", "createdAt": "2025-07-18"}, {"id": "cbq2et6", "personaId": "rodrigo001", "personaNombre": "Rodrigo Castellanos", "tipo": "cumplimiento", "fecha": "2025-09-11", "descripcion": "100% de tickets de soporte resueltos en tiempo durante 3 meses consecutivos. Tasa de resolución más alta de la red internacional.", "registradoPor": "Dra. Gabriela Barranco", "createdAt": "2025-09-11"}, {"id": "84ody6j", "personaId": "rodrigo001", "personaNombre": "Rodrigo Castellanos", "tipo": "mejora", "fecha": "2025-11-20", "descripcion": "Propuso e implementó un checklist de respaldos que redujo errores de configuración en su sede a cero.", "registradoPor": "Dra. Gabriela Barranco", "createdAt": "2025-11-20"}, {"id": "mf441kx", "personaId": "rodrigo001", "personaNombre": "Rodrigo Castellanos", "tipo": "capacitacion", "fecha": "2026-02-18", "descripcion": "Aplicó módulo de reportes avanzados desde el primer día post-capacitación. Generó 3 reportes nuevos sin solicitar asistencia.", "registradoPor": "Dra. Gabriela Barranco", "createdAt": "2026-02-18"}, {"id": "xva0ef6", "personaId": "rodrigo001", "personaNombre": "Rodrigo Castellanos", "tipo": "sin_incidencia", "fecha": "2026-04-24", "descripcion": "Más de 300 días sin incidencias de proceso. Referente de buenas prácticas para la red de sedes.", "registradoPor": "Dra. Gabriela Barranco", "createdAt": "2026-04-24"}],...[{"id": "v40r7k4", "personaId": "veronica001", "personaNombre": "Verónica Espinoza", "tipo": "cumplimiento", "fecha": "2026-01-09", "descripcion": "Procesó inscripciones de periodo sin errores durante 2 semanas consecutivas post-capacitación.", "registradoPor": "Dra. Gabriela Barranco", "createdAt": "2026-01-09"}]];
      setLogros(nl); await save("srio:logros",nl);
    })();
  },[loaded]);
  // ── Fin seed Rodrigo y Verónica ───────────────────────────────────────────



  // Scope
  const scopeCaps     = session?.rol==="superadmin" ? caps : caps.filter(c=>c.impartidaPorId===session?.id);
  const scopePIds     = [...new Set(scopeCaps.map(c=>c.personaId))];
  const scopePersonas = session?.rol==="superadmin" ? personas : personas.filter(p=>scopePIds.includes(p.id));
  const scopeIncs     = session?.rol==="superadmin" ? incs : incs.filter(i=>scopePIds.includes(i.personaId));
  const scopeAvisos   = session?.rol==="superadmin" ? avisos : avisos.filter(a=>scopePIds.includes(a.personaId));
  const canEdit       = session?.rol !== "observador";

  const addUsuario = async u => { const n=[...usuarios,{...u,id:uid(),activo:true,createdAt:today()}]; setUsuarios(n); await save("srio:usuarios",n); setMUsuario(false); notify("Usuario creado."); };
  const toggleUsuario = async id => { const n=usuarios.map(u=>u.id===id?{...u,activo:!u.activo}:u); setUsuarios(n); await save("srio:usuarios",n); notify("Usuario actualizado."); };
  const delUsuario = async id => { const n=usuarios.filter(u=>u.id!==id); setUsuarios(n); await save("srio:usuarios",n); notify("Usuario eliminado."); };
  const addPersona = async p => { const n=[...personas,{...p,id:uid(),createdAt:today()}]; setPersonas(n); await save("srio:personas",n); setMPersona(false); notify("Persona registrada."); };
  const updatePersona = async p => {
    const n=personas.map(x=>x.id===p.id?{...x,...p}:x);
    setPersonas(n); await save("srio:personas",n); setMEditPersona(null); notify("Persona actualizada.");
  };
  const addCap = async c => {
    const u=usuarios.find(u=>u.id===c.impartidaPorId);
    const ids=Array.isArray(c.personaIds)?c.personaIds:[c.personaId].filter(Boolean);
    const nuevas=ids.map(pid=>({...c,personaId:pid,personaIds:undefined,id:uid(),impartidaPor:u?.nombre||"",createdAt:today()}));
    const n=[...caps,...nuevas]; setCaps(n); await save("srio:caps",n); setMCap(false);
    notify(nuevas.length===1?"Capacitación registrada.":`${nuevas.length} capacitaciones registradas.`);
  };
  const addInc = async i => { const p=personas.find(x=>x.id===i.personaId); const n=[...incs,{...i,id:uid(),personaNombre:p?.nombre||"",createdAt:today()}]; setIncs(n); await save("srio:incs",n); setMInc(false); notify("Incidencia registrada."); };
  const savePesos  = async p  => { setPesos(p);  await save("srio:pesos",p);  notify("Pesos actualizados."); };
  const saveComps  = async c  => { setComps(c);  await save("srio:comps",c);  notify("Competencias actualizadas."); };
  const addLogro   = async l  => {
    const p=personas.find(x=>x.id===l.personaId);
    const n=[...logros,{...l,id:uid(),personaNombre:p?.nombre||"",createdAt:today()}];
    setLogros(n); await save("srio:logros",n); setMLogro(false); notify("✅ Logro registrado.");
  };
  const addEval = async e => {
    const p=personas.find(x=>x.id===e.personaId);
    const n=[...evaluaciones,{...e,id:uid(),personaNombre:p?.nombre||"",createdAt:today()}];
    setEvals(n); await save("srio:evals",n); setMEval(false); notify("📋 Evaluación guardada.");
  };

  const addAviso = async a => {
    const p=personas.find(x=>x.id===a.personaId);
    const nuevo={...a,id:uid(),personaNombre:p?.nombre||"",createdAt:today()};
    const na=[...avisos,nuevo]; setAvisos(na); await save("srio:avisos",na);
    // Si es formal o howler => también crear incidencia automática
    // Todos los niveles generan incidencia — solo reminder no suma puntos (marcado con sinPuntos)
    const tipoIncMap={reminder:"olvido", formal:"error_proceso", howler:"incumplimiento"};
    const labelMap={reminder:"Friendly Reminder", formal:"Aviso Formal", howler:"Amonestación"};
    const tipoInc = tipoIncMap[a.nivel] || "olvido";
    const incAuto={personaId:a.personaId,personaNombre:p?.nombre||"",tipo:tipoInc,fecha:a.fecha,
      descripcion:`[Auto — ${labelMap[a.nivel]||a.nivel}] ${a.motivo}`,
      registradaPor:a.enviadoPor,capacitacionId:"",id:uid(),createdAt:today(),
      fromAviso:nuevo.id, sinPuntos:a.nivel==="reminder"};
    const ni=[...incs,incAuto]; setIncs(ni); await save("srio:incs",ni);
    setMAviso(false);
    notify(a.nivel==="howler"?"🔴 Amonestación registrada.":a.nivel==="formal"?"🟠 Aviso formal registrado.":"💛 Friendly reminder registrado.");
  };
  const login = async u => { setSession(u); await save("srio:session",u); };
  const logout = async () => { setSession(null); await save("srio:session",null); setTab("dashboard"); };

  if(loadError) return (
    <div style={{padding:48,fontFamily:"monospace",color:"#b3452e"}}>
      {loadError} <button onClick={()=>window.location.reload()} style={{marginLeft:12}}>Recargar</button>
    </div>
  );
  if(!loaded) return <div style={{padding:48,fontFamily:"monospace",color:"#6b6660"}}>Cargando SRIO…</div>;
  if(!session) return <><style>{STYLE}</style><LoginScreen usuarios={usuarios} onLogin={login}/></>;

  const capsDePersona   = pid => scopeCaps.filter(c=>c.personaId===pid);
  const logrosDePersona = pid => logros.filter(l=>l.personaId===pid);
  const evalsDePersona  = pid => evaluaciones.filter(e=>e.personaId===pid);
  const incsDePersona   = pid => scopeIncs.filter(i=>i.personaId===pid);
  const avisosDePersona = pid => scopeAvisos.filter(a=>a.personaId===pid);
  const personaById     = id  => personas.find(p=>p.id===id);

  const navItems=[["dashboard","Dashboard"],["personas","Personas"],["capacitaciones","Capacitaciones"],["incidencias","Incidencias"],["avisos","Avisos & Amonestaciones"],["evaluaciones","Evaluaciones"],["reportes","Reportes"],...(session.rol==="superadmin"?[["admin","Administración"]]:[])] ;

  return(
    <><style>{STYLE}</style>
    <div className="app">
      <header className="header">
        <div className="header-brand">
          <img src={ESCUDO} className="header-escudo" alt="Escudo Unisant"/>
          <div className="header-text">
            <span className="header-univ">Universidad Santander</span>
            <span className="header-title">SRIO</span>
            <span className="header-sub">Sistema de Registro de Incidencias Operativas</span>
          </div>
        </div>
        <div className="header-right">
          <div style={{display:"flex",alignItems:"center",gap:10}}>
            <div className="avatar" style={{background:ROLE_COLORS[session.rol]}}>{initials(session.nombre)}</div>
            <div style={{display:"flex",flexDirection:"column"}}>
              <span className="avatar-name">{session.nombre.split(" ").slice(0,3).join(" ")}</span>
              <span className="avatar-role">{ROLES[session.rol]} · {session.categoria}</span>
            </div>
          </div>

        </div>
      </header>

      <div className="body-wrap">
        {/* Sidebar */}
        <nav className={`sidebar ${sidebarOpen?"expanded":"collapsed"}`}>
          <div className="sidebar-toggle">
            <button className="sidebar-toggle-btn" onClick={()=>setSidebarOpen(o=>!o)} title={sidebarOpen?"Colapsar":"Expandir"}>
              {sidebarOpen?"◀":"▶"}
            </button>
          </div>

          {navItems.map(([key,label])=>{
            const ICONS={dashboard:"⊞",personas:"👥",capacitaciones:"📋",incidencias:"⚠",avisos:"📣",reportes:"📊",admin:"⚙"};
            return(
              <button key={key} className={`nav-btn ${tab===key?"active":""}`}
                onClick={()=>{setTab(key);setPerfilPersona(null);}}
                title={!sidebarOpen?label:""}>
                <span className="nav-icon">{ICONS[key]||"•"}</span>
                {sidebarOpen&&<span className="nav-label">{label}</span>}
              </button>
            );
          })}

          {/* Usuario en footer del sidebar */}
          <div className="sidebar-footer">
            <div style={{display:"flex",alignItems:"center",gap:10,padding:"8px 10px 4px"}}>
              <div className="avatar" style={{background:ROLE_COLORS[session.rol],width:28,height:28,fontSize:11,flexShrink:0}}>{initials(session.nombre)}</div>
              {sidebarOpen&&<div style={{overflow:"hidden"}}>
                <div style={{fontSize:11,fontWeight:700,color:"var(--blanco)",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}}>{session.nombre.split(" ").slice(0,2).join(" ")}</div>
                <div style={{fontSize:9,color:"#C8A8A8",textTransform:"uppercase",letterSpacing:.6}}>{ROLES[session.rol]}</div>
              </div>}
            </div>
            <button className="nav-btn" onClick={logout} title="Cerrar sesión">
              <span className="nav-icon" style={{fontSize:15}}>⏻</span>
              {sidebarOpen&&<span className="nav-label">Cerrar sesión</span>}
            </button>
          </div>
        </nav>

        {/* Contenido */}
        <main className="main">
          {toast&&<div className={`alert alert-${toast.type}`}>{toast.msg}</div>}
          {session.rol!=="superadmin"&&(
            <div className="scope-banner">
              👁 Visualizas únicamente los registros vinculados a tus capacitaciones · {scopePersonas.length} persona(s) en tu scope
            </div>
          )}
          {tab==="dashboard"     && <Dashboard session={session} scopePersonas={scopePersonas} scopeIncs={scopeIncs} scopeCaps={scopeCaps} scopeAvisos={scopeAvisos} pesos={pesos} periodo={periodo} setPeriodo={setPeriodo} incsDePersona={incsDePersona} capsDePersona={capsDePersona} avisosDePersona={avisosDePersona} evalsDePersona={evalsDePersona} onVerPerfil={p=>{setPerfilPersona(p);setTab("personas");}}/>}
          {tab==="personas"      && <Personas personas={scopePersonas} incsDePersona={incsDePersona} capsDePersona={capsDePersona} avisosDePersona={avisosDePersona} evalsDePersona={evalsDePersona} pesos={pesos} periodo={periodo} canEdit={canEdit} isAdmin={session.rol==="superadmin"} onAdd={()=>setMPersona(true)} onEdit={p=>setMEditPersona(p)} perfilInicial={perfilPersona} onClearPerfil={()=>setPerfilPersona(null)}/>}
          {tab==="capacitaciones"&& <Capacitaciones caps={scopeCaps} personas={personas} usuarios={usuarios} canEdit={canEdit} onAdd={()=>setMCap(true)} personaById={personaById}/>}
          {tab==="incidencias"   && <Incidencias incs={scopeIncs} personas={personas} caps={scopeCaps} avisos={scopeAvisos} canEdit={canEdit} onAdd={()=>setMInc(true)} personaById={personaById}/>}
          {tab==="avisos"        && <Avisos avisos={scopeAvisos} personas={personas} canEdit={canEdit} onAdd={()=>setMAviso(true)} personaById={personaById}/>}
          {tab==="evaluaciones"   && <Evaluaciones personas={scopePersonas} logros={logros} evaluaciones={evaluaciones} competencias={competencias} canEdit={canEdit} onAddLogro={()=>setMLogro(true)} onAddEval={()=>setMEval(true)} logrosDePersona={logrosDePersona} evalsDePersona={evalsDePersona} incsDePersona={incsDePersona} avisosDePersona={avisosDePersona} capsDePersona={capsDePersona} pesos={pesos} periodo={periodo}/>}
        {tab==="reportes"      && <Reportes session={session} personas={scopePersonas} incs={scopeIncs} caps={scopeCaps} avisos={scopeAvisos} logros={logros} incsDePersona={incsDePersona} capsDePersona={capsDePersona}/>}
          {tab==="admin"&&session.rol==="superadmin"&& <AdminUsuarios usuarios={usuarios} pesos={pesos} competencias={competencias} onSavePesos={savePesos} onSaveComps={saveComps} onAdd={()=>setMUsuario(true)} onToggle={toggleUsuario} onDel={delUsuario}/>}
        </main>
      </div>

      {mPersona&&canEdit&&<ModalPersona onClose={()=>setMPersona(false)} onSave={addPersona} personas={personas}/>}
      {mEditPersona&&session.rol==="superadmin"&&<ModalEditPersona persona={mEditPersona} onClose={()=>setMEditPersona(null)} onSave={updatePersona}/>}
      {mCap&&canEdit&&<ModalCap onClose={()=>setMCap(false)} onSave={addCap} personas={personas} usuarios={usuarios} session={session}/>}
      {mInc&&canEdit&&<ModalInc onClose={()=>setMInc(false)} onSave={addInc} personas={scopePersonas} caps={scopeCaps} session={session}/>}
      {mAviso&&canEdit&&<ModalAviso onClose={()=>setMAviso(false)} onSave={addAviso} personas={scopePersonas} caps={scopeCaps} session={session}/>}
      {mUsuario&&session.rol==="superadmin"&&<ModalUsuario onClose={()=>setMUsuario(false)} onSave={addUsuario}/>}
      {mLogro&&canEdit&&<ModalLogro onClose={()=>setMLogro(false)} onSave={addLogro} personas={scopePersonas} session={session}/>}
      {mEval&&canEdit&&<ModalEval onClose={()=>setMEval(false)} onSave={addEval} personas={scopePersonas} competencias={competencias} session={session} logrosDePersona={logrosDePersona} incsDePersona={incsDePersona} avisosDePersona={avisosDePersona} pesos={pesos} periodo={periodo}/>}
    </div></>
  );
}

// ── LOGIN ─────────────────────────────────────────────────────────────────────
function LoginScreen({usuarios,onLogin}){
  const [step,setStep]=useState("inicio");
  const activos=usuarios.filter(u=>u.activo);
  return(
    <div className="login-wrap">
      <div className="login-card">
        <img src={ESCUDO} className="login-escudo" alt="Escudo Unisant"/>
        <div className="login-univ">Universidad Santander</div>
        <div className="login-logo">SRIO</div>
        <div className="login-sub">Sistema de Registro de Incidencias Operativas</div>
        <div className="login-inst">Vicerrectoría de Desarrollo Informático y Tecnología</div>
        {step==="inicio"&&(
          <>
            <button className="google-btn" onClick={()=>setStep("select")}>
              <svg width="20" height="20" viewBox="0 0 24 24">
                <path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
                <path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
                <path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z"/>
                <path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
              </svg>
              Continuar con Google Institucional
            </button>
            <div style={{fontSize:11,color:"var(--muted)",marginTop:12,fontFamily:"Lato,sans-serif"}}>Solo cuentas @usantander.edu.mx autorizadas por el administrador</div>
          </>
        )}
        {step==="select"&&(
          <div style={{textAlign:"left",marginTop:8}}>
            <div style={{fontFamily:"Lato,sans-serif",fontSize:11,color:"var(--muted)",textTransform:"uppercase",letterSpacing:.6,marginBottom:12}}>Selecciona tu cuenta</div>
            {activos.length===0&&<p style={{color:"var(--muted)",fontSize:13}}>Sin usuarios activos. Contacta al administrador.</p>}
            {activos.map(u=>(
              <button key={u.id} className="user-option" onClick={()=>onLogin(u)}>
                <div className="avatar" style={{background:ROLE_COLORS[u.rol]}}>{initials(u.nombre)}</div>
                <div style={{flex:1}}>
                  <div style={{fontWeight:600,fontSize:14}}>{u.nombre}</div>
                  <div style={{fontSize:11,color:"var(--muted)",fontFamily:"Lato,sans-serif"}}>{u.email}</div>
                  <span className={`badge ${ROLE_BADGES[u.rol]}`} style={{marginTop:4}}>{ROLES[u.rol]} · {u.categoria}</span>
                </div>
              </button>
            ))}
            <button className="btn btn-secondary btn-sm" style={{marginTop:8}} onClick={()=>setStep("inicio")}>← Volver</button>
          </div>
        )}
      </div>
    </div>
  );
}

// ── DASHBOARD ─────────────────────────────────────────────────────────────────
function Dashboard({session,scopePersonas,scopeIncs,scopeCaps,scopeAvisos=[],pesos=DEFAULT_PESOS,periodo=7,setPeriodo,avisosDePersona,evalsDePersona,incsDePersona,capsDePersona,onVerPerfil}){

  // Score ponderado por persona en el periodo
  const personasConScore = [...scopePersonas].map(p=>{
    const is  = incsDePersona(p.id)||[];
    const avs = avisosDePersona ? avisosDePersona(p.id)||[] : [];
    const evs = evalsDePersona  ? evalsDePersona(p.id)||[]  : [];
    const {score, tieneEval} = calcScoreIntegrado(is, avs, evs, pesos, periodo);
    const nivel = nivelDesempeno(score, tieneEval);
    return {...p, score, nivel, tieneEval};
  }).sort((a,b)=>b.score-a.score);

  const criticos    = personasConScore.filter(p=>p.nivel.label==="Req. atención").length;
  const alerta      = personasConScore.filter(p=>p.nivel.label==="En desarrollo").length;
  const seguimiento = personasConScore.filter(p=>p.nivel.label==="Satisfactorio").length;
  const top5        = personasConScore.slice(0,5);

  // Cutoff para últimas incidencias en periodo
  const cutoff = new Date(); cutoff.setDate(cutoff.getDate()-periodo);
  const ult5 = [...scopeIncs]
    .filter(i=>new Date(i.fecha)>=cutoff)
    .sort((a,b)=>b.fecha.localeCompare(a.fecha)).slice(0,5);

  return(
    <div className="stack">
      {/* Selector de periodo */}
      <div style={{display:"flex",alignItems:"center",gap:10,flexWrap:"wrap"}}>
        <span style={{fontSize:11,fontWeight:700,color:"var(--muted)",textTransform:"uppercase",letterSpacing:.8,fontFamily:"Lato,sans-serif"}}>Periodo:</span>
        {PERIODOS.map(p=>(
          <button key={p.value} className={`btn btn-sm ${periodo===p.value?"btn-primary":"btn-secondary"}`}
            onClick={()=>setPeriodo(p.value)}>
            {p.label}
          </button>
        ))}
      </div>

      {/* Stats principales */}
      <div style={{display:"grid",gridTemplateColumns:"repeat(5,1fr)",gap:16}}>
        <div className="stat-box rust">
          <span className="stat-num" style={{color:"var(--danger)"}}>{criticos}</span>
          <span className="stat-label">Req. atención</span>
        </div>
        <div className="stat-box amber">
          <span className="stat-num" style={{color:"var(--warn)"}}>{alerta}</span>
          <span className="stat-label">En desarrollo</span>
        </div>
        <div className="stat-box" style={{borderLeft:"4px solid #1d4ed8"}}>
          <span className="stat-num" style={{color:"#1d4ed8"}}>{seguimiento}</span>
          <span className="stat-label">Satisfactorio</span>
        </div>
        <div className="stat-box ink">
          <span className="stat-num">{scopeIncs.filter(i=>new Date(i.fecha)>=cutoff).length}</span>
          <span className="stat-label">Incidencias</span>
        </div>
        <div className="stat-box sage">
          <span className="stat-num">{scopeCaps.length}</span>
          <span className="stat-label">Capacitaciones</span>
        </div>
      </div>

      <div className="grid-2">
        {/* Ranking ponderado */}
        <div className="card">
          <div className="card-title">Ranking de desempeño — {PERIODOS.find(p=>p.value===periodo)?.label}</div>
          {top5.length===0?<p className="empty">Sin datos en este periodo</p>:top5.map((p,idx)=>(
            <div key={p.id} style={{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"10px 0",borderBottom:"1px solid var(--border)"}}>
              <div style={{display:"flex",alignItems:"center",gap:10}}>
                <span style={{fontFamily:"PT Serif,serif",fontSize:20,fontWeight:700,color:"var(--muted)",minWidth:24}}>{idx+1}</span>
                <div>
                  <div style={{fontWeight:700,fontSize:14}}>{p.nombre}</div>
                  <div style={{fontSize:11,color:"var(--muted)",fontFamily:"Lato,sans-serif"}}>{p.sede} · {p.pais}</div>
                </div>
              </div>
              <div style={{display:"flex",gap:8,alignItems:"center"}}>
                <div style={{textAlign:"right"}}>
                  <div style={{fontFamily:"PT Serif,serif",fontSize:22,fontWeight:700,color:p.nivel.color,lineHeight:1}}>{p.score}</div>
                  <div style={{fontSize:9,color:"var(--muted)",textTransform:"uppercase",letterSpacing:.5,fontFamily:"Lato,sans-serif"}}>{p.tieneEval?"score":"parcial"}</div>
                </div>
                <span className={`badge ${p.nivel.badge}`} title={p.tieneEval?"":"Score parcial — sin evaluación de competencias"}>{p.nivel.label}</span>
                <button className="btn btn-sm btn-secondary" onClick={()=>onVerPerfil(p)}>Ver</button>
              </div>
            </div>
          ))}
        </div>

        {/* Últimas incidencias del periodo */}
        <div className="card">
          <div className="card-title">Actividad reciente — {PERIODOS.find(p=>p.value===periodo)?.label}</div>
          {ult5.length===0?<p className="empty">Sin actividad en este periodo</p>:ult5.map(i=>{
            const tipo=TIPOS_INC.find(t=>t.value===i.tipo);
            const avisoOrig=i.fromAviso?scopeAvisos.find(a=>a.id===i.fromAviso):null;
            const nivelOrig=avisoOrig?NIVELES_AVISO.find(n=>n.value===avisoOrig.nivel):null;
            return(
              <div key={i.id} style={{padding:"10px 0",borderBottom:"1px solid var(--border)"}}>
                <div style={{display:"flex",justifyContent:"space-between",marginBottom:4}}>
                  <span style={{fontWeight:700,fontSize:13}}>{i.personaNombre}</span>
                  <span style={{fontSize:11,color:"var(--muted)",fontFamily:"Lato,sans-serif"}}>{fmtDate(i.fecha)}</span>
                </div>
                <div style={{display:"flex",gap:6,alignItems:"center",flexWrap:"wrap"}}>
                  {nivelOrig
                    ? <span className={`badge ${nivelOrig.badge}`}>{nivelOrig.emoji} {nivelOrig.label}</span>
                    : <span className={`badge ${tipo?.badge||"badge-ink"}`}>{tipo?.label||i.tipo}</span>}
                  <span style={{fontSize:11,color:"var(--muted)"}}>{(avisoOrig?.motivo||i.descripcion)?.slice(0,50)}…</span>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// ── PERSONAS ──────────────────────────────────────────────────────────────────
function Personas({personas,incsDePersona,capsDePersona,avisosDePersona,evalsDePersona,pesos=DEFAULT_PESOS,periodo=30,canEdit,isAdmin=false,onAdd,onEdit,perfilInicial,onClearPerfil}){
  const [perfil,setPerfil]=useState(perfilInicial||null);
  const [filtro,setFiltro]=useState("");
  const [periodoP,setPeriodoP]=useState(7);
  useEffect(()=>{ if(perfilInicial){ setPerfil(perfilInicial); onClearPerfil(); } },[perfilInicial]);

  if(perfil){
    const cs=capsDePersona(perfil.id)||[], is=incsDePersona(perfil.id)||[], avs=(avisosDePersona?avisosDePersona(perfil.id):[])||[];
    const evsp = evalsDePersona ? evalsDePersona(perfil.id)||[] : [];
    const {score, tieneEval, incScore, compScore, penalizacion} = calcScoreIntegrado(is, avs, evsp, pesos, periodoP);
    const risk = nivelDesempeno(score, tieneEval);
    // También calcular desglose de incidencias para mostrar
    const {desglose} = calcScore(is, avs, pesos, periodoP);
    const eventos=[
      ...cs.map(c=>({tipo:"cap",fecha:c.fecha,titulo:`Capacitación: ${c.tema}`,desc:(c.notas||"")+(c.linkGrabacion?` 🔗 ${c.linkGrabacion}`:""),id:c.id})),
      ...is.map(i=>{ const tipo=TIPOS_INC.find(t=>t.value===i.tipo); const capRef=i.capacitacionId?cs.find(c=>c.id===i.capacitacionId):null; const avisoRefP=i.fromAviso?avs.find(a=>a.id===i.fromAviso):null; const origenP=avisoRefP?NIVELES_AVISO.find(n=>n.value===avisoRefP.nivel):null; const tituloInc=origenP?`${origenP.emoji} ${origenP.label}: ${avisoRefP?.motivo?.slice(0,50)||""}`:`Incidencia: ${tipo?.label||i.tipo}`; return{tipo:"inc",fecha:i.fecha,titulo:tituloInc,desc:capRef?`Ref. capacitación: "${capRef.tema}"`:"",id:i.id,esAviso:!!i.fromAviso}; }),
      ...avs.map(a=>{ const niv=NIVELES_AVISO.find(n=>n.value===a.nivel); return{tipo:"aviso",fecha:a.fecha,titulo:`${niv?.emoji||""} ${niv?.label||"Aviso"}: ${a.motivo?.slice(0,60)}`,desc:a.textoGenerado?"Texto generado disponible":"",id:a.id}; }),
    ].sort((a,b)=>b.fecha.localeCompare(a.fecha));
    return(
      <div className="stack">
        <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",flexWrap:"wrap",gap:12}}>
          <div style={{display:"flex",gap:12,alignItems:"center",flexWrap:"wrap"}}>
            <button className="btn btn-secondary" onClick={()=>setPerfil(null)}>← Volver</button>
            <h2 style={{fontFamily:"PT Serif,serif",fontSize:22,fontWeight:700}}>{perfil.nombre}</h2>
            <span className={`badge ${risk.badge}`} style={{fontSize:13,padding:"4px 10px"}}>{risk.label}</span>
            <span style={{fontFamily:"PT Serif,serif",fontSize:28,fontWeight:700,color:risk.color,lineHeight:1}}>{score}</span>
            <div style={{display:"flex",flexDirection:"column",justifyContent:"center"}}>
              <span style={{fontSize:10,color:"var(--muted)",fontFamily:"Lato,sans-serif",textTransform:"uppercase",letterSpacing:.5,lineHeight:1.4}}>
                {tieneEval?"score integrado":"score parcial"}
              </span>
              {tieneEval&&<span style={{fontSize:10,color:"var(--muted)",fontFamily:"Lato,sans-serif",lineHeight:1.4}}>
                comp. {compScore} − inc. {penalizacion??0}
              </span>}
              {!tieneEval&&<span style={{fontSize:10,color:"var(--amber)",fontFamily:"Lato,sans-serif",lineHeight:1.4}}>
                sin eval. de competencias
              </span>}
            </div>
            {isAdmin&&<button className="btn btn-sm btn-secondary" onClick={()=>onEdit(perfil)}>✏ Editar persona</button>}
          </div>
          <div style={{display:"flex",alignItems:"center",gap:8,flexWrap:"wrap"}}>
            <span style={{fontSize:11,fontWeight:700,color:"var(--muted)",textTransform:"uppercase",letterSpacing:.8,fontFamily:"Lato,sans-serif"}}>Periodo:</span>
            {PERIODOS.map(p=>(
              <button key={p.value} className={`btn btn-sm ${periodoP===p.value?"btn-primary":"btn-secondary"}`}
                onClick={()=>setPeriodoP(p.value)}>
                {p.label}
              </button>
            ))}
          </div>
        </div>
        <div className="grid-2">
          <div className="card">
            <div className="card-title">Información</div>
            <div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:16}}>
              {[["Sede",perfil.sede],["País",perfil.pais],["Área",perfil.area||"—"],["Cargo",perfil.cargo||"—"],["Registrado",fmtDate(perfil.createdAt)]].map(([l,v])=>(
                <div key={l}><div style={{fontSize:11,color:"var(--muted)",fontFamily:"Lato,sans-serif",marginBottom:2}}>{l}</div><div style={{fontSize:14,fontWeight:500}}>{v}</div></div>
              ))}
            </div>
            <div className="persona-stats" style={{marginTop:20}}>
              <div className="persona-stat"><span className="persona-stat-n" style={{color:"var(--ok)"}}>{cs.length}</span><span className="persona-stat-l">Caps.</span></div>
              <div className="persona-stat"><span className={`persona-stat-n ${risk.cls}`}>{score}</span><span className="persona-stat-l">Score</span></div>
              <div className="persona-stat"><span className="persona-stat-n" style={{color:"var(--danger)"}}>{desglose.length}</span><span className="persona-stat-l">Eventos</span></div>
              <div className="persona-stat"><span className="persona-stat-n" style={{color:"var(--warn)"}}>{avs.length}</span><span className="persona-stat-l">Avisos</span></div>
            </div>
            {desglose.length>0&&(
              <div style={{marginTop:14,background:"var(--gris-lt)",borderRadius:4,padding:"10px 14px"}}>
                <div style={{fontSize:10,fontWeight:700,color:"var(--muted)",textTransform:"uppercase",letterSpacing:.8,fontFamily:"Lato,sans-serif",marginBottom:8}}>Desglose de puntaje — {PERIODOS.find(p=>p.value===periodoP)?.label}</div>
                {Object.entries(desglose.reduce((acc,d)=>{ acc[d.tipo]=(acc[d.tipo]||0)+d.peso; return acc; },{})).map(([tipo,pts])=>{
                  const t=TIPOS_INC.find(x=>x.value===tipo);
                  const n=NIVELES_AVISO.find(x=>x.value==="formal"&&tipo==="aviso_formal"||x.value==="howler"&&tipo==="amonestacion");
                  const label=t?.label||n?.label||tipo;
                  const badge=t?.badge||n?.badge||"badge-ink";
                  return(
                    <div key={tipo} style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:4}}>
                      <span className={`badge ${badge}`} style={{fontSize:10}}>{label}</span>
                      <span style={{fontFamily:"PT Serif,serif",fontSize:16,fontWeight:700,color:"var(--danger)"}}>+{pts} pts</span>
                    </div>
                  );
                })}
              </div>
            )}
          </div>
          <div className="card">
            <div className="card-title">Por tipo de incidencia</div>
            {is.length===0?<p className="empty">Sin incidencias</p>:TIPOS_INC.map(t=>{ const count=is.filter(i=>i.tipo===t.value).length; if(!count)return null; return(
              <div key={t.value} style={{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"8px 0",borderBottom:"1px solid var(--border)"}}>
                <span className={`badge ${t.badge}`}>{t.label}</span>
                <span style={{fontFamily:"PT Serif,serif",fontSize:20,fontWeight:700}}>{count}</span>
              </div>
            );})}
            {avs.filter(a=>a.nivel==="formal"||a.nivel==="howler").length>0&&(<>
              <div style={{marginTop:12,marginBottom:4,fontSize:10,color:"var(--muted)",fontFamily:"Lato,sans-serif",textTransform:"uppercase",letterSpacing:.8,fontWeight:700}}>Avisos y Amonestaciones</div>
              {NIVELES_AVISO.filter(n=>n.value!=="reminder").map(n=>{ const cnt=avs.filter(a=>a.nivel===n.value).length; if(!cnt)return null; return(
                <div key={n.value} style={{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"8px 0",borderBottom:"1px solid var(--border)"}}>
                  <span className={`badge ${n.badge}`}>{n.emoji} {n.label}</span>
                  <span style={{fontFamily:"PT Serif,serif",fontSize:20,fontWeight:700}}>{cnt}</span>
                </div>
              );})}
            </>)}
          </div>
        </div>
        <div className="card">
          <div className="card-title">Historial cronológico</div>
          {eventos.length===0?<p className="empty">Sin actividad</p>:
          <div style={{display:"flex",flexDirection:"column",gap:0}}>
            {eventos.map((e,idx)=>(
              <div key={e.id} className="tl-item">
                <div className="tl-line">
                  <div className={`tl-dot ${e.tipo==="cap"?"tl-dot-cap":e.tipo==="aviso"?"tl-dot-aviso":"tl-dot-inc"}`}/>
                  {idx<eventos.length-1&&<div className="tl-bar"/>}
                </div>
                <div className="tl-content">
                  <div className="tl-date">{fmtDate(e.fecha)}</div>
                  <div className="tl-title">{e.titulo}</div>
                  {e.desc&&<div className="tl-desc">{e.desc}</div>}
                </div>
              </div>
            ))}
          </div>}
        </div>
      </div>
    );
  }

  const filtradas=personas.filter(p=>p.nombre.toLowerCase().includes(filtro.toLowerCase())||p.sede.toLowerCase().includes(filtro.toLowerCase())||p.pais.toLowerCase().includes(filtro.toLowerCase()));
  return(
    <div className="stack">
      <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",flexWrap:"wrap",gap:12}}>
        <h2 style={{fontFamily:"PT Serif,serif",fontSize:22}}>Personas ({personas.length})</h2>
        <div style={{display:"flex",gap:10}}>
          <input className="form-input" placeholder="Buscar…" value={filtro} onChange={e=>setFiltro(e.target.value)} style={{width:240}}/>
          {canEdit&&<button className="btn btn-primary" onClick={onAdd}>+ Registrar persona</button>}
        </div>
      </div>
      {filtradas.length===0?<p className="empty">Sin personas registradas.</p>:
      <div className="grid-2">
        {filtradas.map(p=>{
          const is=incsDePersona(p.id)||[], cs=capsDePersona(p.id)||[];
          const avsp=avisosDePersona?avisosDePersona(p.id)||[]:[];
          const evsp=evalsDePersona?evalsDePersona(p.id)||[]:[];
          const {score:scoreP, tieneEval:teP}=calcScoreIntegrado(is,avsp,evsp,pesos,periodo||30);
          const nivel=nivelDesempeno(scoreP,teP);
          return(
          <div key={p.id} className="persona-card" onClick={()=>setPerfil(p)}>
            <div style={{display:"flex",justifyContent:"space-between",alignItems:"flex-start"}}>
              <div>
                <div style={{fontWeight:700,fontSize:15}}>{p.nombre}</div>
                <div style={{fontSize:12,color:"var(--muted)",fontFamily:"Lato,sans-serif"}}>{p.cargo?`${p.cargo} · `:""}{p.sede} · {p.pais}</div>
              </div>
              <div style={{display:"flex",flexDirection:"column",alignItems:"flex-end",gap:4}}>
                <div style={{display:"flex",gap:6,alignItems:"center"}}>
                  <span className={`badge ${nivel.badge}`} title={teP?"":"Sin evaluación de competencias"}>{nivel.label}</span>
                  {isAdmin&&<button className="btn btn-sm btn-secondary" style={{fontSize:11,padding:"4px 8px"}}
                    onClick={e=>{e.stopPropagation();onEdit(p);}}>✏ Editar</button>}
                </div>
                <div style={{display:"flex",alignItems:"baseline",gap:4}}>
                  <span style={{fontFamily:"PT Serif,serif",fontSize:20,fontWeight:700,color:nivel.color,lineHeight:1}}>{scoreP}</span>
                  <span style={{fontSize:9,color:"var(--muted)",fontFamily:"Lato,sans-serif",textTransform:"uppercase",letterSpacing:.4}}>{teP?"integrado":"parcial"}</span>
                </div>
              </div>
            </div>
            <div className="persona-stats">
              <div className="persona-stat"><span className="persona-stat-n" style={{color:"var(--ok)"}}>{cs.length}</span><span className="persona-stat-l">Caps.</span></div>
              <div className="persona-stat"><span className="persona-stat-n" style={{color:"var(--danger)"}}>{is.length}</span><span className="persona-stat-l">Inc.</span></div>
              {p.area&&<div className="persona-stat"><span className="persona-stat-n" style={{fontSize:13}}>{p.area}</span><span className="persona-stat-l">Área</span></div>}
            </div>
          </div>
        );})}
      </div>}
    </div>
  );
}

// ── CAPACITACIONES ────────────────────────────────────────────────────────────
function Capacitaciones({caps,personas,usuarios,canEdit,onAdd,personaById}){
  const [filtro,setFiltro]=useState("");
  const sorted=[...caps].sort((a,b)=>b.fecha.localeCompare(a.fecha));
  const filtradas=sorted.filter(c=>{ const p=personaById(c.personaId); return c.tema.toLowerCase().includes(filtro.toLowerCase())||(p&&p.nombre.toLowerCase().includes(filtro.toLowerCase())); });
  return(
    <div className="stack">
      <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",flexWrap:"wrap",gap:12}}>
        <h2 style={{fontFamily:"PT Serif,serif",fontSize:22}}>Capacitaciones ({caps.length})</h2>
        <div style={{display:"flex",gap:10}}>
          <input className="form-input" placeholder="Buscar…" value={filtro} onChange={e=>setFiltro(e.target.value)} style={{width:240}}/>
          {canEdit&&<button className="btn btn-sage" onClick={onAdd}>+ Registrar capacitación</button>}
        </div>
      </div>
      <div className="card">
        <div className="table-wrap">
          <table>
            <thead><tr><th>Fecha</th><th>Persona</th><th>Sede</th><th>Tema</th><th>Impartida por</th><th>Categoría</th><th>Evidencia</th><th>Link</th><th>Imgs.</th></tr></thead>
            <tbody>
              {filtradas.length===0?<tr><td colSpan={9}><p className="empty">Sin capacitaciones.</p></td></tr>:filtradas.map(c=>{
                const p=personaById(c.personaId);
                const u=usuarios.find(u=>u.id===c.impartidaPorId);
                return(
                  <tr key={c.id}>
                    <td style={{fontFamily:"Lato,sans-serif",fontSize:12}}>{fmtDate(c.fecha)}</td>
                    <td style={{fontWeight:600}}>{p?.nombre||"—"}</td>
                    <td style={{fontSize:12,color:"var(--muted)"}}>{p?`${p.sede} · ${p.pais}`:"—"}</td>
                    <td>{c.tema}</td>
                    <td style={{fontSize:12}}>{c.impartidaPor||"—"}</td>
                    <td><span className="badge badge-blue">{u?.categoria||"—"}</span></td>
                    <td><span className={`badge ${c.evidencia&&c.evidencia!=="Sin evidencia"?"badge-sage":"badge-ink"}`}>{c.evidencia||"—"}</span></td>
                    <td>{c.linkGrabacion?<a href={c.linkGrabacion} target="_blank" rel="noreferrer" style={{color:"var(--danger)",textDecoration:"underline",fontFamily:"Lato,sans-serif",fontSize:11}}>🔗 Ver</a>:<span style={{color:"var(--muted)"}}>—</span>}</td>
                    <td>{c.imagenes&&c.imagenes.length>0?<div style={{display:"flex",gap:4}}>{c.imagenes.map((img,i)=><img key={i} src={img.base64} alt="" style={{width:32,height:32,objectFit:"cover",borderRadius:3,border:"1px solid var(--border)",cursor:"pointer"}} onClick={()=>window.open(img.base64)}/>)}</div>:<span style={{color:"var(--muted)"}}>—</span>}</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

// ── INCIDENCIAS ───────────────────────────────────────────────────────────────
function Incidencias({incs,personas,caps,avisos=[],canEdit,onAdd,personaById}){
  const [filtroTipo,setFiltroTipo]=useState("");
  const sorted=[...incs].sort((a,b)=>b.fecha.localeCompare(a.fecha));
  const filtradas=filtroTipo?sorted.filter(i=>i.tipo===filtroTipo):sorted;
  return(
    <div className="stack">
      <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",flexWrap:"wrap",gap:12}}>
        <h2 style={{fontFamily:"PT Serif,serif",fontSize:22}}>Incidencias ({incs.length})</h2>
        <div style={{display:"flex",gap:10,flexWrap:"wrap"}}>
          <select className="form-select" value={filtroTipo} onChange={e=>setFiltroTipo(e.target.value)} style={{width:220}}>
            <option value="">Todos los tipos</option>
            {TIPOS_INC.map(t=><option key={t.value} value={t.value}>{t.label}</option>)}
          </select>
          {canEdit&&<button className="btn btn-primary" onClick={onAdd}>+ Registrar incidencia</button>}
        </div>
      </div>
      <div className="card">
        <div className="table-wrap">
          <table>
            <thead><tr><th>Fecha</th><th>Persona</th><th>Sede</th><th>Tipo</th><th>Origen</th><th>Descripción</th><th>Cap. previa</th><th>Registrada por</th></tr></thead>
            <tbody>
              {filtradas.length===0?<tr><td colSpan={8}><p className="empty">Sin incidencias.</p></td></tr>:filtradas.map(i=>{
                const p=personaById(i.personaId);
                const tipo=TIPOS_INC.find(t=>t.value===i.tipo);
                const cap=i.capacitacionId?caps.find(c=>c.id===i.capacitacionId):null;
                const avisoRef=i.fromAviso?avisos.find(a=>a.id===i.fromAviso):null;
                const origenNivel=avisoRef?NIVELES_AVISO.find(n=>n.value===avisoRef.nivel):null;
                const esAutoAviso=!!i.fromAviso;
                return(
                  <tr key={i.id} style={{background:esAutoAviso?"#FFF8EC":undefined}}>
                    <td style={{fontFamily:"Lato,sans-serif",fontSize:12}}>{fmtDate(i.fecha)}</td>
                    <td style={{fontWeight:600}}>{p?.nombre||i.personaNombre}</td>
                    <td style={{fontSize:12,color:"var(--muted)"}}>{p?`${p.sede} · ${p.pais}`:"—"}</td>
                    <td><span className={`badge ${tipo?.badge||"badge-ink"}`}>{tipo?.label||i.tipo}</span></td>
                    <td>
                      {esAutoAviso
                        ? <span className={`badge ${origenNivel?.badge||"badge-amber"}`} style={{gap:4}}>
                            {origenNivel?.emoji} {origenNivel?.label||"Aviso"}
                          </span>
                        : <span style={{fontSize:11,color:"var(--muted)"}}>Manual</span>}
                    </td>
                    <td style={{fontSize:13,maxWidth:200}}>
                      {esAutoAviso
                        ? <span>{avisoRef?.motivo||i.descripcion.replace(/^\[Auto[^\]]*\]\s*/,"")}</span>
                        : i.descripcion}
                    </td>
                    <td>{cap?<span className="badge badge-sage">{cap.tema}</span>:<span style={{color:"var(--muted)"}}>—</span>}</td>
                    <td style={{fontSize:12}}>{i.registradaPor||"—"}</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

// ── REPORTES ──────────────────────────────────────────────────────────────────
function Reportes({session,personas,incs,caps,avisos=[],logros=[],incsDePersona,capsDePersona}){
  const [filtroSede,setFiltroSede]=useState("");
  const [filtroPais,setFiltroPais]=useState("");
  const [filtroPeriodo,setFiltroPeriodo]=useState("todo");
  const [generating,setGenerating]=useState(false);
  const [secciones,setSecciones]=useState(null); // {contexto, hallazgos, riesgos, recomendaciones}

  const sedes=[...new Set(personas.map(p=>p.sede))].filter(Boolean);
  const paises=[...new Set(personas.map(p=>p.pais))].filter(Boolean);
  const filtrarF=f=>{
    if(filtroPeriodo==="todo")return true;
    const d=new Date(f),now=new Date();
    if(filtroPeriodo==="mes")return d>=new Date(now.getFullYear(),now.getMonth(),1);
    if(filtroPeriodo==="trimestre")return d>=new Date(now.getFullYear(),now.getMonth()-3,1);
    if(filtroPeriodo==="año")return d>=new Date(now.getFullYear(),0,1);
    return true;
  };

  const pFilt=personas.filter(p=>(!filtroSede||p.sede===filtroSede)&&(!filtroPais||p.pais===filtroPais));
  const incsFilt=incs.filter(i=>pFilt.some(p=>p.id===i.personaId)&&filtrarF(i.fecha)&&!i.sinPuntos&&!i.fromAviso);
  const capsFilt=caps.filter(c=>pFilt.some(p=>p.id===c.personaId)&&filtrarF(c.fecha));
  const avisosFilt=(avisos||[]).filter(a=>pFilt.some(p=>p.id===a.personaId)&&filtrarF(a.fecha)&&(a.nivel==="formal"||a.nivel==="howler"));

  const tabla=[...pFilt].map(p=>{
    const is=incsDePersona(p.id).filter(i=>filtrarF(i.fecha)&&!i.sinPuntos&&!i.fromAviso);
    const cs=capsDePersona(p.id).filter(c=>filtrarF(c.fecha));
    const avs=(avisos||[]).filter(a=>a.personaId===p.id&&filtrarF(a.fecha));
    const logros_p=[];
    const {score}=calcScore(is,avs,DEFAULT_PESOS,filtroPeriodo==="todo"?3650:filtroPeriodo==="mes"?30:filtroPeriodo==="trimestre"?90:365);
    const risk=riskLevel(score);
    return{...p,is,cs,avs,score,risk};
  }).sort((a,b)=>b.score-a.score);

  const periodoLabel=filtroPeriodo==="todo"?"Historial completo":filtroPeriodo==="mes"?"Este mes":filtroPeriodo==="trimestre"?"Últimos 3 meses":"Este año";

  const generarResumen=async()=>{
    setGenerating(true); setSecciones(null);
    try{
      const datos={
        periodo:periodoLabel,
        sede:filtroSede||"Todas las sedes",
        pais:filtroPais||"Todos los países",
        totalPersonas:pFilt.length,
        totalIncidencias:incsFilt.length,
        totalCapacitaciones:capsFilt.length,
        totalAvisosFormal:avisosFilt.length,
        criticos:tabla.filter(p=>p.risk.label==="CRÍTICO").length,
        alerta:tabla.filter(p=>p.risk.label==="ALERTA").length,
        seguimiento:tabla.filter(p=>p.risk.label==="SEGUIMIENTO").length,
        normal:tabla.filter(p=>p.risk.label==="NORMAL").length,
        topIncidentes:tabla.filter(p=>p.score>0).slice(0,5).map(p=>({
          nombre:p.nombre, sede:p.sede, pais:p.pais, score:p.score, nivel:p.risk.label,
          incidencias:p.is.length, capacitaciones:p.cs.length,
          tiposInc:TIPOS_INC.map(t=>({tipo:t.label,count:p.is.filter(i=>i.tipo===t.value).length})).filter(x=>x.count>0),
          avisos:p.avs.length,
        })),
        distribucionTipos:TIPOS_INC.map(t=>({tipo:t.label,count:incsFilt.filter(i=>i.tipo===t.value).length})).filter(x=>x.count>0),
      };
      const resp=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",
        headers:{"Content-Type":"application/json"},
        body:JSON.stringify({model:"claude-sonnet-4-20250514",max_tokens:1400,
          system:`Eres analista institucional senior de la Vicerrectoría de Desarrollo Informático y Tecnología de Universidad Santander.
Redactas resúmenes ejecutivos formales para reportes de desempeño operativo de sedes.
Tono: institucional, objetivo, directo. Sin adjetivos subjetivos sobre personas. Usa datos concretos.

Responde SOLO con JSON válido (sin markdown), con exactamente estas 4 claves:
{
  "contexto": "1 párrafo. Alcance del reporte: periodo, sedes, universo de personas evaluadas.",
  "hallazgos": "2-3 párrafos. Principales hallazgos: distribución de niveles, tipos de incidencia más frecuentes, patrones identificados. Cita números.",
  "riesgos": "1-2 párrafos. Personas o sedes en nivel crítico/alerta. Patrones de reincidencia. Sin nombrar personas individualmente — usa 'N personas en nivel crítico en [sede]'.",
  "recomendaciones": "1-2 párrafos. Acciones concretas sugeridas: reforzamiento de capacitaciones, seguimiento específico, revisión de procesos."
}`,
          messages:[{role:"user",content:`Genera el resumen ejecutivo para este reporte:
${JSON.stringify(datos,null,2)}`}]
        })
      });
      const data=await resp.json();
      const texto=data.content?.find(b=>b.type==="text")?.text||"{}";
      const parsed=JSON.parse(texto.replace(/```json|```/g,"").trim());
      setSecciones(parsed);
    }catch(e){ setSecciones({contexto:"Error al generar.",hallazgos:"",riesgos:"",recomendaciones:""}); }
    setGenerating(false);
  };

  const seccionesConfig=[
    {key:"contexto",    titulo:"1. Contexto",           color:"var(--rojo-deep)"},
    {key:"hallazgos",   titulo:"2. Hallazgos principales", color:"var(--rojo-deep)"},
    {key:"riesgos",     titulo:"3. Riesgos identificados", color:"var(--danger)"},
    {key:"recomendaciones",titulo:"4. Recomendaciones",  color:"var(--ok)"},
  ];

  return(
    <div className="stack">
      {/* Configuración */}
      <div className="card">
        <div className="card-title">Configurar reporte</div>
        <div className="form-row-3">
          <div className="form-group"><label className="form-label">País</label>
            <select className="form-select" value={filtroPais} onChange={e=>{setFiltroPais(e.target.value);setFiltroSede("");}}>
              <option value="">Todos</option>{paises.map(p=><option key={p}>{p}</option>)}
            </select>
          </div>
          <div className="form-group"><label className="form-label">Sede</label>
            <select className="form-select" value={filtroSede} onChange={e=>setFiltroSede(e.target.value)}>
              <option value="">Todas</option>
              {sedes.filter(s=>!filtroPais||personas.find(p=>p.sede===s)?.pais===filtroPais).map(s=><option key={s}>{s}</option>)}
            </select>
          </div>
          <div className="form-group"><label className="form-label">Período</label>
            <select className="form-select" value={filtroPeriodo} onChange={e=>setFiltroPeriodo(e.target.value)}>
              <option value="todo">Todo el historial</option>
              <option value="mes">Este mes</option>
              <option value="trimestre">Últimos 3 meses</option>
              <option value="año">Este año</option>
            </select>
          </div>
        </div>
        <div className="btn-group">
          <button className="btn btn-primary" onClick={generarResumen} disabled={generating}>
            {generating?"Generando análisis…":"✦ Generar resumen ejecutivo con IA"}
          </button>
          <button className="btn btn-secondary" onClick={()=>window.print()}>Imprimir / PDF</button>
        </div>
      </div>

      {/* Reporte */}
      <div className="card">
        {/* Encabezado institucional */}
        <div style={{display:"flex",justifyContent:"space-between",alignItems:"flex-start",marginBottom:28,paddingBottom:20,borderBottom:"2px solid var(--dorado)"}}>
          <div style={{display:"flex",alignItems:"center",gap:16}}>
            <img src={ESCUDO} alt="Escudo" style={{width:52,height:52,objectFit:"contain",opacity:.85}}/>
            <div>
              <div style={{fontFamily:"Lato,sans-serif",fontSize:10,fontWeight:700,color:"var(--dorado-dk)",textTransform:"uppercase",letterSpacing:1.5,marginBottom:3}}>Universidad Santander · Vicerrectoría TI</div>
              <div style={{fontFamily:"PT Serif,serif",fontSize:24,fontWeight:700,color:"var(--rojo-deep)",lineHeight:1.1}}>Reporte de Incidencias Operativas</div>
              <div style={{fontSize:12,color:"var(--muted)",marginTop:4}}>
                {filtroSede||filtroPais?`${filtroSede||"Todas las sedes"} · ${filtroPais||""}`.replace(/· $/,""):("Todas las sedes · Todos los países")} · {periodoLabel}
              </div>
            </div>
          </div>
          <div style={{fontFamily:"Lato,sans-serif",fontSize:11,color:"var(--muted)",textAlign:"right",lineHeight:2}}>
            <div style={{fontWeight:700,color:"var(--ink)"}}>{session.nombre}</div>
            <div>Vicerrectoría Desarrollo Informático y Tecnología</div>
            <div>Elaborado: {fmtDate(today())}</div>
          </div>
        </div>

        {/* Stats */}
        <div style={{display:"grid",gridTemplateColumns:"repeat(5,1fr)",gap:12,marginBottom:28}}>
          {[
            {label:"Críticos",   val:tabla.filter(p=>p.risk.label==="CRÍTICO").length,    cls:"risk-high", border:"var(--danger)"},
            {label:"En alerta",  val:tabla.filter(p=>p.risk.label==="ALERTA").length,     cls:"risk-mid",  border:"var(--warn)"},
            {label:"Seguimiento",val:tabla.filter(p=>p.risk.label==="SEGUIMIENTO").length,cls:"risk-follow",border:"#1d4ed8"},
            {label:"Incidencias",val:incsFilt.length,                                      cls:"",          border:"var(--rojo)"},
            {label:"Capacitaciones",val:capsFilt.length,                                   cls:"",          border:"var(--ok)"},
          ].map(s=>(
            <div key={s.label} style={{background:"var(--gris-lt)",borderRadius:4,padding:"16px 18px",borderLeft:`4px solid ${s.border}`}}>
              <div style={{fontFamily:"PT Serif,serif",fontSize:32,fontWeight:700,lineHeight:1}} className={s.cls}>{s.val}</div>
              <div style={{fontSize:10,color:"var(--muted)",textTransform:"uppercase",letterSpacing:.8,fontFamily:"Lato,sans-serif",marginTop:4}}>{s.label}</div>
            </div>
          ))}
        </div>

        {/* Resumen ejecutivo estructurado */}
        {secciones&&(
          <div style={{marginBottom:28}}>
            <div style={{fontFamily:"Lato,sans-serif",fontSize:10,fontWeight:700,textTransform:"uppercase",letterSpacing:1.2,color:"var(--muted)",marginBottom:16,display:"flex",alignItems:"center",gap:8}}>
              <span style={{flex:1,height:1,background:"var(--border)",display:"block"}}/>
              Resumen ejecutivo
              <span style={{flex:1,height:1,background:"var(--border)",display:"block"}}/>
            </div>
            <div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:16}}>
              {seccionesConfig.map(s=>secciones[s.key]?(
                <div key={s.key} style={{background:"var(--gris-lt)",borderRadius:4,padding:"16px 20px",borderTop:`3px solid ${s.color}`}}>
                  <div style={{fontFamily:"Lato,sans-serif",fontSize:10,fontWeight:700,textTransform:"uppercase",letterSpacing:.8,color:s.color,marginBottom:10}}>{s.titulo}</div>
                  <p style={{fontSize:13,lineHeight:1.75,color:"var(--ink)"}}>{secciones[s.key]}</p>
                </div>
              ):null)}
            </div>
          </div>
        )}

        <div className="divider"/>

        {/* Tabla de personas */}
        <div style={{fontFamily:"Lato,sans-serif",fontSize:10,fontWeight:700,textTransform:"uppercase",letterSpacing:1,color:"var(--muted)",marginBottom:14}}>Detalle por persona</div>
        <div className="table-wrap">
          <table>
            <thead>
              <tr>
                <th>#</th><th>Nombre</th><th>Sede</th><th>País</th><th>Área</th>
                <th style={{textAlign:"center"}}>Caps.</th>
                <th style={{textAlign:"center"}}>Inc.</th>
                <th style={{textAlign:"center"}}>Avisos</th>
                <th style={{textAlign:"center"}}>Logros</th>
                <th style={{textAlign:"center"}}>Score</th>
                <th>Nivel</th>
              </tr>
            </thead>
            <tbody>
              {tabla.length===0
                ?<tr><td colSpan={11}><p className="empty">Sin datos.</p></td></tr>
                :tabla.map((p,idx)=>{
                  const logrosP=(typeof logros!=="undefined"?logros:[]).filter(l=>l.personaId===p.id);
                  return(
                    <tr key={p.id}>
                      <td style={{fontFamily:"Lato,monospace",fontSize:12,color:"var(--muted)"}}>{idx+1}</td>
                      <td style={{fontWeight:700}}>{p.nombre}</td>
                      <td style={{fontSize:12}}>{p.sede}</td>
                      <td style={{fontSize:12}}>{p.pais}</td>
                      <td style={{fontSize:12,color:"var(--muted)"}}>{p.area||"—"}</td>
                      <td style={{textAlign:"center"}}>{p.cs.length}</td>
                      <td style={{textAlign:"center",fontWeight:700,color:p.is.length>0?"var(--danger)":"inherit"}}>{p.is.length}</td>
                      <td style={{textAlign:"center",color:p.avs.filter(a=>a.nivel==="howler").length>0?"var(--danger)":p.avs.length>0?"var(--warn)":"inherit",fontWeight:p.avs.length>0?700:400}}>
                        {p.avs.length>0?p.avs.length:"—"}
                      </td>
                      <td style={{textAlign:"center",color:"var(--ok)",fontWeight:logrosP.length>0?700:400}}>
                        {logrosP.length>0?logrosP.length:"—"}
                      </td>
                      <td style={{textAlign:"center",fontFamily:"PT Serif,serif",fontSize:16,fontWeight:700,color:p.risk.color}}>{p.score}</td>
                      <td><span className={`badge ${p.risk.badge}`}>{p.risk.label}</span></td>
                    </tr>
                  );
                })}
            </tbody>
          </table>
        </div>

        {/* Distribución por tipo */}
        {incsFilt.length>0&&(
          <>
            <div className="divider"/>
            <div style={{fontFamily:"Lato,sans-serif",fontSize:10,fontWeight:700,textTransform:"uppercase",letterSpacing:1,color:"var(--muted)",marginBottom:14}}>Distribución por tipo de incidencia</div>
            <div style={{display:"flex",gap:12,flexWrap:"wrap"}}>
              {TIPOS_INC.map(t=>{
                const c=incsFilt.filter(i=>i.tipo===t.value).length;
                if(!c)return null;
                const pct=Math.round((c/incsFilt.length)*100);
                return(
                  <div key={t.value} style={{display:"flex",alignItems:"center",gap:10,background:"var(--gris-lt)",borderRadius:4,padding:"8px 14px",border:"1px solid var(--border)"}}>
                    <span className={`badge ${t.badge}`}>{t.label}</span>
                    <span style={{fontFamily:"PT Serif,serif",fontSize:22,fontWeight:700}}>{c}</span>
                    <span style={{fontSize:11,color:"var(--muted)",fontFamily:"Lato,sans-serif"}}>{pct}%</span>
                  </div>
                );
              })}
            </div>
          </>
        )}
      </div>
    </div>
  );
}

// ── ADMIN USUARIOS ────────────────────────────────────────────────────────────
function AdminUsuarios({usuarios,pesos=DEFAULT_PESOS,competencias=DEFAULT_COMPETENCIAS,onSavePesos,onSaveComps,onAdd,onToggle,onDel}){
  const [pesosLocal,setPesosLocal]=useState(pesos);
  const [compsLocal,setCompsLocal]=useState(competencias);
  const updateComp=(id,field,val)=>setCompsLocal(cs=>cs.map(c=>c.id===id?{...c,[field]:val}:c));
  const addComp=()=>setCompsLocal(cs=>[...cs,{id:`c${Date.now()}`,nombre:"Nueva competencia",descripcion:""}]);
  const delComp=id=>setCompsLocal(cs=>cs.filter(c=>c.id!==id));
  const PESO_ITEMS=[
    {key:"olvido",        label:"Olvido recurrente",                    badge:"badge-rust",  group:"inc"},
    {key:"no_consulto",   label:"No consultó recursos",                 badge:"badge-amber", group:"inc"},
    {key:"error_proceso", label:"Error de proceso",                     badge:"badge-rust",  group:"inc"},
    {key:"escalacion",    label:"Escalación innecesaria",               badge:"badge-amber", group:"inc"},
    {key:"incumplimiento",label:"Incumplimiento de instrucción",        badge:"badge-rust",  group:"inc"},
    {key:"retraso",       label:"Retraso injustificado",                badge:"badge-ink",   group:"inc"},
    {key:"aviso_formal",  label:"🟠 Aviso Formal — genera incidencia", badge:"badge-amber", group:"aviso"},
    {key:"amonestacion",  label:"🔴 Amonestación — genera incidencia", badge:"badge-rust",  group:"aviso"},
  ];

  return(
    <div className="stack">
      <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",flexWrap:"wrap",gap:12}}>
        <h2 style={{fontFamily:"PT Serif,serif",fontSize:22,fontWeight:700}}>Gestión de usuarios ({usuarios.length})</h2>
        <button className="btn btn-primary" onClick={onAdd}>+ Crear usuario</button>
      </div>

      {/* Panel de pesos */}
      <div className="card">
        <div className="card-title">⚖ Configuración de pesos — Escala de severidad</div>
        <div style={{fontSize:12,color:"var(--muted)",marginBottom:12,lineHeight:1.6}}>
          Define cuántos puntos suma cada tipo de evento. El nivel se calcula sobre el puntaje acumulado en el periodo activo.
        </div>
        <div style={{display:"flex",gap:8,flexWrap:"wrap",marginBottom:16}}>
          {UMBRALES.map(u=><span key={u.label} className={`badge ${u.badge}`}>{u.label} {u.min}–{u.max===999?"11+":u.max} pts</span>)}
        </div>
        {/* Nota: Friendly Reminder */}
        <div style={{background:"var(--warn-lt)",border:"1px solid var(--warn)",borderRadius:4,padding:"8px 14px",marginBottom:16,fontSize:12,color:"#7a5a00",display:"flex",alignItems:"flex-start",gap:8}}>
          <span style={{fontSize:16,flexShrink:0}}>💛</span>
          <span><strong>💛 Friendly Reminder</strong> — genera incidencia de tipo «Olvido recurrente» como registro histórico, pero <strong>no suma puntos</strong> al score de desempeño. Por eso no aparece en la tabla de pesos.</span>
        </div>
        <div style={{fontSize:11,fontWeight:700,color:"var(--muted)",textTransform:"uppercase",letterSpacing:.8,fontFamily:"Lato,sans-serif",marginBottom:8}}>Incidencias registradas manualmente</div>
        <div style={{display:"grid",gridTemplateColumns:"repeat(2,1fr)",gap:10,marginBottom:16}}>
          {PESO_ITEMS.filter(i=>i.group==="inc").map(item=>(            <div key={item.key} style={{display:"flex",alignItems:"center",justifyContent:"space-between",background:"var(--gris-lt)",borderRadius:4,padding:"10px 14px",border:"1px solid var(--border)"}}>
              <span className={`badge ${item.badge}`} style={{fontSize:10}}>{item.label}</span>
              <div style={{display:"flex",alignItems:"center",gap:8}}>
                <button className="btn btn-sm btn-secondary" style={{padding:"4px 8px",fontSize:14,lineHeight:1}} onClick={()=>setPesosLocal(p=>({...p,[item.key]:Math.max(1,p[item.key]-1)}))}>−</button>
                <span style={{fontFamily:"PT Serif,serif",fontSize:22,fontWeight:700,minWidth:24,textAlign:"center",color:"var(--danger)"}}>{pesosLocal[item.key]||1}</span>
                <button className="btn btn-sm btn-secondary" style={{padding:"4px 8px",fontSize:14,lineHeight:1}} onClick={()=>setPesosLocal(p=>({...p,[item.key]:(p[item.key]||1)+1}))}>+</button>
              </div>
            </div>))}
        </div>
        <div style={{fontSize:11,fontWeight:700,color:"var(--muted)",textTransform:"uppercase",letterSpacing:.8,fontFamily:"Lato,sans-serif",marginBottom:8}}>Avisos que generan incidencia automática</div>
        <div style={{display:"grid",gridTemplateColumns:"repeat(2,1fr)",gap:10,marginBottom:16}}>
          {PESO_ITEMS.filter(i=>i.group==="aviso").map(item=>(
            <div key={item.key} style={{display:"flex",alignItems:"center",justifyContent:"space-between",background:"var(--gris-lt)",borderRadius:4,padding:"10px 14px",border:"1px solid var(--border)"}}>
              <span className={`badge ${item.badge}`}>{item.label}</span>
              <div style={{display:"flex",alignItems:"center",gap:8}}>
                <button className="btn btn-sm btn-secondary" style={{padding:"4px 8px",fontSize:14,lineHeight:1}} onClick={()=>setPesosLocal(p=>({...p,[item.key]:Math.max(1,p[item.key]-1)}))}>−</button>
                <span style={{fontFamily:"PT Serif,serif",fontSize:22,fontWeight:700,minWidth:24,textAlign:"center",color:"var(--danger)"}}>{pesosLocal[item.key]||1}</span>
                <button className="btn btn-sm btn-secondary" style={{padding:"4px 8px",fontSize:14,lineHeight:1}} onClick={()=>setPesosLocal(p=>({...p,[item.key]:(p[item.key]||1)+1}))}>+</button>
              </div>
            </div>
          ))}
        </div>
        <div style={{display:"flex",gap:10}}>
          <button className="btn btn-primary" onClick={()=>onSavePesos(pesosLocal)}>Guardar pesos</button>
          <button className="btn btn-secondary" onClick={()=>setPesosLocal(DEFAULT_PESOS)}>Restaurar predeterminados</button>
        </div>
      </div>

      {/* Panel de competencias */}
      <div className="card">
        <div className="card-title">📋 Competencias evaluables — Configuración</div>
        <div style={{fontSize:12,color:"var(--muted)",marginBottom:16}}>
          Define las competencias que se evalúan en cada persona. Escala 1–5: Deficiente · Regular · Aceptable · Bueno · Excelente.
        </div>
        <div className="stack">
          {compsLocal.map((comp,idx)=>(
            <div key={comp.id} style={{display:"grid",gridTemplateColumns:"1fr 2fr auto",gap:10,alignItems:"start",padding:"12px 14px",background:"var(--gris-lt)",borderRadius:4,border:"1px solid var(--border)"}}>
              <div className="form-group">
                <label className="form-label">Nombre</label>
                <input className="form-input" value={comp.nombre} onChange={e=>updateComp(comp.id,"nombre",e.target.value)}/>
              </div>
              <div className="form-group">
                <label className="form-label">Descripción</label>
                <input className="form-input" value={comp.descripcion} onChange={e=>updateComp(comp.id,"descripcion",e.target.value)}/>
              </div>
              <button className="btn btn-sm btn-secondary" style={{color:"var(--danger)",marginTop:22}} onClick={()=>delComp(comp.id)}>✕</button>
            </div>
          ))}
        </div>
        <div style={{display:"flex",gap:10,marginTop:12}}>
          <button className="btn btn-primary" onClick={()=>onSaveComps(compsLocal)}>Guardar competencias</button>
          <button className="btn btn-secondary" onClick={addComp}>+ Agregar competencia</button>
          <button className="btn btn-secondary" onClick={()=>setCompsLocal(DEFAULT_COMPETENCIAS)}>Restaurar predeterminadas</button>
        </div>
      </div>
      {["superadmin","capacitador","observador"].map(rol=>{
        const grupo=usuarios.filter(u=>u.rol===rol);
        if(!grupo.length)return null;
        return(
          <div key={rol} className="card">
            <div className="card-title">
              <span className={`badge ${ROLE_BADGES[rol]}`}>{ROLES[rol]}</span>
              <span style={{fontSize:14,fontWeight:400,color:"var(--muted)"}}>{grupo.length} usuario(s)</span>
            </div>
            <div className="stack">
              {grupo.map(u=>(
                <div key={u.id} className="user-card">
                  <div className="avatar" style={{background:ROLE_COLORS[u.rol]}}>{initials(u.nombre)}</div>
                  <div className="user-card-info">
                    <div className="user-card-name">{u.nombre}</div>
                    <div className="user-card-email">{u.email}</div>
                    <div style={{fontSize:11,color:"var(--muted)",fontFamily:"Lato,sans-serif",marginTop:2}}>{u.categoria} · Creado {fmtDate(u.createdAt)}</div>
                  </div>
                  <span className={`badge ${u.activo?"badge-sage":"badge-ink"}`}>{u.activo?"Activo":"Inactivo"}</span>
                  <div style={{display:"flex",gap:8}}>
                    <button className="btn btn-sm btn-secondary" onClick={()=>onToggle(u.id)}>{u.activo?"Desactivar":"Activar"}</button>
                    {u.rol!=="superadmin"&&<button className="btn btn-sm btn-secondary" style={{color:"var(--danger)"}} onClick={()=>{ if(window.confirm(`¿Eliminar a ${u.nombre}?`))onDel(u.id); }}>Eliminar</button>}
                  </div>
                </div>
              ))}
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ── MODALES ───────────────────────────────────────────────────────────────────
function ModalPersona({onClose,onSave,personas}){
  const [form,setForm]=useState({nombre:"",sede:"",pais:"México",area:"",cargo:""});
  const set=(k,v)=>setForm(f=>({...f,[k]:v}));
  const [confirmando,setConfirmando]=useState(false);

  const duplicados = form.nombre.length>=3 ? buscarDuplicados(form.nombre, personas) : [];
  const bloqueado  = duplicados.some(d=>d.sim>=0.90);
  const advertencia= !bloqueado && duplicados.some(d=>d.sim>=0.70);

  const handleGuardar = () => {
    if(!form.nombre||!form.sede) return;
    if(bloqueado) return;
    if(advertencia&&!confirmando){ setConfirmando(true); return; }
    onSave(form);
  };

  return(
    <div className="modal-overlay" onClick={onClose}><div className="modal" onClick={e=>e.stopPropagation()}>
      <button className="modal-close" onClick={onClose}>✕</button>
      <div className="modal-title">Registrar persona</div>
      <div className="stack">
        <div className="form-row">
          <div className="form-group">
            <label className="form-label">Nombre completo *</label>
            <input className="form-input" value={form.nombre} onChange={e=>{ set("nombre",e.target.value); setConfirmando(false); }} placeholder="Ej. María González"
              style={{borderColor: bloqueado?"var(--danger)":advertencia?"var(--warn)":"var(--border)"}}/>
          </div>
          <div className="form-group"><label className="form-label">Sede *</label><input className="form-input" value={form.sede} onChange={e=>set("sede",e.target.value)} placeholder="Ej. Campus Sur Guadalajara"/></div>
        </div>

        {/* Alerta de duplicados */}
        {duplicados.length>0&&(
          <div style={{background: bloqueado?"var(--rust-lt)":"var(--amber-lt)", border:`1px solid ${bloqueado?"var(--danger)":"var(--warn)"}`, borderRadius:6, padding:"12px 16px"}}>
            <div style={{fontWeight:700,fontSize:13,color:bloqueado?"var(--danger)":"var(--warn)",marginBottom:8,fontFamily:"Lato,sans-serif",textTransform:"uppercase",letterSpacing:.4}}>
              {bloqueado?"🚫 Posible duplicado — registro bloqueado":"⚠ Nombres similares encontrados"}
            </div>
            {duplicados.map(d=>(
              <div key={d.id} style={{display:"flex",alignItems:"center",gap:10,padding:"6px 0",borderBottom:"1px solid #00000011"}}>
                <div style={{fontFamily:"Lato,sans-serif",fontSize:11,background:bloqueado?"var(--danger)":"var(--warn)",color:"#fff",borderRadius:3,padding:"2px 6px",minWidth:36,textAlign:"center"}}>
                  {Math.round(d.sim*100)}%
                </div>
                <div>
                  <div style={{fontWeight:600,fontSize:13}}>{d.nombre}</div>
                  <div style={{fontSize:11,color:"var(--muted)",fontFamily:"Lato,sans-serif"}}>{d.sede} · {d.pais}{d.cargo?` · ${d.cargo}`:""}</div>
                </div>
              </div>
            ))}
            {bloqueado&&<div style={{fontSize:12,color:"var(--danger)",marginTop:8}}>Corrige el nombre o verifica si ya existe en el sistema.</div>}
            {advertencia&&!confirmando&&<div style={{fontSize:12,color:"var(--warn)",marginTop:8}}>¿Es realmente una persona diferente? Haz clic en "Guardar de todas formas" para continuar.</div>}
            {advertencia&&confirmando&&<div style={{fontSize:12,color:"var(--warn)",marginTop:8,fontWeight:600}}>Confirma haciendo clic en "Guardar de todas formas" nuevamente.</div>}
          </div>
        )}

        <div className="form-row">
          <div className="form-group"><label className="form-label">País *</label><select className="form-select" value={form.pais} onChange={e=>set("pais",e.target.value)}>{PAISES.map(p=><option key={p}>{p}</option>)}</select></div>
          <div className="form-group"><label className="form-label">Área</label><input className="form-input" value={form.area} onChange={e=>set("area",e.target.value)} placeholder="Ej. Servicios Escolares"/></div>
        </div>
        <div className="form-group"><label className="form-label">Cargo</label><input className="form-input" value={form.cargo} onChange={e=>set("cargo",e.target.value)} placeholder="Ej. Coordinadora académica"/></div>
        <div className="btn-group">
          <button className="btn btn-primary" onClick={handleGuardar} disabled={bloqueado}
            style={{opacity:bloqueado?.5:1}}>
            {advertencia&&!confirmando?"⚠ Guardar de todas formas":confirmando?"✓ Sí, es persona diferente":"Guardar"}
          </button>
          <button className="btn btn-secondary" onClick={onClose}>Cancelar</button>
        </div>
      </div>
    </div></div>
  );
}

function ModalCap({onClose,onSave,personas,usuarios,session}){
  const [form,setForm]=useState({personaIds:[],tema:"",fecha:today(),impartidaPorId:session.id,notas:"",evidencia:"Sin evidencia",linkGrabacion:"",imagenes:[]});
  const set=(k,v)=>setForm(f=>({...f,[k]:v}));
  const caps=usuarios.filter(u=>u.activo);
  const [busqueda,setBusqueda]=useState("");

  const togglePersona = pid => {
    const already = form.personaIds.includes(pid);
    set("personaIds", already ? form.personaIds.filter(id=>id!==pid) : [...form.personaIds, pid]);
  };

  const personasFiltradas = personas.filter(p =>
    p.nombre.toLowerCase().includes(busqueda.toLowerCase()) ||
    p.sede.toLowerCase().includes(busqueda.toLowerCase())
  );

  return(
    <div className="modal-overlay" onClick={onClose}><div className="modal" onClick={e=>e.stopPropagation()}>
      <button className="modal-close" onClick={onClose}>✕</button>
      <div className="modal-title">Registrar capacitación</div>
      <div className="stack">

        {/* Selector múltiple de personas */}
        <div className="form-group">
          <label className="form-label">Personas capacitadas *</label>
          <input className="form-input" placeholder="Buscar persona o sede…" value={busqueda} onChange={e=>setBusqueda(e.target.value)} style={{marginBottom:8}}/>
          <div style={{border:"1px solid var(--border)",borderRadius:4,maxHeight:180,overflowY:"auto",background:"var(--papel)"}}>
            {personasFiltradas.length===0 && <div style={{padding:"12px 14px",fontSize:13,color:"var(--muted)"}}>Sin resultados</div>}
            {personasFiltradas.map(p=>{
              const sel=form.personaIds.includes(p.id);
              return(
                <div key={p.id} onClick={()=>togglePersona(p.id)}
                  style={{display:"flex",alignItems:"center",gap:10,padding:"9px 14px",cursor:"pointer",borderBottom:"1px solid var(--border)",background:sel?"var(--sage-lt)":"transparent",transition:"background .1s"}}>
                  <div style={{width:16,height:16,borderRadius:3,border:`2px solid ${sel?"var(--ok)":"var(--border)"}`,background:sel?"var(--ok)":"transparent",flexShrink:0,display:"flex",alignItems:"center",justifyContent:"center"}}>
                    {sel&&<span style={{color:"#fff",fontSize:11,lineHeight:1}}>✓</span>}
                  </div>
                  <div>
                    <div style={{fontSize:13,fontWeight:sel?600:400}}>{p.nombre}</div>
                    <div style={{fontSize:11,color:"var(--muted)",fontFamily:"Lato,sans-serif"}}>{p.sede} · {p.pais}</div>
                  </div>
                </div>
              );
            })}
          </div>
          {form.personaIds.length>0&&(
            <div style={{display:"flex",gap:6,flexWrap:"wrap",marginTop:6}}>
              {form.personaIds.map(pid=>{ const p=personas.find(x=>x.id===pid); return p?(
                <span key={pid} className="badge badge-sage" style={{cursor:"pointer"}} onClick={()=>togglePersona(pid)}>
                  {p.nombre.split(" ")[0]} ✕
                </span>
              ):null;})}
            </div>
          )}
          {form.personaIds.length>0&&<div style={{fontSize:11,color:"var(--ok)",fontFamily:"Lato,sans-serif",marginTop:4}}>✦ Se crearán {form.personaIds.length} registro(s) individual(es)</div>}
        </div>

        <div className="form-row">
          <div className="form-group"><label className="form-label">Fecha *</label><input type="date" className="form-input" value={form.fecha} onChange={e=>set("fecha",e.target.value)}/></div>
          <div className="form-group"><label className="form-label">Evidencia</label>
            <select className="form-select" value={form.evidencia} onChange={e=>set("evidencia",e.target.value)}>
              {EVIDENCIAS.map(e=><option key={e}>{e}</option>)}
            </select>
          </div>
        </div>
        <div className="form-group"><label className="form-label">Tema / Contenido *</label><input className="form-input" value={form.tema} onChange={e=>set("tema",e.target.value)} placeholder="Ej. Proceso de inscripción en plataforma"/></div>
        <div className="form-group"><label className="form-label">Impartida por *</label>
          <select className="form-select" value={form.impartidaPorId} onChange={e=>set("impartidaPorId",e.target.value)}>
            {caps.map(u=><option key={u.id} value={u.id}>{u.nombre} ({u.categoria})</option>)}
          </select>
        </div>
        <div className="form-group"><label className="form-label">Notas adicionales</label><textarea className="form-textarea" value={form.notas} onChange={e=>set("notas",e.target.value)} placeholder="Detalles de lo explicado…"/></div>
        <div className="form-group">
          <label className="form-label">Link de grabación / recurso</label>
          <input className="form-input" value={form.linkGrabacion} onChange={e=>set("linkGrabacion",e.target.value)} placeholder="https://drive.google.com/… o Meet, Zoom, etc."/>
        </div>
        <div className="form-group">
          <label className="form-label">Imágenes / capturas</label>
          <input type="file" accept="image/*" multiple id="cap-img-input" style={{display:"none"}}
            onChange={e=>{ const files=Array.from(e.target.files); Promise.all(files.map(f=>new Promise(res=>{ const r=new FileReader(); r.onload=()=>res({nombre:f.name,base64:r.result}); r.readAsDataURL(f); }))).then(imgs=>set("imagenes",[...(form.imagenes||[]),...imgs])); }}/>
          <label htmlFor="cap-img-input" className="btn btn-secondary" style={{display:"inline-flex",alignItems:"center",gap:6,cursor:"pointer",width:"fit-content"}}>📎 Adjuntar imágenes</label>
          {form.imagenes&&form.imagenes.length>0&&(
            <div style={{display:"flex",gap:10,flexWrap:"wrap",marginTop:10}}>
              {form.imagenes.map((img,i)=>(
                <div key={i} style={{position:"relative"}}>
                  <img src={img.base64} alt="" style={{width:80,height:80,objectFit:"cover",borderRadius:4,border:"1px solid var(--border)"}}/>
                  <button onClick={()=>set("imagenes",form.imagenes.filter((_,j)=>j!==i))} style={{position:"absolute",top:-6,right:-6,background:"var(--danger)",color:"#fff",border:"none",borderRadius:"50%",width:18,height:18,fontSize:10,cursor:"pointer",lineHeight:1}}>✕</button>
                  <div style={{fontSize:10,color:"var(--muted)",marginTop:2,maxWidth:80,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}}>{img.nombre}</div>
                </div>
              ))}
            </div>
          )}
        </div>
        <div className="btn-group">
          <button className="btn btn-sage" onClick={()=>{ if(form.personaIds.length>0&&form.tema)onSave({...form}); }}>
            Guardar {form.personaIds.length>1?`(${form.personaIds.length} personas)`:""}
          </button>
          <button className="btn btn-secondary" onClick={onClose}>Cancelar</button>
        </div>
      </div>
    </div></div>
  );
}

function ModalInc({onClose,onSave,personas,caps,session}){
  const [form,setForm]=useState({personaId:"",tipo:"",fecha:today(),descripcion:"",capacitacionId:"",registradaPor:session.nombre});
  const set=(k,v)=>setForm(f=>({...f,[k]:v}));
  const capsP=caps.filter(c=>c.personaId===form.personaId);
  return(
    <div className="modal-overlay" onClick={onClose}><div className="modal" onClick={e=>e.stopPropagation()}>
      <button className="modal-close" onClick={onClose}>✕</button>
      <div className="modal-title">Registrar incidencia</div>
      <div className="stack">
        <div className="form-row">
          <div className="form-group"><label className="form-label">Persona *</label>
            <select className="form-select" value={form.personaId} onChange={e=>set("personaId",e.target.value)}>
              <option value="">Seleccionar…</option>
              {personas.map(p=><option key={p.id} value={p.id}>{p.nombre} — {p.sede}</option>)}
            </select>
          </div>
          <div className="form-group"><label className="form-label">Fecha *</label><input type="date" className="form-input" value={form.fecha} onChange={e=>set("fecha",e.target.value)}/></div>
        </div>
        <div className="form-group"><label className="form-label">Tipo de incidencia *</label>
          <select className="form-select" value={form.tipo} onChange={e=>set("tipo",e.target.value)}>
            <option value="">Seleccionar tipo…</option>
            {TIPOS_INC.map(t=><option key={t.value} value={t.value}>{t.label}</option>)}
          </select>
        </div>
        {form.personaId&&capsP.length>0&&(
          <div className="form-group"><label className="form-label">Capacitación previa relacionada</label>
            <select className="form-select" value={form.capacitacionId} onChange={e=>set("capacitacionId",e.target.value)}>
              <option value="">Sin vinculación</option>
              {capsP.map(c=><option key={c.id} value={c.id}>{fmtDate(c.fecha)} — {c.tema}</option>)}
            </select>
            {form.capacitacionId&&<div style={{fontSize:12,color:"var(--danger)",marginTop:4,fontFamily:"Lato,sans-serif"}}>⚠ Incidencia vinculada a capacitación documentada.</div>}
          </div>
        )}
        <div className="form-group"><label className="form-label">Descripción *</label><textarea className="form-textarea" value={form.descripcion} onChange={e=>set("descripcion",e.target.value)} placeholder="Describe qué ocurrió y por qué constituye una incidencia…"/></div>
        <div className="form-group"><label className="form-label">Registrada por</label><input className="form-input" value={form.registradaPor} onChange={e=>set("registradaPor",e.target.value)}/></div>
        <div className="btn-group">
          <button className="btn btn-primary" onClick={()=>{ if(form.personaId&&form.tipo&&form.descripcion)onSave(form); }}>Guardar incidencia</button>
          <button className="btn btn-secondary" onClick={onClose}>Cancelar</button>
        </div>
      </div>
    </div></div>
  );
}

function ModalUsuario({onClose,onSave}){
  const [form,setForm]=useState({nombre:"",email:"",rol:"capacitador",categoria:"TI"});
  const set=(k,v)=>setForm(f=>({...f,[k]:v}));
  const desc={superadmin:"Ve todo el sistema, gestiona usuarios y reportes.",capacitador:"Ve y registra solo las personas que él/ella ha capacitado.",observador:"Solo lectura. No puede registrar nada."};
  return(
    <div className="modal-overlay" onClick={onClose}><div className="modal" onClick={e=>e.stopPropagation()}>
      <button className="modal-close" onClick={onClose}>✕</button>
      <div className="modal-title">Crear usuario</div>
      <div style={{background:"var(--gris-lt)",border:"1px solid var(--border)",borderRadius:4,padding:"10px 14px",fontSize:12,color:"var(--rojo-deep)",fontFamily:"Lato,sans-serif",marginBottom:16}}>
        El usuario accederá con su cuenta Google institucional (@usantander.edu.mx)
      </div>
      <div className="stack">
        <div className="form-group"><label className="form-label">Nombre completo *</label><input className="form-input" value={form.nombre} onChange={e=>set("nombre",e.target.value)} placeholder="Ej. Ing. Carlos Mendoza"/></div>
        <div className="form-group"><label className="form-label">Correo institucional *</label><input className="form-input" value={form.email} onChange={e=>set("email",e.target.value)} placeholder="usuario@usantander.edu.mx"/></div>
        <div className="form-row">
          <div className="form-group"><label className="form-label">Rol *</label>
            <select className="form-select" value={form.rol} onChange={e=>set("rol",e.target.value)}>
              <option value="capacitador">Capacitador</option>
              <option value="observador">Observador</option>
              <option value="superadmin">Super Admin</option>
            </select>
          </div>
          <div className="form-group"><label className="form-label">Área / Categoría</label>
            <select className="form-select" value={form.categoria} onChange={e=>set("categoria",e.target.value)}>
              {CATEGORIAS.map(c=><option key={c}>{c}</option>)}
            </select>
          </div>
        </div>
        <div className="hint-box">✦ {desc[form.rol]}</div>
        <div className="btn-group">
          <button className="btn btn-primary" onClick={()=>{ if(form.nombre&&form.email)onSave(form); }}>Crear usuario</button>
          <button className="btn btn-secondary" onClick={onClose}>Cancelar</button>
        </div>
      </div>
    </div></div>
  );
}

// ── NIVELES DE AVISO ──────────────────────────────────────────────────────────
const NIVELES_AVISO = [
  {value:"reminder", label:"Friendly Reminder", emoji:"💛", badge:"badge-amber", color:"var(--warn)", desc:"Recordatorio amable, sin consecuencias en el registro."},
  {value:"formal",   label:"Aviso Formal",       emoji:"🟠", badge:"badge-amber", color:"var(--warn)",    desc:"Advertencia institucional. Cuenta como incidencia leve automáticamente."},
  {value:"howler",   label:"Amonestación",           emoji:"🔴", badge:"badge-rust",  color:"#7f1d1d",      desc:"Notificación grave. Cuenta como incidencia por incumplimiento."},
];

// ── AVISOS / HOWLERS ──────────────────────────────────────────────────────────
function Avisos({avisos,personas,canEdit,onAdd,personaById}){
  const [filtroNivel,setFiltroNivel]=useState("");
  const sorted=[...avisos].sort((a,b)=>b.fecha.localeCompare(a.fecha));
  const filtrados=filtroNivel?sorted.filter(a=>a.nivel===filtroNivel):sorted;

  return(
    <div className="stack">
      <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",flexWrap:"wrap",gap:12}}>
        <h2 style={{fontFamily:"PT Serif,serif",fontSize:22}}>Avisos & Amonestaciones ({avisos.length})</h2>
        <div style={{display:"flex",gap:10,flexWrap:"wrap"}}>
          <select className="form-select" value={filtroNivel} onChange={e=>setFiltroNivel(e.target.value)} style={{width:200}}>
            <option value="">Todos los niveles</option>
            {NIVELES_AVISO.map(n=><option key={n.value} value={n.value}>{n.emoji} {n.label}</option>)}
          </select>
          {canEdit&&<button className="btn btn-primary" onClick={onAdd}>+ Enviar aviso</button>}
        </div>
      </div>

      {/* Stats rápidos */}
      <div style={{display:"grid",gridTemplateColumns:"repeat(3,1fr)",gap:12}}>
        {NIVELES_AVISO.map(n=>{ const c=avisos.filter(a=>a.nivel===n.value).length; return(
          <div key={n.value} className="stat-box" style={{borderLeft:`4px solid ${n.color}`}}>
            <span style={{fontFamily:"PT Serif,serif",fontSize:32,lineHeight:1,color:n.color}}>{c}</span>
            <span style={{fontSize:12,color:"var(--muted)",fontFamily:"Lato,sans-serif",textTransform:"uppercase",letterSpacing:.8}}>{n.emoji} {n.label}</span>
          </div>
        );})}
      </div>

      <div className="card">
        <div className="table-wrap">
          <table>
            <thead><tr><th>Fecha</th><th>Nivel</th><th>Persona</th><th>Sede</th><th>Motivo</th><th>Enviado por</th><th>Texto generado</th></tr></thead>
            <tbody>
              {filtrados.length===0?<tr><td colSpan={7}><p className="empty">Sin avisos registrados.</p></td></tr>:filtrados.map(a=>{
                const p=personaById(a.personaId);
                const nivel=NIVELES_AVISO.find(n=>n.value===a.nivel);
                return(
                  <tr key={a.id}>
                    <td style={{fontFamily:"Lato,sans-serif",fontSize:12}}>{fmtDate(a.fecha)}</td>
                    <td><span className={`badge ${nivel?.badge||"badge-ink"}`}>{nivel?.emoji} {nivel?.label}</span></td>
                    <td style={{fontWeight:600}}>{p?.nombre||a.personaNombre}</td>
                    <td style={{fontSize:12,color:"var(--muted)"}}>{p?`${p.sede} · ${p.pais}`:"—"}</td>
                    <td style={{fontSize:13,maxWidth:180}}>{a.motivo}</td>
                    <td style={{fontSize:12}}>{a.enviadoPor}</td>
                    <td style={{maxWidth:240}}>
                      {a.textoGenerado?(
                        <div style={{fontSize:12,background:"var(--gris-lt)",borderRadius:4,padding:"6px 10px",position:"relative"}}>
                          <span style={{color:"var(--muted)",fontFamily:"Lato,sans-serif",fontSize:10,display:"block",marginBottom:3}}>TEXTO LISTO PARA ENVIAR</span>
                          <span style={{fontSize:12}}>{a.textoGenerado.slice(0,100)}…</span>
                          <button className="btn btn-sm btn-secondary" style={{marginTop:6,width:"100%"}} onClick={()=>{ navigator.clipboard.writeText(a.textoGenerado); alert("Copiado al portapapeles"); }}>📋 Copiar</button>
                        </div>
                      ):<span style={{color:"var(--muted)",fontSize:12}}>—</span>}
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

// ── MODAL AVISO ───────────────────────────────────────────────────────────────
function ModalAviso({onClose,onSave,personas,caps,session}){
  const [form,setForm]=useState({personaId:"",nivel:"reminder",fecha:today(),motivo:"",capacitacionId:"",enviadoPor:session.nombre,textoGenerado:""});
  const set=(k,v)=>setForm(f=>({...f,[k]:v}));
  const [generating,setGenerating]=useState(false);
  const [preview,setPreview]=useState(false);

  const capsP=caps.filter(c=>c.personaId===form.personaId);
  const persona=personas.find(p=>p.id===form.personaId);
  const nivelInfo=NIVELES_AVISO.find(n=>n.value===form.nivel);

  const generarTexto=async()=>{
    if(!form.motivo||!form.personaId){ alert("Completa persona y motivo primero."); return; }
    setGenerating(true);
    const capRef=form.capacitacionId?caps.find(c=>c.id===form.capacitacionId):null;
    try{
      const resp=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:{"Content-Type":"application/json"},
        body:JSON.stringify({model:"claude-sonnet-4-20250514",max_tokens:500,
          system:`Eres asistente de comunicación institucional de la Vicerrectoría de Desarrollo Informático y Tecnología de Universidad Santander (Monterrey). Redactas mensajes para enviar a colaboradores de sedes sobre errores operativos o recordatorios de proceso. Adaptas el tono exacto según el nivel:
- friendly_reminder: amable, constructivo, informal pero profesional. Máx 3 oraciones.
- formal: institucional, directo, deja clara la gravedad. Máx 4 oraciones. Sin insultos.
- howler: firme, contundente, sin rodeos, deja claro que es inaceptable y que queda documentado. Máx 5 oraciones. Institucional pero con peso. Sin groserías.
Responde SOLO el texto del mensaje, listo para copiar y enviar. Sin saludos elaborados ni firma — solo el cuerpo del mensaje.`,
          messages:[{role:"user",content:`Nivel: ${form.nivel}
Persona: ${persona?.nombre||""} (${persona?.sede||""}, ${persona?.pais||""})
Motivo / situación: ${form.motivo}
${capRef?`Capacitación previa sobre este tema: "${capRef.tema}" impartida el ${fmtDate(capRef.fecha)}`:""}
Redacta el mensaje.`}]
        })
      });
      const data=await resp.json();
      const texto=data.content?.find(b=>b.type==="text")?.text||"No se pudo generar.";
      set("textoGenerado",texto);
      setPreview(true);
    }catch(e){ alert("Error al generar texto."); }
    setGenerating(false);
  };

  return(
    <div className="modal-overlay" onClick={onClose}><div className="modal" style={{maxWidth:680}} onClick={e=>e.stopPropagation()}>
      <button className="modal-close" onClick={onClose}>✕</button>
      <div className="modal-title">Enviar aviso</div>
      <div className="stack">

        {/* Selector de nivel */}
        <div className="form-group">
          <label className="form-label">Nivel de aviso *</label>
          <div style={{display:"grid",gridTemplateColumns:"repeat(3,1fr)",gap:10}}>
            {NIVELES_AVISO.map(n=>(
              <div key={n.value} onClick={()=>set("nivel",n.value)}
                style={{border:`2px solid ${form.nivel===n.value?n.color:"var(--border)"}`,borderRadius:6,padding:"12px 14px",cursor:"pointer",background:form.nivel===n.value?`${n.color}11`:"var(--blanco)",transition:"all .15s"}}>
                <div style={{fontSize:18,marginBottom:4}}>{n.emoji}</div>
                <div style={{fontWeight:700,fontSize:13,color:form.nivel===n.value?n.color:"var(--ink)"}}>{n.label}</div>
                <div style={{fontSize:11,color:"var(--muted)",marginTop:4,lineHeight:1.4}}>{n.desc}</div>
              </div>
            ))}
          </div>
        </div>

        <div className="form-row">
          <div className="form-group"><label className="form-label">Persona *</label>
            <select className="form-select" value={form.personaId} onChange={e=>set("personaId",e.target.value)}>
              <option value="">Seleccionar…</option>
              {personas.map(p=><option key={p.id} value={p.id}>{p.nombre} — {p.sede}</option>)}
            </select>
          </div>
          <div className="form-group"><label className="form-label">Fecha *</label>
            <input type="date" className="form-input" value={form.fecha} onChange={e=>set("fecha",e.target.value)}/>
          </div>
        </div>

        {form.personaId&&capsP.length>0&&(
          <div className="form-group"><label className="form-label">Capacitación previa relacionada</label>
            <select className="form-select" value={form.capacitacionId} onChange={e=>set("capacitacionId",e.target.value)}>
              <option value="">Sin vinculación</option>
              {capsP.map(c=><option key={c.id} value={c.id}>{fmtDate(c.fecha)} — {c.tema}</option>)}
            </select>
            {form.capacitacionId&&<div style={{fontSize:12,color:"var(--danger)",marginTop:4,fontFamily:"Lato,sans-serif"}}>⚠ El aviso referenciará esta capacitación documentada.</div>}
          </div>
        )}

        <div className="form-group">
          <label className="form-label">Motivo / situación *</label>
          <textarea className="form-textarea" value={form.motivo} onChange={e=>set("motivo",e.target.value)} placeholder="Describe qué pasó. La IA redactará el mensaje con el tono adecuado según el nivel seleccionado…" style={{minHeight:90}}/>
        </div>

        <div className="form-group"><label className="form-label">Enviado por</label>
          <input className="form-input" value={form.enviadoPor} onChange={e=>set("enviadoPor",e.target.value)}/>
        </div>

        {/* Botón generar */}
        <button className="btn btn-primary" onClick={generarTexto} disabled={generating} style={{background:nivelInfo?.color}}>
          {generating?"Generando mensaje…":`✦ Generar texto con IA (${nivelInfo?.label})`}
        </button>

        {/* Preview del texto generado */}
        {form.textoGenerado&&(
          <div style={{background:"var(--gris-lt)",border:`2px solid ${nivelInfo?.color||"var(--border)"}`,borderRadius:6,padding:20}}>
            <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:10}}>
              <span style={{fontFamily:"Lato,sans-serif",fontSize:11,textTransform:"uppercase",letterSpacing:.6,color:"var(--muted)"}}>Texto generado · listo para enviar</span>
              <button className="btn btn-sm btn-secondary" onClick={()=>{ navigator.clipboard.writeText(form.textoGenerado); }}>📋 Copiar</button>
            </div>
            <textarea className="form-textarea" value={form.textoGenerado} onChange={e=>set("textoGenerado",e.target.value)} style={{minHeight:120,fontSize:14,lineHeight:1.6,background:"var(--blanco)"}}/>
            <div style={{fontSize:11,color:"var(--muted)",marginTop:6,fontFamily:"Lato,sans-serif"}}>Puedes editar el texto antes de guardar.</div>
          </div>
        )}

        <div style={{background: form.nivel==="reminder"?"var(--warn-lt)":"var(--rust-lt)",
          border:`1px solid ${form.nivel==="reminder"?"var(--warn)":"var(--danger)"}`,
          borderRadius:4,padding:"10px 14px",fontSize:12,
          color:form.nivel==="reminder"?"#7a5a00":"var(--danger)",fontFamily:"Lato,sans-serif"}}>
          {form.nivel==="reminder"
            ? "💛 El Friendly Reminder generará una incidencia de tipo «Olvido recurrente» en el registro de " + (persona?.nombre||"esta persona") + ", pero NO sumará puntos al score."
            : `⚠ Este aviso generará automáticamente una incidencia que sumará puntos al score de ${persona?.nombre||"esta persona"}.`}
        </div>

        <div className="btn-group">
          <button className="btn btn-primary" style={{background:nivelInfo?.color}} onClick={()=>{ if(form.personaId&&form.nivel&&form.motivo)onSave(form); }}>
            Registrar y guardar
          </button>
          <button className="btn btn-secondary" onClick={onClose}>Cancelar</button>
        </div>
      </div>
    </div></div>
  );
}

// ══════════════════════════════════════════════════════════════════════════════
// EVALUACIONES
// ══════════════════════════════════════════════════════════════════════════════
function Evaluaciones({personas,logros,evaluaciones,competencias,canEdit,onAddLogro,onAddEval,logrosDePersona,evalsDePersona,incsDePersona,avisosDePersona,capsDePersona,pesos,periodo}){
  const [vista,setVista]=useState("resumen"); // resumen | logros | historial
  const [filtroPers,setFiltroPers]=useState("");

  const personasFiltradas=personas.filter(p=>p.nombre.toLowerCase().includes(filtroPers.toLowerCase())||p.sede.toLowerCase().includes(filtroPers.toLowerCase()));

  // Score integrado: competencias promedio (0-100) menos penalización por incidencias
  const scoreIntegrado=(pid)=>{
    const evs=evalsDePersona(pid);
    const is=incsDePersona(pid)||[];
    const avs=avisosDePersona?avisosDePersona(pid)||[]:[];
    if(evs.length===0) return null;
    const ultima=evs.sort((a,b)=>b.createdAt.localeCompare(a.createdAt))[0];
    const promedioComp=Object.values(ultima.calificaciones||{}).reduce((s,v)=>s+(v||0),0)/Math.max(Object.keys(ultima.calificaciones||{}).length,1);
    const compScore=Math.round((promedioComp/5)*100);
    const {score:incScore}=calcScore(is,avs,pesos,365);
    const penalizacion=Math.min(incScore*3,40); // máx 40 puntos de penalización
    return Math.max(0,compScore-penalizacion);
  };

  const nivelDesempeno=(score)=>{
    if(score===null) return {label:"Sin evaluar",badge:"badge-ink",color:"var(--muted)"};
    if(score>=85) return {label:"Sobresaliente",badge:"badge-sage",color:"var(--ok)"};
    if(score>=70) return {label:"Satisfactorio",badge:"badge-blue",color:"#1d4ed8"};
    if(score>=50) return {label:"En desarrollo",badge:"badge-amber",color:"var(--warn)"};
    return {label:"Requiere atención",badge:"badge-rust",color:"var(--danger)"};
  };

  return(
    <div className="stack">
      <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",flexWrap:"wrap",gap:12}}>
        <h2 style={{fontFamily:"PT Serif,serif",fontSize:22,fontWeight:700}}>Evaluaciones de desempeño</h2>
        <div style={{display:"flex",gap:10,flexWrap:"wrap"}}>
          {canEdit&&<button className="btn btn-sage" onClick={onAddLogro}>+ Registrar logro</button>}
          {canEdit&&<button className="btn btn-primary" onClick={onAddEval}>+ Nueva evaluación</button>}
        </div>
      </div>

      {/* Sub-navegación */}
      <div style={{display:"flex",gap:0,borderBottom:"1px solid var(--border)"}}>
        {[["resumen","Resumen por persona"],["logros","Logros registrados"],["historial","Historial de evaluaciones"]].map(([v,l])=>(
          <button key={v} onClick={()=>setVista(v)}
            style={{background:"none",border:"none",padding:"10px 20px",cursor:"pointer",fontFamily:"Lato,sans-serif",fontSize:12,fontWeight:700,textTransform:"uppercase",letterSpacing:.6,
              color:vista===v?"var(--rojo)":"var(--muted)",borderBottom:`3px solid ${vista===v?"var(--dorado)":"transparent"}`,transition:"all .15s"}}>
            {l}
          </button>
        ))}
      </div>

      {/* VISTA: RESUMEN */}
      {vista==="resumen"&&(
        <div className="stack">
          <input className="form-input" placeholder="Buscar persona o sede…" value={filtroPers} onChange={e=>setFiltroPers(e.target.value)} style={{maxWidth:320}}/>
          {personasFiltradas.length===0?<p className="empty">Sin personas registradas.</p>:
          <div className="grid-2">
            {personasFiltradas.map(p=>{
              const si=scoreIntegrado(p.id);
              const nivel=nivelDesempeno(si);
              const ls=logrosDePersona(p.id);
              const evs=evalsDePersona(p.id);
              const is=incsDePersona(p.id);
              const ultimaEval=evs.sort((a,b)=>b.createdAt.localeCompare(a.createdAt))[0];
              return(
                <div key={p.id} className="persona-card" style={{borderLeft:`4px solid ${nivel.color}`}}>
                  <div style={{display:"flex",justifyContent:"space-between",alignItems:"flex-start"}}>
                    <div>
                      <div style={{fontWeight:700,fontSize:15}}>{p.nombre}</div>
                      <div style={{fontSize:12,color:"var(--muted)",fontFamily:"Lato,sans-serif"}}>{p.sede} · {p.pais}</div>
                    </div>
                    <div style={{textAlign:"right"}}>
                      <span className={`badge ${nivel.badge}`}>{nivel.label}</span>
                      {si!==null&&<div style={{fontFamily:"PT Serif,serif",fontSize:28,fontWeight:700,color:nivel.color,lineHeight:1,marginTop:4}}>{si}</div>}
                      {si!==null&&<div style={{fontSize:9,color:"var(--muted)",textTransform:"uppercase",letterSpacing:.5,fontFamily:"Lato,sans-serif"}}>score integrado</div>}
                    </div>
                  </div>
                  <div className="persona-stats" style={{marginTop:14}}>
                    <div className="persona-stat"><span className="persona-stat-n" style={{color:"var(--ok)"}}>{ls.length}</span><span className="persona-stat-l">Logros</span></div>
                    <div className="persona-stat"><span className="persona-stat-n" style={{color:"var(--danger)"}}>{is.length}</span><span className="persona-stat-l">Inc.</span></div>
                    <div className="persona-stat"><span className="persona-stat-n" style={{color:"#1d4ed8"}}>{evs.length}</span><span className="persona-stat-l">Evals.</span></div>
                    {ultimaEval&&<div className="persona-stat"><span className="persona-stat-n" style={{fontSize:11,fontFamily:"Lato,sans-serif"}}>{fmtDate(ultimaEval.createdAt)}</span><span className="persona-stat-l">Última eval.</span></div>}
                  </div>
                  {ultimaEval&&(
                    <div style={{marginTop:12,background:"var(--gris-lt)",borderRadius:4,padding:"8px 12px"}}>
                      <div style={{fontSize:10,fontWeight:700,color:"var(--muted)",textTransform:"uppercase",letterSpacing:.8,fontFamily:"Lato,sans-serif",marginBottom:6}}>Última evaluación — competencias</div>
                      <div style={{display:"flex",gap:6,flexWrap:"wrap"}}>
                        {Object.entries(ultimaEval.calificaciones||{}).map(([cid,val])=>{
                          const comp=competencias.find(c=>c.id===cid);
                          const esc=ESCALA_COMP.find(e=>e.valor===val);
                          return comp?(
                            <div key={cid} style={{fontSize:11,display:"flex",alignItems:"center",gap:4}}>
                              <span style={{color:"var(--muted)"}}>{comp.nombre.split(" ")[0]}:</span>
                              <span style={{fontWeight:700,color:esc?.color||"var(--ink)"}}>{val}/5</span>
                            </div>
                          ):null;
                        })}
                      </div>
                    </div>
                  )}
                </div>
              );
            })}
          </div>}
        </div>
      )}

      {/* VISTA: LOGROS */}
      {vista==="logros"&&(
        <div className="card">
          <div className="table-wrap">
            <table>
              <thead><tr><th>Fecha</th><th>Persona</th><th>Sede</th><th>Tipo de logro</th><th>Descripción</th><th>Pts +</th><th>Registrado por</th></tr></thead>
              <tbody>
                {logros.length===0?<tr><td colSpan={7}><p className="empty">Sin logros registrados.</p></td></tr>:
                [...logros].sort((a,b)=>b.fecha.localeCompare(a.fecha)).map(l=>{
                  const p=personas.find(x=>x.id===l.personaId);
                  const tipo=TIPOS_LOGRO.find(t=>t.value===l.tipo);
                  return(
                    <tr key={l.id}>
                      <td style={{fontFamily:"Lato,monospace",fontSize:12}}>{fmtDate(l.fecha)}</td>
                      <td style={{fontWeight:700}}>{p?.nombre||l.personaNombre}</td>
                      <td style={{fontSize:12,color:"var(--muted)"}}>{p?`${p.sede} · ${p.pais}`:"—"}</td>
                      <td><span className={`badge ${tipo?.badge||"badge-sage"}`}>{tipo?.label||l.tipo}</span></td>
                      <td style={{fontSize:13,maxWidth:220}}>{l.descripcion}</td>
                      <td style={{fontFamily:"PT Serif,serif",fontSize:18,fontWeight:700,color:"var(--ok)",textAlign:"center"}}>+{tipo?.puntos||1}</td>
                      <td style={{fontSize:12}}>{l.registradoPor||"—"}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </div>
      )}

      {/* VISTA: HISTORIAL */}
      {vista==="historial"&&(
        <div className="card">
          <div className="table-wrap">
            <table>
              <thead><tr><th>Fecha</th><th>Persona</th><th>Sede</th><th>Periodo eval.</th><th>Promedio comp.</th><th>Score integrado</th><th>Nivel</th><th>Evaluador</th></tr></thead>
              <tbody>
                {evaluaciones.length===0?<tr><td colSpan={8}><p className="empty">Sin evaluaciones registradas.</p></td></tr>:
                [...evaluaciones].sort((a,b)=>b.createdAt.localeCompare(a.createdAt)).map(e=>{
                  const p=personas.find(x=>x.id===e.personaId);
                  const vals=Object.values(e.calificaciones||{});
                  const prom=vals.length?Math.round(vals.reduce((s,v)=>s+v,0)/vals.length*10)/10:0;
                  const si=scoreIntegrado(e.personaId);
                  const nivel=nivelDesempeno(si);
                  return(
                    <tr key={e.id}>
                      <td style={{fontFamily:"Lato,monospace",fontSize:12}}>{fmtDate(e.createdAt)}</td>
                      <td style={{fontWeight:700}}>{p?.nombre||e.personaNombre}</td>
                      <td style={{fontSize:12,color:"var(--muted)"}}>{p?`${p.sede}`:"—"}</td>
                      <td style={{fontSize:12}}>{e.periodoLabel||"—"}</td>
                      <td style={{fontFamily:"PT Serif,serif",fontSize:18,fontWeight:700,textAlign:"center"}}>{prom}/5</td>
                      <td style={{fontFamily:"PT Serif,serif",fontSize:18,fontWeight:700,color:nivel.color,textAlign:"center"}}>{si??"-"}</td>
                      <td><span className={`badge ${nivel.badge}`}>{nivel.label}</span></td>
                      <td style={{fontSize:12}}>{e.evaluador||"—"}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </div>
      )}
    </div>
  );
}

// ── MODAL LOGRO ───────────────────────────────────────────────────────────────
function ModalLogro({onClose,onSave,personas,session}){
  const [form,setForm]=useState({personaId:"",tipo:"cumplimiento",fecha:today(),descripcion:"",registradoPor:session.nombre});
  const set=(k,v)=>setForm(f=>({...f,[k]:v}));
  return(
    <div className="modal-overlay" onClick={onClose}><div className="modal" onClick={e=>e.stopPropagation()}>
      <button className="modal-close" onClick={onClose}>✕</button>
      <div className="modal-title">Registrar logro positivo</div>
      <div className="stack">
        <div className="form-row">
          <div className="form-group"><label className="form-label">Persona *</label>
            <select className="form-select" value={form.personaId} onChange={e=>set("personaId",e.target.value)}>
              <option value="">Seleccionar…</option>
              {personas.map(p=><option key={p.id} value={p.id}>{p.nombre} — {p.sede}</option>)}
            </select>
          </div>
          <div className="form-group"><label className="form-label">Fecha *</label>
            <input type="date" className="form-input" value={form.fecha} onChange={e=>set("fecha",e.target.value)}/>
          </div>
        </div>
        <div className="form-group"><label className="form-label">Tipo de logro *</label>
          <div style={{display:"grid",gridTemplateColumns:"repeat(3,1fr)",gap:8}}>
            {TIPOS_LOGRO.map(t=>(
              <div key={t.value} onClick={()=>set("tipo",t.value)}
                style={{border:`2px solid ${form.tipo===t.value?"var(--ok)":"var(--border)"}`,borderRadius:5,padding:"10px 12px",cursor:"pointer",background:form.tipo===t.value?"var(--ok-lt)":"var(--blanco)",transition:"all .15s"}}>
                <div style={{fontWeight:700,fontSize:12,color:form.tipo===t.value?"var(--ok)":"var(--ink)"}}>{t.label}</div>
                <div style={{fontSize:11,color:"var(--muted)",marginTop:2}}>+{t.puntos} pt{t.puntos>1?"s":""}</div>
              </div>
            ))}
          </div>
        </div>
        <div className="form-group"><label className="form-label">Descripción *</label>
          <textarea className="form-textarea" value={form.descripcion} onChange={e=>set("descripcion",e.target.value)} placeholder="Describe el logro o comportamiento positivo observado…"/>
        </div>
        <div className="form-group"><label className="form-label">Registrado por</label>
          <input className="form-input" value={form.registradoPor} onChange={e=>set("registradoPor",e.target.value)}/>
        </div>
        <div style={{background:"var(--ok-lt)",border:"1px solid var(--ok)",borderRadius:4,padding:"8px 14px",fontSize:12,color:"var(--ok)"}}>
          ✅ Los logros quedan en el historial de la persona y se consideran en la evaluación de desempeño anual.
        </div>
        <div className="btn-group">
          <button className="btn btn-sage" onClick={()=>{ if(form.personaId&&form.descripcion)onSave(form); }}>Guardar logro</button>
          <button className="btn btn-secondary" onClick={onClose}>Cancelar</button>
        </div>
      </div>
    </div></div>
  );
}

// ── MODAL EVALUACIÓN ──────────────────────────────────────────────────────────
function ModalEval({onClose,onSave,personas,competencias,session,logrosDePersona,incsDePersona,avisosDePersona,pesos,periodo}){
  const [form,setForm]=useState({personaId:"",periodoLabel:"Enero–Diciembre 2025",calificaciones:{},observaciones:"",evaluador:session.nombre});
  const [analizando,setAnalizando]=useState(false);
  const [analisisIA,setAnalisisIA]=useState(null); // {competencias:[{id,nombre,calificacion,justificacion}], observaciones}
  const set=(k,v)=>setForm(f=>({...f,[k]:v}));
  const setCalif=(cid,val)=>setForm(f=>({...f,calificaciones:{...f.calificaciones,[cid]:val}}));

  const persona=personas.find(p=>p.id===form.personaId);
  const ls=form.personaId?logrosDePersona(form.personaId)||[]:[];
  const is=form.personaId?incsDePersona(form.personaId)||[]:[];
  const avs=form.personaId&&avisosDePersona?avisosDePersona(form.personaId)||[]:[];
  const {score:incScore}=calcScore(is,avs,pesos,365);
  const vals=Object.values(form.calificaciones);
  const promComp=vals.length?Math.round(vals.reduce((s,v)=>s+v,0)/vals.length*10)/10:0;
  const compScore=Math.round((promComp/5)*100);
  const penalizacion=Math.min(incScore*3,40);
  const scoreTotal=Math.max(0,compScore-penalizacion);
  const nivelLabel=scoreTotal>=85?"Sobresaliente":scoreTotal>=70?"Satisfactorio":scoreTotal>=50?"En desarrollo":"Requiere atención";
  const nivelColor=scoreTotal>=85?"var(--ok)":scoreTotal>=70?"#1d4ed8":scoreTotal>=50?"var(--warn)":"var(--danger)";

  const aplicarAnalisis=()=>{
    if(!analisisIA)return;
    const nuevasCalifs={};
    analisisIA.competencias.forEach(c=>{ nuevasCalifs[c.id]=c.calificacion; });
    setForm(f=>({...f,calificaciones:{...f.calificaciones,...nuevasCalifs},
      observaciones:f.observaciones||analisisIA.observaciones||""}));
  };

  const analizarConIA=async()=>{
    if(!form.personaId){ alert("Selecciona una persona primero."); return; }
    setAnalizando(true); setAnalisisIA(null);
    const historial={
      persona:{nombre:persona?.nombre,sede:persona?.sede,pais:persona?.pais,cargo:persona?.cargo||"",area:persona?.area||""},
      incidencias:is.slice(-30).map(i=>({tipo:TIPOS_INC.find(t=>t.value===i.tipo)?.label||i.tipo,fecha:i.fecha,descripcion:i.descripcion,sinPuntos:i.sinPuntos||false})),
      avisos:avs.map(a=>({nivel:NIVELES_AVISO.find(n=>n.value===a.nivel)?.label||a.nivel,fecha:a.fecha,motivo:a.motivo})),
      logros:ls.map(l=>({tipo:TIPOS_LOGRO.find(t=>t.value===l.tipo)?.label||l.tipo,fecha:l.fecha,descripcion:l.descripcion})),
      competencias:competencias.map(c=>({id:c.id,nombre:c.nombre,descripcion:c.descripcion})),
    };
    try{
      const resp=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",
        headers:{"Content-Type":"application/json"},
        body:JSON.stringify({model:"claude-sonnet-4-20250514",max_tokens:1500,
          system:`Eres un analista experto en evaluación de desempeño institucional de Universidad Santander.
Analizas el historial operativo de una persona (incidencias, avisos, logros) y determinas su calificación en cada competencia definida.
Reglas:
- Usa SOLO los datos del historial para fundamentar cada calificación. No inventes.
- Escala 1-5: 1=Deficiente, 2=Regular, 3=Aceptable, 4=Bueno, 5=Excelente
- Si no hay evidencia suficiente para una competencia, califica 3 (Aceptable) y explícalo.
- Sé específico: menciona incidencias o logros concretos como evidencia.
- Responde SOLO con JSON válido, sin markdown, sin explicación extra.
Formato exacto:
{
  "competencias": [
    {"id": "c1", "nombre": "nombre", "calificacion": 3, "justificacion": "Basado en..."}
  ],
  "observaciones": "Resumen ejecutivo breve del desempeño general."
}`,
          messages:[{role:"user",content:`Analiza el historial de esta persona y califica cada competencia:
${JSON.stringify(historial,null,2)}`}]
        })
      });
      const data=await resp.json();
      const texto=data.content?.find(b=>b.type==="text")?.text||"{}";
      const parsed=JSON.parse(texto.replace(/```json|```/g,"").trim());
      setAnalisisIA(parsed);
    }catch(e){ alert("Error al analizar. Intenta de nuevo."); }
    setAnalizando(false);
  };

  return(
    <div className="modal-overlay" onClick={onClose}><div className="modal" style={{maxWidth:740}} onClick={e=>e.stopPropagation()}>
      <button className="modal-close" onClick={onClose}>✕</button>
      <div className="modal-title">Nueva evaluación de desempeño</div>
      <div className="stack">
        <div className="form-row">
          <div className="form-group"><label className="form-label">Persona *</label>
            <select className="form-select" value={form.personaId} onChange={e=>{set("personaId",e.target.value);setAnalisisIA(null);}}>
              <option value="">Seleccionar…</option>
              {personas.map(p=><option key={p.id} value={p.id}>{p.nombre} — {p.sede}</option>)}
            </select>
          </div>
          <div className="form-group"><label className="form-label">Periodo evaluado</label>
            <input className="form-input" value={form.periodoLabel} onChange={e=>set("periodoLabel",e.target.value)} placeholder="Ej. Enero–Diciembre 2025"/>
          </div>
        </div>

        {/* Contexto numérico */}
        {form.personaId&&(
          <div style={{background:"var(--gris-lt)",borderRadius:4,padding:"12px 16px",display:"flex",gap:20,flexWrap:"wrap"}}>
            {[["Incidencias",is.length,"var(--danger)"],["Score inc.",incScore,"var(--danger)"],["Logros",ls.length,"var(--ok)"],["Score total",scoreTotal,nivelColor]].map(([l,v,c])=>(
              <div key={l} style={{textAlign:"center"}}>
                <div style={{fontFamily:"PT Serif,serif",fontSize:24,fontWeight:700,color:c}}>{v}</div>
                <div style={{fontSize:10,color:"var(--muted)",textTransform:"uppercase",letterSpacing:.6,fontFamily:"Lato,sans-serif"}}>{l}</div>
              </div>
            ))}
            <div style={{textAlign:"center",borderLeft:"1px solid var(--border)",paddingLeft:20}}>
              <div style={{fontWeight:700,fontSize:14,color:nivelColor}}>{nivelLabel}</div>
              <div style={{fontSize:10,color:"var(--muted)",textTransform:"uppercase",letterSpacing:.6,fontFamily:"Lato,sans-serif"}}>Nivel proyectado</div>
            </div>
          </div>
        )}

        {/* Botón analizador IA */}
        {form.personaId&&(
          <div style={{background:"var(--rojo-ink)",borderRadius:6,padding:"16px 20px",border:"1px solid var(--dorado)33"}}>
            <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:analisisIA?12:0}}>
              <div>
                <div style={{fontWeight:700,fontSize:13,color:"var(--dorado)",fontFamily:"Lato,sans-serif",letterSpacing:.3}}>✦ Análisis automático de competencias con IA</div>
                <div style={{fontSize:11,color:"#C8A8A8",marginTop:2}}>La IA analiza el historial completo y propone una calificación fundamentada por competencia</div>
              </div>
              <button className="btn btn-gold" onClick={analizarConIA} disabled={analizando} style={{flexShrink:0,marginLeft:16}}>
                {analizando?"Analizando…":"✦ Analizar historial"}
              </button>
            </div>

            {/* Resultado del análisis */}
            {analisisIA&&(
              <div style={{marginTop:12}}>
                <div style={{fontSize:10,fontWeight:700,color:"var(--dorado)",textTransform:"uppercase",letterSpacing:.8,fontFamily:"Lato,sans-serif",marginBottom:10}}>
                  Propuesta de calificación — basada en historial real
                </div>
                {analisisIA.competencias?.map(c=>{
                  const esc=ESCALA_COMP.find(e=>e.valor===c.calificacion);
                  return(
                    <div key={c.id} style={{background:"#ffffff0f",borderRadius:4,padding:"10px 14px",marginBottom:8,borderLeft:`3px solid ${esc?.color||"var(--dorado)"}`}}>
                      <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:4}}>
                        <span style={{fontWeight:700,fontSize:13,color:"var(--blanco)"}}>{c.nombre}</span>
                        <span style={{fontFamily:"PT Serif,serif",fontSize:20,fontWeight:700,color:esc?.color||"var(--dorado)"}}>{c.calificacion}/5
                          <span style={{fontSize:11,fontWeight:400,marginLeft:4,color:"#C8A8A8"}}>({esc?.label})</span>
                        </span>
                      </div>
                      <div style={{fontSize:12,color:"#C8A8A8",lineHeight:1.5}}>{c.justificacion}</div>
                    </div>
                  );
                })}
                {analisisIA.observaciones&&(
                  <div style={{background:"#ffffff0f",borderRadius:4,padding:"10px 14px",marginTop:4}}>
                    <div style={{fontSize:10,fontWeight:700,color:"var(--dorado)",textTransform:"uppercase",letterSpacing:.8,fontFamily:"Lato,sans-serif",marginBottom:4}}>Observaciones generales IA</div>
                    <div style={{fontSize:12,color:"#C8A8A8",lineHeight:1.5}}>{analisisIA.observaciones}</div>
                  </div>
                )}
                <div style={{display:"flex",gap:10,marginTop:12}}>
                  <button className="btn btn-gold" onClick={aplicarAnalisis}>
                    ✓ Aplicar calificaciones al formulario
                  </button>
                  <button className="btn btn-secondary" style={{background:"#ffffff11",color:"#C8A8A8",border:"none"}} onClick={()=>setAnalisisIA(null)}>
                    Descartar
                  </button>
                </div>
              </div>
            )}
          </div>
        )}

        {/* Calificación manual de competencias */}
        <div className="form-group">
          <label className="form-label">Calificación por competencias * <span style={{fontWeight:400,textTransform:"none",fontSize:11,color:"var(--muted)"}}>— puedes ajustar las sugeridas por IA</span></label>
          {competencias.map(comp=>{
            const val=form.calificaciones[comp.id]||0;
            const iaComp=analisisIA?.competencias?.find(c=>c.id===comp.id);
            return(
              <div key={comp.id} style={{marginBottom:12,padding:"12px 16px",background:"var(--gris-lt)",borderRadius:4,border:`1px solid ${val>0?"var(--ok)":"var(--border)"}`}}>
                <div style={{display:"flex",justifyContent:"space-between",alignItems:"flex-start",marginBottom:6}}>
                  <div>
                    <div style={{fontWeight:700,fontSize:13}}>{comp.nombre}</div>
                    <div style={{fontSize:11,color:"var(--muted)"}}>{comp.descripcion}</div>
                  </div>
                  {val>0&&<span style={{fontFamily:"PT Serif,serif",fontSize:18,fontWeight:700,color:ESCALA_COMP.find(e=>e.valor===val)?.color}}>{val}/5</span>}
                </div>
                <div style={{display:"flex",gap:6}}>
                  {ESCALA_COMP.map(e=>(
                    <button key={e.valor} onClick={()=>setCalif(comp.id,e.valor)}
                      style={{flex:1,padding:"7px 4px",border:`2px solid ${val===e.valor?e.color:"var(--border)"}`,borderRadius:4,cursor:"pointer",
                        background:val===e.valor?`${e.color}22`:"var(--blanco)",transition:"all .12s"}}>
                      <div style={{fontFamily:"PT Serif,serif",fontSize:16,fontWeight:700,color:val===e.valor?e.color:"var(--muted)"}}>{e.valor}</div>
                      <div style={{fontSize:9,color:val===e.valor?e.color:"var(--muted)",textTransform:"uppercase",letterSpacing:.3,fontFamily:"Lato,sans-serif"}}>{e.label}</div>
                    </button>
                  ))}
                </div>
              </div>
            );
          })}
        </div>

        <div className="form-group"><label className="form-label">Observaciones generales</label>
          <textarea className="form-textarea" value={form.observaciones} onChange={e=>set("observaciones",e.target.value)} placeholder="Comentarios, áreas de mejora, reconocimientos…" style={{minHeight:80}}/>
        </div>
        <div className="form-group"><label className="form-label">Evaluador</label>
          <input className="form-input" value={form.evaluador} onChange={e=>set("evaluador",e.target.value)}/>
        </div>

        <div className="btn-group">
          <button className="btn btn-primary"
            onClick={()=>{ if(form.personaId&&Object.keys(form.calificaciones).length>0)onSave(form); }}>
            Guardar evaluación
          </button>
          <button className="btn btn-secondary" onClick={onClose}>Cancelar</button>
        </div>
      </div>
    </div></div>
  );
}

// ── MODAL EDITAR PERSONA ──────────────────────────────────────────────────────
function ModalEditPersona({persona,onClose,onSave}){
  const [form,setForm]=useState({...persona});
  const set=(k,v)=>setForm(f=>({...f,[k]:v}));

  return(
    <div className="modal-overlay" onClick={onClose}><div className="modal" onClick={e=>e.stopPropagation()}>
      <button className="modal-close" onClick={onClose}>✕</button>
      <div className="modal-title">Editar persona</div>
      <div style={{background:"var(--amber-lt)",border:"1px solid var(--warn)",borderRadius:4,padding:"8px 14px",fontSize:12,color:"#7a5a00",marginBottom:16,fontFamily:"Lato,sans-serif"}}>
        Solo administradores pueden editar información de personas registradas.
      </div>
      <div className="stack">
        <div className="form-row">
          <div className="form-group">
            <label className="form-label">Nombre completo *</label>
            <input className="form-input" value={form.nombre} onChange={e=>set("nombre",e.target.value)}/>
          </div>
          <div className="form-group">
            <label className="form-label">Sede *</label>
            <input className="form-input" value={form.sede} onChange={e=>set("sede",e.target.value)}/>
          </div>
        </div>
        <div className="form-row">
          <div className="form-group">
            <label className="form-label">País *</label>
            <select className="form-select" value={form.pais} onChange={e=>set("pais",e.target.value)}>
              {PAISES.map(p=><option key={p}>{p}</option>)}
            </select>
          </div>
          <div className="form-group">
            <label className="form-label">Área</label>
            <input className="form-input" value={form.area||""} onChange={e=>set("area",e.target.value)} placeholder="Ej. Servicios Escolares"/>
          </div>
        </div>
        <div className="form-group">
          <label className="form-label">Cargo</label>
          <input className="form-input" value={form.cargo||""} onChange={e=>set("cargo",e.target.value)} placeholder="Ej. Coordinadora académica"/>
        </div>
        <div className="btn-group">
          <button className="btn btn-primary" onClick={()=>{ if(form.nombre&&form.sede) onSave(form); }}>
            Guardar cambios
          </button>
          <button className="btn btn-secondary" onClick={onClose}>Cancelar</button>
        </div>
      </div>
    </div></div>
  );
}

// ── ACCESS GATE ──
// The app's own "Continuar con Google Institucional" login is a non-verified identity
// picker (no password, click a name and you're in as that person) — it was never real
// authentication. This gate is the actual barrier: a team-wide access code checked
// server-side, required before any data (incl. names, incidents, formal warnings) loads.
function AccessGate(){
  const [checking,setChecking]=useState(true);
  const [authed,setAuthed]=useState(false);
  const [code,setCode]=useState("");
  const [error,setError]=useState("");
  const [submitting,setSubmitting]=useState(false);

  useEffect(()=>{ (async()=>{
    const stored=getAccessCode();
    if(stored){ const ok=await verifyAccessCode(stored); setAuthed(ok); if(!ok) clearAccessCodeStored(); }
    setChecking(false);
  })(); },[]);

  const handleSubmit=async(e)=>{
    e.preventDefault();
    if(!code.trim()) return;
    setSubmitting(true); setError("");
    const ok=await verifyAccessCode(code.trim());
    setSubmitting(false);
    if(ok){ setAccessCodeStored(code.trim()); setAuthed(true); }
    else setError("Código incorrecto. Verifica con el administrador.");
  };

  if(checking) return <div style={{padding:48,fontFamily:"monospace",color:"#6b6660"}}>Cargando…</div>;
  if(authed) return <SrioApp onUnauthorized={()=>setAuthed(false)}/>;

  return (
    <div style={{display:"flex",alignItems:"center",justifyContent:"center",height:"100vh",background:"#F7F5F0",fontFamily:"Lato,sans-serif"}}>
      <link href="https://fonts.googleapis.com/css2?family=PT+Serif:wght@700&family=Lato:wght@400;600;700&display=swap" rel="stylesheet"/>
      <form onSubmit={handleSubmit} style={{width:340,background:"#fff",border:"1px solid #E4DFD3",borderRadius:12,padding:36,textAlign:"center",boxShadow:"0 8px 30px rgba(0,0,0,0.08)"}}>
        <div style={{fontFamily:"'PT Serif',serif",fontSize:11,fontWeight:700,letterSpacing:2.5,textTransform:"uppercase",color:"#B08D3E",marginBottom:2}}>Universidad Santander</div>
        <div style={{fontFamily:"'PT Serif',serif",fontSize:26,fontWeight:700,color:"#8B1E1E",marginBottom:16}}>SRIO</div>
        <div style={{fontSize:12,color:"#6b6660",marginBottom:20}}>Código de acceso del equipo</div>
        <input type="password" autoFocus value={code} onChange={e=>setCode(e.target.value)} placeholder="Código de acceso"
          style={{width:"100%",padding:"11px 14px",borderRadius:8,border:"1px solid #E4DFD3",fontSize:14,outline:"none",marginBottom:12,textAlign:"center",boxSizing:"border-box"}}/>
        {error && <div style={{color:"#b3452e",fontSize:12,marginBottom:12}}>{error}</div>}
        <button type="submit" disabled={submitting||!code.trim()} style={{width:"100%",padding:11,borderRadius:8,border:"none",background:submitting||!code.trim()?"#c9c2b3":"#8B1E1E",color:"#fff",fontSize:14,fontWeight:700,cursor:submitting||!code.trim()?"not-allowed":"pointer"}}>
          {submitting?"Verificando...":"Entrar"}
        </button>
      </form>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<AccessGate/>);
