/* ===========================================================================
   MASAR DESIGN SYSTEM: the token layer.

   Canonical spec: _brand-assets/masar/lms-design-system.v1.fr.md (claude-PM).
   Loaded LAST in partials/_header.blade.php, after custom.css, so it is
   authoritative over the whole Infix theme layer.

   WHY THIS FILE EXISTS
   The same design lives in more than one place: this repo's theme CSS (the
   Infix layer) and the brand layer baked into front_pages.details (generated
   by _clients/masar/ops/scripts/sections.py). Before this file there was no
   token in either, so every corner radius was a literal typed at the point of
   use. The two layers drifted: the navbar CTA sat at 2px, the theme button at
   4px, the hero CTA at 5px, cards at 10px. Eleven distinct radii were in use
   against a four value scale.

   THE RULE: corner radius is ONE decision, expressed once, here.
   Change --mb-r and the entire surface follows. Never type a radius literal
   into a component again; if a component needs a corner, it reads the token.

   WHY !important IS USED HERE, DELIBERATELY
   This is a normalisation layer over vendor CSS we do not control. The page
   builder injects its section stylesheets (css/homepageV7/*.css) inside the
   BODY, after everything in the head, and several vendor rules carry two
   classes. Winning by document order is therefore impossible from the head,
   and winning by specificity would mean writing selectors nobody can read.
   The token layer is declared authoritative instead: a component that wants a
   different corner does not fight this file, it changes the token or joins the
   pill list below. Loading in the head rather than at the end of the body is
   also what keeps corners from visibly reflowing on first paint.

   Conformance is measured, not assumed:
     node masar-design-audit.mjs https://test.masar-cc.tn 1440
   A passing page reports exactly two radius groups: the token and
   pill/circle. Anything else is drift and is a defect.
   =========================================================================== */

:root {
  /* Corners. 0 = straight edges, the locked direction (Louay, 2026-08-26).
     This is the only place the value is written for the theme layer; the
     brand layer declares the same token in sections.py. */
  --mb-r: 0px;

  /* Genuinely circular elements ONLY: notification bubbles, avatars, social
     chips, toggles. A pill is a SHAPE, not a corner treatment, so it is a
     separate token and is deliberately exempt from --mb-r. */
  --mb-r-pill: 999px;
}

/* ---------------------------------------------------------------------------
   1. Theme primitives. These are the Infix components that carried their own
   hardcoded radius. Listed explicitly rather than swept with a universal
   selector so that nothing circular is squared off by accident.
   --------------------------------------------------------------------------- */
.theme-btn,
.theme_btn,
.secondary-btn,
.primary-btn,
.cookie_btn,
.btn,
button.accordion-button,
.heading-category,
.heading-search-box,
.cta-section,
.theme_cookies,
.form-control,
.form-select,
input[type="text"], input[type="email"], input[type="password"],
input[type="search"], input[type="number"], input[type="tel"],
select, textarea,
.card,
.nice-select,
.contact_info,
.dismissButton,
.btn-close,
.primary_checkbox .checkmark,
.single-course,
.course-item,
.team-section-item,
.blog-item,
.instructor-item {
  border-radius: var(--mb-r) !important;
}

/* Compound corners the theme sets on open/closed accordion rows. Written out
   so the collapsed and expanded states cannot drift apart again. */
.accordion-button,
.accordion-button.collapsed,
.accordion-item,
.accordion-collapse {
  border-radius: var(--mb-r) !important;
}

/* Media. An image with a soft corner against a square container reads as a
   mistake, so images follow the same decision. */
.course-item img,
.blog-item img,
.instructor-item img,
.team-section-item img,
.payment-methods,
figure img {
  border-radius: var(--mb-r) !important;
}

/* ---------------------------------------------------------------------------
   2. The pill exemption. Anything genuinely round keeps its shape.

   Note on .checkmark: the theme reuses that one class for BOTH a square
   checkbox (.primary_checkbox, was 3px) and round radios (.primary_checkbox2
   and .primary_bulet_checkbox, both 50%). Only the checkbox variant is
   squared above. A blanket .checkmark rule would have flattened every radio
   on the catalogue filters, which is the category error this file exists to
   prevent.

   Third party chrome is deliberately NOT normalised: the Google Maps controls
   on the contact page (.gm-control-active and friends) are Google's own UI,
   not a Masar surface, and restyling them is neither our call nor stable.
   The audit will keep reporting them on that page; that is expected.
   --------------------------------------------------------------------------- */
.notify_count,
.heading-cart-items,
.rounded-circle,
.social-links a,
.social-links,
.avatar,
.user-avatar img,
.form-switch .form-check-input {
  border-radius: var(--mb-r-pill) !important;
}

