:root {
    /* ==================================================================
       THEME KNOBS — the only colours you change to re-skin the site.
       Everything else in this file is computed from them with color-mix().
       ================================================================== */
    --clr-main: #38BDF8; /* Primary accent: links, headings, buttons, logo   */
    --bg-secondary: #F472B6; /* Secondary accent — also the VIP tier             */
    --clr-gold: #FACC15; /* GOLD tier — electric cyan, not a gold            */
    --bg-site: #060A16; /* Page background — every neutral derives from it  */
    --clr-text: #ffffff; /* Foreground — every grey derives from it          */
    /* The WhatsApp fill is a knob PAIR, not a single colour: the button is
       painted with --bg-linear-gradient-whatsapp, a two-stop vertical sweep
       from the first of these to the second. Keep both in the same green
       family — a pair pulled apart into two hues stops reading as one
       button. Its label is --clr-text, not a --clr-on-* token: no single
       flipped-lightness label can serve two stops. */
    --clr-bg-whatsapp: #0a9440; /* WhatsApp fill, top stop                          */
    --clr-bg-whatsapp-end: #0c7a35; /* WhatsApp fill, bottom stop                   */

    /* Fixed colours: brand-mandated or status. NOT design knobs — changing
       these misrepresents a third party or breaks a convention. */
    --clr-bg-telegram: #0275D8;
    --clr-error: #dc3545;

    /* Fallback only — the live value is injected from ThemeConfig::FONT_FAMILY
       (inc/config.php) via wp_add_inline_style(). One font for the whole site;
       never hardcode a font-family anywhere else. */
    --font-family: 'Heebo', sans-serif;

    /* The one exception to "one font for the whole site": the logo wordmark,
       which is a mark rather than text. Fallbacks only, injected live from
       ThemeConfig::LOGO_FONT_FAMILY. The weight is part of the setting because
       a display face ships one or two weights and the wordmark asks for 900 —
       naming the weight the file actually has is what keeps the browser from
       synthesising a bold. With no logo family configured both resolve to the
       site font's own values, so .logo-name needs no second rule. */
    --font-logo: 'Lobster', cursive;
    --font-logo-weight: 400;

    /* ==================================================================
       SHAPE KNOBS — the four numbers that re-shape the site, alongside
       the six colours above. Same contract: these are fallbacks only,
       and the live values are injected into :root from ThemeConfig by
       wp_add_inline_style(). Everything derived from them is computed
       below, so a knob is the whole edit.

       Each one is a BARE NUMBER, not a length. PHP writes the number;
       this file owns the unit. That way PHP never has to know whether a
       knob is px, a percentage or a plain count.
       ================================================================== */
    --radius-px: 10; /* corner radius of the middle step, in px      */
    --shadow-pct: 110; /* shadow + tier-glow strength, in % of default   */
    --grid-columns: 4; /* card grid columns, from 992px up               */
    --card-media-px: 400; /* height of a card's media area, in px           */

    /* ------------------------------------------------------------------
       Derived tokens. Every one is computed from a knob above — colour with
       color-mix(), shape with calc() — so there is nothing to keep in sync by
       hand: change a knob and these follow. Never add a raw hex, a --*-rgb
       triplet or a literal length: a hand-maintained companion value silently
       drifts out of sync.
       `black` below is the CSS keyword, not a theme colour — scrims and
       shadows darken toward true black regardless of the palette.
       ------------------------------------------------------------------ */

    /* Accent variants */
    --clr-border: color-mix(in srgb, var(--bg-secondary) 72%, black);
    --clr-gold-secondary: color-mix(in srgb, var(--clr-gold) 80%, black);

    /* Specular highlights. Each is its own accent lifted toward white, and each
       is used only as the centre stop of the matching gradient below, so a
       filled surface reads as brushed metal catching the light rather than as a
       flat wash. They are the lightest point of their gradient, so the dark
       --clr-on-* label that already suits the accent is more readable on them,
       never less. */
    --clr-secondary-sheen: color-mix(in srgb, var(--bg-secondary) 62%, white);
    --clr-main-sheen: color-mix(in srgb, var(--clr-main) 62%, white);
    --clr-gold-sheen: color-mix(in srgb, var(--clr-gold) 62%, white);

    /* Neutrals, from --bg-site */
    --bg-dark: color-mix(in srgb, var(--bg-site) 50%, black);
    --bg-panel: color-mix(in srgb, var(--bg-site) 95%, var(--clr-text));

    /* Label colours, one per filled surface: the fill's own colour, kept at its
       hue and chroma but pushed to the opposite end of the lightness scale, so
       the text always reads as a shade of the thing it sits on and never has to
       be tuned by hand. --clr-on-gradient covers every surface painted with
       --bg-linear-gradient, whose stops are all one hue, so one token serves
       them all; it derives from the accent and its deep end rather than from
       any single stop, which puts it between the sweep's extremes.
       These are the static fallbacks for browsers without relative colour
       syntax — the adaptive versions are in the @supports block below. */
    --clr-on-telegram: color-mix(in srgb, var(--clr-bg-telegram) 22%, white);
    --clr-on-gradient: color-mix(in srgb, var(--bg-secondary) 22%, white);
    --clr-on-main: color-mix(in srgb, var(--clr-main) 22%, black);
    --clr-on-secondary: color-mix(in srgb, var(--bg-secondary) 22%, white);
    --clr-on-dark: color-mix(in srgb, var(--bg-dark) 22%, white);
    --clr-on-gold: color-mix(in srgb, var(--clr-gold-secondary) 22%, black);
    /* Scrims darken toward true black whatever the palette is, so anything
       drawn on one is light — this is the one label that does not track a knob.
       Never write --clr-text here: a light theme sets it to a dark ink that
       would vanish into the scrim. */
    --clr-on-overlay: white;

    /* Greys, from --clr-text tinted by the page background */
    --clr-text-muted: color-mix(in srgb, var(--clr-text) 86%, var(--bg-site));
    --clr-engagement: color-mix(in srgb, var(--clr-text) 60%, var(--bg-site));

    /* Gradients */
    /* Both gradients are symmetric metal sweeps: the deep end of the accent at
       both edges, the accent itself either side of centre, and the sheen as a
       specular hit in the middle. The slight tilt off horizontal keeps a wide
       bar from looking like a flat band. Symmetry is what makes them reusable —
       the highlight lands mid-element whatever the element's width, so the same
       token works on the header bar and on a 40px round nav button.
       Both are re-declared as a single flat colour by
       ThemeConfig::displayRootCss() when the surface style setting is 'flat',
       so every consumer keeps using `background: var(--bg-linear-gradient)`
       and no rule anywhere branches on the setting. Keep both tokens a CSS
       <image>: a bare colour here would break `background-image` consumers. */
    --bg-linear-gradient: linear-gradient(100deg,
        var(--clr-border) 0%,
        var(--bg-secondary) 30%,
        var(--clr-secondary-sheen) 50%,
        var(--bg-secondary) 70%,
        var(--clr-border) 100%);
    --bg-linear-gradient-secondary: linear-gradient(100deg,
        var(--clr-main-muted) 0%,
        var(--clr-main) 30%,
        var(--clr-main-sheen) 50%,
        var(--clr-main) 70%,
        var(--clr-main-muted) 100%);
    /* The GOLD tier's own sweep, built to the same five-stop shape as the two
       above. It exists so the paid tiers are told apart by their own accent
       rather than by borrowing the VIP one, and so the tier chip has a token to
       read instead of writing gold stops at its call site.
       ThemeConfig::displayRootCss() flattens this one alongside the other two,
       so the surface style setting still reaches every accent surface. */
    --bg-linear-gradient-gold: linear-gradient(100deg,
        var(--clr-gold-secondary) 0%,
        var(--clr-gold) 30%,
        var(--clr-gold-sheen) 50%,
        var(--clr-gold) 70%,
        var(--clr-gold-secondary) 100%);

    /* WhatsApp's own fill. Two stops, top to bottom, rather than the five-stop
       tilted sweep above: this is not an accent surface being lit like metal,
       it is the one green on the page, and a plain vertical shade is what reads
       as a solid button rather than as a second kind of ornament.
       Flattened to --clr-bg-whatsapp by ThemeConfig::displayRootCss() alongside
       the three metal sweeps when the surface style setting is 'flat', so this
       surface obeys the setting like every other one. */
    --bg-linear-gradient-whatsapp: linear-gradient(to bottom,
        var(--clr-bg-whatsapp) 0%,
        var(--clr-bg-whatsapp-end) 100%);

    /* Translucent white: subtle panel fills and hairline borders */
    --surface-translucent: color-mix(in srgb, var(--clr-text) 8%, transparent);
    --border-subtle: color-mix(in srgb, var(--clr-text) 25%, transparent);

    /* Accent hairline: the card-edge counterpart of --border-subtle. The grid
       is the one place a neutral hairline reads as a grey box floating on a
       themed page, because a card is a large closed outline rather than a
       divider — so the edge takes the accent at low alpha instead, and still
       re-tints with the palette like every other derived token. */
    --border-accent: color-mix(in srgb, var(--bg-secondary) 30%, transparent);

    /* Accent washes (filters UI, recent comments) */
    --accent-soft: color-mix(in srgb, var(--bg-secondary) 15%, transparent);
    --accent-strong: color-mix(in srgb, var(--bg-secondary) 55%, transparent);

    /* Primary accent wash (logo glow, focus rings) */
    --clr-main-soft: color-mix(in srgb, var(--clr-main) 30%, transparent);

    /* Deepened primary accent — the outer stops of
       --bg-linear-gradient-secondary, which falls away to it at both edges so
       the sweep reads as a lit metal face rolling into shadow. */
    --clr-main-muted: color-mix(in srgb, var(--clr-main) 70%, black);

    /* Corner radii. One ladder, every step a multiple of --radius-px, so a
       single knob re-shapes every rounded thing on the site while the
       proportions between a checkbox, a card and a pill button hold at any
       setting. No stylesheet in the theme may write a border-radius length of
       its own — pick the step that fits, exactly as colour picks a token.
       The multipliers reproduce the shipped shape at --radius-px: 5.
       --radius-round is a true circle: an avatar is round at every setting,
       including 0, so it is the one step that does not scale. */
    --radius-xs: calc(var(--radius-px) * 0.6px);
    --radius-sm: calc(var(--radius-px) * 0.8px);
    --radius-md: calc(var(--radius-px) * 1px);
    --radius-lg: calc(var(--radius-px) * 1.6px);
    --radius-xl: calc(var(--radius-px) * 2.4px);
    --radius-2xl: calc(var(--radius-px) * 4px);
    --radius-pill: calc(var(--radius-px) * 5px);
    --radius-round: 50%;

    /* Card geometry. Every .listing min-height in the theme — here and in
       home/carousel.css — is this value plus the space that card's header,
       content and engagement rows need at that breakpoint, so a taller or
       shorter media block never leaves the text half of the card stranded.
       The media area is also the ad's photo, i.e. what a visitor actually
       responds to, so this knob trades listing size against how many listings
       reach the screen. */
    --card-media-height: calc(var(--card-media-px) * 1px);

    /* Elevation strength, as a plain multiplier: 100 -> 1, 0 -> 0, 200 -> 2.
       It scales the three shadow tokens below (offsets, blur and alpha alike)
       and the per-layer alpha of the tier glows further down. At 0 every one
       of them resolves to nothing, so the flat end of the knob needs no second
       set of rules. Note what it does NOT reach: the VIP and Gold border
       colours and widths, so the paid tiers stay distinguishable — by their
       edge alone — even with the glow turned off. */
    --shadow-strength: calc(var(--shadow-pct) / 100);

    /* Scrims & shadows */
    --overlay: color-mix(in srgb, black 70%, transparent);
    --shadow-default: 0 calc(2px * var(--shadow-strength)) calc(8px * var(--shadow-strength)) color-mix(in srgb, black calc(30% * var(--shadow-strength)), transparent);
    --shadow-hover: 0 calc(4px * var(--shadow-strength)) calc(16px * var(--shadow-strength)) color-mix(in srgb, black calc(45% * var(--shadow-strength)), transparent);
    --text-shadow-default: 0 calc(1px * var(--shadow-strength)) calc(3px * var(--shadow-strength)) color-mix(in srgb, black calc(40% * var(--shadow-strength)), transparent);
}

