/**
 * Fit Football — Homepage 2026 (section-by-section mirror).
 * Mobile-first: base rules are the phone layout; tablet/desktop widen from
 * there. Tokens from tokens.css only. Card styling (.pdp-rcard) and review
 * card styling (.pdp-rv-card) are shared with the product page
 * (pdp-2026.css, enqueued as a dependency) and never redefined here.
 */

/* `clip`, not `hidden`, and the difference is the whole page's scrolling.
   `overflow-x: hidden` on one axis forces the OTHER axis to compute to `auto`
   (CSS says `visible` cannot pair with a clipping value), so this one line was
   quietly making the entire homepage wrapper a nested scroll container: a
   7700px-tall scrolling box sitting inside the page's own scroll. It cost a
   compositing layer the size of the document, and a touch that lands in it is
   offered to it first — which is how a phone ends up feeling like the page has
   stopped moving under the finger. `clip` cuts the horizontal bleed exactly the
   same way and creates no scroll container at all. `hidden` is left in front of
   it for anything too old to know `clip`. */
.fth { background: var(--ff-paper); overflow-x: hidden; }
.fth { overflow-x: clip; overflow-y: visible; }
.fth * { box-sizing: border-box; }

/* The stray currency-switcher band Elementor leaves between the hero and the
   picker — parked for now, the owner will say where it belongs. Matched by
   what it CONTAINS rather than only by its Elementor id, so it stays hidden
   if that id changes when the page is next edited; the id is kept as a
   belt-and-braces fallback. This file only loads behind the homepage gate, so
   nothing outside the 2026 homepage is affected. */
.elementor-element.e-parent:has(.yay-currency-single-page-switcher),
.elementor-element-833f436 { display: none !important; }

.fth-h2 {
	font-family: var(--ff-font-display); text-transform: uppercase; letter-spacing: .01em;
	font-size: clamp(22px, 4.6vw, 31px); color: var(--ff-ink); margin: 0 0 16px;
	padding: 0 18px;
}
.fth-h2.is-center { text-align: center; padding: 0; }

/* ---------------------------------------------------------------- *
 * Marquee
 * ---------------------------------------------------------------- */