/* ---------------------------------------------------------------------------
   3. Touch targets.

   The theme carries its own rule in custom.css, but it sets min-height ONLY and
   names nine selectors explicitly. That is the same failure mode the radius had:
   an enumerated list of instances instead of a rule, and only one of the two
   axes. It is why a mobile audit still found the menu button at 40x44 and the
   cart at 23x20 while both "passed" the height rule.

   One token, both axes. min-height is inert on an inline box, so the icon-sized
   controls get a display that can honour it.

   This does not claim to reach every control. It reaches the ones a mobile audit
   found; the audit is what finds the next one:
     node design-conformance-audit.mjs <url> --mobile
   --------------------------------------------------------------------------- */
:root { --mb-touch: 44px; }

@media (pointer: coarse) {
  .heading-logo,
  .form-control,
  .form-select,
  select,
  textarea,
  input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]) {
    min-height: var(--mb-touch);
  }

  /* A short word in a vertical link list is only as wide as the word. The row
     is what the thumb aims at, so give it the width too. */
  .footer-item ul li a { min-width: var(--mb-touch); display: inline-flex; align-items: center; }

  /* EXEMPT, deliberately: .enrolled-student a is a metadata label that the
     vendor markup happens to wrap in an anchor ("1 Etudiants" on a course
     card). Forcing 44px would distort every card to dignify something nobody
     taps. The right fix is for it not to be an anchor, which is vendor markup
     we are not rewriting here. The audit will keep listing it; that is why it
     is written down. */

  /* Icon-sized controls: both axes, and a box that can actually apply them. */
  .cart_store,
  .thum-bar,
  .social-list a,
  .social-links a,
  .heading-cart,
  button.theme-btn,
  [role="button"] {
    min-width: var(--mb-touch);
    min-height: var(--mb-touch);
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
}

/* ---------------------------------------------------------------------------
   4. Palette tokens (SPEC-DES-001 / lms-design-system.v1.fr.md section 2.1).
   Values are the canonical app-layer palette; components read these tokens and
   never type a colour literal. Added with the composite-lesson learner surface
   (MASAR-MOD-LEDGER 71); adopt progressively elsewhere, never fork the values.
   --------------------------------------------------------------------------- */
:root {
  --masar-blue: #1f39d4;
  --masar-blue-hover: #1730b0;
  --masar-blue-deep: #122480;
  --masar-blue-soft: #e9ecfb;
  --masar-violet: #8e32e4;
  --masar-violet-soft: #f2e7fc;
  --masar-ink: #141620;
  --masar-text: #2b2f3a;
  --masar-muted: #5b6070;
  --masar-line: #e6e8f0;
  --masar-surface: #ffffff;
  --masar-page: #f7f8fc;
  --masar-success: #1a7f42;
  --masar-success-soft: #e7f6ec;
  --masar-warning: #9a5b12;
  --masar-warning-soft: #fff7ed;
  --masar-danger: #b3261e;
  --masar-danger-soft: #fdecea;
}

/* ---------------------------------------------------------------------------
   5. ONE CONTENT COLUMN (added 2026-09-08 with the LP3 home page rebuild).

   The comp's containment rule is a single centred column with white margins,
   header included. The theme shipped three widths instead: the header was a
   full-bleed `.container-fluid`, `homepageV7/footer-v7.css` widens the footer's
   own container to 1530px above 1581px, and the body sat on Bootstrap's 1320px.
   Measured at 1920 before this rule: header logo x=62, footer x=195, sections
   x=300. Three start edges on one page, which is defect 6 of
   lp3-visual-qa-inventory-20260908.md.

   The header is fixed in markup (both `partials/header/1` and `2`, because
   `header_style` is a setting and either can be the live one). The footer is
   fixed here, because the 1530 lives in a vendor stylesheet loaded per page and
   overriding it at the token layer is what this file is for. Both now resolve
   to the same `.container` the whole site uses.

   Deliberately NOT a new width. 1320px is Bootstrap's own xxl container value,
   which every other `.container` on the page already resolves to at these
   widths; it is being restored, not invented. The override is scoped to the
   same media query the vendor rule uses, so nothing below 1581px changes.
   `max-width: none` was tried first and is wrong: it does not fall back to the
   Bootstrap ladder, it makes the footer full-bleed (measured 1440px at 1440).
   --------------------------------------------------------------------------- */
@media (min-width: 1581px) {
  /* The literal this rule shipped with is now the --mb-content-w token declared
     in section 8, same value, one decision. Custom properties resolve at
     computed value time, so a token declared later in this file is still the
     value this rule reads. Measured at 1920 before and after: 1320px wide,
     left edge 300px, unchanged. */
  .footer .container { max-width: var(--mb-content-w) !important; }
}