/* Adaptive label colours. Relative colour syntax reads the fill's own l/c/h,
   keeps c and h — so the text stays in the fill's colour family — and replaces
   the lightness with a step function: a fill lighter than L 0.62 gets a
   near-black shade, a darker one a near-white shade. The ×1000 multiplier makes
   clamp() snap between the two ends instead of easing through the unreadable
   middle. Recolour a surface and its label follows on its own; the :root
   fallbacks above cover browsers without `oklch(from …)`. */
@supports (color: oklch(from white l c h)) {
    :root {
        --clr-on-telegram: oklch(from var(--clr-bg-telegram) clamp(0.2, (0.62 - l) * 1000, 0.98) c h);
        --clr-on-gradient: oklch(from color-mix(in oklab, var(--bg-secondary), var(--clr-border)) clamp(0.2, (0.62 - l) * 1000, 0.98) c h);
        --clr-on-main: oklch(from var(--clr-main) clamp(0.2, (0.62 - l) * 1000, 0.98) c h);
        --clr-on-secondary: oklch(from var(--bg-secondary) clamp(0.2, (0.62 - l) * 1000, 0.98) c h);
        --clr-on-dark: oklch(from var(--bg-dark) clamp(0.2, (0.62 - l) * 1000, 0.98) c h);
        --clr-on-gold: oklch(from var(--clr-gold-secondary) clamp(0.2, (0.62 - l) * 1000, 0.98) c h);
        /* `/ 1` is required: oklch(from …) inherits the origin's alpha unless
           told otherwise, and --overlay is 70% transparent. */
        --clr-on-overlay: oklch(from var(--overlay) clamp(0.2, (0.62 - l) * 1000, 0.98) c h / 1);
    }
}

body {
    margin: 0;
    font-family: var(--font-family);
    font-size: 16px;
    line-height: 1.45;
    color: var(--clr-text);
    background-color: var(--bg-site);
    /* Ambient accent light. One radial wash pinned to the top of the viewport
       turns the flat page ground into a lit room — the chrome at the top sits
       in the light, the grid below falls away into shadow. It is --clr-main at
       7%, so it re-tints with the palette and stays far too weak to read as a
       second background colour or to move body-text contrast. `fixed` keeps the
       light source still while the page scrolls, which is what makes it read as
       lighting rather than as a banner. */
    background-image: radial-gradient(115% 55% at 50% 0%,
        color-mix(in srgb, var(--clr-main) 7%, transparent) 0%,
        transparent 72%);
    background-repeat: no-repeat;
    background-attachment: fixed;
}

/* ======================================================================
   REUSABLE ACCENT TREATMENTS

   Three independent classes, composable in any combination and on any
   element. They carry look only — never layout, size or type — so the
   component they are added to keeps owning its own box. Each is built
   from the palette tokens like everything else, so all three re-skin
   with the six knobs and follow the radius and surface-style settings
   without a second edit.

   .metal-text  — the accent's metal sweep painted into the glyphs
   .accent-pill — an outlined pill: accent hairline, no fill
   .pulse-soft  — a slow breathing loop that settles on hover

   Do not add a component's own class to these rules. A treatment that
   knows about one caller stops being reusable, and the caller can always
   override a property on its own selector.
   ====================================================================== */

/* Metallic lettering: --bg-linear-gradient-secondary painted into the text
   rather than behind it, so the glyphs read as lit metal instead of flat gold.
   Descendant text is clipped from the same sweep, which is what lets a wrapper
   letter several child elements as one continuous run.

   Two things to know before using it:
   - The element's OWN background is clipped away with everything else, so put
     this on something transparent. A fill belongs on a parent.
   - Outside the @supports guard the glyphs stay `color`, so always set a solid
     fallback colour alongside it — below it is --clr-main, the sweep's own hue.
     Without the guard, a browser that cannot clip renders invisible text. */
.metal-text {
    color: var(--clr-main);
}

@supports ((background-clip: text) or (-webkit-background-clip: text)) {
    .metal-text {
        background-image: var(--bg-linear-gradient-secondary);
        -webkit-background-clip: text;
        background-clip: text;
        -webkit-text-fill-color: transparent;
        color: transparent;
    }
}

/* Outlined pill: an accent hairline around nothing, for a control that has to
   be findable without being filled. Deliberately has no background — a filled
   accent surface needs a --clr-on-* label, which is the opposite problem this
   solves, and leaving it transparent is also what lets .metal-text sit on the
   same element. Hover lifts the hairline to the secondary accent and adds its
   own soft glow, so the affordance costs no colour change in the text. */
.accent-pill {
    padding: 5px 20px;
    border: 1px solid var(--clr-main);
    border-radius: var(--radius-pill);
    /* The longhand is load-bearing: `background: none` is a shorthand and would
       reset background-image, blanking .metal-text's sweep on any element
       carrying both — which, since neither class outranks the other, would then
       depend on the order these two rules happen to sit in. */
    background-color: transparent;
    cursor: pointer;
    text-decoration: none;
    text-align: center;
    transition: border-color 0.3s ease, box-shadow 0.3s ease;
}

.accent-pill:hover {
    text-decoration: none;
    border-color: var(--bg-secondary);
    box-shadow: 0 0 12px var(--clr-main-soft);
}

