/* ============================================================================
 * FILTER — CSS del mòdul filter (A10-F1 2026-09-24, migrat de custom_override.css G66+G08)
 * Selectors acotats a `.filter`/`.button-filter`/`.filter-loader`, com a l'origen.
 * ============================================================================ */
@keyframes filter-spin { to { transform: rotate(360deg); } }

.filter p{ margin-bottom: 0.5rem; }
.button-filter{ cursor: pointer; border: 2px solid #DDD; border-radius: 5px; margin: 0 4px 4px 0; padding: 6px; }
.button-filter input{ display: none; visibility: hidden; }
/* [A10-F1] --button-primary no és cap variable de Style Options ni de :root: sense reserva queda
   invàlida i el navegador hi aplica el valor inicial de border-color (currentColor). La reserva
   explícita reprodueix exactament el mateix resultat, MESURAT amb getComputedStyle abans/després. */
.button-filter.active { border-color: var(--button-primary, currentColor); }
.filter-buttons{ margin-top: 20px; }
/* [A10-F1] `.filter .filter-title{pointer-events:none}` competia, per ordre de càrrega, amb
   responsive_override.css:51 (`.filter .filter-title{pointer-events:all}` a ≤991px), que venia
   DESPRÉS de custom_override.css i per tant guanyava en mòbil. moduls.css es carrega després de
   responsive_override.css, així que aquí cal acotar-la a ≥992px perquè el toggle mòbil no es trenqui. */
@media screen and (min-width: 992px) {
  .filter .filter-title { pointer-events: none; }
}
.filter .filter-title i{ transition: transform 0.3s ease-in-out; }
/* .rotate90 / .rotate90-2: classes protegides (vegeu panel/ajax/_iridian_ai_layer_helpers.php:5426,
   `props:['transform']`, `src:'bootstrap + custom_override.css'`) alternades per JS
   (custom_override.js:3603, `filterTogglerIcon.toggleClass('rotate90-2')`) sobre un element que ja
   porta `transform` per una altra via; l'!important és previ a aquesta migració i es conserva intacte. */
.rotate90{ transform: rotate(-90deg) !important; }
.rotate90-2{ transform: rotate(90deg) !important; }
/* [A10-F1] --main-color-1/-3 (Style Options) NO porten reserva a propòsit: són camps «A»
   (system-twig.css els OMET del tot amb {% if %} quan són buits — test_system_css_no_empty_decls.php
   §"cap camp A alimenta un var() amb fallback"). Afegir-hi un hex trencaria aquest contracte: quan
   el camp és buit el resultat ha de seguir sent «sense declarar», no un color fix. Es migren tal
   qual, com ja estaven a custom_override.css. */
.filter .filter-field > .btn.no-style-btn { display:flex; align-items:center; justify-content:space-between; gap:8px; padding:6px 0; width:100%; font-size:13px; font-weight:600; color:#444; text-decoration:none; }
.filter .filter-field > .btn.no-style-btn:hover { color:var(--main-color-3); }
.filter .filter-field > .btn.no-style-btn .cat-toggle-icon { transition:transform 0.25s ease; color:#888; flex-shrink:0; }
.filter .filter-field > .btn.no-style-btn:hover .cat-toggle-icon { color:var(--main-color-1); }
.filter .filter-field > .btn.no-style-btn[aria-expanded="true"] .cat-toggle-icon { transform:rotate(180deg); }
.filter .cat-filter-group { margin-bottom: 4px; }
.filter .cat-filter-group .filter-field-content { padding-left:8px; }
/* background:none!important neutralitza el `.form-check{background:var(--…)}` de Bootstrap/SO
   sobre aquest checkbox pintat a mà; !important previ a la migració, es conserva intacte. */
.filter .cat-filter-group .form-check { padding:2px 0 2px 1.25rem; display:flex; align-items:center; gap:10px; background:none !important; }
/* display:inline-block!important neutralitza el reset natiu de l'input un cop se li treu
   l'aparença per pintar el checkbox a mà; !important previ a la migració, es conserva intacte. */
.filter .cat-filter-group .form-check input[type="checkbox"] { display:inline-block !important; width:20px; height:20px; appearance:none; -webkit-appearance:none; border:2px solid #ddd; border-radius:4px; cursor:pointer; position:relative; flex-shrink:0; background:#fff; transition:all 0.2s; }
.filter .cat-filter-group .form-check input[type="checkbox"]:checked { background:var(--main-color-3); border-color:var(--main-color-3); }
.filter .cat-filter-group .form-check .form-check-label { color:#555; font-size:14px; cursor:pointer; }
.filter .cat-filter-group .form-check:has(input:checked) .form-check-label { color:var(--main-color-3); font-weight:600; }
.item-list-box { position: relative; }
.filter-loader { position: absolute; inset: 0; background: rgba(255, 255, 255, 0.65); display: flex; align-items: center; justify-content: center; z-index: 10; backdrop-filter: blur(2px); }
.filter-loader__spinner { width: 36px; height: 36px; border: 3px solid rgba(0, 0, 0, 0.1); border-top-color: var(--button-primary, #333); border-radius: 50%; animation: filter-spin 0.7s linear infinite; }



/* ============================================================================
 * LLISTATS — paginació clàssica `.page-item`, miniatures i `fade-image`
 * (A10-F1 2026-09-24, migrat de custom_override.css G70+G74+G85)
 * ============================================================================ */
/* .pagination .page-item: la plantilla activa (themes/irtheme/list_structure/pagination.html) fa
   servir `.pagination-item` (secció PAGINADOR més avall), no `.page-item`. Aquesta classe és 0/38 al
   DOM rastrejat, però `system-twig.css:647` (`.page-item.active .page-link`) encara la dona per
   viva des de Style Options; es migra tal qual, sense verificar-la morta. */
.pagination .page-item:first-child {}
.pagination .page-item{ display:none; }
.pagination .page-item.accessible_page{ display:block; }
.pagination .page-item:last-child {}

.item-list .item .item-miniature img{ transition: all 0.3s ease-in-out; }
.item-list .item .item-miniature:hover img { filter: brightness(90%); }
.portfolio-thumb .img-fluid{ width: 100%; }
.item-list .item .item-miniature.fade-image a,
.post-miniature.fade-image a { display: flex; overflow: hidden; position: relative; }
.item-list .item .item-miniature.fade-image .img-main,
.post-miniature.fade-image .img-main { transition: opacity .5s ease, transform 2s cubic-bezier(0,0,.44,1.18); }
.item-list .item .item-miniature.fade-image .img-hover,
.post-miniature.fade-image .img-hover { position: absolute; inset: unset; opacity: 0; transition: opacity .5s ease, transform 2s cubic-bezier(0,0,.44,1.18); }
.item-list .item .item-miniature.fade-image:hover .img-main,
.post-miniature.fade-image:hover .img-main { opacity: 0; }
.item-list .item .item-miniature.fade-image:hover .img-hover,
.post-miniature.fade-image:hover .img-hover { display: block; z-index: 0; opacity: 1; transform: scale(1.09); width: 100%; height: 100%; }

/*Slider*/
#Sliders-owl.owl-carousel.owl-drag .owl-item {
  margin: 0;
  padding-bottom: 0;
}

.owl-carousel .owl-stage {
  display: flex;
}

.owl-carousel.owl-drag .owl-item {
  padding-bottom: 25px;
}

#Sliders-owl.owl-carousel .owl-nav button {
  border: none;
}

.owl-carousel .owl-nav button {
  position: absolute;
  width: 44px;
  height: 44px;
  line-height: 0;
  font-size: 40px;
  top: 0;
}

.owl-carousel .owl-nav button span {
  position: absolute;
  height: 44px;
  width: 44px;
  top: 0;
  left: 0;
  line-height: 36px;
}

#Sliders-owl.owl-carousel .owl-nav {
  position: absolute;
  z-index: 1000;
  width: 100%;
  height: 50px;
}

#Sliders-owl.owl-carousel .owl-nav button.owl-prev {
  left: 30px;
}

#Sliders-owl.owl-carousel .owl-nav button.owl-next {
  right: 30px;
}


#Sliders-owl.owl-carousel .owl-nav button:hover {
  opacity: 0.6;
}

#Sliders-owl.owl-dots {
  position: absolute;
  z-index: 20;
  top: 30px;
  left: 0;
  right: 0;
}

#Sliders-owl {
  margin-bottom: 60px;
}

#Sliders-owl.owl-carousel .owl-item img.modulimatge {
  margin: 0;
}

#contentslider h1,
#contentslider h2,
#contentslider h3,
#contentslider h4,
#contentslider h5 {
  font-weight: 300;
}

#contentslider .caption .btn {
  border: 1px solid ;
  padding: 8px 35px;
  text-transform: uppercase;
  font-size: 26px;
  margin-top: 15px;
}

#Sliders-owl {
  display: flex;
  align-items: center;
}


#contentslider {
  align-items: center;
}

.caption {
  position: absolute;
}

a.disabled {
  pointer-events: none;
  opacity: 0.6;
}

#Sliders-owl .row {
  margin-left: 0;
  margin-right: 0;
}

#Sliders-owl {
  margin-bottom: 0;
}

#Sliders-owl .portfolio-thumb {
  margin: 0;
  width: 100%
}

#Sliders-owl .caption h2 {
  font-size: min(max(16px, 3.8vw), 60px);
}

#Sliders-owl .caption h3 {
  font-size: min(max(16px, 3.8vw), 60px);
}

@media only screen and (max-width: 767.98px) {
  #Sliders-owl .owl-dots {
    z-index: 20;
    bottom: 0px;
  }

  .post-thumbnail .postthumb {
    width: 100%;
  }

  .single-post-details__header .blog-post-head h2 {
    padding: 0;
  }
}

/* fi slider*/
/*Sliderbanner*/

/*Sliderbanner*/
#Sliders-owl .caption .titleslider {
  font-size: min(max(20px, 6vw), 60px);
}


#Sliders-owl .owl-dots {
  width: 200px;
  left: 0;
  right: 0;
  position: absolute;
  bottom: 35px;
  margin: 0 auto;
  display: flex;
  justify-content: center;
}

.owl-dots {
  margin: 0 auto;
  display: flex;
  justify-content: center;
}

.owl-theme .owl-dots .owl-dot span {
  width: 15px;
  height: 15px;
  display: inline-block;
}


.owl-carousel button.owl-dot {
  margin: 0 3px;
}

.owl-carousel button.owl-dot:focus {
  outline: none;
}

/*Fi slider*/



/*Newsletter*/
#newsletter {
  padding: 60px 0;
}

#newsletter input {
  border: none;
  font-size: 12px;
}

#newsletter .legal {
  text-align: center;
  display: block;
  margin-top: 15px;
}

/* End newsletter*/




/* PAGINADOR */
/* Contenedor general */
.pagination-box {
    display: flex;
    justify-content: center;
    /* [U1.1 · COMPAT 2026-09-24] --pagination-margin-y només es declara (0) amb margin_top_s buit
       (system-twig.css), el 0 que pintava abans; amb valor, segueix margin_top_s com sempre. */
    margin: var(--pagination-margin-y, var(--margin-top-s)) 0;
}

/* Lista */
.pagination {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    gap: 10px;
}

/* Botones */
.pagination-item:not(.accessible_page){
  display: none;
}

.pagination-item--link {
    display: flex;
    align-items: center;
    justify-content: center;

    min-width: 38px;
    height: 38px;

    text-decoration: none;
}

/* Hover */
.pagination-item--link:hover {
}

/* Activa */
.pagination-item.active_page .pagination-item--link {
    color: white;
    cursor: default;
}

/* Accesibilidad */
.pagination-item--link:focus {
}


/* Responsive */
@media only screen and (max-width: 991px) {
    .pagination {
        gap: 5px;
    }

    .pagination-item--link {
        min-width: 32px;
        height: 32px;
    }
}

/* PAGINADOR */

/* SWIPER ARROWS */

