/* ============================================================
   STOREFRONT V2 — COMPONENTES REUSABLES (Sprint UX-1, §4.3)
   ------------------------------------------------------------
   Componentes nuevos / complementarios al set ya cargado por
   tailwind.css (sf-btn / sf-btn-primary / sf-card / sf-skip-link
   ya viven ahi via @apply).

   Este archivo NO redefine clases existentes — solo agrega
   variantes V2 que dependen exclusivamente de los design tokens
   de `_design_tokens.css` (CSS vars `--sf-*`).  Es seguro
   incluirlo despues de tailwind.css sin colisiones.

   Convenciones:
   - Nuevas variantes V2: sufijo descriptivo (sf-btn-danger,
     sf-badge-discount, sf-card-elevated).
   - Componentes nuevos: prefijo sf-v2- evitable solo si NO
     colisiona (sf-skeleton, sf-toast-v2, sf-modal, ...).
   - Si una clase ya esta definida en tailwind.src.css, NO se
     redefine aqui para evitar especificidad rara.
   ============================================================ */

/* ============================================================
   BUTTONS — variantes V2
   ============================================================ */
.sf-btn-danger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sf-space-2);
  padding: var(--sf-space-2) var(--sf-space-4);
  border-radius: var(--sf-radius-md);
  font-weight: 600;
  font-size: var(--sf-font-body-sm);
  background: var(--sf-color-danger);
  color: #ffffff;
  border: 1px solid transparent;
  cursor: pointer;
  transition: filter var(--sf-motion-fast) var(--sf-ease-out),
              transform var(--sf-motion-fast) var(--sf-ease-out),
              box-shadow var(--sf-motion-fast) var(--sf-ease-out);
}
.sf-btn-danger:hover {
  filter: brightness(1.08);
  transform: translateY(-1px);
  box-shadow: var(--sf-shadow-md);
}
.sf-btn-danger:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

.sf-btn-success {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sf-space-2);
  padding: var(--sf-space-2) var(--sf-space-4);
  border-radius: var(--sf-radius-md);
  font-weight: 600;
  font-size: var(--sf-font-body-sm);
  background: var(--sf-color-success);
  color: #ffffff;
  border: 1px solid transparent;
  cursor: pointer;
  transition: filter var(--sf-motion-fast) var(--sf-ease-out),
              transform var(--sf-motion-fast) var(--sf-ease-out),
              box-shadow var(--sf-motion-fast) var(--sf-ease-out);
}
.sf-btn-success:hover {
  filter: brightness(1.08);
  transform: translateY(-1px);
  box-shadow: var(--sf-shadow-md);
}

/* Tamano extra-small / extra-large (complementan sf-btn-lg ya existente) */
.sf-btn-xs {
  padding: var(--sf-space-1) var(--sf-space-2);
  font-size: var(--sf-font-caption);
}
.sf-btn-sm {
  padding: var(--sf-space-1) var(--sf-space-3);
  font-size: var(--sf-font-body-sm);
}
.sf-btn-xl {
  padding: var(--sf-space-4) var(--sf-space-8);
  font-size: var(--sf-font-body-lg);
}

/* ============================================================
   CARDS — variantes V2
   ============================================================ */
.sf-card-elevated {
  background: var(--sf-color-bg-elevated);
  border: 1px solid var(--sf-color-border);
  border-radius: var(--sf-radius-lg);
  padding: var(--sf-space-4);
  box-shadow: var(--sf-shadow-sm);
  transition: box-shadow var(--sf-motion-standard) var(--sf-ease-out),
              transform var(--sf-motion-standard) var(--sf-ease-out);
}
.sf-card-elevated:hover {
  box-shadow: var(--sf-shadow-lg);
  transform: translateY(-2px);
}

.sf-card-flat {
  background: var(--sf-color-bg);
  border: 1px solid var(--sf-color-border);
  border-radius: var(--sf-radius-md);
  padding: var(--sf-space-4);
}

/* ============================================================
   BADGES — variantes semanticas V2
   (sf-badge ya NO esta en tailwind.src.css, definicion completa)
   ============================================================ */