/* The header element carries its own inline padding, set per breakpoint in
   `homepageV7/header-v7.css` (50px and 90px at the widest, 30px, 12px, 0). That
   padding existed to inset a FULL-BLEED header. With a real `.container` inside
   it, it shifts that container off centre instead: measured at 1920, the header
   container landed at x=280 while the footer and every section sat at x=300, and
   the padding is asymmetric so the offset is not even a consistent one.
   Zeroed on both header variants. The `.container` inside supplies the gutter,
   which is the whole point of using it. */
header.heading,
.header_area { padding-inline: 0 !important; }

/* ---------------------------------------------------------------------------
   6. HEADER CLEARANCE, THE STATIC HALF (added 2026-09-09).

   The header is out of flow and `.heading-clear` is the only thing holding the
   page below it. `homepageV7/header-v7.css` sizes that spacer with three fixed
   numbers, 85 / 78 / 65, against a header whose height is content driven. A
   ResizeObserver in the header partial measures the header and writes the spacer
   an inline height, which is exact and is the runtime answer.

   With scripting DISABLED that observer never runs, and QA measured the original
   defect surviving at 1440: the spacer stayed 85px while the header rendered
   96px, so the hero sat 11px underneath. A JavaScript-only fix is not a fix for
   a page that has to hold up without it.

   So the static default is corrected too, to the header's own MEASURED rendered
   heights at the seven QA viewports: 64, 78, 84.4, 84.4, 96, 84.4, 84.4. Only
   the 1440 band differs from the neighbours, which is why the vendor's single
   desktop number was wrong there and nowhere else. Rounded up, never down: a
   spacer one pixel too tall is invisible, one pixel too short is the overlap.

   The observer stays and still wins, because it is exact and it follows a
   wrapping nav or a longer translated label that no fixed number can predict.
   These are the floor beneath it.

   WHY `!important` HERE, when the rest of this section avoids it. The vendor's
   `.heading-clear { height: 85px }` is UNCONDITIONAL and `header-v7.css` loads
   after this file, so a plain media-query rule here has equal specificity and
   loses on document order. Measured: the 1440 band was written as 96px and still
   rendered 85px. The token layer is declared authoritative over vendor CSS at
   the top of this file, and this is that case.

   The observer still wins over BOTH, because it writes its inline height with
   `priority: important`, and an inline important beats an author important. The
   order is therefore: measured runtime value, then this floor, then the vendor.
   --------------------------------------------------------------------------- */
.heading-clear { height: 85px !important; }

@media (max-width: 767px)                          { .heading-clear { height: 65px !important; } }
@media (min-width: 768px) and (max-width: 991px)   { .heading-clear { height: 78px !important; } }
@media (min-width: 992px)                          { .heading-clear { height: 85px !important; } }
/* The one band where the header grows: measured 96px at 1440. */
@media (min-width: 1400px) and (max-width: 1599px) { .heading-clear { height: 96px !important; } }

/* ---------------------------------------------------------------------------
   7. VIEWPORT FIT (added 2026-09-09, doctrine).

   Louay, verbatim, looking at the LP3 hero: "When adapting the designer's file,
   well, we should be designing according to our design system and our standards
   as well. For example, here we have to respect the main viewport. We have to
   take that into account. as well as spacing and centering. we can update our
   design system as needed but our implementation doesn't have to be out of the
   blue."

   THE RULE. The first section of any page, INCLUDING the header above it, fits
   within `100svh` at every width from 1024 up, with the primary call to action
   fully visible without scrolling.

   WHY IT IS A RULE AND NOT A HERO TWEAK. Reproducing a comp's proportions
   literally is not the same as adapting it. A comp is drawn on a fixed canvas
   with no viewport; artboard 6 is 800 tall on a 1200 column, which is a RATIO of
   0.667. Applied to a 1320px column that ratio is 880px of hero, and once the
   header and the section rhythm are added the call to action lands past the fold
   on the commonest laptop there is.

   MEASURED before this rule, at 1440x800: hero section 1040px tall, call to
   action bottom at 899px against a 800px viewport, so 99px below the fold. Also
   below at 1280x720. Fine at 1440x900, 1920x1080 and 390x844, which is what made
   it easy to miss: it fails on the short viewports, not the small ones.

   THE MECHANISM. A first section's height is
       min(the comp's own proportion,
           100svh - the rendered header - TWO rhythm steps)

   TWO, and it is the rule rather than an implementation detail: the section pads
   top AND bottom, so subtracting a single step leaves the section exactly one
   step taller than the space it was measured into. Built with one step first and
   measured at 1440x800: the call to action was still 33px below the fold. With
   two it lands on the fold and the button clears by 115px. Ruled and accepted
   2026-09-09.

   `svh` and not `vh`, because on mobile `vh` is the tallest possible viewport and
   overflows behind the browser chrome. The header term is the MEASURED height
   published by the header partial's ResizeObserver, not a guess, because the
   header is content driven and a hardcoded number there is the drift that section
   6 of this file exists to remove.

   WHAT COMPRESSES AND WHAT DOES NOT. The section's vertical padding compresses,
   because it is proportional and has slack. The type scale, the 4px spacing scale
   between blocks, and the horizontal composition do NOT: they are the design
   system and they hold. The photo scales inside the reduced height rather than
   being cropped. The copy block stays vertically centred in whatever height is
   left, so the composition reads the same, only tighter.

   BELOW 1024 THE RULE DOES NOT APPLY. Narrow layouts stack, a stacked hero is
   legitimately taller than the viewport, and forcing it to fit would crush the
   copy. The rule is about short viewports on wide screens.
   --------------------------------------------------------------------------- */
