/*
 * Layout — header, nav, mobile menu, footer, pagination.
 *
 * Tokens only.
 */

/* ---------------------------------------------------------------- promo bar */

/*
 * Dismissible announcement strip, per the xxxu.net reference.
 *
 * Hidden via a class on <html> set by the pre-paint script in header.php — NOT
 * by JS after load. Otherwise a dismissed bar flashes in and disappears on every
 * single pageview, which is worse than never having dismissed it.
 */
/*
 * Solid accent red — the SAME red as the active pagination chip and the "Nové"
 * nav pill. One red, used consistently, reads as brand. A gradient here made it
 * a fourth colour competing with everything below it.
 */
.tt-promo {
	/* accent-dark (not accent) so #fff text clears WCAG AA 4.5:1 — the bright
	   accent only reaches 3.65:1, which fails for small text. */
	background: var(--tt-accent-dark);
	color: #fff;
	font-size: var(--tt-fs-sm);
	font-weight: 600;
}

.tt-promo-hidden .tt-promo {
	display: none;
}

.tt-promo__inner {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--tt-s3);
	min-height: 34px;
	position: relative;
}

.tt-promo__text {
	color: #fff;
}

a.tt-promo__text:hover {
	color: #fff;
	text-decoration: underline;
}

.tt-promo__close {
	position: absolute;
	right: 0;
	display: grid;
	place-items: center;
	width: 28px;
	height: 28px;
	border-radius: var(--tt-radius-sm);
	color: #fff;
	font-size: 20px;
	line-height: 1;
	opacity: 0.75;
	transition: opacity var(--tt-speed);
}

.tt-promo__close:hover {
	opacity: 1;
	color: #fff;
}

/* ---------------------------------------------------------------- header */

.tt-header {
	/*
	 * Hide-on-scroll ("headroom") header: sticky at the top, slides up out of
	 * view while scrolling DOWN and drops back in on scroll UP — out of the way
	 * but one flick away (owner request). The stickyHeader module in main.js
	 * toggles .is-hidden by watching scroll direction. Sticky, not fixed, so it
	 * keeps its slot in the flow and needs no spacer. Relies on body
	 * overflow-x: clip (base.css) — overflow-x: hidden would silently kill sticky.
	 */
	position: sticky;
	top: 0;
	z-index: 50;
	background: var(--tt-bg-surface);
	border-bottom: 1px solid var(--tt-border);
	transition: transform 0.28s ease;
	will-change: transform;
}

/* Scrolled down: lift the whole bar (border included) clear of the viewport. */
.tt-header.is-hidden {
	transform: translateY(-100%);
}

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

.tt-header__bar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--tt-s2) var(--tt-s3);
	min-height: 64px; /* room for the 40px logo */
}

.tt-header__brand {
	flex: 0 0 auto;
	color: var(--tt-text);
	white-space: nowrap;
}

/*
 * ⚠️ HEIGHT ALONE DOES NOT BOUND A LOGO — the ASPECT RATIO decides its width,
 * and the artwork can change shape under you.
 *
 * The 2026-07-22 logo is 640×111 (5.77:1), where the one it replaced was
 * 295×83 (3.55:1). Same 50px height, but 178px wide became 288px wide — an
 * extra 110px that nothing in the bar had reserved. On a 360px phone the bar
 * has 336px of usable width (gutter resolves to 12px there), so logo + theme
 * toggle + burger + gaps came to ~380px. `.tt-header__bar` is `flex-wrap: wrap`,
 * so that does not overflow sideways like the v0.2.1 bug — it silently WRAPS,
 * dropping the toggle and burger onto a second row and making the header
 * two rows tall on every phone.
 *
 * So the small-screen sizes below are a constraint, not styling: they trade
 * height for width so the row keeps fitting. Re-check them if the artwork's
 * ratio ever changes again.
 */
.tt-header__brand img {
	height: 40px; /* ≈231px wide at the 5.77:1 artwork. Was 50px/288px, which was
	                  chosen against a 1600px container; at --tt-maxw 1280 that same
	                  288px grew from 18% to 22.5% of the bar and read oversized. A
	                  logo is an IMAGE with an explicit height, so neither --tt-maxw
	                  nor the type scale touches it — resize it by hand whenever the
	                  container changes. */
	width: auto;
}

/* < 768px: menu is already in the burger, so the bar is logo + toggle + burger. */
@media (max-width: 767px) {
	.tt-header__brand img {
		height: 40px; /* ≈231px wide */
	}
}

/* < 480px: the tightest real viewport (360px) — leaves ~59px of slack. */
@media (max-width: 479px) {
	.tt-header__brand img {
		height: 32px; /* ≈185px wide */
	}
}

/*
 * Two logo files, one per theme. The artwork carries its own text colour (dark
 * text for light mode, white text for dark), so we swap the whole image on
 * [data-theme] rather than recolouring one. Default shows the light one; the
 * dark one takes over under [data-theme='dark'].
 */
.tt-logo {
	display: block;
}

.tt-logo--dark {
	display: none;
}

[data-theme='dark'] .tt-logo--light {
	display: none;
}

[data-theme='dark'] .tt-logo--dark {
	display: block;
}

/*
 * Two-tone wordmark. See tt_split_wordmark().
 *
 * A single-weight text brand is the dullest thing a header can have — this is
 * how the references read as a logo without one existing.
 */
.tt-mark {
	font-size: var(--tt-fs-xl);
	font-weight: 900;
	letter-spacing: -0.03em;
	line-height: 1;
}

.tt-mark__a {
	color: var(--tt-text);
}

.tt-mark__b {
	/* Gradient text — falls back to a flat accent fill where unsupported */
	color: var(--tt-accent);
	background: var(--tt-grad);
	-webkit-background-clip: text;
	background-clip: text;
	-webkit-text-fill-color: transparent;
}

.tt-header__brand:hover .tt-mark__a {
	color: var(--tt-accent);
}

/* ---------------------------------------------------------------- header stat */

/*
 * Library size. A real number — see header.php for why we don't fake a live
 * "watching" counter the way the references do.
 */
.tt-stat {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 5px var(--tt-s3);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-pill);
	background: var(--tt-bg-raised);
	color: var(--tt-text-muted);
	font-size: var(--tt-fs-xs);
	white-space: nowrap;
}

/*
 * ⚠️ Load-bearing, not redundant.
 *
 * The counter ships with the `hidden` attribute and main.js removes it once a
 * real number arrives. But `display: inline-flex` above is an AUTHOR rule, and
 * author rules beat the browser's built-in `[hidden] { display: none }` no
 * matter the specificity — so without this line the pill renders anyway, empty,
 * on every page where the count never loads.
 */
.tt-stat[hidden] {
	display: none;
}

.tt-stat strong {
	color: var(--tt-text);
	font-weight: 800;
	font-variant-numeric: tabular-nums;
}