/* A slow breathing loop for one element that has to be noticed on a page that
   is otherwise still. Opacity only, so it composes with anything and moves no
   layout. Hovering settles it at full opacity: motion is there to attract
   attention, and it has done its job once the pointer arrives. */
.pulse-soft {
    animation: pulseSoft 1s infinite;
}

.pulse-soft:hover {
    animation-play-state: paused;
    opacity: 1;
}

@keyframes pulseSoft {
    from {
        opacity: 1;
    }

    50% {
        opacity: 0.5;
    }

    to {
        opacity: 1;
    }
}

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

    .pulse-soft {
        animation: none;
    }
}

/* ======================================================================
   MOTIF FIELD

   The logo mark's own bunny and glint, scattered at half a dozen sizes and
   angles across one 400x200 tile and repeated behind a bar. It is the theme's
   texture, not its decoration: at this opacity it reads as the bar having
   depth rather than as a pattern anyone looks at, which is the only way a
   motif survives sitting behind a page of photographs.

   Deliberately applied to the header and footer bars ALONE. Behind the card
   grid it would compete with the ads' own imagery, which is the thing a
   visitor is here to look at.

   The tile is a MASK, not a background image: the shapes carry no colour of
   their own — the fill comes from --clr-main underneath, so the motif re-skins
   with the palette like everything else and no colour literal enters the data
   URI. Baseline is the same as color-mix()'s (Chrome 111 / Safari 16.2 /
   Firefox 113), which is comfortably past unprefixed mask-image, so this needs
   no -webkit- twin.
   ====================================================================== */
.motif-field {
    position: relative;
    /* The field below sits at z-index -1 so it stays under the bar's content.
       That only puts it ABOVE the bar's own background if the bar is a
       stacking context, and isolation is the one way to make it one without
       also changing how it paints or what it clips. Same trap the tier halos
       document further down. */
    isolation: isolate;
    overflow: hidden;
}

.motif-field::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-color: var(--clr-main);
    /* --motif-mask is the configured mark's tile (adx_motif_root_css()); the
       literal is the original bunny tile, used only if that inline style is missing. */
    mask-image: var(--motif-mask, url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20400%20200'%3E%3Cg%20transform='translate(52%2054)%20rotate(-16)%20scale(0.62)%20translate(-24%20-25.45)'%3E%3Cpath%20d='M24%2024L32.6%2027.2L35.5%2033.5L30.5%2041.6L24%2045.4L17.5%2041.6L12.5%2033.5L15.4%2027.2ZM10.5%205.5L7.5%2017L14%2028L18.5%2029.5L15.5%2016ZM37.5%205.5L40.5%2017L34%2028L29.5%2029.5L32.5%2016Z'/%3E%3C/g%3E%3Cg%20transform='translate(238%20132)%20rotate(14)%20scale(0.44)%20translate(-24%20-25.45)'%3E%3Cpath%20d='M24%2024L32.6%2027.2L35.5%2033.5L30.5%2041.6L24%2045.4L17.5%2041.6L12.5%2033.5L15.4%2027.2ZM10.5%205.5L7.5%2017L14%2028L18.5%2029.5L15.5%2016ZM37.5%205.5L40.5%2017L34%2028L29.5%2029.5L32.5%2016Z'/%3E%3C/g%3E%3Cg%20transform='translate(342%2038)%20rotate(-28)%20scale(0.3)%20translate(-24%20-25.45)'%3E%3Cpath%20d='M24%2024L32.6%2027.2L35.5%2033.5L30.5%2041.6L24%2045.4L17.5%2041.6L12.5%2033.5L15.4%2027.2ZM10.5%205.5L7.5%2017L14%2028L18.5%2029.5L15.5%2016ZM37.5%205.5L40.5%2017L34%2028L29.5%2029.5L32.5%2016Z'/%3E%3C/g%3E%3Cg%20transform='translate(146%20168)%20rotate(7)%20scale(0.26)%20translate(-24%20-25.45)'%3E%3Cpath%20d='M24%2024L32.6%2027.2L35.5%2033.5L30.5%2041.6L24%2045.4L17.5%2041.6L12.5%2033.5L15.4%2027.2ZM10.5%205.5L7.5%2017L14%2028L18.5%2029.5L15.5%2016ZM37.5%205.5L40.5%2017L34%2028L29.5%2029.5L32.5%2016Z'/%3E%3C/g%3E%3Cg%20transform='translate(196%2074)%20rotate(24)%20scale(0.19)%20translate(-24%20-25.45)'%3E%3Cpath%20d='M24%2024L32.6%2027.2L35.5%2033.5L30.5%2041.6L24%2045.4L17.5%2041.6L12.5%2033.5L15.4%2027.2ZM10.5%205.5L7.5%2017L14%2028L18.5%2029.5L15.5%2016ZM37.5%205.5L40.5%2017L34%2028L29.5%2029.5L32.5%2016Z'/%3E%3C/g%3E%3Cg%20transform='translate(388%20178)%20rotate(-9)%20scale(0.22)%20translate(-24%20-25.45)'%3E%3Cpath%20d='M24%2024L32.6%2027.2L35.5%2033.5L30.5%2041.6L24%2045.4L17.5%2041.6L12.5%2033.5L15.4%2027.2ZM10.5%205.5L7.5%2017L14%2028L18.5%2029.5L15.5%2016ZM37.5%205.5L40.5%2017L34%2028L29.5%2029.5L32.5%2016Z'/%3E%3C/g%3E%3Cpath%20transform='translate(118%2030)%20scale(1.25)'%20d='M0%20-5Q1.2%20-1.3%205%200Q1.2%201.3%200%205Q-1.2%201.3%20-5%200Q-1.2%20-1.3%200%20-5Z'/%3E%3Cpath%20transform='translate(300%2092)%20scale(0.7)'%20d='M0%20-5Q1.2%20-1.3%205%200Q1.2%201.3%200%205Q-1.2%201.3%20-5%200Q-1.2%20-1.3%200%20-5Z'/%3E%3Cpath%20transform='translate(74%20148)%20scale(1.1)'%20d='M0%20-5Q1.2%20-1.3%205%200Q1.2%201.3%200%205Q-1.2%201.3%20-5%200Q-1.2%20-1.3%200%20-5Z'/%3E%3Cpath%20transform='translate(214%2020)%20scale(0.5)'%20d='M0%20-5Q1.2%20-1.3%205%200Q1.2%201.3%200%205Q-1.2%201.3%20-5%200Q-1.2%20-1.3%200%20-5Z'/%3E%3Cpath%20transform='translate(396%20104)%20scale(0.85)'%20d='M0%20-5Q1.2%20-1.3%205%200Q1.2%201.3%200%205Q-1.2%201.3%20-5%200Q-1.2%20-1.3%200%20-5Z'/%3E%3Cpath%20transform='translate(160%20112)%20scale(0.42)'%20d='M0%20-5Q1.2%20-1.3%205%200Q1.2%201.3%200%205Q-1.2%201.3%20-5%200Q-1.2%20-1.3%200%20-5Z'/%3E%3Cpath%20transform='translate(268%20178)%20scale(0.95)'%20d='M0%20-5Q1.2%20-1.3%205%200Q1.2%201.3%200%205Q-1.2%201.3%20-5%200Q-1.2%20-1.3%200%20-5Z'/%3E%3Cpath%20transform='translate(20%20104)%20scale(0.6)'%20d='M0%20-5Q1.2%20-1.3%205%200Q1.2%201.3%200%205Q-1.2%201.3%20-5%200Q-1.2%20-1.3%200%20-5Z'/%3E%3Cpath%20transform='translate(348%20140)%20scale(0.38)'%20d='M0%20-5Q1.2%20-1.3%205%200Q1.2%201.3%200%205Q-1.2%201.3%20-5%200Q-1.2%20-1.3%200%20-5Z'/%3E%3C/svg%3E"));
    mask-size: 400px 200px;
    mask-repeat: repeat;
    /* The one number to turn if the texture ever reads as a pattern rather than
       as depth. Anything past ~0.15 starts competing with the content. */
    opacity: 0.09;
}

/* A phone sees a tenth of the header a desktop does, and the tile is a fixed
   size, so at the desktop setting a narrow bar lands between shapes and reads as
   plain black — the texture disappears exactly where the site is mostly used.
   The tile shrinks so the same number of shapes fall inside the bar, and the
   fill comes up to match: at this scale the shapes are small enough that 0.09
   reads as dirt on the screen rather than as pattern. */
@media (max-width: 768px) {
    .motif-field::before {
        mask-size: 230px 115px;
        opacity: 0.15;
    }
}

/* Section headings get the mark's glint either side, the same device the logo
   tagline uses, so a section reads as a chapter opening rather than as one
   more line of gold text. currentColor is load-bearing: it makes the glints
   the heading's own accent, so nothing here has to name a colour. */
