/* ============================================================================
   app.css — Sistema visual de GeometriaFactory-Web

   PORTADO desde la línea de base visual aprobada:
   `SDD/Maquetas/GeometriaFactory-Web/assets/css/Estilos-Maqueta.css`, aprobada
   por el Product Owner el 2026-08-11 (`Linea-Base-Visual.md` §7 `EV-01`).

   QUÉ SE PORTÓ Y QUÉ NO
   ---------------------
   Se portó la hoja entera MENOS los tres bloques que `Linea-Base-Visual.md` §6
   declara instrumentos de la maqueta y no producto:
     - el panel del contrato de fachada del visor (`.mq-panel-fachada`, `.mq-fa-*`)
     - el sello de la maqueta y la barra de validación (`.mq-sello-maqueta`,
       `.mq-barra-validacion*`)
     - la portada de la maqueta (`.mq-portada`, `.mq-lista-superficies`, `.mq-prosa`)
   Con ellos se retiró el `padding-bottom: 92px` del cuerpo, que reservaba el
   espacio de la barra de validación.

   NOMBRES
   -------
   Los TOKENS conservan su nombre: son los nombres semánticos del catálogo de
   diseño y ya están en inglés. Los NOMBRES DE CLASE se tradujeron al inglés con
   prefijo `gf-`, según `Norma-De-Nomenclatura.md` §3 y la fila que §6.11 agrega
   para la convención de clases CSS. La maqueta conserva los suyos en castellano
   y no se renombra: §6.11 la declara frontera y línea de base ya aprobada.

   LA PROPIEDAD QUE ESTA HOJA SOSTIENE
   -----------------------------------
   Todo valor visual es un token del catálogo, materializado como variable CSS.
   NO HAY UN SOLO LITERAL DE COLOR FUERA DEL BLOQUE `:root` DE ABAJO. Es la
   propiedad que la maqueta ya cumplía y que la etapa `b` conserva; se verifica
   con `scripts/verify-visual-system.sh`.

   CONTRASTE
   ---------
   Los pares texto/fondo son los medidos en la maqueta. El único par que la
   maqueta medía y que acá no existe es el de la barra de validación, porque el
   bloque no se portó.
   ============================================================================ */

:root {
  /* --- color · superficies, texto y bordes (base §2.1) ------------------- */
  --color-background-primary:   #FFFFFF;
  --color-background-secondary: #F1F1EF;
  --color-background-tertiary:  #F7F6F4;
  --color-text-primary:         #1A1A18;
  --color-text-secondary:       #5C5C57;
  --color-text-tertiary:        #8A8A82;
  --color-text-danger:          #A03030;
  --color-border-secondary:     #D9D9D4;
  --color-border-tertiary:      #E6E6E1;
  --color-border-danger:        #E0B4B4;

  /* --- color · rampa de marca y acentos por módulo (base §2.1) ----------- */
  --color-brand-primary:        #0F6E56;
  --color-brand-primary-dark:   #04342C;
  --color-brand-primary-tint:   #E1F5EE;
  --color-accent-module-a:      #0F6E56;
  --color-accent-module-b:      #534AB7;
  --color-accent-module-c:      #854F0B;
  --color-accent-module-d:      #185FA5;

  /* --- color · estados semánticos, siempre par texto + tint (base §2.1) -- */
  --color-text-success:         #0F6E56;
  --color-background-success:   #E1F5EE;
  --color-text-warning:         #854F0B;
  --color-background-warning:   #FAEEDA;
  --color-text-error:           #993556;
  --color-background-error:     #FBEAF0;
  --color-text-info:            #185FA5;
  --color-background-info:      #E7F0F9;
  --color-border-info:          #BBD4EA;
  /* Texto y contornos sobre la rampa de marca. NO es un color nuevo: es el
     mismo valor que `--color-background-primary` del catálogo, materializado
     bajo el rol en el que esta hoja lo usa, para que ninguna regla escriba
     `#FFFFFF` a mano. */
  --color-text-on-brand:        #FFFFFF;
  /* Tres valores translúcidos que la hoja usaba en línea y que se promueven
     acá por el hallazgo `F26-29` de `SDD/Docs/Audit/F26-Propagacion-r1.md`.
     NO son colores nuevos: los dos primeros son `--color-text-on-brand` con
     opacidad, sobre la rampa de marca del shell; el tercero es
     `--color-text-primary` con opacidad, como velo del diálogo modal. */
  --color-border-on-brand:      rgba(255, 255, 255, .18);
  --color-text-on-brand-soft:   rgba(255, 255, 255, .85);
  --color-backdrop:             rgba(26, 26, 24, .45);

  /* --- tipografía (base §2.2) ------------------------------------------- */
  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --type-title-size:        17px; --type-title-weight:        500;
  --type-body-strong-size:  14px; --type-body-strong-weight:  500;
  --type-body-size:         13px; --type-body-weight:         400;
  --type-caption-size:      12px; --type-caption-weight:      400;
  --type-meta-size:         11px; --type-meta-weight:         500;

  /* --- espaciado (base §2.3, escala base 4) ------------------------------ */
  --space-1:  4px; --space-2:  8px; --space-3: 12px; --space-4: 14px;
  --space-5: 16px; --space-6: 18px; --space-7: 20px; --space-8: 22px;
  --space-9: 28px;

  /* --- radios y bordes (base §2.3) --------------------------------------- */
  --radius-md:   9px;
  --radius-lg:  13px;
  --radius-pill: 20px;
  --radius-icon: 12px;
  --border-hairline: 1px solid var(--color-border-tertiary);
  --border-control:  1px solid var(--color-border-secondary);

  /* --- movimiento (base §8) ---------------------------------------------- */
  --motion-fast: 150ms;
  --motion-base: 200ms;
  --motion-ease: ease;

  /* --- layout del shell (base §3.1) -------------------------------------- */
  --shell-sidebar-width: 168px;
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important; scroll-behavior: auto !important; }
}

