/* Gap Hunter Labs -- estilos de /catalog/. Extraido del <style> inline el
   2026-09-27 (cacheable). Colores desde /css/theme.css. */

  /* Deliberately single-theme (always dark) -- a brand-consistency
     choice made explicitly by the team, not an omission: every
     visitor sees the same look regardless of their own system
     setting. Brand palette pulled directly from
     pluginIcon.svg/pluginIcon_dark.svg (the same mark shared by all
     34 plugins) -- never invented: #0151FC (primary blue) lightened
     to var(--accent) for legibility on a dark ground (mirrors how the
     icon's own dark variant uses the lighter cyan-blue on a
     transparent/dark background, not the darker blue meant for
     white), #02F172 (accent green, used directly for growth-up).
     2026-08-15: added a "Hunting Field" categorical palette (8 hues)
     for the new Field mode -- 3 of the 8 reuse literal brand colors
     (--accent for API, --good for DevOps, --cyan for Codegen), the
     other 5 are new but chosen to stay visually distinct from those
     3 and from each other at a glance (color alone is never the only
     signal -- every category also has a text label). */
  :root {
    /* Lightened from #6C7793 (2026-08-21): that value contrasted
       against --bg at only 4.35:1, below WCAG AA's 4.5:1 minimum for
       normal text. #727EA0 (chosen then) cleared 4.82:1 against --bg --
       but a follow-up audit (2026-08-23) found several real uses of
       this token sit on --surface/--surface-2 instead (.card-niche,
       .card-pricing, .sc-niche, .log-entry li), where #727EA0 only
       measured 4.43:1 / 3.93:1 -- both still below AA. #8894B8 clears
       AA against all three backgrounds this token actually renders on
       (5.94:1 / 5.27:1 / 6.45:1 vs surface/surface-2/bg), confirmed via
       WCAG's own relative-luminance formula, same hue kept again. */
    --page-max: 1280px;
    --page-pad: 24px;
    --page-pad-sm: 16px;
    --control-h: 44px;
    --section-gap: 20px;
    --duration-fast: 120ms;
    --duration-base: 150ms;
    --duration-slow: 300ms;
    --ease-standard: cubic-bezier(.65,0,.35,1);
    --ease-emphasized: cubic-bezier(.2,.7,.3,1);
  }

  * { box-sizing: border-box; }

  /* Scrollbar tuned to the page's own palette instead of the browser
     default (light track) -- track matches --bg exactly so it blends
     into the page, thumb uses --border (the same hairline tone used
     everywhere else) so it's still findable, not literally invisible. */
  html { scrollbar-color: var(--border) var(--bg); scrollbar-width: thin; }
  ::-webkit-scrollbar { width: 11px; height: 11px; }
  ::-webkit-scrollbar-track { background: var(--bg); }
  ::-webkit-scrollbar-thumb { background: var(--border); border-radius: var(--radius-md); border: 2px solid var(--bg); }
  ::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }

  /* Background texture, 2026-08-17: a faint dot-grid (same technique
     .hero-dotgrid used before it was removed 2026-08-28 -- user report,
     just scaled up and dialed way down here) plus a soft radial wash
     of the brand blue near the top, as if the
     header were the page's own light source. Pure CSS, no image asset
     -- consistent with the rest of the site (every mark on this page is
     hand-coded SVG/CSS, nothing rasterized). Panels/cards all paint
     their own opaque --surface, so this only ever shows in the empty
     space between them -- margins, the hero, page gutters -- never
     competing with text contrast. */
  body {
    margin: 0;
    background-color: var(--bg);
    background-repeat: no-repeat;
    color: var(--text);
    font-family: var(--sans);
    font-size: 15px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
  }

  .wrap {
    max-width: var(--page-max);
    margin: 0 auto;
    padding: 16px var(--page-pad) 32px;
  }

  /* ==================== TOP BAR ====================
     A distinct, always-visible strip above the brand row -- separate
     surface tone (--bg, not --surface) with a live-scan hairline under
     it, so it reads as chrome/wayfinding rather than another content
     block. Sticky, not fixed: it should get out of the way of nothing
     on a one-page site, but stay reachable while scrolling the (long)
     Table view or the footer. */
  #top, #fieldView, #tableView, #contentStage, #fieldMain, tr.row { scroll-margin-top: 72px; }

  .topbar {
    position: sticky;
    top: 0;
    z-index: 40;
    background: color-mix(in srgb, var(--bg) 94%, transparent);
    border-bottom: 1px solid var(--border);
  }



  

  @media (max-width: 840px) {
    
}

  

  /* ---- Field/Table view toggle -- lives in the controls row, right
     before the category filter, so it reads as "one more control that
     shapes what the Hunting Field shows" rather than a hero element.
     Distinguished from the two plain <select> boxes beside it by a
     sliding highlight "thumb" instead of a static border -- the one
     bit of motion the user asked for, nothing louder. The thumb's
     position is driven declaratively by :has(), reading whichever
     button switchMode() already marked .active -- no extra JS. */
  /* 2026-10-19: two equal grid columns as wide as the longest label.
     With flex: 1 1 0 the halves were equal but the toggle was sized to
     the sum of both labels, so "Cuadrícula" (ES) overflowed its half.
     --vt-pad keeps the thumb aligned when the padding changes on mobile. */
  .view-toggle {
    --vt-pad: 3px;
    position: relative;
    display: inline-grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: stretch;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: var(--vt-pad);
    flex: none;
    height: var(--control-h);
  }

  .view-toggle .vt-thumb {
    position: absolute;
    top: var(--vt-pad); bottom: var(--vt-pad); left: var(--vt-pad);
    width: calc(50% - var(--vt-pad));
    background: var(--accent);
    border-radius: var(--radius-md);
    box-shadow: none;
    transition: transform var(--duration-slow) var(--ease-standard);
    z-index: 0;
  }

  .view-toggle:has(button[data-mode="table"].active) .vt-thumb {
    transform: translateX(100%);
  }

  .view-toggle button {
    position: relative;
    z-index: 1;
    min-width: 0;
    white-space: nowrap;
    font-family: var(--mono);
    font-size: 11px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    font-weight: 600;
    padding: 0 14px;
    border-radius: var(--radius-md);
    border: none;
    background: transparent;
    color: var(--text);
    cursor: pointer;
    transition: color var(--duration-base) ease 0.05s;
  }

  .view-toggle button.active { color: var(--accent-ink); }

  /* ---- stat tiles -- still used by the Plugin Dossier's own stat
     row (.dossier-stats .stat, see dossierBodyHtml()). The hero's own
     telemetry column below is a separate .tele-* system so redesigning
     the hero never touches this Field-mode dossier look. ---- */
  .stat {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 6px 8px;
    box-shadow: var(--shadow);
    transition: border-color var(--duration-slow) ease;
  }

  /* Decode reveal, see decodeNumber() in JS -- accent-colored glow
     while scrambling, settles back to normal once the real value lands. */
  .stat.decoding { border-color: var(--accent); }
  .stat.decoding .num {
    color: var(--accent);
    text-shadow: none;
    transition: color var(--duration-slow) ease var(--duration-slow), text-shadow var(--duration-slow) ease var(--duration-slow);
  }

  .stat .num {
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
    font-weight: 700;
    line-height: 1.15;
    min-height: 1em;
    overflow-wrap: anywhere;
  }

  .stat .num.accent { color: var(--accent); }
  

  .stat .label {
    margin-top: 3px;
    font-size: 8px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-faint);
  }

  /* ==================== HERO ====================
     Brand, featured-plugin slider, telemetry. Section nav lives only
     in the top bar — a second rail here duplicated Overview / Table /
     Methodology and fought the header. */
  .hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
    grid-template-areas: "brand slider";
    gap: var(--section-gap);
    padding: 20px 0 24px;
    align-items: stretch;
    min-width: 0;
  }

  

  .hero-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-top: auto;
    padding-top: 20px;
    padding-bottom: 8px;
    width: 100%;
    flex-shrink: 0;
  }
  .hero-actions .btn {
    min-height: var(--control-h);
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
  }

  
  .hs-media {
    position: relative;
    display: block;
    overflow: hidden;
    flex: 1 1 auto;
    min-height: 200px;
    background: var(--surface-2);
    --hs-ox: 50%;
    --hs-oy: 18%;
  }
  
  .hs-name {
    font-family: var(--mono);
    font-weight: 700;
    font-size: 15px;
    overflow-wrap: anywhere;
  }
  .hs-niche {
    font-size: 12px;
    color: var(--text-faint);
    overflow-wrap: anywhere;
  }
  
  .hs-dot {
    width: 16px;
    height: 28px;
    padding: 0;
    border: 0;
    background: transparent;
    cursor: pointer;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    appearance: none;
    -webkit-appearance: none;
  }
  .hs-dot::after {
    content: "";
    width: 100%;
    height: 5px;
    border-radius: 1px;
    background: var(--border);
  }
  .hs-dot:nth-child(3n)::after {
    height: 9px;
    background: var(--text-faint);
  }
  .hs-dot.is-active::after {
    height: 14px;
    background: var(--accent);
    box-shadow: none;
  }
  .hs-dot:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
  }
  

  /* ---- telemetry: continuous instrumentation column. Deliberately
     its own .tele-* classes, NOT a reuse of .stat/.num/.label -- those
     stay exactly as they were for the Plugin Dossier, see the comment
     above .stat. ---- */
  .telemetry {
    display: grid;
    grid-template-columns: 1fr 1fr;
    width: 100%;
    max-width: 28rem;
    margin-top: 16px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0;
    min-width: 0;
  }
  

  @media (max-width: 1080px) {
    .hero {
      grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
      grid-template-areas: "brand slider";
      row-gap: var(--section-gap);
    }
  }

  /* Brand + slider stay side by side here; full stacking is reserved
     for the narrower breakpoint below. */
  @media (max-width: 840px) {
    .hero {
      /* minmax(0, 1fr), not bare 1fr -- a bare 1fr track won't shrink
         below its content's min-content width (the longest unbreakable
         word), which was forcing the whole grid -- and so the page --
         wider than a narrow phone's actual viewport instead of letting
         text wrap. Real overflow, found on a real ~360 CSS px screenshot
         (right-edge text silently clipped, not wrapped). Same fix
         applied to .telemetry/.process-steps/.footer-grid below. */
      grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
      grid-template-areas: "brand slider";
      row-gap: 22px;
      column-gap: 24px;
      padding: 20px 0 16px;
    }
    
  }

  /* Below this, 2 columns gets too tight for the number+label pairs --
     back to a single column, with the border logic reset to match. */
  /* 340px, not 460 -- 460 was reverting to a single column on most
     real Android phones (measured against a real 1080x2400 device
     screenshot, ~411 CSS px at a typical ~2.625 DPR for that
     resolution class), which is exactly the width this 2-column grid
     should still be serving. Real phones below ~340 CSS px are rare
     enough now that a single column there is a reasonable floor. */
  @media (max-width: 340px) {
    .telemetry { grid-template-columns: 1fr; }
    
  }

  /* Same real-phone-width finding as .telemetry above: stacking brand
     and thesis at 480px still left a cramped two-column hero on real
     ~411 CSS px phones. 640px is where a half-width column stops
     fitting the title clamp comfortably. */
  @media (max-width: 640px) {
    .hero {
      grid-template-columns: minmax(0, 1fr);
      grid-template-areas: "brand" "slider";
      column-gap: 0;
    }
    
    .hero-actions { margin-top: 16px; padding-bottom: 0; }
    .hs-media{ min-height: 148px; }
    
    .hs-name { font-size: 14px; }
    .telemetry { max-width: none; }
    
    .telemetry { justify-self: stretch; }
  }

  

  /* ---- controls ---- */
  .controls {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: center;
    margin-bottom: var(--section-gap);
  }

  /* Envoltorios nuevos del rediseño mobile (ver @media 640px más abajo)
     -- en escritorio no deben existir visualmente: .ctl-rest y
     .ctl-filters-panel usan display:contents para que sus hijos
     (view-toggle, ambos <select>) sigan siendo hijos directos del flex
     de .controls, exactamente el layout de siempre; el ícono de lupa y
     el botón "Filters" (que en mobile reemplazan/agrupan esos
     controles) se ocultan del todo. */
  .ctl-search-icon, .ctl-filters-btn { display: none; }
  .ctl-rest, .ctl-filters-panel { display: contents; }

  .search {
    flex: 1 1 260px;
    position: relative;
  }

  .search input {
    width: 100%;
    height: var(--control-h);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 0 12px 0 32px;
    color: var(--text);
    font-family: var(--sans);
    font-size: 13.5px;
    outline: none;
  }

  /* Strengthened 2026-08-23 (audit finding): was border-color only, a
     1px hue swap with no outline/glow -- measurably weaker than every
     other interactive element's own :focus-visible treatment on this
     page (.plugin-card, .similar-card, etc. all get a real 2px outline). */
  .search input:focus { border-color: var(--accent); outline: 2px solid var(--accent); outline-offset: 2px; }

  .search::before {
    content: "";
    position: absolute;
    left: 11px;
    top: 50%;
    transform: translateY(-50%);
    width: 13px;
    height: 13px;
    border: 1.6px solid var(--text-faint);
    border-radius: 50%;
  }

  .search::after {
    content: "";
    position: absolute;
    left: 20px;
    top: 62%;
    width: 6px;
    height: 1.6px;
    background: var(--text-faint);
    transform: rotate(45deg);
  }

  select#pricingFilter, select#categoryFilter {
    height: var(--control-h);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 0 10px;
    color: var(--text);
    font-family: var(--sans);
    font-size: 13.5px;
    transition: border-color var(--duration-base) ease;
  }
  select#pricingFilter:hover, select#categoryFilter:hover { border-color: var(--accent); }
  select#pricingFilter:focus-visible, select#categoryFilter:focus-visible {
    border-color: var(--accent);
    outline: 2px solid var(--accent);
    outline-offset: 1px;
  }

  /* Rediseño completo del control bar mobile (a pedido explícito
     2026-09-10, 4ta vuelta) -- ya no son 4 filas apiladas: 1 sola fila
     compacta con la lupa a la izquierda, el toggle Field/Table + un
     botón "Filters" que abre un popover con los 2 <select> reales
     (mismos elementos/ids que ya usa el JS de filtrado, solo
     envueltos distinto -- category/pricingFilter no cambiaron). Click
     en la lupa: el resto (.ctl-rest) se desvanece y el campo de
     búsqueda se expande a todo el ancho; click de nuevo (o Escape)
     revierte. .ctl-search-icon/.ctl-filters-btn/.ctl-rest/
     .ctl-filters-panel solo existen visualmente acá -- en escritorio
     quedan con display:none o display:contents (ver regla base fuera
     de este media query) para no tocar el layout de siempre. */
  @media (max-width: 640px) {
    .controls {
      position: relative; display: flex; align-items: center; gap: 6px;
      border: 1px solid var(--border); border-radius: var(--radius-md);
      background: var(--surface); padding: 6px;
    }
    .ctl-search-icon {
      display: flex; align-items: center; justify-content: center; flex: none;
      width: 32px; height: 32px; border: 0; border-radius: var(--radius-sm);
      background: transparent; color: var(--text-dim); cursor: pointer;
      transition: color var(--duration-base) ease, background var(--duration-base) ease;
    }
    .ctl-search-icon svg { width: 17px; height: 17px; }
    .ctl-search-icon:hover { color: var(--text); background: var(--surface-2); }
    .controls.is-searching .ctl-search-icon { color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
    .search {
      flex: 1 1 auto; max-width: 0; opacity: 0; overflow: hidden; min-width: 0;
      transition: max-width var(--duration-slow) var(--ease-standard), opacity var(--duration-base) ease;
    }
    .controls.is-searching .search { max-width: 1000px; opacity: 1; }
    .search input { border: 0; background: transparent; border-radius: 0; height: 32px; padding: 0 8px 0 4px; }
    /* El ícono de lupa viejo (.search::before/::after, dibujado con
       pseudo-elementos para el input de escritorio) se quedaba
       encendido acá y quedaba flotando sobre el texto -- el padding
       nuevo del input (4px, ya no 32px) dejó de reservarle espacio.
       El botón .ctl-search-icon ya cumple ese rol acá, así que se
       oculta el viejo en vez de reacomodarlo (glitch real reportado
       con captura, 2026-09-10). */
    .search::before, .search::after { display: none; }
    .ctl-rest {
      display: flex; align-items: center; gap: 6px; flex: 1 1 auto; max-width: 1000px; min-width: 0;
      overflow: hidden; transition: max-width var(--duration-slow) var(--ease-standard), opacity var(--duration-base) ease;
    }
    .controls.is-searching .ctl-rest { max-width: 0; opacity: 0; pointer-events: none; }
    .view-toggle { --vt-pad: 2px; flex: 1 1 auto; min-width: 0; height: 32px; }
    .view-toggle button { padding: 0 8px; font-size: 9.5px; }
    .ctl-filters-btn {
      display: inline-flex; align-items: center; gap: 4px; flex: none;
      height: 32px; padding: 0 10px; border: 1px solid var(--border); border-radius: var(--radius-sm);
      background: var(--surface-2); color: var(--text); font-family: var(--sans); font-size: 11.5px; font-weight: 600;
      white-space: nowrap; cursor: pointer; transition: border-color var(--duration-base) ease;
    }
    .ctl-filters-btn svg { width: 9px; height: 6px; flex: none; transition: transform var(--duration-base) ease; }
    .ctl-filters-btn:hover, .ctl-filters-btn[aria-expanded="true"] { border-color: var(--accent); }
    .ctl-filters-btn[aria-expanded="true"] svg { transform: rotate(180deg); }
    .ctl-filters-panel {
      display: none; position: absolute; top: 100%; right: 6px; margin-top: 6px;
      flex-direction: column; gap: 8px; min-width: 200px; padding: 10px;
      background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md);
      box-shadow: var(--shadow); z-index: 20;
    }
    .ctl-filters-panel.is-open { display: flex; }
    .ctl-filters-panel select { width: 100%; }
    
  }

  

  /* ---- FIELD layout ---- */
  /* Sidebar widened 20% (300px -> 360px, 2026-08-15 request) specifically
     to give the Hunting Field diagram more room -- bigger orbit radius,
     bigger node labels, without cramping the "Filter by category" list
     that used to live below it (that list moved into the controls row
     as a <select>, see #categoryFilter, so all the extra width now goes
     straight to the diagram). */
  /* El Hunting Field (sidebar de categorías + panel de marca + process
     strip) se reemplazó por stats + tiles de categoría horizontales
     arriba de la grilla, a pedido explícito 2026-09-10 -- .field-layout
     pasa de 2 columnas a una sola. El mecanismo de filtro real
     (wireCategoryBoard()/updateCategoryBoard(), event delegation sobre
     .cat-board/.cat-cell) no se tocó -- sigue funcionando igual, solo
     cambió dónde vive el HTML. */
  .field-layout { display: flex; flex-direction: column; gap: var(--section-gap); }
  .field-main { min-width: 0; }
  /* Panel de detalle fijo (Fase 5b/5c del plan) -- clickear una card
     ya no reemplaza la grilla (como hacía renderDossier() antes),
     ahora abre un panel al lado. .field-row solo tiene 2 columnas
     cuando hay un detalle abierto (.has-detail); si no, #fieldMain
     ocupa todo el ancho como siempre. El routing por hash no se tocó
     -- setSelectedRepo(null,...) sigue haciendo exactamente lo mismo
     que antes, "cerrar" ya es la conducta correcta porque renderGrid()
     ahora corre siempre. */
  .field-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--section-gap); align-items: start; }
  
  .catalog-explore { margin-bottom: var(--section-gap); padding-bottom: var(--section-gap); border-bottom: 1px solid var(--border); }

  

  .section-head-sm { margin: 0 0 10px; color: var(--accent); font: 700 11px var(--mono); letter-spacing: .12em; text-transform: uppercase; }
  /* Tiles horizontales -- mismo lenguaje que .category-links de
     index.html, reusado acá tal cual para consistencia entre páginas. */
  /* Barra horizontal de chips (a pedido explícito 2026-09-10) -- flex-
     wrap, todas las categorías en una fila continua. border-radius
     corregido a var(--radius-md): 999px (pill totalmente redondeado)
     no es el lenguaje real del sitio -- ningún otro botón/card/chip
     del resto del sitio usa esa forma, todo usa esquinas moderadas. */
  /* nowrap + flex:1 1 0/min-width:0 (a pedido explícito 2026-09-10) --
     las 10 tiles (All categories + 9 categorías) se reparten el ancho
     disponible y se achican juntas en vez de que "Other" salte a una
     segunda línea; label/count truncan con ellipsis si no entran.
     Revierte a chips de ancho natural + wrap en mobile (media query
     abajo), donde 10 tiles apretadas en una sola línea ya no son
     usables como blanco de touch. */
  /* 2da vuelta sobre este bug real, mismo día: el overflow-x:auto de
     la vuelta anterior SÍ contenía el scroll (nunca se escapaba a la
     página), pero con 10 tiles reales el último quedaba "sobresaliendo"
     recortado a la mitad en el borde -- técnicamente contenido, pero
     se ve roto (reportado con captura otra vez). flex-wrap:wrap (el
     mismo comportamiento que ya usan los breakpoints angostos) en vez
     de nowrap+scroll: cuando 10 no entran en una fila, bajan prolijos
     a una segunda en vez de exigir scroll horizontal en un tablero de
     filtros que nadie espera que se scrollee. overflow-x:auto queda
     de red de seguridad extra, ya no hace falta para el caso normal. */
  .catalog-explore .cat-board { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 0; overflow-x: auto; }
  .catalog-explore .cat-cell {
    flex: 1 1 0; min-width: 0; flex-direction: row; align-items: center; gap: 6px; min-height: auto;
    padding: 7px 8px; background: var(--surface); border-radius: var(--radius-md);
    transition: border-color var(--duration-base) ease;
  }
  .catalog-explore .cat-cell::before { display: none; } /* el color ya vive en el ícono, mismo criterio que .tele-row/.qa-item (acento solo al hover, no una barra fija) */
  .catalog-explore .cat-cell:hover { border-color: color-mix(in srgb, var(--cat) 55%, var(--border)); }
  .catalog-explore .cat-cell-icon { flex: none; width: 22px; height: 22px; border-radius: var(--radius-sm); }
  .catalog-explore .cat-cell-icon svg { width: 12px; height: 12px; }
  .catalog-explore .cat-cell-copy { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
  .catalog-explore .cat-cell-label { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--sans); font-size: 11.5px; font-weight: 650; letter-spacing: normal; text-transform: none; color: var(--text); }
  .catalog-explore .cat-cell-count { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 10px; color: var(--text-faint); }

  @media (max-width: 900px) {
    
    .catalog-explore .cat-board { flex-wrap: wrap; }
    .catalog-explore .cat-cell { flex: none; padding: 7px 12px 7px 8px; gap: 8px; }
    .catalog-explore .cat-cell-copy { flex-direction: row; align-items: baseline; gap: 5px; }
    .catalog-explore .cat-cell-label { font-size: 12px; }
    .catalog-explore .cat-cell-count { font-size: 10.5px; }
  }
  /* Vuelve a 3 columnas en vez de 1 (a pedido explícito 2026-09-10) --
     los 3 boxes se achican en vez de apilarse, para que quepan uno al
     lado del otro incluso en un teléfono angosto. */
  @media (max-width: 520px) {
    
    /* Tiles de categoría más chicas (a pedido explícito 2026-09-10,
       ajustado a 3 por fila -- 4 quedaba muy apretado). Grid de 3
       columnas explícitas en vez de flex-wrap -- con flex:none +
       flex-wrap, el ancho "natural" de un chip (icono + texto sin
       recortar) no respeta cuántos quepan en la fila; el texto
       terminaba desbordando el borde real del viewport en vez de
       recortarse con ellipsis (bug real visto en captura). minmax(0,1fr)
       sí le da a cada celda un ancho fijo con base 0, que es lo que
       .cat-cell-label/-count necesitan para que su overflow:hidden +
       text-overflow:ellipsis (ya existían) realmente actúen -- min-width:0
       agregado también en .cat-cell y .cat-cell-copy, sin eso el
       ellipsis no se activa aunque el padre sí tenga ancho fijo. */
    .catalog-explore .cat-board { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 6px; }
    .catalog-explore .cat-cell { flex: none; min-width: 0; padding: 7px 7px; gap: 6px; }
    .catalog-explore .cat-cell-icon { width: 20px; height: 20px; }
    .catalog-explore .cat-cell-icon svg { width: 11px; height: 11px; }
    .catalog-explore .cat-cell-copy { flex-direction: column; align-items: flex-start; gap: 1px; min-width: 0; max-width: 100%; }
    .catalog-explore .cat-cell-label { display: block; max-width: 100%; font-size: 10.5px; }
    .catalog-explore .cat-cell-count { display: block; max-width: 100%; font-size: 9px; }
  }

  .panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 16px;
    box-shadow: var(--shadow);
  }

  .panel + .panel { margin-top: 14px; }

  

  /* Category board — replaces the radial Hunting Field diagram.
     Cell colors are site tokens (not a second hex palette). */
  .cat-board {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    margin-top: 12px;
  }
  .cat-cell {
    --cat: var(--text-dim);
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 44px;
    margin: 0;
    padding: 8px 10px 8px 12px;
    text-align: left;
    color: var(--text);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    position: relative;
    cursor: pointer;
    font: inherit;
    transition: border-color var(--duration-base) ease;
  }
  .cat-cell:hover { border-color: color-mix(in srgb, var(--cat) 55%, var(--border)); }
  .cat-cell::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 3px;
    background: var(--cat);
  }
  .cat-cell[hidden] { display: none; }
  /* Bug real, 2026-09-24: [hidden] y .plugin-card{display:flex} tienen
     la MISMA especificidad (una clase vs un atributo) -- el orden de
     cascada (reglas de autor despues de las del user-agent) hace que
     display:flex le gane a display:none del [hidden] del navegador.
     El JS de paginacion marcaba hidden=true correctamente (confirmado
     en el DOM guardado por el usuario: 131/146 tarjetas con el
     atributo puesto), pero las 146 seguian pintandose en pantalla --
     el bug era 100% visual/CSS, no de JS, y afectaba a TODOS los
     navegadores por igual (reproducido tanto en Edge como en Brave
     limpios una vez que se supo donde mirar). Mismo patron que ya se
     habia resuelto para .cat-cell arriba, nunca generalizado. */
  .plugin-card[hidden] { display: none; }
  .cat-cell-all { grid-column: 1 / -1; --cat: var(--text-dim); }
  .cat-cell.is-active {
    border-color: color-mix(in srgb, var(--cat) 65%, var(--border));
    background: color-mix(in srgb, var(--cat) 12%, var(--surface-2));
  }
  .cat-cell-icon {
    flex: none;
    width: 32px;
    height: 32px;
    display: grid;
    place-items: center;
    color: var(--cat);
    background: color-mix(in srgb, var(--cat) 14%, var(--surface-3));
    border: 1px solid color-mix(in srgb, var(--cat) 40%, var(--border));
    border-radius: var(--radius-md);
  }
  .cat-cell-icon svg {
    width: 18px;
    height: 18px;
    stroke: currentColor;
    fill: none;
    stroke-width: 1.5;
  }
  .cat-cell-label {
    display: block;
    font-family: var(--mono);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
  }
  .cat-cell-count {
    display: block;
    margin-top: 2px;
    font-family: var(--mono);
    font-size: 10px;
    color: var(--text-faint);
    letter-spacing: 0.04em;
    font-variant-numeric: tabular-nums;
  }
  .cat-cell:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }

  

  /* ---- plugin grid (Field mode, no selection) ---- */
  .plugin-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(168px, 100%), 1fr));
    gap: 8px;
  }
  @media (max-width: 640px) {
    .plugin-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .cat-cell { padding: 6px 8px; gap: 8px; min-height: 40px; }
    .cat-cell-icon { width: 28px; height: 28px; }
    .cat-cell-icon svg { width: 16px; height: 16px; }
  }

  .plugin-card {
    text-decoration: none; color: inherit;
    position: relative;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    min-height: 96px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 10px 11px 9px 11px;
    cursor: pointer;
    box-shadow: var(--shadow);
    transition: border-color var(--duration-fast) ease, transform var(--duration-fast) ease;
  }

  .plugin-card:hover { border-color: var(--accent); }
  /* Added 2026-08-21 alongside making these cards keyboard-focusable
     (role="button" tabindex="0") -- without an explicit style, a
     focused div falls back to the browser's default outline, which
     works but looks disconnected from the rest of this page's own
     focus/hover language. Mirrors .plugin-card:hover's own accent
     border instead of inventing a separate visual vocabulary just for
     keyboard focus. :focus-visible (not :focus) so mouse clicks don't
     also show the ring -- only real keyboard navigation does. */
  .plugin-card:focus-visible { border-color: var(--accent); outline: 2px solid var(--accent); outline-offset: 2px; }





  /* Header en flex row (a pedido explícito 2026-09-10) -- antes el
     ícono de categoría era position:absolute sobre la esquina y el
     título le reservaba espacio con un padding-right (34px) calculado
     a mano para que coincidiera exacto con el ancho+offset del ícono;
     cualquier diferencia mínima dejaba el título rozando el ícono sin
     margen real. Con flex + gap la separación la calcula el layout
     mismo, siempre exacta, sin importar cuántas líneas ocupe el
     nombre. Solo dentro de .plugin-card -- .card-cat sigue siendo
     position:absolute para .similar-card/.tbl-similar-card, que no se
     tocan acá. */
  .plugin-card .card-header { display: flex; align-items: flex-start; gap: 8px; }
  .name-cell a { text-decoration: none; color: inherit; display: block; }
  .plugin-card .card-header .card-cat { position: static; flex: none; }
  .plugin-card .card-top { flex: 1; min-width: 0; }

  /* 2-line clamp with an ellipsis instead of a silent hard clip: at
     narrow widths a grid row's height comes from its shortest sibling,
     so a longer name (e.g. "Format Converter Companion") could wrap to
     2 lines while the card itself stays sized for 1, and .plugin-card's
     own overflow:hidden (needed for the corner-frame effect) would cut
     the second line mid-character with no visual indication anything
     was cut. Clamping the name itself makes the truncation visible and
     intentional instead of an invisible bug. */
  .plugin-card .card-name {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
    font-weight: 700;
    font-size: 12.5px;
    line-height: 1.25;
    overflow-wrap: anywhere;
    min-width: 0;
  }
  .plugin-card .card-niche { font-size: 10px; color: var(--text-faint); margin-top: 1px; overflow-wrap: anywhere; }
  .card-cat {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 1;
    width: 22px;
    height: 22px;
    display: grid;
    place-items: center;
    color: var(--cat, var(--text-faint));
    background: color-mix(in srgb, var(--cat, var(--text-faint)) 14%, var(--surface-3));
    border: 1px solid color-mix(in srgb, var(--cat, var(--text-faint)) 40%, var(--border));
    border-radius: var(--radius-sm);
    pointer-events: none;
  }
  .card-cat svg {
    width: 13px;
    height: 13px;
    stroke: currentColor;
    fill: none;
    stroke-width: 1.5;
  }

  .plugin-card .card-metrics {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
    margin-top: auto;
    padding-top: 8px;
    font-family: var(--mono);
  }

  .plugin-card .card-dl-wrap { display: inline-flex; align-items: center; gap: 4px; }
  .plugin-card .dl-icon { flex: none; width: 11px; height: 11px; color: var(--text-faint); }
  .plugin-card .card-dl { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; }

  

  /* ---- chips ---- */
  .chip {
    display: inline-block;
    font-family: var(--mono);
    font-size: 10.5px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    font-weight: 600;
    padding: 3px 8px;
    border-radius: var(--radius);
    white-space: nowrap;
  }

  .chip.free { background: var(--surface-2); color: var(--text-dim); }
  .chip.freemium { background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--accent); }
  /* 2026-08-23 (audit finding): Paid used to share the exact same
     --accent text color as Freemium, distinguished only by background
     opacity (18% vs 30%) -- harder to tell apart at a glance than
     .free/.pending, which each get their own distinct hue. --cyan is
     already part of the brand palette (the logo's second blue), unused
     by any other chip/badge, so this doesn't introduce a new color. */
  .chip.paid { background: color-mix(in srgb, var(--cyan) 22%, transparent); color: var(--cyan); }

  .chip.pending { background: var(--pending-bg); color: var(--pending); animation: pendingPulse 2.4s ease-in-out infinite; }
  @keyframes pendingPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.75; }
  }
  @media (prefers-reduced-motion: reduce) {
    .chip.pending { animation: none; }
  }

  .growth { font-size: 12px; margin-left: 6px; }
  .growth.up { color: var(--good); }
  .growth.down { color: var(--bad); }
  .growth.flat { color: var(--text-faint); }

  /* ---- Plugin Dossier ---- */
  .dossier-back {
    font-family: var(--mono);
    font-size: 12px;
    color: var(--text-faint);
    cursor: pointer;
    margin: 0 0 14px;
    padding: 0;
    border: 0;
    background: none;
    display: inline-block;
    text-align: left;
    line-height: 1.3;
    appearance: none;
    -webkit-appearance: none;
  }

  .dossier-back:hover { color: var(--accent); }
  .dossier-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

  
