  /* Las fuentes se cargan desde index.html con <link> + preconnect: un @import
     acá encadenaba 3 viajes EN SERIE (style.css → CSS de Google Fonts → .woff2)
     antes de poder pintar con la tipografía real. No volver a importarlas acá. */

  :root{
    --paper:   #EAEAE7;
    --field:   #FBFBF9;
    --ink:     #19181A;
    --muted:   #8C8A85;
    --line:    #DAD8D2;
    --accent:  #2B4DE0;
    --accent-soft: rgba(43,77,224,0.10);
    --good:    #2B8C4E;
    --warn:    #D6A22B;
    --bad:     #951515;
    --orange:  #C98405;
    --perfect: #14532D;
    --atk:     #5A6B7A;
    --def:     #2B4DE0;
    --overlay: rgba(234,234,231,0.96);

    --font-display: 'Space Grotesk', system-ui, -apple-system, sans-serif;
    --font-body: 'Inter', system-ui, -apple-system, sans-serif;

    --r-sm: 8px;
    --r-md: 14px;
    --r-lg: 20px;
  }

  [data-theme="dark"]{
    --paper:   #141416;
    --field:   #1E1E21;
    --ink:     #ECEBE8;
    --muted:   #8E8C88;
    --line:    #34343A;
    --accent:  #5E7BFF;
    --accent-soft: rgba(94,123,255,0.14);
    --good:    #43B36B;
    --warn:    #E0B445;
    --bad:     #E0524D;
    --orange:  #E0A62B;
    --perfect: #6BCB8F;
    --atk:     #8595A6;
    --def:     #5E7BFF;
    --overlay: rgba(20,20,22,0.96);
  }

  *{ box-sizing:border-box; margin:0; padding:0; -webkit-tap-highlight-color:transparent; }
  html,body{ height:100%; }
  body{
    font-family:var(--font-body);
    background:var(--paper);
    color:var(--ink);
    overflow:hidden;
    -webkit-font-smoothing:antialiased;
    text-rendering:optimizeLegibility;
  }

  #app{
    position:relative;
    height:100dvh;
    width:100%;
    max-width:520px;
    margin:0 auto;
    overflow:hidden;
  }

  .version-tag{
    position:absolute;
    top: calc(max(10px, env(safe-area-inset-top)) + 30px);
    right: 12px;
    font-family: var(--font-body);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .15em;
    color: var(--muted);
    opacity: 0.5;
    z-index: 100;
    cursor: pointer;
  }

  /* 🩺 Medidor de FPS/frame-time (diagnóstico, ?fps=1). Fijo arriba a la
     izquierda, por ENCIMA del overlay del duelo (z 9999), sin capturar toques
     (pointer-events:none) para no interferir con el botón STOP. Apagado por
     defecto (hidden) → el jugador normal nunca lo ve. */
  #fps-hud{
    position: fixed;
    top: max(6px, env(safe-area-inset-top));
    left: 6px;
    z-index: 9999;
    font: 600 11px/1.3 ui-monospace, Menlo, Consolas, monospace;
    color: #38e06a;
    background: rgba(0,0,0,0.74);
    padding: 4px 7px;
    border-radius: 6px;
    pointer-events: none;
    white-space: nowrap;
    max-width: calc(100vw - 12px);
    overflow: hidden;
    text-overflow: ellipsis;
  }
  #fps-hud.is-bad{ color: #ff5252; }
  #fps-hud[hidden]{ display: none; }

  .top-controls{
    position:absolute;
    top: max(8px, env(safe-area-inset-top));
    right: 10px;
    z-index:101;
    display:flex;
    align-items:center;
    gap:2px;
  }
  .theme-toggle.is-hidden{ display:none; }
  .info-btn{
    position:absolute;
    top: max(8px, env(safe-area-inset-top));
    left: 10px;
    z-index:101;
    width:34px; height:34px;
    display:flex; align-items:center; justify-content:center;
    border:none; background:transparent; color:var(--ink);
    cursor:pointer; padding:0; border-radius:50%;
    transition: background .25s ease;
  }
  .info-btn:active{ background:var(--accent-soft); }
  .info-btn.is-hidden{ display:none; }
  .theme-toggle{
    position:relative;
    top:auto;
    right:auto;
    width:34px;
    height:34px;
    display:flex;
    align-items:center;
    justify-content:center;
    border:none;
    background:transparent;
    color:var(--ink);
    cursor:pointer;
    padding:0;
    border-radius:50%;
    transition: background .25s ease;
  }
  .theme-toggle:active{ background:var(--accent-soft); }
  .theme-toggle__svg{ overflow:visible; }
  .tt-core{
    fill: currentColor;
    transform-origin: 12px 12px;
    transition: transform .45s cubic-bezier(.5,.15,.25,1);
  }
  .tt-rays{
    transform-origin: 12px 12px;
    transition: transform .5s cubic-bezier(.5,.15,.25,1), opacity .35s ease;
    opacity:1;
  }

  [data-theme="dark"] .theme-toggle__svg .tt-rays{
    transform: scale(.35) rotate(-45deg);
    opacity:0;
  }
  [data-theme="dark"] .theme-toggle__svg .tt-core{
    transform: scale(1.05);
  }
  [data-theme="dark"] .theme-toggle__svg .tt-bite{
    transform: translate(0,0);
  }
  .tt-bite{
    fill: #000;
    transform: translate(7px, -7px);
    transition: transform .45s cubic-bezier(.5,.15,.25,1);
  }

  .screen--lab{ justify-content:flex-start; gap:16px; overflow-y:auto; padding-top:30px; padding-bottom:30px; }
  .screen--info{ justify-content:flex-start; gap:0; overflow-y:auto; padding:24px 22px 40px; }
  .info-wrap{ width:100%; max-width:460px; margin:0 auto; }
  .info-wrap h2{ font-family:var(--font-display); font-size:22px; font-weight:800; margin:0 0 4px; color:var(--ink); }
  .info-wrap h3{ font-family:var(--font-display); font-size:14px; font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:var(--accent); margin:22px 0 8px; }
  .info-wrap p{ font-family:var(--font-body); font-size:14px; line-height:1.55; color:var(--ink); opacity:.9; margin:0 0 10px; }
  .info-item{ display:flex; gap:10px; align-items:flex-start; margin:0 0 11px; }
  .info-item .ico{ flex:0 0 auto; width:26px; text-align:center; font-size:17px; line-height:1.4; }
  .info-item .txt{ font-family:var(--font-body); font-size:13.5px; line-height:1.5; color:var(--ink); opacity:.9; }
  .info-item .txt b{ font-weight:700; opacity:1; }
  .info-zone-row{ display:flex; align-items:center; gap:8px; margin:0 0 6px; font-family:var(--font-body); font-size:13px; color:var(--ink); }
  .info-zone-row .swatch{ flex:0 0 auto; width:26px; height:14px; border-radius:3px; }
  .info-ver{ text-align:center; margin-top:30px; font-family:var(--font-body); font-size:12px; color:var(--muted); opacity:.8; }
  .info-ver b{ color:var(--accent); font-weight:700; letter-spacing:.08em; }
  .lab-head{ text-align:center; }
  .lab-title{ font-family:var(--font-display); font-size:24px; font-weight:700; color:var(--ink); }
  .lab-sub{ font-size:12px; color:var(--muted); margin-top:4px; }
  .lab-body{ width:100%; display:flex; flex-direction:column; gap:14px; }
  .lab-group{ font-family:var(--font-display); font-size:11px; font-weight:700; letter-spacing:.1em; text-transform:uppercase; color:var(--muted); margin-top:8px; }
  .lab-row{ display:flex; flex-direction:column; gap:4px; }
  .lab-row__top{ display:flex; justify-content:space-between; align-items:baseline; }
  .lab-row__label{ font-size:13px; color:var(--ink); }
  .lab-row__val{ font-family:var(--font-display); font-size:13px; font-weight:700; color:var(--accent); }
  .lab-row input[type=range]{ width:100%; accent-color:var(--accent); }
  .lab-toggle{ display:flex; align-items:center; gap:8px; font-size:13px; color:var(--ink); }
  .lab-toggle input[type=checkbox]{ accent-color: var(--accent); width:18px; height:18px; }
  .lab-actions{ display:flex; flex-wrap:wrap; gap:8px; width:100%; }
  .lab-actions .btn{ flex:1; min-width:120px; }
  .lab-json{ width:100%; min-height:80px; font-family:monospace; font-size:11px; padding:8px; border:1px solid var(--line); border-radius:var(--r-sm); background:var(--field); color:var(--ink); resize:vertical; }

  .screen--bracket{ justify-content:center; gap:22px; padding:24px; }
  .bracket-head{ text-align:center; }
  .bracket-eyebrow{ font-family:var(--font-display); font-size:11px; font-weight:700; letter-spacing:.2em; text-transform:uppercase; color:var(--muted); }
  .bracket-title{ font-family:var(--font-display); font-size:24px; font-weight:700; color:var(--ink); margin-top:4px; }
  .bracket-grid{ display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:12px; width:100%; }
  .bracket-vs{ font-family:var(--font-display); font-size:20px; font-weight:800; color:var(--accent); }
  .bracket-rivals{ display:flex; flex-direction:column; gap:6px; }
  .fighter{ display:flex; flex-direction:column; padding:8px 12px; border-radius:var(--r-sm); border:2px solid var(--line); background:var(--field); }
  .fighter.is-you{ border-color:var(--accent); background:var(--accent-soft); }
  .fighter__name{ font-family:var(--font-display); font-size:15px; font-weight:700; color:var(--ink); }
  .fighter__hp{ font-size:11px; color:var(--muted); margin-top:2px; }
  .fighter--rival{ display:flex; justify-content:space-between; align-items:center; padding:6px 10px; border-radius:var(--r-sm); border:1.5px solid var(--line); background:var(--field); font-size:13px; }
  .fighter--rival .r-name{ font-family:var(--font-display); font-weight:600; color:var(--ink); }
  .fighter--rival .r-hp{ font-size:10px; color:var(--muted); }
  .fighter--rival.is-king{ border-color:#D4AF37; background:rgba(212,175,55,0.12); }
  .fighter--rival.is-king .r-name{ color:#9A7B14; }
  [data-theme="dark"] .fighter--rival.is-king .r-name{ color:#D4AF37; }
  .fighter--rival.is-current{ border-color:var(--accent); box-shadow:0 0 0 2px var(--accent-soft); }
  .fighter--rival.is-beaten{ opacity:.5; }
  .fighter--rival.is-beaten .r-name{ text-decoration:line-through; color:var(--bad); }

  .screen{
    position:absolute; inset:0;
    display:flex; flex-direction:column;
    padding: max(20px, env(safe-area-inset-top)) 22px max(20px, env(safe-area-inset-bottom));
    opacity:0; visibility:hidden;
    transform: translateY(8px);
    transition: opacity .32s ease, transform .32s ease, visibility .32s;
    pointer-events:none;
  }
  .screen.is-active{ opacity:1; visibility:visible; pointer-events:auto; transform:none; }

  .screen--home{ justify-content:center; gap:30px; }

  /* ===== Fondo Game of Life del menú (v0.3.43) ===== */
  /* Decorativo puro: el JS dibuja a alpha pleno y la opacidad se regula acá
     (único knob). z-index:0 en el canvas + lift del contenido, y NO z-index:-1:
     .screen es stacking context solo mientras transiciona (opacity<1/transform)
     y un hijo con z negativo se re-anclaría al contexto raíz al terminar. */
  /* width/height explícitos: inset:0 NO estira elementos replaced (canvas
     conserva su 300x150 intrínseco si no se le da tamaño). */
  .gol-bg{ position:absolute; inset:0; width:100%; height:100%; z-index:0; pointer-events:none; opacity:.12; }
  [data-theme="dark"] .gol-bg{ opacity:.16; }
  /* Los hijos estáticos pintarían DEBAJO de un hermano posicionado; esto los
     sube sin tocar el layout flex. */
  #screen-home > :not(.gol-bg){ position:relative; z-index:1; }

  .brand{ display:flex; flex-direction:column; align-items:flex-start; gap:10px; }
  .brand__mark{ display:flex; align-items:center; gap:9px; }
  .brand__dot{ width:9px; height:9px; border-radius:50%; background:var(--accent); }
  .brand__kicker{
    font-family:var(--font-body); font-size:11px; font-weight:600;
    letter-spacing:.22em; text-transform:uppercase; color:var(--muted);
  }
  .brand__tag{ font-size:15px; color:var(--muted); letter-spacing:.01em; max-width:300px; }
  .brand__name{
    font-family:var(--font-display); font-weight:700; font-size:64px;
    letter-spacing:-.03em; line-height:.9;
  }

  .field-group{ display:flex; flex-direction:column; gap:18px; }

  .label{
    font-size:11px; font-weight:600; letter-spacing:.16em;
    text-transform:uppercase; color:var(--muted); margin-bottom:9px; display:block;
  }

  .input{
    width:100%; background:var(--field); border:1px solid var(--line);
    border-radius:var(--r-md); padding:15px 16px;
    font-family:var(--font-display); font-size:18px; font-weight:500; color:var(--ink);
    transition:border-color .2s ease;
  }
  .input::placeholder{ color:var(--muted); font-weight:400; }
  .input:focus{ outline:none; border-color:var(--ink); }

  .actions{ display:flex; flex-direction:column; gap:11px; }

  .actions-divider{
    display:flex; align-items:center; gap:12px;
    margin:6px 0 2px;
    color:var(--muted);
    font-size:10px; font-weight:600; letter-spacing:.2em; text-transform:uppercase;
  }
  .actions-divider::before, .actions-divider::after{
    content:''; flex:1; height:1px; background:var(--line);
  }

  .btn{
    width:100%; border:none; border-radius:var(--r-md);
    padding:17px; font-family:var(--font-display); font-size:17px; font-weight:600;
    cursor:pointer; transition: transform .12s ease, opacity .2s ease, background .2s ease;
    letter-spacing:.005em;
  }
  .btn:active{ transform: scale(.985); }
  .btn--primary{ background:var(--accent); color:#fff; }
  .btn--ink{ background:var(--ink); color:var(--field); }
  .btn--outline{ background:transparent; color:var(--ink); border:1px solid var(--ink); }
  .btn--ghost{
    background:transparent; color:var(--muted); padding:8px;
    font-family:var(--font-body); font-size:14px; font-weight:500; text-decoration:underline;
    text-underline-offset:3px; text-decoration-color:var(--line);
  }
  .btn--ghost:active{ transform:none; color:var(--ink); }

  @media (hover:hover){
    .btn--primary:hover{ opacity:.92; }
    .btn--ink:hover{ opacity:.92; }
    .btn--outline:hover{ background:var(--accent-soft); }
    .btn--ghost:hover{ color:var(--ink); }
  }

  .home-foot{ text-align:center; font-size:12px; color:var(--muted); letter-spacing:.02em; line-height:1.5; }
  .page-credit{ text-align:center; font-size:9px; color:var(--muted); opacity:.6; letter-spacing:.02em; line-height:1.6; margin-top:18px; }
  .page-credit a{ color:inherit; text-decoration:underline; }

  /* flex-start + margin:auto en los hijos: centrado cuando entra, scroll cuando
     el contenido crece (p.ej. torneo x4 con lista + botón de inicio). */
  .screen--lobby{ justify-content:flex-start; gap:34px; text-align:center; overflow-y:auto; }
  #lobby-created, #lobby-join{ margin-top:auto; margin-bottom:auto; flex-shrink:0; }
  .lobby-status{ display:flex; flex-direction:column; align-items:center; gap:14px; }
  .mode-select{ display:flex; flex-direction:column; align-items:center; gap:8px; margin-top:4px; width:100%; max-width:300px; }
  .mode-select__label{ font-family:var(--font-display); font-size:10px; font-weight:600; letter-spacing:.16em; text-transform:uppercase; color:var(--muted); }
  .mode-select__opts{ display:flex; flex-direction:column; gap:7px; width:100%; }
  .mode-opt{
    width:100%; display:flex; flex-direction:column; align-items:flex-start; gap:2px;
    text-align:left; padding:10px 14px; border-radius:var(--r-md);
    border:1px solid var(--line); background:var(--field);
    cursor:pointer; transition:all .15s ease;
  }
  .mode-opt__name{ font-family:var(--font-display); font-size:14px; font-weight:700; color:var(--ink); }
  .mode-opt__desc{ font-family:var(--font-body); font-size:11.5px; color:var(--muted); }
  /* Anillo grueso hacia adentro (inset): nunca se recorta contra el contenedor. */
  .mode-opt.is-on{ border-color:var(--accent); background:var(--accent-soft); box-shadow:inset 0 0 0 1px var(--accent); }
  .mode-opt.is-on .mode-opt__name{ color:var(--accent); }
  .walls-toggle{
    width:100%; display:flex; align-items:center; gap:6px;
    padding:10px 14px; border-radius:var(--r-md);
    border:1px dashed var(--line); background:transparent;
    font-family:var(--font-display); font-size:13px; font-weight:600; color:var(--muted);
    cursor:pointer; transition:all .15s ease;
  }
  .walls-toggle.is-on{ border-style:solid; border-color:var(--accent); background:var(--accent-soft); color:var(--ink); }
  .walls-toggle__state{
    margin-left:auto; font-size:10px; font-weight:700;
    letter-spacing:.14em; text-transform:uppercase; color:var(--muted);
  }
  .walls-toggle.is-on .walls-toggle__state{ color:var(--accent); }
  .beta-tag{
    display:inline-block; font-family:var(--font-body); font-size:9px; font-weight:700;
    letter-spacing:.12em; text-transform:uppercase; color:var(--muted);
    border:1px solid var(--line); border-radius:999px; padding:2px 7px;
    vertical-align:middle;
  }
  .lobby-eyebrow{
    font-size:11px; font-weight:600; letter-spacing:.2em; text-transform:uppercase; color:var(--muted);
  }
  .code-display{
    font-family:var(--font-display); font-weight:700; font-size:58px;
    letter-spacing:.18em; padding-left:.18em; color:var(--ink);
  }
  .lobby-hint{ font-size:14px; color:var(--muted); max-width:280px; line-height:1.5; }

  .waiting{ display:flex; align-items:center; justify-content:center; gap:10px; color:var(--muted); font-size:14px; }
  .waiting__pulse{ width:8px; height:8px; border-radius:50%; background:var(--accent); animation:pulse 1.4s ease-in-out infinite; }
  @keyframes pulse{ 0%,100%{ opacity:.25; transform:scale(.8); } 50%{ opacity:1; transform:scale(1.15); } }

  .code-input{
    text-align:center; letter-spacing:.32em; padding-left:.32em;
    font-size:32px; font-weight:700; text-transform:uppercase;
    max-width:260px;
  }

  .screen--game{ padding: max(14px, env(safe-area-inset-top)) 14px max(14px, env(safe-area-inset-bottom)); gap: 12px; }

  .hud-bar{
    display:flex; align-items:stretch; gap:10px;
  }
  .hud-player{
    flex:1;
    background: var(--field);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    padding: 10px 12px;
    display:flex; flex-direction:column; gap:6px;
    position: relative;
    overflow:hidden;
  }
  .hud-player.is-you{ border-color: var(--ink); }
  .hud-name{
    font-family:var(--font-display); font-size:13px; font-weight:600;
    color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  }
  .hud-player.is-you .hud-name{ color:var(--ink); }
  .hud-tag{
    font-family:var(--font-body); font-size:11px; font-weight:500;
    color:var(--muted); margin-left:6px; vertical-align:baseline;
    opacity:.7;
  }
  .hud-tag:empty{ display:none; }
  .tourney-bar{
    display:flex; align-items:center; justify-content:center; gap:8px;
    margin:6px auto 0; padding:4px 12px;
    background:var(--field); border:1px solid var(--line); border-radius:999px;
    width:fit-content;
  }
  .tourney-bar__label{ font-family:var(--font-display); font-size:11px; font-weight:600; letter-spacing:.06em; color:var(--muted); }
  .tourney-bar__count{ font-family:var(--font-display); font-size:13px; font-weight:700; color:var(--ink); }
  .hud-hp-row{
    display:flex; align-items:center; gap:8px;
  }
  .hp-bar{
    flex:1; height:6px; background:var(--line); border-radius:99px; overflow:hidden;
    position:relative;
  }
  /* Capa "fantasma" del daño: al bajar la vida queda pintado el tramo perdido
     y se vacía/desvanece con retardo (estilo juego de pelea). El JS de
     updateHud maneja width/opacity inline, one-shot — mismo precedente que
     la transición de width de .hp-fill; nada anima en loop. */
  .hp-ghost{
    position:absolute; left:0; top:0; height:100%; width:0;
    background:var(--bad); opacity:0;
  }
  .hp-fill{
    height:100%; background:var(--ink); transition: width .4s ease, background .3s ease;
    position:relative;   /* pinta ENCIMA del fantasma: este asoma solo en el tramo perdido */
  }
  .hp-fill.is-low{ background:var(--bad); }
  .hp-fill.is-mid{ background:var(--warn); }
  .hp-num{
    font-family:var(--font-display); font-size:13px; font-weight:700; color:var(--ink);
    min-width: 28px; text-align:right;
  }
  /* Pop del número al recibir daño: late una vez en rojo y vuelve solo a su
     estilo base (sin fill-mode; la clase queda pero la animación no se repite
     hasta el próximo popClass). Solo transform/color, one-shot. */
  .hp-num.is-hit{ animation: hpNumHit .45s cubic-bezier(.2,.8,.3,1.4); }
  @keyframes hpNumHit{
    0%{ transform:scale(1); }
    30%{ transform:scale(1.28); color:var(--bad); }
    65%{ color:var(--bad); }
    100%{ transform:scale(1); }
  }
  @media (prefers-reduced-motion: reduce){
    .hp-ghost{ display:none; }
    .hp-num.is-hit{ animation:none; }
  }
  .hud-buffs{
    display:flex; gap:5px; min-height: 16px;
    flex-wrap: wrap;
  }
  .buff-chip{
    display:inline-flex; align-items:center; gap:3px;
    font-family: var(--font-display);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .05em;
    padding: 2px 6px;
    border-radius: 4px;
    line-height: 1;
  }
  .buff-chip.is-atk{
    background: rgba(200,48,43,0.12);
    color: var(--bad);
    border: 1px solid rgba(200,48,43,0.30);
  }
  .buff-chip.is-def{
    background: rgba(43,77,224,0.10);
    color: var(--def);
    border: 1px solid rgba(43,77,224,0.25);
  }
  .buff-chip .sym{ font-size: 11px; }
  .buff-chip.is-ring{
    background: rgba(214,90,120,0.16);
    border: 1px solid rgba(214,90,120,0.40);
    padding: 2px 5px;
  }
  .buff-chip.is-ring .sym{ font-size: 12px; }
  .buff-chip.is-boots{
    background: rgba(90,160,90,0.14);
    color: var(--good, #3a7d3a);
    border: 1px solid rgba(90,160,90,0.35);
  }
  /* Entrada de un chip nuevo (o que subió de valor): pop one-shot. El JS de
     renderBuffs pone is-new SOLO en esos casos — el resto de los chips se
     rebuildea sin animación en cada updateHud. */
  .buff-chip.is-new{ animation: chipIn .28s cubic-bezier(.2,.8,.3,1.35); }
  @keyframes chipIn{ from{ transform:scale(.5); opacity:0; } to{ transform:scale(1); opacity:1; } }
  @media (prefers-reduced-motion: reduce){
    .buff-chip.is-new{ animation:none; }
  }

  .board-wrap{
    flex:1;
    display:flex; align-items:center; justify-content:center;
    position:relative;
    min-height: 0;
  }
  .board{
    position:relative;
    display:grid;
    grid-template-columns: repeat(7, 1fr);
    grid-template-rows: repeat(7, 1fr);
    gap: 3px;
    background: var(--line);
    padding: 3px;
    border-radius: var(--r-md);
    aspect-ratio: 1;
    width: min(100%, calc(100dvh - 280px));
    max-width: 480px;
  }
  .board.is-large{ gap:2px; }
  .cell{
    background: var(--field);
    border-radius: 4px;
    position:relative;
    cursor: default;
    display:flex; align-items:center; justify-content:center;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: clamp(14px, 4vw, 22px);
    transition: background .15s ease, transform .15s ease;
  }
  .cell.is-reachable{
    background: var(--field);
    cursor: pointer;
  }
  .cell.is-reachable:hover{ background: var(--accent-soft); }
  .cell.is-reachable:active{ transform: scale(.95); }
  .cell.is-reachable::after{
    content:'';
    position:absolute;
    inset: 4px;
    border: 1.5px dashed var(--accent);
    border-radius: 3px;
    opacity: 0.5;
  }
  .cell .item-atk{
    color: var(--atk);
    font-size: clamp(16px, 4.5vw, 24px);
    line-height: 1;
  }
  .cell .item-def{
    color: var(--def);
    font-size: clamp(16px, 4.5vw, 24px);
    line-height: 1;
  }
  .cell .down{
    color: var(--bad);
    font-size: clamp(16px, 4.5vw, 24px);
    line-height: 1;
    font-weight: 800;
  }
  /* 🌀 Modo Caos: cofre sorpresa y portal (emojis, tamaño de los otros ítems) */
  .cell .item-chest{
    font-size: clamp(15px, 4.2vw, 22px);
    line-height: 1;
  }
  .cell .item-portal{
    font-size: clamp(16px, 4.5vw, 24px);
    line-height: 1;
    animation: portalSpin 6s linear infinite;
    will-change: transform;   /* capa propia: el giro no repinta la celda */
  }
  @keyframes portalSpin{ from{ transform:rotate(0deg); } to{ transform:rotate(360deg); } }
  /* Remolino one-shot en la casilla de destino de un teleport (portal o cofre);
     el próximo renderBoard lo limpia solo, igual que .clash-fx */
  .portal-fx{
    position:absolute; left:50%; top:50%; width:100%; height:100%;
    border-radius:50%; border:2.5px dashed var(--accent);
    pointer-events:none; z-index:3; opacity:0;
    animation: portalIn .6s ease-out;
  }
  @keyframes portalIn{
    0%{ transform:translate(-50%,-50%) scale(2.2) rotate(0deg); opacity:.85; }
    100%{ transform:translate(-50%,-50%) scale(.4) rotate(200deg); opacity:0; }
  }
  /* 💣 Bomba: quieta hasta que alguien la pisa; armada, late. ⛰️ terreno alto */
  .cell .item-bomb{
    font-size: clamp(15px, 4.2vw, 22px);
    line-height: 1;
  }
  .cell .item-bomb.is-armed{
    animation: bombPulse .8s ease-in-out infinite;
    will-change: transform;   /* capa propia: el latido no repinta la celda */
  }
  @keyframes bombPulse{ 0%,100%{ transform:scale(1); } 50%{ transform:scale(1.25); } }
  .cell .item-high{
    font-size: clamp(15px, 4.2vw, 22px);
    line-height: 1;
    opacity: .85;
  }
  .cell .item-boots{
    font-size: clamp(15px, 4.2vw, 22px);
    line-height: 1;
  }
  /* Aviso sutil en el área de una bomba armada (nada estridente) */
  .cell.is-blast-warn{
    box-shadow: inset 0 0 0 1.5px rgba(200,120,40,0.45);
  }
  /* Flash one-shot de explosión (el próximo renderBoard lo limpia solo) */
  .bomb-fx{
    position:absolute; inset:0;
    border-radius:6px;
    background: radial-gradient(circle, rgba(255,170,60,0.75) 0%, rgba(255,90,40,0.35) 60%, transparent 100%);
    pointer-events:none; z-index:3; opacity:0;
    animation: bombFlash .55s ease-out;
  }
  @keyframes bombFlash{ 0%{ opacity:.95; transform:scale(.6); } 100%{ opacity:0; transform:scale(1.15); } }
  @media (prefers-reduced-motion: reduce){
    .cell .item-portal{ animation:none; }
    .portal-fx{ animation:none; }
    .cell .item-bomb.is-armed{ animation:none; }
    .bomb-fx{ animation:none; }
  }
  /* Duelo en curso (body.is-dueling, ver setDuelOverlayShown): el overlay es
     semitransparente y el tablero se sigue pintando abajo — pausar TODOS los
     loops decorativos del board para que no compitan con el rAF de la aguja
     (en móvil la trababan). Si se agrega otra animación infinita a una celda,
     sumarla acá. */
  body.is-dueling .cell .item-portal,
  body.is-dueling .cell .item-bomb.is-armed,
  body.is-dueling .cell .item-ring,
  body.is-dueling .cell.is-both-here::before{
    animation-play-state: paused;
  }
  /* Y además: NO componer nada del juego debajo del overlay durante el duelo.
     Con --overlay al 96% el compositor mezclaba tablero+HUD completos en cada
     frame (a DPR 3 en móvil es un overdraw enorme que traba la aguja).
     visibility:hidden mantiene el layout (los getBoundingClientRect siguen
     midiendo bien) y con 96% de opacidad el cambio es imperceptible. */
  body.is-dueling #screen-game > *:not(.duel-overlay){
    visibility: hidden;
  }
  .cell .item-ring{
    width: clamp(20px, 5.5vw, 30px);
    height: clamp(20px, 5.5vw, 30px);
    border-radius: 50%;
    background: conic-gradient(from 90deg,
      #ff0000, #ff8000, #ffff00, #80ff00, #00ff00, #00ff80,
      #00ffff, #0080ff, #0000ff, #8000ff, #ff00ff, #ff0080, #ff0000);
    -webkit-mask: radial-gradient(circle, transparent 38%, #000 40%);
            mask: radial-gradient(circle, transparent 38%, #000 40%);
    filter: drop-shadow(0 0 3px rgba(255,80,80,0.6)) drop-shadow(0 0 5px rgba(80,120,255,0.5));
    animation: ringGlow 2s ease-in-out infinite;
  }
  /* mini-anillo multicolor para usar dentro de textos/toasts/botones */
  .ring-ic{
    display:inline-block;
    width:1em; height:1em;
    vertical-align:-0.15em;
    border-radius:50%;
    background: conic-gradient(from 90deg,
      #ff0000, #ff8000, #ffff00, #80ff00, #00ff00, #00ff80,
      #00ffff, #0080ff, #0000ff, #8000ff, #ff00ff, #ff0080, #ff0000);
    -webkit-mask: radial-gradient(circle, transparent 36%, #000 40%);
            mask: radial-gradient(circle, transparent 36%, #000 40%);
  }
  @keyframes ringGlow{ 0%,100%{ filter:drop-shadow(0 0 2px rgba(255,80,80,0.6)) drop-shadow(0 0 4px rgba(80,120,255,0.5)); } 50%{ filter:drop-shadow(0 0 5px rgba(80,220,120,0.7)) drop-shadow(0 0 7px rgba(220,180,40,0.6)); } }
  .cell .player-marker{
    position:absolute;
    /* Centrado explícito por offset ((100-tamaño)/2): el flex del .cell no
       centra un hijo position:absolute, y transform lo reserva el FLIP/clash. */
    left: 20%; top: 20%;
    width: 60%;
    height: 60%;
    border-radius: 50%;
    z-index: 1;
    transition: transform .35s cubic-bezier(.34,1.56,.64,1);
  }
  .cell .player-marker.is-you{
    background: var(--accent);
    box-shadow: 0 2px 8px rgba(43,77,224,0.4);
  }
  .cell .player-marker.is-opp{
    background: var(--opp-accent, var(--ink));
    box-shadow: 0 2px 8px rgba(25,24,26,0.4);
  }
  .cell .player-marker::after{
    content:'';
    position:absolute;
    top: 15%; left: 15%;
    width: 30%; height: 30%;
    border-radius: 50%;
    background: rgba(255,255,255,0.35);
  }
  .cell .player-marker.has-skin{
    background: transparent !important;
    box-shadow: none !important;
    left: 11%; top: 11%;   /* (100-78)/2: re-centra al ser más grande que la bola */
    width: 78%; height: 78%;
    display: flex; align-items: center; justify-content: center;
    font-size: 22px; line-height: 1;
  }
  .cell .player-marker.has-skin::after{ display:none; }
  .cell .player-marker.has-sprite{
    /* La imagen va por var --sprite-url + !important a propósito: una regla de
       torneo más abajo (.is-you{background:var(--you-accent)}) tiene la misma
       especificidad y, al ir después, su shorthand reseteaba el background-image
       y repintaba el color sólido tapando el sprite. !important le gana. */
    background-image: var(--sprite-url) !important;
    background-color: transparent !important;
    background-size: cover;
    background-position: center;
    box-shadow: none !important;
    width: 60%; height: 60%;   /* igual que la ficha default (antes 82% = se veía enorme) */
  }
  .cell .player-marker.has-sprite::after{ display:none; }
  /* Burbuja de tregua post-duelo: visible, estática, contenida en la casilla */
  .cell .player-marker.has-shield::before{
    content:'';
    position:absolute;
    left:50%; top:50%;
    width: 118%; height: 118%;
    transform: translate(-50%,-50%);
    border-radius: 50%;
    background:
      radial-gradient(circle at 35% 28%, rgba(255,255,255,0.65) 0%, rgba(255,255,255,0) 22%),
      radial-gradient(circle at 50% 50%, rgba(120,170,255,0.10) 0%, rgba(120,170,255,0.22) 78%, rgba(43,77,224,0.30) 100%);
    border: 2px solid rgba(43,77,224,0.55);
    box-shadow: inset 0 0 8px rgba(255,255,255,0.5), 0 0 6px rgba(43,77,224,0.35);
    box-sizing: border-box;
    pointer-events:none;
    z-index: 2;
  }

  /* Dos jugadores en la misma casilla: fichas en diagonal + marco que late */
  .cell.is-both-here{ background: var(--accent-soft); }
  .cell.is-both-here::before{
    content:'';
    position:absolute; inset:1px;
    border:1.5px solid var(--accent);
    border-radius:4px;
    pointer-events:none;
    animation: clashPulse 1.1s ease-in-out infinite;
  }
  @keyframes clashPulse{ 0%,100%{ opacity:.25; } 50%{ opacity:.85; } }
  /* Como las posiciones iniciales: vos abajo-derecha, rival arriba-izquierda */
  .cell .player-marker.is-clash.is-you{ transform: translate(20%,20%) scale(.68); z-index:2; }
  .cell .player-marker.is-clash.is-opp{ transform: translate(-20%,-20%) scale(.68); }

  /* Onda expansiva one-shot al caer ambos en la misma casilla (la agrega JS
     en resolveMovesAndContinue; el próximo renderBoard la limpia solo) */
  .clash-fx{
    position:absolute; left:50%; top:50%; width:100%; height:100%;
    border-radius:50%; border:2.5px solid var(--accent);
    pointer-events:none; z-index:3; opacity:0;
    animation: clashShock .55s ease-out;
  }
  @keyframes clashShock{
    0%{ transform:translate(-50%,-50%) scale(.3); opacity:.9; }
    100%{ transform:translate(-50%,-50%) scale(2.4); opacity:0; }
  }
  /* Las fichas "aterrizan" con un pop; terminan en el transform de reposo de
     .is-clash (la animación pisa el transform solo mientras corre) */
  .cell.is-impact .player-marker.is-clash.is-you{ animation: clashLandYou .35s cubic-bezier(.2,.8,.3,1.4); }
  .cell.is-impact .player-marker.is-clash.is-opp{ animation: clashLandOpp .35s cubic-bezier(.2,.8,.3,1.4); }
  @keyframes clashLandYou{ 0%{ transform:translate(20%,20%) scale(.95); } 100%{ transform:translate(20%,20%) scale(.68); } }
  @keyframes clashLandOpp{ 0%{ transform:translate(-20%,-20%) scale(.95); } 100%{ transform:translate(-20%,-20%) scale(.68); } }
  @media (prefers-reduced-motion: reduce){
    .clash-fx{ animation:none; }
    .cell.is-impact .player-marker.is-clash{ animation:none; }
  }

  /* Sacudida corta del tablero en impactos (choque, bomba). One-shot 240ms,
     amplitud 3px decayendo, SOLO transform (el JS la dispara con shakeBoard()
     y limpia la clase en animationend). Sin will-change permanente: el board
     es una capa grande y la promoción dura lo que dura la animación. */
  .board.is-shake{ animation: boardShake .24s linear; }
  @keyframes boardShake{
    0%,100%{ transform:translate(0,0); }
    20%{ transform:translate(3px,-2px); }
    40%{ transform:translate(-3px,2px); }
    60%{ transform:translate(2px,1px); }
    80%{ transform:translate(-1px,-1px); }
  }
  @media (prefers-reduced-motion: reduce){
    .board.is-shake{ animation:none; }
  }

  /* Aviso de duelo (ambos en casillas contiguas): onda grande centrada en el
     punto medio entre ambos, misma estética azul que .clash-fx. JS setea
     left/top/width/height en px sobre .board; clashShock ya centra con
     translate(-50%,-50%). El próximo renderBoard limpia todo. */
  .duel-fx{
    position:absolute;
    border-radius:50%; border:2.5px solid var(--accent);
    pointer-events:none; z-index:5; opacity:0;
    animation: clashShock .7s ease-out;
  }
  @media (prefers-reduced-motion: reduce){
    .duel-fx{ animation:none; }
  }

  /* Paredes del Modo Paredes: barritas absolutas dentro de la celda */
  .wall-seg{ position:absolute; z-index:4; pointer-events:none;
    background: var(--wall, #8A8A90); border-radius:2px; }
  .wall-seg.is-right{ top:-2px; bottom:-2px; right:-3px; width:4px; }
  .wall-seg.is-bottom{ left:-2px; right:-2px; bottom:-3px; height:4px; }

  .turn-footer{
    display:flex; align-items:center; justify-content:space-between;
    gap: 10px;
    min-height: 36px;
  }
  .turn-msg{
    font-size:13px; color:var(--muted); font-weight:500; letter-spacing:.01em;
    flex:1;
  }
  .turn-msg.is-active{ color:var(--accent); font-weight:600; }
  .game-tools{ display:flex; gap:6px; }
  .icon-btn{
    background:transparent; border:none; cursor:pointer; padding:6px 8px;
    font-family:var(--font-body); font-size:12px; font-weight:600; color:var(--muted);
    letter-spacing:.04em;
  }
  .icon-btn:active{ color:var(--ink); }

  .duel-overlay{
    position:absolute;
    inset: 0;
    background: var(--overlay);
    display:flex; flex-direction:column;
    align-items:center; justify-content:center;
    gap: 24px;
    padding: 30px 22px;
    z-index: 20;
    opacity: 0;
    visibility:hidden;
    transition: opacity .3s ease;
  }
  .duel-overlay.is-show{ opacity:1; visibility:visible; }

  .duel-title{
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 14px;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .duel-countdown{
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 96px;
    letter-spacing: -.03em;
    line-height: 1;
    color: var(--ink);
    min-height: 100px;
    transition: transform .2s ease;
  }
  .duel-countdown.is-pop{ transform: scale(1.1); }

  .duel-game{
    width: 100%;
    max-width: 420px;
    display:flex; flex-direction:column; gap: 24px; align-items:center;
  }

  .speedometer{
    position: relative;
    width: 100%;
    max-width: 380px;
    height: 120px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
  
  .speedo-track{
    position: relative;
    width: 100%;
    height: 24px;
    border-radius: 12px;
    overflow: hidden;
    background: var(--field);
    border: 1px solid var(--line);
  }
  
  .speedo-zone{
    position: absolute;
    top: 0;
    bottom: 0;
  }
  .speedo-zone.is-red{ background: rgba(149,21,21,0.34); }
  .speedo-zone.is-orange2{ background: rgba(178,58,10,0.32); }
  .speedo-zone.is-orange{ background: rgba(201,132,5,0.34); }
  .speedo-zone.is-yellow{ background: rgba(214,162,43,0.32); }
  .speedo-zone.is-green{ background: rgba(43,140,78,0.38); }
  .speedo-zone.is-perfect{ background: rgba(20,83,45,0.38); z-index:2; }

  [data-theme="dark"] .speedo-zone.is-red{ background: rgba(190,50,50,0.48); }
  [data-theme="dark"] .speedo-zone.is-orange2{ background: rgba(205,80,30,0.48); }
  [data-theme="dark"] .speedo-zone.is-orange{ background: rgba(214,150,30,0.48); }
  [data-theme="dark"] .speedo-zone.is-yellow{ background: rgba(224,180,69,0.46); }
  [data-theme="dark"] .speedo-zone.is-green{ background: rgba(67,179,107,0.50); }
  [data-theme="dark"] .speedo-zone.is-perfect{ background: rgba(107,203,143,0.52); z-index:2; }
  
  .speedo-ticks{
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    pointer-events: none;
  }
  .speedo-tick{
    position: absolute;
    top: -6px;
    bottom: -6px;
    width: 1.5px;
    background: var(--line);
    transform: translateX(-50%);
  }
  .speedo-tick.is-major{
    width: 2px;
    background: var(--muted);
  }
  
  .speedo-center-mark{
    position: absolute;
    left: 50%;
    top: 0;
    bottom: 0;
    width: 2px;
    background: var(--perfect);
    opacity: 0.7;
    transform: translateX(-50%);
    z-index: 2;
    transition: opacity .45s ease, box-shadow .45s ease;
  }
  /* PERFECTO activo (solo 1er pase): línea dorada con brillo que late */
  .speedo-center-mark.is-live{
    width: 3px;
    opacity: 1;
    background: linear-gradient(180deg, #F7DC7E, #D4AF37);
    /* Glow base ESTÁTICO; el pulso vive en el ::after animando SOLO opacity
       (compositado por GPU). Antes se animaba box-shadow acá (perfectShine,
       v0.2.70): repintado por CPU de la zona del track en CADA frame durante
       todo el pase 1 — trababa la aguja en móvil. Nunca animar sombras/filter
       en loop dentro del velocímetro. */
    box-shadow: 0 0 4px 1px rgba(212,175,55,.75), 0 0 10px 2px rgba(212,175,55,.35);
  }
  .speedo-center-mark.is-live::after{
    content:'';
    position:absolute; inset:0;
    border-radius: inherit;
    box-shadow: 0 0 8px 2px rgba(212,175,55,1), 0 0 18px 5px rgba(212,175,55,.55);
    animation: perfectShine .9s ease-in-out infinite;
    will-change: opacity;
  }
  @keyframes perfectShine{
    0%,100%{ opacity: 0; }
    50%{ opacity: 1; }
  }
  /* Se apaga al terminar el 1er pase: desvanecimiento rápido del dorado */
  .speedo-center-mark.is-gone{
    width: 3px;
    background: linear-gradient(180deg, #F7DC7E, #D4AF37);
    animation: perfectFadeOut .3s ease-out forwards;
  }
  @keyframes perfectFadeOut{
    from{ opacity: 1; box-shadow: 0 0 8px 2px rgba(212,175,55,1), 0 0 18px 5px rgba(212,175,55,.55); }
    to{ opacity: 0; box-shadow: none; }
  }
  
  .speedo-needle{
    position: absolute;
    top: -20px;
    bottom: -20px;
    width: 4px;
    background: var(--ink);
    border-radius: 2px;
    transform: translateX(-50%);
    box-shadow: 0 0 0 2px var(--field), 0 0 0 3px var(--ink);
    will-change: transform;
  }
  .speedo-needle::after{
    content: '';
    position: absolute;
    top: -4px;
    left: 50%;
    transform: translateX(-50%);
    width: 8px;
    height: 8px;
    background: var(--accent);
    border-radius: 50%;
  }
  
  .speedo-needle-opponent{
    position: absolute;
    top: -20px;
    bottom: -20px;
    width: 4px;
    background: var(--muted);
    border-radius: 2px;
    transform: translateX(-50%);
    box-shadow: 0 0 0 2px var(--field), 0 0 0 2px var(--muted);
    will-change: transform;
    opacity: 0.45;
  }
  .speedo-needle-opponent::after{
    content: '';
    position: absolute;
    top: -4px;
    left: 50%;
    transform: translateX(-50%);
    width: 8px;
    height: 8px;
    background: var(--muted);
    border-radius: 50%;
  }

  .duel-pass{
    position:absolute;
    top: 4px;
    right: 8px;
    font-family: var(--font-body);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .15em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .duel-pass.is-warn{ color: var(--warn); }
  .duel-pass.is-danger{ color: var(--bad); }

  .duel-stop-btn{
    width: 100%;
    max-width: 220px;
    padding: 18px;
    background: var(--accent);
    color: #fff;
    border: none;
    border-radius: var(--r-md);
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 18px;
    letter-spacing: .1em;
    text-transform: uppercase;
    cursor: pointer;
    transition: transform .1s ease, opacity .3s ease;
    opacity: 0;
    visibility: hidden;
    touch-action: manipulation;
    will-change: transform;
  }
  .duel-stop-btn.is-visible{
    opacity: 0.3;
    visibility: visible;
  }
  .duel-stop-btn.is-active{
    opacity: 1;
    visibility: visible;
  }
  .duel-stop-btn.is-pressed{
    opacity: 0.2;
    visibility: visible;
    pointer-events: none;
  }
  .duel-stop-btn:active{ transform: scale(.97); }
  .duel-stop-btn:disabled{ cursor: not-allowed; }
  /* OJO: no agregar animaciones al botón (ni a nada del overlay) que corran
     DURANTE duel-play — la aguja del rival puede seguir barriendo y cualquier
     paint extra ahí se siente como aguja trabada en móvil (v0.3.32). */

  .duel-result{
    display:flex; flex-direction:column; gap: 14px; align-items:center;
    text-align:center;
    max-width: 360px;
  }
  .duel-result__title{
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 32px;
    letter-spacing: -.02em;
    line-height: 1;
  }
  .duel-result__title.is-win{ color: var(--good); }
  .duel-result__title.is-lose{ color: var(--bad); }
  .duel-result__title.is-tie{ color: var(--warn); }
  /* Subtítulo del veredicto (ej: aviso de PERFECTO). Vacío si no aplica. */
  .duel-result__sub{
    min-height: 18px;
    font-family: var(--font-display);
    font-size: 13px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--perfect);
  }
  /* Dos columnas: jugador a la izquierda, rival a la derecha. Cada una muestra
     el daño recibido bien grande y su barra de HP (que anima el descenso). */
  .duel-result__cols{
    display:flex; gap: 22px; width:100%; max-width: 340px;
  }
  .duel-rescol{
    flex:1; min-width:0;
    display:flex; flex-direction:column; align-items:center; gap:6px;
    padding: 12px 12px 14px;
    border-radius: 14px;
    background: var(--field);
    border: 1px solid var(--line);
  }
  .duel-rescol__name{
    font-family: var(--font-display); font-size: 13px; font-weight: 600;
    color: var(--muted);
    max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .duel-rescol__dmg{
    font-family: var(--font-display); font-weight: 700;
    font-size: 42px; line-height: 1; letter-spacing: -.02em;
    color: var(--bad);
  }
  .duel-rescol__dmg.is-zero{ color: var(--muted); opacity:.6; }
  /* Réplica de la fila de vida del HUD (barra + número) bajo el nombre */
  .duel-rescol__hprow{ width:100%; }
  .duel-rescol__bar{ height: 8px; }
  /* Descenso de vida más lento y dramático que el del HUD */
  .duel-rescol__bar .hp-fill{ transition: width .9s cubic-bezier(.25,.8,.3,1), background .3s ease; }
  @media (prefers-reduced-motion: reduce){
    .duel-rescol__bar .hp-fill{ transition:none; }
  }

  .duel-reveal{ display:flex; flex-direction:column; align-items:center; gap:12px; width:100%; max-width:360px; margin-bottom:10px; }
  .duel-reveal__cols{ display:flex; align-items:flex-start; justify-content:space-between; width:100%; gap:16px; }
  .duel-reveal__col{ flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; }
  .duel-reveal__pname{ font-family:var(--font-display); font-size:13px; font-weight:600; color:var(--muted); letter-spacing:.02em; max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .duel-reveal__color{ font-family:var(--font-display); font-size:13px; font-weight:700; letter-spacing:.04em; text-transform:uppercase; }
  .duel-reveal__big{ font-family:var(--font-display); font-size:52px; font-weight:700; line-height:1; letter-spacing:-.02em; }
  .duel-reveal__big.is-perfect-hit{
    color: var(--perfect) !important;
    text-shadow: 0 0 6px rgba(20,83,45,0.4);
    animation: perfectGlowFade 1.3s ease-out;
  }
  @keyframes perfectGlowFade{
    0%{ text-shadow: 0 0 0 rgba(20,83,45,0); }
    30%{ text-shadow: 0 0 16px rgba(20,83,45,.95), 0 0 28px rgba(20,83,45,.5); }
    100%{ text-shadow: 0 0 6px rgba(20,83,45,0.4); }
  }
  @media (prefers-reduced-motion: reduce){
    .duel-reveal__big.is-perfect-hit{ animation:none; }
  }
  .duel-reveal__verdict{ font-family:var(--font-display); font-size:16px; font-weight:600; text-align:center; color:var(--ink); line-height:1.35; }
  .duel-reveal__verdict b{ font-weight:700; }
  .duel-reveal__scoreline{
    font-family: var(--font-display); font-size: 12px; font-weight: 600;
    color: var(--muted); text-align:center; margin-top: -6px;
  }

  /* ===== Ruleta rápida de buff compartido (ambos caen en el mismo ítem) ===== */
  .roulette-overlay{
    position: fixed; inset: 0; z-index: 60;
    display:flex; align-items:center; justify-content:center;
    background: var(--overlay);
  }
  .roulette-box{
    display:flex; flex-direction:column; align-items:center; gap:10px;
    padding: 18px 22px;
    border-radius: 16px;
    background: var(--paper);
    border: 1px solid var(--line);
    box-shadow: 0 12px 32px rgba(0,0,0,.18);
    animation: roulettePop .18s ease-out;
  }
  @keyframes roulettePop{ from{ transform: scale(.85); opacity: 0; } to{ transform: scale(1); opacity: 1; } }
  .roulette-item{ font-size: 34px; line-height: 1; }
  .roulette-caption{
    font-family: var(--font-body); font-size: 11px; font-weight: 600;
    letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
  }
  .roulette-names{ display:flex; gap:10px; }
  .roulette-name{
    font-family: var(--font-display); font-size: 15px; font-weight: 700;
    padding: 6px 14px; border-radius: 999px;
    border: 1px solid var(--line); color: var(--muted);
    max-width: 130px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
    transition: none;
  }
  .roulette-name.is-on{
    color: var(--ink);
    border-color: var(--accent);
    background: var(--accent-soft);
  }
  .roulette-name.is-winner{
    color: #fff;
    background: var(--accent);
    border-color: var(--accent);
    animation: rouletteWin .35s ease-out;
  }
  @keyframes rouletteWin{ 0%{ transform: scale(1); } 45%{ transform: scale(1.14); } 100%{ transform: scale(1); } }

  /* ===== Piedra-papel-tijera por el buff compartido ===== */
  /* Mismo backdrop/caja que la ruleta (.roulette-overlay/.roulette-box). Solo
     one-shots de transform/opacity — regla de perf del duelo; el contest termina
     antes del countdown, nunca convive con duel-play. */
  .rps-choices{ display:flex; gap:12px; }
  .rps-btn{
    font-size: 30px; line-height: 1;
    padding: 12px 16px; border-radius: 14px;
    border: 1px solid var(--line); background: var(--paper);
    cursor: pointer;
    transition: transform .08s ease-out;
  }
  .rps-btn:active{ transform: scale(.92); }
  .rps-btn:disabled{ pointer-events: none; }
  .rps-btn.is-picked{
    border-color: var(--accent);
    background: var(--accent-soft);
  }
  .rps-btn.is-dimmed{ opacity: .35; }
  /* Barra de tiempo para elegir (solo online, v0.3.40): reusa .idle-timer;
     acá solo se acota al ancho de la fila de botones dentro de la caja.
     Doble clase a propósito: .idle-timer (más abajo) pisa una clase sola. */
  .idle-timer.rps-timer{ width: 230px; margin-top: 4px; }
  .rps-status{
    min-height: 14px;
    font-family: var(--font-body); font-size: 11px; font-weight: 600;
    letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
  }
  .rps-reveal{ display:flex; align-items:center; gap:10px; }
  .rps-reveal[hidden]{ display:none; }   /* [hidden] pierde contra display:flex sin esto */
  .rps-vs{
    font-family: var(--font-body); font-size: 11px; font-weight: 600;
    text-transform: uppercase; color: var(--muted);
  }
  .rps-chip{
    font-family: var(--font-display); font-size: 15px; font-weight: 700;
    padding: 6px 14px; border-radius: 999px;
    border: 1px solid var(--line); color: var(--muted);
    max-width: 140px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .rps-chip.is-winner{
    color: #fff;
    background: var(--accent);
    border-color: var(--accent);
    animation: rouletteWin .35s ease-out;
  }
  @media (prefers-reduced-motion: reduce){
    .rps-chip.is-winner, .roulette-name.is-winner{ animation: none; }
    .rps-btn{ transition: none; }
  }

  .screen--result{ justify-content:center; gap:32px; text-align:center; }
  .result-verdict{ display:flex; flex-direction:column; align-items:center; gap:12px; }
  .result-eyebrow{ font-size:11px; font-weight:600; letter-spacing:.2em; text-transform:uppercase; color:var(--muted); }
  .result-title{ font-family:var(--font-display); font-weight:700; font-size:52px; letter-spacing:-.02em; line-height:.95; }
  .result-score{ font-family:var(--font-display); font-weight:500; font-size:22px; color:var(--muted); letter-spacing:.04em; }

  /* "+N HP" que aparece al ganar una ronda de Torneo con cura proporcional
     (v. game.js showHealPop). Un solo disparo, no infinita → transform/opacity ok.
     `forwards` es obligatorio: los keyframes terminan en opacity:0 y sin él,
     al terminar la animación el elemento vuelve a sus estilos base (opacidad 1)
     y el "+N HP" reaparece y queda pegado en pantalla. */
  .heal-pop{
    display:none; font-family:var(--font-display); font-weight:700; font-size:18px;
    color:var(--good); letter-spacing:.02em;
  }
  .heal-pop.is-show{
    display:block; animation: healPop 1.1s ease-out forwards;
  }
  @keyframes healPop{
    0%{ transform: translateY(6px) scale(.85); opacity: 0; }
    22%{ transform: translateY(-2px) scale(1.08); opacity: 1; }
    75%{ transform: translateY(-10px) scale(1); opacity: 1; }
    100%{ transform: translateY(-16px) scale(1); opacity: 0; }
  }
  @media (prefers-reduced-motion: reduce){
    .heal-pop.is-show{ animation: none; opacity: 1; }
  }
  /* Marcador de la serie online (mejor de 3): 3 puntos en orden cronológico.
     Gris = ronda pendiente, azul = ganada por vos, negro = por el rival.
     Vacío (fuera de la serie) no ocupa lugar. */
  .round-dots{ display:flex; gap:12px; justify-content:center; padding:4px 0; }
  .round-dots:empty{ display:none; }
  .round-dot{ width:14px; height:14px; border-radius:50%; background:var(--line); }
  .round-dot.is-you{ background:var(--accent); }
  .round-dot.is-opp{ background:var(--ink); }
  .result-title.is-win{ color: var(--good); }
  .result-title.is-lose{ color: var(--bad); }

  /* 🎖️ Bloque de EXP en el resultado (v0.3.36): chip "+N EXP", barra que se
     llena hacia el próximo nivel y flash dorado si sube. Oculto (hidden) salvo
     que endGame lo pinte. La barra transiciona su ancho una sola vez (fuera del
     duelo → sin costo de rAF); prefers-reduced-motion la deja sin transición. */
  .result-exp{ width:100%; max-width:320px; margin:0 auto; display:flex; flex-direction:column; gap:6px; }
  .result-exp[hidden]{ display:none; }
  .result-exp__row{ display:flex; align-items:baseline; justify-content:space-between; }
  .result-exp__gain{ font-family:var(--font-display); font-weight:700; font-size:16px; color:var(--accent); letter-spacing:.02em; }
  .result-exp__level{ font-family:var(--font-body); font-size:12px; color:var(--muted); }
  .result-exp__bar{ width:100%; height:8px; border-radius:999px; background:var(--line); overflow:hidden; }
  .result-exp__fill{ height:100%; width:0; border-radius:999px; background:var(--accent); transition:width .7s cubic-bezier(.2,.8,.3,1); }
  .result-exp__levelup{
    font-family:var(--font-display); font-weight:700; font-size:14px; color:#9A7B14;
    letter-spacing:.02em; text-align:center;
  }
  [data-theme="dark"] .result-exp__levelup{ color:#D4AF37; }
  .result-exp__levelup[hidden]{ display:none; }
  .result-exp__levelup.is-show{ animation: expLevelUp .9s ease-out; }
  @keyframes expLevelUp{
    0%{ transform: translateY(4px) scale(.8); opacity:0; }
    40%{ transform: translateY(0) scale(1.1); opacity:1; }
    100%{ transform: translateY(0) scale(1); opacity:1; }
  }
  @media (prefers-reduced-motion: reduce){
    .result-exp__fill{ transition:none; }
    .result-exp__levelup.is-show{ animation:none; }
  }

  /* Entrada animada del título de resultado (torneo offline y online): se
     re-dispara con pulseResultTitle() sacando/poniendo la clase con reflow
     forzado en el medio, porque una animación CSS no se reinicia sola. */
  @keyframes resultTitlePop{
    0%{ transform: scale(.82); opacity: 0; }
    60%{ transform: scale(1.06); opacity: 1; }
    100%{ transform: scale(1); opacity: 1; }
  }
  .result-title.is-pulse{ animation: resultTitlePop .38s cubic-bezier(.2,.8,.3,1.3); }
  @media (prefers-reduced-motion: reduce){
    .result-title.is-pulse{ animation: none; }
  }

  /* Momento de campeón (torneo offline de 8 y torneo online x4): mismo dorado
     que ya usa "Rey Julian" (is-king) para consistencia visual. */
  .result-title.is-champion{ color:#9A7B14; animation: resultTitlePop .38s cubic-bezier(.2,.8,.3,1.3), championGlow 1.1s ease-out; }
  [data-theme="dark"] .result-title.is-champion{ color:#D4AF37; }
  @keyframes championGlow{
    0%{ text-shadow: 0 0 0 rgba(212,175,55,0); }
    40%{ text-shadow: 0 0 18px rgba(212,175,55,.9), 0 0 30px rgba(212,175,55,.5); }
    100%{ text-shadow: 0 0 8px rgba(212,175,55,.35); }
  }
  @media (prefers-reduced-motion: reduce){
    .result-title.is-champion{ animation: none; }
  }

  /* Fila de chips con el progreso del torneo offline (uno por rival del
     roster); solo la tiene contenido cuando el resultado viene de Tourney,
     así que queda vacía (y oculta) para online/campaña/partida suelta. */
  .tourney-progress{ display:flex; gap:6px; flex-wrap:wrap; justify-content:center; align-items:center; max-width:320px; margin:0 auto; }
  .tourney-progress:empty{ display:none; }
  .tp-chip{
    width:30px; height:30px; border-radius:50%;
    display:flex; align-items:center; justify-content:center;
    font-size:15px; border:2px solid var(--line); background:var(--field);
    opacity:.4;
  }
  .tp-chip.is-beaten{ opacity:1; border-color:var(--good); }
  .tp-chip.is-current{ opacity:1; border-color:var(--accent); box-shadow:0 0 0 2px var(--accent-soft); }
  .tp-chip.is-king{ border-color:#D4AF37; }

  /* ===== Torneo online (x4) ===== */
  /* Solo el COLOR (tinte del jugador en torneo online). Antes era `background:`
     (shorthand) y, al ir después de .has-sprite con igual especificidad, rompía
     el pintado del sprite de la ficha propia (skin). Debe quedar background-color. */
  .cell .player-marker.is-you{ background-color: var(--you-accent, var(--accent)); }
  .p-dot{ width:10px; height:10px; border-radius:50%; display:inline-block; flex:0 0 auto; }
  #ot-box{ width:100%; max-width:300px; flex-direction:column; gap:8px; }
  .ot-row{ display:flex; align-items:center; gap:10px; padding:9px 13px;
    border:1px solid rgba(127,127,127,.28); border-radius:11px; font-size:14px; color:var(--ink); }
  .ot-row.is-free{ opacity:.45; border-style:dashed; }
  .ot-row .ot-tag{ margin-left:auto; font-size:11px; color:var(--muted); letter-spacing:.04em; }
  #screen-othub, #screen-spectate{ justify-content:center; align-items:center; gap:22px; }
  #othub-list{ width:100%; max-width:320px; display:flex; flex-direction:column; gap:9px; }
  .ot-match{ border:1px solid rgba(127,127,127,.25); border-radius:12px; padding:11px 13px;
    display:flex; flex-direction:column; gap:7px; font-size:14px; color:var(--ink); }
  .ot-match .ot-p{ display:flex; align-items:center; gap:9px; }
  .ot-match .ot-p.is-winner{ font-weight:700; }
  .ot-match .ot-p.is-loser{ opacity:.45; text-decoration:line-through; }
  .ot-match .ot-vs{ font-size:10px; letter-spacing:.16em; color:var(--muted); text-transform:uppercase; }
  .ot-match .btn{ margin-top:4px; }
  #spec-board{ width:min(88vw, 380px); aspect-ratio:1; display:grid; gap:4px; }
  #spec-head{ display:flex; align-items:center; gap:10px; font-size:14px; font-weight:600; color:var(--ink); flex-wrap:wrap; justify-content:center; }
  #spec-note{ font-size:13px; color:var(--muted); min-height:18px; text-align:center; }

  #toast{
    position:fixed; left:50%; bottom:34px; transform:translateX(-50%) translateY(20px);
    background:var(--ink); color:var(--field); padding:13px 20px; border-radius:var(--r-md);
    font-size:14px; font-weight:500; max-width:90%; text-align:center; line-height:1.4;
    opacity:0; pointer-events:none; transition: all .3s ease; z-index:50;
  }
  #toast.is-show{ opacity:1; transform:translateX(-50%) translateY(0); }

  /* ---- Chat en vivo (solo online) ---- */
  .chat-widget{
    position:fixed; right:14px; bottom:76px; z-index:18;
    display:flex; flex-direction:column; align-items:flex-end; gap:10px;
    pointer-events:none;   /* solo los hijos interactivos capturan */
  }
  .chat-toggle{
    pointer-events:auto; position:relative; align-self:flex-end;
    width:48px; height:48px; border-radius:50%; border:1px solid var(--line);
    background:var(--field); color:var(--ink); font-size:22px; line-height:1;
    display:flex; align-items:center; justify-content:center; cursor:pointer;
    box-shadow:0 3px 12px rgba(0,0,0,0.18); transition:transform .15s ease;
  }
  .chat-toggle:active{ transform:scale(0.94); }
  .chat-badge{
    position:absolute; top:-4px; right:-4px; min-width:18px; height:18px; padding:0 4px;
    border-radius:9px; background:var(--bad); color:#fff; font-size:11px; font-weight:700;
    display:flex; align-items:center; justify-content:center; line-height:1;
  }
  .chat-panel{
    pointer-events:auto; width:min(78vw, 300px); height:280px;
    background:var(--field); border:1px solid var(--line); border-radius:var(--r-md);
    box-shadow:0 6px 22px rgba(0,0,0,0.22); overflow:hidden;
    display:none; flex-direction:column;
  }
  .chat-widget.is-open .chat-panel{ display:flex; }
  .chat-log{
    flex:1; overflow-y:auto; padding:10px; display:flex; flex-direction:column; gap:6px;
  }
  .chat-msg{
    max-width:82%; align-self:flex-start; padding:6px 9px; border-radius:var(--r-sm);
    background:var(--accent-soft); display:flex; flex-direction:column; gap:1px;
  }
  .chat-msg.is-mine{ align-self:flex-end; background:var(--accent); }
  .chat-msg__name{ font-size:10px; font-weight:700; color:var(--muted); letter-spacing:.02em; }
  .chat-msg.is-mine .chat-msg__name{ color:rgba(255,255,255,0.85); }
  .chat-msg__text{ font-size:13.5px; line-height:1.35; color:var(--ink); word-break:break-word; }
  .chat-msg.is-mine .chat-msg__text{ color:#fff; }
  .chat-form{
    display:flex; gap:6px; padding:8px; border-top:1px solid var(--line); background:var(--field);
  }
  .chat-input{
    flex:1; min-width:0; padding:8px 10px; border:1px solid var(--line); border-radius:var(--r-sm);
    background:var(--paper); color:var(--ink); font-family:var(--font-body); font-size:14px;
  }
  .chat-input:focus{ outline:none; border-color:var(--accent); }
  .chat-send{
    flex:0 0 auto; width:40px; border:none; border-radius:var(--r-sm);
    background:var(--accent); color:#fff; font-size:16px; cursor:pointer;
  }
  .chat-send:active{ transform:scale(0.94); }

  .howto{
    position:absolute; inset:0; background:var(--overlay);
    display:flex; flex-direction:column; align-items:center; justify-content:center; gap:14px;
    text-align:center; padding:30px; z-index:25;
    opacity:0; visibility:hidden; transform:translateY(8px);
    transition:opacity .32s ease, transform .32s ease, visibility .32s;
  }
  .howto.is-show{ opacity:1; visibility:visible; transform:none; }
  .howto__title{ font-family:var(--font-display); font-weight:700; font-size:24px; }
  .howto__text{ font-size:14px; color:var(--muted); line-height:1.55; max-width:320px; }
  .howto__hint{ font-size:13px; color:var(--accent); font-weight:600; margin-top:4px; }
  .howto__legend{
    display:flex; flex-direction:column; gap:6px; margin-top:6px;
    font-size: 13px; color: var(--muted);
  }
  .howto__legend span{ display:flex; align-items:center; justify-content:center; gap:8px; }
  .howto__legend b{ font-family: var(--font-display); font-weight:700; min-width:20px; }
  .howto__legend .sym-atk{ color: var(--atk); }
  .howto__legend .sym-def{ color: var(--def); }
  .howto__legend .sym-down{ color: var(--bad); }

  /* Franja mini decorativa del speedómetro real (mismos colores, aguja animada
     por CSS en loop continuo — no hay duelo corriendo detrás, es solo ilustrativo). */
  .howto-speedo{
    position:relative; width:100%; max-width:260px; height:14px;
    border-radius:7px; overflow:hidden;
    background:var(--field); border:1px solid var(--line);
    margin: 2px 0;
  }
  /* El barrido anima transform (compositado por GPU) en vez de left (dispara
     layout en cada frame y se ve trabado) — el wrapper ocupa el 100% de la
     franja para que translateX(%) (relativo a su propio ancho) recorra todo
     el contenedor sin fijar píxeles. */
  .howto-speedo__sweep{
    position:absolute; top:0; bottom:0; left:0; width:100%;
    animation: howtoNeedleSweep .9s ease-in-out infinite alternate;
  }
  .howto-speedo__needle{
    position:absolute; top:-4px; bottom:-4px; left:0; width:3px;
    background:var(--ink); border-radius:2px;
  }
  @keyframes howtoNeedleSweep{
    from{ transform:translateX(0); }
    to{ transform:translateX(calc(100% - 3px)); }
  }

  /* Globos "Vos"/rival al arrancar la partida (game.js: showStartBubbles) */
  .start-bubble{
    position:absolute; z-index:30; pointer-events:none;
    padding:6px 14px; border-radius:999px;
    font-family:var(--font-display); font-weight:700; font-size:14px; white-space:nowrap;
    opacity:0; transform:translate(-50%, calc(-100% - 14px)) scale(.85);
    transition: opacity .2s ease, transform .2s ease;
  }
  .start-bubble::after{
    content:''; position:absolute; left:50%; bottom:-5px; transform:translateX(-50%);
    width:0; height:0; border-left:6px solid transparent; border-right:6px solid transparent; border-top:6px solid;
  }
  .start-bubble.is-you{ background:var(--accent); color:var(--field); }
  .start-bubble.is-you::after{ border-top-color:var(--accent); }
  .start-bubble.is-opp{ background:var(--ink); color:var(--field); }
  .start-bubble.is-opp::after{ border-top-color:var(--ink); }
  .start-bubble.is-show{ opacity:1; transform:translate(-50%, calc(-100% - 14px)) scale(1); }
  .start-bubble.is-gone{
    transition: opacity .35s ease, transform .35s ease;
    opacity:0; transform:translate(-50%, calc(-100% - 26px)) scale(.92);
  }

  /* Barra de auto-movimiento por inactividad (solo online, v0.2.96):
     invisible hasta el segundo 3, después se achica y cambia de color
     hasta el auto-movimiento a los 6s. */
  .idle-timer{
    width:100%; max-width:480px; height:6px; margin-top:10px;
    border-radius:999px; overflow:hidden;
    background:var(--field); border:1px solid var(--line);
    opacity:0; visibility:hidden; transition:opacity .2s ease;
  }
  .idle-timer.is-show{ opacity:1; visibility:visible; }
  .idle-timer__fill{ height:100%; width:100%; border-radius:999px; background:var(--good); }
  .idle-timer__fill.is-yellow{ background:var(--warn); }
  .idle-timer__fill.is-red{ background:var(--bad); }
  /* Último segundo: brillo rojo titilante alrededor de la barra */
  .idle-timer.is-danger{ animation: idleDanger .5s ease-in-out infinite; }
  @keyframes idleDanger{
    0%,100%{ box-shadow: 0 0 0 0 rgba(190,50,50,0); }
    50%{ box-shadow: 0 0 10px 3px rgba(190,50,50,.6); }
  }

  @media (prefers-reduced-motion: reduce){
    *{ animation-duration:.01ms !important; transition-duration:.05ms !important; }
  }

  /* ==== Campaña ==== */

  /* Menú de confirmación de inicio: fondo PLANO opaco (--paper) que cubre todo.
     Al confirmar, .camp-box se desvanece en 3s y queda solo el fondo plano. */
  .camp-overlay[hidden]{ display:none; }  /* sin esto, el display:flex le gana al atributo hidden */
  .camp-overlay{
    position:absolute;
    inset:0;
    z-index:300;
    background:var(--paper);
    display:flex;
    align-items:center;
    justify-content:center;
    padding:28px;
  }
  .camp-box{
    display:flex;
    flex-direction:column;
    align-items:center;
    gap:22px;
    text-align:center;
    max-width:320px;
    width:100%;
    opacity:1;
    transition:opacity 3s ease;
  }
  .camp-box.is-fading{
    opacity:0;
    pointer-events:none;
  }
  .camp-eyebrow{
    font-family:var(--font-body);
    font-size:11px;
    font-weight:600;
    letter-spacing:.22em;
    text-transform:uppercase;
    color:var(--muted);
  }
  .camp-title{
    font-family:var(--font-display);
    font-size:24px;
    font-weight:600;
    line-height:1.35;
    color:var(--ink);
  }
  .camp-title b{ color:var(--accent); }
  .camp-actions{
    display:flex;
    flex-direction:column;
    gap:12px;
    width:100%;
  }

  /* Escenas de texto de la campaña: líneas que aparecen de a una sobre fondo plano */
  .screen--scene{
    justify-content:center;
    align-items:center;
    gap:30px;
    padding:36px;
    background:var(--paper);
  }
  .scene-text{
    display:flex;
    flex-direction:column;
    gap:18px;
    max-width:340px;
    width:100%;
  }
  .scene-line{
    font-family:var(--font-display);
    font-size:18px;
    line-height:1.5;
    color:var(--ink);
    text-align:center;
    opacity:0;
    animation:sceneLineIn 1.2s ease forwards;
  }
  @keyframes sceneLineIn{
    from{ opacity:0; transform:translateY(8px); }
    to{ opacity:1; transform:translateY(0); }
  }
  .scene-continue{
    max-width:220px;
    width:100%;
    opacity:0;
    animation:sceneLineIn .8s ease forwards;
  }

  /* Entrada instantánea de pantalla (campaña: la partida aparece de golpe) */
  .screen.is-instant{ transition:none; }

/* ===== 👤 Usuario (v0.2.79) ===== */
/* Usuario permanente abajo del nickname en el HUD: chico y gris. */
.hud-user{
  font-family:var(--font-body);
  font-size:10px;
  font-weight:500;
  letter-spacing:.03em;
  color:var(--muted);
  line-height:1.2;
  margin-top:1px;
}
.hud-user:empty{ display:none; }

/* Usuario en la lista del lobby de torneo */
.ot-user{
  font-size:10.5px;
  font-weight:500;
  color:var(--muted);
  letter-spacing:.02em;
}

/* Ícono de usuario (top-controls): punto de acento cuando ya hay usuario */
.theme-toggle.has-user::after{
  content:'';
  position:absolute;
  top:5px; right:5px;
  width:6px; height:6px;
  border-radius:50%;
  background:var(--accent);
}

/* Overlay de creación de usuario (mismo patrón que camp-overlay) */
.user-overlay[hidden]{ display:none; }  /* [hidden] pierde contra display:flex sin esto */
.user-overlay{
  position:absolute;
  inset:0;
  z-index:300;
  background:var(--paper);
  display:flex;
  align-items:center;
  justify-content:center;
  padding:28px;
}
.user-box{
  display:flex;
  flex-direction:column;
  align-items:center;
  gap:16px;
  text-align:center;
  max-width:320px;
  width:100%;
}
.user-box .input{ text-align:center; text-transform:lowercase; }
.user-hint{
  font-family:var(--font-body);
  font-size:12.5px;
  color:var(--muted);
  line-height:1.5;
}
.user-err{
  font-family:var(--font-body);
  font-size:12px;
  color:var(--bad);
  min-height:15px;
  line-height:1.3;
}

/* Link para alternar registro/login en el overlay de cuenta (v0.2.80) */
.user-switch{
  border:none;
  background:transparent;
  font-family:var(--font-body);
  font-size:12.5px;
  font-weight:500;
  color:var(--muted);
  text-decoration:underline;
  text-underline-offset:3px;
  cursor:pointer;
  padding:4px;
}
.user-box .input + .input{ margin-top:-6px; }

/* Retomar torneo: lista de rivales ya alcanzados (cache local o de cuenta) */
.tr-list{ display:flex; flex-direction:column; gap:6px; width:100%; max-height:280px; overflow-y:auto; }
.tr-row{
  display:flex; justify-content:space-between; align-items:center;
  padding:10px 14px; border-radius:var(--r-sm); border:1.5px solid var(--line);
  background:var(--field); cursor:pointer; text-align:left;
}
.tr-row:hover{ border-color:var(--accent); }
.tr-row__name{ font-family:var(--font-display); font-weight:600; font-size:14px; color:var(--ink); }
.tr-row__hp{ font-size:11px; color:var(--muted); }

/* Selector de niveles de campaña: octógonos numerados (1, 2, 3…), con los
   bordes laterales (las diagonales de esquina) más cortos que los lados
   rectos. El borde se dibuja con dos capas clipeadas con el MISMO polígono
   (un ::before más grande de color borde + un ::after inset de color fondo)
   porque el `border` normal no seguiría el clip-path en las diagonales. */
.level-hex-grid{ display:flex; flex-wrap:wrap; justify-content:center; gap:14px; padding:4px 0; }
.level-hex{
  position:relative; width:60px; height:60px; border:none; padding:0;
  background:transparent; cursor:pointer;
}
.level-hex::before,
.level-hex::after{
  content:''; position:absolute; inset:0;
  clip-path: polygon(38% 0%, 62% 0%, 100% 38%, 100% 62%, 62% 100%, 38% 100%, 0% 62%, 0% 38%);
}
.level-hex::before{ background:var(--line); }
.level-hex::after{ inset:2px; background:var(--field); }
.level-hex:hover::before{ background:var(--accent); }
.level-hex.is-current::before{ background:var(--accent); }
.level-hex.is-current::after{ background:color-mix(in srgb, var(--accent) 12%, var(--field)); }
.level-hex__num{
  position:relative; z-index:1;
  font-family:var(--font-display); font-weight:800; font-size:20px; color:var(--ink);
}
.level-hex.is-current .level-hex__num{ color:var(--accent); }

/* Perfil: stats propias dentro del overlay de cuenta (v0.2.84) */
.user-stats{
  width:100%;
  background:var(--field);
  border:1px solid var(--line);
  border-radius:var(--r-md);
  padding:12px 16px;
  text-align:left;
}
.user-stats__row{
  display:flex;
  align-items:center;
  justify-content:space-between;
  padding:6px 0;
  border-bottom:1px solid var(--line);
  font-family:var(--font-body);
  font-size:13px;
  color:var(--ink);
}
.user-stats__row b{ font-family:var(--font-display); color:var(--accent); }
.user-stats__ach{
  display:flex;
  align-items:center;
  justify-content:space-between;
  padding-top:10px;
  font-family:var(--font-body);
  font-size:13px;
  color:var(--muted);
}
.user-stats__soon{ color:var(--muted); opacity:.7; }

/* 🎖️ Nivel del jugador dentro del perfil (v0.3.36): "Nivel N" + barra + "X / Y
   EXP". Vive arriba de las stats; el nivel existe para cualquiera (incluso sin
   cuenta), pero este bloque solo se ve en el overlay de sesión. */
.user-level{ padding:2px 0 10px; border-bottom:1px solid var(--line); margin-bottom:4px; }
.user-level__head{ display:flex; align-items:baseline; justify-content:space-between; margin-bottom:6px; }
.user-level__lvl{ font-family:var(--font-display); font-weight:700; font-size:15px; color:var(--accent); }
.user-level__exp{ font-family:var(--font-body); font-size:11px; color:var(--muted); }
.user-level__bar{ width:100%; height:7px; border-radius:999px; background:var(--line); overflow:hidden; }
.user-level__fill{ height:100%; width:0; border-radius:999px; background:var(--accent); transition:width .5s ease-out; }
@media (prefers-reduced-motion: reduce){ .user-level__fill{ transition:none; } }

/* 🎨 Selector de skin del perfil (bola al centro, flechas a los costados) */
.user-skin{ width:100%; text-align:center; margin-top:2px; }
.user-skin__label{
  display:block;
  font-family:var(--font-body);
  font-size:12px;
  color:var(--muted);
  margin-bottom:8px;
}
.user-skin__pick{
  display:flex;
  align-items:center;
  justify-content:center;
  gap:20px;
}
.user-skin__arrow{
  background:none;
  border:none;
  cursor:pointer;
  font-size:20px;
  line-height:1;
  color:var(--ink);
  opacity:.55;
  padding:8px 10px;
  transition:opacity .15s, transform .15s;
}
.user-skin__arrow:hover{ opacity:1; }
.user-skin__arrow:active{ transform:scale(.88); }
.skin-preview{
  position:relative;
  width:56px; height:56px;
  border-radius:50%;
  background:var(--accent);
  box-shadow:0 2px 10px rgba(43,77,224,0.4);
  flex:0 0 auto;
}
.skin-preview::after{
  content:'';
  position:absolute;
  top:15%; left:15%;
  width:30%; height:30%;
  border-radius:50%;
  background:rgba(255,255,255,0.35);
}
.skin-preview.has-sprite{
  background-color:transparent;
  background-size:cover;
  background-position:center;
  box-shadow:none;
}
.skin-preview.has-sprite::after{ display:none; }
.user-skin__name{
  display:block;
  font-family:var(--font-display);
  font-size:14px;
  color:var(--ink);
  margin-top:8px;
}
