﻿/* ==========================================================================
   iibiy — mobile-first UX layer
   Loaded after the legacy stylesheets. Everything here is namespaced so it
   adds behaviour without fighting the existing cascade.
   ========================================================================== */

/* ==========================================================================
   DESIGN TOKENS

   Two tiers, deliberately:

     1. PRIMITIVES - raw palette and scales. Components never reference these.
     2. SEMANTICS  - roles ("the canvas", "the accent", "muted text"). The only
        tier components are allowed to use.

   Only the semantic tier flips for dark mode, which is why dark mode can be
   switched on later by setting one attribute rather than revisiting every
   component. Resolution order:

     :root                                light defaults
     @media (prefers-color-scheme: dark)  follow the operating system
     [data-theme="dark"|"light"]          explicit choice, wins over the OS

   The dark semantics are written twice - once for the media query, once for
   the attribute - because CSS cannot OR a media query with a selector. Keep
   the two in step; ThemeTokensTest fails if they drift.
   ========================================================================== */

:root {
  /* ---------- primitives: brand ----------
     The owner's palette, adopted 2026-08-13; primary and hover restated
     2026-08-17 on instruction.

     500 moved #F97316 -> #E66A1F and 600 #EA580C -> #CC5815. Measured, the
     new primary is a genuine improvement on the metric this project has
     tracked all along: white on it goes 2.80 -> 3.26, and ink goes 6.74 ->
     5.80, so both directions get closer without the tone reaching the
     darkness the owner twice rejected.

     --ii-text-on-accent stays ink. White on #E66A1F is 3.26, which is AA for
     large text only; ink is 5.80 and clears AA outright, and it is still the
     more legible of the two in direct sunlight, which is what an Android-first
     audience in Djibouti actually reads in. The one white-on-orange surface in
     the product is the logged-out CTA button, specified that way deliberately -
     see .ii-cta-btn, which carries the measurement beside it.

     50/100/200/700/800 are unchanged: the tints still read correctly under the
     new primary, and 800 is orange-as-TEXT at 5.18 on white, which is a
     different job and a different number. */
  --ii-orange-50:  #FFF7ED;   /* very light background */
  --ii-orange-100: #FFEDD5;   /* light selected background */
  --ii-orange-200: #FED7AA;   /* focus glow - 1.35 on white, never a ring alone */
  --ii-orange-400: #FF7A45;   /* dark-mode accent: 7.12 on #141414 */
  --ii-orange-500: #E66A1F;   /* primary - ink 5.80, white 3.26 */
  --ii-orange-600: #CC5815;   /* hover  - ink 6.75, white 4.22 */
  --ii-orange-700: #D9480F;   /* pressed / active */
  --ii-orange-800: #C2410C;   /* links and orange text: 5.18 on white */

  /* ---------- primitives: neutrals ----------
     Contrast measured against the surfaces they are actually used on. */
  --ii-neutral-0:    #FFFFFF;
  --ii-neutral-25:   #FAFAF9;   /* light canvas */
  --ii-neutral-50:   #F4F4F5;   /* sunken */
  --ii-neutral-100:  #EAEAEA;   /* border */
  --ii-neutral-200:  #D4D4D4;   /* strong border */
  --ii-neutral-400:  #8E8E8E;   /* 3.14 - decorative and disabled only */
  --ii-neutral-500:  #6B6B6B;   /* 5.10 - tertiary text */
  --ii-neutral-600:  #5A5A5A;   /* 6.60 - secondary text */
  --ii-neutral-900:  #111111;   /* 18.08 - primary text */
  --ii-neutral-925:  #202124;   /* dark sunken */
  --ii-neutral-950:  #17181A;   /* dark surface */
  --ii-neutral-1000: #0E0F10;   /* dark canvas */

  /* dark-mode text, measured on #17181A */
  --ii-neutral-d100: #F5F5F4;   /* 16.29 */
  --ii-neutral-d300: #A1A1A6;   /* 6.91  */
  --ii-neutral-d500: #8B8B90;   /* 5.24  */

  /* ---------- primitives: status ---------- */
  --ii-green-50: #E9F6EF;  --ii-green-600: #146C43;  --ii-green-400: #3DD68C;
  --ii-amber-50: #FFFAF0;  --ii-amber-600: #B7791F;  --ii-amber-400: #E9B949;
  --ii-red-50:   #FDECEA;  --ii-red-600:   #B3261E;  --ii-red-400:   #F87171;
  --ii-whatsapp: #25D366;  /* a channel colour, never a UI accent */
  /* The hover was #1ebc5e, typed into two view <style> blocks and nowhere
     else. Named here so the pair travels together - a hover state defined
     apart from its base is how the two copies drifted in the first place. */
  --ii-whatsapp-hover: #1EBC5E;

  /* ---------- primitives: space (4px base) ---------- */
  --ii-space-1: 4px;
  --ii-space-2: 8px;
  --ii-space-3: 12px;
  --ii-space-4: 16px;
  --ii-space-5: 24px;
  --ii-space-6: 32px;
  --ii-space-7: 48px;

  /* ---------- primitives: radius ----------
     Three plus a pill, down from eleven values in use.
     Cards moved 14px -> 12px on 2026-08-17: with controls at 10px the two were
     close enough to read as an inconsistency rather than a hierarchy, and 12
     sits a clear step above the control without looking like a different
     system. The pill is for badges and the search field only. */
  --ii-radius-control: 10px;
  --ii-radius-card:    12px;
  --ii-radius-sheet:   20px;
  --ii-radius-pill:    999px;

  /* ---------- primitives: type ----------
     Inter, for a real variable weight axis (the old stack loaded 400/600/700
     only, so weight 650 rendered as 700 and 550 as 600), tabular figures for
     price columns, and proper French accent and guillemet coverage. */
  --ii-font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  --ii-text-xs:  12px;
  --ii-text-sm:  13px;
  --ii-text-md:  15px;
  --ii-text-lg:  17px;
  --ii-text-xl:  20px;
  --ii-text-2xl: 24px;
  --ii-text-3xl: 32px;

  --ii-leading-body:     1.35;
  --ii-leading-display:  1.15;
  --ii-tracking-display: -0.02em;

  /* Four weights. 100, 550 and 650 are retired - all three were accidents. */
  --ii-weight-regular:  400;
  --ii-weight-medium:   500;
  --ii-weight-semibold: 600;
  --ii-weight-bold:     700;

  /* ---------- primitives: motion ---------- */
  --ii-dur-fast:  150ms;   /* state changes */
  --ii-dur-sheet: 250ms;   /* sheets and overlays */
  --ii-ease: cubic-bezier(.2, .8, .2, 1);

  /* ---------- layout constants ---------- */
  --ii-bnav-h: 60px;
  /* The "Vendre" circle is absolutely positioned at top:-18px inside the bar,
     so it stands this far PROUD of it. The bar's own height does not describe
     the space the bar occupies, and every reservation below has to add this or
     the circle lands on whatever is underneath. */
  --ii-bnav-overhang: 18px;
}

/* ---------- semantics: light ---------- */
:root,
:root[data-theme="light"] {
  /* Tells the browser which palette its OWN widgets should use. Without it
     native controls stay light whatever the page does, which is where most of
     the "white input on a dark page" reports come from: select menus, date
     pickers, scrollbars, the autofill highlight and form control borders are
     drawn by the browser, not by this stylesheet. */
  color-scheme: light;

  --ii-bg-canvas:  var(--ii-neutral-25);
  --ii-bg-surface: var(--ii-neutral-0);
  --ii-bg-sunken:  var(--ii-neutral-50);
  /* Raised: anything floating ABOVE a card - sheets, modals, dropdowns, toasts.
     In light mode elevation is white plus a shadow, so this is the same white.
     In dark mode shadows are invisible and elevation has to come from a lighter
     surface, which is what the dark value provides. */
  --ii-bg-raised:  var(--ii-neutral-0);
  /* The surface of a SECONDARY BUTTON, and the one token that has to move in
     opposite directions per theme.

     A ghost button and a text input on the account form were coming out
     identical - same fill, same border, same radius, same width - so the form
     read as six fields with no actions on it. That was reported from a real
     phone, in both themes.

     Fields are --ii-bg-surface. A button has to sit visibly OFF that, and
     which way is off depends on the theme: in light, surface is white and the
     button steps DOWN to a grey; in dark, surface is near-black and it steps
     UP, because a darker step would read as a hole rather than a control.
     --ii-bg-raised is white in light mode, so it could not carry this. */
  --ii-bg-control: var(--ii-neutral-50);
  /* The surface of a SECONDARY BUTTON, and the one token that has to move in
     opposite directions per theme.

     A ghost button and a text input on the account form were coming out
     identical - same fill, same border, same radius, same width - so the form
     read as six fields with no actions on it. That was reported from a real
     phone, in both themes.

     Fields are --ii-bg-surface. A button has to sit visibly OFF that, and
     which way is off depends on the theme: in light, surface is white and the
     button steps DOWN to a grey; in dark, surface is near-black and it steps
     UP, because a darker step would read as a hole rather than a control.
     --ii-bg-raised is white in light mode, so it could not carry this. */
  --ii-bg-control: var(--ii-neutral-50);
  /* The surface of a SECONDARY BUTTON, and the one token that has to move in
     opposite directions per theme.

     A ghost button and a text input on the account form were coming out
     identical - same fill, same border, same radius, same width - so the form
     read as six fields with no actions on it. That was reported from a real
     phone, in both themes.

     Fields are --ii-bg-surface. A button has to sit visibly OFF that, and
     which way is off depends on the theme: in light, surface is white and the
     button steps DOWN to a grey; in dark, surface is near-black and it steps
     UP, because a darker step would read as a hole rather than a control.
     --ii-bg-raised is white in light mode, so it could not carry this. */
  --ii-bg-control: var(--ii-neutral-50);

  --ii-border:        var(--ii-neutral-100);
  --ii-border-strong: var(--ii-neutral-200);
  /* The edge of an interactive control, where the border IS the affordance.
     WCAG 1.4.11 asks 3:1 for those; --ii-border is a divider and is
     deliberately quieter. */
  --ii-border-control: var(--ii-neutral-300, #B8B8B8);

  --ii-text:           var(--ii-neutral-900);
  --ii-text-secondary: var(--ii-neutral-600);
  --ii-text-muted:     var(--ii-neutral-500);
  --ii-text-disabled:  var(--ii-neutral-400);
  /* Ink, not white: 6.74 on the primary. See the brand primitives above. */
  --ii-text-on-accent: var(--ii-neutral-900);

  --ii-brand:         var(--ii-orange-500);   /* identity */
  --ii-accent:        var(--ii-orange-500);   /* filled action surface */
  --ii-accent-hover:  var(--ii-orange-600);
  --ii-accent-press:  var(--ii-orange-700);
  --ii-accent-text:   var(--ii-orange-800);   /* orange as text: 5.18 on white */
  --ii-accent-subtle: var(--ii-orange-50);
  --ii-accent-border: var(--ii-orange-200);

  /* The ring is the dark step, because the primary at 2.80 and the 200 tint
     at 1.35 are both below the 3.0 WCAG 2.4.11 asks of a focus indicator.
     The tint is the glow around it, not the indicator itself. */
  --ii-focus:      var(--ii-orange-800);
  --ii-focus-ring: 0 0 0 3px rgba(254, 215, 170, .85);

  --ii-success: var(--ii-green-600);  --ii-success-subtle: var(--ii-green-50);
  --ii-warning: var(--ii-amber-600);  --ii-warning-subtle: var(--ii-amber-50);
  --ii-danger:  var(--ii-red-600);    --ii-danger-subtle:  var(--ii-red-50);

  /* Wide, low-alpha, warm-tinted - not the tight black shadows the theme
     used. */
  --ii-shadow-sm: 0 1px 2px rgba(16, 16, 16, .04);
  --ii-shadow-md: 0 4px 16px rgba(16, 16, 16, .06);
  --ii-shadow-lg: 0 12px 32px rgba(16, 16, 16, .10);

  --ii-glass:        rgba(255, 255, 255, .82);
  --ii-glass-border: rgba(16, 16, 16, .08);
  --ii-scrim: linear-gradient(to top, rgba(0, 0, 0, .38), rgba(0, 0, 0, 0) 52%);
}

/* ---------- semantics: dark ----------
   Live. Reachable two ways: the media query below follows the operating
   system, and [data-theme="dark"] is there for an explicit control if one is
   ever added.

   It was gated off for a while, and the reason is worth keeping. A review of
   fifteen mobile screens found white panels on a dark page throughout, which
   is real - style.css owns most of what renders, carries 518 hard-coded light
   values and knows nothing about colour schemes. The legacy override block at
   the end of this file converts what actually renders.

   What made it look unfinishable was a measuring fault, not a styling one.
   Several surfaces appeared to ignore every override, including inline
   !important - because they carry a transition on background-color, and in a
   browser that never composites a frame the transition never advances, so the
   computed value stays at its pre-transition colour indefinitely. With
   transitions disabled the same three surfaces read the correct dark value,
   and a full sweep of the home, category and ad pages returns zero light
   surfaces. So the gate came off.

   If it looks wrong again: disable transitions before believing a measurement.
   */
:root[data-theme="dark"] {
  color-scheme: dark;

  --ii-bg-canvas:  var(--ii-neutral-1000);
  --ii-bg-surface: var(--ii-neutral-950);
  --ii-bg-sunken:  var(--ii-neutral-925);
  --ii-bg-raised:  #232427;
  --ii-bg-control: #2A2B2F;

  --ii-border:        rgba(255, 255, 255, .10);
  --ii-border-strong: rgba(255, 255, 255, .18);
  /* Measured over #17181A: .10 white composites to #2E2F31, 1.33:1 - a border
     nobody can see. .24 only reaches 2.17:1; .34 is the first step that clears
     the 3:1 WCAG 1.4.11 asks of a control edge (#666768, 3.13:1). */
  --ii-border-control: rgba(255, 255, 255, .34);

  --ii-text:           var(--ii-neutral-d100);
  --ii-text-secondary: var(--ii-neutral-d300);
  --ii-text-muted:     var(--ii-neutral-d500);
  /* 3.11:1 at #66666B. Disabled controls are exempt from 1.4.3 so this was not
     a violation, but it read as broken rather than inactive. 3.90:1 here, and
     still unmistakably disabled. */
  --ii-text-disabled:  #7A7A80;
  --ii-text-on-accent: #111111;

  --ii-brand:         var(--ii-orange-400);
  --ii-accent:        var(--ii-orange-400);
  --ii-accent-hover:  #FF8A5B;
  --ii-accent-press:  #E8642F;
  --ii-accent-text:   var(--ii-orange-400);
  --ii-accent-subtle: rgba(255, 122, 69, .14);
  --ii-accent-border: rgba(255, 122, 69, .38);

  --ii-focus:      var(--ii-orange-400);
  --ii-focus-ring: 0 0 0 3px rgba(255, 122, 69, .34);

  --ii-success: var(--ii-green-400);  --ii-success-subtle: rgba(61, 214, 140, .14);
  --ii-warning: var(--ii-amber-400);  --ii-warning-subtle: rgba(233, 185, 73, .14);
  --ii-danger:  var(--ii-red-400);    --ii-danger-subtle:  rgba(248, 113, 113, .14);

  --ii-shadow-sm: 0 1px 2px rgba(0, 0, 0, .40);
  --ii-shadow-md: 0 4px 16px rgba(0, 0, 0, .48);
  --ii-shadow-lg: 0 12px 32px rgba(0, 0, 0, .56);

  --ii-glass:        rgba(23, 24, 26, .78);
  --ii-glass-border: rgba(255, 255, 255, .10);
  --ii-scrim: linear-gradient(to top, rgba(0, 0, 0, .55), rgba(0, 0, 0, 0) 52%);
}

/* ---------- semantics: dark (system preference) ----------
   Same values as the block above, for readers who never open a theme
   control. CSS cannot OR a media query with a selector, so the two are
   written separately; ThemeTokensTest keeps them in step. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --ii-bg-canvas:  var(--ii-neutral-1000);
    --ii-bg-surface: var(--ii-neutral-950);
    --ii-bg-sunken:  var(--ii-neutral-925);
    --ii-bg-raised:  #232427;
    --ii-bg-control: #2A2B2F;

    --ii-border:        rgba(255, 255, 255, .10);
    --ii-border-strong: rgba(255, 255, 255, .18);
    --ii-border-control: rgba(255, 255, 255, .34);

    --ii-text:           var(--ii-neutral-d100);
    --ii-text-secondary: var(--ii-neutral-d300);
    --ii-text-muted:     var(--ii-neutral-d500);
    --ii-text-disabled:  #7A7A80;
    --ii-text-on-accent: #111111;

    --ii-brand:         var(--ii-orange-400);
    --ii-accent:        var(--ii-orange-400);
    --ii-accent-hover:  #FF8A5B;
    --ii-accent-press:  #E8642F;
    --ii-accent-text:   var(--ii-orange-400);
    --ii-accent-subtle: rgba(255, 122, 69, .14);
    --ii-accent-border: rgba(255, 122, 69, .38);

    --ii-focus:      var(--ii-orange-400);
    --ii-focus-ring: 0 0 0 3px rgba(255, 122, 69, .34);

    --ii-success: var(--ii-green-400);  --ii-success-subtle: rgba(61, 214, 140, .14);
    --ii-warning: var(--ii-amber-400);  --ii-warning-subtle: rgba(233, 185, 73, .14);
    --ii-danger:  var(--ii-red-400);    --ii-danger-subtle:  rgba(248, 113, 113, .14);

    --ii-shadow-sm: 0 1px 2px rgba(0, 0, 0, .40);
    --ii-shadow-md: 0 4px 16px rgba(0, 0, 0, .48);
    --ii-shadow-lg: 0 12px 32px rgba(0, 0, 0, .56);

    --ii-glass:        rgba(23, 24, 26, .78);
    --ii-glass-border: rgba(255, 255, 255, .10);
    --ii-scrim: linear-gradient(to top, rgba(0, 0, 0, .55), rgba(0, 0, 0, 0) 52%);
  }
}

/* ---------- compatibility aliases ----------
   Components below were written against the original six variables. Aliasing
   keeps this phase to the foundation rather than rewriting every rule at the
   same time; each alias goes as its component is reworked in a later phase. */
:root {
  --ii-ink:  var(--ii-text);
  --ii-muted: var(--ii-text-muted);
  --ii-line: var(--ii-border);
  --ii-wa:   var(--ii-whatsapp);
  --ii-accent-dark: var(--ii-accent-hover);
  --ii-surface: var(--ii-bg-sunken);
}

/* --------------------------------------------------------------------------
   Base: type and canvas

   style.css declares font-family on roughly 280 selectors, with megadropdown
   and custom adding more, so inheritance from body cannot win and neither can
   a single later class rule. The family is asserted once here rather than
   editing a vendor theme in 280 places.

   Icon fonts are safe: FontAwesome and Flaticon declare their family on the
   ::before pseudo-element, which this does not touch. Bare <i> and the icon
   class patterns are excluded anyway, belt and braces.
   -------------------------------------------------------------------------- */
body,
body *:not(i):not(.fa):not([class*="fa-"]):not([class^="flaticon"]):not([class*=" flaticon"]):not([class*="note-icon"]):not([class*="glyphicon"]) {
  font-family: var(--ii-font-sans) !important;
}

/* `html` carried no background at all - measured rgba(0, 0, 0, 0). Constraint
   69 says to give BODY an explicit token background because the viewer paints
   its own ground behind the page; the same is true one level up, and it is not
   cosmetic. Anything sampling the backdrop of a fixed element - a
   backdrop-filter header, an overscroll area, iOS's rubber-band region - reads
   the canvas, and an unpainted canvas is whatever the engine decides, which is
   usually white. That is the most likely reason a dark page reported a white
   header on iOS while this environment measured it correctly. */
html {
  background-color: var(--ii-bg-canvas);
}

/* `body.global-bg` rather than `body`: common.blade.php carries an inline
   `.global-bg` block that sits after this stylesheet in document order, so
   equal specificity would lose to it. (That block is now tokens too, but the
   ordering reason stands.) */
body,
body.global-bg {
  background-color: var(--ii-bg-canvas);
  color: var(--ii-text);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* Prices and figures that sit in a column. Proportional figures stopped price
   columns in the grid from lining up. */
.ii-card-price,
.ii-card-was,
.ii-result-count,
.tz-new-price,
.tz-discount-price {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* --------------------------------------------------------------------------
   Bottom navigation (mobile only)
   -------------------------------------------------------------------------- */
.bnav { display: none; }

@media (max-width: 991px) {
  .bnav {
    display: flex;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    height: calc(var(--ii-bnav-h) + env(safe-area-inset-bottom));
    padding-bottom: env(safe-area-inset-bottom);
    background: var(--ii-bg-surface);
    border-top: 1px solid var(--ii-line);
    z-index: 1030;
    box-shadow: 0 -2px 12px rgba(0, 0, 0, .06);
  }

  .bnav-item {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    font-size: var(--ii-text-xs);
    font-weight: 600;
    color: var(--ii-muted);
    text-decoration: none;
    min-height: 44px;              /* tap target */
    padding: 6px 2px;
    line-height: 1;
  }
  .bnav-item:hover,
  .bnav-item:focus { color: var(--ii-accent-text); text-decoration: none; }
  .bnav-item.is-on { color: var(--ii-accent-text); }
  .bnav-item svg { width: 22px; height: 22px; fill: currentColor; }
  .bnav-item:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: -2px; }

  /* Raised primary action */
  .bnav-sell { position: relative; color: var(--ii-ink); }
  .bnav-sell-btn {
    position: absolute;
    top: -18px;
    width: 46px; height: 46px;
    border-radius: 50%;
    /* Glyph and circle now follow the same rule as every other filled action:
       ink on the primary, 6.74. A white glyph here would have been 2.80,
       below even the 3.0 bar that applies to a graphic. The word "Vendre"
       sits below the circle in ink, unaffected. */
    background: var(--ii-brand);
    color: var(--ii-text-on-accent);
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 4px 12px rgba(249, 115, 22, .4);
  }
  .bnav-sell-btn svg { width: 26px; height: 26px; fill: var(--ii-text-on-accent); }
  .bnav-sell span:last-child { margin-top: 30px; }

  /* Keep page content clear of the bar AND of the circle standing proud of
     it - reserving only --ii-bnav-h left an 18px band in which the last line
     of a card sat under the button. */
  body.global-bg {
    padding-bottom: calc(var(--ii-bnav-h) + var(--ii-bnav-overhang) + env(safe-area-inset-bottom));
  }

  /* The legacy floating "add listing" pill duplicates the bottom bar. */
  .add_listing.sticky-listing { display: none !important; }
}

/* --------------------------------------------------------------------------
   Page tail on the account surfaces

   The profile, account, favourites and wallet screens all ended flush against
   the footer - the last card's bottom edge and the dark footer's top edge were
   the same line. Two different causes: the profile section carried an inline
   `padding: 0 0 !important` that zeroed both ends, and the other three set a
   top padding (`pt-4`) and never a bottom one.

   Applied to the page's own <section>, not to the cards inside it, so it holds
   whatever the tab is showing - listings, favourites, the wallet - and does
   not have to be repeated for each.

   A compound selector, deliberately. `.pb30-991` (30px below 992px) and
   `.profile-listing-wapper { padding-bottom: 0 }` in responsive.css both match
   these sections at the same specificity (0,1,0), so winning on load order
   alone would be luck. `section.ii-page-tail` is (0,1,1) and settles it
   without !important - the pattern §5.1 records for this cascade.

   Clearance for the fixed bottom navigation is a separate thing entirely and
   lives on the body (above), below the footer. This gap is between the content
   and the footer, so the two do not interact.
   -------------------------------------------------------------------------- */
section.ii-page-tail { padding-bottom: var(--ii-space-6); }        /* 32px - phone */

@media (min-width: 768px) {
  section.ii-page-tail { padding-bottom: var(--ii-space-7); }      /* 48px - tablet */
}

@media (min-width: 992px) {
  section.ii-page-tail { padding-bottom: 56px; }                   /* desktop */
}

/* --------------------------------------------------------------------------
   Logged-out CTA

   Was `.start-partners.bgc-thm` - a full-bleed solid orange band with a white
   button sitting on it, which made the least urgent action on the homepage the
   loudest thing on it, directly under the Vendre CTA it competes with. Now a
   tint panel with the orange kept on the button, where the action is.

   Guests only; the markup keeps that guard.
   -------------------------------------------------------------------------- */
/* The warm tint is the accent's own wash, so it follows the theme instead of
   staying a light peach on a dark page. */
.ii-cta { background: var(--ii-accent-subtle, #FFF4EC); padding: 40px 0; }

@media (min-width: 992px) { .ii-cta { padding: 48px 0; } }

.ii-cta-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--ii-space-5); flex-wrap: wrap;
}
.ii-cta-copy { flex: 1 1 340px; min-width: 0; }

.ii-cta-title {
  margin: 0 0 var(--ii-space-2);
  /* Was #202124 - measured 14.88:1 against the light tint this block used to
     have, and 1.00:1 once the page went dark, which is invisible rather than
     merely poor. The tint is a token now, so the text has to be one too. */
  color: var(--ii-text);
  font-size: var(--ii-text-2xl);
  font-weight: var(--ii-weight-bold);
  line-height: var(--ii-leading-display);
  letter-spacing: var(--ii-tracking-display);
}
.ii-cta-sub {
  margin: 0;
  color: var(--ii-text-secondary);
  font-size: var(--ii-text-md);
  line-height: var(--ii-leading-body);
}
.ii-cta-action { flex: 0 0 auto; }

/* The one white-on-orange surface in the product, specified deliberately.
   Measured: white on #E66A1F is 3.26 and on the #CC5815 hover 4.22 - AA for
   large text, short of the 4.5 AA asks of normal text. Set at 17px/600 so it
   is as close to the large-text threshold as a button label reasonably gets.
   Everything else filled in orange keeps the ink label at 5.80; if these are
   ever brought into line, it is one declaration either way. */
.ii-cta-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 48px; padding: 0 var(--ii-space-6);
  border-radius: var(--ii-radius-control);
  /* Was #E66A1F with #FFFFFF - white on the brand orange is 3.26:1, under the
     4.5 this label needs. Tokens instead: the accent flips with the theme and
     --ii-text-on-accent is the ink the rest of the product puts on it (7.30:1). */
  background: var(--ii-accent); color: var(--ii-text-on-accent);
  font-size: var(--ii-text-lg);
  font-weight: var(--ii-weight-semibold);
  line-height: 1;
  text-decoration: none;
  transition: background var(--ii-dur-fast) var(--ii-ease);
}
.ii-cta-btn:hover,
.ii-cta-btn:focus { background: #CC5815; color: #FFFFFF; text-decoration: none; }
.ii-cta-btn:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: 2px; }

@media (max-width: 767px) {
  .ii-cta-inner { flex-direction: column; align-items: flex-start; }
  .ii-cta-title { font-size: var(--ii-text-xl); }
  .ii-cta-action { width: 100%; }
  .ii-cta-btn { width: 100%; }

  /* `.ii-cta-copy` is `flex: 1 1 340px`, and flex-basis applies to the MAIN
     axis. That 340px was written for the desktop ROW, where it is a width; the
     moment this block turns the container into a COLUMN it becomes a HEIGHT.
     Measured at 375px: the copy holds 64px of text in a 340px box, leaving a
     293px void between the subtitle and the button - the whole complaint.

     0 0 auto, so the block is as tall as its text and the 24px gap on
     .ii-cta-inner is the only thing between the copy and the button. That
     gives title -> 8 -> subtitle -> 24 -> button: the tight pair reads as one
     unit and the button sits clearly apart from it, which is what a CTA panel
     wants. */
  .ii-cta-copy { flex: 0 0 auto; }
}

/* --------------------------------------------------------------------------
   Profile page cards

   Three things were inconsistent on one screen: the profile block was a
   rounded card with a hard-cornered 1px box drawn inside it, the wallet card
   carried a soft shadow on every side and no border, and the two used
   different corner radii. They are one treatment now - a hairline, a 12px
   radius, and a shadow light enough to read as a lift rather than a glow.

   Scoped, not applied to `.wallet-wapper` and friends globally: those legacy
   classes appear on the standalone wallet screen too, and this keeps the
   override where it can be seen.
   -------------------------------------------------------------------------- */
.profile-top-wapper .media-head {
  border: 1px solid #E8E8E8;
  border-radius: var(--ii-radius-card);
  padding: var(--ii-space-4);
  background: var(--ii-bg-surface);
}

/* The shadow here was `rgba(0.2, 0.2, 0.2, .2) 0 0 8px 1px` - a black glow on
   all four sides, and the only element on the page wearing one. */
.tab-content-body .wallet-wapper {
  padding: var(--ii-space-4) var(--ii-space-5) !important;
  border: 1px solid #E8E8E8 !important;
  border-radius: var(--ii-radius-card);
  background: var(--ii-bg-surface) !important;
  box-shadow: 0 1px 2px rgba(16, 16, 16, .04);
}

/* Empty states that sit inside an existing block, where the full ii-empty
   treatment with its illustration and CTA would be too much furniture. */
.ii-empty-line {
  padding: var(--ii-space-5) 0;
  color: var(--ii-text-muted);
  font-size: var(--ii-text-md);
  list-style: none;
}

/* The second line names what will appear here once there is something. An
   empty state that only says "nothing yet" confirms the section loaded and
   nothing else. */
.ii-empty-line small {
  display: block;
  margin-top: var(--ii-space-1);
  font-size: var(--ii-text-sm);
  color: var(--ii-text-disabled);
}

/* --------------------------------------------------------------------------
   Footer

   Same grid as the navbar and the page content: both already use `.container`,
   but the first column carried `pr0 pl0`, so it alone sat outside the gutters
   and nothing lined up. That class is gone from the markup and the column
   widths are now one set (col-sm-6 col-lg-3) rather than four repeated
   breakpoints saying the same thing.

   Everything is scoped under .ii-footer so the legacy `.footer_one` rules in
   style.css are overridden here rather than edited there, where the blast
   radius is unknown.
   -------------------------------------------------------------------------- */
.ii-footer {
  background: #242D45;
  padding: 56px 0 40px;
}
@media (min-width: 992px) { .ii-footer { padding: 64px 0 48px; } }

/* style.css indents this one column by 70px, so the four headings sat at
   15 / 401 / 647 / 964 - three on the grid and one adrift. The columns are the
   grid; nothing should be nudged off it by hand. */
.ii-footer .footer_qlink_widget { padding-left: 0; }

.ii-footer h4 {
  color: #F8FAFC;                              /* 13.06 on the ground */
  font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-semibold);
  margin: 0 0 var(--ii-space-4);
  line-height: var(--ii-leading-body);
}
/*
    The link selectors below are qualified by their widget class on purpose.
    style.css declares `.footer_qlink_widget li a, .footer_contact_widget li a`
    and `.footer_social_widget li a` at (0,2,1); a bare `.ii-footer a` is
    (0,1,1) and loses to them regardless of load order. §5.1: enumerate what
    actually matches the element rather than assuming last-loaded wins.
*/
.ii-footer p,
.ii-footer li {
  color: #B8C0D0;                              /* 7.48 on the ground */
  font-size: var(--ii-text-sm);
  line-height: 1.55;
}
.ii-footer .footer_about_widget p { margin: 0; max-width: 34ch; }

.ii-footer ul { margin: 0; padding: 0; }
.ii-footer li { margin-bottom: var(--ii-space-2); }
.ii-footer li:last-child { margin-bottom: 0; }

/* Neutral by default, brand on hover and on focus - the keyboard gets the same
   signal the pointer does. */
.ii-footer .footer_qlink_widget li a,
.ii-footer .footer_contact_widget li a,
.ii-footer .footer_social_widget li a {
  color: #B8C0D0;
  font-size: var(--ii-text-sm);
  line-height: 1.55;
  text-decoration: none;
  transition: color var(--ii-dur-fast) var(--ii-ease);
}
.ii-footer .footer_qlink_widget li a:hover,
.ii-footer .footer_qlink_widget li a:focus,
.ii-footer .footer_contact_widget li a:hover,
.ii-footer .footer_contact_widget li a:focus,
.ii-footer .footer_social_widget li a:hover,
.ii-footer .footer_social_widget li a:focus {
  color: #E66A1F;                              /* 4.20 on the ground */
  text-decoration: none;
}
.ii-footer a:focus-visible { outline: 2px solid #E66A1F; outline-offset: 3px; border-radius: 4px; }

/* Contact rows: a small glyph, then the value. */
.ii-footer-contact a { display: inline-flex; align-items: center; gap: var(--ii-space-2); }
.ii-footer-ico {
  width: 16px; height: 16px; flex: 0 0 16px;
  fill: currentColor; opacity: .75;
}
.ii-footer-contact a:hover .ii-footer-ico,
.ii-footer-contact a:focus .ii-footer-ico { opacity: 1; }

/* Social row: one size, one gap, no list bullets or inline-item margins. */
.ii-footer-social {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
}
.ii-footer-social li { margin: 0; }
.ii-footer-social a { display: inline-flex; align-items: center; justify-content: center; }
.ii-footer-social i { font-size: 18px; line-height: 1; }

/* Copyright, set apart by a hairline rather than by distance alone. */
.ii-footer-legal {
  margin-top: var(--ii-space-6);
  padding-top: var(--ii-space-5);
  border-top: 1px solid rgba(255, 255, 255, .08);
  text-align: center;
}
.ii-footer-legal p {
  margin: 0;
  color: #B8C0D0;
  font-size: var(--ii-text-sm);
}

@media (max-width: 767px) {
  .ii-footer-cols > [class*="col-"] { margin-bottom: var(--ii-space-6); }
  .ii-footer-cols > [class*="col-"]:last-child { margin-bottom: 0; }
  .ii-footer .footer_about_widget p { max-width: none; }
}

/* --------------------------------------------------------------------------
   Sticky contact bar on the ad detail page
   Primary conversion action, reachable at any scroll position.
   -------------------------------------------------------------------------- */
.ii-contact-bar { display: none; }

@media (max-width: 991px) {
  .ii-contact-bar {
    display: flex;
    position: fixed;
    left: 0; right: 0;
    bottom: calc(var(--ii-bnav-h) + env(safe-area-inset-bottom));
    gap: 8px;
    /* Extra BOTTOM padding, not extra offset: the bar is z-index 1029 against
       the nav's 1030, so the circle is drawn over it, and it protrudes upward
       from the nav into this bar's lower edge. The bar is anchored by its
       bottom, so padding-bottom is what lifts the controls clear - padding-top
       only makes it taller and leaves them exactly where they were. Raising
       the whole bar instead would open a transparent gap above the nav for
       page content to show through. */
    padding: 10px 12px calc(10px + var(--ii-bnav-overhang));
    background: var(--ii-bg-surface);
    border-top: 1px solid var(--ii-line);
    z-index: 1029;
    box-shadow: 0 -2px 12px rgba(0, 0, 0, .06);
  }

  .ii-contact-bar .ii-wa {
    flex: 1;
    display: flex; align-items: center; justify-content: center; gap: 8px;
    min-height: 48px;
    border-radius: 10px;
    background: var(--ii-wa);
    /* --ii-text-on-accent, not --ii-bg-surface. Surface is WHITE in light
       mode, so this button was ink-on-green at 8.96:1 in dark and
       white-on-green at 1.98:1 in light - the failure constraint 71 records as
       fixed was only ever fixed in one theme. The green is a channel colour
       and does not move per theme, so its label must not either. */
    color: var(--ii-text-on-accent);
    font-size: 15px; font-weight: 700;
    text-decoration: none;
  }
  .ii-contact-bar .ii-wa:hover { background: #1da851; color: var(--ii-text-on-accent); text-decoration: none; }
  .ii-contact-bar .ii-wa svg { width: 21px; height: 21px; fill: var(--ii-text-on-accent); }

  .ii-contact-bar .ii-icon {
    width: 48px; min-height: 48px;
    display: flex; align-items: center; justify-content: center;
    border: 1.5px solid var(--ii-border-strong); border-radius: 10px;
    background: var(--ii-bg-surface); color: var(--ii-ink);
    text-decoration: none;
  }
  .ii-contact-bar .ii-icon svg { width: 20px; height: 20px; fill: currentColor; }

  /* Save control. The heart is a solid shape, so saved-ness has to be carried
     by colour rather than by fill: muted when not saved, accent when saved —
     the same language the card favourite already uses. */
  .ii-contact-bar .ii-save-btn { color: var(--ii-muted); cursor: pointer; }
  .ii-contact-bar .ii-save-btn.is-on {
    color: var(--ii-accent);
    border-color: var(--ii-accent);
  }

  /* Detail pages need extra clearance for two stacked bars */
  body.ii-has-contact-bar {
    padding-bottom: calc(var(--ii-bnav-h) + var(--ii-bnav-overhang) + 68px + env(safe-area-inset-bottom));
  }
}

/* --------------------------------------------------------------------------
   Category rail — one swipe to any top-level category
   -------------------------------------------------------------------------- */
.ii-rail {
  /* 16px above, 24px below - not 4/18. Spacing is how grouping is stated: the
     search field and this rail are both NAVIGATION, so they sit on the same
     16px step as the gap under the header, and the larger 24px step below marks
     the boundary where content begins. Measured before, the top of the page ran
     0px / 9px / 18px, which read as three unrelated decisions.

     The 16px step still holds; on home it is now SPLIT, and the override lives
     just below. See there for why. */
  margin: var(--ii-space-4) 0 var(--ii-space-5);
  /* bleed to the screen edges so the row reads as scrollable */
  margin-inline: calc(50% - 50vw);
  padding-inline: max(16px, calc(50vw - 50%));
}

/* On home the 16px step above the rail is not the whole gap under the header,
   because the header no longer ends where the field does: the field sits
   inside the pinned block with 8px under it, then the hairline, then this.
   8 + 16 was 24 of stacked space below the field, against 15.5 above it - the
   same defect as the top edge of the block, from the other side.

   So the step is split rather than removed: 8px inside the block, 8px outside
   it, the hairline centred between them, and 16 in total from the field to the
   rail. The rule above keeps its number; what changed is that on this page
   part of it is now being paid on the other side of the edge.

   Home only. The listing page shows the same rail, but its search block ends
   in the filter chips rather than in this - nothing stacks there, and its
   16px is doing exactly what it says. */
body.ii-home .ii-rail { margin-top: var(--ii-space-2); }
.ii-rail-track {
  display: flex;
  gap: 14px;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  padding: 4px 0 6px;
  scrollbar-width: none;
}
.ii-rail-track::-webkit-scrollbar { display: none; }

/* Says the row continues. The rail bleeds to the screen edges, so the last
   item was cut off flush against the viewport with nothing to distinguish
   "clipped" from "ends here" - a reviewer read the truncated label as a
   rendering fault rather than as an invitation to swipe. The fade is on the
   trailing edge only; the leading edge is the natural start of the row. */
@supports (mask-image: linear-gradient(#000, #000)) or (-webkit-mask-image: linear-gradient(#000, #000)) {
  .ii-rail-track {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
            mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  }
}

/* "You are here". On a category page this rail IS the category switcher, so
   the current one has to be legible as current; before this, eight equal
   destinations gave the reader no way to tell where they were standing. The
   accent ring carries it, the weight change backs it up, and aria-current in
   the markup says the same thing to a screen reader. */
.ii-rail-item.is-on .ii-rail-icon {
  border-color: var(--ii-accent);
  box-shadow: 0 0 0 1px var(--ii-accent);
}
.ii-rail-item.is-on .ii-rail-label {
  color: var(--ii-accent-text);
  font-weight: var(--ii-weight-semibold);
}

.ii-rail-item {
  flex: 0 0 auto;
  width: 76px;
  scroll-snap-align: start;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  text-decoration: none;
  color: var(--ii-ink);
}
.ii-rail-item:hover { text-decoration: none; color: var(--ii-accent-text); }
.ii-rail-item:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: 3px; border-radius: 10px; }

.ii-rail-icon {
  width: 60px; height: 60px;
  border-radius: 50%;
  background: var(--ii-surface, var(--ii-bg-sunken));
  border: 1px solid var(--ii-line);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.ii-rail-icon img { width: 34px; height: 34px; object-fit: contain; }
.ii-rail-icon svg { width: 26px; height: 26px; fill: var(--ii-muted); }
.ii-rail-item:hover .ii-rail-icon { border-color: var(--ii-accent); }

.ii-rail-label {
  /* 12, the scale's floor - and it costs two characters, deliberately.

     This was 11.5px, which is not a size the scale contains. Measured on the
     home rail at 375px, moving to 12 makes exactly one of the nine labels
     reach its two-line clamp: "Beauté & Soin personnel" shows 21 of its 23
     characters as "Beauté & Soin personn…". Nothing else truncates.

     The on-scale alternative that loses nothing is a three-line clamp, and it
     costs 16px of rail height - 105 to 121 - on every mobile home page, which
     pushes the first listings down by that much. §1.22 is the record of what
     happens when this page's opening screen fills with something other than
     products, and two characters of one category name is a much smaller price
     than sixteen pixels above the fold on every visit.

     So: do not "fix" this by going back to 11.5, and do not raise the clamp
     without re-measuring the rail height. */
  font-size: var(--ii-text-xs); line-height: 1.25; text-align: center;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

@media (min-width: 992px) {
  /* Desktop already has the mega-menu in the header. */
  .ii-rail { display: none; }
}

/* --------------------------------------------------------------------------
   Ad card
   Six elements in scanning order: image, price, title, city, age, favourite.
   -------------------------------------------------------------------------- */
.ii-card {
  position: relative;
  background: var(--ii-bg-surface);
  /* Elevation instead of a hairline. The border was the only thing separating
     a white card from a white page, which is why the grid read as flat. */
  border: 0;
  border-radius: var(--ii-radius-card);
  overflow: hidden;
  height: 100%;
  display: flex;
  flex-direction: column;
  box-shadow: var(--ii-shadow-sm);
  transition: box-shadow var(--ii-dur-fast) var(--ii-ease),
              transform var(--ii-dur-fast) var(--ii-ease);
}

/* Guarded: on a touch device this would otherwise latch on after a tap and
   leave the card stuck in its hover state. */
@media (hover: hover) {
  .ii-card:hover {
    box-shadow: var(--ii-shadow-md);
    transform: translateY(-2px);
  }
}

/* Press feedback. Hover does not exist on the device most of this audience
   uses, so the tap is the only opportunity to acknowledge the touch. */
.ii-card:active {
  transform: scale(.985);
  box-shadow: var(--ii-shadow-sm);
}

.ii-card-media {
  display: block;
  position: relative;
  aspect-ratio: 4 / 3;          /* one consistent shape → a clean scan grid */
  background: var(--ii-bg-sunken);
  overflow: hidden;
}
.ii-card-media img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}

/* A hairline drawn inside the image rather than around the card. Listing
   photos are frequently shot against a white wall or cut out on white, and
   without this they bleed into the card and the crop disappears.

   Deliberately not a gradient scrim: the two things sitting on the photo are
   already opaque pills, so a scrim would buy no legibility and would darken
   the bottom of every product photo to pay for it. */
.ii-card-media::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(16, 16, 16, .05);
}

.ii-card-badge {
  position: absolute; top: 8px; left: 8px;
  background: var(--ii-accent); color: var(--ii-text-on-accent);
  font-size: 11px; font-weight: var(--ii-weight-bold);
  padding: 4px 9px; border-radius: var(--ii-radius-pill);
  text-transform: uppercase; letter-spacing: .04em;
  box-shadow: var(--ii-shadow-sm);
}

/* Verified-phone badge on the card image.
   Deliberately on the media, not in the body: the card body is a fixed
   scanning order (price → title → city · age) and this must not push it
   around or add a row. */
.ii-card-verified {
  position: absolute; bottom: 8px; left: 8px;
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--ii-glass);
  -webkit-backdrop-filter: blur(8px) saturate(1.4);
  backdrop-filter: blur(8px) saturate(1.4);
  color: var(--ii-success);
  font-size: 11px; font-weight: var(--ii-weight-semibold);
  padding: 4px 8px; border-radius: var(--ii-radius-pill);
  box-shadow: var(--ii-shadow-sm);
  max-width: calc(100% - 16px);
  white-space: nowrap;
}
.ii-card-verified svg {
  width: 12px; height: 12px; flex: 0 0 12px;
  fill: currentColor;
}

.ii-card-fav {
  position: absolute; top: 6px; right: 6px;
  /* 44px is the WCAG 2.5.5 target size. The white circle reads smaller than
     that because the icon sits in the middle of it, but the whole 44px is
     tappable — which is what matters on a phone. */
  width: 44px; height: 44px;
  border: 0; border-radius: 50%;
  background: transparent;
  color: var(--ii-text-muted);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; font-size: 16px;
  z-index: 2;
  transition: transform var(--ii-dur-fast) var(--ii-ease),
              color var(--ii-dur-fast) var(--ii-ease);
}

/* The visible disc is drawn at 32px inside the 44px target, so the control
   looks proportionate on the photo while the whole WCAG 2.5.5 target stays
   tappable. */
.ii-card-fav::before {
  content: "";
  position: absolute;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--ii-glass);
  /* NO backdrop-filter. It was `blur(8px) saturate(1.4)`, and it is the reason
     the hearts in "Annonces similaires" were reported as "the first one has a
     heart, the rest go grey after you swipe".

     `.ii-cardrail` is a horizontally-scrolling container, and a backdrop
     filter inside one is composited from the scroller's own layer: iOS Safari
     samples it from the wrong region once that layer has been scrolled and
     repainted, so cards that were off-screen when the page painted come back
     with the disc filled from stale or blown-out backdrop instead of the photo
     behind them. The first card is the one that was on screen at paint time,
     which is exactly the card that kept working.

     Nothing was wrong with the button itself - measured across all 20 cards,
     every one had `pointer-events: auto`, opacity 1 and an identical stroke
     colour. It was untappable-looking rather than untappable, which is its own
     kind of broken: a control nobody believes in does not get pressed.

     `--ii-glass` is already a flat translucent (78% at the dark end), so the
     disc reads on any photo without the filter. The border and shadow do the
     separation the blur was there for, and they composite anywhere. */
  border: 1px solid var(--ii-border);
  box-shadow: var(--ii-shadow-sm);
  transition: background var(--ii-dur-fast) var(--ii-ease);
}
.ii-card-fav > * { position: relative; }

.ii-card-fav.is-on,
.ii-card-fav:hover { color: var(--ii-accent); }
.ii-card-fav:active { transform: scale(.88); }
.ii-card-fav:focus-visible {
  outline: none;
  box-shadow: var(--ii-focus-ring);
  border-radius: 50%;
}

.ii-card-body {
  padding: var(--ii-space-3) var(--ii-space-3) var(--ii-space-4);
  display: flex; flex-direction: column; gap: var(--ii-space-1);
  flex: 1;
}

.ii-card-price {
  margin: 0;
  font-size: var(--ii-text-lg);
  font-weight: var(--ii-weight-bold);
  line-height: var(--ii-leading-display);
  letter-spacing: -0.01em;
  color: var(--ii-text);
}
.ii-card-price span {
  font-size: var(--ii-text-xs);
  font-weight: var(--ii-weight-semibold);
  color: var(--ii-text-secondary);
}
.ii-card-was {
  font-size: var(--ii-text-xs);
  font-weight: var(--ii-weight-medium);
  color: var(--ii-text-disabled);
  margin-left: 6px;
}

.ii-card-title {
  color: var(--ii-text-secondary);
  font-size: var(--ii-text-md);
  line-height: var(--ii-leading-body);
  text-decoration: none;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  transition: color var(--ii-dur-fast) var(--ii-ease);
}
.ii-card-title:hover { color: var(--ii-accent-text); text-decoration: none; }

.ii-card-meta {
  font-size: var(--ii-text-xs);
  color: var(--ii-text-muted);
  display: flex; gap: var(--ii-space-2); flex-wrap: wrap;
  margin: var(--ii-space-2) 0 0;
  margin-top: auto;
  padding-top: var(--ii-space-2);
}
.ii-card-age::before { content: "· "; }

@media (max-width: 480px) {
  .ii-card-body { padding: var(--ii-space-3) var(--ii-space-3) var(--ii-space-3); }
  .ii-card-price { font-size: var(--ii-text-md); }
  .ii-card-title { font-size: var(--ii-text-sm); }
}

/* --------------------------------------------------------------------------
   Search suggestions
   -------------------------------------------------------------------------- */
.ui-autocomplete.ui-menu {
  border: 1px solid var(--ii-line) !important;
  border-radius: 10px !important;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .12) !important;
  padding: 4px 0 !important;
  max-height: 320px; overflow-y: auto;
  z-index: 1050 !important;
}
.ui-autocomplete .ui-menu-item-wrapper {
  padding: 10px 14px !important;
  font-size: 14px;
  border: 0 !important;
}
.ui-autocomplete .ui-state-active,
.ui-autocomplete .ui-menu-item-wrapper.ui-state-active {
  background: var(--ii-accent-subtle) !important;
  color: var(--ii-ink) !important;
  margin: 0 !important;
}
.ii-sugg-kind {
  display: block; font-size: 12px; color: var(--ii-muted); margin-top: 2px;
}
.ii-sugg-recent { color: var(--ii-muted); }

/* --------------------------------------------------------------------------
   Trust signals
   -------------------------------------------------------------------------- */
.ii-trust {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 6px 10px;
  margin-top: 6px;
}

/* Scoped under .ii-trust deliberately, not written as bare component classes.
   The ad detail page renders this partial inside .media-head-left, and
   style.css carries `.media-head-left a, .media-head-left span,
   .media-head-left p { font-size:16px; color:#000 }` — specificity (0,1,1),
   which outranks a lone class (0,1,0) no matter how late this file loads. The
   badge kept its background and pill radius but lost its size and colour, so
   the one place the trust story is told rendered it as plain black body text.

   A descendant selector is (0,2,0) and wins cleanly, without !important.
   Both call sites — the ad page and accountSidepanel — wrap these in
   .ii-trust, so nothing is left unstyled. */
.ii-trust .ii-verified {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 12px; font-weight: var(--ii-weight-semibold); color: var(--ii-success);
  background: var(--ii-success-subtle); border-radius: 999px;
  padding: 3px 9px; white-space: nowrap;
}
.ii-trust .ii-verified svg { width: 13px; height: 13px; fill: currentColor; }

.ii-trust .ii-member-since { font-size: 12px; color: var(--ii-muted); white-space: nowrap; }

/* --------------------------------------------------------------------------
   Empty states — never a dead end
   -------------------------------------------------------------------------- */
.ii-empty {
  text-align: center;
  padding: 40px 20px 48px;
  max-width: 460px;
  margin: 0 auto;
}
.ii-empty img { width: 150px; max-width: 55%; opacity: .9; margin-bottom: 18px; }
.ii-empty h3 {
  font-size: 18px; font-weight: 700; color: var(--ii-ink);
  margin: 0 0 6px;
}
.ii-empty p { font-size: 14px; color: var(--ii-muted); margin: 0 0 20px; }
.ii-empty-actions {
  display: flex; flex-wrap: wrap; gap: 10px; justify-content: center;
}
.ii-empty-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 46px; padding: 0 20px;
  border-radius: 10px; font-size: 15px; font-weight: var(--ii-weight-semibold);
  text-decoration: none; border: 0; cursor: pointer;
}
.ii-empty-btn-primary { background: var(--ii-accent); color: var(--ii-text-on-accent); }
.ii-empty-btn-primary:hover { background: var(--ii-accent-dark); color: var(--ii-text-on-accent); text-decoration: none; }
.ii-empty-btn-ghost { background: var(--ii-bg-surface); color: var(--ii-ink); border: 1.5px solid var(--ii-border-strong); }
.ii-empty-btn-ghost:hover { color: var(--ii-ink); text-decoration: none; border-color: var(--ii-border-strong); }
.ii-empty-btn svg { width: 17px; height: 17px; fill: currentColor; }

/* --------------------------------------------------------------------------
   Report ad — quiet by default, findable when needed
   -------------------------------------------------------------------------- */
.ii-report-wrap { text-align: center; padding: 8px 16px 24px; }
.ii-report-link {
  background: none; border: 0;
  /* 44px tall so it clears the WCAG 2.5.5 target size; it was 37px. */
  padding: 12px; min-height: 44px;
  display: inline-flex; align-items: center;
  color: var(--ii-muted); font-size: 13px; text-decoration: underline;
  cursor: pointer;
}
.ii-report-link:hover { color: var(--ii-danger); }

/* --------------------------------------------------------------------------
   Skeleton cards — matched to .ii-card geometry so nothing shifts on load
   -------------------------------------------------------------------------- */
.ii-skel-card {
  border: 1px solid var(--ii-line);
  border-radius: 12px;
  overflow: hidden;
  background: var(--ii-bg-surface);
}
.ii-skel-card .ii-skel-media { aspect-ratio: 4 / 3; }
.ii-skel-card .ii-skel-body { padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 8px; }
.ii-skel-card .ii-skel-line { height: 12px; border-radius: 4px; }
.ii-skel-card .ii-skel-line.w60 { width: 60%; }
.ii-skel-card .ii-skel-line.w90 { width: 90%; }
.ii-skel-card .ii-skel-line.w40 { width: 40%; }

/* --------------------------------------------------------------------------
   Skeleton loading
   -------------------------------------------------------------------------- */
.ii-skel {
  background: linear-gradient(90deg, #eee 25%, #f6f6f6 37%, #eee 63%);
  background-size: 400% 100%;
  animation: ii-shim 1.2s ease infinite;
  border-radius: 8px;
}
@keyframes ii-shim {
  0%   { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .ii-skel { animation: none; }
}

/* The two send icons in the message modal had the retired #FF5E00 baked into
   the SVG three ways - a fill attribute, a stroke attribute and an .st0 class
   that arrived with the icon from SVGRepo. They now inherit, so this is where
   the colour comes from and there is one place to change it.

   Scoped to the icon rather than the button: .input-group-text carries the
   legacy theme's own colour, and repainting the whole control would change
   more than the glyph. */
#chat_submit .icon-bar svg { color: var(--ii-accent); }

/* --------------------------------------------------------------------------
   The retired orange, where it survives on borders

   style.css paints `.profile-top-wapper button { border: 1px solid #ff5e00 }`
   at (0,1,1). That wrapper contains the whole profile page, so it caught every
   design-system button rendered inside it - measured, all 16 favourite hearts
   on a seller's profile carried an orange ring, while the identical card on
   /categories did not, because that page has no such wrapper.

   Two rules rather than one: the legacy buttons in there are meant to have a
   border and only need the colour corrected, while the heart is meant to have
   none and needs the width gone too. Stated at (0,1,1) and (0,2,0) so neither
   needs !important.
   -------------------------------------------------------------------------- */
.profile-top-wapper button { border-color: var(--ii-border-strong); }
.profile-top-wapper .ii-card-fav { border: 0; }

/* Same shape in custom.css, on the report control. */
a.report_user_modal { border-color: var(--ii-border-strong); }

/* --------------------------------------------------------------------------
   Hidden-for-now surfaces
   The in-app chat, offers and enquiry flows stay in the codebase and keep
   working server-side, but are removed from the interface so WhatsApp is the
   single, unambiguous way to reach a seller.
   -------------------------------------------------------------------------- */
.ii-hidden-feature { display: none !important; }

/* In-app chat, offers and their notification badges.
   Routes, controllers, models and existing message data are all untouched and
   still work - this only removes the entry points from the interface so that
   WhatsApp is the single way a buyer reaches a seller. Delete this block to
   bring the feature back. */
.btn-chat,
#chat_id,
#offerForm,
#offer_submit,
#messageCountBadge,
#messageCountBadgeMob,
a.notification,
.menu-click.notification { display: none !important; }

/* Following, and the profile-visitor counter (PLATFORM_AUDIT D5).

   26 follow relationships exist across 708 users - under 4% - with no feed, no
   notification and nothing that happens as a result, so it is a social feature
   on a transactional marketplace. Hidden rather than removed, on the same
   terms as chat above: the `followers` table, the routes, the modals and
   /following all still work, and deleting this block brings it back.

   The buyer-side value people actually want here is a saved search - "tell me
   when something like this appears" - not a followed person. That is worth
   building; this is not worth maintaining in the interface until it is.

   The visitor counter goes with it. It reports IP-days as people (C1), counts
   the owner's own views, and at 109,682 rows against 708 users is mostly
   crawler traffic - a vanity number that is also wrong. */
.media-head-right,
.followerButton,
.viewer-box.sidebar_listing_list { display: none !important; }

/* --------------------------------------------------------------------------
   Larger tap targets on small screens
   -------------------------------------------------------------------------- */
@media (max-width: 767px) {
  .btn, .form-control, select.form-control { min-height: 46px; }
  input[type="text"], input[type="tel"], input[type="number"],
  input[type="email"], input[type="search"], select, textarea { font-size: 16px; } /* stops iOS zoom-on-focus */

  /* The guard above is (0,1,0) and loses to any rule naming a container - see
     the price-row note further down this file, where the override has to live
     because equal specificity is decided by ORDER. */
}

/* --------------------------------------------------------------------------
   The compact filter bar

   Replaces a permanently-open panel of eleven controls with a row of
   dropdowns showing only what this category is actually browsed by. The panel
   still exists behind "Plus de filtres" and is still the definition of what
   can be filtered - this is a lighter way in, not a second system.

   Inactive controls stay neutral. Orange is reserved for the applied state,
   and even then it is the warm tint rather than a saturated fill: eight
   filled orange buttons in a row would make every control look urgent and
   none of them look chosen.
   -------------------------------------------------------------------------- */
.ii-fbar {
  position: relative;
  display: flex; align-items: center; gap: var(--ii-space-2);
  margin-bottom: var(--ii-space-3);
  /* A flex item sizes to its content by default, and .ii-filter-bar is itself
     a flex container - so at 375px this grew to 805px inside a 351px parent
     and .wrapper's overflow-x:hidden simply clipped it. Two controls sat off
     screen with no way to reach them: the scroller could not scroll, because
     it had been given all the room it asked for. */
  width: 100%; max-width: 100%; min-width: 0;
}

/* The primaries scroll rather than wrapping into the several broken rows a
   flex-wrap would give at 375px. "Plus de filtres" stays put beside them. */
.ii-fbar-scroll {
  display: flex; align-items: center; gap: var(--ii-space-2);
  overflow-x: auto; overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 2px;                      /* room for the focus ring */
  margin: -2px;
  flex: 1 1 auto; min-width: 0;
}
.ii-fbar-scroll::-webkit-scrollbar { display: none; }

.ii-fbar-btn,
.ii-fbar-more {
  display: inline-flex; align-items: center; gap: 6px;
  flex: 0 0 auto;
  min-height: 40px;
  padding: 0 12px;
  border: 1px solid var(--ii-border);
  border-radius: var(--ii-radius-control);
  background: var(--ii-bg-surface);
  color: var(--ii-text);
  font-family: var(--ii-font-sans);
  font-size: var(--ii-text-sm);
  font-weight: var(--ii-weight-medium);
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  transition: border-color var(--ii-dur-fast) var(--ii-ease),
              background-color var(--ii-dur-fast) var(--ii-ease),
              color var(--ii-dur-fast) var(--ii-ease);
}
.ii-fbar-btn svg,
.ii-fbar-more svg { width: 16px; height: 16px; fill: currentColor; flex: 0 0 16px; }

.ii-fbar-btn:hover,
.ii-fbar-more:hover { border-color: var(--ii-border-strong); }

.ii-fbar-btn:focus-visible,
.ii-fbar-more:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: 1px; }

/* The label sits quieter than the value it qualifies, so "Trier" reads as a
   caption and "Plus récentes" as the answer. */
.ii-fbar-label { color: var(--ii-text-muted); font-weight: var(--ii-weight-regular); }

/* Applied. The warm tint, not the fill. */
.ii-fbar-btn.is-on {
  background: var(--ii-accent-subtle);
  border-color: var(--ii-accent-border);
  color: var(--ii-accent-text);
}
.ii-fbar-btn.is-on .ii-fbar-label { color: inherit; opacity: .75; }
.ii-fbar-btn[aria-expanded="true"] { border-color: var(--ii-border-strong); }

.ii-fbar-more-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: var(--ii-radius-pill);
  background: var(--ii-accent); color: var(--ii-text-on-accent);
  font-size: 11px; font-weight: var(--ii-weight-bold);
}

/* One dropdown surface, moved under whichever button opened it. */
.ii-fdrop {
  position: absolute; top: calc(100% + 4px); left: 0; z-index: 40;
  width: 288px; max-width: calc(100vw - 32px);
  background: var(--ii-bg-surface);
  border: 1px solid var(--ii-border);
  border-radius: var(--ii-radius-card);
  box-shadow: var(--ii-shadow-md);
  overflow: hidden;
}
.ii-fdrop[hidden] { display: none; }
.ii-fdrop-body { max-height: 320px; overflow-y: auto; }

.ii-fdrop-list { list-style: none; margin: 0; padding: 6px; }
.ii-fdrop-opt {
  display: block; width: 100%; text-align: left;
  min-height: 40px; padding: 0 10px;
  border: 0; border-radius: 8px; background: none;
  color: var(--ii-text);
  font-family: var(--ii-font-sans); font-size: var(--ii-text-md);
  cursor: pointer;
}
.ii-fdrop-opt:hover { background: var(--ii-bg-sunken); }
.ii-fdrop-opt.is-on { background: var(--ii-accent-subtle); color: var(--ii-accent-text); font-weight: var(--ii-weight-semibold); }
.ii-fdrop-opt:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: -2px; }

.ii-fdrop-price { padding: var(--ii-space-3); }
.ii-fdrop-price-row { display: flex; gap: var(--ii-space-2); }
.ii-fdrop-price-row label {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; gap: 4px;
  font-size: var(--ii-text-xs); color: var(--ii-text-muted);
}
.ii-fdrop-price-row input {
  width: 100%; min-height: 40px; padding: 0 10px;
  border: 1px solid var(--ii-border-strong); border-radius: var(--ii-radius-control);
  background: var(--ii-bg-surface); color: var(--ii-text);
  font-family: var(--ii-font-sans); font-size: var(--ii-text-md);
}
.ii-fdrop-price-row input:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: -1px; }

/* iOS zooms the viewport the moment a form control under 16px takes focus, and
   does not zoom back out - the page is left enlarged and off-centre, which is
   the "half the filter, half the listings" report.

   There is already a guard for this near the top of the file, but it is
   `input[type="number"]` at (0,1,0) and the rule above is `.ii-fdrop-price-row
   input` at (0,1,1), so the guard never had a chance: measured, these two
   fields were 15px while every other control on the page was 16.

   It has to be stated HERE rather than beside the guard, because an override
   at equal specificity is decided by order, and the guard sits ~140 lines
   earlier in this file. Anything added inside a filter panel needs to clear
   16px on mobile for the same reason.

   Deliberately NOT fixed with `maximum-scale=1` / `user-scalable=no` on the
   viewport meta: that hides the symptom by taking pinch-zoom away from
   everybody, which fails WCAG 1.4.4. */
@media (max-width: 767px) {
  .ii-fdrop-price-row input { font-size: 16px; }
}

.ii-fdrop-quick { display: flex; flex-wrap: wrap; gap: 6px; margin-top: var(--ii-space-3); }
.ii-fdrop-chip {
  min-height: 34px; padding: 0 10px;
  border: 1px solid var(--ii-border); border-radius: var(--ii-radius-pill);
  background: var(--ii-bg-surface); color: var(--ii-text);
  font-family: var(--ii-font-sans); font-size: var(--ii-text-sm);
  cursor: pointer;
}
.ii-fdrop-chip.is-on { background: var(--ii-accent-subtle); border-color: var(--ii-accent-border); color: var(--ii-accent-text); }

.ii-fdrop-apply {
  width: 100%; min-height: 44px; margin-top: var(--ii-space-3);
  border: 0; border-radius: var(--ii-radius-control);
  background: var(--ii-accent); color: var(--ii-text-on-accent);
  font-family: var(--ii-font-sans); font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-semibold); cursor: pointer;
}
.ii-fdrop-apply:hover { background: var(--ii-accent-hover); }

/* --------------------------------------------------------------------------
   "Plus de filtres" on desktop

   An anchored panel on the surface the bar already owns, not a right-hand
   drawer. The secondary set is small and will stay small - Usage, Carburant,
   then Salles de bain, Surface and Quartier - and a drawer implies the large
   form this bar exists to replace. Reusing .ii-fdrop also means the outside
   click, Escape and one-open-at-a-time behaviour are the ones already built.

   Below 992px none of this renders: the button opens the full-screen sheet.
   -------------------------------------------------------------------------- */
.ii-fdrop.ii-fdrop-wide { width: 380px; }
.ii-fdrop.ii-fdrop-wide .ii-fdrop-body { max-height: 420px; }

.ii-fmore { padding: var(--ii-space-3); }
.ii-fmore-group + .ii-fmore-group { margin-top: var(--ii-space-4); }

.ii-fmore-label {
  display: block; margin-bottom: var(--ii-space-2);
  font-size: var(--ii-text-sm); font-weight: var(--ii-weight-semibold);
  color: var(--ii-text-muted);
}

.ii-fmore-chips { display: flex; flex-wrap: wrap; gap: 6px; }

.ii-fmore-chip {
  min-height: 36px; padding: 0 12px;
  border: 1px solid var(--ii-border); border-radius: 999px;
  background: var(--ii-bg-surface); color: var(--ii-text);
  font-family: var(--ii-font-sans); font-size: var(--ii-text-sm);
  cursor: pointer;
}
.ii-fmore-chip:hover { border-color: var(--ii-border-strong); }
.ii-fmore-chip:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: 1px; }

/* The applied state is the warm tint, never a saturated fill - constraint 51. */
.ii-fmore-chip.is-on {
  background: var(--ii-accent-subtle);
  border-color: var(--ii-accent-border);
  color: var(--ii-accent-text);
  font-weight: var(--ii-weight-semibold);
}

.ii-fmore-empty {
  margin: 0; padding: var(--ii-space-2) 0;
  font-size: var(--ii-text-sm); color: var(--ii-text-muted);
}

.ii-fmore-foot {
  display: flex; align-items: center; gap: var(--ii-space-2);
  margin-top: var(--ii-space-4); padding-top: var(--ii-space-3);
  border-top: 1px solid var(--ii-border);
}

/* Reset is a utility, so it stays neutral. Orange is for Vendre, Promouvoir
   and the button that takes you to the results - constraint 51. */
.ii-fmore-reset {
  min-height: 40px; padding: 0 14px;
  border: 1px solid var(--ii-border); border-radius: var(--ii-radius-control);
  background: var(--ii-bg-surface); color: var(--ii-text);
  font-family: var(--ii-font-sans); font-size: var(--ii-text-sm); cursor: pointer;
}
.ii-fmore-reset:hover { border-color: var(--ii-border-strong); }

.ii-fmore-apply {
  flex: 1 1 auto; min-height: 40px;
  border: 0; border-radius: var(--ii-radius-control);
  background: var(--ii-accent); color: var(--ii-text-on-accent);
  font-family: var(--ii-font-sans); font-size: var(--ii-text-sm);
  font-weight: var(--ii-weight-semibold); cursor: pointer;
}
.ii-fmore-apply:hover { background: var(--ii-accent-hover); }

@media (max-width: 991px) {
  .ii-fdrop.ii-fdrop-wide { width: calc(100vw - 32px); }

  /* The legacy "Afficher le filtre" button, hidden rather than removed.

     It opens the same sheet the bar's own Filtres button does, and it sat
     between the heading and the bar in a 77px block - two controls for one
     action, with the duplicate first. #ii_fbar_more still triggers this
     element (.filter_open_btn is the sheet's open mechanism and jQuery can
     click a hidden node), so it has to stay in the DOM. Constraint 22.

     Measured at 375px: 216px between the heading and the bar, 77 of it this.

     !important, unusually for this file. DECISIONS §5.1 says specificity
     rather than load order is what beats the legacy theme, and that holds -
     but responsive.css declares `.db-991 { display: block !important }`, and
     no amount of specificity beats !important. Verified by enumerating every
     display rule matching this element rather than assuming: the 0-3-0
     selector below was losing to a 0-1-0 one. */
  .col-lg-6 > .dn.db-991 { display: none !important; }

  /* "Trier Plus récentes" is the widest thing in a 375px bar and the value is
     already the default on most visits. The label alone keeps three controls
     inside the width; the chosen order is shown in the dropdown, where it is
     being changed. */
  .ii-fbar-btn[data-fdrop="sort"] #ii_fbar_sort_value { display: none; }
  .ii-fbar-btn[data-fdrop="sort"] .ii-fbar-label { color: inherit; }
}

/* Below 992px the panel is already a full-screen sheet, so the bar keeps only
   what a thumb needs: Trier, the scrollable primaries, and the way in. */
@media (max-width: 991px) {
  .ii-fbar-btn, .ii-fbar-more { min-height: 44px; }
  .ii-fdrop { width: calc(100vw - 32px); }
}

/* --------------------------------------------------------------------------
   Breadcrumb and results heading

   Replaces a centred promotional <h1> ("Découvrez des Exclusives, Tout
   Immobilier") and the removable category chip that was doing navigation's job.
   Left-aligned: it is a label for the results below it, not a banner, and
   centring it put the page's identity a long way from the controls that change
   it.

   Tight by design. This block plus the bar, the chips and the count is
   everything between the header and the first listing, and the whole point of
   step 8B is that a buyer reaches inventory without scrolling.
   -------------------------------------------------------------------------- */
.ii-crumbs {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin: 0 0 var(--ii-space-1);
  font-size: var(--ii-text-sm); color: var(--ii-text-muted);
}
.ii-crumbs a { color: var(--ii-text-muted); text-decoration: none; }
.ii-crumbs a:hover { color: var(--ii-text); text-decoration: underline; }
.ii-crumbs a:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: 2px; border-radius: 2px; }
.ii-crumbs [aria-current="page"] { color: var(--ii-text); }
.ii-crumb-sep { color: var(--ii-text-subtle, var(--ii-text-muted)); }

/* Specificity, not !important: .main-title h1 rules live in the legacy theme
   and would otherwise win on load order (DECISIONS §5.1). */
h1.main-listing-title.ii-results-title {
  margin: 0 0 var(--ii-space-3);
  text-align: left;
  font-family: var(--ii-font-sans);
  font-size: var(--ii-text-xl);
  font-weight: var(--ii-weight-semibold);
  line-height: 1.25;
  color: var(--ii-text);
}

@media (max-width: 991px) {
  h1.main-listing-title.ii-results-title { font-size: var(--ii-text-lg); }
}

/* --------------------------------------------------------------------------
   The legacy panel stops being a second filter interface (L45)

   Step 8 shipped the bar and left what it proxies on screen, so every control
   had a twin: measured at 1280x800, .filter-block was 1238x375 with 36 live
   controls at y=239, the bar sat at y=614, and the first listing card started
   at y=734 - no inventory at all above the fold on an 800px viewport.

   NOT display:none, and this is the whole reason this block is commented.

   iiClearFiltersHiddenByCategory() decides what a category cannot express by
   asking jQuery :visible, and :visible is false for every descendant of a
   display:none ancestor. That function runs from the unconditional
   $('#sub_type').trigger('change') on load, so display:none here would clear
   every filter on every page load: /categories/immobilier?propertyStatus=rent
   &bedrooms=3 was measured going from 38 annonces to no filters at all, with
   the chip row and every control reading as clear. That is C4 again - a filter
   state the interface cannot show you and cannot undo.

   visibility + out of flow is what the mobile sheet has always used for the
   same element (transform + visibility:hidden below), and jQuery deliberately
   ignores visibility, so the category rules keep working untouched. It also
   takes the 36 controls out of the tab order and the accessibility tree, which
   left:-99999px alone would not.

   width:100% resolves against the initial containing block here - no ancestor
   is positioned - so the controls keep real layout boxes and bootstrap-select
   can still measure itself.
   -------------------------------------------------------------------------- */
@media (min-width: 992px) {
  .filter-block {
    position: absolute;
    left: -99999px;
    top: 0;
    width: 100%;
    visibility: hidden;
    pointer-events: none;
  }
}

/* --------------------------------------------------------------------------
   Mobile filter sheet

   The drawer itself already existed (.mob-filter-btn opens .filter-block).
   What it lacked was the thing that makes filtering on a phone bearable: a
   count telling you what a filter set yields before you commit to it. That
   count lives in .ii-sheet-foot and is pinned, so it stays readable while the
   controls scroll.

   Presented as a bottom sheet rather than the original left drawer: it rises
   from the thumb, keeps a strip of results visible above it so changes are
   observable, and the 320px-wide drawer left the controls badly cramped.

   Desktop keeps the sidebar untouched — everything here is below 992px.
   -------------------------------------------------------------------------- */
.ii-sheet-grip,
.ii-sheet-head,
.ii-sheet-foot { display: none; }

@media (max-width: 991px) {
  /* Full screen, not an 86vh sheet (FILTER_DESIGN §7).
     The sheet left ~100px of results showing, which is neither useful context
     nor enough room for the controls - it already scrolled with only four
     fields in it, and Stage 2 added facets, counts and a chip row. Filtering
     is a task, so it gets the screen while it is being done. */
  .filter-block {
    left: 0; right: 0;
    top: 0; bottom: 0;
    width: 100%; max-width: none;
    height: 100%; max-height: none;
    background: var(--ii-bg-surface);
    border-radius: 0;
    display: flex; flex-direction: column;
    transform: translateY(100%);
    transition: transform .28s ease;
    box-shadow: none;
  }

  /* The page behind is fully covered, so letting it scroll underneath only
     loses the reader's place in the results they are about to come back to. */
  body.active { overflow: hidden; }
  .filter-block.show-sidebar { transform: translateY(0); }

  /* Backdrop. body.active is set only by the filter drawer, and the existing
     outside-click handler already closes it.

     Drawn from .wrapper, NOT from body, and that is the whole point.

     mmenu wraps the page in #mm-0.mm-page.mm-slideout, which is
     position:relative with z-index:1 - a stacking context. The sheet lives
     inside it, so the sheet's z-index:9999 only competes with its siblings in
     that context; the entire slideout resolves at z-index 1 against the rest
     of the page. A backdrop on body at 9998 therefore painted OVER the sheet,
     not under it - greying the controls out and swallowing every tap. On a
     real phone the filter was completely unusable.

     .wrapper is inside the slideout and is position:relative with z-index
     auto, so it creates no stacking context of its own: its ::after lands in
     the same context as the sheet, where 9998 < 9999 means what it looks like
     it means.

     This is why the earlier "the sheet opens correctly" check was wrong. It
     measured the sheet's position and stopped there; position was never the
     problem, reachability was. */
  body.active .wrapper::after {
    content: "";
    position: fixed; inset: 0;
    background: rgba(0, 0, 0, .45);
    z-index: 9998;
  }

  /* The bottom navigation is a sibling of the slideout, at z-index 1030, so
     the same stacking rule puts it over the sheet - covering the bottom 60px,
     which is exactly where the result count and the Apply button live. The
     raised Vendre button was sitting on top of Apply.

     Hidden while the sheet is open rather than fought with z-index: the sheet
     is modal, so site-wide navigation underneath it is wrong anyway, and this
     is the one place the two fixed layers can collide. */
  body.active .bnav { display: none; }

  .ii-sheet-grip {
    display: block;
    width: 38px; height: 4px;
    margin: 8px auto 0;
    border-radius: 999px;
    background: var(--ii-border-strong);
    flex: 0 0 auto;
  }

  .ii-sheet-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 10px 16px 8px;
    border-bottom: 1px solid var(--ii-line);
    font-size: 16px; font-weight: 700; color: var(--ii-ink);
    flex: 0 0 auto;
  }
  .ii-sheet-clear {
    margin-left: auto;
    margin-right: var(--ii-space-2);
    min-height: 44px;
    padding: 0 var(--ii-space-2);
    border: 0;
    background: transparent;
    color: var(--ii-accent-text);
    font-size: 14px;
    font-weight: 600;
  }
  .ii-sheet-clear[hidden] { display: none; }
  .ii-sheet-clear:focus-visible {
    outline: 2px solid var(--ii-focus);
    outline-offset: 2px;
  }

  .ii-sheet-x {
    border: 0; background: none;
    font-size: 26px; line-height: 1;
    color: var(--ii-muted);
    cursor: pointer;
    /* Was 24x26 (UI_AUDIT m4): the dismiss control of a modal sheet at just
       over half the minimum target. The glyph stays 26px; only the box grows. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    padding: 0;
  }

  /* The controls scroll; the count bar does not. */
  .filter-block .sidebar_listing_list {
    flex: 1 1 auto;
    height: auto !important;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    box-shadow: none !important;
    padding: 12px 15px !important;
  }

  /* The sheet has its own header, so the panel's original close row would be
     a second, redundant dismiss control. */
  .filter-block .filter-close-wapper { display: none !important; }

  .ii-sheet-foot {
    display: block;
    flex: 0 0 auto;
    padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
    border-top: 1px solid var(--ii-line);
    background: var(--ii-bg-surface);
  }
  .ii-sheet-apply {
    display: block; width: 100%;
    border: 0; border-radius: 10px;
    background: var(--ii-accent); color: var(--ii-text-on-accent);
    font-size: 16px; font-weight: 700;
    padding: 13px 16px;
    cursor: pointer;
  }
  .ii-sheet-apply:active { opacity: .9; }

  /* --- ONE filled action in this sheet, and Effacer is not it -------------
     Reported as "a clear button which competes with the view listing button",
     with a screenshot showing TWO full-width orange bars stacked at the
     bottom: `Effacer` above `Voir 2 annonces`.

     That state could not be reproduced, and the search for it was thorough
     rather than cursory: the exact screen (Vehicule, "Louer 2", the same four
     dropdowns, "Voir 2 annonces") at 375, 390, 430 and at a 660px-tall
     viewport; the sheet markup contains exactly two controls, the header
     `Effacer` and the footer apply; no stylesheet failed to load; only TWO
     rules in the whole cascade can touch `.ii-sheet-clear` - bootstrap's
     `button { border-radius: 0 }` and its own - and `git log -S` shows the
     control has been touched by ONE commit in the project's history and has
     never been a filled footer button.

     So this is not a repair. It is the guarantee stated where it can be
     checked, because `.ii-sheet-clear` was (0,1,0) - the weakest possible
     class selector, which almost any legacy rule outranks. The DESIGN was
     already right and merely undefended.

     Reset is a utility: text, auto width, no fill, in the header. The one
     filled control in the sheet is the footer action, and `Voir N annonces`
     keeps its count deliberately - filters apply live, so that number is the
     payoff of the filtering rather than a promise about what tapping will do.
     Renaming it to `Voir les résultats` was considered and dropped: it loses
     information to fix a problem the label never had.

     NO `!important` - constraint 97 says confirm what you are fighting first,
     and nothing here is fighting. That makes this rule a probe as well as a
     guard: if a filled `Effacer` still appears on a device after this, the
     offender carries `!important`, which is worth knowing and is not
     guessable from here. */
  .filter-block .ii-sheet-head .ii-sheet-clear {
    display: inline-flex;
    align-items: center;
    width: auto;
    background: transparent;
    background-color: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    color: var(--ii-accent-text);
    font-size: 14px;
    font-weight: 600;
  }
  .filter-block .ii-sheet-head .ii-sheet-clear[hidden] { display: none; }

  /* Anything else that lands in the footer is not a second primary. The apply
     button is addressed by its own class so this cannot catch it. */
  .filter-block .ii-sheet-foot button:not(.ii-sheet-apply) {
    width: auto;
    background: transparent;
    color: var(--ii-accent-text);
    border: 0;
    font-weight: 600;
  }
}

@media (prefers-reduced-motion: reduce) {
  .filter-block { transition: none; }
}

/* --------------------------------------------------------------------------
   Phone-first CTA on the legacy auth screens

   These pages still exist and are still reachable, so they lead with the
   route that does not ask for a password, an email, or a verification click.
   The old form stays below it for accounts that predate phone auth.
   -------------------------------------------------------------------------- */
.ii-phone-first { margin: 4px 0 18px; }

.ii-phone-first-btn {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  width: 100%;
  min-height: 50px;
  padding: 12px 16px;
  border-radius: 10px;
  background: var(--ii-accent);
  color: var(--ii-text-on-accent);
  font-size: 16px; font-weight: 700;
  text-decoration: none;
  transition: background .15s ease;
}
.ii-phone-first-btn:hover,
.ii-phone-first-btn:focus { background: var(--ii-accent-dark); color: var(--ii-text-on-accent); text-decoration: none; }
.ii-phone-first-btn:focus-visible { outline: 2px solid var(--ii-ink); outline-offset: 2px; }
.ii-phone-first-btn svg { width: 19px; height: 19px; fill: var(--ii-bg-surface); flex: 0 0 19px; }

.ii-phone-first-note {
  margin: 8px 2px 0;
  /* 13, matching every other muted secondary line here - the banner and
     legacy-switch bodies are both 13. */
  font-size: var(--ii-text-sm); line-height: 1.45;
  color: var(--ii-muted);
  text-align: center;
}

/* "ou" divider between the phone route and the legacy options */
.ii-phone-first-or {
  position: relative;
  text-align: center;
  margin: 16px 0 2px;
}
.ii-phone-first-or::before {
  content: "";
  position: absolute; left: 0; right: 0; top: 50%;
  border-top: 1px solid var(--ii-line);
}
.ii-phone-first-or span {
  position: relative;
  background: var(--ii-bg-surface);
  padding: 0 12px;
  font-size: 12px;
  color: var(--ii-muted);
  text-transform: uppercase;
  letter-spacing: .04em;
}

/* Back-link to the password form on the phone screen */
.pa-alt { margin-top: 18px; text-align: center; }
.pa-alt a { color: var(--ii-accent-text); font-weight: 600; }

/* --------------------------------------------------------------------------
   Held-for-review banner (ad detail, owner only)
   -------------------------------------------------------------------------- */
.ii-pending-banner {
  display: flex; align-items: flex-start; gap: 12px;
  margin: 14px 0 4px;
  padding: 13px 15px;
  border: 1px solid var(--ii-accent-border);
  border-left: 4px solid var(--ii-warning);
  border-radius: 10px;
  background: var(--ii-warning-subtle);
  color: var(--ii-ink);
}
.ii-pending-banner svg { width: 20px; height: 20px; fill: var(--ii-warning); flex: 0 0 20px; margin-top: 1px; }
/* 15 over a 13px body: rounding down would flatten the heading into the
   sentence it introduces. */
.ii-pending-banner strong { display: block; font-size: var(--ii-text-md); margin-bottom: 2px; }
.ii-pending-banner span { font-size: 13px; line-height: 1.45; color: var(--ii-muted); }

/* --------------------------------------------------------------------------
   Result count above the grid

   The filter sheet's count is mobile-only, which left desktop — where the
   filters are always on screen — with no idea how many ads a filter set
   yields. This one shows at every width.
   -------------------------------------------------------------------------- */
.ii-result-count {
  margin: 2px 0 10px;
  padding: 0 15px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ii-muted);
}
.ii-result-count:empty { display: none; }

/* --------------------------------------------------------------------------
   Active-filter chips

   The applied filters, shown beside the count and individually removable.
   Before this the only way to discover why a grid looked short was to reopen
   the sheet and read every control.

   The row scrolls horizontally rather than wrapping: on a phone three chips
   plus the count already exceed 375px, and wrapping pushed the first row of
   cards below the fold. Scrollbar hidden, momentum kept.
   -------------------------------------------------------------------------- */
.ii-filter-bar {
  display: flex;
  align-items: center;
  gap: var(--ii-space-3);
  flex-wrap: wrap;
}
.ii-filter-bar .ii-result-count { margin: 2px 0 10px; }

.ii-chips {
  display: flex;
  align-items: center;
  gap: var(--ii-space-2);
  padding: 0 15px;
  margin: 2px 0 10px;
  min-width: 0;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.ii-chips::-webkit-scrollbar { display: none; }
.ii-chips:not(.has-chips) { display: none; }

.ii-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  /* 32px, not the 44px minimum: a chip is a secondary control sitting beside
     its own primary (the Filtres trigger), and every one of them is a
     shortcut for something reachable in the sheet. Losing a tap here costs a
     re-tap, not a dead end. */
  min-height: 32px;
  padding: 6px 10px 6px 12px;
  border: 1px solid var(--ii-border-strong);
  border-radius: var(--ii-radius-pill);
  background: var(--ii-bg-surface);
  color: var(--ii-text);
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
}
.ii-chip:hover { border-color: var(--ii-accent); text-decoration: none; }
.ii-chip:active { transform: scale(.97); }
.ii-chip:focus-visible {
  outline: 2px solid var(--ii-focus);
  outline-offset: 2px;
}

.ii-chip-x {
  font-size: 16px;
  line-height: 1;
  color: var(--ii-text-muted);
}

/* Measured at 375px, a chip was 138x32 - the whole pill is the target, the x
   is only a glyph - so it fell 12px short of the 44px minimum.

   Extending the touch area past the visual box with an absolutely positioned
   ::before was tried first and does not work here: .ii-chips scrolls
   horizontally, and overflow-x:auto forces overflow-y to clip, so the
   extension is cut off exactly where it is needed. Verified by hit-testing
   the extended edges, which still reported the container.

   So the pill carries the height itself. Twelve pixels on the filter bar is
   a fair price for a control that is now the main way to see and remove an
   active filter. */
.ii-chip { min-height: 44px; }
.ii-chip:hover .ii-chip-x { color: var(--ii-accent-text); }

/* Deliberately not styled as destructive. It clears filters, which is
   trivially undoable - red would rank it with Supprimer. */
.ii-chip-clear {
  border-style: dashed;
  color: var(--ii-accent-text);
  font-weight: 600;
}

@media (prefers-reduced-motion: reduce) {
  .ii-chip:active { transform: none; }
}

/* --------------------------------------------------------------------------
   Quick price ranges inside the filter sheet

   Wraps rather than scrolls, unlike .ii-chips: these sit inside the sheet
   with nothing competing for the row, and a hidden fourth range would be a
   filter the buyer never discovers.
   -------------------------------------------------------------------------- */
.ii-price-quick {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ii-space-2);
  margin-top: var(--ii-space-2);
}
.ii-price-quick:empty { display: none; }

/* --------------------------------------------------------------------------
   Facet chips inside the sheet (Louer / Acheter, and Stage 2 facets)

   Same shape as the price ranges deliberately: they are the same gesture on
   the same surface, and giving them two appearances would imply a difference
   that is not there.
   -------------------------------------------------------------------------- */
.ii-facet-li { display: block; width: 100%; }

.ii-facet-label {
  display: block;
  margin-bottom: var(--ii-space-2);
  font-size: 13px;
  font-weight: 600;
  color: var(--ii-text-muted);
}

.ii-facet-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ii-space-2);
}

/* 44px, not 34. Measured in the open sheet at 375px: every chip here - the
   price ranges and Louer/Acheter - came out 34px tall, ten short of the
   minimum, on the surface where the whole interaction is tapping chips. */
.ii-price-chip,
.ii-facet-chip {
  flex: 0 0 auto;
  min-height: 44px;
  padding: 7px 12px;
  border: 1px solid var(--ii-border-strong);
  border-radius: var(--ii-radius-pill);
  background: var(--ii-bg-surface);
  color: var(--ii-text);
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
}
.ii-price-chip:active,
.ii-facet-chip:active { transform: scale(.97); }
.ii-price-chip:focus-visible,
.ii-facet-chip:focus-visible {
  outline: 2px solid var(--ii-focus);
  outline-offset: 2px;
}

/* Ink on the accent, never white - the label sits on --ii-accent and white
   measures 2.80 there. See DECISIONS §5.1. */
.ii-price-chip.is-on,
.ii-facet-chip.is-on {
  background: var(--ii-accent);
  border-color: var(--ii-accent);
  color: var(--ii-text-on-accent);
}

/* An option that matches nothing is a dead end presented as a choice
   (FILTER_DESIGN §7). Hidden rather than disabled: a disabled control still
   occupies the row and still invites a tap. */
.ii-facet-chip[data-count="0"] { display: none; }

.ii-facet-count {
  margin-left: 5px;
  font-weight: 400;
  opacity: .7;
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  .ii-price-chip:active,
  .ii-facet-chip:active { transform: none; }
}

/* ==========================================================================
   Account / Modifier le profil

   Scoped to #pills-editProfile throughout. `.form-control`, `.caption-box`
   and `.title-label` are shared with the whole legacy theme, so every rule
   here is confined to this panel - restyling them globally has an unknown
   blast radius across screens nobody has re-checked.

   Done in CSS rather than by rebuilding the markup on purpose: every input
   carries a `name` the controller writes from, and updateProfile() writes
   only the fields the request contains. Moving markup to change a layout is
   how a column gets silently dropped (issue L30, closed today).
   ========================================================================== */

/* The password pane's panel, which dark mode never reached.

   "Modifier le profil" sits on `.ii-acct-card` and is themed. "Changer le mot
   de passe" sits on the bare legacy `.form-wapper`, and style.css paints that
   `#fff` at (0,1,0) - the only rule that matches it. So the panel stayed white
   on a dark page and took its three labels down with it: `--ii-text-secondary`
   on white measures 2.57:1. The labels were never wrong; the surface under
   them was.

   Scoped to `#pills-password` (1,1,0) rather than fixed globally, for the
   reason stated above this block: `.form-wapper` is shared with the whole
   legacy theme and repainting it everywhere has a blast radius across screens
   nobody has re-checked.

   `--ii-bg-surface`, not `transparent`: the token is white in light mode, so
   light is byte-identical to before and only dark changes. No dark scope
   needed and no second copy to keep in step. (L74.) */
#pills-password .form-wapper {
  background: var(--ii-bg-surface);
}

/* One page title. It was an <h4> at 18px/500 sitting above section headings
   at 24px/700 - the sections shouting louder than the page they belong to. */
#pills-editProfile .tab-content-head h4,
#pills-editProfile .tab-content-head h4 b {
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--ii-text);
}

/* Sections are labels, not headlines. 24px/700 three times over turned the
   form into three competing blocks; a rule plus a quiet label reads as
   structure instead. */
#pills-editProfile .account_headding_div {
  border-top: 1px solid var(--ii-border);
  margin-top: var(--ii-space-7);
  padding-top: var(--ii-space-6);
  margin-bottom: var(--ii-space-4);
}
#pills-editProfile .account_headding_div h3 {
  font-size: 15px;
  font-weight: 600;
  color: var(--ii-text-muted);
}
#pills-editProfile .account_headding_div p { font-size: 13px; color: var(--ii-text-muted); }
#pills-editProfile .account_headding_div .fa-lock { color: var(--ii-text-muted); font-size: 12px; }

/* Label above field.
   The label sat in its own column, so fields began at x=497 on a 913px
   column - a ~180px canyon on every row, and two scan targets per field
   instead of one. */
#pills-editProfile .caption-box {
  display: block !important;
  margin-bottom: var(--ii-space-5);
}
#pills-editProfile .title-label {
  display: block;
  width: 100%;
  margin: 0 0 6px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ii-text);
  text-align: left;
}

/* A measured column. Fields were 444px inside a 913px card, so the right
   half of every row was empty at desktop width without looking deliberate.
   Bootstrap's w-50 is width:50%!important, hence the !important here. */
#pills-editProfile .form-control,
#pills-editProfile .search_option_two,
#pills-editProfile .d-flex.m-flex {
  width: 100% !important;
  max-width: 560px;
}
#pills-editProfile textarea.form-control { max-width: 560px; }

/* Parsley writes its messages into these; centred under a full-width field
   they floated away from the input they describe. */
#pills-editProfile [id^="parsley-"] {
  text-align: left !important;
  max-width: 560px;
}

/* --- The avatar, as a control --------------------------------------------

   Replaces a 167x21 text button - the smallest target on the page for its
   most visual action - with the avatar itself at 96px, badged so it reads as
   editable, plus a text link for anyone who does not interpret the badge. */
.ii-avatar-field { display: flex; flex-direction: column; align-items: center; gap: var(--ii-space-3); }

.ii-file-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ==========================================================================
   Account / Modifier le profil

   One settings card, read top to bottom. Everything here consumes the
   semantic tokens - no new palette (constraint 16). Scoped to .ii-acct* so
   nothing leaks into the shared .form-control the whole legacy theme uses.
   ========================================================================== */

/* Narrower than the column it sits in. The field column was 913px wide with
   444px fields inside it, so half the card was permanently empty. */
.ii-acct-card {
  max-width: 640px;
  background: var(--ii-bg-surface);
  border: 1px solid var(--ii-border);
  border-radius: var(--ii-radius-card);
  box-shadow: 0 1px 2px rgba(16, 16, 16, .04);
  padding: var(--ii-space-6) var(--ii-space-6) var(--ii-space-5);
}

.ii-acct-head { margin-bottom: var(--ii-space-5); }

.ii-acct-title {
  margin: 0 0 4px;
  font-size: 22px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--ii-text);
}
.ii-acct-sub {
  margin: 0;
  font-size: 14px;
  color: var(--ii-text-muted);
}

/* Section marker: a tinted chip and two lines. Replaces three 24px/700
   headings that shouted louder than the page title. */
.ii-acct-sec {
  display: flex;
  align-items: flex-start;
  gap: var(--ii-space-3);
  margin: 0 0 var(--ii-space-4);
}
.ii-acct-sec-icon {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--ii-accent-tint, #FFEDD5);
  color: var(--ii-accent-text);
}
.ii-acct-sec-text { display: block; min-width: 0; }
.ii-acct-sec-text strong {
  display: block;
  font-size: 15px; font-weight: 600; line-height: 1.3;
  color: var(--ii-text);
}
.ii-acct-sec-text small {
  display: block;
  margin-top: 2px;
  font-size: 13px; line-height: 1.4;
  color: var(--ii-text-muted);
}

/* A hairline between sections, not a nested card. 28px above / 24px below
   keeps majors ~30px apart without the page growing. */
/* The phone field and the actions are SIBLINGS of .ii-acct-body, not children
   of it, so the container has to carry the rhythm to whatever follows. Without
   this the email verification hint - the last thing inside - ended flush
   against the "Numero mobile" label at a 0px gap, which is the "not much
   space" report. Same 20px every field already uses. */
.ii-acct-body { margin-bottom: 20px; }

.ii-acct-body > .ii-acct-sec:not(:first-child) {
  margin-top: var(--ii-space-6);
  padding-top: var(--ii-space-6);
  border-top: 1px solid var(--ii-border);
}

/* Fields: label directly above its control, 20px between fields. */
.ii-acct-field { margin-bottom: 20px; }

/* No `:last-of-type` reset here. It was meant to stop the final field adding
   space before the card's own padding, but `:last-of-type` matches by ELEMENT
   type, not by class - so it fired on whichever .ii-acct-field happened to be
   the last <div> in its parent. That is the Email field, which is last inside
   .ii-acct-body, while "Numero mobile" lives in a SIBLING container and
   follows it. Result: 20px of rhythm under every field and 0px under the email
   one, so the phone label butted straight against the verification hint -
   reported from a real phone as "not much space" between them.

   The rhythm is uniform now. Nothing needs the reset: .ii-acct-actions brings
   its own margin-top and the card its own padding. */

.ii-acct-flabel,
.ii-acct-label {
  display: block;
  margin-bottom: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ii-text);
}

/* One control treatment. Not applied to .form-control globally - only inside
   this card - because that selector is shared with the entire legacy theme. */
.ii-acct-card .form-control {
  width: 100%;
  min-height: 48px;
  border: 1px solid var(--ii-border-strong, #D4D4D4);
  border-radius: var(--ii-radius-control);
  background: var(--ii-bg-surface);
  color: var(--ii-text);
  font-size: 15px;
  padding: 12px 14px;
  box-shadow: none;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.ii-acct-card .form-control:hover:not(:read-only) { border-color: var(--ii-neutral-300, #B8B8B8); }
.ii-acct-card .form-control:focus {
  border-color: var(--ii-focus);
  box-shadow: var(--ii-focus-ring);
  outline: none;
}
.ii-acct-card .form-control[readonly] {
  background: var(--ii-bg-sunken, #F7F7F8);
  color: var(--ii-text-muted);
}

/* Biography: 96px sits inside the 90-110px the brief asks for, with the
   counter tucked into the corner rather than adding a row. */
.ii-acct-textwrap { position: relative; }
.ii-acct-textarea {
  min-height: 96px !important;
  height: 96px !important;
  /* Vertical only, so the drag handle can never pull the field out of the
     card's width, and capped so it cannot be dragged past the viewport
     either. `none` was the other option and is worse: a bio is the one field
     here where more room is genuinely useful. */
  resize: vertical;
  max-height: 320px;
}
/* Below the field, right-aligned, rather than floating inside it. Sitting in
   the bottom-right corner of the textarea, it was overlapped by the text as
   soon as the bio reached the last line - the counter and the characters it
   counts were competing for the same pixels. */
.ii-acct-count {
  display: block;
  margin-top: var(--ii-space-1);
  text-align: right;
  font-size: 12px;
  color: var(--ii-text-muted);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}

/* Leading-icon fields (region, email). */
.ii-acct-select, .ii-acct-inputwrap { position: relative; display: block; }
.ii-acct-select-icon, .ii-acct-input-icon {
  position: absolute;
  left: 14px; top: 50%;
  transform: translateY(-50%);
  color: var(--ii-text-muted);
  pointer-events: none;
  line-height: 0;
}
.ii-acct-card .ii-acct-selectel,
.ii-acct-card .ii-acct-haslead { padding-left: 40px; }

.ii-acct-card .ii-acct-selectel {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding-right: 40px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236B6B6B' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
}

/* Verified: present, but quieter than the field it describes. */
.ii-acct-verified {
  position: absolute;
  right: 8px; top: 50%;
  transform: translateY(-50%);
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 8px;
  border-radius: 999px;
  background: var(--ii-success-subtle);
  color: var(--ii-success);
  font-size: 12px; font-weight: 600;
  white-space: nowrap;
}
.ii-acct-verified-line {
  display: inline-flex; align-items: center; gap: 5px;
  margin-top: 6px;
  font-size: 12px; font-weight: 600;
  color: var(--ii-success);
}

/* Phone row: code, number, action. */
.ii-acct-phone { display: flex; align-items: center; gap: var(--ii-space-2); }
.ii-acct-phone-code {
  flex: 0 0 auto;
  display: inline-flex; align-items: center;
  min-height: 48px;
  padding: 0 12px;
  border: 1px solid var(--ii-border-strong, #D4D4D4);
  border-radius: var(--ii-radius-control);
  background: var(--ii-bg-sunken, #F7F7F8);
  color: var(--ii-text-muted);
  font-size: 15px;
}
.ii-acct-card .ii-acct-phone-input { flex: 1 1 auto; min-width: 0; }

/* Buttons. 48px so a button matches the field it submits - they were 40px
   against 48px inputs, and under the 44px minimum. */
.ii-acct-btn-primary, .ii-acct-btn-ghost {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 48px;
  padding: 0 18px;
  border-radius: var(--ii-radius-control);
  font-size: 15px; font-weight: 600;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease, opacity 120ms ease;
}
.ii-acct-btn-primary {
  border: 1px solid var(--ii-accent);
  background: var(--ii-accent);
  color: var(--ii-text-on-accent);   /* ink, never white - DECISIONS §5.1 */
}
.ii-acct-btn-primary:hover:not(:disabled) { background: var(--ii-accent-hover, #CC5815); border-color: var(--ii-accent-hover, #CC5815); }
/* Save is disabled until the form is dirty. At opacity .5 the brand orange
   read as a washed-out version of itself - "broken" rather than "not yet", the
   reading a review reported. A neutral fill says waiting; the button returns
   to full brand orange the moment anything changes. */
.ii-acct-btn-primary:disabled {
  background: var(--ii-bg-sunken);
  border-color: var(--ii-border-strong);
  color: var(--ii-text-disabled);
  opacity: 1;
  cursor: not-allowed;
}
.ii-acct-btn-ghost {
  border: 1px solid var(--ii-border-strong, #D4D4D4);
  background: var(--ii-bg-surface);
  color: var(--ii-text);
}
.ii-acct-btn-ghost:hover { background: var(--ii-bg-sunken, #F7F7F8); }
.ii-acct-btn-primary:focus-visible, .ii-acct-btn-ghost:focus-visible {
  outline: 2px solid var(--ii-focus); outline-offset: 2px;
}

.ii-acct-actions {
  display: flex; justify-content: flex-end; gap: var(--ii-space-3);
  margin-top: var(--ii-space-6);
  padding-top: var(--ii-space-5);
  border-top: 1px solid var(--ii-border);
}

.ii-acct-err:not(:empty) {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  color: var(--ii-danger);
}

/* Photo row: avatar beside one compact action that states the limits. */
.ii-acct-block { margin-bottom: var(--ii-space-5); }
.ii-photo-row { display: flex; align-items: center; gap: var(--ii-space-4); }
.ii-photo-avatar { flex: 0 0 auto; }

/* Guidance beside the avatar, not a second button - see the note in
   account.blade.php. Replaced .ii-photo-cta, which was a bordered control
   duplicating the avatar's own action. */
.ii-photo-hint { margin: 0; }
.ii-photo-hint strong {
  display: block;
  font-size: 14px; font-weight: 600;
  color: var(--ii-text);
}
.ii-photo-hint small {
  display: block; margin-top: 2px;
  font-size: 12px; color: var(--ii-text-muted);
}

@media (max-width: 575.98px) {
  .ii-acct-card { padding: var(--ii-space-4); border-radius: var(--ii-radius-control); }
  .ii-acct-title { font-size: 20px; }
  /* The action button drops below the number so neither can overflow. */
  .ii-acct-phone { flex-wrap: wrap; }
  .ii-acct-card .ii-acct-phone-input { flex: 1 1 140px; }
  .ii-acct-phone .ii-acct-btn-ghost { flex: 1 1 100%; margin-top: var(--ii-space-2); }
  .ii-acct-actions { flex-direction: column-reverse; }
  .ii-acct-actions > * { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .ii-acct-card .form-control,
  .ii-acct-btn-primary, .ii-acct-btn-ghost { transition: none; }
}

/* The selector carries .profile-top-wapper because style.css owns
   `.profile-top-wapper button { border-radius: 3px }` at (0,1,1) - one class
   plus one element - which outranks a lone .ii-avatar-btn at (0,1,0) no matter
   how much later it appears. The avatar therefore rendered as a 3px-cornered
   box behind a 50% image: a square with a circle sitting in it, the sunken
   background showing at the corners. Matching the specificity is the fix;
   !important on a border-radius would have been the blunt one. */
.profile-top-wapper .ii-avatar-btn,
.ii-avatar-btn {
  position: relative;
  width: 96px; height: 96px;
  /* !important: the theme puts 15px of padding on every <button>, which left
     a 66px content box and squashed the avatar to 64x96 inside its own
     frame - the background then showed round it as a box. */
  padding: 0 !important;
  border: 0; border-radius: 50%;
  background: var(--ii-bg-sunken, var(--ii-neutral-25));
  cursor: pointer;
}
.ii-avatar-btn img {
  width: 96px; height: 96px;
  /* style.css sets a blanket `img { max-width: 100% }`, which resolves
     against the padded content box rather than the 96px frame. */
  max-width: none;
  border-radius: 50%;
  object-fit: cover;
  display: block;
}
.ii-avatar-badge {
  position: absolute; right: -2px; bottom: -2px;
  width: 32px; height: 32px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--ii-accent);
  color: var(--ii-text-on-accent);      /* ink on orange, never white */
  border: 2px solid var(--ii-bg-surface);
}
.ii-avatar-btn:hover .ii-avatar-badge { background: var(--ii-accent-hover); }

.ii-avatar-link {
  border: 0; background: none; padding: var(--ii-space-2) var(--ii-space-3);
  min-height: 44px;
  color: var(--ii-accent-text);          /* orange as TEXT is the 5.18 tone */
  font-size: 14px; font-weight: 600; cursor: pointer;
}
.ii-avatar-link:hover { text-decoration: underline; }

/* --- The editor ----------------------------------------------------------- */

.ii-crop[hidden] { display: none; }
.ii-crop {
  position: fixed; inset: 0; z-index: 10000;
  display: flex; align-items: center; justify-content: center;
  padding: var(--ii-space-4);
}
.ii-crop-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,.55); }

.ii-crop-panel {
  position: relative;
  width: 100%; max-width: 360px;
  background: var(--ii-bg-surface);
  border-radius: var(--ii-radius-sheet, 20px);
  padding: var(--ii-space-5);
  box-shadow: var(--ii-shadow-3, 0 18px 48px rgba(0,0,0,.24));
}

.ii-crop-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--ii-space-4); }
.ii-crop-title { margin: 0; font-size: 17px; font-weight: 600; color: var(--ii-text); }
.ii-crop-x {
  border: 0; background: none; cursor: pointer;
  font-size: 26px; line-height: 1; color: var(--ii-text-muted);
  min-width: 44px; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
}

/* The stage is square and the mask is a ring, so what sits inside the circle
   is exactly what gets written - no separate preview to disagree with it. */
.ii-crop-stage { position: relative; width: 288px; max-width: 100%; margin: 0 auto; aspect-ratio: 1; }
.ii-crop-stage canvas {
  width: 100%; height: 100%; display: block;
  border-radius: var(--ii-radius-card);
  background: var(--ii-neutral-100);
  touch-action: none;          /* the canvas owns drag and pinch, not the page */
  cursor: grab;
}
.ii-crop-stage canvas:active { cursor: grabbing; }
.ii-crop-mask {
  position: absolute; inset: 0; pointer-events: none;
  border-radius: var(--ii-radius-card);
  box-shadow: 0 0 0 9999px rgba(255,255,255,.72) inset;
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
  -webkit-mask: radial-gradient(circle at 50% 50%, transparent 0 49.5%, #000 50%);
          mask: radial-gradient(circle at 50% 50%, transparent 0 49.5%, #000 50%);
}

.ii-crop-hint { margin: var(--ii-space-3) 0 0; text-align: center; font-size: 13px; color: var(--ii-text-muted); }

.ii-crop-zoom { display: flex; align-items: center; gap: var(--ii-space-3); margin-top: var(--ii-space-3); color: var(--ii-text-muted); }
.ii-crop-zoom input[type="range"] { flex: 1; min-height: 44px; accent-color: var(--ii-accent); }

.ii-crop-actions { display: flex; gap: var(--ii-space-3); margin-top: var(--ii-space-5); }
.ii-crop-actions button { flex: 1; min-height: 48px; border-radius: var(--ii-radius-control); font-size: 15px; font-weight: 600; cursor: pointer; }
.ii-crop-cancel { background: var(--ii-bg-surface); border: 1px solid var(--ii-border-strong); color: var(--ii-text); }
.ii-crop-save   { background: var(--ii-accent); border: 1px solid var(--ii-accent); color: var(--ii-text-on-accent); }
.ii-crop-save:hover { background: var(--ii-accent-hover); border-color: var(--ii-accent-hover); }

.ii-crop-x:focus-visible,
.ii-crop-actions button:focus-visible,
.ii-crop-zoom input:focus-visible {
  outline: 2px solid var(--ii-focus);
  outline-offset: 2px;
}

@media (max-width: 400px) {
  .ii-crop-stage { width: 100%; }
}

/* --- Actions -------------------------------------------------------------

   Measured before this: every button on the panel was white with a grey
   border at 40-41px and a 13px label. So the page's primary action - "Mettre
   à jour le profil" - looked exactly like the secondary one beside it, sat
   4px under the minimum target, and carried the smallest text on a screen of
   48px controls.

   Now: one filled primary, one outline secondary, both 48px to match the
   fields they submit. Ink on the orange, never white (DECISIONS §5.1 - white
   measures 2.80 there). */
/* !important, deliberately. `.small-btn` in style.css declares min-height,
   min-width and font-size with !important, which an id selector does not beat
   on its own; and `.btn.big-btn` here forces background-color: surface, which
   is why the primary action rendered white. Enumerated the matching rules
   before assuming load order, per DECISIONS §5.1. */
#pills-editProfile .btn,
#pills-editProfile .orange-border-btn {
  min-height: 48px !important;
  padding: 0 var(--ii-space-5);
  font-size: 15px !important;
  font-weight: 600;
  border-radius: var(--ii-radius-control);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* The submit of the profile form is the one primary action here. */
#pills-editProfile form.parsleyValidateForm > .text-right .btn[type="submit"] {
  background-color: var(--ii-accent) !important;
  border-color: var(--ii-accent) !important;
  color: var(--ii-text-on-accent) !important;
  min-width: 200px !important;
}
#pills-editProfile form.parsleyValidateForm > .text-right .btn[type="submit"]:hover {
  background-color: var(--ii-accent-hover) !important;
  border-color: var(--ii-accent-hover) !important;
}
#pills-editProfile form.parsleyValidateForm > .text-right .btn[type="submit"]:active {
  background-color: var(--ii-accent-pressed) !important;
  border-color: var(--ii-accent-pressed) !important;
}

/* Everything else on the panel stays secondary, so the primary reads as the
   only place the form is committed. */
#pills-editProfile .ii-acct-readonly .btn {
  background: var(--ii-bg-surface);
  border: 1px solid var(--ii-border-strong);
  color: var(--ii-text);
}
#pills-editProfile .ii-acct-readonly .btn:hover { border-color: var(--ii-accent); color: var(--ii-accent-text); }

/* --- Focus ---------------------------------------------------------------

   `.form-control:focus` is declared about fifteen times across style.css, so
   load order alone is not enough to win here - the same lesson DECISIONS §5.1
   records for the trust signals. Scoped by id, which outranks all of them. */
#pills-editProfile .form-control:focus,
#pills-editProfile .btn:focus-visible,
#pills-editProfile .orange-border-btn:focus-visible,
#pills-editProfile button.dropdown-toggle:focus-visible,
#pills-editProfile .ii-avatar-btn:focus-visible {
  border-color: var(--ii-focus);
  box-shadow: var(--ii-focus-ring);
  outline: none;
}

/* Account information, not a form to fill in.
   Phone and e-mail are status: read-only, verified, changed through their own
   flows. Presented as fields they read as inputs the user forgot to complete. */
#pills-editProfile .ii-acct-readonly {
  background: var(--ii-bg-sunken, var(--ii-neutral-25));
  border: 1px solid var(--ii-border);
  border-radius: var(--ii-radius-card);
  padding: var(--ii-space-5);
  max-width: 560px;
}
#pills-editProfile .ii-acct-readonly .caption-box:last-child { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   "Afficher plus" is a real button now

   Measured 128x34 at 375px. UI_AUDIT m4 recorded this: the theme's
   min-height:46px only applies to `.btn`, and this button carries
   `orange-border-btn` instead, so it never picked it up. It matters more than
   it did - paging is now the main way through a 179-listing category, and the
   label grew to "Afficher plus (32 sur 179)".
   -------------------------------------------------------------------------- */
#load_more_listing {
  min-height: 46px;
  padding-left: var(--ii-space-5);
  padding-right: var(--ii-space-5);
}

/* --------------------------------------------------------------------------
   Refining results: dimmed, not replaced (PLATFORM_AUDIT B1)

   A refinement within the same category keeps its results on screen while the
   next set is fetched. Replacing a grid the reader is actively scanning with
   grey boxes loses their place and tells them nothing they did not already
   know - DECISIONS constraint 25. Skeletons are kept for the case where the
   content really is about to be different: a category switch.

   pointer-events is dropped so a card cannot be tapped in the instant before
   it is replaced by a different listing at the same coordinates.
   -------------------------------------------------------------------------- */
.ii-grid-busy {
  opacity: .45;
  pointer-events: none;
  transition: opacity var(--ii-motion-state, 150ms) ease;
}

@media (prefers-reduced-motion: reduce) {
  .ii-grid-busy { transition: none; }
}

/* --------------------------------------------------------------------------
   Category picker: rows that open vs rows that choose

   A main category with subcategories opens them instead of selecting itself,
   so it has to look different from a row that is a final answer. The chevron
   is the whole signal - without it the two behave differently and look
   identical, which is the confusing version of this.
   -------------------------------------------------------------------------- */
.sf-cat-item.sf-cat-opens { position: relative; padding-right: 30px; }

.sf-cat-item.sf-cat-opens::after {
  content: "›";
  position: absolute;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 20px;
  line-height: 1;
  color: var(--ii-text-muted);
}

/* The way back out of a category. Muted, because it is navigation rather
   than one of the choices. */
.sf-cat-item.sf-cat-back {
  color: var(--ii-accent-text);
  font-weight: 600;
}

/* --------------------------------------------------------------------------
   Category attribute rows in the sell flow (FILTER_DESIGN Stage 3)

   Reuses .sf-chips / .sf-chip unchanged - these are the same gesture as the
   État row directly above them, and a second chip appearance would imply a
   difference that is not there.
   -------------------------------------------------------------------------- */
.sf-facet-row + .sf-facet-row { margin-top: var(--ii-space-4); }
#sfFacets[hidden] { display: none; }

/* Attribution for a value the assistant proposed, matching the per-field IA
   treatment DECISIONS §5.3 established: the seller must be able to tell a
   guess from their own answer. Removed the moment they tap. */
.sf-facet-row.sf-facet-ai .sf-label::after {
  content: "IA";
  display: inline-block;
  margin-left: var(--ii-space-2);
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--ii-accent-tint, #FFEDD5);
  color: var(--ii-accent-text, #C2410C);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .02em;
  vertical-align: middle;
}

/* --------------------------------------------------------------------------
   The missing-attribute tail

   Listings that match everything asked of them except that they never
   recorded the attribute being filtered on. Presented as a quiet, collapsed
   disclosure below the matches - visible enough to be found, subdued enough
   not to read as results.
   -------------------------------------------------------------------------- */
.ii-tail { margin: var(--ii-space-4) 0 0; padding: 0 15px; }
.ii-tail[hidden] { display: none; }

.ii-tail-toggle {
  display: flex;
  align-items: center;
  gap: var(--ii-space-2);
  width: 100%;
  min-height: 44px;
  padding: var(--ii-space-3);
  border: 1px dashed var(--ii-border-strong);
  border-radius: var(--ii-radius-control);
  background: transparent;
  color: var(--ii-text-muted);
  font-size: 14px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.ii-tail-toggle:hover { border-color: var(--ii-accent); color: var(--ii-text); }
.ii-tail-toggle:focus-visible {
  outline: 2px solid var(--ii-focus);
  outline-offset: 2px;
}

.ii-tail-caret { margin-left: auto; transition: transform var(--ii-motion-state, 150ms) ease; }
.ii-tail-toggle.is-open .ii-tail-caret { transform: rotate(180deg); }

#ii_tail_grid { margin-top: var(--ii-space-3); }
#ii_tail_grid[hidden] { display: none; }

.ii-tail-note {
  margin: var(--ii-space-2) 0 0;
  font-size: 12px;
  color: var(--ii-text-muted);
  font-variant-numeric: tabular-nums;
}
.ii-tail-note[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
  .ii-tail-caret { transition: none; }
}

/* --------------------------------------------------------------------------
   Hand-off from the legacy create-listing form to /vendre
   -------------------------------------------------------------------------- */
.ii-legacy-switch {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin: 0 0 20px;
  padding: 14px 16px;
  border: 1px solid var(--ii-accent-border);
  border-radius: 12px;
  background: var(--ii-accent-subtle);
}
.ii-legacy-switch strong { display: block; font-size: var(--ii-text-md); color: var(--ii-ink); }
.ii-legacy-switch span { font-size: 13px; color: var(--ii-muted); line-height: 1.4; }
.ii-legacy-switch-btn {
  margin-left: auto;
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 20px;
  border-radius: 10px;
  background: var(--ii-accent); color: var(--ii-text-on-accent);
  font-size: var(--ii-text-md); font-weight: 700;
  text-decoration: none; white-space: nowrap;
}
.ii-legacy-switch-btn:hover,
.ii-legacy-switch-btn:focus { background: var(--ii-accent-dark); color: var(--ii-text-on-accent); text-decoration: none; }

/* --------------------------------------------------------------------------
   "En vérification" badge on the seller's own listing card

   Rendered only to the owner — userProfileListing() shows everyone else
   status = 1 only. Without it a held listing looked live to the one person
   who needed to know it was not.
   -------------------------------------------------------------------------- */
.ii-card-review {
  position: absolute; top: 8px; left: 8px;
  display: inline-flex; align-items: center; gap: 4px;
  background: rgba(255, 250, 240, .96);
  border: 1px solid var(--ii-accent-border);
  color: var(--ii-warning);
  font-size: 11px; font-weight: 700;
  padding: 4px 8px; border-radius: 999px;
  z-index: 3;
  max-width: calc(100% - 16px);
  white-space: nowrap;
}
.ii-card-review svg { width: 12px; height: 12px; flex: 0 0 12px; fill: currentColor; }

/* ==========================================================================
   PHASE B — CONTROL PRIMITIVES

   The product was shipping six button treatments and five input treatments,
   several of them on the same page. These are the shared definitions. Rather
   than rename classes across every template at once, the existing hooks are
   grouped into one rule each, so they are guaranteed to be identical and can
   be collapsed to a single class later without another visual change.

   This file loads after the sell screen's inline <style> (headerscripts is
   yielded before this link), so .sf-* hooks can be aligned from here.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Button
   -------------------------------------------------------------------------- */
.ii-btn,
.ii-empty-btn,
.ii-sheet-apply,
.ii-phone-first-btn,
.ii-legacy-switch-btn,
.sf-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ii-space-2);
  min-height: 48px;
  padding: 0 18px;
  border: 1px solid transparent;
  border-radius: var(--ii-radius-control);
  font-family: var(--ii-font-sans);
  font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-semibold);
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  transition: background-color var(--ii-dur-fast) var(--ii-ease),
              border-color var(--ii-dur-fast) var(--ii-ease),
              color var(--ii-dur-fast) var(--ii-ease),
              transform var(--ii-dur-fast) var(--ii-ease);
}

/* One press state for every button on the site. */
.ii-btn:active,
.ii-empty-btn:active,
.ii-sheet-apply:active,
.ii-phone-first-btn:active,
.ii-legacy-switch-btn:active,
.sf-btn:active { transform: scale(.97); }

.ii-btn:focus-visible,
.ii-empty-btn:focus-visible,
.ii-sheet-apply:focus-visible,
.ii-phone-first-btn:focus-visible,
.ii-legacy-switch-btn:focus-visible,
.sf-btn:focus-visible {
  outline: none;
  box-shadow: var(--ii-focus-ring);
}

/* Disabled reads as "waiting", not "broken". The old treatment washed the
   fill out to a pale peach, which looked like a rendering fault; the label
   beside the button is what explains why it is not ready yet. */
.ii-btn:disabled,
.ii-empty-btn:disabled,
.ii-sheet-apply:disabled,
.sf-btn:disabled,
.sf-btn-primary:disabled {
  opacity: .45;
  cursor: not-allowed;
  transform: none;
  background-color: var(--ii-accent);
  color: var(--ii-text-on-accent);
}

/* Variants */
.ii-btn-primary,
.ii-empty-btn-primary,
.ii-sheet-apply,
.ii-phone-first-btn,
.ii-legacy-switch-btn,
.sf-btn-primary {
  background-color: var(--ii-accent);
  color: var(--ii-text-on-accent);
  border-color: transparent;
}
.ii-btn-primary:hover,
.ii-empty-btn-primary:hover,
.ii-sheet-apply:hover,
.ii-phone-first-btn:hover,
.ii-phone-first-btn:focus,
.ii-legacy-switch-btn:hover,
.ii-legacy-switch-btn:focus,
.sf-btn-primary:hover {
  background-color: var(--ii-accent-hover);
  color: var(--ii-text-on-accent);
  text-decoration: none;
}

.ii-btn-secondary,
.ii-empty-btn-ghost,
.sf-btn-ghost {
  background-color: var(--ii-bg-surface);
  color: var(--ii-text);
  border-color: var(--ii-border-strong);
}
.ii-btn-secondary:hover,
.ii-empty-btn-ghost:hover,
.sf-btn-ghost:hover {
  background-color: var(--ii-bg-sunken);
  color: var(--ii-text);
  border-color: var(--ii-border-strong);
  text-decoration: none;
}

.ii-btn-block,
.sf-btn-block { width: 100%; }

/* --------------------------------------------------------------------------
   Text input, select, textarea
   -------------------------------------------------------------------------- */
.ii-input,
.sf-input,
.sf-select,
.sf-textarea {
  width: 100%;
  min-height: 48px;
  padding: 12px 14px;
  border: 1px solid var(--ii-border-strong);
  border-radius: var(--ii-radius-control);
  background-color: var(--ii-bg-surface);
  color: var(--ii-text);
  font-family: var(--ii-font-sans);
  font-size: 16px;            /* 16px or iOS zooms the page on focus */
  line-height: var(--ii-leading-body);
  -webkit-appearance: none;
  appearance: none;
  transition: border-color var(--ii-dur-fast) var(--ii-ease),
              box-shadow var(--ii-dur-fast) var(--ii-ease);
}

.ii-input::placeholder,
.sf-input::placeholder,
.sf-textarea::placeholder { color: var(--ii-text-disabled); }

/* A visible ring, not just a tinted border. The previous treatment removed
   the outline and tinted a 1.5px border, which is the weakest indicator that
   still technically counts. */
.ii-input:focus,
.sf-input:focus,
.sf-select:focus,
.sf-textarea:focus {
  outline: none;
  border-color: var(--ii-focus);
  box-shadow: var(--ii-focus-ring);
}

/* appearance:none strips the native chevron, and nothing was drawn back, so
   selects looked like plain text fields. */
.ii-select,
.sf-select {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235A5A5A' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 18px 18px;
  padding-right: 40px;
}

.sf-textarea { min-height: 96px; resize: vertical; }

/* --------------------------------------------------------------------------
   Focus visibility for the remaining interactive elements in this layer.
   Several had no focus indicator at all.
   -------------------------------------------------------------------------- */
.ii-contact-bar .ii-wa:focus-visible,
.ii-contact-bar .ii-icon:focus-visible,
.ii-report-link:focus-visible,
.ii-sheet-x:focus-visible,
.sf-chip:focus-visible,
.sf-link:focus-visible {
  outline: none;
  box-shadow: var(--ii-focus-ring);
  border-radius: var(--ii-radius-control);
}

/* Press states for the remaining tappable controls. */
.ii-contact-bar .ii-wa:active,
.ii-contact-bar .ii-icon:active,
.sf-chip:active,
.ii-rail-item:active { transform: scale(.97); }

.ii-contact-bar .ii-wa,
.ii-contact-bar .ii-icon,
.sf-chip,
.ii-rail-item {
  transition: transform var(--ii-dur-fast) var(--ii-ease),
              background-color var(--ii-dur-fast) var(--ii-ease),
              border-color var(--ii-dur-fast) var(--ii-ease),
              color var(--ii-dur-fast) var(--ii-ease);
}

/* --------------------------------------------------------------------------
   Motion respects the system setting across everything added above, not just
   the two places it previously covered.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .ii-card,
  .ii-card-fav,
  .ii-card-fav::before,
  .ii-card-title,
  .ii-btn, .ii-empty-btn, .ii-sheet-apply, .ii-phone-first-btn,
  .ii-legacy-switch-btn, .sf-btn,
  .ii-input, .sf-input, .sf-select, .sf-textarea,
  .ii-contact-bar .ii-wa, .ii-contact-bar .ii-icon, .sf-chip, .ii-rail-item {
    transition: none !important;
  }
  .ii-card:hover,
  .ii-card:active,
  .ii-btn:active, .ii-empty-btn:active, .ii-sheet-apply:active,
  .ii-phone-first-btn:active, .ii-legacy-switch-btn:active, .sf-btn:active,
  .ii-card-fav:active, .sf-chip:active, .ii-rail-item:active {
    transform: none;
  }
}

/* ==========================================================================
   PHASE C - SELL FLOW

   Moved out of an inline <style> in sell/start.blade.php, which carried 95
   lines of hardcoded hex, a second shimmer keyframe and a second button
   system. The screen now consumes the same tokens and primitives as the rest
   of the product, and the duplicates are gone.

   Presented as a guided task rather than a form: a rail states where you are,
   each step is an elevated surface, and the assistant has a surface of its
   own instead of borrowing the success-message style.
   ========================================================================== */

.sf-wrap {
  max-width: 640px;
  margin: 0 auto;
  padding: var(--ii-space-4) var(--ii-space-4) 148px;
}

.sf-h1 {
  font-size: var(--ii-text-2xl);
  font-weight: var(--ii-weight-bold);
  letter-spacing: var(--ii-tracking-display);
  line-height: var(--ii-leading-display);
  margin: var(--ii-space-2) 0 var(--ii-space-1);
  color: var(--ii-text);
}
.sf-sub {
  font-size: var(--ii-text-md);
  color: var(--ii-text-secondary);
  margin: 0 0 var(--ii-space-5);
}

/* --------------------------------------------------------------------------
   Progress rail
   The flow always had two steps and a publish action; nothing said so.
   -------------------------------------------------------------------------- */
.sf-rail {
  display: flex;
  align-items: center;
  gap: var(--ii-space-2);
  margin: 0 0 var(--ii-space-5);
}
.sf-rail-step {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: var(--ii-text-xs);
  font-weight: var(--ii-weight-semibold);
  color: var(--ii-text-disabled);
  white-space: nowrap;
}
.sf-rail-dot {
  width: 22px; height: 22px;
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ii-bg-sunken);
  color: var(--ii-text-disabled);
  font-size: 11px;
  font-weight: var(--ii-weight-bold);
  flex: none;
  border: 1px solid var(--ii-border);
  transition: background-color var(--ii-dur-fast) var(--ii-ease),
              color var(--ii-dur-fast) var(--ii-ease),
              border-color var(--ii-dur-fast) var(--ii-ease);
}
.sf-rail-step.is-on { color: var(--ii-text); }
.sf-rail-step.is-on .sf-rail-dot {
  background: var(--ii-accent);
  border-color: var(--ii-accent);
  color: var(--ii-text-on-accent);
}
.sf-rail-step.is-done { color: var(--ii-text-secondary); }
.sf-rail-step.is-done .sf-rail-dot {
  background: var(--ii-success-subtle);
  border-color: var(--ii-success);
  color: var(--ii-success);
}
.sf-rail-line {
  flex: 1 1 auto;
  height: 2px;
  border-radius: 2px;
  background: var(--ii-border);
}

/* --------------------------------------------------------------------------
   Steps
   -------------------------------------------------------------------------- */
.sf-step {
  background: var(--ii-bg-surface);
  border: 0;
  border-radius: var(--ii-radius-card);
  box-shadow: var(--ii-shadow-sm);
  padding: var(--ii-space-5) var(--ii-space-4);
  margin-bottom: var(--ii-space-3);
}
.sf-step[hidden] { display: none; }
.sf-step-title {
  font-size: var(--ii-text-lg);
  font-weight: var(--ii-weight-semibold);
  letter-spacing: -0.01em;
  margin: 0 0 var(--ii-space-4);
  color: var(--ii-text);
}

/* --------------------------------------------------------------------------
   Photos
   -------------------------------------------------------------------------- */
.sf-drop {
  border: 1.5px dashed var(--ii-border-strong);
  border-radius: var(--ii-radius-card);
  padding: var(--ii-space-5) var(--ii-space-4);
  text-align: center;
  background: var(--ii-bg-sunken);
  transition: border-color var(--ii-dur-fast) var(--ii-ease),
              background-color var(--ii-dur-fast) var(--ii-ease);
}
/* The zone looked droppable and was not; it now is, and says so while a file
   is held over it. */
.sf-drop.is-dragging {
  border-color: var(--ii-accent);
  background: var(--ii-accent-subtle);
}
.sf-photo-actions {
  display: flex; gap: var(--ii-space-2);
  justify-content: center; flex-wrap: wrap;
}

.sf-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
  gap: var(--ii-space-2);
  margin-top: var(--ii-space-4);
}
.sf-thumb {
  position: relative;
  aspect-ratio: 1;
  border-radius: var(--ii-radius-control);
  overflow: hidden;
  background: var(--ii-bg-sunken);
  box-shadow: var(--ii-shadow-sm);
}
.sf-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The main photo is stated rather than implied by position. */
.sf-thumb:first-child::after {
  content: "Principale";
  position: absolute;
  left: 6px; bottom: 6px;
  padding: 3px 7px;
  border-radius: var(--ii-radius-pill);
  background: var(--ii-glass);
  -webkit-backdrop-filter: blur(8px) saturate(1.4);
  backdrop-filter: blur(8px) saturate(1.4);
  color: var(--ii-text);
  font-size: 10px;
  font-weight: var(--ii-weight-semibold);
  box-shadow: var(--ii-shadow-sm);
}
.sf-thumb.sf-skel::after { content: none; }

.sf-thumb button {
  position: absolute; top: 5px; right: 5px;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: rgba(16, 16, 16, .62);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: #fff;
  border: 0;
  font-size: 15px; line-height: 1;
  cursor: pointer;
  transition: transform var(--ii-dur-fast) var(--ii-ease),
              background-color var(--ii-dur-fast) var(--ii-ease);
}
.sf-thumb button:hover { background: rgba(16, 16, 16, .82); }
.sf-thumb button:active { transform: scale(.9); }
.sf-thumb button:focus-visible { outline: none; box-shadow: var(--ii-focus-ring); }

/* One shimmer for the whole product - this screen had its own copy. */
.sf-thumb.sf-skel {
  background: linear-gradient(90deg, var(--ii-bg-sunken) 25%, var(--ii-bg-canvas) 37%, var(--ii-bg-sunken) 63%);
  background-size: 400% 100%;
  animation: ii-shim 1.2s ease infinite;
}

/* --------------------------------------------------------------------------
   Fields
   -------------------------------------------------------------------------- */
.sf-field { margin-bottom: var(--ii-space-4); }
.sf-label {
  display: block;
  font-size: var(--ii-text-sm);
  font-weight: var(--ii-weight-semibold);
  color: var(--ii-text-secondary);
  margin-bottom: 6px;
}
.sf-optional { font-weight: var(--ii-weight-regular); color: var(--ii-text-disabled); }

.sf-price-wrap { position: relative; }
.sf-price-wrap .sf-cur {
  position: absolute; right: 14px; top: 50%;
  transform: translateY(-50%);
  color: var(--ii-text-muted);
  font-size: var(--ii-text-md);
  pointer-events: none;
}
.sf-price-wrap .sf-input { padding-right: 48px; }

/* Comparable-price range. This previously only ever appeared inside the
   assistant's own message, so a seller who chose a category by hand never
   saw it at all. */
.sf-hint-inline {
  display: flex; align-items: center; gap: 6px;
  margin: var(--ii-space-2) 0 0;
  font-size: var(--ii-text-xs);
  color: var(--ii-text-secondary);
}
.sf-hint-inline[hidden] { display: none; }
.sf-hint-inline svg { width: 14px; height: 14px; fill: var(--ii-text-muted); flex: none; }

/* --------------------------------------------------------------------------
   Category type-ahead
   -------------------------------------------------------------------------- */
.sf-cat { position: relative; }
.sf-cat-results {
  position: absolute; z-index: 40;
  left: 0; right: 0; top: calc(100% + 6px);
  background: var(--ii-bg-surface);
  border: 1px solid var(--ii-border);
  border-radius: var(--ii-radius-control);
  max-height: 280px; overflow-y: auto;
  box-shadow: var(--ii-shadow-lg);
  padding: var(--ii-space-1);
}
.sf-cat-results[hidden] { display: none; }
.sf-cat-item {
  padding: 11px 12px;
  cursor: pointer;
  font-size: var(--ii-text-md);
  border-radius: 8px;
  color: var(--ii-text);
}
.sf-cat-item:hover,
.sf-cat-item.sf-active { background: var(--ii-accent-subtle); }
.sf-cat-item small {
  display: block;
  color: var(--ii-text-muted);
  font-size: var(--ii-text-xs);
  margin-top: 2px;
}
.sf-chosen {
  display: flex; align-items: center; gap: var(--ii-space-2);
  background: var(--ii-accent-subtle);
  border: 1px solid var(--ii-accent-border);
  border-radius: var(--ii-radius-control);
  padding: 13px 14px;
  font-size: var(--ii-text-md);
  color: var(--ii-text);
}
.sf-chosen button {
  margin-left: auto;
  background: 0; border: 0;
  /* accent-text, not accent-hover: this sits on the subtle tint, where the
     hover step measures 3.35 and misses AA. The dark step makes 4.88. */
  color: var(--ii-accent-text);
  font-weight: var(--ii-weight-semibold);
  cursor: pointer;
  font-size: var(--ii-text-sm);
  padding: 6px 4px;
}
.sf-chosen button:focus-visible { outline: none; box-shadow: var(--ii-focus-ring); border-radius: 6px; }

/* --------------------------------------------------------------------------
   Chips
   -------------------------------------------------------------------------- */
.sf-chips { display: flex; gap: var(--ii-space-2); flex-wrap: wrap; }
.sf-chip {
  padding: 10px 16px;
  border-radius: var(--ii-radius-pill);
  border: 1px solid var(--ii-border-strong);
  background: var(--ii-bg-surface);
  color: var(--ii-text);
  font-size: var(--ii-text-sm);
  cursor: pointer;
  min-height: 44px;
  display: inline-flex; align-items: center;
}
/* Label uses the hover shade: the accent on its own tint measures 4.34, just
   under AA, and a chip label is not "large text". */
.sf-chip.sf-on {
  border-color: var(--ii-accent);
  background: var(--ii-accent-subtle);
  color: var(--ii-accent-hover);
  font-weight: var(--ii-weight-semibold);
}

/* --------------------------------------------------------------------------
   Sticky publish bar
   -------------------------------------------------------------------------- */
.sf-sticky {
  position: fixed; left: 0; right: 0; bottom: 0;
  background: var(--ii-glass);
  -webkit-backdrop-filter: blur(16px) saturate(1.6);
  backdrop-filter: blur(16px) saturate(1.6);
  border-top: 1px solid var(--ii-glass-border);
  padding: var(--ii-space-3) var(--ii-space-4) calc(var(--ii-space-3) + env(safe-area-inset-bottom));
  z-index: 900;
}
.sf-sticky-inner { max-width: 640px; margin: 0 auto; }
.sf-hint {
  font-size: var(--ii-text-xs);
  color: var(--ii-text-muted);
  text-align: center;
  margin: var(--ii-space-2) 0 0;
}

/* --------------------------------------------------------------------------
   OTP sheet - same anatomy as the filter sheet
   -------------------------------------------------------------------------- */
.sf-sheet {
  position: fixed; inset: 0;
  background: rgba(16, 16, 16, .48);
  z-index: 1000;
  display: flex; align-items: flex-end;
}
.sf-sheet[hidden] { display: none; }
.sf-sheet-card {
  position: relative;                 /* anchors the close control */
  background: var(--ii-bg-surface);
  width: 100%;
  border-radius: var(--ii-radius-sheet) var(--ii-radius-sheet) 0 0;
  padding: var(--ii-space-4) var(--ii-space-5) calc(var(--ii-space-5) + env(safe-area-inset-bottom));
  max-width: 640px; margin: 0 auto;
  box-shadow: var(--ii-shadow-lg);
  animation: sf-rise var(--ii-dur-sheet) var(--ii-ease);
}

/* A visible way out. The sheet used to be dismissible only by tapping the
   backdrop - nothing said so, and a keyboard user had no exit at all.
   44x44 like every other target on this screen (dd20b4e). */
.sf-sheet-x {
  position: absolute;
  top: var(--ii-space-2); right: var(--ii-space-3);
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  border: 0; background: none; cursor: pointer;
  font-size: 26px; line-height: 1;
  color: var(--ii-text-muted);
  border-radius: var(--ii-radius-pill);
}
.sf-sheet-x:hover { color: var(--ii-text); background: var(--ii-bg-sunken); }
.sf-sheet-x:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: -2px; }
@keyframes sf-rise { from { transform: translateY(24px); opacity: 0; } to { transform: none; opacity: 1; } }
@media (min-width: 641px) {
  .sf-sheet { align-items: center; }
  .sf-sheet-card { border-radius: var(--ii-radius-sheet); }
}
/* The grip the filter sheet has and this one did not. */
.sf-sheet-grip {
  width: 38px; height: 4px;
  margin: 0 auto var(--ii-space-4);
  border-radius: var(--ii-radius-pill);
  background: var(--ii-border-strong);
}
.sf-sheet h3 {
  font-size: var(--ii-text-xl);
  font-weight: var(--ii-weight-bold);
  letter-spacing: -0.01em;
  margin: 0 0 6px;
  color: var(--ii-text);
}
.sf-sheet p {
  font-size: var(--ii-text-md);
  color: var(--ii-text-secondary);
  margin: 0 0 var(--ii-space-4);
}
.sf-phone-row { display: flex; gap: var(--ii-space-2); }
.sf-phone-row .sf-select { width: 112px; flex: none; }
.sf-otp {
  letter-spacing: .42em;
  text-align: center;
  font-size: var(--ii-text-2xl);
  font-weight: var(--ii-weight-bold);
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   Held-for-review outcome
   -------------------------------------------------------------------------- */
.sf-pending {
  text-align: center;
  padding: var(--ii-space-6) var(--ii-space-4) var(--ii-space-5);
  border: 0;
  border-radius: var(--ii-radius-card);
  background: var(--ii-bg-surface);
  box-shadow: var(--ii-shadow-sm);
  margin-bottom: var(--ii-space-4);
}
.sf-pending-icon {
  width: 56px; height: 56px;
  margin: 0 auto var(--ii-space-4);
  border-radius: 50%;
  background: var(--ii-warning-subtle);
  display: flex; align-items: center; justify-content: center;
}
.sf-pending-icon svg { width: 28px; height: 28px; fill: var(--ii-warning); }
.sf-pending h2 {
  font-size: var(--ii-text-xl);
  font-weight: var(--ii-weight-bold);
  margin: 0 0 var(--ii-space-2);
  color: var(--ii-text);
}
.sf-pending p {
  font-size: var(--ii-text-md);
  line-height: var(--ii-leading-body);
  color: var(--ii-text-secondary);
  margin: 0 0 var(--ii-space-4);
}
.sf-pending-again { display: inline-block; margin-top: var(--ii-space-3); }

/* --------------------------------------------------------------------------
   Messages
   -------------------------------------------------------------------------- */
.sf-msg {
  font-size: var(--ii-text-sm);
  padding: 11px 13px;
  border-radius: var(--ii-radius-control);
  margin-bottom: var(--ii-space-3);
}
.sf-msg-err { background: var(--ii-danger-subtle);  color: var(--ii-danger); }
.sf-msg-ok  { background: var(--ii-success-subtle); color: var(--ii-success); }

.sf-link {
  background: 0; border: 0;
  color: var(--ii-accent-text);
  font-size: var(--ii-text-sm);
  font-weight: var(--ii-weight-semibold);
  cursor: pointer;
  padding: 10px;
}
.sf-dev {
  background: var(--ii-warning-subtle);
  border: 1px dashed var(--ii-accent-border);
  color: var(--ii-warning);
  font-size: var(--ii-text-xs);
  padding: 9px 11px;
  border-radius: var(--ii-radius-control);
  margin-bottom: var(--ii-space-3);
}

/* ==========================================================================
   PHASE C - THE ASSISTANT

   Photo-to-Ad is the one thing here that no competitor has, and it rendered
   as a static green success box for the eight seconds it takes. It gets its
   own surface, a staged status while it works, and per-field attribution
   afterwards so the seller can tell which words are theirs and which are a
   guess.
   ========================================================================== */

.ii-ai {
  position: relative;
  border-radius: var(--ii-radius-card);
  background: var(--ii-bg-surface);
  border: 1px solid var(--ii-border);
  padding: var(--ii-space-3) var(--ii-space-4);
  margin-bottom: var(--ii-space-4);
  overflow: hidden;
}
.ii-ai[hidden] { display: none; }

/* While thinking, a light sweeps the top edge. One 2px line, not a glowing
   card - the seller is filling in a form, not watching a demo. */
.ii-ai.is-thinking::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: linear-gradient(90deg,
    transparent 0%,
    var(--ii-brand) 35%,
    var(--ii-orange-400) 50%,
    var(--ii-brand) 65%,
    transparent 100%);
  background-size: 220% 100%;
  animation: ii-ai-sweep 1.4s linear infinite;
}
@keyframes ii-ai-sweep {
  0%   { background-position: 120% 0; }
  100% { background-position: -120% 0; }
}

.ii-ai-row { display: flex; align-items: flex-start; gap: 10px; }

.ii-ai-mark {
  width: 26px; height: 26px;
  flex: none;
  border-radius: 8px;
  display: flex; align-items: center; justify-content: center;
  background: var(--ii-accent-subtle);
  color: var(--ii-accent-hover);
}
.ii-ai-mark svg { width: 15px; height: 15px; fill: currentColor; }
.ii-ai.is-thinking .ii-ai-mark svg { animation: ii-ai-pulse 1.4s ease-in-out infinite; }
@keyframes ii-ai-pulse { 0%, 100% { opacity: .45; } 50% { opacity: 1; } }

.ii-ai-body { flex: 1; min-width: 0; }
.ii-ai-title {
  font-size: var(--ii-text-sm);
  font-weight: var(--ii-weight-semibold);
  color: var(--ii-text);
  margin: 2px 0 0;
}
.ii-ai-status {
  font-size: var(--ii-text-xs);
  color: var(--ii-text-secondary);
  margin: 3px 0 0;
  min-height: 16px;
}

/* Per-field attribution. The assistant used to fill fields silently, so the
   seller had no way to tell a guess from their own typing. */
.ii-ai-chip {
  display: inline-flex; align-items: center; gap: 4px;
  margin-left: 6px;
  padding: 2px 7px;
  border-radius: var(--ii-radius-pill);
  background: var(--ii-accent-subtle);
  color: var(--ii-accent-hover);
  font-size: 10px;
  font-weight: var(--ii-weight-bold);
  letter-spacing: .03em;
  vertical-align: 1px;
}

/* A field the assistant just wrote into, so the change is visible rather than
   silent. Fades out on its own; the value stays editable throughout. */
@keyframes ii-ai-fill {
  0%   { background-color: var(--ii-accent-subtle); border-color: var(--ii-accent); }
  100% { background-color: var(--ii-bg-surface);    border-color: var(--ii-border-strong); }
}
.ii-ai-filled { animation: ii-ai-fill 1.6s var(--ii-ease); }

@media (prefers-reduced-motion: reduce) {
  .ii-ai.is-thinking::before,
  .ii-ai.is-thinking .ii-ai-mark svg,
  .ii-ai-filled,
  .sf-sheet-card,
  .sf-thumb.sf-skel { animation: none; }
}

/* ==========================================================================
   PHASE C2 - AD DETAIL

   The page structure is legacy Bootstrap rows and is left alone; what changes
   is the surface treatment, the rhythm between blocks and the hierarchy
   inside them. Restructuring 1,575 lines of nested col-md markup would risk
   far more than it would buy.
   ========================================================================== */

/* The description and specification block. It was a square white box with a
   1px border and 30px of padding on a white page - the same white-on-white
   problem the cards had. */
/* Two containers, near-identical names: `_desc` holds the description, plain
   `additional_details` holds the specification rows. */
.additional_details_desc,
.additional_details {
  background: var(--ii-bg-surface) !important;
  border: 0 !important;
  border-radius: var(--ii-radius-card);
  box-shadow: var(--ii-shadow-sm);
  padding: var(--ii-space-5) var(--ii-space-4) !important;
  margin-bottom: var(--ii-space-4);
}
.additional_details_desc h4,
.additional_details h4 {
  font-size: var(--ii-text-lg);
  font-weight: var(--ii-weight-semibold);
  letter-spacing: -0.01em;
  color: var(--ii-text);
  margin-bottom: var(--ii-space-4);
}
.additional_details_desc h4 b,
.additional_details h4 b { font-weight: inherit; color: inherit !important; }

/* Specification rows. The label column was 16px pure black at the same weight
   as its value, so the two competed; the label is what you scan past. */
.detailsName {
  min-width: 140px;
  font-size: var(--ii-text-sm);
  font-weight: var(--ii-weight-medium);
  color: var(--ii-text-muted);
}
.additional_details .col-md-6.d-flex,
.additional_details_desc .col-md-6.d-flex {
  padding-top: 9px;
  padding-bottom: 9px;
  border-bottom: 1px solid var(--ii-border);
  align-items: baseline;
}
.additional_details .col-md-6.d-flex b,
.additional_details_desc .col-md-6.d-flex b {
  font-weight: var(--ii-weight-semibold);
  color: var(--ii-text);
}
.additional_details_desc .description-content {
  font-size: var(--ii-text-md);
  line-height: 1.6;
  color: var(--ii-text-secondary);
}

/* Gallery. Square corners on a page where everything else is rounded. */
a.listings-images-gallery,
a.listings-images-gallery img {
  border-radius: var(--ii-radius-card);
  overflow: hidden;
}
a.listings-images-gallery img { display: block; }

/* Image count, as a pill over the gallery rather than a line of text under
   it. */
.total-img-caption {
  position: absolute;
  right: 12px; bottom: 12px;
  z-index: 3;
  padding: 5px 11px;
  border-radius: var(--ii-radius-pill);
  /* style.css fills this class with a cream, so the override is explicit. */
  background: var(--ii-glass) !important;
  -webkit-backdrop-filter: blur(10px) saturate(1.4);
  backdrop-filter: blur(10px) saturate(1.4);
  box-shadow: var(--ii-shadow-sm);
}
.total-img-caption p {
  font-size: var(--ii-text-xs);
  font-weight: var(--ii-weight-semibold);
  color: var(--ii-text);
  font-variant-numeric: tabular-nums;
}
.total-img-text { position: relative; }

/* Title and price hierarchy. The title rendered in a legacy grey that is not
   in the palette. */
.single_property_title h1 {
  font-size: var(--ii-text-2xl);
  font-weight: var(--ii-weight-bold);
  line-height: var(--ii-leading-display);
  letter-spacing: var(--ii-tracking-display);
  color: var(--ii-text) !important;
}
.single_property_title p { color: var(--ii-text-muted); font-size: var(--ii-text-md); }

.custom-sticky-section .lsd_list h4,
.custom-sticky-section-2 .lsd_list h4 {
  font-size: var(--ii-text-2xl);
  font-weight: var(--ii-weight-bold);
  letter-spacing: -0.01em;
  color: var(--ii-text);
  font-variant-numeric: tabular-nums;
  margin-bottom: 0;
}
.custom-sticky-section .lsd_list h4 small,
.custom-sticky-section-2 .lsd_list h4 small {
  font-size: var(--ii-text-sm);
  font-weight: var(--ii-weight-semibold);
  color: var(--ii-text-secondary);
}

/* --------------------------------------------------------------------------
   The ad page's opening block, and the rhythm it did not have

   Measured at 1670px before this: the title ended at 484, the price row began
   at 489 and the action row began at 522 - where the price row ended. Title,
   price, and the buttons that act on the listing were touching, with the only
   air being 5px of incidental container padding.

   Four different things are being said there - what this is, what it costs,
   what you can do about it, and its specification - and nothing separated
   them. Grouping by proximity is what tells a reader the heart and the flag
   act on the LISTING rather than on the price; with no gap they read as one
   undifferentiated slab.

   `custom-sticky-section` is not sticky and never was - no rule anywhere gives
   it a position - so a margin here moves nothing but itself.
   -------------------------------------------------------------------------- */
.custom-sticky-section {
  margin-top: var(--ii-space-5);
}

.single_property_social_share .spss {
  margin-top: var(--ii-space-4);
}

/* The step OUT of that block has to be bigger than the steps inside it, or the
   specification card reads as a fourth item in the same list rather than as the
   next section. It was 15 against an internal 16, which is the wrong way round
   by a pixel - close enough to equal that the grouping did no work at all. */
.additional_details {
  margin-top: var(--ii-space-5) !important;
}

/* The signed-in username in the desktop header.

   16/300: a size off the scale and a weight no face here has, so the browser
   was synthesising Inter Light for it. §1.25 swept weight 300 out of the
   header and this one instance survived, because it renders from a different
   block than the navigation did. 15/500 puts it back on the scale beside the
   rest of the header. Constraint 87. */
header .dn-1199 {
  font-size: var(--ii-text-md) !important;
  font-weight: var(--ii-weight-medium) !important;
}
/* A superseded price is a footnote to the current one. */
.tz-discount-price h4 {
  font-size: var(--ii-text-md) !important;
  font-weight: var(--ii-weight-medium) !important;
  color: var(--ii-text-disabled) !important;
  text-decoration: line-through;
}

/* Seller block, as an actual card. */
.media-head-left {
  background: var(--ii-bg-surface);
  border-radius: var(--ii-radius-card);
  box-shadow: var(--ii-shadow-sm);
  padding: var(--ii-space-4);
}

/* Safety note. Advice, not an alert - a quiet informational panel rather than
   loose text in the sidebar. */
.notice-box {
  background: var(--ii-bg-sunken);
  border: 1px solid var(--ii-border);
  border-radius: var(--ii-radius-card);
  padding: var(--ii-space-4);
}
.notice-box h5 {
  font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-semibold);
  color: var(--ii-text);
}
.notice-box p { font-size: var(--ii-text-sm); color: var(--ii-text-secondary); }
.notice-box a.text-primary { color: var(--ii-accent-text) !important; font-weight: var(--ii-weight-semibold); }

/* --------------------------------------------------------------------------
   The in-page WhatsApp button - THE definition

   This was written three times: here, and in `<style>` blocks inside
   `accountSidepanel.blade.php` and `listing_detail.blade.php`. A view's style
   block loads after this sheet and wins ties on order, so the two copies were
   what actually shipped - and both set `font-size: 16px; font-weight: bold`,
   neither of which is on the 7-step scale (constraint 87).

   The cost was not cosmetic. Measured on a seller profile at 375px, before:
   the label "Contacter sur WhatsApp" needed 137px of a 184px button, wrapped
   to two lines, and pushed the control to 62px against its own 48px
   min-height - 36% of the whole seller card, in saturated green, on the card
   whose job is to say who the seller is. On the ad page it rendered 375x64,
   the full width of the viewport.

   Deleting the overrides was the fix; these values were already correct.
   Constraint 84 - one definition, not one per page.

   Ink on the green, never white: white on #25D366 is 1.98:1 and this control
   carries the seller's phone number, the one string a buyer must read exactly.
   Reaching 4.5:1 with white needs #12833F, far enough off-brand to stop
   reading as WhatsApp. Ink is 9.52:1. Constraint 71.
   -------------------------------------------------------------------------- */
.whatsapp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ii-space-2);
  min-height: 48px;
  border-radius: var(--ii-radius-control) !important;
  background-color: var(--ii-whatsapp) !important;
  color: var(--ii-text-on-accent) !important;
  font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-semibold);
  text-decoration: none;
  box-shadow: none !important;
  transition: transform var(--ii-dur-fast) var(--ii-ease),
              background-color var(--ii-dur-fast) var(--ii-ease);
}
.whatsapp-btn:hover,
.whatsapp-btn:focus {
  background-color: var(--ii-whatsapp-hover) !important;
  color: var(--ii-text-on-accent) !important;
  text-decoration: none;
}
.whatsapp-btn:active { transform: scale(.97); }

/* The icon carries the channel, which is what lets the label be short. */
.whatsapp-icon {
  font-size: var(--ii-text-lg);
  color: var(--ii-text-on-accent);
  margin-right: 0 !important;
}

.whatsapp-text {
  color: var(--ii-text-on-accent) !important;
  font-size: var(--ii-text-md) !important;
  font-weight: var(--ii-weight-semibold) !important;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Contact bar - the second surface that earns glass
   -------------------------------------------------------------------------- */
@media (max-width: 991px) {
  .ii-contact-bar {
    background: var(--ii-glass);
    -webkit-backdrop-filter: blur(16px) saturate(1.6);
    backdrop-filter: blur(16px) saturate(1.6);
    border-top: 1px solid var(--ii-glass-border);
    box-shadow: none;
  }
  .ii-contact-bar .ii-wa {
    border-radius: var(--ii-radius-control);
    font-size: var(--ii-text-md);
    font-weight: var(--ii-weight-semibold);
  }
  .ii-contact-bar .ii-icon {
    border-radius: var(--ii-radius-control);
    border-color: var(--ii-border-strong);
    background: var(--ii-bg-surface);
  }
}

/* --------------------------------------------------------------------------
   Bottom navigation - the first
   -------------------------------------------------------------------------- */
@media (max-width: 991px) {
  .bnav {
    background: var(--ii-glass);
    -webkit-backdrop-filter: blur(16px) saturate(1.6);
    backdrop-filter: blur(16px) saturate(1.6);
    border-top: 1px solid var(--ii-glass-border);
    box-shadow: none;
  }
  .bnav-item svg { transition: transform var(--ii-dur-fast) var(--ii-ease); }
  .bnav-item.is-on svg { transform: scale(1.08); }
  .bnav-sell-btn { transition: transform var(--ii-dur-fast) var(--ii-ease); }
  .bnav-sell:active .bnav-sell-btn { transform: scale(.92); }
}

/* ==========================================================================
   PHASE C2 - SEARCH

   The box was a grey Bootstrap field with a square-cornered icon bolted to
   its left edge. It is the buyer's primary entry point and should look like
   the most confident element on the page.
   ========================================================================== */

.search-bar .input-group,
.input-group:has(> #autocomplete-search) {
  background: var(--ii-bg-sunken);
  border: 1px solid var(--ii-border);
  border-radius: var(--ii-radius-pill);
  overflow: hidden;
  transition: background-color var(--ii-dur-fast) var(--ii-ease),
              border-color var(--ii-dur-fast) var(--ii-ease),
              box-shadow var(--ii-dur-fast) var(--ii-ease);
}
.search-bar .input-group:focus-within,
.input-group:has(> #autocomplete-search):focus-within {
  background: var(--ii-bg-surface);
  border-color: var(--ii-focus);
  box-shadow: var(--ii-focus-ring);
}

/* The fill is set inline in four templates, so it has to be overridden here
   rather than in the markup. */
.input-group-text:has(~ #autocomplete-search),
.search-bar .input-group-text {
  background: transparent !important;
  border: 0 !important;
  padding: 0 6px 0 18px;
  color: var(--ii-text-muted);
}
.search-bar .input-group-text .fa { font-size: 15px; }

#autocomplete-search {
  background: transparent !important;
  border: 0 !important;
  box-shadow: none !important;
  height: 52px;
  padding: 0 18px 0 6px;
  font-size: 16px;
  color: var(--ii-text);
}
#autocomplete-search::placeholder { color: var(--ii-text-muted); }

/* jQuery UI sets this class on the input while a request is in flight. There
   was no loading feedback at all before. */
#autocomplete-search.ui-autocomplete-loading {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236B6B6B' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M12 3a9 9 0 1 0 9 9'/%3E%3C/svg%3E") !important;
  background-repeat: no-repeat !important;
  background-position: right 16px center !important;
  background-size: 18px 18px !important;
  animation: ii-spin .7s linear infinite;
}
@keyframes ii-spin { to { transform: rotate(360deg); } }
/* Spinning the field itself would rotate the text, so the icon is animated by
   swapping position rather than transforming the input. */
#autocomplete-search.ui-autocomplete-loading { animation: none; }

/* --------------------------------------------------------------------------
   Suggestion menu
   -------------------------------------------------------------------------- */
.ui-autocomplete.ui-menu {
  border: 1px solid var(--ii-border) !important;
  border-radius: var(--ii-radius-card) !important;
  box-shadow: var(--ii-shadow-lg) !important;
  padding: var(--ii-space-1) !important;
  background: var(--ii-bg-surface) !important;
}
.ui-autocomplete .ui-menu-item-wrapper {
  padding: 10px 12px !important;
  border-radius: 8px;
  font-size: var(--ii-text-md);
  color: var(--ii-text);
}
.ui-autocomplete .ui-state-active,
.ui-autocomplete .ui-menu-item-wrapper.ui-state-active {
  background: var(--ii-accent-subtle) !important;
  color: var(--ii-text) !important;
  border: 0 !important;
  margin: 0 !important;
}
.ii-sugg-kind {
  display: block;
  font-size: var(--ii-text-xs);
  color: var(--ii-text-muted);
  margin-top: 2px;
}
.ii-sugg-recent { color: var(--ii-text-muted); }

/* Group headings, so a list of ten mixed rows reads as sections. */
.ii-sugg-group {
  padding: 10px 12px 4px;
  font-size: 11px;
  font-weight: var(--ii-weight-bold);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ii-text-disabled);
  list-style: none;
  pointer-events: none;
}
.ii-sugg-group:first-child { padding-top: 4px; }

/* The raw-phrase and user-search rows are actions, not results. */
.ui-autocomplete .ii-sugg-action .ui-menu-item-wrapper,
.ui-autocomplete li.ii-sugg-action > div {
  color: var(--ii-text-secondary);
  font-size: var(--ii-text-sm);
}

/* Placeholder rows while a suggestion request is in flight.

   The dropdown used to stay completely silent between keystroke and answer,
   which on a slow connection reads as "search is broken" rather than "search
   is working". These rows are shaped like the real ones, so the menu does not
   jump when they are replaced.

   pointer-events and the items selector below keep them out of both mouse and
   keyboard reach - they are not results and must not be selectable. */
.ii-sugg-skel {
  list-style: none;
  padding: 11px 12px;
  pointer-events: none;
}
.ii-sugg-skel .ii-skel-row {
  display: block;
  height: 12px;
  border-radius: 4px;
  background-image: linear-gradient(90deg,
    var(--ii-bg-sunken) 25%, var(--ii-bg-canvas) 37%, var(--ii-bg-sunken) 63%);
  background-size: 400% 100%;
  animation: ii-shim 1.2s ease infinite;
}
.ii-sugg-skel .ii-skel-row.w70 { width: 70%; }
.ii-sugg-skel .ii-skel-row.w45 { width: 45%; }

/* A failed request has to say so. Before this the dropdown simply hung: the
   $.get had no failure path, so response() was never called. */
.ii-sugg-error {
  list-style: none;
  padding: 11px 12px;
  color: var(--ii-text-muted);
  font-size: var(--ii-text-sm);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .ii-sugg-skel .ii-skel-row { animation: none; }
}

/* ==========================================================================
   PHASE D - THE LEGACY SCREENS

   Account, the legacy auth forms, the desktop filter sidebar and the header
   were never touched by the earlier UX work, so they still carry the theme's
   own controls: five input treatments and six button treatments, two of the
   three oranges, and font-weight 100 on real buttons.

   Rather than restyle each screen, the theme's own control classes are
   brought onto the tokens once. Every legacy form on the site inherits it,
   including ones not audited individually.

   Scope note: iibiy-mobile.css is only loaded by the frontend layout, so none
   of this reaches the admin panel.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Inputs
   -------------------------------------------------------------------------- */
.form-control,
select.form-control,
textarea.form-control {
  min-height: 48px;
  padding: 12px 14px;
  border: 1px solid var(--ii-border-strong);
  border-radius: var(--ii-radius-control);
  background-color: var(--ii-bg-surface);
  color: var(--ii-text);
  font-family: var(--ii-font-sans);
  font-size: 16px;                  /* 16px or iOS zooms on focus */
  line-height: var(--ii-leading-body);
  box-shadow: none;
  transition: border-color var(--ii-dur-fast) var(--ii-ease),
              box-shadow var(--ii-dur-fast) var(--ii-ease);
}
textarea.form-control { min-height: 96px; }

.form-control:focus {
  border-color: var(--ii-focus);
  box-shadow: var(--ii-focus-ring);
  background-color: var(--ii-bg-surface);
  color: var(--ii-text);
}
.form-control::placeholder { color: var(--ii-text-disabled); }

/* Read-only fields were a flat Bootstrap grey that read as broken. */
.form-control:disabled,
.form-control[readonly] {
  background-color: var(--ii-bg-sunken);
  color: var(--ii-text-muted);
  border-color: var(--ii-border);
}

/* Bootstrap splits the radius across an input group; keep the join but on the
   new radius rather than the old 4px. */
.input-group > .form-control:not(:first-child) {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}
.input-group > .form-control:not(:last-child) {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

/* Labels. The legacy forms set these 14px/700 in pure black, which made every
   label shout as loudly as the value under it. */
/* `.title-label { font-weight: bold !important }` in style.css sits at
   (0,1,0); label.title-label is (0,1,1) and wins. */
form label,
.form-group > label,
label.title-label {
  font-size: var(--ii-text-sm);
  font-weight: var(--ii-weight-semibold);
  color: var(--ii-text-secondary) !important;
  font-weight: var(--ii-weight-semibold) !important;
  margin-bottom: 6px;
}

/* --------------------------------------------------------------------------
   bootstrap-select

   Renders a <select> as a <button>, so it needs styling as a control rather
   than inheriting the button rules. It was also one of the sources of
   font-weight 100.
   -------------------------------------------------------------------------- */
/* `.sasw_list button.btn.dropdown-toggle` is stated too, because style.css
   carries it at (0,3,1) WITH !important - `border: 1px solid #ff5e00` plus a
   5% orange tint behind it - and the rule below at (0,2,0) lost even carrying
   !important itself. Same specificity here, and this sheet loads last. */
.bootstrap-select > .dropdown-toggle,
.bootstrap-select > .dropdown-toggle.btn-light,
.sasw_list button.btn.dropdown-toggle {
  min-height: 48px;
  padding: 12px 40px 12px 14px;
  border: 1px solid var(--ii-border-strong) !important;
  border-radius: var(--ii-radius-control) !important;
  background-color: var(--ii-bg-surface) !important;
  color: var(--ii-text) !important;
  font-family: var(--ii-font-sans);
  font-size: var(--ii-text-md) !important;
  font-weight: var(--ii-weight-regular) !important;
  box-shadow: none !important;
  outline: none;
}
.bootstrap-select > .dropdown-toggle:focus-visible {
  border-color: var(--ii-focus) !important;
  box-shadow: var(--ii-focus-ring) !important;
}
.bootstrap-select .filter-option,
.bootstrap-select .filter-option-inner,
.bootstrap-select .filter-option-inner-inner {
  font-weight: var(--ii-weight-regular) !important;
  color: var(--ii-text);
}
.bootstrap-select .dropdown-menu {
  border: 1px solid var(--ii-border);
  border-radius: var(--ii-radius-card);
  box-shadow: var(--ii-shadow-lg);
  padding: var(--ii-space-1);
  background: var(--ii-bg-surface);
}
.bootstrap-select .dropdown-menu li a,
.bootstrap-select .dropdown-item {
  border-radius: 8px;
  padding: 9px 12px;
  font-size: var(--ii-text-md);
  color: var(--ii-text);
}
.bootstrap-select .dropdown-menu li a:hover,
.bootstrap-select .dropdown-item:hover,
.bootstrap-select .dropdown-item.active,
.bootstrap-select .dropdown-menu li.selected a {
  background: var(--ii-accent-subtle) !important;
  color: var(--ii-text) !important;
}

/* --------------------------------------------------------------------------
   Legacy buttons

   Only the theme's own button classes are targeted, not `.btn` wholesale -
   Bootstrap's base class carries layout in places and a blanket restyle would
   reach further than intended.
   -------------------------------------------------------------------------- */
/* Compound selectors: style.css carries `.profile-top-wapper button,
   .orange-border-btn { font-weight: 100 !important }` at (0,1,1), so a single
   class with !important still loses on specificity. */
.btn.btn-thm,
.btn.orange-border-btn,
.orange-border-btn,
.btn.big-btn,
.profile-top-wapper button.big-btn {
  font-family: var(--ii-font-sans);
  font-weight: var(--ii-weight-semibold) !important;
  border-radius: var(--ii-radius-control) !important;
  min-height: 44px;
  font-size: var(--ii-text-md);
  transition: background-color var(--ii-dur-fast) var(--ii-ease),
              border-color var(--ii-dur-fast) var(--ii-ease),
              color var(--ii-dur-fast) var(--ii-ease),
              transform var(--ii-dur-fast) var(--ii-ease);
}
.btn.btn-thm:active,
.btn.orange-border-btn:active,
.btn.big-btn:active { transform: scale(.97); }

.btn.btn-thm:focus-visible,
.btn.orange-border-btn:focus-visible,
.btn.big-btn:focus-visible { outline: none; box-shadow: var(--ii-focus-ring); }

/* The third orange, #FB772A, retired. */
.btn.btn-thm {
  background-color: var(--ii-accent) !important;
  border-color: var(--ii-accent) !important;
  color: var(--ii-text-on-accent) !important;
}
.btn.btn-thm:hover {
  background-color: var(--ii-accent-hover) !important;
  border-color: var(--ii-accent-hover) !important;
  color: var(--ii-text-on-accent) !important;
}

/* The seller profile's "Filtrer" is a filter TOGGLE, not an action, and it
   wore the brand orange - the most saturated thing on the screen apart from
   Vendre in the bottom nav. On a phone the row wraps and it gets a line of its
   own, right-aligned, which made it louder still: two orange controls roughly
   a thumb apart, one of which does nothing but reveal a dropdown.

   Orange marks the one action the business exists to collect. The 1.24 pass
   took it off the profile tabs and the status chips for exactly this reason
   and did not reach here, because this branch renders only for a VISITOR -
   the owner's toolbar shows segments and Trier instead, so every check made
   on one's own profile passed. Same shape as the 1.32 defect one branch over.

   This recolours ONLY. Keeping `.btn-thm` in the markup is deliberate: the
   sizing, weight and radius above are attached to `.btn.btn-thm`, and the
   legacy sheet carries `.profile-top-wapper button { font-weight: 100
   !important }` at (0,1,1). Swapping the class for `.ii-btn-secondary` was
   tried first and measured pale peach at weight 100, 3px radius and 34px
   tall - the untreated legacy button this block exists to correct.

   (0,3,0) to clear the (0,2,0) rule above. Semantics, so it flips with the
   theme rather than pinning a hex - constraint 16. */
.btn.btn-thm.listing-filter-btn {
  background-color: var(--ii-bg-surface) !important;
  border-color: var(--ii-border-strong) !important;
  color: var(--ii-text) !important;
}
.btn.btn-thm.listing-filter-btn:hover,
.btn.btn-thm.listing-filter-btn:focus {
  background-color: var(--ii-bg-sunken) !important;
  border-color: var(--ii-border-strong) !important;
  color: var(--ii-text) !important;
}

/* Secondary actions were transparent with font-weight 100 and a 3px radius.
 *
 * `.orange-border-btn` is listed WITHOUT `.btn` as well, because the button
 * this rule exists for does not carry it: "Afficher plus" is built in JS as
 * `class='mb-0 orange-border-btn'`, so `.btn.orange-border-btn` never matched
 * the one element on the browse page it was written for. It kept style.css's
 * `border: 1px solid #ff5e00` - the retired orange, on the most-used control
 * below the grid. Fourth time a rule here has been written against markup that
 * does not exist; check what the element actually carries. */
.btn.big-btn,
.btn.orange-border-btn,
.orange-border-btn,
.profile-top-wapper button.big-btn {
  background-color: var(--ii-bg-surface) !important;
  border: 1px solid var(--ii-border-strong) !important;
  color: var(--ii-text) !important;
  padding: 0 18px;
}
/* `html body .orange-border-btn:hover` is (0,2,2) AND !important in style.css,
   filling the button with #FF5E00 under white text - 3.06:1, and the retired
   tone. Matched at the same specificity so the later sheet wins, rather than
   escalating into an arms race. */
.btn.big-btn:hover,
.btn.orange-border-btn:hover,
html body .orange-border-btn:hover {
  background-color: var(--ii-bg-sunken) !important;
  border-color: var(--ii-border-strong) !important;
  color: var(--ii-text) !important;
}

/* --------------------------------------------------------------------------
   Retiring the second orange, #FF5E01, from the header and off-canvas menu
   -------------------------------------------------------------------------- */
.mm-navbar,
.off-canvas-header {
  background-color: var(--ii-accent) !important;
  /* Stated rather than inherited: the theme puts white here, which is 2.80
     on the primary. */
  color: var(--ii-text-on-accent) !important;
}
a.AddListingInA {
  background-color: var(--ii-accent) !important;
  color: var(--ii-text-on-accent) !important;
  border-radius: var(--ii-radius-control) !important;
  font-weight: var(--ii-weight-semibold);
}

/* --------------------------------------------------------------------------
   Header
   -------------------------------------------------------------------------- */
@media (max-width: 991px) {
  /* The spacer that clears the fixed header had its height set by jQuery
     after load, so every mobile page rendered under the header and then
     jumped 75px once the script ran. Giving it a height in CSS means the
     layout is correct on first paint; the script then sets the same value. */
  .mobile-header-set { height: 75px; }

  /* The gap under the fixed header, for EVERY mobile page, in one place.
     Measured before: the header ends at y=76 and the home search input begins
     at y=76 - a 0px gap, so the field sat welded to the bar. (It was 1px worse
     than that: custom.js sizes this spacer from `.mobile-menu`'s height, which
     excludes the 1px border on #page, so content actually started one pixel
     UNDER the header.)

     margin-bottom rather than height, deliberately: custom.js overwrites the
     HEIGHT of this element on load and on every resize, so a height set here
     would be thrown away. The margin survives, so the gap holds without having
     to touch that script or fight it.

     16px, not 12 or 24. Against a 52px input, 12px still reads as touching;
     24px pushes the category rail toward the fold on a small phone, and
     vertical space is the scarce axis on mobile. 16px is also the step the
     rest of this rhythm uses, so the top of the page reads as one system. */
  .mobile-header-set { margin-bottom: var(--ii-space-4); }

  /* SOLID, not glass. Reported from a real iPhone: the header renders WHITE
     while the body, the bottom nav and every card render dark. That cannot be
     a light/dark mix-up - forcing each theme here shows body, header and nav
     moving together - and it could not be reproduced in this environment at
     all, where the header measures the dark glass correctly.

     What singles this element out is backdrop-filter, the most browser-variable
     property in this sheet, on the one node reported wrong. It samples whatever
     sits behind a fixed element, and `html` carried NO background at all, so
     there was nothing themed underneath the header to sample - the same gap
     constraint 69 names for `body` ("the viewer paints its own ground behind
     the page"), one level up. Both halves are fixed: html gets a themed
     background below, and this stops being translucent.

     It also puts the sheet back in line with constraint 17, which says glass is
     used in exactly two places - the bottom navigation and the ad-detail
     contact bar. The header was an undocumented third. A solid surface is
     deterministic on every engine, and in practice looks the same: --ii-glass
     is .82 white over white in light and .78 of #17181A over near-black in
     dark, so both were already within a shade of the solid token. */
  #page.stylehome1 {
    background: var(--ii-bg-surface);
    border-bottom: 1px solid var(--ii-border);
  }
  .ssssexample.header.stylehome1 { background: transparent !important; }

  /* THE WHITE HEADER. This is the one, and it took a device diagnostic to see.
     `responsive.css` builds a `.stylehome1:before` - absolutely positioned,
     100% wide, 65px tall - and `menu.css` line 226 then paints it
     `#ffffff !important`. It sits ON TOP of #page's own background and UNDER
     the logo, avatar and burger, which is exactly a white bar with the header
     content on it.

     Why four attempts missed it, which is the part worth keeping:

       - `elementFromPoint` reports a pseudo-element as its ORIGINATING element
         (constraint 31), so the hit-test said `.ssssexample.header,
         transparent` and never mentioned it.
       - Every light-surface sweep I ran iterates `querySelectorAll('body *')`
         and reads each element's own backgroundColor. A pseudo-element is not
         an element and was never enumerated - so "0 light surfaces" was a
         statement about elements, not about paint.
       - Enumerating "every rule that can paint this element" used
         `el.matches(selectorText)`, which is FALSE for any `::before` rule. The
         enumeration that concluded "exactly one rule can reach the header" was
         structurally incapable of seeing this one.

     It was never an iOS problem. The header was white in every browser
     including this one; nothing I measured was looking at the thing painting it.

     There is already a `content: none` attempt in header.blade.php, written
     against `.ssssexample.stylehome1:before` - but the pseudo-element is on
     `#page.stylehome1`, and `ssssexample` is on the INNER div. It has never
     matched anything.

     Made transparent rather than removed, so whatever layout the box
     contributes is untouched and #page's themed background is the single
     source of the header's colour. That also fixes the centring: the white
     strip was 65px inside a 76px header, so content centred in the real box
     sat ~5px low against the visible bar. */
  #page.stylehome1::before {
    background-color: transparent !important;
  }

  /* The 1px vertical rule beside the avatar, from style.css / responsive.css,
     was #E8EEF2 - a light-mode divider left on a dark bar. Found by the same
     pseudo-element sweep that caught the header; it is the only other one on
     the page. */
  .user_setting::before {
    background-color: var(--ii-border) !important;
  }

  /* The header text was still the legacy #484848 in dark mode - "Se connecter"
     measured 1.94:1 against the themed bar behind it. The background had been
     converted and the foreground had not. */
  .ssssexample.header.stylehome1,
  .ssssexample.header.stylehome1 a,
  .ssssexample.header.stylehome1 span,
  .ssssexample.header.stylehome1 p {
    color: var(--ii-text);
  }

  /* ---- Header alignment -------------------------------------------------
     Reported as "not symmetrical", and measured at 375px it is: the header
     centre line is y=38, the logo and the avatar both sit on it, and the
     hamburger sits at y=43. Its <li> is display:block, so the inline-flex
     anchor inside lands on the line box's BASELINE rather than its centre -
     5px low, exactly as reported.

     Horizontally the header pads 20px each side, but #logoLink adds its own
     margin-left:5px, so the logo started at x=25 while the hamburger ended at
     x=355, 20px from the right. That 5px is the whole asymmetry.

     The avatar is 30px against a 34px logo and a 44px hamburger, which is what
     "a little bit small" is. Its tap target was already fine - the anchor
     around it measures 50x46 - so only the image grows. */
  .ssssexample.header.stylehome1 .menu_bar_home2 { margin: 0; }
  .ssssexample.header.stylehome1 .menu_bar_home2 > li {
    display: flex;
    align-items: center;
    height: auto;
  }
  .ssssexample.header.stylehome1 #logoLink { margin-left: 0 !important; }

  /* Element size in the bar. Reported as "a little bit smaller", and measured
     it is a PROPORTION problem rather than an absolute one: the bar is 76px
     tall - taller than a Material app bar at 56dp and an iOS nav bar at 44pt -
     and held a 34px logo, so 21px of empty space sat above and below it. That
     much slack around a small mark is what reads as undersized.

     The bar height is left alone on purpose: custom.js derives the spacer from
     it, so changing it moves the whole page's top offset for a cosmetic gain.
     Growing the contents fixes the ratio with none of that risk - logo and
     avatar go from 45%/47% of the bar to 53%. */
  .ssssexample.header.stylehome1 #logoLink img {
    height: 40px !important;
    width: auto !important;
  }
  .ssssexample.header.stylehome1 .hamburger span,
  .ssssexample.header.stylehome1 .hamburger::before,
  .ssssexample.header.stylehome1 .hamburger::after {
    width: 26px !important;
  }
  /* responsive.css pins this with FOUR !important declarations - max-width,
     max-height, min-width and min-height, all 30px - so width/height alone
     changed nothing. All four have to be answered, which is why this rule
     looks heavier than it should. */
  .ssssexample.header.stylehome1 .user_setting img.rounded-circle {
    width: 40px !important;
    height: 40px !important;
    min-width: 40px !important;
    min-height: 40px !important;
    max-width: 40px !important;
    max-height: 40px !important;
  }

  /* 23x21 is well under the 44px target size, on the control that opens all
     navigation. */
  .hamburger {
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    margin: -11px;
    padding: 11px;
  }
  .hamburger:focus-visible { outline: none; box-shadow: var(--ii-focus-ring); border-radius: 8px; }
}

/* --------------------------------------------------------------------------
   Desktop filter sidebar

   Measured as a position:static, 1238px-wide block with no background, radius
   or shadow - an unstyled strip of widgets, while the mobile sheet had been
   given a proper treatment.
   -------------------------------------------------------------------------- */
@media (min-width: 992px) {
  .filter-block {
    background: var(--ii-bg-surface);
    border-radius: var(--ii-radius-card);
    box-shadow: var(--ii-shadow-sm);
    padding: var(--ii-space-4);
    /* Deliberately not sticky. At this width the block is the full container
       (measured 1238px) and sits above the grid rather than beside it, so
       pinning it would drag a full-width bar over the results. */
  }
  .filter-block .sidebar_listing_list {
    box-shadow: none !important;
    padding: 0 !important;
    background: transparent;
  }
  .filter-block .widget-wrapper { margin-bottom: var(--ii-space-4); }
  .filter-block .sidebar_widget_title,
  .filter-block h4 {
    font-size: var(--ii-text-sm);
    font-weight: var(--ii-weight-semibold);
    color: var(--ii-text-secondary);
    text-transform: uppercase;
    letter-spacing: .05em;
    margin-bottom: var(--ii-space-2);
  }
}

/* --------------------------------------------------------------------------
   Account page

   Only the visual layer here. Cutting the ten-field form down, and hiding the
   password action on accounts that have no password, are product decisions
   rather than styling and are left for a separate change.
   -------------------------------------------------------------------------- */
.our-dashbord .dashboard_title,
.dashbord-title,
.breadcrumb_content h2 {
  font-size: var(--ii-text-2xl);
  font-weight: var(--ii-weight-bold);
  letter-spacing: var(--ii-tracking-display);
  color: var(--ii-text);
}
.dashboard_navigationbar,
.user_profile_wrapper,
.my_dashboard_review,
.profile_edit_form {
  border-radius: var(--ii-radius-card);
}
.breadcrumb_content .breadcrumb-item.active,
.breadcrumb-item.active { color: var(--ii-accent-text); }
.nav-tabs .nav-link.active,
.nav-pills .nav-link.active,
.nav-link.active {
  background-color: var(--ii-accent) !important;
  color: var(--ii-text-on-accent) !important;
  border-radius: var(--ii-radius-control);
}

/* The profile tabs were still painting #FF5E00 with a white label: style.css
   declares `.top-bar-block .nav-pills .nav-link.active` at (0,3,0) WITH
   !important, so the rule above at (0,2,0) lost even carrying !important
   itself. Restated at equal specificity so the segmented control matches every
   other filled accent surface - the same orange, and the same ink label at
   5.80 rather than white at 3.26. */
.top-bar-block .nav-pills .nav-link.active,
.top-bar-block .nav-pills .show > .nav-link {
  background-color: var(--ii-accent) !important;
  color: var(--ii-text-on-accent) !important;
  border-radius: var(--ii-radius-control);
}

/* ==========================================================================
   PHASE E - MOTION, LOADING, DIALOGS, ICONS
   ========================================================================== */

/* --------------------------------------------------------------------------
   The card favourite, as inline SVG

   One path, two states: stroked when off, filled when on. The site-wide
   handler only toggles `is-on`, so nothing swaps markup any more.
   -------------------------------------------------------------------------- */
.ii-card-fav svg {
  width: 19px; height: 19px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linejoin: round;
  transition: fill var(--ii-dur-fast) var(--ii-ease);
}
.ii-card-fav.is-on svg { fill: currentColor; stroke: currentColor; }

/* A saved heart is filled accent; an unsaved one is a quiet outline that does
   not compete with the photo behind it. */
.ii-card-fav { color: var(--ii-text-secondary); }
.ii-card-fav.is-on { color: var(--ii-accent); }

/* --------------------------------------------------------------------------
   The seller's own listing list

   Replaces the browsing card for `isShow == 1`. The old view reused the
   col-6 grid card, so on a 375px phone each listing had ~170px to carry a
   status badge, a two-line title, a price, up to three pills, a favourite
   counter, edit, delete, a date, a stats link and Promote. This is the same
   information in a row that can actually hold it, with one primary action and
   the rest behind an overflow.
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   A sold listing's page

   Reuses the empty-state buttons, because this is the same moment: the thing
   you came for is not here, and the useful response is a way onward rather
   than an apology.
   -------------------------------------------------------------------------- */
.ii-sold-section { padding: 32px 0 48px; }

.ii-sold {
  max-width: 460px;
  margin: 0 auto;
  text-align: center;
}

.ii-sold-media {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  margin-bottom: 20px;
  border-radius: var(--ii-radius-card, 14px);
  overflow: hidden;
  background: var(--ii-bg-sunken);
}
.ii-sold-media img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  /* Muted, because it is a record of something that happened rather than an
     item on offer. */
  filter: grayscale(1);
  opacity: .7;
}
.ii-sold-badge {
  position: absolute; left: 12px; top: 12px;
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--ii-text);
  color: var(--ii-bg-surface);
  font-size: var(--ii-text-sm, 13px);
  font-weight: var(--ii-weight-bold);
}

.ii-sold-title {
  margin: 0 0 8px;
  font-size: var(--ii-text-xl, 20px);
  font-weight: var(--ii-weight-bold);
  color: var(--ii-text);
  line-height: 1.3;
}
.ii-sold-note {
  margin: 0 0 22px;
  font-size: var(--ii-text-md, 15px);
  color: var(--ii-text-secondary);
}

/* Status segments above the list. Horizontally scrollable rather than
   wrapping, so the bar stays one line at 375px and the first two - the ones
   that answer "what is live" and "what is still being checked" - are always
   in view. */
.ii-seg {
  display: flex;
  gap: 8px;
  margin: 0 0 14px;
  padding-bottom: 2px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.ii-seg::-webkit-scrollbar { display: none; }

.ii-seg-item {
  display: inline-flex; align-items: center; gap: 6px;
  flex: 0 0 auto;
  min-height: 38px;
  padding: 0 14px;
  border: 1px solid var(--ii-border);
  border-radius: 999px;
  background: var(--ii-bg-surface);
  color: var(--ii-text-secondary);
  font-size: var(--ii-text-sm, 14px);
  font-weight: var(--ii-weight-semibold);
  white-space: nowrap;
  text-decoration: none;
}
.ii-seg-item:hover { color: var(--ii-text); text-decoration: none; }
.ii-seg-item:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: 2px; }

/* The selected segment is the accent surface, so it carries an ink label like
   every other filled accent in the product. */
.ii-seg-item.is-on {
  background: var(--ii-accent);
  border-color: var(--ii-accent);
  color: var(--ii-text-on-accent);
}

.ii-seg-count {
  font-variant-numeric: tabular-nums;
  font-size: var(--ii-text-xs, 12px);
  color: var(--ii-text-muted);
}
.ii-seg-item.is-on .ii-seg-count { color: var(--ii-text-on-accent); opacity: .75; }

.ii-seg-sortlabel {
  margin: 0 8px 0 0;
  font-size: var(--ii-text-sm, 14px);
  color: var(--ii-text-muted);
}
.ii-seg-sort {
  min-height: 38px;
  padding: 0 10px;
  border: 1px solid var(--ii-border);
  border-radius: 10px;
  background: var(--ii-bg-surface);
  color: var(--ii-text);
  font-size: var(--ii-text-sm, 14px);
}

/* iOS zooms the viewport the moment a form control under 16px takes focus and
   does not zoom back out, leaving the page enlarged and off-centre - which is
   what a horizontal swipe on a real device actually is. The same defect is
   already written up twice in this file: once as the "half the filter, half
   the listings" report on `.ii-fdrop-price-row input`, and once as the guard
   near the top that both of them beat.

   That guard is `select` at (0,1,0). This rule is `.ii-seg-sort`, also
   (0,1,0), and it sits ~3,400 lines later - so ORDER decided it and the guard
   never applied. Measured: 13px, the only control under 16px anywhere on the
   mobile site. It has to be stated HERE for the same reason the price-row
   override does.

   991px, not the guard's 767px: the mobile layout runs to 991 (§3.5), so a
   tablet in portrait gets this select and would zoom on it too.

   Desktop keeps 13px - it is on the type scale there and nothing zooms.
   Constraint 82. */
@media (max-width: 991px) {
  .ii-seg-sort { font-size: 16px; }
}

.ii-mrow {
  display: flex;
  flex-wrap: wrap;              /* lets Promote drop to its own line on a phone */
  align-items: flex-start;
  gap: 12px;
  padding: 12px;
  margin-bottom: 10px;
  background: var(--ii-bg-surface);
  border: 1px solid var(--ii-border);
  border-radius: var(--ii-radius-card, 14px);
}

/* Sold and refused listings are still the seller's, and still need their
   actions - so they are muted rather than hidden. The media dims; the text
   does not, because a washed-out title is harder to scan, not calmer. */
.ii-mrow[data-state="sold"] .ii-mrow-media img,
.ii-mrow[data-state="refused"] .ii-mrow-media img { opacity: .55; }

.ii-mrow-media {
  position: relative;
  flex: 0 0 auto;
  order: 0;
  width: 88px; height: 88px;
  border-radius: 10px;
  overflow: hidden;
  background: var(--ii-bg-sunken);
}
.ii-mrow-media img { width: 100%; height: 100%; object-fit: cover; display: block; }

.ii-mrow-badge {
  position: absolute; left: 4px; bottom: 4px;
  padding: 2px 6px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: var(--ii-weight-bold);
  line-height: 1.4;
  white-space: nowrap;
}
.ii-mrow-badge.is-review  { background: var(--ii-warning-subtle); color: var(--ii-warning); }
.ii-mrow-badge.is-refused { background: var(--ii-danger-subtle);  color: var(--ii-danger); }
.ii-mrow-badge.is-sold    { background: var(--ii-bg-sunken);      color: var(--ii-text-secondary); }
.ii-mrow-badge.is-premium { background: var(--ii-accent-subtle);  color: var(--ii-accent-text); }

/* basis 0, not auto: with wrap enabled, `auto` sizes this item from its own
   text, so a long title made the whole row wrap and stacked the thumbnail,
   the text and the menu into a 306px tower. */
.ii-mrow-body { flex: 1 1 0; min-width: 0; order: 1; }

.ii-mrow-title {
  display: block;
  font-size: var(--ii-text-md, 15px);
  font-weight: var(--ii-weight-semibold);
  color: var(--ii-text);
  line-height: 1.3;
  /* One line. In a list the title identifies the row; it does not have to
     describe the item, because the seller already knows what it is. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ii-mrow-title:hover { color: var(--ii-accent-text); text-decoration: none; }

.ii-mrow-price {
  margin: 2px 0 0;
  font-size: var(--ii-text-md, 15px);
  font-weight: var(--ii-weight-bold);
  color: var(--ii-text);
  font-variant-numeric: tabular-nums;
}
.ii-mrow-price small { font-size: 11px; font-weight: var(--ii-weight-semibold); margin-left: 2px; }

.ii-mrow-meta {
  display: flex; align-items: center; gap: 12px;
  margin: 4px 0 0;
  font-size: var(--ii-text-xs, 12px);
  color: var(--ii-text-muted);
}
.ii-mrow-meta span { display: inline-flex; align-items: center; gap: 4px; }
.ii-mrow-meta svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2; }

.ii-mrow-actions {
  flex: 0 0 auto;
  order: 2;
  display: flex; align-items: center; gap: 6px;
}

/* Secondary, not primary. Promote is worth offering but it is not what the
   seller came here to do, and an accent-filled button on every row would make
   the list read as an upsell. Outlined keeps it available and quiet. */
.ii-mrow-promote {
  /* Phone: its own line, full width, below the row. Inline it left the title
     64px wide, which is not a title. */
  order: 3;
  flex: 1 0 100%;
  margin-top: 2px;
  min-height: 40px;
  padding: 0 12px;
  border: 1px solid var(--ii-accent);
  border-radius: 10px;
  background: transparent;
  color: var(--ii-accent-text);
  font-size: var(--ii-text-sm, 13px);
  font-weight: var(--ii-weight-semibold);
  white-space: nowrap;
  cursor: pointer;
}
.ii-mrow-promote:hover { background: var(--ii-accent-subtle); }
.ii-mrow-promote:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: 2px; }

/* --- overflow menu ------------------------------------------------------ */
.ii-mrow-menu { position: relative; }
.ii-mrow-menu > summary {
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;          /* the target the rest of the project uses */
  border-radius: 10px;
  color: var(--ii-text-secondary);
  cursor: pointer;
  list-style: none;
}
.ii-mrow-menu > summary::-webkit-details-marker { display: none; }
.ii-mrow-menu > summary::marker { content: ""; }
.ii-mrow-menu > summary:hover { background: var(--ii-bg-sunken); }
.ii-mrow-menu > summary:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: 2px; }
.ii-mrow-menu > summary svg { width: 18px; height: 18px; fill: currentColor; }

.ii-mrow-menu-list {
  position: absolute; right: 0; top: calc(100% + 4px);
  z-index: 20;
  min-width: 190px;
  padding: 6px;
  background: var(--ii-bg-surface);
  border: 1px solid var(--ii-border);
  border-radius: 12px;
  box-shadow: var(--ii-shadow-lg);
}
.ii-mrow-menu-list a {
  display: block;
  padding: 10px 12px;
  border-radius: 8px;
  font-size: var(--ii-text-sm, 14px);
  color: var(--ii-text);
  text-decoration: none;
}
.ii-mrow-menu-list a:hover { background: var(--ii-bg-sunken); text-decoration: none; }
.ii-mrow-menu-sep {
  display: block; height: 1px; margin: 6px 4px;
  background: var(--ii-border);
}
.ii-mrow-danger, .ii-mrow-menu-list a.ii-mrow-danger { color: var(--ii-danger); }
.ii-mrow-danger:hover { background: var(--ii-danger-subtle); }

/* The delete form is a submit target for the confirm handler, never seen. */
.ii-mrow-menu-list form { display: none; }

/* The destructive button in the delete dialog. It was styled btn-primary,
   so the button that looked safe was the one that deleted the listing. */
.swal2-actions .btn.ii-swal-danger,
.swal2-actions .btn.ii-swal-danger:hover {
  background-color: var(--ii-danger) !important;
  border-color: var(--ii-danger) !important;
  color: #FFFFFF !important;
}

@media (min-width: 768px) {
  .ii-mrow-media { width: 104px; height: 104px; }
  .ii-mrow-title { font-size: var(--ii-text-lg, 17px); }

  /* Room for it inline here, ahead of the overflow button. */
  .ii-mrow-promote {
    order: 1;
    flex: 0 0 auto;
    margin-top: 0;
    min-height: 36px;
    align-self: center;
  }
  .ii-mrow-actions { align-self: center; }
}

/* --------------------------------------------------------------------------
   Pre-initialisation states

   Taking the full-page preloader off page load revealed what it had been
   covering: markup that is only presentable once a jQuery plugin has
   initialised. Until then it renders in normal document flow, at full size,
   in the middle of the page.

   The preloader was not a loading indicator for these. It was a dust sheet.
   Putting it back would re-hide the symptom and keep the ~2s white screen, so
   these rules give the markup a correct FIRST PAINT instead.

   Every rule here is scoped with :not(<the class the plugin adds on init>),
   so it releases itself the moment the plugin takes over. That matters more
   than it looks: a plain `display: none` on either of these would break the
   feature outright, and a timed or scripted reveal would just be a smaller
   version of the preloader.
   -------------------------------------------------------------------------- */

/* The off-canvas menu is a plain <nav> full of categories until mmenu moves
   it into its fixed panel. Nothing in any stylesheet hid it before then, so
   every page painted ~400px of expanded category list above its own content
   and then collapsed it. mmenu adds .mm-menu on init. */
#menu:not(.mm-menu) { display: none; }

/* The ad-detail gallery has the opposite problem to the menu. Owl's own base
   rule - `.owl-carousel { display: none }`, confirmed by testing the class in
   isolation - correctly stops the photos rendering as a tall stack, but it
   also means the gallery occupies NO height until Owl initialises. So the
   largest element on the page arrives late and shoves everything below it
   down by its full height.

   `.ii-gallery-skel` is a placeholder sibling that holds that space. It sits
   immediately after the carousel in the markup and is removed by the sibling
   selector the moment Owl adds .owl-loaded, so the photo appears exactly
   where the placeholder was: no jump, and something visible in the meantime
   rather than a gap.

   A sibling rather than a wrapper because the swap is then pure CSS and needs
   neither :has() nor a script - the two swap in the same frame. */
.ii-gallery-skel {
  width: 100%;
  /* Matches the carousel exactly. The theme gives
     .listing_single_property_slider a hard `height: 300px` - measured
     identical at 375px and 1280px, so this is a fixed height and not an
     aspect ratio. An aspect-ratio here computed to 926px against the full
     container width and would have introduced a 626px shift, which is worse
     than the gap it replaces. Keep this in step if the theme height changes. */
  height: 300px;
  border-radius: var(--ii-radius-card, 14px);
  background-color: var(--ii-bg-sunken);
  background-image: linear-gradient(90deg,
    var(--ii-bg-sunken) 25%, var(--ii-bg-canvas) 37%, var(--ii-bg-sunken) 63%);
  background-size: 400% 100%;
  animation: ii-shim 1.2s ease infinite;
  animation-iteration-count: 12;
}
.listing_single_property_slider.owl-loaded + .ii-gallery-skel { display: none; }

@media (prefers-reduced-motion: reduce) {
  .ii-gallery-skel { animation: none; background-image: none; }
}

/* --------------------------------------------------------------------------
   The full-page preloader does not run on page load any more

   `.preloader` is an opaque white sheet - fixed, 100% x 100%, z-index 99999 -
   rendered into every page by common.blade.php. script.js removes it on
   $(window).on("load"), then .delay(200).fadeOut(300) on top.

   window.load waits for every image, font, script and third-party tag. These
   pages are server-rendered and their CSS is render-blocking in <head>, so
   the content underneath is complete and correctly styled long before that.
   The sheet was not covering work in progress; it was covering a finished
   page.

   Measured on /categories locally: content ready at 5,977ms, window.load at
   7,502ms - so 1,526ms of opaque white over a page that was ready, plus the
   500ms fade. The three slowest resources it waited on were Google Analytics
   (7,406ms), the Facebook pixel (6,921ms) and a Flaticon font (6,707ms), which
   means site visibility was gated on third-party tracking responding. On
   Djiboutian mobile data, or with either blocked, that wait is longer.

   Hidden by default here rather than deleted, and deliberately WITHOUT
   !important: jQuery's .show() writes an inline display, which beats this
   rule but would lose to an important one. The six AJAX handlers that still
   raise the sheet as a submit lock therefore keep working unchanged, and
   restoring the old behaviour is a one-line revert.
   -------------------------------------------------------------------------- */
.preloader { display: none; }

/* --------------------------------------------------------------------------
   Image loading

   Listing photos are the heaviest thing on every grid, and until now a card
   showed an empty grey box while one arrived. The container shimmers, then
   the image fades in over it.
   -------------------------------------------------------------------------- */
.ii-card-media::before {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(90deg,
    var(--ii-bg-sunken) 25%, var(--ii-bg-canvas) 37%, var(--ii-bg-sunken) 63%);
  background-size: 400% 100%;
  animation: ii-shim 1.2s ease infinite;
  /* Stops on its own if the load event never fires - a broken image should
     not shimmer forever. */
  animation-iteration-count: 12;
}
.ii-card-media.is-loaded::before { opacity: 0; animation: none; }

/* Deliberately no opacity on the image itself.

   The obvious version of this fades the photo in from opacity 0 once a class
   lands. That makes every listing photo on the site depend on a script
   running: if the class never arrives - the load event missed, the script
   errored, JS disabled - the grid renders empty. The shimmer sits behind the
   image instead, and the image simply paints over it. Same effect while
   loading, no failure mode. */

/* The ad detail gallery, which had only a flat grey box - the one place a
   slow image is most obvious, since it is the largest element on the page and
   the reason the buyer opened it.

   Same effect as the card above, built differently and deliberately so. The
   shimmer is the container's OWN background rather than an absolutely
   positioned ::before, so the <img> inside paints over it by normal painting
   order - no z-index, no JS, no class that has to arrive. That is what §5.4 of
   DECISIONS.md describes, and unlike the card version it holds with no script
   running: if the photo loads it covers the shimmer, and if it never loads the
   animation stops after 12 iterations and settles to the same sunken grey the
   gallery showed before.

   Targets `.item`, not the anchor inside it: the anchor is `display: inline`,
   so its background box is the inline box rather than the picture area, while
   `.item` is the block the carousel sizes (measured 408x300) and the shape the
   photo actually fills.

   The selector this replaces, `a.listings-images-gallery`, matched nothing -
   that markup sits inside a commented-out block in listing_detail.blade.php
   and the live carousel uses `a.open-fancybox`. It had been dead since C2. */
.listing_single_property_slider .item {
  background-color: var(--ii-bg-sunken);
  background-image: linear-gradient(90deg,
    var(--ii-bg-sunken) 25%, var(--ii-bg-canvas) 37%, var(--ii-bg-sunken) 63%);
  background-size: 400% 100%;
  animation: ii-shim 1.2s ease infinite;
  animation-iteration-count: 12;
}

@media (prefers-reduced-motion: reduce) {
  .listing_single_property_slider .item { animation: none; background-image: none; }
}

/* --------------------------------------------------------------------------
   Toasts

   The one notification surface the product actually uses. It had a 4px radius
   and a 50%-alpha drop shadow, which is the heaviest shadow anywhere on the
   site.
   -------------------------------------------------------------------------- */
.toast {
  border-radius: var(--ii-radius-card) !important;
  box-shadow: var(--ii-shadow-lg) !important;
  background-color: var(--ii-bg-surface) !important;
  border-left-width: 3px !important;
  padding: 12px 0 !important;
  font-family: var(--ii-font-sans);
  transition: transform var(--ii-dur-sheet) var(--ii-ease),
              opacity var(--ii-dur-sheet) var(--ii-ease) !important;
}
.toast__msg {
  font-size: var(--ii-text-sm);
  color: var(--ii-text);
  margin: 0;
}
.toast--success { border-color: var(--ii-success) !important; }
.toast--error   { border-color: var(--ii-danger) !important; }
.toast--warning { border-color: var(--ii-warning) !important; }
.toast--info    { border-color: var(--ii-accent) !important; }

.toast--success .toast__icon { color: var(--ii-success) !important; background-color: transparent !important; }
.toast--error   .toast__icon { color: var(--ii-danger) !important;  background-color: transparent !important; }
.toast--warning .toast__icon { color: var(--ii-warning) !important; background-color: transparent !important; }
.toast--info    .toast__icon { color: var(--ii-accent) !important;  background-color: transparent !important; }

.progress-track { background: var(--ii-bg-sunken) !important; }
.progress--success { background-color: var(--ii-success) !important; }
.progress--error   { background-color: var(--ii-danger) !important; }
.progress--warning { background-color: var(--ii-warning) !important; }
.progress--info    { background-color: var(--ii-accent) !important; }

/* --------------------------------------------------------------------------
   The other two dialog systems

   sweetalert2 and jquery-confirm are both still called from real flows, so
   they are not removed here - they are brought onto the same surface,
   radius and button treatment so the product stops shipping three visibly
   different dialog languages. Consolidating to one is a code change, not a
   styling one.
   -------------------------------------------------------------------------- */
.swal2-popup {
  border-radius: var(--ii-radius-sheet) !important;
  box-shadow: var(--ii-shadow-lg) !important;
  background: var(--ii-bg-surface) !important;
  font-family: var(--ii-font-sans) !important;
  padding: var(--ii-space-5) !important;
}
.swal2-title {
  font-size: var(--ii-text-xl) !important;
  font-weight: var(--ii-weight-bold) !important;
  color: var(--ii-text) !important;
}
.swal2-html-container,
.swal2-content {
  font-size: var(--ii-text-md) !important;
  color: var(--ii-text-secondary) !important;
}
.swal2-actions .swal2-styled {
  min-height: 48px;
  border-radius: var(--ii-radius-control) !important;
  font-family: var(--ii-font-sans);
  font-size: var(--ii-text-md) !important;
  font-weight: var(--ii-weight-semibold) !important;
  box-shadow: none !important;
}
.swal2-actions .swal2-confirm {
  background-color: var(--ii-accent) !important;
  color: var(--ii-text-on-accent) !important;
}
.swal2-actions .swal2-cancel {
  background-color: var(--ii-bg-surface) !important;
  border: 1px solid var(--ii-border-strong) !important;
  color: var(--ii-text) !important;
}

.jconfirm .jconfirm-box {
  border-radius: var(--ii-radius-sheet) !important;
  box-shadow: var(--ii-shadow-lg) !important;
  background: var(--ii-bg-surface) !important;
  font-family: var(--ii-font-sans);
}
.jconfirm .jconfirm-title {
  font-size: var(--ii-text-xl) !important;
  font-weight: var(--ii-weight-bold) !important;
  color: var(--ii-text) !important;
}
.jconfirm .jconfirm-content { font-size: var(--ii-text-md); color: var(--ii-text-secondary); }
.jconfirm .jconfirm-buttons button {
  min-height: 44px;
  border-radius: var(--ii-radius-control) !important;
  font-family: var(--ii-font-sans);
  font-weight: var(--ii-weight-semibold) !important;
  text-transform: none !important;
  box-shadow: none !important;
}
.jconfirm .jconfirm-buttons button.btn-default {
  background: var(--ii-bg-surface) !important;
  border: 1px solid var(--ii-border-strong) !important;
  color: var(--ii-text) !important;
}
.jconfirm .jconfirm-buttons button.btn-blue,
.jconfirm .jconfirm-buttons button.btn-primary {
  background: var(--ii-accent) !important;
  color: var(--ii-text-on-accent) !important;
  border: 0 !important;
}

/* --------------------------------------------------------------------------
   Motion, completed

   Phases B and C gave the new components press and state transitions. This
   closes the gap on the legacy surfaces those phases did not reach, and
   extends the reduced-motion contract over everything added since.
   -------------------------------------------------------------------------- */
.ii-rail-item,
.ii-empty-btn,
.ii-report-link,
.bootstrap-select > .dropdown-toggle,
.form-control {
  transition-timing-function: var(--ii-ease);
}

@media (prefers-reduced-motion: reduce) {
  .ii-card-media::before,
  .toast,
  .ii-card-fav svg { transition: none !important; animation: none !important; }

  .btn.btn-thm:active,
  .btn.big-btn:active,
  .btn.orange-border-btn:active { transform: none; }

  .swal2-popup, .jconfirm .jconfirm-box { animation: none !important; }
}


/* --------------------------------------------------------------------------
   "Nous avons trouvé" - the assistant's confirm card

   Sits inside the assistant's own surface, under the status line. It states
   what was recognised; it does not ask for approval. The values are already in
   the fields below, and continuing is the confirmation - so there is no accept
   button, no per-field checkbox and no confidence figure. A percentage next to
   a guess invites the seller to audit the guess instead of selling the thing.

   The chips are facts about the item ("Blanc", "Automatique"), not labelled
   form rows. The label is carried in `title` for anyone who wants it.
   -------------------------------------------------------------------------- */
.ii-ai-found {
  margin-top: var(--ii-space-3);
  padding-top: var(--ii-space-3);
  border-top: 1px solid var(--ii-border);
}

.ii-ai-found-lead {
  margin: 0 0 2px;
  font-size: var(--ii-text-sm);
  color: var(--ii-text-muted);
}

.ii-ai-found-headline {
  margin: 0;
  font-size: var(--ii-text-lg);
  font-weight: var(--ii-weight-semibold);
  line-height: 1.25;
  color: var(--ii-text);
}

.ii-ai-found-list {
  display: flex; flex-wrap: wrap; gap: 6px;
  list-style: none; margin: var(--ii-space-2) 0 0; padding: 0;
}

.ii-ai-found-item {
  padding: 3px 10px;
  border: 1px solid var(--ii-border);
  border-radius: 999px;
  background: var(--ii-bg-surface);
  font-size: var(--ii-text-sm);
  color: var(--ii-text);
}

/* Neutral, like every other utility control - constraint 51. This is a way
   back to the fields, not the action the screen is asking for. */
.ii-ai-found-edit {
  margin-top: var(--ii-space-2);
  min-height: 36px; padding: 0 12px;
  border: 1px solid var(--ii-border);
  border-radius: var(--ii-radius-control);
  background: var(--ii-bg-surface);
  color: var(--ii-text);
  font-family: var(--ii-font-sans); font-size: var(--ii-text-sm);
  cursor: pointer;
}
.ii-ai-found-edit:hover { border-color: var(--ii-border-strong); }
.ii-ai-found-edit:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: 1px; }

/* What a later photo added, and the suggestion to take one. Both are quiet:
   neither is an instruction, and the flow has to work for the seller who takes
   a single exterior shot. */
.ii-ai-added,
.ii-ai-nudge {
  margin: var(--ii-space-2) 0 0;
  font-size: var(--ii-text-sm);
  line-height: 1.4;
}
.ii-ai-added { color: var(--ii-accent-text); font-weight: var(--ii-weight-semibold); }
.ii-ai-nudge { color: var(--ii-text-muted); }

/* --------------------------------------------------------------------------
   Numeric category attributes on the sell screen - Année, Kilométrage

   Chips do not fit these: the live corpus runs 1999-2025 and 0-760 000 km, and
   any ladder covering that is either enormous or dishonest. So they are two
   short number fields, sized to what they hold rather than stretched to the
   column width - a four-digit year in a full-width input looks like a mistake.
   -------------------------------------------------------------------------- */
.sf-facet-num { display: flex; align-items: center; gap: 8px; }

.sf-facet-num .sf-input {
  width: 10ch;
  /* The spinners add two hit targets nobody wants on a phone, and they make
     the field look like a stepper for a value that is typed. */
  -moz-appearance: textfield;
}
.sf-facet-num .sf-input::-webkit-outer-spin-button,
.sf-facet-num .sf-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.sf-facet-suffix { font-size: var(--ii-text-sm); color: var(--ii-text-muted); }

/* --------------------------------------------------------------------------
   Grouped attribute chips - Type de bien

   config('constants.propertyType') is [sale mode][usage][type] and must not be
   flattened (DECISIONS constraint 62): "Residentiel" is a kind of LAND, and
   flat it sat beside "Appartement" as though they answered the same question.
   The heading is what carries that meaning, so it is a real element rather
   than a wider gap.

   The chips themselves are .sf-chip unchanged - the same gesture as every
   other facet row, because it IS the same gesture. Only the grouping is new.
   -------------------------------------------------------------------------- */
.sf-facet-groups { display: flex; flex-direction: column; gap: var(--ii-space-3, 12px); }

.sf-chip-group-label {
  display: block;
  margin-bottom: var(--ii-space-2);
  font-size: var(--ii-text-sm);
  font-weight: 600;
  color: var(--ii-text-muted);
}

/* --------------------------------------------------------------------------
   Post-publish enrichment

   Shown after the listing is already live, so everything here is visibly
   optional: the primary action is "Voir mon annonce", the suggestions are
   collapsed triggers, and "Plus tard" is a plain link.

   There is no progress bar and no completeness tick. Both are a score on the
   seller's work, and the offer disappearing when there is nothing left to ask
   says the same thing without grading anybody.
   -------------------------------------------------------------------------- */
.sf-enrich {
  background: var(--ii-bg-surface);
  border: 1px solid var(--ii-border);
  border-radius: var(--ii-radius-card);
  padding: var(--ii-space-4);
}

.sf-enrich-head { display: flex; align-items: flex-start; gap: var(--ii-space-3); }
.sf-enrich-head h2 { margin: 0; font-size: var(--ii-text-lg); font-weight: var(--ii-weight-semibold); }
.sf-enrich-sub { margin: 2px 0 0; font-size: var(--ii-text-sm); color: var(--ii-text-muted); }

/* The only tick on this screen, and it reports the publish - not a score. */
.sf-enrich-check {
  flex: 0 0 32px; width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--ii-accent-subtle); color: var(--ii-accent-text);
}
.sf-enrich-check svg { width: 18px; height: 18px; fill: currentColor; }

.sf-enrich-items { margin: var(--ii-space-4) 0; }
.sf-enrich-item + .sf-enrich-item { margin-top: var(--ii-space-2); }
.sf-enrich-item.is-saving { opacity: .6; pointer-events: none; }

/* Neutral: adding an attribute is a suggestion, not the action the screen is
   asking for - constraint 51. */
.sf-enrich-add {
  min-height: 40px; padding: 0 14px;
  border: 1px dashed var(--ii-border-strong);
  border-radius: var(--ii-radius-control);
  background: transparent; color: var(--ii-text);
  font-family: var(--ii-font-sans); font-size: var(--ii-text-sm);
  cursor: pointer;
}
.sf-enrich-add:hover { background: var(--ii-bg-sunken); }
.sf-enrich-add:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: 1px; }
.sf-enrich-add.is-open { border-style: solid; }

.sf-enrich-body { margin-top: var(--ii-space-2); }
.sf-enrich-save { margin-left: var(--ii-space-2); }

.sf-enrich-done {
  margin: 0; padding: 8px 0;
  font-size: var(--ii-text-sm); color: var(--ii-text-muted);
}
.sf-enrich-done::before { content: '✓ '; color: var(--ii-accent-text); }

.sf-enrich-later {
  display: block; width: 100%;
  margin-top: var(--ii-space-3);
  border: 0; background: none;
  text-align: center; cursor: pointer;
}

/* Group headings inside a proxied dropdown, and the tick on the chosen option.

   Type de bien is three usages, not one flat list: "Résidentiel" under a
   Terrain heading is a kind of land, while the same word in a flat list reads
   as a kind of building competing with Appartement.

   The tick matters for the same reason the tint is kept subtle - selection must
   not be carried by colour alone. */
.ii-fdrop-group {
  padding: 8px 12px 4px;
  font-size: var(--ii-text-xs, 12px);
  font-weight: var(--ii-weight-semibold);
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--ii-text-muted);
}
.ii-fdrop-group:first-child { padding-top: 4px; }

.ii-fdrop-tick { margin-right: 6px; color: var(--ii-accent-text); font-weight: var(--ii-weight-semibold); }

/* A quieter scrollbar - the list is a menu, not a document. */
.ii-fdrop-body { scrollbar-width: thin; scrollbar-color: var(--ii-border-strong) transparent; }
.ii-fdrop-body::-webkit-scrollbar { width: 8px; }
.ii-fdrop-body::-webkit-scrollbar-thumb { background: var(--ii-border-strong); border-radius: 4px; }
.ii-fdrop-body::-webkit-scrollbar-track { background: transparent; }

/* --------------------------------------------------------------------------
   One content grid, and the results row

   The page had three different left edges at 1280px, measured: breadcrumb and
   heading at 0 (their column carries pl-0), filter bar and count at 15 (normal
   Bootstrap gutter), and the visible card edge at 12 (a p-0 wrapper, a row with
   mx-xl-1, and px-2 columns). Three edges within 15px of each other read as
   sloppiness rather than as a grid.

   Everything is brought onto the container's own 15px gutter. The card grid
   gets there by cancelling exactly the 8px its columns add, so the CARD EDGE -
   not the column box - lines up with the text above it.

   Scoped to .listing-fixed-main and the filter block so the shared card
   component is untouched elsewhere (constraint 47).
   -------------------------------------------------------------------------- */
/* One gutter for the page, matching what .col-12 already resolves to at each
   breakpoint - 15px on desktop, 12px below 992px. A literal 15 here looked
   right on desktop and put the breadcrumb 3px out at 375px, which is exactly
   the kind of near-miss this block exists to remove. */
:root { --ii-page-gutter: 15px; --ii-grid-inset: 8px; }

@media (max-width: 991px) {
  /* The inset stays 8px here now that the card columns keep their padding at
     phone widths - see "The card grid needs a gutter" below. It was 0 because
     `custom.css` stripped that padding, which is also why the cards tiled edge
     to edge with no channel between them. */
  :root { --ii-page-gutter: 12px; --ii-grid-inset: 8px; }
}

/* !important because Bootstrap's spacing UTILITIES are !important - pl-0,
   p-0 and mx-xl-1 are all `... !important`, and no amount of specificity
   outranks that. Established by enumerating every rule matching these elements
   rather than assuming, the same way the .db-991 case was. */
.ii-crumbs-col.col-12.pl-0 {
  padding-left: var(--ii-page-gutter) !important;
  padding-right: var(--ii-page-gutter) !important;
}

/* `.home-fixed-main` alongside `.listing-fixed-main`: the two pages carry the
   SAME markup - a `p-0` wrapper column, a `mx-xl-1` card row, `px-2` columns -
   and only the listing page was ever given the correction. So the home page's
   white panel sat at 178 and its cards at 190 while its header, search bar and
   slider all sat at 193. Measured at 1670px.

   Doing this with the tokens rather than a Bootstrap utility in the markup is
   what makes it right at every width. `mx-n2` hard-codes 8px, and the card
   columns LOSE their px-2 below 992px - so an unconditional -8px margin pulls
   the cards 8px off the gutter on a phone, which is exactly what happened when
   this was first tried in the markup. `--ii-grid-inset` is already 8px on
   desktop and 0 below, so the cancellation follows the padding automatically.
   Constraint 21, and constraint 84 - one definition, not one per page. */
.listing-fixed-main > .col-lg-12.p-0,
.home-fixed-main > .col-lg-12.p-0,
.home-fixed-main .row > .col-lg-12.p-0 {
  padding-left: var(--ii-page-gutter) !important;
  padding-right: var(--ii-page-gutter) !important;
}

/* Cancel exactly what the card columns add, so the CARD EDGE lands on the
   gutter rather than the column box doing so. */
.listing-fixed-main .product-listing-wapper.mx-xl-1,
.home-fixed-main .product-listing-wapper.mx-xl-1 {
  margin-left: calc(var(--ii-grid-inset) * -1) !important;
  margin-right: calc(var(--ii-grid-inset) * -1) !important;
}

/* --------------------------------------------------------------------------
   The card grid needs a gutter on a phone, and had none

   `custom.css` carries, under (max-width: 991px):

     .col-xl-3.col-6.px-2.profile-listing-box {
       padding-left: unset !important; padding-right: unset !important; }

   so the shared card column lost its px-2 at exactly the width where the grid
   goes to two across. Measured at 375px on the home page: card 1 ended at
   x=187.5 and card 2 began at x=187.5; row 1 ended at y=737.7 and row 2 began
   at y=737.7. Zero separation on BOTH axes, which is why the grid read as one
   continuous sheet of photographs rather than as separate ads.

   The card is separated by ELEVATION rather than a hairline - a deliberate
   choice recorded on `.ii-card`, because a border was the only thing dividing
   a white card from a white page. But a shadow between two touching cards has
   nowhere to fall, so the choice could not do its job. Space is the fix, and
   the elevation then reads as intended.

   8px per column against the row's -8px gives a 16px channel between cards
   and keeps the CARD EDGE on the page gutter. That is not an invented number:
   desktop already measures 16px between cards (557.7 -> 573.7 at 1280px), so
   this restores parity rather than introducing a second grid. Constraint 21 -
   the page grid is one measurement.

   Specificity: the custom.css rule is (0,4,0) and `!important`, so this is
   written at (0,5,0) with `!important` rather than relying on load order.
   Constraint 20 - the spacing utilities cannot be beaten by specificity
   alone, and §5.1 - specificity, not load order.
   -------------------------------------------------------------------------- */
@media (max-width: 991px) {
  /* Anchored on the COLUMN, not on a row class. The column is the shared
     thing - the same `.col-6.col-md-4.col-xl-3.px-2.profile-listing-box`
     appears under two different row classes, and scoping this to one of them
     fixed the home and listing grids while leaving the seller profile
     untouched. Written at (0,5,0) to clear the custom.css rule's (0,4,0)
     rather than relying on load order. */
  .col-md-4.col-xl-3.col-6.px-2.profile-listing-box {
    padding-left: var(--ii-grid-inset) !important;
    padding-right: var(--ii-grid-inset) !important;
    /* Vertical rhythm to match the horizontal channel. margin-bottom rather
       than the row's `row-gap`: flexbox gap is unsupported on Safari below
       14.1, and this audience is not on new hardware. */
    margin-bottom: var(--ii-space-4);
  }

  /* Each row cancels exactly what its columns now add, so the OUTER edge of
     the grid does not move - only the channel between the cards appears.
     The two rows start from different base margins and must therefore be
     written separately:

       .listing-card-block          0 at this width (mx-xl-1 is xl-only)
       .profile-listing-box-wapper  Bootstrap's default -15px

     The profile row pulls back the ancestor inset this page actually has,
     which is NOT the 15px a Bootstrap container would give it. */
  .listing-card-block {
    margin-left: calc(var(--ii-grid-inset) * -1) !important;
    margin-right: calc(var(--ii-grid-inset) * -1) !important;
  }

  /* 22px, not the 23px that `-15px - var(--ii-grid-inset)` produced. The
     `-15px` term assumes a Bootstrap container's padding, and there is none
     here: `.container-fluid` measures 0 on this page below 992px, and the
     row's ancestor inset is 12px of page gutter plus the 10px padding on
     `.profile-top-wapper`. So -23px overshot by exactly 1px, putting the
     row's own box at x=-1 and x=vw+1 at every phone width - measured 392
     against a 390 viewport, 377 against 375.

     Nothing was visibly wrong, because the row is transparent and the cards
     sat inside it either way. What that 1px produced was a horizontally
     scrollable document, which on a touch screen is a page that swipes
     sideways - and on the four `ii-allow-sticky` pages there is deliberately
     no `overflow-x: hidden` left to mask it (see the note at the sticky
     block). Cards move from x=7 to x=8, 1px nearer this page's own 12px
     gutter and 1px less of the pre-existing misalignment §1.32 left alone. */
  .profile-listing-box-wapper {
    margin-left: calc(-1 * var(--ii-page-gutter) - 10px) !important;
    margin-right: calc(-1 * var(--ii-page-gutter) - 10px) !important;
  }
}

/* Vertical rhythm. The whole point of step 8B is that inventory appears
   quickly, so this block is deliberately tight: breadcrumb and heading belong
   together, and the gap before the cards is the smallest of the lot. */
.ii-crumbs { margin-bottom: 6px; }
h1.main-listing-title.ii-results-title { margin-bottom: 18px; }
.ii-fbar { margin-bottom: 0; }
.ii-filter-bar > .ii-chips.has-chips { margin-top: 16px; }

/* --------------------------------------------------------------------------
   Results row

   "59 annonces" and the sort control, on one line directly above the grid.
   Sort is not a filter - it always has a value and narrows nothing - so it
   belongs with the result it orders rather than among the controls that
   produce it.
   -------------------------------------------------------------------------- */
.ii-results-row {
  position: relative;                 /* the dropdown anchors to this row */
  /* .ii-filter-bar is itself a flex container, so a flex ITEM sizes to its
     content - this row came out 290px wide inside a 1235px parent and the sort
     control sat next to the count instead of opposite it. Same trap .ii-fbar
     already had to be told about. */
  width: 100%; min-width: 0;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--ii-space-3);
  /* .ii-filter-bar is a wrapping flex row with a 12px gap, and that gap sits
     above this row as well - so 8 here reads as 20 on screen. Measured rather
     than assumed; the number in this file is not the number on the page. */
  margin-top: 8px; margin-bottom: 14px;
}

.ii-result-count {
  margin: 0; padding: 0;
  font-size: var(--ii-text-lg);
  font-weight: var(--ii-weight-semibold);
  color: var(--ii-text);
}

.ii-sort-btn {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 36px; padding: 0 10px;
  border: 1px solid transparent; border-radius: var(--ii-radius-control);
  background: transparent; color: var(--ii-text);
  font-family: var(--ii-font-sans); font-size: var(--ii-text-sm);
  cursor: pointer; white-space: nowrap;
}
.ii-sort-btn:hover { border-color: var(--ii-border); background: var(--ii-bg-surface); }
.ii-sort-btn:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: 1px; }
.ii-sort-btn[aria-expanded="true"] { border-color: var(--ii-border-strong); background: var(--ii-bg-surface); }
.ii-sort-btn svg { width: 16px; height: 16px; fill: currentColor; flex: 0 0 16px; }

.ii-sort-label { color: var(--ii-text-muted); }
.ii-sort-value { font-weight: var(--ii-weight-semibold); }

@media (max-width: 991px) {
  .ii-sort-btn { min-height: 44px; }
  /* The label is the first thing to go when width is short; the value still
     says which order is applied. */
  .ii-sort-label { display: none; }
}

/* The dropdown opens upward when there is no room below it - a 21-option
   quartier list opened from a control near the bottom of a phone viewport
   would otherwise run off the screen with no way to reach its last entries. */
.ii-fdrop.ii-fdrop-up { top: auto; bottom: calc(100% + 4px); }

/* Focus inside the menu. :focus-visible alone is not enough here: options are
   reached with arrow keys, which do not always set the :focus-visible heuristic
   the way Tab does, so both are styled. */
.ii-fdrop-opt:focus,
.ii-fmore-chip:focus { outline: 2px solid var(--ii-focus); outline-offset: -2px; }

/* --------------------------------------------------------------------------
   Similar-listings rail

   A horizontal scroller for the ad cards under a listing. Native scrolling
   with scroll-snap rather than a carousel plugin, because the plugin was the
   problem: the section ran Owl over a Bootstrap grid, and since Owl wraps
   every child in a fixed-width .owl-item, the col-* classes inside then
   resolved against that slot instead of against the page. Measured, a 306px
   slot held a 77px card on desktop and a 150px slot held a 75px card at
   375px - the rest was the empty space the section was reported for.

   The card keeps its own markup and its own CSS. The rail decides only how
   many fit and how far one press of an arrow travels.

   Named `cardrail` rather than `rail` because `.ii-rail` is already taken by
   the category shortcut row (partials/category_rail.blade.php), whose
   .ii-rail-item is a 76px column with display:flex. Sharing the name meant
   the card inherited that flex and stopped filling its slot - measured, 16 of
   20 cards came out content-sized at 300-323px inside identical 323px slots.
   -------------------------------------------------------------------------- */
.ii-cardrail-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ii-space-4);
  margin-bottom: var(--ii-space-4);
}

/* 30px centred previously. Left-aligned and smaller so the heading and its
   controls read as one component rather than as a banner with a widget
   underneath it. */
.ii-cardrail-title {
  margin: 0;
  font-size: var(--ii-text-xl);
  font-weight: var(--ii-weight-bold);
  line-height: var(--ii-leading-display);
  color: var(--ii-text);
}
@media (min-width: 768px) {
  .ii-cardrail-title { font-size: var(--ii-text-2xl); }
}

/* Drawn only once the rail is known to overflow, and only where there is a
   pointer to use it. Below 992px the interaction is the swipe, and a pair of
   44px buttons would spend a row of vertical space duplicating a gesture the
   reader already has. */
.ii-cardrail-nav { display: none; gap: var(--ii-space-2); flex: 0 0 auto; }
@media (min-width: 992px) {
  .ii-cardrail-nav.is-ready { display: flex; }
}

.ii-cardrail-btn {
  width: 44px; height: 44px;
  flex: 0 0 44px;
  display: flex; align-items: center; justify-content: center;
  padding: 0;
  border: 1px solid var(--ii-border);
  border-radius: 50%;
  background: var(--ii-bg-surface);
  color: var(--ii-text-secondary);
  box-shadow: var(--ii-shadow-sm);
  cursor: pointer;
  transition: box-shadow var(--ii-dur-fast) var(--ii-ease),
              color var(--ii-dur-fast) var(--ii-ease),
              border-color var(--ii-dur-fast) var(--ii-ease);
}
.ii-cardrail-btn svg { width: 18px; height: 18px; fill: currentColor; }
.ii-cardrail-btn:hover {
  color: var(--ii-text);
  border-color: var(--ii-border-strong);
  box-shadow: var(--ii-shadow-md);
}
.ii-cardrail-btn:focus-visible { outline: 2px solid var(--ii-focus); outline-offset: 2px; }

/* Disabled rather than removed: a control that disappears at the end of the
   run shifts the other one sideways, and the reader loses the target they
   were already aiming at. */
.ii-cardrail-btn[disabled] { opacity: .38; cursor: default; box-shadow: none; }
.ii-cardrail-btn[disabled]:hover {
  color: var(--ii-text-secondary);
  border-color: var(--ii-border);
  box-shadow: none;
}

.ii-cardrail {
  --rail-gap: var(--ii-space-4);
  /* The sliver of the next card. On a phone it is the only thing saying the
     row continues: the scrollbar is hidden and there are no arrows there. */
  --rail-peek: 28px;
  --rail-count: 2;

  display: flex;
  gap: var(--rail-gap);
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  /* Stops a swipe that runs past the end from becoming a page scroll or a
     browser back-gesture. */
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  /* Vertical room for the card shadow and its -2px hover lift, which a
     scroller with no padding would clip. Deliberately none on the sides: a
     horizontal pad becomes the resting scroll offset, so the rail would never
     read as being at position zero, and the first card would no longer line
     up with the page container above it. */
  padding: 6px 0 18px;
  scrollbar-width: none;
}
.ii-cardrail::-webkit-scrollbar { display: none; }

/* Focusable so the arrow keys can scroll it, which also makes it a target for
   the focus ring. */
.ii-cardrail:focus-visible {
  outline: 2px solid var(--ii-focus);
  outline-offset: 2px;
  border-radius: var(--ii-radius-card);
}

/* Two on a phone, matching the results grid the reader arrived from - a card
   that changes size between the grid and the ad below it reads as a different
   kind of object. Three from 768px, four from 1200px, which is the same
   progression as col-6 / col-md-4 / col-xl-3. */
.ii-cardrail-item {
  flex: 0 0 calc(
    (100% - (var(--rail-count) - 1) * var(--rail-gap) - var(--rail-peek))
    / var(--rail-count)
  );
  scroll-snap-align: start;
}

@media (min-width: 768px)  { .ii-cardrail { --rail-count: 3; } }
@media (min-width: 1200px) { .ii-cardrail { --rail-count: 4; } }

/* --------------------------------------------------------------------------
   The page surface, taken off the templates

   .box-container carried `style="background: #ffffff; border-radius: 8px"`
   inline on five views, which no stylesheet can outrank - a third layer under
   the two the dark-mode review named: tokens, then 518 hard-coded values in
   style.css, then inline attributes. One of the five spelled the property
   `bacbackground`, so it had never applied at all and nobody had noticed.

   Unscoped, so light mode keeps exactly the white panel it always had; the
   dark block below overrides it. Both halves matter - removing the inline
   style without this left the container transparent with square corners.
   -------------------------------------------------------------------------- */
.box-container {
  background-color: var(--ii-bg-surface);
  border-radius: 8px;
}

/* --------------------------------------------------------------------------
   Legacy surfaces in dark mode

   A dark-mode review found white panels floating on a dark page and concluded
   there was no token layer. There is one - directly above this - and it works.
   What it never covered is the components that predate it: style.css carries
   518 hard-coded light values and not one reference to a colour scheme, and it
   loads BEFORE this file, so every surface it owns stayed light while the page
   around it went dark.

   The list below is measured, not guessed: [data-theme="dark"] was forced on
   the home, category and ad pages and every box over 60x20 was asked what
   background it computed. Everything here came back light. Re-run that
   measurement before adding to this list, and after changing it.

   Two deliberate choices.

   SCOPED TO DARK. The obvious fix is one token-based rule per component,
   serving both themes at once. That was tried first and it loses: the legacy
   rules win on specificity, and matching 518 of them one at a time is not a
   game worth playing. Dark-only means light mode is provably untouched, which
   also settles the cases where the two disagree - .orange-border-btn is
   transparent in light mode, not white, and stays that way.

   !important, and not as decoration. These override a 12k-line sheet that
   cannot be rewritten safely. The alternative is escalating selector
   specificity, which is the same weapon with worse aim and no boundary. Here
   it is confined to one block that says why.

   Written twice, once per dark scope, because CSS cannot OR a media query with
   a selector - the same reason the semantic block above is duplicated. Keep
   the two in step.
   -------------------------------------------------------------------------- */


/* ---------- the same, for the explicit choice ---------- */

:root[data-theme="dark"] .mm-menu,
:root[data-theme="dark"] .mm-panels,
:root[data-theme="dark"] .mm-panels > .mm-panel {
  background-color: var(--ii-bg-surface) !important;
  color: var(--ii-text) !important;
}
:root[data-theme="dark"] .mm-navbar {
  background-color: var(--ii-bg-sunken) !important;
  border-bottom: 1px solid var(--ii-border) !important;
}
:root[data-theme="dark"] .mm-navbar .mm-navbar__title,
:root[data-theme="dark"] .mm-navbar .mm-navbar-title,
:root[data-theme="dark"] .mm-navbar a,
:root[data-theme="dark"] .mm-listitem > a,
:root[data-theme="dark"] .mm-listitem > span {
  color: var(--ii-text) !important;
}
:root[data-theme="dark"] .mm-listitem,
:root[data-theme="dark"] .mm-listitem__btn {
  border-color: var(--ii-border) !important;
}

:root[data-theme="dark"] .box-container {
  background-color: var(--ii-bg-canvas) !important;
}
:root[data-theme="dark"] .sidebar_listing_list {
  background-color: var(--ii-bg-surface) !important;
  border-color: var(--ii-border) !important;
}

:root[data-theme="dark"] .custom-sticky-section-2 {
  background-color: var(--ii-bg-surface) !important;
  border-bottom: 1px solid var(--ii-border) !important;
}

:root[data-theme="dark"] .input-group.highlight-on-focus,
:root[data-theme="dark"] .form-control,
:root[data-theme="dark"] .btn.dropdown-toggle,
:root[data-theme="dark"] .bootstrap-select > .dropdown-toggle,
:root[data-theme="dark"] .dropdown-menu {
  background-color: var(--ii-bg-surface) !important;
  color: var(--ii-text) !important;
  border-color: var(--ii-border-strong) !important;
}
:root[data-theme="dark"] .form-control::placeholder {
  color: var(--ii-text-muted) !important;
}
:root[data-theme="dark"] .dropdown-menu a,
:root[data-theme="dark"] .dropdown-menu li a {
  color: var(--ii-text) !important;
}

:root[data-theme="dark"] .orange-border-btn {
  background-color: var(--ii-bg-surface) !important;
  color: var(--ii-accent-text) !important;
  border-color: var(--ii-accent) !important;
}
:root[data-theme="dark"] .ii-cta {
  background-color: var(--ii-bg-sunken) !important;
}

/* --------------------------------------------------------------------------
   Category drawer scrim

   mmenu is initialised with the "pagedim-black" extension, which is meant to
   dim the page behind the open drawer. That extension is a no-op in this
   build: `pagedim` appears in no stylesheet the page loads, and not even in
   the mmenu bundle itself. So the drawer opened over a page at full
   brightness and read as a panel stuck to the side rather than as a layer
   above it - which is what a design review reported.

   The blocker element is real and already there: mmenu inserts
   .mm-wrapper__blocker at full viewport size and it is what closes the drawer
   when you tap beside it. It was simply invisible - measured background
   rgba(3, 2, 1, 0), mmenu's own colour at zero alpha, waiting for the CSS that
   never arrived. This gives it the alpha rather than adding an element.
   -------------------------------------------------------------------------- */
.mm-wrapper_opened .mm-wrapper__blocker {
  background-color: rgba(0, 0, 0, .55);
  transition: background-color 220ms var(--ii-ease);
}

@media (prefers-reduced-motion: reduce) {
  .mm-wrapper_opened .mm-wrapper__blocker { transition: none; }
}

/* --------------------------------------------------------------------------
   Tap targets below the floor

   The product sets 44px deliberately - .ii-card-fav carries the WCAG 2.5.5
   reference in its own comment - but only the components rebuilt since then
   observe it. Swept the home and ad pages for anything interactive smaller
   than that and these came back: both carousel arrows at 40x40, the drawer
   close at 33 wide, the ad page favourite and report chips at 35x35, and the
   description toggle at 36 tall.

   Inline text links are deliberately NOT in that list. 2.5.5 exempts text in
   a sentence, and padding "Se connecter" out to 44px would push the header
   apart to satisfy a rule that does not apply to it.

   All of them are resized rather than padded out with an invisible hit area.
   The pseudo-element trick .ii-card-fav uses was tried first for the two chips
   on the ad page and does not work there: hit-testing eight points around a
   44px box centred on each chip returned 0 of 8, so the expanded area is
   clipped or covered by a neighbour. A hit area that does not take hits is
   worse than none, because it looks handled.
   -------------------------------------------------------------------------- */
.banner-card-slider .owl-prev,
.banner-card-slider .owl-next,
.listing_single_property_slider .owl-prev,
.listing_single_property_slider .owl-next {
  min-width: 44px; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
}

.mm-close-btn { min-width: 44px; }

.read-more-btn { min-height: 44px; }

.favoutite-heart,
.report_listing_modal {
  min-width: 44px; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
}

/* --------------------------------------------------------------------------
   Profile › Annonces toolbar

   A design review reported the search field clipped, its magnifier overlapping
   the sort control, and the status chips cut off at the right edge. Measured
   at a 500px viewport, the row holds a 221px search block and an 83px filter
   block inside a 485px bar, and the sort select inside the filter panel
   renders 74px wide around a 151px label - so the applied order is truncated
   to about half of itself and there is nothing to say the rest exists.

   Rather than shave the controls to fit each other, each gets its own line at
   phone widths. Vertical space is the cheap axis on a phone; horizontal space
   is the one that runs out.
   -------------------------------------------------------------------------- */
@media (max-width: 991px) {
  .tab-content-head {
    flex-wrap: wrap;
    gap: var(--ii-space-2);
  }
  .tab-content-head .right-side { flex: 1 1 100%; }
  .tab-content-head .right-side .search-block { width: 100%; }
  .tab-content-head .right-side .search-block form,
  .tab-content-head .right-side .search-block .input-group { width: 100%; }

  .tab-content-head .right-caption {
    flex: 1 1 100%;
    justify-content: flex-end;
    margin-bottom: 0;
  }

  /* Inside the filter panel: the select was sized by whatever room was left
     rather than by its own longest label. */
  .listing-filter-caption .sorting-block,
  .listing-filter-caption .search_option_two,
  .listing-filter-caption .candidate_revew_select,
  .listing-filter-caption .bootstrap-select {
    width: 100% !important;
  }
  .listing-filter-caption .bootstrap-select > .dropdown-toggle { width: 100%; }
}

/* --------------------------------------------------------------------------
   Account form: field widths and scroll anchoring
   -------------------------------------------------------------------------- */

/* REMOVED 2026-08-28. This capped the email, region and phone controls at
   320px on desktop so "short fields stop growing with the card". The approved
   mockup (docs/mockups/iibiy-mon-compte.html) reverses that decision
   explicitly: no px width on any input, every control 100% of its row, and the
   row width owned by the card. Two fields ending 234px short of the other
   three is precisely the misalignment this restyle was asked to fix.

   Both selectors it used, .ii-acct-field and .ii-acct-inputwrap, appear only
   in account.blade.php - checked before deleting, per the brief. */

/* Anchor scrolling below any fixed chrome. Nothing in this project set
   scroll-padding, so an anchor jump or a focus-driven scroll put the target
   flush against the top of the viewport - underneath whatever was pinned
   there. Costs nothing when nothing is pinned. */
:root { scroll-padding-top: 72px; }

/* Initials in place of the placeholder disc. Same box as .user-img so the
   layout does not shift between an account with a photo and one without. */
.ii-initials {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ii-accent-subtle, #FFF4EC);
  color: var(--ii-accent-text);
  font-weight: var(--ii-weight-bold);
  letter-spacing: .02em;
  border-radius: 50%;
  overflow: hidden;
  user-select: none;
}

/* The size is written at (0,2,1), and that is the whole point.

   This rule used to say `font-size: 28px` on `.ii-initials` alone, which is
   (0,1,0) - and `style.css` carries `.media-head-left a, .media-head-left
   span, .media-head-left p { font-size: 16px }` at (0,1,1). The initials are a
   <span> inside `.media-head-left`, so the legacy rule won and they rendered
   at 16px in a 70px circle. The 28px was never applied to anything: it was
   both off the type scale AND inert, which is the harder of the two to notice
   because nothing on screen matched the value you would go looking for.

   §5.1 - the legacy theme wins on specificity, not load order. Beat it with a
   longer selector rather than `!important`.

   24px in the 70px circle and 32px in the 100px desktop one keep the letters
   at roughly a third of the disc at both sizes, and both are on the scale
   (constraint 87). Verified against two-letter initials, which is the widest
   case the generator produces - it takes one letter each from f_name and
   l_name and nothing more. */
.media-head-left .ii-initials { font-size: var(--ii-text-2xl); }

@media (min-width: 992px) {
  /* The disc itself goes 70px -> 100px at this breakpoint. */
  .media-head-left .ii-initials { font-size: var(--ii-text-3xl); }
}

/* The seller's name.

   It carried `style="font-size: 16px!important"` in the markup - an inline
   `!important`, which is the single highest-priority declaration CSS has and
   which no stylesheet can reach. 16px is not on the scale, so the one size no
   rule could correct was also the one that was wrong.

   It made a second rule dead on arrival: `accountSidepanel.blade.php` declared
   `.fsname { font-size: 17px !important }` for >=992px, and the inline
   `!important` outranked it at every width. So the desktop size had ALREADY
   been chosen as 17px and had never once rendered. This adopts that intent at
   both widths rather than inventing a value - the h1 is the primary identity
   on the profile, and 17px is the scale's step above body without becoming a
   headline in a card this compact. Constraint 87. */
h1.fsname { font-size: var(--ii-text-lg); }

/* "Membre depuis" and its date, which are two <p> elements for one fact.

   Found by sweeping the card rather than by being told about it: of the six
   text nodes this card actually paints, four were on the scale after the two
   fixes above and these two were not. Same cause as the initials - the legacy
   `.media-head-left p { font-size: 16px }` - so they are the same defect, one
   selector over, and fixing the two that were reported while leaving these
   would put the card back out of step the moment anyone looked at it.

   13px matches `.ii-verified`, which sits directly above them in this same
   card. Worth knowing: `seller_trust.blade.php` states this identical fact as
   `.ii-trust .ii-member-since` at 12px muted, so the product describes "member
   since" in two components with two treatments. Matching the immediate
   neighbour is the closer relationship of the two; the wider question of
   whether these should be ONE component is not a font-size change. */
.media-head-left p.mb-0 { font-size: var(--ii-text-sm); }

/* --------------------------------------------------------------------------
   Email: editable until verified

   The input and its Verifier button share a row, the same shape the phone
   field already uses for [+253][number][Modifier]. Below 560px the button
   drops to its own line rather than squeezing the field, which is where the
   phone row's own label stopped fitting.
   -------------------------------------------------------------------------- */
.ii-acct-emailrow {
  display: flex;
  align-items: stretch;
  gap: var(--ii-space-2);
  flex-wrap: wrap;
}
.ii-acct-emailrow .ii-acct-inputwrap { flex: 1 1 220px; min-width: 0; }
.ii-acct-verify-btn { flex: 0 0 auto; white-space: nowrap; }

@media (max-width: 559px) {
  .ii-acct-verify-btn { flex: 1 1 100%; }
}

/* The line under the field that says what the button will do. */
.ii-acct-hint {
  display: block;
  margin-top: var(--ii-space-1);
  font-size: 12px;
  color: var(--ii-text-muted);
}

/* Low-resolution notice on the sell flow's photo step. Amber, not red: this is
   advice the seller can act on or ignore, and nothing is being refused. */
.sf-lowres {
  display: flex;
  gap: var(--ii-space-2);
  margin: var(--ii-space-3) 0 0;
  padding: var(--ii-space-3);
  border-left: 3px solid var(--ii-warning, #E8B23A);
  border-radius: var(--ii-radius-control, 8px);
  background: var(--ii-bg-sunken);
  color: var(--ii-text-secondary);
  font-size: var(--ii-text-sm);
  line-height: var(--ii-leading-body);
}
.sf-lowres[hidden] { display: none; }

/* ---------- legacy surfaces: dark (system preference) ----------
   The same overrides as the attribute-scoped block above. Keep in step. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .mm-menu,
    :root:not([data-theme="light"]) .mm-panels,
    :root:not([data-theme="light"]) .mm-panels > .mm-panel {
    background-color: var(--ii-bg-surface) !important;
    color: var(--ii-text) !important;
}
  :root:not([data-theme="light"]) .mm-navbar {
    background-color: var(--ii-bg-sunken) !important;
    border-bottom: 1px solid var(--ii-border) !important;
}
  :root:not([data-theme="light"]) .mm-navbar .mm-navbar__title,
    :root:not([data-theme="light"]) .mm-navbar .mm-navbar-title,
    :root:not([data-theme="light"]) .mm-navbar a,
    :root:not([data-theme="light"]) .mm-listitem > a,
    :root:not([data-theme="light"]) .mm-listitem > span {
    color: var(--ii-text) !important;
}
  :root:not([data-theme="light"]) .mm-listitem,
    :root:not([data-theme="light"]) .mm-listitem__btn {
    border-color: var(--ii-border) !important;
}
  :root:not([data-theme="light"]) .box-container {
    background-color: var(--ii-bg-canvas) !important;
}
  :root:not([data-theme="light"]) .sidebar_listing_list {
    background-color: var(--ii-bg-surface) !important;
    border-color: var(--ii-border) !important;
}
  :root:not([data-theme="light"]) .custom-sticky-section-2 {
    background-color: var(--ii-bg-surface) !important;
    border-bottom: 1px solid var(--ii-border) !important;
}
  :root:not([data-theme="light"]) .input-group.highlight-on-focus,
    :root:not([data-theme="light"]) .form-control,
    :root:not([data-theme="light"]) .btn.dropdown-toggle,
    :root:not([data-theme="light"]) .bootstrap-select > .dropdown-toggle,
    :root:not([data-theme="light"]) .dropdown-menu {
    background-color: var(--ii-bg-surface) !important;
    color: var(--ii-text) !important;
    border-color: var(--ii-border-strong) !important;
}
  :root:not([data-theme="light"]) .form-control::placeholder {
    color: var(--ii-text-muted) !important;
}
  :root:not([data-theme="light"]) .dropdown-menu a,
    :root:not([data-theme="light"]) .dropdown-menu li a {
    color: var(--ii-text) !important;
}
  :root:not([data-theme="light"]) .orange-border-btn {
    background-color: var(--ii-bg-surface) !important;
    color: var(--ii-accent-text) !important;
    border-color: var(--ii-accent) !important;
}
  :root:not([data-theme="light"]) .ii-cta {
    background-color: var(--ii-bg-sunken) !important;
}
}

/* --------------------------------------------------------------------------
   Dark mode, round two: the things the first pass did not reach

   An accessibility audit found four classes of problem that the surface
   conversion missed, all of them measured rather than eyeballed:

   1. Six legacy rules force `color: #000 !important` inside the account,
      wallet and favourites areas (style.css 3776, 5563, 5603, 5760, 5783,
      5858). Black on #17181A is 1.18:1 - not low contrast, unreadable. They
      carry !important, so nothing without it can win.

   2. The desktop header, the tab pills and the footer were never converted.
      The footer's navy #242D45 sits at 1.40:1 against the canvas and belongs
      to neither theme.

   3. Elevation came from box-shadow, which is invisible on a near-black
      page, so sheets, dropdowns, modals and toasts all sat at exactly the
      same apparent depth as the cards behind them. Dark elevation is a
      lighter surface plus a hairline; the shadow is dropped, not kept as
      well.

   4. Control borders at 10% white composite to #2E2F31 - 1.33:1. WCAG
      1.4.11 asks 3:1 where the border IS the affordance, which on an input
      it is. --ii-border-control is .34 white, 3.13:1, and is used only on
      controls; dividers stay quiet at .10.

   Generated from one list into both scopes, because CSS cannot OR a media
   query with a selector and writing them twice by hand is how they drift.
   -------------------------------------------------------------------------- */

:root[data-theme="dark"] .border-btn,
:root[data-theme="dark"] .insight-btn,
:root[data-theme="dark"] .mark-btn-block button,
:root[data-theme="dark"] .withdraw-btn button:hover {
  color: var(--ii-text) !important;
}
:root[data-theme="dark"] .tab-content-body .favourite-icon li p,
:root[data-theme="dark"] .wallet-item-left p {
  color: var(--ii-text-secondary) !important;
}
:root[data-theme="dark"] header.header-nav,
:root[data-theme="dark"] header.header-nav.stricky,
:root[data-theme="dark"] .header-nav.main-menu {
  background-color: var(--ii-bg-surface) !important;
  border-bottom: 1px solid var(--ii-border) !important;
}
:root[data-theme="dark"] .nav-pills .nav-link,
:root[data-theme="dark"] .sign_up_tab li.nav-item .nav-link {
  background-color: var(--ii-bg-sunken) !important;
  color: var(--ii-text) !important;
}
:root[data-theme="dark"] .nav-pills .nav-link.active,
:root[data-theme="dark"] .sign_up_tab li.nav-item .nav-link.active {
  background-color: var(--ii-accent) !important;
  color: var(--ii-text-on-accent) !important;
}
:root[data-theme="dark"] .footer_one {
  background-color: var(--ii-bg-surface) !important;
  border-top: 1px solid var(--ii-border) !important;
}
:root[data-theme="dark"] .ii-fdrop,
:root[data-theme="dark"] .dropdown-menu,
:root[data-theme="dark"] .ii-sheet,
:root[data-theme="dark"] .filter-block,
:root[data-theme="dark"] .modal-content,
:root[data-theme="dark"] .listing-filter-caption,
:root[data-theme="dark"] #toast,
:root[data-theme="dark"] .jq-toast-single {
  background-color: var(--ii-bg-raised) !important;
  border: 1px solid var(--ii-border) !important;
  box-shadow: none !important;
}
:root[data-theme="dark"] .form-control,
:root[data-theme="dark"] .ii-acct-phone-input,
:root[data-theme="dark"] .btn.dropdown-toggle,
:root[data-theme="dark"] .bootstrap-select > .dropdown-toggle,
:root[data-theme="dark"] .ii-acct-btn-ghost,
:root[data-theme="dark"] .ii-cardrail-btn,
:root[data-theme="dark"] .ii-fbar-btn,
:root[data-theme="dark"] .ii-fbar-more,
:root[data-theme="dark"] .ii-sort-btn {
  border-color: var(--ii-border-control) !important;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .border-btn,
  :root:not([data-theme="light"]) .insight-btn,
  :root:not([data-theme="light"]) .mark-btn-block button,
  :root:not([data-theme="light"]) .withdraw-btn button:hover {
    color: var(--ii-text) !important;
  }
  :root:not([data-theme="light"]) .tab-content-body .favourite-icon li p,
  :root:not([data-theme="light"]) .wallet-item-left p {
    color: var(--ii-text-secondary) !important;
  }
  :root:not([data-theme="light"]) header.header-nav,
  :root:not([data-theme="light"]) header.header-nav.stricky,
  :root:not([data-theme="light"]) .header-nav.main-menu {
    background-color: var(--ii-bg-surface) !important;
    border-bottom: 1px solid var(--ii-border) !important;
  }
  :root:not([data-theme="light"]) .nav-pills .nav-link,
  :root:not([data-theme="light"]) .sign_up_tab li.nav-item .nav-link {
    background-color: var(--ii-bg-sunken) !important;
    color: var(--ii-text) !important;
  }
  :root:not([data-theme="light"]) .nav-pills .nav-link.active,
  :root:not([data-theme="light"]) .sign_up_tab li.nav-item .nav-link.active {
    background-color: var(--ii-accent) !important;
    /* --ii-text here was #F5F5F4 on the accent: 2.37:1, and this is the scope
       that actually ships, since nothing sets [data-theme]. The attribute copy
       had --ii-text-on-accent all along, which is why it never showed up.
       Constraint 71 - ink on a coloured surface, never white. */
    color: var(--ii-text-on-accent) !important;
  }
  :root:not([data-theme="light"]) .footer_one {
    background-color: var(--ii-bg-surface) !important;
    border-top: 1px solid var(--ii-border) !important;
  }
  :root:not([data-theme="light"]) .ii-fdrop,
  :root:not([data-theme="light"]) .dropdown-menu,
  :root:not([data-theme="light"]) .ii-sheet,
  :root:not([data-theme="light"]) .filter-block,
  :root:not([data-theme="light"]) .modal-content,
  :root:not([data-theme="light"]) .listing-filter-caption,
  :root:not([data-theme="light"]) #toast,
  :root:not([data-theme="light"]) .jq-toast-single {
    background-color: var(--ii-bg-raised) !important;
    border: 1px solid var(--ii-border) !important;
    box-shadow: none !important;
  }
  :root:not([data-theme="light"]) .form-control,
  :root:not([data-theme="light"]) .ii-acct-phone-input,
  :root:not([data-theme="light"]) .btn.dropdown-toggle,
  :root:not([data-theme="light"]) .bootstrap-select > .dropdown-toggle,
  :root:not([data-theme="light"]) .ii-acct-btn-ghost,
  :root:not([data-theme="light"]) .ii-cardrail-btn,
  :root:not([data-theme="light"]) .ii-fbar-btn,
  :root:not([data-theme="light"]) .ii-fbar-more,
  :root:not([data-theme="light"]) .ii-sort-btn {
    border-color: var(--ii-border-control) !important;
  }
}

/* --------------------------------------------------------------------------
   Dark mode: let text inherit its container again

   The surface conversion themed the boxes; the text inside several of them
   stayed dark because style.css colours it by ELEMENT, not by component:

     style.css:155   p { color: #484848 }
     style.css:173   a { ... }
     style.css:15503 .media-head-left a, span, p { color: #000 }

   On the account panel that produced black on #17181A - 1.18:1, which is not
   low contrast, it is invisible - while the parent element was correctly
   themed at #F5F5F4. The text was overriding a container that already had the
   right answer, so the fix is to stop it overriding rather than to pick a new
   colour: `inherit` takes whatever the themed ancestor resolved to.

   :where() is doing real work here. It contributes ZERO specificity, so
   `:where(:root[data-theme="dark"]) p` is exactly as specific as the `p {}` it
   is correcting - later in the cascade, so it wins - while any component class
   (.ii-card-price, .ii-acct-flabel) is (0,1,0) and still beats BOTH. Without
   :where() the scope would add two classes worth of weight and this would
   flatten every deliberate colour on the page.
   -------------------------------------------------------------------------- */

:where(:root[data-theme="dark"]) p,
:where(:root[data-theme="dark"]) a,
:where(:root[data-theme="dark"]) span,
:where(:root[data-theme="dark"]) li,
:where(:root[data-theme="dark"]) label,
:where(:root[data-theme="dark"]) button,
:where(:root[data-theme="dark"]) h1,
:where(:root[data-theme="dark"]) h2,
:where(:root[data-theme="dark"]) h3,
:where(:root[data-theme="dark"]) h4,
:where(:root[data-theme="dark"]) h5,
:where(:root[data-theme="dark"]) h6 {
  color: inherit;
}

:root[data-theme="dark"] .media-head-left a,
:root[data-theme="dark"] .media-head-left span,
:root[data-theme="dark"] .media-head-left p {
  color: inherit;
}

@media (prefers-color-scheme: dark) {
  :where(:root:not([data-theme="light"])) p,
  :where(:root:not([data-theme="light"])) a,
  :where(:root:not([data-theme="light"])) span,
  :where(:root:not([data-theme="light"])) li,
  :where(:root:not([data-theme="light"])) label,
  :where(:root:not([data-theme="light"])) button,
  :where(:root:not([data-theme="light"])) h1,
  :where(:root:not([data-theme="light"])) h2,
  :where(:root:not([data-theme="light"])) h3,
  :where(:root:not([data-theme="light"])) h4,
  :where(:root:not([data-theme="light"])) h5,
  :where(:root:not([data-theme="light"])) h6 {
    color: inherit;
  }

  :root:not([data-theme="light"]) .media-head-left a,
  :root:not([data-theme="light"]) .media-head-left span,
  :root:not([data-theme="light"]) .media-head-left p {
    color: inherit;
  }
}

/* --------------------------------------------------------------------------
   Three traced overrides

   Found by walking the CSSOM for each element that still failed contrast,
   rather than by guessing at selectors:

     style.css  .profile-top-wapper button { color: black !important }
       Every button in the account panel - Annuler, Modifier, Mettre a jour -
       came out 1.18:1. !important, so .ii-acct-btn-ghost's own colour never
       had a chance. Answered in kind, which is the only thing that beats it.

     style.css  .breadcrumb-item a { color: #000 }
       "Accueil" in the breadcrumb, 1.09:1. (0,1,1), so the element-level
       :where() rule above loses to it.

     .mm-navbar-title
       The drawer heading is inserted by script.js with a hyphenated class, so
       the BEM `.mm-navbar__title` selector never matched it. White on the
       orange bar measures 2.59:1 against the 3.0 large text needs.
   -------------------------------------------------------------------------- */

:root[data-theme="dark"] .profile-top-wapper button {
  color: var(--ii-text) !important;
}
:root[data-theme="dark"] .breadcrumb-item a,
:root[data-theme="dark"] .breadcrumb-item + .breadcrumb-item::before {
  color: var(--ii-text-secondary);
}
:root[data-theme="dark"] .mm-navbar,
:root[data-theme="dark"] .mm-navbar-title,
:root[data-theme="dark"] .mm-navbar a,
:root[data-theme="dark"] .mm-close-btn {
  /* --ii-text, matching the dark drawer surface set in the legacy block above.
     An earlier attempt put ink here on the theory that the bar stays brand
     orange; measuring made it worse (2.59 -> 2.37), because the drawer is not
     built until it opens and a closed-drawer measurement is reading the raw
     source element, not what mmenu renders. Needs confirming with the drawer
     OPEN on a real device - see the note at the end of this file. */
  color: var(--ii-text) !important;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .profile-top-wapper button {
    color: var(--ii-text) !important;
  }
  :root:not([data-theme="light"]) .breadcrumb-item a,
  :root:not([data-theme="light"]) .breadcrumb-item + .breadcrumb-item::before {
    color: var(--ii-text-secondary);
  }
  :root:not([data-theme="light"]) .mm-navbar,
  :root:not([data-theme="light"]) .mm-navbar-title,
  :root:not([data-theme="light"]) .mm-navbar a,
  :root:not([data-theme="light"]) .mm-close-btn {
    color: var(--ii-text) !important;
  }
}

/* The header menu colours its own links (.ace-responsive-menu li a), which
   outranks the element-level :where() rule above, so "Se connecter" and its
   siblings stayed #484848 on the dark bar - 1.94:1. Inherit from the list item,
   which is already themed correctly.

   The `.style2` line below is the same defect, found again on 2026-08-28, and
   it is here because the fix above was written one class short. style.css
   states the grey twice:

     header.header-nav.menu_style_home_one       .ace-responsive-menu li a  #fff     (0,3,3)
     header.header-nav.menu_style_home_one.style2 .ace-responsive-menu li a  #484848  (0,4,3)

   and every page carries `style2` on the header, so the SECOND one is what
   renders. The rule above is (0,3,2) - under both - so it never won and the
   1.94:1 its own comment records was still on the page: Immobilier, Vehicule,
   Mode, "Toutes les categories" and the signed-in username, five of the six
   labels in the desktop bar. Only the `.style2` form needs restating; the
   unqualified one paints #fff, which is already right on a dark bar.

   Still `inherit`, not a token: the <li> computes to --ii-text (measured), so
   inheriting themes the link without touching light mode at all. */
:root[data-theme="dark"] .ace-responsive-menu a,
:root[data-theme="dark"] .ace-responsive-menu li a,
:root[data-theme="dark"] .menu-bar a,
:root[data-theme="dark"] header.header-nav.menu_style_home_one.style2 .ace-responsive-menu li a {
  color: inherit;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ace-responsive-menu a,
  :root:not([data-theme="light"]) .ace-responsive-menu li a,
  :root:not([data-theme="light"]) .menu-bar a,
  :root:not([data-theme="light"]) header.header-nav.menu_style_home_one.style2 .ace-responsive-menu li a {
    color: inherit;
  }
}

/* --------------------------------------------------------------------------
   L52: the select label was clipped to 74px, and the cause was ours

   255f8ad widened the control to 100% of the filter panel and recorded that
   the label still measured 151px of text in a 74px box, attributing it to
   bootstrap-select's own inner element. Re-measured here with the panel
   genuinely OPEN - which needs transitions disabled first, because
   .listing-filter-caption carries transform: scale(0.3) and a 0.3s transition,
   and a transition that starts never advances in this environment
   (constraint 68). That is why the previous pass could not open it: every
   reading it took was a third of the real size.

   Open, at 375px: the panel is 200px, the control 170px, and the label box
   74px. The missing 96px is three nested horizontal paddings. Ours is the
   40px caret gutter on .bootstrap-select > .dropdown-toggle - correct in
   itself - but bootstrap-select declares padding-right: inherit on BOTH
   .filter-option and .filter-option-inner, so the gutter is subtracted twice
   from one control. The 151px label is the CATEGORY select ("Toutes les
   catégories"), not the sort one; both shared the same 74px box.

   Removing the duplicate is the actual fix and it is not local to this page:
   measured across the 8 visible selectpickers on /categories/immobilier, the
   right gap goes 81px -> 41px and the label box 252px -> 292px, with nothing
   newly clipped anywhere. The button keeps its own 40px gutter, so the caret
   is untouched - verified as a 41px gap between the label's right edge and
   the button's.
   -------------------------------------------------------------------------- */
.bootstrap-select > .dropdown-toggle .filter-option-inner {
  padding-right: 0;
}

/* A label that genuinely cannot fit should say so. bootstrap-select ships
   text-overflow: clip, which cuts mid-word with nothing to mark it - the
   "nothing to say the rest exists" half of L52. The longest subcategory
   ("Sous-vêtements neufs et tenues décontractées", 337px) does not fit any
   control this page can offer at 375px, so the ellipsis is the honest end
   state rather than a failure to size. */
.bootstrap-select > .dropdown-toggle .filter-option-inner-inner {
  text-overflow: ellipsis;
}

/* The panel's own min-width is 200px, set when it held one short select. It is
   absolutely positioned against right: 0 with 12px page gutters either side,
   so 320px is what fits at 375px. That takes the label box to 234px, and
   "Prix Décroissant" (116px) and "Toutes les catégories" (151px) then both fit
   outright. Verified no horizontal overflow: the panel spans 33..353 in a
   375px viewport, documentElement.scrollWidth 375.

   Phone-scoped on purpose, alongside the control widths 255f8ad put in the
   same media query. Widening the panel on a desktop was tried and reverted:
   the panel grew to 320px but .dropdown-toggle keeps a 200px cap of its own
   there, so the extra width became dead space inside the panel rather than
   label room. Desktop still gains the padding fix above - the label box goes
   104px to 144px - and the 7px that "Toutes les catégories" still overruns now
   ends in an ellipsis instead of a silent cut. Rebuilding the legacy desktop
   panel is a bigger job than L52. */
@media (max-width: 991px) {
  .listing-filter-caption {
    min-width: min(320px, calc(100vw - 24px));
  }
}

/* --------------------------------------------------------------------------
   L56: the inline "Enregistré" beside Save

   A success toast already fires - the controller flashes success plus
   message_type=toast and common.blade.php calls showToast - but the save is a
   full POST and reload, so the toast arrives after the repaint and is gone
   again in seconds. The mark below stays until the form is edited, so it says
   what is true now rather than reporting an event.

   margin-right: auto keeps it at the far left of a row whose buttons are
   justified right, so it never competes with them for the same edge.
   -------------------------------------------------------------------------- */
.ii-acct-saved {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-right: auto;
  font-size: 13px;
  font-weight: 600;
  color: var(--ii-success);
}
.ii-acct-saved svg { flex: 0 0 auto; }

/* --------------------------------------------------------------------------
   L55: signing out from inside the account area

   Neutral rather than accent: constraint 51 reserves orange for Vendre,
   Promouvoir and Créer un compte, and leaving is not the action being
   encouraged. Not destructive red either - nothing is lost by signing out.

   It lands as a sibling of .profile-top-wapper rather than inside it, so it
   sits below the account card on the page background - which is what a
   page-level action should do. No divider: a --ii-border rule there would be
   a near-white line on a near-white background, 24px under a card that
   already ends in its own edge, so it separated nothing. The margin does it.
   -------------------------------------------------------------------------- */
.ii-acct-signout {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--ii-space-5);
}

.ii-acct-signout-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ii-space-2);
  /* 44px is the hit-target floor this product already sets, and 3cbcea5
     had to go back and fix five controls that missed it. */
  min-height: 44px;
  padding: 0 var(--ii-space-5);
  border: 1px solid var(--ii-border-control);
  border-radius: var(--ii-radius-control);
  /* --ii-bg-control, not --ii-bg-surface: this is a button, and surface is
     what the FIELDS on the same page use. See the token's own note. */
  background: var(--ii-bg-control);
  color: var(--ii-text);
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
}
.ii-acct-signout-btn:hover,
.ii-acct-signout-btn:focus {
  background: var(--ii-bg-sunken);
  color: var(--ii-text);
  text-decoration: none;
}
.ii-acct-signout-btn:focus-visible {
  outline: 2px solid var(--ii-focus);
  outline-offset: 2px;
}

@media (max-width: 575.98px) {
  /* .ii-acct-actions is column-reverse below this width and stretches every
     child, which would turn the mark into a full-width block under the
     buttons. order:1 puts it back above them - in column-reverse a higher
     order is laid out first - and auto width keeps it the size of its text. */
  .ii-acct-actions > .ii-acct-saved {
    order: 1;
    width: auto;
    margin-right: 0;
    align-self: center;
  }
  .ii-acct-signout { justify-content: stretch; }
  .ii-acct-signout-btn { width: 100%; }
}

/* --------------------------------------------------------------------------
   The off-canvas category drawer

   Reported from a real phone: "the list of the categories are in dark and they
   are not visible clearly." Measured with the drawer OPEN - which needs
   transitions disabled first, constraint 68 - every one of the eight category
   names sat at 1.18:1. Not dim: invisible.

   Three separate causes, none of them the one the earlier note in this file
   guessed at:

   1. mmenu ships its own variable layer. `--mm-color-text: rgba(0, 0, 0, .75)`
      is declared on `.mm-menu` and inherited by `.mm-listitem`, so every
      descendant took near-black regardless of what any component rule said.
      The existing dark block here targets `.mm-listitem > a`, a DIRECT child -
      and these links are four levels deeper, inside
      `.mm-listitem > ul.mmmenucategory > li > .category-header > a`, so it
      never matched them.

   2. `custom.css .sidenav a, .mmmenucategory a { color: #000 }` is (0,1,1) and
      outranks the `.category-link` rule in the layout's own <style> block.

   3. That <style> block in common.blade.php is a FOURTH theming layer, after
      tokens, style.css and inline style attributes. ThemeTokensTest only scans
      style="" ATTRIBUTES, so a hardcoded colour in a <style> block was never
      going to be caught. Both are now tokens, and the test has been extended.

   Pointing mmenu's variables at our semantics is the fix that does not need
   repeating per scope: --ii-* already resolve per theme, so the drawer follows
   automatically and the two dark scopes cannot drift apart on it.
   -------------------------------------------------------------------------- */
.mm-menu {
  --mm-color-background: var(--ii-bg-surface);
  --mm-color-text: var(--ii-text);
  --mm-color-text-dimmed: var(--ii-text-muted);
  --mm-color-border: var(--ii-border);
  --mm-color-button: var(--ii-text-secondary);
}

/* (0,1,1), matching custom.css so the later sheet wins without !important. */
.mmmenucategory a,
.sidenav a {
  color: var(--ii-text);
}

/* The chevron that opens a subcategory list, and the row it sits on. */
.mm-menu .category-header,
.mm-menu .category-item,
.mm-menu .subcategory-item {
  color: var(--ii-text);
}

/* --------------------------------------------------------------------------
   The .profile-top-wapper button blanket, and the three defects it caused

   style.css declares, at (0,1,1) and with !important on two of them:

     .profile-top-wapper button, .orange-border-btn {
       border-radius: 3px; background-color: transparent;
       color: black !important; font-weight: 100 !important;
       border: 1px solid #ff5e00;
     }
     html body .profile-top-wapper button:hover { background-color: #ff5e00 !important }

   .profile-top-wapper wraps the WHOLE account, favourites, wallet, reviews and
   public-profile area, so that blanket lands on every design-system control
   rendered inside any of them. Three reports from a real phone trace back to
   it, and all three were verified by walking the CSSOM rather than guessed at.

   1. TAPPING A HEART TURNED IT ORANGE. The hover rule is !important at (0,2,2).
      A tap on iOS latches :hover until you tap elsewhere, so the fill stuck.
      It rendered SQUARE because the same blanket sets border-radius: 3px, which
      beat .ii-card-fav's own 50% at (0,1,0).

   2. THE ACCOUNT BUTTONS LOOKED LIKE INPUT FIELDS. .ii-acct-btn-primary is
      (0,1,0), so it lost background, radius and weight to the blanket: the
      primary action never showed its accent at all - measured #202124 with
      font-weight 100 and a 3px radius, against inputs that carry the same
      border colour at 10px. Nothing on that form read as pressable.

   3. THE SAVED HEART STOPPED SHOWING THE ACCENT. The dark override
      `:root:not([data-theme="light"]) .profile-top-wapper button` is (0,3,1)
      and !important, which outranks `.ii-card-fav.is-on` at (0,2,0) - so the
      dark-mode work flattened the one colour that says an ad is saved.

   Answered at matching or higher specificity rather than by escalating, which
   is the precedent already set here for the avatar and for .orange-border-btn.
   None of these rules needs a per-theme copy: every value is a semantic token,
   so both scopes resolve them and testBothDarkScopesDeclareTheSameRules has
   nothing to compare.
   -------------------------------------------------------------------------- */

/* --- The favourite heart ------------------------------------------------- */

/* (0,2,0) beats the blanket's (0,1,1). The disc is drawn by ::before; the
   button itself must stay unfilled or it paints a square behind the circle. */
.profile-top-wapper .ii-card-fav,
.profile-top-wapper .favoutite-heart {
  border-radius: 50% !important;
  background-color: transparent !important;
}

/* (0,3,2), which is what it takes to beat the (0,3,1) dark blanket. Stated
   once, outside any theme scope, because --ii-accent already resolves per
   theme. */
html body .profile-top-wapper .ii-card-fav.is-on,
html body .profile-top-wapper .favoutite-heart.liked {
  color: var(--ii-accent) !important;
}

/* A tap must not leave a heart looking saved when it is not, so the hover
   tint is for pointers only. Touch gets the resting state and the class
   change, which is the honest signal. */
@media (hover: none) {
  html body .profile-top-wapper button:hover,
  html body .profile-top-wapper .ii-card-fav:hover,
  html body .profile-top-wapper .favoutite-heart:hover {
    background-color: transparent !important;
  }
  html body .profile-top-wapper .ii-card-fav:not(.is-on):hover,
  html body .profile-top-wapper .favoutite-heart:not(.liked):hover {
    color: var(--ii-text-muted) !important;
  }
}

/* --- The account form's own buttons -------------------------------------- */

.profile-top-wapper .ii-acct-btn-primary,
.profile-top-wapper .ii-acct-btn-ghost {
  font-weight: var(--ii-weight-semibold) !important;
  border-radius: var(--ii-radius-control) !important;
  min-height: 48px;
  font-size: var(--ii-text-md);
  padding: 0 var(--ii-space-5);
}

.profile-top-wapper .ii-acct-btn-primary {
  background-color: var(--ii-accent) !important;
  border: 1px solid var(--ii-accent) !important;
}
.profile-top-wapper .ii-acct-btn-primary:disabled {
  background-color: var(--ii-bg-sunken) !important;
  border-color: var(--ii-border) !important;
}

/* The LABEL has to out-specify the dark blanket
   `:root:not([data-theme="light"]) .profile-top-wapper button`, which is
   (0,3,1) - :root and :not([...]) each count in the class column - and carries
   !important. Background and border can stay at (0,2,0) because the blanket
   does not set those; only colour has to climb.

   `html body .profile-top-wapper .ii-acct-btn-primary` is (0,2,2) and LOSES,
   which is worth stating because it was tried and measured: the button turned
   orange while its label stayed #F5F5F4 at 2.37:1. The :disabled variant won
   by accident, its pseudo-class making it (0,3,2). So :not(:disabled) is doing
   real work here - it buys the third class-column slot. Constraint 71 by a
   different route: ink on the accent, never white. */
html body .profile-top-wapper .ii-acct-btn-primary:not(:disabled) {
  color: var(--ii-text-on-accent) !important;
}
html body .profile-top-wapper .ii-acct-btn-primary:disabled {
  color: var(--ii-text-disabled) !important;
}
html body .profile-top-wapper .ii-acct-btn-ghost:not(:disabled) {
  color: var(--ii-text) !important;
}

/* A secondary button must not be mistakable for an input. The inputs on this
   form are --ii-bg-surface behind a --ii-border-control hairline; these carry
   a RAISED surface and the stronger border, so the two read as different
   kinds of thing at a glance rather than only on inspection. */
.profile-top-wapper .ii-acct-btn-ghost {
  background-color: var(--ii-bg-control) !important;
  border: 1px solid var(--ii-border-control) !important;
  color: var(--ii-text) !important;
}

@media (hover: hover) {
  html body .profile-top-wapper .ii-acct-btn-primary:hover {
    background-color: var(--ii-accent-hover) !important;
    border-color: var(--ii-accent-hover) !important;
    color: var(--ii-text-on-accent) !important;
  }
  html body .profile-top-wapper .ii-acct-btn-ghost:hover {
    background-color: var(--ii-bg-sunken) !important;
    color: var(--ii-text) !important;
  }
}
/* Latched hover on touch must not repaint them orange. */
@media (hover: none) {
  html body .profile-top-wapper .ii-acct-btn-primary:hover {
    background-color: var(--ii-accent) !important;
    color: var(--ii-text-on-accent) !important;
  }
  html body .profile-top-wapper .ii-acct-btn-ghost:hover {
    background-color: var(--ii-bg-control) !important;
    color: var(--ii-text) !important;
  }
  html body .profile-top-wapper .ii-acct-signout-btn:hover {
    background-color: var(--ii-bg-control) !important;
    color: var(--ii-text) !important;
  }
}

/* --------------------------------------------------------------------------
   Listing detail: the owner's actions, and the three-icon row

   THE OWNER BUTTONS. style.css `.mark-sold-btn { background-color: #FFFFFF;
   color: #333; border: 1px solid #ddd }` - Modifier, Marquer comme vendu and
   Supprimer rendered as white bars on a dark page. Reported from a real phone
   as "still in white, not clean and professional".

   They are also the only three controls on the page that change what an ad IS,
   so they get the secondary-button treatment rather than being made invisible:
   a control surface, a real border, 44px, and Supprimer carrying the
   destructive colour instead of an inline style="color:red".

   THE ICON ROW. Save / share / report were reported as different sizes, and
   measured at 375px they are: 35x35, 35x35 and 44x44, sitting on three
   different centre lines (538, 542, 543) with 10px and 9px gaps. They are one
   control group and should be one size. 44px is this product's target floor
   and the report button already met it, so the other two come up to it rather
   than the report button coming down.
   -------------------------------------------------------------------------- */

.mark-sold-btn,
.mark-delete-btn {
  background-color: var(--ii-bg-control) !important;
  color: var(--ii-text) !important;
  border: 1px solid var(--ii-border-control) !important;
  border-radius: var(--ii-radius-control) !important;
  min-height: 44px;
  /* 15, not the 14 these inherited. The scale is 12/13/15/17/20/24/32 and 14
     is not on it - these three were among the only off-scale sizes left on the
     page, and they are the seller's primary controls, so the odd size showed
     on the element least able to afford looking accidental. Constraint 87. */
  font-size: var(--ii-text-md) !important;
  font-weight: var(--ii-weight-semibold);
}
.mark-sold-btn .title-name,
.mark-delete-btn .title-name { color: inherit; }

/* Destructive, and stated here rather than as style="color:red" on two
   elements in the template. #C92A2A is the tone d8171cf measured at 5.46:1;
   raw red was 3.29:1. */
.mark-delete-btn,
.mark-delete-btn .title-name,
.mark-delete-btn .title-name span,
.mark-delete-btn > span {
  color: var(--ii-danger) !important;
}
@media (hover: hover) {
  .mark-sold-btn:hover,
  .mark-delete-btn:hover {
    background-color: var(--ii-bg-sunken) !important;
    border-color: var(--ii-border-strong) !important;
  }
}

/* The save / share / report group.

   The markup is .single_property_social_share > .spss > ul > li > a, so a
   direct-child selector matches none of them - that was tried and measured
   changing nothing. The heart is additionally a <button> nested INSIDE its
   <a>, which is why the anchor measured 35px while the button inside it
   measured 44.

   style.css `.spss ul li a` paints rgb(255, 224, 166), a hardcoded peach that
   never follows the theme, so two of the three were light blobs on a dark page
   while the heart was transparent - one group, three different treatments.
   They now share the control surface the contact bar's icons already use. */
.spss ul li > a,
.spss ul li > a > .favoutite-heart,
.single_property_social_share .favoutite-heart {
  width: 44px !important;
  height: 44px !important;
  min-width: 44px;
  min-height: 44px;
  padding: 0 !important;
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  border-radius: var(--ii-radius-control) !important;
  vertical-align: middle;
}
.spss ul li > a {
  background: var(--ii-bg-control) !important;
  border: 1px solid var(--ii-border-control);
  color: var(--ii-text) !important;
}
/* The heart sits inside the anchor, so it must not paint a second surface. */
.spss ul li > a > .favoutite-heart {
  background: transparent !important;
  border: 0 !important;
}
.spss ul li { display: inline-flex; align-items: center; }
.spss ul { display: flex; align-items: center; gap: var(--ii-space-2); margin-bottom: 0; }

/* --------------------------------------------------------------------------
   Profile › Annonces: the header row, the divider, and the first listing

   Three things reported together, all measured on the owner's own profile at
   375px before touching anything:

     .search-block   ends 580
     .right-caption  starts 588      <- 8px, the "touching the dropdown"
     .ii-seg         642 - 682
     listing wrapper starts 696, border-top 1px #F0F1F1, padding-top 0
                                     <- the white line, with the first
                                        listing sitting directly on it

   The search box is now visitor-only (see the note in user_profile.blade.php),
   which leaves Trier alone on the row and lets it sit where it belongs rather
   than wherever the search field left room.

   The divider itself is worth keeping - it separates the controls from the
   results - but it was #F0F1F1, a light-mode hairline that stayed light on a
   dark page, and it had nothing between it and the first row.
   -------------------------------------------------------------------------- */

/* `.listing-wapper, .listing-box-wapper` is (0,1,0) in responsive.css inside
   a media query, so this matches at (0,2,0) and needs no !important. */
.profile-listing-box-wapper.listing-box-wapper,
.listing-wapper.product-listing-wapper {
  border-top-color: var(--ii-border);
  /* Air between the rule and the first listing. 16px is the same step the
     rest of this page uses; at 0 the first row read as attached to the line. */
  padding-top: var(--ii-space-4);
}

@media (max-width: 991px) {
  /* The status segments sit between the sort row and the results, so they take
     the same step below them as everything else here - it was 14px.

     Nothing is set on .tab-content-head or .right-caption on purpose. Rules for
     both were written, measured, and found DEAD: the row carries Bootstrap's
     `mb-3` and `align-items-*` utilities, which are !important (constraint 63),
     so alignment and margin there cannot be set from here without an arms race.
     They do not need to be - `mb-3` is 16px, which is the step this rhythm
     wanted anyway. Measured after: Trier -> 16 -> segments -> 16 -> divider ->
     16 -> first listing. */
  .ii-seg {
    margin-bottom: var(--ii-space-4);
  }

  /* jQuery slideUp/slideDown animates `height` frame by frame from script. A
     CSS `transition: all` on the same element means the browser then tries to
     transition every one of those steps as well - two animation systems on one
     property, which is visible as lag. The JS owns this animation. */
  .subcategory-list { transition: none; }
}

/* --------------------------------------------------------------------------
   The listing page's search bar: position:sticky, not a fixed class-toggle

   Reported as "it jumps, and the filter floats" - both halves real.

   WHAT IT DID. scrollto.js listens to every scroll event and toggles
   `--top-first` / `--top-second` on `.sticky-nav-tabs-container`. Both classes
   resolve to ONE declaration block in style.css:
   `position: fixed; top: 0; z-index: 9999; background: #ffffff; padding: 13px 0`.

   Three defects follow, all measured:

     1. THE JUMP. static -> fixed takes the bar OUT of the document flow, so at
        the threshold everything below it moves up by its full height in a
        single frame. Nothing reserves the space, so no amount of easing helps -
        the layout genuinely changes.
     2. IT COVERS THE HEADER. z-index 9999 against the mobile header's 999 at
        top:0, so the pinned bar paints over the logo, avatar and burger. On a
        phone that removes every navigation control for the rest of the scroll,
        which is why the logo is missing from the reported screenshot.
     3. `#ffffff` hardcoded - a white bar on a dark page.

   The scroll-direction branch is also pointless: up and down apply two class
   NAMES that share one rule, so the states are identical, and the handler pays
   for `.offset()` and `.height()` on every scroll event - forced synchronous
   layout, per frame - to produce no difference.

   WHY SIMPLY WRITING position:sticky DID NOT WORK, which is the useful part.
   Two independent blockers, each proved by measurement:

     a. RANGE. A sticky element travels only within its PARENT's box.
        `.sticky-nav-tabs-container`'s parent is exactly its own height, so the
        available range was 0px and it could never stick. The first ancestor
        with real height is `section.our-listing` at 2501px, and `.sticky-block`
        is its direct child - so the sticky belongs on `.sticky-block`.

     b. SCROLLPORT. `body`, `.mm-page`, `.mm-slideout` and `.wrapper` all
        computed `overflow: hidden auto`. `overflow-x: hidden` forces the other
        axis to `auto`, and that makes each one a scroll container - so the
        nearest scrollport was never the viewport, and sticky inside it can
        never engage. **position:sticky was unusable anywhere on this site**
        while those held. Proved both ways: cleared, it pins; restored, it does
        not.

   The overflow is still needed while the drawer is OPEN, so it is scoped to
   that state with mmenu's own `html.mm-wrapper_opened` rather than removed.
   Verified with the drawer open and closed: scrollWidth stays 375 at 375, and
   the drawer still opens and closes.

   Measured after: natural at 91, then pinned at exactly 76 - immediately below
   the header - at scroll 200, 400, 700 and 1200, with no jump at the threshold
   because sticky reserves its space by definition.

   scrollto.js is deliberately NOT edited: it also drives `.spa-header` on the
   ad page. Its two classes are restated here so they cannot reimpose
   `position: fixed`, which leaves the CSS as the single authority.
   -------------------------------------------------------------------------- */
/* NOT inside a media query. The class-toggle that causes the jump runs at every
   width - scrollto.js has no breakpoint - so scoping the replacement to mobile
   left the desktop jumping exactly as before. Only the offset differs, and that
   is handled by the one override at the end of this block. */
/* Let the viewport be the scrollport again - see (b) above.
   `.mm-page.mm-slideout` is stated at (0,2,0) because that is how style.css
   declares it; `.mm-page` alone is (0,1,0) and loses, which left mmPage as a
   scroll container and sticky dead while everything else read `visible`.

   ONLY on the listing page, via the `ii-allow-sticky` body class. Applied
   site-wide it exposed a pre-existing 3px horizontal overflow on the ad page
   - a Bootstrap `.row` carrying -15px gutters with no matching container
   padding - which `overflow-x: hidden` had been quietly masking for years.
   Unmasking a latent bug on a page this change has no business touching is a
   regression, so the opt-out is per page. The 3px is worth fixing on its own
   merits; it is not this change's job. */
body.ii-allow-sticky,
body.ii-allow-sticky .mm-page.mm-slideout,
body.ii-allow-sticky .wrapper { overflow-x: visible; overflow-y: visible; }

/* mmenu overlays the page while open; keep the containment for that state. */
html.mm-wrapper_opened body.ii-allow-sticky,
html.mm-wrapper_opened body.ii-allow-sticky .mm-page.mm-slideout,
html.mm-wrapper_opened body.ii-allow-sticky .wrapper { overflow-x: hidden; }

/* The listing grid only. `.sticky-block` also appears on user_listing and
   user_profile, where there is no long results column to pin against.

   NOT sticky on desktop - measured, and it does not earn the space. At
   1280x800 the fixed header is 88px and this bar is 103px, so pinning it costs
   191px of PERSISTENT chrome: 23.9% of the viewport. What it holds is ONE
   control, `searchTerm`. The filter bar is not in it - that scrolls away at
   -652 - so the arrangement pinned the control a visitor is LEAST likely to
   reach for mid-scroll while the sort and filters they DO use disappeared.
   Measured with the page scrolled: 4 cards clipped by the bar, 0 fully visible
   in the band it occupied.

   Sticky earns its place on a phone, where returning to the top is a long
   flick and the header is only 76px. On desktop the fixed header already
   provides persistent navigation and Home / wheel / scrollbar make the top
   cheap, so the bar scrolls away.

   The class neutralisation above still applies at EVERY width. That is what
   stops the JUMP, and it must not be scoped to mobile or scrollto.js puts
   `position: fixed` back and the desktop jumps again. Unsticking is not
   reverting. */
body.ii-allow-sticky .our-listing .sticky-block {
  position: static;
}

@media (max-width: 991px) {
  body.ii-allow-sticky .our-listing .sticky-block {
    position: -webkit-sticky;
    position: sticky;
    /* Under the 76px mobile header, not over it. */
    top: 76px;
    /* Under the header (999), over the results. */
    z-index: 40;
    /* Opaque, or results scroll through it. Themed, unlike the #fff it replaces. */
    background: var(--ii-bg-canvas);
  }
}

/* Neutralise the JS toggle so it cannot put the bar back into `fixed`. */
.sticky-nav-tabs-container,
.sticky-nav-tabs-container--top-first,
.sticky-nav-tabs-container--top-second {
  position: static;
  background: transparent;
  box-shadow: none;
  transition: none;
}


/* --------------------------------------------------------------------------
   The category drawer, as a drill-down

   Markup and rationale are in header.blade.php. mmenu now builds 9 panels -
   one root plus one per category, each auto-titled with the category name and
   carrying its own back row - from plain nested <ul>s. It used to build ONE,
   because the whole tree was handed to it inside a single <li>.

   Measured after the restructure: the drill target is the full 300px row, not
   a 24px arrow, because the parent row is a <span> rather than an <a>. mmenu
   only splits a row into "link plus narrow next button" when the <li> contains
   a link of its own; with a span it makes the entire row the next target and
   the category's own "browse everything" link moves inside the panel as
   "Tout {catégorie}", where it is a full-width row too.
   -------------------------------------------------------------------------- */

/* The icon had no size once the legacy `.category-image-front` class went, so
   rows came out 192px tall. 24px matches the category rail's glyph scale. */
.ii-drawer-icon {
  width: 24px;
  height: 24px;
  flex: 0 0 24px;
  object-fit: contain;
}

.ii-drawer-row {
  display: flex;
  align-items: center;
  gap: var(--ii-space-3);
  min-height: 52px;
  padding: var(--ii-space-2) 0;
  font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-medium);
  color: var(--ii-text);
}

.ii-drawer-name { min-width: 0; }

/* "Tout Immobilier" opens the whole category. It is the first row in the
   panel and reads as the primary action there, so it takes the accent ink
   rather than body colour - the same treatment the /vendre picker gives it. */
/* `#menu .mm-listitem > a` is stated at (1,1,1) WITH !important in the dark
   scope, so a bare `.ii-drawer-all` never won the colour even carrying
   !important itself - measured, it came out as body text. Matched at (1,2,1). */
#menu .mm-listitem > a.ii-drawer-all {
  font-weight: var(--ii-weight-semibold);
  color: var(--ii-accent-text) !important;
}

/* mmenu's own rows. Stated here because the library ships light defaults and
   this drawer is themed; the dark scopes further up already cover .mm-menu,
   .mm-panels and .mm-listitem, so this is sizing and rhythm only. */
#menu .mm-listitem {
  font-size: var(--ii-text-md);
}
#menu .mm-listitem > a,
#menu .mm-listitem > span {
  padding: var(--ii-space-3) var(--ii-space-4);
  min-height: 52px;
  display: flex;
  align-items: center;
}
#menu .mm-listitem::after {
  border-color: var(--ii-border);
  left: var(--ii-space-4);
}
#menu .mm-btn_next::after,
#menu .mm-btn_prev::before {
  border-color: var(--ii-text-muted);
}

/* The drill-down navbar: give the buttons their gutters back, and the chevron
   its box.

   Reported as "a confusing arrow covering the category name" - on the
   Immobilier panel the back chevron was drawn straight through the title.
   Measured on the open drawer, and it is two faults at once:

     .mm-navbar        padding 10px   - mmenu ships `0 44px`, and those 44px
                                        ARE the space its absolutely-positioned
                                        prev/close buttons sit in. Flattened to
                                        10px, the title started at x=398 while
                                        the back button occupied 388..432 - a
                                        34px overlap.
     .mm-btn_prev::before  8 x 60px   - mmenu draws the chevron as an 8x8 square
                                        with two borders, rotated 45deg. At 60px
                                        tall that rotation is a 60px DIAGONAL
                                        STROKE across the header, which is the
                                        "arrow" in the report.

   Both restated at `#menu` so this drawer is fixed without touching the
   library or any other mmenu instance. The height is the substantive half: a
   chevron is a corner, and a corner has to be square. */
#menu .mm-navbar {
  padding: 0 44px;
}

/* The source, found on the second pass: style.css groups the navbar and the
   chevron into ONE rule.

     .stylehome1 .mm-navbar,
     .stylehome1 .mm-btn_prev:before { height: 60px; background-color: #FF5E01 }

   So the BAR's height and the BAR's fill are both applied to the chevron.
   That is the whole history of this bug in one declaration: at 8x60 with an
   orange fill and a 45deg rotation it drew a long orange diagonal stroke
   across the title, and once the height was corrected to 8px it became an 8x8
   orange square at 45deg - a diamond, which is what came back on the retest.

   `#FF5E01` is the SECOND orange this project retired from the header and the
   off-canvas menu (see the block further up). It survived here because the
   retirement targeted `.mm-navbar` and this rule quietly carries the chevron
   along with it.

   A chevron is two borders and nothing else: no fill, and a square box so the
   rotation produces a corner rather than a bar. Both stated. */
#menu .mm-btn_next::after,
#menu .mm-btn_prev::before {
  width: 8px;
  height: 8px;
  background: transparent;
}

/* ==========================================================================
   DESKTOP LISTING HEADER - nav scrolls away, search + filters pin

   The reference is Carousell's browse header, and the reason it works is the
   TRADE, not the pinning: the nav row is allowed to leave, and the space it
   frees is spent on the two controls a visitor actually uses while scanning
   results - search, and the filter chips. Pinning search ALONE (which is what
   this page did) cost 23.9% of an 800px viewport to keep one text input on
   screen while the sort and filters scrolled away. Same pixels, opposite value.

   Three parts:

   1. The nav stops being fixed on this page. scrollToFixed sets
      `position: fixed` INLINE, and an author `!important` outranks a normal
      inline declaration, so one rule reclaims it without touching the plugin
      or the class (three stylesheet rules key off .navbar-scrolltofixed, so
      removing the class would change styling). No spacer is involved - the
      plugin never created one here, which is also why the fixed header was
      overlapping the top of the page by ~14px. Letting it scroll fixes the
      "sticky filter hides some of the details" report at the source.

   2. Search and filters are TWO stacked sticky elements rather than one
      wrapper. They are not adjacent in the DOM - the breadcrumb and the
      results heading sit between them - and moving the filters above the
      heading would put the controls that refine a page before the words that
      name it. Stacking two sticky offsets gives the same pinned result while
      letting the breadcrumb and heading scroll away between them, which is
      what should happen to them.

   3. The mark is revealed in the search row only once the bar is stuck, so
      the pinned bar always offers a way home.
   ========================================================================== */

@media (min-width: 992px) {

  /* 1. Let the nav scroll away on the listing page. */
  body.ii-nav-scrolls header.header-nav.navbar-scrolltofixed {
    position: static !important;
    top: auto !important;
  }

  /* 2a. The search row pins to the very top once the nav has gone. */
  body.ii-allow-sticky .our-listing .sticky-block {
    position: -webkit-sticky;
    position: sticky;
    top: 0;
    z-index: 45;
    background: var(--ii-bg-canvas);
  }

  /* Tighter than the 103px this block used to occupy: 44px control with 10px
     above and below is 64px, and the whole pinned stack is budgeted from there.
     Every pixel here is charged to the reader for the rest of the page. */
  body.ii-allow-sticky .our-listing .sticky-block .row.search-bar {
    margin-top: 0 !important;
    margin-bottom: 0 !important;
    padding: 10px 0;
  }

  /* 2b. The filter chips pin directly beneath it. 68px is the search row:
     44px control + 12px padding top and bottom.

     The sticky sits on the ROW, not on `.ii-filter-bar` inside it: a sticky box
     travels only within its parent, and the bar's `.col-12` is exactly the
     bar's own height, so it had zero range and never pinned - measured. The
     row's parent is the page container at ~1742px. Second time this exact trap
     has cost time on this page; check the parent's height before writing
     `position: sticky`. */
  body.ii-allow-sticky .ii-filter-row {
    position: -webkit-sticky;
    position: sticky;
    /* 69, one pixel LESS than the 70px pinned search row above it, so the two
       always overlap by a pixel instead of meeting exactly.

       Meeting exactly is what produced the reported strip of listing photos
       between them: two separately-pinned elements whose heights land on a
       fraction (zoom, device pixel ratio, a 1px border) leave a sub-pixel seam,
       and whatever is scrolling underneath shows through it. It measures 0 here
       and is still visible on a real screen, which is exactly the kind of gap
       that cannot be closed by getting the arithmetic more precise.

       The search bar is z-index 45 against this row's 44, so it paints over the
       overlapping pixel and nothing shifts. */
    top: 69px;
    z-index: 44;
    background: var(--ii-bg-canvas);
    padding: 6px 0;
    margin-left: 0;
    margin-right: 0;
    /* The hairline belongs to the pair, so it sits under the lower of the two. */
    box-shadow: 0 1px 0 var(--ii-border);
  }

  /* The count and sort travel with the filters rather than scrolling away.
     Carousell drops the count and keeps sort as a chip; keeping both is the
     better call HERE because this count is `aria-live` and rewrites itself as
     each filter is applied - watching "181 annonces" move is the feedback that
     tells you the tap worked. It is only worth the pixels because it is live.

     Its 8px/14px margins are for a row with air around it, which it no longer
     has once it is part of a pinned stack. */
  body.ii-allow-sticky .ii-filter-row .ii-results-row {
    margin-top: 0;
    margin-bottom: 0;
  }
  body.ii-allow-sticky .ii-filter-row .ii-filter-bar { gap: 6px; }

  /* The search row: logo | field | button, on one line. */
  .ii-searchrow {
    display: flex;
    align-items: center;
    gap: var(--ii-space-3);
  }
  .ii-searchrow .input-group { flex: 1 1 auto; min-width: 0; }
  .ii-searchrow .form-control,
  .ii-searchrow .input-group-text {
    height: 44px;
    background: var(--ii-bg-sunken);
    border-color: var(--ii-border);
  }
  .ii-searchrow .form-control { font-size: var(--ii-text-md); }

  /* 3. The mark, revealed only when the bar is stuck. Width is animated from 0
     so nothing below it moves - the field simply gives up the space. A
     horizontal shift inside a pinned bar does not disturb reading position the
     way a vertical one does, and it is what the reference does too. */
  .ii-searchrow-logo {
    display: flex;
    align-items: center;
    overflow: hidden;
    width: 0;
    opacity: 0;
    flex: 0 0 auto;
    transition: width var(--ii-dur-fast) var(--ii-ease),
                opacity var(--ii-dur-fast) var(--ii-ease);
  }
  .ii-searchrow-logo img { height: 34px; width: auto; display: block; }
  body.ii-stuck .ii-searchrow-logo { width: 78px; opacity: 1; }

  /* The submit control the field never had. It was first given an accent fill
     with the ink label the product uses on orange - white on it measures 3.26.
     That is HISTORY, not what ships: two later blocks in this same
     `min-width: 992px` condition redeclare the identical eleven properties, so
     the version defined here never reached a screen. It is deleted rather than
     kept, because a live-looking rule is how a reader ends up editing a colour
     and seeing nothing change.

     Where it actually lives now, in cascade order:
       - the ink treatment, so nothing but Vendre carries colour  (search "Ink
         instead" below)
       - `--ii-text-secondary` as the fill, which is the one that wins - #5A5A5A
         on light, #A1A1A6 on dark

     Measured after the delete: 48px tall, radius 0, 28px padding, #5A5A5A -
     identical to before, because none of it was reachable. */
}

/* Below 992px the mark and the button stay out of the way: the mobile header is
   always fixed and always carries the logo, and the search field there is the
   full width of a small screen already. */
@media (max-width: 991px) {
  .ii-searchrow-logo { display: none; }
  .ii-searchrow-btn { display: none; }
  .ii-searchrow { display: block; }
}

/* ==========================================================================
   DESKTOP LISTING HEADER - the vertical rhythm, stated once

   This region had been patched a piece at a time and the result was arbitrary.
   Measured at 1280 before writing any of this:

     nav      0 - 89
              164px OF NOTHING
     search 263 - 328
              1px            <- breadcrumb welded to the search bar
     crumbs 318 - 340
              6px
     title  346 - 371
              18px
     filters 389 - 487
              22px
     cards  503

   164 / 1 / 6 / 18 / 22 is not a rhythm, it is six unrelated decisions. The
   whole region is set here in one place so it can be read and judged as a
   whole.

   WHERE THE 164px CAME FROM - two dead spacers, both for headers that are not
   there:

     89px  scrollToFixed injects a placeholder div to hold the space of the
           header it makes `fixed`. This page makes the header static again, so
           the placeholder reserves the height of a fixed header that no longer
           exists. It is a classless div with an inline height, so it is
           targeted as the header's adjacent sibling.

     75px  `.mobile-header-set` clears the MOBILE fixed header - and it was
           still rendering at desktop, where that header is 0px tall. Reserving
           75px for an element with no height is wrong on every desktop page,
           not just this one, so it is hidden at the breakpoint rather than on
           this page.

   THE SCALE, and why each step is what it is:

     24  nav -> search      you have left navigation and entered the page
     20  search -> crumbs   a tool, then a statement of where you are
      6  crumbs -> title    ONE unit - location plus name. The deliberate
                            exception; anything larger breaks the pair
     20  title -> filters   new group: the controls
     12  filters -> count   same group, so tighter than the group break
     20  count -> grid      content boundary

   Two values for group breaks (20/24), one for within a group (12), one for a
   bonded pair (6). Spacing states the grouping - that is the whole job.
   ========================================================================== */

@media (min-width: 992px) {

  /* --- the two dead spacers ------------------------------------------- */
  body.ii-nav-scrolls header.header-nav.navbar-scrolltofixed + div {
    display: none !important;
  }
  /* Named for mobile, sized for mobile - and NOT dead at desktop, which I got
     wrong first time and caught by measuring. On every page where the desktop
     nav is still `fixed`, this 75px block is the only thing reserving space for
     it: hidden site-wide, the home page's search field jumped to y=36 and sat
     entirely underneath the 88px nav.

     It is only genuinely dead HERE, where the nav has been made static and is
     in the flow reserving its own height. Hence the body-class scope. The
     lesson is the general one: a spacer's NAME is not evidence of what it
     clears - check what actually moves when it goes. */
  body.ii-nav-scrolls .mobile-header-set { display: none; }

  /* --- the rhythm ------------------------------------------------------ */
  /* The container carries `margin-top: 10px` from the legacy sheet, which sat
     on top of the 24px below and made the real gap 34. The row owns this
     spacing now, so the stray 10 is cleared rather than subtracted from. */
  body.ii-allow-sticky .our-listing .sticky-nav-tabs-container {
    margin-top: 0 !important;
    margin-bottom: 0 !important;
  }
  body.ii-allow-sticky .our-listing .sticky-block .row.search-bar {
    margin: 0 !important;
    padding: 24px 0 0;
  }
  /* The clearance that used to live here was keyed to .ii-crumbs-col, a wrapper
     only the listing page has, so it never reached the other pinned-bar page.
     It now sits on .ii-crumbs itself - see "Breadcrumb clearance" at the end of
     this sheet. */
  body.ii-allow-sticky .ii-crumbs { margin-bottom: 6px !important; }
  body.ii-allow-sticky .ii-results-title { margin-bottom: 20px !important; }
  /* The chip bar and the count are one group (controls, then how many they
     matched), so 12 between them; the grid is a content boundary, so 20. The
     grid row already contributes 16px of its own before the first card, so this
     pays the remaining 4. */
  /* The 12 and the 20 are paid ONCE each, by the results row's own margins.
     Stacking a padding here on top of them is what produced 24 and 38 -
     `.ii-results-row` already carries 8px above (plus 4px of its own internal
     centring, which makes the visible 12) and 14 below. */
  body.ii-allow-sticky .ii-filter-row { padding-top: 0; padding-bottom: 0; }
  body.ii-allow-sticky .ii-results-outer { margin-top: 0; margin-bottom: 0; }
  body.ii-allow-sticky .ii-results-row { margin-top: 8px; margin-bottom: 0; }
  body.ii-allow-sticky .ii-filter-row .ii-filter-bar { gap: 12px; }

  /* Pinned, the 24px top padding would waste a quarter of the bar's height on
     nothing, so it collapses to a symmetric 10px once the bar is stuck. */
  body.ii-stuck.ii-allow-sticky .our-listing .sticky-block .row.search-bar {
    padding: 10px 0;
  }
  body.ii-stuck.ii-allow-sticky .ii-filter-row { padding: 6px 0; }

  /* --- the search assembly ---------------------------------------------
     gap: 0, not 16. A flex gap applies between items even when one of them has
     zero width, so the collapsed logo was still pushing the bar 16px to the
     right of every other element in the column. The spacing belongs to the logo
     and appears with it. */
  .ii-searchrow { display: flex; align-items: center; gap: 0; }
  body.ii-stuck .ii-searchrow-logo { margin-right: 16px; }

  /* One bar: icon, field and button flush, rounded only on the outside. */
  .ii-searchbox {
    display: flex;
    align-items: stretch;
    flex: 1 1 auto;
    min-width: 0;
    border-radius: 8px;
    overflow: hidden;
    background: var(--ii-bg-sunken);
    border: 1px solid var(--ii-border);
  }
  .ii-searchbox .input-group { flex: 1 1 auto; min-width: 0; border: 0; }
  /* `#autocomplete-search` is set to 52px elsewhere in this very file, and an
     ID is (1,0,0) - no number of classes beats it, which is why (0,2,0) and
     then (0,3,0) both lost and the bar stayed 56px instead of 50. The ID has to
     be in the selector. Third specificity fight in this region; enumerate the
     matching rules before adding another class. */
  .ii-searchbox .input-group .input-group-text,
  .ii-searchbox .input-group #autocomplete-search,
  .ii-searchbox .input-group .form-control {
    height: 48px;
    min-height: 48px;
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }
  .ii-searchbox .form-control { font-size: var(--ii-text-md); }
  .ii-searchbox .input-group-text { padding-left: 16px; padding-right: 8px; color: var(--ii-text-muted); }
  .ii-searchbox:focus-within { border-color: var(--ii-border-strong); }

  /* --- the submit, deliberately NOT orange ------------------------------
     Vendre is the accent, and it is the action this business runs on. A
     Rechercher button in the same orange makes two things shout equally on one
     screen, and the one that matters loses - the accent stops meaning "the
     important action" the moment a second control wears it.

     Ink instead: unmistakably a button, high contrast in both themes, and
     chromatically silent so the only saturated thing in the header is Vendre.
     It flips with the theme, so it is a dark button on a light page and a light
     one on a dark page - always the opposite of its surroundings, never a
     competitor to the accent. */
  .ii-searchrow-btn {
    flex: 0 0 auto;
    height: 48px;
    padding: 0 28px;
    border: 0;
    border-radius: 0;
    background: var(--ii-text);
    color: var(--ii-bg-canvas);
    font-size: var(--ii-text-md);
    font-weight: var(--ii-weight-semibold);
    cursor: pointer;
    transition: opacity var(--ii-dur-fast) var(--ii-ease);
  }
  .ii-searchrow-btn:hover { opacity: .86; }
  .ii-searchrow-btn:focus-visible { outline: none; box-shadow: var(--ii-focus-ring); }

  .ii-searchrow-logo img { height: 36px; }
}

/* ==========================================================================
   DESKTOP LISTING HEADER - alignment, focus, and a subordinate submit

   Measured before: FOUR different left edges in one column -
   crumbs and title at 15, the chip bar and count at 30, the search assembly at
   46, against a container whose content box runs 15 -> 1250. Nothing lined up
   with anything, which is what reads as "not symmetrical".

   One gutter now: 15px in from the container on both sides, for the search
   assembly, the breadcrumb, the title, the chip bar, the count and the cards.
   One vertical line down the left, one down the right.
   ========================================================================== */

@media (min-width: 992px) {

  /* --- one gutter, both edges ------------------------------------------
     The container already insets 15px. Every row inside it therefore has to
     contribute NOTHING - a Bootstrap `.row` normally pulls out by -15 and its
     `.col` pushes back in by +15, which nets to the container edge. Any row
     that has had its negative margin zeroed must have its padding zeroed too,
     or the col's own padding lands it 15px further in. That is exactly what put
     the chip bar at 30 while the title sat at 15. */
  body.ii-allow-sticky .ii-filter-row,
  body.ii-allow-sticky .ii-results-outer,
  body.ii-allow-sticky .our-listing .sticky-block .row.search-bar {
    margin-left: 0;
    margin-right: 0;
    padding-left: 0;
    padding-right: 0;
  }
  body.ii-allow-sticky .our-listing .sticky-block .row.search-bar > .col-12,
  body.ii-allow-sticky .ii-filter-row > .col-12,
  body.ii-allow-sticky .ii-results-outer > .col-12 {
    padding-left: 0;
    padding-right: 0;
  }
  body.ii-allow-sticky .ii-crumbs-col { padding-left: 0 !important; padding-right: 0 !important; }

  /* --- the rhythm, revised --------------------------------------------- */
  /* 16 below the nav rather than 24: the bar is the first thing on the page
     and does not need a section break above it, only separation. */
  body.ii-allow-sticky .our-listing .sticky-block .row.search-bar { padding: 16px 0 0; }

  /* Crumbs and title now take the SAME step as title -> filters. They were a
     bonded 6px pair, which read as cramped next to three 20s; one value for
     every step in the column is calmer than a considered exception. */
  body.ii-allow-sticky .ii-crumbs { margin-bottom: 20px !important; }

  /* --- the focus ring follows the assembly ----------------------------- */
  /* `.highlight-on-focus` rounds the inner .input-group to 999px and paints its
     own orange border and glow. Inside the joined bar that produced a PILL
     appearing on click over a square assembly - the reported "rounded corner
     when I click on it". The state belongs to the whole control, so the inner
     element gives it up and .ii-searchbox takes it. */
  .ii-searchbox .input-group.highlight-on-focus,
  .ii-searchbox .input-group.highlight-on-focus:focus-within {
    border-radius: 0 !important;
    border: 0 !important;
    box-shadow: none !important;
  }
  .ii-searchbox:focus-within {
    border-color: var(--ii-accent);
    box-shadow: 0 0 0 3px var(--ii-accent-subtle);
  }

  /* --- the submit, quieter than Vendre --------------------------------- */
  /* Ink at 18:1 was heavier than the accent it sits near - the eye went to
     Rechercher before Vendre, which is backwards for this business. This is the
     secondary button the design system already defines (surface + strong border
     + body text), so the search bar now carries no chromatic weight at all and
     Vendre is the only saturated thing on the screen. */
  /* Mid-grey, and the tone is the whole point.

     Ink (#111 light / near-white dark) was 18:1 and became the heaviest thing
     on the page - the eye reached Rechercher before Vendre, which is backwards
     for this business. The first correction went too far the other way: surface
     on a sunken field measured 1.04 against its own background, so the control
     survived only on a 1px border and stopped reading as a button at all.

     `--ii-text-secondary` as a FILL is the middle: #5A5A5A on light, #A1A1A6 on
     dark, so it separates clearly from the field in both themes while staying
     completely achromatic. Colour is what draws the eye, and this has none -
     Vendre is still the only saturated thing on the screen. */
  .ii-searchrow-btn {
    background: var(--ii-text-secondary);
    color: var(--ii-bg-canvas);
    border-left: 0;
  }
  .ii-searchrow-btn:hover { background: var(--ii-text); opacity: 1; }
}

/* ==========================================================================
   AD DETAIL - seller card and type scale
   ========================================================================== */

/* The avatar was rendering as an oval: no border-radius, and `object-fit: fill`
   stretches a non-square source to whatever box it lands in. Square box, cover,
   and a real circle - and a size that reads as a person rather than a favicon. */
.sl_creator .seller-img,
.sl_creator .img-block img {
  width: 48px;
  height: 48px;
  min-width: 48px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  background: var(--ii-bg-sunken);
}

/* 12px is below this product's own body minimum, and these three lines are the
   only trust signals on the page - the one thing a buyer reads before deciding
   whether to message a stranger. 13px with the label weight kept. */
.ii-trust .ii-verified,
.ii-trust .ii-member-since,
.ii-trust .ii-trust-count { font-size: 13px; }

/* Name is the person, handle is the address. Both stay - they answer different
   questions - but they are no longer the same size, so the eye lands on the
   name first and the handle reads as the profile it links to. */
.sl_creator .media-head h5 { font-size: 17px; font-weight: var(--ii-weight-semibold); }
.sl_creator .media-head h5 + p,
.sl_creator .media-head-left > div > p {
  font-size: 13px;
  color: var(--ii-text-muted);
  margin-top: 2px;
}

/* Price outranks the title on an ad page: the title says what it is, the price
   is the fact the decision turns on. They were both 24/700, which made the two
   compete instead of ranking; the price moves up rather than the title moving
   down, because the title carries an INLINE `font-size: 24px!important` and an
   inline !important is the one thing a stylesheet cannot outrank. Only the
   weight is set here - that part the inline does not claim. */
.single_property_title .property_title { font-weight: var(--ii-weight-semibold); }
.single_property_social_share .tz-new-price h4,
.single_property_social_share .lsd_list h4 { font-size: 28px; font-weight: var(--ii-weight-bold); }

/* The neighbourhood line under the title is secondary to both. */
.single_property_title p { font-size: 14px; color: var(--ii-text-muted); }

/* ==========================================================================
   HOME - the search bar pins under the fixed nav

   Different shape from the listing page, deliberately. There the nav LEAVES and
   the pinned stack carries search plus filters, because a visitor scanning
   results needs those controls and does not need the nav. Here there are no
   filters and the nav is the point of the page, so the nav stays fixed and the
   search bar pins directly beneath it - which is what the reference does on its
   own home page.
   ========================================================================== */

/* The banner carousel's arrows are positioned at left/right: -30px, so they
   hang 30px outside the carousel and - with the carousel at 15..1250 in a
   1265 viewport - 10px outside the VIEWPORT on both sides. `overflow-x: hidden`
   on the page was hiding that, and the home page now needs the overflow chain
   clear so its search bar can pin. Measured after opting out: scrollWidth 1275
   against a 1265 client width, and these two are the only things past the edge.

   Tucked just inside the carousel instead. They still overlay the banner edges,
   which is the intended look; they simply stop widening the document. */
.owl-carousel .owl-nav .owl-prev { left: 8px !important; right: auto !important; }
.owl-carousel .owl-nav .owl-next { right: 8px !important; left: auto !important; }

/* The pair above did not both take, and the asymmetry was visible: the left
   arrow hung outside the banner while the right one sat neatly inside it.

   style.css carries a rule for each, and they are not written the same way:

     .banner-card-slider .owl-controls .owl-nav .owl-prev { left: -30px !important }
     .banner-card-slider .owl-controls .owl-nav .owl-next { right: -30px }

   Four classes each, against three above. For `next` the design rule wins
   because `!important` beats a normal declaration whatever the specificity.
   For `prev` both are `!important`, so specificity decides and four classes
   beat three - the legacy value survives. ONE stray `!important`, on one of
   two sibling rules, is the whole difference.

   Matched at the legacy selector's own specificity rather than reaching for
   another `!important`, which is what §5.1 says to do and is the only thing
   that works when the other side already has one.

   NOT fixed by moving `next` out to -30 to match `prev`. The comment above
   records why they were tucked in: at -30 they hang outside the VIEWPORT,
   scrollWidth 1275 against a 1265 client, and the home page needs its
   overflow chain clear for the search bar to pin. Matching the broken side
   would have re-opened that. */
.banner-card-slider .owl-controls .owl-nav .owl-prev,
.listing_single_property_slider .owl-controls .owl-nav .owl-prev {
  left: 8px !important;
  right: auto !important;
}
.banner-card-slider .owl-controls .owl-nav .owl-next,
.listing_single_property_slider .owl-controls .owl-nav .owl-next {
  right: 8px !important;
  left: auto !important;
}

/* Straddling the banner edge: the circle's CENTRE sits on it.

   Half the control over the image, half over the page. Not tucked inside the
   banner, and not clear of it either - both of those were tried and neither is
   what the design asks for. The edge runs through the middle of the circle.

   THE ARITHMETIC, and the part that is easy to get wrong. The offset is NOT
   half the circle. `left`/`right` here resolve against a positioning context
   that sits 5px inside the banner box, established by measurement: at -57 the
   arrow landed at 141 with the banner at 193, so the origin is 198. A 44px
   circle centred on 193 starts at 171, which is 27 before that origin. Hence
   -27, where the obvious answer would have been -22.

   WHY THERE IS STILL A BREAKPOINT. Straddling reaches 22px past the banner, so
   it needs a content edge at least that far in: (vw - 1300) / 2 + 15 >= 22,
   which is about vw 1314. That is a much smaller demand than the 57 this
   replaced, so the gate comes down from 1440 to 1360 and the look now reaches
   the 1366 laptops it used to miss. Below it the rules above keep the arrows
   inside, because there is genuinely nowhere else to put them.

   1360 rather than the 1314 the arithmetic allows: measured at 1340 the arrow
   cleared the viewport edge by 6px, which is inside the margin of error for a
   scrollbar. 1360 measures ~15px clear at its own boundary.

   VERIFY WITH scrollWidth, NOT BY EYE. The failure this guards against is the
   document growing wider than the viewport, which looks like nothing at all
   until the sticky search bar stops pinning. */
@media (min-width: 1360px) {
  .banner-card-slider .owl-controls .owl-nav .owl-prev,
  .owl-carousel .owl-nav .owl-prev {
    left: -27px !important;
    right: auto !important;
  }
  .banner-card-slider .owl-controls .owl-nav .owl-next,
  .owl-carousel .owl-nav .owl-next {
    right: -27px !important;
    left: auto !important;
  }
}

@media (min-width: 992px) {
  /* The sticky goes on the SECTION, not on `.row.search-bar`: the row's parent
     `.container` is exactly the row's own height, so the range was 0 and it
     never pinned. `section.py-0` sits inside `.wrapper` at ~2575px. Third time
     this trap has cost time - check the parent's height before writing
     `position: sticky`. */
  /* `.wrapper >`, because there are TWO `section.py-0` on this page and the
     other one is inside <header>. A bare `section.py-0` made the header's copy
     sticky as well - harmless in effect, but it also meant every measurement
     of "the search section" was reading the wrong element.

     `.sticky-block` covers the profile and seller-listing pages, where the same
     search bar sits one wrapper deeper. Both pin under the fixed nav; neither
     takes `ii-nav-scrolls`, so the nav stays put on all three. */
  /* `.ii-home-stick` is that section, now named. It used to be addressed as
     `.wrapper > section.py-0`; the search has since been split into its own
     section so the category rail does not pin with it on a phone, and there
     are now two `.wrapper > section.py-0` on this page. Naming the one that
     pins is what keeps this rule pointing at the right element. */
  body.ii-allow-sticky:not(.ii-nav-scrolls) .ii-home-stick,
  body.ii-allow-sticky:not(.ii-nav-scrolls) .sticky-block {
    position: -webkit-sticky;
    position: sticky;
    /* The fixed desktop nav is 88px. */
    top: 88px;
    z-index: 45;
    background: var(--ii-bg-canvas);
  }
  /* Same 16px under the nav as the listing page, and the same below. */
  body.ii-allow-sticky:not(.ii-nav-scrolls) .search-bar {
    margin: 0 !important;
    padding: 16px 0;
  }
  /* The column too, and this is the half that was missing.

     Zeroing the ROW's negative margin without zeroing its column's padding
     counts the gutter twice: the row lands on the container's content edge
     and the column then insets it another 15px. Measured on the home page at
     1670px, the search field painted at 208 while the logo, the slider and
     the Vendre button all sat at 193 - the one control a visitor uses first,
     15px adrift from everything around it.

     The listing page never showed this because its own copy of the rule, in
     the .our-listing .sticky-block block above, zeroes both halves. The
     comment there already names this exact failure - "the col's own padding
     lands it 15px further in" - and the home page's rule was written without
     it. Constraint 21: the page grid is one measurement. */
  body.ii-allow-sticky:not(.ii-nav-scrolls) .search-bar > .col-12 {
    padding-left: 0;
    padding-right: 0;
  }
}

/* REVERSED 2026-08-28, on the owner's instruction, and the previous reasoning
   is kept because it was not wrong - it was outvoted.

   It read: "Below 992 the home search keeps its old behaviour: the mobile
   header is already fixed and already carries search within reach, and a
   second pinned band on a 375px screen is the one thing there is no room
   for." That cost is real and it is now being paid deliberately: the header
   (76px) plus the search (54px) is 130px of a 844px viewport - 15.4% of the
   screen given up permanently, and about 21% on the listing page once the
   filter chips join the stack.

   The owner asked for Carousell's arrangement explicitly, having used it:
   nav and search pinned as one block, so search is reachable at any scroll
   depth rather than only after a flick back to the top. On a marketplace
   where the whole funnel starts with a search, that is a defensible trade -
   Carousell, leboncoin and eBay all make it. It is a product decision, not a
   measurement, which is why it is recorded here rather than argued.

   `.sticky-block` is deliberately NOT included: that is the profile and
   seller-listing search, which was not part of the request and keeps its old
   behaviour. */
@media (max-width: 991px) {
  body.ii-allow-sticky:not(.ii-nav-scrolls) .ii-home-stick {
    position: -webkit-sticky;
    position: sticky;
    top: var(--ii-stick-nav);
    z-index: 40;
    /* SURFACE, not canvas. The nav is `--ii-bg-surface`; canvas here made the
       search a visibly different shade directly under it - #FFFFFF against
       #FAFAF9 - which is half of why the two read as separate objects rather
       than one block. Stated here because this rule owns the home sticky, and
       a second background further up would just lose to it on specificity. */
    background: var(--ii-bg-surface);
  }
  body.ii-allow-sticky:not(.ii-nav-scrolls) .sticky-block { position: static; }
}

/* ==========================================================================
   PROFILE > ANNONCES - segments and sort on one line
   ========================================================================== */

@media (min-width: 992px) {
  /* Segments left, Trier right, one row. They are the same control family -
     both narrow the same list - so stacking them read as two toolbars. */
  #pills-announcement .tab-content-head {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: var(--ii-space-4);
    flex-wrap: nowrap;
  }
  #pills-announcement .tab-content-head .ii-seg {
    margin-bottom: 0;
    flex: 1 1 auto;
    min-width: 0;
  }
  #pills-announcement .tab-content-head .right-caption {
    flex: 0 0 auto;
    margin-bottom: 0 !important;
  }
}

/* 13px was the segment label; these are the primary way an owner moves around
   their own listings, so they take the body size the rest of the page uses. */
.ii-seg-item { font-size: var(--ii-text-md); }
.ii-seg-count { font-size: 13px; }

/* While a segment is being fetched. Not a spinner and not a skeleton: the list
   that is already on screen is still true until the new one arrives, and
   covering readable content with a loading state is what constraint 25 exists
   to prevent. It just goes quiet and stops taking clicks. */
.ii-seg-busy { opacity: .55; pointer-events: none; }

/* --------------------------------------------------------------------------
   Home banner: the ratio the width/height attributes promise

   index.blade.php carries `width="1500" height="624"` on every slider image so
   the box is reserved before a byte arrives. Those attributes are only half of
   the contract - CSS owes the other half. With no sizing rule matching the
   image at all (the one selector that reached it, .banner-card-slider a img in
   style.css, sets border-radius and nothing else), the browser took the height
   attribute literally at 624px while the carousel item capped the width at
   613px. A 2.40:1 banner therefore rendered at 0.98:1: the artwork stretched
   to nearly square, standing 624px tall at every viewport.

   Measured on a 1366x660 laptop that was 95% of the viewport, with the first
   listing pushed to 837px - a marketplace home page whose opening screen held
   no products at all. On a 375px phone it was 77% of the viewport.

   `height: auto` hands sizing back to CSS and restores the intrinsic ratio.
   The aspect-ratio restates the reservation for uploads that are not exactly
   1500x624 - these images come from the admin slider table, so the sheet
   cannot assume the dimension - and object-fit covers rather than fills, so an
   odd-sized upload is cropped rather than distorted. Together they keep the
   CLS reservation the attributes were added for, which is why the fix is a
   rule here and not the removal of the attributes there.
   -------------------------------------------------------------------------- */
.banner-card-slider a img,
.banner-card-slider .item img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1500 / 624;
  object-fit: cover;
}

/* --------------------------------------------------------------------------
   Breadcrumb clearance under the pinned search bar

   The 20px that separates the breadcrumb from the pinned bar was written as
   `body.ii-allow-sticky .ii-crumbs-col { padding-top: 20px }` - keyed to a
   column wrapper that only the listing page has. The profile page puts the
   same partial straight into .container, so it matched nothing and the crumb
   had no clearance at all.

   Measured on /u/{username} at 1500x950 before this rule: the crumb's top sat
   at 141 and the pinned bar's bottom at 154, so "Accueil > Profil" rendered
   THIRTEEN PIXELS UNDERNEATH the bar - not merely tight, overlapped. At 375px
   the gap was 0. The listing page was fine throughout, which is why it went
   unnoticed: the page the rule was written on is the page it worked on.

   This is constraint 85 in its layout form. Extracting the breadcrumb into one
   partial (constraint 84) moved the markup to eight pages and left the spacing
   behind on one - so the rule now travels with the component it spaces, not
   with the page it was born on.

   padding, not margin: .ii-crumbs is a block box whose parent is a bare
   .container on the profile page and a padding-less column on the listing
   page. A margin-top would collapse straight through both and the gap would
   land outside the container, where it is one stray padding declaration away
   from vanishing again. Padding cannot collapse.

   Outside any media query, because the bar pins at both widths - 76px under
   the mobile header, and at the top on desktop.
   -------------------------------------------------------------------------- */
body.ii-allow-sticky .ii-crumbs { padding-top: 20px; }

/* The pending banner joins the same 20px step instead of keeping its own 14/4.
   Its own margins are correct where it appears alone (the ad page, the
   post-publish screen); this only applies where it follows a breadcrumb, so
   the column reads identically whether or not anything is under review - which
   is the case that matters, since the banner disappears entirely once nothing
   is held. Sibling margins already collapse to the larger value above it; the
   bottom is what was short, at 4px against a 20px rhythm. */
body.ii-allow-sticky .ii-crumbs + .ii-pending-banner { margin-top: 0; margin-bottom: 20px; }

/* --------------------------------------------------------------------------
   A pinned bar under a FIXED nav needs the page to start below its own pin

   `position: sticky` only reserves the space an element occupies naturally. If
   the element's natural position is ABOVE its `top`, the browser clamps it
   downwards and nothing fills the distance it moved - so everything after it
   is overlapped by exactly that difference, at rest, before any scrolling.

   That is what the profile page did. Its nav stays fixed, so the bar pins at
   `top: 88px`; the section's inline `padding-top: 0 !important` put the bar's
   natural top at 75. Measured at 1500x950, scrollY 0: displaced down 13px, and
   "Accueil > Profil" rendered 13px underneath the bar.

   The listing page shares the identical section markup and never showed it,
   because it also carries `ii-nav-scrolls` - the nav there scrolls away, the
   bar pins at `top: 0`, and a natural position of 92 can never be above zero.
   So the difference was never the markup; it was which of the two pinning
   models the page opted into.

   `ii-fixednav-top` states the requirement rather than a number: pages whose
   nav stays fixed give their first section enough room that the bar starts at
   or below where it pins. Keep this at or above the sticky `top` in the
   fixed-nav rule if that offset ever changes.
   -------------------------------------------------------------------------- */
body.ii-allow-sticky .our-listing.ii-fixednav-top { padding-top: 20px; }

@media (max-width: 991px) {
  /* The mobile bar pins at 76px under the mobile header, and the mobile header
     is shorter, so the same statement needs a smaller number here. */
  body.ii-allow-sticky .our-listing.ii-fixednav-top { padding-top: 12px; }
}

/* --------------------------------------------------------------------------
   Give the accent back to Vendre

   Three groups on the profile page wore the same orange fill: the Vendre CTA
   in the header, the Annonces / Favoris / Portefeuille tabs, and the
   Toutes / En ligne / En verification / Vendues status chips. An accent that
   appears on the one thing the business wants clicked, and also on the two
   things a seller merely reads past, is no longer an accent - it is the page's
   default colour, and Vendre competes with two controls that carry no action
   at all.

   This is not a new palette. It is the decision already taken for the search
   button - "Ink instead ... chromatically silent so the only saturated thing
   in the header is Vendre" - applied to the two groups that never got
   migrated. Every classified this product is measured against holds the same
   line: leboncoin keeps orange for "Deposer une annonce", Carousell keeps red
   for "Sell", Avito keeps its accent for posting. In all three the account
   tabs and the filter chips are achromatic.

   Deliberately NO semantic colour on the status chips - no green for En ligne,
   no amber for En verification. Three saturated hues in a row would re-create
   the problem in a new palette, and the state that actually needs explaining
   already has the pending banner above the list saying so in words.

   Two treatments, not one, because these are two different kinds of control
   and they currently look identical. Tabs move you between pages; chips filter
   the list in front of you. An underline reads as navigation and a filled pill
   reads as a selected value, so the pair stops being one undifferentiated
   double row of pills.
   -------------------------------------------------------------------------- */

/* Tabs: navigation. Ink label over an ink underline, no fill.
   !important and this exact specificity are both required - style.css declares
   `.top-bar-block .nav-pills .nav-link.active` at (0,3,0) WITH !important, so
   an equal-specificity rule LATER in this sheet is what wins. Same reasoning
   as the note beside the rule this supersedes. */
.top-bar-block .nav-pills .nav-link {
  background-color: transparent !important;
  color: var(--ii-text-secondary) !important;
  border-radius: 0;
  border-bottom: 2px solid transparent;
  transition: color var(--ii-dur-fast) var(--ii-ease),
              border-color var(--ii-dur-fast) var(--ii-ease);
}
.top-bar-block .nav-pills .nav-link:hover { color: var(--ii-text) !important; }
.top-bar-block .nav-pills .nav-link.active,
.top-bar-block .nav-pills .show > .nav-link {
  background-color: transparent !important;
  color: var(--ii-text) !important;
  border-bottom-color: var(--ii-text);
  border-radius: 0;
}

/* Chips: a selected value. Ink fill, canvas label - the same inversion the
   search button uses, so it flips with the theme and stays achromatic in both
   rather than needing a dark-mode exception. */
.ii-seg-item.is-on {
  background: var(--ii-text);
  border-color: var(--ii-text);
  color: var(--ii-bg-canvas);
}
.ii-seg-item.is-on .ii-seg-count { color: var(--ii-bg-canvas); opacity: .7; }

/* --------------------------------------------------------------------------
   The home banner holds its own space

   `.owl-carousel { display: none }` (slider.css:7) means the banner has no
   height at all until Owl adds `.owl-loaded`. The card grid therefore paints
   directly under the search bar and is then pushed down by the banner's entire
   height the moment the script runs. Measured at 1500x950 by toggling the
   class Owl toggles: first card at 213, then 475 - a 262px shift on the
   largest element above the fold, and the reason the page reads as though it
   loads twice.

   This is the same defect `.ii-gallery-skel` fixes on the ad page, and it gets
   the same shape of answer: a placeholder SIBLING, removed by one selector the
   moment `.owl-loaded` appears, so the banner arrives exactly where the
   placeholder stood.

   The geometry is derived, not guessed. custom.js runs this carousel with
   `margin: 10` and `items: 2` from 768px up, `items: 1` below - so two flex
   children with a 10px gap occupy precisely the width Owl gives its items,
   and 1500/624 (the ratio every slider image in the table has) turns that
   width into the exact height. It stays correct at widths nobody measured.

   Not a spinner and not a full-page cover: constraint 25, and the preloader
   this project already removed for putting 1,526ms of opaque white over a
   finished page.

   THAT PLACEHOLDER IS GONE, AND SO IS THE WAIT

   Reserving the space fixed the shift but left the real defect standing.
   Resource timing on this page, at 1500x950:

     banner image downloaded   1995ms
     jquery-3.3.1.js           2127ms
     custom.js (Owl init)      3205ms
     domContentLoaded          3982ms

   The largest image above the fold was complete at 2.0s and could not be shown
   until roughly 4.0s, because `display: none` held it back until a script ran.
   Two seconds of shimmer over an image that had already arrived is a more
   honest-looking page, not a faster one, and a placeholder for content you
   already have is scaffolding around a finished wall.

   So the carousel is simply not hidden any more. Before Owl initialises the
   markup is `.banner-card-slider > .item`, which is laid out here with the
   exact geometry Owl will use - flex children, 10px gap, one item below 768px
   and two above - so the real banner paints on the FIRST paint, from HTML, and
   Owl takes it over in place without moving anything.

   This is what leboncoin and Avito do with their hero: server-rendered, with
   the carousel as progressive enhancement rather than a precondition. It also
   means the page keeps working with JavaScript broken or slow, which the
   `.no-js` branch in slider.css was always reaching for.

   The `width`/`height` attributes and the aspect-ratio rule already reserve the
   box before the bytes arrive, so nothing needs to stand in for it.
   -------------------------------------------------------------------------- */
.banner-card-slider:not(.owl-loaded) {
  display: flex;
  gap: 10px;
  width: 100%;
}
.banner-card-slider:not(.owl-loaded) > .item {
  flex: 1 1 0;
  min-width: 0;
}
/* Only what Owl would show: two items from 768px up, one below. The rest stay
   out of the layout until Owl owns them. */
.banner-card-slider:not(.owl-loaded) > .item:nth-child(n + 3) { display: none; }

@media (max-width: 767px) {
  .banner-card-slider:not(.owl-loaded) > .item:nth-child(n + 2) { display: none; }
}

/* --------------------------------------------------------------------------
   The ad page's type, back on the scale

   Measured at 1440px: 27 distinct text styles on one page, NINE of them at
   sizes the scale does not contain - 28, 22, 18, 16 (three times), 11 - and two
   at weights the token set does not contain, 900 and 550. A page this simple
   needs about five roles, and the reason it reads as "some small, some big and
   bold, some fine" is that nothing was choosing between them.

   Nothing here invents a size. Every value below is one of
   12/13/15/17/20/24/32 and one of 400/500/600/700.

   PRICE. `<h4><b>6 456 547</b> <small>Fdj</small></h4>`, where the h4 was 28px
   and the `<b>` inside it computed to NINE HUNDRED - not a decision anyone
   made, but Bootstrap's `b, strong { font-weight: bolder }` escalating from an
   already-bold parent. Inter Black on a marketplace price is shouting. It is
   now 24/700, and the `<b>` inherits so it cannot climb again.

   PRICE VS TITLE. They are the two things a buyer reads, so they take the same
   SIZE and differ in weight: 24/700 against 24/600. The old 28-vs-24 gap made
   the price the loudest thing on the page while the title - the only element
   saying what is being sold - sat under it looking secondary. Equal size, one
   step of weight, and the price keeps its own line.

   SECTION HEADINGS. "Publié par" was 18/500 and "Meubles et décoration
   intérieure détails" was 17/600 - two sibling headings, two sizes, two
   weights. That single pair is most of what the eye reads as inconsistency.
   Both are 17/600 now.

   VALUES. The details table read label 13/500 against value 16/600. The
   hierarchy was right and the step was too big; 15/600 keeps the emphasis and
   puts the value on the same size as body copy.

   LINE HEIGHT. Several elements computed a ratio of exactly 1.0 - the price
   suffix and the @username - which sets type solid with no room to breathe.
   Body leading everywhere it reads as prose, display leading on the headings.
   -------------------------------------------------------------------------- */

/* Price. */
body.ii-ad-page .single_property_social_share .lsd_list h4 {
  font-size: var(--ii-text-2xl);
  font-weight: var(--ii-weight-bold);
  line-height: var(--ii-leading-display);
}
/* Stops `b, strong { font-weight: bolder }` compounding 700 into 900. */
body.ii-ad-page .single_property_social_share .lsd_list h4 b { font-weight: inherit; }
body.ii-ad-page .single_property_social_share .lsd_list h4 small {
  font-size: var(--ii-text-sm);
  font-weight: var(--ii-weight-medium);
  line-height: var(--ii-leading-body);
  color: var(--ii-text-secondary);
}

/* Title. One step of weight below the price, same size. */
body.ii-ad-page .property_title {
  font-size: var(--ii-text-2xl);
  font-weight: var(--ii-weight-semibold);
  line-height: var(--ii-leading-display);
}

/* Section headings - the pair that disagreed. */
body.ii-ad-page h4.mb25,
body.ii-ad-page .single_property_title + * h4 {
  font-size: var(--ii-text-lg);
  font-weight: var(--ii-weight-semibold);
  line-height: var(--ii-leading-display);
}

/* Details table: label, then value. */
body.ii-ad-page .detailsName {
  font-size: var(--ii-text-sm);
  font-weight: var(--ii-weight-medium);
  line-height: var(--ii-leading-body);
  color: var(--ii-text-secondary);
}
body.ii-ad-page .detailsName + b,
body.ii-ad-page .detailsName ~ b {
  font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-semibold);
  line-height: var(--ii-leading-body);
}

/* Text buttons and inline links sat a step above body copy at 16px. */
body.ii-ad-page .read-more-btn,
body.ii-ad-page .text-primary {
  font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-semibold);
  line-height: var(--ii-leading-body);
}

/* The seller block's meta, which set type solid at ratio 1.0. */
body.ii-ad-page .ii-seller-handle,
body.ii-ad-page .single_property_social_share .mb-0 {
  line-height: var(--ii-leading-body);
}

/* 11px is below the floor of the scale. The card is shared, so this is not
   scoped - a 12px minimum is right wherever the badge appears. */
.ii-card-verified { font-size: var(--ii-text-xs); }

/* --------------------------------------------------------------------------
   The ad page's breadcrumb stays on one line

   Its last crumb is a seller-typed listing title, which has no length limit -
   unlike every other page, where the crumbs are category names. Measured at
   375px: "Accueil > Mobilier & Décoration > Chambre à coucher complète..."
   wrapped to two lines, 49px tall. A breadcrumb that wraps has stopped being
   the one-line locator it exists to be, and the H1 directly beneath it already
   says the same words in full.

   So the trail keeps its shape and the current page yields: the ancestors never
   shrink, and the title crumb takes whatever room is left and ellipsises. The
   information lost is the only information duplicated a few pixels below.

   Scoped to this page. Everywhere else the last crumb is a short category name
   that fits, and clamping it there would be solving a problem those pages do
   not have.
   -------------------------------------------------------------------------- */
.listing-title-area .ii-crumbs {
  flex-wrap: nowrap;
  min-width: 0;
  margin-bottom: var(--ii-space-3);
}
.listing-title-area .ii-crumbs > a,
.listing-title-area .ii-crumbs > .ii-crumb-sep {
  flex: 0 0 auto;          /* ancestors and separators never compress */
  white-space: nowrap;
}
.listing-title-area .ii-crumbs [aria-current="page"] {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   The header and the category drawer, on the scale

   Eight distinct text styles across the two, and not one of them was on it.
   The header ran entirely at 16px, mostly at weight 300 - Inter LIGHT for the
   navigation that appears on every page. The drawer set 22px for its title and
   16px at weight 550 for its rows, and 550 is not a weight: there is no such
   face, so the browser rounds it and the result is whatever it lands on.

   Weight 300 is the part worth naming. Light is a display weight. At 15-16px
   on white it thins the strokes exactly where the eye needs them, on the one
   set of links every visitor uses to get anywhere, and it made "Vendre" - the
   action this business exists to collect - the same weight as a passive label.

   WHERE THESE VALUES WERE COMING FROM

   Not from any stylesheet. The drawer's sizes were inline style attributes in
   header.blade.php, which no stylesheet can outrank, and the header's 300 is
   inherited from the legacy style.css. So the design system had never governed
   the header at all - it had simply never been asked to.

   Three inline declarations were removed rather than overridden. There appeared
   to be 581 of them in that file; 579 are inside commented-out markup, and the
   drawer renders from a single loop at the end of the file. The real footprint
   was one h3, one category link and one subcategory link.

   NAV WEIGHT IS 500, NOT 400

   Navigation is scanned, not read. Medium holds its shape at a glance where
   regular starts to blur into the page, and it leaves 600 free to mean "this is
   an action" - which is what Vendre and Se connecter now use, and what
   separates them from the categories beside them.
   -------------------------------------------------------------------------- */

/* Header nav: the category links and "Toutes les catégories". */
header .title,
header .ace-responsive-menu .title {
  font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-medium);
}

/* The two actions, one step heavier than the things they sit beside. */
header .AddListingInA span,
header .font-weight-bold {
  font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-semibold);
}

/* Drawer title. */
.sidenav .off-canvas-header h3 {
  font-size: var(--ii-text-xl);
  font-weight: var(--ii-weight-semibold);
  line-height: var(--ii-leading-display);
}

/* Drawer rows: parent categories, then their children. The step is weight, not
   size - the list is already indented, so a second size would say the same
   thing twice and shrink the child rows below the tap-target comfort of the
   parents. */
.sidenav .category-link {
  font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-semibold);
}
.sidenav .subcategory-item {
  font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-regular);
}

/* The hover dropdown under each nav item. It only shows on hover, which is why
   it survived the first pass - a sweep that skips `display: none` cannot see
   it. One step lighter than the nav item that opens it: the parent is a
   destination, these are its contents.

   Written at the legacy theme's own specificity, not with !important:
   style.css sets this at `header.header-nav.menu_style_home_one
   .ace-responsive-menu ...`, which outranks a plain `header .sub-menu a` no
   matter how late it appears. DECISIONS 5.1 - specificity, not load order, is
   what beats the legacy theme. */
header.header-nav.menu_style_home_one .ace-responsive-menu .sub-menu a,
header.header-nav.menu_style_home_one.style2 .ace-responsive-menu .sub-menu a {
  font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-regular);
}

/* `.font-weight-bold` is a Bootstrap utility and carries !important, so it
   landed on 700 and made "Se connecter" heavier than Vendre - a secondary link
   outweighing the primary action. Matched here rather than by editing the
   class out of the markup, because the utility is used across the legacy
   theme and this is the header's business alone. */
header .font-weight-bold { font-weight: var(--ii-weight-semibold) !important; }

/* mmenu's drilldown panel titles - the bar carrying the back arrow and the name
   of the panel you are in. 22px is off the scale and larger than the drawer
   title it sits under; 17 is the compact-header step, and semibold because it
   is the only thing naming where the drill-down currently is.

   `.mm-sronly` matches the same rule and is screen-reader-only, so its size
   never renders - it is included because excluding it would mean writing a
   longer selector to exempt something invisible. */
.mm-navbar .mm-navbar__title,
.mm-navbar .mm-navbar-title,
.mm-navbar__title,
.mm-navbar-title {
  font-size: var(--ii-text-lg);
  font-weight: var(--ii-weight-semibold);
}

/* ==========================================================================
   LISTING STATISTICS MODAL
   ==========================================================================

   Rebuilt 2026-08-27. What was there showed a single unlabelled number, and
   after impressions and visitor counts were added it showed three unlabelled
   numbers on one line - the report that prompted this was a screenshot of
   "0", "1" and "affichage" overlapping.

   Four figures now, each one this application can actually produce:
   apparitions (listing_impressions), vues, visiteurs (distinct visitor_key)
   and contacts (funnel_events.contact_clicked). Nothing here is estimated.

   Three tiles across at every width, including 375px. Stacking them would
   push the chart below the fold on a phone, and the whole value of this panel
   is the COMPARISON between the three - shown a lot and rarely opened is a
   different problem from barely shown, and you can only see that side by
   side. At 375px each tile is ~105px, which fits a 20px number and a 12px
   label comfortably.
   -------------------------------------------------------------------------- */

.ii-stats { padding: var(--ii-space-4) var(--ii-space-4) var(--ii-space-5); }

/* --- period ------------------------------------------------------------- */
.ii-stats-range {
  display: flex;
  align-items: center;
  gap: var(--ii-space-3);
  margin-bottom: var(--ii-space-4);
}
.ii-stats-range-label {
  font-size: var(--ii-text-sm);
  color: var(--ii-text-secondary);
}
.ii-stats-seg { flex: 0 0 auto; margin-bottom: 0; }

/* ONLY what a <button> needs that an <a> does not.
   The first version reset `background` and `border` here as well, to
   "normalise the button" - and that removed the pill: the border, the surface
   and the selected ink fill are the entire visual treatment of .ii-seg-item.
   The control rendered as two words of plain text, one of them nearly
   invisible, which is what the seller reported. Reset the element, never the
   component. */
.ii-stats-seg .ii-seg-item {
  cursor: pointer;
  font-family: inherit;
  appearance: none;
  -webkit-appearance: none;
}

/* --- the three figures --------------------------------------------------- */
.ii-stats-tiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--ii-space-2);
  margin-bottom: var(--ii-space-5);
}
.ii-stat {
  background: var(--ii-bg-sunken);
  border: 1px solid var(--ii-border);
  border-radius: var(--ii-radius-control);
  padding: var(--ii-space-3);
  min-width: 0;
}
.ii-stat-head {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: var(--ii-text-sm);
  color: var(--ii-text-secondary);
  line-height: 1.3;
}
.ii-stat-head svg {
  width: 14px;
  height: 14px;
  flex: 0 0 14px;
  fill: currentColor;
}
.ii-stat-value {
  font-size: var(--ii-text-2xl);
  font-weight: var(--ii-weight-semibold);
  color: var(--ii-text);
  line-height: 1.15;
  margin-top: var(--ii-space-1);
  /* Tabular figures so the three tiles do not jitter as the numbers change
     between the 7- and 30-day views. */
  font-variant-numeric: tabular-nums;
}
.ii-stat-note {
  font-size: var(--ii-text-sm);
  color: var(--ii-text-muted);
  margin-top: 2px;
  line-height: 1.3;
}

/* --- chart --------------------------------------------------------------- */
.ii-stats-chart { margin-bottom: var(--ii-space-4); }
.ii-stats-chart-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--ii-space-3);
  margin-bottom: var(--ii-space-2);
}
.ii-stats-chart-title {
  font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-semibold);
  color: var(--ii-text);
}
.ii-insight-since {
  font-size: var(--ii-text-sm);
  color: var(--ii-text-muted);
  text-align: right;
}

/* --- footnote and actions ------------------------------------------------ */
.ii-stats-foot {
  font-size: var(--ii-text-sm);
  color: var(--ii-text-muted);
  line-height: 1.5;
  margin: 0 0 var(--ii-space-4);
}
.ii-stats-actions {
  display: flex;
  gap: var(--ii-space-2);
  flex-wrap: wrap;
}
/* Both secondary. Orange marks Vendre alone - a statistics panel carries no
   action the business is trying to collect, and an accent button here would
   compete with the one in the bottom nav (constraint 87). */
.ii-stats-actions .ii-btn {
  flex: 1 1 auto;
  min-width: 140px;
  text-align: center;
}

@media (max-width: 575px) {
  .ii-stats { padding: var(--ii-space-3) var(--ii-space-3) var(--ii-space-4); }
  .ii-stat { padding: var(--ii-space-2); }
  .ii-stat-value { font-size: var(--ii-text-xl); }
  /* The label wraps to two lines at this width rather than truncating - the
     words are the whole point of the tile. */
  .ii-stat-head { align-items: flex-start; }
  .ii-stats-actions .ii-btn { min-width: 0; }
}

/* The chart's empty state. A 350px blank grid was the largest element in the
   panel and said nothing; this says the same thing in two lines and lets the
   modal fit on one screen. */
.ii-stats-empty {
  margin: 0;
  padding: var(--ii-space-5) var(--ii-space-4);
  text-align: center;
  font-size: var(--ii-text-md);
  color: var(--ii-text-secondary);
  background: var(--ii-bg-sunken);
  border: 1px dashed var(--ii-border-strong);
  border-radius: var(--ii-radius-control);
  line-height: 1.5;
}
.ii-stats-empty span {
  display: inline-block;
  margin-top: var(--ii-space-1);
  font-size: var(--ii-text-sm);
  color: var(--ii-text-muted);
}

/* The panel sizes to its content and scrolls only if it truly has to.
   Bootstrap's .modal-lg is 800px; the statistics panel holds three tiles and a
   chart and looked stranded in it, with a scrollbar caused by the 350px chart
   rather than by the amount of content. */
.insight_modal { max-width: 640px; }

/* ONE scroll container, and it is the modal's, not the body's.
   `style.css` carries `.modal-body { max-height: 60vh }` - 540px on a 900px
   screen - so the body became its own scroll region and clipped its content
   while the dialog around it stayed short. That is the inner scrollbar in the
   report, and it is why the buttons sat below a fold that had no reason to
   exist: the panel is 602px, the screen is 900px.

   Enumerated rather than assumed - the cap is in style.css, not in anything
   this project wrote, which is why grepping the design sheet for it found
   nothing. Bootstrap scrolls `.modal` itself when a dialog genuinely exceeds
   the viewport, so removing this leaves exactly one scrollbar and only when
   one is needed. */
.insight_modal .modal-content { max-height: none; }
.insight_modal .modal-body.ii-stats {
  max-height: none;
  overflow: visible;
}

@media (max-width: 575px) {
  /* Full-width on a phone: a centred 640px card with margins wastes the only
     axis that is scarce. */
  .insight_modal { max-width: none; margin: var(--ii-space-3); }
}

/* --------------------------------------------------------------------------
   Opening a modal must not move the page

   Reported as "the whole body shifts a little to the left every time". It
   does, by exactly 7.5px, and the cause is the reverse of the usual one.

   Bootstrap assumes BODY owns the page scrollbar: `.modal-open` sets
   `overflow: hidden` on it, the scrollbar disappears, and its JS adds an
   inline `padding-right` equal to the scrollbar width so the content does not
   jump. Two of those three things are true here.

   On this site the scrolling element is `<html>`, not `<body>` - measured,
   `document.scrollingElement` is html and `body.scrollHeight` never exceeds
   its client height. So `.modal-open`'s overflow rule hides nothing, the
   scrollbar stays (innerWidth - clientWidth remains 15 with the modal open),
   and the padding is added anyway. The content box loses 15px on the right
   while keeping its scrollbar, and anything centred moves left by half of it.

   The compensation is for a scrollbar that never goes away, so it is removed
   rather than corrected. `!important` because Bootstrap writes that padding
   as an INLINE style from JavaScript - an important author declaration is the
   one thing that outranks one (cousin of constraint 91, where the same inline
   write could not be beaten because the rule was not important).

   Applies to every modal on the site, not just the statistics panel: they all
   go through the same Bootstrap path and all shifted.
   -------------------------------------------------------------------------- */
body.modal-open {
  padding-right: 0 !important;
}

/* ==========================================================================
   ACCOUNT PAGE - alignment and the surface underneath the form
   ==========================================================================

   Two reports, both visible on a wide screen, both measured at 1900px.

   1. THE FORM'S PANEL EXTENDED PAST THE FORM. Three surfaces were painted on
      this page, and the middle one was doing nothing:

        media-head (sidebar card)   300.5 .. 609.5    white + border
        profile-top-wapper          625.5 .. 1584.5   white, NO border
        ii-acct-card (the form)     635.5 .. 1275.5   white + border + shadow

      `.ii-acct-card` is capped at 640px on purpose - a form read at 950px is a
      form nobody finishes - but its wrapper still painted white across the
      whole 959px column. The 309px to the right of the card was white on
      white, which is exactly "the border extends": one bordered card sitting
      in the left half of a larger, edgeless white panel.

      The wrapper carries no border and no shadow, so it was never a card. It
      is transparent now and the page background shows through; the only
      surface left is the one that was designed to be one.

   2. THE CARDS SAT 7px LEFT OF THE BREADCRUMB. `.container` puts its content
      edge at 307.5 and the breadcrumb is there. The row cancels the
      container's 15px padding with `margin-left: -15px`, and its columns
      re-add only 8 (`px-2`) - so every card landed at 300.5.

      A Bootstrap row cancels the container's padding so its columns can put it
      back, and the two numbers have to match. This is the same defect 1.32
      found on the profile page, where the element that LOOKED wrong was the
      one that was right.

      Corrected with `--ii-grid-inset` rather than `mx-n2` in the markup:
      the columns lose their `px-2` below 992px, so a hard -8px would pull the
      cards 8px off the gutter on a phone. The token is 8px on desktop and 0
      below, so the cancellation follows the padding by itself. Constraint 21.
   -------------------------------------------------------------------------- */
.account-tab-wapper.profile-top-wapper {
  background: transparent;
  /* style.css gives `.sidebar_listing_list` 10px of padding. That was inset
     from the edge of a painted card; with the card gone the padding only
     pushed the form 10px right of its own column, so the two cards on this
     page each sat a different distance from their column edge. Scoped to the
     account wrapper - the seller profile uses the same class and still wants
     it. */
  padding: 0;
}

@media (min-width: 992px) {
  .ii-account-row {
    margin-left: calc(var(--ii-grid-inset) * -1);
    margin-right: calc(var(--ii-grid-inset) * -1);
  }

  /* ...and the two cards start on the same line. They were 5px apart, from
     two different causes on two different elements: the sidebar's wrapper
     carries 10px of top padding, the form card carries a 15px top margin.
     Neither is wrong on its own, which is why the gap survived - it only
     shows when the two are read side by side, and only above 992px where
     they are. Both go to the column top; below that the cards stack and the
     spacing between them is what matters instead. */
  .ii-account-row > .col-lg-3 > .ii-sidepanel { padding-top: 0; }
  .ii-account-row .ii-acct-card { margin-top: 0; }
}

/* 22px is not on the scale. 20 is, and it is the step below 24 - the account
   heading is a page title inside a card, not a page title on a page.
   Constraint 87, and one of the six off-scale sizes L67 counts here. */
.ii-acct-title { font-size: var(--ii-text-xl); }

/* The sidepanel's own top spacing, moved off an inline `style` attribute in
   layouts/accountSidepanel.blade.php so it can be overridden at all. Same
   10px, same six pages; only the account page above opts out of it, and only
   where the two cards sit side by side. */
.ii-sidepanel { padding-top: 10px; }

/* The account breadcrumb sits directly in `.container`, so it takes that
   element's 15px and not `--ii-page-gutter`, which is 12 below 992px. The
   cards in the row beneath resolve to 12. Three pixels, and exactly the
   near-miss constraint 21 was written about - "a literal 15 looked right on
   desktop and put the breadcrumb 3px out at 375px".

   The listing page solved this with `.ii-crumbs-col`, a column wrapper this
   page does not have; the breadcrumb is a direct child here. So the offset is
   applied to the breadcrumb itself, derived from the token rather than
   written as -3px, which keeps it correct if the gutter ever changes. Only
   below 992px: above it the container's 15 and the token's 15 agree and the
   calc resolves to zero anyway. */
@media (max-width: 991px) {
  .ii-page-account .ii-crumbs {
    margin-left: calc(var(--ii-page-gutter) - 15px);
  }
}

/* ==========================================================================
   "MODIFIER LE PROFIL" - geometry, from docs/mockups/iibiy-mon-compte.html
   ==========================================================================

   Every rule here is nested under `.ii-acct-card` so nothing reaches another
   page. The card already existed and already carried these class names; what
   changes is the geometry, which the mockup owns.

   THE ONE IDEA: the card inline padding is the only alignment reference.
   Fields never carry a width - they are 100% of a row whose width the card
   decides - so a field cannot disagree with the hairline above it or the
   button below it. Everything that follows is that rule applied.

   LOGICAL PROPERTIES THROUGHOUT (padding-inline, inset-inline, margin-inline)
   because this product may be served in Arabic, and left/right values silently
   mirror wrong under dir="rtl". The mockup is written this way for the same
   reason.
   -------------------------------------------------------------------------- */
.ii-acct-card {
  --acct-pad: 32px;          /* the alignment reference for the whole card */
  --acct-control-h: 48px;
  --acct-radius: 10px;
  --acct-trailing-w: 120px;  /* shared by Verifier and Modifier so they line up */
  --acct-gap: 20px;          /* vertical rhythm between fields */

  padding: 0;                /* sections own their padding now, so the hairlines
                                run the full inner width */
  overflow: hidden;          /* keeps the hairlines inside the rounded corners */
}

/* --- heading ------------------------------------------------------------- */
.ii-acct-card .ii-acct-head {
  padding-inline: var(--acct-pad);
  padding-block: var(--acct-pad) 20px;
  margin-block-end: 0;
}

/* --- sections and their hairlines ---------------------------------------- */
.ii-acct-card .ii-acct-body,
.ii-acct-card .ii-acct-block {
  padding-inline: var(--acct-pad);
  padding-block: 22px;
  border-block-start: 1px solid var(--ii-border);
}

.ii-acct-card .ii-acct-sec {
  display: flex;
  gap: 12px;
  margin-block-end: 18px;
}
.ii-acct-card .ii-acct-sec-icon {
  flex: 0 0 auto;
  inline-size: 34px;
  block-size: 34px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--ii-accent-subtle);
  color: var(--ii-accent);
}
.ii-acct-card .ii-acct-sec-text strong {
  display: block;
  font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-semibold);
}
.ii-acct-card .ii-acct-sec-text small {
  display: block;
  font-size: var(--ii-text-sm);
  color: var(--ii-text-secondary);
  max-inline-size: 62ch;
}

/* --- fields: one per row, never side by side ----------------------------- */
.ii-acct-card .ii-acct-field {
  display: flex;
  flex-direction: column;
  min-inline-size: 0;          /* lets a flex child actually shrink */
  margin-block-end: var(--acct-gap);
}
.ii-acct-card .ii-acct-field:last-child { margin-block-end: 0; }

.ii-acct-card .ii-acct-flabel {
  font-size: var(--ii-text-sm);
  font-weight: var(--ii-weight-semibold);
  margin-block-end: 7px;
}

/* NO px width on any control. The row is the width; the field fills it. */
.ii-acct-card .form-control,
.ii-acct-card .ii-acct-textarea,
.ii-acct-card select.form-control {
  inline-size: 100%;
  box-sizing: border-box;
  block-size: var(--acct-control-h);
  padding-inline: 14px;
  border: 1px solid var(--ii-border);
  border-radius: var(--acct-radius);
  background: var(--ii-bg-surface);
  color: var(--ii-text);
  font: inherit;
}
.ii-acct-card .ii-acct-textarea {
  block-size: 112px;
  padding-block: 12px;
  resize: vertical;
  line-height: 1.55;
}

/* --- control + trailing button ------------------------------------------ */
.ii-acct-card .ii-acct-emailrow,
.ii-acct-card .ii-acct-phone {
  display: flex;
  gap: 10px;
  align-items: stretch;
  min-inline-size: 0;
}
.ii-acct-card .ii-acct-emailrow > .ii-acct-inputwrap,
.ii-acct-card .ii-acct-phone > .ii-acct-phone-input {
  flex: 1;
  min-inline-size: 0;
}

/* One fixed width for both trailing buttons, so Verifier and Modifier have
   identical widths and their end edges sit on the same axis as every field
   end edge. */
.ii-acct-card .ii-acct-emailrow > .ii-acct-btn-ghost,
.ii-acct-card .ii-acct-phone > .ii-acct-btn-ghost {
  flex: 0 0 var(--acct-trailing-w);
  inline-size: var(--acct-trailing-w);
  block-size: var(--acct-control-h);
  border: 1px solid var(--ii-border-strong);
  border-radius: var(--acct-radius);
  background: var(--ii-bg-sunken);
  font-size: var(--ii-text-sm);
  font-weight: var(--ii-weight-bold);
  box-shadow: 0 1px 2px rgba(23, 23, 26, .04);
}

/* --- helper text, counter, and the error slot ---------------------------- */
.ii-acct-card .ii-acct-hint {
  display: block;
  margin-block-start: 7px;
  font-size: var(--ii-text-sm);
  color: var(--ii-text-muted);
}
.ii-acct-card .ii-acct-counter-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-block-start: 7px;
}
.ii-acct-card .ii-acct-counter-row .ii-acct-hint { margin-block-start: 0; }
.ii-acct-card .ii-acct-count {
  position: static;            /* was absolutely positioned inside the field */
  font-size: var(--ii-text-sm);
  color: var(--ii-text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-align: end;
}
.ii-acct-card .ii-acct-count.is-near { color: var(--ii-accent); }

/* The error sits in the SAME slot as the helper text, so showing one does not
   move the field below it. The mockup has no error state; this is it, in the
   same visual language. */
.ii-acct-card .ii-acct-err {
  display: block;
  margin-block-start: 7px;
  font-size: var(--ii-text-sm);
  color: var(--ii-danger, #C0392B);
}
.ii-acct-card .ii-acct-err:empty { display: none; }
.ii-acct-card .form-control.parsley-error,
.ii-acct-card .parsley-error .form-control { border-color: var(--ii-danger, #C0392B); }

/* --- footer -------------------------------------------------------------- */
.ii-acct-card .ii-acct-actions {
  padding-inline: var(--acct-pad);
  padding-block: 20px;
  border-block-start: 1px solid var(--ii-border);
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 12px;
  margin: 0;
}
.ii-acct-card .ii-acct-btn-ghost,
.ii-acct-card .ii-acct-btn-primary {
  block-size: var(--acct-control-h);
  border-radius: var(--acct-radius);
  font-size: var(--ii-text-md);
  font-weight: var(--ii-weight-semibold);
}
.ii-acct-card .ii-acct-actions .ii-acct-btn-ghost { padding-inline: 22px; }
.ii-acct-card .ii-acct-actions .ii-acct-btn-primary { padding-inline: 26px; }

/* --- 640px and below ----------------------------------------------------- */
@media (max-width: 640px) {
  .ii-acct-card { --acct-pad: 20px; }

  /* The button drops under its input rather than squeezing it - the input
     keeps the full row and the alignment survives. */
  .ii-acct-card .ii-acct-emailrow,
  .ii-acct-card .ii-acct-phone { flex-wrap: wrap; }
  .ii-acct-card .ii-acct-emailrow > .ii-acct-btn-ghost,
  .ii-acct-card .ii-acct-phone > .ii-acct-btn-ghost { flex: 1 0 100%; inline-size: 100%; }

  .ii-acct-card .ii-acct-actions { flex-direction: column-reverse; align-items: stretch; }
  .ii-acct-card .ii-acct-actions .ii-acct-btn-ghost,
  .ii-acct-card .ii-acct-actions .ii-acct-btn-primary { inline-size: 100%; }
}

/* --------------------------------------------------------------------------
   ...and the correction that made the above actually hold.

   Measured after the first pass: five fields on THREE different end edges and
   two different start edges. The cause is nesting, not styling.

   - `.my_profile_wapper` is a Bootstrap `.row`, so it carries -15px inline
     margins and pulled everything inside the card off the card padding.
   - The fields are NOT all siblings. Email and Region sit inside
     `.ii-acct-body`; the phone field renders outside it, as a direct child of
     the form. Anything that put the padding on a section container therefore
     padded some fields and not others - which is exactly what the three end
     edges were.

   So the inline padding is anchored to the FIELD, not to whatever wrapper it
   happens to sit in. A field is aligned because it is a field, at any depth.
   The section containers keep only their hairline, which is why the rules run
   the full inner width while the fields inside them are inset.
   -------------------------------------------------------------------------- */
.ii-acct-card .my_profile_wapper,
.ii-acct-card .row {
  margin-inline: 0;
  /* ...and its PADDING, which a responsive rule gives it below 992px. Zeroing
     only the margin left a 10px inset that rode on top of the card padding, so
     the card measured 31 at 390px and 43 at 768px where the spec says 20 and
     32. The card padding is the only inset in here. */
  padding-inline: 0;
}

/* Sections: hairline only. No inline padding, so the rule spans the card. */
.ii-acct-card .ii-acct-body,
.ii-acct-card .ii-acct-block {
  padding-inline: 0;
}

/* Everything that is CONTENT gets the one padding value.

   Padded by SECTION CHILD rather than by field, which is the correction. The
   first version listed `.ii-acct-field` and friends by name, and anything that
   was not one of those names missed out: the email hint is a direct child of
   `.ii-acct-body` rather than of its field, and the photo section has no field
   at all, only a label and a row. Both sat 40px to the start of everything
   else - the hint visibly so, beginning outside the input it describes.

   `> *` covers whatever a section happens to contain, including elements added
   later. The phone field is listed separately because it renders outside
   `.ii-acct-body`, as a direct child of the form. */
.ii-acct-card .ii-acct-body > *,
.ii-acct-card .ii-acct-block > *,
.ii-acct-card form > .ii-acct-field,
.ii-acct-card .ii-acct-head,
.ii-acct-card .ii-acct-actions {
  padding-inline: var(--acct-pad);
}

/* ...and nothing nested inside a padded child pads again. */
.ii-acct-card .ii-acct-body > * > .ii-acct-field,
.ii-acct-card .ii-acct-block > * > .ii-acct-field {
  padding-inline: 0;
}

/* The trailing button is a descendant, not always a direct child - the email
   input is wrapped in `.ii-acct-inputwrap` for its leading icon. */
.ii-acct-card .ii-acct-emailrow .ii-acct-btn-ghost,
.ii-acct-card .ii-acct-phone .ii-acct-btn-ghost {
  flex: 0 0 var(--acct-trailing-w);
  inline-size: var(--acct-trailing-w);
  block-size: var(--acct-control-h);
  border-radius: var(--acct-radius);
}

/* The phone group is one joined control: prefix + number share a border, and
   the button sits outside it. Without this the prefix and the input each drew
   their own box. */
.ii-acct-card .ii-acct-phone {
  display: flex;
  gap: 10px;
  align-items: stretch;
  min-inline-size: 0;
}
.ii-acct-card .ii-acct-phone-code {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  padding-inline: 14px;
  background: var(--ii-bg-sunken);
  color: var(--ii-text-secondary);
  border: 1px solid var(--ii-border);
  border-start-start-radius: var(--acct-radius);
  border-end-start-radius: var(--acct-radius);
  border-inline-end: 0;
  font-size: var(--ii-text-md);
}
.ii-acct-card .ii-acct-phone-input {
  flex: 1;
  min-inline-size: 0;
  border-start-start-radius: 0;
  border-end-start-radius: 0;
}

/* The email input keeps room for its leading icon. */
.ii-acct-card .ii-acct-inputwrap { position: relative; flex: 1; min-inline-size: 0; display: flex; }
.ii-acct-card .ii-acct-haslead { padding-inline-start: 42px; }

@media (max-width: 640px) {
  /* The prefix stays joined to the number; only the BUTTON wraps. */
  .ii-acct-card .ii-acct-emailrow .ii-acct-btn-ghost,
  .ii-acct-card .ii-acct-phone .ii-acct-btn-ghost {
    flex: 1 0 100%;
    inline-size: 100%;
  }
}

/* The form is a flex ITEM of `.row.my_profile_wapper`, with no col class and
   no basis, so it shrank to its content and ended 19.9px inside the card -
   which is why the inline padding measured 32.8 at the start and 52.7 at the
   end, and why the section hairlines stopped short of the card edge. Making it
   fill its row is what lets the one padding value on the fields actually be
   symmetric. */
.ii-acct-card form {
  flex: 1 1 100%;
  inline-size: 100%;
  min-inline-size: 0;
}

/* The second px cap, and the reason the first fix did not finish the job:
   `#pills-editProfile .form-control { max-width: 560px }` carries an ID, so it
   outranks any class-only rule scoped to the card. With the field row at
   574.4px it held the plain inputs 14.4px short of the wrapped controls, which
   is why "nom" and "bio" ended on one edge and region, email and phone on
   another.

   Same reasoning as the 320px cap above: the approved mockup puts the width on
   the ROW and never on the control. Matched at (1,2,0) so it actually wins
   rather than looking like it should. */
#pills-editProfile .ii-acct-card .form-control,
#pills-editProfile .ii-acct-card .ii-acct-textarea,
.ii-acct-card .form-control,
.ii-acct-card .ii-acct-textarea {
  max-inline-size: none;
}

/* The photo section sits in a Bootstrap `.col-12`, which carries 15px of its
   own inline padding. That inset its hairline 15px from the card edge while
   the other two ran the full width - three rules, two lengths. The columns
   inside this card carry no layout of their own; the card padding is the only
   inset that should exist here. */
.ii-acct-card [class*="col-"] {
  /* `!important` because responsive.css sets `padding: 12px !important` on
     every column below 500px, and an important declaration is the only thing
     that outranks one. Without it the photo section - the one that lives in
     the avatar form, inside its own column - kept a 12px inset on a phone
     while the profile form did not, so the card showed two hairline lengths
     and two content edges. Constraint 20: the spacing utilities cannot be
     beaten by specificity alone. */
  padding-inline: 0 !important;
}

/* Both section containers span the card explicitly. The photo section lives in
   a second <form> (the avatar upload) inside its own column, so it reached the
   card edge at some widths and stopped 12px short at others - two hairlines,
   two lengths, depending on viewport. Stating the width once removes the
   dependency on how deep each section happens to sit. */
.ii-acct-card .ii-acct-body,
.ii-acct-card .ii-acct-block,
.ii-acct-card .ii-acct-actions,
.ii-acct-card .tab-content-body,
.ii-acct-card .m-w-100 {
  inline-size: 100%;
  max-inline-size: none;
  min-inline-size: 0;
}

/* --------------------------------------------------------------------------
   Region field, and a little more room across the card
   --------------------------------------------------------------------------

   THE REGION FIELD WAS BROKEN BY MY OWN RULE. Giving every control a uniform
   `padding-inline: 14px` overwrote the offset the two icon-leading controls
   need. The email input was exempted explicitly and the region select was not,
   so its pin sat exactly on the first character: icon 446..462, text starting
   at 446, a 16px overlap - the width of the icon.

   It also had `appearance: none` with nothing drawn in its place, so a select
   looked like a text input and gave no sign it could be opened. The caret is
   drawn here as a background image rather than an extra element, because the
   markup is shared and this is presentation.

   PADDING. Asked for a little more room on both sides and left to choose: 40px
   on desktop, 24px at 640 and below, up from 32/20. The card is 640px wide, so
   40 is a shade over 6% a side - enough to stop the fields reading as flush to
   the border, not so much that the inputs lose usable width. Both values still
   come from the single --acct-pad token, so the fields, the section headings
   and the footer buttons all move together and the alignment measured earlier
   is preserved by construction.
   -------------------------------------------------------------------------- */
.ii-acct-card {
  --acct-pad: 40px;
}

@media (max-width: 640px) {
  .ii-acct-card { --acct-pad: 24px; }
}

/* Clear the leading pin, matching the email input exactly: icon at 14, 16
   wide, so text starts at 42 and keeps 12px of air. */
.ii-acct-card .ii-acct-select .form-control,
.ii-acct-card #iiRegion {
  padding-inline-start: 42px;
  padding-inline-end: 40px;   /* room for the caret below */
}

/* The caret. A background image, so no element is added to shared markup and
   nothing has to be mirrored by hand - the position is logical. */
.ii-acct-card .ii-acct-select .form-control,
.ii-acct-card #iiRegion {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%235A5A5A' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 16px 16px;
}
[dir="rtl"] .ii-acct-card .ii-acct-select .form-control,
[dir="rtl"] .ii-acct-card #iiRegion {
  background-position: left 14px center;
}

/* ==========================================================================
   One pinned stack on a phone: the header, the search, and the filter chips

   Asked for against two references, and they are the same request twice:
   Carousell's home (nav and search pinned as one block) and this project's own
   DESKTOP listing header (logo + search on one row, filter chips on the next,
   both pinned). The desktop version already exists - it is the
   `@media (min-width: 992px)` block further up, built in §1.33. Mobile had
   only half of it: the search pinned under the header, and the filter chips
   scrolled away.

   The arithmetic is stated once, here, rather than as three magic numbers:

     --ii-stick-nav      76px   `#page`, the FIXED mobile header. Not sticky -
                                it is already `position: fixed` on both pages,
                                which is why nothing needs to change about it.
     --ii-stick-search   54px   the pinned search row.

   The filter row pins at nav + search MINUS ONE PIXEL. That is not sloppiness,
   it is the fix the desktop block records: two separately-pinned elements
   whose heights land on a fraction - device pixel ratio, zoom, a 1px border -
   leave a sub-pixel seam, and the listing photos scrolling underneath show
   through it as a flickering strip. Overlapping by a pixel closes it in a way
   that more precise arithmetic cannot.

   Both sticky elements are chosen for their PARENT's height, not their own
   (constraint 79, which this page has now cost time on three times):

     .ii-home-stick   parent .wrapper          - the whole page
     .ii-filter-row   parent the page container - ~1742px
                      (NOT .ii-filter-bar, whose .col-12 is exactly the bar's
                       own height: zero range, never pins)

   Backgrounds are opaque and themed: a translucent pinned bar shows the grid
   sliding under it, which reads as a rendering fault rather than a surface.
   ========================================================================== */
:root {
  /* 75, not 76. The mobile nav lost its 1px bottom border when the nav and the
     search became one block, and its box shrank with it - so a search pinned
     at 76 left a 1px slot with the page scrolling through it. Measured: nav
     bottom 75, search top 76. The same class of seam the -1px overlap exists
     to prevent, arrived at from the other direction. */
  --ii-stick-nav: 75px;
  --ii-stick-search: 54px;
}

/* On the listing page the search row is a different height from home's - the
   field is the desktop assembly at 48px plus 8px of breathing room top and
   bottom, so 64 rather than 54. Stated as the variable rather than as a second
   number in the `top` calc, because the filter row's offset is DERIVED from
   this and the two must not be able to disagree.

   They disagreed once, and it is the defect this fixes: the field was taken to
   44px without updating the variable, so the chips pinned at 53 while the
   search ended at 48 - a FIVE PIXEL slot with the results grid scrolling
   through it. Sub-pixel seams are a known hazard here and are handled by the
   -1px overlap; a 5px one is arithmetic, and arithmetic that lives in two
   places will drift again. */
/* 62, not 64. The field was 48 because it carried TWO borders - its own, and
   the inner `.input-group`'s, which the base rule had been painting inside it
   all along. Neutralising the inner box so only `.ii-searchbox` paints took
   the field to its honest 46 (a 44px input in a 1px border), and the block
   with it: 8 + 46 + 8.

   Caught by measuring rather than by looking: the chips pinned at 63 while the
   search ended at 61.6, a 1.4px slot with the results grid scrolling through
   it. Third time this arithmetic has drifted, and the third time the variable
   was the thing that had to move. */
body.ii-nav-scrolls { --ii-stick-search: 62px; }

/* And home's, for the same reason. The block is the field (44px input inside a
   1px border, so 46) plus 8px under it plus the 1px hairline: 55.

   It has now been 54, then 70, now 55, and every one of those was measured
   rather than guessed - which is the only reason the number can move at all
   without something sliding under a pinned bar.

   :root keeps 54 rather than being changed here, because it is not only home's
   number - user_listing and user_profile pin the old unpadded pill under the
   same fixed nav, and their blocks really are 54. */
body.ii-home { --ii-stick-search: 55px; }

@media (max-width: 991px) {
  /* HOME's sticky itself is NOT stated here. It lives with the desktop rule
     that already owns this decision, further down - one place, not two. */

  /* ---- ONE BLOCK -------------------------------------------------------
     The nav and the search read as a single object, the way Carousell's do.
     They were two, and measurably so rather than as a matter of taste:

       #page              background #FFFFFF   + its own 1px bottom border
       the pinned search  background #FAFAF9   (--ii-bg-canvas)

     Two shades and a rule between them is three separate signals that these
     are different things. So: one surface for both, and ONE edge, at the
     bottom of whatever the pinned stack ends with - the search on home, the
     filter chips on the listing page. The nav keeps its border at >=992px,
     where it genuinely is a separate bar.

     `--ii-bg-surface`, not canvas: the page behind is canvas, so the block
     lifts off it instead of blending into it, and the sunken search field
     still reads as a field on top of the block. */
  #page.stylehome1 { border-bottom: 0; }

  /* ---- and the last signal: 16px of CANVAS between the two ---------------
     The shades and the rule between them were dealt with above, and the pair
     still read as two objects on home. The cause was not colour, it was a
     GAP: `.mobile-header-set` - the empty spacer that reserves room for the
     fixed nav - carries `margin-bottom: var(--ii-space-4)`, and on home the
     very next thing in flow is the pinned search. So a 16px stripe of canvas
     (#FAFAF9) sat between two surface (#FFFFFF) bars.

     It showed only at the top of the page: the search pins at 75 and the gap
     collapses on the first scroll, so the block assembled itself correctly
     the moment you moved and looked broken before you did. That is the worst
     version of this defect, not the mildest - first paint is where a visitor
     decides what these things are.

     Closing it costs no vertical room. The 16px comes back as the 8px above
     and below the pill (see the search-bar rule below), so the fold does not
     move: nav 0..75, search 75..145, against 75 + 16 + 54.4 = 145.4 before.
     The space was moved from BETWEEN the bars to INSIDE the block, which is
     the whole difference between two objects and one.

     Scoped by `body.ii-home`, not by `:not(.ii-nav-scrolls)`: user_listing
     and user_profile carry the same sticky class, and their search sits in a
     `.sticky-block` well down the page rather than directly under the nav -
     there the margin is still doing its real job. */
  body.ii-home .mobile-header-set { margin-bottom: 0; }

  body.ii-allow-sticky .ii-home-stick,
  body.ii-allow-sticky.ii-nav-scrolls .our-listing .sticky-block,
  body.ii-allow-sticky.ii-nav-scrolls .ii-filter-row {
    background: var(--ii-bg-surface);
  }

  /* The single edge, on the last row of the stack. */
  body.ii-allow-sticky .ii-home-stick,
  body.ii-allow-sticky.ii-nav-scrolls .ii-filter-row {
    border-bottom: 1px solid var(--ii-border);
  }

  /* Room around the field. It had none - the control sat flush against both
     edges of its own row, which is what made the bar feel cramped and made
     the block below it look like a separate slab rather than a continuation.

     `!important` on the padding is required, and it is constraint 97's case
     rather than a shortcut: `custom.css` states `.search-bar { padding: 0
     !important }` under `@media (max-width: 768px)`. An important author
     declaration cannot be beaten by specificity, so the first version of this
     rule lost silently - the row stayed 48px tall while the variable said 64,
     which turned the 5px slot into a 15px one. Checked before reaching for
     it, which is the part that matters.

     THIS COMMENT WAS CLOSED TWICE. A stray close-comment token after the word
     "continuation" ended it, and the eight lines of prose that followed were
     then parsed as CSS: the backtick sentence became one garbage qualified
     rule, and the remaining prose swallowed the HOME rule below as its
     selector. (Which is also why no close-comment token is written out
     anywhere in this paragraph - doing exactly that is what broke it, and
     spelling it out inside the explanation breaks it a second time.)
     Both were dropped as invalid, silently, which is how the home search row
     kept `padding: 0` from custom.css while this file said otherwise - a
     comment that closes early does not fail loudly, it eats the next rule. */
  body.ii-allow-sticky .ii-home-stick .row.search-bar {
    margin-top: 0 !important;
    margin-bottom: 0 !important;
    /* Top 0, bottom 8, and the asymmetry is the point.

       SPACING THAT STACKS ACROSS A BOUNDARY ADDS UP, and both edges of this
       block were doing it. The nav already ends with a 15.5px inset of its
       own - its content stops at 59.5 and its box runs to 75 - so 8px of
       padding on top of that made the gap between the logo row and the field
       23.5px, against the 15.5px above the logo. The inner gap of a single
       block was LARGER than the block's own outer inset, which is backwards:
       proximity is what says these two rows belong together, and it was
       saying the opposite.

       Dropping this to 0 leaves the nav's 15.5 as the whole gap, so the block
       now reads 15.5 / logo row / 15.5 / field - one number, twice, and no
       new one invented.

       The bottom keeps 8, which is what the listing page uses under the same
       field. It stacks too - 8 here plus the rail's 16px step below - and
       that is dealt with where the rail states its own spacing, not by
       shaving this one thin. */
    padding: 0 0 8px !important;
  }
  body.ii-nav-scrolls .our-listing .sticky-block .row.search-bar {
    margin-top: 0 !important;
    margin-bottom: 0 !important;
    padding: 8px 0 !important;
  }

  /* ---- LISTING: the desktop header, ported rather than approximated -------
     The ask was explicit: on this page the mobile result should be what the
     desktop already does - logo and search on ONE row, filter chips beneath,
     and NO navigation bar in the pinned stack. Minus the Rechercher button,
     which has no horizontal room on a phone.

     The desktop achieves that with `ii-nav-scrolls`: the nav stops being
     fixed, scrolls away like any other content, and the pinned bar becomes
     the top of the page - which is why the bar carries its own logo (the
     partial's `withLogo`, revealed by `body.ii-stuck` past 120px of scroll).
     That class is ALREADY on this page's body; it simply did nothing below
     992px. This makes it mean the same thing at both widths.

     Two elements, and the split matters: `.mobile-header-set` is an EMPTY
     75px spacer whose only job is reserving room for a fixed bar, and `#page`
     is the bar itself - it holds the logo, the links, the avatar and the
     hamburger. Measured: mobile-header-set has 0 children and no text, #page
     has 3 links and an image. So the nav is not deleted, it is un-fixed: it
     scrolls away and comes back at the top of the page, exactly as on
     desktop. The spacer goes with it, because a spacer for a bar that is no
     longer fixed is 75px of nothing. */
  body.ii-nav-scrolls #page.stylehome1 { position: static; }
  body.ii-nav-scrolls .mobile-header-set { display: none; }

  /* With no fixed bar above it, the search pins to the very top - the same
     `top: 0` the desktop rule uses, for the same reason. (0,4,0) to clear the
     (0,3,0) rule that pins it under the 76px header on every other page. */
  body.ii-allow-sticky.ii-nav-scrolls .our-listing .sticky-block {
    top: 0;
    z-index: 45;
  }

  /* The mark, on the one page whose nav leaves. Re-enabled here rather than
     by weakening the blanket hide further up: that hide is right for every
     page whose nav stays fixed and is already showing the logo. */
  body.ii-nav-scrolls .ii-searchrow { display: flex; align-items: center; gap: 0; }
  body.ii-nav-scrolls .ii-searchrow-logo {
    display: block;
    flex: 0 0 auto;
    width: 0;
    overflow: hidden;
    opacity: 0;
    transition: width var(--ii-dur-fast) var(--ii-ease),
                opacity var(--ii-dur-fast) var(--ii-ease);
  }
  body.ii-nav-scrolls .ii-searchrow-logo img { height: 28px; width: auto; display: block; }
  body.ii-stuck.ii-nav-scrolls .ii-searchrow-logo { width: 64px; opacity: 1; margin-right: 12px; }

  /* The field itself, matched to the desktop assembly: ONE bar with the icon
     flush inside it and a modest 8px radius - not the full pill this had. The
     submit stays hidden; there is no horizontal room for it, which is the
     whole reason this is not simply the desktop rule reused.

     HOME IS IN THESE SELECTORS NOW, and it was the last place still showing
     the old control. Same partial, same input, two appearances decided by
     which page you were on:

       listing   47.2px tall, 8px radius, neutral border on focus
       home      53.6px tall, 999px radius, ORANGE border + 3px orange ring

     One search control should not change shape between two pages of the same
     site, and the pill was the taller of the two - 6.4px of chrome bought
     nothing. */
  body.ii-nav-scrolls .ii-searchbox,
  body.ii-home .ii-searchbox {
    display: flex;
    align-items: stretch;
    flex: 1 1 auto;
    min-width: 0;
    border-radius: 8px;
    overflow: hidden;
    background: var(--ii-bg-sunken);
    border: 1px solid var(--ii-border);
  }

  /* The inner `.input-group` is where the pill actually lives, and it is worth
     naming because the radius is not the only thing it brings:

       .search-bar .input-group               999px radius, border, sunken fill
       .search-bar .input-group:focus-within  --ii-focus border + --ii-focus-ring

     That focus pair IS the round orange glow - a 999px border and a 3px
     rgba(254,215,170) ring, drawn on the inner box, which is why it appeared
     as a pill even after the outer box was squared off.

     `.ii-searchbox` is the only thing that should paint, so the inner box
     gives up all four properties in BOTH states.

     THE `:has()` IS LOAD-BEARING, and this is the fourth specificity fight in
     this file - the third that an ID won. The base rule is written as

       .search-bar .input-group,
       .input-group:has(> #autocomplete-search) { ... }

     and a comma list takes the specificity of its MOST specific matching
     selector. `:has()` takes the specificity of its argument, so that second
     selector is (1,1,0) - an ID - and its `:focus-within` twin is (1,2,0).
     The obvious `body.ii-home .ii-searchbox .input-group` is (0,3,1): four
     classes and an element, and it loses on the ID slot to a rule with one
     class. It was written that way first and changed nothing at all.

     So the ID is named here too, exactly as the height rule below has to name
     it, which puts these at (1,4,1) and (1,5,1). The plain class selectors are
     KEPT alongside: where `:has()` is not supported neither is the base rule,
     and the plain ones are then enough to win on their own.

     No `!important` - the base rules carry none, and specificity is a real
     answer where it can be had. */
  body.ii-nav-scrolls .ii-searchbox .input-group,
  body.ii-home .ii-searchbox .input-group,
  body.ii-nav-scrolls .ii-searchbox .input-group:focus-within,
  body.ii-home .ii-searchbox .input-group:focus-within,
  body.ii-nav-scrolls .ii-searchbox .input-group:has(> #autocomplete-search),
  body.ii-home .ii-searchbox .input-group:has(> #autocomplete-search),
  body.ii-nav-scrolls .ii-searchbox .input-group:has(> #autocomplete-search):focus-within,
  body.ii-home .ii-searchbox .input-group:has(> #autocomplete-search):focus-within {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }
  /* The ID is required: `#autocomplete-search` sets its own height elsewhere
     in this file at (1,0,0), and no number of classes beats it - the desktop
     block records losing this fight twice before naming the ID. */
  body.ii-nav-scrolls .ii-searchbox .input-group .input-group-text,
  body.ii-nav-scrolls .ii-searchbox .input-group #autocomplete-search,
  body.ii-nav-scrolls .ii-searchbox .input-group .form-control,
  body.ii-home .ii-searchbox .input-group .input-group-text,
  body.ii-home .ii-searchbox .input-group #autocomplete-search,
  body.ii-home .ii-searchbox .input-group .form-control {
    height: 44px;
    min-height: 44px;
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }
  body.ii-nav-scrolls .ii-searchbox .input-group-text,
  body.ii-home .ii-searchbox .input-group-text {
    padding-left: 12px;
    padding-right: 6px;
    color: var(--ii-text-muted);
  }
  body.ii-nav-scrolls .ii-searchbox:focus-within,
  body.ii-home .ii-searchbox:focus-within { border-color: var(--ii-border-strong); }

  /* The filter chips pin directly beneath the search. `--ii-stick-nav` is not
     in this sum: on this page there is no fixed nav left to sit under. */
  body.ii-allow-sticky .ii-filter-row {
    position: -webkit-sticky;
    position: sticky;
    top: calc(var(--ii-stick-nav) + var(--ii-stick-search) - 1px);
    z-index: 39;
    background: var(--ii-bg-canvas);
  }
  body.ii-allow-sticky.ii-nav-scrolls .ii-filter-row {
    top: calc(var(--ii-stick-search) - 1px);
  }

  /* The chips scroll sideways INSIDE the pinned bar. Without this the row
     widens the document instead, which is the horizontal-swipe defect §1.42
     closed - a page 1px wider than the screen is one that pans. */
  body.ii-allow-sticky .ii-filter-row .ii-filter-bar {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    flex-wrap: nowrap;
  }
  body.ii-allow-sticky .ii-filter-row .ii-filter-bar::-webkit-scrollbar {
    display: none;
  }
}

/* ==========================================================================
   The account page's tab strip, on a phone

   Asked for directly: on mobile /user/account should be the profile form and
   the user's own details, and nothing else - no "Modifier le profil" /
   "Changer le mot de passe" pair above it.

   The strip is already conditional. It renders only when the account HAS a
   password it chose (`$ii_has_password`), which excludes phone-OTP accounts
   and the 308 Google accounts with no password stored - and when it does not
   render, the page shows a single pill reading "Modifier le profil" directly
   above an <h4> reading "Modifier le profil". So on most accounts this strip
   was already absent and the page already looked the way it is asked to look
   here; this makes the phone consistent for the rest.

   Desktop is deliberately untouched: the request was about mobile, and the
   strip is the only route to the password form.

   CONSEQUENCE, stated because it is a real loss and not a styling detail:
   changing a password on a phone is no longer reachable for the accounts that
   have one. Deleting this rule restores it. If it should come back, the
   password pane needs theming first - it is the one part of this page never
   brought onto the token layer, and it renders as a white panel in dark mode
   (measured: "Le mot de passe doit contenir" is 1.08:1, white on white).

   This block sits ABOVE the zoom guard on purpose. `MobileZoomGuardTest`
   reads the LAST `@media (max-width: 991px)` block in this file and asserts
   it is the guard; appending after it silently demotes the guard and the test
   fails, which is exactly what it is for. Anything new that needs a mobile
   media query goes here, not below.

   `!important` IS required here and is not papering over anything. The strip
   carries Bootstrap's `d-flex`, and Bootstrap's display utilities are
   themselves `!important` (constraint 20), so no amount of specificity beats
   them - `display: none` at (0,1,0) measured `flex` on the element. This is
   the case constraint 97 describes: confirm the thing being fought is inline
   or important FIRST, and here it is.
   ========================================================================== */
@media (max-width: 991px) {
  .ii-acct-tabs { display: none !important; }
}

/* ==========================================================================
   iOS zoom-on-focus: the systemic guard

   Last block in the last stylesheet, deliberately. iOS zooms the viewport
   whenever a form control smaller than 16px takes focus and DOES NOT zoom
   back out - the page is left enlarged and off-centre, and a page wider than
   the screen is one that swipes sideways. The zoom is per tab and survives
   navigation, so one tap on one undersized field leaves every page of the
   site pannable until the tab is closed. That is why this reads as "the whole
   site swipes" rather than as a bug on one screen.

   There is a guard for this near the top of the file. Three controls have now
   beaten it, each in a different way, and the pattern is what matters:

     .ii-fdrop-price-row input   (0,1,1) vs guard (0,1,0)  - specificity
     .ii-seg-sort                (0,1,0) vs guard (0,1,0)  - ORDER, 3.4k lines later
     .copy-text input.text       (0,2,1) in style.css      - specificity, another file

   Patching each at its own site is what the first two did, and a fourth would
   have been found the same way - by hand, on a device, after a report. So
   this block states the rule where nothing in this project can outrank it by
   accident: last declaration, last file, at a specificity that clears the
   container-qualified rules that beat the original guard.

   No `!important`: every rule being overridden is a normal author
   declaration, so order and specificity are the correct instruments and an
   important here would only hide the next mistake (constraint 97).

   991px, not the original guard's 767px. The mobile layout runs to 991
   (§3.5), and a tablet in portrait is 768 - squarely inside the band the old
   guard left open.

   Deliberately NOT fixed with `maximum-scale=1` / `user-scalable=no` on the
   viewport meta. That hides the symptom by taking pinch-zoom away from
   everybody and fails WCAG 1.4.4 - the same call the price-row note records.

   Constraint 82. */
@media (max-width: 991px) {
  /* The generic half. Catches anything new that is not container-qualified. */
  input[type="text"], input[type="tel"], input[type="number"],
  input[type="email"], input[type="search"], input[type="password"],
  input[type="url"], input[type="date"], input:not([type]),
  select, textarea {
    font-size: 16px;
  }

  /* The named half, and it is not optional: every selector below outranks the
     generic list on SPECIFICITY, so being last in the last file is not enough
     on its own. They are reproduced here at their own specificity, where the
     later file then wins on order.

     This list is not hand-collected. It is every rule in the four frontend
     sheets that sets a font-size under 16px on a selector naming a form
     control, minus the ::placeholder rules - a placeholder's size does not
     zoom anything, only the control's own does. `MobileZoomGuardTest` re-runs
     that scan and fails if a seventh appears, which is the whole reason the
     list can be trusted to be complete. Do not edit it by hand without
     running that test. */
  .copy-text input.text,                                  /* share modal, EVERY page */
  .ii-seg-sort,                                           /* profile sort, owner view */
  .sasw_list .search_area input,
  .sasw_list .search_area textarea,
  .form_grid .contact_form input,                         /* /contact, 4 fields */
  .form_grid .contact_form textarea.form-control,         /* /contact, the message */
  .checkout_form .checkout_coupon form input.coupon_input,
  .checkout_form .checkout_coupon form textarea,
  .mbp_comment_form.style2 .comments_form .form-group input,
  .bsp_reveiw_wrt .comments_form .form-group input,
  .mbp_comment_form.style2 .comments_form .form-group textarea,
  .bsp_reveiw_wrt .comments_form .form-group textarea {
    font-size: 16px;
  }
}

/* ==========================================================================
   Dark mode: three labels the legacy sheet had blacked out

   Reported from a real phone on 2026-08-28: the profile's status chips and
   the wallet balance were invisible, and the header avatar wore a white
   square. The first two are one defect wearing two faces, and the shape is
   worth stating once because it will keep happening.

   The dark theme's catch-all is

     :where(:root:not([data-theme="light"])) a|span|button { color: inherit }

   and `:where()` contributes ZERO specificity ON PURPOSE (constraint 69) -
   without it the blanket outranks every component class and flattens every
   deliberate colour on the page. So the blanket is (0,0,1): a bare element
   selector. ANY legacy rule carrying even one class outranks it and keeps
   painting its light-mode ink onto a dark surface.

   Scanned: style.css, menu.css, responsive.css, custom.css and
   ace-responsive-menu.css hold **652** such rules. They are NOT all bugs -
   most land on a light card, a modal or the admin, where dark ink is correct.
   A rule here is only a defect where it lands on a dark surface, which means
   this class of bug can only be found by RENDERING and measuring, never by
   grepping the stylesheet. That is why there is no enumerated guard for it
   the way there is for the iOS zoom - the list would be 652 long and mostly
   wrong. Measure the page, fix what bites.

   No `!important` anywhere below: everything being overridden is a normal
   author declaration, so specificity and order are the correct instruments
   and an important would only hide the next one (constraint 97).
   ========================================================================== */

/* 1. The seller's status chips - measured 1.18:1, black on #17181A.

      style.css  .tab-content-head a   #000   (0,1,1)
      this file  .ii-seg-item          token  (0,1,0)   <- lost

   The chips are <a>; the statistics segments ("7 jours" / "30 jours") are
   <button>, which `.tab-content-head a` cannot match - which is exactly why
   the same class rendered correctly in one place and invisibly in the other,
   and why this read as a dark-mode bug rather than a specificity one.

   `a.ii-seg-item` ties the legacy rule at (0,1,1) and wins on order, this
   being the last stylesheet. It must stay BELOW `.ii-seg-item.is-on` (0,2,0)
   so the selected chip keeps its ink-on-canvas fill - qualifying this any
   further (e.g. `.tab-content-head a.ii-seg-item`, (0,2,1)) would outrank
   `.is-on` and break the selected state instead. */
a.ii-seg-item { color: var(--ii-text-secondary); }
a.ii-seg-item:hover { color: var(--ii-text); }

/* 2. The wallet balance - measured 1.18:1, black on #17181A, while the "Fdj"
      beside it sat at 16.29:1. A balance of 0 that cannot be read is not a
      cosmetic problem: it is the one number that screen exists to show.

      style.css  .wallet-item-left span            #000   (0,1,1)

   Qualified with the amount's own container so it beats that at (0,2,1) and
   touches nothing else inside the wallet row. The token, not #fff: it is
   near-black in light mode, so this is correct in both themes and needs no
   dark scope. */
.wallet-item-left .amoumt-item span { color: var(--ii-text); }

/* 3. The header avatar's white square.

   The avatar is `<a class="btn dropdown-toggle">` wrapping a 40px round
   image. Carrying `.btn` gets it Bootstrap's `border: 1px solid transparent`
   and `border-radius: .25rem` (4px from style.css) - invisible in light mode,
   which is why nobody ever saw it. Then this sheet's dark pass repaints every
   `.btn.dropdown-toggle` edge to --ii-border-control (rgba(255,255,255,.34))
   so that real select-like controls have a visible edge, and the avatar -
   which is a `.btn.dropdown-toggle` only by accident of markup - gets a
   34%-white 4px square drawn around a circle.

   Scoped to `.user_setting`, the header's own wrapper. style.css already
   declares `.user_setting .btn { border: 1px solid transparent }`, so
   transparent is the intended look for this control and this restores it
   rather than inventing one. (0,5,0) against the existing (0,4,0); both
   carry !important, so specificity decides between them.

   Both scopes, in step - `ThemeTokensTest` fails if they drift. */
:root[data-theme="dark"] .user_setting .btn.dropdown-toggle {
  background-color: transparent !important;
  border-color: transparent !important;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .user_setting .btn.dropdown-toggle {
    background-color: transparent !important;
    border-color: transparent !important;
  }
}