.fth-marquee {
	display: flex; align-items: center; justify-content: center; gap: 8px;
	background: var(--ff-ink); color: #fff; padding: 11px 16px;
	font-family: var(--ff-font-body); font-weight: 700; font-size: 12.5px;
	text-transform: uppercase; letter-spacing: .04em; text-align: center;
}
.fth-marquee svg { width: 15px; height: 15px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* ---------------------------------------------------------------- *
 * Cleats/Kits split picker — full-bleed, two selectable options.
 *
 * TWO registered custom properties drive everything:
 *   --ffp-split  where the two halves meet (50% at rest)
 *   --ffp-zoom   how far the photo in a half is pushed in
 * Both animate via their own transition; every other value here is *derived*
 * from them with calc() and carries no transition of its own, so nothing
 * chases anything else — clip edge, photo offset, title position and label
 * width all land on the same frame.
 *
 * @property is what makes a custom property animatable at all: without the
 * registration `transition-property: --ffp-split` is ignored and it jumps.
 *
 * TRANSITION LONGHANDS, NOT THE SHORTHAND, AND NO var() IN THEM: measured on
 * the live page, `transition: --ffp-split var(--picker-dur) var(--picker-ease)`
 * computes to `transition-property: none; transition-duration: 0s` — a custom
 * property name plus var() substitution in the same shorthand voids the whole
 * declaration, so the split jumped instead of animating. That is the bug
 * actually responsible for "it snaps open". Keep these as longhands with
 * literal values; it has been reintroduced by accident once already.
 *
 * WHY THE PHOTO IS OFFSET: each half renders its photo at the full 100vw and
 * is then clipped, so left-half/right-half would otherwise show the LEFT edge
 * and the RIGHT edge of their photos, never the middle. The translateX below
 * re-centres each photo inside the strip that is actually visible, at every
 * split position, so the centre of the picture is always what is seen.
 *
 * Easing: an expo-out curve puts ~99% of its motion in the first third of the
 * duration, which the eye reads as a snap. easeInOutQuart eases out of rest,
 * carries through the middle and settles — that is what reads smooth.
 * ---------------------------------------------------------------- */

@property --ffp-split {
	syntax: '<percentage>';
	inherits: true;
	initial-value: 50%;
}
@property --ffp-zoom {
	syntax: '<number>';
	inherits: true;
	initial-value: 1;
}

.fth-picker {
	--ffp-split: 50%;
	--picker-h: clamp(320px, 34vw, 460px);
	position: relative; left: 50%; right: 50%; margin-left: -50vw; margin-right: -50vw;
	width: 100vw; height: var(--picker-h); overflow: hidden;
	background: var(--ff-ink);
	transition-property: --ffp-split;
	transition-duration: .95s;
	transition-timing-function: cubic-bezier(.76, 0, .24, 1); /* easeInOutQuart */
}
.fth-picker[data-active="0"] { --ffp-split: 78%; }
.fth-picker[data-active="1"] { --ffp-split: 22%; }

/* The seam between the two options — a hairline of paper so they never read
   as one photograph split by accident. */
.fth-picker::after {
	content: ''; position: absolute; top: 0; bottom: 0; z-index: 3;
	left: var(--ffp-split); width: 2px; margin-left: -1px;
	background: var(--ff-paper); pointer-events: none;
}

/* The zoom runs on its own, shorter clock than the split. A hover that takes
   .95s to arrive reads as nothing happening at all, and on a tap the whole
   zoom has to finish inside the ~620ms before the page navigates. --ffp-split
   lives on .fth-picker and --ffp-zoom on .fth-picker-half, so the two can be
   timed independently. */
.fth-picker-half {
	--ffp-zoom: 1;
	position: absolute; inset: 0; width: 100%; height: 100%;
	transition-property: --ffp-zoom;
	transition-duration: .5s;
	transition-timing-function: cubic-bezier(.33, 1, .68, 1);
}
.fth-picker-half:first-child { clip-path: inset(0 calc(100% - var(--ffp-split)) 0 0); }
.fth-picker-half:last-child  { clip-path: inset(0 0 0 var(--ffp-split)); }

.fth-picker-img { position: absolute; inset: 0; overflow: hidden; }
.fth-picker-img picture, .fth-picker-img img { display: block; width: 100%; height: 100%; }
.fth-picker-img img { object-fit: cover; object-position: center center; }

/* Left strip runs 0 → split, so its middle is split/2; right strip runs
   split → 100%, middle (split + 100%)/2. Each photo is shifted from its own
   middle (50%) to there. No transition — recomputed from the animated vars. */
.fth-picker-half:first-child .fth-picker-img img {
	transform: translateX(calc(var(--ffp-split) / 2 - 50%)) scale(var(--ffp-zoom));
}
.fth-picker-half:last-child .fth-picker-img img {
	transform: translateX(calc((var(--ffp-split) + 100%) / 2 - 50%)) scale(var(--ffp-zoom));
}

/* Hover is guarded by `hover: hover` so a phone never inherits a stuck hover
   state after a tap; touch gets the same push-in through .is-press, which the
   script sets on pointerdown (iOS will not fire :active reliably on its own). */
@media (hover: hover) {
	.fth-picker-half:hover { --ffp-zoom: 1.08; }
}
.fth-picker-half.is-press { --ffp-zoom: 1.08; }
.fth-picker[data-active] .fth-picker-half.is-chosen { --ffp-zoom: 1.14; }
.fth-picker[data-active] .fth-picker-half:not(.is-chosen) { --ffp-zoom: 1.2; }

/* A permanent wash so both sides read as UI you can choose between rather
   than as two photographs, and so the titles stay legible on any upload. */
.fth-picker-shade {
	position: absolute; inset: 0; pointer-events: none;
	background: linear-gradient(180deg, rgba(8,11,15,.42) 0%, rgba(8,11,15,.18) 42%, rgba(8,11,15,.52) 100%);
	opacity: .82;
	transition-property: opacity;
	transition-duration: .95s;
	transition-timing-function: cubic-bezier(.76, 0, .24, 1);
}
.fth-picker-half:hover .fth-picker-shade { opacity: .6; }
.fth-picker[data-active] .fth-picker-half.is-chosen .fth-picker-shade { opacity: .52; }
.fth-picker[data-active] .fth-picker-half:not(.is-chosen) .fth-picker-shade { opacity: 1; }

/* The name, bottom left of each half — the same move the brand banners make,
   so the two rows speak with one voice. It is pinned to the left edge of the
   strip that is actually visible, so it travels with the split rather than
   sliding out of view when a side narrows. */
.fth-picker-name {
	position: absolute; bottom: 0; z-index: 2;
	display: block; padding: 0 clamp(16px, 2.4vw, 30px) clamp(16px, 2.2vw, 28px);
	max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
	font-family: var(--ff-font-display); text-transform: uppercase;
	font-size: clamp(26px, 3.4vw, 46px); line-height: 1; letter-spacing: .005em;
	color: #fff; text-shadow: 0 2px 16px rgba(0,0,0,.4);
	pointer-events: none;
	transition: transform .4s cubic-bezier(.22,.9,.3,1);
}
.fth-picker-half:first-child .fth-picker-name { left: 0; }
.fth-picker-half:last-child  .fth-picker-name { left: var(--ffp-split); }
.fth-picker-half:hover .fth-picker-name { transform: translateY(-4px); }

@media (max-width: 639px) {
	.fth-picker { --picker-h: clamp(400px, 100vw, 500px); transition-duration: .85s; }
	.fth-picker-shade { transition-duration: .85s; }
	/* NOT the half: that clock is the zoom, and it has to finish inside the
	   ~620ms before the tap navigates away or the push-in is never seen. */
	/* Phone: each name is bounded by its own slide, so a long word is cut at
	   the seam instead of running under the other side. */
	.fth-picker-name { padding: 0 14px 14px; font-size: clamp(20px, 5.6vw, 30px); }
	.fth-picker-half:first-child .fth-picker-name { left: 0; width: var(--ffp-split); }
	.fth-picker-half:last-child  .fth-picker-name { left: var(--ffp-split); width: calc(100% - var(--ffp-split)); }
}

/* Reduced motion: shorten and flatten, do NOT zero.
   A 0s duration here does not read as "respectful", it reads as broken — the
   panel teleports between two states. So the split still travels, just
   briefly, and the parts that actually provoke motion sickness (the photo
   push-in and its parallax against the clip edge) are dropped entirely.
   NOTE: Windows' Settings → Accessibility → Visual effects → Animation
   effects = Off makes every browser on the machine report `reduce`, so this
   branch is what that machine sees — it is not a rare path. */
@media (prefers-reduced-motion: reduce) {
	/* !important is not decoration here. Elementor's frontend.min.css ships
	   `@media (prefers-reduced-motion: reduce) { html * { transition-duration:
	   0s !important } }` — a blanket kill on every transition on the page. It
	   is why this panel teleported on any machine with Windows' animation
	   effects switched off, no matter what easing or technique was used here.
	   `.fth-picker` outranks `html *`, so this takes the component back. */
	.fth-picker, .fth-picker-half, .fth-picker-shade { transition-duration: .4s !important; }
	.fth-cbn-img { transition-duration: .5s !important; }
	/* NOTE — do NOT pin `.fth-cbn-frame { transform: none }` here, and do not
	   gate the fixed-background script on this query either. Both were tried
	   (2026-09-04) and both are wrong: a real `background-attachment: fixed`
	   backdrop is layout, not animation — no browser turns it off for reduced
	   motion — and our frame transform is only the arithmetic that reproduces
	   it. Killing it does not calm the page down, it deletes the design. It
	   also deletes it for a whole class of machine that never asked: Windows'
	   Animation effects = Off makes every browser on the box report `reduce`
	   (see the note above this block), which is exactly how the owner's own
	   desktop lost the effect while phones kept it. The banner's real motion —
	   the photo push-in and the copy rising — is what this block shortens. */
	.fth-cbn-kicker, .fth-cbn-h, .fth-cbn-cta,
	.fth-cbn-cta::before, .fth-cbn-cta svg { transition-duration: .3s !important; }
	.fth-picker-half:hover, .fth-picker-half.is-press,
	.fth-picker[data-active] .fth-picker-half.is-chosen,
	.fth-picker[data-active] .fth-picker-half:not(.is-chosen) { --ffp-zoom: 1; }
}

/* ---------------------------------------------------------------- *
 * Tile rows — Shop by Brand/Surface/Model, Support your team
 * ---------------------------------------------------------------- */

.fth-tilesec { padding: 32px 0; }

.fth-tilegrid { display: flex; flex-direction: column; gap: 20px; padding: 0 18px; }

.fth-tile { display: block; }
.fth .fth-tile, .fth .fth-tile-label, .fth .fth-tile:hover, .fth .fth-tile:visited,
.fth .fth-picker-half, .fth .fth-picker-name, .fth .fth-picker-half:hover, .fth .fth-picker-half:visited,
.fth .fth-cbn-cta, .fth .fth-cbn-cta:hover, .fth .fth-cbn-cta:visited,
.fth .fth-cbn-cta span, .fth .fth-cbn-h, .fth .fth-cbn-kicker { text-decoration: none !important; }
/* The 4/3 here is only a floor for a picture whose real size we could not
   read — every tile that knows its own dimensions carries an inline
   aspect-ratio, so the box is exactly the shape of the file that was
   uploaded and the photo is shown whole rather than centre-cropped. */
.fth-tile-img { position: relative; display: block; aspect-ratio: 4/3; border-radius: var(--ff-radius-md); overflow: hidden; background: var(--ff-paper-soft); }

/* The watermark: a brand mark or crest, large and faint over the middle of
   the picture. `contain` so a wide swoosh and a square crest both sit inside
   the same box without either being cropped, and it rides the tile's own
   hover push-in so the mark never looks pasted on top of a moving photo. */
.fth-tile-icon {
	position: absolute; inset: 0; z-index: 1;
	display: flex; align-items: center; justify-content: center;
	pointer-events: none;
}
/* Selector carries .fth-tile-img on purpose: `.fth-tile-img img` sets the
   banner photo to cover at 100%, and it is declared after this block with the
   same weight, so a plainer `.fth-tile-icon img` loses and the mark gets
   stretched and cropped like a photograph. */
.fth-tile-img .fth-tile-icon img {
	width: 54%; height: 54%; object-fit: contain;
	opacity: .3;
	filter: drop-shadow(0 2px 12px rgba(0,0,0,.25));
	transition: transform .5s cubic-bezier(.22,.9,.3,1), opacity .4s ease;
}
.fth-tile:hover .fth-tile-img .fth-tile-icon img { transform: scale(1.06); opacity: .4; }
.fth-tile-img img { width: 100%; height: 100%; object-fit: cover; object-position: center center; display: block; transition: transform .5s cubic-bezier(.22,.9,.3,1); }
.fth-tile:hover .fth-tile-img img { transform: scale(1.04); }
.fth-tile-fallback { position: relative; background: none; }
.fth-tile-fallback::before {
	content: ''; position: absolute; inset: 0;
	background: linear-gradient(150deg, var(--ff-paper-soft), var(--ff-line));
}
.fth-tile-label {
	display: flex; align-items: center; gap: 7px; margin-top: 11px;
	font-family: var(--ff-font-display); text-transform: uppercase; letter-spacing: .01em;
	font-size: clamp(17px, 3.6vw, 21px); line-height: 1.1; color: var(--ff-ink);
}
.fth-tile-label svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; transition: transform .2s ease; }
.fth-tile:hover .fth-tile-label svg { transform: translateX(3px); }
.fth-tile.is-locked { cursor: default; opacity: .55; }

