/**
 * App shell: sidebar + topbar + área de contenido.
 *
 * Estructura esperada:
 *   .ds-app
 *     ├── .ds-sidebar
 *     ├── .ds-sidebar-scrim   (solo navegación compacta)
 *     ├── .ds-tabbar          (solo móvil)
 *     └── .ds-main
 *           ├── .ds-topbar
 *           └── .ds-content
 *
 * La navegación tiene tres formas, una por tamaño:
 *
 *   ≥ 1024px  sidebar fija, colapsable a iconos y con la preferencia guardada.
 *   768–1023  sidebar deslizable desde el borde, con el botón ☰ de la topbar.
 *   < 768px   **barra de pestañas flotante abajo**: el sidebar no existe y las
 *             secciones principales están siempre a la vista.
 *
 * La última no es el mismo panel con otro disparador. Un cajón lateral en un
 * teléfono se abre desde el borde superior izquierdo —la esquina más lejana al
 * pulgar de quien sostiene el aparato con una mano— y tapa la pantalla entera
 * para elegir entre cuatro secciones. La barra inferior no tapa nada, está donde
 * descansa la mano y, sobre todo, **muestra dónde se está parado sin abrir
 * nada**: un menú escondido obliga a abrirlo para responder esa pregunta.
 */

.ds-app {
  display: grid;
  grid-template-columns: var(--ds-sidebar-width) minmax(0, 1fr);
  min-height: 100vh;
  min-height: 100dvh;
  transition: grid-template-columns var(--ds-duration-base) var(--ds-ease);
}

.ds-app[data-sidebar="collapsed"] {
  grid-template-columns: var(--ds-sidebar-width-collapsed) minmax(0, 1fr);
}

/* ---------- Sidebar ---------- */

.ds-sidebar {
  grid-row: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--ds-sidebar-bg);
  color: var(--ds-sidebar-text);
  /* Sin línea de separación: el sidebar ya tiene su propia superficie oscura,
     que separa mucho mejor que un pelo de un 9% de blanco. */
  position: sticky;
  top: 0;
  height: 100vh;
  z-index: var(--ds-z-sidebar);
}

.ds-sidebar__brand {
  display: flex;
  align-items: center;
  gap: var(--ds-space-3);
  height: var(--ds-topbar-height);
  padding: 0 var(--ds-space-4);
  border-bottom: 1px solid var(--ds-sidebar-border);
  flex-shrink: 0;
  overflow: hidden;
}

.ds-sidebar__logo {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: var(--ds-radius-md);
  background: var(--ds-accent);
  color: #fff;
  font-weight: var(--ds-weight-semibold);
  font-size: var(--ds-text-md);
}

.ds-sidebar__brand-text {
  display: grid;
  min-width: 0;
}