/* The one place green earns its keep — reads as "live/available" */
.tt-stat__dot {
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: var(--tt-live);
	box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18);
}

@media (max-width: 767px) {
	.tt-stat {
		display: none;
	}
}

/* ---------------------------------------------------------------- search */

/*
 * Restored in this version after 0.8.16 removed it. Read the long note at the
 * top of template-parts/search-form.php first — the icon-that-expands shape is
 * forced by the header's width budget, not chosen for looks. A 240px field in
 * this row wraps the bar to two lines between 1200px and 1440px.
 *
 * ⚠️ min-width: 0 on the wrapper AND the input. A flex item's floor is its
 * content's intrinsic width, and an input gets its intrinsic width from `size`
 * (default 20 chars ≈ 180px), so `flex-shrink` alone is a lie. This exact
 * omission pushed the whole mobile header sideways in v0.2.1.
 */
.tt-search-wrap {
	position: relative;
	flex: 0 0 auto;
	min-width: 0;
}

.tt-search__toggle {
	display: grid;
	place-items: center;
	width: 34px;
	height: 34px;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-pill);
	background: var(--tt-bg-raised);
	color: var(--tt-text);
	line-height: 1;
	transition: border-color var(--tt-speed), color var(--tt-speed);
}

.tt-search__toggle:hover {
	border-color: var(--tt-accent);
	color: var(--tt-accent);
}

.tt-search__toggle svg {
	display: block;
}

.tt-search {
	display: flex;
	align-items: center;
	gap: 4px;
	min-width: 0;
	padding: 0 4px 0 var(--tt-s2);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-pill);
	background: var(--tt-bg-raised);
}

.tt-search:focus-within {
	border-color: var(--tt-accent);
}

.tt-search__input {
	/* Fills the form; the FORM owns the width so one rule sizes the overlay. */
	flex: 1 1 auto;
	min-width: 0;
	height: 32px;
	border: 0;
	background: none;
	color: var(--tt-text);
	/*
	 * ⚠️ 16px, not 14px. iOS Safari ZOOMS the whole page when a font-size under
	 * 16px input takes focus, and it does not zoom back out — on a phone that
	 * reads as the site breaking the moment you tap search. Now that this field is
	 * in the bar on mobile too (0.29.3) that applies to every visitor, not just
	 * whoever opened the menu panel.
	 */
	font-size: 16px;
}

/* Chrome's built-in clear button sits on a background we don't control. */
.tt-search__input::-webkit-search-cancel-button {
	filter: grayscale(1);
	opacity: 0.5;
}

.tt-search__input::placeholder {
	color: var(--tt-text-muted);
}

.tt-search__input:focus {
	outline: none;
}

.tt-search__submit {
	flex: 0 0 auto;
	display: grid;
	place-items: center;
	width: 26px;
	height: 26px;
	border-radius: 50%;
	color: var(--tt-text-muted);
	transition: color var(--tt-speed);
}

.tt-search__submit:hover {
	color: var(--tt-accent);
}

.tt-search__submit svg {
	display: block;
}

/*
 * WITH JS the field is an overlay anchored to the right edge of its 34px button,
 * so it costs the flex row 34px instead of ~240px — which is what lets ONE
 * instance serve every width, phones included (0.29.3). Scoped to .tt-js so the
 * no-JS fallback stays a plain visible form (see the template comment).
 */
.tt-js .tt-search-wrap .tt-search {
	position: absolute;
	top: calc(100% + var(--tt-s2));
	right: 0;
	z-index: 60;
	box-shadow: var(--tt-shadow);
	/*
	 * ⚠️ NARROWS TO THE VIEWPORT. Anchored right and 280px wide, this would run off
	 * the LEFT edge on a 320px screen once the gutter is counted — and because the
	 * overflow is off-screen to the left, `overflow-x: clip` on body hides it
	 * rather than showing a scrollbar, so the field would simply be cut in half
	 * with nothing to indicate why. min() keeps it inside the viewport instead.
	 */
	width: min(280px, calc(100vw - 2 * var(--tt-s4)));
	/*
	 * ⚠️ CLOSED IN CSS, not by JS on init. `tt-js` is set by the pre-paint inline
	 * script in header.php, so this rule applies on the FIRST paint — the field is
	 * never briefly an inline 240px flex item, which is what wrapped the header to
	 * two rows for a frame. An `is-open` state class rather than [hidden] because
	 * the attribute cannot exist before JS runs, and `:not([hidden])` would
	 * therefore read as "open" pre-paint — the exact flash this avoids.
	 */
	display: none;
}

.tt-js .tt-search-wrap .tt-search.is-open {
	display: flex;
}

/* ---------------------------------------------------------------- theme toggle */

.tt-theme-toggle {
	flex: 0 0 auto;
	display: grid;
	place-items: center;
	width: 34px;
	height: 34px;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-pill);
	background: var(--tt-bg-raised);
	color: var(--tt-text);
	font-size: var(--tt-fs-base);
	line-height: 1;
	transition: border-color var(--tt-speed), color var(--tt-speed);
}

.tt-theme-toggle:hover {
	border-color: var(--tt-accent);
	color: var(--tt-accent);
}

/*
 * Show exactly one glyph, driven off [data-theme] on <html> so the icon can
 * never disagree with what the inline pre-paint script decided.
 *
 * Both hidden by default: if JS never runs, we show neither rather than both.
 */
.tt-theme-toggle__dark,
.tt-theme-toggle__light {
	display: none;
}

[data-theme='dark'] .tt-theme-toggle__dark {
	display: block;
}

[data-theme='light'] .tt-theme-toggle__light {
	display: block;
}

/* Block removes the inline-svg baseline gap so the icon sits dead-centre. */
.tt-theme-toggle svg {
	display: block;
}

/* ---------------------------------------------------------------- burger */

.tt-burger {
	flex: 0 0 auto;
	/*
	 * First in the DOM (so it opens tab order), but pushed to the far right of the
	 * bar on mobile — owner wants it right of the theme toggle. `order` moves it
	 * past the search + toggle cluster; below 1200px it is .tt-theme-toggle's
	 * margin-left:auto that pins that cluster (search · toggle · burger) right.
	 * Irrelevant at >=1200px where the burger is display:none.
	 */
	order: 10;
	display: grid;
	place-items: center;
	width: 36px;
	height: 36px;
	border-radius: var(--tt-radius-sm);
	color: var(--tt-text);
}

.tt-burger__box,
.tt-burger__box::before,
.tt-burger__box::after {
	display: block;
	width: 18px;
	height: 2px;
	border-radius: 2px;
	background: currentColor;
}

.tt-burger__box {
	position: relative;
}

.tt-burger__box::before,
.tt-burger__box::after {
	content: '';
	position: absolute;
	left: 0;
}

.tt-burger__box::before {
	top: -6px;
}

