/* ==========================================================================
   WordPress / Elementor adaptations.

   masana.css is a BYTE-IDENTICAL copy of the HTML template's stylesheet, so
   syncing the two is a file copy and nothing else. Every difference the CMS
   forces lives in this file instead. If a rule here could have gone in
   masana.css, it is in the wrong file.
   ========================================================================== */

/* ---- brand indigo tokens follow the kit global (round 2026-09-15) -------
   masana.css declares --bg-invert and --text-on-accent as hardcoded hex.
   Here they are made to follow the Elementor kit's "Fons invers" global
   instead, same fallback value, so a colour change in the panel (kit post 5)
   repaints every place that reads either token without editing this file.

   Declared on `body`, not `:root`: the kit writes `--e-global-color-*` on
   `.elementor-kit-5`, which IS the `<body>` element (confirmed: every
   template — home, qui-som, a blog post — serves `elementor-kit-5` on
   `<body>`). A custom property that references another resolves against the
   value visible on the element where it is DECLARED, so on `:root` the kit
   variable does not exist yet (`:root` is above `<body>` in the cascade) and
   the fallback would always win — the token would look tied and never
   actually follow the panel. */
body {
  --bg-invert: var(--e-global-color-mbginvert, #2C3360);
  --text-on-accent: var(--e-global-color-mbginvert, #2C3360);
  /* Round 2026-09-15 (bluish sweep): every remaining near-black/grey-violet
     text and surface token now derives from the same kit global, at the
     tint/alpha the brief computed, so they repaint together with it. */
  --text-primary: var(--e-global-color-mbginvert, #2C3360);
  --text-secondary: var(--e-global-color-mbginvert, #2C3360);
  --text-tertiary: var(--e-global-color-mbginvert, #2C3360);
  --bg-elevated: color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 5%, #fff);
  --border: color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 14%, #fff);
  --border-strong: color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 26%, #fff);
  --shadow-header: 0 2px 18px color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 10%, transparent);
  --shadow-card: 0 10px 30px color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 12%, transparent);
}

/* ---- translucent indigo rules tied to the kit (round 2026-09-15) --------
   masana.css states these as literal rgba(44, 51, 96, …) because a plain
   CSS custom property cannot carry an alpha-mixed colour on its own — each
   needs `color-mix()` against the kit global, which is CMS-only. Checked
   against the rendered site (home, /serveis/, /qui-som/) before writing:
   `.hero-media` and `.servei-card` are both live container classes there,
   so both scrims repaint with the panel. `.btn` is never applied to any
   rendered element (Elementor's Button widget carries `.elementor-button`
   only, confirmed by scanning every post's `_elementor_data`), and the
   mobile panel's own `.primary-nav` box-shadow is zeroed by the (0,3,0)
   override further down in this file — so neither one is bridged; both
   stay at the literal from masana.css and are listed as such in the report. */
.hero-media {
  --hero-shade: linear-gradient(
    100deg,
    color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 72%, transparent) 0%,
    color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 50%, transparent) 34%,
    color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 8%, transparent) 62%,
    color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 0%, transparent) 100%
  );
}
/* .servei-card's own background is a SOLID tone (masana.css:611), not a
   translucent one — the brief's "solid tones and text" rule applies: the
   plain global, no color-mix. It is listed alongside ::after below only
   because both belong to the same component. */
.servei-card { background: var(--e-global-color-mbginvert, #2C3360); }
.servei-card::after {
  background: linear-gradient(
    to top,
    color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 90%, transparent) 0%,
    color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 74%, transparent) 26%,
    color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 28%, transparent) 55%,
    color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 5%, transparent) 100%
  );
}

/* ---- the fixed-chrome offset --------------------------------------------
   The template puts this on `#main`. Hello Elementor renders `<main
   id="content" class="site-main">` instead, and with Theme Builder the
   header/footer are sibling divs rather than part of the theme's own
   markup — so the offset has to move to the element that actually exists.
   Same arithmetic as the template: rule + header, plus the breadcrumb bar
   when a page has one. */
.site-main { padding-top: calc(var(--header-h) + 6px); }
body:has(.breadcrumb) .site-main {
  padding-top: calc(var(--header-h) + 6px + var(--crumb-h));
}

/* Elementor's own top-level wrapper adds nothing we want, and Hello adds a
   default page title we never use — the H1 is authored inside the layout. */
.site-main > .page-header,
.site-main > .entry-header,
.elementor-page .site-main > h1.entry-title { display: none; }

/* ---- Elementor container neutralisation ---------------------------------
   Elementor gives every container a default padding and a boxed width. Both
   fight the template, which does its own spacing with `.section` and its own
   width with `.container`. Zeroed here so a container is a plain flex box
   and the template's classes are the only thing positioning anything.

   Scoped to `.elementor` so the editor's own chrome is untouched. */
.elementor .e-con,
.elementor .e-con-inner {
  --container-default-padding-top: 0px;
  --container-default-padding-right: 0px;
  --container-default-padding-bottom: 0px;
  --container-default-padding-left: 0px;
}

/* Elementor widgets carry a bottom margin from the kit; the template's own
   rhythm already accounts for spacing, so a widget must contribute none. */
.elementor-widget:not(:last-child) { margin-block-end: 0; }

/* Images inside widgets inherit Elementor's `display:inline-block`, which
   reintroduces the baseline gap the template's reset removed. */
.elementor-widget-image img,
.elementor-widget-theme-site-logo img { display: block; }

/* ---- widgets that replace hand-written markup ---------------------------
   The template's own form, nav and carousel JS no-ops on these pages
   (its selectors are absent), because Elementor Pro's widgets provide them.
   These rules re-dress those widgets in the template's design. */

/* Nav menu → the template's `.primary-nav` look. */
.site-header .elementor-nav-menu a.elementor-item {
  padding: var(--sp-2) 0;
  color: var(--text-primary);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}
.site-header .elementor-nav-menu a.elementor-item:hover,
.site-header .elementor-nav-menu a.elementor-item.elementor-item-active {
  color: var(--accent-strong);
}

/* Form → the template's field styling and 4-signal error pattern. */
.elementor-field-group .elementor-field {
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg-pure);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius-sm);
  transition: border-color var(--dur-fast) var(--ease-out-cubic);
}
.elementor-field-group .elementor-field:focus { border-color: var(--accent-strong); }
.elementor .elementor-field-group > label.elementor-field-label {
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  margin-bottom: var(--sp-2);
}
.elementor .elementor-message.elementor-message-danger,
.elementor .elementor-field-group .elementor-message-danger {
  border-left: 8px solid var(--text-primary);
  padding-left: var(--sp-3);
  font-weight: 600;
}

/* Admin bar shifts the whole viewport down; the fixed chrome must follow or
   it sits under the bar for logged-in users only — a bug that is invisible
   to the person building the site and obvious to nobody else. */
body.admin-bar .site-header { top: calc(6px + 32px); }
body.admin-bar .accent-rule { top: 32px; }
body.admin-bar .breadcrumb { top: calc(var(--header-h) + 6px + 32px); }
@media screen and (max-width: 782px) {
  body.admin-bar .site-header { top: calc(6px + 46px); }
  body.admin-bar .accent-rule { top: 46px; }
  body.admin-bar .breadcrumb { top: calc(var(--header-h) + 6px + 46px); }
}

/* ---- Elementor Button → the template's .btn ------------------------------
   Elementor's Button widget puts `.elementor-button` on the anchor and gives
   no control for adding a class to it, so the template's `.btn` rules cannot
   reach it. The alternative was an HTML widget with hand-written markup,
   which renders identically but takes the button out of the editor — and the
   whole point of using WordPress is that the client can change the label.

   So: one documented bridge block. It is the ONLY place in this port where a
   design rule is stated twice; if `.btn` changes in masana.css, change it
   here too. The Form widget's submit is a <button type="submit"> inside
   .elementor-form, NOT a Button widget, so it needs its own selector — it
   rendered with a 4px radius while every other button was a pill. */