.ds-sidebar__brand-name {
  font-size: var(--ds-text-md);
  font-weight: var(--ds-weight-semibold);
  color: var(--ds-sidebar-text-strong);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ds-sidebar__brand-role {
  font-size: var(--ds-text-xs);
  color: var(--ds-sidebar-text);
  opacity: 0.75;
  white-space: nowrap;
}

.ds-sidebar__nav {
  flex: 1;
  overflow-y: auto;
  padding: var(--ds-space-4) var(--ds-space-3);
  display: grid;
  gap: var(--ds-space-1);
  align-content: start;
}

.ds-sidebar__group-label {
  font-size: var(--ds-text-xs);
  font-weight: var(--ds-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ds-sidebar-text);
  opacity: 0.55;
  padding: var(--ds-space-4) var(--ds-space-3) var(--ds-space-1);
  white-space: nowrap;
  overflow: hidden;
}

.ds-sidebar__group-label:first-child {
  padding-top: 0;
}

.ds-sidebar__link {
  display: flex;
  align-items: center;
  gap: var(--ds-space-3);
  padding: var(--ds-space-2) var(--ds-space-3);
  border-radius: var(--ds-radius-md);
  color: var(--ds-sidebar-text);
  font-size: var(--ds-text-md);
  font-weight: var(--ds-weight-medium);
  text-decoration: none;
  border: none;
  background: none;
  width: 100%;
  text-align: left;
  cursor: pointer;
  transition: background var(--ds-duration-fast) var(--ds-ease),
              color var(--ds-duration-fast) var(--ds-ease);
}

.ds-sidebar__link:hover {
  background: var(--ds-sidebar-hover);
  color: var(--ds-sidebar-text-strong);
  text-decoration: none;
}

.ds-sidebar__link[aria-current="page"] {
  background: var(--ds-sidebar-active);
  color: var(--ds-sidebar-text-strong);
}

.ds-sidebar__link:focus-visible {
  outline: 2px solid var(--ds-accent);
  outline-offset: -2px;
}

.ds-sidebar__icon,
/* Los enlaces del pie del sidebar insertan el <svg> directamente. */
.ds-sidebar__link > svg {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  display: grid;
  place-items: center;
}

.ds-sidebar__label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ds-sidebar__badge {
  margin-left: auto;
  flex-shrink: 0;
  min-width: 20px;
  padding: 0 var(--ds-space-2);
  border-radius: var(--ds-radius-full);
  background: var(--ds-accent);
  color: #fff;
  font-size: var(--ds-text-xs);
  font-weight: var(--ds-weight-semibold);
  text-align: center;
}

/*
 * El sidebar ya no tiene pie. La versión, el correo y «Salir» se mudaron al
 * diálogo de opciones de la topbar: son cosas que se tocan una vez y estaban
 * ocupando el lugar más visible de una columna que se usa cien veces por día.
 * La clase se borró en lugar de dejarla sin usar, para que nadie la reponga
 * creyendo que el sidebar todavía la dibuja.
 */

/* Estado colapsado: se ocultan las etiquetas y el ítem se centra. */
.ds-app[data-sidebar="collapsed"] .ds-sidebar__label,
.ds-app[data-sidebar="collapsed"] .ds-sidebar__brand-text,
.ds-app[data-sidebar="collapsed"] .ds-sidebar__group-label,
.ds-app[data-sidebar="collapsed"] .ds-sidebar__badge {
  display: none;
}

.ds-app[data-sidebar="collapsed"] .ds-sidebar__link,
.ds-app[data-sidebar="collapsed"] .ds-sidebar__brand {
  justify-content: center;
  padding-inline: 0;
}

.ds-sidebar-scrim {
  display: none;
}

/* ---------- Main ---------- */

.ds-main {
  grid-row: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.ds-topbar {
  position: sticky;
  top: 0;
  z-index: var(--ds-z-topbar);
  display: flex;
  align-items: center;
  gap: var(--ds-space-3);
  height: var(--ds-topbar-height);
  padding: 0 var(--ds-space-5);
  /* La superficie propia ya la separa del contenido; la línea sobraba. */
  background: var(--ds-surface);
  flex-shrink: 0;
}

.ds-topbar__title {
  font-size: var(--ds-text-lg);
  font-weight: var(--ds-weight-semibold);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ds-topbar__actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--ds-space-2);
}

.ds-content {
  flex: 1;
  width: 100%;
  max-width: var(--ds-content-max);
  padding: var(--ds-space-5);
  display: grid;
  gap: var(--ds-space-5);
  align-content: start;
}

/* ---------- Encabezado de página ---------- */

.ds-page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--ds-space-4);
  flex-wrap: wrap;
}

.ds-page-header__text {
  display: grid;
  gap: var(--ds-space-1);
  min-width: 0;
}

.ds-page-header__actions {
  display: flex;
  gap: var(--ds-space-2);
  flex-wrap: wrap;
}

/* ---------- Barra de pestañas inferior (se muestra solo en móvil) ---------- */

/*
 * Flotante, de vidrio, y del ancho de sus pestañas.
 *
 * Flotante —separada de los tres bordes— por dos motivos: se ve que hay
 * contenido por debajo, y en los teléfonos con barra de gestos la franja
 * inferior es del sistema, así que una barra pegada al borde pelea con el
 * deslizamiento para volver al inicio.
 *
 * De vidrio porque una barra opaca corta la pantalla en dos y hace creer que la
 * lista termina ahí. Con el desenfoque se sigue viendo que el contenido pasa por
 * debajo, que es lo que invita a seguir desplazando.
 *
 * Y del ancho de sus pestañas, centrada, en lugar de borde a borde: así se lee
 * como un objeto apoyado sobre el contenido —que es lo que la sombra y el radio
 * ya decían— y no como el piso de la pantalla. En una pantalla angosta se
 * recorta sola hasta el ancho disponible.
 */