.tt-burger__box::after {
	top: 6px;
}

/*
 * ⚠️ 1200px IS A MEASURED THRESHOLD, NOT A ROUND NUMBER — and it is one of
 * THREE rules that must move together (.tt-burger here, .tt-hnav below, and
 * .tt-mobile-menu further down). Split them and you get either both menus at
 * once or neither.
 *
 * Raised from 900px in v0.21.5. `.tt-header__bar` is `flex-wrap: wrap`, so when
 * the row runs out of width it does NOT overflow sideways like the v0.2.1 bug —
 * it silently WRAPS, and the only symptom is a header that is quietly two rows
 * tall (72px → 92px). Measured at 1040px viewport, the row carries FOUR visible
 * items, not three:
 *
 *     logo 288 + menu 628 + live-visitor .tt-stat 129 + toggle 34 + gaps 36
 *       = 1115px of content, so it needs a 1163px viewport (24px gutter × 2).
 *
 * ⚠️ THE `.tt-stat` COUNTER IS THE ITEM EVERY ESTIMATE FORGETS. Leave it out and
 * the arithmetic says the bar fits in ~975px, which is how the old breakpoint
 * looked defensible.
 *
 * ⚠️ This did NOT start with the 2026-07-22 logo. Verified by forcing the OLD
 * 295×83 artwork (178px wide) back in at 900px with the menu inline: the bar
 * STILL wrapped to 92px. The 900px breakpoint had been ~260px too low all along
 * and the header was two rows on every 900–1160px screen; the wider 5.77:1 logo
 * only stretched that band. So this is a latent bug being fixed, not a
 * regression being patched.
 *
 * Below this the burger takes over, and its panel already lists every route.
 */
@media (min-width: 1200px) {
	.tt-burger {
		display: none;
	}
}

/* ---------------------------------------------------------------- inline nav */

/*
 * Menu inline in the header bar, CENTRED (owner request). `margin-inline:auto`
 * puts an equal auto-margin on each side, so the menu sits centred between the
 * logo and the right cluster (its right auto also pins the theme toggle right).
 * Below 1200px the menu is hidden — see the mobile rule that then pins the toggle
 * right instead. Bold, full-strength labels; active route is accent.
 */
.tt-hnav {
	flex: 0 0 auto;
	margin-inline: auto;
}

/*
 * Item spacing (owner request, v0.21.1). Was `gap: 2px` with 8px link padding,
 * which at the 16px type of v0.20.0 left the labels almost touching.
 *
 * The gap and the padding do different jobs, so both moved: padding is the
 * hover pill's own breathing room, the gap is the space BETWEEN pills. Raising
 * only the padding would have widened the hover targets until they merged into
 * one bar; raising only the gap would leave each pill hugging its own text.
 */
.tt-hnav__list {
	display: flex;
	align-items: center;
	gap: var(--tt-s3);
}

.tt-hnav__link {
	display: inline-flex;
	align-items: center;
	padding: 5px var(--tt-s3);
	border-radius: var(--tt-radius-sm);
	color: var(--tt-text);
	/*
	 * ⚠️ Owner-set size, changed twice. 13px since v0.8.14 → 16px by owner
	 * request in v0.20.0 → 14px in 0.35.6, again at his request, after the
	 * container dropped to 1280 and the menu was the one thing left that had
	 * not scaled with it. Do not "restore" 16px as a fix; it is his call both
	 * times.
	 */
	font-size: var(--tt-fs-md); /* 14px. Was a literal 16px — see the note in tokens.css. */
	font-weight: 700;
	white-space: nowrap;
	transition: background var(--tt-speed), color var(--tt-speed);
}

.tt-hnav__link:hover {
	background: var(--tt-bg-raised);
	color: var(--tt-text);
}

.tt-hnav__link.is-active {
	color: var(--tt-accent);
}

/* Pairs with the .tt-burger rule above — 1199/1200 must stay adjacent. */
@media (max-width: 1199px) {
	/* Burger takes over — its panel already lists these routes + categories. */
	.tt-hnav {
		display: none;
	}

	/*
	 * With the menu hidden its centring auto-margins vanish, so nothing would pin
	 * the right cluster. An auto margin on its FIRST visible member does it: that
	 * item absorbs the free space to its left, pushing the whole cluster (search ·
	 * stat · toggle · burger at order:10) to the right edge.
	 *
	 * ⚠️ ONLY ONE ITEM MAY CARRY THIS. Two auto margins SHARE the free space
	 * equally, which splits the cluster in half and strands a gap mid-bar. Search
	 * is the first cluster member at every width below 1200px (0.29.3 — it no
	 * longer drops out on phones), so it is the one, and `.tt-theme-toggle` must
	 * NOT get an auto margin back.
	 */
	.tt-search-wrap {
		margin-left: auto;
	}
}

/* ---------------------------------------------------------------- header @ phone */

/*
 * Phones. Search STAYS in the bar here (owner, 0.29.3) — it can, because what
 * sits in the row is a 34px icon, not a text field. The field opens as an
 * overlay below the bar and narrows to the viewport, so it never contributes
 * width to the row and the v0.2.1 sideways-overflow has nothing to act on.
 *
 *   [ brand ................. search  toggle  burger ]
 */
@media (max-width: 639px) {
	.tt-header__bar {
		gap: var(--tt-s2);
		padding-bottom: var(--tt-s2);
	}

	.tt-mark {
		font-size: 16px;
	}
}

/* ---------------------------------------------------------------- mobile menu */

.tt-mobile-menu {
	position: fixed;
	inset: 0;
	z-index: 100;
	background: rgba(0, 0, 0, 0.6);
}

/*
 * No-JS / no-CSS fallback: stay fully hidden. The animated open below is scoped
 * to .tt-js (main.js adds it), so if the stylesheet or script never loads the
 * panel can't slide into view on its own — it just stays gone.
 */
.tt-mobile-menu[hidden] {
	display: none;
}

/*
 * With JS the panel SLIDES IN FROM THE RIGHT, so it must stay in layout to
 * animate — display:none can't transition. Hidden state fades the backdrop and
 * parks the panel off the right edge; visibility flips after the fade so the
 * off-screen panel isn't focusable or clickable.
 */
.tt-js .tt-mobile-menu {
	opacity: 1;
	visibility: visible;
	transition: opacity var(--tt-speed);
}

.tt-js .tt-mobile-menu[hidden] {
	display: block;
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	transition: opacity var(--tt-speed), visibility 0s var(--tt-speed);
}

.tt-mobile-menu__panel {
	position: absolute;
	inset: 0 0 0 auto; /* pinned to the RIGHT */
	width: min(84vw, 340px);
	overflow-y: auto;
	background: var(--tt-bg-surface);
	border-left: 1px solid var(--tt-border);
	box-shadow: -12px 0 32px rgba(0, 0, 0, 0.28);
	transform: translateX(0);
	transition: transform var(--tt-speed);
}