/* ============================================================================
   Base
   ============================================================================ */

html { box-sizing: border-box; }
*, *::before, *::after { box-sizing: inherit; }

body.gf-body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--type-body-size);
  line-height: 1.45;
  color: var(--color-text-primary);
  background: var(--color-background-tertiary);
}

/* Escala tipográfica del catálogo también para los encabezados desnudos:
   sin esto manda la escala del framework de grilla y la jerarquía se rompe. */
h1 { margin: 0; line-height: 1.2; font-size: var(--type-title-size); font-weight: var(--type-title-weight); }
h2, h3, h4 { margin: 0; line-height: 1.2; font-size: var(--type-body-strong-size); font-weight: var(--type-body-strong-weight); }
h2.gf-title, h3.gf-title { font-size: var(--type-title-size); font-weight: var(--type-title-weight); }
h2.gf-meta, h3.gf-meta { font-size: var(--type-meta-size); font-weight: var(--type-meta-weight); }
p { margin: 0 0 var(--space-3); }

a { color: var(--color-brand-primary); }

/* Foco visible en todo elemento interactivo (base §7): anillo de 2px que no
   depende sólo del color. */
:focus-visible {
  outline: 2px solid var(--color-brand-primary);
  outline-offset: 2px;
  border-radius: var(--radius-md);
}
.gf-shell-sidebar :focus-visible { outline-color: var(--color-background-primary); }

.gf-sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.gf-skip-link {
  position: absolute; left: var(--space-2); top: -60px; z-index: 60;
  background: var(--color-background-primary); color: var(--color-brand-primary);
  border: var(--border-control); border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3); font-size: var(--type-body-size);
  transition: top var(--motion-fast) var(--motion-ease);
}
.gf-skip-link:focus { top: var(--space-2); }

/* Tipografía semántica */
.gf-title       { font-size: var(--type-title-size);       font-weight: var(--type-title-weight); }
.gf-body-strong { font-size: var(--type-body-strong-size); font-weight: var(--type-body-strong-weight); }
.gf-body        { font-size: var(--type-body-size);        font-weight: var(--type-body-weight); }
.gf-caption     { font-size: var(--type-caption-size);     font-weight: var(--type-caption-weight); color: var(--color-text-secondary); }
.gf-meta        { font-size: var(--type-meta-size);        font-weight: var(--type-meta-weight);
                  letter-spacing: .04em; text-transform: uppercase; color: var(--color-text-secondary); }
.gf-num         { font-variant-numeric: tabular-nums; }

/* Iconos SVG inline, heredan color por currentColor (base §6.1) */
.gf-icon { width: 1em; height: 1em; flex: none; vertical-align: -0.125em; }
.gf-icon--24 { width: 24px; height: 24px; }
.gf-icon--20 { width: 20px; height: 20px; }
.gf-icon--16 { width: 16px; height: 16px; }

/* ============================================================================
   Shell de acceso (Experiencia-De-Uso §3.2 · Primer arranque §4.1)
   Sin barra lateral, sin barra superior, sin navegación.
   ============================================================================ */

.gf-canvas {
  /* Se descuenta la barra de validación fija, que no forma parte del producto. */
  min-height: calc(100vh - 92px);
  display: flex; flex-direction: column; align-items: center;
  padding: var(--space-9) var(--space-5) var(--space-7);
  background: var(--color-background-tertiary);
}

/* Anclada a la franja superior, nunca centrada verticalmente. */
.gf-access-card {
  width: 100%; max-width: 380px;
  background: var(--color-background-primary);
  border: var(--border-hairline);
  border-radius: var(--radius-lg);
  padding: var(--space-7);
}

.gf-identity {
  display: flex; align-items: center; gap: var(--space-2);
  color: var(--color-brand-primary);
  font-size: var(--type-body-strong-size); font-weight: var(--type-body-strong-weight);
  margin-bottom: var(--space-5);
}

.gf-access-card h1 { font-size: var(--type-title-size); font-weight: var(--type-title-weight); }
.gf-subtitle {
  margin: var(--space-2) 0 var(--space-5);
  font-size: var(--type-caption-size); color: var(--color-text-secondary);
}

/* ============================================================================
   Shell de trabajo (Experiencia-De-Uso §3.2 · base §3.1)
   ============================================================================ */

.gf-shell { display: flex; min-height: calc(100vh - 92px); align-items: stretch; }