.elementor .elementor-widget-button .elementor-button,
.elementor .elementor-form .elementor-button[type="submit"] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: 48px;
  padding: var(--sp-3) var(--sp-8);
  border: 2px solid transparent;
  border-radius: var(--radius-pill);
  background-color: var(--e-global-color-accent, #eea03a);
  color: var(--e-global-color-mbginvert, #2C3360);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  fill: var(--e-global-color-mbginvert, #2C3360);
  transition: background var(--dur-fast) var(--ease-out-cubic),
              transform var(--dur-hover) var(--ease-smooth),
              box-shadow var(--dur-hover) var(--ease-smooth);
}
.elementor .elementor-widget-button .elementor-button:hover,
.elementor .elementor-widget-button .elementor-button:focus,
.elementor .elementor-form .elementor-button[type="submit"]:hover,
.elementor .elementor-form .elementor-button[type="submit"]:focus {
  background: var(--e-global-color-mhover, #db8a1e);
  color: var(--e-global-color-mbginvert, #2C3360);
  transform: translate3d(0, -2px, 0);
  box-shadow: 0 8px 20px color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 22%, transparent);
}
.elementor-widget-button .elementor-button:active { transform: none; box-shadow: none; }

/* ---- Elementor Nav Menu → the template's .primary-nav -------------------
   The mobile panel is Elementor's own dropdown, stretched to the full
   viewport so it reads like the template's overlay. DEVIATION WORTH KNOWING:
   the template's overlay also traps focus, marks the rest of the page
   `inert` and locks scroll. Elementor's dropdown does none of that — it
   manages `aria-expanded` and nothing else. Swap in the template's own
   markup if that accessibility behaviour is required. */
.site-header .elementor-nav-menu--main .elementor-nav-menu {
  gap: clamp(var(--sp-4), 2vw, var(--sp-8));
}
.site-header .elementor-nav-menu--dropdown {
  background: var(--bg-pure);
}
@media (max-width: 900px) {
  .site-header .elementor-menu-toggle { margin-left: auto; }
  .site-header .elementor-nav-menu--dropdown {
    position: fixed;
    inset: calc(var(--header-h) + 6px) 0 0 0;
    overflow-y: auto;
  }
  .site-header .elementor-nav-menu--dropdown a.elementor-item {
    padding: var(--sp-5) var(--gutter);
    font-size: 1.0625rem;
    border-bottom: 1px solid var(--border);
  }
}

/* ---- fixed chrome must be exactly as tall as the design says -------------
   Elementor 4.x gives `.e-con` a 16px block padding that does NOT come from
   the `--padding-*` custom properties the JSON `padding` setting writes —
   setting padding in the layout data left `--padding-top: 0px` while the
   computed padding stayed `16px 0px`.

   It matters more than it looks: `.site-header` is fixed, and `.site-main`'s
   top offset is calculated from --header-h. An extra 32px made the header
   116px tall while content still cleared only 90px, so the first 26px of
   every page sat underneath the header. Measured, not assumed. */
.elementor .site-header.e-con,
.elementor .site-footer.e-con { padding-block: 0; padding-inline: 0; }

/* ---- Elementor inverts every flex row -----------------------------------
   Elementor writes `--flex-direction: column` onto EVERY container and
   `.e-con { flex-direction: var(--flex-direction) }`. The template's CSS
   never states `flex-direction` on these classes because the CSS default is
   already `row` — so under Elementor they silently invert and every flex row
   becomes a column.

   That is what turned the 5-card services grid on the home page into five
   full-width stacked cards. Restoring the variable is better than setting
   flex_direction in the layout JSON: it keeps direction owned by the
   stylesheet, so these classes behave the same in both implementations. */
.elementor .e-con.serveis-grid,
.elementor .e-con.btn-row,
.elementor .e-con.header-inner,
.elementor .e-con.footer-bottom { --flex-direction: row; }
/* The footer's translucent indigo rule (masana.css `.footer-bottom`) is a
   hardcoded rgba; tied to the kit global here so it follows the panel too.
   border-top-color only recolours a border masana.css already declares
   `1px solid` — harmless on the other three selectors above, which declare
   no border-top of their own. */
.elementor .e-con.footer-bottom {
  border-top-color: color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 28%, transparent);
}
/* `.related-serveis` is deliberately NOT in that list. Pinning its direction
   unconditionally at (0,3,0) beat the per-element `@media` rule Elementor
   emits for mobile — same specificity, and this file loads later — so the
   pills could never stack. Its direction comes from the container's own
   Layout settings instead. */

/* ---- card width inside a flex row ---------------------------------------
   Elementor's `.e-con.e-flex` (specificity 0,2,0) declares
   `flex: var(--flex-grow) var(--flex-shrink) var(--flex-basis)` with basis
   `auto`, which outranks the template's `.servei-card { flex: 0 1 calc(...) }`
   (0,1,0). Result: five cards at full container width instead of a 3+2 grid.

   Feeding Elementor's variable rather than out-specifying its shorthand —
   raising specificity would work until the next class is added, whereas the
   variable is the documented seam. Values mirror masana.css exactly:
   3-up, 2-up under 900px, 1-up under 760px. --sp-6 is 24px. */
.elementor .e-con.servei-card { --flex-basis: calc((100% - (var(--sp-6) * 2)) / 3); }

@media (max-width: 900px) {
  .elementor .e-con.servei-card { --flex-basis: calc((100% - var(--sp-6)) / 2); }
}
@media (max-width: 760px) {
  .elementor .e-con.servei-card { --flex-basis: 100%; }
}

/* ---- Posts / Archive widget → the template's .card-grid + .post-card -----
   The template hand-writes blog cards; Elementor's Posts and Archive Posts
   widgets emit their own structure. Rather than replace them with static
   markup (which would stop the client's real posts appearing), the widget is
   re-dressed here to the template's card exactly: square image, 28px radius,
   centred title and body, one column across the whole mobile range.

   Values mirror masana.css — `.card-grid` and `.post-card`. If those change,
   change these. */
.masana-posts .elementor-posts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--sp-8);
}
@media (max-width: 900px) {
  .masana-posts .elementor-posts { grid-template-columns: 1fr; }
}

.masana-posts .elementor-post {
  position: relative;
  text-align: center;
  padding: 0;
  border: 0;
  transition: transform var(--dur-hover) var(--ease-smooth);
}
.masana-posts .elementor-post:hover { transform: translate3d(0, -4px, 0); }

.masana-posts .elementor-post__thumbnail {
  aspect-ratio: 1 / 1;          /* the template's square card image */
  border-radius: var(--radius-card);
  overflow: hidden;
  margin-bottom: var(--sp-6);
  padding-bottom: 0 !important; /* widget sets a padding-bottom ratio hack */
}
.masana-posts .elementor-post__thumbnail img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 620ms var(--ease-smooth);
}
.masana-posts .elementor-post:hover .elementor-post__thumbnail img { transform: scale(1.03); }

.elementor .masana-posts .elementor-post__title {
  font-size: clamp(1.125rem, 1.5vw, 1.375rem);
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.015em;
  margin: 0 0 var(--sp-3);
}
.elementor .masana-posts .elementor-post__title,
.elementor .masana-posts .elementor-post__title a { color: var(--text-primary); text-decoration: none; }
/* The h3 itself was inheriting the brand orange (2.16:1 on white). Invisible
   today because the anchor repaints the text, but a title rendered without a
   link would have shipped unreadable. */
.elementor .masana-posts .elementor-post:hover .elementor-post__title a { color: var(--accent-strong); }

.masana-posts .elementor-post__excerpt p {
  color: var(--text-secondary);
  font-size: 0.9375rem;
  margin: 0;
}

/* Whole card clickable, as in the template — and the accessible name stays
   the title, because the stretched layer is a pseudo-element rather than a
   wrapping link. */
.masana-posts .elementor-post__title a::after {
  content: "";
  position: absolute;
  inset: 0;
}
.masana-posts .elementor-post__thumbnail__link { pointer-events: none; }

/* ---- the nav breakpoint must agree in BOTH systems ----------------------
   Elementor's shipped frontend.min.css hides `.elementor-nav-menu--main`
   inside a hardcoded `@media (max-width: 1024px)`. Moving the kit's tablet
   breakpoint to 900 moved the burger but NOT that rule, which left the band
   901–1024px with the burger hidden AND the desktop menu hidden — no
   navigation at all. Found by counting painted links, not by looking at the
   header, which still appeared normal because the logo and CTA were there.

   Two class selectors (0,2,0) beat Elementor's single class (0,1,0), so this
   needs no !important. The boundary is 900px in three places now — this file,
   the kit's viewport_tablet, and masana.css. They must move together. */
@media (min-width: 901px) {
  .site-header .elementor-nav-menu--main { display: flex; }
  .site-header .elementor-nav-menu--dropdown { display: none; }
  .site-header .elementor-menu-toggle { display: none; }
}
@media (max-width: 900px) {
  .site-header .elementor-nav-menu--main { display: none; }
  .site-header .elementor-menu-toggle { display: flex; }
}

/* ==========================================================================
   Fixes for defects found by auditing rendered COLOUR and PAINT, which the
   geometry checks could not see. Each one made content invisible.
   ========================================================================== */

/* ---- B1: the kit repaints every Heading widget --------------------------
   Elementor's generated per-page CSS carries
     .elementor-widget-heading .elementor-heading-title { color: var(--e-global-color-primary) }
   at specificity (0,2,0), beating every heading rule in masana.css (0,1,1 or
   0,0,1). --e-global-color-primary is the brand indigo, so EVERY heading was
   painted indigo — including the ones sitting on indigo. Measured: the home
   hero h1, the "Serveis" h2 and every CTA-band h2 came out at 1.00–1.32:1.
   They were not merely low contrast, they were invisible.

   Matching (0,2,0) and loading after Elementor is what wins this back. The
   inverted-surface block below must stay AFTER the default. */