.dossier-sheet { display: flex; flex-direction: column; gap: 14px; min-width: 0; }

  /* One header card: back control + two columns that share a bottom
     edge (stats strip vs Install/GitHub). Gap/Fix/Facts stay siblings
     below, not inside this box. */
  .dossier-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 16px 18px;
    min-width: 0;
  }

  

  .dossier-title-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
  .dossier-title-row :is(h1, h2) { font-family: var(--mono); font-size: 24px; margin: 0; overflow-wrap: anywhere; }
  
  /* flex-basis 0 so the remote GIF's intrinsic height cannot blow out
     the right column. Leftover height fills the image; CTAs stay on
     the shared bottom edge with the stats strip. */
  .dossier-gif {
    width: 100%;
    flex: 1 1 0;
    height: 0;
    min-height: 140px;
    object-fit: cover;
    object-position: top left;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    display: block;
    background: var(--surface-2);
  }
  
  /* Fila "Share" de cada ficha (2026-09-24): X y LinkedIn son enlaces
     planos; "Copy link" es un boton que plugin-page.js muestra solo si hay
     clipboard. [hidden] explicito: .share-btn fija display, que le ganaria
     al [hidden] del navegador (mismo bug que ya paso con .plugin-card). */
  .share-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 2px; }
  .share-label {
    font-family: var(--mono); font-size: 10.5px; font-weight: 700; letter-spacing: 0.08em;
    text-transform: uppercase; color: var(--text-faint); margin-right: 2px;
  }
  .share-btn {
    display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px;
    font-family: var(--mono); font-size: 11.5px; font-weight: 600; line-height: 1.2;
    color: var(--text-dim); background: transparent; border: 1px solid var(--border);
    border-radius: var(--radius-md); text-decoration: none; cursor: pointer;
    transition: color var(--duration-base) ease, background var(--duration-base) ease, border-color var(--duration-base) ease;
  }
  .share-btn svg { width: 13px; height: 13px; flex: none; }
  /* Solo el logo de X (el texto "X" al lado era redundante); el nombre accesible va en aria-label. */
  .share-btn.share-icon { padding: 6px 9px; }
  .share-btn:hover { color: var(--text); background: var(--surface-2); }
  .share-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .share-btn.copied { color: var(--good); border-color: color-mix(in srgb, var(--good) 45%, var(--border)); }
  .share-btn[hidden] { display: none; }

  .btn {
    font-family: var(--mono);
    font-size: 12.5px;
    font-weight: 600;
    padding: 10px 16px;
    border-radius: var(--radius-md);
    text-decoration: none;
    white-space: nowrap;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    display: inline-flex;
    align-items: center;
    gap: 8px;
    transition: border-color var(--duration-base) ease, opacity var(--duration-base) ease, transform var(--duration-fast) ease;
  }

  .btn.primary { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
  .btn:hover { border-color: var(--accent); }
  .btn.primary:hover { opacity: 0.9; }

  .btn:active { transform: scale(0.97); }
  @media (prefers-reduced-motion: reduce) {
    .btn { transition: border-color var(--duration-base) ease, opacity var(--duration-base) ease; }
    .btn:active { transform: none; }
  }

  .btn.primary .btn-icon { width: 20.8px; height: 20.8px; }

  /* Added 2026-08-21: real GitHub/JetBrains marks on the Install/View
     buttons -- inline SVG, not the JetBrains-provided mp-widget.js
     script (that widget needs script-src/connect-src opened to
     plugins.jetbrains.com, which would undo the CSP hardening from
     2026-08-20; a static icon on the existing working link gets the
     same visual result with zero new attack surface). Sized to sit
     inline with the button's own 12.5px text -- 16px reads as "icon
     next to a label," not "icon competing with the label." currentColor
     on the GitHub mark so it inverts correctly in both .btn and
     .btn.primary (dark-on-accent vs. light-on-surface); JetBrains' mark
     keeps its own real brand colors (the 4-square logo) since that's
     how their own guidelines present it, same treatment .btn.primary
     already gives real brand colors elsewhere on this page. */
  .btn-icon { width: 16px; height: 16px; flex: none; }

  

  .ib-title { font-family: var(--mono); font-size: 11px; letter-spacing: 0.07em; text-transform: uppercase; font-weight: 700; }
  
  .facts-list {
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: 13.5px;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: 20px;
  }
  .facts-list li {
    display: flex; justify-content: space-between; align-items: baseline;
    gap: 12px;
    padding: 8px 0;
    border-bottom: 1px solid var(--border);
    min-width: 0;
  }
  @media (max-width: 900px) {
    .facts-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }
  @media (max-width: 640px) {
    .facts-list { grid-template-columns: 1fr; }
  }
  .facts-list li:last-child {
    border-bottom: none;
    grid-column: 1 / -1;
  }
  .facts-list .fk { color: var(--text-faint); font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; flex: none; }
  .facts-list .fv { color: var(--text); text-align: right; overflow-wrap: anywhere; }
  .facts-list .fv code { background: var(--surface-2); padding: 1px 6px; border-radius: var(--radius-sm); font-size: 0.9em; }

  .copy-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    vertical-align: middle;
    width: 22px;
    height: 22px;
    margin-left: 4px;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--text-faint);
    cursor: pointer;
    border-radius: var(--radius-sm);
    transition: color var(--duration-base) ease, background var(--duration-base) ease, transform var(--duration-fast) ease;
  }
  .copy-btn svg { width: 13px; height: 13px; }
  .copy-btn:hover { color: var(--text); background: var(--surface-2); }
  .copy-btn:active { transform: scale(0.9); }
  .copy-btn.copied { color: var(--good); }
  .copy-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
  @media (prefers-reduced-motion: reduce) {
    .copy-btn:active { transform: none; }
  }

  .similar-section { margin-top: 0; }
  .similar-title { font-family: var(--mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-faint); margin-bottom: 10px; }
  .similar-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 10px; }
  .similar-card {
    position: relative;
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 12px 32px 12px 14px; cursor: pointer; font-size: 12.5px;
  }
  .similar-card:hover { border-color: var(--accent); }
  /* Keyboard focus state added 2026-08-23 (audit finding) -- these
     cards had role="button"/tabindex="0" with no visible focus state
     at all, same reasoning as .plugin-card:focus-visible above. */
  .similar-card:focus-visible { border-color: var(--accent); outline: 2px solid var(--accent); outline-offset: 2px; }
  .similar-card .sc-name { font-weight: 600; color: var(--text); margin-bottom: 2px; }
  .similar-card .sc-niche { color: var(--text-faint); font-size: 11px; }
  .similar-card .sc-dl { margin-top: 8px; font-family: var(--mono); font-size: 12px; color: var(--text-dim); }

  .catalog-pager {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 10px 12px;
    margin-top: var(--section-gap);
  }
  .catalog-pager-btn {
    min-height: var(--control-h);
    min-width: var(--control-h);
    padding: 10px 16px;
  }
  .catalog-pager-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
  }
  .catalog-pager-status {
    font-family: var(--mono);
    font-size: 12px;
    color: var(--text-dim);
    min-width: 8em;
    text-align: center;
  }

  /* ---- TABLE mode (unchanged behavior, restyled to match) ---- */

  .table-scroll {
    overflow-x: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background:
      linear-gradient(to right, var(--surface) 30%, rgba(0,0,0,0)),
      linear-gradient(to right, rgba(0,0,0,0), var(--surface) 70%) 100% 0,
      linear-gradient(to right, color-mix(in srgb, var(--accent) 35%, transparent), rgba(0,0,0,0) 12px),
      linear-gradient(to left, color-mix(in srgb, var(--accent) 35%, transparent), rgba(0,0,0,0) 12px) 100% 0;
    background-repeat: no-repeat;
    background-size: 40px 100%, 40px 100%, 12px 100%, 12px 100%;
    background-attachment: local, local, scroll, scroll;
    box-shadow: var(--shadow);
  }

  table { width: 100%; border-collapse: collapse; min-width: 880px; }

  thead th {
    position: sticky; top: 0;
    background: var(--surface-2);
    text-align: left;
    font-family: var(--mono);
    font-size: 11px;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--text-faint);
    padding: 11px 14px;
    border-bottom: 1px solid var(--border);
    cursor: pointer;
    white-space: nowrap;
    user-select: none;
  }

  thead th { transition: color var(--duration-base) ease, background var(--duration-base) ease; }
  thead th:hover { color: var(--text); background: var(--surface-2); }
  thead th.sorted { color: var(--accent); }

  thead th .arrow { font-size: 9px; margin-left: 3px; opacity: 0.7; display: inline-block; animation: arrowIn var(--duration-base) ease; }
  @keyframes arrowIn {
    from { opacity: 0; transform: scale(0.5); }
    to   { opacity: 0.7; transform: scale(1); }
  }
  @media (prefers-reduced-motion: reduce) {
    thead th .arrow { animation: none; }
  }

  thead th:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

  tbody tr.row { cursor: pointer; border-bottom: 1px solid var(--border); transition: background var(--duration-base) ease; }
  tbody tr.row:hover { background: var(--surface-2); }

  #tbody.just-sorted { animation: tbodySortFlash var(--duration-slow) ease; }
  @keyframes tbodySortFlash {
    from { opacity: 0.4; }
    to   { opacity: 1; }
  }
  @media (prefers-reduced-motion: reduce) {
    #tbody.just-sorted { animation: none; }
  }
  tbody tr.row:last-child { border-bottom: none; }
  /* Added 2026-08-21 alongside making rows keyboard-focusable -- same
     reasoning as .plugin-card:focus-visible above: an unstyled default
     outline works but doesn't match this page's own focus/hover
     vocabulary. Inset (not outline-offset) since a row spans the full
     table width and an offset ring would visually collide with its
     neighbors above/below. */
  tbody tr.row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; background: var(--surface-2); }
  tbody td { padding: 11px 14px; vertical-align: middle; }
  td.name-cell { max-width: 240px; }
  .plugin-name { font-weight: 600; font-size: 14px; }
  .niche { font-size: 12px; color: var(--text-dim); margin-top: 1px; }
  td.num-cell { font-family: var(--mono); font-variant-numeric: tabular-nums; text-align: right; font-size: 13.5px; }
  /* 2026-08-23 (audit finding): the 4 numeric-column <th> elements each
     had the identical inline style="text-align:right" repeated by hand.
     One shared class instead. */
  th.num-head { text-align: right; }

  

  /* Table mode en teléfono: cada fila se convierte en una card apilada
     en vez de la tabla ancha con scroll horizontal (a pedido explícito
     2026-09-10 -- "las pestañas [columnas] no se logran ver completas
     ... no tiene orden interno", confirmado con captura real: headers
     cortados a la mitad, look "glitcheado"). Mismo <tr>/<td> real, sin
     tocar el JS de sort/click/expand -- solo display:flex en vez de
     table-row, y label por celda vía data-label + ::before (agregado
     en pluginCardHtml de la tabla) en vez de depender del <thead>
     (oculto acá). Reviews/Rating (.tbl-optional) se ocultan -- mismo
     criterio que ya se aplicó al dossier: siempre "—", cero dato real
     en todo el catálogo hoy. */
  @media (max-width: 640px) {
    .table-scroll { overflow-x: visible; border: 0; border-radius: 0; background: none; box-shadow: none; }
    table { min-width: 0; }
    thead { display: none; }
    tbody { display: block; }
    tr.row {
      display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px;
      background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
      box-shadow: var(--shadow); padding: 12px; margin-bottom: 10px;
    }
    /* Mismo bug que .plugin-card[hidden] arriba: display:flex acá adentro
       tiene mas especificidad que el [hidden] del navegador solo en este
       breakpoint (a escritorio tr.row no fija display, asi que ahi la
       tabla paginaba bien). */
    tr.row[hidden] { display: none; }
    tr.row:last-child { border-bottom: 1px solid var(--border); }
    tr.row:has(+ tr.expand-row .expand-inner.open) {
      margin-bottom: 0; border-bottom: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0;
    }
    tr.row td { display: block; padding: 0; }
    tr.row td.name-cell {
      flex: 1 1 100%; max-width: none; padding-bottom: 10px; margin-bottom: 2px;
      border-bottom: 1px solid var(--border);
    }
    tr.row td.tbl-optional { display: none; }
    tr.row td[data-label] { flex: 1 1 auto; min-width: 84px; }
    tr.row td[data-label]::before {
      content: attr(data-label); display: block; margin-bottom: 2px;
      font-family: var(--mono); font-size: 8.5px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-faint);
    }
    td.num-cell { text-align: left; }
    
  }

  
  .tbl-similar-card {
    position: relative;
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md);
    padding: 9px 28px 9px 11px; cursor: pointer; font-size: 11.5px;
  }
  .tbl-similar-card .card-cat { top: 5px; right: 5px; width: 20px; height: 20px; }
  .tbl-similar-card .card-cat svg { width: 12px; height: 12px; }
  .tbl-similar-card:hover { border-color: var(--accent); }
  .tbl-similar-card:focus-visible { border-color: var(--accent); outline: 2px solid var(--accent); outline-offset: 2px; }
  .tbl-similar-card .sc-name { font-weight: 600; color: var(--text); margin-bottom: 1px; font-size: 12px; }
  .tbl-similar-card .sc-niche { color: var(--text-faint); font-size: 10.5px; }
  .tbl-similar-card .sc-dl { margin-top: 6px; font-family: var(--mono); font-size: 11px; color: var(--text-dim); }
  

  /* Animate a newly mounted empty-result message. */
  .no-results {
    padding: 40px;
    text-align: center;
    color: var(--text-faint);
    font-family: var(--mono);
    font-size: 13px;
    animation: noResultsIn var(--duration-slow) ease;
  }
  @keyframes noResultsIn {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  @media (prefers-reduced-motion: reduce) {
    .no-results { animation: none; }
  }
  

  /* Element changed 2026-08-21: was <footer class="notes">, but this
     holds the real Methodology/Gap-Fix explainer content -- part of the
     page's own substance, not closing boilerplate (the actual page
     footer, .site-footer, is separate and comes right after this). A
     <footer> landmark here was misleading for screen-reader users
     navigating by landmark; <section> matches what it actually is. */
  section.notes {
    margin-top: var(--section-gap);
    padding-top: var(--section-gap);
    border-top: 1px solid var(--border);
    font-size: 12.5px;
    color: var(--text-faint);
    line-height: 1.7;
  }

  section.notes b { color: var(--text-dim); }

  

/* ==================== SITE FOOTER ====================
     Quiet close for the page: brand, in-page nav, and real external
     channels. No HUD ornaments, indexed 01/02 labels, or closing
     wordmark. The footer count is kept aligned with the external
     catalog JSON for accurate first paint. */
  .site-footer {
    margin-top: var(--section-gap);
    border-top: 1px solid var(--border);
    background: var(--surface);
  }

  

  @media (max-width: 760px) {
    .dossier-card { padding: 14px 14px; }
    
    .dossier-gif { flex: none; height: auto; min-height: 0; aspect-ratio: 16 / 9; }
    
  }
  @media (max-width: 640px) {
    .wrap { padding: 16px var(--page-pad-sm) 24px; }
    
  }

  .content-stage { min-width: 0; }

  /* First-load laboratory splash. Covers the page until the catalog
     has painted, then fades. Independent of Field/Table switching. */
  html.boot { overflow: hidden; }

  
  .sl-status { font:600 11px var(--mono); letter-spacing:.12em; text-transform:uppercase; color:var(--text-dim); }
  .sl-status { margin-top:6px; color:var(--accent); }
  
:root { --card-pad:16px; --card-pad-sm:10px; }
  /* Sin padding horizontal propio -- .wrap ya pone 24px de cada lado;
     esta regla sumaba OTRO padding encima del header, doble inset a
     la izquierda respecto a .controls/.content-stage (que nunca lo
     tuvieron) -- no se notaba en el layout viejo centrado, se ve como
     desalineado real ahora con el sidebar al lado (feedback real
     2026-09-10). */
  .catalog-heading { max-width:var(--page-max); margin:var(--section-gap) auto 0; }
  /* Ilustración del hero de Catalog (a pedido explícito 2026-09-10) --
     misma composición que una referencia visual del usuario (nodos
     hexagonales conectados por líneas de circuito con glow), pero con
     íconos GENÉRICOS en vez de logos reales de producto (ya se había
     acordado esta regla al planificar el rediseño: "evitar logos de
     marca, usar solo íconos propios"). Cada hexágono usa el mismo
     ícono+color real de una categoría del catálogo (CAT_ICON_INNER/
     --accent/--good/etc. ya definidos en este archivo) -- no es
     decoración inventada, es la red de categorías reales del catálogo.
     SVG hand-rolled, sin librería ni asset externo, mismo criterio que
     el resto del sitio (sparkline, decision-diagram). */
  .catalog-heading { display: flex; align-items: center; gap: var(--section-gap); }
  /* Solo top/bottom a la mitad (a pedido explícito 2026-09-10) -- left/
     right se quedan en el 7% que ya trae .page-hero (shell.css). Selector
     compuesto (.catalog-heading.page-hero, especificidad 0,2,0) en vez
     de solo .catalog-heading -- shell.css carga después del <style> de
     esta página, así que un empate de especificidad con el padding:7%
     shorthand de .page-hero lo hubiera ganado shell.css igual. */
  .catalog-heading.page-hero { padding-top: 3.5%; padding-bottom: 3.5%; }
  .ch-copy { flex: 1 1 360px; min-width: 0; }
  
/* Limpieza real (2026-09-10): este bloque tenía ~20 líneas de CSS de
     index.html pegadas por error (.identity-section/.identity-process/
     .identity-step/.identity-card/.identity-grid/.identity-links/
     .section-head/.method-fact/.tele-row) -- ninguna de esas clases
     existe en el HTML de esta página. No era solo peso muerto: dos
     bugs reales salían de ahí por coincidencia de nombre de clase --
     .category-links .cat-cell (flex-direction:column, declarado
     después de .catalog-explore .cat-cell) ganaba el empate de
     especificidad y convertía las tiles de categoría en cards altas
     apiladas en vez del chip horizontal ya construido (bug reportado
     con captura); y .plugin-card compartía selector con
     .method-fact/.tele-row, pisando su padding real (10px 11px 9px
     11px) por --card-pad-sm (10px parejo) sin que nadie lo notara.
     Solo quedan .eyebrow/.catalog-heading h1, que sí son reales acá. */
  .eyebrow { margin:0; color:var(--accent); font:700 11px var(--mono); letter-spacing:.12em; text-transform:uppercase; }
  .catalog-heading h1 { margin:4px 0 0; }

/* 2026-09-27: CTA de busqueda sin resultados. */
.no-results .miss-cta { display: inline-block; margin-left: 6px; color: var(--accent); font-weight: 600; text-decoration: none; }
.no-results .miss-cta:hover { text-decoration: underline; }

/* =====================================================================
   2026-09-27 -- Catalogo v2: encabezado de marca con cifras, pestanas
   JetBrains | VS Code, tarjetas del sistema nuevo, hubs (cuando existan)
   y banda de servicios. Todo acotado a .catalog-v2 para ganarle a las
   reglas del diseno anterior sin tocarlas (las fichas heredan este CSS).
   ===================================================================== */
.catalog-v2 { display: flex; flex-direction: column; gap: 0; padding-top: 16px; }
.catalog-v2 h3 { font-family: var(--sans); }
.catalog-v2 .btn { font: 600 14px var(--sans); letter-spacing: 0; text-transform: none; padding: 9px 16px;
  border-radius: var(--radius); border: 1px solid var(--border-strong); background: var(--surface); color: var(--text); text-decoration: none; }
.catalog-v2 .btn:hover { border-color: var(--accent); color: var(--accent); }
.catalog-v2 .btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.catalog-v2 .btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--accent-ink); }