:root {
  /* Fallback only. The header partial overwrites this with the measured value on
     load, on resize and on any layout change inside the header. 96px is the
     tallest the header was measured at, so the fallback errs toward a shorter
     hero rather than a call to action under the fold. */
  --mb-header-h: 96px;
}

.mlp3-hero {
  /* The comp's own proportion: artboard 6 is 800 tall on a 1200 column. */
  --mlp3-hero-comp: 66.7cqw;
  /* TWO rhythm steps, not one: the section pads top AND bottom, so a fit that
     subtracts a single step leaves the section one step taller than the
     space it was measured into. Caught by measurement, 1440x800 still 33px
     over after the first attempt. */
  --mlp3-hero-fit: calc(100svh - var(--mb-header-h) - (2 * var(--mlp3-rhythm)));
  --mlp3-hero-h: min(var(--mlp3-hero-comp), var(--mlp3-hero-fit));
}

/* ---------------------------------------------------------------------------
   8. FIXED OVERLAYS AND THE FIRST SCREEN (added 2026-09-17, doctrine).

   THE RULE, and it is the other half of section 7. A viewport-fixed overlay
   never occludes the first section's primary call to action, at any width.
   Section 7 makes the call to action FIT the first screen; a fixed overlay that
   then covers it hands back exactly the defect section 7 removed, and it does it
   without changing a single number section 7 measures.

   MEASURED, 2026-09-17, on the LP3 home page with a fresh consent state:

     1024x768  41.1 % of the call to action covered, hit test div.theme_cookies
     1025x768  41.1 %
     1280x800  44.0 %, hit test div.theme_cookies_info
     1440x900  16.4 %
     390 / 768 / 1920 / 2560   0 %

   The centre of the button still hit-tested to the anchor, so the page passed
   every criterion that asks whether the call to action is VISIBLE and CLICKABLE.
   It is the same shape this workstream has been caught by twice: absent at the
   roughly 2000px window the work is reviewed in, present on the two commonest
   laptop sizes.

   THE CAUSE is a vendor consent bar shaped as a BAR:
     .theme_cookies { position: fixed; left: 40px; bottom: 40px; width: 980px }
   980px pinned to the left edge. The LP3 call to action is left aligned inside
   the centred content column, so from 1024 to 1440 the bar's top edge lands
   inside the button's lower band. At 1920 and up the viewport is tall enough
   that the bar clears it, and at 2560 the bar is still pinned 40px from the left
   edge while the content sits in a column starting at 620px, which is the drift
   QA recorded as the bar floating over the About photograph.

   THE MECHANISM. Not a hero override and not a z-index fight: the overlay is
   given a SHAPE that cannot reach the call to action. A compact toast, bounded
   width, anchored to the END side of the content column, so it occupies the
   gutter the composition already leaves empty rather than spanning the column.
   The button keeps its geometry; section 7's measurements are untouched, which
   was verified by re-running the section 7 instrument at all eight viewports
   before and after this rule (identical to 0.00px on every figure).

   WHY THE END SIDE. The LP3 call to action is left aligned in its column at
   every width from 1024 up, so the end side is the only anchor that is free of
   it by construction rather than by arithmetic that a longer translation or a
   taller button could break.

   WHY LOGICAL PROPERTIES. `frontend_style_rtl.css` is the sheet that loads for
   an RTL locale and it carries its own physical left/right for this component.
   Logical insets follow the direction instead of being mirrored by hand, and
   they beat both physical sheets from here because this file loads last.

   WHY `!important`. Same case the top of this file states, and one more: the
   consent bar's own blade renders a `<style>` block IN THE BODY (it interpolates
   the admin-set background colour), which is later in document order than every
   head stylesheet. Winning from the head without `!important` is not possible.

   ONE CONSEQUENCE, STATED. The consent blade renders the admin-set
   `bg_color` and `text_color` into that body `<style>` block. Overriding them
   here means the consent toast follows the brand tokens and no longer follows
   the two colour pickers in the admin. That is the intended trade: the toast is
   site chrome sitting over the first screen, and a second palette for it is the
   drift this file exists to remove. The pickers still carry their values; they
   simply no longer paint this surface.

   WHAT IS DELIBERATELY NOT `!important`: `display`. The vendor's dismiss script
   is `$('.theme_cookies').hide(500)` / `.show()`, which writes and clears an
   INLINE display. An author `!important` beats a normal inline declaration, so
   `display: flex !important` here would pin the bar open and break consent
   dismissal. The layout below therefore states `display` normally and wins on
   document order alone, which is enough.
   --------------------------------------------------------------------------- */