.category-title,
.archive h1 {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.45em;
}

.category-title::before,
.category-title::after,
.archive h1::before,
.archive h1::after {
    content: "";
    flex: none;
    width: 0.4em;
    height: 0.4em;
    background-color: currentColor;
    mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='-6%20-6%2012%2012'%3E%3Cpath%20d='M0%20-5Q1.2%20-1.3%205%200Q1.2%201.3%200%205Q-1.2%201.3%20-5%200Q-1.2%20-1.3%200%20-5Z'/%3E%3C/svg%3E");
    mask-size: contain;
    mask-repeat: no-repeat;
    opacity: 0.55;
}

/* Header CSS */

/* The header is ONE object: the logo row and the nav row sit in the same dark
   band, on the same motif, divided by a hairline rather than by a change of
   colour. A full-bleed accent panel behind the nav would make the first thing on
   every page a block of saturated violet the width of the screen — louder than
   the logo above it and louder than any ad below it. Carrying .motif-field, the
   band takes its position, isolation, overflow and texture from that class, so
   nothing here repeats them. */
.site-header {
    background-color: var(--bg-dark);
}

.site-header-top {
    padding-top: 22px;
    padding-bottom: 20px;
}

.top-bar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 20px;
}

/* Layout and type only — the CTA's whole appearance comes from the
   .accent-pill .metal-text .pulse-soft trio it carries in header.php. This is
   the site's own "advertise with us" CTA sitting in the header chrome, not a
   contact button on an ad, so it takes the accent rather than
   --clr-bg-whatsapp: a saturated green pill is the loudest thing on the page
   and pulls rank over the logo next to it. Ad cards keep the green fill, where
   green is the thing being identified. */
.whatsapp-advertise {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 20px;
}

.whatsapp-advertise span {
    display: inline-block;
}

.logo {
    display: inline-block;
    text-decoration: none;
}

/* Fluid between the mobile floor and the desktop ceiling, so the header needs no
   per-breakpoint size steps and adapts to whatever LOGO_TEXT/LOGO_TAGLINE a site sets. */
.logo img {
    max-height: clamp(35px, 8vw, 70px);
    width: auto;
}

/* CSS text logo (no image) */
.text-logo {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    text-decoration: none;
}

.text-logo:hover {
    text-decoration: none;
}

/* The mark and the wordmark share one line; the tagline sits under both. */
.logo-main {
    display: flex;
    align-items: center;
    gap: 0.22em;
    /* The floor matters more than the ceiling here: at ≤768px the header is one
       non-wrapping row, so the wordmark and the advertise pill have to fit side
       by side on the narrowest phone. A 30px floor put them past the edge at
       320px and gave the whole page a horizontal scrollbar. */
    font-size: clamp(20px, 6.2vw, 42px);
    font-weight: 900;
    font-family: var(--font-family);
    line-height: 1;
    letter-spacing: 1px;
}

/* The wordmark, and the site's only element allowed a face of its own — see
   --font-logo in :root. It overrides .logo-main's family AND its 900 weight,
   since a display face rarely ships one; the mark beside it is an SVG and is
   untouched either way.
   Its colour is .metal-text's sweep, painted into the glyphs;
   widening the background to 200% keeps that sweep's deep outer stops off a
   short word, so a two-letter name reads as lit metal instead of as lettering
   with dim ends. The glow has to be a filter rather than a text-shadow: a
   text-shadow is painted over the clipped gradient, which would blur the very
   lettering it is there to lift off the bar. */
.logo-name {
    font-family: var(--font-logo);
    font-weight: var(--font-logo-weight);
    background-size: 200% 100%;
    background-position: center;
    filter: drop-shadow(0 0 14px var(--clr-main-soft)) drop-shadow(0 2px 3px var(--overlay));
}

/* The mark (inc/template-helpers.php, render_logo_mark()). Sized in em, so it
   tracks the wordmark through the clamp() above and in the footer variant with
   no size of its own to keep in step, and every face takes a step of the
   primary accent — sheen for the lit ones, the accent itself for the table's
   neighbours, the deepened accent for the faces turned away. The mark therefore
   re-skins with --clr-main like the rest of the theme, and holds no colour the
   palette does not own. */
.logo-mark {
    width: 1.08em;
    height: 1.08em;
    flex: none;
    filter: drop-shadow(0 0 10px var(--clr-main-soft));
    transition: transform 0.3s ease, filter 0.3s ease;
}

/* The base is also the mark's rim: the outline is stroked in the mid tone as
   well as filled with it, so the silhouette keeps a lit edge where the faces
   turned away from the light meet the dark bar behind them. */
.logo-mark-base {
    fill: var(--clr-main);
    stroke: var(--clr-main);
    stroke-width: 1.5;
    stroke-linejoin: round;
}

.logo-facet-mid {
    fill: var(--clr-main);
}

.logo-facet-light {
    fill: var(--clr-main-sheen);
}

.logo-facet-dark {
    fill: var(--clr-main-muted);
}

/* The glints take the secondary accent's sheen rather than the primary's, which
   is what separates them from the stone they sit beside instead of reading as
   two more of its faces. */
.logo-mark-spark {
    fill: var(--clr-secondary-sheen);
    /* Without fill-box the scale below runs from the SVG's own origin and
       throws each glint across the mark instead of pulsing it in place. */
    transform-box: fill-box;
    transform-origin: center;
}

/* Motion is hover-only. The header already has one thing moving on its own —
   the advertise CTA's pulse — and a second permanent animation beside it turns
   the bar into competing attention-grabs rather than a lit logo. */
.text-logo:hover .logo-mark {
    transform: scale(1.06) rotate(-5deg);
    filter: drop-shadow(0 0 16px var(--clr-main-soft));
}

.text-logo:hover .logo-mark-spark {
    animation: logoSpark 1.2s ease-in-out infinite;
}

/* The second glint only: adjacent siblings, so the two twinkle out of step. */
.text-logo:hover .logo-mark-spark + .logo-mark-spark {
    animation-delay: 0.35s;
}

@keyframes logoSpark {
    from,
    to {
        opacity: 1;
        transform: scale(1);
    }

    50% {
        opacity: 0.35;
        transform: scale(0.8);
    }
}

/* Ornamental rules either side, so the line reads as a strapline under the
   wordmark rather than as a second line of text. They are a fixed width rather
   than a flexible one: the tagline is already the widest thing in the block, so
   a rule that grew to fill the row would widen the header on every string. */
.logo-tagline {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5em;
    font-size: clamp(11px, 2.4vw, 18px);
    font-weight: 700;
    letter-spacing: 0.5px;
    text-align: center;
    color: var(--clr-text-muted);
    margin-top: 4px;
}

.logo-tagline::before,
.logo-tagline::after {
    content: "";
    flex: none;
    width: 1.7em;
    height: 1px;
}

/* Each rule fades away from the text it flanks. The angles are physical, not
   writing-mode relative, so ::before is the right-hand rule on this RTL page
   and takes the sweep that is strongest at its left end. */
.logo-tagline::before {
    background-image: linear-gradient(90deg, var(--accent-strong), transparent);
}

.logo-tagline::after {
    background-image: linear-gradient(90deg, transparent, var(--accent-strong));
}

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

    .text-logo:hover .logo-mark {
        transform: none;
    }

    .text-logo:hover .logo-mark-spark {
        animation: none;
    }
}

/* Smaller footer variant */
.logo-footer .logo-main {
    font-size: 40px;
}

.logo-footer .logo-tagline {
    font-size: 14px;
}

.btn {
    display: inline-block;
    font-size: 18px;
    padding: 10px 15px;
    border: 2px solid var(--clr-main);
    background: var(--clr-main);
    color: var(--clr-on-main);
    text-align: center;
    font-weight: 500;
    border-radius: var(--radius-pill);
    cursor: pointer;
    transition: all 0.3s ease;
    text-decoration: none;
}

.btn-outline {
    border: 2px solid var(--clr-main);
    background: transparent;
    color: var(--clr-main);
}

.btn:hover {
    text-decoration: none;
    filter: brightness(0.75);
}

/* Responsive Design */
@media (max-width: 768px) {
    /* Header stays a single row: the logo and the advertise button shrink
       instead of wrapping, and neither one's text breaks across lines.
       Both logo lines are sized by clamp(), so there is no size step here. */
    .top-bar {
        flex-wrap: nowrap;
        gap: 10px;
    }

    .top-bar-item {
        min-width: 0;
    }

    .logo-main,
    .logo-tagline {
        white-space: nowrap;
    }

    /* The tagline's flanking rules go: this is the width at which the header is
       forced into a single non-wrapping row, and the tagline is the widest
       thing in the logo block, so its ornament is what has to give. */
    .logo-tagline::before,
    .logo-tagline::after {
        display: none;
    }

    .logo-footer .logo-main {
        font-size: 30px;
    }

    /* Label above the number rather than beside it — both on one line each. */
    .whatsapp-advertise {
        flex-direction: column;
        align-items: center;
        gap: 0;
        font-size: 13px;
        padding: 6px 12px;
        line-height: 1.3;
        white-space: nowrap;
    }
}