/* ---------------------------------------------------------------- *
 * Tile carousel — Shop by Surface
 *
 * The surface photographs are shot on white on purpose, so the row is
 * meant to dissolve into the page: no card, no border, no radius, no
 * wash over the picture. The only thing added is the word itself, and
 * it earns its legibility from a halo rather than from a panel — a
 * scrim here would draw exactly the box the photography avoids.
 * ---------------------------------------------------------------- */

.fth-tilecar-wrap { position: relative; }
.fth-tilecar {
	overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none;
	-webkit-overflow-scrolling: touch; padding: 2px 18px 6px;
	/* Snap points land on the card edge, not the rail edge — without this the
	   rail rests 18px in and shaves the first card on load. */
	scroll-padding-left: 18px;
}
.fth-tilecar::-webkit-scrollbar { display: none; }
.fth-tilecar-track { display: flex; gap: 14px; align-items: flex-start; }
.fth-tilecar-track .fth-tile { flex: 0 0 80%; scroll-snap-align: start; }

.fth-tilecar .fth-tile-img { border-radius: 0; background: none; overflow: visible; }
.fth-tilecar .fth-tile-img img { transform: none; }
.fth-tilecar .fth-tile:hover .fth-tile-img img { transform: none; }
.fth-tilecar .fth-tile-img .fth-tile-icon img { opacity: .18; }

/* ---------------------------------------------------------------- *
 * The name on a picture
 * ---------------------------------------------------------------- */

.fth-tile-over {
	position: absolute; inset: 0; z-index: 2;
	display: flex; pointer-events: none;
}
.fth-tile-name {
	font-family: var(--ff-font-display); text-transform: uppercase;
	line-height: .94; letter-spacing: .005em;
}

/* Surface: centred, ink on the white of the photograph. The halo is white,
   not a shadow — it lifts the word off the grass without darkening it. */
.fth-tilecar .fth-tile-over { align-items: center; justify-content: center; padding: 16px; text-align: center; }
.fth-tilecar .fth-tile-name {
	font-size: clamp(34px, 8vw, 58px); color: #fff;
	/* One soft shadow, nothing more: the word is white on grass, and the
	   shadow is only there so it still holds on a pale patch of the pitch. */
	text-shadow: 0 2px 14px rgba(0,0,0,.38);
	transition: transform .45s cubic-bezier(.22,.9,.3,1);
}
.fth-tilecar .fth-tile:hover .fth-tile-name { transform: translateY(-3px); }

/* Brand: bottom left, white on the lightest scrim that still holds it —
   the banner is the message, the wordmark only names it.
   The scrim is painted by a ::before rather than on the element, and that is
   not a style choice. Elementor ships an inline lazy-load rule —
   `.e-con.e-parent:nth-of-type(n+2):not(.e-lazyloaded):not(.e-no-lazyload) *
   { background-image: none !important }` — which is both !important and four
   classes deep, so no honest selector of ours outranks it. Its `*` matches
   elements only: a pseudo-element is simply out of its reach. Any decorative
   gradient in this file should be drawn the same way. */
.fth-tilesec--brand .fth-tile-over,
.fth-tilesec--model .fth-tile-over {
	align-items: flex-end; justify-content: flex-start;
	padding: clamp(14px, 3vw, 26px);
}
.fth-tilesec--brand .fth-tile-over::before,
.fth-tilesec--model .fth-tile-over::before {
	content: ''; position: absolute; inset: 0;
	background: linear-gradient(to top, rgba(0,0,0,.42) 0%, rgba(0,0,0,.12) 34%, rgba(0,0,0,0) 62%);
	opacity: 1; transition: opacity .4s ease;
}
.fth-tilesec--brand .fth-tile:hover .fth-tile-over::before,
.fth-tilesec--model .fth-tile:hover .fth-tile-over::before { opacity: .78; }
.fth-tilesec--brand .fth-tile-name,
.fth-tilesec--model .fth-tile-name { position: relative; z-index: 1; }
.fth-tilesec--brand .fth-tile-name,
.fth-tilesec--model .fth-tile-name {
	font-size: clamp(26px, 5.4vw, 42px); color: #fff;
	text-shadow: 0 2px 16px rgba(0,0,0,.4);
	transition: transform .4s cubic-bezier(.22,.9,.3,1);
}
.fth-tilesec--brand .fth-tile:hover .fth-tile-name,
.fth-tilesec--model .fth-tile:hover .fth-tile-name { transform: translateY(-4px); }

/* Same treatment, one size down: a model tile is a third of the row where a
   brand tile is a half, and Anton at the brand's size would run out of box
   on "Superfly". The scrim is a touch deeper too — these are photographs of
   a cleat rather than flat brand art, so the bottom edge is busier. */
.fth-tilesec--model .fth-tile-name {
	font-size: clamp(19px, 4vw, 30px);
	letter-spacing: .015em;
}
.fth-tilesec--model .fth-tile-over::before {
	background: linear-gradient(to top, rgba(0,0,0,.52) 0%, rgba(0,0,0,.18) 38%, rgba(0,0,0,0) 66%);
}

/* ---------------------------------------------------------------- *
 * Carousel progress line
 *
 * A scrollbar rather than a percentage: the length of the fill says how
 * much of the row fits on screen, which a 0–100% bar cannot express.
 * Ink on a hairline, Anton numerals — the header's own vocabulary.
 * ---------------------------------------------------------------- */

.fth-carousel-nav--prog {
	display: flex; align-items: center; gap: 16px;
	justify-content: flex-start; margin-top: 20px; padding: 0 18px;
}
.fth-carprog-track {
	position: relative; flex: 1 1 auto; height: 2px;
	background-color: var(--ff-line); overflow: hidden;
}
.fth-carprog-fill {
	/* Both edges are set by the script; `right: 70%` is only what the bar
	   looks like in the split second before it first runs. */
	position: absolute; top: 0; bottom: 0; left: 0; right: 70%;
	background-color: var(--ff-ink);
	transition: left .28s cubic-bezier(.3,.8,.3,1), right .28s cubic-bezier(.3,.8,.3,1);
}
.fth-carousel-arrows { display: flex; gap: 8px; }

/* Card widths are deliberately not a whole number of screens: a sliver of
   the next tile is always showing, which is what tells someone there is more
   to swipe. Sized so four surfaces never quite fit — otherwise on a wide
   screen the row would sit still and the arrows would switch themselves off. */
@media (min-width: 640px) {
	.fth-tilecar-track .fth-tile { flex-basis: 47%; }
}
@media (min-width: 900px) {
	.fth-tilecar { padding: 2px 32px 6px; scroll-padding-left: 32px; }
	.fth-carousel-nav--prog { padding: 0 32px; gap: 22px; }
	.fth-tilecar-track .fth-tile { flex-basis: 38%; }
}
@media (min-width: 1280px) {
	.fth-tilecar-track .fth-tile { flex-basis: 31%; }
}