.gf-shell-sidebar {
  width: var(--shell-sidebar-width); flex: none;
  background: var(--color-brand-primary-dark);
  color: var(--color-background-primary);
  display: flex; flex-direction: column;
  padding: var(--space-5) var(--space-3);
}
.gf-shell-sidebar .gf-identity { color: var(--color-background-primary); }

.gf-nav { display: flex; flex-direction: column; gap: var(--space-1); }
.gf-nav a {
  display: flex; align-items: center; gap: var(--space-2);
  min-height: 40px; padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md); text-decoration: none;
  font-size: var(--type-body-size);
  color: var(--color-background-primary); opacity: .72;
}
.gf-nav a:hover { opacity: 1; }
.gf-nav a[aria-current="page"] { background: var(--color-brand-primary); opacity: 1; }

.gf-sidebar-footer {
  margin-top: auto; padding-top: var(--space-4);
  border-top: 1px solid var(--color-border-on-brand);
  display: flex; flex-direction: column; gap: var(--space-2);
}
.gf-sidebar-person { font-size: var(--type-body-size); }
.gf-sidebar-role   { font-size: var(--type-meta-size); opacity: .78; }

.gf-shell-content {
  flex: 1 1 auto; min-width: 0;
  padding: var(--space-6) var(--space-7);
  background: var(--color-background-tertiary);
}

.gf-page-header {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  align-items: flex-start; justify-content: space-between;
  margin-bottom: var(--space-5);
}
.gf-page-header h1 { font-size: var(--type-title-size); font-weight: var(--type-title-weight); }
.gf-page-header .gf-subtitle { margin: var(--space-1) 0 0; max-width: 62ch; }

.gf-divider { border: 0; border-top: 1px solid var(--color-border-tertiary); margin: var(--space-5) 0; }

/* ============================================================================
   Controles de formulario (base §4.6)
   ============================================================================ */

.gf-field { margin-bottom: var(--space-4); }
.gf-field label {
  display: block; margin-bottom: var(--space-1);
  font-size: var(--type-caption-size); color: var(--color-text-secondary);
}
.gf-input, .gf-select, .gf-textarea {
  width: 100%; min-height: 38px;
  padding: var(--space-2) var(--space-3);
  font-family: inherit; font-size: var(--type-body-size); color: var(--color-text-primary);
  background: var(--color-background-primary);
  border: var(--border-control); border-radius: var(--radius-md);
}
.gf-input::placeholder, .gf-textarea::placeholder { color: var(--color-text-tertiary); }
.gf-textarea { min-height: 132px; resize: vertical; }
.gf-textarea--work-text {
  font-family: var(--font-mono); font-size: var(--type-caption-size); line-height: 1.5;
  white-space: pre; overflow: auto;
}
.gf-requirement {
  display: block; margin-top: var(--space-1);
  font-size: var(--type-caption-size); color: var(--color-text-secondary);
}
.gf-field--error .gf-input,
.gf-field--error .gf-textarea { border-color: var(--color-border-danger); border-width: 2px; }
.gf-field--error .gf-requirement { color: var(--color-text-danger); }

.gf-field-search { position: relative; }
.gf-field-search .gf-icon {
  position: absolute; left: var(--space-3); top: 50%; transform: translateY(-50%);
  color: var(--color-text-secondary); pointer-events: none;
}
.gf-field-search .gf-input { padding-left: calc(var(--space-3) * 2 + 16px); }

.gf-filters {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  align-items: flex-end; margin-bottom: var(--space-4);
}
.gf-filters .gf-field { margin-bottom: 0; min-width: 180px; }

/* ============================================================================
   Botones (base §4.9)
   ============================================================================ */

.gf-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-height: 34px; min-width: 34px;
  padding: var(--space-2) var(--space-4);
  font-family: inherit; font-size: var(--type-body-size); font-weight: 500;
  border-radius: var(--radius-md); border: 1px solid transparent;
  cursor: pointer; text-decoration: none;
  transition: background var(--motion-fast) var(--motion-ease);
}
.gf-btn--primary   { background: var(--color-brand-primary); color: var(--color-text-on-brand); }
.gf-btn--primary:hover { background: var(--color-brand-primary-dark); color: var(--color-text-on-brand); }
.gf-btn--secondary { background: var(--color-background-primary); color: var(--color-text-secondary); border-color: var(--color-border-secondary); }
.gf-btn--destructive{ background: var(--color-background-primary); color: var(--color-text-danger); border-color: var(--color-border-danger); }
.gf-btn--pill       { background: var(--color-background-primary); color: var(--color-text-secondary);
                      border-color: var(--color-border-secondary); border-radius: var(--radius-pill); }
.gf-btn--full      { width: 100%; }
.gf-btn[disabled], .gf-btn[aria-disabled="true"] { opacity: .55; cursor: not-allowed; }

.gf-row-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); justify-content: flex-end; }
.gf-footer-actions  { display: flex; flex-wrap: wrap; gap: var(--space-3); justify-content: flex-end; margin-top: var(--space-5); }

/* Indicador de operación en curso, dentro del propio control */
.gf-spinner {
  width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  animation: gf-spin 700ms linear infinite;
}
@keyframes gf-spin { to { transform: rotate(360deg); } }