@media (max-width: 480px) {
    /* Only the label survives — the number is one tap away in the chat itself. */
    .whatsapp-advertise {
        font-size: 12px;
        padding: 6px 10px;
    }

    /* Scoped to the pill rather than written as a bare class, so it outranks
       `.whatsapp-advertise span { display: inline-block }` above. On its own
       the class ties one weight lower than that rule and the number stays on
       screen at exactly the width this block exists to clear. */
    .whatsapp-advertise .whatsapp-advertise-number {
        display: none;
    }
}

/* Breadcrumbs */
/* The trail is orientation, not content: it sits between the header band and the
   page's own heading and has to read as a step down from both. Muted at 13px
   with real room around it, so the <h1> below is unmistakably the first thing on
   the page. */
#breadcrumbs {
    padding: 18px 0 6px;
    font-size: 13px;
    color: var(--clr-engagement);
}

/* The trail opens with the site's own mark rather than a generic house glyph.
   It is a mask over an accent fill, not an inline SVG: the mark's facets are
   invisible at this size, so all that is needed is the silhouette and its two
   glints — and drawn this way the icon re-skins with --clr-main and lives in one
   place, the same treatment .motif-field and the heading glints use. The span it
   sits on is empty and aria-hidden: the home link beside it is already named. */
.breadcrumb-home {
    display: inline-block;
    vertical-align: -0.15em;
    width: 1.15em;
    height: 1.15em;
    background-color: var(--clr-main);
    /* --mark-mask is the configured mark (adx_motif_root_css()); the literal is the
       original bunny, used only if that inline style is missing. */
    mask-image: var(--mark-mask, url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2048%2048'%3E%3Cpath%20d='M24%2024L32.6%2027.2L35.5%2033.5L30.5%2041.6L24%2045.4L17.5%2041.6L12.5%2033.5L15.4%2027.2ZM10.5%205.5L7.5%2017L14%2028L18.5%2029.5L15.5%2016ZM37.5%205.5L40.5%2017L34%2028L29.5%2029.5L32.5%2016Z'/%3E%3Cpath%20d='M44.2%207.4Q45.1%2010.1%2047.8%2011Q45.1%2011.9%2044.2%2014.6Q43.3%2011.9%2040.6%2011Q43.3%2010.1%2044.2%207.4Z'/%3E%3Cpath%20d='M4.2%2023Q4.95%2025.25%207.2%2026Q4.95%2026.75%204.2%2029Q3.45%2026.75%201.2%2026Q3.45%2025.25%204.2%2023Z'/%3E%3C/svg%3E"));
    mask-size: contain;
    mask-repeat: no-repeat;
    mask-position: center;
}

/* Main Content */

h1, h2 {
    line-height: 1.2;
}

/* Page titles take the accent, which is what marks a heading as chrome naming a
   section rather than as content. `.category-title` carries the same accent
   itself, so the homepage bands and an archive's own <h1> match.

   The two headings that deliberately opt out are `.single-ad-title` and the
   article headline: both are the post's own words at two or three lines, and a
   saturated accent at that length outshouts the page it introduces. */
h1 {
    color: var(--clr-main);
    text-shadow: var(--text-shadow-default);
}

.archive h1 {
    text-align: center;
}

.grid {
    display: grid;
    /* Tighter than the cards are wide. The photo is the card, so the grid reads
       as one wall of them; the row gap runs a little wider than the column gap
       because a card's own contact strip already separates it from the row
       below, where nothing separates it from its neighbour. */
    grid-column-gap: 20px;
    grid-row-gap: 26px;
    /* Column count is a knob (--grid-columns), but only here: the ≤991px query
       below pins two columns whatever it is set to, because a phone has room
       for two cards and no setting changes that. */
    grid-template-columns: repeat(var(--grid-columns), 1fr);
    grid-auto-rows: 1fr; /* Make all rows equal height */
}

/* The card is a photograph with a strip of controls under it, and nothing else.
   Everything the card says about the ad — city, title, freshness, appearance,
   counts — rides the photo in .card-caption, so the media starts at the card's
   own top edge and takes every pixel the contact strip does not need. A text
   header above the photo costs a fixed 80px on every card in the grid and puts
   the least interesting thing first. */
.listing {
    position: relative;
    display: flex;
    flex-direction: column;
    /* Tracks the media area rather than being a second number to keep in step
       with it: the addition is the space the contact strip below it needs.
       Change --card-media-px and the card grows with it. */
    min-height: calc(var(--card-media-height) + 62px);
    /* An opaque surface, one step up from the page. This is load-bearing, not
       decoration: the tier halos are box-shadows spreading from the card's edge,
       and an opaque card is what keeps the near side of that bloom behind the
       card instead of washing across it. */
    background-color: var(--bg-panel);
    border: 1px solid var(--border-accent);
    border-radius: var(--radius-lg);
    overflow: hidden;
    direction: rtl;
    transition: box-shadow 0.3s ease, border-color 0.3s ease;
}

article.listing {
    /* No contact strip at all, so the card is the photo. */
    min-height: var(--card-media-height);
}

/* The media is the card's spring. A grid row is as tall as its tallest card
   (grid-auto-rows: 1fr), and every card in that row is stretched to match, so
   the slack has to land somewhere: growing the figure puts it in the photo,
   where it reads as a taller image, instead of leaving the empty band above the
   contact buttons that stretching .grid-item-content produces. The height stays as the flex basis, so a card with no slack to
   absorb is exactly --card-media-height as before. */
.listing figure {
    position: relative;
    overflow: hidden;
    margin: 0;
    padding: 0;
    flex: 1 1 auto;
    height: var(--card-media-height);
    background-color: var(--bg-dark);
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Branded backdrop behind the card media, visible while the image loads.
   --site-name is injected into :root by ThemeConfig::siteNameRootCss(), so the
   site name is never written into a stylesheet. */
.listing figure::before {
    content: var(--site-name, '');
    position: absolute;
    inset: 0;
    z-index: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 15px;
    font-size: 20px;
    font-weight: 600;
    text-align: center;
    color: var(--clr-text-muted);
    opacity: .3;
    pointer-events: none;
}

/* No transition here: nothing in the stylesheet changes a property on card media,
   and a `transition: all` fights the inline transform the gallery slide sets in
   scripts.js — the animation owns those properties for its duration. */
.listing img,
.listing video {
    position: relative;
    z-index: 1;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
}

/* Gallery arrows. Small, inset, and absent until the pointer arrives: they are
   a second way to see the ad, not the first thing about it, and two 80px discs
   parked over the subject's face on every card in a grid of thirty is the
   loudest chrome on the page. They sit above the photo's centre so they clear
   the caption block at its foot. */
.listing figure button {
    position: absolute;
    top: 42%;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    height: 34px;
    width: 34px;
    font-size: 14px;
    border-radius: var(--radius-round);
    z-index: 4;
    opacity: 0;
    border: none;
    color: var(--clr-on-overlay);
    background-color: var(--overlay);
    transition: opacity 0.25s ease, background-color 0.25s ease;
    cursor: pointer;
}

.listing:hover figure button,
.listing figure button:focus-visible {
    opacity: 0.85;
}

.listing figure button:hover {
    opacity: 1;
    background-color: color-mix(in srgb, black 85%, transparent);
}

/* A touch device has no hover to reveal them with, so there they are always on
   — held back rather than hidden, since a swipe is the primary gesture anyway. */
@media (hover: none) {
    .listing figure button {
        opacity: 0.7;
    }
}

.listing figure button.prev {
    left: 8px;
}

.listing figure button.next {
    right: 8px;
}

/* ======================================================================
   THE CARD CAPTION
   ======================================================================
   Everything the card says about the ad, in one block laid over the foot of the
   photo: city, freshness and counts on the first line, the ad's title on the
   second, its appearance facets on the third.

   One block, not one pill per fact, and that is the whole idea. Scrim pills
   scattered over a photograph read as unrelated stickers; a single block
   anchored to the bottom edge reads as the card's caption, which is what it is.
   It also needs no offsets kept in step with each other — the rows simply stack,
   and a row the ad cannot fill closes on its own.

   The scrim is a gradient rather than a fill: opaque enough under the text to
   carry white type over a bright photo, and gone by the middle of the frame.
   `black` here is the CSS keyword, not a theme colour — a scrim darkens toward
   true black whatever the palette is, so there is nothing to re-theme.

   Written with logical properties, so the block needs no RTL assumption: the
   caption spans the media's full width and its rows start at the reading edge. */
.card-caption {
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    z-index: 3;
    display: flex;
    flex-direction: column;
    gap: 5px;
    padding: 50px 12px 11px;
    background-image: linear-gradient(to top,
        color-mix(in srgb, black 90%, transparent) 0%,
        color-mix(in srgb, black 78%, transparent) 30%,
        color-mix(in srgb, black 46%, transparent) 60%,
        color-mix(in srgb, black 14%, transparent) 82%,
        transparent 100%);
    color: var(--clr-on-overlay);
    /* The scrim alone cannot carry the top row. The caption's rows stack, so the
       meta line always sits on the weakest part of the gradient — and against a
       bright photo (a beach, a sky) that lands around 3:1, which is fine for the
       title in the strong part below and not for 11.5px type. A shadow under
       every row is what closes that, and it is the right tool: pushing the
       gradient opaque enough to do it alone would black out a third of the
       photograph, which is the thing the card is selling. Inherited, so each row
       gets it; the title restates a heavier one of its own. */
    text-shadow: 0 1px 3px color-mix(in srgb, black 75%, transparent);
    pointer-events: none;
}

/* The meta line. The counts take `margin-inline-start: auto` (further down)
   rather than the row taking `space-between`, so they sit at the far end
   whether or not the card has a city to put at the near one — an article card
   has neither city nor facets and still gets the same line. */
.card-caption-meta {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    font-size: 11.5px;
    line-height: 1.4;
}

.card-city {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    min-width: 0;
    margin: 0;
    font-style: normal;
    font-size: inherit;
    font-weight: 500;
    color: color-mix(in srgb, var(--clr-on-overlay) 90%, transparent);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.card-city i {
    flex: none;
    font-size: 0.92em;
    color: var(--clr-main);
}

/* The title. Two lines is the design rather than a limit worked around: an ad
   title on this site is a keyword line rather than a name, so a third line buys
   nothing and costs the photograph. The shadow sits under the type rather than
   in the scrim, which lets the scrim stay light enough to keep the picture
   readable through it. */
.listing .card-title {
    font-family: var(--font-family);
    margin: 0;
    font-size: 15.5px;
    font-weight: 600;
    line-height: 1.25;
    color: var(--clr-on-overlay);
    text-shadow: 0 1px 4px color-mix(in srgb, black 80%, transparent);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* The "new" marker. It sits inline with the city because freshness is a fact
   about the ad like its location, not a third corner ornament competing with
   the tier chip and the ribbon. A flat accent fill, never the metal sweep:
   metal on a card means something was paid for, and this is asserted for free. */
.card-caption .label-new {
    flex: none;
    padding: 1px 7px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.04em;
    line-height: 1.6;
    border-radius: var(--radius-pill);
    /* A wash of the accent rather than a solid fill of it. Almost every ad in a
       fresh category is under seven days old, so a saturated pill here lands on
       every card in the grid and labels nothing — the wash keeps the marker
       readable where it matters, on the minority of cards that lack it. */
    background-color: color-mix(in srgb, var(--clr-main) 20%, transparent);
    color: var(--clr-main);
}

/* Appearance facets: the values run together and separated by a middot rather
   than boxed one chip per facet. Four chips read as four things to weigh; one
   line reads as one description, which is what it is. No icons either — a globe,
   a bust and a pair of scissors are three more shapes on a photograph, and the
   words say it themselves.

   The separator belongs to the item that follows it, so a facet that wraps takes
   its dot with it and no line starts on a dangling separator. The row's own gap
   sits either side of that dot, which is why the item repeats it: even spacing
   without a separator element between every pair. */
.listing .attributes-overlay {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    font-size: 11.5px;
    line-height: 1.4;
    color: color-mix(in srgb, var(--clr-on-overlay) 74%, transparent);
}

.attribute-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

.attribute-item + .attribute-item::before {
    content: '\00B7';
    opacity: 0.55;
}

/* A narrow card has room for the title and one more thing, so the facets go
   before the title has to give up a line. */
@media (max-width: 576px) {
    .card-caption {
        padding: 40px 9px 9px;
        gap: 3px;
    }

    .listing .attributes-overlay {
        display: none;
    }

}

/* VIP Listings - the top tier, the widest and warmest halo in the grid. It
   outranks GOLD on spread and on the crisp accent ring at the border, which is
   what keeps the two readable apart: their accents are neighbouring hues, so
   weight rather than colour alone has to carry the difference.

   The halo is box-shadow and must stay box-shadow. A blurred full-size
   pseudo-element at z-index -1 only sits *behind* the card while the card is
   not a stacking context, and archive/filters-listing.css puts `transform:
   scale(1)` on every .listing for the filter animation — which makes every card
   on an archive page exactly that. The bloom then paints over the card surface
   instead of around it and floods the whole card with accent. */
.listing.vip {
    position: relative;
    border-color: var(--bg-secondary);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--clr-secondary-sheen) calc(26% * var(--shadow-strength)), transparent),
    0 0 20px color-mix(in srgb, var(--bg-secondary) calc(18% * var(--shadow-strength)), transparent),
    0 0 44px color-mix(in srgb, var(--clr-border) calc(15% * var(--shadow-strength)), transparent);
}

/* Both tier rules below scale by ALPHA rather than by geometry: the offsets and
   blur radii are what separate VIP's wide halo from GOLD's tighter one, and
   scaling those too would collapse the difference between two paid tiers at low
   --shadow-pct settings. */

/* GOLD Listings - Luxurious gold gradient effect */
.listing.gold {
    border-color: var(--clr-gold);
    box-shadow: 0 0 14px color-mix(in srgb, var(--clr-gold) calc(22% * var(--shadow-strength)), transparent),
    0 0 28px color-mix(in srgb, var(--clr-gold-secondary) calc(12% * var(--shadow-strength)), transparent);
}

/* Hover effects for premium listings */
.listing.vip:hover {
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--clr-secondary-sheen) calc(45% * var(--shadow-strength)), transparent),
    0 6px 28px color-mix(in srgb, var(--bg-secondary) calc(32% * var(--shadow-strength)), transparent),
    0 12px 52px color-mix(in srgb, var(--clr-border) calc(24% * var(--shadow-strength)), transparent);
}