/* Encabezado: texto a la izquierda, cifras a la derecha. */
/* 2026-10-19: one column; the spec sheet runs full width under the lead. */
.catalog-v2 .catalog-heading.gh-page-header { display: grid; grid-template-columns: minmax(0, 1fr);
  align-items: start; gap: 0; max-width: none; margin: 0; }

@media (max-width: 900px) { .catalog-v2 .catalog-heading.gh-page-header { grid-template-columns: 1fr; } }

/* Pestanas de plataforma */
.platform-tabs { display: flex; gap: 4px; margin: 32px 0 0; border-bottom: 1px solid var(--border); overflow-x: auto; }
.platform-tab { display: inline-flex; align-items: center; gap: 10px; margin-bottom: -1px; padding: 12px 18px;
  border-bottom: 2px solid transparent; color: var(--text-dim); font: 600 15px var(--sans); text-decoration: none; white-space: nowrap; }
.platform-tab svg { width: 18px; height: 18px; flex: none; }
.platform-tab:hover { color: var(--text); }
.platform-tab[aria-selected="true"] { color: var(--text); border-bottom-color: var(--accent); }
.platform-tab[aria-selected="true"] svg { color: var(--accent); }
.pt-count { padding: 2px 9px; border-radius: var(--radius); background: var(--surface-3); color: var(--text-dim); font-size: 12px; font-weight: 700; }
.platform-tab[aria-selected="true"] .pt-count { background: var(--accent-soft); color: var(--accent); }
.platform-panel { padding-top: 24px; }
.platform-panel[hidden] { display: none; }