/* ============================================================================
   Insignias y chips de estado (base §4.8 · Representacion-Fila-De-Trabajo §2)
   El texto está siempre presente; el color es refuerzo.
   ============================================================================ */

.gf-badge {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 2px var(--space-3); border-radius: var(--radius-pill);
  font-size: var(--type-meta-size); font-weight: var(--type-meta-weight);
  white-space: nowrap;
}
.gf-badge--neutral    { background: var(--color-background-secondary); color: var(--color-text-secondary); }
.gf-badge--warning  { background: var(--color-background-warning);   color: var(--color-text-warning); }
.gf-badge--success     { background: var(--color-background-success);   color: var(--color-text-success); }
.gf-badge--danger   { background: var(--color-background-error);     color: var(--color-text-error); }
.gf-badge--info      { background: var(--color-background-info);      color: var(--color-text-info); }

/* ============================================================================
   Bandas de resultado (Primer arranque §4.4)
   ============================================================================ */

.gf-banner {
  display: flex; gap: var(--space-3); align-items: flex-start;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md); border: 1px solid transparent;
  font-size: var(--type-body-size);
  margin-bottom: var(--space-5);
}
.gf-banner--error        { background: var(--color-background-error);   color: var(--color-text-error);   border-color: var(--color-border-danger); }
.gf-banner--confirmation { background: var(--color-background-success); color: var(--color-text-success); border-color: var(--color-brand-primary-tint); }
.gf-banner--warning     { background: var(--color-background-warning); color: var(--color-text-warning); border-color: var(--color-text-warning); }
.gf-banner--info         { background: var(--color-background-info);    color: var(--color-text-info);    border-color: var(--color-border-info); }

/* ============================================================================
   Tarjetas, bloques y grillas
   ============================================================================ */

.gf-card {
  background: var(--color-background-primary);
  border: var(--border-hairline); border-radius: var(--radius-lg);
  padding: var(--space-5);
}
.gf-card + .gf-card { margin-top: var(--space-4); }

.gf-block { margin-bottom: var(--space-5); }
.gf-block > h2 { margin-bottom: var(--space-3); }

.gf-access-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: var(--space-4);
}
.gf-access-card-module {
  background: var(--color-background-primary); border: var(--border-hairline);
  border-radius: var(--radius-lg); padding: var(--space-5);
  display: flex; flex-direction: column; gap: var(--space-2);
}
.gf-icon-holder {
  width: 36px; height: 36px; border-radius: var(--radius-icon);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--color-brand-primary-tint); color: var(--color-brand-primary);
}

.gf-key-value { display: grid; grid-template-columns: max-content 1fr; gap: var(--space-2) var(--space-4); }
.gf-key-value dt { font-size: var(--type-caption-size); color: var(--color-text-secondary); margin: 0; }
.gf-key-value dd { margin: 0; font-size: var(--type-body-size); }

/* ============================================================================
   Grilla de listado (base §4.3)
   ============================================================================ */

.gf-table-wrapper { overflow-x: auto; }
.gf-table { width: 100%; border-collapse: collapse; background: var(--color-background-primary); }
.gf-table caption { text-align: left; padding: var(--space-2) var(--space-4); font-size: var(--type-caption-size); color: var(--color-text-secondary); }
.gf-table th {
  text-align: left; padding: var(--space-2) var(--space-4);
  font-size: var(--type-meta-size); font-weight: var(--type-meta-weight);
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--color-text-secondary);
  background: var(--color-background-tertiary);
  border-bottom: var(--border-hairline);
}
.gf-table td { padding: var(--space-3) var(--space-4); border-bottom: var(--border-hairline); vertical-align: middle; }
.gf-table tbody tr:hover { background: var(--color-background-tertiary); }
.gf-table .gf-td-actions { text-align: right; white-space: nowrap; }

.gf-initials {
  width: 28px; height: 28px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--color-brand-primary-tint); color: var(--color-brand-primary);
  font-size: var(--type-meta-size); font-weight: 600;
}

.gf-group { border: var(--border-hairline); border-radius: var(--radius-lg); overflow: hidden; margin-bottom: var(--space-4); background: var(--color-background-primary); }
.gf-group-header {
  width: 100%; display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4); background: var(--color-background-tertiary);
  border: 0; border-bottom: var(--border-hairline); text-align: left;
  font-family: inherit; font-size: var(--type-body-strong-size); font-weight: 500; cursor: pointer;
}
.gf-group-count { margin-left: auto; font-size: var(--type-caption-size); color: var(--color-text-secondary); }

/* Tarjetas apiladas: versión angosta de la fila de trabajo y de cuenta */
.gf-stacked-cards { display: none; flex-direction: column; gap: var(--space-3); }
.gf-row-card {
  background: var(--color-background-primary); border: var(--border-hairline);
  border-radius: var(--radius-lg); padding: var(--space-4);
}
.gf-row-card-header { display: flex; gap: var(--space-3); align-items: center; justify-content: space-between; }
.gf-row-card .gf-row-actions { justify-content: stretch; margin-top: var(--space-3); }
.gf-row-card .gf-row-actions .gf-btn { flex: 1 1 120px; }

/* ============================================================================
   Estado vacío (base §5) — invitación, no hueco
   ============================================================================ */