.listing.gold:hover {
    box-shadow: 0 6px 20px color-mix(in srgb, var(--clr-gold) calc(34% * var(--shadow-strength)), transparent),
    0 12px 34px color-mix(in srgb, var(--clr-gold-secondary) calc(20% * var(--shadow-strength)), transparent);
}

/* Regular listings hover effect. Both tier rules above override box-shadow
   outright, so this pairing only ever paints a regular card: the scrim gives
   the lift and the accent ring picks the edge out of the grid. */
.listing:hover {
    border-color: color-mix(in srgb, var(--bg-secondary) 55%, transparent);
    box-shadow: 0 calc(4px * var(--shadow-strength)) calc(14px * var(--shadow-strength)) var(--overlay);
}

/* The paid tiers' name, in the media's top-right corner. The tiers are already
   told apart by their border and halo, but only against each other — a card seen
   on its own says nothing about what the advertiser paid for. Each chip takes its
   own tier's accent sweep, so it reads as the same metal as the border around
   it, and it is inert to the pointer: the whole card is a link.

   It claims one top corner of the photo and the "real pictures" ribbon claims
   the other, so the two frame the media as a pair. They are the only two
   ornaments a card hangs on the picture; everything else it has to say lives in
   the caption at the foot. */
.tier-chip {
    position: absolute;
    top: 10px;
    right: 10px;
    z-index: 4;
    padding: 2px 9px;
    font-size: 10px;
    font-weight: 800;
    letter-spacing: 0.09em;
    line-height: 1.7;
    border-radius: var(--radius-pill);
    background-image: var(--bg-linear-gradient);
    color: var(--clr-on-gradient);
    box-shadow: var(--shadow-default);
    pointer-events: none;
}

.listing.gold .tier-chip {
    background-image: var(--bg-linear-gradient-gold);
    color: var(--clr-on-gold);
}

/* The "real pictures" corner ribbon.

   Three parts. The outer span is a box pinned to a corner with `overflow:
   hidden`; the inner span is a band rotated 45deg across it, long enough that
   both of its ends leave through the box's top and left edges and are cut
   there; and the box's own `::before`/`::after` are two fold wedges sitting
   against those same edges, just past the band, where the ribbon would carry on
   behind the corner.

   The wedges are what make it a ribbon rather than a stripe, and they have to
   be drawn *inside* the corner: the band's own ends cannot carry them, because
   `.listing` and `.gallery-main` both clip, so a tail hung off a band end is
   either cut away with it or — if the band is shortened to keep the tails
   inside — left floating in the middle of the photo. Hence the split: the band
   is deliberately over-long and clipped, and the folds are separate elements
   positioned at the edges the band crosses.

   Two numbers are the whole design; everything else derives from them, so a
   caller that wants a bigger ribbon restates only those plus the type size:

     --ribbon-offset  how far the band's near edge sits from the corner
     --ribbon-depth   how thick the band is

   `--ribbon-far` is where the band's far edge meets each edge of the box —
   `(offset + depth) * root-2` along the axis — which is where the wedges go.
   `--ribbon-span` only has to overrun the corner it crosses; the chord at the
   band's own distance is `2 * offset + depth`, and the span adds a depth at each
   end of it — `2 * offset + 3 * depth` — so both end faces are cut by the box's
   edges rather than landing inside it. */