:root {
  /* The one content column, section 5's number, named. Section 5's footer rule
     reads this token rather than repeating the literal. */
  --mb-content-w: 1320px;

  /* Toast geometry. Width is a bound, not a size: the toast is as narrow as its
     content allows and never wider than this, nor wider than the viewport less
     both gutters. */
  --mb-toast-w: 360px;
  --mb-toast-gutter: 20px;
  /* Anchored to the content column's end edge where there is a gutter to sit in,
     and to the viewport gutter where there is not (below 1360px the max() picks
     the gutter, so the toast never leaves the screen). */
  --mb-toast-inset: max(var(--mb-toast-gutter), calc((100vw - var(--mb-content-w)) / 2));
  /* Elevation, derived from --masar-ink (#141620 = 20 22 32), not a new colour. */
  --mb-elev-1: 0 2px 16px rgba(20, 22, 32, 0.12);
}

.theme_cookies {
  position: fixed !important;
  inset-block-end: var(--mb-toast-gutter) !important;
  inset-inline-end: var(--mb-toast-inset) !important;
  inset-inline-start: auto !important;
  width: min(var(--mb-toast-w), calc(100vw - (2 * var(--mb-toast-gutter)))) !important;
  /* A toast can never be taller than the screen it sits on, whatever the
     translated consent text turns out to be. */
  max-height: calc(100svh - (2 * var(--mb-toast-gutter))) !important;
  overflow-y: auto !important;
  padding: 16px !important;
  /* display is NOT !important: see the note above on the dismiss script. */
  display: flex;
  flex-direction: column;
  align-items: stretch !important;
  justify-content: flex-start !important;
  gap: 12px !important;
  background: var(--masar-surface) !important;
  color: var(--masar-text) !important;
  border: 1px solid var(--masar-line) !important;
  box-shadow: var(--mb-elev-1) !important;
  /* Corners come from section 1, which already lists .theme_cookies. No radius
     literal is typed here, ever. */
}

.theme_cookies .theme_cookies_info {
  display: flex !important;
  /* ROW, stated explicitly. The vendor turns this container into a COLUMN below
     768px, and with only wrap and alignment declared here that column survived:
     measured at 390, the icon sat centred on its own line above a left aligned
     paragraph, which is the vendor's mobile shape wearing our alignment. The
     toast has one shape at every width, so the direction is declared too. */
  flex-direction: row !important;
  flex-wrap: wrap !important;
  align-items: center !important;
  justify-content: flex-start !important;
  text-align: start !important;
  gap: 10px !important;
}

/* Row one is the icon plus the text, and it is stated as a basis rather than
   left to intrinsic widths: the paragraph claims the whole row less the icon and
   its gap, so the buttons cannot fit beside it and wrap to row two on their own.
   A longer translated label simply pushes them onto separate rows, which is a
   taller toast and never a broken one. */
.theme_cookies .theme_cookies_info > .icon {
  flex: 0 0 32px !important;
  width: 32px !important;
  height: 32px !important;
  /* The vendor gives this badge `border-radius: 50%`, which is the only curved
     corner left on a surface ruled square. It reads the token like every other
     component in this file rather than a literal, so it follows --mb-r if the
     corner decision ever changes. Size and colour are untouched.

     MEASURED, and it is why the rest of this rule exists: squaring the CONTAINER
     does not square what you SEE. The peach disc is not CSS. It was
     `<circle r="40" fill="#ffe5d3">` filling the whole viewBox of the vendor's
     `img/icon/cookies_icon.svg`, and this element computes
     `background-color: rgba(0, 0, 0, 0)` with `overflow: visible`, so its radius
     was painting nothing at all. */
  border-radius: var(--mb-r) !important;

  /* THE VISIBLE HALF. `img/masar-lp3/cookies-icon.svg` is a MASAR OWNED COPY of
     the vendor icon with exactly one element changed: the full viewBox background
     circle is now a rect with the same #ffe5d3 fill over the same box. One line
     differs between the two files; all 26 glyph paths and the five crumb circles
     are byte identical, and the vendor file is untouched.

     WHY THAT PATH. `public/uploads/` is the UPLOADS tree and is not in git, so a
     file there is not a deliverable. `img/masar-lp3/` is a TRACKED theme path that
     already holds this build's Masar owned SVGs, and it sits beside the
     `css/masar-*.css` files, which is where the rest of this override layer lives.

     WHY A BACKGROUND AND NOT A NEW `src`. The icon is an `<img>` whose src the
     consent blade interpolates from the DATABASE, `assetPath($cookie->image)`. CSS
     cannot rewrite a src, and the blade is a vendor file. So the container paints
     the Masar copy and the vendor `<img>` is taken out of flow below. The URL is
     RELATIVE to this stylesheet, so it carries no host and survives any domain.

     The `display: none` below deliberately carries NO `!important`: this file does
     not put an author important on `display` anywhere inside this component, so
     that the rule can never be confused with one that would fight the consent
     script's own inline show and hide. It wins on specificity alone, 0-3-1 against
     the vendor's 0-2-1. */
  background-image: url("../img/masar-lp3/cookies-icon.svg") !important;
  background-repeat: no-repeat !important;
  background-position: center !important;
  background-size: contain !important;
}