.gf-empty {
  text-align: center; padding: var(--space-9) var(--space-5);
  background: var(--color-background-primary);
  border: var(--border-hairline); border-radius: var(--radius-lg);
}
.gf-empty svg { width: 96px; height: 96px; color: var(--color-border-secondary); }
.gf-empty h2 { margin: var(--space-4) 0 var(--space-2); font-size: var(--type-body-strong-size); }
.gf-empty p  { color: var(--color-text-secondary); font-size: var(--type-caption-size); max-width: 46ch; margin-inline: auto; }

/* Aviso de indisponibilidad (Estado degradado §3) */
.gf-unavailable-notice {
  background: var(--color-background-primary);
  border: 1px solid var(--color-border-danger); border-left-width: 4px;
  border-radius: var(--radius-lg); padding: var(--space-5);
}
.gf-unavailable-notice h2 {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--type-body-strong-size); color: var(--color-text-danger); margin-bottom: var(--space-2);
}
.gf-unavailable-notice p { color: var(--color-text-secondary); font-size: var(--type-caption-size); max-width: 62ch; }

/* Cartel de reconexión: banda superpuesta en el borde superior, no empuja */
.gf-reconnect-notice {
  position: fixed; inset: 0 0 auto 0; z-index: 50;
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  background: var(--color-background-warning); color: var(--color-text-warning);
  border-bottom: 1px solid var(--color-text-warning);
  font-size: var(--type-body-size);
}
body.gf-inert .gf-shell,
body.gf-inert .gf-canvas { pointer-events: none; filter: grayscale(.35) opacity(.7); }

/* Esqueletos de carga (base §5) */
.gf-skeleton {
  background: linear-gradient(90deg, var(--color-background-secondary) 25%, var(--color-background-tertiary) 37%, var(--color-background-secondary) 63%);
  background-size: 400% 100%; animation: gf-shimmer 1.4s ease infinite;
  border-radius: var(--radius-md); height: 14px; margin-bottom: var(--space-3);
}
.gf-skeleton--tall { height: 132px; }
.gf-skeleton--field { height: 38px; }
@keyframes gf-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

.gf-indeterminate-bar {
  height: 4px; width: 100%; border-radius: var(--radius-pill);
  background: var(--color-background-secondary); overflow: hidden; position: relative;
}
.gf-indeterminate-bar::after {
  content: ""; position: absolute; inset: 0; width: 35%;
  background: var(--color-brand-primary); animation: gf-sweep 1.2s ease-in-out infinite;
}
@keyframes gf-sweep { 0% { left: -35%; } 100% { left: 100%; } }

/* ============================================================================
   Vista de trabajo: cuatro partes (Wireframes-Vista-De-Trabajo §2)
   Izquierda ~1/3 (datos y texto) · derecha ~2/3 (escena arriba, árbol abajo).
   Disposición decidida aguas arriba y probada en el aula: no se rediseña.
   ============================================================================ */

.gf-two-columns {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: var(--space-5);
  align-items: start;
}
.gf-column { min-width: 0; display: flex; flex-direction: column; gap: var(--space-4); }

/* Bloque de la escena: relación de aspecto próxima a 4:3 [ASUNCIÓN de 03] */
.gf-scene {
  position: relative; width: 100%; aspect-ratio: 4 / 3; min-height: 240px;
  background: var(--color-background-primary);
  border: var(--border-hairline); border-radius: var(--radius-lg);
  overflow: hidden;
}
.gf-scene svg { width: 100%; height: 100%; display: block; }

/* Lienzo del visor real (assets/js/Visor-Tridimensional.js). Ocupa el área
   entera; el rótulo y la leyenda quedan por encima. */
.gf-scene-canvas {
  position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; display: block;
  border-radius: var(--radius-lg); cursor: grab; touch-action: none;
}
.gf-scene-canvas:active { cursor: grabbing; }
/* Las dos casillas de movimiento automático, al pie del área de dibujo. Son
   preferencias de quien mira la escena, no instrumento de validación: por eso
   viven acá y no en el panel del contrato. */
.gf-scene-motion {
  position: absolute; left: var(--space-3); bottom: var(--space-3); right: var(--space-3); z-index: 1;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4);
  font-size: var(--type-caption-size); color: var(--color-text-secondary);
  background: var(--color-background-tertiary);
  border: var(--border-hairline); border-radius: var(--radius-pill);
  padding: var(--space-2) var(--space-3);
}
.gf-scene-motion[hidden] { display: none; }
.gf-scene-motion__option { display: inline-flex; align-items: center; gap: var(--space-2); }
.gf-scene-motion__option input { width: 16px; height: 16px; margin: 0; accent-color: var(--color-brand-primary); }
/* Objetivo de toque de al menos 24 px sin agrandar el recuadro visible. */
.gf-scene-motion__option label { cursor: pointer; padding: var(--space-1) 0; min-height: 24px; display: inline-flex; align-items: center; }
.gf-scene-motion__note { color: var(--color-text-tertiary); }