.sf-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--sf-space-1);
  padding: 0 var(--sf-space-2);
  height: 1.5rem;
  border-radius: var(--sf-radius-full);
  font-size: var(--sf-font-caption);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--sf-tracking-wide);
  white-space: nowrap;
}
.sf-badge-success {
  background: rgb(22 163 74 / 0.1);
  color: var(--sf-color-success);
}
.sf-badge-warning {
  background: rgb(245 158 11 / 0.1);
  color: var(--sf-color-warning);
}
.sf-badge-danger {
  background: rgb(225 29 72 / 0.1);
  color: var(--sf-color-danger);
}
.sf-badge-info {
  background: rgb(37 99 235 / 0.1);
  color: var(--sf-color-info);
}
.sf-badge-neutral {
  background: var(--sf-color-bg-subtle);
  color: var(--sf-color-text-muted);
}
.sf-badge-discount {
  background: var(--sf-color-danger);
  color: #ffffff;
  animation: sf-pulse 2s ease-in-out infinite;
}
.sf-badge-new {
  background: var(--sf-color-primary);
  color: var(--sf-color-primary-fg);
}

/* ============================================================
   FORM INPUTS — extension V2
   (sf-input ya existe via tailwind.src.css con @apply, no se
   redefine; solo se agregan utilidades complementarias)
   ============================================================ */
.sf-input-error {
  border-color: var(--sf-color-danger) !important;
  box-shadow: 0 0 0 3px rgb(225 29 72 / 0.15);
}
.sf-input-error:focus {
  outline-color: var(--sf-color-danger);
}
.sf-form-helper {
  display: block;
  margin-top: var(--sf-space-1);
  font-size: var(--sf-font-caption);
  color: var(--sf-color-text-muted);
}
.sf-form-error {
  display: block;
  margin-top: var(--sf-space-1);
  font-size: var(--sf-font-caption);
  color: var(--sf-color-danger);
  font-weight: 600;
}

/* ============================================================
   SKELETONS — loaders durante HTMX swap / SSR pendiente
   ============================================================ */
.sf-skeleton {
  background: linear-gradient(
    90deg,
    var(--sf-color-bg-elevated) 25%,
    var(--sf-color-border) 50%,
    var(--sf-color-bg-elevated) 75%
  );
  background-size: 200% 100%;
  border-radius: var(--sf-radius-md);
  animation: sf-skeleton-loading 1.5s ease-in-out infinite;
}
.sf-skeleton-text {
  height: 1rem;
  margin-bottom: var(--sf-space-2);
}
.sf-skeleton-title {
  height: 1.5rem;
  width: 60%;
  margin-bottom: var(--sf-space-3);
}
.sf-skeleton-img {
  width: 100%;
  aspect-ratio: 1 / 1;
}
.sf-skeleton-circle {
  border-radius: var(--sf-radius-full);
}

/* ============================================================
   TYPOGRAPHY — V2 (complementan sf-display / sf-h1 / sf-h2 /
   sf-eyebrow ya en tailwind.src.css)
   ============================================================ */
.sf-h3 {
  font-size: var(--sf-font-h3);
  font-weight: 700;
  line-height: var(--sf-leading-snug);
  letter-spacing: var(--sf-tracking-tight);
  color: var(--sf-color-text);
}
.sf-h4 {
  font-size: var(--sf-font-h4);
  font-weight: 600;
  line-height: var(--sf-leading-snug);
  color: var(--sf-color-text);
}
.sf-h5 {
  font-size: var(--sf-font-h5);
  font-weight: 600;
  line-height: var(--sf-leading-snug);
  color: var(--sf-color-text);
}
.sf-h6 {
  font-size: var(--sf-font-h6);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--sf-tracking-wide);
  color: var(--sf-color-text);
}
.sf-body-lg {
  font-size: var(--sf-font-body-lg);
  line-height: var(--sf-leading-relaxed);
  color: var(--sf-color-text);
}
.sf-body {
  font-size: var(--sf-font-body);
  line-height: var(--sf-leading-normal);
  color: var(--sf-color-text);
}
.sf-body-sm {
  font-size: var(--sf-font-body-sm);
  line-height: var(--sf-leading-normal);
  color: var(--sf-color-text);
}
.sf-caption {
  font-size: var(--sf-font-caption);
  line-height: var(--sf-leading-normal);
  color: var(--sf-color-text-muted);
}
.sf-overline {
  font-size: var(--sf-font-overline);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--sf-tracking-wider);
  color: var(--sf-color-text-muted);
}