/* Cabecera de seccion */
.cv-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 20px; }
.cv-eyebrow { margin: 0; color: var(--accent); font: 700 12px var(--sans); letter-spacing: .1em; text-transform: uppercase; }
.cv-head h2 { margin: 6px 0 0; color: var(--text); font: 600 clamp(22px, 2.4vw, 28px) / 1.2 var(--serif); letter-spacing: -0.01em; }
.cv-lead { max-width: 68ch; margin: 8px 0 0; color: var(--text-dim); line-height: 1.6; }

/* Barra de controles */
@media (min-width: 641px) {
  .catalog-v2 .controls { gap: 10px; margin-bottom: 20px; padding: 10px; background: var(--surface);
    border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); }
}
.catalog-v2 .search input, .catalog-v2 .controls select { height: 40px; border: 1px solid var(--border-strong); border-radius: var(--radius);
  background: var(--surface); color: var(--text); font: 400 14px var(--sans); }
.catalog-v2 .search input:focus, .catalog-v2 .controls select:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }
.catalog-v2 .vsx-controls .search { flex: 1 1 100%; }

/* Chips de categoria */
.catalog-v2 .catalog-explore { margin-bottom: 20px; padding-bottom: 0; border-bottom: 0; }
.catalog-v2 .section-head-sm { color: var(--text-faint); font: 600 13px var(--sans); letter-spacing: 0; text-transform: none; }
.catalog-v2 .catalog-explore .cat-cell { border-radius: var(--radius); border-color: var(--border); background: var(--surface); box-shadow: none; }
.catalog-v2 .catalog-explore .cat-cell[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-soft); }
.catalog-v2 .cat-cell-label { font-family: var(--sans); }