.ds-tabbar {
  display: none;
  position: fixed;
  left: calc(var(--ds-tabbar-inset) + var(--ds-safe-left));
  right: calc(var(--ds-tabbar-inset) + var(--ds-safe-right));
  /*
   * Se apoya cerca del borde: el hueco es el margen propio más **la mitad** del
   * área segura. El área segura entera son 34px en un iPhone y dejaba la barra
   * flotando lejos del pulgar; la mitad la acerca sin que se meta con la barra
   * de gestos, que es la franja fina del final.
   */
  bottom: calc(var(--ds-tabbar-inset) + var(--ds-safe-bottom) / 2);
  z-index: var(--ds-z-tabbar);
  height: var(--ds-tabbar-height);
  /*
   * Se ajusta a sus pestañas y se centra, en lugar de estirarse de borde a
   * borde. Con todo el ancho, cinco pestañas de 94px repartían 36px de aire
   * dentro de cada una: la pastilla activa salía un rectángulo largo y las
   * etiquetas quedaban lejos unas de otras sin que eso agrupara nada.
   *
   * `fit-content` con `left`/`right` puestos **ya trae el tope**: el ancho
   * disponible es el rectángulo entre los dos, así que en una pantalla angosta
   * la barra se recorta sola hasta el 100% útil y las columnas se reparten lo
   * que haya. Y con los dos lados fijados, `margin-inline: auto` es lo que la
   * centra dentro de ese rectángulo, áreas seguras incluidas.
   */
  width: fit-content;
  margin-inline: auto;
  padding: var(--ds-space-1);
  /* Cápsula: el radio es la mitad del alto, así los extremos son semicírculos
     y la barra se lee como un objeto apoyado encima y no como un bloque. */
  border-radius: var(--ds-radius-full);
  /*
   * `color-mix` da la versión translúcida del token sin tener que declarar un
   * color aparte: la barra sigue el tema como todo lo demás, incluso si mañana
   * cambia el color de superficie.
   */
  background: color-mix(in srgb, var(--ds-surface) var(--ds-glass-tint), transparent);
  /*
   * La saturación es la mitad del efecto, no un adorno. Con el tinte tan bajo, lo
   * que llega de abajo son los colores del contenido: subirlos hace que se
   * transparenten **vivos**, que es lo que separa el vidrio de un velo gris. Sin
   * esto, bajar el tinte solo deja la barra deslavada.
   */
  -webkit-backdrop-filter: blur(var(--ds-glass-blur)) saturate(200%);
  backdrop-filter: blur(var(--ds-glass-blur)) saturate(200%);
  /*
   * Dos cosas en la misma propiedad:
   *
   * - La sombra de un objeto flotando, no la de un panel: una sola, difusa y
   *   suave. Con `--ds-shadow-lg` —en oscuro, un halo negro al 48%— y un
   *   contorno opaco, alrededor del vidrio quedaba dibujado un rectángulo y se
   *   veía el bloque antes que la barra.
   * - El reborde claro del canto, por dentro: una línea arriba y un aro tenue
   *   alrededor. Es lo que le da **borde** a un vidrio que ya casi no tiene
   *   color propio; sin él, a este tinte la barra deja de leerse como un objeto
   *   y pasa a parecer una zona borrosa de la pantalla.
   */
  box-shadow:
    0 6px 24px rgb(16 24 36 / 12%),
    inset 0 1px 0 rgb(255 255 255 / 55%),
    inset 0 0 0 1px rgb(255 255 255 / 22%);
}

/*
 * En oscuro el reborde va mucho más bajo: sobre un fondo casi negro, el mismo
 * blanco del tema claro dibuja un tubo de neón alrededor de la barra.
 */
:root[data-theme="dark"] .ds-tabbar {
  box-shadow:
    0 6px 24px rgb(0 0 0 / 28%),
    inset 0 1px 0 rgb(255 255 255 / 12%),
    inset 0 0 0 1px rgb(255 255 255 / 6%);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ds-tabbar {
    box-shadow:
      0 6px 24px rgb(0 0 0 / 28%),
      inset 0 1px 0 rgb(255 255 255 / 12%),
      inset 0 0 0 1px rgb(255 255 255 / 6%);
  }
}

/*
 * Sin desenfoque, el vidrio es solo una barra semitransparente: el texto de
 * abajo se lee a través de la barra y las etiquetas se vuelven ilegibles. Donde
 * no hay `backdrop-filter` —o donde el sistema pidió menos transparencia— la
 * barra se vuelve opaca, que es feo pero legible.
 */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ds-tabbar { background: var(--ds-surface); }
}

@media (prefers-reduced-transparency: reduce) {
  .ds-tabbar { background: var(--ds-surface); }
}

/*
 * Columnas iguales y **cuadradas**: `1fr` reparte a partir de la mayor de todas,
 * y como la etiqueta mide siempre el lado del cuadrado menos el relleno, esa
 * mayor es el cuadrado. La pestaña sale del ancho de su alto sin que ninguna
 * etiqueta la estire ni la angoste, que es lo que hace redondo al fondo del
 * hover y del activo.
 *
 * El `minmax(0, …)` es lo que la deja encogerse igual: es el mínimo explícito
 * que `1fr` solo tiene cuando el contenido se lo permite. Cuando la barra topa
 * con el ancho de la pantalla —un teléfono de 320px— las columnas reparten lo
 * que haya y las pestañas quedan angostas, que es mejor que salirse del vidrio.
 *
 * Sin separación entre pestañas: las pastillas son redondas y solo se pinta la
 * activa, así que el hueco no separaba nada y sí alejaba las etiquetas.
 */