.swiper-pagination-bullet-active:hover,.swiper-pagination-bullet-active { opacity: 1; background-color: #fff; width: 40px; box-shadow: 0 2px 8px 0 rgba(0,0,0,0.1); -webkit-transition: all 200ms linear; transition: all 200ms linear; width: 20px; height: 20px; }
.swiper-pagination-bullet { width: 20px; height: 20px; border-radius: 50%; }
.swiper-horizontal>.swiper-pagination-bullets,.swiper-pagination-bullets.swiper-pagination-horizontal,.swiper-pagination-custom,.swiper-pagination-fraction { bottom: 10px; left: 0; width: 100%; }
.swiper-pagination { position: absolute; text-align: center; transition: .3s opacity; transform: translate3d(0,0,0); z-index: 10; }

.swiper .swiper-button-next { right: 15px; }
.swiper .swiper-button-prev { left: 15px; }
.swiper .swiper-button { filter: drop-shadow(0px 0px 8px rgba(0, 0, 0, 0.1)); border-radius: 100px; position: absolute; z-index: 2; opacity: 0; transition: all 0.3s ease-in-out; margin-top: 0; padding: 10px; }
.swiper .swiper-button::after{ content: unset; }
.swiper .swiper-button svg{ min-width: 100%; }
.swiper:hover .swiper-button:not(.swiper-button-disabled) { opacity: 1; }
.swiper:hover .swiper-button.swiper-button-disabled { opacity: 0.5; }
/* SWIPER ARROWS */


.miniature-content{
  display: flex;
  overflow: hidden;
  position: relative;
}

/* ==== MODULS-B · 00 · PRIMITIVES — BEGIN (fase 1 core; no editar fora del grup) ==== */
/* [MODULS-B · G0b 2026-09-25 · P5] Primitives compartides de botó i formulari (SPEC_MODULS_B.md
   §4 P5). Van DESPRÉS de Bootstrap (A4b encara no fet) i ABANS dels grups GA…GF, que només hi
   col·loquen el contenidor (alineació, fila d'enviament, gap): no pinten .btn/.btn-brand ni
   .form-control. En mode core, aquesta primitiva dona l'aspecte nou; amb Estils, `system.css`
   (`.btn:not(.no-style-btn *):not(.no-style-btn)` (0,3,0) i `.form-control`, carregat després)
   hi passa per sobre sense cap canvi. Cap variable d'Estils: només tokens del core. */

.btn.btn-brand {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  padding: var(--space-2xs) var(--space-s);
  border: 1px solid transparent;
  border-radius: var(--ial-r-md);
  background-color: var(--bs-primary);
  color: var(--ial-c-text-inverse);
  font-weight: 600;
  text-decoration: none;
  transition: background-color var(--ial-t-fast), border-color var(--ial-t-fast);
}
.btn.btn-brand:hover {
  background-color: color-mix(in srgb, var(--bs-primary) 88%, var(--ial-c-surface));
  color: var(--ial-c-text-inverse);
}
/* [MODULS-B · FINE-1 · P0-6] Bootstrap `.btn:focus{outline:0}` (0,2,0) deixava la primitiva sense
   cap anell de focus visible. (0,2,0), guanya per ordre de càrrega (moduls.css va després de
   Bootstrap).
   [MODULS-B · T2, revisió Opus 25/09] Bootstrap 5.0.2 també declara, al MATEIX selector,
   `box-shadow: 0 0 0 .25rem rgba(13,110,253,.25)` (0,2,0): sense neutralitzar-lo, tornava a
   pintar un segon halo (blau) sobre el CTA, DARRERE de l'`outline` d'aquí. `box-shadow:none` fa
   que hi hagi un sol pintor (l'`outline`) — mateix patró que ja s'aplica a `.form-control:focus-
   visible` (MB-O, més avall).
   [MODULS-B · S1 · T1, ampliació de D-C aprovada per l'ORQ 25/09] `var(--ia-color-focus-ring,
   var(--bs-primary))`: en core, sense cap redeclaració, cau al fallback `--bs-primary` (mateix
   comportament d'abans). Dins del vel dels banners/parallax (surface_contexts de GD), on es
   redeclara `--ia-color-focus-ring` a `var(--ial-c-text-inverse)`, el CTA hereta aquest valor
   SENSE cap truc d'especificitat (custom property heretada) — abans N-02 no hi arribava mai (la
   primitiva `.btn.btn-brand:focus-visible`/l'Estils `.btn:not(...):focus-visible` tenen més pes
   que el redeclarador bare `:focus-visible` d'N-02): 1,05:1 real al CTA. Amb Estils, el mateix
   token ja és el que consumeix `ST:718` — cap canvi de comportament fora del vel. */
.btn.btn-brand:focus-visible {
  outline: 2px solid var(--ia-color-focus-ring, var(--ial-c-focus));
  outline-offset: 2px;
  box-shadow: none;
}
@media (prefers-reduced-motion: reduce) {
  .btn.btn-brand {
    transition: none;
  }
}

.form-control,
.form-select {
  border: 1px solid color-mix(in srgb, currentColor 14%, transparent);
  border-radius: var(--ial-r-md);
  padding: var(--space-2xs) var(--space-xs);
  background-color: transparent;
  color: inherit;
}
/* [MODULS-B · FINE-1 · P0-6 · MB-O] Focus visible propi del control (Bootstrap/FR l'anul·len amb
   `outline:0` en algunes capes). `box-shadow:none` neutralitza el de Bootstrap
   `.form-control:focus{box-shadow:...}` (0,1,1 pseudo-classe): sense això quedaven DOS anells
   alhora, l'`outline` d'aquí i el `box-shadow` de Bootstrap. */
.form-control:focus-visible {
  outline: 2px solid var(--ial-c-focus);
  outline-offset: 2px;
  box-shadow: none;
}
.form-check-input {
  border: 1px solid color-mix(in srgb, currentColor 14%, transparent);
  accent-color: var(--bs-primary);
}
/* [MODULS-B · G0b 2026-09-25 · §2.7-bis punt 5] .btn-fluid ja existeix a system-twig.css:762
   (`.btn-fluid:not(.no-style-btn *):not(.no-style-btn){ width: 100%; display: block; }`); aquesta
   és la primitiva compartida en mode core, sense cap variable d'Estils. */
.btn.btn-fluid {
  display: flex;
  width: 100%;
}
/* ==== MODULS-B · 00 · PRIMITIVES — END ==== */

/* ==== MODULS-B · GA · TEXT I DADES — BEGIN (fase 1 core; no editar fora del grup) ==== */
/* [MODULS-B · GA 2026-09-25] Remaquetat de zero, fase 1 = només core (SPEC_MODULS_B.md §1, §5 GA).
   Direcció D1: clar i neutre, radi 8px (--ial-r-md), sense ombres per defecte, un sol accent
   (--bs-primary), mèdia sense radi. Cap variable d'Estils, cap color literal i 0 marcadors
   d'importància forçada (llevat del cas MB-EXC ja documentat a §2.5/§2.6). TitleList (`.title`)
   no té regla pròpia:
   `.title` també és classe interna de Banners/IconText i sortiria del seu abast (§5 GA). */

/* --- Ritme vertical (§1.2): l'arrel no aporta marge exterior; el dona el Grid --- */
.title_wrapper,
.titletext,
.content-text,
.contentext,
.legacy-html,
.legacy-animatedtitle,
.legacy-animatedphrase,
.animatednumber,
.progressbar,
.splitter,
.line,
.product-list:where(.legacy-customlist),
.swiper:where(.legacy-customlist) {
  margin-block: 0;
}
.title_wrapper > :where(:first-child),
.titletext > :where(:first-child),
.content-text > :where(:first-child),
.contentext > :where(:first-child),
.legacy-html > :where(:first-child),
.product-list:where(.legacy-customlist) > :where(:first-child) {
  margin-block-start: 0;
}
/* [MODULS-B · P1-2 2026-09-25] `:where(:last-child)` desembolica a `p` quan el darrer fill és un
   paràgraf: sense el `:where(:not(p))`, aquesta regla trencava la reserva de `margin-bottom` de
   `p` a §2.7-bis (treia el marge del darrer paràgraf a totes les pàgines, també al footer). Es
   limita a elements que NO siguin `p`; el darrer `p` es queda amb el `margin-bottom` d'Estils. */
.title_wrapper > :where(:last-child):where(:not(p)),
.titletext > :where(:last-child):where(:not(p)),
.content-text > :where(:last-child):where(:not(p)),
.contentext > :where(:last-child):where(:not(p)),
.legacy-html > :where(:last-child):where(:not(p)),
.product-list:where(.legacy-customlist) > :where(:last-child):where(:not(p)) {
  margin-block-end: 0;
}

/* --- Title (.title_wrapper): res de mida ni color (primitiva, D2); text-wrap:balance --- */
.title_wrapper :where(.title) {
  text-wrap: balance;
}

/* --- TitleText (.titletext): títol i text separats amb --space-xs --- */
.titletext :where(.titletext__title) {
  margin-block-end: var(--space-xs);
}

/* [MODULS-B · FINE-1 · P0-10 · decisió (g)] `justify` = `start` (inici): BS5 no té `.text-justify`
   (la va treure), així que Title (`text-{{align}}` a `.title_wrapper`, title.html) no feia res
   explícit amb `align=justify`; TitleText porta `{{align}}` NU sobre el propi títol
   (`titletext__title`, titletext.html). TitleList (`{{align}}` nu sobre `.title` mateix,
   titlelist.html, SENSE cap embolcall propi) es queda fora d'abast aquí, com ja documenta el
   comentari de dalt (`.title` és classe compartida amb Banners/IconText, MB-B ho prohibeix com a
   arrel bar sense qualificador: `$MB_GENERIC_FORBIDDEN_ROOTS`). IconText/ImageText tenen la seva
   pròpia regla equivalent, arrelada a `.icontext` (GC, més avall). Sense cap marcador
   d'importància forçada: si mai Estils declara `text-align` sobre `.title`, Estils mana (decisió c). */
.title_wrapper:where(.text-justify) {
  text-align: start;
}
.titletext :where(.justify) {
  text-align: start;
}

/* [MODULS-B · GA · FINE-J2 2026-09-29, actualitzat J3b · FIX P2 2026-09-29] Consumidor de
   `io_align` (SPEC_INSTANCE_OPTIONS.md §4.1, §6.1, §6.3): `data-io-align` a `.title_wrapper`
   (emès per `iridian_io_root_attrs`, `structure.php`). Align=1/2 continuen emetent
   `text-right`/`text-center` (BS, marcat forçat) EN PARAL·LEL a `data-io-align`: mateix valor,
   sense conflicte (BAIX risc, CENS_J2.md §1).
   [FIX P2] L'ADAPTADOR legacy ara resol Align=0 -> `start` (abans `''`, «hereta» — el panell
   nou mentia: marcava «Hereda» mentre `title.php` sempre pinta `text-left`, revisió orquestrador
   29/09). Aquesta regla `[data-io-align="start"]`, ABANS inert (Align=0 mai hi arribava), ara SÍ
   rep l'atribut — però `title.html` segueix sense tocar-se i segueix emetent `text-left` (BS,
   marcat com a forçat, prioritat màxima a `bootstrap4.min.css`): SEMPRE guanya sobre aquest
   `text-align:start` (0 d'especificitat, `:where()`, sense aquell marcat). Zero canvi visual,
   confirmat: `test_align_inherit_option.php` (FINE-E1, assert D/E02) segueix intacte i en verd.
   `text-align:start` == `text-align:left` en LTR, així que encara que mai guanyés, tampoc
   canviaria res. */
.title_wrapper:where([data-io-align="start"]) {
  text-align: start;
}
.title_wrapper:where([data-io-align="center"]) {
  text-align: center;
}
.title_wrapper:where([data-io-align="end"]) {
  text-align: end;
}

/* Consumidor de `io_align` de TitleText (`data-io-align` a `.titletext`). Des de U5 la plantilla
   ja no emet cap classe d'alineació sobre `.titletext__title`: aquesta regla és l'únic mecanisme
   d'alineació de TitleText (l'alineació legacy 0/1/2 arriba via l'adaptador `iridian_io_root_attrs`). */
.titletext:where([data-io-align="start"]) :where(.titletext__title) {
  text-align: start;
}
.titletext:where([data-io-align="center"]) :where(.titletext__title) {
  text-align: center;
}
.titletext:where([data-io-align="end"]) :where(.titletext__title) {
  text-align: end;
}

/* --- Text (.content-text) i InputText (.contentext): ritme de text ric --- */
.content-text :where(ul),
.content-text :where(ol),
.contentext :where(ul),
.contentext :where(ol) {
  padding-inline-start: var(--space-s);
}
.content-text :where(blockquote),
.contentext :where(blockquote) {
  margin: 0;
  padding-inline-start: var(--space-s);
  border-inline-start: 2px solid color-mix(in srgb, currentColor 14%, transparent);
}
.content-text :where(hr),
.contentext :where(hr) {
  border-color: color-mix(in srgb, currentColor 14%, transparent);
}
.content-text :where(img),
.contentext :where(img) {
  max-width: 100%;
}
/* [MB-EXC] guanya l'`height` inline que l'editor de text posa a la imatge (abans a
   custom_override.css:708, buidat per G0a) */
.contentext :where(img) {
  height: auto !important;
}

/* --- Html (.legacy-html): contingut lliure, només el marge del primer/últim fill (ja escrit dalt) --- */

/* --- AnimatedTitle (.legacy-animatedtitle): com Title --- */
.legacy-animatedtitle {
  text-wrap: balance;
}

/* --- Animatedphrase (.legacy-animatedphrase): subratllat amb l'accent, sense fons groc.
   Especificitat (0,2,0), calculada, per guanyar el `.mark{background-color:white}` inline de la
   plantilla (mateixa especificitat que un sol .mark, però moduls.css hi guanya per selector més
   específic, no per ordre de càrrega). El keyframe `reveal` i el marcador d'importància forçada
   que porta `left` són de la plantilla i no es toquen. */
.legacy-animatedphrase .mark {
  background-color: transparent;
  border-block-end: 2px solid var(--bs-primary);
  height: var(--ial-sp-1);
}

/* --- AnimatedNumber (.animatednumber): xifra i llegenda ---
   [MODULS-B · FINE-1 · MB-M 25/09] `font-size`/`font-weight`/`line-height` de `.animatednumber__number`
   i `font-size`/`margin-top` de `.animatednumber__text` eren codi mort: la plantilla els fixa amb
   `<style>#{{idElement}} .animatednumber__number{font-size:3rem;font-weight:700;line-height:1.1}`
   i `#{{idElement}} .animatednumber__text{margin-top:.5rem;font-size:1rem}`
   (animatednumber.html:54-65), (1,1,0), que sempre guanya (`inline_owned`, GA.json). Es retiren. */
.animatednumber :where(.animatednumber__number) {
  font-variant-numeric: tabular-nums;
}
.animatednumber :where(.animatednumber__prefix),
.animatednumber :where(.animatednumber__suffix) {
  font-size: var(--step-1);
}
/* [MODULS-B · FINE-1 · MB-M 25/09] `color-mix(currentColor 72%)` sumat a l'`opacity:.75` inline de
   la mateixa plantilla (`animatednumber.html:63`) compondria una opacitat efectiva ~54% (contrast
   ≈3,7:1, per sota de 4,5:1). En treure el color d'aquí, la llegenda es queda amb el to atenuat que
   ja dona l'`opacity` inline, sense doblar el desdibuixat. */

/* --- ProgressBar (.progressbar): "TRENCAT" (caixa blanca, contrast 2,2). Sense .card blanca:
   superfície tènue pròpia (peça agrupadora, §1.3); etiqueta amb --step--1 i space-between; pista
   de 8px (--ial-sp-2) amb --ial-r-pill; el farcit cedeix sempre al color inline (colourTitle) i
   només hi cau el primary quan colourTitle és buit (§2.6, exemple literal de l'espec). */
.progressbar {
  background-color: color-mix(in srgb, currentColor 5%, transparent);
  border-color: color-mix(in srgb, currentColor 14%, transparent);
  border-radius: var(--ial-r-md);
  padding: var(--space-xs) var(--space-s);
}
/* [MODULS-B · FINE-1 · P0-5 · MB-L] Abans `:where(.progressbar > p:first-child)` era base (0,0,0):
   perdia contra qualsevol regla d'element global de `p`. `:where(.progressbar) > p` és (0,0,1),
   empata amb Bootstrap i hi guanya per ordre; amb Estils, `main p` (0,0,2) mana. `display:flex` +
   `space-between` es treuen (D, §I.2 P1): la caixa només té un `p` (sense segon fill), així que
   l'espaiat entre dos fills mai actuava. */
:where(.progressbar) > p {
  margin-block-end: var(--space-2xs);
  font-size: var(--step--1);
}
.progressbar :where(.list-group) {
  display: block;
  min-block-size: var(--ial-sp-2);
  border-radius: var(--ial-r-pill);
  background-color: var(--bs-primary);
  overflow: hidden;
}

/* --- Splitter (.splitter) i Line (.line): res més que el marge inline de la instància --- */
.line :where(hr) {
  opacity: 1;
  border-color: color-mix(in srgb, currentColor 14%, transparent);
}

/* --- CustomList (.product-list / .swiper, tots dos qualificats amb :where(.legacy-customlist) —
   [MODULS-B · P0-2 2026-09-25] `.product-list` sol també l'agafen els llistats reals del lloc
   (custom_listado_category.html, list_structure/list.html → legacy-list-productos/legacy-list-blog),
   que l'espec deixa fora d'aquest grup; el qualificador limita l'arrel al mòdul CustomList):
   "TRENCAT" (miniatures 404, títols subratllats). Targetes amb superfície tènue, radi
   --ial-r-md i --ial-shadow-sm al hover; títol enllaçat sense subratllat (només al hover);
   miniatura amb aspect-ratio 4/3 i object-fit cover, amb superfície tènue de reserva quan la
   imatge falla. */
.product-list:where(.legacy-customlist) :where(.item),
.swiper:where(.legacy-customlist) :where(.swiper-slide) {
  background-color: color-mix(in srgb, currentColor 5%, transparent);
  border-radius: var(--ial-r-md);
  transition: box-shadow var(--ial-t-fast);
}
.product-list:where(.legacy-customlist) :where(.item:hover),
.swiper:where(.legacy-customlist) :where(.swiper-slide:hover) {
  box-shadow: var(--ial-shadow-sm);
}
@media (prefers-reduced-motion: reduce) {
  .product-list:where(.legacy-customlist) :where(.item),
  .swiper:where(.legacy-customlist) :where(.swiper-slide) {
    transition: none;
  }
}
.product-list:where(.legacy-customlist) :where(a),
.swiper:where(.legacy-customlist) :where(a) {
  text-decoration: none;
}
.product-list:where(.legacy-customlist) :where(a:hover),
.swiper:where(.legacy-customlist) :where(a:hover) {
  text-decoration: underline;
}
.product-list:where(.legacy-customlist) :where(.post-details),
.product-list:where(.legacy-customlist) :where(.post-content),
.swiper:where(.legacy-customlist) :where(.post-details),
.swiper:where(.legacy-customlist) :where(.post-content) {
  padding: var(--space-2xs) var(--space-xs);
}
.product-list:where(.legacy-customlist) :where(img),
.swiper:where(.legacy-customlist) :where(img) {
  display: block;
  inline-size: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background-color: color-mix(in srgb, currentColor 5%, transparent);
}
/* ==== MODULS-B · GA · TEXT I DADES — END ==== */

/* ==== MODULS-B · GB · ACCIONS I ICONES — BEGIN (fase 1 core; no editar fora del grup) ==== */
/* [MODULS-B · GB 2026-09-25] Button/ButtonLink/ButtonSheet, Download/DownloadSheet,
   ButtonComercialDigital, SharePage, SocialNetworks/SocialNetworksSheet, FloatingSocialIcon,
   IconPicker (SPEC_MODULS_B.md §5 GB). D1: clar i neutre, radi 8 px, sense ombres, un sol accent.
   D5: icones de xarxes/compartir monocromes del color del text (currentColor/inherit; mai un color
   de marca fix). La primitiva `.btn.btn-brand` i `.form-control` són de `00 · PRIMITIVES` (G0b, P5):
   aquest grup només n'afegeix el contenidor/alineació. */

/* ---- Button / ButtonLink / ButtonSheet (modules/button.php) ---- */
/* [MODULS-B · GB · FINE-J2 2026-09-29] Consumidor de `io_align` (SPEC_INSTANCE_OPTIONS.md §4.1,
   §6.1, §6.3): `data-io-align` a `.btn-wrapper` (`button.php:57`, `wrapperClass` NOMÉS es genera
   quan `alignment !== 'left'`, mateixa clau que llegeix l'adaptador legacy de `io_align` —
   CENS_J2.md §5). Mateix valor que la classe `align-center`/`align-right` d'amunt: conviuen sense
   conflicte (BAIX risc). `start` (=left) avui mai té wrapper (sense atribut emès, `''`=hereta):
   la regla es deixa preparada per quan un `io_align="start"` explícit calgui. `io_size` (Button)
   QUEDA FORA d'aquesta unitat: l'àncora (`.btn-wrapper` vs `.btn.btn-brand`, root de
   `00 · PRIMITIVES`) és ambigua i qualsevol regla amb propietats de mida tocaria «aspecte del
   .btn», prohibit dins `[data-io-` (§6.2) — decisió pendent, escalada a l'orquestrador. */
.btn-wrapper:where([data-io-align="start"]) {
  display: flex;
  justify-content: flex-start;
}
.btn-wrapper:where([data-io-align="center"]) {
  display: flex;
  justify-content: center;
}
.btn-wrapper:where([data-io-align="end"]) {
  display: flex;
  justify-content: flex-end;
}

/* `.btn.btn-fluid` és la primitiva compartida (00 · PRIMITIVES, §2.7-bis punt 5): aquest grup no
   en repeteix la declaració. */

/* Variants pròpies `.btn-white`/`.btn-black` (§2.7-bis punt 4: capa Mòdul/Grid, `variants` al
   fixture): outline blanc/negre de les classes antigues de contingut desat (§4 G0a;
   `custom_override.css:293-296`, ara buidades, «es conserven perquè són al contingut desat»).
   Subjecte `.btn.btn-white`/`.btn.btn-black` (≤(0,2,0)) dona l'aspecte al core amb tokens (D1/D5,
   sense colors fixos). A més REDECLAREN (mai consumeixen) les custom properties de botó del core
   (`--button-*`, i les de `header`/`footer`, consumides per `.btn` dins `header`/`footer` a
   system-twig.css:715, 769, 877) amb `transparent`/tokens del core, perquè guanyin també quan
   Estils pinta el `.btn` amb `var(--button-bg-color)` — si no, Estils tornaria a pintar el botó amb
   el color de marca en comptes del blanc/negre desat. */
.btn.btn-white {
  background-color: transparent;
  border-color: var(--ial-c-text-inverse);
  color: var(--ial-c-text-inverse);
  --button-bg-color: transparent;
  --button-bg-color-hover: transparent;
  --button-color: var(--ial-c-text-inverse);
  --button-color-hover: var(--ial-c-text-inverse);
  --button-border-color: var(--ial-c-text-inverse);
  --button-border-color-hover: var(--ial-c-text-inverse);
  --header-button-bg-color: transparent;
  --header-button-bg-color-hover: transparent;
  --header-button-color: var(--ial-c-text-inverse);
  --header-button-color-hover: var(--ial-c-text-inverse);
  --header-button-border-color: var(--ial-c-text-inverse);
  --header-button-border-color-hover: var(--ial-c-text-inverse);
  --footer-button-bg-color: transparent;
  --footer-button-bg-color-hover: transparent;
  --footer-button-color: var(--ial-c-text-inverse);
  --footer-button-color-hover: var(--ial-c-text-inverse);
  --footer-button-border-color: var(--ial-c-text-inverse);
  --footer-button-border-color-hover: var(--ial-c-text-inverse);
}
.btn.btn-black {
  background-color: transparent;
  border-color: var(--ial-c-text-1);
  color: var(--ial-c-text-1);
  --button-bg-color: transparent;
  --button-bg-color-hover: transparent;
  --button-color: var(--ial-c-text-1);
  --button-color-hover: var(--ial-c-text-1);
  --button-border-color: var(--ial-c-text-1);
  --button-border-color-hover: var(--ial-c-text-1);
  --header-button-bg-color: transparent;
  --header-button-bg-color-hover: transparent;
  --header-button-color: var(--ial-c-text-1);
  --header-button-color-hover: var(--ial-c-text-1);
  --header-button-border-color: var(--ial-c-text-1);
  --header-button-border-color-hover: var(--ial-c-text-1);
  --footer-button-bg-color: transparent;
  --footer-button-bg-color-hover: transparent;
  --footer-button-color: var(--ial-c-text-1);
  --footer-button-color-hover: var(--ial-c-text-1);
  --footer-button-border-color: var(--ial-c-text-1);
  --footer-button-border-color-hover: var(--ial-c-text-1);
}
.btn.btn-white:hover,
.btn.btn-black:hover {
  background-color: color-mix(in srgb, currentColor 8%, transparent);
}
/* [MODULS-B · FINE-1 · P0-6] El hover de la primitiva (`00`, `.btn.btn-brand:hover`) pinta
   `color: var(--ial-c-text-inverse)` amb (0,3,0) — la mateixa especificitat que aquí. Sense
   repetir el color de cada variant, el hover de `.btn-black` quedava amb text blanc sobre el seu
   propi fons quasi transparent (il·legible). Aquestes dues regles van DESPRÉS de `00` al full, i
   guanyen l'empat per ordre. */
.btn.btn-white:hover {
  color: var(--ial-c-text-inverse);
}
.btn.btn-black:hover {
  color: var(--ial-c-text-1);
}

/* Download/DownloadSheet (modules/download.html, downloadsheet.html): mateixa arrel `.btn.btn-brand`
   i cap classe pròpia addicional (`btn-wrapper`/`btn-fluid` no s'hi apliquen). L'aspecte és 100% de
   la primitiva P5 (00): aquest grup no hi afegeix cap declaració. */

/* ---- ButtonComercialDigital (modules/buttoncomercialdigital.php) ---- */
/* El botó és la primitiva P5 (`.btn.btn-brand.btn-comercial-digital`); aquest grup només dissenya el
   modal amb el patró de flotant (§1.3): superfície pròpia, vora i ombra del core, scrim al darrere. */
.bcd-modal[hidden] { display: none; }
.bcd-modal {
  position: fixed;
  inset: 0;
  z-index: 9999;
}
.bcd-modal :where(.bcd-modal__overlay) {
  position: absolute;
  inset: 0;
  /* [MODULS-B · FINE-1 · orquestrador 25/09 · MB-F] recepta del vel a 64% (abans 55%), unificada
     amb GD i amb Video (GC). */
  background-color: color-mix(in srgb, var(--bs-dark) 64%, transparent);
}
.bcd-modal :where(.bcd-modal__dialog) {
  position: relative;
  max-width: min(90vw, 30rem);
  margin: var(--space-m) auto;
  padding: var(--space-m);
  background-color: var(--ial-c-surface);
  border: 1px solid var(--ial-c-border);
  border-radius: var(--ial-r-lg);
  box-shadow: var(--ial-shadow-md);
}
.bcd-modal :where(.bcd-modal__close) {
  position: absolute;
  top: var(--space-2xs);
  right: var(--space-2xs);
  border: 0;
  background-color: transparent;
  color: inherit;
  font-size: var(--step-1);
  line-height: 1;
  cursor: pointer;
}
.bcd-modal :where(.bcd-field) { margin-block-end: var(--space-xs); }
.bcd-modal :where(.bcd-field) > label { display: block; margin-block-end: var(--space-3xs); }
.bcd-modal :where(.bcd-required) { color: var(--bs-primary); }
.bcd-modal :where(.bcd-field__error) {
  font-size: var(--step--1);
  color: color-mix(in srgb, currentColor 72%, transparent);
}
.bcd-modal :where(.bcd-consent__item) {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2xs);
  margin-block-end: var(--space-2xs);
}
.bcd-modal :where(.bcd-actions) {
  display: flex;
  justify-content: flex-end;
  margin-block-start: var(--space-s);
}
.bcd-modal :where(.bcd-feedback) {
  font-size: var(--step--1);
  margin-block-start: var(--space-2xs);
}