.theme_cookies .theme_cookies_info > .icon > img { display: none; }

.theme_cookies .theme_cookies_info > p {
  /* 46px, not 42: the icon is 32 and the gap is 10, so 42 makes row one add up to
     the line width EXACTLY and a sub-pixel rounding either way decides whether
     the paragraph wraps under the icon. 46 leaves 4px of slack, which is the
     difference between a layout that holds and one that holds on this machine. */
  flex: 1 1 calc(100% - 46px) !important;
  min-width: 0 !important;
  max-width: none !important;
  margin-bottom: 0 !important;
  font-size: 14px !important;
  line-height: 1.5 !important;
  color: var(--masar-text) !important;
}

.theme_cookies .theme_cookies_info > .cookie_btn {
  flex: 1 1 auto !important;
  margin: 0 !important;
  padding: 12px 16px !important;
  min-height: var(--mb-touch) !important;
  font-size: 14px !important;
  font-weight: 600 !important;
  white-space: nowrap !important;
  background: var(--masar-blue) !important;
  color: var(--masar-surface) !important;
  border: 1px solid var(--masar-blue) !important;
}

.theme_cookies .theme_cookies_info > .cookie_btn:hover {
  background: var(--masar-blue-hover) !important;
  border-color: var(--masar-blue-hover) !important;
}

/* The customise control is secondary and reads as secondary. */
.theme_cookies .theme_cookies_info > .cookeSettingModalBtn {
  background: transparent !important;
  color: var(--masar-blue) !important;
  border: 1px solid var(--masar-line) !important;
}

.theme_cookies .theme_cookies_info > .cookeSettingModalBtn:hover {
  background: var(--masar-blue-soft) !important;
  border-color: var(--masar-blue) !important;
}