/* Tarjetas (JetBrains y VS Code comparten el mismo lenguaje) */
.catalog-v2 .plugin-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; }
.catalog-v2 .plugin-card, .catalog-v2 .vsx-card { position: relative; display: flex; flex-direction: column; gap: 10px;
  min-width: 0; padding: 20px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow); color: var(--text); text-decoration: none;
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease; }
.catalog-v2 .plugin-card:hover, .catalog-v2 .vsx-card:hover { border-color: var(--accent); box-shadow: var(--shadow-lg); }
.catalog-v2 .plugin-card[hidden] { display: none; }
.catalog-v2 .card-header { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 0; padding: 0; border: 0; }
.catalog-v2 .card-cat { display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; flex: none;
  border-radius: var(--radius); color: var(--cat, var(--accent)); background: color-mix(in srgb, var(--cat, var(--accent)) 12%, var(--surface)); }
.catalog-v2 .card-cat svg { width: 20px; height: 20px; }
.catalog-v2 .vsx-mark { --cat: var(--text-dim); }
.catalog-v2 .card-top { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.catalog-v2 .card-name { color: var(--text); font: 600 16px / 1.3 var(--sans); letter-spacing: 0; text-transform: none; }
.catalog-v2 .card-niche { color: var(--text-faint); font: 500 13px var(--sans); letter-spacing: 0; text-transform: none; }
.catalog-v2 .card-pitch { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
  margin: 0; color: var(--text-dim); font-size: 14px; line-height: 1.55; }
.catalog-v2 .card-metrics { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-top: auto; padding-top: 12px; border-top: 1px solid var(--border); color: var(--text-faint); font: 500 13px var(--sans); }
.catalog-v2 .card-dl-wrap { display: inline-flex; align-items: center; gap: 6px; }
.catalog-v2 .dl-icon svg { width: 14px; height: 14px; }
.catalog-v2 .card-dl { color: var(--text); font-weight: 600; font-family: var(--sans); }
.catalog-v2 .card-go { color: var(--accent); font-weight: 600; white-space: nowrap; }
.catalog-v2 .no-results { padding: 24px; border: 1px dashed var(--border-strong); border-radius: var(--radius); color: var(--text-dim); text-align: center; }

/* Hubs y servicios */
.catalog-hubs { margin-top: 40px; }
.catalog-services { margin-top: 56px; }
.svc-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px; }
.svc-card { display: flex; flex-direction: column; gap: 8px; padding: 22px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow); }
.svc-kicker { margin: 0; color: var(--accent); font: 700 12px var(--sans); letter-spacing: .08em; text-transform: uppercase; }
.svc-card h3 { margin: 0; color: var(--text); font-size: 17px; }
.svc-card p { margin: 0; color: var(--text-dim); line-height: 1.55; }
.svc-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: auto; padding-top: 10px; }
.svc-band { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px; margin-top: 16px;
  padding: 22px 24px; border: 1px solid var(--accent); border-radius: var(--radius); background: var(--surface); }