/* ---- SharePage (modules/sharepage.html) ---- */
.share-page {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
}
.share-page :where(.share-title) {
  font-size: var(--step--1);
  color: color-mix(in srgb, currentColor 72%, transparent);
}
.share-page :where(.share-item) {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
}
.share-page :where(a) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  padding: var(--space-2xs);
  border-radius: var(--ial-r-pill);
  background-color: var(--ios-bg, color-mix(in srgb, currentColor 5%, transparent));
  border: 1px solid var(--ios-edge, color-mix(in srgb, currentColor 14%, transparent));
  text-decoration: none;
  transition: background-color var(--ial-t-fast);
}
/* [MODULS-B · FINE-1 · P0-5 · MB-L · S-07] `color`/`font-size` són reservats per l'element sol `a`
   (§2.7-bis punt 3, llindar ≤(0,0,1)). Abans el subjecte anava sencer dins de `:where(a)`, que és
   base (0,0,0): perdia contra `a{color}` (`custom_override.css:44`, (0,0,1)) i contra
   `a:where(:not(.btn)…)` (`main.css:84`, (0,0,1)), i les icones socials es pintaven amb l'accent
   en lloc de monocromes (D5). `:where(.share-page)` treu la classe de l'especificitat; `a` (fora
   del `:where()`) puja el subjecte a (0,0,1): empata amb `CO:44`/`main.css:84` i hi guanya per
   ordre de càrrega (moduls.css és posterior). Amb Estils, `main a` (0,0,2) continua manant. */
:where(.share-page) a {
  color: inherit;
  font-size: 1.125em;
}
.share-page :where(a):hover {
  background-color: color-mix(in srgb, currentColor 8%, transparent);
}

/* [J6 · io_variant] Sempre a la PEÇA `a` de la icona, mai l'arrel. boxed = cercle tènue d'avui (explícit,
   core_default); clean = icona sola (xapa i vora transparents; la vora conserva 1px perquè la mida no
   canviï). El :hover (8 %) es manté com a resposta. Només tokens --ios-* (T4 IOC-K). */
.share-page:where([data-io-variant="boxed"]) :where(a) {
  --ios-bg: color-mix(in srgb, currentColor 5%, transparent);
  --ios-edge: color-mix(in srgb, currentColor 14%, transparent);
}
.share-page:where([data-io-variant="clean"]) :where(a) {
  --ios-bg: transparent;
  --ios-edge: transparent;
}
.share-page :where(svg) {
  inline-size: 1em;
  block-size: 1em;
  fill: currentColor;
}

/* ---- SocialNetworks / SocialNetworksSheet (modules/socialnetworks.html) ---- */
.social-icons :where(.sociallist) {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  list-style: none;
  padding-inline-start: 0;
  margin-block: 0;
}
.social-icons :where(.sociallist) > li { margin: 0; padding: 0; }
.social-icons :where(a) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  padding: var(--space-2xs);
  border-radius: var(--ial-r-pill);
  background-color: var(--ios-bg, color-mix(in srgb, currentColor 5%, transparent));
  border: 1px solid var(--ios-edge, color-mix(in srgb, currentColor 14%, transparent));
  text-decoration: none;
  transition: background-color var(--ial-t-fast);
}
/* [MODULS-B · FINE-1 · P0-5 · MB-L] `color`/`font-size` reservats per l'element sol `a` (§2.7-bis
   punt 3): mateix bug i mateix fix que a SharePage — `:where(a)` era base (0,0,0); `a` fora del
   `:where()` puja el subjecte a (0,0,1). */
:where(.social-icons) a {
  color: inherit;
  font-size: 1.125em;
}
.social-icons :where(a):hover {
  background-color: color-mix(in srgb, currentColor 8%, transparent);
}

/* [J6 · io_variant] Sempre a la PEÇA `a` de la icona, mai l'arrel. boxed = cercle tènue d'avui (explícit,
   core_default); clean = icona sola (xapa i vora transparents; la vora conserva 1px perquè la mida no
   canviï). El :hover (8 %) es manté com a resposta. Només tokens --ios-* (T4 IOC-K). */
.social-icons:where([data-io-variant="boxed"]) :where(a) {
  --ios-bg: color-mix(in srgb, currentColor 5%, transparent);
  --ios-edge: color-mix(in srgb, currentColor 14%, transparent);
}
.social-icons:where([data-io-variant="clean"]) :where(a) {
  --ios-bg: transparent;
  --ios-edge: transparent;
}

/* ---- FloatingSocialIcon (modules/floatingsocialicon.html) ---- */
/* [MODULS-B · FINE-1 · P0-11] z-index: 9999 quedava per SOBRE de `.site-header` (main.css:1273,
   1030) i podia tapar l'hamburguesa a 390. Es baixa a 1020, per SOTA del header i de les galetes
   (`custom_override.css` ~342/`:343`, 10000/10000000000). [MODULS-B · FINE-1 · P0-comentari]
   Correcció: 1020 NO queda per sobre del panell mòbil de fet — `.hv2-mobile-panel` (z-index:999,
   main.css:1697) viu DINS de `.site-header`, que és el seu propi context d'apilament sencer
   (`position:sticky` + `z-index:1030`, main.css ~1273); comparat des de fora, tot aquest context
   (i per tant el panell mòbil, encara que el seu z-index intern sigui 999) es pinta SEMPRE per
   sobre d'un `.floating-icon` (1020) que en queda fora — el 999 intern és irrellevant en la
   comparació entre contextos. Els offsets sumen `env(safe-area-inset-*)` (E: T3/z literal, ja
   acceptat). */
.floating-icon {
  position: fixed;
  z-index: 1020;
  display: flex;
}
.floating-icon.top-left {
  top: calc(var(--space-m) + env(safe-area-inset-top, 0px));
  left: calc(var(--space-m) + env(safe-area-inset-left, 0px));
}
.floating-icon.top-right {
  top: calc(var(--space-m) + env(safe-area-inset-top, 0px));
  right: calc(var(--space-m) + env(safe-area-inset-right, 0px));
}
.floating-icon.bottom-left {
  bottom: calc(var(--space-m) + env(safe-area-inset-bottom, 0px));
  left: calc(var(--space-m) + env(safe-area-inset-left, 0px));
}
.floating-icon.bottom-right {
  bottom: calc(var(--space-m) + env(safe-area-inset-bottom, 0px));
  right: calc(var(--space-m) + env(safe-area-inset-right, 0px));
}
.floating-icon :where(.floating-icon-btn) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--fi-size, var(--space-l));
  block-size: var(--fi-size, var(--space-l));
  min-inline-size: 40px;
  min-block-size: 40px;
  padding: var(--fi-padding, var(--space-2xs));
  border-radius: var(--fi-border-radius, var(--ial-r-pill));
  background-color: var(--fi-bg-color, var(--ial-c-surface));
  /* [MODULS-B · FINE-1 · P0-11] Abans queia a `inherit`: sobre una banda fosca o al footer, el
     glif blanc de `--ial-c-surface` es tornava invisible (glif blanc sobre blanc, Diagnòstic 9).
     El flotant té ara color de text propi (§1.3), coherent amb la seva pròpia superfície. */
  color: var(--fi-icon-color, var(--ial-c-text-1));
  box-shadow: var(--ial-shadow-md);
  text-decoration: none;
}
@media (max-width: 575.98px) {
  .floating-icon :where(.floating-icon-btn) {
    min-inline-size: 44px;
    min-block-size: 44px;
  }
}
.floating-icon :where(svg),
.floating-icon :where(.floating-icon-svg-code) svg {
  inline-size: 1.25em;
  block-size: 1.25em;
  fill: currentColor;
}
/* Glif de reserva `<i class="icon …">` quan la instància no té SVG ni imatge: mida a `::before`
   (on Font Awesome pinta el glif) i color amb la variable d'instància `--fi-icon-color`
   (`module_vars`, §2.7-bis punt 4: configuració d'instància, reserva al core amb `currentColor`).
   [MODULS-B · S3 2026-09-28 · S-15] La reserva final era `currentColor` en dur: aquest selector
   (0,2,0) guanya SEMPRE `.icon{color:var(--icon-color)}` d'ST (0,1,0) per especificitat, així
   que `icon_color` d'Estils no hi arribava mai. Ara cau a `--ial-c-icon` (pontejat des
   d'`icon_color` a system-twig.css; `currentColor` només és el default del core). */
.floating-icon-btn :where(.icon)::before {
  font-size: var(--fi-icon-size, 1.25em);
}
.floating-icon-btn .icon {
  color: var(--fi-icon-color, var(--ial-c-icon));
}

/* ---- IconPicker (modules/iconpicker.html) ---- */
.legacy-iconpicker { display: inline-flex; }
.iconpicker-wrapper {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--ip-size, var(--space-l));
  block-size: var(--ip-size, var(--space-l));
  padding: var(--ip-padding, var(--space-2xs));
  border-radius: var(--ip-border-radius, var(--ial-r-pill));
  background-color: var(--ip-bg-color, var(--ip-variant-bg, color-mix(in srgb, currentColor 5%, transparent)));
  color: var(--ip-icon-color, inherit);
}

/* [J6 · io_variant] Sempre a la PEÇA .iconpicker-wrapper, mai l'arrel. boxed = xapa tènue d'avui (explícit,
   core_default); clean = icona sola. El color de fons triat a la instància (--ip-bg-color) mana sobre la
   variant. Només tokens --ip-* (T4 IOC-K). */
.legacy-iconpicker:where([data-io-variant="boxed"]) :where(.iconpicker-wrapper) {
  --ip-variant-bg: color-mix(in srgb, currentColor 5%, transparent);
}
.legacy-iconpicker:where([data-io-variant="clean"]) :where(.iconpicker-wrapper) {
  --ip-variant-bg: transparent;
}
.iconpicker-wrapper :where(svg) {
  fill: currentColor;
  stroke: currentColor;
}
/* Mateix cas que a FloatingSocialIcon: glif de reserva sense SVG/imatge, mida a `::before` i color
   amb la variable d'instància `--ip-icon-color` (`module_vars`).
   [MODULS-B · S3 2026-09-28 · S-15] Mateix fix que `.floating-icon-btn .icon`: reserva final a
   `--ial-c-icon` en lloc de `currentColor` en dur, perquè `icon_color` d'Estils hi arribi malgrat
   l'especificitat (0,2,0) d'aquest selector. */
.iconpicker-wrapper :where(.icon) {
  display: flex;
  align-items: center;
  justify-content: center;
  inline-size: 100%;
  block-size: 100%;
  line-height: 1;
}
.iconpicker-wrapper :where(.icon)::before {
  font-size: var(--ip-icon-size, calc(var(--ip-size, var(--space-l)) * .65));
}
.iconpicker-wrapper .icon {
  color: var(--ip-icon-color, var(--ial-c-icon));
}

@media (prefers-reduced-motion: reduce) {
  .btn.btn-white:hover,
  .btn.btn-black:hover,
  .share-page :where(a),
  .social-icons :where(a) {
    transition: none;
  }
}

/* ==== MODULS-B · GB · ACCIONS I ICONES — END ==== */

/* ==== MODULS-B · GC · MÈDIA — BEGIN (fase 1 core; no editar fora del grup) ==== */
/* [MODULS-B · GC 2026-09-25] Mèdia remaquetada de zero (SPEC_MODULS_B.md §5 GC, D1: clar i
 * neutre, mèdia SENSE radi — el dona el Grid amb cgv2-radius-*, sense ombres per defecte, un
 * sol accent --bs-primary). Roots: .image, .image-link, .icontext, .video, .ytplayer,
 * .legacy-map, .beforeafter, .instagram-feeds, .legacy-socialmediapost, .legacy-adsense.
 * NOTA (actualitzada amb §2.7-bis, 2026-09-25): `.icon` (IconText, compartida amb GB —
 * IconPicker, FloatingSocialIcon, SocialNetworks) és primitiva reservada a Estils amb llindar
 * ≤ (0,1,0), no ≤ (0,0,1) com a la versió anterior del contracte. Només se n'estila el
 * contenidor (cercle, superfície tènue), mai `font-size`/`color` directes sobre el glif.
 */

/* --- Image, ImageLink, ImageMultiLanguage (.image) --- */
.image,
.image-link {
    display: block;
}
.image picture,
.image img,
.image-link picture,
.image-link img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 0;
}
.image figcaption,
.image-link figcaption {
    margin-block-start: var(--space-2xs);
    font-size: var(--step--1);
    color: color-mix(in srgb, currentColor 72%, transparent);
}
.image-link:focus-visible {
    outline: 2px solid var(--ial-c-focus);
    outline-offset: 2px;
}

/* --- ImageSvg / ImageSvgLink: la icona manté la mida intrínseca, no la de .img-fluid/.fullwidth ---
   [MODULS-B · FINE-1 · P0-7] La regla anterior apuntava a `img` ((0,1,1): `.image` + descendant +
   `img`), però el markup real (`imagesvg.html`, `imagesvglink.html`) posa la classe `.img-fluid` a
   l'element (o a l'`<a>`/`<div>` embolcallador): `.portfolio-thumb .img-fluid{width:100%}`
   (`moduls.css:71`, (0,2,0)) hi guanyava sempre. El subjecte ara és `.img-fluid`: (0,2,0), empata
   amb `:71` i hi guanya per ordre de càrrega (aquesta secció és molt posterior al fitxer).
   `display:inline-block` torna a fer efectiu el `text-align` del Grid (l'SVG deixa de ser bloc).
   [MODULS-B · FINE-1 · P0-7 · RONDA 2 — regressió UI/UX 2619] `.img-fluid` a seques també casava
   amb l'`<a class="img-fluid">` embolcallador (`imagesvglink.html:3`): l'enllaç passava a
   `inline-block; width:auto` i, sense contingut amb mida encara (el seu `<img>` fill, que casa
   amb la MATEIXA regla, també era `width:auto`), s'encongia a 0 — cicle de mida buida (0x0,
   invisible). El subjecte es limita a l'`<img>` real (`:where(img).img-fluid`, base (0,2,0), no
   puja perquè `img` queda dins `:where()`): l'`<a>` embolcallador ja NO hi casa i es queda amb el
   seu `display:inline` per defecte, sense cap cicle de mida. */
.image:where(.image-svg) :where(img).img-fluid {
    display: inline-block;
    width: auto;
    max-width: 100%;
    height: auto;
}
/* ImageSvgLink (`imagesvglink.html:5`) no declara `width`/`height` a l'`<img>` (a diferència
   d'ImageSvg, `imagesvg.html:5`, que sí ho fa i per tant té mida intrínseca real): sense cap
   mida coneguda i amb un SVG que només té `viewBox` (p. ex. `home_icon*.svg`), `width:auto` es
   resol amb l'algorisme de mida per defecte del navegador (~300px), constret per `max-width:100%`
   — tornaria a ocupar tota la columna a mòbil, la mateixa regressió visual que evitava P0-7. Es
   limita l'ample a un valor de referència NOMÉS quan no hi ha `width` a l'HTML (ImageSvg2617 al
   viu: 196px).
   [MODULS-B · FINE-1 · P0-7 · RONDA 3] La reserva anterior (`inline-size`, subjecte dins
   `:where()`) pesava base/real (0,1,0) — `.image` sol, ja que `img` quedava embolcallat — i
   perdia SEMPRE contra `width:auto` de la regla d'amunt (real (0,2,0)), amb independència de
   l'ordre: `inline-size`/`width` competeixen com el mateix eix físic, i quan l'especificitat
   difereix guanya la més alta. Es reformula amb `.img-fluid` FORA de `:where()` (mateixa
   especificitat real (0,2,0) que la regla de `width:auto`) i amb `width` (el mateix eix físic):
   ara l'empat el desempata l'ordre de càrrega — aquesta regla és posterior. També cobreix el cas
   `width=""` que emet `imagesvg.html` quan el camp és buit (abans no casava amb `:not([width])`,
   perquè l'atribut hi és igualment present, buit). Una icona amb `width` propi (no buit) continua
   fora d'aquest selector i conserva la seva mida. */
.image:where(.image-svg) :where(img:not([width]), img[width=""]).img-fluid {
    width: min(196px, 100%);
}

/* --- ImageText: graella imatge + text (2 columnes ≥992px, imatge 40%; 1 columna a 390) --- */
.icontext:where(.legacy-imagetext) {
    display: grid;
    grid-template-columns: 1fr;
    align-items: center;
    gap: var(--space-s);
}
@media (min-width: 992px) {
    .icontext:where(.legacy-imagetext) {
        grid-template-columns: 40% 1fr;
    }
}
.icontext:where(.legacy-imagetext) > :where(.image) {
    margin: 0;
}

/* --- IconText / ImageText: espai intern comú del bloc de text (títol → text) --- */
.icontext :where(.feature-info) {
    display: flex;
    flex-direction: column;
    gap: var(--space-2xs);
}
/* [MODULS-B · FINE-1 · P0-10 · decisió (g)] `justify` = `start`, mateixa recepta que a GA:
   icontext.html/imagetext.html posen `{{align}}` nu sobre el propi títol («justify title»). */
.icontext :where(.title.justify) {
    text-align: start;
}

/* --- IconText: disposició (icona a sobre, o a l'esquerra amb .left) --- */
.icontext:where(.legacy-icontext) {
    display: flex;
    flex-direction: column;
    gap: var(--space-xs);
}
.icontext:where(.legacy-icontext).left {
    flex-direction: row;
    align-items: flex-start;
}

/* --- IconText / ImageText: consumidors de `io_align` i `io_media` (`data-io-*` a l'arrel `.icontext`) ---
   [MODULS-B · PILOT 2 · unitat 1] Fins ara `data-io-align` s'emetia (adaptador legacy Align 0/1/2) però cap
   regla el llegia. Sense atribut (Hereda, o `io_media` mai triat) no aplica cap d'aquestes regles: el cas per
   defecte queda byte-idèntic. Especificitat (0,2,0) (arrel + atribut fora de :where): així `io_media` guanya
   sobre el token de Grid `.left` de l'arrel (mateix (0,2,0), va després a la font). Ordre: align → media, perquè
   `io_media="start"` (fila) pugui refer l'`align-items` de la columna. Sense forçats d'importància, colors ni px. */
.icontext:where(.legacy-icontext)[data-io-align="start"],
.icontext:where(.legacy-imagetext)[data-io-align="start"] {
    text-align: start;
}
.icontext:where(.legacy-icontext)[data-io-align="center"] {
    text-align: center;
    align-items: center;
}
.icontext:where(.legacy-imagetext)[data-io-align="center"] {
    text-align: center;
}
.icontext:where(.legacy-icontext)[data-io-align="end"] {
    text-align: end;
    align-items: flex-end;
}
.icontext:where(.legacy-imagetext)[data-io-align="end"] {
    text-align: end;
}
/* IconText · io_media: `top` = icona a sobre (columna); `start` = icona a l'inici i el text al costat (fila). */
.icontext:where(.legacy-icontext)[data-io-media="top"] {
    flex-direction: column;
}
.icontext:where(.legacy-icontext)[data-io-media="start"] {
    flex-direction: row;
    align-items: flex-start;
}
/* ImageText · io_media: `start` = imatge a l'inici (defecte a ≥992px), `end` = imatge al final (només ≥992px;
   a mòbil la imatge sempre va a sobre), `top` = una sola columna a tots els amples. */
.icontext:where(.legacy-imagetext)[data-io-media="top"] {
    grid-template-columns: 1fr;
}
@media (min-width: 992px) {
    .icontext:where(.legacy-imagetext)[data-io-media="start"] {
        grid-template-columns: 40% 1fr;
    }
    .icontext:where(.legacy-imagetext)[data-io-media="end"] {
        grid-template-columns: 1fr 40%;
    }
    .icontext:where(.legacy-imagetext)[data-io-media="end"] > :where(.image) {
        order: 1;
    }
}
/* [SPEC_MODULS_B §2.7-bis] .icon és primitiva reservada a Estils NOMÉS per a `color`/`font-size`
   (llindar ≤ (0,1,0) per a aquestes dues propietats); `display`/mida/fons no hi són reservats, així
   que l'arrel pot anar literal (sense :where()), especificitat (0,2,0) — dins el sostre G1.
   [MODULS-B · GC, Xavi 25/09 punt 5] Calia pujar l'especificitat perquè, amb (0,1,0), aquesta
   regla empatava amb `[class*=" fa-"]`/`.fa-solid` de fa-core.css (també (0,1,0)): a l'empat
   guanya la darrera carregada al DOM (fa-core, després de moduls.css), i el glif quedava
   `display:inline-block` sense flex, sense centrar — no un problema de line-height. */