.gf-scene-caption {
  position: absolute; left: 50%; top: 50%; z-index: 1; transform: translate(-50%, -50%);
  margin: 0; max-width: 80%; text-align: center;
  font-size: var(--type-caption-size); color: var(--color-text-secondary);
}
.gf-scene-label {
  position: absolute; left: var(--space-3); top: var(--space-3); z-index: 1;
  font-size: var(--type-meta-size); font-weight: var(--type-meta-weight);
  color: var(--color-text-secondary); background: var(--color-background-tertiary);
  border: var(--border-hairline); border-radius: var(--radius-pill);
  padding: 2px var(--space-3);
}
.gf-piece { cursor: pointer; }
.gf-piece .gf-piece-body {
  fill: var(--color-background-secondary);
  stroke: var(--color-text-secondary); stroke-width: 1.5;
  transition: fill var(--motion-fast) var(--motion-ease);
}
.gf-piece[aria-pressed="true"] .gf-piece-body {
  fill: var(--color-brand-primary-tint); stroke: var(--color-brand-primary); stroke-width: 2.5;
}
.gf-piece-label { font-size: var(--type-meta-size); fill: var(--color-text-secondary); }
.gf-piece[aria-pressed="true"] .gf-piece-label { fill: var(--color-brand-primary); font-weight: 600; }

/* Árbol de la estructura */
.gf-tree { list-style: none; margin: 0; padding: 0; font-size: var(--type-body-size); }
.gf-tree ul { list-style: none; margin: 0; padding-left: var(--space-5); }
.gf-node {
  display: flex; align-items: center; gap: var(--space-2);
  width: 100%; min-height: 28px; padding: var(--space-1) var(--space-2);
  border: 0; background: transparent; border-radius: var(--radius-md);
  font-family: inherit; font-size: var(--type-body-size); color: var(--color-text-primary);
  text-align: left; cursor: pointer;
}
.gf-node:hover { background: var(--color-background-tertiary); }
/* El estado vive en el `<li role="treeitem">`, que es el único portador de rol
   y el que recibe el foco; el `<span>` interior sólo lo dibuja. */
[role="treeitem"][aria-selected="true"] > .gf-node { background: var(--color-brand-primary-tint); color: var(--color-brand-primary); font-weight: 500; }
.gf-node-index { font-variant-numeric: tabular-nums; color: var(--color-text-secondary); }
[role="treeitem"][aria-selected="true"] > .gf-node .gf-node-index { color: inherit; }
.gf-tree [role="treeitem"] { outline-offset: -2px; }

/* Recorrido colapsable, absorbido del árbol del visor original. La flecha es
   el control de despliegue; en el nodo de pieza el resto del botón sigue
   siendo la selección por índice. */
.gf-node--branch, .gf-node--leaf { font-family: var(--font-mono); font-size: var(--type-caption-size); }
.gf-node--leaf { cursor: default; }
.gf-node-arrow {
  flex: 0 0 auto; width: 14px; height: 14px; display: inline-block; position: relative;
  transition: transform var(--motion-fast) var(--motion-ease);
}
.gf-node-arrow::before {
  content: ""; position: absolute; left: 3px; top: 3px;
  width: 6px; height: 6px; border-right: 1.5px solid var(--color-text-secondary);
  border-bottom: 1.5px solid var(--color-text-secondary); transform: rotate(-45deg);
}
[role="treeitem"][aria-expanded="true"] > .gf-node .gf-node-arrow { transform: rotate(90deg); }
.gf-tree-children[hidden] { display: none; }
.gf-json-key  { color: var(--color-accent-module-d); font-weight: 500; }
.gf-json-count { color: var(--color-text-tertiary); margin-left: var(--space-1); }
.gf-json-string { color: var(--color-accent-module-c); }
.gf-json-number { color: var(--color-accent-module-b); font-variant-numeric: tabular-nums; }
.gf-json-other   { color: var(--color-text-secondary); }
.gf-scroll-box { max-height: 420px; overflow: auto; }