@media (min-width: 640px) {
	.fth-tilegrid[data-count="2"] { flex-direction: row; }
	.fth-tilegrid[data-count="2"] .fth-tile { flex: 1 1 0; }
	.fth-tilegrid[data-count="3"], .fth-tilegrid[data-count="4"], .fth-tilegrid[data-count="6"] { display: grid; grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 900px) {
	.fth-tilegrid[data-count="3"] { grid-template-columns: repeat(3, 1fr); }
	.fth-tilegrid[data-count="4"] { grid-template-columns: repeat(4, 1fr); }
	.fth-tilegrid[data-count="6"] { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1280px) {
	.fth-tilegrid[data-count="6"] { grid-template-columns: repeat(6, 1fr); }
}

/* ---------------------------------------------------------------- *
 * Shop by Model — a fixed grid of equal banners
 *
 * Two up on a phone, three up from tablet: six models land as 3x2 and 2x3,
 * never as a row of one-sixth-wide slivers or a single tall column. Both
 * selectors are doubled (.fth-tilegrid.fth-tilegrid--model) so they outrank
 * the [data-count="6"] rules above, which this row would otherwise inherit.
 *
 * The height is the whole point of the block. Everywhere else on this page a
 * picture is drawn in the shape it was uploaded in, but the six model banners
 * were shot at four different ratios, and a grid of tiles that each stop at a
 * different line reads as a mistake. So here — and only here — one ratio is
 * set for all of them and the markup leaves its inline aspect-ratio off.
 * Portrait, because the banners are portrait: a landscape box would cut the
 * cleat in half to make the row shorter.
 * ---------------------------------------------------------------- */

.fth-tilegrid.fth-tilegrid--model {
	display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 12px;
}
.fth-tilesec--model .fth-tile-img { aspect-ratio: 3/4; }

@media (min-width: 700px) {
	.fth-tilegrid.fth-tilegrid--model { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
	/* Three across is a much wider box, so it is also allowed to be a little
	   less tall — otherwise two rows of it fill more than a screen. */
	.fth-tilesec--model .fth-tile-img { aspect-ratio: 4/5; }
}

/* ---------------------------------------------------------------- *
 * "What Customers Say" — the review carousel.
 *
 * Reference layout (green wash, tile badge, a focused card between two
 * peeking neighbours, round arrows underneath) carrying our own type: Anton
 * italic for the heading and card titles, Inter for the words people wrote.
 *
 * The focus effect is transform + opacity only, set by a class the script
 * puts on whichever card is nearest the middle of the rail — so it costs a
 * compositor pass, not a layout, and the native scroll snap keeps doing the
 * actual scrolling.
 * ---------------------------------------------------------------- */

.fthrv {
	padding: 52px 0 46px;
	background: linear-gradient(180deg,
		color-mix(in srgb, var(--ff-brand) 22%, #fff) 0%,
		color-mix(in srgb, var(--ff-brand) 7%, #fff) 38%,
		#fff 82%);
}

.fthrv-head { display: flex; flex-direction: column; align-items: center; gap: 18px; margin-bottom: 30px; padding: 0 18px; }

.fthrv-h {
	margin: 0; text-align: center;
	font-family: var(--ff-font-display); font-style: italic; text-transform: uppercase;
	font-size: clamp(26px, 5vw, 52px); line-height: 1; letter-spacing: .01em;
	color: var(--ff-ink);
}

.fthrv-badge {
	display: inline-flex; align-items: center;
	padding: 11px 16px; background: #fff; border-radius: var(--ff-radius-md);
	box-shadow: 0 2px 10px rgba(11,15,20,.07);
}

/* --- rail --- */

.fthrv-rail {
	overflow-x: auto; scroll-snap-type: x mandatory;
	scrollbar-width: none; -webkit-overflow-scrolling: touch;
	padding: 14px 0 20px;
}
.fthrv-rail::-webkit-scrollbar { display: none; }

/* The gutters are padding on the track, not margins on the cards, so the
   first and last card can still snap to the centre of the viewport. */
.fthrv-track {
	display: flex; gap: 18px; align-items: stretch;
	padding-inline: calc(50vw - min(190px, 42vw));
}
/* Once the row loops there is always a card either side, so the gutters that
   existed only to let the first and last card reach the centre would just be
   a gap where a review should be. */
.fthrv-track.is-loop { padding-inline: 0; }

.fthrv-card {
	flex: 0 0 min(380px, 84vw); scroll-snap-align: center;
	display: flex; flex-direction: column; align-items: flex-start;
	background: #fff; border-radius: var(--ff-radius-lg);
	padding: 26px 26px 22px;
	box-shadow: 0 4px 22px rgba(11,15,20,.09);
	opacity: .48; transform: scale(.9);
	transition: opacity .45s cubic-bezier(.33,1,.68,1), transform .45s cubic-bezier(.33,1,.68,1);
	/* NO will-change here, and it is load-bearing. The reviews rail is cloned
	   three times for its loop, so this selector matches 36 cards, and a
	   permanent `will-change: transform` gives every one of them its own
	   compositing layer for a scale that only runs for .45s when the focused
	   card changes. Measured on a phone viewport: 113 layers with this line,
	   77 without, and the per-frame Layerize pass halved. The browser still
	   promotes a card for the length of its own transition, which is the
	   entire benefit; the rest was rent. */
}
.fthrv-card.is-focus { opacity: 1; transform: scale(1); }

.fthrv-rating { display: block; margin-bottom: 14px; }
.fthrv-rating .pdp-rv-tile { width: 23px; height: 23px; }

.fthrv-title {
	margin: 0 0 12px;
	font-family: var(--ff-font-display); font-style: italic; text-transform: uppercase;
	font-size: clamp(17px, 1.7vw, 21px); line-height: 1.15; letter-spacing: .01em;
	color: var(--ff-ink);
}

/* The quote rule from the reference, in our brand green. */
.fthrv-body {
	margin: 0; padding-left: 15px; border-left: 3px solid var(--ff-brand);
	font-family: var(--ff-font-body); font-size: 15px; line-height: 1.55;
	color: var(--ff-ink-soft);
}

/* --- photos and clips --- */

.fthrv-media { display: flex; gap: 8px; margin-top: 16px; }

.fthrv-shot {
	position: relative; flex: none; width: 62px; height: 62px; padding: 0;
	border: 0; border-radius: var(--ff-radius-sm); overflow: hidden;
	background: var(--ff-paper-soft); cursor: pointer;
}
.fthrv-shot img, .fthrv-shot video { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .4s cubic-bezier(.33,1,.68,1); }
.fthrv-shot:hover img, .fthrv-shot:hover video { transform: scale(1.08); }
.fthrv-play {
	position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
	color: #fff; background: rgba(8,11,15,.28);
}
.fthrv-play svg { width: 20px; height: 20px; }

/* --- attribution --- */

.fthrv-by { display: flex; align-items: center; gap: 11px; margin-top: 20px; width: 100%; }

.fthrv-av {
	flex: none; width: 40px; height: 40px; border-radius: 50%;
	display: flex; align-items: center; justify-content: center;
	background: var(--ff-brand); color: #fff;
	font-family: var(--ff-font-display); font-size: 18px; line-height: 1;
}

.fthrv-who { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.fthrv-name { font-family: var(--ff-font-body); font-size: 14px; font-weight: 700; color: var(--ff-ink); }
.fthrv-date { font-family: var(--ff-font-body); font-size: 12.5px; color: var(--ff-ink-mute); }

.fthrv-verified {
	margin-left: auto; flex: none;
	font-family: var(--ff-font-body); font-size: 10.5px; font-weight: 700;
	text-transform: uppercase; letter-spacing: .05em;
	color: var(--ff-brand-ink);
}

/* --- arrows --- */

.fthrv-nav { display: flex; justify-content: center; gap: 14px; }

.fthrv-arrow {
	width: 52px; height: 52px; border-radius: 50%; border: 0;
	background: #fff; box-shadow: 0 2px 12px rgba(11,15,20,.12);
	display: flex; align-items: center; justify-content: center; cursor: pointer;
	transition: transform .25s ease, box-shadow .25s ease;
}
.fthrv-arrow:hover { transform: translateY(-2px); box-shadow: 0 6px 18px rgba(11,15,20,.16); }
.fthrv-arrow:disabled { opacity: .38; cursor: default; transform: none; box-shadow: 0 2px 12px rgba(11,15,20,.12); }
.fthrv-arrow svg { width: 20px; height: 20px; fill: none; stroke: var(--ff-ink); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* --- lightbox --- */

.fthrv-box {
	position: fixed; inset: 0; z-index: 99999;
	display: flex; align-items: center; justify-content: center;
	background: rgba(6,9,12,.88); padding: 24px;
	opacity: 0; transition: opacity .3s ease;
}
.fthrv-box.is-open { opacity: 1; }
.fthrv-box img, .fthrv-box video { max-width: min(1000px, 94vw); max-height: 88vh; display: block; border-radius: var(--ff-radius-md); }
.fthrv-box-x {
	position: absolute; top: 18px; right: 18px; width: 44px; height: 44px;
	border: 0; border-radius: 50%; background: rgba(255,255,255,.14); color: #fff;
	font-size: 26px; line-height: 1; cursor: pointer;
}

@media (max-width: 639px) {
	.fthrv { padding: 40px 0 34px; }
	.fthrv-card { padding: 22px 20px 18px; border-radius: var(--ff-radius-md); }
	.fthrv-arrow { width: 46px; height: 46px; }
}

@media (prefers-reduced-motion: reduce) {
	.fthrv-card { transition-duration: .2s; }
	.fthrv-shot img, .fthrv-shot video { transition: none; }
}

/* ---------------------------------------------------------------- *
 * Carousel nav (shared: reviews + product rails)
 * ---------------------------------------------------------------- */

.fth-carousel-nav { display: flex; justify-content: center; gap: 12px; margin-top: 18px; }
.fth-carousel-arrow {
	width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--ff-line);
	background: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer;
	transition: background .15s ease;
}
.fth-carousel-arrow:hover { background: var(--ff-paper-soft); }
.fth-carousel-arrow:disabled { opacity: .35; cursor: default; }
.fth-carousel-arrow svg { width: 18px; height: 18px; fill: none; stroke: var(--ff-ink); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.fth-prod-nav { margin-top: 12px; }

/* ---------------------------------------------------------------- *
 * Jerseys — crest rows for clubs and countries
 *
 * Three badges at a time on a phone, which is what makes a crest big
 * enough to recognise; the rail loops, so a flick never hits a wall.
 * ---------------------------------------------------------------- */

.fth-jerseys { padding: 32px 0; }
.fth-jerseys .fth-h2 { margin-bottom: 22px; }

.fth-crest + .fth-crest { margin-top: 30px; }

.fth-h3 {
	margin: 0;
	font-family: var(--ff-font-display); text-transform: uppercase; letter-spacing: .02em;
	font-size: clamp(15px, 3.4vw, 19px); color: var(--ff-ink);
}
.fth-crest .fth-prod-head {
	display: flex; align-items: baseline; justify-content: space-between;
	gap: 16px; padding: 0 18px; margin-bottom: 14px;
}

.fth-crest-wrap { position: relative; }
.fth-crest-rail {
	overflow-x: auto; scroll-snap-type: x proximity;
	scrollbar-width: none; -webkit-overflow-scrolling: touch;
	/* Margin, not padding — same reason as .fth-prod-rail: this row loops,
	   so it opens scrolled past any padding-left. */
	padding: 2px 0 4px; margin-inline: 18px;
}
.fth-crest-rail::-webkit-scrollbar { display: none; }
.fth-crest-track { display: flex; gap: 12px; align-items: flex-start; }

/* Three across on a phone: (100% - two gaps) / 3, worked out from the rail's
   own padding so the third badge stops exactly at the edge. */
.fth-crest-tile {
	flex: 0 0 calc((100% - 24px) / 3);
	scroll-snap-align: start;
	display: flex; flex-direction: column; align-items: center; gap: 9px;
	text-align: center;
}

.fth-crest-badge {
	position: relative; display: flex; align-items: center; justify-content: center;
	width: 100%; aspect-ratio: 1; overflow: hidden;
	background-color: var(--ff-paper-soft);
	border: 1px solid var(--ff-line);
	transition: transform .45s cubic-bezier(.22,.9,.3,1), border-color .35s ease;
}
.fth-crest-badge img {
	width: 68%; height: 68%; object-fit: contain; display: block;
	transition: transform .45s cubic-bezier(.22,.9,.3,1);
}
@media (hover: hover) {
	.fth-crest-tile:hover .fth-crest-badge { transform: translateY(-4px); border-color: var(--ff-ink); }
	.fth-crest-tile:hover .fth-crest-badge img { transform: scale(1.07); }
}
.fth-crest-tile:active .fth-crest-badge { transform: translateY(-2px); }

/* Standing in for a crest nobody has uploaded yet. */
.fth-crest-mono {
	font-family: var(--ff-font-display); text-transform: uppercase;
	font-size: clamp(20px, 5vw, 30px); letter-spacing: .02em;
	color: var(--ff-ink-mute);
}

.fth-crest-name {
	font-family: var(--ff-font-body); font-weight: 600;
	font-size: clamp(11px, 2.8vw, 13px); line-height: 1.25; color: var(--ff-ink);
	display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.fth .fth-crest-tile, .fth .fth-crest-tile:hover, .fth .fth-crest-tile:visited,
.fth .fth-crest-tile * { text-decoration: none !important; }

/* Eight on a phone, nine everywhere else. Three across is the phone's row,
   and the ninth crest is both the odd one out of that count and the least
   stocked of the nine — so it is the one that steps aside. Marked with a
   class rather than :nth-child because the looping rail clones the whole
   set, and a positional rule would then pick a different tile in each copy. */
@media (max-width: 639px) {
	.fth-crest-tile.is-deskonly { display: none; }
}

@media (min-width: 640px) {
	.fth-crest-tile { flex-basis: calc((100% - 48px) / 5); }
}
@media (min-width: 900px) {
	.fth-crest-rail { padding: 2px 0 4px; margin-inline: 32px; }
	.fth-crest .fth-prod-head { padding: 0 32px; }
	/* Nine across is the whole row. With the arrows gone, a desktop that had
	   to be scrolled would simply be hiding crests, so the row is sized to the
	   count rather than the count left to overflow the row. */
	.fth-crest-tile { flex-basis: calc((100% - 96px) / 9); }
}

@media (prefers-reduced-motion: reduce) {
	.fth-crest-badge, .fth-crest-badge img { transition-duration: .2s !important; }
}

/* ---------------------------------------------------------------- *
 * Campaign banner — the pack promos and the Mystery Box
 *
 * Full bleed, one photograph, copy standing on it. Height is set per
 * breakpoint rather than taken from the file: this is the one place on
 * the page where the frame has a job of its own — it has to hold a
 * headline and a button — so the picture fills it and is cropped to fit.
 * Everywhere else on this page the picture still dictates its own shape.
 * ---------------------------------------------------------------- */

.fth-cbn {
	position: relative; left: 50%; right: 50%;
	margin-left: -50vw; margin-right: -50vw; width: 100vw;
	height: clamp(400px, 108vw, 500px);
	display: flex; align-items: flex-end;
	overflow: hidden; background: var(--ff-ink);
	isolation: isolate;
}

.fth-cbn-media { position: absolute; inset: 0; overflow: hidden; z-index: 0; }
.fth-cbn-frame, .fth-cbn-frame picture { display: block; width: 100%; height: 100%; }

/* Fixed-background illusion: the script sizes the frame to the full
   viewport and slides it with a transform computed from the window's own
   position, so the photo stays pinned to the screen and the window (the
   banner's own box, clipped by .fth-cbn-media's overflow:hidden) is what
   appears to travel down over it as the page scrolls — the same look as
   `background-attachment: fixed`, without it: a real fixed background
   breaks the moment any ancestor gets a transform/filter/will-change (a new
   containing block forms and "fixed" pins to that instead of the viewport),
   and is patchy on mobile browsers besides. No JS dependency: until the
   script adds this class the frame is simply 100% of its window, i.e. an
   ordinary full-bleed crop. */
.fth-cbn-frame--fixedbg {
	position: absolute; top: 0; left: 0; width: 100%;
	/* Full screen height, but never shorter than the window it has to fill.
	   `100vh` alone is not enough: a phone held sideways has a viewport around
	   350px tall while this banner's window is up to 500px, and the frame
	   would then end above the window's bottom edge and show a band of the ink
	   backing — the vertical gap this whole treatment exists to avoid. */
	height: 100vh; min-height: 100%;
	will-change: transform;
}

/* Any uploaded size is welcome here, on every screen — phone, tablet and
   desktop all run this one rule, there is no breakpoint that changes it.
   The picture fills its frame's full height and is cropped from the centre
   outwards (`cover` + `center center`), so a wide file loses its sides and a
   tall one loses its top and bottom, but neither can ever leave a vertical
   gap. That matters more here than on any other image on the page: the frame
   travels upwards over a pinned photograph, so a short picture would not just
   letterbox, it would slide a bare edge through the window mid-scroll.
   The size attributes the <img> carries are declared for layout stability
   only — object-fit is what decides what is actually seen.

   On top of that it starts a little over-scaled and settles to true size as
   the banner comes into view — the picture arrives rather than just being
   there. If the script never runs the image simply stays slightly zoomed,
   which is a perfectly good banner, so nothing here depends on JavaScript. */
.fth-cbn-img {
	/* !important on the height, and it is load-bearing — measured on the live
	   page 2026-09-04. Something in the theme/Elementor img sizing out-ranks a
	   plain `height: 100%` here (only the height; the width and object-fit from
	   this same rule apply fine), and the picture then falls back to its own
	   aspect ratio anchored at the top of the frame. That single miss caused
	   both of the faults the owner reported: on a phone the file is narrower
	   than the frame is tall, so it ended short and left a band of bare ink
	   under the copy; on a desktop it ended up ~1700px tall inside a 720px
	   frame, so only the top third of the photograph was ever on screen and the
	   subject sat below the fold of the banner. With the height forced, the box
	   is the frame and `cover` + `center center` finally mean what they say. */
	width: 100%; height: 100% !important; object-fit: cover; object-position: center center; display: block;
	transform: scale(1.12);
	/* Never lands on exactly 1: a fractional banner height would otherwise
	   leave a hairline of the ink backing showing along an edge. */
	transition: transform 1.7s cubic-bezier(.22, .61, .36, 1);
	/* No will-change: the frame this picture fills already carries one (it is
	   the box that moves every frame), and a second promoted layer inside a
	   promoted layer buys nothing but another entry in the layer list. */
}
.fth-cbn.is-in .fth-cbn-img { transform: scale(1.015); }
@media (hover: hover) {
	.fth-cbn:hover .fth-cbn-img { transform: scale(1.05); transition-duration: .9s; }
}

/* The wash. Drawn on a pseudo-element, never on the box: Elementor's
   lazy-load rule deletes background-image on every element inside a deep
   container — see the note on the brand tiles. Two layers, because the copy
   sits at the bottom on a phone and off to one side on a desktop. */
.fth-cbn-media::after {
	content: ''; position: absolute; inset: 0; z-index: 1;
	background:
		linear-gradient(to top, rgba(6,9,13,.62) 0%, rgba(6,9,13,.22) 34%, rgba(6,9,13,0) 62%),
		linear-gradient(100deg, rgba(6,9,13,.5) 0%, rgba(6,9,13,.18) 42%, rgba(6,9,13,0) 74%);
	transition: opacity .6s ease;
}
.fth-cbn--right .fth-cbn-media::after {
	background:
		linear-gradient(to top, rgba(6,9,13,.62) 0%, rgba(6,9,13,.22) 34%, rgba(6,9,13,0) 62%),
		linear-gradient(260deg, rgba(6,9,13,.5) 0%, rgba(6,9,13,.18) 42%, rgba(6,9,13,0) 74%);
}

.fth-cbn-inner {
	position: relative; z-index: 2; width: 100%;
	display: flex; padding: 0 22px 34px;
}
.fth-cbn--left  .fth-cbn-inner { justify-content: flex-start; }
.fth-cbn--right .fth-cbn-inner { justify-content: flex-end; }

.fth-cbn-copy { max-width: min(560px, 88%); text-align: left; }
.fth-cbn--right .fth-cbn-copy { text-align: right; }

/* Copy rises with the picture, a beat behind it.
   The hidden state is gated on .is-armed, which only the script adds, and the
   script arms a banner in the same breath as it starts watching it. Written
   the other way round — hidden in CSS, revealed by JS — a headline and a
   button vanish for good the moment anything stops the observer firing, and
   an invisible call to action is a worse outcome than no animation. */
.fth-cbn.is-armed .fth-cbn-kicker,
.fth-cbn.is-armed .fth-cbn-h,
.fth-cbn.is-armed .fth-cbn-price,
.fth-cbn.is-armed .fth-cbn-cta {
	opacity: 0; transform: translateY(14px);
}
.fth-cbn-kicker, .fth-cbn-h, .fth-cbn-price, .fth-cbn-cta {
	transition: opacity .7s ease, transform .7s cubic-bezier(.22, .61, .36, 1);
}
.fth-cbn.is-armed.is-in .fth-cbn-kicker { opacity: 1; transform: none; transition-delay: .12s; }
.fth-cbn.is-armed.is-in .fth-cbn-h      { opacity: 1; transform: none; transition-delay: .2s; }
.fth-cbn.is-armed.is-in .fth-cbn-price  { opacity: 1; transform: none; transition-delay: .28s; }
.fth-cbn.is-armed.is-in .fth-cbn-cta    { opacity: 1; transform: none; transition-delay: .38s; }

.fth-cbn-kicker {
	margin: 0 0 10px;
	font-family: var(--ff-font-body); font-weight: 600;
	font-size: clamp(11px, 2.6vw, 13px); letter-spacing: .18em; text-transform: uppercase;
	color: rgba(255,255,255,.85) !important;
}

/* !important on the colour: the theme sets a dark colour on every h2 in page
   content and beats a single class. On a photograph that is unreadable. */
.fth-cbn-h, .fth .fth-cbn-h {
	margin: 0;
	font-family: var(--ff-font-display); text-transform: uppercase;
	font-size: clamp(34px, 9.4vw, 72px); line-height: .92; letter-spacing: .005em;
	color: #fff !important; text-shadow: 0 2px 26px rgba(0,0,0,.38);
}

/* "From €69". The word is small and quiet, the number is not — a price is
   the one thing on a promo banner people go looking for, so it is set in the
   display face at headline weight with a hairline rule tying it to the label. */
.fth-cbn-price {
	display: inline-flex; align-items: baseline; gap: 7px;
	margin: 12px 0 0;
}
.fth-cbn--right .fth-cbn-price { flex-direction: row-reverse; }
.fth-cbn-price-l {
	font-family: var(--ff-font-body); font-weight: 600;
	font-size: clamp(10px, 2.2vw, 11px); letter-spacing: .18em; text-transform: uppercase;
	color: rgba(255,255,255,.62);
}
.fth-cbn-price-v {
	font-family: var(--ff-font-body); font-weight: 700;
	font-size: clamp(14px, 3vw, 16px); line-height: 1; letter-spacing: .01em;
	color: #fff;
}
.fth .fth-cbn-price, .fth .fth-cbn-price span { text-decoration: none !important; color: inherit; }
.fth .fth-cbn-price-v { color: #fff !important; }

/* The button. A solid plate that reads as a button from across the room,
   with the ink wiping across it on hover and the arrow stepping out. The
   fill is a pseudo-element so the label can sit above it. */
.fth-cbn-cta {
	position: relative; overflow: hidden;
	display: inline-flex; align-items: center; gap: 14px;
	margin-top: clamp(20px, 4.4vw, 32px);
	padding: clamp(18px, 4vw, 24px) clamp(30px, 6.5vw, 46px);
	background-color: #fff; color: var(--ff-ink);
	font-family: var(--ff-font-display); text-transform: uppercase;
	font-size: clamp(16px, 4vw, 22px); letter-spacing: .1em; line-height: 1;
	/* A button label never wraps — two stacked words make a box, not a button. */
	white-space: nowrap;
}
.fth-cbn-cta::before {
	content: ''; position: absolute; inset: 0; z-index: 0;
	background-color: var(--ff-ink);
	transform: scaleX(0); transform-origin: right center;
	transition: transform .55s cubic-bezier(.76, 0, .24, 1);
}
.fth-cbn-cta > span, .fth-cbn-cta > svg { position: relative; z-index: 1; }
.fth-cbn-cta svg {
	width: clamp(19px, 4.4vw, 24px); height: clamp(19px, 4.4vw, 24px); flex: none;
	fill: none; stroke: currentColor; stroke-width: 2.1;
	stroke-linecap: round; stroke-linejoin: round;
	transition: transform .4s cubic-bezier(.22, .61, .36, 1);
}
.fth .fth-cbn-cta, .fth .fth-cbn-cta:hover, .fth .fth-cbn-cta:visited { color: var(--ff-ink); }
@media (hover: hover) {
	.fth-cbn-cta:hover::before { transform: scaleX(1); transform-origin: left center; }
	.fth .fth-cbn-cta:hover { color: #fff; }
	.fth-cbn-cta:hover svg { transform: translateX(5px); }
}
/* Touch has no hover, so the press itself has to answer. */
.fth-cbn-cta:active::before { transform: scaleX(1); transform-origin: left center; }
.fth .fth-cbn-cta:active { color: #fff; }

@media (min-width: 760px) {
	.fth-cbn {
		height: clamp(400px, 33vw, 540px);
		align-items: center;
	}
	.fth-cbn-inner { padding: 0 clamp(40px, 7vw, 110px); }
	/* "A little off centre", not hard against the edge: the copy takes just
	   under half the width and leans to its own side, which leaves the
	   photograph the other half to actually be seen in. */
	.fth-cbn-copy { max-width: min(46%, 620px); }
}

/* ---------------------------------------------------------------- *
 * Product sections — Best Sellers, Latest Drops, packs
 * ---------------------------------------------------------------- */

.fth-prod { padding: 34px 0; }
.fth-prod-sub { font-family: var(--ff-font-body); font-style: italic; color: var(--ff-ink-mute); font-size: 13.5px; margin: -6px 0 16px; padding: 0 18px; }
/* Best Sellers: everything visible at once, in the grid the category pages
   already use. Columns, gaps and breakpoints are copied from .ffs-grid in
   search-2026.css on purpose — a product should not be able to tell which
   page it is being shown on. */
.fth-prodgrid {
	display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 28px 12px; padding: 0 18px;
	align-items: start;
}
@media (min-width: 700px) { .fth-prodgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 36px 20px; } }
@media (min-width: 900px) { .fth-prodgrid { padding: 0 32px; } }
@media (min-width: 1040px) { .fth-prodgrid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.fth-prodgrid .pdp-rcard { width: 100%; }

/* Botiga underlines every link in page content, and it reaches the card
   through the anchor, where it strikes the name and the price alike. The
   card already says text-decoration: none — it is simply outranked here. */
.fth .pdp-rcard, .fth .pdp-rcard:hover, .fth .pdp-rcard:visited,
.fth .pdp-rcard * { text-decoration: none !important; }

/* Heading and "View all" on one line, the way a section header reads on a
   shop: what this row is on the left, the way out of it on the right. */
.fth-prod .fth-prod-head {
	display: flex; align-items: baseline; justify-content: space-between;
	gap: 16px; padding: 0 18px; margin-bottom: 16px;
}
.fth-prod .fth-prod-head .fth-h2 { padding: 0; margin: 0; }
.fth-prod .fth-prod-head .fth-viewall { flex: none; }
@media (min-width: 900px) { .fth-prod .fth-prod-head { padding: 0 32px; } }

.fth-prod-wrap { position: relative; }
/* The side gutter is a MARGIN, not padding, and that is not a style choice.
   A looping rail opens already scrolled onto the middle copy, so a
   padding-left has been scrolled past before anyone sees it and the first
   card sits flush against the phone's edge — which is exactly how this row
   looked once data-loop arrived. Insetting the scroller's own box instead
   keeps the gutter at every scroll position, in both directions. */
.fth-prod-rail { overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; -webkit-overflow-scrolling: touch; padding: 2px 0 6px; margin-inline: 18px; }
.fth-prod-rail::-webkit-scrollbar { display: none; }
/* A looping rail is silently moved back by one set whenever the scroll drifts
   off the middle copy, and `mandatory` snapping fights that jump — the engine
   re-snaps from wherever the jump landed and the row twitches. `proximity`
   still parks cards on their edges but leaves a programmatic scrollLeft alone.
   Same reason .fth-crest-rail, the other looping row, uses it. */
.fth-prod-rail[data-loop] { scroll-snap-type: x proximity; }
.fth-prod-track { display: flex; gap: 14px; align-items: flex-start; }
/* Card width is set so the rail shows the SAME number of products across as
   the grid does at that width, plus a sliver of the next one to say the row
   keeps going. At 62% a phone got one and a half cards — the row read as a
   stack of posters rather than a shelf of products, which is what "the boxes
   are too big" was about. Two up on a phone matches .fth-prodgrid exactly. */
.fth-prod-track .pdp-rcard { flex: 0 0 46%; scroll-snap-align: start; }

@media (min-width: 640px) { .fth-prod-track .pdp-rcard { flex-basis: 31%; } }
@media (min-width: 900px) { .fth-prod-track .pdp-rcard { flex-basis: 23%; } }

.fth-viewall {
	font-family: var(--ff-font-body); font-size: 13.5px; font-weight: 600; color: var(--ff-ink);
	text-decoration: underline; text-underline-offset: 3px;
}

/* ---------------------------------------------------------------- *
 * "Show more" — the phone-only fold under Best Sellers
 *
 * A phone only ever sees the first four cards; this opens the next four.
 * The pre-open state is set here rather than by the script so the fold is
 * already drawn in the first paint — a grid that shows eight cards and then
 * snaps to four is worse than no fold at all.
 *
 * The button is the widest, plainest thing on the page on purpose: full
 * bleed inside the grid's own gutter, a single hairline, Anton in ink, no
 * fill until it is pressed. Nothing else here is a box, so a box is enough
 * to make it the obvious thing to touch.
 * ---------------------------------------------------------------- */

.fth-morewrap { margin-top: 22px; padding: 0 18px; }
.fth-more {
	display: flex; align-items: center; justify-content: center; gap: 10px;
	width: 100%; margin: 0; padding: 17px 20px;
	background-color: transparent; color: var(--ff-ink);
	border: 1px solid var(--ff-ink); border-radius: 0;
	font-family: var(--ff-font-display); text-transform: uppercase;
	font-size: 17px; letter-spacing: .12em; line-height: 1;
	cursor: pointer; -webkit-appearance: none; -webkit-tap-highlight-color: transparent;
	transition: background-color .28s ease, color .28s ease;
}
.fth-more svg {
	width: 17px; height: 17px; flex: none;
	fill: none; stroke: currentColor; stroke-width: 2.2;
	stroke-linecap: round; stroke-linejoin: round;
	transition: transform .28s ease;
}
.fth-more:active { background-color: var(--ff-ink); color: var(--ff-paper); }
.fth-more:active svg { transform: translateY(2px); }
@media (hover: hover) {
	.fth-more:hover { background-color: var(--ff-ink); color: var(--ff-paper); }
	.fth-more:hover svg { transform: translateY(2px); }
}
/* Nothing left behind it — the script closes the row down rather than
   leaving a button that does nothing. */
.fth-morewrap.is-done { display: none; }

@media (max-width: 699px) {
	/* Before the first tap, and again for whatever is still folded after it.
	   n+5 is FF_HOME26_FOLD + 1 — the two have to move together. */
	.fth-prodgrid[data-more]:not(.is-open) > :nth-child(n+5),
	.fth-prodgrid[data-more] > .fth-hid { display: none; }

	.fth-prodgrid[data-more].is-open > :nth-child(n+5):not(.fth-hid) {
		animation: fth-more-in .34s cubic-bezier(.22,.9,.3,1) both;
	}
}
/* A wide screen shows the whole row already: no fold, no button. */
@media (min-width: 700px) { .fth-morewrap { display: none; } }

@keyframes fth-more-in {
	from { opacity: 0; transform: translateY(12px); }
	to   { opacity: 1; transform: none; }
}

/* ---------------------------------------------------------------- *
 * Tablet / desktop container width
 * ---------------------------------------------------------------- */

@media (min-width: 900px) {
	.fth-h2 { padding: 0 32px; }
	.fth-tilegrid { padding: 0 32px; gap: 16px; }
	.fth-prod-sub { padding: 0 32px; }
	.fth-prod-rail { padding: 2px 0 6px; margin-inline: 32px; }
	.fth-rvcar-rail { padding: 4px 32px; }
}

@media (min-width: 1280px) {
	.fth-h2, .fth-tilegrid, .fth-prod-sub, .fth-prod-rail, .fth-rvcar-rail,
	.fth-prodgrid, .fth-prod .fth-prod-head {
		max-width: var(--ff-container); margin-left: auto; margin-right: auto;
		padding-left: 0; padding-right: 0;
	}
}

@media (prefers-reduced-motion: reduce) {
	.fth-tile-img img { transition: none; }
	.fth-tile-over, .fth-tile-name, .fth-carprog-fill { transition-duration: .2s !important; }
	/* The revealed cards still appear, they just stop sliding in. */
	.fth-prodgrid[data-more].is-open > :nth-child(n+5):not(.fth-hid) { animation: none; }
	.fth-more, .fth-more svg { transition-duration: .01ms !important; }
}

/* ---------------------------------------------------------------- *
 * Community
 *
 * Full width on purpose, with no --ff-container cap: the three figures
 * ARE the section, and letting them spread edge to edge is what makes
 * them read as a statement rather than as a widget. Every size is in vw
 * so the band keeps its proportions from a 320px phone to a 4K desktop.
 *
 * Hairlines between the figures — no cards, no shadow, no coloured
 * chips. The weight comes from type and space, per §3 of the design
 * system: blocks are separated by hairlines, not by boxes.
 * ---------------------------------------------------------------- */

.fth-com {
	background: var(--ff-paper);
	border-top: 1px solid var(--ff-line);
	padding: clamp(44px, 6vw, 84px) clamp(18px, 4vw, 56px) clamp(40px, 5.4vw, 76px);
	text-align: center;
}

.fth-com-kicker {
	margin: 0 0 10px;
	font-family: var(--ff-font-body);
	font-size: 11px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
	color: var(--ff-ink-mute);
}

.fth-com-h {
	margin: 0 0 clamp(28px, 3.6vw, 46px);
	font-family: var(--ff-font-display); text-transform: uppercase; letter-spacing: .01em;
	font-size: clamp(24px, 4.6vw, 40px); line-height: 1.04; color: var(--ff-ink);
}

.fth-com-stats {
	display: grid; grid-template-columns: repeat(3, 1fr);
	list-style: none; margin: 0 0 clamp(26px, 3.4vw, 42px); padding: 0;
}

.fth-com-stat {
	display: flex; flex-direction: column; align-items: center; gap: 7px;
	padding: 0 clamp(6px, 1.4vw, 20px);
}

/* A hairline between the figures, never on the outer edges. */
.fth-com-stat + .fth-com-stat { border-left: 1px solid var(--ff-line); }

/* A figure is data, so it is set in the BODY face — the display face is for
   names. Setting numbers in Anton uppercase is the same mistake that made
   the product spec rows shout (design/ff-design-system.md §2). */
.fth-com-stat b {
	font-family: var(--ff-font-body); font-weight: 800;
	font-size: clamp(30px, 6.2vw, 74px); line-height: 1; letter-spacing: -.022em;
	color: var(--ff-ink);
}

.fth-com-stat span {
	font-family: var(--ff-font-body);
	font-size: 11px; font-weight: 600; line-height: 1.35;
	letter-spacing: .14em; text-transform: uppercase; color: var(--ff-ink-mute);
}

/* Identity, not a second call to action: quiet type, a hairline under the
   handle, and the note mark so it is recognised before it is read. */
.fth-com-handle {
	display: inline-flex; align-items: center; gap: 7px;
	margin-bottom: clamp(18px, 2.2vw, 26px);
	color: var(--ff-ink-soft); text-decoration: none;
	font-family: var(--ff-font-body); font-size: var(--ff-text-sm); font-weight: 600;
}
.fth-com-handle .fth-com-mark { flex: none; }
.fth-com-handle span {
	padding-bottom: 1px; border-bottom: 1px solid var(--ff-line);
	transition: color .16s cubic-bezier(.22,.61,.36,1), border-color .16s cubic-bezier(.22,.61,.36,1);
}
.fth-com-handle:hover span { color: var(--ff-ink); border-bottom-color: var(--ff-ink); }

.fth-com-cta {
	display: flex; align-items: center; justify-content: center; gap: 9px;
	width: min(100%, 330px); margin: 0 auto; min-height: 54px; padding: 0 26px;
	border-radius: 4px;
	background: var(--ff-ink); color: #fff; text-decoration: none;
	font-family: var(--ff-font-body); font-size: var(--ff-text-sm); font-weight: 700;
	letter-spacing: .06em; text-transform: uppercase;
	transition: background-color .16s cubic-bezier(.22,.61,.36,1);
}
.fth-com-cta svg { flex: none; }
.fth-com-cta:hover, .fth-com-cta:focus-visible { background: #1d2530; color: #fff; }

/* ----------------------------------------------------------------------
   RAILS: a horizontal fling stays in the rail.
   2026-09-17, with the swipe fix in home-2026.js. Without this, a fast
   sideways swipe that reaches the end of a rail hands the rest of its
   momentum to whatever scrolls behind it — the page, or on Android the
   browser's own back gesture. The rail stops dead and something else
   moves, which reads as the carousel misbehaving even when it has not.
   `contain` keeps the chain inside the rail without disabling the
   overscroll effect itself.
   -------------------------------------------------------------------- */
.fth-tilecar,
.fthrv-rail,
.fth-prod-rail,
.fth-cbn-rail {
	overscroll-behavior-x: contain;
}