.icontext .icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    inline-size: 48px;
    block-size: 48px;
    border-radius: var(--ial-r-pill);
    background-color: color-mix(in srgb, currentColor 5%, transparent);
}
/* Glif (Font Awesome, ::before): mida/línia només, mai font-size ni color directes (Estils).
   Pseudoelement, fora de G2 (§2.7-bis); arrel dins :where() perquè el pseudoelement ja compta
   (0,0,1) i (0,2,0)+(0,0,1) superaria el sostre G1. */
:where(.icontext) .icon::before {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    margin: 0;
}

/* --- Video, VideoMP4, VideoIframe (.video) --- */
.video :where(.video-player__thumbnail) {
    position: relative;
    display: block;
    overflow: hidden;
}
.video :where(.video-player__img) {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-radius: 0;
}
.video :where(.video-player__overlay) {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}
.video :where(.video-player__overlay)::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    /* [MODULS-B · FINE-1 · orquestrador 25/09 · MB-F] 64% (abans 55%), recepta única del vel a tot
       arreu fora de GD (banners/parallax ja hi són). */
    background-color: color-mix(in srgb, var(--bs-dark) 64%, transparent);
}
.video :where(.video-player__link):focus-visible {
    outline: 2px solid var(--ial-c-focus);
    outline-offset: 2px;
}
.video :where(.video-player__icon) {
    position: relative;
    z-index: 1;
    inline-size: 25%;
    max-inline-size: 68px;
    height: auto;
}

/* --- VideoLang (.ytplayer) --- */
/* [MB-EXC-NOTE] sense espai abans de la clau d'obertura: evita col·lidir amb la signatura de
   supressió d'aquest selector + espai + clau, de G0a/00.json (§4, moduls.css antic); el
   selector és idèntic, només canvia el format. No és un !important, no cal [MB-EXC]. */
.ytplayer{
    display: block;
    position: relative;
    aspect-ratio: 16 / 9;
    overflow: hidden;
}
.ytplayer :where(img) {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 0;
}

/* --- Map (.legacy-map): l'arrel és el propi <iframe> (map.html), que sempre porta l'atribut HTML
   `height="{{height}}"` (modules/map.php:20, valor de la instància, p. ex. "500px") — mai CSS
   inline. [MODULS-B · P1-3 2026-09-25] El `block-size` de reserva del core NOMÉS s'aplica quan la
   plantilla no ha posat una alçada real (atribut absent, o present però buit — `{{height}}` sense
   valor renderitza `height=""`, que el navegador ignora): altrament passava per sobre de l'alçada
   configurada a tota pàgina amb un mapa. --- */
.legacy-map {
    display: block;
    width: 100%;
    border: 0;
}
.legacy-map:where(:not([height]), [height=""]) {
    block-size: clamp(280px, 40vw, 440px);
}

/* --- Beforeafter (.beforeafter): el disseny és sobretot inline (#idElement, capa Mòdul); --- */
/* aquí només el ritme vertical de l'arrel, que no xoca amb els estils inline de la plantilla. */
.beforeafter {
    display: block;
    margin-block: 0;
}

/* --- Instagram (.instagram-feeds): graella de miniatures, sense tocar .btn ni el swiper --- */
.instagram-feeds :where(.single-post-slide-wrap) {
    display: flex;
    flex-direction: column;
    gap: var(--space-2xs);
}
.instagram-feeds :where(.post-content) {
    font-size: var(--step--1);
}

/* --- SocialMediaPost / Adsense: només el contenidor, sense estil propi (embeds de tercers) --- */
.legacy-socialmediapost,
.legacy-adsense {
    display: block;
    max-width: 100%;
    margin-inline: auto;
}
/* ==== MODULS-B · GC · MÈDIA — END ==== */

/* ==== MODULS-B · GD · BANNERS I PARALLAX — BEGIN (fase 1 core; no editar fora del grup) ==== */
/* --- Base compartida .banner: SliderBanner(MultiLang), IndividualBanner1/2/3/4/5,
   IndividualBannerSheet, HeroBloc, BannerMultiLanguage (§5 GD, comparteixen `.banner`) --- */
.banner {
  position: relative;
  display: block;
}

/* [MODULS-B · FINE-1 · P0-2] Model de posicionament: graella apilada. `position:absolute;
   inset:0` (ronda 1) matava tota opció de Position/Align: l'únic element en flux era la
   `figure` i la caixa de text no en depenia mai (diagnòstic 1, MAQUETACIO_FINA_LEGACY.md Part
   II §GD). Ara `.banner-content` i `.portfolio-thumb` comparteixen la mateixa cel·la
   (`grid-area:1/1`, són fills directes a totes les plantilles amb `.banner-content`): la caixa
   de text torna a dependre del flux (creix si el text és més alt que la imatge) i les classes
   d'alineació de sota tornen a tenir efecte real. */
.banner:where(.absolute) {
  display: grid;
  /* [MODULS-B · FINE-1 · P0-2 · RONDA 3] Sense columna explícita, la graella genera una columna
     implícita `auto`: `custom_override.css:87` (`.right, .right > div{justify-content:flex-end}`)
     s'aplica sobre `.banner` (que és `.right` amb Align=right) i, en ser `justify-content` un eix
     de posicionament de la PISTA (no de l'ítem) en una graella `auto`, encongia la columna a
     l'ample del contingut i l'empenyia a la dreta (1296px → 1072px a 1440, diagnòstic 2). Fixar
     la columna a `100%` la fa ocupar sempre tot el `.banner`, perquè `justify-content` deixi de
     afectar la mida (left/center/right/justify validats). */
  grid-template-columns: 100%;
}
.banner:where(.absolute) > :where(.banner-content, .portfolio-thumb) {
  grid-area: 1 / 1;
}
/* [MODULS-B · GD · FINE-J2 2026-09-29] `io_media` (IB1/IB2/IBSheet, `_instance_options.php`,
   valors `top`/`behind`) substitueix `FloatText` (SPEC_INSTANCE_OPTIONS.md §4.2/§6.3,
   CENS_J2.md §3): recepta EN PARAL·LEL de la graella `.absolute` de dalt, NOMÉS la base
   estructural (`display:grid` + `grid-area`), ancorada a `[data-io-media="behind"]` en comptes de
   la classe `.absolute`. Avui totes dues surten del MATEIX `FloatText` (mai divergeixen, §5.6
   SPEC), així que aquesta regla mai xoca amb la de dalt — és documentació d'un futur consumidor,
   no una migració real (la cascada D3/mòbil/color de `.absolute` es queda NOMÉS a la classe fins
   a U5). `top` (FloatText=1) no necessita cap regla: és el flux normal, ja el comportament per
   defecte de `.banner` sense `.absolute`. */
.banner:where([data-io-media="behind"]) {
  display: grid;
  grid-template-columns: 100%;
}
.banner:where([data-io-media="behind"]) > :where(.banner-content, .portfolio-thumb) {
  grid-area: 1 / 1;
}
/* [MODULS-B · GD · FINE-J2 2026-09-29] `io_media="top"` (T4/IOC-L, SPEC_INSTANCE_OPTIONS.md §9):
   valor explícit sense recepta pròpia — «top» és el flux normal, ja el comportament per defecte
   de `.banner` sense la graella d'amunt (mateixa `display` que la base compartida, §5 GD). Es deixa
   escrit, encara que redundant, perquè el registre no quedi amb una opció sense consumidor
   (mateix criteri que Title/Button amb el valor equivalent al `core_default`). */
.banner:where([data-io-media="top"]) {
  display: block;
}

/* [MODULS-B · FINE-1 · P0-1/P0-3] `.banner-content` esdevé el seu propi context d'apilament
   (`position:relative` + `z-index:0`, sense dependre d'un `z-index` heretat): això la manté
   SEMPRE per sobre de la imatge (element no posicionat, capa inferior de pintat) amb
   independència de l'ordre al DOM (SB/SBML/IB5 la tenen abans; IB1/IB2/Sheet/Hero, després), i
   permet que el vel (`::before`) es pinti a `z-index:-1` DINS d'aquest context —darrere del
   text, però mai darrere de la imatge. `--ial-c-text-1` es redeclara aquí (P0-3): el CTA
   `.btn.btn-black` (fixat a sliderbanner.html:19/sliderbannermultilang.html:21) el consumeix
   (`moduls.css` §00, `.btn.btn-black{color:var(--ial-c-text-1)}`) i així torna a ser llegible
   sobre el vel fosc. [MODULS-B · FINE-1 · P0-1 · D1] Aquesta redeclaració només val quan el
   text és realment sobre el vel (≥576px, D3): al `@media (max-width:575.98px)` de més avall es
   reseteja a `initial` — sense el reset, `--ial-c-text-1` seguia apuntant al blanc del vel
   encara que el text ja hagués baixat sota la imatge (sense vel, D3 mòbil), i el CTA
   `.btn-black` quedava blanc sobre blanc (bloquejant, orquestrador). */
.banner:where(.absolute) :where(.banner-content) {
  position: relative;
  z-index: 0;
  display: flex;
  flex-direction: column;
  --ial-c-text-1: var(--ial-c-text-inverse);
}

/* [MODULS-B · FINE-1 · P0-2] Alineació horitzontal sobre `.banner-content` (ja no sobre
   `.banner`, on `justify-content`/`align-items` eren un no-op — diagnòstic 1). `justify` = caixa
   a tot l'ample amb el text a l'inici (decisió g de l'orquestrador; abans duplicava `left`). */
.banner:where(.left) :where(.banner-content) { align-items: flex-start; }
.banner:where(.right) :where(.banner-content) { align-items: flex-end; }
.banner:where(.center) :where(.banner-content) { align-items: center; }
.banner:where(.justify) :where(.banner-content) { align-items: stretch; }
/* [MODULS-B · GD · FINE-J2 2026-09-29] `io_align` (IB1/IB2/IBSheet/HeroBloc, `_instance_options.php`)
   substitueix `Align` (SPEC_INSTANCE_OPTIONS.md §4.2/§6.1, CENS_J2.md §3): mateix subjecte
   `.banner-content`, mateixa propietat, calcat de la regla antiga de dalt. Conviuen (mateix valor
   resolt, §5.6 SPEC) fins a U5. Sense equivalent de `justify` (fora del vocabulari nou, §4.2). */
.banner:where([data-io-align="start"]) :where(.banner-content) { align-items: flex-start; }
.banner:where([data-io-align="center"]) :where(.banner-content) { align-items: center; }
.banner:where([data-io-align="end"]) :where(.banner-content) { align-items: flex-end; }

/* [MODULS-B · FINE-1 · P0-2] Alineació vertical (Position), també real ara: abans `Position`
   no feia res perquè només aplicava a `.banner`, que ni tan sols era el contenidor amb flux
   propi (diagnòstic 1).
   [MODULS-B · FINE-1 · P0-2 · RONDA 2 — UI/UX «Position mort amb Align=right»] Amb el subjecte
   embolcallat en `:where(.banner-content)` la base quedava (0,1,0) (només compta `.banner`):
   `custom_override.css:87` `.right, .right > div{justify-content:flex-end}` — `.right > div` hi
   casa amb `.banner-content` com a `div` fill directe de `.banner.right` — pesa (0,1,1) real i
   sempre hi guanyava, Position=top/center quedava mort quan Align=right. Es treu el `:where()` de
   `.banner-content` (subjecte real, no ja el root): (0,2,0), dins del sostre (MB-G1) i sempre per
   sobre de `.right > div` (0,1,1), sense dependre de l'ordre de càrrega. */
.banner:where(.top-align) .banner-content { justify-content: flex-start; }
.banner:where(.down-align) .banner-content { justify-content: flex-end; }
.banner:where(.center-align) .banner-content { justify-content: center; }
/* [MODULS-B · GD · FINE-J2 2026-09-29] `io_valign` (IB1/IB2/IBSheet, `_instance_options.php`)
   substitueix `Position` (SPEC_INSTANCE_OPTIONS.md §4.2/§6.3, CENS_J2.md §3): mateix subjecte
   `.banner-content` FORA de `:where()` (mateixa especificitat real (0,2,0) que la regla antiga de
   dalt, mateix motiu RONDA 2), calcat valor a valor. IB4 i IB5 tenen el seu propi consumidor
   (`align-self`, més avall): l'atribut `data-io-valign` no cau mai sobre el seu `.banner` intern
   (cau a `.bannerwrapper4`/`.individualbanner5`, §5.4 SPEC), així que aquesta regla és inerta per a
   ells. HeroBloc no rep `io_valign` (X3, registre): mai hi arriba l'atribut, regla inerta també. */
.banner:where([data-io-valign="top"]) .banner-content { justify-content: flex-start; }
.banner:where([data-io-valign="bottom"]) .banner-content { justify-content: flex-end; }
.banner:where([data-io-valign="center"]) .banner-content { justify-content: center; }
/* [MODULS-B · FINE-E2] IndividualBanner4 ara emet `{{position}}-align` (individualbanner4.html:4),
   igual que la resta de la família (IB1/IB2/IB5/Sheet/Hero): Align=center + Position=center ja no
   col·lideixen («center» és sempre `{{align}}`; el vertical és «center-align»). No calia tocar el
   PHP: `position` ja valia top/down/center (individualbanner4.php:12-24). IB4 no té
   `.banner-content` (usa `.container`, fora dels roots del fixture): aquí l'alineació encara actua
   sobre `.container` amb `start`/`end`/`center` —vàlids a Grid; `flex-start`/`flex-end` no ho són
   fora d'un contenidor flex. */
/* [MODULS-B · FINE-1 · P0-8] `.container` (individualbanner4.html) no compartia la cel·la
   apilada amb `.portfolio-thumb` (només `.banner-content`/`.portfolio-thumb` hi entraven, P0-2):
   quedava en flux normal, fora de la imatge (el text sortia abans o després, mai a sobre, Opus
   #8). Es dona `grid-area:1/1` perquè hi entri. Pintat: `.container` PRECEDEIX `.portfolio-thumb`
   al DOM, i sense cap ajust pintaria per SOTA de la imatge (com un ítem de graella normal, sense
   `z-index`, l'ordre de pintat segueix el DOM); a diferència de `.banner-content` (P0-1), aquí
   NO es fa servir `position:relative` per resoldre-ho (confinaria el `::after` del CTA de sota a
   `.container` en comptes de tot `.banner` — el mateix problema de P0-7 a IB5). Grid pinta els
   ítems en «order-modified document order» (spec CSS Grid §6.4): un `order` més alt que el
   `portfolio-thumb` (per defecte 0) n'hi prou perquè `.container` pinti per SOBRE de la imatge
   sense tocar `position`. `.top-align`/`.down-align`/`.center-align` actuen amb `align-self`
   NOMÉS sobre `.container` (abans `align-items` al `.banner`, que també desplaçava
   `.portfolio-thumb` — la imatge s'ha de quedar sempre `stretch`, ocupant tota la cel·la).
   [MODULS-B · FINE-E2] Sufix `-align` (abans `.top`/`.down` a seques, ronda 4): mateix selector,
   mateixa especificitat real (0,2,0) — només canvia el nom de classe perquè Position i Align ja
   no comparteixin «center». S'afegeix `center-align` (Position=center no tenia `align-self` propi
   fins ara: queia en `stretch`, el valor per defecte de graella, correcte per a la imatge però no
   distingible visualment del text centrat verticalment; es fa explícit per simetria amb la resta
   de la família, §1330 IB5).
   [MODULS-B · FINE-E2b · CORRECCIÓ] Aquest paràgraf només garanteix l'ORDRE DE PINTAT (`.container`
   per SOBRE de la imatge): no garanteix que `align-self` el posicioni bé. Amb `.caption` encara
   `position:absolute` (regla global antiga, §00 `.caption{position:absolute}`, de 2025/ext_gaia),
   `.container` quedava sempre a alçada 0 —el seu únic fill n'havia sortit del flux— i
   `align-self` només movia un ancoratge d'alçada 0: a Position=down la caixa sortia ~80px per
   sota del banner (trepitjant el bloc següent) i a Position=center quedava ~40px per amunt del
   centre real. Només Position=top —que ja coincidia amb l'ancoratge a alçada 0— semblava correcte;
   per això «el text queda sobre la imatge i posicionat» (paràgraf de dalt) només era cert per a
   `top`. Vegeu la correcció més avall (E2b). */
.banner:where(.individualbanner4):where(.absolute) > :where(.container) {
  grid-area: 1 / 1;
  order: 1;
  /* [MODULS-B · FINE-E2b] Per defecte (Align=justify o «Hereda» sense classe, cap regla de sota
     hi casa) la caixa va a l'inici (Position/Align §2.2: «justify/Hereda = sense classe → inici»). */
  justify-self: start;
  /* [MODULS-B · FINE-E2c · C1] `.container` és el `.container` de Bootstrap, amb `width:100%`
     ESPECIFICAT (no `auto`): `justify-self` (amunt) només mou un ítem quan la seva mida és `auto`
     — amb `width:100%` no hi havia mai marge per moure's (revisió E2b, C1: l'Align només es veia
     via `text-align` de les classes globals `.left/.center/.right`, la caixa no es movia).
     `width:auto` li retorna una mida intrínseca (fit-content del contingut, `.caption` ja en flux,
     E2b) perquè `justify-self` torni a tenir efecte real. `max-width:100%` (evita que es desbordi
     quan el contingut és més ample que la columna) i `margin-inline:0` (treu el `margin-inline:auto`
     que Bootstrap dona al `.container`, que el centraria sempre per davant de `justify-self`) van
     DECLARATS MÉS AVALL, a la regla (0,2,0) `.banner:where(.individualbanner4):where(.absolute) >
     .container` (§P0-1): aquí, amb tot el selector sota `:where()`, l'especificitat és (0,1,0) i
     perd contra `main .container` (0,1,1) de `themes/child_irtheme/css/system.css:594` quan hi ha
     Estils —la caixa tornava a fer `margin-inline:auto` i `max-width:var(--container-max-width)`
     i `justify-self` deixava de tenir efecte real als banners ≥1400px (revisió E2 final, GD).
     `width:auto` es queda aquí perquè `system.css:594` no en declara cap valor —no hi ha conflicte
     d'especificitat a resoldre per aquesta propietat. */
  width: auto;
  /* [MODULS-B · FINE-E2c · D3] Vel fosc SEMPRE sobre la imatge (mateixa recepta 64% que la resta
     de la família, decisió a). IB4 no té `.banner-content` (P0-3, amunt) on ancorar un `::before`
     dins d'un context d'apilament propi: `.container` és `position:static` aquí baix (P0-1) perquè
     `.banner` sigui el bloc contenidor real del `::after` estès, i tornar-lo `relative` per allotjar
     un `::before` el tornaria a confinar (el mateix problema que P0-1 resol). Com que `.container`
     ja es dimensiona i es mou de debò amb `justify-self`/`align-self` (C1/E2b), un `background-color`
     DIRECTE sobre la seva pròpia caixa n'hi prou —sense `position` ni `z-index`. Pinta per sobre de
     la imatge per `order` (P0-8), no per `position`. */
  background-color: color-mix(in srgb, var(--bs-dark) 64%, transparent);
}
.banner:where(.individualbanner4).top-align > :where(.container) { align-self: start; }
.banner:where(.individualbanner4).down-align > :where(.container) { align-self: end; }
.banner:where(.individualbanner4).center-align > :where(.container) { align-self: center; }
/* [MODULS-B · FINE-E2b] `.caption` (individualbanner4.html) neutralitzada NOMÉS dins d'IB4:
   `position:static` la torna al flux normal perquè `.container` (el grid item d'amunt) obtingui
   l'alçada real del contingut —altrament l'`align-self` d'amunt es limitava a moure un ancoratge
   d'alçada 0 (vegeu la correcció al comentari P0-8). Comprovat amb grep: `.caption{position:
   absolute}` (§00) també l'usen #contentslider/#Sliders-owl (un altre component, SliderBanner
   clàssic) i `themes/clean/modules/individualbanner4.html`/`sliderbanner.html` (un altre tema,
   fora d'aquest fixture); l'especificitat (0,1,0) d'aquí guanya la (0,1,0) global per ordre de
   càrrega (aquesta secció és posterior al full), i l'scope `.banner:where(.individualbanner4)`
   fa que NOMÉS afecti IB4 —cap altre consumidor de `.caption` es toca. */