.elementor .elementor-widget-heading .elementor-heading-title { color: var(--text-primary); }

/* Raised to (0,3,0) on purpose. The kit writes
   `.elementor-widget-heading .elementor-heading-title` at (0,2,0), and until
   now this block matched it at (0,2,0) too — so the winner was decided by
   load order. The front end loaded the theme last and looked right; the
   EDITOR injects Elementor's CSS last, so the kit won there and every title
   on an indigo band rendered indigo-on-indigo. Same CSS, opposite result,
   which is how the editor ended up disagreeing with the live site.

   At (0,3,0) this wins in both places regardless of order — and still loses
   to a per-widget colour from the Style panel, which Elementor emits at
   (0,4,0), so the client can still recolour any individual heading. */
/* (0,4,0), not (0,3,0). Three classes only ties with what it has to beat,
   so the winner fell to load order — theme last on the front end, Elementor
   last in the EDITOR. That is why the CTA band's heading rendered dark on
   indigo in the editor while the site showed it white. Each of these blocks
   is a container, so `.e-con` adds the class that settles it in both places.
   Still below a per-widget colour from the Style panel at (0,4,0)+. */
/* hero headline removed 2026-09-15 so its colour comes from the widget's own Style control (Pixun). */
.elementor .e-con.serveis .elementor-heading-title,
.elementor .e-con.band-invert .elementor-heading-title,
.elementor .e-con.cta-band .elementor-heading-title,
.elementor .e-con.servei-card .elementor-heading-title,
.elementor .serveis .elementor-heading-title,
.elementor .band-invert .elementor-heading-title,
.elementor .cta-band .elementor-heading-title,
.elementor .servei-card .elementor-heading-title { color: var(--text-invert) !important; }

/* The body copy on those bands has the same exposure. */
.elementor .e-con.cta-band .elementor-widget-text-editor,
.elementor .e-con.cta-band .elementor-widget-text-editor p,
.elementor .e-con.band-invert .elementor-widget-text-editor,
.elementor .e-con.band-invert .elementor-widget-text-editor p,
.elementor .e-con.serveis .elementor-widget-text-editor,
.elementor .e-con.serveis .elementor-widget-text-editor p { color: var(--text-invert) !important; }

/* ---- B2: absolutely-positioned images never painted ---------------------
   The template positions these images `absolute; inset: 0` inside a
   `position: relative` parent. Elementor wraps every widget in
   `.elementor-widget`, which its own CSS sets to `position: relative` — so
   THAT became the containing block, and since its only child is out of flow
   it collapsed to height 0. Measured: hero image 1461x0, card image 384x0.
   The photographs were served (200 OK) and simply never drawn.

   Making the wrappers static hands the containing block back to
   `.hero-media` / `.servei-card`, which are the elements the template
   positions against. */
.hero-media .elementor-widget,
.hero-media .elementor-widget-container,
.servei-card .elementor-widget-image,
.servei-card .elementor-widget-image .elementor-widget-container { position: static; }

/* ---- B5: service cards must be links ------------------------------------
   In the template each card is an `<a class="servei-card">`. The container's
   `link` setting did not produce an anchor, so five primary navigation paths
   were lost and the cards were unreachable by keyboard. The title is now the
   link and this stretches it over the whole card — which also keeps the
   accessible name to the service name rather than the whole card. */
.servei-card .elementor-heading-title a { text-decoration: none; color: inherit; }
.servei-card .elementor-heading-title a::after { content: ""; position: absolute; inset: 0; z-index: 2; }
.servei-card:has(.elementor-heading-title a:focus-visible) {
  outline: 3px solid var(--text-invert);
  outline-offset: 4px;
}

/* ---- B6: Posts widget meta line -----------------------------------------
   #ADADAD on white is 2.24:1. The reference card has no meta line at all,
   so the faithful fix is to remove it rather than recolour it. */
.masana-posts .elementor-post__meta-data { display: none; }

/* ---- B7: form errors ----------------------------------------------------
   Elementor's #D9534F on white is 3.96:1 at 17px — below AA. The reference
   deliberately signals errors with NO hue: border, inset rail, glyph, text.
   Restoring that, including the invalid field's own border, which Elementor
   never marks. */
.elementor-message.elementor-message-danger,
.elementor-field-group .elementor-message-danger { color: var(--text-primary); }

.elementor-field-group .elementor-field[aria-invalid="true"] {
  /* !important is deliberate and confined to this one state. Elementor
     writes field styling as `.elementor-element-<id> .elementor-field-group
     .elementor-field { border-color: … }`, and a per-element selector cannot
     be out-specified from a theme stylesheet without hard-coding widget ids.
     Verified needed: without it the invalid border stayed
     rgb(201,196,214), identical to a valid field. */
  border-color: var(--text-primary) !important;
  border-left-width: 8px !important;
}

/* A2: Elementor kills the focus ring on form fields at (0,3,0). */
.elementor-field-group .elementor-field-textual:focus,
.elementor-field-group .elementor-field:focus {
  outline: 3px solid var(--accent-strong);
  outline-offset: 3px;
}

/* A5: the required asterisk at #FF0000 is 4.00:1 on white. */
.elementor .elementor-mark-required .elementor-field-label:after { color: var(--text-primary); }

/* ---- B3: Theme Builder templates have no .site-main ---------------------
   Archive and single templates are rendered by Elementor as
   `<div class="elementor elementor-location-archive|single">` DIRECTLY on
   <body> — Hello's `<main class="site-main">` is not in the document at all.
   So the fixed-chrome offset never applied and the page title sat at y=92
   while the breadcrumb bar ends at y=134: the heading was underneath it.

   Ordinary pages keep using .site-main; these two locations need the same
   arithmetic applied to their own wrapper. */
.elementor-location-single,
.elementor-location-archive {
  padding-top: calc(var(--header-h) + 6px + var(--crumb-h));
}
body.error404 .elementor-location-single {
  /* the 404 has no breadcrumb */
  padding-top: calc(var(--header-h) + 6px);
}

/* B4 companion: the template styles `img.post-hero`, but Elementor puts the
   class on the widget wrapper, so the image itself got none of it. */
.post-hero img {
  width: 100%;
  height: auto;
  aspect-ratio: 2 / 1;
  object-fit: cover;
  border-radius: var(--radius-card);
  margin-bottom: clamp(var(--sp-10), 5vw, var(--sp-16));
}

/* The invalid-field border COLOUR needs the same specificity Elementor uses
   for its own field styling — the 8px rail applied but the colour did not,
   leaving the border indistinguishable from a valid field. Measured after an
   empty submit: rgb(201,196,214), i.e. still --border-strong. */
.elementor-field-group .elementor-field-textual[aria-invalid="true"],
.elementor-field-group textarea.elementor-field[aria-invalid="true"],
.elementor-field-group select.elementor-field[aria-invalid="true"],
.elementor-field-group input.elementor-field[aria-invalid="true"] {
  border-color: var(--text-primary);
  border-left-width: 8px;
}


/* =========================================================================
   ROUND 8 — defects reported against the HTML template
   ========================================================================= */

/* ---- 8.1 the footer lost its vertical padding ---------------------------
   The rule above zeroes `padding-block` on `.site-header` AND `.site-footer`
   together, because Elementor's `.e-con` adds a 16px block padding that is
   not reachable through the `--padding-*` variables. That is right for the
   header, whose height is fixed by `--header-h`. It is WRONG for the footer:
   the template gives it `padding-block: clamp(48px, 5vw, 64px)` and zeroing
   it made the footer content sit flush against both edges of the orange
   band. Restored at a specificity that clears the zeroing rule (0,3,0). */
.elementor .site-footer.e-con.e-parent {
  padding-block: clamp(var(--sp-12), 5vw, var(--sp-16));
}

/* ---- 8.2 the mobile menu could never appear -----------------------------
   `masana.css` turns `.primary-nav` into a full-screen overlay below 900px:
   position fixed, opacity 0, visibility hidden, revealed by the template JS
   adding `.nav-open` to the header. The Elementor menu widget carries the
   `primary-nav` class for its desktop styling, so it inherited the overlay —
   and `.nav-open` is never set here, because the template's nav JS no-ops on
   Elementor markup. The burger lives INSIDE that widget, so the toggle was
   hidden too: no menu and no way to open one.

   The widget is put back to a plain static box; Elementor's own dropdown
   panel (styled further down) provides the overlay instead. */