/* ============================================================
   ANIMATIONS
   ============================================================ */
@keyframes sf-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.6; }
}

@keyframes sf-skeleton-loading {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

@keyframes sf-slide-in-right {
  from { transform: translateX(100%); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}

@keyframes sf-slide-in-left {
  from { transform: translateX(-100%); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}

@keyframes sf-slide-up {
  from { transform: translateY(20px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

@keyframes sf-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes sf-scale-in {
  from { opacity: 0; transform: scale(0.95); }
  to { opacity: 1; transform: scale(1); }
}

/* Utility classes para aplicar las animaciones */
.sf-anim-slide-in-right {
  animation: sf-slide-in-right var(--sf-motion-standard) var(--sf-ease-out);
}
.sf-anim-slide-in-left {
  animation: sf-slide-in-left var(--sf-motion-standard) var(--sf-ease-out);
}
.sf-anim-slide-up {
  animation: sf-slide-up var(--sf-motion-standard) var(--sf-ease-out);
}
.sf-anim-fade-in {
  animation: sf-fade-in var(--sf-motion-standard) var(--sf-ease-out);
}
.sf-anim-scale-in {
  animation: sf-scale-in var(--sf-motion-fast) var(--sf-ease-bounce);
}

/* ============================================================
   SURFACES — utilidades cross-cutting
   ============================================================ */
.sf-surface {
  background: var(--sf-color-bg);
  color: var(--sf-color-text);
}
.sf-surface-elevated {
  background: var(--sf-color-bg-elevated);
  color: var(--sf-color-text);
}
.sf-surface-subtle {
  background: var(--sf-color-bg-subtle);
  color: var(--sf-color-text);
}
.sf-divider {
  height: 1px;
  background: var(--sf-color-border);
  border: 0;
  margin: var(--sf-space-4) 0;
}

/* Stat card / stat mini / .sf-kpi-grid — MOVIDOS a `_kpi_strip.css`
   (IT76): sus consumidores viven en el app-shell, que no carga este
   archivo. Fuente unica alla; los dos shells lo enlazan. */

/* ============================================================
 * Tabla kernel (IT48, S29) — skin canonico de TODA tabla del
 * app-shell. `.sf-table` es el nombre canonico; `.workspace-table`
 * (legacy) queda unificado a este mismo skin. La densidad vive
 * ACA (los px-* / py-* utilitarios por celda quedan anulados por
 * especificidad) — prohibido re-declarar paddings por vertical.
 * ============================================================ */
.sf-table,
.workspace-table {
    width: 100%;
    font-size: 13px;
    border-collapse: collapse;
}
.sf-table thead th,
.workspace-table thead th {
    padding: .5rem .75rem;
    font-size: .62rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .1em;
    color: var(--sf-color-text-muted, #64748b);
    background: var(--sf-color-bg-subtle, #f8fafc);
    border-bottom: 1px solid var(--sf-color-border, #e2e8f0);
}
.sf-table tbody td,
.workspace-table tbody td {
    padding: .5rem .75rem;
    border-bottom: 1px solid var(--sf-color-border-soft);
}
.sf-table tbody tr,
.workspace-table tbody tr {
    transition: background-color .15s ease;
}
.sf-table tbody tr:hover,
.workspace-table tbody tr:hover {
    background: var(--sf-color-bg-subtle, rgba(248, 250, 252, .86));
}
.dark .sf-table thead th,
html[data-theme="dark"] .sf-table thead th,
.dark .workspace-table thead th,
html[data-theme="dark"] .workspace-table thead th {
    background: rgba(15, 23, 42, .55);
}
.dark .sf-table tbody tr:hover,
html[data-theme="dark"] .sf-table tbody tr:hover,
.dark .workspace-table tbody tr:hover,
html[data-theme="dark"] .workspace-table tbody tr:hover {
    background: rgba(51, 65, 85, .3);
}

/* ============================================================
 * Rank list (IT55, S29) — listas "Top N" de dashboards (top
 * productos, top clientes, top campanas...). Anatomia kernel:
 * medallon de posicion (podio 1/2/3 con metalicos), label+meta,
 * valor tabular y barra de proporcion RELATIVA AL LIDER (width
 * via data-style-width -> style-bind.js, CSP-safe, re-scan en
 * swaps htmx). No re-declarar rankings ad-hoc por vertical.
 * ============================================================ */
.sf-ranklist { list-style: none; margin: 0; padding: 0; }
.sf-ranklist__item {
    display: grid;
    grid-template-columns: auto 1fr auto;
    column-gap: .65rem;
    align-items: center;
    padding: .5rem 0;
    border-bottom: 1px solid var(--sf-color-border-soft);
}
.sf-ranklist__item:last-child { border-bottom: 0; }
.sf-ranklist__rank {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.6rem;
    height: 1.6rem;
    border-radius: 999px;
    font-size: .7rem;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    background: var(--sf-color-bg-subtle, #f1f5f9);
    color: var(--sf-color-text-muted, #64748b);
    border: 1px solid var(--sf-color-border, #e2e8f0);
}
.sf-ranklist__rank--1 {
    background: linear-gradient(135deg, #fbbf24, #d97706);
    color: #fff; border: 0;
    box-shadow: 0 2px 6px -1px rgba(217, 119, 6, .5);
}
.sf-ranklist__rank--2 {
    background: linear-gradient(135deg, #cbd5e1, #94a3b8);
    color: #fff; border: 0;
}
.sf-ranklist__rank--3 {
    background: linear-gradient(135deg, #fdba74, #ea580c);
    color: #fff; border: 0;
}
.sf-ranklist__body { min-width: 0; }
.sf-ranklist__label {
    font-size: .82rem;
    font-weight: 600;
    color: var(--sf-color-text, #0f172a);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sf-ranklist__meta {
    font-size: .68rem;
    color: var(--sf-color-text-muted, #64748b);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sf-ranklist__value {
    font-size: .82rem;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.sf-ranklist__track {
    grid-column: 2 / -1;
    height: 4px;
    margin-top: .3rem;
    border-radius: 999px;
    background: var(--sf-color-bg-subtle, rgba(148, 163, 184, .18));
    overflow: hidden;
}
.sf-ranklist__bar {
    display: block;
    height: 100%;
    width: 0;
    border-radius: 999px;
    background: var(--sf-rank-bar, linear-gradient(90deg, #3b82f6, #6366f1));
    transition: width .5s ease;
}
.sf-ranklist__bar--emerald { --sf-rank-bar: linear-gradient(90deg, #34d399, #059669); }
.sf-ranklist__bar--blue    { --sf-rank-bar: linear-gradient(90deg, #60a5fa, #2563eb); }
.sf-ranklist__bar--amber   { --sf-rank-bar: linear-gradient(90deg, #fbbf24, #d97706); }
.sf-ranklist__bar--violet  { --sf-rank-bar: linear-gradient(90deg, #a78bfa, #7c3aed); }

/* ── htmx indicators (IT122, QA ISSUE-009) ────────────────────────────
 * htmx inyecta estas reglas via <style> SIN nonce y la CSP las bloquea.
 * Se desactiva la inyección (meta htmx-config en base_storefront) y las
 * reglas viven acá — mismo patrón que app_shell + styles.css. */
.htmx-indicator { opacity: 0; transition: opacity 200ms ease-in; }
.htmx-request .htmx-indicator { opacity: 1; }
.htmx-request.htmx-indicator { opacity: 1; }

/* ── Costos de envío por zona (carrito / checkout) ──────────────────────
   Bloque plegable con las tarifas REALES del tenant. `<details>` nativo:
   sin JS y CSP-safe. Colores por tokens --sf-*: dark pareado gratis. */
.sf-envio-zonas {
    margin-top: .75rem;
    border: 1px solid var(--sf-color-border, #e2e8f0);
    border-radius: var(--sf-radius-lg, .75rem);
    background: var(--sf-color-bg-subtle, #f8fafc);
    overflow: hidden;
}
.sf-envio-zonas__sum {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .6rem .75rem;
    cursor: pointer;
    list-style: none;
    font-size: .8rem;
    font-weight: 700;
    color: var(--sf-color-text, #0f172a);
}
.sf-envio-zonas__sum::-webkit-details-marker { display: none; }
.sf-envio-zonas__chev {
    margin-left: auto;
    font-size: .7rem;
    color: var(--sf-color-text-muted, #64748b);
    transition: transform .15s ease;
}
.sf-envio-zonas[open] .sf-envio-zonas__chev { transform: rotate(180deg); }
.sf-envio-zonas__body { padding: 0 .75rem .75rem; }
.sf-envio-zonas__body .sf-table { font-size: .78rem; }
.sf-envio-zonas__ciudades {
    font-size: .7rem;
    font-weight: 400;
    color: var(--sf-color-text-muted, #64748b);
    margin-top: .1rem;
}
.sf-envio-zonas__na {
    font-size: .72rem;
    color: var(--sf-color-text-muted, #64748b);
}
.sf-envio-zonas__nota {
    margin-top: .5rem;
    font-size: .72rem;
    line-height: 1.5;
    color: var(--sf-color-text-muted, #64748b);
}

/* `.sf-note` / `.sf-state-disc` viven en `_notes.css` (IT265) — el
 * app-shell tambien los necesita y este archivo solo lo carga el
 * storefront. Los dos shells linkean ese archivo; aca no se re-declaran.
 */

/* ============================================================
 * RAIL DE PROGRESO — `.sf-rail` (IT263, S25/S29)
 * ------------------------------------------------------------
 * Horizontal en desktop, TIMELINE VERTICAL en mobile (<640px):
 * cinco labels en fila en un celular quedan en 2 letras por
 * columna. Mismo markup, sin duplicar partial.
 * ============================================================ */
.sf-rail {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    margin: var(--sf-space-6) 0 var(--sf-space-2);
    padding: 0;
    list-style: none;
}
.sf-rail__step { position: relative; min-width: 0; text-align: center; }
.sf-rail__step::before {
    content: "";
    position: absolute;
    top: 19px;
    right: 50%;
    width: 100%;
    height: 2px;
    background: var(--sf-color-border);
    z-index: 0;
}
.sf-rail__step:first-child::before { display: none; }
.sf-rail__step--done::before,
.sf-rail__step--current::before { background: var(--sf-color-primary); }

.sf-rail__dot {
    position: relative;
    z-index: 1;
    width: 40px;
    height: 40px;
    margin: 0 auto;
    display: grid;
    place-items: center;
    font-size: .9rem;
    border-radius: var(--sf-radius-full);
    background: var(--sf-color-bg-elevated);
    border: 2px solid var(--sf-color-border);
    color: var(--sf-color-text-subtle);
}
.sf-rail__step--done .sf-rail__dot,
.sf-rail__step--current .sf-rail__dot {
    background: var(--sf-color-primary);
    border-color: var(--sf-color-primary);
    color: var(--sf-color-primary-fg);
}
.sf-rail__step--current .sf-rail__dot {
    box-shadow: 0 0 0 5px color-mix(in srgb, var(--sf-color-primary) 22%, transparent);
}
.sf-rail__label {
    margin-top: var(--sf-space-2);
    padding: 0 .25rem;
    font-size: var(--sf-font-caption);
    line-height: var(--sf-leading-snug);
    color: var(--sf-color-text-muted);
}
.sf-rail__step--done .sf-rail__label,
.sf-rail__step--current .sf-rail__label {
    color: var(--sf-color-text);
    font-weight: 600;
}
.sf-rail__now {
    display: inline-block;
    margin-top: .3rem;
    font-size: var(--sf-font-overline);
    font-weight: 800;
    letter-spacing: var(--sf-tracking-wider);
    text-transform: uppercase;
    color: color-mix(in srgb, var(--sf-color-primary) 50%, var(--sf-color-text));
}

@media (max-width: 640px) {
    .sf-rail { grid-auto-flow: row; grid-auto-columns: auto; }
    .sf-rail__step {
        display: grid;
        grid-template-columns: 40px 1fr;
        align-items: center;
        gap: var(--sf-space-3);
        padding: var(--sf-space-1) 0;
        text-align: left;
    }
    .sf-rail__step::before {
        top: auto;
        bottom: 50%;
        right: auto;
        left: 19px;
        width: 2px;
        height: 100%;
    }
    .sf-rail__body {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        gap: .5rem;
    }
    .sf-rail__label { margin-top: 0; padding: 0; font-size: var(--sf-font-body-sm); }
    .sf-rail__now { margin: 0; }
}

/* ============================================================
 * CODIGO DESTACADO — `.sf-code` (IT263)
 * Un dato que el comprador tiene que LEER EN VOZ ALTA o mostrar
 * en el mostrador (codigo de retiro BOPIS, referencia de pago).
 * ============================================================ */
.sf-code {
    text-align: center;
    padding: var(--sf-space-4);
    border-radius: var(--sf-radius-lg);
    background: color-mix(in srgb, var(--sf-color-primary) 9%, var(--sf-color-bg-elevated));
    border: 1px dashed color-mix(in srgb, var(--sf-color-primary) 45%, var(--sf-color-border));
}
.sf-code__value {
    font-family: var(--sf-font-mono);
    font-size: clamp(1.65rem, 6vw, 2.1rem);
    font-weight: 800;
    letter-spacing: .3em;
    /* El tracking del ULTIMO caracter empuja el bloque a la izquierda;
       el indent lo devuelve al centro optico. */
    text-indent: .3em;
    line-height: 1.2;
    color: color-mix(in srgb, var(--sf-color-primary) 55%, var(--sf-color-text));
}

/* ============================================================
 * LISTA DE DATOS — `.sf-datalist` (IT263)
 * Pares etiqueta/valor (datos bancarios, direccion de entrega).
 * Colapsa a una columna en mobile.
 * ============================================================ */
.sf-datalist { margin: 0; display: grid; gap: .45rem; }
.sf-datalist__row {
    display: grid;
    grid-template-columns: 6.5rem 1fr;
    gap: var(--sf-space-2);
    align-items: baseline;
}
.sf-datalist__key {
    font-size: var(--sf-font-caption);
    text-transform: uppercase;
    letter-spacing: var(--sf-tracking-wide);
    color: var(--sf-color-text-muted);
}
.sf-datalist__val {
    margin: 0;
    font-size: var(--sf-font-body-sm);
    font-weight: 600;
    color: var(--sf-color-text);
    overflow-wrap: anywhere;
}
@media (max-width: 480px) {
    .sf-datalist__row { grid-template-columns: 1fr; gap: 0; }
}

/* ============================================================
 * PANEL DE CONTENIDO — `.sf-panel` (IT263, S29.1)
 * ------------------------------------------------------------
 * Superficie canonica de una seccion del storefront publico.
 * La DENSIDAD (padding, radio, sombra, separacion entre paneles)
 * vive aca y en ningun otro lado: `_components.css` se carga
 * DESPUES del bundle Tailwind, asi que un `p-5` por template no
 * la puede pisar — y esa es la idea (S29.1, densidad kernel-owned).
 *
 * Se diferencia de `.sf-card-elevated`, que es la tarjeta de un
 * ITEM clickeable (levanta en hover): un panel de contenido no
 * se mueve cuando le pasas el mouse por encima.
 * ============================================================ */
.sf-panel {
    background: var(--sf-color-bg);
    border: 1px solid var(--sf-color-border);
    border-radius: var(--sf-radius-xl);
    padding: var(--sf-space-5);
    box-shadow: var(--sf-shadow-xs);
}
@media (min-width: 640px) {
    .sf-panel { padding: var(--sf-space-6); }
}
.sf-panel + .sf-panel { margin-top: var(--sf-space-4); }
.sf-panel__title {
    display: flex;
    align-items: center;
    gap: var(--sf-space-2);
    margin: 0 0 var(--sf-space-3);
    font-size: var(--sf-font-caption);
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: var(--sf-tracking-wider);
    color: var(--sf-color-text-muted);
}

/* Cierre de una tabla kernel: la fila del TOTAL. Sin esto cada pagina se
   inventaba un `<tr>` con `font-bold` y un borde distinto. */
.sf-table tfoot td {
    padding: .65rem .75rem;
    font-size: var(--sf-font-body-sm);
    font-weight: 800;
    color: var(--sf-color-text);
    border-top: 2px solid var(--sf-color-border);
}

/* Columna lateral que acompana al scroll (resumen de compra del checkout y
   de la pagina de seguimiento). Solo desde lg: en mobile el aside va debajo
   y pegarlo taparia el contenido. Tailwind no tiene `lg:top-24` compilado en
   el bundle, asi que el offset vive aca y no como utilidad fantasma. */
@media (min-width: 1024px) {
    .sf-sticky-aside {
        position: sticky;
        top: 5.5rem;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
 * PORTADA SIN FOTOS — miniatura, ficha compacta y tira de categoria (IT265)
 * ═══════════════════════════════════════════════════════════════════════
 * La portada estaba diseñada alrededor de fotos que no existen: cada
 * tarjeta de producto reservaba ~370px de alto a una imagen que nunca
 * llega, y cada categoria era un degradado de 570px con la inicial
 * encima. Una tienda recien publicada — que es TODA tienda el primer dia
 * — se veia como una pagina rota.
 *
 * La miniatura tipografica ocupa el lugar de la foto y se ve terminada
 * sin ella; cuando la foto exista, entra en el MISMO cuadrado y no hay
 * que rediseñar nada.
 *
 * El tono sale de `--sf-viz-1..8` (paleta categorica del kernel, validada
 * para daltonismo, con set propio en dark) via el filtro `sf_tone_slot`,
 * que mapea el nombre a un slot estable. Como CLASE y no como `style=`:
 * la CSP de produccion bloquea el atributo.
 * ═════════════════════════════════════════════════════════════════════ */

.sf-tile {
    display: grid;
    place-items: center;
    flex: none;
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.02em;
    border-radius: var(--sf-radius-lg);
    border: 1px solid transparent;
    overflow: hidden;
    background: var(--sf-color-bg-subtle);
    color: var(--sf-color-text-muted);
}
.sf-tile img { width: 100%; height: 100%; object-fit: cover; }
/* El tinte se mezcla contra la SUPERFICIE, no un hex fijo: asi sigue al
   tema del visitante y tambien a la paleta de fondo que eligio el dueño. */
.sf-tile--1 { --sf-tile-c: var(--sf-viz-1); }
.sf-tile--2 { --sf-tile-c: var(--sf-viz-2); }
.sf-tile--3 { --sf-tile-c: var(--sf-viz-3); }
.sf-tile--4 { --sf-tile-c: var(--sf-viz-4); }
.sf-tile--5 { --sf-tile-c: var(--sf-viz-5); }
.sf-tile--6 { --sf-tile-c: var(--sf-viz-6); }
.sf-tile--7 { --sf-tile-c: var(--sf-viz-7); }
.sf-tile--8 { --sf-tile-c: var(--sf-viz-8); }
.sf-tile[class*="sf-tile--"] {
    background: color-mix(in srgb, var(--sf-tile-c) 13%, var(--sf-color-bg-elevated));
    border-color: color-mix(in srgb, var(--sf-tile-c) 30%, transparent);
    /* 55% y no mas: mezclado contra la TINTA, no el acento puro. Medido
       sobre los 8 slots en los dos temas, el peor caso es el ambar
       (`--sf-viz-4`), que al 62% daba 4.00:1 en tema claro — por debajo
       de AA. Al 55% el peor sube a 4.72:1 (claro) y 5.35:1 (oscuro).
       La letra pierde un poco de saturacion; el FONDO de la miniatura
       sigue llevando el color de la categoria, que es donde se lee.
       Guard: test_portada_sin_fotos.py::MiniaturaContrasteTest */
    color: color-mix(in srgb, var(--sf-tile-c) 55%, var(--sf-color-text));
}
.sf-tile--sm { width: 3.25rem; height: 3.25rem; font-size: 1.15rem; }
.sf-tile--md { width: 2.75rem; height: 2.75rem; font-size: 1rem; }

/* ── Ficha compacta de producto ─────────────────────────────────────── */
.sf-pcard-compact {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.6rem;
    border: 1px solid var(--sf-color-border);
    border-radius: var(--sf-radius-xl);
    background: var(--sf-color-bg-elevated);
    text-decoration: none;
    color: inherit;
    transition: border-color .15s ease, transform .15s ease;
}
.sf-pcard-compact:hover { border-color: var(--sf-color-primary); transform: translateY(-1px); }
.sf-pcard-compact:focus-visible { outline: 2px solid var(--sf-color-primary); outline-offset: 2px; }
/* Sin stock: la ficha se atenua entera en vez de gritar en rojo. Doce
   tarjetas con un badge rojo cada una convierten el aviso en ruido. */
.sf-pcard-compact.is-out { opacity: .62; }
/* Los hijos son <span> porque viven dentro de un <a>, y un <a> no puede
   contener <p> ni <div> en HTML valido. Un <span> es INLINE: sin declarar
   `display` el rubro y el nombre salen en el mismo renglon, y solo "se ven"
   apilados cuando el nombre es largo y wrappea de casualidad — que es
   exactamente como se veia la mitad de la grilla. */
.sf-pcard-compact__body { display: block; min-width: 0; flex: 1 1 auto; }
.sf-pcard-compact__cat {
    display: block;
    margin: 0;
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--sf-color-text-muted);
}
.sf-pcard-compact__name {
    display: block;
    margin: 0.1rem 0 0;
    font-size: 0.9rem;
    font-weight: 650;
    letter-spacing: -0.01em;
    color: var(--sf-color-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sf-pcard-compact__foot {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    margin-top: 0.2rem;
    flex-wrap: wrap;
}
.sf-pcard-compact__price {
    font-weight: 700;
    font-size: 0.92rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    color: var(--sf-color-text);
}
.sf-pcard-compact__was {
    font-size: 0.72rem;
    text-decoration: line-through;
    color: var(--sf-color-text-muted);
    white-space: nowrap;
}
.sf-pcard-compact__go {
    flex: none;
    width: 1.9rem; height: 1.9rem;
    display: grid; place-items: center;
    border-radius: var(--sf-radius-md, 8px);
    border: 1px solid var(--sf-color-border);
    color: var(--sf-color-text-muted);
    transition: color .15s ease, border-color .15s ease, transform .15s ease;
}
.sf-pcard-compact:hover .sf-pcard-compact__go {
    color: var(--sf-color-primary);
    border-color: var(--sf-color-primary);
    transform: translateX(2px);
}
.sf-grid-compact {
    display: grid;
    gap: 0.65rem;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
}

/* ── Tira de categoria con muestras ─────────────────────────────────── */
.sf-catrow {
    display: block;
    padding: 0.9rem 1rem;
    border: 1px solid var(--sf-color-border);
    border-radius: var(--sf-radius-xl);
    background: var(--sf-color-bg-elevated);
    text-decoration: none;
    color: inherit;
    transition: border-color .15s ease, transform .15s ease;
}
.sf-catrow:hover { border-color: var(--sf-color-primary); transform: translateY(-1px); }
.sf-catrow:focus-visible { outline: 2px solid var(--sf-color-primary); outline-offset: 2px; }
.sf-catrow__head { display: flex; align-items: center; gap: 0.75rem; }
.sf-catrow__head > span:nth-child(2) { min-width: 0; }
.sf-catrow__name {
    display: block;
    margin: 0;
    font-size: 1rem;
    font-weight: 650;
    letter-spacing: -0.012em;
    color: var(--sf-color-text);
}
.sf-catrow__k { display: block; margin: 0; font-size: 0.76rem; color: var(--sf-color-text-muted); }
.sf-catrow__go { margin-left: auto; flex: none; color: var(--sf-color-text-muted); transition: transform .15s ease; }
.sf-catrow:hover .sf-catrow__go { transform: translateX(3px); color: var(--sf-color-primary); }
.sf-catrow__chips { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.7rem; }
.sf-catrow__chip {
    max-width: 100%;
    padding: 0.18rem 0.5rem;
    border-radius: 999px;
    border: 1px solid var(--sf-color-border);
    background: var(--sf-color-bg-subtle);
    font-size: 0.7rem;
    color: var(--sf-color-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.sf-grid-catrow {
    display: grid;
    gap: 0.75rem;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr));
}