/* Closed: slid a full panel-width off the right edge. */
.tt-js .tt-mobile-menu[hidden] .tt-mobile-menu__panel {
	transform: translateX(100%);
}

/*
 * Brand + close bar. Sticky (top:0 inside the scrolling panel) so the logo and
 * the close button stay reachable no matter how far down the category list you
 * scroll — the old absolute close scrolled away with the content.
 */
.tt-mobile-menu__head {
	position: sticky;
	top: 0;
	z-index: 1;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tt-s3);
	padding: var(--tt-s3) var(--tt-s4);
	background: var(--tt-bg-surface);
	border-bottom: 1px solid var(--tt-border);
}

.tt-mobile-menu__brand {
	display: inline-flex;
	align-items: center;
	min-width: 0;
}

.tt-mobile-menu__brand .tt-logo {
	height: 32px;
	width: auto;
}

.tt-mobile-menu__brand .tt-mark {
	font-size: 20px;
	font-weight: 800;
	letter-spacing: -0.01em;
}

.tt-mobile-menu__close {
	flex: none;
	display: grid;
	place-items: center;
	width: 40px;
	height: 40px;
	margin-right: calc(-1 * var(--tt-s2)); /* optically align the X to the edge */
	color: var(--tt-text-muted);
	border-radius: var(--tt-radius-sm);
	transition: color var(--tt-speed), background var(--tt-speed);
}

.tt-mobile-menu__close:hover {
	color: var(--tt-text);
	background: var(--tt-bg-raised);
}

.tt-mobile-menu__nav {
	padding: var(--tt-s3) var(--tt-s4) var(--tt-s5);
}

.tt-mobile-menu__title {
	display: flex;
	align-items: center;
	gap: var(--tt-s2);
	margin: var(--tt-s5) 0 var(--tt-s2);
	font-size: var(--tt-fs-2xs);
	font-weight: 800;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--tt-text-muted);
}

/* Small accent tick before the label — another cue this block isn't the menu. */
.tt-mobile-menu__title::before {
	content: '';
	width: 14px;
	height: 2px;
	border-radius: 2px;
	background: var(--tt-accent);
}

.tt-mobile-menu__list {
	margin: 0;
	padding: 0;
	list-style: none;
}

.tt-mobile-menu__list a {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tt-s3);
	min-height: 48px; /* comfortable thumb target */
	padding: var(--tt-s2) 0;
	border-bottom: 1px solid var(--tt-border);
	font-size: var(--tt-fs-base);
	font-weight: 600;
	color: var(--tt-text);
	transition: color var(--tt-speed);
}

/* No doubled hairline where a list meets the next section or the panel edge. */
.tt-mobile-menu__list li:last-child a {
	border-bottom: none;
}

.tt-mobile-menu__list a:hover,
.tt-mobile-menu__list a:active,
.tt-mobile-menu__list a.is-active {
	color: var(--tt-accent);
}

.tt-mobile-menu__list a.is-active {
	font-weight: 800;
}

.tt-mobile-menu__name {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/*
 * Popular-categories block — deliberately styled UNLIKE the menu above it: a
 * raised, rounded, bordered card with tighter 14px rows and softer inner
 * dividers, so it reads as a distinct "browse" group rather than more nav.
 */
.tt-mobile-menu__list--cats {
	padding: 0 var(--tt-s3);
	background: var(--tt-bg-raised);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius);
	overflow: hidden;
}

.tt-mobile-menu__list--cats a {
	min-height: 44px;
	font-size: var(--tt-fs-md);
	font-weight: 600;
	/* Softer than the menu's full-strength hairline — it's inside a card now. */
	border-bottom-color: color-mix(in srgb, var(--tt-border) 55%, transparent);
}


.tt-mobile-menu__all {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	margin-top: var(--tt-s4);
	font-size: var(--tt-fs-sm);
	font-weight: 700;
	color: var(--tt-accent);
	transition: gap var(--tt-speed);
}

.tt-mobile-menu__all:hover {
	gap: 11px;
}

/* Third of the three coupled header breakpoints — see the .tt-burger note. */
@media (min-width: 1200px) {
	/*
	 * The .tt-js hidden state above forces display:block to keep the panel
	 * animatable; this must out-specify it on desktop so the menu is truly gone,
	 * not just invisible. Same specificity, later in source → wins.
	 */
	.tt-mobile-menu,
	.tt-js .tt-mobile-menu[hidden] {
		display: none;
	}
}

/* Respect reduced-motion: show/hide instantly, no slide. */
@media (prefers-reduced-motion: reduce) {
	.tt-js .tt-mobile-menu,
	.tt-js .tt-mobile-menu[hidden],
	.tt-mobile-menu__panel {
		transition: none;
	}
}

/* ----------------------------------------------------------------- 404 */

/*
 * The dead-end hero. Centred column, capped narrow, because this is the ONE
 * place on the site where a reading-width cap is right: it is three short
 * centred lines, not a prose column, so the cap reads as composition rather
 * than as the cropped text that got reverted three times elsewhere.
 *
 * ⚠️ COMPACT, NOT FULL-VIEWPORT. v0.27.1 pinned this to calc(100dvh - 190px)
 * and centred it inside that, and it looked broken: a small island of content
 * marooned in a screen of empty black, with the grid shoved out of sight. Owner
 * rejected it on sight. The hero is a joke you read in two seconds, so it wants
 * to sit right on top of the way out, not hold a whole screen on its own.
 * Do not reintroduce a viewport-height rule here.
 */
.tt-404 {
	max-width: 620px;
	margin: 0 auto;
	padding: var(--tt-s5) 0 var(--tt-s6);
	text-align: center;
}

/*
 * aspect-ratio + object-fit, not a bare max-width, so swapping the image for a
 * differently-proportioned one later cannot change the page's layout. The
 * rounded corners and border match the video cards; a bare rectangle floating
 * in the middle of the page looked like a broken embed.
 */
.tt-404__img {
	display: block;
	width: 100%;
	max-width: 320px;
	aspect-ratio: 4 / 3;
	margin: 0 auto var(--tt-s5);
	object-fit: cover;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius);
}

.tt-404__title {
	margin: 0 0 var(--tt-s3);
	font-size: clamp(1.75rem, 6vw, var(--tt-fs-5xl));
	line-height: 1.15;
}

.tt-404__lead {
	margin: 0 auto var(--tt-s5);
	max-width: 46ch;
	color: var(--tt-text-muted);
	font-size: clamp(var(--tt-fs-base), 2.2vw, var(--tt-fs-xl));
	line-height: 1.6;
}

/*
 * The handover line to the grid below. Smaller and quieter than the lead: it is
 * a label for what follows, not part of the joke.
 */
