/*
Theme Name: Tightent
Theme URI: https://tightent.shop
Author: Tightent
Description: Custom WordPress block theme for tightent.shop — Thai event equipment manufacturer. Built from the Figma source of truth; WooCommerce ready.
Version: 1.61.0
Requires at least: 6.7
Tested up to: 6.8
Requires PHP: 8.1
License: GNU General Public License v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: tightent
Tags: block-theme, full-site-editing, e-commerce, thai
*/

/* -------------------------------------------------------------------------
 * Everything below is what theme.json cannot express.
 * Tokens live in theme.json; this file only holds effects, layout behaviour
 * and the two responsive rules the design needs. Keep it short.
 * ---------------------------------------------------------------------- */

:root {
	--tt-radius-sm: var(--wp--custom--radius--sm);
	--tt-radius-md: var(--wp--custom--radius--md);
	--tt-radius-lg: var(--wp--custom--radius--lg);
	--tt-control: var(--wp--custom--control--height);
	--tt-glass-bg: var(--wp--custom--glass--background);
	--tt-glass-border: var(--wp--custom--glass--border);
	--tt-glass-blur: var(--wp--custom--glass--blur);
}

/* --- Focus: one visible ring everywhere, since the design defines none. --- */
:where(a, button, .wp-block-button__link, summary, input, select, textarea):focus-visible {
	outline: 2px solid var(--wp--preset--color--accent-blue);
	outline-offset: 2px;
}

/* --- Buttons -------------------------------------------------------------- */
.wp-block-button__link {
	min-height: var(--tt-control);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 10px;
	transition: filter .15s ease;
}
.wp-block-button__link:hover { filter: brightness(.92); }

.wp-block-button.is-style-line .wp-block-button__link {
	background-color: var(--wp--preset--color--line-green);
	color: var(--wp--preset--color--base);
}
.wp-block-button.is-style-gold .wp-block-button__link {
	background-color: var(--wp--preset--color--brand-gold);
	color: var(--wp--preset--color--brand-blue);
}
.wp-block-button.is-style-outline .wp-block-button__link {
	background-color: transparent;
	color: var(--wp--preset--color--brand-blue);
	box-shadow: inset 0 0 0 1px currentColor;
}
.wp-block-button.is-style-glass .wp-block-button__link {
	background-color: var(--tt-glass-bg);
	color: var(--wp--preset--color--base);
	box-shadow: inset 0 0 0 .5px var(--tt-glass-border);
	backdrop-filter: blur(var(--tt-glass-blur));
	-webkit-backdrop-filter: blur(var(--tt-glass-blur));
}
.wp-block-button.is-style-compact .wp-block-button__link {
	min-height: 32px;
	padding: 4px 16px;
	border-radius: var(--wp--custom--radius--pill);
}

/* The LINE mark, injected so patterns stay free of inline SVG. */
.wp-block-button.is-style-line .wp-block-button__link::before,
.has-line-mark .wp-block-button__link::before,
.has-facebook-mark .wp-block-button__link::before {
	content: "";
	inline-size: 26px;
	block-size: 26px;
	flex: 0 0 auto;
	background: currentColor;
	-webkit-mask: var(--tt-line-icon) center / contain no-repeat;
	mask: var(--tt-line-icon) center / contain no-repeat;
}
/*
 * LINE is 26px (owner, 1 Oct 2026): at 20 the cut-out letters closed up and
 * the mark read as a plain speech bubble. The gap grows with it (10 -> 13).
 */
.wp-block-button.is-style-line .wp-block-button__link,
.has-line-mark .wp-block-button__link { gap: 13px; }
/* Official glyphs (simple-icons, CC0), 1 Oct 2026: the Facebook button carries its mark too, at 20px. */
.has-facebook-mark .wp-block-button__link::before {
	inline-size: 20px;
	block-size: 20px;
	-webkit-mask-image: var(--tt-facebook-icon);
	mask-image: var(--tt-facebook-icon);
}
/*
 * On a ground that is not LINE green the white glyph becomes LINE's own app
 * icon (green square, white bubble, letters showing green). Never on green:
 * a green square on a green button disappears.
 */
.is-style-band-green .wp-block-button.is-style-line .wp-block-button__link::before,
.tt-icon.tt-icon--line > a::before {
	background: var(--tt-line-app-icon) center / contain no-repeat;
	-webkit-mask: none;
	mask: none;
}
/* On the green band the button is white with green text — mark follows the text. */
.is-style-band-green .wp-block-button__link { color: var(--wp--preset--color--line-green); }

/* --- Group styles --------------------------------------------------------- */
.wp-block-group.is-style-card {
	background-color: var(--wp--preset--color--surface-1);
	border-radius: var(--tt-radius-md);
	box-shadow: var(--wp--preset--shadow--card);
	padding: var(--wp--preset--spacing--50);
}
.wp-block-group.is-style-glass {
	background-color: var(--tt-glass-bg);
	border: .5px solid var(--tt-glass-border);
	border-radius: var(--tt-radius-lg);
	backdrop-filter: blur(var(--tt-glass-blur));
	-webkit-backdrop-filter: blur(var(--tt-glass-blur));
}
/*
 * Text on the navy band names its own colour, and names it once.
 *
 * `band` is #00326C in BOTH style variations — the dark variation redefines
 * base, contrast and the band's border, and leaves the fill alone — so anything
 * sitting on it must be light in both, and neither `base` nor `contrast` is
 * that. `contrast` is black in the light theme, `base` is #0B1220 in the dark
 * one; each was used here and each is wrong in exactly one theme. One custom
 * property, set beside the colour it has to work against.
 */
.wp-block-group.is-style-band-dark {
	color: var(--wp--custom--band--text);
}
/*
 * `band-green` (#30D935) is a LIGHT ground and no template renders it today, so
 * it is deliberately left on the page's own text colour rather than given the
 * band's white — white on that green measures about 1.8:1. When something does
 * use it, it needs its own answer, not this one.
 */
.wp-block-group.is-style-band-green {
	color: var(--wp--preset--color--base);
}
.is-style-band-green :where(h1, h2, h3, h4, h5, h6) { color: #fff; }
.is-style-band-dark  :where(h1, h2, h3, h4, h5, h6) { color: var(--wp--custom--band--text); }
/* Bands are a navy fill in the light theme and a raised surface in the dark one,
   where they need a hairline to separate from the page. */
.is-style-band-dark,
.has-band-background-color {
	border-block: 1px solid var(--wp--custom--band--border);
}
.tt-footer { border-block-start: 1px solid var(--wp--custom--band--border); }
.tt-header { border-block-end: 1px solid var(--wp--custom--band--border); }

/* Product photos are cut-outs on white; on a dark surface they need their own
   white plate or they float on a dark rectangle. */
.tt-card .wc-block-components-product-image,
.tt-product .woocommerce-product-gallery__image {
	background: var(--wp--custom--card--frame);
	border-radius: var(--tt-radius-md);
}
.tt-card .wc-block-components-product-image { border: 6px solid var(--wp--custom--card--frame); }

/* --- Heading with the gold rule the section titles use -------------------- */
.wp-block-heading.is-style-ruled {
	display: flex;
	align-items: center;
	gap: var(--wp--preset--spacing--50);
}
.wp-block-heading.is-style-ruled::after {
	content: "";
	flex: 1 1 auto;
	block-size: 2px;
	background: var(--wp--preset--color--brand-gold);
}

/* --- Product cards: equal height, CTA pinned to the bottom ---------------- */
.tt-card { block-size: 100%; display: flex; flex-direction: column; overflow: hidden; }
/* One text block for every surface — shop, home, landing pages. Everything in it
   reads from the left edge, and the price + button sit on the bottom of the
   card whatever the title and the feature line did above them. */
.tt-card__body {
	flex: 1 1 auto;
	display: flex;
	flex-direction: column;
	align-items: stretch;
	text-align: start;
}
.tt-card__body > * { text-align: start; }
/*
 * The body and the foot are constrained-layout groups, and WordPress gives every
 * child of one `margin-inline: auto` plus a max-width. On a column flex parent
 * that auto margin centres each box — so the title, the feature line, the price
 * and the button were all sitting in the middle of the card while their
 * `text-align` computed, correctly and uselessly, as `start`. Cancel both.
 */
.tt-card__body > *,
.tt-card__foot > * {
	margin-inline: 0;
	max-inline-size: none;
	inline-size: 100%;
}
.tt-card__foot { margin-block-start: auto; padding-block-start: var(--wp--preset--spacing--30); }
.tt-card .wp-block-buttons { margin-block-start: 0; justify-content: flex-start; }
.tt-card .wp-block-post-title { margin: 0; }
/*
 * A ROW OF CARDS IS ONE SHAPE (owner, 22 Sep 2026).
 *
 * The foot already sits on the bottom of the card, so the price row and the
 * button lined up across a row by accident of that — and everything else about
 * them differed. Measured on the inflatables category at 1440: three buttons
 * 193, 168 and 125px wide, and on the sets category one 276px wide inside a
 * 256px card, which is the overflow the sky tube shows.
 *
 * The cause is one line: `.tt-card__foot` was shrink-to-fit, so it took the
 * width of whatever was in it and the button inherited that. The read-more
 * carries `is-layout-flex` from the block wrapper it was born in, which makes
 * it a flex CONTAINER — its own width, not its parent's.
 */
.tt-card__body,
.tt-card__foot {
	inline-size: 100%;
	box-sizing: border-box;
	/* the card is a column flex container; a child that does not stretch is a
	   child as wide as its own longest line, which is where 201px of body
	   inside a 284px card came from */
	align-self: stretch;
}
/*
 * TWO LINES OF TITLE, ALWAYS. A one-line title and a two-line one made two
 * different cards of the same design; the space is reserved either way and a
 * third line is cut rather than allowed to push the card open. `min-block-size`
 * is in `em` so it follows the type rather than a number that has to be kept in
 * step with it.
 */
.tt-card .wp-block-post-title {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	min-block-size: 2.6em;
	line-height: 1.3;
}
/*
 * THE PRICE ROW KEEPS ITS HEIGHT whether or not "เริ่มต้นที่" is there. The
 * prefix is inline, so a card without it is not shorter — but a long price
 * beside it wraps on a phone and that card alone becomes taller. Two lines are
 * reserved at 390 and one at 1440, which is what each width actually needs.
 */
.tt-card .wc-block-components-product-price,
.tt-card .tt-cat-card__meta {
	display: block;
	min-block-size: 1.3em;
	line-height: 1.3;
}
@media (max-width: 781px) {
	.tt-card .wc-block-components-product-price,
	.tt-card .tt-cat-card__meta { min-block-size: 2.6em; }
}
/*
 * ONE BUTTON WIDTH, CENTRED, AND NEVER WIDER THAN THE CARD. `min()` rather than
 * a width plus a max-width: on a narrow card the second term wins and the
 * button is the card's inner width, which is the only sensible answer there.
 */
.tt-card__cta.wp-block-read-more {
	/* `border-box`, or the 16px of padding on each side lands OUTSIDE the width
	   and a 180px button measures 212 — which is what overflowed the card */
	box-sizing: border-box;
	inline-size: min(100%, 180px);
	margin-inline: auto;
	margin-block-start: var(--wp--preset--spacing--20);
}
.tt-product-grid .wc-block-product-template__responsive,
.tt-product-row .wc-block-product-template__responsive { align-items: stretch; }

.tt-footer__legal { margin-block-start: var(--wp--preset--spacing--40); }
.tt-footer__legal a { text-decoration: underline; }

/* --- Cookie consent -------------------------------------------------------
 * A card in the bottom-left corner on a desktop, a bottom sheet on a phone.
 * Same frosted panel as the tiles and the floating menu button, so it reads as
 * part of the site rather than a plugin someone bolted on.
 */
.tt-consent {
	position: fixed;
	z-index: 400;
	inset-block-end: 0;
	inset-inline-start: 0;
	padding: 16px;
	padding-block-end: calc(16px + env(safe-area-inset-bottom, 0px));
	max-inline-size: 420px;
}
.tt-consent[hidden] { display: none !important; }
/*
 * While the consent banner is open the page grows by its height, so a primary
 * button near the bottom of a short page can always be scrolled clear of it
 * (cycle 75). The variable is set and removed by assets/js/consent.js; with no
 * banner there is no variable and no padding.
 */
body { padding-block-end: var(--tt-consent-space, 0px); }
.tt-consent__panel {
	padding: 20px;
	border-radius: 14px;
	/* Opaque, not frosted. The mobile CTA bar is a green bar fixed to the same
	   edge, and at 82% it bled through and tinted the reject button green — it
	   read as a styled "safe" button when it is the plain one. */
	background: rgba(255, 255, 255, .96);
	box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .5), 0 12px 32px rgba(0, 0, 0, .22);
	color: #354156;
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
	.tt-consent__panel {
		background: rgba(255, 255, 255, .96);
		-webkit-backdrop-filter: blur(14px) saturate(1.4);
		backdrop-filter: blur(14px) saturate(1.4);
	}
}
.tt-consent__title {
	margin: 0 0 6px;
	color: #00366F;
	font-size: var(--wp--preset--font-size--medium);
	font-weight: 700;
	line-height: 1.4;
}
.tt-consent__text { margin: 0 0 12px; font-size: var(--wp--preset--font-size--small); line-height: 1.6; }
.tt-consent__cats { list-style: none; margin: 0 0 14px; padding: 0; display: grid; gap: 8px; }
.tt-consent__cats[hidden] { display: none !important; }
.tt-consent__cats label { display: flex; gap: 8px; align-items: start; font-size: var(--wp--preset--font-size--small); line-height: 1.5; }
.tt-consent__cats input { margin-block-start: 3px; accent-color: var(--wp--preset--color--brand-blue); }
.tt-consent__cats strong { color: #00366F; }
.tt-consent__actions { display: flex; flex-wrap: wrap; gap: 8px; }
.tt-consent__btn {
	min-block-size: 44px;
	padding-inline: 16px;
	border: 1px solid var(--wp--preset--color--brand-blue);
	border-radius: var(--tt-radius-sm);
	/* Opaque, not transparent. Transparent let the green CTA bar behind the panel
	   through and tinted "ปฏิเสธทั้งหมด" green — a rejection button that looks
	   like the friendly one is a dark pattern by accident. */
	background: var(--wp--preset--color--base);
	color: var(--wp--preset--color--brand-blue);
	font: inherit;
	font-weight: 700;
	cursor: pointer;
}
.tt-consent__btn--accept {
	background: var(--wp--preset--color--brand-blue);
	color: #fff;
	flex: 1 1 auto;
}
.tt-consent__btn:focus-visible { outline: 3px solid var(--wp--preset--color--brand-gold); outline-offset: 2px; }

@media (max-width: 600px) {
	/* A sheet, not a card: at this width a corner card covers the content it is
	   asking about. */
	.tt-consent { inset-inline: 0; max-inline-size: none; padding: 0; }
	/* The sheet covers the CTA bar completely, so there is nothing to blur and
	   every bit of translucency is just the green bar showing through. */
	.tt-consent__panel {
		border-radius: 14px 14px 0 0;
		background: var(--wp--preset--color--base);
		-webkit-backdrop-filter: none;
		backdrop-filter: none;
	}
	.tt-consent__btn { inline-size: 100%; }
}

/* --- Floating menu button (phones and small tablets) ---------------------
 * The header scrolls away and the sheet then has no opener. This is the same
 * frosted panel as the tiles, 44px so it clears the tap-target minimum, and it
 * is only ever on screen when the header is not — the JS in
 * blocks/site-nav/view.js watches the header with an IntersectionObserver and
 * hides this while the sheet itself is open.
 */
.tt-floatburger { display: none; }
@media (max-width: 1080px) {
	.tt-floatburger {
		display: grid;
		place-items: center;
		position: fixed;
		inset-block-start: calc(16px + env(safe-area-inset-top, 0px));
		inset-inline-start: calc(16px + env(safe-area-inset-left, 0px));
		z-index: 310;
		inline-size: 44px;
		block-size: 44px;
		padding: 0;
		border: 0;
		border-radius: 50%;
		background: rgba(255, 255, 255, .9);
		box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .5), 0 4px 14px rgba(0, 0, 0, .18);
		cursor: pointer;
		transition: opacity .18s ease, transform .18s ease;
	}
	@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
		.tt-floatburger {
			background: rgba(255, 255, 255, .72);
			-webkit-backdrop-filter: blur(14px) saturate(1.4);
			backdrop-filter: blur(14px) saturate(1.4);
		}
	}
	/* Our own `display` beats the UA [hidden] rule — CLAUDE.md 12. */
	.tt-floatburger[hidden] { display: none !important; }
	/* the drawer is open: the floating menu button would sit on its title (27 Sep 2026) */
	body.tt-cfg-locked .tt-floatburger { display: none !important; }
	.tt-floatburger::before {
		content: "";
		inline-size: 20px;
		block-size: 14px;
		background: #00366F;
		-webkit-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 14"><g fill="black"><rect width="20" height="2" rx="1"/><rect y="6" width="20" height="2" rx="1"/><rect y="12" width="20" height="2" rx="1"/></g></svg>') center / contain no-repeat;
		mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 14"><g fill="black"><rect width="20" height="2" rx="1"/><rect y="6" width="20" height="2" rx="1"/><rect y="12" width="20" height="2" rx="1"/></g></svg>') center / contain no-repeat;
	}
	.tt-floatburger:focus-visible {
		outline: 3px solid var(--wp--preset--color--brand-gold);
		outline-offset: 2px;
	}
	.tt-floatburger:active { transform: scale(.94); }
}
@media (prefers-reduced-motion: reduce) {
	.tt-floatburger { transition: none; }
	.tt-floatburger:active { transform: none; }
}

/* --- Language switcher (hidden until there is a second language) ---------
 * Same segmented shape as the shop's usage toggle, so the two read as one
 * family. Nothing renders until tightent_has_second_language() is true.
 */
.tt-nav__lang,
.tt-sheet__lang {
	display: inline-flex;
	padding: 3px;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--wp--custom--radius--pill);
}
.tt-nav__lang-item,
.tt-sheet__lang-item {
	display: inline-flex;
	align-items: center;
	min-block-size: 30px;
	padding-inline: 12px;
	border-radius: var(--wp--custom--radius--pill);
	color: var(--wp--preset--color--muted);
	font-weight: 700;
	font-size: var(--wp--preset--font-size--small);
	text-decoration: none;
}
.tt-nav__lang-item.is-current,
.tt-sheet__lang-item.is-current {
	background: var(--wp--preset--color--brand-blue);
	color: var(--wp--preset--color--base);
}
.tt-sheet__lang { margin-block: 8px 0; }

/* --- Frosted glass panel over photography --------------------------------
 * One treatment, used everywhere a caption sits on top of a photo: the
 * application tiles on the home page, their arrow, and the category tiles.
 * Values are the brief's: blur 14, saturate 1.4, white at 72%, a 1px inner
 * highlight, 14px radius, 12px in from the tile edge.
 *
 * The opacity is declared twice on purpose. Without backdrop-filter a 72% white
 * is just a washed-out hole in the photo and the text loses contrast, so those
 * browsers get 90% instead — @supports, not a guess.
 *
 * Contrast, measured against the panel composited over the extremes a photo can
 * be (WCAG AA needs 4.5 for this text):
 *   navy #00366F  6.02 over black, 11.95 over white
 *   slate #354156 5.18 over black, 10.28 over white
 */