.svc-band > div { display: flex; flex-direction: column; gap: 4px; }
.svc-band strong { color: var(--text); font-size: 17px; }
.svc-band span { color: var(--text-dim); }

@media (prefers-reduced-motion: reduce) {
  .catalog-v2 .plugin-card, .catalog-v2 .vsx-card { transition: none; }
  .catalog-v2 .plugin-card:hover, .catalog-v2 .vsx-card:hover { transform: none; }
}

/* 2026-10-05: al paginar, el foco pasa a los resultados por codigo
   (js/catalog-scroll.js); sin contorno en ese contenedor. */
#fieldMain:focus, #tableView:focus, #vsxGrid:focus { outline: none; }

/* 2026-09-27 (v2 de la parte baja): brecha a todo el ancho con citas,
   datos en grilla y relacionados con la tarjeta del catalogo. */
.pb-gap { margin-top: 24px; }
.pb-text p { margin: 0 0 12px; }
.pb-text p:last-child { margin-bottom: 0; }
.pb-text em { color: var(--text); }
.pb-quotes { display: grid; gap: 10px; margin: 4px 0 0; padding: 0; list-style: none; }
.pb-quotes li { padding: 12px 16px; border-left: 3px solid var(--accent); border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--surface-2); color: var(--text-dim); font-size: 15px; line-height: 1.6; }
.pb-facts { margin-top: 16px; }
.pb-facts-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; }
.pb-facts-head .pb-title { margin: 0; }
.pb-facts-head .share-row { margin: 0; padding: 0; border: 0; }
.facts-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1px; margin: 0; overflow: hidden;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--border); }
.facts-grid .fact { display: flex; flex-direction: column; gap: 4px; min-width: 0; padding: 14px 16px; background: var(--surface); }
.facts-grid dt { color: var(--text-faint); font: 500 12.5px var(--sans); }
.facts-grid dd { display: flex; align-items: center; gap: 6px; margin: 0; color: var(--text); font: 600 15px var(--sans); overflow-wrap: anywhere; }
.facts-grid dd code { padding: 1px 6px; border-radius: var(--radius); background: var(--surface-2); font: 500 13px var(--mono); }
.similar-section { margin-top: 32px; }
.similar-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 16px; }
.similar-head h2.similar-title { margin: 0; }
.similar-all { color: var(--accent); font: 600 14px var(--sans); text-decoration: none; }
.similar-all:hover { text-decoration: underline; }
.similar-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; }
.rel-card { display: flex; flex-direction: column; gap: 8px; min-width: 0; padding: 20px; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); color: var(--text); text-decoration: none;
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease; }
.rel-card:hover { border-color: var(--accent); box-shadow: var(--shadow-lg); }
.rel-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.rel-card .card-cat { position: static; display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 38px; height: 38px; border-radius: var(--radius); color: var(--cat); background: color-mix(in srgb, var(--cat) 12%, var(--surface)); }
.rel-card .card-cat svg { width: 20px; height: 20px; }
.rel-name { font: 600 16px / 1.3 var(--sans); }
.rel-niche { color: var(--text-faint); font: 500 13px var(--sans); }
.rel-pitch { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
  margin: 0; color: var(--text-dim); font-size: 14px; line-height: 1.55; }