.tt-404__sub {
	margin: 0;
	color: var(--tt-text);
	font-size: var(--tt-fs-md);
	font-weight: 600;
}

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

.tt-page-title {
	margin: var(--tt-s5) 0 var(--tt-s4);
	font-size: clamp(1.25rem, 3vw, var(--tt-fs-4xl));
}

/*
 * Archive / search heading row: title on the left, the count badge pushed to the
 * right edge (margin-left:auto) so it reads as a stat about the heading rather
 * than a caption stacked beneath it. The row owns the vertical margins; the title
 * and count both reset to 0 inside it. Title gets min-width:0 so a long term name
 * can shrink instead of shoving the fixed-width pill off the row.
 */
.tt-page-head {
	display: flex;
	align-items: center;
	gap: var(--tt-s4);
	margin: var(--tt-s5) 0 var(--tt-s4);
}

.tt-page-head .tt-page-title {
	margin: 0;
	min-width: 0;
}

.tt-page-head .tt-count {
	margin: 0;
	margin-left: auto;
	flex: 0 0 auto;
}

.tt-page-subtitle {
	margin: calc(var(--tt-s4) * -1) 0 var(--tt-s4);
	color: var(--tt-text-muted);
	font-size: var(--tt-fs-md);
}

/*
 * Video count beside an archive / search heading — "87 videí".
 *
 * A SOLID filled pill in the count-blue tokens.css reserves for stats, not a pale
 * tinted outline — the outline version read as washed-out and flimsy next to a bold
 * H1. Solid fill + white text gives it the same visual weight as the title it sits
 * beside. Its own class, NOT .tt-page-subtitle, because that class also carries
 * plain fallback sentences ("Nic jsme nenašli…") that must stay sentences.
 *
 * inline-flex so the pill hugs its text; the negative top margin only applies when
 * it is NOT inside .tt-page-head (which zeroes it and lays it out in a row).
 */
.tt-count {
	display: inline-flex;
	align-items: baseline;
	gap: 6px;
	margin: calc(var(--tt-s2) * -1) 0 var(--tt-s5);
	padding: 8px 16px;
	border-radius: var(--tt-radius-pill);
	background: var(--tt-info);
	color: var(--tt-accent-contrast);
	font-size: var(--tt-fs-sm);
	font-weight: 700;
	line-height: 1;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	box-shadow: 0 2px 10px color-mix(in srgb, var(--tt-info) 35%, transparent);
}

.tt-count strong {
	color: var(--tt-accent-contrast);
	font-size: var(--tt-fs-lg);
	font-weight: 800;
	font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------- pagination */

/*
 * WordPress emits .navigation.pagination with its own class names — we can't
 * tt- prefix those, so they're scoped under .tt-container to avoid leaking.
 */
.tt-container .navigation.pagination {
	margin: var(--tt-s6) 0 var(--tt-s5);
}

/* Centred strip — a left-aligned row of chips looks stranded under a full grid.
   Mobile-first: one row, never wrapping. The prev/next words are dropped below
   600px (see the font-size:0 rule) so the whole strip fits a 360px viewport. */
.tt-container .nav-links {
	display: flex;
	flex-wrap: nowrap;
	gap: var(--tt-s1);
	align-items: center;
	justify-content: center;
}

.tt-container .page-numbers {
	display: inline-grid;
	place-items: center;
	min-width: 34px;
	height: 34px;
	padding: 0 var(--tt-s2);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius);
	background: var(--tt-bg-surface);
	color: var(--tt-text);
	font-size: var(--tt-fs-md);
	font-weight: 700;
	font-variant-numeric: tabular-nums;
	transition: color var(--tt-speed), border-color var(--tt-speed),
		background var(--tt-speed), transform var(--tt-speed),
		box-shadow var(--tt-speed);
}

/* Hover lift only on the links — the current page and the dots aren't clickable */
.tt-container a.page-numbers:hover {
	border-color: var(--tt-accent);
	color: var(--tt-accent);
	transform: translateY(-2px);
	box-shadow: var(--tt-shadow);
}

.tt-container .page-numbers.current {
	background: var(--tt-accent);
	border-color: var(--tt-accent);
	color: var(--tt-accent-contrast);
	box-shadow: var(--tt-shadow-accent);
}

.tt-container .page-numbers.dots {
	min-width: 18px;
	padding: 0;
	border-color: transparent;
	background: none;
	color: var(--tt-text-muted);
}

/*
 * Prev / Next carry a word ("Předchozí" / "Další") and a directional chevron.
 * They stay neutral chips — the solid accent is the current page's alone, so the
 * eye reads "where am I" before "where next".
 *
 * ⚠️ On mobile the two words are what forced the strip onto two rows. Below
 * 600px they collapse to the chevron alone: `font-size: 0` kills the text node
 * (there is no element around it to hide), and the pseudo-elements re-declare
 * their own size so the chevrons survive it.
 */
.tt-container .page-numbers.prev,
.tt-container .page-numbers.next {
	/* inline-flex, not the inherited inline-grid — a single-column grid would
	   stack the chevron pseudo-element above the word instead of beside it. */
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* ⚠️ gap MUST be 0 while the label is collapsed. `font-size: 0` makes the
	   word zero-WIDTH but it is still a flex item, so any gap sits between the
	   chevron and that empty item and shoves the glyph off-centre. The gap comes
	   back in the 600px block, where there is a real word to separate. */
	gap: 0;
	padding: 0;
	font-size: 0;
	background: var(--tt-bg-raised);
}

.tt-container .page-numbers.prev::before {
	content: "‹";
	font-size: var(--tt-fs-xl);
	line-height: 1;
}

.tt-container .page-numbers.next::after {
	content: "›";
	font-size: var(--tt-fs-xl);
	line-height: 1;
}

/* From 600px there is room for the words and full-size chips again. */
@media (min-width: 600px) {
	.tt-container .nav-links {
		flex-wrap: wrap;
		gap: var(--tt-s2);
	}

	.tt-container .page-numbers {
		min-width: 42px;
		height: 42px;
		padding: 0 var(--tt-s3);
	}

	.tt-container .page-numbers.dots {
		min-width: 24px;
		padding: 0;
	}

	.tt-container .page-numbers.prev,
	.tt-container .page-numbers.next {
		gap: var(--tt-s2);
		padding: 0 var(--tt-s4);
		font-size: var(--tt-fs-md);
	}
}

/* ---------------------------------------------------------------- archive */

/*
 * Archive SEO copy, parked BELOW the grid + pagination (see archive.php).
 * Down here it needs the same top-border separator the homepage's .tt-seo block
 * uses — otherwise the prose reads as orphaned text floating under the pagination.
 *
 * This holds ~650 words now (hand-written half + the generated half from
 * term description), not the footnote paragraph it was built for, so it reads
 * at body size rather than 14px muted.
 *
 * ⚠️ NO max-width here. A ~70ch reading cap on a full-width column has been
 * reported as "cropped text" and reverted three times in this theme
 * (.tt-single__content, .tt-page, .tt-article__body). The container is already
 * capped; the prose fills it.
 */