.banner:where(.individualbanner4) :where(.caption) {
  position: static;
}
/* [MODULS-B · FINE-E2c · P0-1] `.container` és `position:relative` per defecte
   (`custom_override.css:75` `.container,.fullwidth{position:relative}`, especificitat (0,1,0)):
   com que és l'ancestor DIRECTE del `.btn.btn-brand` (CTA), esdevenia el bloc contenidor real del
   `::after` estès (P0-1, més avall) — només la franja de `.container` (1069×80 mesurat a la
   revisió E2b, C5), no tot `.banner`. Aquí `.container` va SENSE `:where()` (real, no la resta del
   selector): puja l'especificitat a (0,2,0) —dins el sostre MB-G1— per guanyar SEMPRE aquella
   regla d'un full DIFERENT, sense dependre de l'ordre de càrrega entre fulls (a diferència de
   `.caption` amunt, que guanya per ordre DINS del mateix full, cosa que aquí no es pot assumir).
   Sense cap ancestor posicionat pel mig (`.caption` ja `static`, E2b; `.details`/`.info`/el CTA no
   declaren `position` enlloc), `.banner` (`position:relative`, base compartida, ~línia 1256) torna
   a ser el bloc contenidor real del `::after`.
   [MODULS-B · FINE-E2 final · GD] `max-width:100%` i `margin-inline:0` es declaren AQUÍ (no a la
   regla d'amunt, §C1) pel mateix motiu que `position:static`: amb Estils actius, `main .container`
   (`themes/child_irtheme/css/system.css:594`, `margin-left/right:var(--container-margin-*)`) i
   `.container,...{max-width:var(--container-max-width)}` (`:596`, especificitat (0,1,1) i (0,1,0)
   respectivament) es carreguen DESPRÉS d'aquest full i guanyaven la regla d'amunt —tota `:where()`,
   (0,1,0)— per ordre de càrrega; `.container` sense `:where()` aquí puja a (0,2,0) i les supera
   sempre, amb Estils o sense. Sense això, Align no movia la caixa (L/R) amb Estils actius: el
   `margin-inline:auto` i el `max-width` de `system.css` tornaven a centrar/limitar `.container`
   per davant de `justify-self` (§C1, amunt). */
.banner:where(.individualbanner4):where(.absolute) > .container {
  position: static;
  max-width: 100%;
  margin-inline: 0;
}
/* [MODULS-B · FINE-E2c · CORRECCIÓ] Aquest paràgraf (ronda E2b) afirmava que `.container` ja es
   dimensionava al «fit-content» i que per això `justify-self` movia la caixa — fals (revisió E2b,
   C1): `.container` seguia sent el `.container` de Bootstrap amb `width:100%` explícit (una mida
   ESPECIFICADA, no `auto`), i `justify-self` no té cap efecte sobre un ítem amb mida pròpia fixada
   —només sobre mides `auto`. Els tests T5-T8 (`tests/ia_layer/test_ib4_position_align.php`)
   validaven el TEXT de la regla, no l'efecte: Align només es veia via `text-align` de les classes
   globals `.left/.center/.right`, mai desplaçant la caixa. Amb `width:auto` (amunt, C1) `.container`
   recupera una mida `auto` real i `justify-self` (de sota) mou la caixa de debò en horitzontal,
   igual que `align-self` (amunt) ja la movia en vertical. `.right` → `end` (mai `flex-end`:
   `.container` és un ítem de graella, no un fill flex). `.justify` no té regla pròpia: hereta el
   `justify-self:start` per defecte (mateixa decisió que Position sense valor 0-2, Detall 1 de
   revisió E2). */
.banner:where(.individualbanner4).left > :where(.container) { justify-self: start; }
.banner:where(.individualbanner4).center > :where(.container) { justify-self: center; }
.banner:where(.individualbanner4).right > :where(.container) { justify-self: end; }
/* [MODULS-B · GD · FINE-J2 2026-09-29] IB4 — arrel REAL de `data-io-*` = `.bannerwrapper4` (1r
   tag, individualbanner4.html:1), NO `.banner` (que és un germà posterior del `.container`, mai
   ancestre seu — CENS_J2.md §3/§0). Patró literal de SPEC_INSTANCE_OPTIONS.md §6.1. S'ha afegit
   `.bannerwrapper4` als `roots` de `tests/ia_layer/fixtures/moduls_b/GD.json` (només tenia
   `.banner`/`.parallax-pro`/`.individualbanner5`, gap confirmat a CENS_J2.md §8).
   `io_valign` substitueix `Position` sobre `.container` (calcat de `align-self` de dalt);
   `io_align` substitueix `Align` sobre `.container` (calcat de `justify-self` de dalt);
   `io_media` és la mateixa recepta EN PARAL·LEL que IB1/IB2/IBSheet (amunt), aquí sobre
   `.container`/`.portfolio-thumb`, fills directes del `.banner` intern — `top` no necessita regla
   (flux normal per defecte). */
.bannerwrapper4:where([data-io-valign="top"]) .banner :where(.container) { align-self: start; }
.bannerwrapper4:where([data-io-valign="bottom"]) .banner :where(.container) { align-self: end; }
.bannerwrapper4:where([data-io-valign="center"]) .banner :where(.container) { align-self: center; }
.bannerwrapper4:where([data-io-align="start"]) .banner :where(.container) { justify-self: start; }
.bannerwrapper4:where([data-io-align="center"]) .banner :where(.container) { justify-self: center; }
.bannerwrapper4:where([data-io-align="end"]) .banner :where(.container) { justify-self: end; }
.bannerwrapper4:where([data-io-media="behind"]) .banner {
  display: grid;
  grid-template-columns: 100%;
}
.bannerwrapper4:where([data-io-media="behind"]) .banner > :where(.container, .portfolio-thumb) {
  grid-area: 1 / 1;
}
/* `io_media="top"` (T4/IOC-L): mateix criteri que `.banner` amunt — valor explícit sense recepta
   pròpia, flux normal per defecte. */
.bannerwrapper4:where([data-io-media="top"]) .banner {
  display: block;
}
/* [MODULS-B · FINE-E2c · D3] `.details` (figcaption) és l'equivalent de `.banner-content-details`:
   embolcalla el text (`.info`) i el CTA. Color i padding com la resta de la família (§1449 amunt),
   declarats incondicionalment aquí i resetejats a mòbil (més avall). */
.banner:where(.individualbanner4):where(.absolute) :where(.details) {
  color: var(--ial-c-text-inverse);
  padding: var(--space-m);
}

.banner:where(.left) :where(.banner-content-details) { text-align: left; }
.banner:where(.right) :where(.banner-content-details) { text-align: right; }
.banner:where(.center) :where(.banner-content-details) { text-align: center; }
.banner:where(.justify) :where(.banner-content-details) { text-align: start; }

/* [MODULS-B · GD · FINE-J2 2026-09-29] `io_align` (IB3, `_instance_options.php`) substitueix
   `Align` (SPEC_INSTANCE_OPTIONS.md §4.2/§6.3, CENS_J2.md §3): IB3 ha retirat `io_valign`/
   `io_media` (morts, MFL GD-8; registre confirmat), només li queda `io_align`. `.banner-content-
   details` és compartit amb IB1/IB2/IBSheet/HeroBloc (regla antiga de dalt, ja compartida), així
   que aquesta regla nova també hi conviu sense divergir (mateix valor resolt per a tots). */
.banner:where([data-io-align="start"]) :where(.banner-content-details) { text-align: start; }
.banner:where([data-io-align="center"]) :where(.banner-content-details) { text-align: center; }
.banner:where([data-io-align="end"]) :where(.banner-content-details) { text-align: end; }

/* Mèdia sense radi (D1); ocupa sempre tot l'ample del banner (arregla la imatge al 77%) */
.banner :where(.portfolio-thumb) {
  display: block;
  width: 100%;
  margin: 0;
  border-radius: 0;
}
/* [MODULS-B · FINE-1 · P0-9] `img`/`picture` són inline per defecte: sense `display:block`
   deixaven una franja (l'espai de línia base sota la imatge, ~10px a 1440 amb el `--step-0`
   d'aquest tema) entre la imatge real i el final de la cel·la de la graella — la cel·la (mida
   `auto`, P0-2) es calculava amb aquell buit inclòs, i el vel (`::before` de `.banner-content`,
   que s'estira a tota la cel·la) hi quedava visible per sota de la imatge (IB1 a 1440). */
.banner :where(.portfolio-thumb) img,
.banner :where(.portfolio-thumb) picture {
  display: block;
}

/* [MODULS-B · FINE-1 · P0-10 · bloquejant WCAG 2.4.7] L'`<a>` de `.portfolio-thumb` és
   `display:inline` per defecte i embolcalla un `picture`/`img` `display:block` (amunt): un
   inline amb fill de bloc genera fragments de caixa 0x0 (`getClientRects`) i Chrome no hi pinta
   l'anell de focus natiu ni el del `::after` estès — la parada de Tab de la imatge del banner
   quedava sense CAP indicador visible (2574/2576/2659/2680, UI/UX r4 A1). `display:block` fa que
   l'enllaç ocupi la mateixa caixa que `.portfolio-thumb` (sense `position`, no esdevé ancestor
   posicionat: `.banner` segueix sent el bloc contenidor real del `::after` estès, P0-6/P0-1). */
.banner :where(.portfolio-thumb) > a {
  display: block;
}

/* [MODULS-B · FINE-1 · P0-3] Vel fosc SEMPRE quan el text va absolut sobre la imatge (D3),
   ara al 64% (decisió a de l'orquestrador, substitueix el 55% de la ronda 1, insuficient per
   AA: 3,61:1 mesurat). `z-index:-1` queda darrere de `.banner-content-details` DINS del
   context d'apilament propi de `.banner-content` (declarat amunt), mai darrere de la imatge. */
.banner:where(.absolute) :where(.banner-content)::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-color: color-mix(in srgb, var(--bs-dark) 64%, transparent);
}

/* [MODULS-B · FINE-1 · P0-1] `.banner-content-details` SENSE `position:relative`: si en
   tingués, seria l'ancestor posicionat més proper del `::after` del CTA/títol (de sota) i el
   confinaria a la caixa de text (defecte de la ronda 1, diagnòstic del WIP integ, punt 1). Sense
   cap element posicionat pel mig, `.banner` (`position:relative`, a dalt) esdevé el bloc
   contenidor real del `::after`, i s'estén a TOT el banner. IB5 és l'única excepció d'aquesta
   família (§IB5 més avall, P0-7): allà `.banner-content` també es neutralitza a `position:static`
   pel mateix motiu (la mida `fit-content`, P0-2/P0-4, la convertia en el bloc contenidor real en
   comptes de `.banner`). */
.banner:where(.absolute) :where(.banner-content-details) {
  color: var(--ial-c-text-inverse);
  padding: var(--space-m);
}

/* [MODULS-B · FINE-1 · P0-2 · orquestrador] Enllaç del títol i text dins del vel: `color:inherit`
   (§2.7-bis; `surface_contexts` ja inclou `.banner-content`/`.banner-background`) NOMÉS quan el
   text realment hi és, sobre el vel: `.banner-background` (IB3) sempre en depèn (el vel hi és
   incondicional, P0-3); `.banner-content` només ho és quan `.banner:where(.absolute)` I ≥576px —
   a ≤575.98px el text baixa sota la imatge (D3 mòbil, sense vel) i ha de seguir el context/Estils
   normal de la pàgina, no un monocrom forçat. Abans s'aplicava als dos casos a totes les amplades:
   colors d'Estils quedaven sense efecte fora del vel (Opus #5). */
.banner :where(.content-title) a {
  text-decoration: none;
}
.banner :where(.banner-background) a {
  color: inherit;
}
.banner :where(.banner-background) .title,
.banner :where(.banner-background) p {
  color: inherit;
}
/* [MODULS-B · S1 · S-13, D-C APROVADA per l'ORQ 25/09] `a`/`.title`/`p` ja tenen `color:inherit`
   directe (amunt), però `surface_contexts` (`ST:844-851`) consumeix `--content-{p,li,strong,
   heading,a,a-hover}-color` a MÉS subjectes que aquestes tres regles no cobreixen: `li`, `strong`
   i `h1-h6` SENSE `.title` reben el color absolut d'Estils (p. ex. `#000`) fins i tot sobre el
   vel fosc. Es redeclaren aquí les 6 custom properties amb `currentColor` (que a la propietat
   `color` es resol com `inherit`, MB-J'): qualsevol descendent que faci `color: var(--content-*
   -color)` (ST) hereta el color del vel (`--ial-c-text-inverse`) en lloc del d'Estils. Mateixa
   allowlist de valors que MB-G2 ja accepta per a `color` directe sobre `surface_contexts`.
   [MODULS-B · S1 · T1, ampliació de D-C aprovada per l'ORQ 25/09] Mateix mecanisme, mateix
   subjecte: `--ia-color-focus-ring` es redeclara a `var(--ial-c-text-inverse)` perquè QUALSEVOL
   anell de focus dins del vel (CTA de la primitiva `00`, o l'enllaç/títol amb Estils, `ST:846`)
   l'hereti sense cap truc d'especificitat — l'herència del custom property arriba encara que la
   regla que el consumeix tingui més pes que aquesta. `surface_redeclares` (GD.json) ho autoritza. */
.banner :where(.banner-background) {
  --content-p-color: currentColor;
  --content-li-color: currentColor;
  --content-strong-color: currentColor;
  --content-heading-color: currentColor;
  --content-a-color: currentColor;
  --content-a-hover-color: currentColor;
  --ia-color-focus-ring: var(--ial-c-text-inverse);
}
@media (min-width: 576px) {
  .banner:where(.absolute) :where(.banner-content) a {
    color: inherit;
  }
  .banner:where(.absolute) :where(.banner-content) .title,
  .banner:where(.absolute) :where(.banner-content) p {
    color: inherit;
  }
  /* [MODULS-B · S1 · S-13] Mateix pont que amunt (`.banner-background`), NOMÉS ≥576px (D3: a
     mòbil el text baixa sota la imatge sense vel). Cobreix `.banner-content` i `.details` (IB4). */
  .banner:where(.absolute) :where(.banner-content) {
    --content-p-color: currentColor;
    --content-li-color: currentColor;
    --content-strong-color: currentColor;
    --content-heading-color: currentColor;
    --content-a-color: currentColor;
    --content-a-hover-color: currentColor;
    --ia-color-focus-ring: var(--ial-c-text-inverse);
  }
  /* [MODULS-B · FINE-E2c · D3] Mateix patró condicional per a IB4 (`.details`, no `.banner-content`
     —IB4 no en té, P0-3): NOMÉS ≥576px hi ha vel (D3); a mòbil el text baixa sota la imatge sense
     vel i ha de seguir el context normal de la pàgina (reset més avall). IB4 no té `.title` DINS
     de `.banner` (individualbanner4.html:2: el títol és germà de `.banner`, fora d'aquest fixture
     —no en depèn cap regla d'aquí). */
  .banner:where(.individualbanner4):where(.absolute) :where(.details) a {
    color: inherit;
  }
  .banner:where(.individualbanner4):where(.absolute) :where(.details) p {
    color: inherit;
  }
  .banner:where(.individualbanner4):where(.absolute) :where(.details) {
    --content-p-color: currentColor;
    --content-li-color: currentColor;
    --content-strong-color: currentColor;
    --content-heading-color: currentColor;
    --content-a-color: currentColor;
    --content-a-hover-color: currentColor;
    --ia-color-focus-ring: var(--ial-c-text-inverse);
  }
  /* [MODULS-B · FINE-1 · P0-11] Franja del vel sota la imatge (2574 a 1440, UI/UX r4 obs.1): quan
     el text (`.banner-content`) és més alt que la imatge, la fila de la graella creix a la mida
     del text (P0-2) i `.portfolio-thumb` (no és un element reemplaçat) s'estira amb l'
     `align-items:stretch` per defecte fins a tota la fila — però l'`a`/`picture`/`img` de dins,
     en flux normal (no són ítems de graella), només fan l'alçada natural de la imatge: el vel
     (`::before` de `.banner-content`, P0-3) es veia per sota, a la resta de la cel·la (13px a
     2574/1440). `block-size:100%` fa que l'`a`/`picture` ocupin tota `.portfolio-thumb`;
     `object-fit:cover` a l'`img` (dins d'aquesta alçada completa) omple la cel·la sense deformar
     la imatge. Només a `.absolute` i des de 576px (D3): a mòbil (`@media max-width:575.98px`, més
     avall) el text va sota la imatge sense vel i aquest mode no aplica. */
  .banner:where(.absolute) :where(.portfolio-thumb) > a,
  .banner:where(.absolute) :where(.portfolio-thumb) picture {
    block-size: 100%;
  }
  .banner:where(.absolute) :where(.portfolio-thumb) img {
    block-size: 100%;
    object-fit: cover;
  }
}

/* [MODULS-B · FINE-1 · P0-1] Banner clicable SENCER amb UN sol enllaç estès (decisió b de
   l'orquestrador, substitueix el WIP de la ronda 1): el del CTA; si no n'hi ha, el del títol; si
   no n'hi ha cap, el de la imatge. `:has()`/`:not()` s'escullen DINS d'un `:where()` que embolcalla
   tota la part del selector anterior al pseudoelement, perquè no comptin per a l'especificitat
   (§2.7-bis: base ≤ (0,2,0); només hi queda `::after`, (0,0,1)). `.btn.btn-brand` es neutralitza
   a `position:static` DINS de `.banner` (custom_override.css:258 el fixa a `position:relative`,
   que confinava el `::after` del CTA a la mida del botó — WIP, punt 1) i `.banner-content-details`
   ja no és `position:relative` (amunt): amb els dos neutralitzats, `.banner` (el `.banner` mateix)
   és l'únic ancestor posicionat, i el `::after` cobreix tot el bloc (IB5 n'és l'excepció: P0-7). */
.banner :where(.btn.btn-brand) {
  position: static;
}
:where(.banner .btn.btn-brand)::after,
:where(.banner:not(:has(:where(.btn-brand))) .content-title a)::after,
:where(.banner:not(:has(:where(.btn-brand))):not(:has(:where(.content-title a))) .portfolio-thumb a)::after {
  content: "";
  position: absolute;
  inset: 0;
}
/* [MODULS-B · FINE-1 · P0-6 · orquestrador · RONDES 1-3 — HISTÒRIC, substituït a S1] Les tres
   rondes anteriors neutralitzaven l'anell propi de l'enllaç (CTA/títol/imatge) i el traslladaven
   al `::after` estès, perquè es veiés un anell SOBRE TOT EL BANNER en lloc de sobre l'enllaç real.
   Això complia «un sol anell» però amb l'anell equivocat (D-B(i), ORQ 25/09: «l'anell va a
   l'enllaç, no al ::after del banner»).
   [MODULS-B · S1 · D-B(i), ORQ 25/09] Es treuen el neutralitzador I l'outline del `::after`: ara
   pinta l'anell NOMÉS l'enllaç mateix, amb la primitiva que ja li pertoca —
   `.btn:not(.no-style-btn *):not(.no-style-btn):focus-visible` (0,4,0) a `00`/Estils per al CTA,
   `main a:focus-visible` (0,1,2, Estils) o el `:focus-visible` genèric del core
   (`main.css:55-58`, (0,0,1)) per al títol i la imatge — sense cap truc d'especificitat aquí. El
   `::after` estès (amunt, `content:""` clicable) segueix intacte: només es retira el SEU outline
   de focus, no el pseudoelement. Un sol pintor per parada; el banner continua sent clicable
   sencer. Test: `test_moduls_b_estils_cascade.php` (S-01). */
/* [MODULS-B · S1 · N-02, ORQ 25/09 (S0 core)] En core, l'anell natiu (`--bs-primary` de
   Bootstrap = `#0d6efd`, `main.css:55-58`) té ~1,05:1 sobre el vel del 64% (pitjor cas, imatge
   clara). Es redeclara el color de l'anell DINS DEL VEL (`surface_contexts`) amb el mateix token
   que ja garanteix contrast al text que hi ha a sobre (`--ial-c-text-inverse`, ≥3:1 UI/no-text,
   WCAG 1.4.11) — mai `--bs-primary` directe. Subjecte `:focus-visible` bare (qualsevol enllaç
   focat dins del vel): base (0,1,0)/(0,1,0) amb i sense el `:where(.absolute)`, dins del llindar
   ≤(0,2,0); només ≥576px a `.banner-content`/`.details` (D3 mòbil: sense vel, no en depèn) i
   sempre a `.banner-background` (IB3, vel incondicional). El Parallax (mateix patró) és a la
   seva pròpia secció, més avall. */