/* ---------------------------------------------------------------------------
   9. FIXED OVERLAYS, THE GENERAL RULE (added 2026-09-17, finding C1).

   SECTION 8 WAS SCOPED TOO NARROWLY AND THAT IS THE DEFECT, not the toast.
   It said a viewport-fixed overlay never occludes THE FIRST SECTION'S PRIMARY
   CALL TO ACTION, which is a sentence about the home page written while looking
   at the home page. The toast then shipped and covered 81.4 percent of the only
   clickable contact on `/contact-us` at 390, with `elementFromPoint` at the
   link's centre returning the toast rather than the anchor. Worse than the
   defect section 8 replaced, because that one always left the button clickable
   at its centre.

   THE RULE, restated at the right scope:

     A VIEWPORT-FIXED OVERLAY NEVER OCCLUDES INTERACTIVE CONTENT, ON ANY PAGE,
     AND NEVER STACKS ON TOP OF OTHER FIXED CHROME.

   Interactive content, not "content": a link, a button, a field, anything that
   takes the pointer or the keyboard. That is the harm. Covering a paragraph is
   a blemish; covering the only email address on a contact page is a broken page.

   WHAT IS MEASURABLE ABOUT IT, so it is a rule and not a wish. For every element
   matching `a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"])`
   that is visible on the first screen and is not inside the overlay itself, the
   overlay's box must intersect it at ZERO area, and `elementFromPoint` at its
   centre and at its edges must return the element and not the overlay.

   THREE MECHANISMS, and each one is here because a measurement demanded it.

   A. THE PAGE RESERVES THE BAND. While a consent decision is pending the page
      carries bottom padding the size of the overlay's band, so nothing at the end
      of the document is TRAPPED underneath it. Without this, the last thing on
      every page is unreachable while the toast is open, which is the failure mode
      that does not show up at scroll 0 and is why it is easy to miss.

   B. THE OCCUPANTS OF AN EDGE STACK. The theme ships its own fixed mobile bottom
      bar, `.heading_icons`, four links, `z-index: 90`, MEASURED at 56px up to
      767, 70px from 768, and `display: none` from 992 up. The toast sat on top of
      it, so four navigation links hit-tested to the toast at 390 and two at 768.
      Two fixed bars cannot OVERLAP on one edge, so the toast rests on the bar and
      both keep their place.

      THE REJECTED VERSION IS RECORDED BECAUSE IT LOOKED RIGHT. The first fix hid
      the bar while consent was pending. It zeroed the same six occlusions and it
      was worse: a visitor who never answers the toast never gets the site's
      mobile navigation at all, and hiding interactive content is the same outcome
      for the user as covering it, which is the rule at the top of this section
      failing on its own author. Ruled out 2026-09-17.

   C. THE OVERLAY IS COMPACT ENOUGH TO CLEAR THE PAGE'S OWN CONTROLS. Below 768
      the toast is reshaped so its top edge sits above the contact page's email
      link. This is arithmetic, not taste: at 390 the email link's bottom edge is
      at y=690.67 and the band's bottom is 824, so any overlay taller than 133px
      covers it.

   HOW THE PENDING STATE IS READ, WITH NO JAVASCRIPT ADDED. The consent element
   carries a hidden `display` in its inline style attribute when hidden, and an
   EMPTY style attribute when shown: measured on both pages, because jQuery's
   `.show()` clears the inline value rather than writing one, and this file's
   `display: flex` is then what applies. So `:has()` on that attribute is the
   state, it needs no script, and it cannot go stale. `:has()` support was
   asserted in the browser under test rather than assumed.

   WHY THE SELECTOR TESTS TWO SPELLINGS, AND DO NOT SIMPLIFY IT BACK TO ONE.
   THERE ARE TWO PRODUCERS OF THAT ATTRIBUTE AND THEY ARE NOT EQUALLY RELIABLE.

     - THE SCRIPT PATH cannot vary. `$(...).hide()` assigns the PROPERTY, and the
       browser reserialises the whole attribute from the CSSOM. MEASURED: setting
       `el.style.display = 'none'` yields the attribute `display: none;`, space and
       semicolon, every time, whatever the script wrote.
     - THE SERVER PATH is hand typed. `components/footer-cookie.blade.php` renders
       `style="display: none"` as literal text in the template. Nothing normalises
       it, so it is one edit away from `display:none`.

   A selector matching only `display: none` therefore passed BY A COINCIDENCE OF
   SPELLING, and the failure it hid is the silent kind: on `display:none` the
   pending state read TRUE while the notice was hidden, so the page kept 191px of
   padding it did not need and the back-to-top control sat 211px up with no toast
   beneath it. MEASURED on localhost 2026-09-17 at 390, 1024 and on both pages.
   Nothing catches that, because there is no failing assertion for a page that
   looks correct.

   So both spellings are tested, and the match is case insensitive with the ` i`
   flag, because the uppercase variant failed the same way in the same measurement.
   The flag costs nothing in support terms: it has been in every engine since 2016
   and this rule already requires `:has()`, which is years newer, so any browser
   that can read the selector at all can read the flag.

   WHAT IS STILL NOT COVERED, named so it is a known edge and not a surprise:
   internal whitespace such as `display : none`. No producer emits it, the CSSOM
   cannot, and an attribute selector matches substrings rather than patterns, so
   covering every spacing would mean enumerating spellings forever. If a third
   producer is ever added, it belongs in this list.

   WHAT THIS RULE DOES NOT ACHIEVE, stated plainly rather than left to be found.
   A bottom-docked overlay cannot leave every non-interactive BLOCK uncovered on a
   short viewport. Measured on `/contact-us`: the three contact cards run from
   y=605 to y=1110 at 390 in a 844 tall viewport, with 16px gaps, so there is no
   band at the bottom of the first screen that is free of them, at any overlay
   height. The cards' text stays partly covered at 390, 768, 1024, 1025, 1280 and
   1440; the LINKS inside them do not. Zeroing the blocks as well would mean
   either moving the page's content or shrinking the viewport the page lays out
   into, and the second one reintroduces the home page defect: at 1024x768 the
   hero call to action ends at y=627 and the band starts at 592.
   --------------------------------------------------------------------------- */

:root {
  /* THE OTHER OCCUPANT OF THE BOTTOM EDGE, measured rather than assumed. The
     theme ships a fixed mobile bottom bar, `.heading_icons`, four links,
     `position: fixed`, `z-index: 90`. Measured on the rendered page with consent
     already dismissed, so the measurement is of the bar and not of a rule about
     it: 56px at every width up to 767, 69.78px at exactly 768 and 70px from 769,
     and `display: none` from 992 up. Three bands, three values, derived per
     breakpoint because one number would be wrong in two of them. */
  --mb-bottom-chrome: 0px;

  /* The toast's own height ceiling per band. 156px is what it measures from 768
     up. Below 768 it is trimmed and CAPPED, for the reason in mechanism C. */
  --mb-toast-max-h: 156px;

  /* The band the bottom edge owns IN TOTAL: both occupants plus their gutters.
     Derived from the two tokens above rather than typed, so a change to either
     moves the reservation with it. Resolves to 196px from 992 up, 266px from 768
     to 991, and 191px below 768. */
  --mb-toast-band: calc(var(--mb-toast-max-h) + var(--mb-bottom-chrome) + (2 * var(--mb-toast-gutter)));
}