.tt-archive-desc {
	margin: var(--tt-s5) 0 var(--tt-s6);
	padding-top: var(--tt-s5);
	border-top: 1px solid var(--tt-border);
	color: var(--tt-text);
	font-size: var(--tt-fs-base);
	line-height: 1.7;
}

.tt-archive-desc p {
	margin: 0 0 var(--tt-s3);
}

.tt-archive-desc p:last-child {
	margin-bottom: 0;
}

/*
 * Headings. There were none before, so an <h2> inherited the block's context and
 * rendered as barely-emphasised body text — invisible as structure to a reader
 * and weak as an outline to a crawler.
 */
.tt-archive-desc h2 {
	margin: var(--tt-s5) 0 var(--tt-s3);
	color: var(--tt-text);
	font-size: var(--tt-fs-xl);
	font-weight: 800;
	line-height: 1.3;
}

.tt-archive-desc h3 {
	margin: var(--tt-s4) 0 var(--tt-s2);
	font-size: var(--tt-fs-lg);
	font-weight: 700;
}

.tt-archive-desc a {
	color: var(--tt-accent);
}

.tt-archive-desc a:hover {
	text-decoration: underline;
}

/* ---- read-more ---- */

/*
 * .tt-readmore lives in single.css, which does NOT load on archives — so the two
 * rules the shared [data-readmore] JS depends on are repeated here rather than
 * pulling a whole stylesheet in for them. Clamped at 8 lines, not single.css's 4:
 * this block is an order of magnitude longer, and a 4-line peek reads as broken.
 */
.tt-archive-desc .tt-readmore__body.is-collapsed {
	display: -webkit-box;
	-webkit-line-clamp: 8;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.tt-archive-desc .tt-readmore__toggle {
	margin-top: var(--tt-s3);
	padding: 6px var(--tt-s3);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-pill);
	background: var(--tt-bg-surface);
	color: var(--tt-accent);
	font-size: var(--tt-fs-sm);
	font-weight: 700;
	cursor: pointer;
	transition: border-color var(--tt-speed);
}

.tt-archive-desc .tt-readmore__toggle:hover {
	border-color: var(--tt-accent);
}

/*
 * Homepage SEO block — same shared [data-readmore] JS, DIFFERENT clamp.
 *
 * ⚠ IT CANNOT USE -webkit-line-clamp LIKE THE ARCHIVE ONE ABOVE. .tt-seo__text
 * is `column-count: 2` from 900px up (components.css), and line-clamp needs
 * `display: -webkit-box`, which destroys multi-column layout outright — the two
 * columns collapse into one. So this clamps by HEIGHT instead, which columns
 * respect: the text keeps flowing into two columns and both are simply shorter.
 *
 * 14em against the block's own line-height of 1.75 is 8 lines, matching the
 * archive clamp. Paragraph margins mean the cut can land mid-line, so the mask
 * fades the last line out — without it a half-sliced line reads as a rendering
 * bug rather than as "there is more below".
 *
 * The mask must be dropped when expanded, or the last line of the FULL text
 * stays faded; .is-collapsed carries it, the base rule does not.
 */
/*
 * ⚠ The em here resolves against THIS element, not against the text inside it.
 * .tt-readmore__body is a bare wrapper, so without these two declarations it
 * would inherit the base 1rem and 14em would clamp to roughly 224px instead of
 * the 8 lines intended — the copy is set in --tt-fs-sm. Matching the inner
 * element's type here keeps "14em == 8 lines" true. If you change the block's
 * font-size or line-height, change it in BOTH places or the clamp drifts.
 */
.tt-seo .tt-readmore__body {
	font-size: var(--tt-fs-sm);
	line-height: 1.75;
}

.tt-seo .tt-readmore__body.is-collapsed {
	max-height: 14em;
	overflow: hidden;
	-webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
	mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
}

.tt-seo .tt-readmore__toggle {
	margin-top: var(--tt-s3);
	padding: 6px var(--tt-s3);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-pill);
	background: var(--tt-bg-surface);
	color: var(--tt-accent);
	font-size: var(--tt-fs-sm);
	font-weight: 700;
	cursor: pointer;
	transition: border-color var(--tt-speed);
}

.tt-seo .tt-readmore__toggle:hover {
	border-color: var(--tt-accent);
}

/* ---------------------------------------------------------------- ads */

/*
 * Ad slots render nothing when empty (see inc/ads.php), so these styles only
 * ever apply to a slot with real code in it — no reserved blank boxes.
 */
.tt-ad {
	margin: var(--tt-s4) 0;
	text-align: center;

	/*
	 * ⚠ CONTAINMENT IS THE POINT OF THIS BLOCK — do not "simplify" it away.
	 * JuicyAds creatives are third-party HTML we do not control and they ship
	 * their own positioning and z-index (commonly position: fixed with
	 * z-index: 2147483647). Without containment they escaped the slot and
	 * painted OVER the sticky header, the open mobile drawer and the search
	 * panel — reported 2026-09-13 with five screenshots.
	 *
	 * `contain: layout paint` is the fix and it does two separate jobs:
	 *   - it makes .tt-ad the containing block for position: fixed descendants,
	 *     so a "fixed" creative pins to the SLOT, not to the viewport;
	 *   - it clips painting to the slot box, so nothing bleeds outside it.
	 * `isolation: isolate` + z-index: 0 caps the stacking context, so however
	 * large an inner z-index is it can never beat the header (50), the search
	 * panel (60) or the mobile drawer (100).
	 * Raising OUR z-index would be the wrong fix — the ad must stay BELOW the
	 * chrome, not win against it.
	 */
	position: relative;
	z-index: 0;
	isolation: isolate;
	contain: layout paint;
	max-width: 100%;
	overflow: hidden;
}

.tt-ad img,
.tt-ad iframe {
	max-width: 100%;
	margin-inline: auto;
}

/*
 * Reserve each zone's configured height. Required BECAUSE of the containment
 * above: a creative that positions itself out of flow leaves the slot at zero
 * height, and a clipping box of zero height would hide the ad completely. The
 * numbers mirror the JuicyAds zone dimensions (data-height), so a well-behaved
 * in-flow banner fills the box exactly and reserves nothing extra. An empty
 * slot renders no markup at all (inc/ads.php), so this never leaves a blank box.
 */
.tt-ad--header {
	min-height: 100px;
}

.tt-ad--below-player,
.tt-ad--footer {
	min-height: 250px;
}

.tt-ad--footer {
	margin-block: var(--tt-s6);
}

/* ---------------------------------------------------------------- footer */