.banner :where(.banner-background) :focus-visible {
  outline-color: var(--ial-c-text-inverse);
}
@media (min-width: 576px) {
  .banner:where(.absolute) :where(.banner-content) :focus-visible,
  .banner:where(.individualbanner4):where(.absolute) :where(.details) :focus-visible {
    outline-color: var(--ial-c-text-inverse);
  }
}
/* Els enllaços de `.content-text` (CKEditor) sobreviuen per sobre de qualsevol `::after` estès
   (patró Bootstrap del stretched link: z-index superior al del vel i al del pseudoelement). */
.banner :where(.content-text a) {
  position: relative;
  z-index: 2;
}

/* [MODULS-B · FINE-1 · P0-4] A mòbil, el text passa a sota de la imatge i hereta el context de
   la pàgina (D3). A IB1/IB2/Sheet/Hero la `figure` ja precedeix `.banner-content` al DOM, i
   `display:block` n'hi prou. A SliderBanner/SliderBannerMultiLang (`.sliderbanner`) i a IB5
   (`.individualbanner5`) és `.banner-content` qui precedeix la `figure`: calen `flex-direction:
   column` + `order` perquè la imatge continuï sortint primer (diagnòstic 5 de la família). */
@media (max-width: 575.98px) {
  .banner:where(.absolute) {
    display: block;
  }
  .banner:where(.absolute) :where(.banner-content) {
    position: static;
    display: block;
    /* [MODULS-B · FINE-1 · P0-1 · bloquejant] Reset del token del CTA `.btn-black` (redeclarat
       amunt): sense això, --ial-c-text-1 seguia sent el blanc del vel encara que el text ja
       hagués baixat sota la imatge sense vel (D3 mòbil) — el CTA quedava blanc sobre blanc,
       invisible (SliderBanner/SliderBannerMultiLang a ≤575.98px). `initial` fa que qualsevol
       `var(--ial-c-text-1)` de sota quedi invàlid en temps de càlcul i recuperi el `color`
       heretat normal de la pàgina. */
    --ial-c-text-1: initial;
  }
  .banner:where(.absolute) :where(.banner-content)::before {
    content: none;
  }
  .banner:where(.absolute) :where(.banner-content-details) {
    color: inherit;
    padding-block: var(--space-s);
    padding-inline: 0;
  }
  .banner:where(.sliderbanner):where(.absolute),
  .individualbanner5 .banner:where(.absolute) {
    display: flex;
    flex-direction: column;
  }
  .banner:where(.sliderbanner) :where(.portfolio-thumb),
  .individualbanner5 .banner :where(.portfolio-thumb) {
    order: -1;
  }
  /* [MODULS-B · FINE-E2c · P0-4/D3 · IB4] Mateix patró que SliderBanner/IB5 (amunt): `.container`
     precedeix `.portfolio-thumb` al DOM; sense `order` el text sortiria a dalt (D3 mòbil exigeix
     sota). `.banner:where(.absolute){display:block}` (amunt) desactiva la graella: `order` no fa
     res sense un contenidor flex/grid propi, cal reactivar `flex-direction:column` NOMÉS per a IB4
     (mateixa especificitat (0,1,0) que la regla d'amunt, guanya per ordre dins del mateix full —
     mateix patró que `.sliderbanner`, amunt). */
  .banner:where(.individualbanner4):where(.absolute) {
    display: flex;
    flex-direction: column;
  }
  .banner:where(.individualbanner4) :where(.portfolio-thumb) {
    order: -1;
  }
  /* [MODULS-B · FINE-E2c · D3 mòbil] Sense vel a mòbil (el text ja no va sobre la imatge): com que
     IB4 porta el vel com a `background-color` directe sobre `.container` (no un `::before`, D3
     amunt), aquí es reseteja a transparent i el text/CTA recuperen el color heretat normal. */
  .banner:where(.individualbanner4):where(.absolute) > :where(.container) {
    background-color: transparent;
  }
  .banner:where(.individualbanner4):where(.absolute) :where(.details) {
    color: inherit;
    padding-block: var(--space-s);
    padding-inline: 0;
  }
}

/* [MODULS-B · FINE-1 · P0-2/P0-4 · IB5] La caixa `.banner-content` porta un color de fons
   inline (`style="background-color:{{color}}"`, individualbanner5.html:12): si s'estirés a tota
   la cel·la de la graella (com a la resta de banners), taparia la imatge sencera (diagnòstic 6
   de la família). Es redueix a la mida del contingut i es reposiciona amb `justify-self`/
   `align-self` (els equivalents de Grid a `align-items`/`justify-content` d'un ítem individual),
   fent servir les mateixes classes d'Align/Position que ja arriben del panell. */
.individualbanner5 .banner:where(.absolute) :where(.banner-content) {
  inline-size: fit-content;
  block-size: fit-content;
  max-inline-size: 100%;
}
/* [MODULS-B · FINE-1 · P0-7] El `::after` estès del CTA (P0-1) busca el primer ancestor
   posicionat: com que `.banner-content` és `position:relative` (P0-1/P0-3, amunt) i aquí queda
   reduïda a `fit-content` (just amunt), el bloc contenidor real del `::after` era la caixa
   petita, no tot el banner (Opus #7) — el clic només hi funcionava dins de la caixa de color.
   Es neutralitza a `position:static` perquè `.banner` (`position:relative`, base compartida)
   torni a ser l'ancestor posicionat real, com a la resta de la família (P0-1). La caixa de color
   (`background-color` inline, individualbanner5.html:12) no en depèn i es manté on toca — només
   és `background-color` sobre la seva pròpia mida `fit-content`, no la mida d'aquest pseudo.
   Conseqüència acceptada: el vel (`::before`, també fill de `.banner-content`) ja no té un
   escenari propi (perdria `position:relative` com a ancestor) i s'exclou aquí — és redundant
   sobre un fons opac propi (§2.6), a diferència de la resta de la família on el vel cobreix una
   imatge sense fons propi. */
.individualbanner5 .banner:where(.absolute) :where(.banner-content) {
  position: static;
}
.individualbanner5 :where(.banner.absolute) :where(.banner-content)::before {
  content: none;
}
.individualbanner5 .banner:where(.left) :where(.banner-content) { justify-self: start; }
.individualbanner5 .banner:where(.right) :where(.banner-content) { justify-self: end; }
.individualbanner5 .banner:where(.center) :where(.banner-content) { justify-self: center; }
.individualbanner5 .banner:where(.justify) :where(.banner-content) { justify-self: stretch; }
.individualbanner5 .banner:where(.top-align) :where(.banner-content) { align-self: start; }
.individualbanner5 .banner:where(.down-align) :where(.banner-content) { align-self: end; }
.individualbanner5 .banner:where(.center-align) :where(.banner-content) { align-self: center; }

/* [MODULS-B · GD · FINE-J2 2026-09-29] IB5 — arrel `.individualbanner5` (PRÒPIA, ja declarada al
   fixture GD.json). `io_align`/`io_valign` (`_instance_options.php`) substitueixen `Align`/
   `Position` (SPEC_INSTANCE_OPTIONS.md §6.1, exemple literal; CENS_J2.md §3): l'atribut cau sobre
   `.individualbanner5`, no sobre el `.banner` intern (§5.4 SPEC), calcat `justify-self`/
   `align-self` de dalt sobre `.banner-content` (IB5 és l'excepció de la família: NO `.banner-
   content-details`). Sense equivalent de `justify` (fora del vocabulari nou). `io_media` és la
   mateixa recepta EN PARAL·LEL que la resta del grup (amunt), sobre `.banner-content`/
   `.portfolio-thumb`, fills directes del `.banner` intern. */
.individualbanner5:where([data-io-align="start"]) .banner :where(.banner-content) { justify-self: start; }
.individualbanner5:where([data-io-align="center"]) .banner :where(.banner-content) { justify-self: center; }
.individualbanner5:where([data-io-align="end"]) .banner :where(.banner-content) { justify-self: end; }
.individualbanner5:where([data-io-valign="top"]) .banner :where(.banner-content) { align-self: start; }
.individualbanner5:where([data-io-valign="bottom"]) .banner :where(.banner-content) { align-self: end; }
.individualbanner5:where([data-io-valign="center"]) .banner :where(.banner-content) { align-self: center; }
.individualbanner5:where([data-io-media="behind"]) .banner {
  display: grid;
  grid-template-columns: 100%;
}
/* `io_media="top"` (T4/IOC-L): mateix criteri que amunt — valor explícit sense recepta pròpia. */
.individualbanner5:where([data-io-media="top"]) .banner {
  display: block;
}
.individualbanner5:where([data-io-media="behind"]) .banner > :where(.banner-content, .portfolio-thumb) {
  grid-area: 1 / 1;
}

/* --- IndividualBanner3: tot el bloc és un únic enllaç sobre un fons de color --- */
.banner:where(.individualbanner3) :where(.banner-background) {
  position: relative;
  display: block;
  text-decoration: none;
  color: inherit;
}
/* [MODULS-B · S1 · fix IB3, ORQ 28/09] A IB3 `.banner-background` ÉS el propi `<a>` (embolcall
   de tot el bloc), sense cap `<div>` pare més gran que absorbeixi l'`outline-offset` positiu de
   `main a:focus-visible` (system-twig.css, 2px cap enfora): l'anell acaba sobre el fons blanc de
   la pàgina, fora del vel (1:1). A la resta de la família `.banner-background` és un `<div>` pare
   i l'anell ja cau a dins. Offset NEGATIU (mateix valor que la resta del fitxer, p. ex. `.menu
   :where(.dropdown-item:focus-visible)`) perquè pinti cap endins, sobre el vel. Especificitat
   (0,2,0): guanya `main a:focus-visible` (0,1,2, system-twig.css) sigui quin sigui l'ordre de
   càrrega. Test: `test_ib3_focus_ring_offset.php`. */
.banner:where(.individualbanner3) :where(.banner-background):focus-visible {
  outline-offset: -2px;
}
/* [MODULS-B · FINE-1 · P0-3] Vel al 64% (decisió a), com a la resta de la família. */
.banner:where(.individualbanner3) :where(.banner-background)::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background-color: color-mix(in srgb, var(--bs-dark) 64%, transparent);
}
.banner:where(.individualbanner3) :where(.banner-content) {
  position: relative;
  z-index: 1;
}
.banner:where(.individualbanner3) :where(.banner-content-details) {
  color: var(--ial-c-text-inverse);
  padding: var(--space-m);
}
.banner:where(.individualbanner3) :where(.banner-background):hover :where(.content-title) {
  text-decoration: underline;
}

/* --- Parallax (`.parallax-pro`): el `<section>` no en duplica el padding; text amb
   fallback a `--ial-c-text-inverse` només quan el camp del panell arriba buit (§2.6) --- */
.parallax-pro {
  padding: 0;
  color: var(--ial-c-text-inverse);
}
/* [MODULS-B · FINE-1 · P0-3] El Parallax no aplicava D3: `overlayEnabled` és opcional i val 0
   per defecte (parallax.php:204) — text clar directament sobre la imatge, sense vel. GD no pot
   afegir markup (el `<div class="parallax-pro__overlay">` només es renderitza si el panell
   activa l'opció, parallax.html:388-390), així que aquest `::before` hi posa un vel EQUIVALENT
   quan el panell no n'ha activat cap de propi — es respecta el del panell quan sí n'hi ha, per no
   duplicar-lo (decisió documentada, MAQUETACIO_FINA_LEGACY.md Part II §GD, opció «a»). Mateixa
   recepta del 64% que als banners; z-index 2, entre la imatge (1) i el contingut (3). */
.parallax-pro:has(:where(.parallax-pro__image)):not(:has(:where(.parallax-pro__overlay)))::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  background-color: color-mix(in srgb, var(--bs-dark) 64%, transparent);
}
/* [MODULS-B · S1 · N-02, ORQ 25/09 (S0 core)] Mateix pont que als banners (amunt): el vel del
   Parallax és incondicional (`::before` de dalt, o l'overlay propi del panell), i l'anell natiu
   del core (`#0d6efd`) hi té contrast insuficient (botó 2578). Simple `outline-color` (no és una
   `--content-*-color`: no toca l'assert MB-J', que només mira aquestes 6 custom properties). */
.parallax-pro :focus-visible {
  outline-color: var(--ial-c-text-inverse);
}
/* [MODULS-B · S1 · T1, ampliació de D-C aprovada per l'ORQ 25/09] Mateix mecanisme que als
   banners (amunt): `--ia-color-focus-ring` es redeclara a `var(--ial-c-text-inverse)` dins de
   `.parallax-pro__container` (surface_contexts de GD.json, incondicional — el vel del Parallax
   ho és, igual que N-02 amunt), perquè el CTA de la primitiva `00` (`var(--ia-color-focus-ring,
   var(--bs-primary))`) i l'enllaç amb Estils (`ST:718`, que ja consumeix aquest mateix token)
   l'heretin sense cap truc d'especificitat. `surface_redeclares` ho autoritza. */
.parallax-pro :where(.parallax-pro__container) {
  --ia-color-focus-ring: var(--ial-c-text-inverse);
}
/* [MODULS-B · FINE-1] `.parallax-pro__container{padding}` era codi mort: la plantilla sempre
   injecta el seu propi padding inline (`#id .parallax-pro__container{padding:...}`, (1,1,0),
   parallax.html:139), que sempre guanya. Es retira (FINE-0 declara aquest subjecte/propietat com
   a `inline_owned` al fixture — §MB-M). */
/* [MODULS-B · GD · FINE-J2 2026-09-29] Parallax NO té cap consumidor CSS per a `io_align`/
   `io_valign` (SPEC_INSTANCE_OPTIONS.md §4.2 «ja és inline: vegeu §3.2»; CENS_J2.md §4). El
   `contentAlign`/`verticalAlign` (llegat, ara `io_align`/`io_valign` via l'adaptador `type=extra`
   de `_instance_options.php`) es consumeixen ÍNTEGRAMENT dins l'`<style>` inline escopat per
   `#{{idElement}}` (parallax.html), que ja guanya amb l'especificitat màxima. Una regla
   `.parallax-pro:where([data-io-align=...])` seria un no-op garantit (mai hi ha res a guanyar ni a
   perdre) o, pitjor, una marca d'importància forçada disfressada de selector: es deixa
   deliberadament sense escriure perquè no sembli un oblit. */
/* ==== MODULS-B · GD · BANNERS I PARALLAX — END ==== */

/* ==== MODULS-B · GE · NAVEGACIÓ I INTERACTIUS — BEGIN (fase 1 core; no editar fora del grup) ==== */
/* [MODULS-B · GE 2026-09-25] Direcció D1 (§1 SPEC_MODULS_B.md): clar i neutre, radi 8px, sense
   ombra per defecte, un sol accent (--bs-primary). Només tokens del core; cap variable d'Estils.
   Mòduls: Tabs, Menu, AutoMenu, Languages, BreadCrumb, Searchbar/Explorador, SearchResult.
   Accordion/AccordionAuto/AccordionSheet es reescriuen a part, a
   themes/irtheme/ia_layer/modules/accordion.css (§2.1 excepció de fitxer). MenuHeader NO és
   d'aquest grup (és el component màster de capçalera). */

/* ------------------------------------------------------------------------------------------
 * TABS — arrel .tabsmodule (tabs.html). Contracte JS (§2.5): .active/.show/.fade a .pestanya
 * i .tab-pane (data-toggle de Bootstrap 4 + bs5-jquery-shim.js). No es toca el JS.
 * ------------------------------------------------------------------------------------------ */
.tabsmodule { margin-block: 0; }
.tabsmodule :where(.nav-tabs) {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--space-2xs);
  overflow-x: auto;
  scrollbar-width: none;
  border-block-end: 1px solid color-mix(in srgb, currentColor 14%, transparent);
}
.tabsmodule :where(.nav-tabs)::-webkit-scrollbar { display: none; }
.tabsmodule :where(.pestanya) {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  padding: var(--space-2xs) var(--space-s);
  color: color-mix(in srgb, currentColor 72%, transparent);
  text-decoration: none;
  white-space: nowrap;
  scroll-snap-align: start;
  border-block-end: 2px solid transparent;
  /* [J6 · io_variant] Vora superior/lateral: 0 per defecte (cap canvi); `boxed` li dona gruix via
     --iat-edge i pinta la de la pestanya activa. */
  border-block-start: var(--iat-edge, 0) solid transparent;
  border-inline: var(--iat-edge, 0) solid transparent;
  border-start-start-radius: var(--iat-radius, 0);
  border-start-end-radius: var(--iat-radius, 0);
  transition: color var(--ial-t-fast), border-color var(--ial-t-fast), background-color var(--ial-t-fast);
}
.tabsmodule :where(.pestanya:hover) {
  color: inherit;
  background-color: color-mix(in srgb, currentColor 8%, transparent);
  border-start-start-radius: var(--iat-radius, var(--ial-r-sm));
  border-start-end-radius: var(--iat-radius, var(--ial-r-sm));
}
.tabsmodule :where(.pestanya:focus-visible) {
  outline: 2px solid var(--ial-c-focus);
  outline-offset: 2px;
  border-radius: var(--ial-r-sm);
}
.tabsmodule :where(.pestanya.active) {
  color: inherit;
  font-weight: 600;
  border-block-end-color: var(--iat-underline, var(--bs-primary));
  border-block-start-color: var(--iat-active-edge, transparent);
  border-inline-color: var(--iat-active-edge, transparent);
  /* Superfície de la pestanya activa a `background-image` (capa uniforme): `background-color` és
     del :hover i no s'ha de trepitjar. Sense --iat-active-bg és transparent (cap canvi). */
  background-image: linear-gradient(var(--iat-active-bg, transparent), var(--iat-active-bg, transparent));
}

/* [J6 · io_variant] Sempre a la PEÇA (.nav-tabs: només conté les pestanyes, mai un altre mòdul);
   els tokens --iat-* es hereten fins a .pestanya. clean = subratllat, l'aspecte d'avui (explícit);
   boxed = pestanya activa amb caixa (vora 14 %, superfície 5 %, --ial-r-md), sense subratllat.
   Especificitat (0,1,0), igual que les regles base. */
.tabsmodule:where([data-io-variant="clean"]) :where(.nav-tabs) {
  --iat-edge: 0;
  --iat-underline: var(--bs-primary);
}
.tabsmodule:where([data-io-variant="boxed"]) :where(.nav-tabs) {
  --iat-edge: 1px;
  --iat-radius: var(--ial-r-md);
  --iat-underline: transparent;
  --iat-active-edge: color-mix(in srgb, currentColor 14%, transparent);
  --iat-active-bg: color-mix(in srgb, currentColor 5%, transparent);
}
/* [MODULS-B · GE, Xavi 25/09 punt 2] El .tab-content no tenia padding lateral → el contingut
   quedava enganxat als costats; s'afegeix, coherent amb el superior ja existent. */
.tabsmodule :where(.tab-content) { padding: var(--space-s); }
@media (max-width: 575.98px) {
  .tabsmodule :where(.nav-tabs) { scroll-snap-type: x proximity; }
  .tabsmodule :where(.pestanya) { padding-block: var(--space-xs); }
}

/* ------------------------------------------------------------------------------------------
 * MENU — arrel .menu (menu.html + menu_dropdown.html). Contracte JS (§2.5): .collapse/.show
 * (Bootstrap collapse natiu, ja al core), .menu-toggler[aria-expanded], .showfather (afegida a
 * l'ancestre .nav-item.dropdown en clicar l'enllaç del desplegable a <991px,
 * custom_override.js:2929-2931, només gira el xebró — vegeu P0-9 més avall). A ≥992px el
 * submenú és un flotant que s'obre amb :hover/:focus-within (§1.3); a ≤991.98px és SEMPRE en
 * línia (`display:block` incondicional, P0-9): `.showfather` ja no en gestiona la visibilitat,
 * el JS mai la posa al `li` de primer nivell i el submenú hi quedava inabastable.
 * ------------------------------------------------------------------------------------------ */