.label-verified {
    position: absolute;
    --ribbon-offset: 30px;
    --ribbon-depth: 21px;
    --ribbon-span: calc(2 * var(--ribbon-offset) + 3 * var(--ribbon-depth));
    --ribbon-fold: calc(var(--ribbon-depth) / 3.5);
    --ribbon-centre: calc((var(--ribbon-offset) + var(--ribbon-depth) / 2) / 1.4142);
    --ribbon-far: calc((var(--ribbon-offset) + var(--ribbon-depth)) * 1.4142);
    width: calc(var(--ribbon-centre) + (var(--ribbon-span) + var(--ribbon-depth)) / 2.8284 + var(--ribbon-fold));
    height: calc(var(--ribbon-centre) + (var(--ribbon-span) + var(--ribbon-depth)) / 2.8284 + var(--ribbon-fold));
    overflow: hidden;
    /* Explicit rather than inherited: the card sits in an RTL page and the
       single-page gallery forces LTR on itself, and the ribbon has to letter the
       same way in both. */
    direction: rtl;
    font-size: 10px;
    /* Whatever this is laid over is a link or a slider. */
    pointer-events: none;
}

.label-verified > span {
    position: absolute;
    display: block;
    width: var(--ribbon-span);
    line-height: var(--ribbon-depth);
    /* Rotation is about the band's own centre, so placing that centre on the
       diagonal is the whole positioning job. */
    left: calc(var(--ribbon-centre) - var(--ribbon-span) / 2);
    top: calc(var(--ribbon-centre) - var(--ribbon-depth) / 2);
    transform: rotate(-45deg);
    text-align: center;
    font-weight: 700;
    letter-spacing: 0.3px;
    /* The primary accent sweep, so the ribbon is the same metal as the rest of
       the theme's ornament and re-skins with the palette. Its label flips with
       the accent's own lightness, so a light --clr-main gets dark text. */
    background-image: var(--bg-linear-gradient-secondary);
    color: var(--clr-on-main);
    /* A lit edge along the top of the band and a dark one under it, so it reads
       as raised rather than printed on the photo, plus the drop shadow that
       lifts it off the media. */
    box-shadow: inset 0 1px 0 var(--clr-main-sheen),
    inset 0 -1px 0 var(--clr-main-muted),
    0 calc(2px * var(--shadow-strength)) calc(8px * var(--shadow-strength)) var(--overlay);
}

/* The two fold wedges: the underside of the ribbon, showing where the band runs
   off each edge and the rest of it would carry on behind the corner.

   Each is a `clip-path` triangle, not the usual zero-size-box-with-borders
   trick. Borders can only build a right-isoceles triangle whose square corner
   faces the box corner, and the shape needed here has none: it wants one edge
   lying *along* the band's far edge (the 45deg line `x + y = --ribbon-far`), one
   running down the box edge from where the band crosses it, and a third closing
   the two. Built from borders it comes out as a chevron sitting a hair off the
   band, with a sliver of photo showing between the two.

   So each wedge is a plain box straddling the crossing point — F across, 2F
   along the edge — and the polygon names the three corners in its own
   percentages: the crossing point, the point up the band's far edge, and the
   point down the edge. Both wedges are therefore flush with the band by
   construction, at any --ribbon-depth. They take the shaded mix rather than the
   accent: this is the part in shadow. */
.label-verified::before,
.label-verified::after {
    content: "";
    position: absolute;
    background-color: var(--clr-main-muted);
}

/* Down the left edge. */
.label-verified::before {
    left: 0;
    top: calc(var(--ribbon-far) - var(--ribbon-fold));
    width: var(--ribbon-fold);
    height: calc(2 * var(--ribbon-fold));
    clip-path: polygon(0 50%, 100% 0, 0 100%);
}

/* Along the top edge — the same triangle mirrored across the corner diagonal. */
.label-verified::after {
    top: 0;
    left: calc(var(--ribbon-far) - var(--ribbon-fold));
    width: calc(2 * var(--ribbon-fold));
    height: var(--ribbon-fold);
    clip-path: polygon(50% 0, 0 100%, 100% 0);
}

/* On a card the box is pinned to the media's top-left corner. */
/* The photo now starts at the card's own top edge, so the ribbon sits in the
   corner of the card itself. */
.listing .label-verified {
    top: 0;
    left: 0;
    /* Above the media and its loading backdrop, below the caption. */
    z-index: 2;
    --ribbon-offset: 26px;
    --ribbon-depth: 18px;
    font-size: 9px;
}

/* Card Content Layout. Sized by its own rows and no more — the figure above
   takes the card's spare height, so this block never stretches and never opens
   a gap above the contact buttons. */
.grid-item-content {
    display: flex;
    flex-direction: column;
    flex: 0 0 auto;
    padding: 0;
}

/* The counts, at the far end of the caption's meta line. They need no scrim of
   their own — the caption already carries one — and no pill: boxed, three tiny
   numbers read as a control, where set as plain type beside the city they read
   as what they are, a footnote about the ad.

   `margin-inline-start: auto` is what puts them at the far end, and it works
   whether or not there is a city at the near one. They sit further apart than
   the facets below: a facet is one word and the middot already separates it,
   while a count is an icon and a number that have to read as one unit before
   the next pair starts. */
.listing .post-engagement {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: none;
    margin-inline-start: auto;
    font-size: inherit;
    font-weight: 500;
    line-height: 1.4;
    color: color-mix(in srgb, var(--clr-on-overlay) 78%, transparent);
    white-space: nowrap;
}

/* An ad nobody has viewed, commented on or liked yet. The row still exists —
   render_card_engagement() explains why — but a line of zeroes is not a
   reading, and on a new category it would be on every card in the grid. */
.listing .post-engagement-empty {
    display: none;
}

/* Individual engagement items. The count is the reading; the icon only says
   which metric it is, so the glyph sits back from the number at a smaller size.
   Both take the pill's own foreground — on a scrim there is no dim grey left
   that stays legible. */
.engagement-item {
    display: flex;
    align-items: center;
    gap: 4px;
}

/* The item is a flex row, so the two boxes are already centred on each other —
   what is left is optical. A digit's box carries descender space it never fills,
   which lifts its ink above the box centre, so a box-centred glyph lands low;
   0.05em of its own size puts the two ink centres together, and 0.85em makes the
   glyph about as tall as the digits beside it. Both are em, so they hold at the
   smaller mobile size without a second rule. */
.engagement-item i {
    position: relative;
    top: -0.05em;
    font-size: 0.85em;
    opacity: 0.75;
}

/* Contact Buttons — the strip under the photo.
   Each button keeps its natural height, and the block is not padded out to a
   fixed number of rows: an ad with no phone gets a taller photo rather than a
   double-height green button, so every card in a grid row ends in an identical
   contact strip.

   ONE FILLED CONTROL PER CARD. Two saturated slabs stacked — a violet phone bar
   over a green WhatsApp bar — are the loudest thing in a grid of thirty cards,
   and repeated thirty times they stop reading as buttons and start reading as
   the page's background pattern. So the strip is a row rather than a column,
   and the phone is demoted from a filled button to a quiet surface: it is a
   number you can tap, which is information, while WhatsApp is the action. The
   green then has one job on the card and gets noticed doing it.

   The row wraps rather than being split by a media query. The two flex bases
   are what decide it: side by side on a card wide enough for both, stacked on
   one that is not, with no breakpoint to keep in step with the column count. */
.grid-item-contact {
    margin-top: auto; /* Push to bottom */
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: 8px;
}

.grid-item-contact .phone,
.grid-item-contact .whatsapp,
.grid-item-contact .telegram {
    position: relative;
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    display: block;
    padding: 9px 8px;
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.01em;
    text-align: center;
    margin: 0;
    z-index: 2;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: filter 0.25s ease, background-color 0.25s ease, border-color 0.25s ease;
}

/* The number, not a call to action: a translucent wash of the card's own
   surface with a hairline round it. It reads as tappable without competing with
   the one filled control beside it, and it takes --clr-text legitimately —
   the fill is mostly the card showing through, so the page foreground is
   correct there by construction. */
.grid-item-contact .phone {
    flex: 1 1 126px;
    background-color: var(--surface-translucent);
    border-color: var(--border-subtle);
    color: var(--clr-text);
}