@media (max-width: 900px) {
  .site-header .elementor-widget-nav-menu.primary-nav {
    position: static;
    inset: auto;
    opacity: 1;
    visibility: visible;
    background: none;
    overflow: visible;
    transition: none;
    /* masana.css now slides its overlay in with a transform, and the widget
       wears `primary-nav`, so it was picking that up too — a 48px-wide
       widget translated 48px to the right. Worse, a transformed ancestor
       becomes the containing block for `position: fixed` descendants, so
       the panel inside it stopped being viewport-sized and collapsed to
       48x0. Same trap as the hidden-overlay one above, one property later. */
    transform: none;
    will-change: auto;
    box-shadow: none;
    pointer-events: auto;
  }
}

/* Elementor switches to its burger at a hardcoded 1024px (`dropdown-tablet`),
   the template switches at 900. Left alone there is a 901–1024px band with
   the desktop menu hidden and the burger shown — chrome that matches neither
   design. Both systems are pinned to the template's 900px boundary here. */
@media (min-width: 901px) {
  .site-header .elementor-menu-toggle { display: none !important; }
  .site-header .elementor-nav-menu--main { display: block !important; }
  .site-header .elementor-nav-menu--dropdown { display: none !important; }
}
@media (max-width: 900px) {
  .site-header .elementor-menu-toggle {
    display: flex !important;
    color: var(--text-primary);
    background: none;
    border: 0;
    padding: var(--sp-2);
  }
  .site-header .elementor-nav-menu--main { display: none !important; }
  /* Elementor only renders the dropdown panel when the toggle is active, so
     this shows nothing until the burger is pressed. */
  .site-header .elementor-nav-menu--dropdown { background: var(--bg-pure); }
}

/* ---- 8.3 the select did not match the template's fields -----------------
   The form bridge enumerates `input` and `textarea`; `select` was never in
   the list, so it kept Elementor's own field styling — 1px #69727d, 4px
   radius, 17px text — beside inputs that had the template's 2px border and
   12px radius. Values copied from `.form-field select` in masana.css. */
.elementor-field-group select.elementor-field,
.elementor-field-group .elementor-field-textual.elementor-select,
.elementor-field-group select.elementor-field-textual {
  width: 100%;
  height: auto;
  padding: var(--sp-3) var(--sp-4);
  padding-right: calc(var(--sp-4) + 20px);   /* room for Elementor's chevron */
  background-color: var(--bg-pure);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--text-primary);
  transition: border-color var(--dur-fast) var(--ease-out-cubic);
}
.elementor-field-group select.elementor-field:focus,
.elementor-field-group select.elementor-field-textual:focus {
  border-color: var(--accent-strong);
}


/* ---- 8.4 the dropdown panel opened at zero height -----------------------
   Elementor keeps `.elementor-nav-menu--dropdown` at `max-height: 0` and
   lifts it to `var(--menu-height)` through a rule keyed on the toggle's
   `.elementor-active` state. That rule is not reaching the panel here (the
   panel is not the toggle's immediate sibling once the bridge repositions
   it), so pressing the burger set aria-expanded="true" and revealed a 360x0
   box — verified live before this rule existed. The panel is already
   `position: fixed` with `inset: calc(var(--header-h) + 6px) 0 0 0`, so
   dropping the cap lets the inset define its height, which is exactly the
   template's full-screen overlay. */
.site-header .elementor-widget-nav-menu:has(.elementor-menu-toggle.elementor-active)
  .elementor-nav-menu--dropdown,
.site-header .elementor-menu-toggle.elementor-active ~ .elementor-nav-menu--dropdown {
  max-height: none;
}

/* "Contacte" is the header CTA on desktop and a normal menu row on mobile —
   the template does this with `.primary-nav .nav-cta`. The WordPress menu
   had no Contacte item at all, so once the burger worked the mobile menu
   still could not reach the contact page. The item now exists carrying the
   `nav-cta` class and is hidden above the breakpoint, where the CTA button
   shows instead. Without this it renders twice on desktop. */
@media (min-width: 901px) {
  .site-header .elementor-nav-menu li.nav-cta { display: none; }
}

/* ---- 8.5 the service card was only clickable on its title ---------------
   In the template `.servei-card` IS the anchor — it wraps the picture and
   the h3, so the whole card navigates. Elementor cannot nest a container
   inside a link, so the link lives on the heading; that left the image and
   the card's padding inert. The same stretched pseudo-element used for the
   blog cards restores the full hit area while keeping the accessible name
   as the service title alone. */
.elementor .e-con.servei-card { position: relative; }

/* Two things make this harder than the blog card. `.servei-card::after` is
   already taken — it is the scrim gradient that keeps the white title
   readable over the photo — so the stretched layer has to hang off the
   link. And the title is `position: absolute` (it overlays the image at the
   card's bottom), which made IT the containing block: the link's `::after`
   measured 336x58, the size of the title, instead of the 384x331 card.

   So the title box is stretched to the card's full height while its text
   stays pinned to the bottom by flex alignment — visually identical — and
   the link's overlay then reaches the card's edges with negative insets
   that cancel the title's own gutters. */
.servei-card .elementor-heading-title {
  top: 0;
  display: flex;
  align-items: flex-end;
}
.servei-card .elementor-widget-heading a::after {
  content: "";
  position: absolute;
  top: 0;
  right: calc(-1 * var(--sp-6));
  bottom: calc(-1 * var(--sp-6));
  left: calc(-1 * var(--sp-6));
  /* No border-radius: a radius here clips the corners out of hit-testing. */
}
.servei-card .elementor-widget-image { pointer-events: none; }
.servei-card .elementor-widget-heading a:focus-visible { outline: none; }
.elementor .e-con.servei-card:has(.elementor-widget-heading a:focus-visible) {
  outline: 3px solid var(--accent-strong);
  outline-offset: 6px;
  border-radius: var(--radius-card);
}


/* ---- 8.6 the post thumbnail jumped on hover -----------------------------
   Elementor's item-ratio machinery absolutely-positions the card image and
   centres it with `transform: scale(1.01) translate(-50%, -50%)`, at
   specificity (0,4,0):
     .elementor-posts-container.elementor-has-item-ratio
       .elementor-post__thumbnail.elementor-fit-height img
   The template's hover is `transform: scale(1.03)`. A transform REPLACES a
   transform, so hovering threw away the centring translate and the image
   lurched down-right by half its own size. Measured: the image was rendered
   568px wide inside a 379px square box.

   The fix is to stop using Elementor's centring at all — the template crops
   with `aspect-ratio` + `object-fit`, which needs no transform, so the hover
   scale has the transform property to itself. Written at (0,5,0) to clear
   Elementor's rule rather than relying on load order. */
.masana-posts .elementor-posts-container.elementor-has-item-ratio
  .elementor-post__thumbnail.elementor-fit-height img,
.masana-posts .elementor-posts-container.elementor-has-item-ratio
  .elementor-post__thumbnail img {
  position: static;
  top: auto;
  left: auto;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: none;
  transition: transform 620ms var(--ease-smooth);
}
.masana-posts .elementor-posts-container.elementor-has-item-ratio
  .elementor-post:hover .elementor-post__thumbnail img {
  transform: scale(1.03);
}


/* ---- 8.7 the stretched card link only covered the heading ---------------
   `.elementor-widget` is `position: relative`, so it — not the card — was
   the containing block for the heading link's stretched `::after`. The hit
   area therefore matched the heading box and the image stayed inert, which
   is the same trap the hero image hit earlier in this port. Verified with
   elementFromPoint over the image: it returned the image widget's DIV, not
   an anchor. */
.servei-card .elementor-widget-heading { position: static; }


/* ---- 8.8 Elementor's default container gap ------------------------------
   Every `.e-con` is a flex box with a 20px default gap. The template does
   all its vertical spacing with margins and padding, so that gap is pure
   addition. In the footer it landed between `.footer-grid` and
   `.footer-bottom`, on top of the template's own 48px margin: measured
   464px tall against the template's 444px, with every other footer
   property already identical.

   Every layout class in masana.css declares its own gap — .btn-row,
   .serveis-grid, .card-grid, .split, .contact-grid, .footer-grid and the
   rest — so the default is spurious wherever the template is in charge,
   which is everywhere. Measured on the home page: 3277px of content in
   WordPress against the template's 3164px, ~20px per container.

   Only the VARIABLES are zeroed, never the `gap` property itself. Elementor
   applies it as `.e-con { gap: var(--row-gap) var(--column-gap) }` at
   specificity (0,1,0) — exactly the same as `.serveis-grid { gap: ... }`.
   Writing `gap: 0` here would therefore beat the template's own gaps on
   load order and collapse every grid in the build; zeroing the variable
   leaves those declarations untouched and winning. */
.elementor .e-con,
.elementor .e-con-inner {
  --gap: 0px;
  --row-gap: 0px;
  --column-gap: 0px;
}