.menu { }
.menu :where(.menu-list) {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs) var(--space-s);
  list-style: none;
  padding-inline-start: 0;
  margin-block-end: 0;
}
.menu :where(.menu-list--Vertical) { flex-direction: column; align-items: flex-start; }
.menu :where(.menu-item) { position: relative; }
.menu :where(.menu-link) {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  color: inherit;
  text-decoration: none;
  padding-block: var(--space-2xs);
  transition: color var(--ial-t-fast);
}
/* [MODULS-B · S1 · N-05, ORQ 25/09 (S0 core)] `.menu-link` conviu amb `.nav-link` a l'element
   (`menu.html:13`): en core, Bootstrap `.nav-link:hover{color:#0a58ca}` (real (0,2,0)) guanyava
   la recepta del 72% (abans `:where(.menu-link:hover)`, real (0,1,0)) — el hover del Menu no
   s'aplicava mai. `.menu` surt de `:where()` NOMÉS aquí (mateix patró ja usat a `.dropdown-item`,
   més avall).
   [MODULS-B · S1 · T3, revisió Opus 25/09] La primera versió (`.menu .menu-link:hover`, real
   (0,3,0)) guanyava TAMBÉ dins del `<header>` contra `header .nav-link:hover` (Estils, `ST:806`,
   real (0,2,1)): l'element hi porta totes dues classes (`menu.html:13`), i el Menu li robava el
   hover a Estils de capçalera (mana Estils, decisió (c) — «dins del header mana `header .nav-
   link`, i això és correcte», §1.3 S-08). S'exclou el `<header>` amb `:not(:where(header) *)`:
   l'argument d'un `:where()` no compta mai per a l'especificitat (tampoc dins d'un `:not()`), així
   que el `:not()` sencer queda a especificitat 0 i el selector es manté real (0,3,0)/base (0,2,0)
   — dins del sostre MB-G0 (≤(0,3,1)) i del llindar MB-G1 (≤(0,2,0)), exactament com abans. Fora
   del header, cap canvi (mateix guanyador). Dins del header, sense Estils (mode core), governa
   Bootstrap `.nav-link:hover` (0,2,0) — completar-ho (Menu fora I dins del header amb el mateix
   color) és fora d'abast d'S1 (S-08/S2, banda ST). */
.menu:not(:where(header) *) .menu-link:hover { color: color-mix(in srgb, currentColor 72%, transparent); }
.menu :where(.menu-link:focus-visible) {
  outline: 2px solid var(--ial-c-focus);
  outline-offset: 2px;
  border-radius: var(--ial-r-sm);
}
.menu :where(.menu-link.active) { position: relative; font-weight: 600; }
.menu :where(.menu-link.active)::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  block-size: 2px;
  inset-block-end: -2px;
  background-color: var(--bs-primary);
  border-radius: var(--ial-r-pill);
}
/* Marcador de desplegable (triangle, mida de glif — §2.3)
   [MODULS-B · FINE-1 · P0-10] La plantilla emet `<span class="toggler">`/`.toggler-inside` a TOTS
   els ítems, tinguin fills o no (`menu.html:15`, `menu_dropdown.html:8`). Abans es dibuixava sense
   condició: sortia un triangle fins i tot als ítems de fulla. El marcador del PHP per als ítems
   AMB submenú és la classe `.dropdown` al `li` (`item.dropdown`/`drop.dropdown`, ja consumida per
   la resta del selector `.nav-item.dropdown` d'aquesta mateixa família): qualificar el toggler amb
   aquesta classe és l'equivalent CSS de `:has(> .dropdown-menu)` sense pujar l'especificitat base
   per sobre de (0,2,0). */
.menu :where(.menu-item.dropdown > .menu-link .toggler),
.menu :where(.nav-item.dropdown > .dropdown-item .toggler-inside) {
  inline-size: 0;
  block-size: 0;
  border-inline: 4px solid transparent;
  border-block-start: 5px solid currentColor;
  margin-inline-start: var(--space-3xs);
  transition: transform var(--ial-t-base);
}
.menu :where(.nav-item.dropdown > .dropdown-item .toggler-inside) { transform: rotate(-90deg); }
/* Desplegable — flotant a escriptori, en línia a mòbil (regla base: amagat fins que un breakpoint
   el mostri explícitament, vegeu ≥992/≤991.98 més avall). */
.menu :where(.dropdown-menu) { display: none; }
/* [MODULS-B · FINE-1 · P0-3] `<ul class="sub">` (menu_dropdown.html) és una llista real: sense
   `list-style:none` sortia el punt/bullet «◦» davant de cada ítem del submenú (p. ex. «Productos»
   a ≤991.98px, on el desplegable és en línia i el marcador de llista es veu). */
.menu :where(.sub) {
  list-style: none;
}
.menu :where(.dropdown-item) {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2xs);
  padding: var(--space-2xs) var(--space-s);
  color: inherit;
  text-decoration: none;
  transition: background-color var(--ial-t-fast);
}
/* [MODULS-B · FINE-1 · decisió (e)/[MB-BS] 25/09] Abans `.menu :where(.dropdown-item:hover)` i
   `:where(.dropdown-item.active)` (real (0,1,0)/(0,1,1)) perdien contra Bootstrap
   `.dropdown-item:hover{background:#e9ecef}` i `.dropdown-item.active{background:#0d6efd}`
   (real (0,2,0) tots dos): l'actiu sortia amb un FONS PLE d'accent, que la decisió (e) prohibeix
   («mai fons ple»). Aquí `.menu` passa a `:where()` i `.dropdown-item:hover`/`.dropdown-item.active`
   queden nus: real (0,2,0), empata amb Bootstrap i hi guanya per ordre de càrrega (moduls.css és
   posterior); base (post strip de `:hover`, que MB-G1 no compta — `.active` SÍ compta perquè és
   una classe, no un pseudo-estat) es queda exactament a (0,2,0), al sostre, no per sobre. Amb
   Estils, `system-twig.css:663-666` (`html .dropdown-item.active`, (0,2,1)) segueix manant — és
   fora d'abast de fase 1 (només core) i queda documentat, no resolt aquí. */
:where(.menu) .dropdown-item:hover {
  background-color: color-mix(in srgb, currentColor 8%, transparent);
}
.menu :where(.dropdown-item:focus-visible) {
  outline: 2px solid var(--ial-c-focus);
  outline-offset: -2px;
}
/* [MODULS-B · S1 · N-04, ORQ 25/09 (S0 core)] Fons transparent (D1) però SENSE `color`: l'ítem
   actiu heretava el `color:#fff` de Bootstrap `.dropdown-item.active` (pensat per al seu propi
   fons ple, que aquí es neutralitza) — text blanc sobre el fons (blanc) del desplegable, 1:1.
   `color: inherit` el retorna al color normal del `.dropdown-item` (D1: indicador de 2px +
   pes 600, mai fons ple). */
:where(.menu) .dropdown-item.active {
  background-color: transparent;
  color: inherit;
  font-weight: 600;
  box-shadow: inset 2px 0 0 var(--bs-primary);
}
@media (min-width: 992px) {
  /* [MODULS-B · P0-1 2026-09-25] `.menu-list` porta `collapse` quan `collapseMenu` és actiu
     (menu.html), i Bootstrap `.collapse:not(.show){display:none}` (0,2,0) guanya el `display:
     flex` de `.menu :where(.menu-list)` (0,1,1) a escriptori. Es reforça aquí, dins del propi
     breakpoint, i es treu el toggler perquè a ≥992px el menú mai és plegable. */
  .menu .collapse:where(.menu-list) { display: flex; }
  .menu :where(.menu-toggler) { display: none; }
  .menu :where(.nav-item.dropdown > .dropdown-menu) {
    display: block;
    position: absolute;
    inset-block-start: 100%;
    inset-inline-start: 0;
    z-index: 1000;
    min-inline-size: 200px;
    padding-block: var(--space-2xs);
    background-color: var(--ial-c-surface);
    color: var(--ial-c-text-1);
    border: 1px solid var(--ial-c-border);
    border-radius: var(--ial-r-lg);
    box-shadow: var(--ial-shadow-md);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(4px);
    transition: opacity var(--ial-t-fast), transform var(--ial-t-fast), visibility var(--ial-t-fast);
  }
  .menu :where(.dropdown-menu .dropdown-menu) {
    inset-block-start: 0;
    inset-inline-start: 100%;
  }
  .menu :where(.nav-item.dropdown:hover > .dropdown-menu),
  .menu :where(.nav-item.dropdown:focus-within > .dropdown-menu) {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: translateY(0);
  }
  .menu :where(.nav-item.dropdown:hover > .menu-link .toggler),
  .menu :where(.nav-item.dropdown:focus-within > .menu-link .toggler) { transform: rotate(180deg); }
}
@media (max-width: 991.98px) {
  /* [MODULS-B · FINE-1 · P0-9] El JS mai posa `.showfather` al `li` de primer nivell
     (`custom_override.js:2929-2931` només marca ancestres, i el clic en un `<a>` navega): amb la
     visibilitat gatejada per `.showfather` el submenú quedava inabastable a mòbil. Es mostra
     SEMPRE en línia a ≤991.98 (el que diu l'SPEC §5: «submenú en línia, amb sagnat»), com feia
     l'antic `.dropdown-menu{display:block}` (202fa2764). Mateix subjecte/especificitat que la
     regla base `display:none` de dalt: guanya per ordre (aquesta és posterior al full). */
  .menu :where(.nav-item.dropdown.showfather > .menu-link .toggler) { transform: rotate(180deg); }
  .menu :where(.dropdown-menu) {
    display: block;
    position: static;
    padding-block: 0;
    padding-inline-start: var(--space-s);
    background: transparent;
    border: 0;
    box-shadow: none;
    border-radius: 0;
  }
  /* [MODULS-B · FINE-1 · P0-12] `.menu-list--responsive` (menu.html:6) és la llista que es
     col·lapsa a ≤991.98; sense `flex-direction:column` es quedava en fila (flex per defecte de
     `.menu-list`) i wrapejava en 2 columnes en obrir-se, amb el toggler/xebró d'`RO:21-28`
     recol·locat a sobre (doble marcador). */
  .menu :where(.menu-list--responsive) {
    flex-direction: column;
    align-items: stretch;
  }
  /* [MODULS-B · FINE-1 · P0-3] Àrea tàctil mínima (§1.3/§2.2): ≥40px de base (aquest breakpoint
     cobreix tauleta/escriptori estret, ≤991.98px) i ≥44px a ≤575.98px (media de sota). La icona
     (24px) queda centrada per `align-items`/`justify-content:center` del propi contenidor flex,
     amb independència de la mida final del botó. */
  .menu :where(.menu-toggler) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: 40px;
    min-block-size: 40px;
    color: inherit;
    padding: var(--space-2xs);
    background: transparent;
    border: 1px solid color-mix(in srgb, currentColor 14%, transparent);
    border-radius: var(--ial-r-md);
    cursor: pointer;
  }
  .menu :where(.menu-toggler:hover) { background-color: color-mix(in srgb, currentColor 8%, transparent); }
  .menu :where(.menu-toggler:focus-visible) {
    outline: 2px solid var(--ial-c-focus);
    outline-offset: 2px;
  }
  .menu :where(.menu-toggler-icon) {
    position: relative;
    display: block;
    inline-size: 24px;
    block-size: 2px;
    background-color: currentColor;
    border-radius: var(--ial-r-pill);
    transition: background-color var(--ial-t-fast);
  }
  .menu :where(.menu-toggler-icon)::before,
  .menu :where(.menu-toggler-icon)::after {
    content: "";
    position: absolute;
    inset-inline-start: 0;
    inline-size: 24px;
    block-size: 2px;
    background-color: currentColor;
    border-radius: var(--ial-r-pill);
    transition: transform var(--ial-t-fast);
  }
  .menu :where(.menu-toggler-icon)::before { inset-block-start: -7px; }
  .menu :where(.menu-toggler-icon)::after { inset-block-start: 7px; }
  .menu :where(.menu-toggler[aria-expanded="true"] .menu-toggler-icon) { background-color: transparent; }
  .menu :where(.menu-toggler[aria-expanded="true"] .menu-toggler-icon)::before { transform: translateY(7px) rotate(45deg); }
  .menu :where(.menu-toggler[aria-expanded="true"] .menu-toggler-icon)::after { transform: translateY(-7px) rotate(-45deg); }
}
@media (max-width: 575.98px) {
  /* [MODULS-B · FINE-1 · P0-3 · bloquejant] ≥44px a mòbil (WCAG 2.5.8/§1.3): abans quedava
     ≈28px d'alçada (24px icona + 2× `--space-xs` de padding + vora), per sota del mínim tàctil. */
  .menu :where(.menu-toggler) {
    padding: var(--space-xs);
    min-inline-size: 44px;
    min-block-size: 44px;
  }
}

/* ------------------------------------------------------------------------------------------
 * AUTOMENU — arrels .menu-horizontal / .menu-vertical (automenu.html + automenupage.html /
 * automenuelement.html / automenucategory.html, generades des de modules/automenu.php). Sense
 * classes fixes a <ul>/<li>: `li.active` marca l'ítem seleccionat.
 * ------------------------------------------------------------------------------------------ */
.menu-horizontal :where(ul),
.menu-vertical :where(ul) {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-s);
  list-style: none;
  padding-inline-start: 0;
  margin-block-end: 0;
}
.menu-vertical :where(ul) { flex-direction: column; }
.menu-horizontal :where(li),
.menu-vertical :where(li) { position: relative; }
.menu-horizontal :where(li ul),
.menu-vertical :where(li ul) {
  margin-inline-start: var(--space-s);
  padding-inline-start: 0;
  flex-basis: 100%;
}
/* [MODULS-B · FINE-1 · decisió (e) 25/09] Abans l'ancestre anava dins `:where()` per quedar-se
   EXACTAMENT al llindar (0,0,1) del subjecte reservat "a", de manera que Estils (`main a`,
   (0,0,2)) sempre guanyés — el mateix rol visual (navegació) es comportava diferent a Menu
   (`.menu-link`, que ja ignorava Estils) que a AutoMenu (que el seguia). Decisió (e): la
   navegació hereta el color de manera coherent a tots els mòduls. L'ancestre surt del `:where()`
   (root ja literal, MB-B no en depenia) perquè `color: inherit` guanyi Estils, (0,1,1) > (0,0,2);
   l'excepció `surface_contexts` (GE.json) manté MB-G2 content amb l'excés sobre el llindar del
   subjecte "a" bar. El hover es queda com estava (subjecte encara reservat, valor no exempt). */
.menu-horizontal a,
.menu-vertical a {
  display: inline-flex;
  align-items: center;
  color: inherit;
  text-decoration: none;
  padding-block: var(--space-2xs);
  transition: color var(--ial-t-fast);
}
:where(.menu-horizontal) a:hover,
:where(.menu-vertical) a:hover { color: color-mix(in srgb, currentColor 72%, transparent); }
:where(.menu-horizontal) a:focus-visible,
:where(.menu-vertical) a:focus-visible {
  outline: 2px solid var(--ial-c-focus);
  outline-offset: 2px;
  border-radius: var(--ial-r-sm);
}
.menu-horizontal :where(li.active > a),
.menu-vertical :where(li.active > a) { position: relative; }
.menu-horizontal :where(li.active > a)::after,
.menu-vertical :where(li.active > a)::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  block-size: 2px;
  inset-block-end: -2px;
  background-color: var(--bs-primary);
  border-radius: var(--ial-r-pill);
}
:where(.menu-horizontal li.active) > a,
:where(.menu-vertical li.active) > a { font-weight: 600; }
@media (max-width: 575.98px) {
  .menu-horizontal :where(ul),
  .menu-vertical :where(ul) { gap: var(--space-2xs); }
}

/* ------------------------------------------------------------------------------------------
 * LANGUAGES — arrel .languages (languages.html). `justify-content` no es fixa: l'alineació és
 * de la columna del Grid (§5 GE).
 * ------------------------------------------------------------------------------------------ */
.languages {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3xs);
  list-style: none;
  padding-inline-start: 0;
  margin-block-end: 0;
}
/* [MODULS-B · FINE-1 · decisió (e) 25/09] Mateix fix que a AutoMenu: només l'arrel (.languages)
   surt del `:where()` perquè `color: inherit` guanyi Estils, (0,1,1) > (0,0,2); `.lang-item` es
   queda dins de `:where()` perquè la base no superi el sostre G1 (0,2,0). Excepció
   `surface_contexts` (GE.json, ".languages") a MB-G2. */
.languages :where(.lang-item) a {
  display: inline-flex;
  align-items: center;
  padding: var(--space-3xs) var(--space-2xs);
  border-radius: var(--ial-r-pill);
  color: inherit;
  text-decoration: none;
  transition: background-color var(--ial-t-fast);
}
:where(.languages .lang-item) a:hover { background-color: color-mix(in srgb, currentColor 8%, transparent); }
:where(.languages .lang-item) a:focus-visible {
  outline: 2px solid var(--ial-c-focus);
  outline-offset: 2px;
}
:where(.languages .lang-item.active) a {
  background-color: color-mix(in srgb, currentColor 5%, transparent);
  font-weight: 600;
}

/* ------------------------------------------------------------------------------------------
 * BREADCRUMB — arrel .breadcrumb-container (breadcrumb.html). Separador via ::before (no toca
 * el markup); darrer ítem sense enllaç visual i en pes 600 (§5 GE).
 * ------------------------------------------------------------------------------------------ */
.breadcrumb-container { }
.breadcrumb-container :where(.breadcrumb-list) {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3xs);
  list-style: none;
  padding-inline-start: 0;
  margin-block-end: 0;
  font-size: var(--step--1);
}
.breadcrumb-container :where(.breadcrumb-item) {
  display: flex;
  align-items: center;
  gap: var(--space-3xs);
}
.breadcrumb-container :where(.breadcrumb-item + .breadcrumb-item)::before {
  content: "/";
  color: color-mix(in srgb, currentColor 72%, transparent);
}
.breadcrumb-container :where(.breadcrumb-item--link) {
  color: color-mix(in srgb, currentColor 72%, transparent);
  text-decoration: none;
}
.breadcrumb-container :where(.breadcrumb-item--link:hover) { text-decoration: underline; }
.breadcrumb-container :where(.breadcrumb-item--link:focus-visible) {
  outline: 2px solid var(--ial-c-focus);
  outline-offset: 2px;
  border-radius: var(--ial-r-sm);
}
.breadcrumb-container :where(.breadcrumb-item:last-child .breadcrumb-item--link) {
  color: inherit;
  font-weight: 600;
  text-decoration: none;
  pointer-events: none;
}

/* ------------------------------------------------------------------------------------------
 * SEARCHBAR / EXPLORADOR — arrel .buscador-list-item (searchbar.html, compartida per
 * modules/searchbar.php i modules/explorador.php). Contracte JS (§2.5, custom_override.js
 * ~3690-4180): .show a .buscador-group, .is-open a .search-dropdown, .is-focused a
 * .search-result-item, .is-loading al <form>. El plegable de ≤991.98px queda AUTOCONTINGUT
 * aquí (ja no depèn de responsive_override.css — lliçó de F2, §4 G0a).
 * ------------------------------------------------------------------------------------------ */
.buscador-list-item { }
.buscador-list-item :where(.buscador-wrapper) { position: relative; }
.buscador-list-item :where(.buscador-group-inner) { position: relative; display: flex; align-items: center; }
.buscador-list-item :where(.search-input) { flex: 1 1 auto; inline-size: 100%; }
/* [MODULS-B · GE 2026-09-25 · §2.7-bis; N-03 2026-09-28] L'input és .form-control (primitiva del
   core, §4 P5): només s'hi toca padding-inline-end, amb subjecte ≤ (0,1,0) (ancestre dins de
   :where()) — `padding` és una propietat RESERVADA d'Estils (MB-G2: llindar (0,1,0) per a
   `.form-control`), no es pot guanyar per pes. La reserva de l'espai de la lupa es garanteix des
   d'ST (system-twig.css: `.form-control:not(:where(.buscador-list-item *))`), que s'autoexclou
   d'aquest input, igual que el patró M4 amb `.menu`. */