.rel-foot { display: flex; justify-content: space-between; gap: 10px; margin-top: auto; padding-top: 12px;
  border-top: 1px solid var(--border); color: var(--text-faint); font: 500 13px var(--sans); }
.rel-go { color: var(--accent); font-weight: 600; }
@media (prefers-reduced-motion: reduce) { .rel-card { transition: none; } .rel-card:hover { transform: none; } }
/* 2026-09-27: ficha con el sistema nuevo (encabezado de marca + tarjetas). */
.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 16px 0 12px; color: var(--text-faint); font: 500 14px var(--sans); }
.crumbs a { color: var(--accent); text-decoration: none; }
.crumbs a:hover { text-decoration: underline; }
.plugin-hero.gh-page-header { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); align-items: center; gap: 32px 40px; margin: 0; }
.plugin-hero.no-media { grid-template-columns: 1fr; }
.plugin-hero.gh-page-header .ph-title-row { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 14px; }
.plugin-hero.gh-page-header .ph-title-row h1 { margin: 0; max-width: none; }
.plugin-hero .ph-niche { margin: 8px 0 0; color: var(--text-faint); font: 500 15px var(--sans); }
.plugin-hero .ph-lead { max-width: 60ch; margin: 14px 0 0; color: var(--text-dim); font-size: 17px; line-height: 1.65; }
.plugin-hero .ph-lead code, .pb-text code { padding: 1px 6px; border-radius: var(--radius); background: var(--surface-2); color: var(--text); font: 500 .9em var(--mono); }
.plugin-hero .ph-lead strong { color: var(--text); }
.ph-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }
.ph-actions .btn, .hire-band .btn { display: inline-flex; align-items: center; gap: 8px; padding: 10px 16px; border: 1px solid var(--border-strong);
  border-radius: var(--radius); background: var(--surface); color: var(--text); font: 600 14px var(--sans); text-decoration: none; text-transform: none; letter-spacing: 0; }
