    body {
      font-family: "Montserrat", sans-serif;
      background:
        radial-gradient(circle at top left, rgba(181,39,0,.08), transparent 24%),
        radial-gradient(circle at top right, rgba(82,110,59,.09), transparent 24%),
        linear-gradient(180deg, #fff8ed 0%, #f5efe6 100%);
    }
    .academy-card {
      background: linear-gradient(160deg, rgba(255,255,255,.95) 0%, rgba(255,248,237,.98) 100%);
      box-shadow: 0 18px 40px rgba(0,0,0,.08);
    }
    /* Visual Fit V5 (Practice board+rail, Jorge approved): Area
       privada/Entrenamiento/Idioma move OFF the fixed corner overlay
       and INTO Rail A, in normal flow (spec section 12) -- no more
       position:fixed cluster to clear, no more #practice-main
       padding-top reservation. Kept as its own flex-row nav, now
       simply the first row of .board-right-rail's internal grid
       (grid-area assigned below in the V5 rail block). */
    .practice-corner-controls {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 6px;
    }
    .practice-corner-link {
      display: inline-flex;
      align-items: center;
      border-radius: 999px;
      border: 1px solid rgba(33,33,33,.15);
      background: #fff;
      padding: 5px 11px;
      font-size: .78rem;
      font-weight: 600;
      color: #212121;
      text-decoration: none;
      white-space: nowrap;
    }
    .practice-corner-link-accent {
      background: #B52700;
      border-color: #B52700;
      color: #fff;
    }
    .practice-corner-lang {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      border-radius: 999px;
      border: 1px solid rgba(33,33,33,.15);
      background: #fff;
      padding: 5px 10px;
      font-size: .7rem;
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: .16em;
      color: #788089;
      white-space: nowrap;
    }
    .practice-corner-lang select {
      border: none;
      background: transparent;
      font: inherit;
      color: #212121;
    }
    /* Visual Fit V5: #practice-main no longer needs any padding-top
       reservation for a fixed corner cluster (retired above). Instead,
       at >=1001px (board-first desktop/laptop), <main> and #practice-shell
       shed their normal Tailwind vertical padding/margin entirely -- the
       board's viewport-first formula (see .board-container below) is
       what actually bounds the layout now, so any leftover ancestor
       padding above/below it is exactly the kind of dead vertical band
       the V5 board contract forbids. Below 1001px the page stacks
       (board-first mobile, R3) and keeps its normal padding -- scroll
       is expected and fine there. */
    @media (min-width: 1081px) {
      #practice-main {
        padding-top: 3px;
        padding-bottom: 3px;
      }
      #practice-shell.container-practice {
        padding-top: 0;
        padding-bottom: 0;
        margin-top: 0;
      }
      /* Sin esto, .practice-layout/.board-panel/.board-stage son bloques
         "auto" (crecen con su contenido) y nada limita realmente la
         altura del rail al viewport: con contenido real (explicacion,
         solucion, valoracion) el rail crece y empuja al board hacia
         abajo/fuera de la pantalla, aunque .board-container calcule bien
         su propio tamano. Este es el techo real que .board-right-rail
         necesita para que su overflow:hidden llegue a aplicar algo. */
      .practice-layout {
        height: calc(100dvh - (var(--qce-board-edge) * 2));
        min-height: 0;
      }
    }
    .container-practice {
      max-width: 1480px;
      margin: 0 auto;
      padding: 12px 16px;
    }
    /* Visual Fit V4 (2026-09-04, Jorge approved): the board's own shell
       (#practice-shell) no longer shares the 1480px ceiling used by the
       promo/fallback surfaces above -- board sizing is viewport-first
       (see .board-panel and .board-container below). The promo card and
       #fallback-shell keep the original 1480px cap unchanged: they are
       not part of the board's path and stay visually neutral. */
    #practice-shell.container-practice {
      max-width: none;
    }
    /* Practice C: board-first en escritorio/portátil. La tarjeta
       promocional "Jugar Quaternity" no se muestra encima del tablero
       mientras se practica; su contenido sigue existiendo en el DOM
       (no se borra) y se restaura sin cambios en <=1024px (ver media
       query existente más abajo), donde el layout ya se apila. */
    section[aria-labelledby="practice-play-title"] {
      display: none;
    }
    .practice-layout {
      display: block;
      width: 100%;
    }
    /* Visual Fit V5 (Practice board+rail, Jorge approved): rail width now
       lives in --qce-rail-a-w/--qce-rail-b-w (below), shared between
       .board-stage's own column track and .board-right-rail's internal
       grid, so both stay in sync from one source. The whole-rail scroll
       (max-height + overflow-y:auto) is retired: it used to scroll ALL
       of the rail as one block whenever content grew tall (task,
       explanation, guided-sequence, solution, rating all together).
       Now only the contextual Rail B zone (#rail-b-panel) scrolls
       internally when it genuinely overflows -- Rail A's own essential
       controls stay reachable without scroll (spec section 8). */
    .gadget-secondary {
      min-width: 0;
    }
    /* Cambio de regimen (Jorge, "DOS RAILS OBLIGATORIOS EN MODO
       LATERAL"): Rail B ya NO depende de un breakpoint como >=1500px --
       mientras Practice este en modo lateral (>1080px, ver
       @media max-width:1080px mas abajo) los DOS rails coexisten
       siempre. Los rails se comprimen (clamp con minimos mas bajos que
       antes) en vez de que Rail B desaparezca. --qce-rail-total-w
       incluye Rail B incondicionalmente. El umbral de apilado sube de
       1000 a 1080px: medido con datos reales, a 1024x900 dos rails
       dejaban el board en 490x490 (limitado por ancho) mientras que
       apilado a esa misma anchura el board llega a ~894x894 (limitado
       por alto) -- el board domina mucho mas apilado ahi. A 1120px
       (>1080) los dos rails no le cuestan nada al board (ya viene
       limitado por alto, 464x464, igual en los dos regimenes), asi que
       ahi si merece la pena mantener el layout lateral con dos rails. */
    :root {
      --qce-rail-a-w: clamp(186px, 15vw, 300px);
      --qce-rail-b-w: clamp(186px, 14vw, 320px);
      --qce-rail-gap: clamp(6px, 1vw, 20px);
      --qce-rail-total-w: calc(var(--qce-rail-a-w) + var(--qce-rail-gap) + var(--qce-rail-b-w));
      --qce-board-edge: 3px;
      --qce-coord-gutter: 1.15rem;
      --qce-coord-font: .8rem;
    }
    .board-panel {
      grid-area: board;
      min-width: 0;
      /* Visual Fit V4: 1380px ceiling removed together with
         #practice-shell's 1480px cap above -- the board-stage grid below
         (fr column + subordinate rail) is what actually bounds the
         board's usable width now, not a fixed px ceiling on this
         wrapper. */
      width: 100%;
      padding: .22rem;
      justify-self: center;
    }
    @media (min-width: 1081px) {
      .board-panel {
        height: 100%;
        min-height: 0;
      }
    }
    .board-wrapper {
      display: flex;
      justify-content: center;
      width: 100%;
    }
    .board-container,
    .board-area {
      /* Visual Fit V5 (Practice board+rail, Jorge approved): the -114px/
         -116px reservations are retired for good -- they budgeted for a
         header/corner-cluster band that no longer exists (nav moved
         into Rail A, in flow; see .practice-corner-controls and
         .board-right-rail). #practice-main/#practice-shell now carry
         only --qce-board-edge (3px) of padding above/below at
         >=1001px, so the board's own vertical budget is
         100dvh minus that same minimal edge on both sides --
         validated in tmp/practice-v5-prototype/ at 1746x842 (836px,
         3px/3px), 1366x768 (762px, 3px/3px) and 1120x470 (464px,
         3px/3px), zero page overflow in all three. */
      width: min(100%, calc(100dvh - (var(--qce-board-edge) * 2)));
    }
    .board-area {
      aspect-ratio: 1 / 1;
      justify-self: center;
    }
    .summary-card {
      border-radius: 1.55rem;
      padding: 1rem 1rem .95rem;
    }
    .summary-top {
      display: flex;
      align-items: flex-start;
      justify-content: space-between;
      gap: 1rem;
      margin-bottom: .7rem;
    }
    .summary-head {
      min-width: 0;
    }
    .summary-block {
      min-width: 0;
    }
    /* Visual Fit V5: kicker en mayusculas, referencia Escuela/Curso Q --
       color neutro por defecto, con acentos puntuales por bloque via
       selector de ID mas abajo (INTERACCION=verde, OBJETIVO/NIVEL=azul,
       EXPLICACION/FEEDBACK=azul, SOLUCION=verde). */
    .summary-label {
      font-size: .64rem;
      font-weight: 800;
      letter-spacing: .14em;
      text-transform: uppercase;
      color: #6e6a64;
    }
    .compact-legend-stack {
      display: contents;
    }
    /* Misma familia de tarjeta que .rail-panel (indice de Escuela):
       blanco, radio moderado, sombra muy sutil, borde superior fino de
       color asignado por bloque (ver seccion de acentos QCE). */
    .compact-highlight {
      border-radius: .95rem;
      border-top: 3px solid transparent;
      border-left: 0;
      background: #fff;
      box-shadow: 0 1px 2px rgba(16,24,32,.05), 0 8px 20px rgba(16,24,32,.05);
      padding: .8rem .9rem;
    }
    .difficulty-badge {
      margin-top: .68rem;
      display: inline-flex;
      align-items: center;
      gap: .45rem;
      width: fit-content;
      border-radius: 999px;
      border: 1px solid currentColor;
      background: rgba(33,33,33,.04);
      padding: .28rem .68rem;
      font-size: .72rem;
      font-weight: 900;
      letter-spacing: .12em;
      text-transform: uppercase;
    }
    .difficulty-badge.hidden {
      display: none;
    }
    .difficulty-basico {
      color: #2ecc71;
    }
    .difficulty-medio {
      color: #f1c40f;
    }
    .difficulty-intermedio {
      color: #5dade2;
    }
    .difficulty-avanzado {
      color: #e67e22;
    }
    .difficulty-experto {
      color: #e74c3c;
    }
    .difficulty-selector-wrap {
      margin-top: .7rem;
      display: grid;
      gap: .35rem;
    }
    .difficulty-selector-wrap.hidden {
      display: none;
    }
    .difficulty-selector {
      width: 100%;
      border-radius: .85rem;
      border: 1px solid rgba(255,255,255,.16);
      background: rgba(255,255,255,.08);
      color: #fff;
      padding: .45rem .62rem;
      font-size: .78rem;
      font-weight: 800;
      outline: none;
    }
    .difficulty-selector option {
      color: #111827;
    }
    .difficulty-selector-wrap.is-light .difficulty-selector {
      border-color: rgba(33,33,33,.16);
      background: #fff;
      color: #212121;
    }
    .difficulty-selector-wrap.is-light .difficulty-selector-label {
      color: #526e3b;
    }
    .difficulty-selector-label {
      font-size: .68rem;
      font-weight: 900;
      letter-spacing: .12em;
      text-transform: uppercase;
    }
    .difficulty-rating-scale {
      display: flex;
      justify-content: space-between;
      gap: .75rem;
      color: #6b7280;
      font-size: .75rem;
      line-height: 1.4;
    }
    .practice-queue-panel {
      display: grid;
      gap: .55rem;
      border-top: 1px solid rgba(33,33,33,.08);
      padding-top: .8rem;
    }
    .practice-queue-summary {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: .75rem;
      font-size: .76rem;
      font-weight: 800;
      color: #52606d;
    }
    .practice-queue-mode {
      display: inline-flex;
      align-items: center;
      min-height: 1.75rem;
      border-radius: 999px;
      background: #fff8ed;
      padding: .25rem .65rem;
      color: #526e3b;
    }
    .practice-queue-actions {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: .5rem;
    }
    .practice-queue-actions.hidden {
      display: none;
    }
    .practice-queue-actions [hidden] {
      display: none;
    }
    .practice-queue-actions button,
    .practice-queue-actions a {
      display: inline-flex;
      min-height: 44px;
      align-items: center;
      justify-content: center;
      border: 1px solid rgba(33,33,33,.12);
      border-radius: .75rem;
      background: #fff;
      padding: .55rem .65rem;
      text-align: center;
      font-size: .76rem;
      font-weight: 800;
      color: #212121;
    }
    .practice-queue-actions button:hover,
    .practice-queue-actions a:hover {
      border-color: rgba(82,110,59,.45);
      background: #fff8ed;
    }
    @media (max-width: 430px) {
      .practice-queue-actions {
        grid-template-columns: 1fr;
      }
    }
    /* #training-meta-grid (Interaccion) es la tarjeta QCE real (verde,
       ver acentos mas abajo); sus dos hijos (turno/ayuda) ya no llevan
       caja propia -- una sola tarjeta para ambos, no dos anidadas. */
    .compact-meta-grid {
      border-radius: .95rem;
      border-top: 3px solid transparent;
      background: #fff;
      box-shadow: 0 1px 2px rgba(16,24,32,.05), 0 8px 20px rgba(16,24,32,.05);
      padding: .8rem .9rem;
      display: grid;
      gap: .55rem;
    }
    .compact-meta-card {
      border-radius: .95rem;
      border-top: 3px solid transparent;
      background: #fff;
      box-shadow: 0 1px 2px rgba(16,24,32,.05), 0 8px 20px rgba(16,24,32,.05);
      padding: .72rem .8rem;
    }
    #training-meta-grid.compact-meta-grid > .compact-meta-card {
      border-radius: 0;
      border-top: 0;
      background: none;
      box-shadow: none;
      padding: 0;
    }
    .compact-meta-card .board-side-note {
      margin-top: .42rem;
      border: 0;
      background: transparent;
      box-shadow: none;
      padding: 0;
      color: #3f474f;
    }
    /* board-stage/board-right-rail deben declararse ANTES del bloque
       @media (max-width: 1080px) de más abajo: con igual especificidad,
       CSS resuelve el empate por orden de aparición en el archivo, así
       que si esta regla base fuera posterior a la media query anularía
       el apilado móvil aunque el media query "gane" visualmente en el
       código. Ver fix(practice): restore mobile board-first layout. */
    /* Visual Fit V5 (Practice board+rail, Jorge approved): el tablero
       manda y va a la DERECHA (spec section 1/3); el rail (Rail A, con
       Rail B fusionado o como segunda columna interna) va a la
       IZQUIERDA. align-items:stretch (antes "start") para que el rail
       ocupe todo el alto de la fila -- el board sigue calculando su
       propio tamaño via .board-container/.board-area (aspect-ratio +
       align-self/justify-self:center en .board-core, mas abajo), sin
       verse afectado por stretch. */
    .board-stage {
      display: grid;
      grid-template-columns: var(--qce-rail-total-w) minmax(0, 1fr);
      grid-template-areas: "rail board";
      gap: var(--qce-rail-gap) 1rem;
      align-items: stretch;
    }
    /* minmax(0,1fr) (no "auto"): la fila toma exactamente el alto
       disponible de .board-stage (ver .practice-layout/.board-panel
       arriba), en vez de crecer para ajustarse al contenido del rail.
       Sin esto, un rail con contenido real (explicacion, solucion,
       valoracion) crece libremente y empuja al board hacia abajo/fuera
       de la pantalla -- confirmado con datos reales via CDP headless
       (board.bottom negativo, page overflow) antes de este fix. */
    @media (min-width: 1081px) {
      .board-stage {
        height: 100%;
        min-height: 0;
        grid-template-rows: minmax(0, 1fr);
      }
    }
    .board-core {
      grid-area: board;
      align-self: center;
      justify-self: center;
    }
    .board-actions {
      grid-area: rail;
    }
    /* .board-right-rail es su propio grid interno: Rail A (columna 1,
       fija) + Rail B (columna 2, #rail-b-panel) -- SIEMPRE dos columnas
       mientras Practice este en modo lateral (sin breakpoint propio:
       este es ya el comportamiento por defecto). Solo el apilado
       movil/tablet (@media max-width:1000px, mas abajo) las funde en
       una columna -- ese es el UNICO punto donde el regimen cambia. */
    .board-right-rail {
      display: grid;
      height: 100%;
      min-height: 0;
      overflow: hidden;
      gap: var(--qce-rail-gap);
      grid-template-columns: 1fr var(--qce-rail-b-w);
      grid-template-rows: auto minmax(0, 1fr) auto auto auto auto;
      grid-template-areas:
        "nav     railb"
        "middle  railb"
        "state   railb"
        "actions railb"
        "report  railb"
        "legend  railb";
    }
    .practice-corner-controls { grid-area: nav; }
    /* Rail scroll/jerarquia fix (Jorge, produccion): Area privada/
       Entrenamiento/Idioma pasa a vivir dentro de un <details> propio
       para liberar altura vertical en modo lateral/single-rail sin
       tocar el grid-area "nav" en si -- el <details> es ahora el hijo
       directo de esa area. */
    #rail-nav-collapsible { grid-area: nav; }
    /* "middle" (Turno/Ayuda/secuencia guiada -- Objetivo y Explicacion
       viven en Rail B, no aqui, correccion de distribucion Jorge) es una
       unica zona real con su propio scroll interno
       -- no una lista de areas de grid separadas -- porque su
       contenido real (con datos reales, no el mock) puede exceder
       facilmente el presupuesto vertical en viewports bajos (1120x470).
       Las acciones (botones) NUNCA deben perder espacio por esto: por
       eso "actions" se queda fuera de esta zona, en su propia fila
       auto (garantizada), y "middle" absorbe el recorte/scroll cuando
       hace falta (spec seccion 6/8: comprimir Rail A, mantener
       acciones accesibles, scroll interno solo en la zona adecuada). */
    .gadget-panel {
      grid-area: middle;
      display: flex;
      flex-direction: column;
      gap: var(--qce-rail-gap);
      overflow-y: auto;
      min-height: 0;
    }
    /* Hotfix (Jorge, 2026-09-11): #state-box (cartel de acierto/error) sale
       de .gadget-panel -- esa zona hace overflow-y:auto y podia dejar el
       cartel de "Correcto" fuera de la vista sin scroll manual dentro del
       propio rail (reportado sobre Escuela quien_defiende_a_quien). Fila
       "state" propia, altura auto (nunca recortada), entre el contenido del
       ejercicio ("middle") y los botones ("actions"). Ningun grid-area, id
       ni logica JS existente distinta de esta se ha modificado. */
    #state-box { grid-area: state; }
    .board-control-panel { grid-area: actions; }
    #report-panel { grid-area: report; }
    #symbol-legend-panel { grid-area: legend; }
    #rail-b-panel { grid-area: railb; }
    /* Rail B: columna real de alto completo (spec section 5/7), no una
       lista pegada arriba. Bloques en flujo normal; scroll solo si el
       contenido realmente excede el alto disponible. */
    #rail-b-panel {
      display: flex;
      flex-direction: column;
      gap: var(--qce-rail-gap);
      min-height: 0;
      overflow-y: auto;
    }
    /* R3: board-first en el rango apilado. El tablero es el elemento
       principal (contrato de producto); cuando dos rails + board dejan
       de caber bien, .board-stage se apila con el tablero PRIMERO y el
       rail (unico, Rail B fusionado) DESPUÉS, nunca al revés. Umbral
       subido de 1000px a 1080px (spec "DOS RAILS OBLIGATORIOS EN MODO
       LATERAL", Jorge): medido con datos reales, mantener dos rails a
       1024px dejaba el board en 490x490 (limitado por ancho) cuando
       apilado ahi llega a ~894x894 (limitado por alto) -- el board
       domina mucho mas apilado. A 1120px (por encima del umbral) los
       dos rails no le cuestan nada al board, que ya viene limitado por
       alto en ambos regimenes (464x464), asi que ahi si compensa el
       layout lateral con dos rails. */
    @media (max-width: 1080px) {
      .board-stage {
        grid-template-columns: 1fr;
        grid-template-areas:
          "board"
          "rail";
      }
      /* Board-first movil (spec section 14): el rail entero se apila
         debajo del tablero en UNA sola columna, sin clip ni scroll
         propio -- la pagina entera hace scroll vertical (permitido). */
      /* BUG V5 corregido: esta lista seguia con las areas antiguas
         (title/meta/objective/guided/classification/state) de antes de
         consolidar "middle" -- .gadget-panel{grid-area:middle} no
         encontraba esa area aqui y caia en auto-placement, generando
         columnas implicitas fantasma (el "hueco vacio en medio" y las
         tarjetas flotando a la derecha reportados). Debe coincidir
         exactamente con la lista base/1500px de arriba. */
      .gadget-panel {
        overflow-y: visible;
        max-height: none;
      }
      .board-right-rail {
        height: auto;
        min-height: 0;
        overflow: visible;
        grid-template-columns: 1fr;
        grid-template-rows: none;
        grid-template-areas:
          "nav"
          "middle"
          "state"
          "actions"
          "report"
          "legend"
          "railb";
      }
      #rail-b-panel {
        overflow-y: visible;
      }
      .gadget-secondary {
        width: 100%;
        max-width: none;
        min-width: 0;
        flex: 1 1 auto;
      }
      .board-panel {
        min-width: 0;
        max-width: none;
        width: 100%;
        flex: 1 1 auto;
      }
      .board-container,
      .board-area {
        max-width: min(97vw, 960px);
      }
      .summary-card {
        padding: 1rem;
      }
      /* Tablero primero (spec V5 responsive, seccion 3): la tarjeta
         "Jugar Quaternity" ya no se reinserta por encima del tablero al
         apilarse -- se queda oculta, igual que en desktop, para que el
         orden apilado empiece siempre por nav+tablero. */
      /* Rail unico fusionado (portatil, spec seccion 2): botones mas
         compactos, cercanos al lenguaje de Escuela/Curso -- no
         gigantes. */
      .board-control-actions button {
        padding-top: .62rem;
        padding-bottom: .62rem;
        font-size: .85rem;
      }
      .board-right-rail .board-control-panel {
        gap: .55rem;
      }
    }
    @media (max-width: 680px) {
      main.px-6 {
        padding-left: .85rem;
        padding-right: .85rem;
      }
      .container-practice {
        padding-left: 0;
        padding-right: 0;
      }
      .gadget-secondary {
        max-width: none;
        width: 100%;
      }
      .board-panel {
        border-radius: 1.3rem;
        padding: .45rem .25rem .55rem;
      }
      .board-container,
      .board-area {
        max-width: min(98vw, 980px);
      }
      .board-middle {
        grid-template-columns: .85rem minmax(0, 1fr) .85rem;
        gap: .06rem;
      }
      .board-axis-side {
        min-width: .85rem;
        font-size: .62rem;
      }
      .board-axis-top,
      .board-axis-bottom {
        font-size: .64rem;
      }
      .summary-top {
        gap: .55rem;
      }
      .summary-card {
        padding: .88rem;
      }
      .compact-highlight,
      .compact-meta-card {
        padding: .72rem;
      }
      /* Movil (spec V5 responsive, seccion 3): "si un grupo de botones
         no cabe bien en dos columnas, apilalo en una sola columna" --
         Volver a intentar / Reportar incidencia dejan de partirse en
         dos columnas estrechas. Selector reforzado (2 clases, no 1):
         la regla base de .board-control-actions-secondary se declara
         MAS ABAJO en este archivo (seccion de primitivas de layout) y
         con igual especificidad ganaria por orden de aparicion pese a
         estar dentro de este media query -- ver el mismo patron ya
         documentado arriba para .board-stage/.board-right-rail. */
      .board-control-panel .board-control-actions-secondary {
        grid-template-columns: 1fr;
      }
      .rail-panel,
      .gadget-panel {
        gap: .5rem;
      }
    }
    /* 1120x470 (spec section 6): a poca altura, Rail A se compacta
       primero -- nunca se reduce el board ni se fuerza scroll de
       pagina. Validado en tmp/practice-v5-prototype/: board se
       mantiene en 464x464 (3px/3px) y las acciones esenciales quedan
       accesibles sin scroll. Excluye <=1000px: ahi ya se aplica el
       apilado board-first con su propio scroll de pagina normal. */
    @media (max-height: 560px) and (min-width: 1081px) {
      :root { --qce-rail-gap: 6px; }
      /* Selector reforzado (.board-right-rail antepuesto): .rail-panel
         y .compact-copy/.compact-title/.board-side-note tienen su
         regla base MAS ABAJO en el archivo (seccion de primitivas) y
         con igual especificidad ganarian por orden de aparicion pese a
         estar dentro de este media query -- mismo patron ya
         documentado para .board-stage/.board-right-rail. */
      .board-right-rail .rail-panel,
      .compact-highlight,
      .compact-meta-card,
      .compact-meta-grid {
        padding: .35rem .6rem;
      }
      .summary-top { margin-bottom: .2rem; }
      .summary-label { font-size: .58rem; margin-bottom: .1rem; }
      .board-right-rail .compact-copy,
      .board-right-rail .compact-title,
      .board-right-rail .board-side-note { font-size: .78rem; line-height: 1.22; }
      #exercise-instruction { font-size: .84rem !important; }
      .practice-corner-controls { gap: 4px; }
      .practice-corner-link, .practice-corner-lang {
        padding: 3px 9px;
        font-size: .62rem;
      }
      .board-right-rail .board-control-panel { gap: .4rem; }
      .board-control-actions button {
        padding-top: .45rem;
        padding-bottom: .45rem;
      }
      .board-control-panel .board-control-actions-secondary {
        grid-template-columns: repeat(3, 1fr);
      }
      .practice-queue-panel { padding-top: .4rem; gap: .3rem; }
      .practice-queue-copy { display: none; }
      .difficulty-selector-wrap { margin-top: .3rem; }
      .difficulty-selector-label { font-size: .6rem; }
      .difficulty-selector { padding: .3rem .5rem; font-size: .7rem; }
    }
    /* Modo lateral, ancho medio/bajo (spec "DOS RAILS OBLIGATORIOS EN
       MODO LATERAL"): Rail A y Rail B se comprimen -- letra algo menor,
       menos padding, botones mas bajos, gaps menores -- en vez de
       desaparecer o fusionarse. Independiente del compacto por altura
       de arriba (uno reacciona a poco alto, este a poco ancho; a veces
       ambos aplican a la vez y se suman, sin conflicto). El minimo
       legible se mantiene (.72rem/.7rem, nunca por debajo). */
    @media (max-width: 1400px) and (min-width: 1081px) {
      :root { --qce-rail-gap: 8px; }
      .board-right-rail .rail-panel,
      .compact-highlight,
      .compact-meta-card,
      .compact-meta-grid {
        padding: .55rem .65rem;
      }
      .summary-label { font-size: .6rem; }
      .board-right-rail .compact-copy,
      .board-right-rail .compact-title,
      .board-right-rail .board-side-note { font-size: .82rem; }
      .board-control-actions button {
        padding-top: .55rem;
        padding-bottom: .55rem;
        padding-left: 1rem;
        padding-right: 1rem;
        font-size: .82rem;
      }
      .board-right-rail .board-control-panel { gap: .5rem; }
      .practice-corner-link, .practice-corner-lang {
        padding: 4px 10px;
        font-size: .68rem;
      }
    }
    @media (max-width: 1150px) and (min-width: 1081px) {
      .board-right-rail .rail-panel,
      .compact-highlight,
      .compact-meta-card,
      .compact-meta-grid {
        padding: .45rem .6rem;
      }
      .summary-label { font-size: .58rem; }
      .board-right-rail .compact-copy,
      .board-right-rail .compact-title,
      .board-right-rail .board-side-note { font-size: .78rem; }
      .board-control-actions button {
        padding-top: .48rem;
        padding-bottom: .48rem;
        padding-left: .85rem;
        padding-right: .85rem;
        font-size: .78rem;
      }
      .board-control-panel .board-control-actions-secondary {
        grid-template-columns: repeat(2, 1fr);
      }
    }
    .board-frame,
    .board-shell {
      border-radius: 0;
      background: transparent;
      border: 0;
      padding: 0;
      box-shadow: none;
    }
    /* Visual Fit V5: coordenadas compactas, referencia Curso Q (spec
       section 5/18) -- pequenas, discretas, pegadas al tablero, sin
       crecer con el viewport (--qce-coord-font/--qce-coord-gutter son
       constantes en rem, no vw/dvh: el crecimiento del viewport
       beneficia al tablero, no a las coordenadas). */
    .board-axis-top,
    .board-axis-bottom {
      display: grid;
      grid-template-columns: repeat(12, 1fr);
      color: #6d829c;
      font-size: var(--qce-coord-font);
      font-weight: 900;
      letter-spacing: .01em;
      text-align: center;
      opacity: .92;
    }
    .board-axis-top { margin-bottom: .18rem; }
    .board-axis-bottom { margin-top: .18rem; }
    .board-axis-side {
      display: grid;
      grid-template-rows: repeat(12, 1fr);
      color: #6d829c;
      font-size: var(--qce-coord-font);
      min-width: var(--qce-coord-gutter);
      align-items: center;
      justify-items: center;
      font-weight: 900;
      opacity: .92;
    }
    .board-middle {
      display: grid;
      grid-template-columns: var(--qce-coord-gutter) minmax(0, 1fr) var(--qce-coord-gutter);
      gap: .1rem;
    }
    .board-area {
      position: relative;
      border-radius: .72rem;
      overflow: hidden;
      background:
        repeating-conic-gradient(from 90deg, #f7efe7 0 25%, #c79f80 0 50%) 0 0 / calc(200% / 12) calc(200% / 12);
      box-shadow: inset 0 0 0 1px rgba(52,52,52,.12), 0 18px 32px rgba(0,0,0,.08);
    }
    .public-hidden {
      display: none !important;
    }
    .rail-panel {
      border-radius: 1.55rem;
      border: 1px solid rgba(23,28,34,.08);
      border-top: 3px solid transparent;
      background: linear-gradient(180deg, rgba(247,241,231,.96) 0%, rgba(255,255,255,.94) 100%);
      padding: .95rem;
      box-shadow: 0 12px 24px rgba(0,0,0,.05);
    }
    /* ---------------------------------------------------------------
       Acentos QCE (spec: interaccion=verde, objetivo/nivel=azul,
       explicacion/feedback=azul, solucion/varias soluciones=verde,
       entrenamiento=rojo/teja ya en .practice-corner-link-accent). Un
       borde superior fino, nunca la tarjeta entera coloreada. IDs (no
       clases nuevas) porque #state-box reasigna su className por JS en
       cada actualizacion y perderia cualquier clase añadida aqui. */
    #training-meta-grid { border-top-color: #526E3B; }
    #training-meta-grid .summary-label { color: #526E3B; }
    #training-objective-block { border-top-color: #2f6db4; }
    #training-objective-block .summary-label { color: #2f6db4; }
    #training-explanation-block { border-top-color: #2f6db4; }
    #solution-box { border-top-color: #526E3B; }
    #expert-mate-panel { border-top-color: #2f6db4; }
    #minute-replay-panel { border-top-color: #2f6db4; }
    .board-actions .rail-panel {
      padding: .82rem;
    }
    .board-actions .summary-card {
      margin-bottom: 0;
    }
    .board-side-note {
      border-radius: 1.4rem;
      border: 1px solid rgba(255,255,255,.08);
      background: rgba(255,255,255,.08);
      padding: .88rem;
      font-size: .9rem;
      line-height: 1.62;
      color: rgba(255,255,255,.9);
    }
    @media (max-width: 720px) {
      .board-right-rail {
        grid-template-columns: 1fr;
      }
    }
    /* Visual Fit V5: la vieja tarjeta oscura envolvente se retira -- su
       fondo/color pasan a ser los de .gadget-panel definido arriba
       (grid-area:middle, zona real con scroll propio, ver
       .board-right-rail). Sus hijos (turno/objetivo/nivel/secuencia
       guiada/estado) ya no son tarjetas QCE independientes con su
       propio grid-area: viven en flujo normal dentro de esta unica
       zona scrollable. */
    .practice-layout .gadget-panel p,
    .practice-layout .gadget-panel div,
    .practice-layout .rail-panel {
      word-break: normal;
      overflow-wrap: anywhere;
    }
    /* BUG V5 corregido: overflow-wrap:anywhere es una propiedad heredada
       -- se filtraba a los <button>/<select> dentro de .rail-panel
       (p.ej. board-control-panel), partiendo palabras letra a letra en
       botones estrechos ("Volver a intentar el ejercicio" -> "Vo/lv/er").
       Los controles nunca deben partir palabras; solo el texto largo
       (explicacion, etc.) puede hacerlo. */
    .practice-layout .rail-panel button,
    .practice-layout .rail-panel select,
    .practice-layout .gadget-panel button,
    .practice-layout .gadget-panel select {
      word-break: normal;
      overflow-wrap: normal;
    }
    .gadget-panel .compact-title,
    .gadget-panel .compact-copy,
    .gadget-panel .section-kicker,
    .gadget-panel .section-label {
      word-break: normal;
      overflow-wrap: normal;
      hyphens: none;
    }
    .classification-stack {
      display: grid;
      gap: .8rem;
    }
    .classification-copy {
      font-size: .95rem;
      line-height: 1.65;
      color: rgba(33,33,33,.84);
    }
    .classification-chip-row {
      display: flex;
      flex-wrap: wrap;
      gap: .45rem;
    }
    .classification-chip {
      display: inline-flex;
      align-items: center;
      border-radius: 999px;
      border: 1px solid rgba(33,33,33,.08);
      background: rgba(255,255,255,.8);
      padding: .42rem .78rem;
      font-size: .74rem;
      font-weight: 700;
      letter-spacing: .06em;
      text-transform: uppercase;
      color: #212121;
    }
    .classification-chip.is-primary {
      border-color: rgba(181,39,0,.12);
      background: rgba(181,39,0,.08);
      color: #b52700;
    }
    .replay-status-panel.hidden {
      display: none;
    }
    .replay-status-summary {
      font-size: .92rem;
      line-height: 1.6;
      color: rgba(33,33,33,.82);
    }
    .replay-status-list {
      display: grid;
      gap: .55rem;
      margin-top: .8rem;
      padding-left: 0;
      list-style: none;
    }
    .replay-status-item {
      display: flex;
      align-items: flex-start;
      gap: .55rem;
      font-size: .86rem;
      line-height: 1.55;
      color: #3f474f;
    }
    .replay-status-dot {
      width: .62rem;
      height: .62rem;
      flex: 0 0 .62rem;
      border-radius: 999px;
      margin-top: .35rem;
      background: #d1a96f;
    }
    .replay-status-item.type-time .replay-status-dot {
      background: #b52700;
    }
    .replay-status-item.type-mate .replay-status-dot {
      background: #212121;
    }
    .replay-status-item.type-absorb .replay-status-dot {
      background: #526E3B;
    }
    .compact-copy {
      font-size: .92rem;
      line-height: 1.5;
    }
    .compact-title {
      font-size: 1rem;
      line-height: 1.2;
    }
    .state-box-tight {
      padding: .85rem .9rem;
      font-size: .9rem;
      line-height: 1.55;
    }
    .gadget-state-success {
      background: linear-gradient(180deg, rgba(52, 101, 28, .96) 0%, rgba(68, 129, 38, .92) 100%);
      color: #fffef2;
      border: 1px solid rgba(214, 255, 156, .68);
      box-shadow:
        inset 0 0 0 1px rgba(255,255,255,.08),
        0 14px 28px rgba(52,101,28,.26);
    }
    .gadget-state-error {
      background: linear-gradient(180deg, rgba(122, 31, 9, .96) 0%, rgba(171, 46, 16, .94) 100%);
      color: #fff4ef;
      border: 1px solid rgba(255, 184, 160, .48);
      box-shadow:
        inset 0 0 0 1px rgba(255,255,255,.06),
        0 14px 28px rgba(122,31,9,.22);
    }
    .gadget-state-neutral {
      background: #fff;
      color: #212121;
      border: 1px solid rgba(33,33,33,.08);
    }
    .state-title {
      display: block;
      font-size: 1.15rem;
      font-weight: 800;
      letter-spacing: .08em;
      text-transform: uppercase;
    }
    .state-copy {
      display: block;
      margin-top: .35rem;
      font-size: .95rem;
      line-height: 1.6;
      font-weight: 600;
    }
    .solution-card {
      border-radius: 1rem;
      border: 1px solid rgba(33,33,33,.08);
      background: rgba(255,255,255,.84);
      padding: .8rem .95rem;
    }
    .solution-card.is-current {
      border-color: rgba(181,39,0,.16);
      background: rgba(181,39,0,.06);
    }
    .solution-card-title {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: .45rem;
      font-size: .92rem;
      font-weight: 800;
      color: #212121;
    }
    .solution-card-copy {
      margin-top: .32rem;
      font-size: .85rem;
      line-height: 1.6;
      color: #5f655f;
    }
    .solution-card-actions {
      display: flex;
      justify-content: flex-end;
      margin-top: .7rem;
    }
    .solution-step-item {
      border-radius: 1rem;
      background: rgba(255,255,255,.72);
      padding: .72rem .9rem;
    }
    .board-grid {
      position: absolute;
      inset: 0;
      display: grid;
      grid-template-columns: repeat(12, 1fr);
      grid-template-rows: repeat(12, 1fr);
      z-index: 2;
    }
    .grid-cell {
      position: relative;
      appearance: none;
      -webkit-appearance: none;
      padding: 0;
      margin: 0;
      border: 0;
      box-shadow: inset 0 0 0 1px rgba(72,53,43,.08);
      transition: background .12s ease, box-shadow .12s ease;
      background: transparent;
    }
    button.grid-cell { cursor: pointer; }
    button.grid-cell:hover { background: rgba(44,147,132,.14); }
    .grid-cell.is-selected {
      background: rgba(44,147,132,.2);
      box-shadow: inset 0 0 0 2px rgba(44,147,132,.92);
    }
    .grid-cell.is-expected-source {
      background: rgba(44,147,132,.18);
      box-shadow: inset 0 0 0 2px rgba(44,147,132,.78);
    }
    .grid-cell.is-expected-destination {
      background: rgba(240,190,75,.22);
      box-shadow: inset 0 0 0 2px rgba(240,190,75,.92);
    }
    .grid-cell.is-expected-answer {
      background: rgba(181,39,0,.16);
      box-shadow: inset 0 0 0 2px rgba(181,39,0,.86);
    }
    .symbol-panel {
      display: grid;
      gap: .55rem;
    }
    .board-control-panel {
      display: grid;
      gap: .72rem;
    }
    .board-control-actions {
      display: grid;
      gap: .55rem;
    }
    .board-control-actions-secondary {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: .55rem;
    }
    /* Rail pedagogico: desplegables nativos <details>/<summary>, sin JS
       propio (foco/teclado/estado accesibles de forma nativa). */
    .rail-collapsible > summary.rail-collapsible-summary {
      cursor: pointer;
      list-style: none;
    }
    .rail-collapsible > summary.rail-collapsible-summary::-webkit-details-marker {
      display: none;
    }
    .rail-collapsible > summary.rail-collapsible-summary::before {
      content: "+ ";
      font-weight: 800;
    }
    .rail-collapsible[open] > summary.rail-collapsible-summary::before {
      content: "\2212 ";
    }
    .rail-collapsible-body {
      margin-top: .6rem;
    }
    /* Rail scroll/jerarquia fix: resumenes de "nav" y "mas opciones" en
       tono secundario (mismo patron visual que .practice-queue-summary),
       nunca como titulo de tarjeta -- son accesos secundarios, no
       contenido esencial del ejercicio. */
    /* Hotfix (Jorge, 2026-09-11): "#rail-nav-collapsible" tambien lleva la
       clase "rail-panel" (para su grid-area "nav") y ".board-actions
       .rail-panel" (mas arriba, padding .82rem uniforme) empata en
       especificidad con un selector de una sola clase -- por
       especificidad (no por orden) ".board-actions .rail-panel" ganaria
       siempre y este padding compacto quedaria muerto para nav (asi
       llevaba desde V5.1: la intencion de .55rem/.82rem nunca llego a
       aplicarse). Selector de dos clases para igualar especificidad y
       que el mas tardio en el archivo (este) gane de verdad. */
    .board-actions .rail-nav-collapsible,
    .rail-secondary-collapsible {
      padding: .4rem .65rem;
    }
    .rail-secondary-collapsible {
      border-top: 1px solid rgba(33,33,33,.08);
      border-radius: 0;
      background: none;
      box-shadow: none;
      padding-left: 0;
      padding-right: 0;
      padding-bottom: 0;
    }
    /* Hotfix (Jorge, 2026-09-11): rail-nav-collapsible/rail-secondary-collapsible
       pasan a estar abiertos por defecto (ver atributo "open" en
       practice.html) -- antes vivian siempre plegados. Body mas compacto
       (solo estos dos, no .rail-collapsible-body generico, para no afectar
       Objetivo/Explicacion/Solucion/valoracion experto en Rail B). */
    .rail-nav-collapsible > .rail-collapsible-body,
    .rail-secondary-collapsible > .rail-collapsible-body {
      margin-top: .4rem;
    }
    .rail-nav-collapsible > summary.rail-collapsible-summary,
    .rail-secondary-collapsible > summary.rail-collapsible-summary {
      font-size: .76rem;
      font-weight: 700;
      color: #52606d;
      text-transform: uppercase;
      letter-spacing: .04em;
    }
    .rail-secondary-collapsible .board-control-actions-secondary {
      grid-template-columns: repeat(3, 1fr);
      gap: .4rem;
    }
    .rail-secondary-collapsible .board-control-actions-secondary button {
      padding: .42rem .35rem;
      font-size: .7rem;
      line-height: 1.2;
    }
    /* Hotfix (Jorge, 2026-09-11): con el desplegable ahora abierto por
       defecto, la fila de 3 columnas (Volver a intentar/Captura/Reportar)
       queda visible tambien en el rango apilado movil (<=680px, mismo
       umbral que R3 board-first mas abajo) -- ahi se apila a 1 columna
       para no partir "Volver a intentar" en un hueco demasiado estrecho.
       Reemplaza el 2-columnas <=480px anterior (nunca llegaba a aplicarse
       en produccion: el desplegable estaba siempre cerrado). */
    @media (max-width: 680px) {
      .rail-secondary-collapsible .board-control-actions-secondary {
        grid-template-columns: 1fr;
      }
    }
    /* Hotfix (Jorge, 2026-09-11): a poca altura en modo lateral (1120x470
       confirmado, mismo umbral que la regla general mas arriba en el
       archivo -- ver @media max-height:560px/min-width:1081px), el body
       ahora abierto de rail-nav-collapsible/rail-secondary-collapsible
       debe comprimirse tanto como el resto del rail o
       #retry-exercise/#capture-board/#report-toggle quedan recortados por
       .board-right-rail{overflow:hidden}. Colocado DESPUES de las reglas
       incondicionales de arriba (mismo selector, misma especificidad): en
       CSS, a igual especificidad gana la declarada mas tarde en el
       archivo, asi que puesto antes habria quedado sin efecto. */
    @media (max-height: 560px) and (min-width: 1081px) {
      .board-actions .rail-nav-collapsible,
      .rail-secondary-collapsible {
        padding: .3rem .5rem;
      }
      .rail-nav-collapsible > .rail-collapsible-body,
      .rail-secondary-collapsible > .rail-collapsible-body {
        margin-top: .3rem;
      }
      .rail-secondary-collapsible .board-control-actions-secondary {
        gap: .3rem;
      }
      .rail-secondary-collapsible .board-control-actions-secondary button {
        padding: .3rem .3rem;
        font-size: .64rem;
      }
      /* #state-box via ID (mayor especificidad que ".board-actions
         .rail-panel", que si no ganaria por ser dos clases igual que la
         regla generica de compactado de mas arriba) -- confirmado en
         1120x470 que sin esto el cartel de exito/objetivo por defecto se
         queda demasiado alto y desplaza retry/capture/report fuera del
         area visible de .board-right-rail (overflow:hidden). */
      #state-box {
        padding: .45rem .55rem;
        font-size: .78rem;
        line-height: 1.32;
      }
    }
    .symbol-panel-heading {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: .6rem;
    }
    .symbol-panel-heading p {
      margin: 0;
    }
    .symbol-panel #symbol-toggle {
      padding-left: .9rem;
      padding-right: .9rem;
      white-space: nowrap;
    }
    .symbol-panel .symbol-buttons {
      display: none;
      gap: .45rem;
    }
    .symbol-panel.is-open .symbol-buttons {
      display: grid;
    }
    .symbol-panel button {
      width: 100%;
      border-radius: 999px;
      border: 1px solid rgba(33,33,33,.12);
      padding: .45rem .7rem;
      font-size: .72rem;
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: .08em;
      background: rgba(255,255,255,.9);
      color: rgba(33,33,33,.88);
      transition: transform .12s ease, box-shadow .12s ease, background .12s ease;
    }
    .symbol-panel button:hover { transform: translateY(-1px); }
    .symbol-panel button.is-active {
      background: #212121;
      color: #fff;
      border-color: #212121;
      box-shadow: 0 10px 16px rgba(0,0,0,.12);
    }
    .symbol-panel button.is-disabled {
      opacity: .45;
      pointer-events: none;
    }
    .symbol-help {
      display: none;
      border-radius: 1rem;
      border: 1px solid rgba(181,39,0,.18);
      background: rgba(255,248,237,.95);
      padding: .6rem .75rem;
      font-size: .78rem;
      line-height: 1.4;
      color: #3b3f45;
      box-shadow: inset 0 0 0 1px rgba(255,255,255,.6);
    }
    .symbol-help.is-visible {
      display: block;
    }
    .symbol-panel.is-open .symbol-help.is-visible {
      display: block;
    }
    .board-overlay-layer {
      position: absolute;
      inset: 0;
      pointer-events: none;
      z-index: 8;
    }
    .board-overlay-marks {
      position: absolute;
      inset: 0;
      z-index: 7;
    }
    .symbol-mark {
      position: absolute;
      width: calc(100% / 12);
      height: calc(100% / 12);
      border-radius: .4rem;
      pointer-events: none;
    }
    .symbol-mark.type-undefended {
      background: rgba(233, 114, 141, 0.24);
      box-shadow: inset 0 0 0 2px rgba(217, 78, 117, 0.92);
    }
    .symbol-mark.type-attacked {
      background: rgba(240, 190, 75, 0.24);
      box-shadow: inset 0 0 0 2px rgba(240, 190, 75, 0.92);
    }
    .symbol-mark.type-real-free,
    .symbol-mark.type-real-attacked {
      background: transparent;
      box-shadow: none;
      display: grid;
      place-items: center;
    }
    .symbol-mark.type-real-free::before,
    .symbol-mark.type-real-attacked::before {
      content: "★";
      font-size: clamp(1.45rem, 2.6vw, 2rem);
      font-weight: 900;
      line-height: 1;
      text-shadow:
        0 0 1px rgba(255,255,255,.98),
        0 0 6px rgba(255,255,255,.68),
        0 1px 1px rgba(23,28,34,.18);
    }
    .symbol-mark.type-real-free::before {
      color: #3fa8ff;
    }
    .symbol-mark.type-real-attacked::before {
      color: #f4c318;
    }
    .symbol-mark.type-pinned {
      background: transparent;
      box-shadow: inset 0 0 0 2px rgba(198, 36, 27, 0.98);
      border: 2px dashed rgba(198, 36, 27, 0.98);
      border-radius: 999px;
    }
    .board-timeout-layer {
      position: absolute;
      inset: 0;
      pointer-events: none;
      z-index: 3;
    }
    .board-timeout-mark {
      position: absolute;
      width: calc(100% / 12);
      height: calc(100% / 12);
      box-sizing: border-box;
      border: 3px solid rgba(181,39,0,.98);
      border-radius: .2rem;
      background:
        repeating-linear-gradient(
          135deg,
          rgba(181,39,0,.34) 0 5px,
          rgba(255,255,255,0) 5px 10px
        ),
        rgba(181,39,0,.12);
      box-shadow: inset 0 0 0 1px rgba(255,255,255,.28), 0 0 0 1px rgba(181,39,0,.16);
    }
    .board-timeout-label {
      position: absolute;
      top: .2rem;
      left: .2rem;
      border-radius: 999px;
      background: rgba(181,39,0,.96);
      color: #fff;
      padding: .08rem .32rem;
      font-size: .58rem;
      font-weight: 800;
      letter-spacing: .06em;
      text-transform: uppercase;
    }
    .board-overlay-svg {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      display: block;
      overflow: visible;
      pointer-events: none;
      z-index: 8;
    }
    .symbol-arrow {
      fill: none;
      stroke: rgba(181,39,0,.86);
      stroke-width: 3;
      stroke-linecap: round;
      stroke-linejoin: round;
      opacity: .98;
      vector-effect: non-scaling-stroke;
    }
    .symbol-arrow-head {
      fill: rgba(181,39,0,.24);
      stroke: rgba(181,39,0,.9);
      stroke-width: 3;
      stroke-linecap: round;
      stroke-linejoin: round;
      opacity: .98;
      vector-effect: non-scaling-stroke;
    }
    .symbol-arrow-origin {
      fill: rgba(255,255,255,.18);
      stroke-width: 1.1;
      opacity: .98;
      vector-effect: non-scaling-stroke;
    }
    .symbol-arrow-target {
      fill: rgba(255,255,255,.12);
      stroke-width: .9;
      opacity: .98;
      vector-effect: non-scaling-stroke;
    }
    .symbol-legend {
      border-radius: 1rem;
      border: 1px solid rgba(33,33,33,.08);
      background: rgba(255,255,255,.92);
      padding: .8rem .9rem;
      font-size: .76rem;
      line-height: 1.45;
      color: #4b5563;
    }
    .symbol-legend-grid {
      display: grid;
      gap: .45rem;
    }
    .symbol-legend strong {
      color: #212121;
    }
    .piece,
    .piece-chip {
      position: absolute;
      display: grid;
      place-items: center;
      background: transparent;
      border: 0;
      box-shadow: none;
      pointer-events: none;
    }
    .piece {
      width: 8.3334%;
      aspect-ratio: 1;
    }
    .piece-chip { inset: 0; }
    .piece-image {
      width: var(--quaternity-piece-size, 92%);
      height: var(--quaternity-piece-size, 92%);
      max-width: var(--quaternity-piece-size, 92%);
      max-height: var(--quaternity-piece-size, 92%);
      object-fit: contain;
      display: block;
      user-select: none;
      pointer-events: none;
      margin: auto;
    }
    .piece-glyph {
      font-size: 2rem;
      font-weight: 700;
      line-height: 1;
      display: inline-block;
    }
    .piece-glyph.army-w { color: #faf7f1; text-shadow: -1px 0 #444, 0 1px #444, 1px 0 #444, 0 -1px #444; }
    .piece-glyph.army-r { color: #cf2f1e; }
    .piece-glyph.army-b { color: #111111; }
    .piece-glyph.army-g { color: #12814a; }
    .piece-visual {
      position: relative;
      display: grid;
      place-items: center;
      width: 100%;
      height: 100%;
    }
    .piece-visual.is-zombie::before {
      content: "";
      position: absolute;
      inset: 9%;
      border-radius: .18rem;
      border: 2px solid rgba(160, 24, 24, .92);
      background:
        repeating-linear-gradient(
          135deg,
          rgba(45, 45, 45, .58) 0 4px,
          rgba(137, 16, 16, .22) 4px 8px
        );
      box-shadow:
        inset 0 0 0 1px rgba(255,255,255,.16),
        0 0 0 1px rgba(90, 12, 12, .18);
      z-index: 0;
    }
    .piece-visual > * {
      position: relative;
      z-index: 1;
    }
    .piece-visual.is-zombie .piece-image,
    .piece-visual.is-zombie .piece-glyph {
      filter:
        saturate(1.08)
        contrast(1.06)
        brightness(.96)
        drop-shadow(-0.9px 0 0 rgba(255,255,255,.96))
        drop-shadow(0.9px 0 0 rgba(255,255,255,.96))
        drop-shadow(0 -0.9px 0 rgba(255,255,255,.96))
        drop-shadow(0 0.9px 0 rgba(255,255,255,.96))
        drop-shadow(0 0 2px rgba(0,0,0,.72));
      opacity: .98;
    }
    .piece-visual.is-zombie .piece-image.piece-image-white {
      filter:
        saturate(1.02)
        contrast(1.04)
        brightness(.98)
        drop-shadow(-1px 0 0 rgba(17,17,17,.98))
        drop-shadow(1px 0 0 rgba(17,17,17,.98))
        drop-shadow(0 -1px 0 rgba(17,17,17,.98))
        drop-shadow(0 1px 0 rgba(17,17,17,.98))
        drop-shadow(0 0 2px rgba(255,255,255,.58));
    }
    .piece-visual.is-zombie-king::before {
      border-width: 3px;
      border-color: rgba(193, 18, 31, .98);
      box-shadow:
        inset 0 0 0 1px rgba(255,255,255,.24),
        0 0 0 2px rgba(193,18,31,.18),
        0 0 10px rgba(127, 29, 29, .28);
    }
    .piece-visual.is-absorbed .piece-image,
    .piece-visual.is-absorbed .piece-glyph {
      filter: saturate(1.02) drop-shadow(0 0 3px rgba(40,90,170,.28));
    }
    .piece-visual-direction {
      position: absolute;
      inset: 0;
      z-index: 1;
      pointer-events: none;
    }
    .piece-image,
    .piece-glyph {
      position: relative;
      z-index: 2;
    }
    .piece-visual-direction-marker {
      /* R3 microfix: tamaño relativo a la pieza (misma unidad geometrica
         que .piece-visual-direction, que ya llena la pieza con inset:0),
         no rem/px fijos. Un unico valor para todo el rango de viewport:
         escala junto con la pieza y el tablero, sin depender de ningun
         breakpoint concreto. */
      position: absolute;
      width: 35%;
      height: 35%;
      display: grid;
      place-items: center;
      filter:
        drop-shadow(0 1px 1px rgba(15, 23, 42, .88))
        drop-shadow(0 0 1px rgba(15, 23, 42, .92));
      opacity: .98;
    }
    .piece-visual-direction-marker svg {
      width: 100%;
      height: 100%;
      overflow: visible;
    }
    .piece-visual-direction-marker path {
      fill: none;
      stroke: rgba(255,255,255,.98);
      stroke-width: 3.2;
      stroke-linecap: round;
      stroke-linejoin: round;
    }
    .piece-visual-direction-marker.dir-up {
      top: 3%;
      left: 50%;
      transform: translateX(-50%);
    }
    .piece-visual-direction-marker.dir-right {
      top: 50%;
      right: 2%;
      transform: translateY(-50%) rotate(90deg);
    }
    .piece-visual-direction-marker.dir-down {
      bottom: 3%;
      left: 50%;
      transform: translateX(-50%) rotate(180deg);
    }
    .piece-visual-direction-marker.dir-left {
      top: 50%;
      left: 2%;
      transform: translateY(-50%) rotate(-90deg);
    }
    .board-message {
      display: grid;
      place-items: center;
      min-height: 100%;
      padding: 2rem;
      text-align: center;
      font-size: .95rem;
      font-weight: 700;
      color: #4b5563;
      background: rgba(255,255,255,.58);
    }
    .minute-replay-panel {
      border: 1px solid rgba(33,72,111,.12);
      background:
        radial-gradient(circle at top right, rgba(47,109,180,.08), transparent 42%),
        linear-gradient(180deg, rgba(255,255,255,.98) 0%, rgba(250,246,240,.98) 100%);
    }
    .minute-replay-files {
      display: grid;
      grid-template-columns: repeat(12, minmax(0, 1fr));
      gap: .16rem;
      color: #6e6a64;
      font-size: .58rem;
      font-weight: 700;
      letter-spacing: .14em;
      text-transform: uppercase;
    }
    .minute-replay-files span,
    .minute-replay-ranks span {
      display: flex;
      align-items: center;
      justify-content: center;
      min-height: 1rem;
    }
    .minute-replay-stage {
      display: grid;
      grid-template-columns: 1.1rem minmax(0, 1fr) 1.1rem;
      gap: .28rem;
      align-items: stretch;
      margin: .28rem 0;
    }
    .minute-replay-ranks {
      display: grid;
      grid-template-rows: repeat(12, minmax(0, 1fr));
      gap: .16rem;
      color: #6e6a64;
      font-size: .58rem;
      font-weight: 700;
    }
    .minute-replay-grid {
      display: grid;
      grid-template-columns: repeat(12, minmax(0, 1fr));
      grid-template-rows: repeat(12, minmax(0, 1fr));
      aspect-ratio: 1 / 1;
      overflow: hidden;
      border-radius: 1.1rem;
      border: 1px solid rgba(33,33,33,.08);
      background: #f7f1e7;
      box-shadow: inset 0 1px 0 rgba(255,255,255,.45);
    }
    .minute-replay-cell {
      position: relative;
      display: flex;
      align-items: center;
      justify-content: center;
      min-width: 0;
      min-height: 0;
    }
    .minute-replay-cell.is-light {
      background: #f6efe7;
    }
    .minute-replay-cell.is-dark {
      background: #caa27f;
    }
    .minute-replay-cell.is-from::after,
    .minute-replay-cell.is-to::after {
      content: "";
      position: absolute;
      inset: .08rem;
      border-radius: .45rem;
      border: 2px solid rgba(33,72,111,.75);
      pointer-events: none;
    }
    .minute-replay-cell.is-to::after {
      border-color: rgba(181,39,0,.82);
      box-shadow: 0 0 0 2px rgba(181,39,0,.10) inset;
    }
    .minute-replay-piece {
      position: relative;
      display: inline-flex;
      width: 86%;
      height: 86%;
      align-items: center;
      justify-content: center;
    }
    .minute-replay-piece img {
      width: 100%;
      height: 100%;
      object-fit: contain;
      filter: drop-shadow(0 2px 6px rgba(0,0,0,.16));
    }
    .minute-replay-piece.is-zombie img {
      filter: grayscale(1) brightness(.86) drop-shadow(0 2px 6px rgba(0,0,0,.12));
    }
    .minute-replay-piece.is-absorbed img {
      opacity: .94;
    }
    .minute-replay-origin {
      position: absolute;
      right: -.04rem;
      bottom: -.02rem;
      display: inline-flex;
      min-width: .76rem;
      height: .76rem;
      align-items: center;
      justify-content: center;
      border-radius: 999px;
      background: rgba(255,255,255,.96);
      color: #212121;
      font-size: .42rem;
      font-weight: 800;
      box-shadow: 0 2px 6px rgba(0,0,0,.12);
    }
    .minute-replay-progress {
      height: .35rem;
      width: 100%;
      overflow: hidden;
      border-radius: 999px;
      background: rgba(33,72,111,.10);
    }
    .minute-replay-progress-bar {
      height: 100%;
      width: 0;
      border-radius: 999px;
      background: linear-gradient(90deg, #21486f 0%, #b52700 100%);
      transition: width .24s ease;
    }
    .training-access-guard {
      max-width: 46rem;
      margin: 5rem auto;
      padding: 0 1.25rem;
      font-family: "Montserrat", sans-serif;
    }
    .training-access-guard-card {
      padding: 2rem;
      border: 1px solid rgba(33, 33, 33, .14);
      border-radius: 8px;
      background: #FFF8ED;
      color: #212121;
      text-align: center;
    }
    .training-access-guard-kicker {
      color: #526E3B;
      font-size: .78rem;
      font-weight: 700;
      text-transform: uppercase;
    }
    .training-access-guard-action {
      display: inline-flex;
      margin-top: 1rem;
      padding: .75rem 1rem;
      border-radius: 6px;
      background: #526E3B;
      color: #FFFFFF;
      font-weight: 700;
      text-decoration: none;
    }
