/*
Theme Name: Criteo Tech Community
Author: Criteo Tech Community
Description: Self-contained block theme for the Criteo Tech Community — a Caards/Recap-style magazine layout with a floating brand header, a corporate-style footer, post cards, dark mode, and multi-author bylines. Based on Automattic's Spiel, extended for Criteo.
Requires at least: 6.5
Tested up to: 7.0
Requires PHP: 8.0
Version: 3.11.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: criteo-tech-community
Tags: blog, news, grid-layout, block-styles, featured-images, full-site-editing, sticky-post, threaded-comments, wide-blocks, block-patterns
*/

/*
 * Button Block
 * Control the hover stylings of outline block style.
 * Unnecessary once block styles are configurable via theme.json
 * https://github.com/WordPress/gutenberg/issues/42794
 */
.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-background):hover {
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--base);
	border-color: var(--wp--preset--color--primary);
}

/*
 * Pull quote Block
 * Reset the browser default margins for blockquote element
 * https://github.com/WordPress/gutenberg/issues/44129
 */
.wp-block-pullquote blockquote,
.wp-block-pullquote p {
	margin: 0;
}

/* ==========================================================================
   Homepage post cards (Recap-style previews) + pagination.
   Scoped to the front page (body.home). Cards use the theme palette so the
   criteo-header dark-mode remap (:root[data-theme="dark"]) recolors them too.
   ========================================================================== */

/* Soft page background so the white cards lift by contrast (Recap-style)
   instead of relying on a hard border, plus a brand-tinted wash across the top
   (2.6.0). The wash replaces the visual weight the masthead photograph carried
   until 2.5.0 — without it the navy header pill floats on flat grey directly
   above the first white card and the page has no entry point.

   EVERY STOP IS AT OR DARKER THAN THE RESTING COLOUR, and that is a hard
   constraint, not taste: white cards on #f3f4f6 are only 1.11:1 (see the card
   rules below), so their edge is carried by a hairline and a shadow. A stop
   lighter than the page would dissolve the cards into the background.

   Literal hex per mode, never presets — the same rule as the banner scrim
   further down. `base`, `contrast`, `secondary` and `tertiary` are all remapped
   by the colour-mode toggle and `primary` never is, so stops routed through
   presets would invert with the theme.

   (Historical: until 3.9.0 the stops were hand-composited tints — #d8d7e8 was navy
   #130183 at 12% over the grey, #e0f3ef a mint pull at ~8%. That mint was #00eaa0,
   a colour the brand palette does not contain; it is gone from the theme entirely
   as of 3.10.0. The paragraphs below about tint strength describe THAT wash, not
   the brand gradient that replaced it — they are kept because the reasoning about
   band length versus what sits on top of it still applies.)

   STRENGTHENED AND SHORTENED IN 2.7.1. It shipped at 6%/5% over an 820px band and
   was effectively invisible: 13/14/6 per channel spread over most of a screen
   height is below the threshold where the eye reads a gradient rather than flat
   grey, and the homepage hero added afterwards covers the strongest top 400px —
   the region that was bare when those values were picked. Doubling the tint and
   shortening the band to 560px makes the same colour path steeper, so it reads in
   the places actually left visible: around the header pill, in the gutters beside
   the hero, and in the gap above the lead card. It still finishes before the first
   card row. If the top of the page ever gains or loses a full-width band again,
   re-check this — the band length is tuned to what sits on top of it.

   THE BAND MUST FINISH ON `background-color` IN BOTH BLOCKS (#f3f4f6 light,
   #14171d dark). That is what lets the wash be a sized 560px band with the flat
   colour continuing beneath it, instead of needing `background-attachment:
   fixed` (new to this codebase, and janky on iOS). A gradient on `body` is
   otherwise sized to the WHOLE document, so on a long page like /articles/ it
   would fade out imperceptibly. Desync the end and a seam appears at 560px.

   WHY THIS IS TWO LAYERS AS OF 3.9.0. The wash is now the brand team's
   `criteo-sunrise` gradient (theme.json), and the brand gradients are 45°, not
   180°. A diagonal gradient's isolines are diagonal, so unlike the old vertical
   ramp it CANNOT land flat on the resting colour at the bottom of the band — swap
   it in on its own and you get a diagonal seam across the page at y=560. The fix
   is a second layer: a vertical transparent→resting fade painted OVER the diagonal
   brand gradient. `background-size` and `background-repeat` apply to every layer,
   so the fade reaches full #f3f4f6 exactly where the band ends and the seam rule
   above is satisfied while the brand direction is preserved. Layer order matters —
   the fade must be listed FIRST (topmost).

   Literal hex per mode, never presets — the same rule as the banner scrim
   further down. `base`, `contrast`, `secondary` and `tertiary` are all remapped
   by the colour-mode toggle and `primary` never is, so stops routed through
   presets would invert with the theme. This is also why the fade layer is
   written as literal rgba() rather than a `color-mix()` on the preset.

   DO NOT scope this first block to `:root[data-theme="light"]`. It is unscoped
   on purpose: it matches in both modes, and the dark block below deliberately
   overrides only the two colour properties. Scoping it would leave the dark
   band with no `background-size` and it would tile down the page.

   Known, accepted: dark-mode.css transitions `background-color` on body, but
   `background-image` cannot interpolate — so on toggle the band snaps while the
   rest of the page eases over 0.2s. At these alphas that is not worth rebuilding
   the wash as a separate fade-able overlay layer.

   THE SOFTNESS IS MANDATED BY THE BRAND GUIDELINES. NOT A DEFECT. DO NOT "FIX" IT.
   Criteo Brand Guidelines v5.0, "Signature Gradients", defines Sunrise as
   #ffdccc → #d8d2fe and states: "The Sunrise gradient is primarily used for
   backgrounds. To ensure enough contrast with overlapping text and elements,
   please only use at 50% opacity." Composite those two stops at 50% over white and
   you get #FFEEE6 → #EBE9FE — which is, to within a rounding step, the #FFEDE5 →
   #EBE8FE below. So this wash IS the brand asset applied exactly as specified, and
   its lightness is the guideline doing its job, not a value somebody under-tuned.

   Measured white-card separation, for whoever comes here because the cards look
   like they are floating:

     shipped   brand-exact Sunrise   #EBE8FE → #FFEDE5   1.20 / 1.14
     rejected  Sunrise deepened 10%  #D5D1F2 → #E7D5DB   1.48 / 1.41
     (the 2.7.1 wash this replaced measured 1.42 / 1.15)

   Both were built and screenshotted side by side on 2026-08-14 and brand fidelity
   won — and the guideline discovered afterwards confirms the deepened variant would
   have BREACHED the spec, not merely deviated from taste. The card edge here rests
   on its hairline and shadow rather than on the wash; if cards ever start
   dissolving into the page, the lever is the card's own border, not this gradient.

   Live consumers of this band: the homepage, /articles/, category archives and
   search. Nothing else has a page background beyond theme.json's flat `base`. */