.ph-actions .btn:hover { border-color: var(--accent); }
.ph-actions .btn.primary, .hire-band .btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.ph-actions .btn.primary:hover, .hire-band .btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.ph-actions .btn-icon { display: inline-flex; }
.ph-actions .btn-icon svg { width: 16px; height: 16px; }
.ph-stats { margin-top: 24px; grid-template-columns: repeat(4, minmax(0, 1fr)); }
/* 2026-10-05: cuadro de cifras ~55% mas chico en area (ancho y alto a ~2/3).
   Los selectores llevan .ph-stats para ganarle a .hdr-stat de shell.css, que
   carga despues, sin tocar las cifras de la home. */
.ph-stats { max-width: 390px; }
.ph-stats .hdr-stat { gap: 2px; padding: 10px 13px; }
.ph-stats .hdr-stat-num { font-size: 17px; }
.ph-stats .hdr-stat-label { font-size: 11.5px; }
.ph-media { overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); box-shadow: var(--shadow-lg); }
.ph-media img, .ph-media video { display: block; width: 100%; height: auto; }
.plugin-body { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); align-items: start; gap: 16px; margin-top: 24px; }
.pb-card { padding: 24px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); }
.pb-title { display: flex; align-items: center; gap: 10px; margin: 0 0 12px; color: var(--text); font: 700 18px var(--sans); letter-spacing: 0; text-transform: none; }
.pb-text { color: var(--text-dim); font-size: 15.5px; line-height: 1.7; }
.pb-facts .facts-list { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }
.pb-facts .facts-list li { display: flex; justify-content: space-between; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--border); font: 400 14px var(--sans); }
.pb-facts .facts-list li:last-child { border-bottom: 0; }
.pb-facts .fk { color: var(--text-faint); text-transform: none; letter-spacing: 0; font: 400 14px var(--sans); }
.pb-facts .fv { display: inline-flex; align-items: center; gap: 6px; min-width: 0; color: var(--text); font: 600 14px var(--sans); text-align: right; overflow-wrap: anywhere; }
.pb-facts .share-row { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border); }
.share-label { font: 600 12px var(--sans); letter-spacing: .06em; }
.share-btn { font-family: var(--sans); }
.similar-section { margin-top: 32px; }
h2.similar-title { margin: 0 0 16px; color: var(--text); font: 700 22px var(--sans); letter-spacing: 0; text-transform: none; }
.similar-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }
.similar-card { display: flex; flex-direction: column; gap: 6px; padding: 18px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow); color: var(--text); text-decoration: none; transition: border-color .15s ease; }
.similar-card:hover { border-color: var(--accent); }
.similar-card .card-cat { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: var(--radius);
  color: var(--cat); background: color-mix(in srgb, var(--cat) 12%, var(--surface)); }
.similar-card .card-cat svg { width: 18px; height: 18px; }
.sc-name { font: 600 15px var(--sans); }
.sc-niche { color: var(--text-faint); font: 500 13px var(--sans); text-transform: none; letter-spacing: 0; }
.sc-dl { margin-top: auto; color: var(--text-dim); font: 500 13px var(--sans); }
.plugin-asof { margin: 20px 0 0; color: var(--text-faint); font-size: 13px; }
/* 2026-10-05: menos aire entre los relacionados y el footer (shell.css le da
   72px de margen al footer en todo el sitio; aca solo en las fichas). */
body .site-footer.gh-footer { margin-top: 24px; }
@media (max-width: 900px) {
  .plugin-hero.gh-page-header, .plugin-body { grid-template-columns: 1fr; }
  .ph-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* 2026-09-27: h2 semanticos (antes span/div) + banda de contratacion. */
h2.ib-title, h2.similar-title { margin: 0; font: inherit; font-weight: 700; color: inherit; }
.hire-band { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px;
  margin-top: 24px; padding: 20px 22px; border: 1px solid var(--accent); border-radius: var(--radius);
  background: var(--surface); }
.hire-band > div { display: flex; flex-direction: column; gap: 4px; }
.hire-band strong { color: var(--text); font-size: 16px; }
.hire-band span { color: var(--text-dim); }

/* ------------------------------------------------------------------
   Generado por pipeline/build_plugin_pages.py -- NO editar a mano.
   Ajustes minimos para la ficha estatica: en el catalogo el dossier es
   un panel dentro de una pagina, aca es la pagina entera, y las tarjetas
   "similar" pasan de <div> con JS a <a> reales.
   ------------------------------------------------------------------ */
.plugin-page .dossier-sheet { margin: 0; }
.plugin-page .dossier-card { animation: none; }
.crumbs {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-family: var(--mono); font-size: 12px; color: var(--text-faint);
  margin: 0 0 var(--space-4);
}
.crumbs a { color: var(--text-dim); text-decoration: none; }
.crumbs a:hover { color: var(--accent); }
.crumbs [aria-current="page"] { color: var(--text); }
.crumb-sep { opacity: .55; }
a.similar-card, a.tbl-similar-card { text-decoration: none; color: inherit; }
.dossier-back { text-decoration: none; }
.plugin-asof {
  font-family: var(--mono); font-size: 11.5px; color: var(--text-faint);
  margin: var(--space-4) 0 0;
}