.ds-tabbar__list {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  align-items: stretch;
  height: 100%;
  gap: 0;
}

.ds-tabbar__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-width: 0;
  padding: 0 var(--ds-space-1);
  border: none;
  /* La pastilla del ítem sigue la forma de la cápsula que lo contiene. */
  border-radius: var(--ds-radius-full);
  background: none;
  color: var(--ds-text-muted);
  font: inherit;
  font-size: var(--ds-tabbar-label);
  font-weight: var(--ds-weight-medium);
  text-decoration: none;
  cursor: pointer;
  position: relative;
  touch-action: manipulation;
  transition: color var(--ds-duration-fast) var(--ds-ease),
              background var(--ds-duration-fast) var(--ds-ease);
}

.ds-tabbar__item:hover { color: var(--ds-text); text-decoration: none; }

.ds-tabbar__item:active { background: var(--ds-surface-hover); }

/*
 * La pestaña activa se marca con color **y** con el fondo suave. Solo con color
 * no alcanza: hay quien no lo distingue, y con la barra sobre contenido de
 * cualquier tono el contraste entre dos grises cambia con lo que haya debajo.
 */
.ds-tabbar__item[aria-current="page"] {
  color: var(--ds-accent);
  background: var(--ds-accent-soft);
}

.ds-tabbar__icon {
  display: grid;
  place-items: center;
  flex-shrink: 0;
}

/*
 * El ancho fijo de la etiqueta es lo que hace cuadrada a la pestaña: mide el lado
 * del cuadrado menos el relleno del ítem, así que la columna del grid nunca crece
 * ni se queda corta por lo que diga el texto. Va acá y no en la pestaña —con un
 * `min-width`— porque `max-width: 100%` la deja encogerse: la columna termina con
 * el cuadrado como máximo y cero como mínimo, y en un teléfono de 320px la barra
 * se angosta en lugar de desbordarse.
 *
 * Centrado a mano: con el ancho puesto, la caja ya no se ajusta al texto y sin
 * `text-align` las etiquetas cortas quedan pegadas a la izquierda de su columna.
 */
.ds-tabbar__label {
  width: calc(var(--ds-tabbar-item) - var(--ds-space-1) * 2);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: center;
  line-height: 1.1;
}

/*
 * La acción rápida no es una sección: no se marca como activa ni se queda
 * seleccionada, así que se distingue con el color de la marca. Es lo único de la
 * barra que **hace** algo en lugar de llevar a algún lado.
 */
.ds-tabbar__item--action {
  color: var(--ds-accent);
}

.ds-tabbar__item--action .ds-tabbar__icon {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: var(--ds-radius-full);
  background: var(--ds-accent);
  color: var(--ds-text-on-accent);
}

/* Contador sobre el icono: no hay ancho para ponerlo al lado. */
.ds-tabbar__badge {
  position: absolute;
  top: 4px;
  left: 50%;
  margin-left: 2px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--ds-radius-full);
  background: var(--ds-danger);
  color: #fff;
  font-size: 10px;
  font-weight: var(--ds-weight-semibold);
  line-height: 16px;
  text-align: center;
}

/* Marca que hay algo en alguna de las secciones que no entraron en la barra. */
.ds-tabbar__item--more .ds-tabbar__badge {
  min-width: 8px;
  height: 8px;
  padding: 0;
  margin-left: 6px;
}

/* ---------- Navegación compacta: sidebar deslizable (tablet) ---------- */