/* ---- 8.9 form field spacing --------------------------------------------
   The template sets `.form-field { margin-bottom: var(--sp-6) }` = 24px;
   Elementor's default row gap is 10px, so the form read visibly tighter
   than the reference.

   NOT matched: the template's fields are also 46px taller apiece, because
   its `.field-error` paragraph carries a `hidden` attribute that its own
   `display: flex` overrides — so an empty error line is permanently laid
   out under every input. That happens to be CLS-safe, but reproducing it
   in Elementor would double up when Elementor inserts its real message,
   so the height difference stands and is recorded here rather than
   papered over. */
.elementor-form .elementor-field-group { margin-bottom: var(--sp-6); }
.elementor-form .elementor-field-group.elementor-field-type-submit { margin-bottom: 0; }


/* =========================================================================
   ROUND 9 — spacing, taken from the HTML template's own numbers
   =========================================================================

   WHY ANY OF THIS IS NEEDED. Every Elementor widget is a flex item of its
   container, and a flex item establishes an independent formatting context.
   So the margins the template puts on `h2`, `p` and friends are trapped
   INSIDE the widget and never produce space between one block and the next.
   Elementor's default 20px container gap used to mask that with a uniform
   fudge; removing it (round 8.8, which the footer needed) exposed every
   place the template's margins could not reach.

   The fix is to re-declare the template's own values on the widget wrapper,
   so the gap that crosses a widget boundary is the gap masana.css asks for.
   Reference values, read from css/styles.css:
     h2                     margin-bottom  --sp-8   32px
     .split-body h2         margin-bottom  --sp-6   24px
     .section-heading       margin-bottom  --sp-12  48px
     p + p                  margin-top     --sp-4   16px
     .prose p + p           margin-top     --sp-5   20px
     .page-head p           margin-top     --sp-4   16px
     .split-body .btn       margin-top     --sp-8   32px
     .serveis/.blog .btn-row margin-top    --sp-12  48px
   ========================================================================= */

/* h1 -> lede in a page head. `.page-head p` cannot match: Elementor's text
   editor renders the lede as a DIV, so the template's rule never fired and
   the two ran together. */
.elementor .page-head .lede { margin-top: var(--sp-4); }

/* A section title and the grid under it. The template's `.section-heading`
   wrapper — the one carrying 48px — appears on exactly two sections, both
   on the home page (`.section.serveis` and `.section.blog`); checked, not
   assumed. Scoped to those, so the rule cannot reach a CTA band or a
   service page heading. */
.elementor .section.serveis > .elementor-widget-heading:has(h2),
.elementor .section.serveis > .container > .elementor-widget-heading:has(h2),
.elementor .section.blog > .elementor-widget-heading:has(h2),
.elementor .section.blog > .container > .elementor-widget-heading:has(h2) {
  margin-block-end: var(--sp-12);
}

/* The CTA band's heading sits 16px off its paragraph, not 32 —
   `.cta-band h2 { margin-bottom: var(--sp-4) }`. A blanket section-title
   rule had been pushing it to 48. */
.elementor .cta-band .e-con > .elementor-widget-heading:has(h2),
.elementor .e-con.cta-band > .elementor-widget-heading:has(h2) { margin-block-end: var(--sp-4); }

/* Any other h2 keeps the global 32px, and 24px inside a split body. Keyed on
   :has(h2) so h1 and h3 — which the template gives no bottom margin — are
   not caught by a blanket heading rule. */
.elementor .e-con > .elementor-widget-heading:has(h2) { margin-block-end: var(--sp-8); }
.elementor .split-body > .elementor-widget-heading:has(h2),
.elementor .prose > .elementor-widget-heading:has(h2) { margin-block-end: var(--sp-6); }

/* Consecutive prose blocks are the template's `p + p`. */
.elementor .e-con > .elementor-widget-text-editor + .elementor-widget-text-editor {
  margin-top: var(--sp-4);
}
.elementor .prose > .elementor-widget-text-editor + .elementor-widget-text-editor,
.elementor .split-body > .elementor-widget-text-editor + .elementor-widget-text-editor {
  margin-top: var(--sp-5);
}

/* Buttons were sitting hard against the paragraph above them. */
/* The `.btn-row` selectors that used to be here set the real `margin-top` at
   (0,3,0)+, which beat the variable-driven rule in round 10 and so blocked a
   panel Margin. They are gone; round 10 supplies the same 32px through
   `--margin-top` at (0,2,0), where the panel can outrank it. The widget
   button keeps a real margin — a widget's Margin control writes the property
   directly, so there is no variable to feed. */
.elementor .split-body > .elementor-widget-button { margin-top: var(--sp-8); }
/* 48px is the template's value for the button under a CARD GRID
   (`.serveis .btn-row`, `.blog .btn-row`) and nowhere else; everywhere else
   it is 32px — the paragraph's own bottom margin, trapped in its widget.
   Both values now live in round 10 as `--margin-top`, so the panel can
   override them. Nothing is declared here any more. */

/* A widget that follows a media block or a grid needs the same air. */
.elementor .e-con > .elementor-widget-image + .elementor-widget-text-editor,
.elementor .e-con > .elementor-widget-image + .elementor-widget-heading {
  margin-top: var(--sp-6);
}

/* ---- 9.2 footer logo -----------------------------------------------------
   Elementor centres image widgets by default, which pushed the footer
   wordmark off the column's left edge while the address under it stayed
   flush left. */
.site-footer .footer-logo,
.site-footer .footer-logo a,
.site-footer .elementor-widget-image { text-align: left; }
.site-footer .footer-logo img { margin-left: 0; margin-right: auto; }

/* ---- 9.3 the contact form needs air -------------------------------------
   Field spacing was raised to the template's 24px in round 8.9; these are
   the gaps inside a field, which Elementor sets tighter than the reference
   (`.form-field label { margin-bottom: var(--sp-2) }`). */
.elementor-form .elementor-field-group > label { margin-bottom: var(--sp-2); }
.elementor-form .elementor-field-group { margin-bottom: var(--sp-7, 28px); }
.elementor-form .elementor-field-type-acceptance { margin-top: var(--sp-2); }
.elementor-form .elementor-field-type-submit { margin-top: var(--sp-4); }

/* ---- 9.4 the mobile menu must match the template ------------------------
   Measured side by side against the reference at 375px. Elementor's
   defaults differed on every one of these: a 38px borderless toggle with a
   3px radius against the template's 48px bordered 12px-radius button, menu
   rows indented by Elementor's own padding instead of sitting on the
   gutter, and the Contacte row rendering as grey-on-orange body text
   instead of the template's pill button. */