.tt-app__bar,
.tt-tiles .wp-block-cover__inner-container {
	background: rgba(255, 255, 255, .9);
	box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .45);
	border-radius: 14px;
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
	.tt-app__bar,
	.tt-tiles .wp-block-cover__inner-container {
		/*
		 * 48%, not the 40% the mockup asks for: at 40 the navy on the busiest
		 * tile (ธงชายหาด, four printed flags) measures 4.29:1 at 1440 and 4.04:1
		 * at 768 against the worst pixel behind the bar — under AA. Stepped up in
		 * 4s, 44 still fails at 768 (4.41), 48 clears everywhere with 4.79 to
		 * spare at the worst width. Lowest passing value, not a round one.
		 */
		background: rgba(255, 255, 255, .48);
		-webkit-backdrop-filter: blur(3px) saturate(1.1);
		backdrop-filter: blur(3px) saturate(1.1);
	}
}
.tt-tiles .wp-block-cover__inner-container :where(h3, h3 a) { color: #00366F; }
.tt-tiles .wp-block-cover__inner-container :where(p) { color: #354156; }

/* --- WooCommerce product gallery ------------------------------------------
 * WooCommerce ships the gallery at `opacity: 0` and lets wc-single-product.js
 * fade it in once flexslider has initialised. inc/performance.php dequeues that
 * script to get rid of PhotoSwipe — its comment claims "the gallery slider
 * stays", and it does not: the same handle carries both. The result was a
 * product page with no product photograph on it, on every product, invisible to
 * every check because the <img> is present, loaded and correctly sized — only
 * its container is transparent.
 *
 * The theme does its own variation image swap (assets/js/variation-radios.js,
 * gotcha 7), so nothing here needs flexslider. Reveal the gallery in CSS and lay
 * the thumbnails out ourselves.
 */
.woocommerce-product-gallery { opacity: 1 !important; }
/*
 * ONE UNIFORM GRID, and the main image above it.
 *
 * "the plain grid they already are" was wrong: Woo's classic sheet floats the
 * thumbnails at 25% and lets each keep its own aspect, so a row could hold a
 * 128x128 beside a 128x421 and the next row started wherever the tallest one
 * ended. Measured on the beach flag: six tiles, six heights, 165px of hole.
 *
 * Four per row at 1440, three at 390, nothing between them. The first child —
 * the main photograph, or the live preview on a configured product — spans the
 * row: it is the thing the page is about, not a tile.
 */
.woocommerce-product-gallery__wrapper {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 0;
}
.woocommerce-product-gallery__wrapper > :first-child { grid-column: 1 / -1; }
@media (max-width: 781px) {
	.woocommerce-product-gallery__wrapper { grid-template-columns: repeat(3, 1fr); }
}
/*
 * Woo sizes the thumbnails at `width: 25%` and floats them, which is its own
 * four-column layout done the 2014 way. Inside a grid track that 25% is 25% of
 * the TRACK: a 128px column with a 32px picture in the corner of it, measured.
 * `!important` for the same reason as the `opacity` line above — Woo's rule is
 * more specific than anything a theme sheet can say about the same element
 * without naming its whole ancestry, and this is scoped to the one wrapper.
 */
.woocommerce-product-gallery__wrapper > * {
	float: none !important;
	inline-size: auto !important;
	margin: 0 !important;
}
/*
 * A square tile with the whole photograph inside it. `contain`, not `cover`:
 * the fill behind it (below) is what the leftover space is for, and a per-slot
 * `object-fit` written inline by tightent_slot_focus_attr() still beats this,
 * which is how an attachment set to `cover` keeps cropping.
 *
 * No radius on a tile: with no gaps between them, eight rounded corners meeting
 * in the middle of the grid is four little holes.
 */
.tt-product .tt-gal__tile { aspect-ratio: 1; border-radius: 0; }
.tt-gal__tile .wp-block-image,
.tt-gal__tile figure { block-size: 100%; margin: 0; }
/* `div.images img { height: auto }` is Woo's, and it is what letterboxes a
   picture inside a square tile instead of filling it. */
.tt-gal__tile img {
	inline-size: 100%;
	block-size: 100% !important;
	object-fit: contain;
	display: block;
}
/*
 * The gallery stays in view while a long right column scrolls (owner, 26 Sep
 * 2026) — desktop only. The left column stops stretching, or it is as tall
 * as the right and has nowhere to stick. `--tt-gal-top` comes from
 * assets/js/sticky-gallery.js: below the header, or bottom-aligned for a
 * gallery taller than the window.
 */
@media (min-width: 782px) {
	/* align-self: core sets `align-items: normal !important` on every columns block */
	.tt-product > .wp-block-columns > .wp-block-column:first-child {
		align-self: flex-start;
		position: sticky;
		top: var(--tt-gal-top, calc(var(--wp--custom--header--height) + 24px));
	}
}
/*
 * The main image is a square frame too (owner, 26 Sep 2026), with the radius it
 * always had. `!important` on object-fit, scoped to the gallery: a slot set to
 * `cover` writes object-fit INLINE on the <img>, no selector reaches an inline
 * style, and a product photograph must never be cropped.
 */
.tt-product .tt-gal__lead { aspect-ratio: 1; }
.tt-gal__lead .wp-block-image,
.tt-gal__lead figure { block-size: 100%; margin: 0; }
.tt-product .tt-gal__lead img {
	display: block;
	inline-size: 100%;
	block-size: 100% !important;
	object-fit: contain;
}
.tt-product .tt-gal__lead img,
.tt-product .tt-gal__tile img { object-fit: contain !important; }

/* --- Category tiles ------------------------------------------------------
 * Cycle 32. The photo fills the tile again and a thin glass bar rides on top of
 * it. Cycle 31 did the opposite — the image stopped short of a 104px panel — and
 * the panel won too much of the tile for what it carries: a name and an arrow.
 *
 * Our tile assets are cut-outs on transparency, so `cover` crops a background
 * that is mostly nothing; the radial wash is what the transparent part shows.
 */
.tt-tiles .wp-block-cover {
	background-color: var(--wp--preset--color--surface-lilac);
	/*
	 * The cover ships as a ROW flex container — that is why the bar used to sit
	 * at the bottom through `align-self`, the cross axis. Stacking the image,
	 * the floor line and the bar needs the main axis, so the direction changes
	 * here and `justify-content` becomes the vertical one.
	 */
	flex-direction: column;
	align-items: stretch;
	justify-content: flex-end;
	/* The tile's own padding is the bar's 12px inset on all four sides. */
	padding: 12px !important;
	/* 8% behind the product, deepening to 12% at the foot of the tile — the
	   product reads as standing on something rather than floating on a flat
	   panel, and the glass has more to blur where it overlaps. */
	background-image:
		linear-gradient(
			to bottom,
			transparent 45%,
			color-mix(in srgb, var(--wp--preset--color--brand-blue) 12%, transparent) 100%
		),
		radial-gradient(
			ellipse 80% 60% at 50% 38%,
			color-mix(in srgb, var(--wp--preset--color--brand-blue) 8%, transparent) 0%,
			transparent 70%
		);
}
/*
 * Cycle 37: the image is the tile. `contain` against the whole tile box with an
 * 8px inset, centred — no safe area, no reserved strip, no arithmetic. The bar
 * lies over whatever the product's height leaves it, and the glass is thin
 * enough to read the product through (see the glass block above).
 */
.tt-tiles .wp-block-cover__image-background {
	position: absolute;
	inset: 8px;
	/* Stated, not `auto`: an absolutely positioned REPLACED element sizes itself
	   from its intrinsic dimensions when the sizes are auto, insets or no insets
	   (gotcha 23). Left on auto the boxes came out anywhere from 43% to 96% of
	   the tile. */
	inline-size: calc(100% - 16px);
	block-size: calc(100% - 16px);
	margin: 0;
	object-fit: contain;
	object-position: center;
}

/* --- The glass bar, shared by category and application tiles --------------
 * The name, one line of description and the arrow — all of it visible at rest.
 * Nothing here appears on hover any more: copy that only a mouse can reach is
 * copy a phone never gets, and half the traffic is a phone.
 *
 * The bar is capped at 22% of the tile (26% at 390) and verify-tiles.js
 * measures that at all three widths — the cap is why the tile heights below are
 * what they are, and not the other way round.
 */
.tt-tiles,
.tt-apps { --tt-tile-bar: 72px; }
.tt-tiles .wp-block-cover__inner-container,
.tt-app__bar {
	box-sizing: border-box;
	display: grid;
	grid-template-columns: minmax(0, 1fr) auto;
	align-items: center;
	column-gap: 10px;
	margin: 0;
	align-content: center;
	padding: 10px 14px;
	min-block-size: var(--tt-tile-bar);
	overflow: hidden;
}
/* In a column container `align-self` is the horizontal axis: flex-end here would
   shrink the bar to its text and push it to the right. */
.tt-tiles .wp-block-cover__inner-container { align-self: stretch; flex: 0 0 auto; }
.tt-tiles .wp-block-cover__inner-container > h3,
.tt-app__title {
	grid-column: 1;
	margin: 0;
	line-height: 1.5;
	/* One line, and no name reaches the clamp at 1440 — measured against a
	   nowrap clone, not assumed. */
	display: -webkit-box;
	-webkit-line-clamp: 1;
	line-clamp: 1;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
/*
 * 20px, stated: the bar is a fixed-height component and the fluid presets move
 * under it. The heading also ships as has-large-font-size (24px), which is
 * section-heading size and ellipsised ฉากหลังและแบนเนอร์ in a 288px tile — hence
 * the !important, which the preset class forces. It is NOT on .tt-app__title:
 * carrying it there beat the <=600 rule and left the application names at 20px
 * on a 167px tile (gotcha 19 — a later rule is not automatically a winner).
 */
.tt-tiles .wp-block-cover__inner-container > h3 { font-size: 20px !important; }
.tt-app__title { font-size: 20px; }
/* The arrow: a filled navy disc at the end of the bar. The category tiles have
   no element for it, so it is drawn as ::after; the application tiles ship a
   real <svg> and only need the disc around it. */
.tt-tiles .wp-block-cover__inner-container::after,
.tt-app__arrow {
	grid-column: 2;
	grid-row: 1 / -1;
	align-self: center;
	inline-size: 28px;
	block-size: 28px;
	border-radius: 50%;
	background-color: #00366F;
	color: #fff;
}
.tt-tiles .wp-block-cover__inner-container::after {
	content: "";
	background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5 12h14M13 6l6 6-6 6" stroke="white" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>');
	background-position: center;
	background-size: 15px;
	background-repeat: no-repeat;
}
/* One line of description, on the tile, at rest. */
.tt-tiles .wp-block-cover__inner-container > p,
.tt-app__line {
	grid-column: 1;
	margin: 0;
	font-size: 14px;
	line-height: 1.5;
	display: -webkit-box;
	-webkit-line-clamp: 1;
	line-clamp: 1;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
/* One line means one line: the application tiles carry two and the second stays
   out of the bar entirely. */
.tt-app__line + .tt-app__line { display: none; }
.tt-tiles a { color: inherit; text-decoration: none; }
.tt-tiles a:hover { text-decoration: underline; }

/* Icons on the dark band ship as navy glyphs — invert them so they read. */
.is-style-band-dark .wp-block-image img { filter: brightness(0) invert(1); }
/*
 * THE 1.67:1 BUG (cycle 66, found by the 20 Sep gate rehearsal).
 *
 * This line said `contrast`, which is #000000 in the light theme, and the band
 * it paints is #00326C. So every paragraph and list item on a navy band
 * rendered black on navy — 1.67:1 against a 4.5:1 requirement — on the home
 * page, the shop, both split pages and all six application pages, since
 * 10e88fd on 4 September. The headings carried their own #fff, which is why the
 * section looked half-right and nobody read it as broken; verify-contrast.js
 * walked description sections only, so nothing measured it.
 */
.has-band-background-color :where(p, li) { color: var(--wp--custom--band--text); }
.is-style-band-dark .wp-block-group[class*="grid"] .wp-block-image img { filter: none; }

/* --- Outdoor / Indoor split ----------------------------------------------
 * Two compare sliders, flush. The diagonal seam and the badge are part of the
 * tightent/compare-slider block now, not of the columns.
 */
.tt-split { gap: 0 !important; }
.tt-split .wp-block-column { margin-block: 0; }

/* --- Client logos (29 Sep 2026) ------------------------------------------
 * One white tile per logo, all the same 3:2 box, the site's card radius and a
 * hairline border. The optical balance is in the files themselves (equal mark
 * area, scripts/build-client-logos.py), so the tile just shows the whole file.
 * Rows wrap with the last one centred: seven across at desktop, five on a
 * tablet, three on a phone.
 */
.tt-clients {
	--tt-client-gap: var(--wp--preset--spacing--40);
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--tt-client-gap);
	list-style: none;
	margin: var(--wp--preset--spacing--70) 0 0;
	padding: 0;
}
.tt-client {
	flex: 0 0 calc((100% - 6 * var(--tt-client-gap)) / 7);
	box-sizing: border-box;
	aspect-ratio: 3 / 2;
	margin: 0;
	background: #fff;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-radius-md);
	overflow: hidden;
}
.tt-client img { display: block; inline-size: 100%; block-size: 100%; object-fit: contain; }
@media (max-width: 1024px) {
	.tt-client { flex-basis: calc((100% - 4 * var(--tt-client-gap)) / 5); }
}
@media (max-width: 600px) {
	.tt-clients { --tt-client-gap: 10px; }
	.tt-client { flex-basis: calc((100% - 2 * var(--tt-client-gap)) / 3); }
}

/* --- Icons in a navy circle ---------------------------------------------- */
.tt-icon-badge {
	inline-size: 96px;
	block-size: 96px;
	display: grid;
	place-items: center;
	border-radius: 50%;
	background: var(--wp--preset--color--brand-blue);
	margin-inline: auto;
}
/* The badge holds an inline Lucide outline in currentColor. This used to mask a
   single placeholder SVG over every badge, which is why a delivery card and a
   UV card showed the same glyph no matter which image was behind them. */
.tt-icon-badge {
	color: #fff;
	position: relative;
}
.tt-icon-badge .tt-ico { display: block; }

/* --- Category tile, photo variant (is-style-overlay) ----------------------
 * Not used yet: our tile images are cut-outs. Switch the tiles to this style
 * once real photography lands — it matches the Figma treatment.
 */
.tt-tiles .wp-block-cover.is-style-overlay { background-color: transparent; }
.tt-tiles .wp-block-cover.is-style-overlay .wp-block-cover__image-background {
	object-fit: cover;
	object-position: center;
}
.tt-tiles .wp-block-cover.is-style-overlay::after {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient(to top, rgba(0, 50, 108, .85) 0%, rgba(0, 50, 108, .15) 55%, rgba(0, 50, 108, 0) 100%);
	border-radius: inherit;
}
.tt-tiles .wp-block-cover.is-style-overlay .wp-block-cover__inner-container {
	background: none;
	position: relative;
	z-index: 1;
	padding-block-start: 0;
}
.tt-tiles .wp-block-cover.is-style-overlay :where(h3, h3 a, p) { color: var(--wp--preset--color--base) !important; }
.tt-tiles .wp-block-cover.is-style-overlay .wp-block-cover__inner-container::after {
	content: "";
	position: absolute;
	inset-block-end: 0;
	inset-inline-end: 0;
	inline-size: 36px;
	block-size: 36px;
	border-radius: 50%;
	background: var(--wp--preset--color--base) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5 12h14M13 6l6 6-6 6" stroke="%2300366F" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>') center / 18px no-repeat;
}

/* --- USP / trust strip: 6 up on desktop, 3x2 on phones ------------------- */
.tt-usp-strip {
	display: grid;
	grid-template-columns: repeat(6, minmax(0, 1fr));
	gap: var(--wp--preset--spacing--50);
	align-items: start;
}
.tt-usp-strip > * { margin-block: 0; }

/* --- Hero ---------------------------------------------------------------- */
.tt-hero { border-radius: var(--wp--custom--radius--hero); overflow: hidden; }

/* the copy and the buttons stay above the 390 scrim below */
.tt-hero > .wp-block-cover__inner-container { position: relative; z-index: 2; }

/*
 * THE COPY STOPS BEFORE THE TENT (cycle 74).
 *
 * Measured at 1440: the Thai paragraph ran to 48.7% of the hero while the
 * products begin at 46.9%, so its first line ended on the tent's printed wall.
 * 460px puts the right edge at about 43% — clear of the product with room for
 * the line to grow when the copy is proofread.
 *
 * This caps the TEXT, not the inner container: the buttons below keep their own
 * width, and a narrower container would have moved them too.
 */
.tt-hero .wp-block-cover__inner-container :where(h1, h2, p) {
	max-inline-size: 460px;
}

/*
 * THE HERO BOX KEEPS THE RATIO THE SLOT ASKS FOR (owner, 21 Sep 2026).
 *
 * Every hero slot asks Non for 1600 x 750 — ratio 2.133. The box was a fixed
 * 563px tall, so on a full-width page it measured 1440 x 563: ratio 2.558, and
 * `object-fit: cover` then threw away the top and bottom of a photograph
 * delivered exactly to spec. The application pages showed it worst because they
 * are alignfull; the home hero is inside the 1200px content width and happened
 * to come out at 2.13 already, which is why it never looked wrong.
 *
 * `aspect-ratio` instead of a height, so the box follows the picture rather than
 * the picture following the box. A photograph delivered to the slot's own
 * specification is now shown whole at every desktop width.
 *
 * THE CARD MUST NOT STRETCH IT. `min-block-size: 0` removes the block's inline
 * 563px; without that the ratio would only ever be able to make the box TALLER.
 * The glass card is 325px at 1440 against a 675px box, and 325 against 366 at
 * the 782px edge — it fits, and if copy ever grows past the box the overflow is
 * visible rather than silently re-cropping the photograph.
 */
@media (min-width: 782px) {
	.wp-block-cover.tt-hero.tt-hero {
		/*
		 * `!important` because core/cover writes `aspect-ratio: unset` INLINE,
		 * and no selector at any specificity reaches an inline style. Same
		 * situation as the lightbox's inline object-fit (gotcha 39), and for the
		 * same reason it is not gotcha 19: this is scoped to one class on one
		 * block, not a broad rule reaching for force.
		 */
		aspect-ratio: 1600 / 750 !important;
		/*
		 * min-content, not 0 (owner, 26 Sep 2026: a hero never cuts content).
		 * The ratio still sets the height whenever the card is shorter than it —
		 * which is every hero today — and a longer card now grows the box
		 * instead of being cut off by the overflow the rounded corners need.
		 */
		min-block-size: min-content !important;
		block-size: auto;
	}
}

@media (max-width: 781px) {
	/*
	 * THE HERO SPLITS ON A PHONE (owner, 21 Sep 2026).
	 *
	 * The scrim that cycle 74 put under the copy did its job — the gold kicker
	 * stopped running across the bright tent print — and it did it by darkening
	 * the photograph the customer came to see. The owner looked at it and chose
	 * the other answer: at this width the copy and the photograph stop sharing
	 * the same rectangle.
	 *
	 * So the cover becomes a column. The copy sits on flat navy, the photograph
	 * sits under it WHOLE, with nothing over it — no scrim, no gradient, no
	 * crop. `object-fit: contain` and `block-size: auto` are both needed: the
	 * first stops the image filling a box it was never sized for, the second
	 * lets the box take the picture's own height instead of the cover's.
	 *
	 * Desktop is untouched. Everything here is inside the phone query, and the
	 * 1440 layout still has the photo behind the copy with the block's own
	 * horizontal gradient, exactly as before.
	 */
	/*
	 * The class is repeated on purpose. `.wp-block-cover.tt-hero` further down
	 * this file sets `padding: var(--tt-hero-pad) !important`, and an identical
	 * selector later in the source wins — so the first version of this left the
	 * photograph 20px narrower than the hero on both sides. Repeating the class
	 * raises specificity without an `#id` hack and without moving either block
	 * somewhere its neighbours no longer explain it (gotcha 19).
	 */
	/*
	 * ONE SHAPE (owner, 21 Sep 2026). The token is `0 0 35px 35px` — square at
	 * the top, round at the bottom — which reads as a card on a desktop hero
	 * that sits directly under the header. On a phone it is a card with a
	 * gutter either side, and two square corners on top made it look like two
	 * objects that happen to touch. This one applies everywhere at this width.
	 */
	.wp-block-cover.tt-hero.tt-hero {
		border-radius: 35px;
	}
	/*
	 * THE SPLIT IS FOR THE APPLICATION PAGES ONLY (owner, 21 Sep 2026).
	 *
	 * Those heroes are photographs of real events: at 390 the copy on top of
	 * one is unreadable without darkening the picture, so there the cover
	 * becomes a column — copy on flat navy, photograph whole underneath.
	 *
	 * The home hero went the other way. Its photograph is a studio export with
	 * room built into it, so on a phone it stays what it is at 1440: one card,
	 * the picture covering all of it, the block's own translucent navy gradient
	 * over the picture and nothing else — no second block, no solid navy, no
	 * mask, no scrim. Everything this query used to do to it is now behind
	 * `body:not(.home)`.
	 */
	body:not(.home) .wp-block-cover.tt-hero.tt-hero:not(.tt-hero--compact) {
		display: flex;
		flex-direction: column;
		/* the cover's inline min-height, and the 780px cap below, both go */
		min-block-size: 0 !important;
		max-block-size: none !important;
		padding: 0 !important;
		background-color: #00326C;
	}
	/*
	 * ...unless the hero IS the screen. The application pages carry the hero
	 * alignfull, so at 390 it measures 390 wide at x=0 — there is no card and
	 * no gutter, and rounding its top corners puts two white notches against
	 * the edge of the phone. Flush at the top, round at the bottom, which is
	 * what the token has always said. Reported to the owner 21 Sep 2026.
	 */
	.wp-block-cover.tt-hero.tt-hero.alignfull {
		border-radius: var(--wp--custom--radius--hero);
	}
	/*
	 * The block's own overlay span is a full-bleed gradient. With the photo out
	 * from under the copy there is nothing for it to sit on, and left in place
	 * it tinted the navy band a second time.
	 */
	body:not(.home) .tt-hero:not(.tt-hero--compact) > .wp-block-cover__background { display: none !important; }

	body:not(.home) .tt-hero:not(.tt-hero--compact) > .wp-block-cover__inner-container {
		order: 1;
		position: static;
		inline-size: 100%;
		max-inline-size: none;
		padding: var(--tt-hero-pad, clamp(20px, 5vw, 64px));
		/*
		 * Gotcha 40, again. `inline-size: 100%` plus padding made the inner
		 * container 390px wide inside a 350px hero, starting at -20 — so the
		 * left padding was spent outside the card and the Thai ran to the very
		 * edge. The card had padding; the box was simply the wrong width.
		 */
		box-sizing: border-box;
	}
	.tt-hero .wp-block-cover__inner-container :where(h1, h2, p) {
		max-inline-size: none;
	}

	/*
	 * THE PHOTOGRAPH IS CROPPED TO ITS PRODUCTS (owner, 21 Sep 2026).
	 *
	 * Shown whole, a 1600 x 750 hero gives the phone its empty left wall — the
	 * space the desktop copy sits in — as half the frame, and the tents end up
	 * a few hundred pixels wide. A square window over the product half is what
	 * the mobile slot will deliver; until it is filled the same window is taken
	 * out of the wide file here, so the two look the same and filling the slot
	 * changes the picture, not the layout.
	 *
	 * `.tt-hero__pic` is the wrapper `inc/hero-slot.php` adds only when the
	 * mobile file exists — it has to carry the order, because once the image is
	 * a flow item the flex item is the <picture>, not the <img>.
	 */
	body:not(.home) .tt-hero:not(.tt-hero--compact) > picture.tt-hero__pic,
	body:not(.home) .tt-hero:not(.tt-hero--compact) > img.wp-block-cover__image-background {
		order: 2;
		display: block;
		inline-size: 100%;
	}
	body:not(.home) .tt-hero:not(.tt-hero--compact) img.wp-block-cover__image-background {
		position: static;
		inline-size: 100%;
		block-size: auto;
		max-block-size: none;
		/*
		 * THE WHOLE PHOTOGRAPH, ITS OWN RATIO (owner, 26 Sep 2026). The 13/10
		 * window that used to be cut out of it lost people and products at the
		 * edges; under the copy card on a phone the picture is shown complete.
		 */
		aspect-ratio: auto;
		object-fit: contain;
		object-position: 50% 50%;
		/* a cover image is absolutely positioned and inset-0; none of that
		   applies once it is a flow item */
		inset: auto;
	}
	/*
	 * THE HOME HERO IS THE DESKTOP HERO, NARROWER (owner, 21 Sep 2026).
	 *
	 * Nothing above touches it, so what is left is the cover doing what a cover
	 * does: the picture fills the card, the block's own 90deg navy gradient
	 * lies over it — 0.85 where the copy is, 0 by the right edge — and the
	 * products stay visible through it. Two things are stated here and nowhere
	 * else.
	 *
	 * WHICH PART OF A 2.13 FILE SHOWS IN A 0.76 BOX is a decision, and it
	 * belongs to whoever is looking at the photograph, not to this file.
	 * `inc/hero-slot.php` writes the slot's focal point onto the cover as
	 * `--tt-hero-focus`, so Media -> Tightent Slots moves the crop. 60% is the
	 * default because a hero export puts its products right of centre; a slot
	 * with a focal point set overrides it without anybody editing CSS.
	 */
	body.home .tt-hero img.wp-block-cover__image-background {
		object-position: var(--tt-hero-focus, 60% 50%);
	}
	/*
	 * A SHADOW, NOT A DARKER LAYER. The gradient is already carrying the copy;
	 * where a bright print passes under a descender this keeps the letterform
	 * readable without taking another 10% off the photograph the customer came
	 * to see. Soft and wide on purpose — at this blur it reads as depth rather
	 * than as an outline.
	 */
	body.home .tt-hero :where(h1, h2, p) {
		text-shadow: 0 1px 14px rgba(0, 20, 45, .55);
	}
}

/* Text sitting on a photo scrim is white in every style variation — it must not
   follow the "base" preset, which flips to navy in the dark variation. */
.tt-hero :where(h1, h2, h3, p),
.tt-compare :where(h1, h2, h3, p),
.tt-tiles .is-style-overlay :where(h3, h3 a, p) { color: #fff; }

/* --- FAQ accordion (core/details) ---------------------------------------- */
.wp-block-details.is-style-faq {
	background: var(--wp--preset--color--surface-blue-soft);
	border-radius: var(--tt-radius-lg);
	padding: var(--wp--preset--spacing--50) var(--wp--preset--spacing--60);
}
.wp-block-details.is-style-faq summary {
	color: var(--wp--preset--color--brand-blue);
	font-size: var(--wp--preset--font-size--large);
	font-weight: 700;
	line-height: 1.5;
	cursor: pointer;
	list-style: none;
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: var(--wp--preset--spacing--50);
}
.wp-block-details.is-style-faq summary::-webkit-details-marker { display: none; }
.wp-block-details.is-style-faq summary::after {
	content: "";
	inline-size: 25px;
	block-size: 25px;
	flex: 0 0 auto;
	background: currentColor;
	-webkit-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6" stroke="black" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>') center / contain no-repeat;
	mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6" stroke="black" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>') center / contain no-repeat;
	transition: transform .2s ease;
}
.wp-block-details.is-style-faq[open] summary::after { transform: rotate(180deg); }
.wp-block-details.is-style-faq > :not(summary) { color: var(--wp--preset--color--muted); }

/* --- Header --------------------------------------------------------------- */
.tt-header {
	position: sticky;
	top: 0;
	z-index: 100;
	box-shadow: var(--wp--preset--shadow--header);
}
.tt-header > .wp-block-group { min-block-size: calc(var(--wp--custom--header--height) - 2 * var(--wp--preset--spacing--50)); }
.tt-header .wp-block-navigation { font-weight: 700; }
.tt-header .wp-block-navigation .wp-block-navigation-item__content { color: var(--wp--preset--color--contrast); }
.tt-header .wp-block-navigation .current-menu-item > .wp-block-navigation-item__content { color: var(--wp--preset--color--brand-blue); }

/* --- Footer --------------------------------------------------------------- */
/* The footer is a dark surface in both style variations, so its text is white
   outright — not the "base" preset, which is the page colour and flips. */
.tt-footer { color: #fff; }
.tt-footer :where(a) { color: #fff; }
.tt-footer :where(h1, h2, h3, h4, h5, h6) { color: var(--wp--preset--color--brand-gold); }
.tt-footer .wp-block-list { list-style: none; padding-inline-start: 0; }
/*
 * A footer link is a target, not a line of text.
 *
 * At the inherited line-height these sat 21px tall with 21.4px between
 * neighbours, against the 24×24 minimum — Lighthouse reported six of them on
 * every page of the site. The link is made to fill the row rather than the row
 * being padded, so the tappable box and the visible text stay the same thing.
 */
.tt-footer .wp-block-list li > a {
	display: inline-flex;
	align-items: center;
	min-block-size: 24px;
	padding-block: 2px;
}
/* never wider than its column: at 782-900px the column is 191-220px and 257 scrolled the page sideways */
.tt-footer .wp-block-buttons .wp-block-button__link { min-inline-size: min(257px, 100%); }

/* Glass icon buttons. The glyph is a mask so one rule sizes all three. */
.tt-icon { margin: 0; }
/*
 * Pills, not squares (owner, 29 Sep 2026): a fixed 49px box cut "Facebook" to
 * "Faceb". Auto width, icon then label on one line, one height for all three;
 * the row wraps whole pills, never a label.
 */
.tt-icon > a {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	inline-size: auto;
	min-block-size: 44px;
	padding-inline: 14px;
	white-space: nowrap;
	border: .5px solid var(--tt-glass-border);
	border-radius: var(--tt-radius-sm);
	backdrop-filter: blur(var(--tt-glass-blur));
	-webkit-backdrop-filter: blur(var(--tt-glass-blur));
}
.tt-icon > a::before {
	content: "";
	inline-size: 20px;
	block-size: 20px;
	background: currentColor;
	-webkit-mask: var(--tt-glyph) center / contain no-repeat;
	mask: var(--tt-glyph) center / contain no-repeat;
}
.tt-icon > a::before { flex: none; }
.tt-footer .tt-social.tt-social { flex-wrap: wrap; gap: 10px; }
.tt-icon--phone    { --tt-glyph: var(--tt-phone-icon); }
.tt-icon--line     { --tt-glyph: var(--tt-line-icon); }
.tt-icon--facebook { --tt-glyph: var(--tt-facebook-icon); }
.tt-icon--line > a { gap: 10px; }
.tt-icon--line > a::before { inline-size: 26px; block-size: 26px; }

/* --- Floating LINE button (mobile) --------------------------------------- */
.wp-block-buttons.tt-line-float { display: none; }

/* --- Responsive: the only two breakpoints the design needs --------------- */
/* --- Navigation: hamburger below 900px, not core's 600px -----------------
 * At 768 the seven Thai nav items wrap the header onto three rows. Core's
 * overlay breakpoint is hard-coded at 600px, so raise it here for our header.
 */
@media (max-width: 1080px) {
	.tt-header .wp-block-navigation__responsive-container:not(.is-menu-open) {
		display: none;
	}
	.tt-header .wp-block-navigation__responsive-container-open:not(.always-shown) {
		display: flex;
	}
	.tt-header .wp-block-navigation__responsive-container.is-menu-open {
		display: flex;
	}
}

/* --- Grids that must collapse on small screens --------------------------- */
/*
 * Eight categories: four across on a desktop is two clean rows, three across
 * would leave a row of two hanging. Below 1200 it is two, which is four rows of
 * two — still no orphan.
 */
.tt-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
/* The pattern's own min-height is 300px, at which a 50px bar is 16.7% of the
   tile — over the 15% cap. 340 brings it to 14.7%. */
.tt-tiles .wp-block-cover { min-height: 340px !important; }
@media (min-width: 1200px) {
	.tt-tiles { grid-template-columns: repeat(4, minmax(0, 1fr)) !important; }
	/* Four across makes each tile narrower; 380 keeps the crop of the product
	   readable and puts the 50px bar at 13.2% of the tile. */
	.tt-tiles .wp-block-cover { min-height: 380px !important; }
}
@media (max-width: 600px) {
	/* Two up, not one: six full-width tiles made the section 1 680px tall at
	   390 — over the 1.5x viewport rule. */
	/* 265, not 230: a 44px bar is 16.6% of it, and the one name that needs two
	   lines (ฉากหลังและแบนเนอร์) still comes in under the 20% allowed here. The
	   tile height follows the bar cap — the cap is the rule. */
	.tt-tiles .wp-block-cover { min-height: 265px !important; }
	/* 48, not 44: the two-line name measures 42px inside a 44px bar — one pixel
	   of clearance top and bottom, which reads as cramped even though it passes.
	   48px is 18.1% of the tile, still inside the 20% this width allows. */
	.tt-tiles,
	.tt-apps { --tt-tile-bar: 48px; }
	/* !important because WordPress emits the preset class with one — the heading
	   carries has-large-font-size and was staying at 20px however specific this
	   selector got. */
	.tt-tiles h3 { font-size: var(--wp--preset--font-size--medium) !important; }
	.tt-tiles p  { font-size: var(--wp--preset--font-size--small); }

	.tt-tiles .wp-block-cover__inner-container,
	.tt-app__bar { padding: 0 10px; column-gap: 6px; }
	/* Name and arrow only at this width. A 167px tile has room for the name or
	   for the sentence, not both, and the name is the one that navigates. */
	.tt-tiles .wp-block-cover__inner-container > p,
	.tt-app__line { display: none; }
	/* The bar states 20px so a fixed-height component does not move under the
	   fluid presets; here the tile is 167px wide and the name needs the smaller
	   step back. */
	.tt-tiles .wp-block-cover__inner-container > h3 {
		font-size: var(--wp--preset--font-size--medium) !important;
	}
	/* A second line here rather than an ellipsis: 20px of Thai in an 83px column
	   used to break words down the middle (ธงชาย / หาด), and a cut name is worse
	   than a bar 21px taller. */
	.tt-tiles .wp-block-cover__inner-container > h3 {
		-webkit-line-clamp: 2;
		line-clamp: 2;
	}
	.tt-tiles .wp-block-cover__inner-container::after,
	.tt-app__arrow {
		inline-size: 22px;
		block-size: 22px;
	}
	.tt-tiles .wp-block-cover__inner-container::after { background-size: 12px; }
}

@media (max-width: 781px) {
	/* The logo must never be squeezed out of the header row. */
	.tt-header .wp-block-site-logo { flex: 0 0 auto; }
	/* Header: let the row wrap rather than overflow on narrow phones. */
	.tt-header .wp-block-site-logo,
	.tt-header .wp-block-site-logo img { inline-size: 120px; max-inline-size: 120px; }
	.tt-header .is-layout-flex { flex-wrap: wrap; row-gap: var(--wp--preset--spacing--30); }
	.tt-header .wp-block-button__link { padding-inline: 16px; }
	.tt-hero { --tt-hero-pad: clamp(20px, 5vw, 64px); }
	.wp-block-cover.tt-hero { padding: var(--tt-hero-pad) !important; }
	.tt-footer .wp-block-buttons .wp-block-button__link { min-inline-size: 0; inline-size: 100%; }
	.tt-header > .wp-block-group { min-block-size: var(--wp--custom--header--height-mobile); }
	.tt-header .wp-block-site-logo img { max-inline-size: 120px; }
	.wp-block-buttons.tt-line-float {
		display: flex;
		position: fixed;
		inset-block-end: 20px;
		inset-inline-end: 20px;
		z-index: 200;
	}
	.tt-usp-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); }

	/* Product / post rows scroll instead of wrapping. */
	.tt-scroll-row .wp-block-columns {
		flex-wrap: nowrap;
		overflow-x: auto;
		scroll-snap-type: x mandatory;
		padding-block-end: var(--wp--preset--spacing--40);
	}
	.tt-scroll-row .wp-block-column {
		flex: 0 0 72%;
		scroll-snap-align: start;
	}
}

/* -------------------------------------------------------------------------
 * Single product
 * ---------------------------------------------------------------------- */

/* Variation picker: radios drive Woo's own select, which stays for its JS. */
.tt-variant-select { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); }
.tt-variants { display: grid; gap: var(--wp--preset--spacing--30); margin-block: var(--wp--preset--spacing--30); }
.tt-variant {
	display: grid;
	/*
	 * minmax(0, 1fr), not 1fr: a grid track sized 1fr still has min-width auto,
	 * so the name column refused to shrink below its text and pushed the price
	 * past the viewport — 12px of horizontal scroll on the product page at 390,
	 * where the picker is two columns wide.
	 */
	grid-template-columns: 20px minmax(0, 1fr) auto;
	align-items: center;
	gap: var(--wp--preset--spacing--40);
	min-block-size: 57px;
	padding-inline: var(--wp--preset--spacing--40);
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-radius-sm);
	background: var(--wp--preset--color--base);
	cursor: pointer;
}
.tt-variant.is-selected {
	background: var(--wp--preset--color--surface-blue-selected);
	border-color: var(--wp--preset--color--accent-blue);
}
.tt-variant input { position: absolute; opacity: 0; pointer-events: none; }
.tt-variant__dot {
	inline-size: 20px; block-size: 20px; border-radius: 50%;
	border: 2px solid var(--wp--preset--color--border);
}
.tt-variant.is-selected .tt-variant__dot {
	border-color: var(--wp--preset--color--accent-blue);
	box-shadow: inset 0 0 0 4px var(--wp--preset--color--base);
	background: var(--wp--preset--color--accent-blue);
}
.tt-variant__name  { font-weight: 700; min-inline-size: 0; overflow-wrap: anywhere; }
.tt-variant__price { font-weight: 700; color: var(--wp--preset--color--brand-blue); }
.tt-variant:focus-within { outline: 2px solid var(--wp--preset--color--accent-blue); outline-offset: 2px; }

/* Add to cart is the secondary CTA; LINE above it is primary. */
.tt-product .single_add_to_cart_button {
	background-color: var(--wp--preset--color--brand-blue);
	color: var(--wp--preset--color--base);
	border: 0;
	border-radius: var(--tt-radius-sm);
	min-block-size: var(--tt-control);
	padding-inline: 24px;
	font-weight: 700;
}
.tt-product form.cart { display: flex; flex-wrap: wrap; gap: var(--wp--preset--spacing--40); align-items: center; }
.tt-product form.cart .quantity input { min-block-size: var(--tt-control); border: 1px solid var(--wp--preset--color--border); border-radius: var(--wp--custom--radius--xs); }
/* the same − / + as the configurator, around WooCommerce's own field (26 Sep 2026) */
.tt-product form.cart .quantity.tt-qtybox { display: inline-flex; align-items: stretch; border: 1px solid var(--wp--preset--color--border); border-radius: var(--tt-radius-sm); overflow: hidden; background: #fff; }
.tt-product form.cart .quantity.tt-qtybox input { inline-size: 72px; border: 0; border-inline: 1px solid var(--wp--preset--color--border); border-radius: 0; text-align: center; font-weight: 700; -moz-appearance: textfield; }
.tt-product form.cart .quantity.tt-qtybox input::-webkit-outer-spin-button,
.tt-product form.cart .quantity.tt-qtybox input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.tt-qtypm { inline-size: 44px; min-block-size: 44px; border: 0; background: var(--wp--preset--color--surface-2); color: var(--wp--preset--color--brand-blue); font: inherit; font-size: 20px; font-weight: 700; cursor: pointer; }
.tt-qtypm[disabled] { opacity: .4; cursor: not-allowed; }
.tt-product .woocommerce-variation-price { font-size: var(--wp--preset--font-size--large); font-weight: 700; color: var(--wp--preset--color--brand-blue); }

/* Text links under the CTAs */
.tt-product-links p { margin: 0; }
.tt-product-links a { font-weight: 700; }

/* Chips and included list in the info card */
.tt-chip {
	margin: 0;
	padding: 6px 14px;
	border-radius: var(--wp--custom--radius--pill);
	background: var(--wp--preset--color--surface-blue-soft);
	color: var(--wp--preset--color--brand-blue);
	font-weight: 700;
}
.tt-included { list-style: none; padding-inline-start: 0; }
.tt-included li { padding-block: 8px; border-block-end: 1px solid var(--wp--preset--color--border); display: flex; justify-content: space-between; gap: 16px; }

/* Trust strip */
.tt-trust { border-block: 1px solid var(--wp--preset--color--border); }
.tt-trust__item { margin: 0; }

/* Mobile sticky bar: LINE + Add to cart, per the agreed CTA order. */
/* One class is not enough: WordPress emits `.wp-block-group.is-layout-flex
   { display: flex }` for this group, which outranks a single class and left the
   bar rendering in the page flow above the footer on desktop. */
.wp-block-group.tt-sticky-bar { display: none; }
@media (max-width: 781px) {
	/* Same box as .tt-cta-bar — the two bars are never on screen together, and a
	   product page that changed height at the bottom of the viewport read as a
	   different site. */
	.wp-block-group.tt-sticky-bar {
		display: flex;
		align-items: center;
		position: fixed;
		inset-inline: 0;
		inset-block-end: 0;
		z-index: 300;
		box-sizing: border-box;
		block-size: calc(64px + env(safe-area-inset-bottom, 0px));
		padding: 8px 12px calc(8px + env(safe-area-inset-bottom, 0px));
		background: var(--wp--preset--color--base);
		box-shadow: 0 -2px 12px rgba(0, 0, 0, .12);
	}
	.tt-sticky-bar .wp-block-buttons {
		display: grid;
		grid-template-columns: 1fr 1.4fr;
		gap: 8px;
		inline-size: 100%;
		margin: 0;
	}
	.tt-sticky-bar .wp-block-button,
	.tt-sticky-bar .wp-block-button__link {
		inline-size: 100%;
		block-size: 48px;
		display: flex;
		align-items: center;
		justify-content: center;
		padding-block: 0;
	}
	.tt-sticky-cart .wp-block-button__link { background-color: var(--wp--preset--color--brand-blue); }
	/* the floating LINE bubble would collide with the bar */
	body.single-product .wp-block-buttons.tt-line-float { display: none; }
	.tt-product form.cart { position: relative; }
}

/* Attribute label sits above its list, as in the Figma product page. */
.tt-variants__label {
	display: block;
	margin-block: var(--wp--preset--spacing--40) var(--wp--preset--spacing--20);
	font-weight: 700;
}

/* Colour attributes: 48px image swatches from the variation photos. */
.tt-swatches { display: flex; flex-wrap: wrap; gap: var(--wp--preset--spacing--30); }
.tt-swatch {
	position: relative;
	display: grid;
	justify-items: center;
	gap: 4px;
	cursor: pointer;
	padding: 3px;
	border: 2px solid var(--wp--preset--color--border);
	border-radius: var(--tt-radius-sm);
	background: var(--wp--custom--card--frame, #fff);
}
.tt-swatch input { position: absolute; opacity: 0; pointer-events: none; }
.tt-swatch img { inline-size: 48px; block-size: 48px; object-fit: contain; display: block; }
.tt-swatch.is-selected { border-color: var(--wp--preset--color--brand-blue); }
.tt-swatch__name {
	font-size: var(--wp--preset--font-size--micro);
	color: var(--wp--preset--color--muted);
	max-inline-size: 60px;
	text-align: center;
	line-height: 1.3;
}
.tt-swatch:focus-within { outline: 2px solid var(--wp--preset--color--accent-blue); outline-offset: 2px; }

/* Trust strip icons */
.tt-trust__icon { margin: 0; }
.tt-trust__item { gap: var(--wp--preset--spacing--30); }

/* Woo prints its own label in the variations table; ours sits above the list.
   Our rows and swatches are <label> too, so exclude them explicitly. */
.tt-product .variations label:not(.tt-variant):not(.tt-swatch),
.tt-product .variations th.label { display: none; }
.tt-product .variations,
.tt-product .variations tbody,
.tt-product .variations tr,
.tt-product .variations td { display: block; width: 100%; }
.tt-product .reset_variations { font-size: var(--wp--preset--font-size--small); }

/* --- Shop archive --------------------------------------------------------- */
/* Woo's filter blocks size themselves from their own content and can push past
   a narrow column; keep them inside it. */
.tt-shop__sidebar, .tt-shop__sidebar * { max-inline-size: 100%; }
.tt-shop__sidebar .wp-block-woocommerce-product-filter-price input { min-inline-size: 0; }

/* On a three-up grid the price and the CTA do not fit on one line. */
/* The one thing in the card that is centred, and deliberately: it spans the
   card, so a label pinned to its left edge would read as a stray link rather
   than a button. */
.tt-card .wp-block-buttons { inline-size: 100%; }
.tt-card .wp-block-button { inline-size: 100%; }
.tt-card .wp-block-button__link {
	inline-size: 100%;
	block-size: 44px;
	justify-content: center;
	text-align: center;
}

.tt-chips-row { border-block-end: 1px solid var(--wp--preset--color--border); position: relative; }
/* The row is a scroller at every width, so nine categories can never become two
   rows. The padding below is tuned so they fit on one line from 1200 up —
   measured, not guessed: 9 chips need 1091px of the 1128px available at 1200.
   `safe center` centres them while they fit and falls back to flex-start when
   they do not, which is what stops the first chip becoming unreachable. */
.tt-chips-row .wp-block-group {
	flex-wrap: nowrap;
	overflow-x: auto;
	justify-content: safe center;
	gap: 10px;
	scrollbar-width: none;
}
.tt-chips-row .wp-block-group::-webkit-scrollbar { display: none; }
.tt-chip-link,
.tt-chip-link a { white-space: nowrap; }
.tt-chip-link { margin: 0; }
.tt-chip-link a {
	display: inline-flex;
	align-items: center;
	min-block-size: 40px;
	padding-inline: 14px;
	border-radius: var(--wp--custom--radius--pill);
	border: 1px solid var(--wp--preset--color--brand-blue);
	color: var(--wp--preset--color--brand-blue);
	font-weight: 700;
	text-decoration: none;
}
.tt-chip-link.is-current a {
	background: var(--wp--preset--color--brand-blue);
	color: var(--wp--preset--color--base);
}
/* Usage toggle — one segmented control, so กลางแจ้ง / ในร่ม reads as a choice
   between two states rather than as two more filters in a list. */
.tt-usage-toggle {
	display: inline-flex;
	gap: 0;
	padding: 3px;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--wp--custom--radius--pill);
	background: var(--wp--preset--color--surface, #f5f5f5);
}
.tt-usage-toggle__item { margin: 0; }
.tt-usage-toggle__item a {
	display: inline-flex;
	align-items: center;
	min-block-size: 34px;
	padding-inline: 16px;
	border-radius: var(--wp--custom--radius--pill);
	color: var(--wp--preset--color--muted);
	font-weight: 700;
	text-decoration: none;
	white-space: nowrap;
}
.tt-usage-toggle__item.is-current a {
	background: var(--wp--preset--color--base);
	color: var(--wp--preset--color--brand-blue);
	box-shadow: 0 1px 3px rgba(0, 0, 0, .12);
}

/* The sheet's filter group leaves the page; the ones above it do not. */
.tt-sheet__hint { margin: 0 0 8px; color: var(--wp--preset--color--muted); font-size: var(--wp--preset--font-size--small); }

.tt-shop__bar { border-block-end: 1px solid var(--wp--preset--color--border); padding-block-end: var(--wp--preset--spacing--30); }
.tt-shop__bar .woocommerce-result-count { margin: 0; color: var(--wp--preset--color--muted); }
.tt-shop__bar select { min-block-size: var(--tt-control); border: 1px solid var(--wp--preset--color--border); border-radius: var(--tt-radius-sm); padding-inline: 12px; }
.tt-filters h3 { margin-block-end: var(--wp--preset--spacing--30); }

@media (max-width: 781px) {
	.tt-shop { display: block; }
	.tt-shop__sidebar { margin-block-end: var(--wp--preset--spacing--60); }
	.tt-chips-row .wp-block-group { justify-content: flex-start; flex-wrap: nowrap; overflow-x: auto; }
}

/* --- Shop sidebar: category list ----------------------------------------- */
.tt-cat-list { list-style: none; padding-inline-start: 0; margin: 0; }
.tt-cat-list li { margin: 0; }
.tt-cat-list a {
	display: flex;
	justify-content: space-between;
	gap: 12px;
	padding: 9px 12px;
	border-radius: var(--tt-radius-sm);
	color: var(--wp--preset--color--contrast);
	text-decoration: none;
}
/* The event-type labels are long Thai strings; the count must stay on the first
   line of the row rather than being pushed under the label or broken up. */
.tt-cat-list a span {
	color: var(--wp--preset--color--muted);
	font-size: var(--wp--preset--font-size--small);
	white-space: nowrap;
	flex-shrink: 0;
	align-self: flex-start;
	line-height: 1.6;
}
.tt-cat-list a:hover { background: var(--wp--preset--color--surface-2); text-decoration: none; }
.tt-cat-list li.is-current a { background: var(--wp--preset--color--brand-blue); color: #fff; font-weight: 700; }
.tt-cat-list li.is-current a span { color: #fff; opacity: .8; }

/* --- Long filter lists: six, then a toggle -------------------------------
 * A checkbox rather than script — the sidebar sheet already works this way, and
 * a filter list that needs JS to open is a filter list that is closed while the
 * page is still loading.
 */
.tt-more-check { position: absolute; opacity: 0; pointer-events: none; }
.tt-more-item { display: none; }
.tt-more-check:checked ~ .tt-cat-list .tt-more-item { display: list-item; }
.tt-more-toggle {
	display: inline-block;
	margin-block-start: 4px;
	padding: 6px 12px;
	color: var(--wp--preset--color--brand-blue);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 700;
	cursor: pointer;
}
.tt-more-toggle:hover { text-decoration: underline; }
.tt-more-toggle .tt-more-hide { display: none; }
.tt-more-check:checked ~ .tt-more-toggle .tt-more-show { display: none; }
.tt-more-check:checked ~ .tt-more-toggle .tt-more-hide { display: inline; }

/* --- Price slider and sorting: theme controls, not browser defaults -------
 * The block exposes --wc-product-filter-price-slider for the selected range and
 * lays out as a grid (slider on top, the two inputs underneath), so the track
 * gets the full sidebar width instead of the gap between two inputs.
 */
.tt-filters .wp-block-woocommerce-product-filter-price-slider {
	--wc-product-filter-price-slider: var(--wp--preset--color--brand-blue);
}
.tt-filters .wc-block-product-filter-price-slider__range {
	background: var(--wp--preset--color--border);
	border-radius: 999px;
}
.tt-filters input[type="range"] { accent-color: var(--wp--preset--color--brand-blue); }
.tt-filters input[type="range"]::-webkit-slider-thumb {
	-webkit-appearance: none;
	inline-size: 20px;
	block-size: 20px;
	border-radius: 50%;
	background: var(--wp--preset--color--base);
	border: 3px solid var(--wp--preset--color--brand-blue);
	box-shadow: var(--wp--preset--shadow--card);
	cursor: grab;
}
.tt-filters input[type="range"]::-moz-range-thumb {
	inline-size: 20px;
	block-size: 20px;
	border-radius: 50%;
	background: var(--wp--preset--color--base);
	border: 3px solid var(--wp--preset--color--brand-blue);
}
.tt-filters input[type="text"],
.tt-filters input[type="number"],
.tt-shop__bar select {
	min-block-size: var(--tt-control);
	padding-inline: 12px;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-radius-sm);
	background: var(--wp--preset--color--surface-1);
	color: var(--wp--preset--color--contrast);
	font-family: inherit;
	font-size: var(--wp--preset--font-size--medium);
}
.tt-shop__bar select {
	appearance: none;
	-webkit-appearance: none;
	padding-inline-end: 40px;
	background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6" stroke="%2300366F" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>');
	background-repeat: no-repeat;
	background-position: right 12px center;
	background-size: 18px;
	cursor: pointer;
}
.tt-filters .wc-block-components-price-slider__button,
.tt-filters .wp-block-woocommerce-product-filter-clear-button a {
	color: var(--wp--preset--color--brand-blue);
	font-weight: 700;
}

/* --- Filters as a bottom sheet on phones ---------------------------------
 * A checkbox, not <details>: a closed <details> hides its content at every
 * width, which took the sidebar off the desktop layout entirely.
 */
.tt-filters-check,
.tt-filters-toggle,
.tt-filters-scrim { display: none; }

@media (max-width: 781px) {
	.tt-filters-toggle {
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 8px;
		min-block-size: var(--tt-control);
		border: 1px solid var(--wp--preset--color--brand-blue);
		border-radius: var(--tt-radius-sm);
		color: var(--wp--preset--color--brand-blue);
		font-weight: 700;
		cursor: pointer;
	}
	.tt-filters-toggle::after {
		content: "";
		inline-size: 18px;
		block-size: 18px;
		background: currentColor;
		-webkit-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 6h16M7 12h10M10 18h4" stroke="black" stroke-width="2" stroke-linecap="round"/></svg>') center / contain no-repeat;
		mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 6h16M7 12h10M10 18h4" stroke="black" stroke-width="2" stroke-linecap="round"/></svg>') center / contain no-repeat;
	}
	.tt-filters-sheet__body { display: none; }
	.tt-filters-check:checked ~ .tt-filters-sheet__body {
		display: block;
		position: fixed;
		inset-inline: 0;
		inset-block-end: 0;
		z-index: 320;
		max-block-size: 78vh;
		overflow-y: auto;
		border-start-start-radius: var(--tt-radius-lg);
		border-start-end-radius: var(--tt-radius-lg);
		background: var(--wp--preset--color--surface-1);
		box-shadow: 0 -8px 30px rgba(0, 0, 0, .25);
		padding: var(--wp--preset--spacing--50);
		padding-block-end: calc(var(--wp--preset--spacing--70) + env(safe-area-inset-bottom));
	}
	.tt-filters-check:checked ~ .tt-filters-scrim {
		display: block;
		position: fixed;
		inset: 0;
		z-index: 310;
		background: rgba(0, 0, 0, .45);
	}
	.tt-filters-check:checked ~ .tt-filters-sheet__body .tt-filters { box-shadow: none; padding: 0; }
	.tt-filters-toggle:focus-within,
	.tt-filters-check:focus-visible + .tt-filters-toggle {
		outline: 2px solid var(--wp--preset--color--accent-blue);
		outline-offset: 2px;
	}
}

/* Price inputs would otherwise overflow a 276px sidebar. */
.tt-filters .wc-block-product-filter-price-slider__left input,
.tt-filters .wc-block-product-filter-price-slider__right input {
	inline-size: 100%;
	min-inline-size: 0;
	text-align: center;
}

/* --- Landing page sections ----------------------------------------------- */
.tt-set { block-size: 100%; overflow: hidden; }
/*
 * The LINE button sits on the SAME LINE across a row, whatever the card above
 * it is (cycle 74). The three sets have four and five contents lines and one
 * title that wraps to two, so the buttons landed at three different heights in
 * a row of cards that are themselves exactly the same height — which reads as
 * three cards of different sizes.
 *
 * LAY IT OUT, DO NOT CALCULATE IT (gotcha 27). The first attempt gave the body
 * `calc(100% - var(--tt-set-img))`, a variable nothing sets: the body became as
 * tall as the whole card and pushed the button out through `overflow: hidden`,
 * so all three buttons vanished. The card is the column, the body is the item
 * that takes the slack, and the buttons are last with the leftover above them.
 */
.tt-set { display: flex; flex-direction: column; }
.tt-set > .tt-set__img { flex: 0 0 auto; }
.tt-set > .wp-block-group:last-child {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	flex: 1 1 auto;
}
/*
 * is-layout-constrained gives every child `margin-inline: auto` (gotcha 21).
 * In normal block flow that is harmless — the child is full width, so `auto`
 * resolves to zero — but the moment this became a column flex container the
 * children became shrink-to-fit and those margins centred every one of them.
 *
 * `margin-inline: 0` was the obvious answer and it loses: measured on the page,
 * the heading still computes `margin-left: 81.25px`. So state the WIDTH instead
 * — with no free space there is nothing for an auto margin to absorb, and the
 * position holds whichever rule wins.
 */
.tt-set > .wp-block-group:last-child > * { inline-size: 100%; max-inline-size: none; box-sizing: border-box; }
.tt-set > .wp-block-group:last-child > .wp-block-buttons { margin-block-start: auto; }
.tt-set__img { margin: 0; background: var(--wp--custom--card--frame, transparent); }
.tt-set__list { list-style: none; padding-inline-start: 0; }
.tt-set__list li {
	padding-block: 6px;
	padding-inline-start: 24px;
	position: relative;
	color: var(--wp--preset--color--muted);
	font-size: var(--wp--preset--font-size--small);
}
.tt-set__list li::before {
	content: "";
	position: absolute;
	inset-inline-start: 0;
	inset-block-start: 11px;
	inline-size: 14px;
	block-size: 14px;
	border-radius: 50%;
	background: var(--wp--preset--color--brand-blue);
}
.tt-badge {
	margin: 0;
	align-self: flex-start;
	/* align-self only bites inside a flex parent; the stacked set rows use a
	   constrained group, where a <p> would otherwise stretch the full width. */
	inline-size: fit-content;
	font-weight: 700;
	padding: 4px 12px;
	border-radius: var(--wp--custom--radius--pill);
	background: var(--wp--preset--color--brand-gold);
	color: var(--wp--preset--color--brand-blue);
	font-weight: 700;
}
.tt-step { block-size: 100%; }
.tt-case { block-size: 100%; overflow: hidden; }
.tt-case__where { margin: 0; font-weight: 700; }
.tt-case__where::before { content: "◉ "; }
.tt-video { position: relative; margin: 0 0 var(--wp--preset--spacing--40); }
.tt-video::after {
	content: "";
	position: absolute;
	inset-block-start: 50%;
	inset-inline-start: 50%;
	translate: -50% -50%;
	inline-size: 62px;
	block-size: 62px;
	border-radius: 50%;
	background: rgba(255, 255, 255, .9) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M8 5v14l11-7z" fill="%2300366F"/></svg>') center / 26px no-repeat;
}
.tt-videos .wp-block-column:last-child .tt-video:last-child { margin-block-end: 0; }

@media (max-width: 781px) {
	.tt-steps { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
	.tt-case { grid-column: auto; }
	.tt-videos { display: block; }
}
@media (max-width: 900px) {
	.wp-block-group[class*="tt-"] .wp-block-group.is-layout-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* =========================================================================
 * MOBILE PASS (<= 780px)
 *
 * Figma has no mobile frames, so these are our rules. Two shapes only:
 * icon groups become a 2x2 grid of compact cards, and card rows become a
 * scroll-snap carousel with one card at ~80% and the next one peeking. A
 * vertical stack of full-width cards is never the answer — it turns every
 * section into a scroll marathon.
 * ====================================================================== */

/* --- Carousel: works with no JS; the dots are added by carousel-dots.js -- */
.tt-carousel__dots { display: none; }

@media (max-width: 780px) {
	/* 4. section padding down ~30%, hero capped */
	:root {
		--wp--preset--spacing--90: clamp(20px, 4.2vw, 28px);
		--wp--preset--spacing--80: 44px;
		--wp--preset--spacing--70: 34px;
	}
	/*
	 * A floor, and no ceiling (owner, 26 Sep 2026: a hero never cuts content).
	 * The 70vh cap met overflow: hidden and could clip the card's own copy.
	 */
	.tt-hero,
	.wp-block-cover.tt-hero { min-block-size: min(70vh, 460px) !important; }
	.tt-compare { min-block-size: min(60vh, 380px) !important; }

	/* 1. icon / benefit groups: 2x2 compact cards, 40px glyphs */
	.tt-icon-grid { display: grid !important; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--wp--preset--spacing--30); }
	.tt-icon-grid > .wp-block-column,
	.tt-icon-grid > * { flex-basis: auto !important; margin: 0 !important; }
	.tt-icon-grid .is-style-card { padding: var(--wp--preset--spacing--40); block-size: 100%; }
	.tt-icon-grid .tt-icon-badge { inline-size: 56px; block-size: 56px; }
	.tt-icon-grid .tt-icon-badge .tt-ico { inline-size: 40px; block-size: 40px; }
	.tt-icon-grid h3 { font-size: var(--wp--preset--font-size--medium); }
	.tt-icon-grid p  { font-size: var(--wp--preset--font-size--small); }

	/* 1b. steps: compact numbered list, number left, text right */
	.tt-steps { display: grid !important; grid-template-columns: 1fr !important; gap: var(--wp--preset--spacing--30); counter-reset: tt-step; }
	.tt-steps .tt-step {
		display: grid;
		grid-template-columns: 44px 1fr;
		align-items: start;
		column-gap: var(--wp--preset--spacing--40);
		text-align: start;
		padding: var(--wp--preset--spacing--40);
	}
	.tt-steps .tt-step .tt-icon-badge { display: none; }
	.tt-steps .tt-step::before {
		counter-increment: tt-step;
		content: counter(tt-step);
		grid-row: 1 / span 2;
		inline-size: 44px;
		block-size: 44px;
		display: grid;
		place-items: center;
		border-radius: 50%;
		background: var(--wp--preset--color--brand-blue);
		color: #fff;
		font-weight: 700;
		font-size: var(--wp--preset--font-size--sub);
	}
	.tt-steps .tt-step h3,
	.tt-steps .tt-step p { text-align: start !important; margin: 0; }

	/* 2. card rows -> scroll-snap carousel */
	.tt-carousel { position: relative; }
	.tt-carousel .wp-block-columns,
	.tt-carousel .wc-block-product-template,
	.tt-carousel > .wp-block-group.is-layout-grid,
	.tt-carousel .wp-block-woocommerce-product-template {
		display: flex !important;
		flex-wrap: nowrap !important;
		gap: var(--wp--preset--spacing--40);
		overflow-x: auto;
		scroll-snap-type: x mandatory;
		scroll-padding-inline-start: 0;
		padding-block-end: var(--wp--preset--spacing--40);
		-webkit-overflow-scrolling: touch;
		scrollbar-width: none;
	}
	.tt-carousel .wp-block-columns::-webkit-scrollbar,
	.tt-carousel .wc-block-product-template::-webkit-scrollbar,
	.tt-carousel .wp-block-group.is-layout-grid::-webkit-scrollbar { display: none; }
	.tt-carousel .wp-block-column,
	.tt-carousel .wc-block-product-template > li,
	.tt-carousel .wp-block-group.is-layout-grid > *,
	.tt-carousel .wp-block-woocommerce-product-template > li {
		flex: 0 0 80% !important;
		max-inline-size: 80%;
		scroll-snap-align: start;
		margin: 0 !important;
	}
	.tt-carousel__dots {
		display: flex;
		justify-content: center;
		gap: 8px;
		margin-block-start: var(--wp--preset--spacing--30);
	}
	.tt-carousel__dot {
		inline-size: 7px;
		block-size: 7px;
		border-radius: 50%;
		border: 0;
		padding: 0;
		background: var(--wp--preset--color--border);
		transition: inline-size .15s ease, background .15s ease;
	}
	.tt-carousel__dot[aria-current="true"] {
		inline-size: 22px;
		border-radius: 999px;
		background: var(--wp--preset--color--brand-blue);
	}

	/* Browse grids stay grids, two up — a carousel is for a curated row, not for
	   eight products you are meant to scan. */
	.tt-product-grid:not(.tt-carousel) .wc-block-product-template,
	.tt-product-grid:not(.tt-carousel) .wp-block-woocommerce-product-template {
		display: grid !important;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: var(--wp--preset--spacing--30);
	}
	.tt-product-grid:not(.tt-carousel) .tt-card .wp-block-post-title { font-size: var(--wp--preset--font-size--small); }
	.tt-product-grid:not(.tt-carousel) .tt-card .wp-block-woocommerce-product-price { font-size: var(--wp--preset--font-size--medium); }

	/* 3. photo grids */
	/* core/gallery is flex, so flex-basis has to move too or width is ignored.
	   The gap must be pinned in the same unit the basis subtracts: the pattern
	   sets blockGap to spacing-40 (16px) while this basis assumed spacing-30
	   (12px), which made two 169px tiles plus a 16px gap overflow a 350px row by
	   4px and drop every image onto its own line. */
	.wp-block-gallery.columns-3,
	.wp-block-gallery.columns-4 {
		--wp--style--unstable-gallery-gap: 12px;
		gap: 12px !important;
	}
	.wp-block-gallery.columns-3 .wp-block-image,
	.wp-block-gallery.columns-4 .wp-block-image {
		width: calc(50% - 6px) !important;
		flex-basis: calc(50% - 6px) !important;
		flex-grow: 0 !important;
	}
	.wp-block-gallery .wp-block-image img { aspect-ratio: 4 / 3; object-fit: cover; }

	/* 5. more than six sizes: two columns */
	.tt-variants:has(> :nth-child(7)) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	.tt-variants:has(> :nth-child(7)) .tt-variant { padding-inline: var(--wp--preset--spacing--30); gap: var(--wp--preset--spacing--30); }
	.tt-variants:has(> :nth-child(7)) .tt-variant__price { font-size: var(--wp--preset--font-size--small); }
}

@media (max-width: 780px) {
	/* The spec card carries eight work photos; four is enough on a phone and it
	   takes ~800px off the section. */
	.tt-work-gallery .wp-block-image:nth-child(n + 5) { display: none; }
	/* Spec table scrolls instead of squeezing four variant columns into 390px. */
	.tt-info table { display: block; overflow-x: auto; white-space: nowrap; }
}

/* --- About / Contact ------------------------------------------------------ */
.tt-person { block-size: 100%; overflow: hidden; }
.tt-checks { list-style: none; padding-inline-start: 0; }
.tt-checks li { padding-block: 6px; padding-inline-start: 30px; position: relative; }
.tt-checks li::before {
	content: "";
	position: absolute;
	inset-inline-start: 0;
	inset-block-start: 10px;
	inline-size: 18px;
	block-size: 18px;
	border-radius: 50%;
	background: var(--wp--preset--color--line-green);
	-webkit-mask: none;
}
.tt-checks li::after {
	content: "";
	position: absolute;
	inset-inline-start: 5px;
	inset-block-start: 15px;
	inline-size: 8px;
	block-size: 4px;
	border-inline-start: 2px solid #fff;
	border-block-end: 2px solid #fff;
	rotate: -45deg;
}
.tt-line-panel { align-self: stretch; }
.tt-line-card {
	background: var(--wp--preset--color--surface-1);
	border-radius: var(--tt-radius-lg);
	padding: var(--wp--preset--spacing--50);
	box-shadow: var(--wp--preset--shadow--card);
}
.tt-form-placeholder { --tt-note: var(--wp--preset--color--brand-gold); }
.tt-placeholder-note {
	border-inline-start: 4px solid var(--tt-note);
	background: color-mix(in srgb, var(--tt-note) 12%, transparent);
	padding: var(--wp--preset--spacing--40);
	border-radius: var(--tt-radius-sm);
	font-size: var(--wp--preset--font-size--small);
}

/* --- Post thumbnails ------------------------------------------------------
 * A post with no featured image still needs the card to hold its shape, so the
 * slot carries a themed placeholder that the real image covers when there is one.
 */
.tt-thumb {
	position: relative;
	/* 4:3, not 16:9 (owner, 29 Sep 2026): the event photographs are 3:2 and 16:9 cut
	   a fifth off every one, the watermark in the corner with it */
	aspect-ratio: 4 / 3;
	overflow: hidden;
	background: var(--wp--preset--color--brand-blue-deep) url(assets/images/placeholder-post.svg) center / cover no-repeat;
}
.tt-thumb .wp-block-post-featured-image,
.tt-thumb figure { margin: 0; block-size: 100%; }
.tt-thumb img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
.tt-thumb--single { border-radius: var(--tt-radius-lg); margin-block-end: var(--wp--preset--spacing--50); }

/* --- Spec card accordions -------------------------------------------------
 * Open and inert on desktop (they read as headings), collapsible on phones.
 */
.tt-info .tt-acc { margin: 0; }
.tt-info .tt-acc > summary {
	font-size: var(--wp--preset--font-size--large);
	font-weight: 700;
	color: var(--wp--preset--color--brand-blue);
	list-style: none;
	margin-block-end: var(--wp--preset--spacing--40);
}
.tt-info .tt-acc > summary::-webkit-details-marker { display: none; }
@media (min-width: 781px) {
	.tt-info .tt-acc > summary { cursor: default; pointer-events: none; }
}
@media (max-width: 780px) {
	.tt-info .tt-acc {
		border-block-end: 1px solid var(--wp--preset--color--border);
		padding-block-end: var(--wp--preset--spacing--40);
	}
	.tt-info .tt-acc > summary {
		cursor: pointer;
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: var(--wp--preset--spacing--40);
		font-size: var(--wp--preset--font-size--sub);
	}
	.tt-info .tt-acc > summary::after {
		content: "";
		inline-size: 22px;
		block-size: 22px;
		flex: 0 0 auto;
		background: currentColor;
		-webkit-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6" stroke="black" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>') center / contain no-repeat;
		mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6" stroke="black" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>') center / contain no-repeat;
		transition: transform .2s ease;
	}
	.tt-info .tt-acc[open] > summary::after { transform: rotate(180deg); }
}

/* Legacy description: clamped on phones behind a read-more. */
.tt-longread.is-clamped {
	max-block-size: 640px;
	overflow: hidden;
	-webkit-mask-image: linear-gradient(to bottom, #000 74%, transparent);
	mask-image: linear-gradient(to bottom, #000 74%, transparent);
}
.tt-longread__more {
	display: block;
	inline-size: 100%;
	min-block-size: var(--tt-control);
	margin-block-start: var(--wp--preset--spacing--40);
	border: 1px solid var(--wp--preset--color--brand-blue);
	border-radius: var(--tt-radius-sm);
	background: transparent;
	color: var(--wp--preset--color--brand-blue);
	font: inherit;
	font-weight: 700;
	cursor: pointer;
}

/* --- Header: 88px, 64px once scrolled, blurred ---------------------------- */
.tt-header {
	--tt-h: var(--wp--custom--header--height);
	background-color: color-mix(in srgb, var(--wp--preset--color--base) 82%, transparent);
	backdrop-filter: saturate(1.4) blur(14px);
	-webkit-backdrop-filter: saturate(1.4) blur(14px);
	transition: background-color .2s ease, box-shadow .2s ease;
	padding-block: 0 !important;
}
.tt-header > .wp-block-group {
	min-block-size: var(--tt-h) !important;
	transition: min-block-size .2s ease;
}
.tt-header.is-compact { --tt-h: var(--wp--custom--header--height-compact); }
.tt-header.is-compact .wp-block-site-logo img { max-block-size: 40px; inline-size: auto; }
.tt-header .wp-block-site-logo img { max-block-size: 56px; inline-size: auto; transition: max-block-size .2s ease; }

@media (max-width: 1080px) {
	.tt-header { --tt-h: var(--wp--custom--header--height-mobile); }
}
@media (prefers-reduced-motion: reduce) {
	.tt-header, .tt-header > .wp-block-group, .tt-header .wp-block-site-logo img { transition: none; }
}

/* =========================================================================
 * MODERNISATION
 * ====================================================================== */

/* --- 3. Mobile sticky CTA bar --------------------------------------------
 * One bar, never two: the product template swaps this for its own LINE +
 * Add-to-cart bar, and the old floating LINE bubble is gone.
 */
.tt-cta-bar { display: none; }
@media (max-width: 780px) {
	/*
	 * 64px of bar: 8px of padding, a 48px button, 8px of padding, and nothing
	 * else. The home-indicator inset is added underneath rather than eaten out of
	 * the 64, so the button keeps its full height on a phone that has one.
	 */
	.tt-cta-bar {
		display: flex;
		align-items: center;
		gap: 8px;
		position: fixed;
		inset-inline: 0;
		inset-block-end: 0;
		z-index: 300;
		box-sizing: border-box;
		block-size: calc(64px + env(safe-area-inset-bottom, 0px));
		padding: 8px 12px calc(8px + env(safe-area-inset-bottom, 0px));
		background: color-mix(in srgb, var(--wp--preset--color--surface-1) 92%, transparent);
		backdrop-filter: blur(12px);
		-webkit-backdrop-filter: blur(12px);
		box-shadow: 0 -1px 0 var(--wp--preset--color--border);
	}
	body.single-product .tt-cta-bar { display: none; }
	.tt-cta-bar__line {
		flex: 1 1 auto;
		block-size: 48px;
		display: inline-flex;
		align-items: center;
		justify-content: center;
		gap: 13px;
		border-radius: var(--tt-radius-sm);
		background: var(--wp--preset--color--line-green);
		color: #fff;
		font-weight: 700;
		text-decoration: none;
	}
	.tt-cta-bar__line::before {
		content: "";
		inline-size: 26px;
		block-size: 26px;
		background: currentColor;
		-webkit-mask: var(--tt-line-icon) center / contain no-repeat;
		mask: var(--tt-line-icon) center / contain no-repeat;
	}
	.tt-cta-bar__phone {
		flex: 0 0 48px;
		/* border-box, or the 1px outline makes this button 50px against the
		   LINE button's 48 and the bar picks up 2px of slack. */
		box-sizing: border-box;
		block-size: 48px;
		border-radius: var(--tt-radius-sm);
		border: 1px solid var(--wp--preset--color--brand-blue);
		position: relative;
	}
	.tt-cta-bar__phone::after {
		content: "";
		position: absolute;
		inset: 0;
		margin: auto;
		inline-size: 22px;
		block-size: 22px;
		background: var(--wp--preset--color--brand-blue);
		-webkit-mask: var(--tt-phone-icon) center / contain no-repeat;
		mask: var(--tt-phone-icon) center / contain no-repeat;
	}
	/* keep the last section clear of the bar */
	.wp-site-blocks > footer { padding-block-end: 72px; }
}

/* --- 4. Variants ---------------------------------------------------------- */
@keyframes tt-price-bump {
	0%   { transform: none; }
	35%  { transform: translateY(-3px) scale(1.04); }
	100% { transform: none; }
}
.tt-price-bump { animation: tt-price-bump .32s ease; }
.tt-variant__lead {
	display: block;
	font-size: var(--wp--preset--font-size--micro);
	font-weight: 400;
	color: var(--wp--preset--color--muted);
}
.tt-swatch-name {
	margin-block-start: var(--wp--preset--spacing--20);
	font-weight: 700;
}
.tt-swatch-name span { color: var(--wp--preset--color--muted); font-weight: 400; }
.tt-product .reset_variations { display: none !important; }

/* --- 5. Product card ------------------------------------------------------ */
.tt-card { position: relative; }
.tt-card .wc-block-components-product-image { position: relative; overflow: hidden; }
/*
 * THE PHOTOGRAPH WHOLE, ON A BLURRED COPY OF ITSELF (owner, 26 Sep 2026) — the
 * same rule and the same numbers as the product gallery: scale before blur,
 * 1.6 because `filter: blur()` samples past the edge of the layer and fades to
 * transparent there. The box is square and a square photograph covers it, so
 * the backdrop shows only behind a photograph of another shape. `--tt-fill` is
 * written per card by tightent_product_card_extras(); nothing here fires
 * without it.
 */
.tt-card .wc-block-components-product-image[style*="--tt-fill"]::before {
	content: "";
	position: absolute;
	inset: 0;
	background: var(--tt-fill) center / cover no-repeat;
	transform: scale(1.6);
	filter: blur(22px) saturate(1.1) brightness(0.8);
}
.tt-card .wc-block-components-product-image > * { position: relative; }
/*
 * A CATEGORY AS ONE CARD (cycle "landing-page product picks"). The folding
 * tents are eight products and one idea; eight cards of the same tent is a list,
 * not a section. The card is the product card's shape exactly — same square
 * box, same blurred fill, same call to action — so a row does not have to know
 * which of its cards is a category. What differs is the line where the price
 * goes: a category has no price, it has a range, so it says how many models and
 * what the cheapest one starts at.
 */
.tt-cat-card__media img {
	inline-size: 100%;
	aspect-ratio: 1;
	object-fit: contain;
	display: block;
}
.tt-cat-card__meta {
	margin: 0;
	font-size: var(--wp--preset--font-size--large);
	font-weight: 700;
	line-height: 1.3;
	color: var(--wp--preset--color--brand-blue);
}
/* the link under the grid, in the same voice as the section's own heading */
.tt-context-grid__all a {
	font-weight: 700;
	text-decoration: underline;
	text-underline-offset: 3px;
}
/*
 * The card's call to action. It is `core/read-more` rather than a button block:
 * a button in a query loop has nowhere to get the address from, and the one
 * that was there carried `href="#"` on every card the site renders. read-more
 * is the block that exists for this and takes the permalink from the loop, so
 * it only needs to be made to look like the button it replaced.
 */
.tt-card__cta.wp-block-read-more {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 32px;
	padding: 4px 16px;
	border-radius: var(--wp--custom--radius--pill);
	text-decoration: none;
}
@media (hover: hover) {
	.tt-card:hover { transform: translateY(-3px); }
}
.tt-card { transition: transform .18s ease, box-shadow .18s ease; }
.tt-card:hover { box-shadow: 0 10px 24px rgba(0, 0, 0, .12); }
.tt-card__badge {
	position: absolute;
	inset-block-start: 10px;
	inset-inline-start: 10px;
	z-index: 2;
	padding: 3px 10px;
	border-radius: var(--wp--custom--radius--pill);
	background: var(--wp--preset--color--brand-gold);
	color: var(--wp--preset--color--brand-blue);
	font-size: var(--wp--preset--font-size--micro);
	font-weight: 700;
}
.tt-card__from {
	/* Inline now: the label and the starting price read as one sentence rather
	   than a caption above a number. Empty on a fixed-price product — see the
	   note in inc/woocommerce.php. */
	display: inline;
	font-size: var(--wp--preset--font-size--micro);
	color: var(--wp--preset--color--muted);
	font-weight: 400;
	line-height: 1.4;
	text-align: start;
}
/* whole card clickable without nesting links */
.tt-card__link {
	position: absolute;
	inset: 0;
	z-index: 1;
	border-radius: inherit;
}
.tt-card :where(a, button) { position: relative; z-index: 2; }

/* --- 6. Product sticky top bar (desktop) ---------------------------------- */
.tt-buybar {
	position: fixed;
	inset-inline: 0;
	inset-block-start: 0;
	z-index: 250;
	transform: translateY(-100%);
	transition: transform .22s ease;
	background: color-mix(in srgb, var(--wp--preset--color--surface-1) 94%, transparent);
	backdrop-filter: blur(14px);
	-webkit-backdrop-filter: blur(14px);
	box-shadow: 0 1px 0 var(--wp--preset--color--border);
}
.tt-buybar.is-visible { transform: none; }
.tt-buybar__inner {
	max-inline-size: var(--wp--style--global--content-size, 1200px);
	margin-inline: auto;
	display: flex;
	align-items: center;
	gap: var(--wp--preset--spacing--50);
	padding: 10px var(--wp--preset--spacing--50);
}
.tt-buybar__name { font-weight: 700; margin: 0; flex: 1 1 auto; }
.tt-buybar__price { font-weight: 700; color: var(--wp--preset--color--brand-blue); white-space: nowrap; }
.tt-buybar__cta { display: flex; gap: 10px; }
.tt-buybar__cta a, .tt-buybar__cta button {
	min-block-size: 40px;
	padding-inline: 18px;
	border-radius: var(--tt-radius-sm);
	border: 0;
	font: inherit;
	font-weight: 700;
	display: inline-flex;
	align-items: center;
	gap: 8px;
	text-decoration: none;
	cursor: pointer;
}
.tt-buybar__line { background: var(--wp--preset--color--line-green); color: #fff; }
.tt-buybar__cart { background: var(--wp--preset--color--brand-blue); color: #fff; }
@media (max-width: 780px) { .tt-buybar { display: none; } }

/* --- 7. FAQ: animated open/close, plus -> minus --------------------------- */
.wp-block-details.is-style-faq summary::after {
	-webkit-mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14" stroke="black" stroke-width="2.5" stroke-linecap="round"/></svg>');
	mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14" stroke="black" stroke-width="2.5" stroke-linecap="round"/></svg>');
	transition: transform .22s ease, opacity .22s ease;
}
.wp-block-details.is-style-faq[open] summary::after { transform: rotate(90deg); }
.wp-block-details.is-style-faq[open] summary::before {
	content: "";
	position: absolute;
	inset-inline-end: var(--wp--preset--spacing--60);
	inline-size: 25px;
	block-size: 25px;
	background: currentColor;
	-webkit-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5 12h14" stroke="black" stroke-width="2.5" stroke-linecap="round"/></svg>') center / contain no-repeat;
	mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5 12h14" stroke="black" stroke-width="2.5" stroke-linecap="round"/></svg>') center / contain no-repeat;
}
.wp-block-details.is-style-faq { position: relative; }
.wp-block-details.is-style-faq[open] summary::after { opacity: 0; }
.wp-block-details.is-style-faq > :not(summary) {
	animation: tt-reveal .24s ease;
}
@keyframes tt-reveal { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }

/* --- 8. Micro-interactions ------------------------------------------------ */
.wp-block-button__link { position: relative; overflow: hidden; }
@media (hover: hover) {
	.wp-block-button__link:hover { transform: translateY(-1px) scale(1.015); }
}
.wp-block-button__link:active { transform: scale(.985); }
.tt-ripple {
	position: absolute;
	border-radius: 50%;
	transform: scale(0);
	background: rgba(255, 255, 255, .45);
	animation: tt-ripple .5s ease-out forwards;
	pointer-events: none;
}
@keyframes tt-ripple { to { transform: scale(2.4); opacity: 0; } }

/* Gated on html.js: with no JS the section is simply visible, never a blank
   band where content should be. */
.js .tt-reveal { opacity: 0; transform: translateY(14px); }
.js .tt-reveal.is-in { opacity: 1; transform: none; transition: opacity .5s ease, transform .5s ease; }

/* The loaded state is the attribute's VALUE, never a class: core's lightbox
   rewrites className on every page image it opens, which used to take the blur
   off for good (interactions.js, cycle 62). The reduced-motion override below
   carries the same specificity as the blur rule (0,1,1), so it wins on source
   order alone — keep it after this one (gotcha 19). */
img[data-tt-blurup] { transition: filter .4s ease; }
img[data-tt-blurup="wait"] { filter: blur(12px); }

/* --- 9. Counter strip ----------------------------------------------------- */
.tt-counters { text-align: center; }
.tt-counter__value {
	font-size: var(--wp--preset--font-size--xx-large);
	font-weight: 700;
	color: var(--wp--preset--color--brand-gold);
	line-height: 1.2;
	display: block;
}
.tt-counter__label { color: rgba(255, 255, 255, .82); margin: 0; }

@media (prefers-reduced-motion: reduce) {
	.tt-price-bump,
	.wp-block-details.is-style-faq > :not(summary),
	.js .tt-reveal.is-in { animation: none !important; transition: none !important; }
	.js .tt-reveal { opacity: 1; transform: none; }
	.wp-block-button__link:hover,
	.wp-block-button__link:active,
	.tt-card:hover { transform: none; }
	.tt-ripple { display: none; }
	img[data-tt-blurup="wait"] { filter: none; }
	.tt-buybar { transition: none; }
}

/* --- Breadcrumbs ---------------------------------------------------------- */
.tt-breadcrumbs ol {
	display: flex;
	flex-wrap: wrap;
	gap: 6px 10px;
	list-style: none;
	margin: 0;
	padding: 0;
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--muted);
}
.tt-breadcrumbs li + li::before { content: "/"; margin-inline-end: 10px; color: var(--wp--preset--color--border); }
.tt-breadcrumbs a { color: var(--wp--preset--color--muted); text-decoration: none; }
.tt-breadcrumbs a:hover { color: var(--wp--preset--color--brand-blue); }

/* ======================================================================
 * BRIEF ALIGNMENT — 2026-09-04
 * ====================================================================== */

/* --- Brand tagline (header + footer) ------------------------------------- */
.tt-brand { gap: 2px; }
.tt-brand__tagline {
	margin: 0;
	color: var(--wp--preset--color--muted);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	line-height: 1.3;
}
.tt-footer .tt-brand__tagline {
	color: color-mix(in srgb, #fff 62%, transparent);
	margin-block: 4px var(--wp--preset--spacing--40);
}
/*
 * The header tagline sits centred under the mark, exactly as wide as it
 * (owner, 29 Sep 2026). At 10px with 0.08em tracking the line was 157px
 * under a 123px logo, so the size comes down until it fits and the
 * tracking is only the last fraction: --tt-logo-w is the logo's rendered
 * width, 14.02 the line's width per px of font. Lighter grey, still AA
 * (#767676 is 4.5:1 on white).
 */
/*
 * position: relative only where the brand is in flow: in the mobile bar
 * (site-nav/style.css) it is absolutely centred, which already makes it the
 * box the link's overlay fills - relative there put it back in the row and
 * doubled the phone header's height.
 */
.tt-header .tt-brand { --tt-logo-w: 122.6px; inline-size: fit-content; }
@media (min-width: 1081px) {
	.tt-header .tt-brand { position: relative; }
}
.tt-header .tt-brand__tagline {
	inline-size: var(--tt-logo-w);
	max-inline-size: none;
	margin-inline: 0;
	/* core's .has-micro-font-size is !important; it reads this variable, so set that */
	--wp--preset--font-size--micro: calc(var(--tt-logo-w) / 14.02);
	letter-spacing: .0085em;
	white-space: nowrap;
	text-align: center;
	color: #767676;
	/* the mark sits 2.8 % left of centre in its file (x 35-273 of 326): centre on the mark, not the box */
	translate: calc(var(--tt-logo-w) * -.0276) 0;
	/* a translated box paints above the link's overlay; let the tap through to it */
	pointer-events: none;
}
/* the logo's link covers the tagline too: one target for the whole brand */
.tt-header .tt-brand .wp-block-site-logo a::after { content: ""; position: absolute; inset: 0; }
/*
 * Not in the mobile bar (<= 1080px): the bar is 64px and the 120px logo is
 * 55 of it, so the line hung 2px out of the header over its shadow. Fitting
 * it would take the logo below 100px or the type below 7.5px.
 */
@media (max-width: 1080px) {
	.tt-header .tt-brand__tagline { display: none; }
}
/*
 * The mobile bar takes over at 1080px, not 900 (owner, 29 Sep 2026): with the
 * full menu the logo was squeezed from 1049px down (61px wide at 1000, 17px at
 * 950). In the bar the logo is the phone's 120px.
 */
@media (min-width: 782px) and (max-width: 1080px) {
	.tt-header .wp-block-site-logo,
	.tt-header .wp-block-site-logo img { inline-size: 120px; max-inline-size: 120px; block-size: auto; max-block-size: none; }
}

/* --- Desktop LINE bubble --------------------------------------------------
 * Mobile has the sticky CTA bar, so the bubble is desktop-only: two floating
 * LINE affordances on one screen is worse than one.
 */
.tt-line-bubble {
	display: none;
	position: fixed;
	inset-block-end: 24px;
	inset-inline-end: 24px;
	z-index: 320;
	inline-size: 48px;
	block-size: 48px;
	border-radius: 50%;
	background: var(--wp--preset--color--line-green);
	box-shadow: 0 6px 20px rgb(6 199 85 / 0.35);
	opacity: 0;
	transform: translateY(10px) scale(0.9);
	transition: opacity 0.25s ease, transform 0.25s ease;
}
.tt-line-bubble::after {
	content: "";
	position: absolute;
	inset: 0;
	margin: auto;
	inline-size: 28px;
	block-size: 28px;
	background: #fff;
	-webkit-mask: var(--tt-line-icon) center / contain no-repeat;
	mask: var(--tt-line-icon) center / contain no-repeat;
}
@media (min-width: 781px) {
	.tt-line-bubble { display: block; }
	.tt-line-bubble.is-visible { opacity: 1; transform: none; }
	.tt-line-bubble:hover { transform: scale(1.06); }
}
@media (prefers-reduced-motion: reduce) {
	.tt-line-bubble { transition: none; }
}

/* --- Native lightbox: dark backdrop -------------------------------------- */
.wp-lightbox-overlay .scrim {
	background-color: #0B1220;
	opacity: 0.92;
}
.wp-lightbox-overlay button.close-button { color: #fff; }

/* --- Applications grid ---------------------------------------------------- */
.tt-apps {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--wp--preset--spacing--40);
	margin-block-start: var(--wp--preset--spacing--70);
}
.tt-app {
	position: relative;
	/* Two rows: whatever is left, then the bar. A photograph ignores them and
	   fills the tile from the absolute positioning below; a cut-out takes the
	   first row and therefore ends exactly at the bar, whatever the bar's real
	   height (three lines of Thai at 390 is 59px, not the 48px it states). */
	display: grid;
	grid-template-rows: 1fr auto;
	/* border-box, or the padding is added to the stated height and the tile is
	   364px where the coverage numbers assume 340. */
	box-sizing: border-box;
	padding: 12px;
	overflow: hidden;
	border-radius: var(--tt-radius-md);
	/*
	 * A stated height, not a ratio. 4/3 of these column widths gives 292px at
	 * 1440 and 265px at 768, at which the 50px bar is 17% and 19% of the tile —
	 * both over the cap. Raising the floor while keeping `aspect-ratio` does not
	 * work: a stretched grid item takes the row height and then DERIVES its width
	 * from the ratio, so the tiles came out 340 x 4/3 = 453px wide and three of
	 * them burst the 1200px grid (77px of document overflow at 768).
	 */
	block-size: 340px;
	text-decoration: none;
	isolation: isolate;
}
.tt-app__img {
	position: absolute;
	inset: 0;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
	transition: transform 0.5s ease;
}
/*
 * A tile whose slot is empty shows a category cut-out, not a photograph, and a
 * cut-out cropped to fill the tile loses the product — the same reason the
 * category tiles are `contain`. PHP marks which one the tile got; the fit
 * follows from the attribute, so it is correct in the first paint.
 */
.tt-app[data-media="fallback"] {
	/* Once the photo stops filling the tile there is nothing to see the tile
	   against, and the glass has nothing to blur — the same surface and wash the
	   category tiles use put both back. */
	background-color: var(--wp--preset--color--surface-lilac);
	background-image: radial-gradient(
		ellipse 80% 60% at 50% 38%,
		color-mix(in srgb, var(--wp--preset--color--brand-blue) 8%, transparent) 0%,
		transparent 70%
	);
}
.tt-app[data-media="fallback"] .tt-app__img {
	inset: 8px;
	inline-size: calc(100% - 16px);
	block-size: calc(100% - 16px);
	object-fit: contain;
	object-position: center;
}
/* The dark scrim over the bottom half of every photo is gone: the panel below
   carries the contrast on its own, measured rather than assumed. */
.tt-app__bar {
	grid-row: 2;
	z-index: 2;
}
/*
 * Fixed navy, not the brand-blue preset. The panel sits on a photograph, so it
 * is photo context, not page context — and the dark style variation turns
 * brand-blue into #5B9BE8, which on a near-white panel would fall to about 2:1.
 * Same reasoning as the white text on .tt-hero and .tt-compare.
 */
.tt-app__title {
	color: #00366F;
	font-weight: 700;
	font-size: var(--wp--preset--font-size--medium);
	line-height: 1.4;
}
.tt-app__line {
	/* #7D7D7D measured 2.08:1 on the panel over a dark photo — a fail. This is
	   5.18:1 in the same place and 10.28:1 over a white one. */
	color: #354156;
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.5;
}
.tt-app__arrow {
	display: grid;
	place-items: center;
	transition: background-color 0.25s ease, color 0.25s ease;
}
.tt-app__arrow svg { inline-size: 14px; block-size: 14px; }
.tt-app:hover .tt-app__img,
.tt-app:focus-visible .tt-app__img { transform: scale(1.07); }
.tt-app:hover .tt-app__arrow,
.tt-app:focus-visible .tt-app__arrow {
	background-color: var(--wp--preset--color--brand-gold);
	color: #00366F;
}
@media (max-width: 900px) {
	.tt-apps { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 600px) {
	.tt-apps { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
	.tt-app__img, .tt-app__arrow { transition: none; }
	.tt-app:hover .tt-app__img { transform: none; }
}

/* --- Testimonial project photo -------------------------------------------- */
.tt-quote__photo { margin-block-start: var(--wp--preset--spacing--40); }
.tt-quote__photo img { inline-size: 100%; }

/* --- Gold highlight inside a heading -------------------------------------- */
/*
 * Brand gold as TEXT, on a light ground, is a darker gold.
 *
 * #DEB200 on white is 2.00:1 — it is a colour for a badge, a rule or an icon,
 * where WCAG asks nothing of it, and not for a word somebody has to read.
 * #806700 is the same hue at 5.44:1 on white — and, which is the point, 5.05:1
 * on the pale blue tint the highlighted numbers actually sit on. The first
 * choice, #8A6F00, measured 4.82:1 on white and 4.48:1 on that tint: it would
 * have shipped a "passes 4.5:1" decision that did not, on the one ground it was
 * used on. The bright gold is untouched everywhere it is decoration, including
 * the footer headings, which sit on navy.
 */
.tt-hl-gold { color: var(--wp--custom--gold--text); font-weight: 700; }
.tt-stars { color: var(--wp--custom--gold--text); }

/* --- Intro split ---------------------------------------------------------- */
@media (max-width: 781px) {
	/* photo first on a phone, so the section still opens on an image */
	.tt-intro-split { flex-wrap: wrap; }
}

/* --- Assembly showcase ---------------------------------------------------- */
.tt-assembly__lead { color: color-mix(in srgb, #fff 72%, transparent); }
.tt-assembly__grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--wp--preset--spacing--40);
	margin-block-start: var(--wp--preset--spacing--70);
}
.tt-clip { margin: 0; }
.tt-clip__frame {
	position: relative;
	display: block;
	overflow: hidden;
	border-radius: var(--tt-radius-md);
	aspect-ratio: 16 / 9;
	border: 1px solid color-mix(in srgb, #fff 18%, transparent);
}
/*
 * SINGLE-PHOTO FRAMES NEVER CUT THE SUBJECT (owner, 21 Sep 2026).
 *
 * The same rule product pages have followed since 373470c, now on the landing,
 * Indoor, Outdoor and About pages. Measured before the change: the "กางเสร็จใน
 * 1 นาที" posters lost 44% of the picture at both widths — square stand-ins in
 * a 16:9 frame — the brand video still lost 26% and the application proof 25%.
 *
 * Each frame keeps its own box, so nothing on the page moves. The photograph
 * sits WHOLE inside it, on a blurred copy of itself where the shapes disagree,
 * which is the same fill the product frames use. A photograph whose shape
 * already matches its frame looks exactly as it did — fitted and filled are the
 * same picture when the ratios agree.
 *
 * The blurred copy is the element's own `--tt-fill`, set in the pattern from
 * the same URL, so there is no second <img> and nothing for a screen reader to
 * announce twice.
 *
 * COVER BACKGROUNDS ARE NOT HERE, deliberately, for the reason the original
 * rule gives about the band: a cover IS a wide crop, and a whole photograph
 * letterboxed into one would be mostly blur.
 */
.tt-clip__img,
.tt-clip__frame video,
.tt-clip__frame img,
.tt-brandvid__img,
.tt-proof__img {
	inline-size: 100%;
	block-size: 100%;
	object-fit: contain;
	object-position: center;
	display: block;
}
.tt-clip__img,
.tt-brandvid__img,
.tt-proof__img { position: relative; z-index: 1; }
.tt-clip__frame,
.tt-brandvid__frame,
.tt-proof__media { position: relative; isolation: isolate; overflow: hidden; }
/*
 * The backdrop is drawn by the FRAME, and the frame is where `--tt-fill` is
 * set. The first version put the property on the <img> and read it on the
 * frame's ::before — custom properties inherit DOWNWARDS, so the frame could
 * not see it and every backdrop rendered empty.
 */
.tt-clip__frame::before,
.tt-brandvid__frame::before,
.tt-proof__media::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	background: var(--tt-fill) center / cover no-repeat;
	/* 1.6, not 1.4 — blur() samples past the edge of the layer (373470c) */
	transform: scale(1.6);
	filter: blur(22px) saturate(1.1) brightness(0.8);
}
.tt-clip__index {
	position: absolute;
	inset-block-start: 8px;
	inset-inline-start: 8px;
	padding: 2px 7px;
	border-radius: 4px;
	/* the poster frames are product cut-outs on white, so the index needs its
	   own ground rather than relying on the photo behind it */
	background: color-mix(in srgb, #0B1220 62%, transparent);
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 12px;
	letter-spacing: 0.12em;
	color: #fff;
}
.tt-clip__play {
	position: absolute;
	inset: 0;
	margin: auto;
	inline-size: 52px;
	block-size: 52px;
	display: grid;
	place-items: center;
	border-radius: 50%;
	background: color-mix(in srgb, #0B1220 55%, transparent);
	border: 1.5px solid color-mix(in srgb, #fff 70%, transparent);
	color: #fff;
	backdrop-filter: blur(4px);
	-webkit-backdrop-filter: blur(4px);
}
.tt-clip__play svg { inline-size: 22px; block-size: 22px; margin-inline-start: 2px; }
.tt-clip__caption {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 12px;
	margin-block-start: 12px;
	padding-block-start: 10px;
	border-block-start: 1px solid color-mix(in srgb, #fff 18%, transparent);
}
.tt-clip__name { color: #fff; font-weight: 500; }
.tt-clip__time {
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: var(--wp--preset--font-size--small);
	letter-spacing: 0.06em;
	color: var(--wp--preset--color--brand-gold);
	white-space: nowrap;
}
@media (max-width: 781px) {
	.tt-assembly__grid { grid-template-columns: 1fr; }
}

/* --- Brand video captions -------------------------------------------------- */
.tt-video__caption {
	margin-block-start: 10px;
	color: var(--wp--preset--color--muted);
}
.tt-video__caption strong { color: var(--wp--preset--color--brand-blue); }

/* --- Compare slider: sun / moon handle ------------------------------------ */
.tt-compare__handle--daynight {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 4px;
	font-size: 0;
}
.tt-compare__handle--daynight svg {
	inline-size: 17px;
	block-size: 17px;
	display: block;
}
.tt-compare__sun { color: var(--wp--preset--color--brand-gold); }
.tt-compare__moon { color: var(--wp--preset--color--brand-blue-deep); }

/* --- Ghost button ---------------------------------------------------------
 * Third-tier CTA: reads as a link with a hit area, so it never competes with
 * the green LINE button next to it.
 */
.wp-block-button.is-style-ghost > .wp-block-button__link {
	background: transparent;
	color: var(--wp--preset--color--brand-blue);
	border: 1px dashed color-mix(in srgb, var(--wp--preset--color--brand-blue) 45%, transparent);
	font-weight: 500;
}
.wp-block-button.is-style-ghost > .wp-block-button__link:hover,
.wp-block-button.is-style-ghost > .wp-block-button__link:focus-visible {
	background: var(--wp--preset--color--surface-blue-soft);
	border-style: solid;
}



/* --- Product card feature line ---------------------------------------------
 * One line, never two: the line is the only thing between the title and the
 * price, and letting it wrap moved the price on that one card. It is printed
 * even when a product has no features (`.is-empty`) so its height is reserved
 * and every price in a row starts level.
 */
.tt-card__features {
	display: block;
	margin-block: 4px 0;
	color: var(--wp--preset--color--muted);
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.4;
	min-block-size: 1.4em;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	text-align: start;
}
/* A middle dot with half an em of air either side, not a drawn rule: at this
   size a 1px bar read as a rendering artefact. */
.tt-card__feature + .tt-card__feature::before {
	content: "·";
	margin-inline: .5em;
	color: var(--wp--preset--color--muted);
}
.tt-card__feature { white-space: nowrap; }

/* --- In-page anchors ------------------------------------------------------- */
html { scroll-behavior: smooth; }
[id="event-sets"] { scroll-margin-block-start: 96px; }
@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }
}

/* The constrained-layout rule that centres group children has the same
   specificity as .tt-badge and is emitted after this file, so the badge needs
   one more class to stay pinned to the start of the card. */
.wp-block-group > .tt-badge { margin-inline: 0 auto; }

/* ======================================================================
 * CYCLE 10 — polish
 * ====================================================================== */

/* --- Brand video: real grid, no empty cells ------------------------------
 * The old 62% / 38% two-column build left the small clips stacked against a
 * much taller left column, so the section read as one poster and two floating
 * play triangles over whitespace. Lead clip spans both rows; every cell is
 * 16:9 and always has a poster.
 */
.tt-brandvid {
	display: grid;
	grid-template-columns: 2.05fr 1fr;
	grid-template-rows: auto auto;
	gap: 16px;
	margin-block-start: var(--wp--preset--spacing--70);
	align-items: start;
}
.tt-brandvid__item { margin: 0; }
/*
 * Every media box has a declared aspect ratio and the media inside it is
 * cropped to fit. That is the whole point: the section is the same height
 * whatever shape the file somebody uploads happens to be.
 *
 * It used to let the lead frame stretch — `aspect-ratio: auto` with
 * `flex: 1 1 auto` — on the reasoning that a strict ratio would leave a hole
 * under it. Measured, that frame came out 796 wide by **1500 tall** and was
 * dragging the whole section to 1536px. A hole is a layout problem; a box that
 * grows without limit is a different page.
 */
.tt-brandvid__item--lead { grid-row: span 2; }
.tt-brandvid__item--lead .tt-brandvid__frame { aspect-ratio: 4 / 3; }
.tt-brandvid__frame {
	position: relative;
	display: block;
	overflow: hidden;
	border-radius: 15px;
	aspect-ratio: 16 / 9;
	background: var(--wp--preset--color--surface-2);
}
/* Poster and clip alike — a <video> ignores object-fit unless it is told.
   `contain` since 21 Sep: this is a single-photo frame and must not cut the
   subject; the blurred fill behind it is drawn by .tt-brandvid__frame::before. */
.tt-brandvid__img,
.tt-brandvid__frame video,
.tt-brandvid__frame img {
	inline-size: 100%;
	block-size: 100%;
	object-fit: contain;
	object-position: center;
	display: block;
}
.tt-brandvid__play {
	position: absolute;
	inset: 0;
	margin: auto;
	inline-size: 62px;
	block-size: 62px;
	display: grid;
	place-items: center;
	border-radius: 50%;
	background: color-mix(in srgb, #0B1220 45%, transparent);
	border: 1.5px solid color-mix(in srgb, #fff 75%, transparent);
	color: #fff;
	backdrop-filter: blur(4px);
	-webkit-backdrop-filter: blur(4px);
}
.tt-brandvid__play svg { inline-size: 26px; block-size: 26px; margin-inline-start: 3px; }
.tt-brandvid .tt-video__caption { margin-block-start: 10px; }
@media (max-width: 781px) {
	.tt-brandvid { grid-template-columns: 1fr; }
	.tt-brandvid__item--lead { grid-row: auto; }
}

/* --- Intro split: the image column is half the row, not a thumbnail ------- */
.tt-intro-split > .wp-block-column { flex-basis: 50%; }
.tt-intro-split .tt-intro-split__img { margin: 0; inline-size: 100%; }
.tt-intro-split .tt-intro-split__img img {
	inline-size: 100%;
	block-size: auto;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	border-radius: 16px;
	display: block;
}

/* --- "Most Popular" inline above the title ------------------------------- */
.tt-badge--inline {
	display: inline-block;
	inline-size: fit-content;
	margin: 0 auto 0 0;
	padding: 4px 12px;
	border-radius: var(--wp--custom--radius--pill);
	background: var(--wp--preset--color--brand-gold);
	color: var(--wp--preset--color--brand-blue);
	font-weight: 700;
}

/* --- Event set cards: two buttons, one row at desktop -------------------- */
.tt-set .wp-block-buttons { flex-wrap: nowrap; }
@media (max-width: 900px) {
	.tt-set .wp-block-buttons { flex-wrap: wrap; }
}

/* --- Applications grid on a phone: two up, fixed height ------------------ */
@media (max-width: 600px) {
	.tt-apps { grid-template-columns: repeat(2, 1fr); gap: 12px; }
	/* 265 to match the category tiles beside it, and because a 44px bar is
	   16.6% of it — inside the 20% this width allows. */
	.tt-app { block-size: 265px; }
	.tt-app { padding: 8px; }
	.tt-app__title {
		font-size: var(--wp--preset--font-size--small);
		/* Three lines here. งานแสดงสินค้าและกิจกรรมธุรกิจ needs all three in a
		   169px tile, and only the tiles that need them grow — the bar height is
		   a minimum, not a fixed value. */
		-webkit-line-clamp: 3;
		line-clamp: 3;
	}
	/* No safe-area override any more: the grid row ends where the bar starts, so
	   a taller bar takes its room from the image by itself. */
	.tt-app__line { font-size: var(--wp--preset--font-size--micro); }
	.tt-app__arrow svg { inline-size: 12px; block-size: 12px; }
}

/* --- Case cards and the event gallery: two up on a phone -----------------
 * These are grid layouts nested one level deeper than the ≤900px rule above
 * reaches, so at 390px they were still rendering four columns and breaking the
 * Thai labels to one character per line.
 */
/* The card photo has an aspect ratio but no width, so in a one-column grid it
   kept its intrinsic size and sat in the corner of a full-width card. */
.tt-case .wp-block-image,
.tt-case .wp-block-image img { inline-size: 100%; display: block; }

/* --- Landing hero glass card on a phone ---------------------------------- */
@media (max-width: 600px) {
	.tt-hero .wp-block-group.is-style-glass {
		padding: 20px !important;
	}
	.tt-hero .is-style-glass .wp-block-buttons {
		flex-wrap: wrap;
		gap: 10px;
	}
	.tt-hero .is-style-glass .wp-block-button { width: auto; }
	/* The two labels together need ~270px; the glass card gives ~270 once the
	   padding is 20. Trimming the horizontal padding and the label size keeps
	   them on one row, and flex-wrap above still lets them drop if a longer
	   label ever arrives. */
	.tt-hero .is-style-glass .wp-block-button__link {
		width: auto;
		padding-inline: 14px;
		font-size: var(--wp--preset--font-size--small);
		white-space: nowrap;
	}
	/* no line clamp: a hero shows its whole sentence (owner, 26 Sep 2026) */
}

/* --- Shop chips: signal that the row scrolls ----------------------------- */
@media (max-width: 780px) {
	.tt-chips-row { position: relative; }
	.tt-chips-row .wp-block-group {
		/* the last chip stops ~24px short of the edge, and the row fades out, so
		   it is visible that there is more to the right */
		padding-inline-end: 24px;
		-webkit-mask-image: linear-gradient(to right, #000 calc(100% - 44px), transparent 100%);
		mask-image: linear-gradient(to right, #000 calc(100% - 44px), transparent 100%);
		scrollbar-width: none;
	}
	.tt-chips-row .wp-block-group::-webkit-scrollbar { display: none; }
}

/* --- Stacked event sets on a phone --------------------------------------- */
/* --- Footer link columns two up on a phone ------------------------------- */
@media (max-width: 781px) {
	.tt-footer .wp-block-columns {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: var(--wp--preset--spacing--60);
	}
	.tt-footer .wp-block-columns > .wp-block-column { flex-basis: auto !important; }
	/* the brand block and the contact block keep the full width */
	.tt-footer .wp-block-columns > .wp-block-column:first-child,
	.tt-footer .wp-block-columns > .wp-block-column:last-child { grid-column: 1 / -1; }
}

/* ======================================================================
 * CYCLE 10b
 * ====================================================================== */

/* --- Product card: one baseline for every price in a row ------------------
 * The feature line is optional, so a card that has one pushed its price a line
 * lower than its neighbours. Rather than reserve a fixed slot — which would
 * leave a hole on every card without features — the text block is a flex column
 * and its last child is pushed to the bottom, so prices line up whatever sits
 * above them.
 */
.tt-product-grid .tt-card,
.tt-product-row .tt-card {
	display: flex;
	flex-direction: column;
	block-size: 100%;
}
.tt-product-grid .tt-card > .wp-block-group,
.tt-product-row .tt-card > .wp-block-group {
	display: flex;
	flex-direction: column;
	flex: 1 1 auto;
}
/* Superseded by .tt-card__foot — kept out deliberately: with the feature line
   now always present, "push the last child down" and "pin the foot" were two
   rules doing one job, and the loser was whichever loaded second. */

/* --- Pre-footer LINE band ------------------------------------------------
 * The band was a full-bleed #30D935 wall. Green is a button colour from here
 * on: as a background it fought the CTA sitting inside it and every photo above
 * it. The band now uses the same navy as the factory section, and the only
 * green on the section is the LINE button itself.
 */
.tt-line-band__lead { color: color-mix(in srgb, #fff 78%, transparent); }

/* --- Mobile sticky bar: white ground, green button ----------------------- */
@media (max-width: 780px) {
	.tt-cta-bar {
		background: #fff;
		backdrop-filter: none;
		-webkit-backdrop-filter: none;
		box-shadow: 0 -2px 12px rgb(11 18 32 / 0.10);
		border-block-start: 1px solid var(--wp--preset--color--border);
		gap: 10px;
		padding: 8px 12px calc(8px + env(safe-area-inset-bottom));
		block-size: calc(64px + env(safe-area-inset-bottom));
		align-items: center;
	}
	.tt-cta-bar__line {
		flex: 1 1 auto;
		block-size: 48px;
		background: var(--wp--preset--color--line-green);
		color: #fff;
	}
	.tt-cta-bar__phone {
		flex: 0 0 48px;
		block-size: 48px;
		background: #fff;
		border: 1px solid var(--wp--preset--color--brand-blue);
	}
	.wp-site-blocks > footer { padding-block-end: 84px; }

}

/* The product page swaps in its own bar: LINE green + Add to Cart navy, on the
   same white ground. Its base rules live at 781px, so this matches that
   breakpoint rather than opening a one-pixel window where the bar is unstyled. */
@media (max-width: 781px) {
	.tt-sticky-bar {
		background: #fff;
		box-shadow: 0 -2px 12px rgb(11 18 32 / 0.10);
		border-block-start: 1px solid var(--wp--preset--color--border);
	}
	.tt-sticky-bar .wp-block-button__link { block-size: 48px; }
}

/* ======================================================================
 * CYCLE 10c
 * ====================================================================== */

/* --- Home compare slider sits in the content column ---------------------- */
.tt-compare--boxed {
	border-radius: var(--wp--custom--radius--hero);
	overflow: hidden;
}

/* --- Application proof --------------------------------------------------- */
.tt-proof {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var(--wp--preset--spacing--70);
	align-items: center;
}
/* The ratio belongs to the box, not to the media inside it — same rule as the
   brand video tiles. A <video> with no intrinsic size cannot then stretch it. */
.tt-proof__media {
	border-radius: 16px;
	overflow: hidden;
	background: var(--wp--preset--color--surface-2);
	aspect-ratio: 4 / 3;
}
/* `contain` since 21 Sep — single-photo frame, blurred fill behind it. */
.tt-proof__img,
.tt-proof__video,
.tt-proof__media video,
.tt-proof__media img {
	inline-size: 100%;
	block-size: 100%;
	object-fit: contain;
	object-position: center;
	display: block;
}
.tt-proof__heading { margin: 0 0 var(--wp--preset--spacing--50); }
.tt-proof__points {
	list-style: none;
	padding: 0;
	margin: 0 0 var(--wp--preset--spacing--50);
	display: grid;
	gap: var(--wp--preset--spacing--40);
}
.tt-proof__point {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 14px;
	align-items: start;
	margin: 0;
}
.tt-proof__icon {
	display: grid;
	place-items: center;
	inline-size: 40px;
	block-size: 40px;
	border-radius: 50%;
	background: var(--wp--preset--color--surface-blue-soft);
	color: var(--wp--preset--color--brand-blue);
	flex: none;
}
.tt-proof__point-title { display: block; color: var(--wp--preset--color--brand-blue); }
.tt-proof__point-line { display: block; color: var(--wp--preset--color--muted); font-size: var(--wp--preset--font-size--small); line-height: 1.55; }
.tt-proof__text { color: var(--wp--preset--color--muted); margin: 0 0 var(--wp--preset--spacing--40); }
.tt-proof__link {
	font-weight: 700;
	color: var(--wp--preset--color--brand-blue);
	text-decoration: none;
}
.tt-proof__link:hover { text-decoration: underline; }
@media (max-width: 900px) {
	.tt-proof { grid-template-columns: 1fr; gap: var(--wp--preset--spacing--60); }
}

/* --- Shop USP strip: icon above its label -------------------------------- */
.tt-usp {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: 10px;
}
.tt-usp__icon {
	display: grid;
	place-items: center;
	inline-size: 48px;
	block-size: 48px;
	border-radius: 50%;
	background: var(--wp--preset--color--surface-blue-soft);
	color: var(--wp--preset--color--brand-blue);
}
.tt-usp > p { margin: 0; }

/* --- One card: the name never runs past two lines ------------------------ */
.tt-card .wp-block-post-title {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

/* --- Section head: heading left, button right, one line on a phone ------- */
.tt-head-mini { display: none; }
@media (max-width: 600px) {
	.tt-sec-head {
		flex-wrap: nowrap !important;
		gap: 12px !important;
		align-items: center;
	}
	.tt-sec-head .wp-block-heading { font-size: var(--wp--preset--font-size--sub); }
	/* the full heading wraps to three lines at this width and pushes the button
	   onto its own row; the short form keeps them side by side */
	.tt-head-full { display: none; }
	.tt-head-mini { display: inline; }
	.tt-sec-head .wp-block-buttons { flex: none; }
	.tt-sec-head .wp-block-button__link { white-space: nowrap; }
}

/* --- Contact ------------------------------------------------------------- */
.tt-contact {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--wp--preset--spacing--70);
}
@media (min-width: 900px) {
	.tt-contact { grid-template-columns: 0.85fr 1.15fr; gap: var(--wp--preset--spacing--80); }
}
.tt-contact__h2 { margin: 0 0 var(--wp--preset--spacing--50); }
.tt-contact__block { margin-block-end: var(--wp--preset--spacing--50); }
.tt-contact__label {
	display: block;
	margin-block-end: 4px;
	color: var(--wp--preset--color--muted);
	font-size: var(--wp--preset--font-size--small);
	letter-spacing: 0.04em;
	text-transform: uppercase;
}
.tt-contact__value { margin: 0; line-height: 1.7; }
.tt-contact__value a { color: var(--wp--preset--color--brand-blue); text-decoration: none; font-weight: 500; }
.tt-contact__value a:hover { text-decoration: underline; }
.tt-contact__intro { color: var(--wp--preset--color--muted); margin: 0 0 var(--wp--preset--spacing--50); }

.tt-contact__socials { display: flex; gap: 12px; margin-block: var(--wp--preset--spacing--50); }
.tt-contact__social {
	inline-size: 44px;
	block-size: 44px;
	border-radius: 50%;
	border: 1px solid var(--wp--preset--color--border);
	position: relative;
	transition: border-color .2s ease, background-color .2s ease;
}
.tt-contact__social::after {
	content: "";
	position: absolute;
	inset: 0;
	margin: auto;
	inline-size: 20px;
	block-size: 20px;
	background: var(--wp--preset--color--brand-blue);
	-webkit-mask: var(--tt-icon) center / contain no-repeat;
	mask: var(--tt-icon) center / contain no-repeat;
}
.tt-contact__social--facebook { --tt-icon: var(--tt-facebook-icon); }
.tt-contact__social--instagram { --tt-icon: var(--tt-instagram-icon); }
.tt-contact__social--line { --tt-icon: var(--tt-line-icon); }
.tt-contact__social:hover { border-color: var(--wp--preset--color--brand-blue); }
/* the only green on this page, besides the LINE button in the form column */
/* the app icon on the white circle (owner, 1 Oct 2026), 26px like every LINE mark */
.tt-contact__social--line::after { inline-size: 26px; block-size: 26px; background: var(--tt-line-app-icon) center / contain no-repeat; -webkit-mask: none; mask: none; }

.tt-contact__qr { display: flex; align-items: center; gap: 14px; }
.tt-contact__qr img {
	inline-size: 120px;
	block-size: 120px;
	border-radius: 10px;
	border: 1px solid var(--wp--preset--color--border);
	background: #fff;
	object-fit: contain;
}
.tt-contact__qr-note { color: var(--wp--preset--color--muted); font-size: var(--wp--preset--font-size--small); line-height: 1.6; }

/* --- Contact form, styled to the theme ----------------------------------- */
.tt-contact__form .frm-fluent-form .ff-el-input--label label {
	font-weight: 700;
	color: var(--wp--preset--color--brand-blue);
	margin-block-end: 6px;
}
.tt-contact__form .frm-fluent-form .ff-el-form-control {
	/* Fluent Forms does not set border-box on its controls, so 100% width plus
	   this padding pushed the field past the viewport — 10px of horizontal
	   scroll on Contact at 390. */
	box-sizing: border-box;
	inline-size: 100%;
	min-block-size: 48px;
	padding: 10px 14px;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-radius-sm);
	background: var(--wp--preset--color--surface-1);
	font-family: inherit;
	font-size: var(--wp--preset--font-size--medium);
	color: var(--wp--preset--color--contrast);
}
.tt-contact__form .frm-fluent-form textarea.ff-el-form-control { min-block-size: 132px; }
.tt-contact__form .frm-fluent-form .ff-el-form-control:focus {
	outline: 2px solid var(--wp--preset--color--accent-blue);
	outline-offset: 1px;
	border-color: transparent;
}
.tt-contact__form .frm-fluent-form .ff-el-group { margin-block-end: var(--wp--preset--spacing--40); }
.tt-contact__form .frm-fluent-form .ff-btn-submit {
	min-block-size: 48px;
	padding-inline: 28px;
	border: 0;
	border-radius: var(--tt-radius-sm);
	background: var(--wp--preset--color--brand-blue);
	color: #fff;
	font-family: inherit;
	font-weight: 700;
	cursor: pointer;
}
.tt-contact__form .frm-fluent-form .ff-btn-submit:hover { filter: brightness(1.08); }
.tt-contact__form .frm-fluent-form .error-text,
.tt-contact__form .frm-fluent-form .ff-el-is-error .text-danger { color: #B3261E; font-size: var(--wp--preset--font-size--small); }
.tt-contact__form .ff-message-success {
	padding: var(--wp--preset--spacing--50);
	border-radius: var(--tt-radius-sm);
	background: var(--wp--preset--color--surface-blue-soft);
	color: var(--wp--preset--color--brand-blue);
	font-weight: 500;
}

/* --- Office map: a picture until somebody asks for the real one ---------- */
/*
 * The frame moved from the link to an inner element (22 Sep 2026): the picture
 * is now a <button> that swaps itself for the Google embed, and the two deep
 * links sit UNDER it rather than inside it. `.tt-map` is therefore the column,
 * not the card.
 */
.tt-map { display: block; max-inline-size: 600px; }
.tt-map__load,
.tt-map__frame {
	display: block;
	position: relative;
	inline-size: 100%;
	padding: 0;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: 16px;
	overflow: hidden;
	background: none;
	text-align: start;
	font: inherit;
	color: inherit;
	cursor: pointer;
}
/* A stated height, because an iframe has no intrinsic one. Close to the
   picture's box (measured 388px at 1440, 271 at 390) so the click moves the
   page by a few dozen pixels rather than by a screenful. */
.tt-map__frame { block-size: 270px; cursor: auto; }
@media (min-width: 900px) { .tt-map__frame { block-size: 386px; } }
.tt-map img { inline-size: 100%; block-size: auto; display: block; }
/* The two ways out, under the map. The Google one is the button because it is
   what almost everybody wants; Apple Maps is a link beside it for the iPhone. */
.tt-map__links {
	display: flex;
	flex-wrap: wrap;
	gap: 12px 20px;
	align-items: center;
	margin-block-start: 16px;
}
.tt-map__btn {
	display: inline-block;
	padding: 12px 24px;
	border-radius: 999px;
	background: var(--wp--preset--color--brand-blue);
	color: #fff;
	font-weight: 700;
	text-decoration: none;
}
.tt-map__btn:hover,
.tt-map__btn:focus-visible { background: var(--wp--preset--color--deep-blue, var(--wp--preset--color--brand-blue)); color: #fff; }
.tt-map__apple {
	color: var(--wp--preset--color--brand-blue);
	font-size: var(--wp--preset--font-size--small);
	text-decoration: underline;
}
.tt-map__cta {
	display: flex;
	flex-wrap: wrap;
	gap: 4px 16px;
	justify-content: space-between;
	align-items: baseline;
	padding: 12px 16px;
	background: var(--wp--preset--color--surface-1);
	color: var(--wp--preset--color--muted);
	font-size: var(--wp--preset--font-size--small);
}
.tt-map__open { color: var(--wp--preset--color--brand-blue); font-weight: 700; white-space: nowrap; }

/* ======================================================================
 * CYCLE 10d
 * ====================================================================== */

/* --- 1. "Most Popular" no longer pushes the card contents down ----------- */
.tt-set { position: relative; }
.tt-set .tt-badge,
.tt-set .tt-badge--inline {
	position: absolute;
	inset-block-start: 12px;
	inset-inline-end: 12px;
	z-index: 2;
	margin: 0;
}
/* the image is the top of the card, so the chip needs to read on a photo */
.tt-set .tt-badge { box-shadow: 0 2px 8px rgb(11 18 32 / 0.18); }
/*
 * Taking the badge out of flow is not enough: it is still a DOM sibling, so the
 * constrained layout's `> * + *` gap landed on the title and pushed the featured
 * card 12px below the other two.
 */
.tt-set .tt-badge + * { margin-block-start: 0 !important; }

/* --- 2. Chips scroll rather than wrap on tablets too --------------------- */
@media (max-width: 1023px) {
	.tt-chips-row .wp-block-group {
		justify-content: flex-start;
		flex-wrap: nowrap;
		overflow-x: auto;
		padding-inline-end: 24px;
		-webkit-mask-image: linear-gradient(to right, #000 calc(100% - 44px), transparent 100%);
		mask-image: linear-gradient(to right, #000 calc(100% - 44px), transparent 100%);
		scrollbar-width: none;
	}
	.tt-chips-row .wp-block-group::-webkit-scrollbar { display: none; }
	/* a chip that wraps its own label defeats the point of the scroll */
	.tt-chip-link,
	.tt-chip-link a { white-space: nowrap; }
}

/* --- 3. Card names really do clamp at two lines -------------------------- */
/* The title element computes `display: flow-root`, which beat the earlier
   `-webkit-box` rule and left long names running to three lines. The clamp goes
   on the anchor inside it, which nothing else styles. */
.tt-card .wp-block-post-title,
.tt-card .wp-block-post-title > a {
	overflow-wrap: anywhere;
}
.tt-card .wp-block-post-title > a {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

/* --- 5. The map fills its column on a desktop --------------------------- */
@media (min-width: 900px) {
	.tt-map { max-inline-size: none; }
}

/* --- 6. Portfolio grid: four from 1024, two from 400, one below ----------
 * One block, in source order, because these all match at the narrow end and the
 * last one wins: the single-column rule used to sit above a later two-column
 * rule and never applied.
 */
@media (max-width: 1023px) {
	.tt-carousel.is-layout-grid,
	.wp-block-group.tt-carousel.is-layout-grid { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
}
@media (max-width: 400px) {
	/* the location line needs the room once the card is under ~170px */
	.tt-carousel.is-layout-grid,
	.wp-block-group.tt-carousel.is-layout-grid { grid-template-columns: 1fr !important; }
}

/* --- Structured product description (inc/product-description.php) ---------
 * One stylesheet for every section of every product. The legacy descriptions
 * carried their own <style> inside post_content, which wp_kses_post strips the
 * tag from and leaves the CSS as visible text — so nothing here may ever move
 * back into the content (gotcha 29).
 *
 * --- cycle 42c-template-3: one page, one rhythm ---------------------------
 *
 * Four cycles of section-by-section work had left eleven separately-designed
 * blocks stacked on top of each other: alternating tinted grounds, a navy video
 * panel, a full-bleed photo band, headings at three sizes and four layouts for
 * "some pictures and a paragraph". Each was defensible on its own and together
 * they read as a slide deck.
 *
 * What replaces it is the arrangement the legacy pages already used and the one
 * Krystian asked for: every section on white, one heading (centred, gold
 * subtitle, copy at 800px), photographs as cards with a radius and a soft
 * shadow, and a row of three pictures as the default shape. A split or an
 * asymmetric grid is now the exception, for long copy or a single photograph.
 *
 * No content-visibility here, and it is not an oversight. `content-visibility:
 * auto` on these sections measured LCP 4.8s against 5.1s and 593KB against
 * 628KB — a real win — but a section nobody has scrolled to is then genuinely
 * unrendered, so a full-page screenshot photographs eleven empty boxes and any
 * script that measures the DOM reports zeros. Revisit only with the tooling
 * fixed first.
 */
.tt-pd {
	--tt-pd-gap: var(--wp--preset--spacing--50);
	/* the one measure every column of words in a description stops at */
	--tt-pd-measure: 900px;
	/* and the one the sections themselves stop at */
	--tt-pd-content: 1200px;
	--tt-pd-card: 16px;
	--tt-pd-shadow: 0 10px 30px rgba(0, 54, 111, .10);
	/*
	 * The one tint in the description. It is not in theme.json on purpose: it is
	 * a grouping device for two adjacent sections, not a brand surface, and
	 * surface-2 (#F6F6FF) reads lilac next to the navy bands.
	 */
	/*
	 * Two different jobs, two tokens (cycle 42c-template-8).
	 *
	 * --tt-pd-grey is a COMPONENT fill: the ground behind a contained photograph,
	 * a video thumb, a size card. It stays #f8fafc because those boxes sit on
	 * white and a stronger grey would draw a border where none is wanted.
	 * --tt-pd-light is the SECTION tint from the artboard. Collapsing them into
	 * one value would have repainted five components nobody asked to change.
	 */
	--tt-pd-grey: #f8fafc;
	/* the space between the words and the edge of the window */
	--tt-pd-gutter: 24px;
}

/* ---------------------------------------------------------------- rhythm */
/*
 * Every section is white and pays the same padding. scroll-margin keeps an
 * in-page anchor clear of the fixed desktop buy bar.
 */
.tt-pd__section {
	background: none;
	padding-block: 64px;
	/* the gutter a band needs so its words never touch the window edge */
	padding-inline: var(--tt-pd-gutter);
	border: 0;
	margin-block: 0;
	scroll-margin-block-start: 84px;
}
.tt-pd__wrap { max-inline-size: var(--tt-pd-content); margin-inline: auto; }
.tt-pd__section--hero { padding-block: 48px 64px; }

/* --- the tint map ---------------------------------------------------------
 * A band is a section that carries a background to the edge of the window. The
 * <section> is already full width, so a band is nothing but a background and,
 * for the dark ones, a text colour.
 *
 * WHICH section gets which tint is decided once, in PHP
 * (tightent_description_tint), from Non's artboard. The values below are
 * sampled from that PNG (cycle 42c-template-8).
 */
.tt-pd {
	/* #F1F4FA — a cool grey-blue, sampled from the artboard. It replaces the
	   #f8fafc of 42c-template-4, which at 2 units off white barely registered as
	   a band at all. */
	--tt-pd-light: #F1F4FA;
	/*
	 * The brand gold, dark enough to be READ (cycle 42c-template-8).
	 *
	 * #DEB200 is a surface colour: on a badge, a rule or a diagram it is the brand
	 * and it is right. As small text on white it is 2.00:1 and on the light band
	 * 1.82:1 — it had been failing AA on every description subtitle since
	 * subtitles existed. #8A6900 is 5.12:1 and 4.65:1. Navy sections keep #DEB200,
	 * which is 5.96:1 there and where the bright gold belongs.
	 */
	--tt-pd-gold-ink: #8A6900;
}
.tt-pd__section--light { background: var(--tt-pd-light); }

/*
 * FLAT #00366F, not a gradient.
 *
 * 42c-template-7 used the LINE ribbon's 135deg gradient here. The artboard is
 * flat: sampled across the full width at four heights in both navy bands, every
 * pixel #00366F — which is the brand-blue token we already have, so the palette
 * gains nothing new (Krystian's decision, cycle 42c-template-8).
 */
.tt-pd__section--navy {
	background: var(--wp--preset--color--brand-blue);
	color: #fff;
	--tt-pd-dia-gold: var(--wp--preset--color--brand-gold);
	--tt-pd-dia-base: rgba(255, 255, 255, .34);
}
.tt-pd__section--navy .tt-pd__h2 { color: #fff; }
.tt-pd__section--navy .tt-pd__lead,
.tt-pd__section--navy .tt-pd__videoline { color: rgba(255, 255, 255, .78); }
.tt-pd__section--navy .tt-pd__sub { color: var(--wp--preset--color--brand-gold); }
.tt-pd__section--navy .tt-pd__diagram figcaption { color: rgba(255, 255, 255, .78); }
.tt-pd__section--navy .tt-pd__note {
	color: rgba(255, 255, 255, .72);
	border-block-start-color: rgba(255, 255, 255, .22);
}

/*
 * GREEN: LINE's own #06C755, and the text on it is NAVY.
 *
 * White on this green is 2.26:1 — fine for a button label a customer recognises
 * by its shape, and not fine for a band of body copy. Navy on it is 6.0:1. The
 * button keeps the white pill and a navy label, which is what the artboard
 * draws (Krystian's decision, cycle 42c-template-8).
 */
.tt-pd__section--green {
	background: var(--wp--preset--color--line-green);
	color: var(--wp--preset--color--brand-blue);
}
.tt-pd__section--green .tt-pd__h2,
.tt-pd__section--green strong { color: var(--wp--preset--color--brand-blue); }

/*
 * No two adjacent sections carry the SAME tint (cycle 42c-template-8). Where it
 * happens anyway — a product whose order puts two light sections together — the
 * seam between them is dropped so they read as one block rather than as two
 * panels that failed to line up. The template check fails on the arrangement;
 * this only stops it looking like a mistake in the meantime.
 */
.tt-pd__section--light + .tt-pd__section--light,
.tt-pd__section--navy + .tt-pd__section--navy { padding-block-start: 0; }

/* ---------------------------------------------------------- the heading */
/*
 * Centred title, gold subtitle under it, copy at a readable measure. The
 * selector is the DIRECT child, so the hero — whose heading sits inside its own
 * text column — keeps its left alignment without needing a rule to undo this.
 */
.tt-pd__wrap > .tt-pd__h2,
.tt-pd__wrap > .tt-pd__eyebrow,
.tt-pd__wrap > .tt-pd__pill { text-align: center; }

/*
 * ...unless the section's content is a split or a column of prose
 * (cycle 42c-template-7). Which sections those are is decided in PHP by
 * tightent_description_align() — the stylesheet only knows that the class is
 * there. Everything that was centred has to be undone together: the heading,
 * the eyebrow, the lead, and the `margin-inline: auto` that was centring the
 * lead's BOX rather than its text (gotcha 21 is the same trap in reverse —
 * reading text-align alone would have said these were already left).
 */
.tt-pd__section--left .tt-pd__wrap > .tt-pd__h2,
.tt-pd__section--left .tt-pd__wrap > .tt-pd__eyebrow,
.tt-pd__section--left .tt-pd__wrap > .tt-pd__pill { text-align: start; }
.tt-pd__section--left .tt-pd__wrap > .tt-pd__pill { margin-inline: 0; }
.tt-pd__section--left .tt-pd__lead,
.tt-pd__section--left .tt-pd__wrap > .tt-pd__p {
	text-align: start;
	margin-inline: 0;
}
.tt-pd__h2 {
	/* 36 at 1440, 28 at 390 (cycle 42c-template-7). The hero stays at 40, so a
	   description still opens on a bigger line than the sections under it. */
	font-size: clamp(28px, 1.2rem + 1.2vw, 36px);
	line-height: 1.4;
	font-weight: 700;
	color: var(--wp--preset--color--brand-blue);
	/* +8px under the title: at 36px the old .6em ran the subtitle into the copy */
	margin: 0 0 calc(.6em + 8px);
}
/*
 * Two golds, because no one gold works on both grounds (cycle 42c-template-8).
 *
 * The brand gold #DEB200 is 5.96:1 on navy and 2.00:1 on white — it has been
 * failing AA on every description subtitle since subtitles existed, and only
 * surfaced when the contrast check started measuring the LIGHT band. #8A6900 is
 * 5.12:1 on white and 4.65:1 on the light band; on navy it would be 2.33:1, so
 * navy keeps the bright one (the rule for that is with the navy block above).
 *
 * This is the subtitle only. The brand gold is untouched everywhere it is a
 * surface rather than small text — badges, the gold rule, the diagram.
 */
.tt-pd__sub {
	display: block;
	/* 17px — a step above the small preset, which at a 36px title read as a
	   caption rather than as the second half of the heading */
	font-size: 17px;
	font-weight: 500;
	color: var(--tt-pd-gold-ink);
	margin-block-start: 8px;
}
.tt-pd__lead,
.tt-pd__wrap > .tt-pd__p {
	font-size: var(--wp--preset--font-size--medium);
	color: var(--wp--preset--color--muted);
	max-inline-size: var(--tt-pd-measure);
	margin-inline: auto;
	margin-block: 0 1.2em;
	text-align: center;
}
.tt-pd__p { margin: 0 0 .9em; font-size: var(--wp--preset--font-size--medium); }
.tt-pd__section--solo { text-align: center; }
.tt-pd__section--solo .tt-pd__feat { text-align: start; }
.tt-pd__badge,
.tt-pd__eyebrow,
.tt-pd__pill {
	display: inline-block;
	background: var(--wp--preset--color--brand-gold);
	color: #2a2100;
	font-weight: 700;
	font-size: 12px;
	letter-spacing: .04em;
	padding: 5px 11px;
	border-radius: 6px;
	margin-block-end: 10px;
}
/* the video's badge is a pill, the way the legacy page drew it */
.tt-pd__pill { border-radius: 999px; padding: 6px 16px; }
.tt-pd__wrap > .tt-pd__pill { display: block; inline-size: fit-content; margin-inline: auto; }

/*
 * Footnotes. Italic, under a thin rule, and inside the text column — the print
 * section's note used to start at the very edge of its tinted panel, which read
 * as copy that had escaped the container.
 *
 * Prompt ships 400/500/700 and no italic face, so this is a synthesised oblique.
 * That is the honest trade for the shape Krystian asked for; a real italic
 * would mean a fourth font file.
 */
.tt-pd__note {
	font-size: var(--wp--preset--font-size--small);
	font-style: italic;
	color: var(--wp--preset--color--muted);
	max-inline-size: var(--tt-pd-measure);
	margin: 24px auto 0;
	padding-block-start: 12px;
	border-block-start: 1px solid var(--wp--preset--color--border);
	text-align: center;
}

/* ------------------------------------------------------------ photo card */
/*
 * The one photograph frame: radius, soft shadow, a zoom on hover, and the
 * theme's lightbox (the <figure> comes from core/image — inside a description
 * it is a layout-transparent wrapper, not a block).
 */
.tt-pd .wp-block-image { margin: 0; }
.tt-pd .wp-block-image img { display: block; inline-size: 100%; block-size: auto; }
.tt-pd__grid3 > .wp-block-image,
.tt-pd__grid4 > .wp-block-image,
.tt-pd__grid2 > .wp-block-image,
.tt-pd__cardmedia,
.tt-pd__hero-media,
.tt-pd__sizeguide,
.tt-pd__band .tt-pd__half > .wp-block-image,
.tt-pd__brandshot > .wp-block-image {
	border-radius: var(--tt-pd-card);
	overflow: hidden;
	box-shadow: var(--tt-pd-shadow);
	background: var(--wp--preset--color--surface-2);
}
.tt-pd__grid2 > .wp-block-image img,
.tt-pd__grid3 > .wp-block-image img,
.tt-pd__grid4 > .wp-block-image img,
.tt-pd__cardmedia img,
.tt-pd__hero-media img,
.tt-pd__sizeguide img,
.tt-pd__band .tt-pd__half > .wp-block-image img,
/* the set thumbnail already sits inside a bordered card, so it takes the
   radius and the zoom but not a second shadow on top of the card's own */
.tt-pd__setcard > .wp-block-image img,
.tt-pd__brandshot > .wp-block-image img { transition: transform .4s ease; }
.tt-pd__grid2 > .wp-block-image:hover img,
.tt-pd__grid3 > .wp-block-image:hover img,
.tt-pd__grid4 > .wp-block-image:hover img,
.tt-pd__cardmedia:hover img,
.tt-pd__hero-media:hover img,
.tt-pd__sizeguide:hover img,
.tt-pd__band .tt-pd__half > .wp-block-image:hover img,
.tt-pd__setcard > .wp-block-image:hover img,
.tt-pd__brandshot > .wp-block-image:hover img { transform: scale(1.05); }

/* --------------------------------------------------------------- 1. hero */
.tt-pd__hero { display: grid; gap: 14px; }
.tt-pd__hero--media {
	grid-template-columns: 1fr 1fr;
	gap: 60px;
	align-items: center;
}
.tt-pd__hero-text { min-inline-size: 0; }
.tt-pd__hero-text .tt-pd__p { text-align: start; max-inline-size: none; margin-inline: 0; }
.tt-pd__h2--hero { font-size: 40px; line-height: 1.35; text-align: start; }
.tt-pd__h2--hero .tt-pd__sub {
	font-size: var(--wp--preset--font-size--sub);
	font-weight: 400;
	color: var(--wp--preset--color--muted);
}
.tt-pd__hero-media { min-inline-size: 0; }
.tt-pd .tt-pd__heroimg { inline-size: 100%; block-size: auto; aspect-ratio: 4 / 3; object-fit: contain; }
/*
 * Natural ratio, never cropped (cycle 42c-tent-3d, tents via hero.image_fit).
 * The image takes auto sizes under two maxima — the column's width and 600px of
 * height. The ratio itself comes from an inline aspect-ratio the renderer writes
 * from the <img> width/height (gotcha 58: a lazy sizes="auto" image has no
 * natural ratio to keep). The card centres in the column and shrinks to hug it, so
 * the radius, shadow, zoom and lightbox button follow the picture and no bar of
 * card background shows.
 */
.tt-pd__hero--natural .tt-pd__hero-media { justify-self: center; max-inline-size: 100%; }
.tt-pd .tt-pd__hero--natural .tt-pd__heroimg { inline-size: auto; block-size: auto; aspect-ratio: auto; max-inline-size: 100%; max-block-size: 600px; object-fit: fill; }
.tt-pd__promobar { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0; }
.tt-pd__promo {
	background: var(--wp--preset--color--surface-2);
	border: 1px solid var(--wp--preset--color--border);
	border-radius: 8px;
	padding: 8px 14px;
	font-size: var(--wp--preset--font-size--small);
}
.tt-pd__chips {
	display: flex;
	gap: 16px;
	flex-wrap: wrap;
	border-block: 1px solid var(--wp--preset--color--border);
	padding-block: 16px;
	margin-block: 8px;
}
.tt-pd__chip { flex: 1 1 140px; }
.tt-pd__chip strong { display: block; }
.tt-pd__chip span { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }

/* the strip — the heading-less intro the backdrop and the flag still use */
.tt-pd__strip { display: grid; gap: 14px; }
.tt-pd__strip--media { grid-template-columns: 1fr 440px; gap: 60px; align-items: center; }
.tt-pd__striplead { max-inline-size: 80ch; }
.tt-pd__striplead .tt-pd__p { text-align: start; max-inline-size: none; margin-inline: 0; }
.tt-pd__striplead .tt-pd__p:last-child { margin-block-end: 0; }
.tt-pd__stripmedia {
	border-radius: var(--tt-pd-card);
	overflow: hidden;
	box-shadow: var(--tt-pd-shadow);
	background: var(--wp--preset--color--surface-2);
}
.tt-pd .tt-pd__stripimg { inline-size: 100%; block-size: auto; aspect-ratio: 4 / 3; object-fit: contain; }

/* ---------------------------------------------------------- the photo band */
/*
 * One photograph across the window with a claim on it. The section is a
 * one-cell grid: the photograph and the 1200px wrap occupy the same cell, so
 * the claim sits ON the picture without anything being positioned absolutely
 * against a height that has to be kept in sync (gotcha 27).
 */
.tt-pd__section--band {
	padding: 0;
	display: grid;
	grid-template-areas: "band";
	overflow: hidden;
}
.tt-pd__section--band > * { grid-area: band; min-inline-size: 0; }
/*
 * The photograph is the container's width, and above 1600 it keeps going past
 * it and dissolves into the page.
 *
 * One element, one mask — no second image and no overlay. The fade is
 * `(100% - container) / 2` measured against the ELEMENT's own width, so the
 * fully opaque middle lands exactly on the container: at 1920 the element is
 * 1800 wide (60..1860), the fade is 300 each side, and the opaque region is
 * 360..1560 — which is where the 1200px container sits at that width, and
 * therefore where the claim starts. Nothing to keep in sync.
 *
 * `max(0px, …)` because below the container width that expression goes
 * NEGATIVE, and a negative colour stop is a broken mask rather than no mask.
 */
.tt-pd__bandmedia { position: relative; block-size: 480px; }

@media (min-width: 1601px) {
	.tt-pd__bandmedia {
		--tt-band-fade: max(0px, calc((100% - var(--tt-pd-content)) / 2));
		inline-size: min(100%, calc(var(--tt-pd-content) + 600px));
		margin-inline: auto;
		-webkit-mask-image: linear-gradient(to right, transparent 0,
			#000 var(--tt-band-fade), #000 calc(100% - var(--tt-band-fade)), transparent 100%);
		mask-image: linear-gradient(to right, transparent 0,
			#000 var(--tt-band-fade), #000 calc(100% - var(--tt-band-fade)), transparent 100%);
	}
}
.tt-pd__bandmedia .wp-block-image { block-size: 100%; border-radius: 0; box-shadow: none; }
/*
 * Gotcha 19/29: `.tt-pd .wp-block-image img` sets `block-size: auto` with two
 * classes and an element, so a single extra class on the <img> loses. Three
 * classes and the element is what it takes to state the height here.
 */
.tt-pd .tt-pd__bandmedia .wp-block-image img {
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
	display: block;
}
/* the whole photograph, chosen on the slot: its own blurred copy fills the sides */
.tt-pd__bandmedia[data-tt-fit="whole"] { isolation: isolate; overflow: hidden; }
.tt-pd__bandmedia[data-tt-fit="whole"]::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	background: var(--tt-fill) center / cover no-repeat;
	transform: scale(1.6);
	/* true colours: blurred, never darkened (owner, 30 Sep 2026) */
	filter: blur(22px);
}
.tt-pd__bandmedia[data-tt-fit="whole"] > * { position: relative; z-index: 1; }
/*
 * The claim's wrap spans the window and pushes itself in with padding, rather
 * than being a 1200px box: `inline-size: 100%` plus padding on a content-box
 * element is 100% PLUS the padding, which is 40px of document overflow at 390.
 * `max()` puts the text at exactly the same x as every other section's — 120 at
 * 1440, the gutter on a phone — with nothing to keep in sync.
 */
.tt-pd__section--band .tt-pd__wrap {
	box-sizing: border-box;
	align-self: end;
	position: relative;
	inline-size: 100%;
	max-inline-size: none;
	padding-block: 0 40px;
	padding-inline: max(var(--tt-pd-gutter), calc((100% - var(--tt-pd-content)) / 2));
}
/*
 * A FEATHERED SHADOW UNDER THE COPY (owner, 30 Sep 2026, replacing 7127425's
 * glass panel). No box, no edge: navy a little larger than the words, blurred
 * so it is flat under them and feathers out well past them. The photograph and its side fills are untouched
 * everywhere else. A band with no copy renders no claim and so no shadow.
 * If contrast fails: the subtitle gets larger/bolder first, then the centre
 * deepens — never a wider shadow.
 *
 * The section is the size container, so the copy can be held to 70% of the
 * photograph's width (100cqw, or the photograph itself in whole-photo mode).
 */
.tt-pd__section--band { container-type: inline-size; }
.tt-pd__bandclaim {
	--tt-band-shade: .75;
	position: relative;
	isolation: isolate;
	inline-size: fit-content;
	max-inline-size: calc(.7 * min(100cqw, var(--tt-pd-content) + 600px));
}
.tt-pd__bandclaim::before {
	content: "";
	position: absolute;
	/* a blur fades over about 2x its radius: 52px of margin past the words
	   with a 24px blur keeps every line on the flat centre, and the edge
	   feathers over ~48px (an ellipse was tried and read as an oval blob) */
	inset: -52px -60px;
	z-index: -1;
	border-radius: 56px;
	background: rgb(0 30 66 / var(--tt-band-shade));
	filter: blur(24px);
	pointer-events: none;
}
/* the shadow needs room below the words, or the band's edge clips its lower half */
@media (min-width: 782px) {
	.tt-pd__section--band .tt-pd__wrap { padding-block-end: 72px; }
}
.tt-pd__bandclaim[data-tt-side="right"] { margin-inline-start: auto; text-align: end; }
.tt-pd__bandclaim[data-tt-side="right"] .tt-pd__h2--band { text-align: end; }
/*
 * Whole-photo mode: the photograph is 480px x its ratio, centred, with a
 * blurred fill either side. The copy starts 116px inside the photograph — the
 * shadow's full reach (60px + 2 x 24px blur + a margin) — and is at most 70% of it, so neither the words nor the
 * shadow sit on the fill.
 */
@media (min-width: 782px) {
	.tt-pd__section--band:has([data-tt-fit="whole"]) .tt-pd__bandclaim {
		margin-inline-start: max(0px, calc((100cqw - 480px * var(--tt-band-r, 99)) / 2 + 116px - max(var(--tt-pd-gutter), (100cqw - var(--tt-pd-content)) / 2)));
		max-inline-size: min(calc(.7 * min(100cqw, 480px * var(--tt-band-r, 99))), calc(480px * var(--tt-band-r, 99) - 232px));
	}
	.tt-pd__section--band:has([data-tt-fit="whole"]) .tt-pd__bandclaim[data-tt-side="right"] {
		margin-inline-start: auto;
		margin-inline-end: max(0px, calc((100cqw - 480px * var(--tt-band-r, 99)) / 2 + 116px - max(var(--tt-pd-gutter), (100cqw - var(--tt-pd-content)) / 2)));
	}
}
.tt-pd__h2--band { color: #fff; text-align: start; margin-block-end: .12em; }
.tt-pd__bandline {
	color: rgba(255, 255, 255, .88);
	font-size: var(--wp--preset--font-size--medium);
	margin: 0;
}

/* -------------------------------------------------------------- 2. video */
/*
 * Centred, on white. The panel is gone: it existed to hide the letterbox of a
 * 9:16 film in a 16:9 frame, and the frame is now the shape of the film.
 */
/* the video title is a section title like any other (cycle 42c-template-4
   set one size for all of them; 42c-template-3 had given this one 34px) */
/* The video heading followed the scale of cycle 42c-template-4 (32px) and was
   the one section title that did not move to 36 in template-7. Its own size is
   gone rather than restated: one scale, set in one place. */
.tt-pd__lead--video { max-inline-size: 650px; }

/*
 * The row: the player, and a stack of two square cards beside it.
 *
 * The player's track has to be a DEFINITE width — a child whose width is `100%`
 * inside an `auto` track is circular, and the grid resolves it to min-content,
 * which rendered a 360px player at 12px: exactly its two borders.
 *
 * With no photographs the row is ONE column and the player is centred in it,
 * rather than a grid with an empty track beside a 360px player.
 */
.tt-pd__videorow {
	display: grid;
	grid-template-columns: var(--tt-pd-vmax, 360px) 312px;
	gap: 16px;
	justify-content: center;
	align-items: stretch;
	margin-block-start: 8px;
}
.tt-pd__videorow--solo { grid-template-columns: var(--tt-pd-vmax, 360px); }

/*
 * The stack owns no height of its own. Its two rows are `1fr` and the cards
 * inside are absolutely positioned, so the row's height comes from the PLAYER
 * and each card works out to (640 - 16) / 2 = 312 — the figure the design asks
 * for, derived rather than typed, so it still holds if the film's ratio changes.
 *
 * Measured the other way round first: a 1080x1920 still in a 400px column is
 * 711px tall, the row became 711, and the player was stretched out of its 9:16
 * shape. Whichever item carries intrinsic height wins the row, so it has to be
 * the one whose shape matters.
 */
.tt-pd__videostack {
	display: grid;
	grid-template-rows: 1fr 1fr;
	gap: 16px;
	min-block-size: 0;
}
.tt-pd__videosquare {
	position: relative;
	border-radius: var(--tt-pd-card);
	overflow: hidden;
	box-shadow: var(--tt-pd-shadow);
	background: var(--tt-pd-grey);
	min-block-size: 0;
}
.tt-pd__videosquare .wp-block-image { position: absolute; inset: 0; }
/* gotcha 29 again: three classes and the element, or `.tt-pd .wp-block-image
   img { block-size: auto }` wins and the photograph sits at its own aspect */
.tt-pd .tt-pd__videosquare .wp-block-image img {
	inline-size: 100%;
	block-size: 100%;
	object-fit: contain;
	display: block;
	transition: transform .4s ease;
}
.tt-pd__videosquare:hover .wp-block-image img { transform: scale(1.05); }
.tt-pd__player {
	position: relative;
	/* border-box, because the 6px frame is part of the 360px the design asks
	   for — content-box made the player 372px wide on the page */
	box-sizing: border-box;
	inline-size: 100%;
	max-inline-size: var(--tt-pd-vmax, 720px);
	aspect-ratio: var(--tt-pd-ratio, 16 / 9);
	margin-inline: auto;
	border-radius: 24px;
	border: 6px solid var(--wp--preset--color--surface-2);
	box-shadow: 0 18px 50px rgba(0, 54, 111, .16);
	overflow: hidden;
	background: #000;
}
.tt-pd__videoframe {
	position: absolute;
	inset: 0;
	inline-size: 100%;
	/* gotcha 29: `.woocommerce img { height: auto }` is two simple selectors and
	   beats a single class, so the height is stated from inside .tt-pd */
	block-size: 100%;
	border: 0;
	display: block;
	object-fit: cover;
}
.tt-pd .tt-pd__videoframe { block-size: 100%; }
/*
 * Three layers in one frame, stacked EXPLICITLY (cycle 42c-template-6).
 *
 * The clip played and nobody could see it. All three were `position: absolute`
 * with `z-index: auto`, so they painted in document order — and the script
 * inserts the iframe as `box.firstChild`, which puts it UNDERNEATH the poster
 * and the guard that PHP had already rendered. Measured: the iframe present,
 * youtube.com answering, googlevideo.com streaming, zero console errors, and
 * the frames one second and four seconds apart identical to within 0.001 of a
 * grey level — because what was on screen was the poster, not the film.
 *
 * Document order is not a stacking plan. The poster is the floor, the film is
 * above it, and the guard is above both.
 */
.tt-pd .tt-pd__player .tt-pd__videoposter,
.tt-pd .tt-pd__player .wp-block-image img {
	position: absolute;
	inset: 0;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
	display: block;
	z-index: 0;
}
.tt-pd__player .wp-block-image { position: absolute; inset: 0; z-index: 0; }
.tt-pd .tt-pd__player .tt-pd__videoframe { z-index: 1; }
/* the guard: a transparent play button over the loop (cycle 61) — a tap swaps in
   the player with controls and sound instead of pausing a controlless clip */
.tt-pd__videoguard { position: absolute; inset: 0; display: block; z-index: 2; margin: 0; padding: 0; border: 0; background: transparent; appearance: none; cursor: pointer; }
.tt-pd__videoguard[hidden] { display: none; }
.tt-pd__videoline {
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--muted);
	text-align: center;
	margin: 18px auto 0;
	max-inline-size: 650px;
}

/* ------------------------------------------- 3 & 4. three photos and copy */
/*
 * The default shape of a description section, and the one the legacy pages
 * used: a row of square photographs, then the heading and the paragraph under
 * them. Anything with a `style` in its JSON opts out.
 */
.tt-pd__grid2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }
/*
 * FOUR PHOTOGRAPHS, CENTRED (owner, 21 Sep 2026).
 *
 * This replaces the rule of 19 Sep, where a row held up to six with the last
 * two behind a sideways scroll and a slice of the fifth showing to advertise
 * it. No scroll now, nothing peeking, at any width.
 *
 * The basis is the FOUR-UP width whatever the count, and the row is centred.
 * That is the whole of requirement 2: one tile, two, three or four are all the
 * same size, and a short row sits in the middle instead of hard against the
 * left. A three-photo row was left-aligned until today — `flex: 0 0 23%` with
 * no justification put three tiles at the start of a row with a quarter of it
 * empty on the right.
 *
 * `flex-wrap` plus `justify-content: center` is also what gives the phone its
 * 2 x 2 below, and puts a third photograph in the middle of the second line
 * with no rule of its own. One mechanism, two layouts.
 */
.tt-pd__grid3 {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	/* the basis is derived from the gap, so a media query that changes one
	   cannot leave the other behind — which is exactly what the 600px rule
	   below used to do: it set gap 12px while the basis still assumed 10, and
	   two tiles plus the gap came to 2px more than the row (gotcha 40). */
	--tt-row-gap: 14px;
	gap: var(--tt-row-gap);
	margin-block-end: 36px;
	/* a flex row stretches its items to the tallest one, which made a square
	   tile 289 wide and 341 tall the moment one tile in the row was taller */
	align-items: start;
}
.tt-pd__grid3 > * { flex: 0 0 calc((100% - 3 * var(--tt-row-gap)) / 4); }
/* auto-fit, not four fixed tracks: a product that names three sets was leaving
   an empty column on the right, so the row read as one card short */
.tt-pd__grid4 { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 20px; }
.tt-pd .tt-pd__sq { aspect-ratio: 1; object-fit: cover; inline-size: 100%; block-size: auto; }
/* a drawing in a photo row: whole, on a light ground (cycle 42c-tent-1c) */
.tt-pd .tt-pd__grid3 > .wp-block-image img.tt-pd__sq--contain,
.tt-pd .tt-pd__grid4 > .wp-block-image img.tt-pd__sq--contain { object-fit: contain; background: var(--tt-pd-grey); padding: 8px; box-sizing: border-box; }
/*
 * Tent construction: four square tiles (cycle 42c-tent-4c). All four are WHITE
 * and the picture fills its tile — a drawing on white used to sit inset on a
 * grey pad inside a tinted card, so the first tile read as a frame within a
 * frame beside three photographs that filled theirs. An empty slot is the dashed
 * placeholder, same size. Two by two on a phone, never four stacked.
 */
/* the tent install row is four tiles and takes the same four-up basis as every
   other row since 21 Sep — it had its own, 0.5px different, for no reason that
   survives the rows being the same shape */
.tt-pd .tt-pd__grid3--build > * { flex: 0 0 calc((100% - 3 * var(--tt-row-gap)) / 4); }
.tt-pd .tt-pd__grid3--build > * { background: #fff; }
.tt-pd .tt-pd__grid3--build > .wp-block-image img.tt-pd__sq--contain { background: #fff; padding: 0; }
/*
 * On a phone: 2 x 2 (owner, 21 Sep 2026).
 *
 * The 19 Sep rule was one horizontal swipe with the next photograph peeking at
 * 78% — which is gone with the scroll. Four tiles are two rows of two, and a
 * THIRD photograph lands centred under the first two because the row is still
 * `justify-content: center` and still wraps. No rule of its own, and nothing to
 * keep in step with the desktop layout.
 *
 * The negative margin is gone with the scroll too: it existed to let the row
 * bleed into the page gutter so a swiped photograph could reach the screen
 * edge. A centred grid that does that is a grid with 16px of overflow
 * (gotcha 40).
 */
@media (max-width: 781px) {
	.tt-pd__grid3 { --tt-row-gap: 10px; }
	.tt-pd__grid3 > *,
	.tt-pd .tt-pd__grid3--build > * { flex: 0 0 calc((100% - var(--tt-row-gap)) / 2); }
}
.tt-pd__bullets { list-style: none; padding: 0; margin: 20px 0 0; display: grid; gap: 14px; }
.tt-pd__bullets li { padding-inline-start: 18px; border-inline-start: 3px solid var(--wp--preset--color--brand-gold); }
.tt-pd__bullets strong { display: block; }
.tt-pd__bullets span { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }
/* No photographs above them, so the list takes the width instead of running
   down the middle of the measure (cycle 42c-rollout-2b). One column below 782px:
   a two-column Thai bullet at 390 leaves about 170px of text per line, which
   breaks every title across two rows. */
@media (min-width: 782px) {
	.tt-pd__bullets--two { grid-template-columns: 1fr 1fr; column-gap: 32px; }
}
/*
 * A SINGLE-column list keeps the text measure (cycle 64b-2b). The heading and
 * the lead above it sit in `--tt-pd-measure` and the list was taking the whole
 * 1200px wrap, so it started 150px to the left of the sentence it belongs to —
 * measured on the backdrop at 1440: lead 270→1170, bullets 120→1320. The
 * two-column variant is untouched: it is a grid that exists to use the width,
 * and it only appears where there are no photographs above it.
 */
.tt-pd__bullets:not(.tt-pd__bullets--two) {
	max-inline-size: var(--tt-pd-measure);
	margin-inline: auto;
}

/* the price a card takes from its own product (cycle 64b-2b) */
/* wc_price() nests its own spans, and `.tt-pd__cardbody span` makes every span
   a block — so the currency symbol sat on its own line above the number
   (cycle 64b-3). Scoped through .tt-pd__cardbody because the two selectors are
   otherwise the same weight and the cardbody rule is declared later in this
   file: gotcha 19, measured twice in one cycle. */
.tt-pd__cardbody .tt-pd__cardprice span,
.tt-pd__cardbody .tt-pd__cardprice bdi { display: inline; padding-inline: 0; font-size: inherit; color: inherit; }
.tt-pd__cardprice {
	display: block;
	padding-inline: 6px;
	margin-block-start: 6px;
	font-weight: 700;
	color: var(--wp--preset--color--brand-blue);
}

/* the exceptions: a split for long copy, an asymmetric grid for one photo */
.tt-pd__split { display: grid; grid-template-columns: 1fr 1fr; gap: var(--tt-pd-gap); align-items: center; }
.tt-pd__split--3 { grid-template-columns: 1fr 1.2fr 1fr; }
.tt-pd__split--asym { grid-template-columns: 1fr 1.05fr; }
.tt-pd__split--flip > .tt-pd__half:last-child { order: -1; }
.tt-pd__half { min-inline-size: 0; }
.tt-pd__asym { display: grid; gap: var(--wp--preset--spacing--30); }
.tt-pd__asymrow { display: grid; grid-template-columns: 1fr 1fr; gap: var(--wp--preset--spacing--30); }
.tt-pd__asymlead { aspect-ratio: 16 / 10; border-radius: var(--tt-pd-card); overflow: hidden; box-shadow: var(--tt-pd-shadow); }
.tt-pd__asymsmall { aspect-ratio: 1; border-radius: var(--tt-pd-card); overflow: hidden; box-shadow: var(--tt-pd-shadow); }
.tt-pd__asymimg { inline-size: 100%; object-fit: cover; display: block; }
.tt-pd .tt-pd__asymimg { block-size: 100%; }
.tt-pd__wide { inline-size: 100%; block-size: auto; border-radius: var(--tt-pd-card); }

/* feature cards — a photograph, a short title, one line */
.tt-pd__cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
/* ...and four columns where a section has four cards (the flag's three bases
   plus its bag), rather than three and an orphan on the next row */
.tt-pd__cards--4 { grid-template-columns: repeat(4, 1fr); }
.tt-pd__card {
	display: flex;
	flex-direction: column;
	gap: 8px;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card);
	background: #fff;
	padding: 10px 10px 16px;
}
.tt-pd__cardmedia { border-radius: 12px; overflow: hidden; }
/*
 * An accessory card shows the whole object and never enlarges it.
 *
 * `scale-down` is the whole requirement in one keyword: it is `contain` or
 * `none`, whichever ends up smaller — so a picture bigger than the box is fitted
 * inside it and a picture smaller than the box is left at its own size instead
 * of being stretched across a card.
 *
 * Getting there the long way round cost an hour. First a fixed media box with
 * the picture contained: the FIGURE core/image wraps every description
 * photograph in is a grid item, it carries the automatic minimum size
 * (`min-block-size: auto`), and it grew to whatever the picture wanted —
 * 263x1500 inside a 210px box — while `max-block-size: 100%` on the <img> did
 * nothing, because the figure's own height was never definite. Then the same
 * with the figure positioned: the figure came out right at 263x210 and the
 * image still rendered 1500 tall inside it. A cap that has to resolve through
 * two boxes is a cap that will not resolve; `object-fit` needs neither.
 */
.tt-pd__cardmedia--fit {
	position: relative;
	block-size: 210px;
	background: var(--tt-pd-grey);
}
.tt-pd__cardmedia--fit .wp-block-image { position: absolute; inset: 0; }
.tt-pd .tt-pd__cardmedia--fit .wp-block-image img {
	inline-size: 100%;
	block-size: 100%;
	aspect-ratio: auto;
	object-fit: scale-down;
}
.tt-pd .tt-pd__cardimg { inline-size: 100%; block-size: auto; aspect-ratio: 4 / 3; object-fit: cover; display: block; }
.tt-pd__cardbody { text-align: center; }
.tt-pd__cardbody strong,
.tt-pd__cardtitle { display: block; padding-inline: 6px; color: var(--wp--preset--color--brand-blue); }
.tt-pd__cardbody span,
.tt-pd__cardtext { display: block; padding-inline: 6px; font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }

/* ------------------------------------------------------------- 5. sizes */
/*
 * The comparison graphic on the left, the four cards on the right — the legacy
 * arrangement. The graphic is a normal image slot, so the day a version without
 * prices drawn into it exists, it is one field in the JSON.
 */
.tt-pd__sizesplit {
	display: grid;
	grid-template-columns: minmax(0, 470px) minmax(0, 1fr);
	gap: 40px;
	align-items: center;
}
.tt-pd__sizeguide { align-self: start; }
/*
 * The placeholder: the shape a photograph would take, drawn as an outline so it
 * reads as "missing" rather than as a design choice. The dash is the whole
 * point — a solid grey panel looks deliberate.
 */
.tt-pd__guideph {
	display: grid;
	place-items: center;
	aspect-ratio: 1;
	/* its padding used to sit OUTSIDE the flex basis, so a waiting tile came out
	   48px wider than the photographs beside it */
	box-sizing: border-box;
	padding: 24px;
	border: 2px dashed var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card);
	background: var(--tt-pd-grey);
}
.tt-pd__guideph span {
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--muted);
	text-align: center;
	max-inline-size: 22ch;
}
/* the photograph on the right instead: the cards lead, the picture confirms */
.tt-pd__sizesplit--flip { grid-template-columns: minmax(0, 1fr) minmax(0, 470px); }
.tt-pd__sizesplit--flip > .tt-pd__sizeguide { order: 2; }
.tt-pd .tt-pd__guideimg { inline-size: 100%; block-size: auto; }
.tt-pd__sizecards { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }
/* no comparison graphic beside them: the cards take the whole row */
.tt-pd__sizecards--wide { grid-template-columns: repeat(4, 1fr); }
.tt-pd__sizecard {
	display: grid;
	justify-items: center;
	gap: 8px;
	text-align: center;
	padding: 20px 16px;
	background: #fff;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card);
	box-shadow: 0 10px 30px rgba(0, 54, 111, .06);
}
.tt-pd__sizeart { block-size: 76px; display: grid; place-items: end center; color: var(--wp--preset--color--brand-blue); }
.tt-pd__sizeart svg { transform: scale(var(--tt-scale, 1)); transform-origin: bottom center; }
/*
 * The panel silhouette (cycle 64b-2b): a plain rectangle at the size's own
 * proportion. Width is the size against the widest card, height is the size's
 * own height against the tallest — on the backdrop every panel is 230 cm tall,
 * so the six differ only in width, which is exactly the fact the card is
 * there to show. `aspect-ratio` is deliberately NOT used: it would tie the two
 * axes together and undo that.
 */
/*
 * The one-row size comparison (cycle 64b-4). The panels sit on a common
 * BASELINE — `align-items: end` — because that is what makes six widths read as
 * one drawing rather than six. On a phone the row scrolls: six panels plus the
 * made-to-order one need about 700px and the viewport has 390, and squeezing
 * them would destroy the only thing the row is for.
 */
.tt-pd__sizerow {
	display: flex;
	align-items: flex-start;
	justify-content: center;
	gap: 18px;
	margin-block-start: 8px;
	padding-block-end: 6px;
}
.tt-pd__sizepanel {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	flex: 0 0 auto;
	text-align: center;
}
.tt-pd__sizepanel .tt-pd__sizerect { color: var(--wp--preset--color--brand-blue); }
.tt-pd__sizelabel {
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--brand-blue);
	white-space: nowrap;
}
.tt-pd__sizepanel .tt-pd__sizefrom { margin-block-start: 0; font-size: var(--wp--preset--font-size--small); }
.tt-pd__sizepanel--any .tt-pd__sizeuse { max-inline-size: 150px; }

@media (max-width: 781px) {
	.tt-pd__sizerow {
		justify-content: flex-start;
		overflow-x: auto;
		scroll-snap-type: x proximity;
		-webkit-overflow-scrolling: touch;
		/* the gutter the rest of the section keeps, so the first panel does not
		   sit against the edge and the last one can scroll clear of it */
		padding-inline: 4px;
	}
	.tt-pd__sizepanel { scroll-snap-align: center; }
	/* the row runs past the viewport by design; without this the last panel
	   reads as clipped rather than as "there is more to the right" */
	.tt-pd__sizerow {
		-webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
		mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
	}
}

.tt-pd__sizerect {
	display: block;
	/*
	 * A PIXEL basis, not `100%` (cycle 64b-3). `.tt-pd__sizeart` is a grid with
	 * no declared columns, so its single track is sized to its content — and the
	 * content is an empty span, which is 4px of border. Every percentage width
	 * therefore resolved against 4px and all six panels rendered as hairlines
	 * 4.6px to 8px wide: measured, not guessed. A width in px cannot be
	 * collapsed by the track it sits in.
	 */
	inline-size: calc(var(--tt-w, 1) * 104px);
	block-size: calc(var(--tt-h, 1) * 64px);
	border: 2px solid currentColor;
	border-radius: 3px;
	background: color-mix(in srgb, currentColor 8%, transparent);
}
/*
 * Made to order: the widest standard panel, dashed, "this and wider".
 * AFTER the rule above, not before it: `border: 2px solid currentColor` is a
 * shorthand and resets border-style, the two selectors weigh the same, and the
 * modifier declared first lost — measured as `solid` on all seven panels.
 * Gotcha 19, third time in this cycle.
 */
.tt-pd__sizerect--any { border-style: dashed; background: none; }
.tt-pd__sizenum { font-size: var(--wp--preset--font-size--x-large); font-weight: 700; color: var(--wp--preset--color--brand-blue); line-height: 1.1; }
.tt-pd__sizenum em { font-style: normal; font-size: var(--wp--preset--font-size--small); font-weight: 500; margin-inline-start: 4px; }
.tt-pd__sizebox { font-size: var(--wp--preset--font-size--small); }
/* 11px gold on a white card was 2.00:1 — the same defect as the subtitle, in a
   different component, and it takes the same dark gold (cycle 42c-template-8) */
.tt-pd__sizebox strong { display: block; color: var(--tt-pd-gold-ink); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; }
.tt-pd__sizeuse { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }
/* the "from" price, read off the cheapest purchasable variation of that size
   (cycle 64b-2b) — last line of the card, under the specification */
.tt-pd__sizefrom {
	display: block;
	margin-block-start: 8px;
	font-weight: 700;
	color: var(--wp--preset--color--brand-blue);
}
/* what the big number is — one word, directly under it */
.tt-pd__sizecap {
	font-size: 11px;
	text-transform: uppercase;
	letter-spacing: .06em;
	color: var(--wp--preset--color--muted);
	margin-block-start: -4px;
}
/* the specification rows under a size card's number */
.tt-pd__sizespecs { display: grid; gap: 2px; margin-block-start: 2px; }
.tt-pd__sizespec {
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--muted);
	line-height: 1.5;
}
.tt-pd__sizespec em {
	font-style: normal;
	color: var(--wp--preset--color--brand-blue);
	margin-inline-end: 6px;
}

/* size tiles — still a guide and not a control: no pointer, no selected state */
.tt-pd__sizes { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; }
.tt-pd__size {
	display: flex;
	flex-direction: column;
	gap: 4px;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card);
	background: #fff;
	padding: 18px 16px 20px;
}
.tt-pd__size.is-featured { border-color: var(--wp--preset--color--brand-gold); }
.tt-pd__sizeunit { font-size: 14px; font-weight: 600; margin-inline-start: 6px; }
.tt-pd__sizename { font-size: var(--wp--preset--font-size--small); }

/* the bag card, directly under the sizes and with nothing between them */
.tt-pd__band {
	background: #fff;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card);
	padding: var(--wp--preset--spacing--60);
}

/* --------------------------------------------------- 6. print & branding */
/* ------------------------------------------------- branding (7b) */
/*
 * Two columns: the print photograph on the left, everything that is words on
 * the right. The section is ~600px tall at 1440 and that is a consequence of
 * the parts, not a height anyone set — the photograph is capped at 420 and the
 * tiles are three rows of a fixed shape.
 */
.tt-pd__brand {
	display: grid;
	grid-template-columns: minmax(0, 420px) minmax(0, 1fr);
	gap: 40px;
	align-items: start;
}
.tt-pd__brand--solo { grid-template-columns: 1fr; }
.tt-pd__brandaside { display: grid; gap: 16px; align-content: start; }
/*
 * 420px is a CAP on the photograph, so the height is stated on the box and the
 * image is told to cover it. Leaving the height to `auto` and capping with
 * max-block-size is gotcha 23 again: an <img> resolves `auto` from the file's
 * own ratio and ignores the cap entirely.
 */
.tt-pd__brandshot { block-size: 420px; border-radius: var(--tt-pd-card); overflow: hidden; }
/* three classes, because `.tt-pd .wp-block-image img { block-size: auto }` is
   two and an element and would otherwise let the photograph size itself — which
   it did on the flag, 280px tall inside a 420px frame (gotcha 29 again) */
.tt-pd .tt-pd__brandshot .tt-pd__brandimg { inline-size: 100%; block-size: 100%; object-fit: contain; }
.tt-pd__brandshot .wp-block-image,
.tt-pd__brandshot figure { margin: 0; block-size: 100%; }
/* the shape a photograph would take, dashed so it reads as missing rather than
   as a design choice — the same argument as .tt-pd__guideph */
.tt-pd__brandph {
	display: grid;
	place-items: center;
	/* 420 INCLUDING the padding and the dashed border: without border-box this
	   box measured 472 and sat 52px taller than the photograph it stands in for
	   (gotcha 40) */
	box-sizing: border-box;
	block-size: 420px;
	padding: 24px;
	/*
	 * Styled for the ground it is ON, not the ground it was born on. In
	 * 42c-template-7b branding was navy and this was white-on-translucent-white;
	 * 42c-template-8 moved branding to the light band and the same rule left the
	 * words at 1.10:1 — invisible. The colours are now the light-ground ones and
	 * the navy override lives with the rest of the navy rules.
	 */
	border: 2px dashed var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card);
	background: rgba(255, 255, 255, .55);
	color: var(--wp--preset--color--muted);
	font-size: var(--wp--preset--font-size--small);
	text-align: center;
}
.tt-pd__section--navy .tt-pd__brandph {
	border-color: rgba(255, 255, 255, .38);
	background: rgba(255, 255, 255, .06);
	color: rgba(255, 255, 255, .8);
}
.tt-pd__brandbody { min-inline-size: 0; }
/* the intro is a lead-in, not a paragraph: two lines and then the tiles */
.tt-pd__brandbody > .tt-pd__lead {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	margin-block-end: 16px;
}
/* the footnote sits under the tiles, not under a whole section */
.tt-pd__brandbody > .tt-pd__note { margin-block-start: 16px; }

/*
 * The tiles. 2x3 at every width the section is two columns at, and 2x3 again at
 * 390 — three rows of two is the shape, not a consequence of the viewport.
 */
.tt-pd__tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.tt-pd__tile {
	display: grid;
	grid-template-columns: 20px minmax(0, 1fr);
	align-items: start;
	gap: 4px 10px;
	padding: 12px 14px;
	background: #fff;
	border-radius: 10px;
	/*
	 * A white card inside a navy section must state its own text colour. The
	 * section sets `color: #fff` and the card had none, so the bold title
	 * inherited white ONTO WHITE and vanished — the body line survived only
	 * because it happened to carry an explicit muted grey. Anything light
	 * dropped onto a dark section needs this line (cycle 42c-template-7b).
	 */
	color: var(--wp--preset--color--brand-blue);
}
/*
 * On the light band a white card needs an edge (cycle 42c-template-8). #FFFFFF
 * on #F1F4FA is a 1.06:1 step — enough to see when you look for it, not enough
 * to make the tile read as an object. The shadow does that; it was not needed
 * in 7b because the tiles then sat on navy, where white IS the edge.
 */
.tt-pd__section--light .tt-pd__tile { box-shadow: var(--tt-pd-shadow); }
.tt-pd__tileicon { color: var(--wp--preset--color--brand-blue); line-height: 0; grid-row: 1; }
.tt-pd__tile strong { grid-column: 2; font-size: 15px; line-height: 1.4; }
.tt-pd__tile span {
	grid-column: 2;
	font-size: 13px;
	line-height: 1.45;
	color: var(--wp--preset--color--muted);
}
/* under the photograph, and small: it is a footnote with a picture */
.tt-pd__brandaside .tt-pd__diagram {
	background: rgba(255, 255, 255, .06);
	border-radius: var(--tt-pd-card);
	padding: 12px;
}
.tt-pd__brandaside .tt-pd__diagram svg { inline-size: min(150px, 100%); }

/*
 * With a diagram under it the photograph gives up height rather than the
 * section growing: the left column is a stack of two things and the right
 * column is the one that decides how tall this section is. Left alone, the
 * umbrella — the only product with a diagram — ran 136px taller than every
 * other product's branding section for no reason a reader could see.
 */
.tt-pd__brandaside--dia .tt-pd__brandshot { block-size: 320px; }
.tt-pd__diagram { margin: 0; display: grid; justify-items: center; gap: 12px; }
.tt-pd__diagram svg { inline-size: min(280px, 100%); block-size: auto; }
/* 22ch put "ตามแบบ" on a line of its own; the caption is one sentence and it
   gets the whole column (cycle 42c-template-3) */
.tt-pd__diagram figcaption {
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--muted);
	text-align: center;
	max-inline-size: none;
}
.tt-pd__facts { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.tt-pd__fact {
	display: grid;
	justify-items: center;
	gap: 6px;
	text-align: center;
	padding: 18px 14px;
	background: #fff;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card);
}
/* `.tt-pd__fact span` below is 0,1,1 and this was 0,1,0, so the icon — which IS
   a span — has been rendering in the muted grey of the card's body text since
   cycle 42c-template-2, never in brand blue. Nothing looked broken: a grey icon
   on a white card is a plausible design. Scoped, not !important (gotcha 19). */
.tt-pd__fact .tt-pd__facticon { color: var(--wp--preset--color--brand-blue); line-height: 0; }
.tt-pd__fact span { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }

/* ------------------------------------------- the customer photo strip */
/*
 * One row of small pictures drifting right to left. Two copies of the track sit
 * side by side and the pair slides by exactly one track width PLUS one gap —
 * miss the gap and the row jumps 12px every time the loop comes round.
 *
 * The animation is on the tracks rather than the row, so `:hover` on the row
 * pauses both at once; `animation-play-state` keeps the position instead of
 * snapping back the way a cancelled animation would.
 */
.tt-pd__section--photos { padding-block-end: 64px; }
/*
 * The row is the container's width, not the window's, and its two edges fade
 * rather than being cut off — a card sliced by a hard edge reads as a broken
 * layout, a card fading out reads as "there is more".
 *
 * The mask is on the same element that clips, so the fade sits exactly on the
 * boundary the cards disappear at.
 */
.tt-pd__marquee {
	display: flex;
	gap: 12px;
	overflow: hidden;
	margin-block-start: 28px;
	inline-size: min(100%, var(--tt-pd-content));
	margin-inline: auto;
	--tt-strip-fade: 60px;
	-webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--tt-strip-fade),
		#000 calc(100% - var(--tt-strip-fade)), transparent 100%);
	mask-image: linear-gradient(to right, transparent 0, #000 var(--tt-strip-fade),
		#000 calc(100% - var(--tt-strip-fade)), transparent 100%);
}
.tt-pd__striptrack {
	display: flex;
	flex: 0 0 auto;
	gap: 12px;
	animation: tt-pd-marquee 40s linear infinite;
}
@keyframes tt-pd-marquee {
	from { transform: translateX(0); }
	to { transform: translateX(calc(-100% - 12px)); }
}
.tt-pd__marquee:hover .tt-pd__striptrack,
.tt-pd__marquee:focus-within .tt-pd__striptrack { animation-play-state: paused; }
.tt-pd__stripcard {
	margin: 0;
	flex: 0 0 auto;
	inline-size: 140px;
	block-size: 140px;
	border-radius: 12px;
	overflow: hidden;
	background: var(--tt-pd-grey);
}
.tt-pd__stripcard .tt-pd__fill,
.tt-pd__stripcard .wp-block-image { block-size: 100%; }
/* gotcha 29: three classes and the element, or `block-size: auto` wins */
.tt-pd .tt-pd__stripcard .wp-block-image img {
	inline-size: 100%;
	block-size: 100%;
	object-fit: contain;
	display: block;
}
/*
 * Reduced motion: no drift at all, and the row becomes something the visitor
 * scrolls themselves. A paused marquee that cannot be reached is worse than no
 * marquee — the photographs past the fold would be unreachable.
 */
@media (prefers-reduced-motion: reduce) {
	.tt-pd__striptrack { animation: none; }
	.tt-pd__marquee { overflow-x: auto; }
	.tt-pd__marquee .tt-pd__striptrack[aria-hidden="true"] { display: none; }
}

/* ------------------------------------------------------ 7. realisations */
/*
 * Tiles FILL, and the exceptions say so per slot (owner, 18 Sep 2026 — it
 * replaces the "never cropped" rule written here on the 16th).
 *
 * That rule was right while every photograph in here was a square cut out of a
 * 2x2 collage: a fixed box with `cover` would have taken a slice out of somebody
 * else's finished work, and `contain` cost nothing because the files were already
 * square. It stopped being right the moment the galleries filled with event
 * photography — nine pictures at nine ratios, each letterboxed into a square,
 * which the owner read on a phone as a fault rather than as care.
 *
 * The rule now is **crop the frame, never the product**. A photograph from a job
 * covers its tile, with the focal point recorded per slot so the product stays
 * whole; a packshot on a plain background, where the product already reaches the
 * edges, opts back into `contain` and keeps the light ground behind it.
 * `tightent_slot_focus_attr()` writes both as an inline style, which is why the
 * declaration below is only the default for a photograph nobody has judged yet.
 */
.tt-pd__works { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
/*
 * Four across and smaller (19 Sep 2026). The owner sorted far more work than a
 * 3x3 grid of 380px tiles could hold, and nine big tiles were both a lot of
 * page and a lot of bytes for a section most readers scroll past.
 *
 * Eight show, six on a phone, the rest are `display:none` until the control
 * flips `is-open` — which is also what stops them downloading, since a lazy
 * image in a box that is not rendered is never fetched.
 */
.tt-pd__works > :nth-child(n + 9) { display: none; }
.tt-pd__worksbox.is-open .tt-pd__works > * { display: block; }
.tt-pd__worksmore {
	display: none;
	margin: 18px auto 0;
	padding: 10px 22px;
	border: 1px solid var(--wp--preset--color--brand-blue);
	border-radius: 999px;
	background: #fff;
	color: var(--wp--preset--color--brand-blue);
	font: inherit;
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
	cursor: pointer;
}
.tt-pd__worksmore:hover { background: var(--wp--preset--color--brand-blue); color: #fff; }
.tt-pd__worksbox.has-more-desktop .tt-pd__worksmore { display: block; }
.tt-pd__worksbox.is-open .tt-pd__worksmore { display: none; }
@media (max-width: 781px) {
	/* two columns and six tiles from a tablet down — four narrow tracks on a
	   700px screen were postage stamps */
	.tt-pd__works { grid-template-columns: repeat(2, 1fr); gap: 10px; }
	.tt-pd__works > :nth-child(n + 7) { display: none; }
	.tt-pd__worksbox.has-more-mobile .tt-pd__worksmore { display: block; }
	.tt-pd__worksbox.is-open .tt-pd__works > * { display: block; }
	.tt-pd__worksbox.is-open .tt-pd__worksmore { display: none; }
}
.tt-pd__work { margin: 0; }
/*
 * A realisation tile waiting for its photograph (cycle 64h). It renders on
 * local and staging only — `tt_pd_placeholders()` decides, and production
 * never emits this element — so the page can be judged as the 3x3 grid it
 * will be. Square, like the photographs it stands in for.
 */
/*
 * A labelled break inside the size cards (cycle 64j). Full width of the grid,
 * so the cards below it read as one group rather than as a new column.
 */
.tt-pd__sizegroup {
	grid-column: 1 / -1;
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 4px 10px;
	margin: 8px 0 0;
	padding-block-end: 8px;
	border-block-end: 1px solid var(--wp--preset--color--border);
	font-size: var(--wp--preset--font-size--medium);
}
.tt-pd__sizegroup strong { color: var(--wp--preset--color--brand-blue); font-weight: 700; }
.tt-pd__sizegroup span {
	font-size: var(--wp--preset--font-size--small);
	font-weight: 400;
	color: var(--wp--preset--color--muted);
}
.tt-pd__sizegroup:first-child { margin-block-start: 0; }

.tt-pd__workph {
	display: grid;
	place-items: center;
	aspect-ratio: 1;
	padding: 24px;
	border: 2px dashed var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card);
	background: var(--tt-pd-grey);
}
.tt-pd__workph span {
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--muted);
	text-align: center;
}
.tt-pd__works .wp-block-image {
	margin: 0;
	border-radius: var(--tt-pd-card);
	overflow: hidden;
	box-shadow: var(--tt-pd-shadow);
	/* the same grey as the grouping block: what shows beside a photograph that
	   does not fill the tile should read as paper, not as a second colour */
	background: var(--tt-pd-grey);
}
/*
 * 380, not 260 (cycle 42c-template-5). The photographs were re-cut at the
 * collage cells' own shape, which is square; in a 387x260 box `contain` left
 * 63px of grey down each side of every tile. A tile the height of its own width
 * fits a 1:1 photograph with a few pixels to spare — still contained, so a
 * picture that is not quite square is letterboxed rather than cut.
 */
.tt-pd .tt-pd__works .wp-block-image img {
	inline-size: 100%;
	/* square off the column, not a fixed 380px: four narrower tracks made a
	   fixed height a tall rectangle (19 Sep 2026) */
	aspect-ratio: 1;
	block-size: auto;
	object-fit: cover;
	object-position: 50% 50%;
	display: block;
	transition: transform .4s ease;
}
.tt-pd__works .wp-block-image:hover img { transform: scale(1.05); }
/*
 * Fit on a blurred fill (19 Sep 2026). A portrait photograph of a whole flag
 * cannot be cropped square without cutting the flag, and letterboxed it leaves
 * two grey bars. So: the picture whole, and the space beside it an enlarged
 * blurred copy of itself. `--tt-fill` is set inline per slot by
 * tightent_slot_fill_open(); nothing here fires without it.
 *
 * `scale` before `blur`, or the blur samples the transparent edge of the layer
 * and draws a pale rim down both sides of the tile.
 */
/*
 * `.tt-gal__tile` is the product gallery's tile and takes the same rule (cycle
 * "product gallery lightbox"). It needs no wrapper of its own: the tile is
 * already an element, so it carries `--tt-fill` itself — see
 * tightent_frame_fill_url(), which is the same decision the slots make here.
 */
.tt-pd__fill,
.tt-gal__lead[style*="--tt-fill"],
.tt-gal__tile[style*="--tt-fill"] { position: relative; display: block; overflow: hidden; border-radius: inherit; }
.tt-gal__lead[style*="--tt-fill"]::before,
.tt-gal__tile[style*="--tt-fill"]::before,
.tt-pd__fill::before {
	content: "";
	position: absolute;
	inset: 0;
	background: var(--tt-fill) center / cover no-repeat;
	/*
	 * 1.6, not 1.4. `filter: blur()` samples past the edge of the layer and
	 * fades to transparent there, so the layer has to be bigger than the tile by
	 * more than the blur radius or the sides of the tile show through pale.
	 */
	transform: scale(1.6);
	filter: blur(22px) saturate(1.1) brightness(0.8);
}
.tt-pd__fill > *,
.tt-gal__lead[style*="--tt-fill"] > *,
.tt-gal__tile[style*="--tt-fill"] > * { position: relative; }
/*
 * The single-photo frames (owner, 19 Sep 2026). Each keeps exactly the box it
 * had — 4:3 for the intro card, 420px for the printing shot, a square beside a
 * video — and the fill takes that box so the photograph can sit whole inside it
 * without the section moving. Each needs its own line because each frame sizes
 * its picture differently, and a wrapper that does not inherit the sizing
 * collapses to nothing.
 */
/*
 * SINGLE-PHOTO FRAMES SHOW THE WHOLE PHOTOGRAPH (owner, 19 Sep 2026).
 *
 * `contain` is the default here rather than something the import writes per
 * slot, because these photographs do not all come from a slot — the intro card
 * falls back to a gallery photograph on a product that names no slot, and a
 * rule that only reaches slots would leave exactly those pages cropping. A
 * per-slot override from Media → Tightent Slots is written inline on the <img>
 * and still beats this.
 */
.tt-pd__brandshot .tt-pd__fill { block-size: 100%; }
.tt-pd__videosquare .tt-pd__fill { position: absolute; inset: 0; }
.tt-pd__hero-media .tt-pd__fill,
.tt-pd__stripmedia .tt-pd__fill { border-radius: var(--tt-pd-card); }
/*
 * The wrapped figure must be TRANSPARENT.
 *
 * This is what broke the fill the first time (owner's screenshots, 19 Sep):
 * `.tt-pd__works .wp-block-image` paints `--tt-pd-grey`, the figure is
 * positioned by the rule above and therefore paints over the ::before, and the
 * only blur a reader could see was the sliver outside the figure's own rounded
 * corners. Nothing was wrong with the blur; something opaque was sitting on it.
 */
.tt-pd__fill .wp-block-image,
.tt-pd__fill .wp-block-image img,
.tt-pd__fill img { background: transparent; }
.tt-pd__cap { display: block; text-align: center; font-size: var(--wp--preset--font-size--small); font-weight: 600; margin-block-start: 8px; }
/*
 * The lightbox crops too, and it took a measurement to see it.
 *
 * The overlay is appended to <body>, outside .tt-pd, so nothing above reaches
 * it — and core's own rule is `.wp-lightbox-overlay .wp-block-image img`, two
 * classes and an element, which beat a first attempt at
 * `.wp-lightbox-overlay img` (gotcha 19, again). Core sizes the enlarged figure
 * from the THUMBNAIL's box, so with `object-fit: cover` a 4:3 photograph opened
 * from a 3:2 tile lost a strip off the top and bottom at full screen — the one
 * place a visitor looks at a realisation properly.
 *
 * And the winning `object-fit: cover` is INLINE — the Interactivity API writes
 * it onto the element — so no selector, at any specificity, can reach it. This
 * is the case `!important` exists for. It is scoped to the overlay and touches
 * nothing else on the page, which is the opposite of the shared image rule that
 * caused gotcha 19.
 *
 * Measured on umbrella-work-1: core sizes the enlarged figure from the
 * THUMBNAIL's box, not from the file, so the box settles at 1200x806 (1.488)
 * for a photograph that is 1.333 — about a tenth of it off the top and bottom,
 * at full screen, which is the one place a visitor looks at a realisation
 * properly. Contained, the whole photograph sits inside that box instead.
 */
.wp-lightbox-overlay img { object-fit: contain !important; }

/* --------------------------------------------------------- 8. how to order */
/*
 * The site-wide four steps, numbered. The connector is drawn only where all
 * four sit on one row — on a phone they are a column and a horizontal line
 * between them would point at nothing.
 */
.tt-pd__order {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 20px;
}
.tt-pd__ostep {
	position: relative;
	display: grid;
	justify-items: center;
	gap: 8px;
	text-align: center;
	padding: 26px 18px 22px;
	background: #fff;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card);
}
.tt-pd__onum {
	inline-size: 42px;
	block-size: 42px;
	display: grid;
	place-items: center;
	border-radius: 50%;
	background: var(--wp--preset--color--brand-gold);
	color: #2a2100;
	font-weight: 700;
	font-size: 18px;
}
.tt-pd__oicon {
	color: var(--wp--preset--color--accent-blue);
	background: var(--wp--preset--color--surface-blue-soft);
	inline-size: 52px;
	block-size: 52px;
	border-radius: 14px;
	display: grid;
	place-items: center;
}
.tt-pd__ostep strong { display: block; color: var(--wp--preset--color--brand-blue); }
.tt-pd__ostep > span:last-of-type { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }
.tt-pd__octa { display: flex; justify-content: center; margin-block-start: 32px; }

/* --------------------------------------------------------------- 9. FAQ */
.tt-pd__faqs { display: grid; gap: 12px; max-inline-size: 900px; margin-inline: auto; }
.tt-pd__faq {
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card);
	padding: 20px 24px;
	background: #fff;
	box-shadow: var(--tt-pd-shadow);
}
.tt-pd__faq summary {
	cursor: pointer;
	font-weight: 600;
	color: var(--wp--preset--color--brand-blue);
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	/* both spellings: Safari still uses the -webkit- pseudo-element */
	list-style: none;
}
.tt-pd__faq summary::-webkit-details-marker { display: none; }
.tt-pd__faq summary::after {
	content: "+";
	flex: 0 0 auto;
	font-size: 22px;
	font-weight: 400;
	line-height: 1;
	color: var(--wp--preset--color--brand-gold);
}
.tt-pd__faq[open] summary::after { content: "\2212"; }
.tt-pd__faq > div { margin-block-start: 12px; color: var(--wp--preset--color--muted); }

/* ------------------------------------------------------------- 10. sets */
.tt-pd__setcard {
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card);
	padding: 12px;
	background: #fff;
	display: flex;
	flex-direction: column;
	gap: 6px;
}
.tt-pd__setcard.is-featured { border-color: var(--wp--preset--color--brand-gold); }
.tt-pd__setimg { aspect-ratio: 1; border-radius: 10px; background: var(--wp--preset--color--surface-2); }
.tt-pd__setcard > .wp-block-image { border-radius: 10px; overflow: hidden; }
.tt-pd__setitems { list-style: none; padding: 0; margin: 0; font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }
.tt-pd__setprice { margin-block-start: auto; font-weight: 700; color: var(--wp--preset--color--brand-blue); }
/* the computed from-price sits at the foot of the card, so every card's button stays level */
.tt-pd__setfrom { margin: auto 0 0; }
.tt-pd__setfrom .tt-pd__setprice { margin: 0; }
.tt-set__from { margin-block: auto 0; font-weight: 400; }
.tt-set__from strong { font-weight: 700; }
/* one green button per card, full width of the card */
.tt-pd__setcta {
	inline-size: 100%;
	margin-block-start: 10px;
	padding: 12px 14px;
	font-size: var(--wp--preset--font-size--small);
	border-radius: 10px;
}

/* -------------------------------------------------- the remaining pieces */
.tt-pd__included { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 10px 22px; }
/*
 * With a line under each item the list stops being a row of dots and becomes a
 * column of entries, so it takes one track and the dot aligns with the first
 * line rather than floating beside two (cycle 64b-2b).
 */
/* the dashed stand-ins for a slot that is still empty (cycle 64b-3) */
.tt-pd__kitph,
.tt-pd__cardmedia--ph {
	display: grid;
	place-items: center;
	min-block-size: 180px;
	padding: 16px;
	border: 2px dashed color-mix(in srgb, var(--wp--preset--color--brand-blue) 35%, transparent);
	border-radius: 12px;
	color: var(--wp--preset--color--muted);
	font-size: var(--wp--preset--font-size--small);
	text-align: center;
}
.tt-pd__cardmedia--ph { min-block-size: 140px; }

.tt-pd__included--noted { grid-template-columns: 1fr; gap: 14px; text-align: start; }
/* the waiting kit photograph, as a strip under the list rather than half the section */
.tt-pd__kitph--strip { aspect-ratio: auto; min-block-size: 96px; margin-block-start: 22px; }
/* no kit photograph: the list owns the section and reads in two columns */
@media (min-width: 782px) {
	.tt-pd__included--wide.tt-pd__included--noted { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px 40px; }
}
.tt-pd__included--noted li { align-items: start; }
.tt-pd__included--noted li::before { margin-block-start: 8px; }
/*
 * The ITEM's own title is a block; a <strong> the copy puts inside the note is
 * not. `.tt-pd__included--noted strong` caught both, so a bold phrase in a note
 * broke onto its own line — gotcha 19 for the second time on this page. Direct
 * children only.
 */
.tt-pd__included--noted > li > strong { display: block; color: var(--wp--preset--color--brand-blue); }
.tt-pd__included--noted > li > span strong { display: inline; color: inherit; }
.tt-pd__included--noted span {
	display: block;
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--muted);
}
.tt-pd__included li { display: flex; gap: 10px; align-items: baseline; }
.tt-pd__included li::before { content: ""; flex: 0 0 8px; block-size: 8px; border-radius: 2px; background: var(--wp--preset--color--brand-gold); }

.tt-pd__feat, .tt-pd__step { display: flex; gap: 14px; border: 1px solid var(--wp--preset--color--border); border-radius: 12px; padding: 15px; margin-block-end: 12px; }
.tt-pd__featnum, .tt-pd__stepnum { flex: 0 0 38px; block-size: 38px; display: grid; place-items: center; border-radius: 10px; background: var(--wp--preset--color--surface-2); color: var(--wp--preset--color--brand-blue); font-weight: 700; }
.tt-pd__feat strong, .tt-pd__step strong { display: block; }
.tt-pd__feat span, .tt-pd__step span { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }
.tt-pd__steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 12px; }

.tt-pd__tablewrap { overflow-x: auto; }
.tt-pd__table { inline-size: 100%; border-collapse: collapse; }
.tt-pd__table th, .tt-pd__table td { border: 1px solid var(--wp--preset--color--border); padding: 11px 13px; text-align: start; vertical-align: top; }
.tt-pd__table th { background: #fff; color: var(--wp--preset--color--brand-blue); inline-size: 34%; font-weight: 600; }
.tt-pd__table--guide th { inline-size: 42%; }
.tt-pd__ph { display: inline-block; background: repeating-linear-gradient(45deg, #eef1f7, #eef1f7 7px, #e4e9f2 7px, #e4e9f2 14px); border: 1px dashed #c3cadb; border-radius: 8px; padding: 4px 10px; font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }

.tt-pd__bar { display: flex; flex-wrap: wrap; gap: 22px; justify-content: center; background: var(--wp--preset--color--surface-2); border-radius: var(--tt-pd-card); padding: 16px; margin-block-start: 18px; font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }

/*
 * One offer, centred. `justify-content: space-between` put the sentence and the
 * button at opposite ends of whatever box the ribbon was in — which, once the
 * ribbon spanned the window, was opposite ends of the screen. They belong
 * together: the words, then the button that acts on them.
 */
.tt-pd__section--ribbon { padding-block: 0; }
/*
 * The row inside the green band (cycle 42c-template-8). It used to be a
 * navy pill on white and carried its own background, colour and radius; the
 * tint map owns all three now, so what is left here is the arrangement.
 */
.tt-pd__ribbon {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	text-align: center;
	gap: 14px 22px;
}
.tt-pd__ribbontext { max-inline-size: 60ch; }
/* the emphasis was gold on navy; on LINE green it has to be dark to stay
   readable, so it leans on weight rather than colour */
.tt-pd__ribbon strong { color: var(--wp--preset--color--brand-blue); }
/* the band is short: it is one sentence and one button, not a section */
.tt-pd__section--ribbon { padding-block: 22px; }
/* border-box: every place this goes full width (the ribbon on a phone, the
   order CTA, a set card) it is 100% of its column PLUS 56px of its own padding
   otherwise — which is 12px of document overflow at 390. */
.tt-pd__cta { box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; border-radius: 10px; padding: 14px 28px; font-weight: 700; text-decoration: none; border: 0; cursor: pointer; font: inherit; font-weight: 700; }
.tt-pd__cta--line { background: var(--wp--preset--color--line-green); color: #fff; gap: 13px; }
/* a white pill for a button that sits ON the LINE green, where a green button
   would disappear into its own band (cycle 42c-template-8) */
.tt-pd__cta--onlight { background: #fff; color: var(--wp--preset--color--brand-blue); }

/* --- the 16:9 tutorial video (cycle 42c-tent-2c) --------------------------
 * Words and steps on the left, the film on the right, on the section's navy.
 * The player column is a little wider than the text column: at 1440 that makes
 * the film ~620px wide and ~350px tall, which with 64px padding either side
 * lands the section near the 480px it was briefed at. The frame is capped at
 * 720px however wide the column gets.
 */
.tt-pd__tutorial {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
	gap: 48px;
	align-items: center;
}
.tt-pd__tutorialtext > .tt-pd__h2 { text-align: start; margin-block-end: 8px; }
.tt-pd__tutorialtext > .tt-pd__pill { margin-inline: 0; }
.tt-pd__section--tutorial .tt-pd__lead--video { text-align: start; margin: 0 0 20px; max-inline-size: none; }
.tt-pd__tsteps { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.tt-pd__tstep { display: grid; grid-template-columns: 32px minmax(0, 1fr); gap: 12px; align-items: start; }
.tt-pd__tstepnum {
	display: grid;
	place-items: center;
	inline-size: 32px;
	block-size: 32px;
	border-radius: 50%;
	background: var(--wp--preset--color--brand-gold);
	color: #2a2100;
	font-weight: 700;
	font-size: 15px;
}
/* `> div >`: the numeral is a span inside the step too, and a bare
   `.tt-pd__tstep span` (0,1,1) repainted it white on gold at 2.00:1 — caught by
   the contrast check, not by eye (gotchas 19 and 49) */
.tt-pd__tstep > div > strong { display: block; color: #fff; font-size: 16px; line-height: 1.4; }
.tt-pd__tstep > div > span { color: rgba(255, 255, 255, .78); font-size: 14px; }

/* the frame: 16:9, white 6px border INCLUDED in the 16:9 box, radius 16 */
.tt-pd__player--tutorial {
	position: relative;
	box-sizing: border-box;
	inline-size: 100%;
	max-inline-size: 720px;
	margin-inline-start: auto;
	aspect-ratio: 16 / 9;
	border: 6px solid #fff;
	border-radius: 16px;
	overflow: hidden;
	background: #000;
}
.tt-pd .tt-pd__player--tutorial .tt-pd__videoposter,
.tt-pd .tt-pd__player--tutorial .wp-block-image,
.tt-pd .tt-pd__player--tutorial .wp-block-image img {
	position: absolute;
	inset: 0;
	inline-size: 100%;
	block-size: 100%;
	margin: 0;
	object-fit: cover;
	z-index: 0;
}
.tt-pd__player--tutorial .tt-pd__videoframe { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; border: 0; z-index: 1; }
.tt-pd__playbtn {
	position: absolute;
	inset: 50% auto auto 50%;
	transform: translate(-50%, -50%);
	z-index: 2;
	display: grid;
	place-items: center;
	inline-size: 76px;
	block-size: 76px;
	padding: 0 0 0 4px;
	border: 0;
	border-radius: 50%;
	background: rgba(255, 255, 255, .94);
	color: var(--wp--preset--color--brand-blue);
	box-shadow: 0 8px 24px rgba(0, 0, 0, .28);
	cursor: pointer;
	transition: transform .2s ease;
}
.tt-pd__playbtn:hover,
.tt-pd__playbtn:focus-visible { transform: translate(-50%, -50%) scale(1.08); }
.tt-pd__playbtn[hidden] { display: none; }  /* gotchas 12, 33, 37 */

@media (max-width: 781px) {
	/* text first, the film full width under it */
	.tt-pd__tutorial { grid-template-columns: 1fr; gap: 24px; }
	/*
	 * Scoped under .tt-pd: the vertical player's phone rule
	 * (`.tt-pd__player { max-inline-size: min(300px, 100%); border-width: 4px;
	 * border-radius: 18px }`) sits later in this file at the same specificity
	 * and was winning — a 300px tutorial with a 4px frame at 390 (gotcha 19).
	 */
	.tt-pd .tt-pd__player--tutorial { max-inline-size: none; margin-inline: 0; border-width: 6px; border-radius: 16px; }
	.tt-pd__playbtn { inline-size: 64px; block-size: 64px; }
}

/* ================================================================ tents */
/*
 * The three tent section types (cycle 42c-tent-1): models, fabrics and the size
 * table. Same card radius, shadow and measure as every other section — they are
 * new SHAPES in the shared system, not a second visual language.
 */

/*
 * --- models: one row of spec cards (cycle 42c-tent-4b)
 *
 * Each card is `grid-template-rows: subgrid` over the parent's nine rows —
 * photo, name, positioning, five spec rows (the <dl> is a subgrid of five
 * itself) and the price row — so a row sits on one line across every card even
 * when a value above it wraps. The current card's gold edge is a shadow, not a
 * wider border: a 2px border would push that card's rows 1px off the others.
 */
.tt-pd__models {
	display: grid;
	grid-auto-flow: column;
	grid-auto-columns: minmax(0, 1fr);
	grid-template-rows: repeat(9, auto);
	gap: 0 16px;
}
.tt-pd__model {
	grid-row: span 9;
	display: grid;
	grid-template-rows: subgrid;
	padding: 0 16px 6px;
	background: #fff;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card);
	box-shadow: var(--tt-pd-shadow);
	/* a card on a light band states its own colour (gotcha 51) */
	color: var(--wp--preset--color--brand-blue);
}
.tt-pd__model.is-current { border-color: var(--wp--preset--color--brand-gold); box-shadow: 0 0 0 1px var(--wp--preset--color--brand-gold), var(--tt-pd-shadow); }
.tt-pd__modelname { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; padding-block-start: 12px; font-size: 20px; font-weight: 700; line-height: 1.3; }
.tt-pd__modelhere {
	background: var(--wp--preset--color--brand-gold);
	color: #2a2100;
	font-size: 12px;
	font-weight: 700;
	line-height: 1.5;
	padding: 2px 10px;
	border-radius: 999px;
}
.tt-pd__modelpos { padding-block: 2px 10px; font-size: 14px; font-weight: 500; line-height: 1.45; }
.tt-pd__modelspecs { grid-row: span 5; display: grid; grid-template-rows: subgrid; margin: 0; }
/* label ABOVE value, a thin rule between rows */
.tt-pd__modelspec { padding-block: 7px; border-block-start: 1px solid var(--wp--preset--color--border); }
.tt-pd__modelspec dt { font-size: 12px; line-height: 1.4; color: var(--wp--preset--color--muted); }
.tt-pd__modelspec dd { margin: 0; font-size: 14px; line-height: 1.45; color: var(--wp--preset--color--contrast, #1a1a1a); }
/* the price row: price left, the card's only link right (cycle 42c-tent-3c) */
.tt-pd__modelfoot { display: flex; align-items: center; justify-content: space-between; gap: 8px; border-block-start: 1px solid var(--wp--preset--color--border); }
.tt-pd__modelprice { font-weight: 700; font-size: 15px; }
/* a 44px tap target, the text itself unchanged */
.tt-pd__modellink { display: inline-flex; align-items: center; min-block-size: 44px; min-inline-size: 44px; font-size: 14px; font-weight: 700; color: var(--wp--preset--color--brand-blue); text-decoration: none; white-space: nowrap; }
.tt-pd__modellink:hover,
.tt-pd__modellink:focus-visible { text-decoration: underline; text-underline-offset: 3px; }
/* the frame is a preview: say so with the cursor */
.tt-pd__modelmedia img { cursor: zoom-in; }

/* --- a compact table, scrolling sideways on a phone rather than squeezing */
.tt-pd__tablescroll {
	overflow-x: auto;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card);
	background: #fff;
	color: var(--wp--preset--color--contrast, #1a1a1a);
}
.tt-pd__table { inline-size: 100%; border-collapse: collapse; font-size: 14px; }
.tt-pd__table th,
.tt-pd__table td { padding: 10px 14px; text-align: start; border-block-end: 1px solid var(--wp--preset--color--border); white-space: nowrap; }
/*
 * At desktop widths the cells WRAP. `nowrap` is right on a phone, where the
 * table scrolls on purpose; at 1440 it made the models table (removed in
 * 42c-tent-4b) 1262px inside a 1198px box. Headers and the first column stay on
 * one line — they are the keys.
 */
@media (min-width: 782px) {
	.tt-pd__table td { white-space: normal; }
	/* the size table is short numbers and one sentence: the numbers stay on one
	   line and the note takes what is left */
	.tt-pd__table--sizes td { white-space: nowrap; }
	.tt-pd__table--sizes td.tt-pd__tablenote { white-space: normal; min-inline-size: 180px; }
}
.tt-pd__table thead th { background: var(--tt-pd-grey); font-weight: 700; color: var(--wp--preset--color--brand-blue); }
.tt-pd__table tbody tr:last-child > * { border-block-end: 0; }
.tt-pd__tablenote { color: var(--tt-pd-gold-ink); font-size: 13px; }

/* --- "what is in the set", one column per size (cycle 74)
 * The cells carry a second line saying what the item is, so `nowrap` — right
 * for a table of numbers — would push the widest note across the page. These
 * cells wrap at every width and the row header, which is the item and its
 * link, stays on one line because it is the key.
 */
.tt-pd__table--included td { white-space: normal; vertical-align: top; min-inline-size: 140px; }
.tt-pd__table--included th[scope="row"] { font-weight: 600; white-space: nowrap; }
.tt-pd__table--included th[scope="row"] a { color: var(--wp--preset--color--brand-blue); }
.tt-pd__cellnote { display: block; margin-block-start: 4px; font-size: 13px; line-height: 1.5; color: var(--tt-pd-gold-ink); }

/* --- fabrics: exactly two cards, row for row */
.tt-pd__fabcards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }
.tt-pd__fabcard {
	display: flex;
	flex-direction: column;
	background: #fff;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card);
	box-shadow: var(--tt-pd-shadow);
	overflow: hidden;
	color: var(--wp--preset--color--contrast, #1a1a1a);
}
/*
 * 4:3, contain on white, in BOTH cards (cycle 42c-tent-3b). `overflow: hidden`
 * is the load-bearing part: a box sized only by aspect-ratio keeps an automatic
 * minimum size while overflow is visible, so HD PVC's square photograph grew its
 * box to 588x588 and pushed that card's title 196px below SUB PU's.
 */
.tt-pd__fabmedia { aspect-ratio: 4 / 3; background: #fff; overflow: hidden; }
.tt-pd .tt-pd__fabimg { inline-size: 100%; block-size: 100%; object-fit: cover; }
.tt-pd__fabph { display: grid; place-items: center; border-block-end: 2px dashed var(--wp--preset--color--border); color: var(--wp--preset--color--muted); font-size: var(--wp--preset--font-size--small); }
.tt-pd__fabbody { padding: 20px 22px 22px; }
.tt-pd__fabname { margin: 0 0 4px; font-size: 22px; color: var(--wp--preset--color--brand-blue); }
.tt-pd__fablead { margin: 0 0 14px; color: var(--wp--preset--color--muted); }
.tt-pd__fabrows { display: grid; margin: 0; }
.tt-pd__fabrow { display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 12px; padding-block: 9px; border-block-start: 1px solid var(--wp--preset--color--border); font-size: 15px; }
.tt-pd__fabrow dt { color: var(--wp--preset--color--muted); }
.tt-pd__fabrow dd { margin: 0; }

/* --- size table beside its photograph */
.tt-pd__sizesplit2 { display: grid; grid-template-columns: 320px minmax(0, 1fr); gap: 28px; align-items: start; }
/* no art: one column, or the table sits in the 320px track (21 Sep) */
.tt-pd__sizesplit2--solo { grid-template-columns: minmax(0, 1fr); }
.tt-pd__sizesmedia { border-radius: var(--tt-pd-card); overflow: hidden; }
.tt-pd__sizesmedia.tt-pd__guideph { aspect-ratio: auto; block-size: 240px; }
.tt-pd .tt-pd__sizesimg { inline-size: 100%; block-size: auto; }

/* --- the band, waiting for its photograph (a product opts in) */
.tt-pd__bandph {
	/* the band's height INCLUDING the dashed rules — measured 484 without (gotcha 40) */
	box-sizing: border-box;
	display: grid;
	place-items: center;
	background: var(--tt-pd-grey);
	border-block: 2px dashed var(--wp--preset--color--border);
	color: var(--wp--preset--color--muted);
	font-size: var(--wp--preset--font-size--small);
}

/*
 * --- models: the folded frame above each card (cycle 42c-tent-1b; 130px since
 * 42c-tent-4b). A fixed box with the same air on every side, contain on white,
 * so five frames of different widths sit in five identical boxes and none is
 * cropped. border-box, or the padding adds to the height (gotcha 40).
 */
.tt-pd__modelmedia {
	box-sizing: border-box;
	display: grid;
	place-items: center;
	block-size: 130px;
	padding: 10px;
	margin-inline: -16px;
	background: #fff;
	border-block-end: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card) var(--tt-pd-card) 0 0;
	overflow: hidden;
}
.tt-pd__modelmedia--empty { background: var(--tt-pd-grey); }
/*
 * Size the figure and the image THROUGH the box: core/image wraps the photo in a
 * <figure>, and `.tt-pd .wp-block-image img` (0,2,1) beats a rule on the image's
 * own class — the first render drew a 387x1002 frame inside a 200px box
 * (gotchas 19, 37 and 44).
 */
.tt-pd .tt-pd__modelmedia .wp-block-image { margin: 0; block-size: 100%; inline-size: 100%; display: grid; place-items: center; }
.tt-pd .tt-pd__modelmedia .wp-block-image img { block-size: 110px; inline-size: 100%; object-fit: contain; }

.tt-pd__zonenote { margin: 8px 0 0; font-size: 13px; color: var(--tt-pd-gold-ink); }

/* --- branding: design proofs (cycle 42c-tent-3) --------------------------
 * One white card with the tiles' radius and shadow: a title, the free-design
 * badge, and three 4:3 thumbnails that never crop.
 *
 * The row is `repeat(3, minmax(100px, 1fr))` in a horizontal scroller. That is
 * the whole of the 390 rule the brief set: while three columns of at least 100px
 * fit, they share the width and nothing scrolls; when they do not, each holds
 * 100px and the row scrolls with snap points instead of shrinking the pictures.
 */
.tt-pd__proofcard {
	margin-block-start: 12px;
	padding: 14px 16px 16px;
	background: #fff;
	border-radius: 10px;
	box-shadow: var(--tt-pd-shadow);
	color: var(--wp--preset--color--brand-blue);
}
.tt-pd__proofhead { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; margin-block-end: 12px; }
.tt-pd__proofhead strong { font-size: 15px; line-height: 1.4; }
/* the hero's promo chip, smaller: it sits beside a 15px title, not in a bar */
.tt-pd__promo--sm { padding: 3px 10px; font-size: 13px; font-weight: 700; color: var(--wp--preset--color--brand-blue); }
.tt-pd__proofs {
	display: grid;
	grid-template-columns: repeat(3, minmax(100px, 1fr));
	gap: 12px;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	overscroll-behavior-x: contain;
}
.tt-pd__proof {
	scroll-snap-align: start;
	aspect-ratio: 4 / 3;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: 12px;
	background: #fff;
	overflow: hidden;
}
.tt-pd .tt-pd__proof .wp-block-image { margin: 0; block-size: 100%; inline-size: 100%; }
/* contain on white: a proof is someone's artwork, and a crop would cut it */
.tt-pd .tt-pd__proof .wp-block-image img { inline-size: 100%; block-size: 100%; object-fit: contain; background: #fff; }
/* the dashed "waiting" box, as elsewhere; border-box so the dash stays inside 4:3 */
.tt-pd__proofph {
	box-sizing: border-box;
	display: grid;
	place-items: center;
	padding: 6px;
	border: 2px dashed var(--wp--preset--color--border);
	background: var(--tt-pd-grey);
	color: var(--wp--preset--color--muted);
	font-size: 12px;
	line-height: 1.35;
	text-align: center;
}

/* --- fabrics: one photograph, three details under it, full card width */
.tt-pd__fabmedia .wp-block-image,
.tt-pd__fabthumb .wp-block-image { margin: 0; block-size: 100%; }
.tt-pd__fabmedia .wp-block-image img,
.tt-pd__fabthumb .wp-block-image img { inline-size: 100%; block-size: 100%; object-fit: cover; }
/* the main photograph never crops; a thumbnail only when its data says so */
.tt-pd__fabmedia .wp-block-image img,
.tt-pd__fabthumb--contain .wp-block-image img { object-fit: contain; background: #fff; }
.tt-pd__fabthumb--contain { background: #fff; }
.tt-pd__fabthumbs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; padding: 6px 6px 0; }
.tt-pd__fabthumb { aspect-ratio: 1; overflow: hidden; border-radius: 8px; background: var(--tt-pd-grey); }

/* --- customise: groups of photo tiles (cycle 42c-tent-1b) */
.tt-pd__inbox { margin: 0 0 28px; font-weight: 500; }
.tt-pd__cgroup + .tt-pd__cgroup { margin-block-start: 32px; }
.tt-pd__cgrouptitle { margin: 0 0 14px; font-size: 20px; color: var(--wp--preset--color--brand-blue); }
.tt-pd__ctiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; }
.tt-pd__ctile {
	display: flex;
	flex-direction: column;
	background: #fff;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--tt-pd-card);
	box-shadow: var(--tt-pd-shadow);
	overflow: hidden;
	color: var(--wp--preset--color--contrast, #1a1a1a);
}
.tt-pd__ctilemedia { aspect-ratio: 4 / 3; display: grid; place-items: center; background: #fff; border-block-end: 1px solid var(--wp--preset--color--border); }
.tt-pd__ctileph { background: repeating-linear-gradient(45deg, var(--tt-pd-grey), var(--tt-pd-grey) 8px, #eef1f6 8px, #eef1f6 16px); }
/* through the box, not the image's own class — see the model card note above */
.tt-pd .tt-pd__ctilemedia .wp-block-image { margin: 0; block-size: 100%; inline-size: 100%; }
.tt-pd .tt-pd__ctilemedia .wp-block-image img { inline-size: 100%; block-size: 100%; object-fit: scale-down; }
.tt-pd__ctilebody { display: flex; flex-direction: column; gap: 4px; padding: 12px 14px 14px; flex: 1; }
.tt-pd__ctilebody strong { font-size: 15px; line-height: 1.4; color: var(--wp--preset--color--brand-blue); }
.tt-pd__ctileprice { font-size: 14px; color: var(--wp--preset--color--muted); }
.tt-pd__ctilelink {
	margin-block-start: auto;
	align-self: flex-start;
	padding: 6px 0 0;
	background: none;
	border: 0;
	font: inherit;
	font-size: 14px;
	font-weight: 700;
	color: var(--wp--preset--color--brand-blue);
	text-decoration: underline;
	text-underline-offset: 3px;
	cursor: pointer;
}


@media (max-width: 1024px) {
	.tt-pd__ctiles { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 781px) {
	/*
	 * 390: the same single row, scrolling sideways (cycle 42c-tent-4b). 290px
	 * cards in a 350px column let the next one show at the edge, which is the
	 * whole hint that there is more; snap stops a card half in view. A scroller
	 * clips on both axes, so the bottom padding makes room for the card shadow
	 * (0 10px 30px — about 40px) and the negative margin gives that room back.
	 * Sideways the scroller runs out over the 20px page gutter the same way, so
	 * a card slides to the screen edge instead of being cut at the text column.
	 */
	.tt-pd__models {
		grid-auto-columns: 290px;
		overflow-x: auto;
		scroll-snap-type: x mandatory;
		scrollbar-width: none;
		padding: 4px 20px 40px;
		margin: 0 -20px -36px;
		scroll-padding-inline: 20px;
	}
	.tt-pd__model { scroll-snap-align: start; }
	.tt-pd__fabcards { grid-template-columns: 1fr; }
	/* two tiles a row on a phone, as briefed */
	.tt-pd__ctiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
	.tt-pd__bandph { block-size: 260px; }
	.tt-pd__sizesplit2 { grid-template-columns: 1fr; }
	.tt-pd__table th,
	.tt-pd__table td { padding: 8px 10px; font-size: 13px; }
}

/*
 * Image beside content: the image column is centred against the words at
 * desktop widths (cycle 42c-tent-2). Three splits still pinned it to the top —
 * branding, the tent size table and the non-tent size art — so a 420px photo
 * beside a 530px column of tiles sat flush with the heading and left a gap
 * under itself. The hero, the strip and the generic split already centred.
 * Below 782px everything stacks and this does not apply.
 */
@media (min-width: 782px) {
	.tt-pd__brand,
	.tt-pd__sizesplit2 { align-items: center; }
	.tt-pd__sizesplit > .tt-pd__sizeguide { align-self: center; }
}

@media (prefers-reduced-motion: reduce) {
	.tt-pd img { transition: none !important; }
	.tt-pd .wp-block-image:hover img,
	.tt-pd__cardmedia:hover img { transform: none; }
}

/* ------------------------------------------------------ tablet and phone */
@media (min-width: 1200px) {
	/* the connector, only where the four steps share one row */
	.tt-pd__ostep::after {
		content: "";
		position: absolute;
		inset-block-start: 47px;
		inset-inline-start: 100%;
		inline-size: 20px;
		block-size: 1px;
		background: var(--wp--preset--color--border);
	}
	.tt-pd__ostep:last-child::after { content: none; }
}

@media (max-width: 1024px) {
	.tt-pd__sizesplit { grid-template-columns: 1fr; gap: 28px; }
	/*
	 * Stacked, the picture comes first whichever side it takes on a desktop —
	 * `order: 2` is about left and right, and there is no right here.
	 */
	.tt-pd__sizesplit--flip > .tt-pd__sizeguide { order: 0; }
	.tt-pd__brand { grid-template-columns: 1fr; gap: 28px; }
	.tt-pd__order { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 781px) {
	.tt-pd { --tt-pd-gutter: 20px; }
	.tt-pd__section { padding-block: 40px; }
	/*
	 * Branding stacked: photograph, then the words. The tiles stay 2x3 — three
	 * rows of two is the shape of this block, and letting them fall into one
	 * column would make the section twice as tall for no gain (cycle
	 * 42c-template-7b). The photograph loses its 420px cap because at 390 the
	 * width is the constraint, not the height.
	 */
	.tt-pd__brandshot,
	.tt-pd__brandaside--dia .tt-pd__brandshot,
	.tt-pd__brandph { block-size: auto; aspect-ratio: 3 / 2; }
	.tt-pd__section--hero { padding-block: 40px; }
	/* edge to edge, inside the page's own 16px gutter, and no fade at all */
	.tt-pd__section--band { padding: 0 16px; }
	.tt-pd__bandmedia {
		block-size: 260px;
		inline-size: 100%;
		-webkit-mask-image: none;
		mask-image: none;
		border-radius: var(--tt-pd-card);
		overflow: hidden;
	}
	.tt-pd__section--band .tt-pd__wrap { padding-inline: 16px; }
	.tt-pd__marquee { --tt-strip-fade: 32px; inline-size: calc(100% - 32px); }
	/*
	 * The band is 16px in from the page here, so a shadow that feathered past it
	 * would smudge the white margin. The wrap is exactly the band's width: it
	 * clips the shadow to the band's rounded foot, with room above the words so
	 * the top edge feathers instead of being cut.
	 */
	.tt-pd__section--band .tt-pd__wrap { padding-block: 64px 24px; overflow: hidden; border-end-start-radius: var(--tt-pd-card); border-end-end-radius: var(--tt-pd-card); }
	/* full width, so the feather spans the band and has no side edge to see */
	.tt-pd__bandclaim { inline-size: auto; max-inline-size: none; }
	.tt-pd__bandclaim::before {
		filter: none;
		border-radius: 0;
		inset: -56px -16px -24px;
		background: linear-gradient(to top,
			rgb(0 30 66 / var(--tt-band-shade)) 0 62%,
			rgb(0 30 66 / calc(var(--tt-band-shade) * .45)) 80%,
			rgb(0 30 66 / 0) 100%);
	}
	.tt-pd__h2--band { font-size: 24px; }
	.tt-pd__bandline { font-size: var(--wp--preset--font-size--small); }
	/* Section titles keep the one size (cycle 42c-template-4 §2). Only the hero
	   steps down, because it is 40px on a desktop and not a section title. */
	.tt-pd__h2--hero { font-size: 28px; }
	.tt-pd .tt-pd__hero--natural .tt-pd__heroimg { max-block-size: 480px; }
	.tt-pd__hero--media,
	.tt-pd__strip--media,
	.tt-pd__split,
	.tt-pd__split--3,
	.tt-pd__split--asym { grid-template-columns: 1fr; gap: 24px; }
	.tt-pd__split--flip > .tt-pd__half:last-child { order: 0; }
	.tt-pd__grid4 { grid-template-columns: repeat(2, 1fr); }
	.tt-pd__facts { grid-template-columns: 1fr; }
	.tt-pd__sizes { grid-template-columns: repeat(2, 1fr); }
	.tt-pd__ribbon { flex-direction: column; border-radius: var(--tt-pd-card); padding: 16px; }
	.tt-pd__ribbon .tt-pd__cta { inline-size: 100%; }
	/* the number moves to the left of the words, the card becomes a row */
	.tt-pd__order { grid-template-columns: 1fr; }
	.tt-pd__ostep {
		grid-template-columns: 42px 1fr;
		justify-items: start;
		text-align: start;
		align-items: center;
		column-gap: 16px;
		padding: 18px;
	}
	.tt-pd__onum { grid-row: span 3; }
	.tt-pd__oicon { display: none; }
	.tt-pd__octa .tt-pd__cta { inline-size: 100%; }
	.tt-pd__octa { display: block; }
}

@media (max-width: 600px) {
	.tt-pd__grid2,
	.tt-pd__cards, .tt-pd__sizes { grid-template-columns: repeat(2, 1fr); gap: 10px; }
	/*
	 * The realisations gallery keeps two columns — one turns eight photographs
	 * into eight screens of scrolling (DESIGN.md). The three-photo rows are a
	 * different object: three pictures, each of them a detail somebody is meant
	 * to look at, and at 170px wide nobody can. They stack.
	 */
	/* `grid-template-columns` here has done nothing since the row became a flex
	   container on 19 Sep; only the gap and the margin ever applied. Removed, and
	   the gap set through the variable the basis is derived from. */
	.tt-pd__grid3 { --tt-row-gap: 12px; margin-block-end: 28px; }
	/* four size cards, one under the other, under the comparison graphic */
	.tt-pd__sizecards { grid-template-columns: 1fr; gap: 12px; }
	/* ...unless the product asks for two across */
	.tt-pd__sizecards--m2 { grid-template-columns: repeat(2, 1fr); }
	/* and the sets, which are a price each and need their words readable */
	.tt-pd__grid4 { grid-template-columns: 1fr; gap: 12px; }
	/* two columns of ~170px, and the photographs are square */
	/* the tile is square off its own column now, so no phone height override */
	/* a 148px-wide card does not need a 210px picture box above its words */
	.tt-pd__cardmedia--fit { block-size: 150px; }
	/* the strip: smaller cards, same row */
	.tt-pd__stripcard { inline-size: 100px; block-size: 100px; }
	.tt-pd__player { max-inline-size: min(300px, 100%); border-width: 4px; border-radius: 18px; }
	/*
	 * One column: the player, then the two squares as a pair under it. Source
	 * order is already player-then-stack, so nothing needs reordering — and the
	 * squares take an explicit ratio here, because on a phone there is no player
	 * height beside them to inherit.
	 */
	.tt-pd__videorow,
	.tt-pd__videorow--solo { grid-template-columns: 1fr; justify-content: stretch; gap: 10px; }
	.tt-pd__videostack { grid-template-rows: none; grid-template-columns: 1fr 1fr; gap: 10px; }
	.tt-pd__videosquare { aspect-ratio: 1; }
	.tt-pd__diagram svg { inline-size: min(220px, 100%); }
	.tt-pd__table th { inline-size: 40%; }
	.tt-pd__sizenum { font-size: 32px; }
	.tt-pd__faq { padding: 16px 18px; }
	.tt-pd__band { padding: var(--wp--preset--spacing--40); }
}

/* --- Tent configurator (inc/configurator, assets/js/configurator.js) -------
 * Tokens only: navy, gold, surface-2, border, the existing radii. No new
 * colours and no new font — the prototype was drawn in this palette and the
 * point of rebuilding it here rather than porting it was to keep it that way.
 */
/*
 * `hidden` has to win. Every panel below sets `display`, and a display value in
 * a stylesheet outranks the user agent's `[hidden] { display: none }` — so the
 * drawer, the popover and the toast all rendered open and empty on first paint
 * until this rule existed.
 */
.tt-cfg [hidden],
.tt-cfg__drawer[hidden],
.tt-cfg__drawer [hidden],
.tt-cfg__scrim[hidden],
.tt-cfg__popover[hidden],
.tt-cfg__toast[hidden],
[data-tt-cfg-variant][hidden],
[data-tt-cfg-roofhd][hidden],
[data-tt-cfg-roofsub][hidden] { display: none !important; }

.tt-cfg { display: grid; gap: var(--wp--preset--spacing--50); margin-block: var(--wp--preset--spacing--50); }
.tt-cfg__step { display: grid; gap: 12px; }
.tt-cfg__step.is-dim { opacity: .45; }
.tt-cfg__step.is-skipped .tt-cfg__sws { display: none; }
.tt-cfg__step.is-error { outline: 2px solid var(--wp--preset--color--brand-gold); outline-offset: 8px; border-radius: var(--tt-radius-md); }
.tt-cfg__steptitle { display: flex; align-items: center; gap: 10px; font-size: var(--wp--preset--font-size--medium); margin: 0; color: var(--wp--preset--color--brand-blue); }
.tt-cfg__num { flex: 0 0 26px; block-size: 26px; border-radius: 999px; background: var(--wp--preset--color--brand-blue); color: #fff; font: 600 13px/26px inherit; text-align: center; }
.tt-cfg__stepnote { margin-inline-start: auto; font-size: var(--wp--preset--font-size--small); font-weight: 400; color: var(--wp--preset--color--muted); }
.tt-cfg__note { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); margin: 0; }

/* every control hides its real input and styles the label — the input still
   receives focus, so the ring has to be drawn from it */
/* Scoped by NAME PREFIX, not by ancestor: the drawer is a sibling of .tt-cfg
   (it has to be, it is position:fixed), so a `.tt-cfg input` rule left radio
   circles sitting on top of the roof diagrams — and naming the groups one by one
   let it happen again the day the drawer grew its own size/fabric/colour row. */
.tt-cfg input[type="radio"],
input[type="radio"][name^="tt_cfg_"] { position: absolute; inline-size: 1px; block-size: 1px; opacity: 0; pointer-events: none; }
:is(.tt-cfg__pill, .tt-cfg__fab, .tt-cfg__sw, .tt-cfg__tile, .tt-cfg__none):has(input:focus-visible) { outline: 3px solid var(--wp--preset--color--brand-gold); outline-offset: 2px; }

.tt-cfg__pills { display: flex; flex-wrap: wrap; gap: 8px; }
.tt-cfg__pill { display: grid; gap: 2px; padding: 9px 16px; border: 1px solid var(--wp--preset--color--border); border-radius: 999px; cursor: pointer; background: #fff; }
/* gotcha 33: display:grid beats the UA [hidden]; the single-size radio lives in a hidden pill */
.tt-cfg__pill[hidden] { display: none; }
/* one size is a spec line, not a step (1 Oct 2026) */
.tt-cfg__single { margin: 0; }
.tt-cfg__singleline { margin: 0; font-size: var(--wp--preset--font-size--medium); }
.tt-cfg__singlekey { color: var(--wp--preset--color--muted); }
/* gotcha 33: this component sets `display`, so it needs its own [hidden] rule —
   a second-axis option the chosen size does not sell is hidden (cycle 74). */
.tt-cfg__pill[hidden] { display: none; }
.tt-cfg__pill.is-on { border-color: var(--wp--preset--color--brand-blue); background: var(--wp--preset--color--brand-blue); color: #fff; }
.tt-cfg__pilltext { font-weight: 600; }
.tt-cfg__pillfrom { font-size: 12px; opacity: .75; }
.tt-cfg__pillnote { display: block; font-size: 11px; font-weight: 600; color: var(--wp--preset--color--brand-blue); }
/* the selected pill turns navy; its note has its own colour and must follow the pill's white (gotcha 51) */
.tt-cfg__pill.is-on .tt-cfg__pillnote { color: inherit; }

.tt-cfg__fabs { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.tt-cfg__fab { display: grid; gap: 6px; padding: 10px; border: 1px solid var(--wp--preset--color--border); border-radius: var(--tt-radius-md); cursor: pointer; background: #fff; align-content: start; }
.tt-cfg__fab.is-on { border-color: var(--wp--preset--color--brand-blue); box-shadow: inset 0 0 0 1px var(--wp--preset--color--brand-blue); }
.tt-cfg__fabmedia { display: block; aspect-ratio: 4 / 3; border-radius: 10px; overflow: hidden; background: var(--wp--preset--color--surface-2); }
.tt-cfg__fabmedia img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
.tt-cfg__fabhead { display: flex; align-items: center; gap: 6px; }
.tt-cfg__fabdesc { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }
.tt-cfg__fabprice { font-weight: 700; color: var(--wp--preset--color--brand-blue); }
.tt-cfg__compare { justify-self: start; background: none; border: 0; padding: 0; font: inherit; font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--brand-blue); text-decoration: underline; cursor: pointer; }

.tt-cfg__i { inline-size: 20px; block-size: 20px; border-radius: 999px; border: 1px solid var(--wp--preset--color--border); background: #fff; color: var(--wp--preset--color--brand-blue); font: 600 12px/1 inherit; cursor: pointer; flex: 0 0 auto; }

.tt-cfg__sws { display: flex; flex-wrap: wrap; gap: 10px; }
.tt-cfg__sw { display: grid; justify-items: center; gap: 4px; cursor: pointer; font-size: 12px; }
.tt-cfg__swdot { inline-size: 34px; block-size: 34px; border-radius: 999px; background: var(--tt-sw); border: 1px solid var(--wp--preset--color--border); box-shadow: 0 0 0 2px #fff inset; }
.tt-cfg__sw.is-on .tt-cfg__swdot { outline: 2px solid var(--wp--preset--color--brand-blue); outline-offset: 2px; }

/* --- cycle 50c: no-price choices (the flag shape) ------------------------
   Four silhouettes in a row at 1440, two at 390. The art is drawn, not
   photographed: at 40px the outline is the only thing that reads. */
.tt-cfg__shapes { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
@media (min-width: 640px) { .tt-cfg__shapes { grid-template-columns: repeat(4, 1fr); } }
.tt-cfg__shape { display: grid; justify-items: center; gap: 6px; padding: 10px 6px; border: 1px solid var(--wp--preset--color--border); border-radius: var(--tt-radius-md); background: #fff; cursor: pointer; text-align: center; }
.tt-cfg__shape[hidden] { display: none !important; }
.tt-cfg__shape input { position: absolute; opacity: 0; pointer-events: none; }
.tt-cfg__shapeart { color: var(--wp--preset--color--muted); display: block; }
.tt-cfg__shapeart svg { display: block; fill: currentColor; }
.tt-cfg__shapeart img { display: block; inline-size: auto; block-size: 96px; }
.tt-cfg__shapename { font-size: 12px; line-height: 1.5; }
.tt-cfg__shape--open { border-style: dashed; }
.tt-cfg__shape--open .tt-cfg__shapeart { inline-size: 36px; block-size: 96px; border: 1px dashed var(--wp--preset--color--border); border-radius: 4px; }
.tt-cfg__shape.is-on { border-color: var(--wp--preset--color--brand-blue); box-shadow: inset 0 0 0 1px var(--wp--preset--color--brand-blue); }
.tt-cfg__shape.is-on .tt-cfg__shapeart { color: var(--wp--preset--color--brand-blue); }

/* cycle 52c: a labelled group of accessories */
.tt-cfg__accgroup { font-size: 13px; font-weight: 700; color: var(--wp--preset--color--brand-blue); margin: 14px 0 8px; }
.tt-cfg__accgroup:first-child { margin-block-start: 0; }
.tt-cfg__accs + .tt-cfg__accgroup { margin-block-start: 18px; }

/* one grid per group, every card the same size, two across at every width (owner, 26 Sep 2026) */
.tt-cfg__accs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-rows: 1fr; gap: 10px; }
.tt-cfg__acc { display: grid; grid-template-columns: 44px 1fr; grid-template-areas: "m n" "m p" "s s" "x x"; gap: 2px 10px; align-items: center; padding: 10px; border: 1px solid var(--wp--preset--color--border); border-radius: var(--tt-radius-md); background: #fff; }
.tt-cfg__acc.is-on { border-color: var(--wp--preset--color--brand-blue); }
/* Gotchas 33 and 37 again: this row sets `display`, so it needs its own
   [hidden] rule or an accessory with no price at the chosen size renders. */
.tt-cfg__acc[hidden] { display: none !important; }
.tt-cfg__accmedia { grid-area: m; inline-size: 44px; block-size: 44px; border-radius: 8px; overflow: hidden; background: var(--wp--preset--color--surface-2); }
.tt-cfg__accmedia img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
.tt-cfg__accname { grid-area: n; font-size: 13px; font-weight: 600; }
.tt-cfg__accprice { grid-area: p; font-size: 12px; color: var(--wp--preset--color--muted); }
.tt-cfg__acc .tt-cfg__stepper { grid-area: s; justify-self: end; margin-block-start: 6px; }

.tt-cfg__stepper { display: inline-flex; align-items: center; gap: 8px; }
.tt-cfg__pm { inline-size: 30px; block-size: 30px; border-radius: 999px; border: 0; background: var(--wp--preset--color--brand-blue); color: #fff; font: 600 17px/1 inherit; cursor: pointer; }
.tt-cfg__pm[disabled] { background: #D5DCE8; color: #8E98AC; cursor: not-allowed; }
.tt-cfg__qty { min-inline-size: 2ch; text-align: center; font-weight: 700; }

.tt-cfg__price { display: grid; gap: 10px; padding: 16px; border-radius: var(--tt-radius-md); background: var(--wp--preset--color--surface-2); }
.tt-cfg__totalrow { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.tt-cfg__totallabel { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }
.tt-cfg__total { font-size: var(--wp--preset--font-size--x-large); color: var(--wp--preset--color--brand-blue); }
.tt-cfg__total.is-provisional { opacity: .6; }
/* a quoted size has words where a number goes, and they have to fit (cycle 64b-3) */
.tt-cfg__total.is-quote {
	font-size: var(--wp--preset--font-size--medium);
	font-weight: 600;
	line-height: 1.35;
}
.tt-cfg__pricenote { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); margin: 0; }
.tt-cfg__error { margin: 0; padding: 10px 12px; border-radius: 10px; background: #FDECEC; color: #8A1C1C; font-size: var(--wp--preset--font-size--small); }
.tt-cfg__chips { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.tt-cfg__chip { font-size: 12px; padding: 4px 10px; border-radius: 999px; background: #fff; border: 1px solid var(--wp--preset--color--border); }
.tt-cfg__edit { background: none; border: 0; padding: 0 4px; font: inherit; font-size: 12px; color: var(--wp--preset--color--brand-blue); text-decoration: underline; cursor: pointer; }
.tt-cfg__ctas { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.tt-cfg__cta { display: inline-flex; align-items: center; justify-content: center; min-block-size: 48px; border: 0; border-radius: var(--tt-radius-sm); font: inherit; font-weight: 700; text-decoration: none; cursor: pointer; }
.tt-cfg__cta--cart { background: var(--wp--preset--color--brand-blue); color: #fff; }
.tt-cfg__cta--line { background: var(--wp--preset--color--line-green); color: #fff; gap: 13px; }
.tt-pd__cta--line::before,
.tt-cfg__cta--line::before {
	content: "";
	inline-size: 26px;
	block-size: 26px;
	flex: 0 0 auto;
	background: currentColor;
	-webkit-mask: var(--tt-line-icon) center / contain no-repeat;
	mask: var(--tt-line-icon) center / contain no-repeat;
}
.tt-cfg__more { min-block-size: 44px; border: 1px solid var(--wp--preset--color--brand-blue); border-radius: var(--tt-radius-sm); background: #fff; color: var(--wp--preset--color--brand-blue); font: inherit; font-weight: 700; cursor: pointer; }

/* ---------------------------------------------------------- the drawer */
.tt-cfg__scrim { position: fixed; inset: 0; z-index: 300; background: rgba(11,42,91,.45); }
.tt-cfg__drawer {
	position: fixed; inset-block: 0; inset-inline-end: 0; z-index: 310;
	inline-size: min(640px, 100vw);
	display: grid; grid-template-rows: auto 1fr auto;
	background: var(--wp--preset--color--surface-1, #fff);
	box-shadow: -18px 0 50px rgba(11,42,91,.22);
	transform: translateX(100%);
	transition: transform .22s ease;
}
.tt-cfg__drawer.is-open { transform: none; }
.tt-cfg__dhead { display: flex; align-items: flex-start; gap: 12px; padding: 18px 20px; border-block-end: 1px solid var(--wp--preset--color--border); }
.tt-cfg__dhead h2 { margin: 0; font-size: var(--wp--preset--font-size--large); color: var(--wp--preset--color--brand-blue); }
/* a ring for keyboard focus only - not after a tap, not when the drawer opens (27 Sep 2026) */
.tt-cfg__close:focus:not(:focus-visible) { outline: none; box-shadow: none; }
.tt-cfg__close { margin-inline-start: auto; inline-size: 40px; block-size: 40px; border: 0; border-radius: 999px; background: var(--wp--preset--color--surface-2); font-size: 22px; line-height: 1; cursor: pointer; flex: 0 0 auto; }
.tt-cfg__dbody { overflow: auto; padding: 20px; display: grid; gap: var(--wp--preset--spacing--50); }
.tt-cfg__dsec { display: grid; gap: 10px; }
.tt-cfg__dfoot { padding: 14px 20px; border-block-start: 1px solid var(--wp--preset--color--border); background: var(--wp--preset--color--surface-2); display: grid; gap: 10px; }

.tt-cfg__row { display: grid; gap: 8px; }
.tt-cfg__rowtitle { display: flex; align-items: center; gap: 8px; margin: 0; font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--brand-blue); }
.tt-cfg__used { margin-inline-start: auto; font-weight: 400; color: var(--wp--preset--color--muted); }
.tt-cfg__tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.tt-cfg__tile { display: grid; justify-items: center; gap: 2px; padding: 10px 6px; border: 1px solid var(--wp--preset--color--border); border-radius: 12px; cursor: pointer; background: #fff; }
.tt-cfg__tile.is-on { border-color: var(--wp--preset--color--brand-blue); box-shadow: inset 0 0 0 1px var(--wp--preset--color--brand-blue); }
.tt-cfg__tilesvg { inline-size: 72px; block-size: 72px; }
.tt-cfg__tilesvg svg,
.tt-cfg__tilesvg img { inline-size: 100%; block-size: 100%; display: block; object-fit: contain; }
.tt-cfg__tileid { font-weight: 700; font-size: 13px; }
.tt-cfg__tilesides { font-size: 11px; color: var(--wp--preset--color--muted); }
.tt-cfg__tileprice { font-size: 12px; font-weight: 600; color: var(--wp--preset--color--brand-blue); }
.tt-cfg__none { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 12px 14px; border: 1px solid var(--wp--preset--color--border); border-radius: 12px; cursor: pointer; background: #fff; }
.tt-cfg__none.is-on { border-color: var(--wp--preset--color--brand-blue); box-shadow: inset 0 0 0 1px var(--wp--preset--color--brand-blue); }
.tt-cfg__included { display: grid; gap: 6px; padding: 16px; border-radius: var(--tt-radius-md); background: var(--wp--preset--color--surface-2); }
.tt-cfg__badge { justify-self: start; background: var(--wp--preset--color--brand-gold); color: #2a2100; font-weight: 700; font-size: 12px; padding: 4px 10px; border-radius: 6px; }
.tt-cfg__included p { margin: 0; font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }

.tt-cfg__wallgroup { display: grid; gap: 6px; padding: 12px; border: 1px solid var(--wp--preset--color--border); border-radius: var(--tt-radius-md); background: #fff; }
.tt-cfg__wall { display: grid; grid-template-columns: 1fr auto auto; gap: 10px; align-items: center; }
.tt-cfg__wall.is-max { opacity: .5; }
.tt-cfg__wallname { display: flex; align-items: center; gap: 6px; font-size: var(--wp--preset--font-size--small); }
.tt-cfg__wallprice { font-size: 12px; color: var(--wp--preset--color--muted); white-space: nowrap; }
.tt-cfg__maxnote { color: var(--wp--preset--color--brand-gold); }
/*
 * "why is this option not here" (cycle 42c-tent-1). Muted, not gold: it explains
 * a product fact, it is not a warning about something the customer did. And an
 * explicit [hidden] rule, because this block sets `display` — gotchas 12, 33
 * and 37, which is three times now.
 */
.tt-cfg__whynote { color: var(--wp--preset--color--muted); }
.tt-cfg__whynote[hidden] { display: none; }

.tt-cfg__items { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.tt-cfg__items li { display: flex; justify-content: space-between; gap: 12px; font-size: var(--wp--preset--font-size--small); }
.tt-cfg__sharerow { display: flex; gap: 8px; }
.tt-cfg__sharerow input { flex: 1 1 auto; min-inline-size: 0; padding: 10px 12px; border: 1px solid var(--wp--preset--color--border); border-radius: var(--tt-radius-sm); font: inherit; font-size: 13px; background: var(--wp--preset--color--surface-2); }
.tt-cfg__copy { padding-inline: 16px; border: 0; border-radius: var(--tt-radius-sm); background: var(--wp--preset--color--brand-blue); color: #fff; font: inherit; font-weight: 700; cursor: pointer; }

/* --------------------------------------------------- popover and toast */
.tt-cfg__popover {
	position: fixed; z-index: 320; inset-block-start: 50%; inset-inline-start: 50%;
	transform: translate(-50%, -50%);
	inline-size: min(480px, calc(100vw - 32px)); max-block-size: 80vh; overflow: auto;
	background: #fff; border-radius: var(--tt-radius-md); padding: 20px;
	box-shadow: 0 18px 50px rgba(11,42,91,.28);
	display: grid; gap: 10px;
}
.tt-cfg__popover.is-table { inline-size: min(620px, calc(100vw - 32px)); }
.tt-cfg__popover h3 { margin: 0; padding-inline-end: 44px; font-size: var(--wp--preset--font-size--medium); color: var(--wp--preset--color--brand-blue); }
.tt-cfg__popover .tt-cfg__close { position: absolute; inset-block-start: 12px; inset-inline-end: 12px; }
.tt-cfg__popmedia:empty { display: none; }
.tt-cfg__popmedia img { inline-size: 100%; block-size: auto; border-radius: 10px; display: block; }
.tt-cfg__popover p { margin: 0; font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }
.tt-cfg__table { inline-size: 100%; border-collapse: collapse; font-size: 13px; }
.tt-cfg__table :is(th, td) { border: 1px solid var(--wp--preset--color--border); padding: 8px 10px; text-align: start; vertical-align: top; color: var(--wp--preset--color--contrast, inherit); }
.tt-cfg__table thead th { background: var(--wp--preset--color--surface-2); color: var(--wp--preset--color--brand-blue); }
/*
 * `.tt-cfg__ph` — the striped stand-in — is gone (owner, 21 Sep 2026). A tile
 * with no photograph now renders no media element at all; these two rules are
 * what lets the tile close the gap instead of leaving a hole where the box was.
 */
.tt-cfg__acc--nopic { grid-template-columns: 1fr auto; grid-template-areas: "n s" "p s" "x x"; }
.tt-cfg__acc--nopic .tt-cfg__stepper { align-self: center; margin-block-start: 0; }
.tt-cfg__toast { position: fixed; z-index: 330; inset-block-end: 20px; inset-inline: 20px; margin-inline: auto; inline-size: fit-content; max-inline-size: calc(100vw - 40px); background: var(--wp--preset--color--brand-blue); color: #fff; padding: 12px 18px; border-radius: 999px; font-size: var(--wp--preset--font-size--small); box-shadow: 0 10px 30px rgba(11,42,91,.3); }

/* ------------------------------------------------- the gallery preview */
.tt-cfg__preview { display: grid; place-items: center; gap: 8px; padding: 12px; background: var(--wp--preset--color--surface-2); border-radius: var(--tt-radius-md); }
.tt-cfg__previewinner { inline-size: min(100%, 420px); aspect-ratio: 1; }
.tt-cfg__previewinner svg, .tt-cfg__previewinner img { inline-size: 100%; block-size: 100%; object-fit: contain; display: block; }
/* a photograph sits on a blurred copy of itself; the roof drawing does not */
.tt-cfg__previewinner[style*="--tt-fill"]:has(> img:not(.tt-cfg__roofimg)) { position: relative; overflow: hidden; border-radius: var(--tt-radius-md); }
.tt-cfg__previewinner[style*="--tt-fill"]:has(> img:not(.tt-cfg__roofimg))::before {
	content: "";
	position: absolute;
	inset: 0;
	background: var(--tt-fill) center / cover no-repeat;
	transform: scale(1.6);
	filter: blur(22px) saturate(1.1) brightness(0.8);
}
.tt-cfg__previewinner > img { position: relative; object-fit: contain !important; }
.tt-cfg__previewlabel { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }

body.tt-cfg-locked { overflow: hidden; }
/*
 * One green button on screen, always. The drawer carries its own cart and
 * LINE row, so the page's row steps aside while the drawer is open — on a
 * desktop it sits behind the fixed panel anyway, which is occlusion, not a
 * decision. The drawer is a SIBLING of .tt-cfg (gotcha 34), so this selector
 * reaches the page row only.
 */
body.tt-cfg-locked .tt-cfg .tt-cfg__ctas { display: none; }

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

@media (max-width: 781px) {
	.tt-cfg__fabs, .tt-cfg__ctas { grid-template-columns: 1fr; }
	/* half a phone is too narrow for picture-beside-name: stack the card */
	.tt-cfg__acc { grid-template-columns: minmax(0, 1fr); grid-template-areas: "m" "n" "p" "s" "x"; align-content: start; }
	.tt-cfg__acc--nopic { grid-template-areas: "n" "p" "s" "x"; }
	.tt-cfg__acc .tt-cfg__stepper,
	.tt-cfg__acc--nopic .tt-cfg__stepper { justify-self: start; align-self: end; margin-block-start: 6px; }
	.tt-cfg__drawer { inline-size: 100vw; }
	/* the drawer footer IS the sticky bar here; the page-level one is filtered
	   off in inc/configurator/render.php */
	.tt-cfg__dbody { padding: 16px; }
	.tt-cfg__tilesvg { inline-size: 62px; block-size: 62px; }
}

/* --- cycle 45d: share by device, and the QR modal ------------------------- */
.tt-cfg__ctarow { display: grid; gap: 10px; }
.tt-cfg__cta--ghost { background: #fff; color: var(--wp--preset--color--brand-blue); border: 1px solid var(--wp--preset--color--brand-blue); }
.tt-cfg__stale { color: var(--wp--preset--color--brand-gold); font-weight: 600; }
.tt-cfg__qrmodal { text-align: center; }
.tt-cfg__qrmodal h3 { padding-inline: 8px; text-align: start; }
.tt-cfg__qr { display: grid; place-items: center; }
/* A version-14 symbol is 73 modules. At 260px that is 3.2 device pixels per
   module on a 1x monitor, which is close to what a phone camera can resolve at
   arm's length; 320 gives it room. */
.tt-cfg__qr svg { inline-size: min(320px, 74vw); block-size: auto; display: block; image-rendering: pixelated; }
.tt-cfg__qrmodal code { font-size: 15px; font-weight: 700; color: var(--wp--preset--color--brand-blue); }
.tt-cfg__gone { margin-block: 1em; }

@media (min-width: 782px) {
	.tt-cfg__ctarow { grid-template-columns: 1fr 1fr; }
	.tt-cfg__ctarow:has(> :only-child) { grid-template-columns: 1fr; }
}

/* --- cycle 45d-fix: the base row at the top of the drawer ---------------- */
.tt-cfg__base { gap: 8px; padding: 12px; border: 1px solid var(--wp--preset--color--border); border-radius: var(--tt-radius-md); background: var(--wp--preset--color--surface-2); }
.tt-cfg__base.is-wanted { border-color: var(--wp--preset--color--brand-gold); box-shadow: inset 0 0 0 1px var(--wp--preset--color--brand-gold); }
.tt-cfg__baserow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.tt-cfg__baselabel { flex: 0 0 5.5em; font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }
.tt-cfg__pill--mini { padding: 5px 12px; gap: 0; }
.tt-cfg__pill--mini .tt-cfg__pilltext { font-size: 13px; }
.tt-cfg__base .tt-cfg__swdot { inline-size: 26px; block-size: 26px; }
.tt-cfg__base .tt-cfg__swname { font-size: 11px; }
.tt-cfg__basenote { color: var(--wp--preset--color--brand-gold); font-weight: 600; }
/*
 * The roof colour as a drawer step (owner, 26 Sep 2026). Each of these sets a
 * `display` of its own, which beats the UA `[hidden]` rule (gotcha 33).
 */
.tt-cfg__dsec[hidden],
.tt-cfg__baserow[hidden],
.tt-cfg__colourprompt[hidden] { display: none !important; }
.tt-cfg__colourstep.is-wanted { padding: 12px; border: 1px solid var(--wp--preset--color--brand-gold); border-radius: var(--tt-radius-md); box-shadow: inset 0 0 0 1px var(--wp--preset--color--brand-gold); }
.tt-cfg__basevalue { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; }
.tt-cfg__basevalue .tt-cfg__swdot { inline-size: 18px; block-size: 18px; }
.tt-cfg__colourprompt {
	inline-size: 100%;
	min-block-size: 44px;
	margin-block-start: 8px;
	border: 2px solid var(--wp--preset--color--brand-gold);
	border-radius: var(--tt-radius-sm);
	background: #fff;
	color: var(--wp--preset--color--brand-blue);
	font: inherit;
	font-weight: 700;
	cursor: pointer;
}
.tt-cfg__cta[disabled] { opacity: .45; cursor: not-allowed; }
/* quantity: the whole configuration x this, one cart line (26 Sep 2026) */
.tt-cfg__qtyrow { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-block-end: 12px; }
.tt-cfg__qtylabel { font-weight: 700; color: var(--wp--preset--color--brand-blue); }
.tt-cfg__stepper--qty { display: inline-flex; align-items: stretch; gap: 0; border: 1px solid var(--wp--preset--color--border); border-radius: var(--tt-radius-sm); overflow: hidden; background: #fff; }
.tt-cfg__qtypm { inline-size: 44px; min-block-size: 44px; border: 0; background: var(--wp--preset--color--surface-2); color: var(--wp--preset--color--brand-blue); font: inherit; font-size: 20px; font-weight: 700; cursor: pointer; }
.tt-cfg__qtypm[disabled] { opacity: .4; cursor: not-allowed; }
.tt-cfg__qtyinput { inline-size: 72px; border: 0; border-inline: 1px solid var(--wp--preset--color--border); text-align: center; font: inherit; font-weight: 700; -moz-appearance: textfield; }
.tt-cfg__qtyinput::-webkit-outer-spin-button,
.tt-cfg__qtyinput::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.tt-cfg__qtynote { flex: 1 1 100%; margin: 0; }
.tt-cfg__qtynote[hidden] { display: none; }
.tt-cfg__accmatch { grid-area: x; justify-self: start; margin-block-start: 6px; padding: 4px 10px; border: 1px solid var(--wp--preset--color--brand-blue); border-radius: 999px; background: #fff; color: var(--wp--preset--color--brand-blue); font: inherit; font-size: 12px; font-weight: 600; cursor: pointer; }
.tt-cfg__accmatch[hidden] { display: none; }
/* the breakdown a /c/<code> page shows the office (26 Sep 2026) */
.tt-cfg__fromlink { display: grid; gap: 6px; padding: 12px; margin-block-end: 12px; border: 1px solid var(--wp--preset--color--border); border-radius: var(--tt-radius-md); background: var(--wp--preset--color--surface-2); color: var(--wp--preset--color--brand-blue); }
.tt-cfg__fromlink[hidden] { display: none; }
.tt-cfg__fromlinkhead { margin: 0; font-size: 12px; color: var(--wp--preset--color--muted); }
.tt-cfg__fromlinkname { margin: 0; font-weight: 700; }
.tt-cfg__fromlinkitems { margin: 0; padding: 0; list-style: none; display: grid; gap: 4px; font-size: 14px; }
.tt-cfg__fromlinkitems li,
.tt-cfg__fromlinktotal { display: flex; justify-content: space-between; gap: 12px; }
.tt-cfg__fromlinkitems li span:last-child { white-space: nowrap; }
.tt-cfg__fromlinktotal { margin: 4px 0 0; padding-block-start: 6px; border-block-start: 1px solid var(--wp--preset--color--border); font-weight: 700; }

/* --- cycle 45f: one green button per product page ------------------------ */
/* Off screen, not gone: copying without the Clipboard API means selecting a
   real field, and a display:none one cannot be selected. */
.tt-cfg__offscreen { position: absolute; inline-size: 1px; block-size: 1px; padding: 0; border: 0; clip-path: inset(50%); overflow: hidden; white-space: nowrap; }
.tt-cfg__sharebits:not(:has(.tt-cfg__stale:not([hidden]))) { margin: 0; }
.tt-cfg--plain { margin-block-start: var(--wp--preset--spacing--40); }
.tt-cfg__ctas--plain { grid-template-columns: 1fr; }

/* ==========================================================================
   FOOTER AT 390 — one order, one contact row, reachable taps
   (owner's review, 21 Sep 2026)

   What he saw, and what each line answers:

     "legal links wrap inside the products column"  — they were a pattern
       nested in that column. They are now the footer's own last row, full
       width, in the markup rather than by CSS.
     "two link columns of unequal length"  — 159px each, 403px tall, side by
       side on a 350px footer. They stack.
     "contact data scattered"  — the order is now fixed: brand and address,
       then the three contact actions in one row, then the link groups, then
       the e-mail, then legal.
     "the Facebook button is a bare word"  — the icons carry a visible label
       beside the glyph, so each action reads as an action.

   Measured before: footer 1084px tall, 24 of 28 links with a tap target under
   44px. Desktop is untouched — everything here is inside the phone query.
   ========================================================================== */
@media (max-width: 781px) {
	.tt-footer .wp-block-columns {
		display: flex;
		flex-direction: column;
		gap: var(--wp--preset--spacing--60);
	}
	.tt-footer .wp-block-column { inline-size: 100% !important; flex-basis: auto !important; }

	/*
	 * The three contact actions as one row of equal buttons. `nowrap` is kept
	 * from the desktop group — three labelled buttons fit 350px at 1fr each,
	 * and wrapping them would put one alone on a second line.
	 */
	/* pills that wrap whole (29 Sep 2026); 48, not 44: the whole pill is the tap target */
	.tt-footer .tt-icon > a { min-block-size: 48px; }
	.tt-footer .tt-icon__label {
		font-size: var(--wp--preset--font-size--small);
		line-height: 1;
		white-space: nowrap;
	}

	/* every footer link is reachable, not only the buttons */
	/*
	 * `.tt-footer .wp-block-list li > a` sets 24px further up this file and is
	 * more specific than `.tt-footer a`, so the first version of this rule lost
	 * silently and the list links stayed 28px tall (gotcha 19 — the fourth time
	 * this file has paid for it). Matched at the same depth instead of reaching
	 * for !important.
	 */
	.tt-footer a,
	.tt-footer .wp-block-list li > a {
		min-block-size: 44px;
		display: inline-flex;
		align-items: center;
	}
	.tt-footer .wp-block-list li > a { inline-size: 100%; }

	/*
	 * The legal row: full width at the very bottom, centred, wrapping as one
	 * list rather than as a column of single links.
	 */
	.tt-footer__legalrow { inline-size: 100%; }
	.tt-footer__legalrow .tt-footer__legal {
		display: flex;
		flex-wrap: wrap;
		justify-content: center;
		gap: 4px 14px;
		text-align: center;
	}
	.tt-footer__legalrow .tt-footer__legal a { min-block-size: 44px; }
}

/* --- cart & checkout, the Thai way (26 Sep 2026) --------------------------- */
.wc-block-components-totals-wrapper .tt-when,
.tt-when { display: grid; gap: 4px; margin-block: 12px 4px; padding: 12px 14px !important; box-sizing: border-box; border: 1px solid var(--wp--preset--color--border); border-radius: var(--tt-radius-md); background: var(--wp--preset--color--surface-2); color: var(--wp--preset--color--brand-blue); }
.tt-when p { margin: 0; font-size: 14px; line-height: 1.5; }
.tt-when__title { font-weight: 700; }
.tt-when__deposit { font-weight: 600; }
.tt-when__help { justify-self: start; margin-block-start: 4px; font-size: 14px; font-weight: 700; color: var(--wp--custom--color--line, #06C755); text-decoration: underline; }
.tt-cart-edit { display: inline-block; margin-block-start: 6px; font-size: 14px; font-weight: 600; color: var(--wp--preset--color--brand-blue); text-decoration: underline; }
.tt-postnote { margin: 4px 0 0; font-size: 13px; color: var(--wp--preset--color--muted); }
.tt-postnote[hidden] { display: none; }
/* the empty cart (26 Sep 2026) */
.tt-empty-cart { padding-block: var(--wp--preset--spacing--60); }
.tt-empty-cart__lead { color: var(--wp--preset--color--muted); }
.tt-empty-cart__cats { gap: 10px; margin-block: var(--wp--preset--spacing--50); }
.tt-empty-cart__line a { font-weight: 700; color: var(--wp--custom--color--line, #06C755); }
/* the phone's pinned checkout bar carries the total too (26 Sep 2026) */
.tt-sticky-total { display: none; }
.wc-block-cart__submit-container--sticky { display: flex; align-items: center; gap: 12px; }
.wc-block-cart__submit-container--sticky .tt-sticky-total { display: block; flex: 0 0 auto; font-weight: 700; color: var(--wp--preset--color--brand-blue); white-space: nowrap; }
.wc-block-cart__submit-container--sticky .wc-block-cart__submit-button { flex: 1 1 auto; }
/* one country only (Thailand): the country field has nothing to ask (26 Sep 2026) */
.wc-block-components-address-form__country { display: none; }

/* ------------------------------------------------------------------
 * Factory strip — the landing pages' one line about the factory
 * (owner, 29 Sep 2026). Photo left, three facts right; without a photo
 * the facts span the width in three columns. Every colour stated, so it
 * reads the same on whatever ground it is dropped on (gotcha 51).
 * ------------------------------------------------------------------ */
.tt-fstrip { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--wp--preset--spacing--60); align-items: center; box-sizing: border-box; inline-size: 100%; max-inline-size: 1200px; margin-inline: auto; padding: var(--wp--preset--spacing--50); border: 1px solid var(--wp--preset--color--border); border-radius: 16px; background: var(--wp--preset--color--surface-blue-soft); color: #1a1a1a; }
.tt-fstrip--nophoto { grid-template-columns: minmax(0, 1fr); }
.tt-fstrip__media { margin: 0; }
.tt-fstrip__img { display: block; inline-size: 100%; block-size: auto; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 12px; }
.tt-fstrip__title { margin: 0 0 var(--wp--preset--spacing--40); color: var(--wp--preset--color--brand-blue); }
.tt-fstrip__facts { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--wp--preset--spacing--40); }
.tt-fstrip--nophoto .tt-fstrip__facts { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.tt-fstrip__fact { display: flex; gap: 12px; align-items: flex-start; }
.tt-fstrip__icon { flex: none; line-height: 0; color: var(--wp--preset--color--brand-blue); }
.tt-fstrip__text { display: grid; gap: 2px; }
.tt-fstrip__text strong { color: #1a1a1a; }
.tt-fstrip__text span { color: #595959; font-size: .9375rem; }
.tt-fstrip__link { display: inline-block; margin-block-start: var(--wp--preset--spacing--50); font-weight: 600; color: var(--wp--preset--color--brand-blue); }
@media (max-width: 781px) {
	.tt-fstrip { grid-template-columns: minmax(0, 1fr); }
	.tt-fstrip--nophoto .tt-fstrip__facts { grid-template-columns: minmax(0, 1fr); }
}

/* ------------------------------------------------------------------
 * The hero honours its slot's fit on desktop (29 Sep 2026).
 * inc/hero-slot.php writes --tt-hero-fit / --tt-hero-pos from the slot
 * screen; three classes, because core's cover rule pins object-fit: cover
 * with two (gotcha 19). Phones keep their own layout: none of this applies
 * below 782px.
 * ------------------------------------------------------------------ */
@media (min-width: 782px) {
	.wp-block-cover.tt-hero .wp-block-cover__image-background {
		object-fit: var(--tt-hero-fit, cover);
		object-position: var(--tt-hero-pos, 50% 50%);
	}
	/*
	 * The home hero file carries its own backdrop (29 Sep 2026): the owner's
	 * render on royal blue, extended left by scripts/build-hero-home.py so the
	 * products clear the copy. A navy gradient on top would only turn that blue
	 * into a second, different blue; the copy sits on the file's darkest part
	 * (white 14:1, the gold eyebrow 6:1 there). Phones keep theirs.
	 */
	body.home .wp-block-cover.tt-hero > .wp-block-cover__background { display: none; }
	/*
	 * The lead is as wide as the button row (owner, 29 Sep 2026): the copy
	 * column shrinks to its widest line that is not the lead - the buttons -
	 * and the lead, contained on its inline axis, adds nothing to that width
	 * and then fills it.
	 */
	body.home .tt-hero .wp-block-cover__inner-container > .wp-block-group { inline-size: fit-content; }
	body.home .tt-hero .tt-hero__lead { contain: inline-size; max-inline-size: none; }
	/* "fit": whatever the picture does not cover is the navy the gradient starts from */
	.wp-block-cover.tt-hero.tt-hero--fit { background-color: var(--wp--preset--color--band); }
	/* "fit on a blurred fill": a blurred copy behind the picture, under the gradient */
	.wp-block-cover.tt-hero.tt-hero--blur { overflow: hidden; }
	.wp-block-cover.tt-hero.tt-hero--blur::before {
		content: "";
		position: absolute;
		inset: -24px;
		z-index: 0;
		background: var(--tt-hero-fill) center / cover no-repeat;
		filter: blur(24px);
	}
}

@media (min-width: 782px) and (max-width: 1100px) {
	/*
	 * Below 1100 the hero narrows and the button row reached the tent (1px at
	 * 1024). Half the left padding gives the copy its room back; `!important`
	 * because core writes the cover's padding inline.
	 */
	body.home .wp-block-cover.tt-hero.tt-hero { padding-left: var(--wp--preset--spacing--60) !important; }
}
@media (min-width: 782px) and (max-width: 1015px) {
	/*
	 * Narrower still, the copy cannot clear the products (the hero keeps the
	 * file's ratio, so the tent starts at 40 % however narrow it gets): a navy
	 * scrim comes back under the copy, dense to just past the lead's end and
	 * gone by 75 %. The block's own gradient (0.85 -> 0.35) left the lead at
	 * 2.8:1 over the tent at 782; `!important` because core writes it inline.
	 */
	body.home .wp-block-cover.tt-hero > .wp-block-cover__background {
		display: block;
		opacity: 1;
		background: linear-gradient(90deg, rgba(0, 38, 90, .92) 0%, rgba(0, 38, 90, .86) 52%, rgba(0, 38, 90, 0) 75%) !important;
	}
}

/* ------------------------------------------------------------------
 * ผลงานที่ผ่านมา (29 Sep 2026): photograph, the client's logo, one line.
 * Three across at desktop, two on a tablet, one on a phone. A quote and a
 * name are optional and take no room while empty; so does the note shown
 * to the owner while a line is unwritten (never on production).
 * ------------------------------------------------------------------ */
/* flex, not grid: a row with fewer than three cards is centred (owner, 29 Sep 2026) */
.tt-works {
	--tt-works-cols: 3;
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--wp--preset--spacing--50);
	list-style: none;
	margin: var(--wp--preset--spacing--70) 0 0;
	padding: 0;
}
.tt-work { box-sizing: border-box; flex: 0 1 calc((100% - (var(--tt-works-cols) - 1) * var(--wp--preset--spacing--50)) / var(--tt-works-cols)); min-inline-size: 0; margin: 0; display: flex; flex-direction: column; background: #fff; border: 1px solid var(--wp--preset--color--border); border-radius: var(--tt-radius-md); overflow: hidden; color: #1a1a1a; }
/* overflow: a lazy image carries a 3000x1500 placeholder size (gotcha 58), and as a flex
   item the figure would otherwise grow to it - 1500px tall photographs, 29 Sep 2026 */
.tt-work__photo { margin: 0; aspect-ratio: 4 / 3; overflow: hidden; background: var(--wp--preset--color--surface-2); }
.tt-work__photo img { display: block; inline-size: 100%; block-size: 100%; object-fit: cover; }
.tt-work__body { display: grid; gap: var(--wp--preset--spacing--30); padding: var(--wp--preset--spacing--40); }
.tt-work__logo { display: block; inline-size: 120px; aspect-ratio: 3 / 2; }
.tt-work__logo img { display: block; inline-size: 100%; block-size: 100%; object-fit: contain; }
.tt-work__text { margin: 0; color: #1a1a1a; }
.tt-work__quote { margin: 0; padding: 0 0 0 var(--wp--preset--spacing--30); border-inline-start: 3px solid var(--wp--preset--color--brand-gold); color: #595959; }
.tt-work__quote p { margin: 0; }
.tt-work__quote cite { display: block; margin-block-start: 4px; font-style: normal; font-weight: 700; color: #1a1a1a; }
/* a brand with no logo in the slots: its name, set where the logo would sit */
.tt-work__name { display: flex; align-items: center; min-block-size: 80px; font-weight: 700; font-size: 1.25rem; letter-spacing: .02em; color: var(--wp--preset--color--brand-blue); }
@media (max-width: 1024px) { .tt-works { --tt-works-cols: 2; } }
@media (max-width: 600px)  { .tt-works { --tt-works-cols: 1; } }

/* ------------------------------------------------------------------
 * The catalogue band's cover (29 Sep 2026): page one of the PDF, as a
 * printed cover - a white edge, a soft shadow, a slight tilt. Room is
 * left round it so the tilt and the shadow are never clipped.
 * ------------------------------------------------------------------ */
.tt-catcover { flex: none; display: block; line-height: 0; padding: 8px 10px 14px; }
.tt-catcover img {
	display: block;
	block-size: 120px;
	inline-size: auto;
	aspect-ratio: 170 / 240;
	border: 3px solid #fff;
	border-radius: 2px;
	box-shadow: 0 10px 22px rgba(0, 12, 40, .45), 0 2px 5px rgba(0, 12, 40, .3);
	transform: rotate(-4deg);
	/* the band inverts images to white (.is-style-band-dark .wp-block-image img); not this one */
	filter: none;
}
/* the fallback: the factory strip's weight - 2 on a 24 grid at 28px is 2.33px, so 1 at 56px */
.tt-catcover--icon { padding: 0; color: #fff; }
.tt-catcover__ico { stroke-width: 1; }
@media (max-width: 600px) {
	.tt-catcover img { block-size: 96px; }
}

/* ------------------------------------------------------------------
 * Event post galleries (29 Sep 2026): an even grid of 4:3 tiles, three
 * across, two on a phone. Core's flex gallery gave a portrait photograph
 * a row of its own and left holes beside it. The lightbox shows each
 * photograph whole. `:not(#individual-image)` matches core's own selector,
 * which carries an id's weight (gotcha 19).
 * ------------------------------------------------------------------ */
.wp-block-gallery.tt-event-gallery.has-nested-images {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 12px;
}
.wp-block-gallery.tt-event-gallery.has-nested-images figure.wp-block-image:not(#individual-image) {
	inline-size: auto;
	margin: 0;
	aspect-ratio: 4 / 3;
	/* a lazy image's 3000x1500 placeholder must not size the tile (gotcha 58) */
	overflow: hidden;
}
.wp-block-gallery.tt-event-gallery.has-nested-images figure.wp-block-image:not(#individual-image) img {
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
}
@media (max-width: 600px) {
	.wp-block-gallery.tt-event-gallery.has-nested-images { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
}

/* Home factory band (29 Sep 2026): the link to About sits on navy - the site's
   link colour is navy too, so it needs its own (gold, as the sub-headline) */
.tt-factory .tt-factory__more a { color: var(--wp--preset--color--brand-gold); font-weight: 700; text-underline-offset: 3px; }
.tt-factory .tt-factory__sub { margin-block-start: var(--wp--preset--spacing--30); }

/* ------------------------------------------------------------------
 * Blog title band (29 Sep 2026): the index and the category pages open
 * on a slim navy band - title and breadcrumb - and the list right below.
 * ------------------------------------------------------------------ */
.tt-titleband { padding-block: 32px; }
.tt-titleband h1 { margin: 0; font-size: clamp(1.75rem, 1.4rem + 1.2vw, 2.5rem); line-height: 1.5; }
:is(.tt-titleband, .tt-hero) .tt-breadcrumbs { margin-block-start: 4px; font-size: var(--wp--preset--font-size--small); }
:is(.tt-titleband, .tt-hero) .tt-breadcrumbs ol { margin: 0; color: #fff; } /* the base ol is muted grey: 2.4:1 on navy */
:is(.tt-titleband, .tt-hero) .tt-breadcrumbs a { color: color-mix(in srgb, #fff 78%, transparent); }
:is(.tt-titleband, .tt-hero) .tt-breadcrumbs a:hover { color: #fff; }
:is(.tt-titleband, .tt-hero) .tt-breadcrumbs [aria-current] { color: #fff; }
:is(.tt-titleband, .tt-hero) .tt-breadcrumbs li + li::before { color: color-mix(in srgb, #fff 45%, transparent); }
@media (max-width: 600px) { .tt-titleband { padding-block: 20px; } }

/* ------------------------------------------------------------------
 * Quantity pricing under the price (inc/qty-tiers.php, 29 Sep 2026).
 * ------------------------------------------------------------------ */
.tt-qty-from { font-size: .6em; font-weight: 500; color: var(--wp--preset--color--muted); margin-inline-end: .25em; }
.tt-qtytiers { margin-block: var(--wp--preset--spacing--30) var(--wp--preset--spacing--40); }
.tt-qtytiers__title { margin: 0 0 6px; font-weight: 700; font-size: var(--wp--preset--font-size--small); }
.tt-qtytiers__table { border-collapse: collapse; inline-size: 100%; max-inline-size: 360px; font-size: var(--wp--preset--font-size--small); }
.tt-qtytiers__table th, .tt-qtytiers__table td { padding: 6px 10px; border: 1px solid var(--wp--preset--color--border); text-align: start; font-weight: 500; }
.tt-qtytiers__table td { font-weight: 700; color: var(--wp--preset--color--brand-blue); text-align: end; }
.tt-qtytiers__spec { margin: 8px 0 0; font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--muted); }
/* configurator size groups (the LED backdrop by height, 29 Sep 2026): a heading on its own row */
.tt-cfg__pillgroup { flex: 0 0 100%; margin-block: 6px 0; font-size: var(--wp--preset--font-size--small); font-weight: 700; color: var(--wp--preset--color--muted); }
.tt-cfg__pillgroup:first-child { margin-block-start: 0; }
/* the contact page's band carries a line and two buttons (29 Sep 2026) */
.tt-titleband__intro { margin: 10px 0 0; max-inline-size: 60ch; }
.tt-titleband__ctas { margin-block-start: var(--wp--preset--spacing--40); gap: 12px; }
.tt-titleband .is-style-outline .wp-block-button__link { color: #fff; border-color: rgba(255, 255, 255, .7); background: transparent; }
.tt-titleband .is-style-outline .wp-block-button__link:hover { background: rgba(255, 255, 255, .12); }

/* ------------------------------------------------------------------
 * The contact map before it loads (29 Sep 2026): a card at the loaded
 * map's own height (270 / 386), so the click moves nothing.
 * ------------------------------------------------------------------ */
.tt-map__card {
	display: grid;
	align-content: center;
	justify-items: center;
	gap: 12px;
	box-sizing: border-box;
	/* the frame's height plus its two 1px borders: the click must not move the page */
	block-size: 272px;
	padding: 20px;
	border: 1px solid var(--wp--preset--color--border);
	border-radius: 16px;
	background: var(--wp--preset--color--surface-2, #f6f7fb);
	text-align: center;
}
@media (min-width: 900px) { .tt-map__card { block-size: 388px; } }
.tt-map__pin { color: var(--wp--preset--color--brand-blue); line-height: 0; }
.tt-map__addr { margin: 0; line-height: 1.6; }
.tt-map__cardbtns { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin: 4px 0 0; }
.tt-map__card .tt-map__load {
	display: inline-block;
	inline-size: auto;
	padding: 12px 24px;
	border: 1.5px solid var(--wp--preset--color--brand-blue);
	border-radius: 999px;
	background: #fff;
	color: var(--wp--preset--color--brand-blue);
	font-weight: 700;
	cursor: pointer;
}
.tt-map__links[hidden] { display: none; }

/* ------------------------------------------------------------------
 * The LED backdrop's configurator (29 Sep 2026, assets/js/configurator-hw.js):
 * width-only pills in one row per height, a checkbox for the light, one
 * price with the choice beside it.
 * ------------------------------------------------------------------ */
.tt-cfg--hw #tt-cfg-size .tt-cfg__pillfrom { display: none; }
/* the tent size pill's measurements (owner, 29 Sep 2026); a row that does not fit wraps */
.tt-cfg--hw #tt-cfg-size .tt-cfg__pills { gap: 8px; }
.tt-cfg--hw #tt-cfg-size .tt-cfg__pill { flex: none; box-sizing: border-box; block-size: 40px; padding: 0 14px; border-width: 1.5px; border-radius: 999px; align-content: center; justify-content: center; white-space: nowrap; }
.tt-cfg--hw #tt-cfg-size .tt-cfg__pilltext { font-size: 15px; font-weight: 600; line-height: 1; }
.tt-cfg--hw .tt-cfg__pillgroup { flex: 0 0 100%; margin-block: 4px 4px; font-size: 13px; color: var(--wp--preset--color--muted); }
.tt-cfg--hw .tt-cfg__pillgroup:first-child { margin-block-start: 0; }
.tt-cfg--hw .tt-cfg__pillbreak { flex: 0 0 100%; block-size: 0; }
.tt-cfg__nolight { display: flex; align-items: flex-start; gap: 10px; cursor: pointer; line-height: 1.5; }
.tt-cfg__nolight input { inline-size: 20px; block-size: 20px; margin: 2px 0 0; flex: none; accent-color: var(--wp--preset--color--brand-blue); }
.tt-cfg__nolight small { color: var(--wp--preset--color--muted); font-size: var(--wp--preset--font-size--small); }
.tt-cfg__choice { display: inline-block; margin-inline-start: 10px; padding: 3px 10px; border-radius: 999px; background: var(--wp--preset--color--surface-2, #eef1f7); color: var(--wp--preset--color--brand-blue); font-size: var(--wp--preset--font-size--small); font-weight: 700; }
.tt-cfg__choice[hidden] { display: none; }
.tt-cfg-headline { margin: 0; font-size: var(--wp--preset--font-size--large); font-weight: 700; color: var(--wp--preset--color--brand-blue); }

/* ------------------------------------------------------------------
 * Indoor / Outdoor / About: a short hero (owner, 29 Sep 2026).
 * 440px at a desktop, 360px on a phone, the glass card centred in it.
 * `tt-hero--compact` is written by inc/hero-slot.php on those three pages
 * only; the application pages keep the ratio box and the phone split.
 * With the slot empty the box is a flat navy band (`tt-hero--empty`);
 * a file uploaded later fills the same box at its focal point, so the
 * height does not move. A floor, not a ceiling: a card taller than the
 * box grows it rather than being cut (26 Sep 2026).
 * ------------------------------------------------------------------ */
.wp-block-cover.tt-hero.tt-hero.tt-hero--compact {
	aspect-ratio: auto !important;
	min-block-size: 440px !important;
	block-size: auto;
	padding-block: 24px !important;
	align-items: center;
	background-color: #00326C;
}
.wp-block-cover.tt-hero.tt-hero--compact .wp-block-cover__image-background {
	object-fit: cover;
	object-position: var(--tt-hero-pos, var(--tt-hero-focus, 50% 50%));
}
@media (max-width: 781px) {
	.wp-block-cover.tt-hero.tt-hero.tt-hero--compact {
		min-block-size: 360px !important;
		padding-block: 20px !important;
	}
}

/*
 * INDOOR / OUTDOOR: TEXT BESIDE THE WHOLE PHOTOGRAPH (owner, 1 Oct 2026).
 * `tt-hero--split` is added by inc/hero-slot.php on those two pages only; Contact
 * and About keep the compact hero above. A navy text card and the photograph,
 * never cropped: the frame takes the file's own ratio (--tt-hero-w/-h), clamped
 * to 440-640px on a desktop, with a blurred copy of the picture behind what
 * `contain` leaves - as in the product gallery. Five classes: the compact rules
 * carry four (gotcha 19); a grid, because core/cover is a flex row (gotcha 26).
 */
.wp-block-cover.tt-hero.tt-hero.tt-hero--compact.tt-hero--split {
	container-type: inline-size;
	display: grid;
	grid-template-columns: 1fr;
	align-items: center;
	gap: 24px;
	min-block-size: 0 !important;
	padding: 24px max(var(--wp--preset--spacing--50), calc((100% - 1200px) / 2)) !important;
	background: transparent;
	border-radius: 0;
	overflow: visible;
}
.tt-hero--split > .wp-block-cover__background { display: none; }
.tt-hero--split > .tt-hero__media {
	position: relative;
	isolation: isolate;
	overflow: hidden;
	inline-size: 100%;
	border-radius: 16px;
	/* stacked: the photograph at its own shape, whole */
	aspect-ratio: var(--tt-hero-w, 16) / var(--tt-hero-h, 9);
	background: var(--wp--preset--color--surface-2);
}
.tt-hero--split > .tt-hero__media::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	background: var(--tt-fill) center / cover no-repeat;
	transform: scale(1.6);
	filter: blur(22px);
}
.wp-block-cover.tt-hero.tt-hero.tt-hero--compact.tt-hero--split > .tt-hero__media > .wp-block-cover__image-background {
	position: relative;
	inset: auto;
	z-index: 1;
	display: block;
	inline-size: 100%;
	block-size: 100%;
	object-fit: contain;
	object-position: 50% 50%;
}
.tt-hero--split > .wp-block-cover__inner-container {
	position: relative;
	z-index: 1;
	inline-size: 100%;
	max-inline-size: none;
	margin: 0;
	padding: 0;
}
/* the quote button's navy fill vanishes on the navy card: an outline keeps it a button */
.tt-hero--split .wp-block-button:not(.is-style-line) > .wp-block-button__link {
	background: transparent;
	border: 2px solid currentColor;
	color: #fff;
}
.tt-hero--split > .wp-block-cover__inner-container > .wp-block-group.is-style-glass {
	margin: 0;
	max-inline-size: none;
	background: var(--wp--preset--color--brand-blue);
	-webkit-backdrop-filter: none;
	backdrop-filter: none;
	border: 0;
	border-radius: 16px;
	box-shadow: none;
}
@media (min-width: 1025px) {
	/*
	 * Two columns only from 1025. At 768 the 45/55 split put the Outdoor H1 on
	 * three lines and shrank the photograph to a 383x440 portrait frame, so the
	 * owner's rule ("two columns if the H1 still fits on 2 lines, otherwise
	 * stack") stacks it. The columns line up with the 1200px content.
	 */
	.wp-block-cover.tt-hero.tt-hero.tt-hero--compact.tt-hero--split {
		grid-template-columns: 40fr 60fr;
		gap: 32px;
		padding: 40px max(var(--wp--preset--spacing--50), calc((100% - 1200px) / 2)) !important;
	}
	.tt-hero--split > .wp-block-cover__inner-container { grid-column: 1; grid-row: 1; }
	/* the card's own padding (spacing-70, written inline by the pattern) made it taller than the photograph */
	.tt-hero--split > .wp-block-cover__inner-container > .wp-block-group.is-style-glass { padding: 32px !important; }
	.tt-hero--split > .tt-hero__media {
		grid-column: 2;
		grid-row: 1;
		aspect-ratio: auto;
		/* the column's width at the file's own ratio, held to 440-640px */
		block-size: clamp(440px, calc((100cqw - 32px) * .6 * var(--tt-hero-h, 9) / var(--tt-hero-w, 16)), 640px);
	}
}


/* SEO step 2: the category / landing line under a product lead, and the
   product links in a landing hero (white text on the glass panel) */
.tt-pd-context { margin-block: 0; color: var(--wp--preset--color--muted); }
.tt-pd-context a { color: var(--wp--preset--color--brand-blue); text-decoration: underline; text-underline-offset: 2px; }
.tt-hero .tt-hero__links a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* About: one wide team/office photograph (patterns/team-wide.php, 1 Oct 2026) */
.tt-team-wide__media { margin: 0; border-radius: var(--tt-radius-md, 16px); overflow: hidden; }
.tt-team-wide__img { display: block; inline-size: 100%; block-size: auto; }

/*
 * A short hero with no photograph yet (`tt-hero--empty`, About and Contact until
 * their slots are filled, owner 1 Oct 2026): a title band, not a glass card
 * floating in front of nothing. The card loses its frame, fill and blur; the
 * words sit on the navy itself. Filling the slot brings the card back.
 */
.wp-block-cover.tt-hero.tt-hero.tt-hero--compact.tt-hero--empty .wp-block-group.is-style-glass {
	background: transparent;
	border: 0;
	box-shadow: none;
	-webkit-backdrop-filter: none;
	backdrop-filter: none;
	padding-inline: 0 !important;
}