/*
 * DARK footer band with white, bolder text. Three columns (brand + tagline ·
 * Procházet · Informace) so it carries weight instead of reading as a thin strip.
 *
 * ⚠️ WAS A SOLID RED ACCENT BAND from v0.8.0 until v0.22.0. It changed because
 * of the logo, and the reasoning is worth keeping: the logo artwork CONTAINS
 * the brand red, so on a red band the word "PORNO" measured **1.01:1** against
 * it and simply vanished. The only way to render the logo at all was to force
 * it pure white with `brightness(0) invert(1)` — and that flattened the
 * Czech-flag roundel into a featureless white disc, which is what the owner
 * reported as "messed up". No filter can fix that: a filter cannot recolour one
 * word and not another. **The band was the problem, not the file.**
 *
 * A dark band solves it outright — the logo drops in unfiltered, flag colours
 * and all. The brand red is not lost, it moves to the top hairline and keeps
 * its job as an accent rather than competing with the artwork.
 *
 * The band is mode-independent (see --tt-footer-bg): every rule below paints
 * --tt-accent-contrast (white), so a light-mode footer would be white-on-white.
 * Overlays use color-mix over --tt-accent-contrast, not hardcoded rgba, to stay
 * token-driven.
 */
.tt-footer {
	margin-top: var(--tt-s8);
	padding: var(--tt-s7) 0 var(--tt-s5);
	background: var(--tt-footer-bg);
	/* The brand red, kept as a signature edge now the band itself is dark. */
	border-top: 3px solid var(--tt-accent);
	color: var(--tt-accent-contrast);
	font-size: var(--tt-fs-md);
	font-weight: 600;
}

.tt-footer__top {
	display: grid;
	grid-template-columns: 1.6fr 1fr 1fr;
	gap: var(--tt-s5) var(--tt-s6);
	padding-bottom: var(--tt-s5);
	margin-bottom: var(--tt-s5);
	border-bottom: 1px solid color-mix(in srgb, var(--tt-accent-contrast) 28%, transparent);
}

@media (max-width: 767px) {
	.tt-footer__top {
		grid-template-columns: 1fr 1fr;
	}

	/* Brand spans the full width above the two link columns. */
	.tt-footer__brandcol {
		grid-column: 1 / -1;
	}
}

/*
 * ⚠ NO SINGLE-COLUMN BREAKPOINT BELOW THIS. The two link columns (Informace /
 * Ostatní) stay SIDE BY SIDE all the way down — owner, 2026-09-13, after seeing
 * them stacked on a phone. There used to be a `max-width: 479px` rule here
 * dropping .tt-footer__top to `1fr`; it is deliberately gone, so do not
 * "restore mobile stacking" as a tidy-up.
 *
 * What the rule was protecting against was the longest labels ("Provozní
 * podmínky") crowding a ~140px column at 320px. That is handled properly below
 * instead of by stacking: the gap tightens and long labels are allowed to wrap
 * rather than forced onto one line.
 */
@media (max-width: 479px) {
	.tt-footer__top {
		gap: var(--tt-s4) var(--tt-s4);
	}

	/*
	 * Labels wrap to a second line instead of overflowing the column. overflow-wrap
	 * rather than a hyphenation or truncation: a clipped nav label is unreadable,
	 * a wrapped one is merely two lines tall.
	 */
	.tt-footer__links a {
		overflow-wrap: anywhere;
	}
}

/* Brand column */
.tt-footer__brand {
	display: inline-block;
	margin-bottom: var(--tt-s3);
	color: var(--tt-accent-contrast);
}

/*
 * The footer uses the DARK (light-on-dark) logo variant — see footer.php — and
 * renders it UNFILTERED, as the artwork was drawn.
 *
 * ⚠️ DO NOT REINTRODUCE `filter: brightness(0) invert(1)` HERE. It was needed
 * only while the band was red, and it is what made the footer logo look broken:
 * the filter flattens every colour to white, which turns the Czech-flag roundel
 * into a plain white disc. Against --tt-footer-bg the real colours all clear
 * their thresholds (measured: red word 3.6:1, blue 6.7:1, white 18.6:1), so
 * there is nothing left for it to fix.
 */
.tt-footer__brand img {
	height: 44px;
	width: auto;
}

/* The two-tone wordmark (the no-logo fleet fallback) goes solid white here.
   Held over from the red band, where the accent tail vanished into the
   background; on the dark band it is now a choice, so the footer brand stays
   one flat mark and lets the accent hairline carry the colour. */
.tt-footer__brand .tt-mark {
	font-size: 24px;
}

.tt-footer__brand .tt-mark__a,
.tt-footer__brand .tt-mark__b {
	color: var(--tt-accent-contrast);
	background: none;
	-webkit-text-fill-color: var(--tt-accent-contrast);
}

.tt-footer__tag {
	max-width: 42ch;
	margin: 0;
	/* Pure white; hierarchy comes from weight, not from a tint. (On the old red
	   band a tint outright failed AA — on the dark band it would pass, but the
	   flat white keeps the footer reading as one block.) */
	color: var(--tt-accent-contrast);
	font-size: var(--tt-fs-sm);
	font-weight: 500;
	line-height: 1.6;
}

/*
 * Social profile row — sits under the tagline in the brand column.
 *
 * ⚠️ FIRST PASS WAS A THIN ACCENT GLYPH ON A NEAR-NEUTRAL TILE AND IT READ AS
 * DEAD (owner: "dull"). Two things were wrong and only one of them was colour:
 * a 15-17px glyph in a 40px box is mostly empty tile, and a 1-2px red stroke on
 * a near-black surface muddies at that weight no matter what the contrast ratio
 * says. Ratio is a floor, not a measure of whether something reads.
 *
 * Now: WHITE glyph (18.6:1 on this band) on an accent-TINTED tile, so the mark
 * itself is crisp and the colour is carried by the surface, which can hold it at
 * a size a hairline stroke cannot. Hover fills the tile solid accent — white on
 * accent is 3.6:1, past the 3:1 AA floor for a graphic — so the state change is
 * a surface change and the glyph never gets less legible. That keeps faith with
 * the .tt-footer__links rule below (never hover into the least legible state)
 * while inverting HOW, because these are shapes, not text.
 *
 * ⚠️ Still no width/height on the svg — glyphs are scaled in PHP via
 * tt_social_icon( $network, 1.3 ) so each keeps its own hand-balanced ratio.
 * One CSS size rule here would flatten all four to the same box and undo it.
 */
.tt-footer__social {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tt-s2);
	margin-top: var(--tt-s3);
}

.tt-footer__social a {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 42px;
	height: 42px;
	border-radius: var(--tt-radius);
	color: var(--tt-accent-contrast);
	background: color-mix(in srgb, var(--tt-accent) 16%, transparent);
	border: 1px solid color-mix(in srgb, var(--tt-accent) 38%, transparent);
	transition: background var(--tt-speed), border-color var(--tt-speed),
		transform var(--tt-speed), box-shadow var(--tt-speed);
}