@media (max-width: 900px) {
  .site-header .elementor-menu-toggle {
    width: 48px;
    height: 48px;
    padding: 0;
    /* Pixun, 2026-09-08: no border on the menu button. `border-radius`
       dropped with it — nothing else in this rule (or elsewhere on
       `.elementor-menu-toggle`) reads it, checked. The 48x48 tap target is
       unaffected either way: `* { box-sizing: border-box }` (masana.css)
       means the border was already carved out of, not added to, the box —
       removing it just gives the icon 4px more breathing room, it doesn't
       shrink the target. Focus is still visible on keyboard nav: the
       generic `:focus-visible { outline: 3px solid var(--accent-strong);
       outline-offset: 3px }` in masana.css applies here too (nothing
       overrides it for this element, checked both stylesheets and
       Elementor core's frontend.css), and an outline is independent of
       border. */
    background: none;
    color: var(--text-primary);
    align-items: center;
    justify-content: center;
  }
  .site-header .elementor-menu-toggle i,
  .site-header .elementor-menu-toggle svg {
    width: 22px;
    height: 22px;
    font-size: 22px;
    fill: currentColor;
    color: currentColor;
  }

  /* Rows sit on the gutter, as the template's do — its `ul` carries the
     inset and the anchors have no horizontal padding of their own. */
  .site-header .elementor-nav-menu--dropdown a.elementor-item {
    padding: var(--sp-5) 0;
    color: var(--text-primary);
    font-size: 1.0625rem;
    font-weight: 600;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    border-bottom: 0;                 /* the separator belongs to the li */
  }
  .site-header .elementor-nav-menu--dropdown li { border-bottom: 1px solid var(--border); }

  /* Contacte is a pill button in the template, not a menu row. */
  .site-header .elementor-nav-menu--dropdown li.nav-cta { border-bottom: 0; }
  .site-header .elementor-nav-menu--dropdown li.nav-cta a.elementor-item {
    margin-top: var(--sp-6);
    padding: var(--sp-5) var(--sp-6);
    background-color: var(--e-global-color-accent, #eea03a);
    color: var(--e-global-color-mbginvert, #2C3360);
    border-radius: 999px;
    text-align: center;
    justify-content: center;
  }
  .site-header .elementor-nav-menu--dropdown li.nav-cta a.elementor-item:hover {
    background-color: var(--text-on-accent);
    color: var(--bg-pure);
  }
  /* :focus-visible split out from :hover above 2026-09-07 — that combined rule
     is dead for :hover (the (0,3,0) block further down wins the cascade for
     it) but was still live for :focus-visible, so tabbing to the pill kept
     the pre-round indigo/white instead of the current mhover/mbginvert scheme
     every other button-style control uses on hover/focus. */
  .site-header .elementor-nav-menu--dropdown li.nav-cta a.elementor-item:focus-visible {
    background-color: var(--e-global-color-mhover, #db8a1e);
    color: var(--e-global-color-mbginvert, #2C3360);
  }
}


/* ---- 9.5 the clients page ------------------------------------------------
   Same trapped-margin problem, this time inside an HTML widget: the
   template's `.client-list { margin-top: var(--sp-10) }` and the inline
   `margin-top: var(--sp-12)` on the paragraph after it are both sealed
   inside their widget's formatting context. Re-declared on the wrappers.
   Reference: prose -> list 40px, list -> prose 48px. */
.elementor .container > .elementor-widget-html:has(.client-list) { margin-block-start: var(--sp-10); }
.elementor .container > .elementor-widget-html:has(.client-list) + .e-con.prose { margin-block-start: var(--sp-12); }

/* No rule for the carousel's top margin: the heading above it already
   carries the generic 32px `h2` bottom margin, and adding one here stacked
   the two into 64px. Measured, not assumed. */


/* ---- 9.6 paragraph after a tick list ------------------------------------
   masana.css now gives `.tick-list + p` a 24px top margin. In WordPress the
   list and the paragraph are separate widgets, so that adjacency never
   matches — the same trapped-margin problem as everywhere else in round 9.
   Re-declared on the widget wrapper. */
.elementor .e-con > .elementor-widget-html:has(.tick-list) + .elementor-widget-text-editor,
.elementor .e-con > .elementor-widget-text-editor:has(.tick-list) + .elementor-widget-text-editor {
  margin-block-start: var(--sp-6);
}

/* And the "Més informació" button that follows a tick list on the service
   pages: the template gives it 32px (`.split-body .btn { margin-top:
   var(--sp-8) }`), and it was sitting flush against the last bullet. */
.elementor .e-con > .elementor-widget-html:has(.tick-list) + .elementor-widget-button,
.elementor .e-con > .elementor-widget-text-editor:has(.tick-list) + .elementor-widget-button,
.elementor .e-con > .elementor-widget-html:has(.tick-list) + .btn-row,
.elementor .e-con > .elementor-widget-text-editor:has(.tick-list) + .btn-row {
  margin-block-start: var(--sp-8);
}


/* ---- 9.7 the select was drawn twice -------------------------------------
   Elementor wraps a select in `div.elementor-field.elementor-select-wrapper`
   and that wrapper carries the `.elementor-field` class, so the form bridge
   styled it as if it were the field: 2px border, 12px radius, white ground
   and 12px/16px padding — around a select that already had all four. The
   result was a box inside a box, 80px tall against the select's 52px.

   The wrapper goes back to being a plain positioning context for the
   chevron (its `::before`, absolutely positioned), and the select is the
   only thing with a border. */
.elementor-field-group .elementor-select-wrapper {
  border: 0;
  border-radius: 0;
  background: none;
  padding: 0;
  display: block;
}

/* Elementor positions the chevron with a hard `top: 26px`, measured against
   the padded wrapper it expected. With the wrapper collapsed onto the select
   the glyph sat 8px below centre, so it is centred properly instead. */
.elementor-field-group .elementor-select-wrapper { position: relative; }
.elementor-field-group .elementor-select-wrapper::before {
  top: 50%;
  transform: translateY(-50%);
  line-height: 1;
}


/* ---- 9.8 the article page -----------------------------------------------
   Three things masana.css asks for that could not reach Elementor's markup.

   The hero's own `margin-bottom` — clamp(40px, 5vw, 64px) — lost to the
   widget-margin reset, which is written logically as `margin-block-end`, so
   the first paragraph sat 16px under the photo instead of 64.

   The body's `max-width: none` needs to beat Elementor's own width handling
   on the container, hence the `.e-con` in the selector. */
.elementor .e-con > .elementor-widget.post-hero {
  margin-block-end: clamp(var(--sp-10), 5vw, var(--sp-16));
}
.elementor .e-con.prose.post-body { max-width: none; }


/* ---- 9.9 the mobile menu slides in and out from the right ---------------
   State comes from `.site-header.nav-open`, mirrored off the toggle's class
   by masana-wp.js — see the comment there.

   BOTH DIRECTIONS ARE TRANSITIONS, and that matters. The first version held
   the open state with a keyframe animation and `animation-fill-mode: both`.
   Opening looked right; closing snapped. The reason is that the animation
   was overriding the base `translate` from a higher level of the cascade
   while the SPECIFIED value stayed at `100%` the whole time — so removing
   the class removed the animation, the property jumped back to a value it
   had never officially left, and a transition needs a change in specified
   style to fire. Nothing changed, so nothing interpolated.

   Declaring the open value as a real declaration means the specified value
   genuinely changes in both directions, and each gets its own curve: enter
   slower with a long tail-out so it decelerates into place, exit quicker and
   closer to linear, because a panel you have dismissed should leave rather
   than linger.

   `translate` rather than `transform`, because Elementor collapses this
   panel with `transform: scaleY()`; pinning `transform: none` stops that
   squashing it on the way out. Only `translate` and `visibility` change, so
   the travel stays on the compositor. `visibility` is delayed to the end of
   the exit so the panel stays painted while it leaves, and it keeps the
   closed menu out of the tab order — that is why it is not just
   `pointer-events`.

   The selectors carry the extra classes because Elementor styles this
   element at (0,4,0) on the toggle's sibling; ours are (0,5,0). */
@media (max-width: 900px) {
  .site-header .elementor-nav-menu--dropdown {
    max-height: none;
    transform: none !important;
    translate: 100% 0;
    visibility: hidden;
    pointer-events: none;
    box-shadow: -18px 0 40px color-mix(in srgb, var(--e-global-color-mbginvert, #2C3360) 10%, transparent);
    transition: translate 320ms cubic-bezier(0.4, 0, 0.7, 0.2),
                visibility 0s linear 320ms;
  }
  .site-header.nav-open .elementor-nav-menu--toggle
    .elementor-nav-menu--dropdown.elementor-nav-menu__container {
    translate: 0 0;
    visibility: visible;
    pointer-events: auto;
    animation: none;              /* Elementor's `hide-scroll` fights the slide */
    transition: translate 520ms cubic-bezier(0.16, 1, 0.3, 1),
                visibility 0s;
  }

  /* The rows follow the panel in, a beat behind it. The delays are on the
     OPEN state only — on close they all leave together with the panel, and a
     staggered exit would leave rows visibly trailing off the screen edge. */
  .site-header .elementor-nav-menu--dropdown li {
    opacity: 0;
    translate: 12px 0;
    transition: opacity 200ms var(--ease-out-cubic),
                translate 200ms var(--ease-out-cubic);
  }
  .site-header.nav-open .elementor-nav-menu--toggle
    .elementor-nav-menu--dropdown.elementor-nav-menu__container li {
    opacity: 1;
    translate: 0 0;
    transition: opacity 260ms var(--ease-out-cubic),
                translate 260ms var(--ease-out-cubic);
  }
  /* These must carry the same weight as the open-state rule above: its
     `transition` shorthand resets `transition-delay` to 0, and at lower
     specificity the stagger was being wiped — every row arrived at once.
     Caught by stepping the timeline: all six read 100 at t=260ms. */
  .site-header.nav-open .elementor-nav-menu--toggle
    .elementor-nav-menu--dropdown.elementor-nav-menu__container li:nth-child(1) { transition-delay: 130ms; }
  .site-header.nav-open .elementor-nav-menu--toggle
    .elementor-nav-menu--dropdown.elementor-nav-menu__container li:nth-child(2) { transition-delay: 175ms; }
  .site-header.nav-open .elementor-nav-menu--toggle
    .elementor-nav-menu--dropdown.elementor-nav-menu__container li:nth-child(3) { transition-delay: 220ms; }
  .site-header.nav-open .elementor-nav-menu--toggle
    .elementor-nav-menu--dropdown.elementor-nav-menu__container li:nth-child(4) { transition-delay: 265ms; }
  .site-header.nav-open .elementor-nav-menu--toggle
    .elementor-nav-menu--dropdown.elementor-nav-menu__container li:nth-child(5) { transition-delay: 310ms; }
  .site-header.nav-open .elementor-nav-menu--toggle
    .elementor-nav-menu--dropdown.elementor-nav-menu__container li:nth-child(6) { transition-delay: 355ms; }
}

/* The panel still opens and closes; it simply stops travelling. */
@media (max-width: 900px) and (prefers-reduced-motion: reduce) {
  .site-header .elementor-nav-menu--dropdown,
  .site-header.nav-open .elementor-nav-menu--toggle
    .elementor-nav-menu--dropdown.elementor-nav-menu__container {
    translate: 0 0;
    transition: visibility 0s;
  }
  .site-header .elementor-nav-menu--dropdown li,
  .site-header.nav-open .elementor-nav-menu--dropdown li {
    opacity: 1;
    translate: 0 0;
    transition-duration: 0s;
    transition-delay: 0s;
  }
}


/* ---- 9.11 every button gets its air, not just the ones I thought of -----
   The template says `.split-body .btn { margin-top: var(--sp-8) }` — 32px —
   and that margin is trapped inside the button widget here, so round 9 fed
   it back in case by case: buttons after a tick list, buttons in a
   `.split-body`, `.btn-row` containers. Case-by-case missed one. The Veu i
   dades block on /serveis/ has no tick list before its button, so no rule
   matched and the button sat flush against the paragraph — measured 0px
   against 32px on its four siblings.

   So the rule is now general: any button that follows something gets the
   32px. `:not(:first-child)` keeps it off the buttons that open a container,
   whose spacing belongs to the container. */
.elementor .e-con > .elementor-widget-button:not(:first-child) {
  margin-block-start: var(--sp-8);
}
/* The chrome sizes its own button — the header CTA sits in a flex row, not
   in a column of prose. */
.site-header .e-con > .elementor-widget-button:not(:first-child),
.site-footer .e-con > .elementor-widget-button:not(:first-child) {
  margin-block-start: 0;
}


/* ---- 9.12 mobile menu: cover, stacking, and the active row --------------

   1. THE ACTIVE ROW WAS GREY. Elementor paints the current page's item with
      its own `#3f444b` background — measured rgb(63,68,75) behind orange
      text. The panel is white and the row should be too; the accent text is
      already what marks the current page, so the fill only muddied it.

   2. THE PANEL DID NOT RELIABLY COVER THE PAGE. Its computed `z-index` was
      `auto`, so anything on the page that creates its own stacking context —
      a reveal transform, a positioned media block — could paint straight
      through it. It also relied on `top: calc(var(--header-h) + 6px)` being
      exactly right, and measured 11px off. Both go away by covering the
      whole viewport and letting the header, which sits above it, draw the
      logo and the close button over the top. No arithmetic to get wrong and
      no sliver of page showing through.

      z-index 90 is deliberately BELOW the header's 100: the panel must cover
      the page, not the chrome.

   3. THE CLOSE BUTTON is pinned above both, so it can never end up behind
      the panel it dismisses. */
@media (max-width: 900px) {
  .site-header .elementor-nav-menu--dropdown {
    inset: 0;
    z-index: 90;
    background: var(--bg-pure);
  }

  /* The list clears the header rather than the panel starting below it, and
     carries the extra breathing room at the top. */
  .site-header .elementor-nav-menu--dropdown ul.elementor-nav-menu {
    padding-top: calc(var(--header-h) + 6px + var(--sp-8));
  }

  .site-header .elementor-menu-toggle {
    position: relative;
    z-index: 120;
  }
  /* No rule for `.accent-rule` here. It is already `position: fixed` with
     `z-index: 101` in masana.css, and the panel sits at 90, so it was never
     at risk of being covered. An earlier version set `position: relative`
     to "protect" it — which put the 6px bar back into the flow and pushed
     `.site-main` down by exactly 6px on mobile and tablet, the empty strip
     under the breadcrumb. Measured: `.site-main` top 6 at 768, 0 at 1280. */

  /* Current page reads as orange text on the panel's own white, with no
     fill of its own — hover and focus included, so the row never flashes
     grey on the way past. */
  .site-header .elementor-nav-menu--dropdown a.elementor-item,
  .site-header .elementor-nav-menu--dropdown a.elementor-item:hover,
  .site-header .elementor-nav-menu--dropdown a.elementor-item:focus,
  .site-header .elementor-nav-menu--dropdown a.elementor-item.elementor-item-active,
  .site-header .elementor-nav-menu--dropdown a.elementor-item.highlighted {
    background-color: transparent;
  }
  .site-header .elementor-nav-menu--dropdown a.elementor-item.elementor-item-active {
    color: var(--accent-strong);
  }
  /* …except the CTA, which IS a filled pill. */
  .site-header .elementor-nav-menu--dropdown li.nav-cta a.elementor-item,
  .site-header .elementor-nav-menu--dropdown li.nav-cta a.elementor-item:hover,
  .site-header .elementor-nav-menu--dropdown li.nav-cta a.elementor-item.elementor-item-active {
    background-color: var(--e-global-color-accent, #eea03a);
    color: var(--e-global-color-mbginvert, #2C3360);
  }
}


/* ---- 9.13 why the exit still snapped ------------------------------------
   The slide out was correct and never visible. Elementor collapses the
   closed panel with

     .elementor-nav-menu--toggle .elementor-menu-toggle:not(.elementor-active)
       + .elementor-nav-menu__container { max-height: 0; overflow: hidden }

   at (0,4,0), which beat the `max-height: none` in our base rule at (0,2,0).
   Measured the moment the close is pressed: `max-height` back to `0px` and
   the panel's height straight to 0 — the box vanishes on the same frame, so
   there is nothing left on screen for the 320ms translate to move. It looked
   like the transition was not firing; it was firing on an element that had
   already been flattened.

   Same anchor, one class more (0,5,0), so the panel keeps its height all the
   way out and the travel is visible. */
@media (max-width: 900px) {
  .site-header .elementor-nav-menu--toggle .elementor-menu-toggle:not(.elementor-active)
    + .elementor-nav-menu__container {
    max-height: none;
    overflow: hidden auto;
  }
}


/* =========================================================================
   ROUND 10 — the Style panel outranks the theme, without losing the theme
   =========================================================================

   NOTHING IS REMOVED HERE. Every value below is the one the design already
   uses. What changes is only its PRIORITY, so that a number typed into a
   widget's Style panel takes effect instead of being silently ignored.

   How Elementor spaces a container: the panel does not write `padding` — it
   writes the custom properties `--padding-top` / `--margin-top`, which
   `.e-con` then maps and consumes:

     .e-con { --padding-block-start: var(--padding-top);
              padding-block-start: var(--padding-block-start); … }

   A theme rule writing `padding-block:` outright overrides that entire
   chain, so the panel's value lands in a variable nothing reads. The fix is
   to feed the SAME variables the panel feeds.

   THE SPECIFICITY BAND IS THE WHOLE TRICK. The value has to sit:
     ABOVE  Elementor's own default   `.e-con { --padding-top: … }`   (0,1,0)
     BELOW  the panel's output        `.elementor-769 .elementor-element
                                       .elementor-element-<id>`       (0,3,0)
   which leaves exactly (0,2,0). A first attempt used `:where()` — that drops
   to (0,0,0), lost to `.e-con`'s own default, and collapsed section padding
   to zero across the site. Measured, reverted; hence the two-class selectors
   below, each of which is deliberately exactly two classes.

   The `padding-block` / `margin-block-start` line in each rule is plumbing:
   it points the real property back at the variable, overriding masana.css's
   direct declaration (0,1,0). The panel never writes that property, only the
   variables, so it costs nothing to hold it at the same weight. */

.e-con.section {
  --padding-top: clamp(var(--sp-16), 7vw, var(--sp-24));
  --padding-bottom: clamp(var(--sp-16), 7vw, var(--sp-24));
  padding-block: var(--padding-block-start) var(--padding-block-end);
}
.e-con.page-head {
  --padding-top: clamp(var(--sp-12), 5vw, var(--sp-20));
  --padding-bottom: clamp(var(--sp-12), 5vw, var(--sp-20));
  padding-block: var(--padding-block-start) var(--padding-block-end);
}
/* masana.css carries a tighter variant, `.section--tight { padding-block:
   clamp(--sp-12, 5vw, --sp-16) }`, at one class. The `.e-con.section` rule
   above is two, so it was overriding it and inflating six sections from 64px
   to 89.6px — caught by diffing all 24 page/width fingerprints against the
   baseline, not by looking. Restated here at matching weight, after the base
   rule so it wins the tie, and still below the panel. */
.e-con.section--tight {
  --padding-top: clamp(var(--sp-12), 5vw, var(--sp-16));
  --padding-bottom: clamp(var(--sp-12), 5vw, var(--sp-16));
}

/* Declared after `.e-con.section` so it wins the tie at equal specificity,
   exactly as the `.page-head + .section` rule in masana.css does. */
.page-head + .section {
  --padding-top: clamp(var(--sp-12), 5vw, var(--sp-20));
}
.site-footer.e-con {
  --padding-top: clamp(var(--sp-12), 5vw, var(--sp-16));
  --padding-bottom: clamp(var(--sp-12), 5vw, var(--sp-16));
  padding-block: var(--padding-block-start) var(--padding-block-end);
}
.e-con.btn-row {
  --margin-top: var(--sp-8);
  margin-block-start: var(--margin-block-start);
}
.serveis .btn-row,
.blog .btn-row { --margin-top: var(--sp-12); }


/* ---- prose width belongs to the panel, not to the stylesheet ------------
   `masana.css` caps `.legal-prose` at `max-width: 78ch` and `.prose` at
   `68ch`. Both are containers here, and `max-width` beats Elementor's Width
   control at any specificity — the panel writes `--width`/`width`, which a
   max-width simply clamps. Typing 100% did nothing.

   The cap is removed and the SAME value moved into each container's own
   Width setting, so the rendering is unchanged and the field now works. */
/* Only `.legal-prose` gives up its cap — its width is stored in the panel.
   `.prose` keeps `max-width: 68ch` from masana.css: it is a LIMIT, and on the
   split layouts `.split-body` also carries `.prose`, so replacing a limit
   with a forced Width pushed that column 157px past the viewport at 1280. */
.e-con.legal-prose { max-width: none; }


/* ---- "On som" column, rebuilt as native widgets -------------------------
   Heading + Google Maps + Text + Icon List, so every part is editable. These
   rules only dress the new widgets in the site's palette; each is kept at a
   low enough specificity that a value set in the Style panel still wins
   (Elementor emits those at three classes or more). */
.on-som-map iframe {
  border-radius: var(--radius-card);
  display: block;
  width: 100%;
}
.elementor .on-som-list .elementor-icon-list-text { color: var(--text-secondary); }
.elementor .on-som-list a .elementor-icon-list-text { color: var(--accent-strong); }
.elementor .on-som-list .elementor-icon-list-icon i,
.elementor .on-som-list .elementor-icon-list-icon svg {
  color: var(--accent-strong);
  fill: var(--accent-strong);
}
.on-som-list .elementor-icon-list-item { align-items: flex-start; }
.on-som-list .elementor-icon-list-icon { padding-top: 0.15em; }


/* ---- an image must never paint outside its own box ----------------------
   Reported from a real handset: in the Qui som split the photo covered the
   heading that belongs BELOW it. Measured in WordPress, every level around
   that image is `overflow: visible` —

     .split-media            visible   328x205
       .elementor-widget-image visible 328x205
         img                  aspect-ratio 1.6/1, width/height attrs 1200x800

   Chromium resolves the image to exactly 205px so nothing spills. A browser
   that resolves `aspect-ratio` on a REPLACED element differently paints it
   at its larger natural height, and with nothing clipping it, it covers the
   text that follows. Clipping it to its wrapper makes that impossible:
   the photo can be cropped, but it can never sit on top of the heading.

   Applied to the image WIDGET, deliberately, not to `.split-media` — that
   is an Elementor container and `overflow` is one of its panel controls, so
   writing it there would take the setting away from the client. */
.split-media .elementor-widget-image,
.servei-detail .elementor-widget-image,
.elementor-widget.post-hero,
.split-media .elementor-widget-image .elementor-widget-container,
.servei-detail .elementor-widget-image .elementor-widget-container,
.elementor-widget.post-hero .elementor-widget-container {
  overflow: hidden;
  border-radius: var(--radius-card);
}
/* The blog cards already clip — `.masana-posts .elementor-post__thumbnail`
   carries `overflow: hidden` from the square-crop rule — so they are not
   listed again here. */


/* ---- "Altres serveis" pills, now Button widgets -------------------------
   The block was an HTML widget holding `<ul class="related-serveis">`, which
   the client could not edit and whose links could not carry a dynamic tag.
   It is now a container of Button widgets, one per service.

   masana.css styles `.related-serveis a`; Elementor's button is
   `.elementor-button` inside a widget, so the same look is restated here for
   that markup. Values copied from `.related-serveis a` — outlined pill, not
   the filled accent button — and kept at two classes so a value set in the
   Style panel still wins. */
/* Raised to (0,4,0) and stated as the LONGHAND on purpose. In the editor
   these rendered as solid brown pills with brown text on brown fill —
   unreadable — while the front end showed them correctly outlined. Two
   reasons, both fixed here:

     1. This block set `background` (shorthand); the kit sets
        `background-color` (longhand) on `.elementor-widget-button
        .elementor-button`. Longhand and shorthand both write the same
        property, so the one that wins is decided by weight and order — and
        in the EDITOR Elementor's CSS is injected last, where on the front
        end the theme loads last. Same CSS, opposite winner: the identical
        trap as the heading colour.
     2. Three classes tied with the rule it had to beat.

   `.elementor` takes it to (0,4,0), which wins in both places, and it still
   loses to a per-widget colour from the Style panel at (0,4,0)+ declared
   later, so the client can still recolour an individual pill. */
.elementor .related-serveis .elementor-widget-button .elementor-button {
  display: inline-block;
  min-height: 0;
  padding: var(--sp-2) var(--sp-5);
  border: 2px solid var(--accent-strong);
  border-radius: var(--radius-pill);
  /* !important, deliberately, after three rounds of losing this in the
     editor. Specificity was raised twice and the editor still painted these
     as solid brown with brown text on brown — unreadable. The cost is
     stated plainly: the Style panel can no longer set a background or text
     colour on a pill. Everything else about them stays panel-editable. */
  background: transparent !important;
  background-color: transparent !important;
  background-image: none !important;
  color: var(--accent-strong) !important;
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: normal;
  text-transform: none;
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease-out-cubic),
              color var(--dur-fast) var(--ease-out-cubic);
}
.elementor .related-serveis .elementor-widget-button .elementor-button:hover,
.elementor .related-serveis .elementor-widget-button .elementor-button:focus-visible {
  border-color: var(--accent-strong);
  background: var(--accent-strong);
  background-color: var(--accent-strong);
  color: var(--text-invert);
  transform: none;
  box-shadow: none;
}
/* The row's spacing. `masana.css` sets `.related-serveis { gap: var(--sp-3) }`
   — a LITERAL gap from a fixed token, which never reads Elementor's
   `--row-gap`/`--column-gap`. That is not a specificity fight: the value is
   structurally disconnected, so no panel Gap setting could ever reach it.
   Re-pointed at Elementor's variables here, with the template's 12px kept as
   the fallback, so the Layout panel's Gap (including its tablet and mobile
   values) actually applies. */
.elementor .e-con.related-serveis {
  gap: var(--row-gap, var(--sp-3)) var(--column-gap, var(--sp-3));
  --margin-top: var(--sp-6);
  margin-block-start: var(--margin-block-start);
}
.related-serveis .elementor-widget-button { width: auto; }


/* The tablet/mobile pill layout is NOT here. Elementor emits a container's
   own settings at three classes, which beats any two-class rule this file
   can write — measured: a media query setting `--gap` and
   `--flex-direction` was ignored entirely, the gap stayed 12px and the
   direction stayed row. The responsive gap (8px) and the mobile stacking
   live in the container's Layout settings instead, where they win and stay
   editable in the panel. */

/* ---- pills are a row, not a stack of buttons ----------------------------
   `.elementor .e-con > .elementor-widget-button:not(:first-child)` gives
   every button after the first a 32px top margin — right for a button that
   follows a paragraph, wrong for a row of pills. Measured: items 2-5 sat
   exactly 32px below item 1, which is the misalignment. */
.elementor .related-serveis .elementor-widget-button,
.elementor .e-con.related-serveis > .elementor-widget-button:not(:first-child) {
  margin-block-start: 0;
  margin-top: 0;
}
.e-con.related-serveis {
  --align-items: flex-start;
  align-items: flex-start;
}


/* =========================================================================
   Language switcher — [masana_lang_switcher]
   =========================================================================

   Polylang has no Elementor widget, so this is a Shortcode widget sitting in
   `mhdr007` (Header Right), between the socials and the Contacte button.
   Deliberately NOT given hide_tablet/hide_mobile — Contacte and the socials
   both fold away below 900px, and this is the one control that must stay
   reachable at every width, so it stays in the header row next to the
   hamburger toggle instead of moving into the dropdown panel (which is built
   from wp_nav_menu() output and cannot host an arbitrary widget). Text only,
   matched to the nav's own type scale, so it reads as chrome rather than a
   second CTA next to the real one. */
.lang-switcher ul {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.lang-switcher li { display: flex; align-items: center; gap: var(--sp-2); }
.lang-switcher li + li::before {
  content: "/";
  color: var(--border-strong);
}
.lang-switcher a {
  color: var(--text-primary);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-out-cubic);
}
.lang-switcher a:hover,
.lang-switcher a:focus-visible { color: var(--accent-strong); }
.lang-switcher li.current-lang a { color: var(--accent-strong); }