body.home,
body.blog,
body.archive,
body.search {
	background-color: #f3f4f6;
	background-image:
		linear-gradient(
			180deg,
			rgba( 243, 244, 246, 0 ) 0%,
			rgba( 243, 244, 246, 1 ) 100%
		),
		linear-gradient( 45deg, #EBE8FE 26%, #FFEDE5 74% );
	background-repeat: no-repeat;
	background-size: 100% 560px;
}

/* Dark mode is Twilight TINTED, never at full strength: #3D1EF8 behind the
   #1c2029 cards is a lit screen, not a reading surface. These are Twilight's own
   two stops composited at 25% over the dark `base` #14171d — and note that lands
   #141236, which is within a hair of the #141235 this block used before 3.9.0.
   The hand-tuned dark band was already, in effect, a muted Twilight, so adopting
   the brand gradient here keeps the existing calibration and only adds the
   electric end. Mint at a low alpha over near-black goes murky green, which is
   why this stayed single-hue navy in the first place. */
:root[data-theme="dark"] body.home,
:root[data-theme="dark"] body.blog,
:root[data-theme="dark"] body.archive,
:root[data-theme="dark"] body.search {
	background-color: var( --wp--preset--color--base );
	background-image:
		linear-gradient(
			180deg,
			rgba( 20, 23, 29, 0 ) 0%,
			rgba( 20, 23, 29, 1 ) 100%
		),
		linear-gradient( 135deg, #1E1954 26%, #141236 74% );
}

/* The homepage hero is a template part (parts/home-hero.html), and core wraps every
   part in its own <div class="wp-block-template-part">. That wrapper is a flow child
   of `.wp-site-blocks`, so it inherits the 32px block-gap — while the hero group's
   inline `margin-top: 0` is now INSIDE the wrapper and cannot suppress it. Zero it
   here so the hero sits exactly where it did as inline template markup, tight under
   the header pill with only its own spacing-20 spacer between them. */
.wp-block-template-part.criteo-home-hero {
	margin-top: 0;
}

/* Grid gaps: give the hero grid and the 3-col feed room to breathe. */
.criteo-cards .wp-block-post-template.is-layout-grid {
	gap: 1.75rem;
}

/* The Featured band: TWO across, and the biggest cards on the homepage.
   patterns/magazine-body.php

   THIS IS THE POINT OF THE CLASS, so don't "simplify" it back to a plain
   `columnCount` and nothing else. The band replaced `criteo-cards-quad`, a
   four-across override that produced 312px cards with no excerpt — directly beneath a
   three-card row of 425px cards WITH excerpts. That inverted the hierarchy: the posts
   deliberately promoted rendered smaller and thinner than the unpromoted ones, and at
   312px the multi-author bylines truncated mid-name. Featured now means visibly
   larger: 520-652px cards, with excerpts, above the row of 425px ones.

   `minmax(520px, 1fr)` rather than a fixed `repeat(2, …)`. The block declares
   `columnCount: 2`, which core emits as a fixed two-track grid that does NOT reflow —
   in a ~700px container that would mean two ~330px cards, straight back into the
   byline-wrap zone the four-across version suffered from. An `auto-fill`/`minmax()`
   track list picks the count from the available width instead.

   NOTE THAT IT RESPONDS TO THE BAND'S CONTAINER, NOT THE VIEWPORT — the band is
   `alignwide` inside a constrained layout, so the two are not the same number and it is
   easy to mis-predict. Measured on this site:

     container 1332px (alignwide desktop) → 2 columns, 652px cards
     container  703px                     → 1 column, 703px
     container  553px                     → 1 column, 553px
     container  236px (390px phone)       → 1 column, 236px

   The switch to one column happens once the container can no longer fit 2 x 520px plus
   the 1.75rem gap, i.e. below ~1068px. So a card is either >= 520px in a two-up row or
   it has the container to itself; it is never squeezed into a narrow multi-column track.
   Above ~420px multi-author bylines fit on one line, which is why this band needs NO
   byline clamp, unlike `criteo-cards-duo` below — now the clamp's only consumer. Below
   600px core takes it to one column anyway and the card is as wide as the phone allows,
   where a wrapped byline is normal and harmless.

   Below 600px core forces a single column anyway from a QUADRUPLED class selector
   (specificity 0,4,0) that deliberately outranks this; it lands on the same answer, so
   there is no mobile case to keep in sync here.

   Specificity 0,3,0 beats the `.wp-container-*` rule core emits at 0,1,0. */
.criteo-cards-feature .wp-block-post-template.is-layout-grid {
	grid-template-columns: repeat( auto-fill, minmax( 520px, 1fr ) );
}

/* 16/9 here, against the 3/2 every other card grid uses, and it is a proportion fix
   rather than a preference. Wider cards make the shared 3/2 crop enormous: measured at
   652px, a 3/2 image is 433px tall — 66% of a 654px card — so the card reads as a big
   picture with a strip of text under it, and the band came to 1367px against the
   ordinary row's 465px. 16/9 takes the image to ~367px and the band to roughly a third
   less height.

   It also happens to CROP NOTHING: the featured images on this site are 1024x576, i.e.
   16:9 already, and they are title cards with text in them. 3/2 was shaving ~16% off
   their width. Wider is both shorter and more faithful here.

   Declared on the IMG for the same reason as the 3/2 rule below — the block writes its
   ratio inline onto the <figure> only, and the isLink <a> between them has no height.

   NOTE THE DOUBLED CLASS. `.criteo-cards-feature .wp-block-post … img` is specificity
   0,3,1 — exactly the same as the 3/2 rule it has to beat, which sits LATER in this file
   and therefore won on source order. Matching on both of the band's classes makes this
   0,4,1, so it wins wherever either rule happens to sit. Keep in sync with `aspectRatio`
   in magazine-body.php. */
.criteo-cards.criteo-cards-feature .wp-block-post .wp-block-post-featured-image img {
	aspect-ratio: 16 / 9;
}

/* The category sits in the BOTTOM META ROW here, not as a kicker above the title —
   this band carries no date (a promoted post is not promoted for being recent), so
   the category takes the slot the date holds in every other grid, and the headline
   gets the top of the text body. See the long note in magazine-body.php.

   Only the box model changes: the uppercase / 0.72rem / brand-blue link treatment is
   inherited from the base `.wp-block-post-terms` rule further down, and in the meta
   row that reads as a tag, which is the intent. What has to go is the base rule's
   `margin: 0 0 0.55rem` — a kicker's gap to the title beneath it, which inside a
   flex row just pushes the label off centre.

   `flex: 0 0 auto` + `white-space: nowrap` make it hold its width the way the date
   did; the byline gets `min-width: 0` below so IT is what gives way when an author
   list runs long, since the row is `flex-wrap: nowrap`.

   DOUBLED CLASS, same trap as the rule above: the base rule is 0,3,0 and sits later
   in this file, so a 0,3,0 override here would tie and lose on source order. */
.criteo-cards.criteo-cards-feature .wp-block-post .wp-block-post-terms {
	margin: 0;
	flex: 0 0 auto;
	white-space: nowrap;
}

.criteo-cards.criteo-cards-feature .wp-block-post .criteo-byline {
	min-width: 0;
}

/* Keep the meta-row hairlines on one line, whatever the byline says.

   Cards in a grid row are equal height and the meta row is bottom-pinned with
   `margin-top: auto`, so the card BOTTOMS always align. The visible hairline is the
   meta row's top border, though, and that moves with the row's own height — which
   varies because the byline wraps. The byline shares its row with the date via
   `space-between`, so it only ever gets ~half the card width to play with.

   FIRST measured on the homepage's four-across Featured band (~312px cards): byline
   23 / 47 / 70px, hairlines up to 70px out of line within a single row. That band is
   no longer a card grid (2.8.0) and no longer uses this rule — at 312px the clamp was
   still truncating author names mid-word, which is part of why it became rows — but the
   measurements are kept because they are what established the wrap thresholds below.

   Measured again on /articles/ when it became two-across-plus-a-rail, which is where
   the `-duo` selector came from and is now the rule's ONLY consumer — the wrap starts
   far earlier than the 312px figure above suggested:

     420px cards → bylines 23px            spread  0   ✓
     351px cards → bylines 23, 46px        spread 24   ✗
     314px cards → bylines 23, 46, 68px    spread 46   ✗

   That page runs 314-420px in the split and 323-404px stacked, so almost its whole
   range is in the wrap zone and the clamp applies unconditionally rather than behind a
   media query. The cost is ~23px of reserved space per card at the very widest desktop,
   where one line would have done; the benefit is a 0px spread at every width.

   Fixed by clamping to two lines AND reserving exactly two lines, so the row height is
   constant whatever the byline says. `2.8em` is 2 x the 1.4 line-height declared here —
   expressed in em rather than px because the `small` preset is fluid, so a hardcoded
   pixel value would drift with the viewport.

   Specificity 0,3,0, which beats the plain `.criteo-byline` rule the companion
   plugin ships at 0,1,0 — the theme cannot rely on stylesheet order there. */
.criteo-cards-duo .wp-block-post .criteo-byline {
	line-height: 1.4;
	min-height: 2.8em;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

/* The card container = each post-template item.

   THESE VALUES ARE COPIED IN ONE OTHER PLACE: `.criteo-home-editorial__plate`, which since
   3.6.0 is a card too but is deliberately outside `.criteo-cards` (it has no image, no terms
   row and no meta row, so inheriting this block would mean overriding most of it). If you
   change the base colour, border, radius, shadow, transition or the hover values below,
   change them there as well. */
.criteo-cards .wp-block-post {
	display: flex;
	flex-direction: column;
	background-color: var( --wp--preset--color--base );
	border: 1px solid rgba( 16, 24, 40, 0.06 );
	border-radius: 20px;
	overflow: hidden;
	box-shadow: 0 1px 2px rgba( 16, 24, 40, 0.04 ), 0 10px 28px rgba( 16, 24, 40, 0.06 );
	transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

.criteo-cards .wp-block-post:hover {
	transform: translateY( -3px );
	border-color: #fe5000;
	box-shadow: 0 2px 4px rgba( 16, 24, 40, 0.06 ), 0 18px 42px rgba( 16, 24, 40, 0.12 );
}

/* Featured image sits flush to the card top; card overflow clips it to the
   rounded corners. Fills the width at a consistent 3/2 crop. */
.criteo-cards .wp-block-post .wp-block-post-featured-image {
	margin: 0;
}

/* The 3/2 crop has to be declared on the IMG, not just the figure.
   `post-featured-image` already writes `aspect-ratio:3/2` inline — but only onto
   the <figure>. Between them sits the block's own <a> (isLink), which has no
   height, so the img keeps `aspect-ratio: auto` and renders at its SOURCE ratio,
   stretching the figure to match. Measured across the site before this rule:
   1.0, 1.31, 1.33, 1.5, 1.57, 1.66, 1.78, 1.79, 1.91, 2.0 and even 3.04 — which is
   what made card interiors and meta rows fail to line up in every grid.

   Deliberately NOT also set on the figure: the hero override below only retargets
   the img, and keeping both rules aimed at the same element is what guarantees it
   still wins. */
.criteo-cards .wp-block-post .wp-block-post-featured-image img {
	display: block;
	width: 100%;
	height: 100%;
	aspect-ratio: 3 / 2;
	object-fit: cover;
}

/* Text body: padded, and grows so the meta row can pin to the bottom
   (keeps cards in a row the same visual rhythm). */
.criteo-cards .wp-block-post > .wp-block-group {
	display: flex;
	flex-direction: column;
	flex: 1 1 auto;
	padding: 1.15rem 1.3rem 1.3rem;
}

/* The card body is a constrained-layout group, so WordPress puts
   `margin-inline:auto` + a max-width on every child. Inside our flex column
   that centers short titles/terms. Reset it so everything is full-width and
   left-aligned. */
.criteo-cards .wp-block-post > .wp-block-group > * {
	max-width: none !important;
	margin-left: 0 !important;
	margin-right: 0 !important;
	width: 100%;
}

/* Category label — small, uppercase, brand blue. */
/* --- Card eyebrow: a PILL, not a line of text ----------------------------
   Criteo Brand Guidelines v5.0, Card System: the card eyebrow ("CASE STUDY" in the
   book) is a component — a rounded pill holding uppercase MONOSPACE — and not the
   bare coloured caps this used to be. Outlined on light cards, filled on dark. The
   Eyebrow Tags & Badges page sets the type as Hurme Mono; every section eyebrow in
   the book itself is drawn the same way.

   MONO IS A SYSTEM STACK, DELIBERATELY. Hurme Mono is licensed and not in hand, the
   same situation as Hurme No. 4 standing in as Manrope for body copy. Do not bundle
   a lookalike to close the gap — if a Hurme web licence ever lands, this one
   declaration and the Manrope family are the only two places that change.

   THE LABEL IS NAVY, NOT ORANGE, AND THAT IS A DELIBERATE DEVIATION. The book draws
   this pill with orange text inside an orange outline. Orange #fe5000 on white is
   3.30:1 — fine for a print deck, a fail for ~11px web text against the 4.5:1 AA
   floor, and no amount of weight fixes it at this size. So the brand orange is
   carried by the OUTLINE, which is a non-text element and only needs 3:1 (it has
   3.30:1), while the label keeps `primary` at 15.44:1. Same split in reverse on
   dark, where the book fills the pill: lavender fill 11.31:1 against the card, navy
   label 10.71:1 against the fill.

   Flex + hidden separator so a post in two categories renders two pills rather than
   "PILL, PILL" — core emits a `__separator` span between terms. */
.criteo-cards .wp-block-post .wp-block-post-terms {
	display: flex;
	flex-wrap: wrap;
	gap: 0.35rem;
	margin: 0 0 0.55rem;
	font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
		"Liberation Mono", monospace;
	font-size: 0.64rem;
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	line-height: 1.2;
}

.criteo-cards .wp-block-post .wp-block-post-terms .wp-block-post-terms__separator {
	display: none;
}

.criteo-cards .wp-block-post .wp-block-post-terms a {
	display: inline-block;
	padding: 0.2rem 0.55rem;
	border: 1px solid #fe5000;
	border-radius: 999px;
	color: var( --wp--preset--color--primary );
	text-decoration: none;
	transition: background-color 0.15s ease;
}

/* Tint rather than underline — an underline inside a pill reads as a mistake. The
   label stays navy on the tint (13.70:1), so the hover costs no contrast. Same
   idiom as the topic-rail pills further down. */
.criteo-cards .wp-block-post .wp-block-post-terms a:hover,
.criteo-cards .wp-block-post .wp-block-post-terms a:focus-visible {
	background-color: rgba( 254, 80, 0, 0.10 );
	text-decoration: none;
}

/* Title — clamp to 2 lines so cards in a row stay even. */
.criteo-cards .wp-block-post .wp-block-post-title {
	margin: 0 0 0.5rem;
	line-height: 1.28;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

/* The large hero title can run longer. Dormant since 2.1.0 — kept for the rollback
   file; see the long note on the `.criteo-front-hero` block further down. */
.home .criteo-front-hero > .wp-block-column:first-child .wp-block-post-title,
.criteo-magazine .criteo-front-hero > .wp-block-column:first-child .wp-block-post-title {
	-webkit-line-clamp: 3;
}

.criteo-cards .wp-block-post .wp-block-post-title a {
	color: var( --wp--preset--color--contrast );
	text-decoration: none;
}

/* Title turns Criteo orange only when the title link itself is hovered/focused
   (not on whole-card hover). */
.criteo-cards .wp-block-post .wp-block-post-title a:hover,
.criteo-cards .wp-block-post .wp-block-post-title a:focus {
	color: #fe5000;
}

/* Excerpt — muted grey, comfortable line-height, clamped to 3 lines.
   (The palette has no grey preset — tertiary is brand orange — so use a
   neutral here.) */
.criteo-cards .wp-block-post .wp-block-post-excerpt {
	margin: 0 0 0.35rem;
	color: rgba( 20, 23, 29, 0.66 );
}

.criteo-cards .wp-block-post .wp-block-post-excerpt p {
	margin: 0;
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.criteo-cards .wp-block-post .wp-block-post-excerpt .wp-block-post-excerpt__more-text {
	display: none;
}

/* Meta row (author + date) — pinned to the card bottom with a hairline,
   author on the left, date on the right. */
.criteo-cards .wp-block-post > .wp-block-group > .wp-block-group {
	margin-top: auto;
	padding-top: 0.85rem;
	border-top: 1px solid rgba( 16, 24, 40, 0.09 );
	font-size: 0.8rem;
	display: flex;
	flex-wrap: nowrap;
	align-items: center;
	justify-content: space-between;
	gap: 0.75rem;
}

/* The byline (criteo/post-byline) replaced the core author-name block on cards;
   its styling lives in the plugin's header.css (.criteo-byline). */

/* Date is muted grey (Recap-style). The theme's tertiary preset is brand
   orange, so we set a neutral grey explicitly here. */
.criteo-cards .wp-block-post .wp-block-post-date,
.criteo-cards .wp-block-post .wp-block-post-date time {
	color: rgba( 20, 23, 29, 0.55 );
	white-space: nowrap;
}

/* The large hero card keeps a bigger title but the same card treatment. Dormant
   since 2.1.0 — kept for the rollback file, same reason as above. */
.home .criteo-front-hero .wp-block-post-title,
.criteo-magazine .criteo-front-hero .wp-block-post-title {
	line-height: 1.2;
}

/* --- Pagination: make the current page obvious ---------------------------- */
/* --- Pagination: numbers always dead centre ------------------------------
   Every template used to set `justifyContent: space-between` with nothing centring
   the numbers, so the numbers block landed wherever the leftover space put it — and
   moved when `previous` or `next` was missing. Measured across /articles/ at 1280px,
   the numbers' centre was 126px on page 1 (no `previous`), 655px in the middle, and
   1148px on the last page (no `next`): a 1022px swing between first and last.

   A three-column grid with EXPLICIT `grid-column` on each child is what fixes it.
   Auto placement is not enough: with no `previous` element to occupy column 1, the
   numbers would simply flow into it and sit left again. Naming the columns pins the
   numbers to the centre cell whether or not their siblings render.

   The outer 1fr columns are equal, so the centre cell is centred in the container
   regardless of how wide prev/next happen to be.

   `.is-layout-flex` is in the selector because core ships `body .is-layout-flex
   { display: flex }` — specificity 0,1,1, which quietly beats a lone class at 0,1,0
   and left this rule doing nothing. Same family of trap as core's
   `.wp-block-group.is-layout-flex > :is(*, div) { margin: 0 }` noted further up:
   these layout rules carry an extra element in the selector, so a plain class never
   wins against them. */
.wp-block-query-pagination.is-layout-flex {
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	align-items: center;
	gap: 1rem;
}

.wp-block-query-pagination.is-layout-flex > .wp-block-query-pagination-previous {
	grid-column: 1;
	justify-self: start;
	margin: 0;
}

.wp-block-query-pagination.is-layout-flex > .wp-block-query-pagination-numbers {
	grid-column: 2;
	justify-self: center;
	margin: 0;
}

.wp-block-query-pagination.is-layout-flex > .wp-block-query-pagination-next {
	grid-column: 3;
	justify-self: end;
	margin: 0;
}

/* On a phone the three columns squeeze the numbers to nothing, so stack instead:
   numbers first, then prev/next side by side beneath. */
@media ( max-width: 600px ) {
	.wp-block-query-pagination.is-layout-flex {
		grid-template-columns: 1fr 1fr;
		row-gap: 0.85rem;
	}

	.wp-block-query-pagination.is-layout-flex > .wp-block-query-pagination-numbers {
		grid-column: 1 / -1;
		grid-row: 1;
	}

	.wp-block-query-pagination.is-layout-flex > .wp-block-query-pagination-previous {
		grid-column: 1;
		grid-row: 2;
	}

	.wp-block-query-pagination.is-layout-flex > .wp-block-query-pagination-next {
		grid-column: 2;
		grid-row: 2;
	}
}

.wp-block-query-pagination .wp-block-query-pagination-numbers .page-numbers {
	padding: 0.15em 0.6em;
	border-radius: 8px;
	text-decoration: none;
	color: var( --wp--preset--color--contrast );
}

.wp-block-query-pagination .wp-block-query-pagination-numbers .page-numbers:hover {
	background-color: rgba( 254, 80, 0, 0.12 );
	color: #fe5000;
}

.wp-block-query-pagination .wp-block-query-pagination-numbers .page-numbers.current {
	font-weight: 700;
	color: #ffffff;
	background-color: var( --wp--preset--color--primary );
}

/* --- The rule above the paginator ----------------------------------------
   Mode-agnostic neutral, and that is the fix rather than a preference. All four
   listing templates used to set this border inline as `var:preset|color|contrast`,
   which the colour-mode toggle remaps: #000000 in light, #eef1f6 in dark. So in dark
   mode a near-WHITE 1px rule ran the width of the column — by far the loudest line on
   the page, when every other hairline in this theme is a low-alpha neutral.

   Fixed by giving those wrappers a `criteo-pagination` class and moving the border
   here. The alternative was overriding an inline style with `!important`; adding a
   class instead keeps the cascade honest. 0.32 alpha reads at roughly the same weight
   as the card hairlines in both modes, so no dark variant is needed. */
.criteo-pagination {
	border-top: 1px solid rgba( 127, 127, 127, 0.32 );
}

/* --- Previous / Next ------------------------------------------------------
   These had NO treatment at all — only grid placement — so they rendered as bare
   body-coloured text sitting beside the numbers, which do get a chip with padding, a
   radius and a hover state. Two controls doing the same job looked like one control
   and one sentence, and in dark mode "Next Page" was simply light grey text floating
   at the edge of the column.

   Styled as outline pills, reusing the tag-pill vocabulary further down this file
   (neutral border, orange on hover) rather than inventing a third idiom. Every colour
   is a literal or a mode-agnostic neutral: `contrast` is what caused the problem
   above, and `primary` never remaps, so neither is safe for chrome that sits on a
   surface which flips. */
.wp-block-query-pagination .wp-block-query-pagination-previous,
.wp-block-query-pagination .wp-block-query-pagination-next {
	display: inline-flex;
	align-items: center;
	box-sizing: border-box;
	/* 44px touch floor, stated rather than derived: the `small` preset is fluid and
	   bottoms out near 13.9px at 390px, where padding-only maths measured 42px. Same
	   reasoning as the tag pills. */
	min-height: 36px;
	padding: 0.3em 0.9em;
	border: 1px solid rgba( 127, 127, 127, 0.4 );
	border-radius: 999px;
	color: inherit;
	font-size: 0.85rem;
	font-weight: 600;
	text-decoration: none;
	transition: border-color 0.15s ease, color 0.15s ease, background-color 0.15s ease;
}

.wp-block-query-pagination .wp-block-query-pagination-previous:hover,
.wp-block-query-pagination .wp-block-query-pagination-next:hover,
.wp-block-query-pagination .wp-block-query-pagination-previous:focus-visible,
.wp-block-query-pagination .wp-block-query-pagination-next:focus-visible {
	border-color: #fe5000;
	color: #fe5000;
	background-color: rgba( 254, 80, 0, 0.08 );
}

.wp-block-query-pagination .wp-block-query-pagination-previous:focus-visible,
.wp-block-query-pagination .wp-block-query-pagination-next:focus-visible {
	outline: 2px solid #fe5000;
	outline-offset: 2px;
}

@media ( max-width: 600px ) {
	/* Stacked below the numbers at this width, one per half-column — centre them in
	   their own cell so the pair reads as a matched pair rather than hugging the
	   outer edges of the page. */
	.wp-block-query-pagination.is-layout-flex > .wp-block-query-pagination-previous,
	.wp-block-query-pagination.is-layout-flex > .wp-block-query-pagination-next {
		justify-content: center;
		min-height: 44px;
	}
}

/* --- Dark mode (criteo-header toggle sets :root[data-theme="dark"]) --------
   The next two rules are the other half of the pair `.criteo-home-editorial__plate`
   duplicates — see the note on `.criteo-cards .wp-block-post` above. */
:root[data-theme="dark"] .criteo-cards .wp-block-post {
	background-color: #1c2029;
	border-color: rgba( 255, 255, 255, 0.08 );
	box-shadow: 0 1px 2px rgba( 0, 0, 0, 0.3 ), 0 14px 34px rgba( 0, 0, 0, 0.4 );
}

:root[data-theme="dark"] .criteo-cards .wp-block-post:hover {
	border-color: #fe5000;
	box-shadow: 0 2px 4px rgba( 0, 0, 0, 0.4 ), 0 20px 46px rgba( 0, 0, 0, 0.55 );
}

:root[data-theme="dark"] .criteo-cards .wp-block-post > .wp-block-group > .wp-block-group {
	border-top-color: rgba( 255, 255, 255, 0.08 );
}

/* The book's DARK card fills the eyebrow pill rather than outlining it — lavender
   ground, navy label — and that inversion is what this reproduces. It also retires
   #6ea8ff, a light blue that was never in the brand palette and had the same problem
   the mint `secondary` did in 3.10.0: a colour invented to solve a dark-mode
   legibility problem, when Lavender #d8d2fe was sitting in the palette doing exactly
   that job at 11.31:1 against the card, with the navy label at 10.71:1 on the fill.
   NOTE the article-header kicker still uses #6ea8ff — that one is out of scope here
   and is the last off-brand colour left in the theme. */
:root[data-theme="dark"] .criteo-cards .wp-block-post .wp-block-post-terms a {
	background-color: #d8d2fe;
	border-color: #d8d2fe;
	color: var( --wp--preset--color--primary );
}

:root[data-theme="dark"] .criteo-cards .wp-block-post .wp-block-post-terms a:hover,
:root[data-theme="dark"] .criteo-cards .wp-block-post .wp-block-post-terms a:focus-visible {
	background-color: #ffffff;
	border-color: #ffffff;
}

:root[data-theme="dark"] .criteo-cards .wp-block-post .wp-block-post-excerpt {
	color: rgba( 238, 241, 246, 0.66 );
}

:root[data-theme="dark"] .criteo-cards .wp-block-post .wp-block-post-date,
:root[data-theme="dark"] .criteo-cards .wp-block-post .wp-block-post-date time {
	color: rgba( 238, 241, 246, 0.55 );
}

/* ==========================================================================
   Footer band — modelled on criteo.com's corporate footer (3.5.0). Full-bleed
   pale band, logo, three link columns + social icons on a four-track grid, and
   a copyright line. Replaced the floating navy pill that mirrored the header.

   THE MAJORITY OF THIS BLOCK EXISTS TO OUTRANK theme.json, not to style from
   scratch. Four global rules land on this footer and three of them make it
   unreadable; each override below names the one it is beating. Nothing needs
   `!important`, because theme.json's element and block styles emit at (0,0,1)
   or (0,1,0).

   EVERY SELECTOR HERE IS DELIBERATELY DOUBLED (`.criteo-footer .criteo-footer-x`)
   even where a single class would win on the front end, and the reason is the
   EDITOR. functions.php calls `add_editor_style( 'style.css' )`, and core
   rewrites every selector in an editor stylesheet to sit under
   `.editor-styles-wrapper` — which adds a class to core's rules and to ours
   alike. Single-class rules that beat core on order in the front end therefore
   TIE with it inside the canvas and lose. That was not theoretical: with
   `.criteo-footer-cols > *` at one class, core's flow block-gap won in the
   editor and columns 2-4 sat 32px below column 1 while an author was editing
   them, and `.criteo-footer-title`'s margin was zeroed so headings collapsed
   onto their lists. One extra class makes the winner the same in both places.
   ========================================================================== */
/* ALMOST IMPERCEPTIBLE ON PURPOSE — READ THIS BEFORE "FIXING" THE CONTRAST.
   This shipped at #E3E9F6 with a 0.32-alpha hairline, chosen so the band was
   clearly visible on both page backgrounds, and that was REJECTED on review: the
   brief is that you should barely be able to tell where the footer starts, so it
   sits quietly with the rest of the page instead of announcing itself as a
   separate slab.

   So the values below are deliberately at the bottom of the perceptible range,
   and the measurements are recorded so nobody re-derives them as a bug:

     #F6F9FE  vs #f3f4f6 (body.home/.blog/.archive/.search)   1.04:1
     #F6F9FE  vs #ffffff (single/page templates)              1.06:1
     #171A20  vs #14171d (dark page)                          1.05:1

   1.04:1 IS the intent, not an oversight — it is the same number the note on the
   2.7.1 gradient wash at the top of this file calls "effectively invisible",
   which there was a defect and here is the requirement. Nothing about text
   legibility rides on it: contrast/black on this band still measures 19.9:1.

   A happy side effect is that #F6F9FE is now criteo.com's actual footer colour,
   so the light mode matches the corporate original exactly.

   AND THERE IS NO BORDER. A 1px hairline was the single most noticeable thing
   about the old treatment — it drew the exact line the brief asks not to draw.
   The boundary is now carried only by the band's own faint tint and the 64px of
   top padding. If a whisper of an edge is ever wanted back, the cheapest version
   is `border-top: 1px solid rgba(127,127,127,0.10)`, not the 0.32 this replaced. */
.criteo-footer {
	background-color: #F6F9FE;
}

.criteo-footer .criteo-footer-inner {
	box-sizing: border-box;
	/* Deliberately NOT the header pill's `calc(100% - 4rem)`. That 4rem exists
	   because the pill animates `width` on scroll and needed a value to animate
	   between; a full-bleed band has no such constraint and should instead line
	   up with the page's own content edge, which is the root padding. Copying
	   the pill's 4rem would leave the footer up to 1rem wider per side than the
	   cards above it, between roughly 600px and 1400px. */
	width: min(
		var( --wp--style--global--wide-size, 1332px ),
		calc( 100% - 2 * var( --wp--style--root--padding-right, 2rem ) )
	);
	margin-inline: auto;
}

/* --- Links ----------------------------------------------------------------
   WITHOUT THIS RULE EVERY FOOTER LINK IS ORANGE AND HOVERS TO LOW-CONTRAST.
   theme.json's `styles.elements.link` sets `color: tertiary` (#fe5000) with a
   `:hover` of `secondary`, which is right for links in running prose on white and
   wrong for a 13-item footer directory: measured on the #F6F9FE band that is
   3.13:1 at rest, under the 4.5:1 AA floor for body text. That number is unchanged
   from the earlier, darker #E3E9F6 band — near-white is near-white as far as
   orange is concerned — so re-tinting the band does not let this override be
   dropped.

   The hover half of that used to be far worse. Until 3.10.0 `secondary` was
   #00eaa0, a mint that measured 1.50:1 here and 1.58:1 on white — pointing at a
   link all but erased it — and that was on top of it being absent from the brand
   palette entirely. It is now Electric Blue #3d1ef8, which is 7.67:1 on white, so
   article-prose link hover clears AA on its own for the first time. This footer
   override survives that fix because it exists for the REST colour, not the hover.

   Routed through `contrast` rather than a literal #000 on purpose — the colour
   toggle remaps `contrast` to #eef1f6, so dark mode inherits 14.4:1 from this
   one declaration and needs no override of its own. That is the mirror image of
   the warning at `.criteo-pagination`: `contrast` is the wrong choice for
   *chrome* on a flipping surface, and exactly the right one for *text* on it.

   The orange stays, as the hover — matching the tag pills and the pagination
   arrows, and measuring 4.94:1 on the dark band. */
.criteo-footer a {
	color: var( --wp--preset--color--contrast );
	text-decoration: none;
}

.criteo-footer a:hover,
.criteo-footer a:focus-visible {
	color: #fe5000;
	text-decoration: underline;
	text-underline-offset: 2px;
}

/* --- Column headings ------------------------------------------------------
   Beating two theme.json element rules: `elements.heading` sets
   `font-weight: 800` and `elements.h2` the fluid `large` size, both of which
   are page-heading values that would make four column labels shout.

   criteo.com separates headings from links by ONE weight step (500 vs 400) at
   the same 16px. This theme's body weight is already 500, so the same one-step
   relationship lands on 600 against 400 here; both are in the bundled Manrope
   set. `h2` is kept as the tag for document outline, and restyled rather than
   downgraded to a paragraph. */
.criteo-footer .criteo-footer-title {
	margin: 0 0 1.5rem;
	font-size: var( --wp--preset--font-size--small );
	font-weight: 600;
	line-height: 1.3;
}

/* --- Link lists -----------------------------------------------------------
   DOUBLED CLASS, and it is load-bearing: theme.json's `blocks."core/list"`
   sets `padding-left: var(--wp--preset--spacing--40)` and emits it as
   `:root :where(.wp-block-list)` at (0,1,0) — a genuine tie with a single
   `.criteo-footer-links`. style.css does print after global styles, so a tie
   would resolve our way today, but this file's own rule is not to depend on
   that (see the note on `.criteo-cards-duo .criteo-byline`). */
.criteo-footer .criteo-footer-links {
	margin: 0;
	padding-left: 0;
	list-style: none;
}

.criteo-footer .criteo-footer-links li {
	margin-bottom: 1rem;
	/* SAME SIZE AS THE HEADING, differing only in weight — that is the whole of
	   criteo.com's hierarchy here (16px/500 over 16px/400) and it needs stating
	   explicitly: the list otherwise inherits the theme's `medium` body size and
	   rendered at 20px against 16.7px headings, i.e. links LOUDER than the labels
	   grouping them. Measured in the browser, not assumed. */
	font-size: var( --wp--preset--font-size--small );
	/* 400 against the heading's 600. Body weight here is 500, so this is a
	   deliberate step DOWN — without it the one-step separation collapses. */
	font-weight: 400;
	line-height: 1.3;
}

.criteo-footer .criteo-footer-links li:last-child {
	margin-bottom: 0;
}

/* --- Social icons ---------------------------------------------------------
   WITHOUT THIS RULE THE FOUR ICONS ARE LINKEDIN-BLUE, BLACK, YOUTUBE-RED AND
   ONE UNCOLOURED. The pattern deliberately omits `iconColor`/`iconColorValue`
   (those emit an inline `style="color:..."` per <li> that no stylesheet can
   override, which is fine on a permanently navy pill and impossible on a
   surface that flips), so core's `is-style-logos-only` per-service colours
   apply instead. Those are `:where()`-wrapped at (0,1,0); this is (0,2,0) and
   takes all of them, including the `feed` service for which core ships no
   colour rule at all.

   `inherit` rather than a colour: the icons then track the footer text through
   `contrast` and need no dark-mode variant. The <li> is the right target
   because core sets the anchor and its svg to `fill/color: currentColor` at
   (0,4,0), so colour has to arrive by inheritance from here. */
.criteo-footer .wp-block-social-link {
	color: inherit;
}

.criteo-footer .wp-block-social-link:hover {
	color: #fe5000;
}

/* --- The column grid -----------------------------------------------------
   criteo.com's four 25% tracks with a 30px gutter. Three hold link columns and
   the fourth the social icons — which is where the corporate site puts them,
   under the Contact Sales button that was dropped here, so nothing is left
   empty. A fourth link column added later lands in that track and the grid
   simply grows a fifth on the next row.

   `minmax(0, 1fr)` rather than `1fr`: a bare `1fr` floors at min-content, so a
   long unbroken link label ("Terms and Conditions") would widen its own track
   and unbalance the row. */
.criteo-footer .criteo-footer-cols {
	display: grid;
	grid-template-columns: 1fr;
	gap: 2.5rem 1.875rem;
	margin-top: 3.5rem;
}

/* Core's flow-layout block gap — `:root :where(.is-layout-flow) > *` with a
   `margin-block-start` — lands on each grid child and pushes columns 2, 3 and 4
   down inside their own cells, so the row arrives visibly stepped. Zeroing it
   here is what keeps the four column tops level. */
.criteo-footer .criteo-footer-cols > * {
	margin-block-start: 0;
}

@media ( min-width: 576px ) {
	.criteo-footer .criteo-footer-cols {
		grid-template-columns: repeat( 2, minmax( 0, 1fr ) );
	}
}

@media ( min-width: 992px ) {
	.criteo-footer .criteo-footer-cols {
		grid-template-columns: repeat( 4, minmax( 0, 1fr ) );
	}
}

/* Right-aligned in the fourth track, as on criteo.com. Below the four-track
   breakpoint the icons share a row with a link column, where hard-right reads
   as detached — so they go back to the left edge and line up with everything
   else. `justify-content` is overridden rather than the block's attribute
   changed, because `is-content-justification-right` is a render-time class the
   editor owns. */
.criteo-footer .criteo-footer-social {
	/* ALIGNED TO THE TOP OF THE ROW, level with the column headings. As a grid item
	   the <ul> stretches to the full row height by default and its own flex
	   `align-items: center` then parks the icons at the vertical middle of a
	   ~250px cell — which looks like an accident rather than a fourth column. On
	   criteo.com this track starts at the heading line (with the Contact Sales
	   button that was dropped here), so starting there is both tidier and more
	   faithful. */
	align-self: start;
	gap: 1.25rem;
}

@media ( max-width: 991px ) {
	.criteo-footer .criteo-footer-social {
		justify-content: flex-start;
	}
}

/* --- Logo ----------------------------------------------------------------
   240px, i.e. DELIBERATELY LARGER THAN THE HEADER'S, which renders at 210x51
   (DB header part 93 sets `core/site-logo` width 210). It shipped at 160px and
   was called too small on review, with the header as the stated floor; 240
   clears that floor rather than tying it, because the footer is the roomier
   context and this is the only brand mark on the band. Exact header parity, if
   that is ever preferred, is `width: 210px` — nothing else needs to change.

   THE ASSETS ARE THE 768x187 DERIVATIVES, NOT THE 300x73 ONES THIS STARTED
   WITH, and the swap is required rather than incidental: 240 CSS px is 480
   device px on a 2x screen, so the 300px file would have been visibly soft
   exactly where the logo got bigger. 768px is the smallest bundled derivative
   that stays sharp at 2x, and still only ~7.6KB. Do not reach for the 4100x1000
   original — with no attachment `id` core emits no `srcset`, so every phone
   would decode 4.1 megapixels twice for this.

   `aspect-ratio` tracks the asset's real ratio (4.107) and reserves the box
   without the `width`/`height` attributes that would have forced `is-resized`
   and an inline style onto the block. Update it if the derivative changes. */
.criteo-footer .criteo-footer-logo {
	margin: 0;
}

/* THE ASSET HAS TRANSPARENT PADDING BAKED INTO IT, AND THAT IS WHY THIS PULLS LEFT.
   Measured off the PNGs rather than guessed: in the 768x187 canvas the artwork
   occupies 634x63 at offset (67, 62), i.e. 67px of empty pixels on each side and
   62px top and bottom, identical in both the light and reverse variants. So at a
   240px box the wordmark starts 240 x 67/768 = 20.9px in from the box edge, and
   the logo sat almost 21px right of the "Tech & Community" column beneath it —
   visibly out of line, with nothing in the CSS to blame for it.

   Cancelled with a negative inset expressed AS A FRACTION OF THE WIDTH, not as a
   literal -20.9px, so the two stay locked together: change `--criteo-logo-w` and
   the alignment follows. Both numbers come from the file, so re-crop or re-export
   the asset and this ratio is the one thing that must be updated with it.

   A negative margin rather than cropping the padding out of the PNG, deliberately:
   cropping would also strip the 62px top/bottom, shortening the box from 58px to
   ~20px and moving every vertical gap around the logo. This changes the one axis
   that was wrong and leaves the approved rhythm untouched. Nothing aligns to the
   logo's right edge or its vertical centre, so the remaining padding is harmless.

   `aspect-ratio` stays the FULL canvas ratio (768/187), not the artwork's, because
   the box being reserved is still the whole image. */
.criteo-footer .criteo-footer-logo img {
	--criteo-logo-w: 240px;
	width: var( --criteo-logo-w );
	height: auto;
	aspect-ratio: 768 / 187;
	margin-left: calc( var( --criteo-logo-w ) * -67 / 768 );
}

/* The two variants are mutually exclusive; see the note in patterns/footer.php
   for why one asset cannot serve both modes. Doubled class so this cannot be
   beaten by core's `.wp-block-image.aligncenter { display: table }`, which is
   also (0,2,0) — the pattern sets no alignment, but an editor could. */
.criteo-footer .criteo-footer-logo--dark {
	display: none;
}

/* --- Bottom row ----------------------------------------------------------
   criteo.com separates this with 48px of whitespace and no rule, and the band's
   own top hairline is already doing boundary duty. Stated as an explicit
   `margin-top` so it beats core's flow block-gap rather than adding to it. */
.criteo-footer .criteo-footer-bottom {
	margin-top: 3rem;
}

.criteo-footer .criteo-footer-copy {
	margin: 0;
	font-size: var( --wp--preset--font-size--small );
	font-weight: 400;
	line-height: 1.3;
}

/* --- Dark mode -----------------------------------------------------------
   ONE DECLARATION IS THE WHOLE OF IT, and that is the point of routing the text
   through `contrast` and the icons through `inherit`: neither needs a dark
   variant, and with the hairline gone there is nothing else left to flip.

   #171A20 is a hair above the #14171d page (1.05:1), matching how faint the
   light band is. It is deliberately NOT #1c2029, the panel surface
   assets/dark-mode.css uses elsewhere — that measured 1.10:1 and read as a
   distinctly separate slab, which is what review asked to remove.

   Deliberately keyed to `[data-theme="dark"]` and NOT to
   `prefers-color-scheme`. OS preference reaches this site only through the boot
   script in inc/companion.php, which reads localStorage first; a media query
   here would desync the footer from every other surface for anyone who has
   toggled against their OS. */
:root[data-theme="dark"] .criteo-footer {
	background-color: #171A20;
}

:root[data-theme="dark"] .criteo-footer .criteo-footer-logo {
	display: none;
}

:root[data-theme="dark"] .criteo-footer .criteo-footer-logo--dark {
	display: block;
}

/* ==========================================================================
   Featured hero card (left column of .criteo-front-hero) fills its column so
   its bottom aligns with the 2x2 grid beside it; the image grows to absorb the
   extra height (big hero image, compact text below). Side-by-side only —
   below 782px the columns stack, so keep the normal 3/2 crop there.

   DORMANT SINCE 2.1.0 BUT DO NOT DELETE. The homepage no longer has a
   `.criteo-front-hero` element — it was replaced by the masthead + lead banner +
   card row — so nothing on the live site matches these rules. They stay because
   `retired/front-page-feed.html`, the documented rollback artefact, uses this exact
   two-column hero. Deleting this block would leave the restore path rendering an
   unstyled homepage, and the failure would only surface at the worst moment: when
   someone is already rolling back.

   The same applies to the two `.criteo-front-hero` title rules further up the file.
   ========================================================================== */
@media ( min-width: 782px ) {
	.home .criteo-front-hero > .wp-block-column:first-child .wp-block-query,
	.criteo-magazine .criteo-front-hero > .wp-block-column:first-child .wp-block-query,
	.home .criteo-front-hero > .wp-block-column:first-child .wp-block-post-template,
	.criteo-magazine .criteo-front-hero > .wp-block-column:first-child .wp-block-post-template,
	.home .criteo-front-hero > .wp-block-column:first-child .wp-block-post,
	.criteo-magazine .criteo-front-hero > .wp-block-column:first-child .wp-block-post {
		height: 100%;
	}

	/* Text body keeps its natural height; the image takes the slack. */
	.home .criteo-front-hero > .wp-block-column:first-child .wp-block-post > .wp-block-group,
	.criteo-magazine .criteo-front-hero > .wp-block-column:first-child .wp-block-post > .wp-block-group {
		flex: 0 0 auto;
	}

	.home .criteo-front-hero > .wp-block-column:first-child .wp-block-post-featured-image,
	.criteo-magazine .criteo-front-hero > .wp-block-column:first-child .wp-block-post-featured-image {
		flex: 1 1 auto;
		min-height: 300px;
	}

	.home .criteo-front-hero > .wp-block-column:first-child .wp-block-post-featured-image img,
	.criteo-magazine .criteo-front-hero > .wp-block-column:first-child .wp-block-post-featured-image img {
		height: 100%;
		aspect-ratio: auto;
	}
}

/* ==========================================================================
   Single post (article page): editorial header, two-column body with a slim
   utility rail, reading typography, and a full-width "Read next".
   Sections are centred and capped so the rail + content sit balanced (no
   lopsided empty space) without a widget sidebar.
   ========================================================================== */
.criteo-article-header,
.criteo-article-body,
.criteo-readnext {
	max-width: 1040px;
	margin-inline: auto;
}

/* --- Editorial header ---------------------------------------------------- */
/* The header is a constrained group; make its children span the section
   width, left-aligned (WP otherwise caps + centres them). */
.criteo-article-header > * {
	max-width: none !important;
	margin-left: 0 !important;
	margin-right: 0 !important;
}

.criteo-article-header .wp-block-post-terms a {
	color: var( --wp--preset--color--primary );
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	text-decoration: none;
}

.criteo-article-header .wp-block-post-terms a:hover {
	color: #fe5000;
}

.criteo-article-header .wp-block-post-title {
	text-wrap: balance;
	letter-spacing: -0.02em;
}

/* Meta row under the cover image: date (left) · author(s) (right). */
.criteo-article-meta {
	width: 100%;
	font-size: 0.9rem;
}

.criteo-article-meta .wp-block-post-date {
	color: rgba( 20, 23, 29, 0.55 );
	font-size: 0.85rem;
}

/* Separator between the header and the article body. */
.criteo-article-sep.wp-block-separator {
	max-width: none;
	height: 0;
	border: 0;
	border-top: 1px solid rgba( 127, 127, 127, 0.28 );
	opacity: 1;
}

.criteo-article-meta [class*="publishpress-authors"] a,
.criteo-article-meta [class*="ppma"] a {
	color: var( --wp--preset--color--contrast );
	font-weight: 600;
	text-decoration: none;
}

.criteo-article-meta [class*="publishpress-authors"] a:hover,
.criteo-article-meta [class*="ppma"] a:hover {
	color: #fe5000;
}

/* Wide cover image. */
.criteo-article-cover {
	width: 100%;
}

.criteo-article-cover img {
	width: 100%;
	border-radius: 14px;
}

/* Dark mode: the header's muted text used fixed dark values that don't flip —
   lighten the lead + date, and use a legible blue for the navy category. */
:root[data-theme="dark"] .criteo-article-lead {
	color: rgba( 238, 241, 246, 0.7 );
}

:root[data-theme="dark"] .criteo-article-meta .wp-block-post-date {
	color: rgba( 238, 241, 246, 0.55 );
}

:root[data-theme="dark"] .criteo-article-header .wp-block-post-terms a {
	color: #6ea8ff;
}

/* ==========================================================================
   Listing pages: two-column body with a sticky topics/tags rail.
   templates/index.html (/articles/) and templates/category.html.
   ==========================================================================

   Copies the article body's proven sticky idiom verbatim rather than inventing one:
   flex parent + `align-items: flex-start` + `flex: 0 0 <n>rem` on the aside +
   `position: sticky; top: 5.5rem`, unsticking at 900px. See the note below that block
   for why `align-items` and not `align-self`.

   NOT `wp:columns` — see the long comment in templates/index.html. */
.criteo-list-body {
	gap: 3rem;
	align-items: flex-start;
}

.criteo-list-main {
	flex: 1 1 auto;
	/* Without this a flex child refuses to shrink below its content's min-content
	   width, so a long unbroken title would push the rail off the row. */
	min-width: 0;
}

/* The `<main>` wrapper is `layout: constrained` with `contentSize: 650px`, which is
   NARROWER than this ~856px column — so every child would be capped at 650px and
   centred, leaving the cards floating in the middle of their own column. Same reset the
   article body needs, for the same reason. */
.criteo-list-main > * {
	max-width: none !important;
	margin-left: 0 !important;
	margin-right: 0 !important;
}

/* 26rem = 416px, so with the 3rem gap the split lands at ~856px / ~428px of the 1332px
   wide band. That is what puts the 2-across cards at ~414px — within a few pixels of
   the 425px this page showed at 3-across full width, which is the point: adding the rail
   should not resize the cards. */
.criteo-list-rail {
	flex: 0 0 26rem;
	position: sticky;
	top: 5.5rem;
	/* A guard, not a live constraint. At this width the rail measures ~460px (6 category
	   rows plus 12 pills over ~4 wrapped rows at 32px each), so this never engages.

	   NOTE the figure is width-dependent and was originally recorded here as the only
	   number, which is what let the mobile cost go unnoticed: at 390px the same content
	   measures ~495px, because the pills grow to their 44px touch floor and the narrower
	   column wraps them onto more rows. That is why the rail collapses into a disclosure
	   below 1200px — see `.criteo-rail-disclosure` further down.

	   It is here because the retired homepage aside failed in exactly this spot: three
	   modules came to ~1764px against a viewport and the column had to scroll inside
	   itself, which the user rejected. That rail was removed rather than fixed. This one
	   is safe by being SHORT, not by being clever — so if someone adds a third module the
	   failure is a scrollbar inside the rail rather than a sticky element whose bottom can
	   never be reached. */
	max-height: calc( 100vh - 5.5rem - 2rem );
	overflow-y: auto;
}

/* --- The disclosure shell ------------------------------------------------
   The rail is wrapped in `<details class="criteo-rail-disclosure" open>`. Above this
   breakpoint that wrapper is inert: the summary is hidden and the content is open, so the
   sidebar renders exactly as it did before the disclosure existed.

   Everything visible about it lives in the `max-width: 1199px` block below. */
@media ( min-width: 1200px ) {

	.criteo-rail-disclosure__summary {
		display: none;
	}
}

/* --- Rail modules -------------------------------------------------------- */
.criteo-list-rail .criteo-rail__module + .criteo-rail__module {
	margin-top: 2.25rem;
}

/* Uppercase small caps over a hairline — the house heading treatment, matching the
   Featured band's section head rather than inventing a third style. Note this is a
   neutral grey, NOT the `primary` preset: `primary` is #130183 in BOTH colour modes (the
   one preset the toggle never remaps), which is precisely the bug that made the header
   dropdown's category labels invisible on a dark panel. */
.criteo-rail__heading {
	margin: 0 0 0.9rem;
	padding-bottom: 0.55rem;
	border-bottom: 1px solid rgba( 16, 24, 40, 0.12 );
	font-size: 0.72rem;
	font-weight: 700;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: rgba( 16, 24, 40, 0.55 );
}

:root[data-theme="dark"] .criteo-rail__heading {
	border-bottom-color: rgba( 238, 241, 246, 0.16 );
	color: rgba( 238, 241, 246, 0.6 );
}

.criteo-rail__topics {
	margin: 0;
	padding: 0;
	list-style: none;
}

.criteo-rail__topic {
	margin: 0;
}

/* The count sits hard right, so the names form a column and the numbers form another.
   `space-between` on a flex row does that without a table or a fixed width. */
.criteo-rail__topic-link {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 0.75rem;
	padding: 0.4rem 0;
	color: var( --wp--preset--color--contrast );
	text-decoration: none;
	font-size: 0.95rem;
	line-height: 1.3;
	border-bottom: 1px solid rgba( 16, 24, 40, 0.06 );
	transition: color 0.15s ease;
}

:root[data-theme="dark"] .criteo-rail__topic-link {
	border-bottom-color: rgba( 255, 255, 255, 0.06 );
}

.criteo-rail__topic:last-child .criteo-rail__topic-link {
	border-bottom: 0;
}

.criteo-rail__topic-link:hover,
.criteo-rail__topic-link:focus-visible {
	color: #fe5000;
}

.criteo-rail__topic-link:focus-visible {
	outline: 2px solid #fe5000;
	outline-offset: 2px;
}

/* The category you are already looking at, on a category archive. Brand orange rather
   than a blue selection state, so it reads as "you are here" in the same language the
   rest of the site uses for the active link. */
.criteo-rail__topic-link.is-current {
	color: #fe5000;
	font-weight: 700;
}

.criteo-rail__topic-name {
	min-width: 0;
}

/* Tabular figures so the numbers line up vertically even at different digit counts. */
.criteo-rail__topic-count {
	flex: 0 0 auto;
	font-size: 0.8rem;
	font-variant-numeric: tabular-nums;
	color: rgba( 20, 23, 29, 0.5 );
}

:root[data-theme="dark"] .criteo-rail__topic-count {
	color: rgba( 238, 241, 246, 0.5 );
}

/* --- Stacked: rail above the cards --------------------------------------- */
/* 1200px, NOT the 900px the article rail uses, and the number is measured rather than
   inherited.

   The rail is `flex: 0 0 26rem` — fixed and non-shrinking — so as the viewport narrows it
   holds 416px and the MAIN column absorbs all of the loss. Measured before this
   breakpoint was raised, with the split still active:

     1440px → main 868px (cards 420px)   ✓
     1280px → main 730px (cards 351px)   ✓
     1100px → main 562px (cards 267px)   ✗ below the byline-wrap threshold
      901px → main 373px (cards 172px)   ✗ main NARROWER THAN THE RAIL

   Two cards need ~316px each to stay above the width where multi-author bylines wrap to
   a third line, so main needs ~660px, so the container needs ~1124px, so the viewport
   needs ~1188px. Hence 1200px. Below it the rail goes full-width above the cards and the
   grid reflows on its own.

   `order: -1` lifts the rail above the cards even though it comes second in the DOM.
   That mismatch is deliberate and documented in the templates: a sighted reader gets the
   filters first, a screen-reader user still gets the articles first. */
@media ( max-width: 1199px ) {

	.criteo-list-body {
		flex-wrap: wrap;
		gap: 2rem;
	}

	.criteo-list-rail {
		order: -1;
		flex-basis: 100%;
		position: static;
		top: auto;
		max-height: none;
		overflow-y: visible;
	}

	/* Now that the main column is full width, a fixed two-track grid would jump the cards
	   from ~322px (just inside the split) to ~553px (just outside it) across a single
	   pixel. An `auto-fill`/`minmax()` track list picks the column count from the width
	   instead, so the card size stays continuous: 3 across at ~1135px, 2 at ~836px, 1 on a
	   phone. Same idiom the homepage's Featured band uses via `criteo-cards-feature`,
	   which replaced the four-across `criteo-cards-quad` in 2.9.0 — only with a much
	   larger `minmax()` floor, because that band's whole purpose is to be wider than the
	   ordinary rows rather than narrower.

	   Below 600px core's post-template stylesheet forces a single column from a quadrupled
	   class selector (0,4,0) regardless, which is why there is no phone case here. */
	.criteo-cards-duo .wp-block-post-template.is-layout-grid {
		grid-template-columns: repeat( auto-fill, minmax( 320px, 1fr ) );
	}

	/* Side by side once stacked — two short modules read better as columns than as one
	   tall list pushed above the articles. Falls to one column on a phone. */
	.criteo-list-rail .criteo-rail {
		display: grid;
		grid-template-columns: repeat( auto-fit, minmax( 15rem, 1fr ) );
		gap: 1.5rem 2rem;
	}

	.criteo-list-rail .criteo-rail__module + .criteo-rail__module {
		margin-top: 0;
	}

	/* --- Collapsed: the rail becomes one tappable row --------------------
	   Measured at 390px: expanded, the rail is 495px and the first article card starts at
	   670px — nothing readable on first paint on a 390x844 phone. Collapsed it is ~48px and
	   the first card lands at ~200px, so two cards clear the fold.

	   The treatment is lifted from the article table-of-contents (`.criteo-toc`, in the
	   companion plugin's header.css) rather than reinvented: it solves the same problem —
	   a plain heading when it is a sidebar, a bordered disclosure card when it stacks — and
	   its colours are mode-agnostic, so this needs no dark-mode variant beyond the border.
	   If you change one, look at the other. */
	.criteo-rail-disclosure {
		border: 1px solid rgba( 127, 127, 127, 0.22 );
		border-radius: 14px;
	}

	/* The uppercase eyebrow treatment of `.criteo-rail__heading`, so the collapsed row reads
	   as the same family of thing as the headings it hides.

	   `currentColor` for the chevron and a neutral grey for the text — NOT the `primary`
	   preset, which is #130183 in BOTH colour modes (the one preset the toggle never remaps)
	   and is exactly what made the header dropdown's category labels invisible on a dark
	   panel until plugin 1.8.3.

	   THE TOUCH TARGET IS AN EXPLICIT FLOOR, NOT PADDING ARITHMETIC — and this is the third
	   time that has mattered in this file. `padding: 0.85rem` on a 0.72rem/1.4 line looks
	   like it clears 44px; measured, it renders exactly 43px (27.2px padding + 16.1px line).
	   One pixel under, invisibly. `min-height` states the requirement instead of hoping the
	   sums land, and `box-sizing: border-box` is what makes the number mean the real height
	   rather than the content box — same quirk documented on the tag pills. */
	.criteo-rail-disclosure__summary {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 0.5rem;
		box-sizing: border-box;
		min-height: 44px;
		padding: 0.85rem 1rem;
		font-size: 0.72rem;
		font-weight: 700;
		letter-spacing: 0.1em;
		text-transform: uppercase;
		color: rgba( 16, 24, 40, 0.55 );
		cursor: pointer;
		/* Both lines are required to suppress the native marker — `list-style` covers
		   Firefox and modern Chrome, the pseudo-element covers older WebKit. */
		list-style: none;
	}

	.criteo-rail-disclosure__summary::-webkit-details-marker {
		display: none;
	}

	:root[data-theme="dark"] .criteo-rail-disclosure {
		border-color: rgba( 255, 255, 255, 0.16 );
	}

	:root[data-theme="dark"] .criteo-rail-disclosure__summary {
		color: rgba( 238, 241, 246, 0.6 );
	}

	.criteo-rail-disclosure__summary:hover,
	.criteo-rail-disclosure__summary:focus-visible {
		color: #fe5000;
	}

	.criteo-rail-disclosure__summary:focus-visible {
		outline: 2px solid #fe5000;
		outline-offset: 2px;
	}

	/* The house chevron, copied verbatim from the two existing disclosures rather than
	   paraphrased: 0.5rem square, two 2px currentColor borders, 45deg closed and -135deg
	   open. Only the chevron animates — neither precedent animates the panel, and adding a
	   height transition would be the first in the codebase and would owe a
	   `prefers-reduced-motion` guard with no precedent to copy. */
	.criteo-rail-disclosure__summary::after {
		content: "";
		flex: 0 0 auto;
		width: 0.5rem;
		height: 0.5rem;
		border-right: 2px solid currentColor;
		border-bottom: 2px solid currentColor;
		transform: translateY( -2px ) rotate( 45deg );
		transition: transform 0.15s ease;
	}

	.criteo-rail-disclosure[open] .criteo-rail-disclosure__summary::after {
		transform: translateY( 1px ) rotate( -135deg );
	}

	/* Inset the modules from the card border once expanded. The summary already has its
	   own padding, so this only needs the three remaining sides. */
	.criteo-rail-disclosure[open] .criteo-rail {
		padding: 0 1rem 1rem;
	}
}

/* --- Two-column body ----------------------------------------------------- */
.criteo-article-body {
	gap: 3rem;
	align-items: flex-start;
}

.criteo-article-body > .criteo-article-sidebar {
	flex: 0 0 13rem;
	position: sticky;
	top: 5.5rem;
}

.criteo-article-main {
	flex: 1 1 auto;
	min-width: 0;
}

/* Let the article content fill the (wider) main column instead of the theme's
   narrow default content-size. Both the main group and post-content use
   constrained layout, which would otherwise cap + centre the text — reset both
   levels so the article fills the column, left-aligned. */
.criteo-article-main > *,
.criteo-article-main .wp-block-post-content > * {
	max-width: none !important;
	margin-left: 0 !important;
	margin-right: 0 !important;
}

/* When the whole rail is toggled off, centre the article at a comfortable measure. */
.criteo-article-body:not( :has( .criteo-article-sidebar ) ) .criteo-article-main {
	max-width: 800px;
	margin-inline: auto;
}

/* --- Reading column ------------------------------------------------------ */
.criteo-article :where( h2, h3, h4 ) {
	line-height: 1.25;
	text-wrap: balance;
	margin-top: 1.8em;
	margin-bottom: 0.5em;
}

.criteo-article :where( p, li ) {
	line-height: 1.7;
}

.criteo-article :where( ul, ol ) {
	padding-left: 1.4rem;
}

.criteo-article :where( li ) {
	margin-bottom: 0.35rem;
}

/* In-content links → brand orange. */
.criteo-article a:where( :not( .wp-element-button ) ) {
	color: #fe5000;
	text-decoration: underline;
	text-underline-offset: 2px;
}

/* Inline code + code blocks (mode-agnostic neutral tint). */
.criteo-article :where( code ):not( pre code ) {
	background-color: rgba( 127, 127, 127, 0.16 );
	padding: 0.1em 0.36em;
	border-radius: 4px;
	font-size: 0.9em;
	/* Inline code is often a single unbreakable token (a method name, a type
	   signature). At narrow widths one of those will push the whole page wider
	   than the viewport and give the article a horizontal scrollbar — so let it
	   break mid-token. `anywhere` (not `break-word`) so it also shrinks the
	   element's min-content width, which is what actually stops the overflow. */
	overflow-wrap: anywhere;
}

/* Same hazard in headings, which set the largest type in the article: a long
   URL or identifier with no spaces overflows before it can wrap. */
.criteo-article :where( h1, h2, h3, h4, h5, h6 ) {
	overflow-wrap: break-word;
}

.criteo-article :where( pre ) {
	background-color: rgba( 127, 127, 127, 0.12 );
	border: 1px solid rgba( 127, 127, 127, 0.2 );
	border-radius: 14px;
	padding: 1rem 1.25rem;
	overflow-x: auto;
	font-size: 0.9em;
	line-height: 1.5;
}

.criteo-article :where( pre ) code {
	background: none;
	padding: 0;
}

/* Blockquote — brand-orange rule. */
.criteo-article :where( blockquote ) {
	margin-left: 0;
	padding-left: 1.25rem;
	border-left: 3px solid #fe5000;
	font-style: italic;
}

/* Figures + captions. */
.criteo-article :where( img ) {
	border-radius: 8px;
}

.criteo-article :where( figcaption ) {
	margin-top: 0.5rem;
	text-align: center;
	font-size: 0.85rem;
	color: rgba( 127, 133, 145, 1 );
}

/* Tags row at the foot of the article. */
.criteo-article-tags .wp-block-post-terms a {
	display: inline-block;
	margin: 0 0.25rem 0.35rem 0;
	padding: 0.12rem 0.6rem;
	border: 1px solid rgba( 127, 127, 127, 0.35 );
	border-radius: 999px;
	text-decoration: none;
	color: inherit;
}

.criteo-article-tags .wp-block-post-terms a:hover {
	border-color: #fe5000;
	color: #fe5000;
}

/* --- Read next (full width, left-aligned heading) ------------------------ */
.criteo-readnext > * {
	max-width: none !important;
	margin-left: 0 !important;
	margin-right: 0 !important;
}

.criteo-readnext__title {
	letter-spacing: 0.02em;
	text-align: left;
}

/* --- Responsive: stack the rail under the content below ~900px ----------- */
@media ( max-width: 900px ) {
	.criteo-article-body {
		flex-wrap: wrap;
		gap: 2rem;
	}

	.criteo-article-body > .criteo-article-sidebar {
		flex-basis: 100%;
		position: static;
		top: auto;
	}
}

/* --- Responsive: phones --------------------------------------------------
   The xxx-large preset bottoms out at 2.592rem (~41px) below the fluid range's
   768px floor, which on a 390px screen turns the article title into six lines
   that fill the whole first screen. Scale it with the viewport instead. */
@media ( max-width: 781px ) {
	/* !important to match core's preset class (.has-xxx-large-font-size ships
	   its font-size as !important, so a plain declaration can't win). */
	.criteo-article-header .wp-block-post-title {
		font-size: clamp( 1.85rem, 7.4vw, 2.592rem ) !important;
		line-height: 1.15;
	}

	/* Stacked above the article, the rail's own margins double up with the
	   flex gap; keep it tight so the post starts near the top of the screen. */
	.criteo-article-body > .criteo-article-sidebar .criteo-readmeta {
		margin-bottom: 1rem;
	}

	/* The inline progress bar is useless once the rail is stacked at the top of
	   the page (you scroll past it immediately). Promote it to the standard
	   mobile pattern: a hairline pinned to the top of the viewport. Safe to fix
	   here — unlike the header, nothing in the article creates a containing
	   block for it. */
	.criteo-article-body > .criteo-article-sidebar .criteo-progress {
		position: fixed;
		inset: 0 0 auto;
		z-index: 40;
		height: 3px;
		border-radius: 0;
		background-color: transparent;
	}
}

/* ==========================================================================
   Magazine homepage (templates/front-page.html)
   Curated tiles instead of one paginated feed. Was a page template trialled behind
   a toggle; it is now simply the homepage. The cards themselves need
   no rules here — `.criteo-cards` already styles any post Query Loop
   site-wide — so this is just the section furniture: headings, "see all"
   links, tag chips and the closing button.
   Muted greys are hardcoded: the palette has no grey preset, and `tertiary`
   is brand orange.
   ========================================================================== */

/* --- Section header: title left, "see all" right, hairline under ---------- */
.criteo-magazine-section__head {
	margin-bottom: 1.5rem;
	padding-bottom: 0.75rem;
	border-bottom: 1px solid rgba( 16, 24, 40, 0.12 );
	gap: 1rem;
}

:root[data-theme="dark"] .criteo-magazine-section__head {
	border-bottom-color: rgba( 238, 241, 246, 0.16 );
}

.criteo-magazine-section__title {
	margin: 0;
	letter-spacing: -0.01em;
}

/* A brand-orange bar before the label, so "Editorial" and "Featured" read as departments
   at a glance rather than as two more headings among the eleven h2s this page prints (nine
   of them post titles). Both homepage section heads get it: `.criteo-magazine-section__title`
   is used by patterns/magazine-editorial.php and patterns/magazine-body.php and by nothing
   else, so this rule is already homepage-scoped without a second class. /articles/ uses
   `.criteo-rail__heading` and the footer columns use `.criteo-footer-title`; neither is
   touched.

   WHAT THIS REPLACED: an emoji per section (✍️ Editorial, 📌 Featured). Rejected on three
   counts. The Editorial slot already carries a portrait-and-role signature with a rule down
   its inside edge, which says "signed opinion" far better than a pictogram of a hand can.
   Emoji are drawn differently per platform — ✍️ is a hand-and-pen on Apple and a flatter
   glyph in Segoe UI Emoji. (That argument used to carry an exception: the Browse-all button's
   📰, said to get away with it at 1.05rem inside a pill. As of 3.11.0 there is no exception,
   because that emoji is gone too — replaced by a Lucide arrow, the brand's actual icon
   library. THE THEME NOW SHIPS NO EMOJI AT ALL, and the guidelines sanction them only for
   X/Twitter post copy, never for UI.) And nothing fits
   "Featured" the way ✍️ fits "Editorial", so one of the two would always have been a
   compromise.

   INSIDE the heading, not a sibling in the head group. A sibling would land in the RIGHT-hand
   slot, because the rule below gives this element `flex-grow: 1` precisely so that siblings
   pack to the right edge — and that slot is already taken, by the Editorial month.
   `content: ""` also contributes nothing to the accessibility tree, so the
   headings keep an accessible name of exactly "Editorial" / "Featured" with no `aria-hidden`
   to maintain. That is the concrete win over the emoji route, which needs one.

   THE TEXT SHIFTS RIGHT BY ~12px AND THAT IS CORRECT. The bar's own left edge sits where the
   label used to start, which is also where the cards below start — so the leftmost ink stays
   on the page's left margin and the optical alignment is preserved. Do not "fix" this by
   hanging the bar outside the container; at narrow widths it would run into the page edge.

   `em`, not px: `large` is a FLUID preset, 1.25rem → 1.5rem (theme.json), so 20px on a phone
   and 24px on desktop. A pixel height would be right at one end of that range and wrong at
   the other — the same reason the card byline reserves `2.8em` rather than a pixel height.
   0.78em keeps the bar inside the font's own ascent and descent, so it does not raise the
   line box; measured 29px tall heads before and after, with the Editorial month's baseline
   unmoved. If that ever stops holding, make the heading `position: relative` and the bar
   `position: absolute` with a matching `padding-left`, which takes it out of the line box
   altogether.

   `#fe5000` as a literal, and NO dark variant, for the reason documented on the editorial
   plate and the Browse-all pill: `tertiary` IS remapped by the colour-mode toggle, to
   #9aa3b2, so a preset here would go grey in dark mode. Brand orange clears both grounds,
   and a decoration sitting beside its own text label owes no contrast ratio.

   SOLID, and since 3.6.1 it is the ONLY brand mark on either section. The Editorial card used
   to carry a navy → orange → mint gradient rule across its top edge, just below this head; it
   was removed because two brand marks stacked that closely competed, and of the two this one
   is attached to an actual label. Three stops across a 3px bar would have been mush regardless.

   Forced-colours mode discards `background`, so the bar vanishes there — correct for a
   decoration whose label survives, and unlike the plate, which lost its only boundary and
   needed a real border put back. Nothing to add to that media query. */
.criteo-magazine-section__title::before {
	content: "";
	display: inline-block;
	width: 3px;
	height: 0.78em;
	margin-right: 0.55rem;
	vertical-align: -0.04em;
	background: #fe5000;
}

/* Let the title absorb the free space so anything else in the head packs to the
   right edge, reading "Title ———————— May 2026". The Editorial month is the only
   consumer left — the retired category shelves' "‹ › See all" nav and the retired
   Featured carousel's arrows both used this same slot.

   `flex-grow` and NOT an auto margin on the sibling: this is a core flex group, and
   core ships `.wp-block-group.is-layout-flex > :is(*, div) { margin: 0 }` at
   specificity 0,2,1 — it beats a plain class selector, so any `margin-left:auto`
   here computes to 0px. Growing the first child leaves `space-between` no free
   space to distribute, which achieves the same packing without a specificity
   fight. */
.criteo-magazine-section__head > .criteo-magazine-section__title {
	flex-grow: 1;
}



/* --- Homepage masthead (RETIRED — retired/homepage-banner.php) ------------
   NOT ON ANY PAGE SINCE 2.5.0, and KEPT ON PURPOSE. The masthead was removed from
   the homepage and its pattern moved to retired/, which is unregistered; these rules
   stay for the same reason `.criteo-front-hero` below does — the retired pattern
   cannot be restored without them, and there is no way to re-derive the framing
   values from the markup alone. Delete this block only if you also delete
   retired/homepage-banner.php and assets/images/homepage-banner.jpg.
   (The mobile overrides in the media query further down are part of the same set.)

   A core cover block holding a wide team photograph and the site's h1. 320px tall,
   deliberately SHORTER than the 440px lead-article banner directly beneath it: both
   are full-width images with text over them, and without a difference in weight the
   top of the homepage reads as two identical slabs rather than as a hierarchy.

   The photograph ships inside the theme (assets/images/homepage-banner.jpg), so
   unlike every other image on the site its dimensions are known and fixed: 800x417.
   ========================================================================== */
.criteo-home-banner {
	border-radius: 20px;
	overflow: hidden;
}

/* Framing. The source is 800x417 (1.92) shown in a 1332x320 box (4.16), so
   `object-fit: cover` scales it up 1.67x and then discards about 54% of the height.
   `object-position` is what decides WHICH 46% survives, and the default (centre)
   is wrong here — it keeps torsos and floor and cuts the faces off.

   18% from the top puts the visible window at roughly y=34..226 in source pixels:
   the branded backdrop, the whole row of faces, and torsos down to about the waist.
   The bottom edge of that window is dark navy shirts, which is what the headline
   sits on.

   Framing lives here rather than in a pre-cropped file on purpose: the same image
   then also works at the much squarer mobile ratio, where cover trims width instead
   of height and a pre-cropped wide strip would lose the people at both ends. */
.criteo-home-banner .wp-block-cover__image-background {
	object-position: center 18%;
}

/* Bottom-up scrim — the same technique and the same reasoning as the lead banner
   below: literal rgba(0,0,0,...) rather than a preset, because `contrast` and `base`
   are remapped by the colour-mode toggle and would lighten the photo in dark mode.

   Stronger at the bottom than the lead banner's (0.9 vs 0.85) and reaching higher,
   because this band is 120px shorter: the headline sits closer to the middle of the
   frame, where the photograph is busiest.

   z-index 0, NOT above 1 — core pins `.wp-block-cover__inner-container` at
   `z-index: 1` with a selector that outranks anything reasonable here, and at an
   equal z-index this pseudo-element would paint last and cover the text. */
.criteo-home-banner::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	pointer-events: none;
	background: linear-gradient(
		to top,
		rgba( 0, 0, 0, 0.88 ) 0%,
		rgba( 0, 0, 0, 0.55 ) 30%,
		rgba( 0, 0, 0, 0.12 ) 62%,
		rgba( 0, 0, 0, 0 ) 85%
	);
}

/* The site's h1. Slightly smaller than the lead article's title so the lead still
   wins the eye — the masthead says what this is, the lead says what to read. */
.criteo-home-banner__title {
	margin: 0;
	max-width: 34rem;
	font-size: clamp( 1.6rem, 2.8vw, 2.4rem );
	line-height: 1.14;
	letter-spacing: -0.02em;
	color: #ffffff;
	text-wrap: balance;
	text-shadow: 0 1px 16px rgba( 0, 0, 0, 0.5 );
}

/* --- Lead article: two-column split card (templates/front-page.html) -------
   Photo left (58%), and category / headline / summary / byline right (42%), inside a
   single card whose corner radius clips the photo flush to its left edge.

   Until 2.2.0 this was a full-bleed `core/cover` with white text over the photograph,
   rendered by patterns/magazine-lead.php. All of that CSS is gone: the scrim gradient,
   the literal #fff type colours, the 46rem inner container, the cover `min-height` and
   its mobile `!important`. The markup is now an ordinary Query Loop, so the type takes
   its colours from `.criteo-cards` like every other card on the site — which is also
   how it inherits the whole dark-mode treatment for free.

   EVERYTHING HERE IS SCOPED `.criteo-cards.criteo-home-lead ... .wp-block-post`,
   giving specificity 0,4,x. That is not decoration: the rules being overridden are
   `.criteo-cards .wp-block-post ...` at 0,3,x, and a 0,3,x override would merely TIE
   and then depend on which rule happens to come later in this file. Both class names
   sit on the same `wp-block-query` element, so chaining them is free.

   Six of `.criteo-cards`'s rules are right for a grid card and wrong for a lead; each
   counter-rule below says which. Two of them are not overrides at all but
   REPLACEMENTS: `.criteo-cards` styles the card's padded body and its meta row with
   DIRECT-child selectors (`> .wp-block-group`), and those silently stop matching the
   moment `wp:columns` is the post-template's child. Nothing warns you; the padding and
   the hairline just vanish. */

/* Slightly rounder than a grid card (16px) so it sits with the 320px masthead above
   it, which is also 20px. */
.criteo-cards.criteo-home-lead .wp-block-post {
	border-radius: 20px;
}

/* COUNTER-RULE 1 of 6 — no hover lift.
   `.criteo-cards .wp-block-post:hover` raises the card 3px, which is right for a
   312px tile in a grid and wrong for a 1332px slab: the whole page appears to twitch.
   The orange border and the deeper shadow are kept — only the movement goes. */
.criteo-cards.criteo-home-lead .wp-block-post:hover {
	transform: none;
}

/* The photo column stretches to the row height, so the figure needs to be a flex
   child that can grow. Without `display: flex` here the figure sits at its intrinsic
   height and leaves a gap under the image. */
.criteo-cards.criteo-home-lead .wp-block-columns > .wp-block-column:first-child {
	display: flex;
}

.criteo-cards.criteo-home-lead .wp-block-post .wp-block-post-featured-image {
	flex: 1 1 auto;
	margin: 0;
}

/* `post-featured-image` with `isLink` puts an <a> between the figure and the img, and
   that anchor has no height of its own — the same gap-under-the-image trap the card
   image rule documents. Both links in the chain need to fill. */
.criteo-cards.criteo-home-lead .wp-block-post .wp-block-post-featured-image,
.criteo-cards.criteo-home-lead .wp-block-post .wp-block-post-featured-image a {
	display: block;
	height: 100%;
}

/* COUNTER-RULE 2 of 6 — the photo fills the row instead of being cropped to 3/2.
   `.criteo-cards ... img` forces `aspect-ratio: 3 / 2` through a DESCENDANT selector,
   so it reaches straight into the column and would fix the photo's height independently
   of the text beside it. Releasing the ratio and filling instead is what keeps the two
   columns exactly as tall as each other whatever the headline length. Same technique as
   the dormant hero-fill rules further up this file. */
.criteo-cards.criteo-home-lead .wp-block-post .wp-block-post-featured-image img {
	width: 100%;
	height: 100%;
	aspect-ratio: auto;
	object-fit: cover;
}

/* REPLACEMENT 3 of 6 — the text column's padding.
   `.criteo-cards .wp-block-post > .wp-block-group` supplies a grid card's padded body,
   but its direct child here is `.wp-block-columns`, so that rule no longer applies and
   the text would sit flush against the photo. Padding on this side only, because the
   columns block runs at `blockGap: 0` so the photo can bleed to the card edge.

   `box-sizing: border-box` IS LOAD-BEARING, not tidiness. `.wp-block-column` computes
   to `content-box`, so `flex-basis: 42%` sizes the CONTENT box and this 72px of
   horizontal padding gets added outside it. The column then asks for 42% + 72px, the
   row overflows, both columns flex-shrink proportionally, and the split silently lands
   at 55/45 instead of the 58/42 the markup asks for — measured 730px/600px before this
   line, 772px/560px after. The photo pays for the text's padding.

   (Same family as the pill/heading box-sizing mismatch documented elsewhere in this
   file: never assume a block's box-sizing, measure it.) */
.criteo-cards.criteo-home-lead .wp-block-columns > .wp-block-column:last-child {
	box-sizing: border-box;
	padding: 2rem 2.25rem;
}

/* NO FEATURED IMAGE — collapse to a single full-width text column.
   `post-featured-image` renders NOTHING when a post has no thumbnail (it does not
   render an empty figure), so without this the card shows the text at 42% beside 58%
   of blank card background — which reads as a broken image, not as a design. Verified
   by clearing the lead post's thumbnail: a 771x340px void.

   Worth guarding even though only 2 of 187 posts are image-less today, because this
   slot always holds the NEWEST post — whatever an author publishes next, with no
   editorial step in between to catch it.

   `:has()` is doing real work here and is the only way to express "style the parent
   because a descendant is absent" in CSS. If it were ever unsupported the page simply
   degrades to the void described above, which is what it did before this rule — so
   there is no worse failure mode than the status quo. */
.criteo-cards.criteo-home-lead .wp-block-columns:not( :has( .wp-block-post-featured-image ) ) > .wp-block-column:first-child {
	display: none;
}

/* `flex-grow`, NOT `flex-basis: 100%`. The 42% width is written as an INLINE style by
   the column block, and an inline style outranks every selector — so setting
   `flex-basis` here does nothing without `!important` (measured: the text stayed at
   42% and the void simply moved to the right-hand side). Growing into the space the
   hidden sibling vacated gets to the same place without the specificity fight, since
   core leaves columns at `flex-grow: 0`. */
.criteo-cards.criteo-home-lead .wp-block-columns:not( :has( .wp-block-post-featured-image ) ) > .wp-block-column:last-child {
	flex-grow: 1;
}

/* COUNTER-RULE 4 of 6 — the headline runs to three lines, not two.
   `.criteo-cards ... .wp-block-post-title` clamps to 2 lines to keep tiles even. A lead
   headline at this size needs the third line; `line-height` tightens because the type
   is much larger here. Precedent: the retired two-column hero raised the same clamp to
   3 for its lead card. */
.criteo-cards.criteo-home-lead .wp-block-post .wp-block-post-title {
	margin: 0 0 0.9rem;
	-webkit-line-clamp: 3;
	line-height: 1.14;
	letter-spacing: -0.02em;
	text-wrap: balance;
}

/* COUNTER-RULE 5 of 6 — the summary runs to five lines, not three.
   The excerpt is core's default 55 words, the same length the cards and /articles/
   show, which is roughly six lines at this column width. Five is the ceiling: enough
   that almost nothing is cut, low enough that one unusually long excerpt cannot stretch
   the card past the photo. The muted colour and dark-mode variant come from
   `.criteo-cards` unchanged. */
.criteo-cards.criteo-home-lead .wp-block-post .wp-block-post-excerpt {
	margin: 0;
}

.criteo-cards.criteo-home-lead .wp-block-post .wp-block-post-excerpt p {
	-webkit-line-clamp: 5;
}

/* REPLACEMENT 6 of 6 — the meta row.
   `.criteo-cards .wp-block-post > .wp-block-group > .wp-block-group` draws the hairline
   and splits author-left / date-right, and it is two levels of direct child, so it also
   stops matching once columns are in the way. Rebuilt here — but deliberately WITHOUT
   `margin-top: auto`, which exists to bottom-align cards against their siblings in a
   grid row. With `perPage: 1` there are no siblings, and pushing the meta row to the
   bottom would tear it away from the summary it belongs to.

   `.wp-block-group` is unambiguous inside this component: the meta row is the only
   group in the markup (columns and column are not groups). */
.criteo-cards.criteo-home-lead .wp-block-post .wp-block-group {
	margin: 1.35rem 0 0;
	padding-top: 0.9rem;
	border-top: 1px solid rgba( 16, 24, 40, 0.09 );
	font-size: 0.8rem;
	display: flex;
	flex-wrap: nowrap;
	align-items: center;
	justify-content: space-between;
	gap: 0.75rem;
}

:root[data-theme="dark"] .criteo-cards.criteo-home-lead .wp-block-post .wp-block-group {
	border-top-color: rgba( 255, 255, 255, 0.08 );
}

/* Side-by-side only above 1100px — NOT core's 782px column breakpoint, and the reason
   is measurable rather than aesthetic.

   The photo fills the row height, and the row height is set by the text. As the
   viewport narrows the text column narrows too, so the same words wrap onto more
   lines and the text gets TALLER while the photo gets NARROWER. The photo's rendered
   aspect ratio therefore collapses as the viewport shrinks. Measured, same article:

     1440px → 1.66   1280px → 1.37   1200px → 1.30   1100px → 1.21
     1024px → 1.14    960px → 1.00    900px → 0.94    782px → 0.77

   Below ~1000px the lead photograph is square or PORTRAIT — a landscape press photo
   cropped to a tall box, which looks like a mistake. A two-column split simply needs
   width. So it stacks at 1100px, where the ratio is still 1.21 and climbing, and the
   stacked block below takes over. Tablets get the stacked layout; laptops keep the
   split. */
@media ( min-width: 1100px ) {

	/* Vertically centre the text against the photo — the layout the design calls for.
	   Deliberately done HERE, on the text column, rather than with the columns block's
	   own `verticalAlignment: center` attribute: that sets `align-items: center` on the
	   flex container, which would stop the photo column stretching and defeat
	   counter-rule 2. */
	.criteo-cards.criteo-home-lead .wp-block-columns > .wp-block-column:last-child {
		display: flex;
		flex-direction: column;
		justify-content: center;
	}

	/* A floor, so a post with a short title and a short excerpt cannot squash the
	   photo into a letterbox. The row is normally taller than this — driven by the
	   text — and this only engages at the short end. */
	.criteo-cards.criteo-home-lead .wp-block-post > .wp-block-columns {
		min-height: 420px;
	}
}

/* Stacked: photo above, text below. Covers everything from a phone up to 1099px, so
   the phone-specific block at the end of this file only has to adjust padding and the
   corner radius.

   BOTH `!important`s are unavoidable and both mirror core exactly. Core ships
   `@media (min-width: 782px) { .wp-block-columns { flex-wrap: nowrap !important } }`
   and uses `flex-basis: 100% !important` for its own stacking, so between 782 and
   1099px we are overriding core's `!important` with our own — there is no
   specificity-only route. The `flex-basis` also has to beat the block's INLINE
   `flex-basis: 58%`/`42%`, which no selector can outrank. */
@media ( max-width: 1099px ) {

	.criteo-cards.criteo-home-lead .wp-block-columns {
		flex-wrap: wrap !important;
	}

	.criteo-cards.criteo-home-lead .wp-block-columns > .wp-block-column {
		flex-basis: 100% !important;
	}

	/* Give the photo its 3/2 crop back. Filling the row height is meaningless once the
	   photo is above the text rather than beside it, and with `height: 100%` still
	   applied it would collapse to nothing. */
	.criteo-cards.criteo-home-lead .wp-block-post .wp-block-post-featured-image img {
		height: auto;
		aspect-ratio: 3 / 2;
	}

	.criteo-cards.criteo-home-lead .wp-block-columns > .wp-block-column:last-child {
		padding: 1.75rem 2rem;
	}
}

/* --- Editorial of the month (patterns/magazine-editorial.php) ------------------------
   IT IS A CARD, AND 3.6.0 REVERSED 3.1.0 TO MAKE IT ONE. The slot shipped as the one
   object on this homepage that was deliberately NOT a tile — two hairlines and a measure,
   later a square-cornered gradient band — on the theory that being categorically
   not-a-card would make it read as a different KIND of content before a word of it was
   read. On the page it read as a foreign object instead: a hard-edged tinted field at full
   card width, wedged between the three-card news row above and the Featured band below,
   with every other surface on the page rounded and floating.

   SO THE CLAIM MOVED FROM THE CHROME TO THE LAYOUT, which is where it was always safer.
   The Editorial is still unmistakably not one of the grid cards — it is the full width of
   the container, it carries no image, its headline is far larger than a card's, and it is
   SIGNED, with a portrait and job title down a ruled column. None of that needed the box
   removed to be legible, and all of it survives the card treatment untouched.

   WHAT THE CARD CHROME IS: `.criteo-cards .wp-block-post`'s, verbatim — same base colour,
   same 1px hairline, same 16px radius, same two-layer shadow, same 3px hover lift to an
   orange border. Those values are duplicated here rather than tokenised into
   `--criteo-card-*` custom properties, following the precedent `.criteo-cards.criteo-home-lead`
   set: this theme hand-writes against the card rules and cross-references them. IF YOU
   CHANGE THE CARD CHROME, CHANGE IT IN BOTH PLACES — there is a matching note on
   `.criteo-cards .wp-block-post` pointing back here.

   NOTHING IN THE CHROME SAYS "EDITORIAL" ANY MORE, and that is settled rather than pending.
   3.6.0 kept one survivor of the old backdrop — a 3px navy → orange → mint rule across the
   card's top edge — on the theory that a plain card needed some mark to carry the slot's
   identity. It did not work in place and was removed in 3.6.1: on an otherwise clean white
   card the bar was the loudest thing on the page for something that is not the page's most
   important item, and it still read as a leftover of the band it came from. The gradient wash,
   the `criteo_tc_editorial_backdrop` setting, the `criteo-home-editorial--backdrop` class and
   now the rule itself are all gone.

   SO THE SLOT IS ANNOUNCED ENTIRELY BY ITS SECTION HEAD AND ITS LAYOUT. The head already says
   "Editorial" beside an orange department bar (`.criteo-magazine-section__title::before`) with
   the month in the right-hand slot — a label, which is a better way to name a department than
   a colour is. Below it, the full width, the missing image, the oversized headline and the
   signed column do the rest. If a mark is ever wanted here again, note that the head is where
   it belongs, not the card.

   NO IMAGE EITHER, which answers 2.2.0 rather than ignoring it: the objection to the old
   full-bleed lead cover was "that treatment fought any image", and a treatment with no
   image cannot fight one. It also sidesteps this site's featured images being 1024x576
   title cards with text baked in — see the `criteo-cards-feature` note on why the Featured
   band chose 16/9 — since any wide letterbox crop would cut words out of the picture. This
   was re-confirmed at 3.6.0 against the option of giving the slot a lead-card-style image
   column, and it is why the slot is a card WITHOUT being just another card.

   The section head is NOT here: patterns/magazine-editorial.php reuses
   `.criteo-magazine-section__head` and `__title` verbatim from the Featured band, so the
   slot is announced exactly the way its sibling department is and this block adds no
   heading CSS at all.

   Hairlines inside the plate are `rgba( 127, 127, 127, 0.28 )`, the same neutral
   `.criteo-article-sep` uses and for the same stated reason: mode-agnostic, so they need no
   dark variant. (The card's OWN border is not one of them — it takes the cards'
   `rgba( 16, 24, 40, 0.06 )` and has a dark variant, because matching the cards is the
   point.) `#fe5000` is a literal for the same reason the Browse-all pill's colours are —
   `tertiary` IS remapped by the colour-mode toggle, to #9aa3b2, so a preset here would go
   grey in dark mode.
   ========================================================================== */

/* The month, in the right-hand slot of the section head. `.criteo-magazine-section__title`
   already carries `flex-grow: 1`, so this needs no `margin-left: auto` — see the long note
   on that rule for why the theme expresses it that way round. */
.criteo-home-editorial__month {
	margin: 0;
	color: rgba( 16, 24, 40, 0.55 );
	font-weight: 600;
	letter-spacing: 0.02em;
	white-space: nowrap;
}

:root[data-theme="dark"] .criteo-home-editorial__month {
	color: rgba( 238, 241, 246, 0.6 );
}

/* THE CARD. Every value below is `.criteo-cards .wp-block-post`'s — see the block header on
   why they are duplicated rather than tokenised, and keep the two in sync.

   NO `position: relative` AND NO `overflow: hidden`, and both are absences worth explaining
   because the cards carry them. `overflow: hidden` exists on `.criteo-cards .wp-block-post` to
   clip a flush featured image into the radius, and this card has no image and nothing else
   touching its edges. `position: relative` was here only to anchor the accent rule removed in
   3.6.1. If anything flush ever gets added back, both come back with it.

   16px, NOT the lead card's 20px, and this page really does use both. The lead's rounder
   corner is justified in its own comment by PROXIMITY — "so it sits with the 320px masthead
   above it, which is also 20px" — not by being full width. This card's neighbours are the
   three-card news row directly above and the Featured band directly below, both 16px, and it
   is nowhere near the hero. Same rule, different neighbour, different answer.

   NO `border-bottom` any more. The 3.1.0 plate closed with a hairline because it had no other
   edge; a rule drawn inside a bordered card reads as a mistake rather than as a boundary.

   The padding is the retired backdrop variant's measured desktop figure, kept because it was
   derived against this same headline at this same width. It is deliberately more generous than
   a grid card's 1.15/1.3rem: this card is the full width of the container, and card padding
   scaled to a 425px tile looks mean at 1332px.

   FORCED-COLOURS MODE NEEDS NOTHING NOW, and the `@media ( forced-colors: active )` override
   that used to sit at the end of this section is gone with the gradient panel. That panel had no
   border at all, and forced-colours discards `background-image` and `background-color`, so it
   lost its only boundary and had to have a `CanvasText` border put back by hand. A real `border`
   survives forced-colours (the colour is overridden, the 1px is not), so this card keeps its
   edge for free. Only the shadow drops out, and it carries no meaning. */
.criteo-home-editorial__plate {
	padding: 2.25rem 2rem 2rem;
	background-color: var( --wp--preset--color--base );
	border: 1px solid rgba( 16, 24, 40, 0.06 );
	border-radius: 20px;
	box-shadow: 0 1px 2px rgba( 16, 24, 40, 0.04 ), 0 10px 28px rgba( 16, 24, 40, 0.06 );
	transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

/* The same 3px lift and orange border every other card on the page takes, which is the point:
   a surface that matches the cards at rest but stays inert under the pointer reads as disabled
   next to them. Not gated behind `prefers-reduced-motion` — the card hovers this mirrors are
   not either, and matching them is the whole exercise. (The theme has no reduced-motion query
   at all now — its only one belonged to the carousel removed in 3.8.0, and was about scrolling
   rather than hover.)

   NOTE the plate is not itself a link — only the headline inside it is, exactly as on the grid
   cards and the lead card, none of which are fully clickable either. */
.criteo-home-editorial__plate:hover {
	transform: translateY( -3px );
	border-color: #fe5000;
	box-shadow: 0 2px 4px rgba( 16, 24, 40, 0.06 ), 0 18px 42px rgba( 16, 24, 40, 0.12 );
}

/* Dark mode, and again these are the cards' values verbatim — #1c2029, not the #1b202a the
   retired gradient panel used. Taking the card system's exact colour is the change. */
:root[data-theme="dark"] .criteo-home-editorial__plate {
	background-color: #1c2029;
	border-color: rgba( 255, 255, 255, 0.08 );
	box-shadow: 0 1px 2px rgba( 0, 0, 0, 0.3 ), 0 14px 34px rgba( 0, 0, 0, 0.4 );
}

:root[data-theme="dark"] .criteo-home-editorial__plate:hover {
	border-color: #fe5000;
	box-shadow: 0 2px 4px rgba( 0, 0, 0, 0.4 ), 0 20px 46px rgba( 0, 0, 0, 0.55 );
}

/* `stretch`, NOT `flex-start`, and the two do different jobs here. With `flex-start` the aside
   shrank to its own content, so the rule down its inside edge stopped wherever the byline
   happened to end — a short line floating beside a tall column of prose. Stretching it makes that
   rule span the full height of the row, which is what makes it read as structure rather than as
   decoration, and the signature is then centred WITHIN that full height by the aside itself. */
.criteo-home-editorial__row {
	align-items: stretch;
	gap: var( --wp--preset--spacing--60 );
}

/* `flex: 1 1 auto` + `min-width: 0` on the prose and a fixed rem width on the signature —
   the article body's proven idiom (`.criteo-article-body`, which the listing rail then
   copied verbatim rather than inventing one). The ratio is never a percentage that has to
   be re-derived at each breakpoint. */
.criteo-home-editorial__body {
	flex: 1 1 auto;
	min-width: 0;
}

/* NOTE there is deliberately no `> * { max-width: none !important }` reset here, unlike
   `.criteo-article-header`. That reset exists to undo core's constrained layout, and this
   group uses FLOW layout precisely so there is nothing to undo — see the note in
   magazine-editorial.php on why constrained was wrong (it would have added up to 48px of
   root padding inside the plate). The two measures below are therefore free to differ. */

/* THE HEADLINE CARRIES NO `fontSize` ATTRIBUTE, and that is mechanical rather than taste.
   Core emits preset font-size classes with `!important`, so the phone override below could
   not take a `has-*-font-size` class down without a second `!important`. One clamp states
   the whole range instead. Precedent: `.criteo-home-banner__title` above, and the note in
   magazine-body.php on why the pre-2.1.0 featured tile carried no `fontSize` either.

   THE MAX IS 2.35rem, DELIBERATELY BELOW THE LEAD CARD'S 2.592rem (`xx-large`). The lead is
   the page's biggest headline and must stay so: the editorial is a different KIND of
   content, not a higher-RANKED one, and sizing it above the lead would assert a ranking the
   `editorial` tag does not express. That is the 2.9.0 lesson — a treatment may not assert a
   semantic the data does not carry — applied to type size. Reading order of weight is
   therefore lead > editorial > featured > row, and nothing on the page disagrees.

   The measure is the theme's OWN `contentSize`, read as a var with a literal fallback —
   same idiom as `.criteo-footer-inner`'s wide-size, and as the header pill's in
   assets/header.css. */
.criteo-home-editorial .wp-block-post-title {
	margin: 0 0 0.9rem;
	max-width: var( --wp--style--global--content-size, 650px );
	font-size: clamp( 1.65rem, 2.6vw, 2.35rem );
	line-height: 1.1;
	letter-spacing: -0.025em;
	text-wrap: balance;
}

/* MANDATORY, NOT DECORATION — and it is the navy trap in a new place. theme.json gives
   `core/post-title` a link hover of `primary`, which is #130183 in BOTH colour modes: the
   one preset the toggle never remaps. `.criteo-cards` overrides it to orange, but this
   component is deliberately NOT a card, so it would inherit the theme.json value and the
   headline would turn navy-on-near-black when pointed at — the same 1.06:1 failure as the
   header dropdown's category labels. Orange is #fe5000 in both modes, so one rule serves
   both and there is no dark variant needed below. */
.criteo-home-editorial .wp-block-post-title a {
	color: var( --wp--preset--color--contrast );
	text-decoration: none;
}

.criteo-home-editorial .wp-block-post-title a:hover,
.criteo-home-editorial .wp-block-post-title a:focus {
	color: #fe5000;
}

/* The standfirst. Muted neutral hardcoded: the palette has no grey preset, and `tertiary`
   is brand orange. */
.criteo-home-editorial .wp-block-post-excerpt {
	margin: 0;
	max-width: 46rem;
	color: rgba( 20, 23, 29, 0.66 );
}

.criteo-home-editorial .wp-block-post-excerpt p {
	margin: 0;
}

.criteo-home-editorial .wp-block-post-excerpt .wp-block-post-excerpt__more-text {
	display: none;
}

:root[data-theme="dark"] .criteo-home-editorial .wp-block-post-excerpt {
	color: rgba( 238, 241, 246, 0.66 );
}

/* The signature column, and the rule down its inside edge is the point of it: it makes the
   byline STRUCTURAL rather than small print under the prose, which is what "signed opinion"
   means typographically. `box-sizing: border-box` so the padding comes out of the 15rem
   rather than being added to it — the same trap documented at length on the lead card's
   text column. */
.criteo-home-editorial__aside {
	flex: 0 0 15rem;
	box-sizing: border-box;
	padding-inline-start: var( --wp--preset--spacing--50 );
	border-inline-start: 1px solid rgba( 127, 127, 127, 0.28 );

	/* The signature sits at the vertical centre of the row. The aside is stretched to the full
	   row height (see `__row` above), so this centres the CONTENT inside that height rather than
	   shrinking the box — which is what keeps the inside-edge rule full-length while the portrait
	   and name line up against the middle of the prose. */
	display: flex;
	flex-direction: column;
	justify-content: center;
}

/* Zero, not the 0.45rem this carried while the aside held four separate lines. The byline is now
   the only child, and a trailing margin on the only child offsets it against the centring above
   by half its height. Spacing between authors is the byline's own business. */
.criteo-home-editorial__aside > * {
	margin: 0;
}

.criteo-home-editorial__aside .criteo-byline {
	font-weight: 700;
}

/* --- Stacked byline: name over job title (criteo/post-byline showTitles) -------------
   Emitted by blocks/post-byline/render.php as `.criteo-byline--stacked`, which keeps the base
   `.criteo-byline` class so the header's colour rules and the card clamp still apply.

   Written here rather than scoped to the Editorial slot because the block is generic: anything
   that turns `showTitles` on gets a sane layout without a second copy of these rules. The
   Editorial slot is simply its only caller today.

   `.criteo-byline` is a SPAN, so the display switch is doing real work — `__person` inside an
   inline parent would put every author on one run-on line. Nothing else sets `display` on
   `.criteo-byline` (assets/header.css only gives it a font-weight), so this has no fight on its
   hands today.

   ONE LATENT CONFLICT, recorded rather than pre-emptively fixed: the two-line byline clamp at
   `.criteo-cards-duo .wp-block-post .criteo-byline` is 0,3,0 and sets `display: -webkit-box`, so
   it would beat this 0,1,0 rule and flatten the stack. That cannot happen now — `-duo` is the
   /articles/ card grid and cards never pass `showTitles` — but if titles ever go on cards, that
   is the rule to reckon with, not this one. */
.criteo-byline--stacked {
	display: block;
}

/* Each author is a row: portrait, then name over title. `align-items: center` puts the portrait
   against the middle of the name/title pair, so it reads level whether or not there is a title —
   which matters because the title is present for only a minority of authors and the alignment
   must not visibly change between them.

   The gap between authors is larger than the gap between a name and its own title. That
   difference is what groups each pair as one signature instead of reading as four unrelated lines
   when there are two authors. */
.criteo-byline--stacked .criteo-byline__person {
	display: flex;
	align-items: center;
	gap: 0.7rem;
}

.criteo-byline--stacked .criteo-byline__person + .criteo-byline__person {
	margin-top: 0.7rem;
}

/* `flex: 0 0 auto` so the portrait never shrinks when a long name wraps beside it, and the
   explicit width/height because the attachment is requested at 96px and would otherwise render at
   its intrinsic size. `object-fit: cover` keeps a non-square upload from distorting — the source
   images are not guaranteed square, and a stretched face is worse than a cropped one. */
.criteo-byline--stacked .criteo-byline__avatar {
	flex: 0 0 auto;
	width: 3rem;
	height: 3rem;
	border-radius: 999px;
	object-fit: cover;

	/* A hairline containing edge, so a portrait with a white or transparent background does not
	   dissolve into the panel behind it. Mode-agnostic neutral, so it needs no dark variant. */
	box-shadow: 0 0 0 1px rgba( 127, 127, 127, 0.28 );
}

/* The name/title stack beside the portrait. `min-width: 0` so a long name wraps rather than
   pushing the portrait out of the 15rem column. */
.criteo-byline--stacked .criteo-byline__who {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

/* The title is deliberately quieter than the name AND not bold: the aside sets
   `font-weight: 700` on the whole byline for the name's sake, so this has to opt back out or the
   two lines carry identical weight and the hierarchy collapses.

   `0.92em`, relative rather than absolute, because the byline is rendered at the fluid `small`
   preset — a px value here would stop tracking it across viewports. */
.criteo-byline--stacked .criteo-byline__title {
	display: block;
	margin-top: 0.1rem;
	font-size: 0.92em;
	font-weight: 500;
	line-height: 1.35;
	color: rgba( 20, 23, 29, 0.6 );
}

:root[data-theme="dark"] .criteo-byline--stacked .criteo-byline__title {
	color: rgba( 238, 241, 246, 0.6 );
}

/* The signature column becomes a stacked meta block below the prose at 900px, REUSING the
   article body's existing breakpoint rather than adding one. Measured on the container,
   which is `min( 1332, viewport - 2 x clamp( 32px, 3.3613445vw, 48px ) )` because root
   padding is spacing-60:

     viewport 1440 → container 1332 → prose 1044, signature 240   ok
     viewport 1280 → container 1194 → prose  906, signature 240   ok
     viewport 1100 → container 1026 → prose  738, signature 240   ok
     viewport  960 → container  892 → prose  604, signature 240   ok, tight
     viewport  900 → container  836 → prose  548, signature 240   FAILS, 29% to the aside

   Below that the aside is taking more than a quarter of the row for four short lines, and
   the headline's measure is what suffers, so the two stack instead. */
@media ( max-width: 900px ) {

	.criteo-home-editorial__row {
		flex-wrap: wrap !important;
		gap: var( --wp--preset--spacing--40 );
	}

	.criteo-home-editorial__body,
	.criteo-home-editorial__aside {
		flex: 1 1 100%;
	}

	/* The rule moves from the inside edge to the top: it is still separating the signature
	   from the prose, just along the axis they now meet on. */
	.criteo-home-editorial__aside {
		padding-inline-start: 0;
		padding-top: 0.9rem;
		border-inline-start: 0;
		border-top: 1px solid rgba( 127, 127, 127, 0.28 );
	}
}

/* --- Tag pills (patterns/topic-rail.php) ----------------------------------------
   Compact pills for the rail's tag module. Class names kept as `criteo-category-tags__*`
   although the component moved: these rules carry a lot of measured behaviour (the
   flex-start note, the box-sizing note, the nowrap note) and renaming them would have
   meant re-deriving all of it for no gain. The `__label` rules that used to sit here went
   with the full-width band — the rail uses a shared `.criteo-rail__heading` instead.

   Deliberately WITHOUT counts. The number a reader would care about is "posts in this
   scope with this tag", but the tag archive they land on shows the tag's site-wide total
   instead — so a count on the pill would contradict the page it leads to. (The rail's
   CATEGORY list does show counts, because there the two numbers agree.)

   The neutral `rgba(127,127,127,0.4)` border and `#fe5000` hover are mode-agnostic on
   purpose: they need no dark-mode variant, unlike the `contrast` preset which the
   colour-mode toggle remaps. */
.criteo-category-tags__list {
	display: flex;
	flex-wrap: wrap;
	/* Don't let a tall pill drag its row-mates up with it. The list is a wrapping
	   flex row, so the default `stretch` made every pill in a row match the tallest
	   one — a single two-line label was rendering a whole row at 44px. */
	align-items: flex-start;
	gap: 0.4rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.criteo-category-tags__item {
	margin: 0;
	max-width: 100%;
}

.criteo-category-tags__pill {
	display: inline-flex;
	align-items: center;
	/* Comfortable without being a CTA. Raised to the 44px floor on mobile below —
	   `min-height` rather than padding maths, because the fluid font presets bottom
	   out near 13.9px at 390px and padding-only rules have measured 42px there. */
	/* These boxes are `content-box` by default here, which makes `min-height` mean
	   "content only" — a 32px min-height plus 9.6px padding and 2px border actually
	   rendered 44px. Switching to border-box makes the number the real height, which
	   matters because the aside's total height decides the sticky gate below.
	   (The Join/RSS pills have the same quirk: their `min-height: 44px` renders 62px.
	   Left alone — 62px is a fine CTA — but don't copy the idiom without checking.) */
	box-sizing: border-box;
	min-height: 32px;
	max-width: 100%;
	padding: 0.3rem 0.7rem;
	border: 1px solid rgba( 127, 127, 127, 0.4 );
	border-radius: 999px;
	color: var( --wp--preset--color--contrast );
	text-decoration: none;
	font-size: 0.8rem;
	font-weight: 600;
	line-height: 1.2;
	/* A tag label reads as one token, so keep it on one line and let the LIST wrap
	   between pills instead. Two-line labels also made rows uneven (see above). */
	white-space: nowrap;
	transition: border-color 0.15s ease, color 0.15s ease, background-color 0.15s ease;
}

.criteo-category-tags__pill:hover,
.criteo-category-tags__pill:focus-visible {
	border-color: #fe5000;
	color: #fe5000;
	background-color: rgba( 254, 80, 0, 0.08 );
}

.criteo-category-tags__pill:focus-visible {
	outline: 2px solid #fe5000;
	outline-offset: 2px;
}

/* --- Closing "Browse all articles" button --------------------------------
   ELECTRIC BLUE, NOT ORANGE, AS OF 3.11.0 — AND THE ORANGE MUST NOT COME BACK.
   Criteo Brand Guidelines v5.0 give three separate reasons, which is why 3.10.0's
   "leave the CTA orange" call was reversed one version later:

   1. Eyebrow Tags & Buttons: "Use the Electric Blue style for the main action and
      reserve the others for secondary or supporting choices, keeping one clear
      priority per layout." This is the ONLY button in the homepage body, so it is
      that layout's main action. (The header's Join Us pill is chrome on its own
      sticky surface and does not compete for the page's one priority.)
   2. Programmatic/Display: "Never use the same color for the CTA button as the
      background or logo — it disappears." The logo is Sunrise orange #fe5000, which
      is exactly what this button used to be.
   3. It retires a contrast compromise this comment used to document. White on the
      old orange was 3.3:1 and failed AA, so the label settled for near-black at
      5.4:1. White on Electric Blue is 7.67:1 and on the Blue hover 15.44:1 — the
      button now gets the white label the brand actually shows AND better contrast
      than it had.

   Copy also checked against the guide's CTA rules: "maximum 2-4 words", strong verbs
   rather than "Click Here / Submit / Go". "Browse all articles" is three and leads
   with a verb.

   Every colour here is still a LITERAL, never a preset — that part of the original
   fix is untouched and still load-bearing. Left on the default fill style the button
   took `contrast` as its background, and the colour-mode toggle remaps that, so it
   rendered a pure-black slab in light mode and a near-white slab in dark. (Third
   time this remap has bitten this file — see the header white-on-white notes and the
   featured-tile dim.)

   There IS a hand-written dark-mode rule below, and it does not reopen that bug: the
   distinction is between a preset silently inverting the button and us choosing a
   different sanctioned style on purpose. Every value in both blocks is still literal
   hex. */
.criteo-magazine-browse {
	margin-top: var( --wp--preset--spacing--50, 2rem );
}

.criteo-magazine-browse .wp-block-button__link {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	background-color: #3d1ef8;
	color: #ffffff;
	border: 0;
	border-radius: 999px;
	padding: 0.9rem 1.9rem;
	font-size: 0.95rem;
	font-weight: 700;
	line-height: 1.2;
	box-shadow: 0 1px 2px rgba( 16, 24, 40, 0.06 ), 0 10px 24px rgba( 61, 30, 248, 0.28 );
	transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.15s ease;
}

/* Deepen along the brand's OWN axis and lift like the cards do. #3d1ef8 -> #130183
   are Twilight's two endpoints, so the hover is on-brand by construction rather than
   an invented darker shade — and white climbs from 7.67:1 to 15.44:1 rather than
   getting tighter. The guidelines specify no interaction states whatsoever ("hover",
   "rollover", "pressed", "disabled" appear zero times in all 42 pages — it is a
   print, deck and social document), so the gesture is the theme's own and only the
   colours are taken from the brand. */
.criteo-magazine-browse .wp-block-button__link:hover,
.criteo-magazine-browse .wp-block-button__link:focus-visible {
	background-color: #130183;
	color: #ffffff;
	transform: translateY( -3px );
	box-shadow: 0 2px 4px rgba( 16, 24, 40, 0.08 ), 0 16px 34px rgba( 61, 30, 248, 0.38 );
}

/* THE RING IS ROUTED THROUGH `contrast` BECAUSE IT SITS ON THE PAGE, NOT THE BUTTON.
   `outline-offset: 3px` puts a 3px gap of PAGE between button and ring, so the ring
   has to contrast with the page background — and that background flips. The old
   literal #14171d was picked against the light page and measured 1.00:1 against the
   dark one, i.e. keyboard focus was completely invisible in dark mode. That was a
   real bug, not a style preference, and it predates the recolour.
   No literal passes both grounds: orange is 3.00:1 on light (exactly at the floor),
   white is 1.10:1 on light, near-black is 1.00:1 on dark. `contrast` flips WITH the
   page — #000 on #f3f4f6 is 19.08:1, #eef1f6 on #14171d is 15.85:1.
   This is the distinction drawn at `.criteo-pagination`: `contrast` is the wrong
   choice for chrome sitting on a surface that does NOT flip, and exactly the right
   one for something that must track a surface that does. */
.criteo-magazine-browse .wp-block-button__link:focus-visible {
	outline: 2px solid var( --wp--preset--color--contrast );
	outline-offset: 3px;
}

/* --- Dark mode: the WHITE button style, not the Electric Blue one ----------
   The guidelines define exactly three button fills — Electric Blue with a white
   label, WHITE with an Electric Blue label, and orange with a white label — and the
   white one is a first-class option, not an improvisation.

   IT IS USED HERE FOR A MEASURED REASON. Electric Blue #3d1ef8 against the dark page
   #14171d is 2.34:1, under the 3:1 that WCAG 1.4.11 asks for to identify a UI
   component: the LABEL was always legible (white on the blue is 7.67:1), but the
   button's own edge barely separated from the page. White on that page is 17.95:1.
   Inverting to the white style costs nothing in label contrast — Electric Blue on
   white is also 7.67:1, the same pair the other way round.

   WORTH KNOWING THE BOOK ITSELF DOES NOT DO THIS. Its dark Card System specimen
   keeps Electric Blue buttons on a #130183 card, which measures 2.01:1 — the brand
   is content to separate a button from its ground by hue rather than luminance.
   That works in a print deck and does not clear the bar this theme holds itself to,
   which is why the two diverge here. Light mode is unaffected: Electric Blue on the
   #f3f4f6 page is 6.97:1 and stays exactly as it is.

   Hover swaps to the OTHER sanctioned style rather than inventing a shade, which is
   the same gesture light mode makes (there it deepens Electric Blue to Blue). The
   shadow drops its blue tint at rest because a coloured glow under a white pill on a
   near-black page reads as a smear; it comes back on hover, when the button is blue
   again and the tint is doing what the book's shadow system shows. */
:root[data-theme="dark"] .criteo-magazine-browse .wp-block-button__link {
	background-color: #ffffff;
	color: #3d1ef8;
	box-shadow: 0 1px 2px rgba( 0, 0, 0, 0.3 ), 0 10px 24px rgba( 0, 0, 0, 0.45 );
}

:root[data-theme="dark"] .criteo-magazine-browse .wp-block-button__link:hover,
:root[data-theme="dark"] .criteo-magazine-browse .wp-block-button__link:focus-visible {
	background-color: #3d1ef8;
	color: #ffffff;
	box-shadow: 0 2px 4px rgba( 0, 0, 0, 0.4 ), 0 16px 34px rgba( 61, 30, 248, 0.45 );
}

/* Lucide `arrow-right`, decorative — the anchor's accessible name is the label
   beside it. Sized in `em` so it tracks the label rather than needing its own
   breakpoint, and `flex-shrink: 0` so it never squashes if the button is ever
   constrained. Stroke colour comes from `currentColor` in the markup. */
.criteo-magazine-browse__icon {
	width: 1.15em;
	height: 1.15em;
	flex-shrink: 0;
}

.criteo-magazine-outro {
	margin-bottom: 0;
}

/* --- Responsive ---------------------------------------------------------- */
@media ( max-width: 781px ) {
	/* Keep the section title and "see all" on one line as long as they fit;
	   the flex wrap in the markup handles it when they don't. */
	.criteo-magazine-section__head {
		margin-bottom: 1.15rem;
		align-items: baseline;
	}


	/* No `position: static` reset for the aside here any more — the sticky rule is
	   now gated behind `min-width: 782px and min-height: 880px`, so at this
	   breakpoint it simply never applies. */

	/* NOTE: there is deliberately no `order` / `display: contents` juggling here
	   any more. Featured used to live inside the right-hand column, so getting it
	   above the shelves on a phone meant dissolving the column with
	   `display: contents` and ordering the three modules by hand — which also left
	   visual order differing from DOM order for screen readers.
	   The band now sits above `.criteo-magazine-body` in the markup and the aside is
	   simply the second column, so the stacked order — Featured, shelves, topics,
	   pills — falls out of DOM order for free, and core's
	   `flex-basis: 100% !important` on `.wp-block-column` handles the stacking. */


	/* Tag pills become touch targets here — the 44px floor. */
	.criteo-category-tags__pill {
		min-height: 44px;
		padding: 0.55rem 0.9rem;
	}

	/* The masthead drops to 200px on a phone. Same `!important` reason as the lead
	   banner below: the cover block writes its `min-height` as an inline style, which
	   no stylesheet selector can outrank.

	   At this width cover trims the image horizontally rather than vertically, so the
	   18% vertical framing stops mattering and `center` keeps the middle of the group
	   — which is where most of the people are.

	   RETIRED with the masthead in 2.5.0 — see the note on the desktop rules above for
	   why these stay. */
	.criteo-home-banner {
		min-height: 200px !important;
		border-radius: 14px;
	}

	.criteo-home-banner .wp-block-cover__image-background {
		object-position: center center;
	}

	.criteo-home-banner__title {
		max-width: none;
		font-size: clamp( 1.35rem, 5.4vw, 1.7rem );
	}

	/* The lead card, stacked. Core has already turned the columns into rows for us
	   (`flex-wrap: wrap` plus `flex-basis: 100% !important` on `.wp-block-column`), so
	   what is left is undoing the two things that only make sense side by side.

	   No `!important` needed anywhere here any more — that was only ever required
	   because the old cover block wrote `min-height` as an inline style. A Query Loop
	   writes no inline styles, so ordinary selectors are enough. */
	.criteo-cards.criteo-home-lead .wp-block-post {
		border-radius: 20px;
	}

	/* Tighter than the 1.75rem/2rem the stacked block above uses — matches the padding a normal card uses
	   at this width, since that is effectively what this now is. `box-sizing` is
	   inherited from the desktop rule and still matters: stacked columns are
	   `flex-basis: 100%`, and with `content-box` the padding would push the column
	   past the card and clip. */
	.criteo-cards.criteo-home-lead .wp-block-columns > .wp-block-column:last-child {
		padding: 1.35rem 1.3rem 1.4rem;
	}

	/* The editorial plate on a phone. The clamp above already floors the headline at
	   1.65rem, so nothing needs walking back here — which is the payoff for sizing it in
	   CSS instead of with a `has-*-font-size` class. What is left is the standfirst's
	   measure, which is meaningless at this width, and the plate's inset. The row already
	   stacked at 900px. */
	.criteo-home-editorial .wp-block-post-excerpt {
		max-width: none;
	}

	/* The card's inset, tightened for phones. 2rem each side takes 64px out of a ~326px
	   container — a fifth of the width spent on padding, which pushes the headline into far
	   more lines than it needs. 1.15rem is what a normal card uses at this width, so this is
	   the same convergence on the card system the desktop rule makes.

	   Both this and the desktop rule are 0,1,0 and this one is later in the file, so it wins
	   on source order. (Until 3.6.0 this lived on the `--backdrop` variant at 0,2,0 and
	   carried a note explaining why it could not simply be inherited — that note went with
	   the variant.) */
	.criteo-home-editorial__plate {
		padding: 1.4rem 1.15rem 1.3rem;
	}

	/* The month drops under the heading rather than fighting it for the row: at 390px the
	   container is ~326px and "Editorial" plus "September 2026" does not fit on one line,
	   and the head's `flexWrap: wrap` would otherwise leave it stranded mid-row. */
	.criteo-home-editorial__month {
		white-space: normal;
	}
}

/* ==========================================================================
   Twilight display-headline treatment (3.10.0)
   Block style on core/heading — "Twilight gradient" in the editor's Styles panel.
   Registered in inc/companion.php; see criteo_tc_register_block_styles().
   ========================================================================== */

/* Criteo Brand Guidelines v5.0 sanctions Twilight for "backgrounds, containers, and as a
   display headline color treatment". This is that third use — the gradient clipped to the
   glyphs. It reads in both colour modes without a variant: the gradient is literal hex end
   to end, and electric blue on near-black is as legible as it is on white.

   THE `color` DECLARATION BELOW THE CLIP IS THE FALLBACK AND MUST STAY ABOVE IT.
   `background-clip: text` with `color: transparent` is the standard technique and its
   failure mode is catastrophic rather than degraded — if the clip does not apply, the
   heading is invisible rather than merely unstyled. Declaring `primary` first means any
   engine that drops the clip renders a real colour instead of nothing.

   THE SOLID COLOUR AND THE CLIP MUST BE IN SEPARATE RULES, WITH THE CLIP BEHIND @supports.
   Putting `color: <solid>` and `color: transparent` in one block does NOT create a
   fallback — `transparent` is universally supported and simply wins on source order, so
   an engine without `background-clip: text` renders invisible text. The @supports gate is
   what actually makes the solid the fallback. This was built the wrong way first; the bug
   is only visible in engines nobody tests on, which is exactly why it is written out here.

   DARK MODE NEEDS ITS OWN GRADIENT — TWILIGHT IS UNUSABLE ON THE DARK GROUND. Measured
   against `base` #14171d, BOTH Twilight stops fail even the 3:1 large-text floor:
   Electric Blue #3d1ef8 is 2.34:1 and Blue #130183 is 1.16:1, i.e. the tail of the
   headline all but disappears. The dark variant inverts to the light end of the same
   brand family — Lilac #8b78fb (5.28:1) into Lavender #d8d2fe (12.45:1), so its WORST end
   still clears AA for body text, never mind display sizes. Same 135deg axis and same
   26%/74% stops, so it reads as the same treatment, mirrored for the ground it sits on.

   Stop ORDER is deliberate, not arbitrary: light-mode Twilight runs 7.67:1 into 15.44:1,
   so the headline resolves TOWARD its strongest colour. Lilac into Lavender does the same
   thing on dark (5.28 into 12.45). Reversing the pair would technically pass contrast at
   both ends but the headline would fade out as it read, which is the opposite gesture.
   The guidelines specify Twilight for display headlines without qualifying the
   background; on a near-black page the light-family inversion is the only legible way to
   honour that.

   Scoped to headings only. The guideline says DISPLAY headlines, and clipped body copy is
   a legibility problem — which is also why this is not registered for core/paragraph. */
.is-style-criteo-twilight-text {
	color: var( --wp--preset--color--primary );
}

:root[data-theme="dark"] .is-style-criteo-twilight-text {
	color: var( --wp--preset--color--lavender );
}

@supports ( background-clip: text ) or ( -webkit-background-clip: text ) {
	.is-style-criteo-twilight-text {
		background-image: var( --wp--preset--gradient--criteo-twilight );
		-webkit-background-clip: text;
		background-clip: text;
		/* `text-fill-color` is what blanks the glyphs in WebKit/Blink; `color: transparent`
		   covers engines that honour background-clip without it. Both are needed. */
		-webkit-text-fill-color: transparent;
		color: transparent;
	}

	:root[data-theme="dark"] .is-style-criteo-twilight-text {
		background-image: linear-gradient( 135deg, #8b78fb 26%, #d8d2fe 74% );
		color: transparent;
	}
}

/* Windows High Contrast / forced colours throws away background-image, so the
   transparent-text declarations above would leave nothing on screen. Hand the glyphs back
   to the system palette — both variants, or the dark rule would keep its transparency.
   Unlike the note at .criteo-home-editorial__plate — where forced colours needed nothing
   because a 1px border survives on its own — this treatment carries ALL of its colour in a
   background, so it cannot survive untouched. */
@media ( forced-colors: active ) {
	.is-style-criteo-twilight-text,
	:root[data-theme="dark"] .is-style-criteo-twilight-text {
		background-image: none;
		color: CanvasText;
		-webkit-text-fill-color: currentColor;
	}
}