.tt-footer__social a:hover,
.tt-footer__social a:focus-visible {
	background: var(--tt-accent);
	border-color: var(--tt-accent);
	transform: translateY(-3px);
	box-shadow: 0 6px 18px color-mix(in srgb, var(--tt-accent) 45%, transparent);
}

/* Link columns */
.tt-footer__heading {
	margin: 0 0 var(--tt-s3);
	font-size: var(--tt-fs-lg); /* owner: 12 → 14 → 16. The links below are now accent-red,
	                    so the white heading needs the extra size to still lead. */
	font-weight: 800;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--tt-accent-contrast);
}

.tt-footer__links {
	display: flex;
	flex-direction: column;
	gap: var(--tt-s2);
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * Accent-red links (owner request, v0.22.1) — possible only because the band
 * went dark in v0.22.0. On the old red band this rule would have been red on
 * red; on --tt-footer-bg the brand accent measures 5.10:1, which clears AA for
 * body text. The white headings above still lead, so the column reads
 * heading → links rather than as one flat block.
 *
 * ⚠️ The hover no longer dims with `opacity`. That was safe on white text, but
 * fading red toward a near-black background LOWERS contrast (~4.2:1, under AA)
 * — the state you reach by pointing at a link must never be the least legible
 * one. It brightens toward white instead, so hover both reads as a state change
 * and improves contrast.
 */
.tt-footer__links a {
	display: inline-block;
	color: var(--tt-accent);
	font-size: var(--tt-fs-md);
	font-weight: 500;
	transition: color var(--tt-speed), transform var(--tt-speed);
}

.tt-footer__links a:hover {
	color: color-mix(in srgb, #fff 35%, var(--tt-accent));
	transform: translateX(3px);
}

/* Bottom: centred, white, bold. */
.tt-footer__bottom {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--tt-s2);
	text-align: center;
}

.tt-footer__bottom p {
	margin: 0;
}

/* Compliance text — present on every page, a touch quieter than the copyright. */
.tt-footer__legal {
	max-width: 90ch;
	font-size: var(--tt-fs-xs);
	font-weight: 500;
	line-height: 1.6;
	color: var(--tt-accent-contrast);
}

.tt-footer__copy {
	font-weight: 700;
}

/* ---------------------------------------------------------------- back to top */

.tt-to-top {
	position: fixed;
	right: var(--tt-s4);
	bottom: var(--tt-s4);
	z-index: 60;
	display: grid;
	place-items: center;
	width: 42px;
	height: 42px;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-sm);
	background: var(--tt-bg-raised);
	color: var(--tt-text);
	box-shadow: var(--tt-shadow);

	/* Hidden until scrolled — visibility (not display) so it can transition */
	opacity: 0;
	visibility: hidden;
	transform: translateY(8px);
	transition: opacity var(--tt-speed), transform var(--tt-speed), visibility var(--tt-speed);
}

.tt-to-top.is-visible {
	opacity: 1;
	visibility: visible;
	transform: none;
}

.tt-to-top:hover {
	border-color: var(--tt-accent);
	color: var(--tt-accent);
}

/*
 * Sponsor text tile site-wide, top of <main> (tt_sponsor_tile, inc/ads.php).
 * Lives in layout.css, not single.css: it renders on EVERY page.
 * One centred line of text, nothing else (owner, 2026-09-17: no tag, no button).
 * Kept short on purpose: every px here pushes the player down.
 */
/* ⚠ Class names avoid "sponsor" / "ad": ad-blocker cosmetic filters hide those, and the
   wrapper around JuicyAds must not match either or blocking JA would take this tile with it. */
.tt-callout-wrap {
	margin-top: var(--tt-s3);
}

/*
 * Header ad row: JuicyAds 300x100 left, sponsor tile right. Phones stack them
 * with the tile as the full-width strip BELOW JuicyAds (owner, 2026-09-17); from tablet up both are 300x100
 * boxes side by side, centred.
 */
.tt-toprow {
	display: flex;
	flex-direction: column;
	margin: var(--tt-s4) 0;
}

.tt-toprow .tt-ad {
	margin: 0;
}

.tt-toprow .tt-ad + .tt-callout {
	margin: var(--tt-s3) auto 0;
}

.tt-callout {
	position: relative;
	display: block;
	overflow: hidden;
	margin-bottom: var(--tt-s3);
	padding: 10px 0;
	/* Square corners, matching the JuicyAds creative (owner). */
	width: 300px;
	max-width: 100%;
	margin-inline: auto;
	background: var(--tt-accent-dark); /* same red as .tt-section-more buttons */
	box-shadow: var(--tt-shadow-accent);
	color: var(--tt-accent-contrast);
	/* One line inside 300px: 14px uppercase, tightened so the house label fills the box. */
	font-size: var(--tt-fs-md);
	font-weight: 700;
	letter-spacing: -0.02em;
	white-space: nowrap;
	text-overflow: ellipsis;
	line-height: 1.3;
	text-align: center;
	text-transform: uppercase;
	transition: background var(--tt-speed);
}

/* A light sweep across the plate every few seconds: the "flashy" part. */
.tt-callout::after {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient(110deg, transparent 30%, var(--tt-callout-shine) 50%, transparent 70%);
	transform: translateX(-100%);
	animation: tt-callout-shine 3s ease-in-out infinite;
	pointer-events: none;
}

.tt-callout__label {
	position: relative;
}

.tt-callout:hover {
	/* Same hover as .tt-section-more (owner). */
	background: color-mix(in srgb, var(--tt-accent-dark), #000 15%);
	color: var(--tt-accent-contrast);
}

@keyframes tt-callout-shine {
	0%, 40% { transform: translateX(-100%); }
	100% { transform: translateX(100%); }
}

@media (prefers-reduced-motion: reduce) {
	.tt-callout::after { animation: none; }
}

@media (min-width: 768px) {
	.tt-toprow {
		flex-direction: row;
		justify-content: center;
		align-items: center;
		gap: var(--tt-s4);
	}

	.tt-toprow .tt-ad {
		flex: none;
		width: 300px;
	}

	/* 300x100 from tablet up, to match the JuicyAds creative beside it. */
	.tt-callout {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 300px;
		height: 100px;
		margin: 0 auto var(--tt-s3);
		/* The 300x100 box has room for two lines: let it wrap, balanced, instead of
		   one cramped edge-to-edge row (that single line is the PHONE strip rule). */
		padding: 0 var(--tt-s4);
		font-size: var(--tt-fs-3xl);
		letter-spacing: 0.01em;
		line-height: 1.25;
		white-space: normal;
		text-wrap: balance;
	}

	.tt-toprow .tt-callout,
	.tt-toprow .tt-ad + .tt-callout {
		flex: none;
		margin: 0;
	}
}