/* Lista de observaciones (Representacion-Lista-De-Observaciones §2) */
.gf-findings { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.gf-finding { display: flex; gap: var(--space-3); align-items: flex-start; }
.gf-finding-body { min-width: 0; }
.gf-finding-location { font-size: var(--type-caption-size); color: var(--color-text-secondary); }
.gf-values { display: grid; grid-template-columns: max-content max-content; gap: 2px var(--space-4); margin-top: var(--space-1); }
.gf-values dt { font-size: var(--type-caption-size); color: var(--color-text-secondary); margin: 0; }
.gf-values dd { margin: 0; font-variant-numeric: tabular-nums; font-size: var(--type-body-size); }

/* Bloque de comentario del docente: contenedor propio, tono neutro, sin severidad */
.gf-comment {
  background: var(--color-background-secondary);
  border: var(--border-hairline); border-radius: var(--radius-lg);
  padding: var(--space-4);
}

/* Texto original, colapsado por omisión */
.gf-original-text {
  font-family: var(--font-mono); font-size: var(--type-caption-size); line-height: 1.5;
  white-space: pre; overflow: auto; max-height: 260px;
  background: var(--color-background-tertiary);
  border: var(--border-hairline); border-radius: var(--radius-md);
  padding: var(--space-3); margin: var(--space-2) 0 0;
}
.gf-disclosure summary {
  cursor: pointer; font-size: var(--type-meta-size); font-weight: var(--type-meta-weight);
  letter-spacing: .04em; text-transform: uppercase; color: var(--color-text-secondary);
}

/* ============================================================================
   Diálogos de confirmación (base §4.4)
   ============================================================================ */

.gf-dialog {
  border: var(--border-hairline); border-radius: var(--radius-lg);
  padding: var(--space-6); max-width: 520px; width: calc(100% - var(--space-7));
  background: var(--color-background-primary); color: var(--color-text-primary);
}
.gf-dialog::backdrop { background: var(--color-backdrop); }
.gf-dialog h2 { font-size: var(--type-title-size); margin-bottom: var(--space-3); }
.gf-dialog p  { font-size: var(--type-body-size); color: var(--color-text-secondary); }

/* ============================================================================
   Sello de versión (Representacion-Sello-De-Version §2)
   Jerarquía baja, pero contraste AA: usa color.text.secondary y no tertiary.
   ============================================================================ */

.gf-seal {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  font-size: var(--type-meta-size); font-weight: var(--type-meta-weight);
  color: var(--color-text-secondary);
}
.gf-canvas .gf-seal { margin-top: var(--space-6); justify-content: center; }
.gf-shell-sidebar .gf-seal { color: var(--color-text-on-brand-soft); }
.gf-seal-button {
  background: transparent; border: 0; padding: 2px var(--space-1); margin-left: -4px;
  font: inherit; color: inherit; cursor: pointer; text-decoration: underline dotted;
  border-radius: var(--radius-md);
}
.gf-diagnostics {
  margin-top: var(--space-2); padding: var(--space-3);
  background: var(--color-background-primary); border: var(--border-hairline);
  border-radius: var(--radius-md); color: var(--color-text-primary);
  font-size: var(--type-caption-size); font-weight: 400; text-transform: none; letter-spacing: 0;
  max-width: 320px;
}
.gf-shell-sidebar .gf-diagnostics { color: var(--color-text-primary); }

/* ============================================================================
   Utilidades de composición

   Existen para que ningún HTML de la maqueta lleve un atributo `style=` en
   línea. Cada una materializa un token del catálogo —espaciado, tipografía o
   color— y ninguna introduce un valor propio. Los pocos valores que no son
   tokens (medidas de columna, alturas de esqueleto, medidas de lectura) viven
   acá, en una regla con nombre, y no repartidos por doce archivos.
   ============================================================================ */

.gf-mt-2 { margin-top: var(--space-2); }
.gf-mt-3 { margin-top: var(--space-3); }
.gf-mt-4 { margin-top: var(--space-4); }
.gf-mt-5 { margin-top: var(--space-5); }
.gf-mt-6 { margin-top: var(--space-6); }
.gf-mt-7 { margin-top: var(--space-7); }
.gf-mt-9 { margin-top: var(--space-9); }
.gf-mt-auto { margin-top: auto; }
.gf-ml-auto { margin-left: auto; }
.gf-mb-4 { margin-bottom: var(--space-4); }
.gf-mb-5 { margin-bottom: var(--space-5); }
.gf-no-margin { margin: 0; }
.gf-margin-top-1 { margin: var(--space-1) 0 0; }
.gf-margin-top-2 { margin: var(--space-2) 0 0; }
.gf-margin-block-5 { margin: var(--space-5) 0; }
.gf-padding-4 { padding: var(--space-4); }
.gf-indent-2 { padding-left: var(--space-2); }

.gf-nested-list { margin: var(--space-1) 0 0; padding-left: var(--space-6); }
.gf-flat-list { list-style: none; padding: 0; margin: 0 0 var(--space-9); }
.gf-flat-list--tight { list-style: none; padding: 0; margin: 0; }

.gf-width-short { max-width: 280px; }
.gf-width-field { width: 100%; max-width: 380px; }
.gf-width-medium { max-width: 520px; }
.gf-width-wide { max-width: 640px; }

.gf-flex-row { display: flex; align-items: center; gap: var(--space-3); }
.gf-flex-row--wrap { flex-wrap: wrap; }
.gf-justify-start { justify-content: flex-start; }
.gf-flexible { flex: 1 1 40px; }
.gf-clickable { cursor: pointer; }
.gf-muted { opacity: .85; }

/* Encabezado de tabla que no quiere el tratamiento de encabezado: se usa
   cuando la primera columna es una etiqueta de fila y no un rótulo de columna. */
th.gf-th-plain {
  background: transparent; text-transform: none; letter-spacing: 0; color: inherit;
  font-size: var(--type-body-size); font-weight: var(--type-body-weight);
}
th.gf-th-plain--strong { font-weight: var(--type-body-strong-weight); }
th.gf-th-plain--body { font-size: var(--type-body-strong-size); font-weight: var(--type-body-strong-weight); }
th.gf-th-plain--brief  { font-size: var(--type-caption-size); }

/* Anchos relativos de los esqueletos de carga que imitan campos y columnas. */
.gf-width-30 { width: 30%; }
.gf-width-34 { width: 34%; }
.gf-width-38 { width: 38%; }
.gf-width-40 { width: 40%; }
.gf-width-45 { width: 45%; }
.gf-width-46 { width: 46%; }

/* Medidas de los esqueletos de carga, por lo que reemplazan. */
.gf-skeleton--row   { height: 42px; }
.gf-skeleton--block { height: 150px; }
.gf-skeleton--scene { height: 260px; }

.gf-textarea--short { min-height: 64px; }
.gf-textarea--medium { min-height: 72px; }

.gf-icon-holder--success { background: var(--color-background-success); color: var(--color-text-success); }
.gf-piece .gf-piece-body--outline,
.gf-piece[aria-pressed="true"] .gf-piece-body--outline { fill: none; }

/* ============================================================================
   Versión angosta · punto de quiebre principal 768 px [ASUNCIÓN de 03]
   ============================================================================ */

@media (max-width: 768px) {
  .gf-shell { flex-direction: column; }
  .gf-shell-sidebar { width: 100%; flex-direction: row; flex-wrap: wrap; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4); }
  .gf-shell-sidebar .gf-identity { margin-bottom: 0; }
  .gf-nav { flex-direction: row; flex-wrap: wrap; }
  .gf-sidebar-footer { margin-top: 0; width: 100%; border-top: 0; padding-top: 0; flex-direction: row; align-items: center; gap: var(--space-4); }
  .gf-shell-content { padding: var(--space-5) var(--space-4); }

  /* Las filas dejan de ser filas de tabla y pasan a tarjetas apiladas */
  .gf-table-wrapper { display: none; }
  .gf-stacked-cards { display: flex; }

  /* Vista de trabajo: una sola columna, con la escena en primer lugar */
  .gf-two-columns { grid-template-columns: minmax(0, 1fr); }
  .gf-column--scene { order: 1; }
  .gf-column--data  { order: 2; }
  /* Envío de trabajo: acá la previsualización NO sube al primer lugar */
  .gf-two-columns--submission .gf-column--scene { order: 2; }
  .gf-two-columns--submission .gf-column--data  { order: 1; }

  .gf-footer-actions { flex-direction: column-reverse; }
  .gf-footer-actions .gf-btn { width: 100%; }
  .gf-filters .gf-field { min-width: 100%; }
}