.grid-item-contact .phone i {
    font-size: 0.92em;
    color: var(--clr-main);
}

.grid-item-contact .phone:hover {
    background-color: var(--accent-soft);
    border-color: var(--accent-strong);
}

.grid-item-contact .social-buttons {
    flex: 1 1 116px;
    display: flex;
    gap: 6px;
    flex-wrap: nowrap;
    min-width: 0;
}

/* An even split that can still shrink: a fixed 50% basis plus the row's gap
   overflows the card. */
.grid-item-contact .social-buttons .whatsapp,
.grid-item-contact .social-buttons .telegram {
    flex: 1 1 0;
    min-width: 0;
}

/* An ad with both a WhatsApp number and a Telegram link splits the row three
   ways, and at that width neither word survives — both labels ellipsize. Two
   brand glyphs side by side say it more clearly than two truncated words, and
   these are the two most recognisable logos on the phone in the visitor's hand.
   The word is kept whenever a card has only one of them. */
.grid-item-contact .social-buttons .whatsapp:not(:only-child) .contact-label,
.grid-item-contact .social-buttons .telegram:not(:only-child) .contact-label {
    display: none;
}

.grid-item-contact .social-buttons .whatsapp:not(:only-child),
.grid-item-contact .social-buttons .telegram:not(:only-child) {
    font-size: 16px;
}

/* The one gradient fill with no --clr-on-* token behind it: the sweep runs
   across two greens of different lightness, so no single flipped-lightness
   label serves both stops and the button takes the page foreground instead.
   Safe while both stops stay dark enough for it; a site that lightens the
   WhatsApp pair should give this button a flat fill rather than invent a
   token for it. Same reasoning as .filter-results .btn. */
.grid-item-contact .whatsapp {
    background: var(--bg-linear-gradient-whatsapp);
    color: var(--clr-text);
}

.grid-item-contact .telegram {
    background-color: var(--clr-bg-telegram);
    color: var(--clr-on-telegram);
}

.grid-item-contact .whatsapp:hover,
.grid-item-contact .telegram:hover {
    text-decoration: none;
    filter: brightness(0.88);
}

.grid-item-contact .phone:hover {
    text-decoration: none;
}

/* Footer. A hairline rather than a change of colour separates it from the page,
   the same device the header uses between its two rows, so the two ends of the
   document are built the same way. */
footer {
    background: var(--bg-panel);
    border-top: 1px solid var(--border-accent);
    padding-top: 38px;
    padding-bottom: 30px;
}

.site-disclaimer {
    margin-top: 22px;
    margin-inline: auto;
    max-width: 68ch;
    text-align: center;
    font-size: 12.5px;
    line-height: 1.6;
    color: var(--clr-engagement);
}

.site-credit {
    margin-top: 22px;
    text-align: center;
    font-size: 12.5px;
    color: var(--clr-engagement);
}

/* Only tighten the gap when the disclaimer actually precedes the credit — an empty
   FOOTER_DISCLAIMER prints no element, and the credit keeps its usual 22px. */
.site-disclaimer + .site-credit {
    margin-top: 10px;
}

button,
input,
select,
textarea {
    font: inherit;
}

* {
    box-sizing: border-box;
}

*:before,
*:after {
    box-sizing: border-box;
}

a {
    color: var(--clr-main);
    text-decoration: none;
}

a:hover,
a:focus {
    text-decoration: underline;
}

img {
    vertical-align: middle;
}

.text-center {
    text-align: center;
}

ul,
ol {
    margin-top: 0;
    margin-bottom: 15px;
}

ul ul,
ol ul,
ul ol,
ol ol {
    margin-bottom: 0;
}

.hidden {
    display: none !important;
}

.underline {
    text-decoration: underline;
}

.text-left {
    text-align: left;
}

.small {
    font-size: 85%;
}

.bg-dark {
    background-color: var(--bg-dark) !important;
}

section.grid {
    margin-bottom: 25px;
}

/* Comment honeypot: never shown to a human, so bots that fill it are unmasked.
   Deliberately not .sr-only — this must be hidden from screen readers too. */
.comment-trap {
    display: none !important;
}

/* Visually hidden, still announced. `.sr-only` is the theme's own class;
   `.screen-reader-text` is WordPress core's, emitted by markup the theme does not
   write — the heading inside the_posts_pagination(), the comment-form notes, the
   skip link. Without it here, core's "hidden" headings render as visible text on
   any page that uses one. */
.sr-only,
.screen-reader-text {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

hr {
    margin-top: 15px;
    margin-bottom: 15px;
    border-color: var(--bg-secondary);
}

#topBtn {
    position: fixed;
    bottom: 15px;
    right: 15px;
    font-size: 20px;
    background: var(--bg-dark);
    color: var(--clr-on-dark);
    opacity: .8;
    border: none;
    cursor: pointer;
    display: none;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    padding: 0;
    z-index: 3;
    border-radius: var(--radius-round);
}

/* Responsive */
@media (max-width: 991px) {

    /* minmax(0, 1fr), not minmax(150px, 1fr): a 150px floor plus the gap needs
       315px of grid, which is wider than the container on a 320px phone, and the
       whole page gains a horizontal scrollbar. The tracks are already equal and
       already as wide as the container allows; the floor only ever fired at the
       widths where it broke the page. */
    .grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        grid-column-gap: 14px;
        grid-row-gap: 18px;
    }

    .listing {
        min-height: calc(var(--card-media-height) + 58px);
    }

    .card-caption-meta {
        font-size: 10.5px;
        gap: 6px;
    }

    .listing .post-engagement {
        gap: 8px;
    }

    .listing .card-title {
        font-size: 14px;
    }

    .grid-item-contact .phone,
    .grid-item-contact .whatsapp,
    .grid-item-contact .telegram {
        font-size: 13px;
        padding: 8px 6px;
    }

    /* Two columns on a phone puts a card at roughly half the desktop width, so
       the ribbon steps down with it — the two design numbers and the type size,
       and the derived geometry follows. */
    .listing .label-verified {
        --ribbon-offset: 21px;
        --ribbon-depth: 15px;
        font-size: 8px;
    }

    .listing .label-verified > span {
        letter-spacing: 0;
    }

}

/* Two cards to a phone screen puts a card at about 170px, which is not wide
   enough for a phone number and a spelled-out WhatsApp button side by side — so
   the strip wraps and the card ends in two stacked bars again, which is the
   thing the row was built to avoid. The glyph is the most recognised logo on the
   phone in the visitor's hand, and the number is the part that has to stay
   readable, so the word goes and the row holds.

   This block sits below the base .grid-item-contact rules on purpose: it ties
   with them on specificity, and a media query adds none — source order is what
   decides it. */
@media (max-width: 576px) {

    .grid-item-contact .contact-label {
        display: none;
    }

    /* The number is the button's whole content and must not ellipsize — a
       half-shown phone number is worse than no button. Everything else in the
       row gives way to it: the glyph loses its own icon's companion, the
       padding tightens, and the type steps down to the smallest size the digits
       still read at. */
    /* The basis is what decides whether the row wraps, so it is set below the
       width the number actually needs rather than at it: the button grows into
       whatever is left over, and a card 20px narrower than the one this was
       measured on still keeps its single row. */
    .grid-item-contact .phone {
        flex: 1 1 70px;
        padding-inline: 6px;
        font-size: 11.5px;
        letter-spacing: 0;
    }

    .grid-item-contact .phone i {
        display: none;
    }

    .grid-item-contact .social-buttons {
        flex: 0 1 auto;
    }

    .grid-item-contact .social-buttons .whatsapp,
    .grid-item-contact .social-buttons .telegram {
        flex: 0 0 auto;
        padding-inline: 11px;
        font-size: 15px;
    }

}

/* Below about 360px two cards leave under 120px of usable strip, which is less
   than the number needs beside anything else. Rather than ellipsize it, the row
   stacks — and a stacked button is full-width, which is room enough for the
   WhatsApp word, so the label is restored here. */
@media (max-width: 359px) {

    .grid-item-contact .contact-label {
        display: inline;
    }

    .grid-item-contact .phone,
    .grid-item-contact .social-buttons {
        flex: 1 1 100%;
    }

    .grid-item-contact .social-buttons .whatsapp,
    .grid-item-contact .social-buttons .telegram {
        flex: 1 1 0;
        font-size: 13px;
    }

}

/* The card title steps down once the columns get narrow — four of them below
   xxl — so more titles fit inside the two-line clamp instead of hitting the
   ellipsis. The clamp is the design; this only changes how much reaches it. */
@media (max-width: 1399px) {

    .listing .card-title {
        font-size: 15px;
    }

}

@media (max-width: 450px) {
    .listing figure {
        /* A ceiling, not a fixed height: a card media area set shorter than
           300px stays as short as it was configured. */
        height: min(var(--card-media-height), 300px);
    }

}