@media (max-width: 991.98px) { :root { --mb-bottom-chrome: 70px; } }

@media (max-width: 767.98px) {
  :root {
    --mb-bottom-chrome: 56px;
    --mb-toast-max-h: 95px;
  }
}

/* A. The page reserves the band while the decision is pending, so nothing at the
   end of the document is trapped under EITHER occupant. */
body:has(.theme_cookies:not([style*="display: none" i]):not([style*="display:none" i])) {
  padding-block-end: var(--mb-toast-band) !important;
}

/* B. THE TWO OCCUPANTS STACK. They do not replace one another.

   The first version of this rule hid the mobile bar while consent was pending,
   and that was wrong on its own terms: a visitor who never answers the toast
   never gets the site's mobile navigation at all, and hiding interactive content
   is the same outcome for the user as covering it. Ruled out 2026-09-17.

   So the toast RESTS ON the bar. Its bottom inset is the viewport gutter, or the
   chrome it has to clear, whichever is larger: one expression, correct in all
   three bands, and it collapses to the plain gutter from 992 up where there is no
   bar, which is what keeps the home page's numbers identical. */
.theme_cookies {
  inset-block-end: max(var(--mb-toast-gutter), var(--mb-bottom-chrome)) !important;
}

/* C. Compact below 768, so the overlay's top edge clears the page's own controls
   once it has been lifted onto the bar.

   THE ARITHMETIC, because this is the band with no slack in it. At 390 the bar's
   top edge is y=788 and the contact page's email link ends at y=690.67, so the
   toast has 97.33px to live in. MEASURED on the rendered page, the layout below
   comes out 94.19px with its top edge at y=693.81, clearing the link by 3.14px.
   Every value here was cut to reach that: 12px type, 6px of vertical padding, a
   5px row gap. What was NOT cut is the touch target: both buttons keep
   `min-height: var(--mb-touch)`, 44px, which is 47 percent of the whole toast and
   is not negotiable. 3.14px is a thin margin and it is stated rather than
   rounded up: it is why the cap below exists.

   The icon is dropped rather than shrunk. MEASURED: with it in the row the
   paragraph wraps to three lines at 390 and the toast comes out 134.53px. */
@media (max-width: 767.98px) {
  .theme_cookies {
    padding: 6px 8px !important;
    gap: 5px !important;
    /* THE GUARD, and it is what makes C a rule rather than a lucky measurement.
       The height is CONTENT driven: one longer translation adds a line and the
       top edge slides back over the email link. The cap is derived from the band
       rather than chosen: 788 - 690.67 = 97.33 is the largest safe height, and
       95 takes it with 2.33px to spare in the worst case against the 3.14px the
       page measures today. `overflow-y: auto` from section 8 means a longer text
       scrolls inside a toast that stays put instead of growing into the page. */
    max-height: var(--mb-toast-max-h) !important;
  }

  .theme_cookies .theme_cookies_info {
    gap: 5px !important;
  }

  .theme_cookies .theme_cookies_info > .icon { display: none; }

  .theme_cookies .theme_cookies_info > p {
    flex: 1 1 100% !important;
    font-size: 12px !important;
    line-height: 1.3 !important;
  }

  .theme_cookies .theme_cookies_info > .cookie_btn {
    padding: 6px 10px !important;
    font-size: 12px !important;
  }
}

/* D. THE THIRD OCCUPANT OF THE BOTTOM EDGE.
   The vendor's back-to-top control (#back-top) is viewport-fixed at the end
   corner with no z-index of its own, so while the notice is pending the toast
   paints over it and its centre hit-tests to the toast's own settings button.
   MEASURED on localhost 2026-09-17: unreachable at EVERY scroll position at
   390, 768, 1024, 1025 and 1280; reachable from 1440 up, where the toast is
   narrower than the gap the control sits in.
   This is the same conflict the stacking rule above resolves for the mobile
   bar, and it gets the same answer rather than a different one: the occupants
   stack, none is hidden. The control rides above the toast for exactly as long
   as the toast is present, and returns to the vendor's own position the moment
   the decision is made, because the selector is the pending state. */
body:has(.theme_cookies:not([style*="display: none" i]):not([style*="display:none" i])) #back-top {
  inset-block-end: calc(var(--mb-toast-band) + var(--mb-toast-gutter)) !important;
}