/* ---- Controles de movimiento automático de la escena (F-25) ------------------------------------
   PORTADAS DE LA MAQUETA APROBADA, valor por valor, con la traducción de `Norma-De-Nomenclatura.md`
   §6.12: `mq-escena-movimiento` ⟶ `gf-scene-motion`. No se inventó ninguna regla ni ningún valor;
   lo único que cambia es el nombre, que es lo que la norma manda. */
.gf-scene-motion {
  position: absolute; left: var(--space-3); bottom: var(--space-3); right: var(--space-3); z-index: 1;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4);
  font-size: var(--type-caption-size); color: var(--color-text-secondary);
  background: var(--color-background-tertiary);
  border: var(--border-hairline); border-radius: var(--radius-pill);
  padding: var(--space-2) var(--space-3);
}
.gf-scene-motion[hidden] { display: none; }
.gf-scene-motion-option { display: inline-flex; align-items: center; gap: var(--space-2); }
.gf-scene-motion-option input { width: 16px; height: 16px; margin: 0; accent-color: var(--color-brand-primary); }
/* Objetivo de toque de al menos 24 px sin agrandar el recuadro visible. */
.gf-scene-motion-option label { cursor: pointer; padding: var(--space-1) 0; min-height: 24px; display: inline-flex; align-items: center; }

/* ---- Árbol de la estructura (F-13) -------------------------------------------------------------
   PORTADO DE LA MAQUETA APROBADA, valor por valor, con la traducción de §6.12: `mq-arbol` ⟶
   `gf-tree`, `mq-nodo` ⟶ `gf-node`, `mq-nodo-indice` ⟶ `gf-node-index`, `mq-arbol-hijos` ⟶
   `gf-tree-children`. El estado de selección vive en el `role="treeitem"`, que es el único
   portador de rol y el que recibe el foco; el interior sólo lo dibuja. */
.gf-tree { list-style: none; margin: 0; padding: 0; font-size: var(--type-body-size); }
.gf-tree ul { list-style: none; margin: 0; padding-left: var(--space-5); }
.gf-node {
  display: flex; align-items: center; gap: var(--space-2);
  width: 100%; min-height: 28px; padding: var(--space-1) var(--space-2);
  border: 0; background: transparent; border-radius: var(--radius-md);
  font-family: inherit; font-size: var(--type-body-size); color: var(--color-text-primary);
  text-align: left; cursor: pointer;
}
.gf-node:hover { background: var(--color-background-tertiary); }
[role="treeitem"][aria-selected="true"] > .gf-node { background: var(--color-brand-primary-tint); color: var(--color-brand-primary); font-weight: 500; }
.gf-node-index { font-variant-numeric: tabular-nums; color: var(--color-text-secondary); }
[role="treeitem"][aria-selected="true"] > .gf-node .gf-node-index { color: inherit; }
.gf-tree [role="treeitem"] { outline-offset: -2px; }
.gf-node--leaf { font-family: var(--font-mono); font-size: var(--type-caption-size); cursor: default; }
.gf-tree-children[hidden] { display: none; }
.gf-scene-motion-note { color: var(--color-text-tertiary); }