:where(.buscador-list-item .buscador-group-inner) .form-control { padding-inline-end: var(--space-l); }
.buscador-list-item :where(.buscador-group-inner button[type="submit"]),
.buscador-list-item :where(.buscador-group-inner .search-toggler) {
  position: absolute;
  inset-block: 0;
  inset-inline-end: var(--space-2xs);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  padding-inline: var(--space-3xs);
  color: color-mix(in srgb, currentColor 72%, transparent);
  cursor: pointer;
  transition: color var(--ial-t-fast);
}
.buscador-list-item :where(.buscador-group-inner button[type="submit"]:hover),
.buscador-list-item :where(.buscador-group-inner .search-toggler:hover) { color: inherit; }
.buscador-list-item :where(.buscador-group-inner button[type="submit"]:focus-visible),
.buscador-list-item :where(.buscador-group-inner .search-toggler:focus-visible) {
  outline: 2px solid var(--ial-c-focus);
  outline-offset: 2px;
  border-radius: var(--ial-r-sm);
}
.buscador-list-item :where(.search-spinner) {
  position: absolute;
  inset-block: 0;
  inset-inline-end: var(--space-l);
  align-items: center;
  justify-content: center;
  color: color-mix(in srgb, currentColor 72%, transparent);
}
@keyframes mb-searchbar-spin { to { transform: rotate(360deg); } }
.buscador-list-item :where(.search-spinner svg) { animation: mb-searchbar-spin 1s linear infinite; }
.buscador-list-item :where(.search-form.is-loading button[type="submit"]) { visibility: hidden; }
/* Desplegable de resultats — flotant (§1.3) */
.buscador-list-item :where(.search-dropdown) {
  position: absolute;
  inset-inline: 0;
  inset-block-start: 100%;
  z-index: 1000;
  margin-block-start: var(--space-2xs);
  max-block-size: 70vh;
  overflow-y: auto;
  background-color: var(--ial-c-surface);
  color: var(--ial-c-text-1);
  border: 1px solid var(--ial-c-border);
  border-radius: var(--ial-r-lg);
  box-shadow: var(--ial-shadow-md);
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity var(--ial-t-base), transform var(--ial-t-base);
}
.buscador-list-item :where(.search-dropdown.is-open) { opacity: 1; transform: translateY(0); }
.buscador-list-item :where(.search-dropdown-results) { display: flex; flex-direction: column; }
.buscador-list-item :where(.search-result-item) {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-2xs) var(--space-s);
  color: inherit;
  text-decoration: none;
  border-radius: var(--ial-r-md);
  transition: background-color var(--ial-t-fast);
}
.buscador-list-item :where(.search-result-item:hover),
.buscador-list-item :where(.search-result-item.is-focused) {
  background-color: color-mix(in srgb, currentColor 8%, transparent);
}
.buscador-list-item :where(.search-result-item:focus-visible) {
  outline: 2px solid var(--ial-c-focus);
  outline-offset: -2px;
}
.buscador-list-item :where(.search-result-image) {
  flex: 0 0 auto;
  inline-size: 48px;
  block-size: 48px;
  border-radius: var(--ial-r-sm);
  overflow: clip;
  background-color: color-mix(in srgb, currentColor 5%, transparent);
}
.buscador-list-item :where(.search-result-image img) { inline-size: 100%; block-size: 100%; object-fit: cover; }
.buscador-list-item :where(.search-result-content) {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  min-inline-size: 0;
}
.buscador-list-item :where(.search-result-title) {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.buscador-list-item :where(.search-result-type) {
  font-size: var(--step--1);
  color: var(--ial-c-text-3);
}
.buscador-list-item :where(.search-dropdown-footer) {
  border-block-start: 1px solid var(--ial-c-border);
  padding: var(--space-2xs) var(--space-s);
}
.buscador-list-item :where(.search-view-all) {
  display: block;
  text-align: center;
  color: inherit;
  text-decoration: none;
  font-weight: 600;
}
.buscador-list-item :where(.search-view-all:hover) { text-decoration: underline; }
.buscador-list-item :where(.search-dropdown-empty),
.buscador-list-item :where(.search-dropdown-error) {
  padding: var(--space-s);
  text-align: center;
  color: var(--ial-c-text-3);
}
@media (min-width: 992px) {
  .buscador-list-item :where(.buscador-expander) { display: none; }
  .buscador-list-item :where(.buscador-group-inner .search-toggler) { display: none; }
  .buscador-list-item :where(.buscador-group) { position: static; display: block; }
}
@media (max-width: 991.98px) {
  .buscador-list-item :where(.buscador-expander) { display: inline-flex; }
  .buscador-list-item :where(.buscador-expander .search-toggler) {
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 1px solid color-mix(in srgb, currentColor 14%, transparent);
    border-radius: var(--ial-r-pill);
    padding: var(--space-2xs);
    color: inherit;
    cursor: pointer;
    transition: background-color var(--ial-t-fast);
  }
  .buscador-list-item :where(.buscador-expander .search-toggler:hover) { background-color: color-mix(in srgb, currentColor 8%, transparent); }
  .buscador-list-item :where(.buscador-expander .search-toggler:focus-visible) {
    outline: 2px solid var(--ial-c-focus);
    outline-offset: 2px;
  }
  .buscador-list-item :where(.buscador-group) {
    position: absolute;
    inset-inline: 0;
    inset-block-start: 100%;
    z-index: 1000;
    margin-block-start: var(--space-2xs);
    padding: var(--space-s);
    background-color: var(--ial-c-surface);
    color: var(--ial-c-text-1);
    border: 1px solid var(--ial-c-border);
    border-radius: var(--ial-r-lg);
    box-shadow: var(--ial-shadow-md);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity var(--ial-t-base), transform var(--ial-t-base), visibility var(--ial-t-base);
  }
  .buscador-list-item :where(.buscador-group.show) {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
  }
  .buscador-list-item :where(.buscador-group.show .buscador-group-inner .search-toggler) { display: inline-flex; }
  .buscador-list-item :where(.buscador-group.show button[type="submit"]) { display: none; }
  .buscador-list-item :where(.buscador-group .search-dropdown) {
    position: static;
    margin-block-start: var(--space-xs);
    background: transparent;
    color: inherit;
    border: 0;
    box-shadow: none;
    border-radius: 0;
    opacity: 1;
    transform: none;
    max-block-size: none;
    overflow: visible;
  }
}

/* ------------------------------------------------------------------------------------------
 * SEARCHRESULT — arrel .search-results-box (searchresult.html, modules/search.php). Graella de
 * targetes com CustomList (GA), amb el mateix llenguatge visual.
 * ------------------------------------------------------------------------------------------ */
.search-results-box { }
.search-results-box :where(.search-results-header) { margin-block-end: var(--space-m); }
.search-results-box :where(.search-results-count) {
  font-size: var(--step--1);
  color: color-mix(in srgb, currentColor 72%, transparent);
}
.search-results-box :where(.search-results-list) { row-gap: var(--space-m); }
.search-results-box :where(.item-inner) {
  block-size: 100%;
  display: flex;
  flex-direction: column;
  border: 1px solid color-mix(in srgb, currentColor 14%, transparent);
  border-radius: var(--ial-r-md);
  overflow: clip;
  transition: box-shadow var(--ial-t-base);
}
.search-results-box :where(.item-inner:hover) { box-shadow: var(--ial-shadow-sm); }
.search-results-box :where(.item-miniature) {
  aspect-ratio: 4 / 3;
  overflow: clip;
  background-color: color-mix(in srgb, currentColor 5%, transparent);
}
.search-results-box :where(.item-miniature img) {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  transition: transform var(--ial-t-base);
}
.search-results-box :where(.item-inner:hover .item-miniature img) { transform: scale(1.03); }
.search-results-box :where(.item-details) {
  flex: 1 1 auto;
  padding: var(--space-s);
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}
/* [MODULS-B · GE 2026-09-25 · §2.7-bis] Subjecte reservat "a": ancestre dins de :where(). */
/* [MODULS-B · FINE-1 · decisió (e) 25/09] Mateix fix que a AutoMenu/Languages: només l'arrel
   (.search-results-box) surt del `:where()`; `.item-title` es queda a dins per no superar el
   sostre G1 (0,2,0). `color: inherit` guanya Estils, (0,1,1) > (0,0,2). Excepció
   `surface_contexts` (GE.json, ".search-results-box") a MB-G2. */
.search-results-box :where(.item-title) a { text-decoration: none; color: inherit; }
:where(.search-results-box .item-title) a:hover { text-decoration: underline; }
:where(.search-results-box .item-title) a:focus-visible {
  outline: 2px solid var(--ial-c-focus);
  outline-offset: 2px;
  border-radius: var(--ial-r-sm);
}
.search-results-box :where(.item-summary) { flex: 1 1 auto; }
.search-results-box :where(.item-summary p) {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.search-results-box :where(.search-no-results) { text-align: center; padding: var(--space-l) var(--space-s); }
.search-results-box :where(.search-no-results .search-suggestions) {
  font-size: var(--step--1);
  color: color-mix(in srgb, currentColor 72%, transparent);
}

/* ------------------------------------------------------------------------------------------
 * Moviment reduït (§1.6) — cobreix totes les transicions/animacions d'aquesta secció.
 * ------------------------------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .tabsmodule :where(.pestanya) { transition: none; }
  .menu :where(.menu-link),
  .menu :where(.menu-link .toggler),
  .menu :where(.dropdown-item .toggler-inside),
  .menu :where(.dropdown-item),
  .menu :where(.nav-item.dropdown > .dropdown-menu),
  .menu :where(.menu-toggler-icon),
  .menu :where(.menu-toggler-icon)::before,
  .menu :where(.menu-toggler-icon)::after { transition: none; }
  .menu-horizontal :where(a),
  .menu-vertical :where(a) { transition: none; }
  .languages :where(.lang-item a) { transition: none; }
  .breadcrumb-container :where(.breadcrumb-item--link) { transition: none; }
  .buscador-list-item :where(.buscador-group-inner button[type="submit"]),
  .buscador-list-item :where(.search-toggler),
  .buscador-list-item :where(.search-dropdown),
  .buscador-list-item :where(.search-result-item),
  .buscador-list-item :where(.buscador-group) { transition: none; }
  .buscador-list-item :where(.search-spinner svg) { animation: none; }
  .search-results-box :where(.item-inner),
  .search-results-box :where(.item-miniature img) { transition: none; }
}
/* ==== MODULS-B · GE · NAVEGACIÓ I INTERACTIUS — END ==== */

/* ==== MODULS-B · GF · FORMULARIS — BEGIN (fase 1 core; no editar fora del grup) ==== */
/* [MODULS-B · GF 2026-09-25] Composició de formularis (SPEC_MODULS_B.md §1, §2, §5 GF).
   NO es redefineix .btn/.btn-brand ni .form-control/.form-select/.form-check-input: són
   primitives de la secció 00, escrites per G0b. Aquest grup només col·loca el contenidor
   (graella, ritme, etiquetes, fila de consentiment/enviament, missatges, mida tàctil).
   Tècnica de graella: cada camp és `grid-column: 1/-1` per defecte; els que embolcallen un
   `<input>` passen a `span 1` (2 columnes a >=768px); la fila de consentiment (`.checkbox-inline`)
   i la d'enviament (`:has(button)`) tornen a `1/-1`, en aquest ordre de cascada (mateixa
   especificitat gràcies a `:where()`, guanya la que ve després). Així el textarea (Comentario)
   queda a amplada completa sense haver-lo de referenciar mai pel seu nom d'etiqueta. */

/* ---- SimpleForm (.contact-form-wrapper) ---- */
.contact-form-wrapper :where(.row) {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-s) var(--space-m);
  margin-block: 0;
}
@media (min-width: 768px) {
  .contact-form-wrapper :where(.row) { grid-template-columns: repeat(2, 1fr); }
}
.contact-form-wrapper :where(.form-group) { grid-column: 1 / -1; }
.contact-form-wrapper :where(.form-group:has(input)) { grid-column: span 1; }
.contact-form-wrapper :where(.form-group:has(.checkbox-inline)) { grid-column: 1 / -1; }
.contact-form-wrapper :where(.form-group:has(button)) { grid-column: 1 / -1; }
.contact-form-wrapper :where(.row > small) { grid-column: 1 / -1; }

.contact-form-wrapper :where(.form-group label:not(.checkbox-inline)) {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
}
.contact-form-wrapper :where(.checkbox-inline) {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  min-block-size: 44px;
  padding-block: var(--space-3xs);
}
.contact-form-wrapper :where(.checkbox-inline input[type="checkbox"]) {
  inline-size: 20px;
  block-size: 20px;
  flex: 0 0 auto;
  accent-color: var(--bs-primary);
}
@media (min-width: 576px) {
  .contact-form-wrapper :where(.checkbox-inline) { min-block-size: 40px; }
}
.contact-form-wrapper :where(.form-group:has(button)) {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
}
.contact-form-wrapper :where(.form-group:has(button)) button:where(:last-child) {
  margin-inline-start: auto;
}
@media (max-width: 575.98px) {
  .contact-form-wrapper :where(.form-group:has(button)) button:where(:last-child) {
    margin-inline-start: 0;
    inline-size: 100%;
  }
}
.contact-form-wrapper :where(.text-danger) {
  padding-inline-start: var(--space-2xs);
  border-inline-start: 2px solid color-mix(in srgb, currentColor 14%, transparent);
}
.contact-form-wrapper :where(.text-success) {
  padding-inline-start: var(--space-2xs);
  border-inline-start: 2px solid color-mix(in srgb, currentColor 14%, transparent);
}

/* ---- AdvancedForm (.form-wrapper), formulari muntat pel form-render de tercers ---- */
.form-wrapper :where(.rendered-form) {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-s) var(--space-m);
  margin-block: 0;
}
@media (min-width: 768px) {
  .form-wrapper :where(.rendered-form) { grid-template-columns: repeat(2, 1fr); }
}
.form-wrapper :where(.rendered-form .form-group) { grid-column: 1 / -1; }
.form-wrapper :where(.rendered-form .form-group:has(input)) { grid-column: span 1; }
.form-wrapper :where(.rendered-form .form-group:has(button)) {
  grid-column: 1 / -1;
  display: flex;
  justify-content: flex-end;
}
/* [MODULS-B · FINE-1 · P0-13] Abans el flex anava a `label` (`:not(.checkbox-inline)`), però
   l'etiqueta del form-render NO embolcalla el control: conté el text + `span.formbuilder-required`
   («*») + `.tooltip-element` com a GERMANS. `display:flex;flex-direction:column` hi trencava
   l'asterisc i el tooltip a una línia pròpia (problema 1 de l'anàlisi). El flex-column ara va al
   GRUP (`.form-group`), que sí que embolcalla `label` + control: exclou el grup del botó
   (`:not(:has(button))`, que ja té el seu propi `display:flex` d'amplada completa més amunt). */
.form-wrapper :where(.rendered-form .form-group:not(:has(button))) {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
}
/* [MODULS-B · FINE-1 · P0-13] El botó que el formBuilder de tercers posa per defecte és
   `btn-default btn` / `btn-primary btn` (className del JSON), MAI `.btn.btn-brand`: la primitiva
   de `00` no li arriba. Es dona l'aspecte de la primitiva P5 amb un subjecte propi
   (`.btn:not(.btn-brand)`), qualificat dins de `.form-wrapper` perquè no toqui cap altre botó de
   la pàgina. */
/* [MODULS-B · GF · FINE-E3] `advancedform.html` passa `disableInjectedStyle:'bootstrap'` a
   `formRender()`: form-render.min.js treu la classe `formbuilder-embedded-bootstrap` de
   `.rendered-form` un cop pintat, així que el seu <style> injectat
   (`.rendered-form.formbuilder-embedded-bootstrap .btn{…}`, (0,3,0)) deixa de casar amb res —
   ja no hi ha cap injecció a guanyar, i el marcador d'importància forçada (abans necessari a 5
   propietats) hi sobra. `.form-wrapper`/`.rendered-form` van dins d'un `:where()` i
   `.btn:not(.btn-brand)` en surt: base real (0,2,0), que ja guanya sense cap marcador el `.btn`
   (0,1,0) de `bootstrap.min.css` (l'únic competidor en mode core; en mode Estils, `system.css`
   reforça a (0,3,0) i mana igualment, com qualsevol altre botó). */
:where(.form-wrapper .rendered-form) .btn:not(.btn-brand) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  padding: var(--space-2xs) var(--space-s);
  border: 1px solid transparent;
  border-radius: var(--ial-r-md);
  background-color: var(--bs-primary);
  color: var(--ial-c-text-inverse);
  font-size: var(--step-0);
  font-weight: 600;
  text-decoration: none;
}
/* Hover: `.btn:not(.btn-brand):hover` és (0,3,0) real — guanya el `.btn:hover` de Bootstrap (que
   no arriba a (0,3,0)) per especificitat real, sense necessitat de cap marcador d'importància
   forçada. */
:where(.form-wrapper .rendered-form) .btn:not(.btn-brand):hover {
  background-color: color-mix(in srgb, var(--bs-primary) 88%, var(--ial-c-surface));
  color: var(--ial-c-text-inverse);
}
:where(.form-wrapper .rendered-form) .btn:focus-visible:not(.btn-brand) {
  outline: 2px solid var(--ial-c-focus);
  outline-offset: 2px;
}

/* ---- Register (form:where(.legacy-register)) ---- */
form:where(.legacy-register) .form-group {
  margin-block-end: var(--space-s);
}
form:where(.legacy-register) .input-group {
  position: relative;
  display: flex;
  align-items: center;
}
form:where(.legacy-register) .input-group-append {
  position: absolute;
  inset-inline-end: var(--space-2xs);
}
form:where(.legacy-register) .password-requirements {
  font-size: var(--step--1);
  color: color-mix(in srgb, currentColor 72%, transparent);
  margin-block-start: var(--space-3xs);
}
form:where(.legacy-register) .form-checks {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  margin-block-end: var(--space-s);
}
form:where(.legacy-register) .checkbox-inline {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  min-block-size: 44px;
  padding-block: var(--space-3xs);
}
form:where(.legacy-register) .checkbox-inline :where(input[type="checkbox"]) {
  inline-size: 20px;
  block-size: 20px;
  flex: 0 0 auto;
  accent-color: var(--bs-primary);
}
@media (min-width: 576px) {
  form:where(.legacy-register) .checkbox-inline { min-block-size: 40px; }
}
form:where(.legacy-register) .form-send {
  display: flex;
  justify-content: flex-end;
}
@media (max-width: 575.98px) {
  form:where(.legacy-register) .form-send button { inline-size: 100%; }
}

/* ---- MailChimp (.mailchimpform) ---- */
.mailchimpform :where(.row) { margin-block: 0; }
.mailchimpform :where(.form-group) {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  flex-wrap: wrap;
}
.mailchimpform :where(.checkbox-inline) {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  min-block-size: 44px;
  padding-block: var(--space-3xs);
}
.mailchimpform :where(.checkbox-inline input[type="checkbox"]) {
  inline-size: 20px;
  block-size: 20px;
  flex: 0 0 auto;
  accent-color: var(--bs-primary);
}
@media (min-width: 576px) {
  .mailchimpform :where(.checkbox-inline) { min-block-size: 40px; }
}
@media (max-width: 575.98px) {
  .mailchimpform :where(.form-group) button { inline-size: 100%; }
}

/* ---- Login (.main-navigation:where(.legacy-login)) ---- */
.main-navigation:where(.legacy-login) :where(.nav-link) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-inline-size: 44px;
  min-block-size: 44px;
  padding: var(--space-2xs);
}
@media (min-width: 576px) {
  .main-navigation:where(.legacy-login) :where(.nav-link) {
    min-inline-size: 40px;
    min-block-size: 40px;
  }
}
.main-navigation:where(.legacy-login) :where(.dropdown-menu) {
  background-color: var(--ial-c-surface);
  border: 1px solid var(--ial-c-border);
  border-radius: var(--ial-r-lg);
  box-shadow: var(--ial-shadow-md);
  color: var(--ial-c-text-1);
  padding: var(--space-2xs);
}
.main-navigation:where(.legacy-login) :where(.dropdown-menu) :where(a) {
  display: block;
  padding: var(--space-2xs) var(--space-xs);
  min-block-size: 44px;
}
@media (min-width: 576px) {
  .main-navigation:where(.legacy-login) :where(.dropdown-menu) :where(a) { min-block-size: 40px; }
}
/* ==== MODULS-B · GF · FORMULARIS — END ==== */