@media (max-width: 1023.98px) {
  .ds-app,
  .ds-app[data-sidebar="collapsed"] {
    grid-template-columns: minmax(0, 1fr);
  }

  .ds-sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: var(--ds-sidebar-width);
    transform: translateX(-100%);
    transition: transform var(--ds-duration-base) var(--ds-ease);
  }

  /* Fuera del escritorio el atributo pasa a significar abierto/cerrado, no ancho. */
  .ds-app[data-sidebar="open"] .ds-sidebar {
    transform: translateX(0);
    box-shadow: var(--ds-shadow-lg);
  }

  .ds-app[data-sidebar="collapsed"] .ds-sidebar__label,
  .ds-app[data-sidebar="collapsed"] .ds-sidebar__brand-text,
  .ds-app[data-sidebar="collapsed"] .ds-sidebar__group-label,
  .ds-app[data-sidebar="collapsed"] .ds-sidebar__badge {
    display: revert;
  }

  .ds-app[data-sidebar="collapsed"] .ds-sidebar__link,
  .ds-app[data-sidebar="collapsed"] .ds-sidebar__brand {
    justify-content: flex-start;
    padding-inline: var(--ds-space-3);
  }

  .ds-app[data-sidebar="open"] .ds-sidebar-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: calc(var(--ds-z-sidebar) - 1);
    background: var(--ds-overlay);
  }

  .ds-content {
    padding: var(--ds-space-4);
    gap: var(--ds-space-4);
  }

  .ds-topbar {
    padding: 0 var(--ds-space-4);
  }
}

/* ---------- Móvil: el sidebar se va, entra la barra de pestañas ---------- */

@media (max-width: 767.98px) {
  /*
   * Se oculta el panel entero, no se lo deja invisible al costado: un `<aside>`
   * fuera de pantalla sigue siendo tabulable y un lector de pantalla lo lee
   * igual, así que quedarían dos navegaciones para la misma cosa.
   */
  .ds-sidebar,
  .ds-sidebar-scrim,
  .ds-app[data-sidebar="open"] .ds-sidebar-scrim {
    display: none;
  }

  /*
   * El ☰ de la topbar ya no abre nada: la navegación es la barra de abajo.
   *
   * El selector lleva al padre a propósito. `.ds-btn` declara `inline-flex` con
   * la misma especificidad que `.ds-topbar__menu`, y `components.css` se carga
   * después de esta hoja: sin el `.ds-topbar` delante, el botón gana por orden
   * y el ☰ se queda en pantalla sin hacer nada.
   */
  .ds-topbar .ds-topbar__menu {
    display: none;
  }

  .ds-tabbar {
    display: block;
  }

  /*
   * Arriba no hay barra: hay un título.
   *
   * En el teléfono, una franja con su propia superficie y su línea inferior
   * ocupa 60px de los 640 útiles para decir algo que la persona **acaba de
   * elegir** tocando una pestaña. Se le saca el fondo propio, el borde y la
   * altura fija: queda el título de la pantalla, con el color de fondo del
   * contenido, y el botón de opciones a su derecha.
   *
   * Y **no queda pegado arriba**: `position: static`. Un título que se queda
   * flotando mientras el contenido pasa por debajo es otra vez una barra, solo
   * que sin la línea; en una pantalla de 640px de alto se lleva la primera
   * franja todo el tiempo para repetir lo que la pestaña activa ya dice. Se
   * desplaza con la página, como el encabezado que es. La navegación no se
   * pierde: la barra de pestañas de abajo sí es fija, y ahí está el ⚙ a un
   * toque de distancia.
   */
  .ds-topbar {
    position: static;
    height: auto;
    min-height: 0;
    padding: calc(var(--ds-safe-top) + var(--ds-space-3)) var(--ds-space-3) var(--ds-space-2);
    padding-left: calc(var(--ds-space-3) + var(--ds-safe-left));
    padding-right: calc(var(--ds-space-3) + var(--ds-safe-right));
    gap: var(--ds-space-2);
    background: var(--ds-bg);
    border-bottom: none;
    align-items: center;
  }

  /* Título de pantalla, no rótulo de barra: es el encabezado de lo que sigue. */
  .ds-topbar__title {
    font-size: var(--ds-text-2xl);
    font-weight: var(--ds-weight-semibold);
    letter-spacing: -0.015em;
    line-height: var(--ds-leading-tight);
  }


  .ds-content {
    padding: var(--ds-space-3);
    /* Sin barra que separe, el contenido empieza pegado al título. */
    padding-top: var(--ds-space-1);
    padding-left: calc(var(--ds-space-3) + var(--ds-safe-left));
    padding-right: calc(var(--ds-space-3) + var(--ds-safe-right));
    /* Espacio para que la barra de pestañas no se coma la última fila. */
    padding-bottom: var(--ds-tabbar-clearance);
    gap: var(--ds-space-4);
  }

  /* El encabezado de página se apila y sus acciones ocupan el ancho: en 360px
     dos botones de contenido variable uno al lado del otro parten el texto. */
  .ds-page-header {
    flex-direction: column;
    align-items: stretch;
    gap: var(--ds-space-3);
  }

  .ds-page-header__actions {
    flex-wrap: nowrap;
  }

  .ds-page-header__actions > .ds-btn {
    flex: 1 1 0;
    min-width: 0;
  }
}
