/* Powershift content blocks — design pass 1, translated from the Website Mockups canvas
   (design-reference/mockups/*.dc.html). Every colour/font/space references a theme.json
   preset; uppercase comes from CSS, never content. Corner language: single 22px chamfer
   on cards/panels, 14px on feature tiles, square everything else. */

/* ── shared card (mockup: white, shadow, 22px chamfer, no border) ── */
.pwr-b-card {
	background: var(--wp--preset--color--base);
	/* The card sets its own ink: the page's default text is light now (dark site, 1 Oct),
	   and inherited onto a white card it vanishes — measured on the series highlights. */
	color: var(--wp--preset--color--contrast);
	box-shadow: var(--wp--preset--shadow--m);
	overflow: hidden;
	display: flex;
	flex-direction: column;
	clip-path: polygon(0 0, 100% 0, 100% calc(100% - 22px), calc(100% - 22px) 100%, 0 100%);
	list-style: none;
	transition: transform var(--wp--custom--motion--base) var(--wp--custom--motion--ease-standard);
}

/* ── THE card hover, and the only one ──
   Every card on the site dips 2px and does nothing else (Wayne, 14 Oct): no title recolour,
   no shadow swap. The lift IS the affordance, and it has to read identically on an
   applications tile, a case study tile, a series card, a product card and an archive post
   card. A new card built on .pwr-b-card inherits it; a card built on anything else must
   repeat these two lines and nothing more. */
.pwr-b-card:hover { transform: translateY(-2px); }
@media (prefers-reduced-motion: reduce) { .pwr-b-card:hover { transform: none; } }
.pwr-b-card__body { padding: var(--wp--preset--spacing--m) var(--wp--preset--spacing--m) var(--wp--preset--spacing--l); display: flex; flex-direction: column; gap: var(--wp--preset--spacing--s); flex: 1; }
.pwr-b-card__media { display: block; text-decoration: none; }
.pwr-b-card__img { height: 240px; object-fit: cover; width: 100%; display: block; }
.pwr-b-card__img--placeholder { background: repeating-linear-gradient(135deg, var(--wp--preset--color--grey-bright) 0 2px, transparent 2px 14px); display: flex; align-items: center; justify-content: center; color: var(--wp--preset--color--grey-light); font-size: var(--wp--preset--font-size--xs); letter-spacing: var(--wp--custom--tracking--eyebrow); text-transform: uppercase; }

/* ── case study tile (handoff §2, re-cut white 1 Oct meeting) ──
   WHITE, like every card on the site (the meeting's universal card rule). The ground is
   Deep Grey everywhere now, so the shadow defaults to the dark-ground weight — `m` is
   Deep Grey at 10%, invisible against the Deep Grey page. The shadow rides the <li>
   wrapper because clip-path clips box-shadow on its own element. */
.pwr-b-card-shadow {
	display: flex;
	list-style: none;
	filter: drop-shadow(var(--wp--preset--shadow--m-dark));
}

/* What a card gets by sitting in the wrapper: the wrapper is the grid item and owns the
   shadow, the card fills it, and the card is the positioning context for the title link
   that stretches over the whole tile. Declared here so a card needs no variant of its own
   to join in — the applications tile is the one that did (Wayne, 14 Oct). */
.pwr-b-card-shadow > .pwr-b-card { flex: 1; position: relative; }
.pwr-b-card-shadow > .pwr-b-card .pwr-b-title a::after { content: ""; position: absolute; inset: 0; }

.pwr-b-card--cs {
	flex: 1;
	position: relative;
	background: var(--wp--preset--color--base);
	box-shadow: none;
}
.pwr-b-card--cs:focus-within { box-shadow: var(--wp--preset--shadow--focus); }

.pwr-b-card--cs .pwr-b-card__media { position: relative; aspect-ratio: 16 / 10; overflow: hidden; }
/* absolutely positioned so the image is out of flow: an aspect-ratio box that is also a
   grid/flex item gets stretched to the row height, which is what made these boxes 768px. */
.pwr-b-card--cs .pwr-b-card__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; }
.pwr-b-card--cs .pwr-b-card__img--placeholder { position: absolute; inset: 0; }
/* The extra photos of a cycling card (product and series pages, assets/cardcycle/): stacked over
   the featured image, hidden until the script fades one in. No script, only the featured shows. */
.pwr-b-card--cs .pwr-b-card__img--frame { opacity: 0; transition: opacity 1.2s ease; }
.pwr-b-card--cs .pwr-b-card__img--frame.is-on { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .pwr-b-card--cs .pwr-b-card__img--frame { display: none; } }
.pwr-b-card--cs .pwr-b-card__body {
	gap: var(--wp--preset--spacing--xs);
	padding: var(--wp--preset--spacing--s) var(--wp--preset--spacing--s) var(--wp--preset--spacing--m);
}
.pwr-b-card--cs .pwr-b-eyebrow { color: var(--wp--preset--color--orange); margin: 0; }
.pwr-b-card--cs .pwr-b-title {
	margin: 0;
	/* `xl`, the applications tile's size — that card is the rule (Wayne, 15 Oct). It sat a
	   step down at `m` for four-across tiles of ~330px; the grid is three across now and the
	   tile is ~432px, so the old reason is gone twice over. */
	font-size: var(--wp--preset--font-size--xl);
	font-weight: 800;
	line-height: 1.3;
	text-transform: uppercase; /* CSS does the caps — stored content stays sentence case */
	color: var(--wp--preset--color--contrast);
	display: -webkit-box;
	-webkit-line-clamp: 3; /* the 96-char worst case is exactly 3 lines here */
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.pwr-b-card--cs .pwr-b-title a { color: inherit; text-decoration: none; }
/* the title anchor is the card's single tab stop; this gives it the whole card's hit area */
.pwr-b-card--cs .pwr-b-title a::after { content: ""; position: absolute; inset: 0; }
/* `s`, like every other card's running text. It was `xs` — the LABEL size the eyebrows and
   the meta use — which made this the only body copy on the site set at 11px (Wayne, 14 Oct). */
.pwr-b-card--cs .pwr-b-textlink {
	margin-top: auto; /* pins to the foot — this is what keeps a row of tiles even */
	padding-top: var(--wp--preset--spacing--s);
	color: var(--wp--preset--color--orange);
	font-weight: 800;
	font-size: var(--wp--preset--font-size--s);
	letter-spacing: var(--wp--custom--tracking--normal);
	text-transform: uppercase;
}

/* product cards: renders sit on an ink panel (mockup products band) */
.pwr-b-products .pwr-b-card__media { background: var(--wp--preset--color--contrast); height: 220px; display: flex; align-items: center; justify-content: center; padding: var(--wp--preset--spacing--s); }
.pwr-b-products .pwr-b-card__img { aspect-ratio: auto; max-height: 100%; width: auto; max-width: 100%; object-fit: contain; }
.pwr-b-products .pwr-b-card__img--placeholder { width: 100%; height: 100%; }

/* ── grids ──
   Collapse is keyed off the GRID's own inline size, not the viewport (handoff §2.3/§6):
   a tile grid can sit in a Columns column, where a media query reports the wrong width. */
/* The container is the grid's PARENT, never the grid itself — an element cannot match a
   @container query against its own size, which silently left every grid at its widest
   column count. */
.pwr-b-applications, .pwr-b-clients, .pwr-b-case-studies { container-type: inline-size; }
/* the products band is full-bleed, so its RAIL is the container the grid is measured against */
.pwr-b-products__rail { container-type: inline-size; }
.pwr-b-grid { display: grid; gap: var(--wp--preset--spacing--m); list-style: none; padding: 0; margin: 0; }
.pwr-b-grid--2 { grid-template-columns: repeat(2, 1fr); }
.pwr-b-grid--3 { grid-template-columns: repeat(3, 1fr); }
.pwr-b-grid--logos { grid-template-columns: repeat(6, 1fr); gap: var(--wp--preset--spacing--s); }
.pwr-b-grid--4 { grid-template-columns: repeat(4, 1fr); }
/* Case study tiles run FOUR across at wide width and never fall below two — a single-column
   tile on a phone wastes the whole row (Wayne). Stepped off the grid's own inline size. */
@container (max-width: 1180px) { .pwr-b-grid--4 { grid-template-columns: repeat(3, 1fr); } }
@container (max-width: 880px)  { .pwr-b-grid--4 { grid-template-columns: repeat(2, 1fr); } .pwr-b-grid--3 { grid-template-columns: repeat(2, 1fr); } }
/* --4 was missing here: four case study cards stayed two-up on a phone and ran 17px off the screen. */
@container (max-width: 560px)  { .pwr-b-grid--2, .pwr-b-grid--3, .pwr-b-grid--4 { grid-template-columns: 1fr; } }

/* Fewer cards than columns: a grid leaves the empty tracks on the right, so a short set
   switches to flex and centres (Wayne, 2 Oct). The basis mirrors the --4 grid's track width
   at each container step, so a short row's cards are the same size as a full row's. */
.pwr-b-grid--short {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
}
/* The basis tracks --3, the only grid that uses --short, so a short row's cards are exactly
   the size a full row's would be. It followed --4 until 15 Oct, when the case studies grid
   went to three: left alone, two tiles would have sat at the old four-across width and come
   out narrower than the three-across rows above them. */
.pwr-b-grid--short > li { flex: 0 1 calc((100% - 2 * var(--wp--preset--spacing--m)) / 3); }
@container (max-width: 880px)  { .pwr-b-grid--short > li { flex-basis: calc((100% - var(--wp--preset--spacing--m)) / 2); } }
@container (max-width: 560px)  { .pwr-b-grid--short > li { flex-basis: 100%; } }
@media (max-width: 1024px) { .pwr-b-grid--logos { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 640px)  { .pwr-b-grid--logos { grid-template-columns: repeat(3, 1fr); } }

/* ── typography helpers ── */
.pwr-b-eyebrow { font-family: Syncopate, "Red Hat Display", sans-serif; font-weight: 700; font-size: var(--wp--preset--font-size--xs); text-transform: uppercase; letter-spacing: var(--wp--custom--tracking--eyebrow); color: var(--wp--preset--color--orange); margin: 0; }
.pwr-b-title { font-weight: 900; text-transform: uppercase; line-height: 1.15; margin: 0; }
.pwr-b-title a { color: var(--wp--preset--color--contrast); text-decoration: none; }
/* no :hover colour — the card's dip is the whole hover (see .pwr-b-card:hover) */
.pwr-b-muted { color: var(--wp--preset--color--grey); font-size: var(--wp--preset--font-size--s); font-weight: 300; margin: 0; }
.pwr-b-textlink { font-weight: 800; font-size: var(--wp--preset--font-size--s); text-transform: uppercase; letter-spacing: var(--wp--custom--tracking--normal); color: var(--wp--preset--color--orange); text-decoration: none; margin-top: auto; }
/* The arrow is the theme's assets/css/links.css: one icon, → to go somewhere, ↓ to download. */
.pwr-b-textlink:hover { color: var(--wp--preset--color--orange-dark); }
/* A card is ONE target, so its forward link answers the card's hover and not only its own
   (Wayne, 14 Oct) — on the case study and series cards that link is a <span>, which has no
   hover of its own at all. The arrow that travels with it is in the theme's links.css.
   --download is excluded on purpose: a datasheet is a different destination from the card's,
   so it keeps its own hover and stays put while the card is hovered. */
.pwr-b-card:hover .pwr-b-textlink:not(.pwr-b-textlink--download),
.pwr-b-prodcard:hover .pwr-b-textlink:not(.pwr-b-textlink--download) {
	color: var(--wp--preset--color--orange-dark);
}

/* ── best-for chips (mockup: squared, 2px ink border, uppercase) ── */
.pwr-b-pills { display: flex; flex-wrap: wrap; gap: var(--wp--preset--spacing--s); margin: 0; padding: 0; list-style: none; }
.pwr-b-pill { font-size: var(--wp--preset--font-size--s); font-weight: 700; text-transform: uppercase; letter-spacing: var(--wp--custom--tracking--normal); border: 2px solid var(--wp--preset--color--contrast); color: var(--wp--preset--color--contrast); padding: var(--wp--preset--spacing--xs) var(--wp--preset--spacing--s); text-decoration: none; }
.pwr-b-pill--accent { border-color: var(--wp--preset--color--contrast); color: var(--wp--preset--color--contrast); }
/* On an ink band. Not a filled chip: over a photograph a solid fill would read as a button,
   and these are labels. The border carries the shape and the type carries the contrast. */
.pwr-b-pill--light { border-color: color-mix(in srgb, var(--wp--preset--color--base) 55%, transparent); color: var(--wp--preset--color--base); }

/* ── product hero (approved mock 3a/3b): centred type, transparent render
   overlapping it, and the spec card straddling the panel edge below ── */
/* ── product spec table (Claude Design handoff table.md, option 3a/3b) ──
   Replaces the old 4+4 stat-tile grids. One chamfered white card; label/value rows
   in two columns wide, one column narrow. Collapse keys off a CONTAINER query, not
   the viewport, because the block can sit in the 840 rail or a Columns column. */
.pwr-b-specs-shadow {
	/* clip-path clips box-shadow on the same element, so the shadow rides this wrapper */
	filter: drop-shadow(var(--wp--preset--shadow--l));
}
.pwr-b-specs {
	background: var(--wp--preset--color--base);
	clip-path: polygon(0 0, 100% 0, 100% calc(100% - 22px), calc(100% - 22px) 100%, 0 100%);
	padding: var(--wp--preset--spacing--l) var(--wp--preset--spacing--l) var(--wp--preset--spacing--xl);
	container-type: inline-size;
}

.pwr-b-specs__head {
	display: flex;
	flex-wrap: wrap; /* narrow cards: the nowrap datasheet link drops below the title
	                    instead of overflowing the card (clipped at 430px otherwise) */
	justify-content: space-between;
	align-items: baseline;
	gap: var(--wp--preset--spacing--s) var(--wp--preset--spacing--m);
	border-bottom: 4px solid var(--wp--preset--color--orange);
	padding-bottom: var(--wp--preset--spacing--s);
}
/* Two class levels: theme.json's `:root :where(h2)` would otherwise tie on specificity
   and win on order, restoring the 48px heading. */
.pwr-b-specs .pwr-b-specs__title {
	margin: 0;
	font-family: Syncopate, "Red Hat Display", sans-serif;
	font-weight: 700;
	font-size: var(--wp--preset--font-size--xs);
	letter-spacing: var(--wp--custom--tracking--eyebrow);
	text-transform: uppercase;
	line-height: 1.4;
	color: var(--wp--preset--color--contrast);
}
.pwr-b-specs__datasheet {
	font-weight: 800;
	font-size: var(--wp--preset--font-size--s);
	letter-spacing: var(--wp--custom--tracking--normal);
	text-transform: uppercase;
	white-space: nowrap;
	text-decoration: none;
	color: var(--wp--preset--color--orange);
}
.pwr-b-specs__datasheet:hover { color: var(--wp--preset--color--orange-dark); }

.pwr-b-specs__list {
	margin: 0;
	display: grid;
	grid-template-columns: 1fr 1fr;
	column-gap: var(--wp--preset--spacing--xl);
}
.pwr-b-specs__row {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: var(--wp--preset--spacing--m);
	padding: var(--wp--preset--spacing--s) 0;
	border-bottom: 1px solid var(--wp--preset--color--grey-bright);
}
.pwr-b-specs__label {
	font-weight: 400;
	font-size: var(--wp--preset--font-size--s);
	color: var(--wp--preset--color--grey);
}
.pwr-b-specs__value {
	margin: 0;
	font-weight: 700;
	font-size: var(--wp--preset--font-size--s);
	color: var(--wp--preset--color--contrast);
	text-align: right;
	white-space: nowrap; /* values never split mid-unit */
}
.pwr-b-specs__value--long { /* dimensions-class outliers only */
	white-space: normal;
	overflow-wrap: anywhere;
	max-width: 60%;
}

@container (max-width: 720px) {
	.pwr-b-specs__list { grid-template-columns: 1fr; }
}
/* Fallback where container queries are unavailable (harmless double-apply otherwise) */
@media (max-width: 720px) {
	.pwr-b-specs__list { grid-template-columns: 1fr; }
}

/* "Hanging card" block style: pulls the card up over a dark hero panel (mock 3a). */

@media print {
	.pwr-b-specs-shadow { filter: none; }
	.pwr-b-specs { clip-path: none; border: 1px solid var(--wp--preset--color--grey-bright); }
}

/* ── logo tiles ──
   No tile plate since 2 Oct (Wayne): the marks are the full-colour dark-ground set and the
   whole site ground is Deep Grey now, so they sit straight on the page. The box still does
   the layout job — square, centred, area-sized img. */
.pwr-b-logo { aspect-ratio: 1; display: flex; align-items: center; justify-content: center; padding: var(--wp--preset--spacing--s); margin: 0; position: relative; }
/* The img's width is set inline by ps_render_client_logo_tile() from the mark's own ratio, so
   every logo covers the same share of its tile — see that function. The logo files are white
   marks cropped to their edges (29 Sep); the caps below only catch an uncropped upload. */
.pwr-b-logo img { height: auto; max-width: 100%; max-height: 100%; object-fit: contain; opacity: .9; transition: opacity 200ms; }
.pwr-b-logo:hover img { opacity: 1; }
.pwr-b-logo figcaption { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

/* ── clients: the scrolling row (display = row) ──
   The gallery marquee's loop (assets/gallery/gallery.css header): two identical halves, the
   animation lands on -50%, so the gutter is a MARGIN on each tile — a flex `gap` would put the
   seam half a gutter out and the row would jump once a cycle. Pace is per logo, so the row
   moves at the same speed however many clients are ticked. The edges fade out rather than
   cut, so a logo arrives and leaves instead of being sliced by the band. */
.pwr-b-clients--row {
	--pwr-logo-tile: clamp(128px, 13vw, 196px); /* was clamp(112px, 11vw, 168px) — bigger logos, 28 Oct */
	--pwr-logo-gutter: var(--wp--preset--spacing--s);
	--pwr-logo-pace: 2.6s;
	position: relative;
}
.pwr-b-clients__viewport {
	overflow: hidden;
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
	mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.pwr-b-clients__track {
	display: flex;
	width: max-content;
	margin: 0;
	padding: 0;
	list-style: none;
	will-change: transform;
	animation: pwr-logo-row calc(var(--pwr-logo-pace) * var(--pwr-logo-count, 12)) linear infinite;
}
.pwr-b-clients__track > li {
	flex: 0 0 auto;
	width: var(--pwr-logo-tile);
	margin-inline-end: var(--pwr-logo-gutter);
}
@keyframes pwr-logo-row {
	from { transform: translate3d(0, 0, 0); }
	to   { transform: translate3d(-50%, 0, 0); }
}
.pwr-b-clients--row:hover .pwr-b-clients__track,
.pwr-b-clients--row:focus-within .pwr-b-clients__track,
.pwr-b-clients--row.is-paused .pwr-b-clients__track {
	animation-play-state: paused;
}

/* Pause / play: the design system's bare IconButton — the glyph alone, grey, orange on hover.
   Two bars while it moves, a triangle once paused. 40px target, right under the row's end. */
.pwr-b-clients__toggle {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	margin: var(--wp--preset--spacing--xs) 0 0 auto;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
	color: var(--wp--preset--color--grey);
}
.pwr-b-clients__toggle[hidden] { display: none; }
.pwr-b-clients__toggle::before {
	content: "";
	width: 12px;
	height: 14px;
	/* two bars */
	background: linear-gradient(90deg, currentColor 35%, transparent 35% 65%, currentColor 65%);
}
.pwr-b-clients--row.is-paused .pwr-b-clients__toggle::before {
	background: currentColor;
	clip-path: polygon(0 0, 100% 50%, 0 100%);
}
.pwr-b-clients__toggle:is(:hover, :focus-visible) { color: var(--wp--preset--color--orange-dark); }
.pwr-b-clients__toggle:focus-visible { outline: 2px solid var(--wp--preset--color--orange); outline-offset: 2px; }

/* Motion declined: nothing moves, so the row becomes a strip you swipe by hand, the repeat
   half goes (it would show every logo twice), and so does the button, with nothing to pause. */
@media (prefers-reduced-motion: reduce) {
	.pwr-b-clients__viewport { overflow-x: auto; -webkit-mask-image: none; mask-image: none; scroll-snap-type: x mandatory; }
	.pwr-b-clients__track { animation: none; }
	.pwr-b-clients__track > [aria-hidden="true"] { display: none; }
	.pwr-b-clients__track > li { scroll-snap-align: start; }
	.pwr-b-clients__toggle { display: none; }
}

/* ── definition rows ── */
.pwr-b-rows { margin: 0; padding: 0; }
.pwr-b-rows > div { display: flex; justify-content: space-between; gap: var(--wp--preset--spacing--m); padding: var(--wp--preset--spacing--s) 0; border-bottom: 1px solid var(--wp--preset--color--grey-bright); font-size: var(--wp--preset--font-size--s); }
.pwr-b-rows dt { font-weight: 700; color: var(--wp--preset--color--contrast); text-transform: uppercase; letter-spacing: var(--wp--custom--tracking--normal); }
.pwr-b-rows dd { margin: 0; color: var(--wp--preset--color--grey); text-align: right; }

/* ── key features: each one a Stat card (Wayne, 29 Sep) ──
   The same look as the theme's "Stat card" group style (functions.php, `pwr-stat`): white,
   grey-bright hairline, 4px orange-dark top rule, square, 32px padding. The features are drawn
   by this block, not built from groups, so the style cannot be picked on them. Change one, change
   both. Square rather than the old 14px chamfer: on this site the chamfer means clickable, and
   a feature is not a link.

   The case study hero wore this for a day as green stat cards; on 15 Oct Client / Application
   / Location became a data table instead, so this is back to one user. */
.pwr-b-features { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--wp--preset--spacing--m); }
.pwr-b-features li {
	background: var(--wp--preset--color--base);
	border: 1px solid var(--wp--preset--color--grey-bright);
	/* Core orange, not orange-dark (jobs sheet, 30 Oct: "the orange accent line at the top of
	   the card is the incorrect colour"): orange-dark is the HOVER step only. */
	border-top: 4px solid var(--wp--preset--color--orange);
	border-radius: 0;
	padding: 32px;
	display: flex; flex-direction: column; gap: var(--wp--preset--spacing--s);
}
.pwr-b-features strong { text-transform: uppercase; letter-spacing: var(--wp--custom--tracking--tight); font-weight: 800; font-size: var(--wp--preset--font-size--m); color: var(--wp--preset--color--contrast); }
.pwr-b-features span { color: var(--wp--preset--color--grey); font-size: var(--wp--preset--font-size--s); font-weight: 300; line-height: 1.6; }
@media (min-width: 861px) { .pwr-b-features--2col { grid-template-columns: repeat(3, 1fr); } }
/* The optional animated glyph, above the lead. Sizing and motion belong to the glyphs
   plugin's own stylesheet — all this does is set the accent colour the glyph strokes in
   (it is drawn in currentColor) and pull the lead up under it, since the li is a flex
   column with a gap sized for the title/body pair rather than for an icon.

   The frame is named too, not just the block. `.pwr-b-features span` below colours the
   feature's body copy grey, and the glyph plugin's frame is a <span> — so the accent set
   on the block was overwritten one level down and the glyph inherited #495c73 through
   currentColor. The wrapper is a <div> for the same reason; the frame is the plugin's
   markup and not ours to change. */
.pwr-b-features .pwr-glyph-block,
.pwr-b-features .pwr-glyph-block .pwr-glyph-frame {
	color: var(--pwr-accent, var(--wp--preset--color--orange));
}
.pwr-b-features .pwr-glyph-block {
	margin-block-end: calc(var(--wp--preset--spacing--s) * -0.5);
}

/* ── case study story: Challenge / Solution / Highlights (back on fields, Wayne 12 Oct) ──
   Challenge and Highlights are the Key Features card above, laid in a carousel (carousel.css).
   The section sets --pwr-accent (orange for the challenge, green-light for the highlights)
   and that one value colours both the card's top bar and the glyph, which already reads it.

   Rhythm and rule are the products index's (Wayne, 12 Oct): each section carries xl above and
   below, and consecutive sections are split by the same hairline as .pwr-b-srow--light rows. */
.pwr-b-csstory,
.pwr-b-cssolution { margin-block: 0; padding-block: var(--wp--preset--spacing--xl); }
/* NO RULES between the story's sections (Wayne, 14 Oct). They used to be full-width hairlines,
   a border-image outset 100vw each side, above every section and below the last of the run.
   The sections carry xl top and bottom, which is separation enough; the rule on top of that was
   reading as a divider between arguments that are one argument. If they ever come back they
   want the border-image outset again, not a 100vw pseudo-element, which adds a scrollbar. */
.pwr-b-csstory__title { margin: 0 0 var(--wp--preset--spacing--s); }
.pwr-b-csstory__intro { max-width: 62ch; margin-block-end: var(--wp--preset--spacing--l); font-size: var(--wp--preset--font-size--m); }
.pwr-b-csstory__intro > :first-child { margin-block-start: 0; }
.pwr-b-csstory__intro > :last-child { margin-block-end: 0; }
.pwr-b-csstory .pwr-b-features li { border-top-color: var(--pwr-accent); }

/* Story prose (Wayne, 13 Oct — experiment): one size up from body, and the editor's three marks
   each get an effect in the section accent. Bold = accent bold (a pill until 29 Oct), italic = a slow glow pulse, list = accent squares, an all-bold paragraph = pull-line. */
.pwr-b-csstory__prose { max-width: 62ch; font-size: var(--wp--preset--font-size--xl); line-height: 1.6; }
/* Todd (13 Oct): bigger, airier challenge and solution — more room around each section too. */
:is(.pwr-b-csstory--challenge, .pwr-b-cssolution) { padding-block: var(--wp--preset--spacing--xl); }
.pwr-b-csstory__pull {
	font-size: var(--wp--preset--font-size--xl);
	font-weight: 600;
	line-height: 1.35;
	padding-inline-start: var(--wp--preset--spacing--s);
	border-inline-start: 3px solid var(--pwr-accent);
}
.pwr-b-csstory__prose strong {
	/* Plain bold in the section accent — orange in the challenge, green in the solution (sheet
	   #127, 29 Oct: "remove the bolded rectangles"). Was a filled square pill with a sheen
	   sweeping across it, 13–29 Oct. */
	font-weight: 700;
	color: var(--pwr-accent);
}
.pwr-b-csstory__prose em {
	color: var(--pwr-accent);
	animation: pwr-story-pulse 2.6s ease-in-out infinite;
	animation-delay: var(--pwr-mark-d, 0ms);
}
@keyframes pwr-story-pulse {
	0%, 100% { text-shadow: 0 0 0 transparent; }
	50%      { text-shadow: 0 0 0.55em color-mix(in srgb, var(--pwr-accent) 85%, transparent); }
}
/* ── the marks arrive one after another (Wayne, 14 Oct) ──
   A mark is not a block, so it does not drop in: its COLOUR arrives as the section comes
   into view, mark after mark in reading order. Until then bold is just bold and italic is
   just italic — only colour changes, so nothing reflows.

   `--pwr-mark-d` is stamped on each mark by the theme's assets/js/motion.js and does double
   duty: it is the transition delay the colour lands on, and the animation delay for the pulse
   that follows — so the loops run offset instead of in unison, which they did when
   every mark started at page load.

   Keyed on `.pwr-marks`, which motion.js puts ONLY on the prose blocks it actually tagged —
   not on `.pwr-motion-js`, which is on <html> and would therefore have undressed a prose
   block that starts on screen, where the script deliberately does not tag and so would never
   have revealed it again. No script, no hidden state. */
.pwr-b-csstory__prose.pwr-marks:not(.pwr-marks-in) :is(strong, em) {
	color: inherit;
	animation: none;
}

/* The transition belongs to the ARRIVAL and only to it. Declared on the base rule it also
   animated the undressing: the script sets its classes after first paint, so the marks were
   painted dressed and then faded out one by one — a staggered DISAPPEARANCE (Wayne, 14 Oct).
   Here it is only in the after-change style when `.pwr-marks-in` lands, so going out is
   instantaneous (and off-screen, so unseen) and only the arrival is ever animated.

   A long, even fade (Wayne, 14 Oct: "a lot slower"). The easing matters as much as the
   duration here: the cubic-bezier(0.22, 1, 0.36, 1) this started on is the blocks' landing
   curve, which spends most of its change in the first third — on a colour fade that reads
   as quick however long the duration says, so it moved to ease-in-out, where the time is
   spent evenly and the slowness is actually felt. The gap between marks is MARK_STEP in the
   theme's assets/js/motion.js; keep the two in proportion, or the marks stop reading as a
   sequence and become one wash. */
.pwr-b-csstory__prose.pwr-marks.pwr-marks-in :is(strong, em) {
	transition: color 1.3s ease-in-out;
	transition-delay: var(--pwr-mark-d, 0ms);
}

@media (prefers-reduced-motion: reduce) {
	.pwr-b-csstory__prose :is(em, strong) { animation: none; transition: none; }
	/* Belt-and-braces: motion.js exits before stamping anything, so `.pwr-motion-js` is never
	   set here — but if it ever were, the marks must still be dressed. Matched at the hidden
	   state's own weight and printed after it, so these win. */
	.pwr-b-csstory__prose.pwr-marks:not(.pwr-marks-in) strong { background-color: var(--pwr-accent); color: var(--wp--preset--color--contrast); }
	.pwr-b-csstory__prose.pwr-marks:not(.pwr-marks-in) em { color: var(--pwr-accent); }
}
/* Lists in the story are bulleted lists (web catch-up, 20 Oct — they were a cloud of pills
   from 13 Oct): from the toolbar's bullet list, or a paragraph of bolded items and commas,
   which ps_story_prose() turns into one. 23 Oct (web catch-up): ruled rows like every list
   on the site (the theme's lists.css), but the square stays the section's ACCENT — orange in
   the challenge, green in the solution — because that colour is the story's language. */
.pwr-b-csstory__prose ul { list-style: none; padding: 0; }
.pwr-b-csstory__prose ul li {
	position: relative;
	padding: var(--wp--preset--spacing--s) 0 var(--wp--preset--spacing--s) var(--wp--preset--spacing--m);
	border-bottom: 1px solid color-mix(in srgb, currentColor 20%, transparent);
}
.pwr-b-csstory__prose ul li:last-child { border-bottom: 0; }
.pwr-b-csstory__prose ul li::before { content: ""; position: absolute; inset-inline-start: 0; top: calc(var(--wp--preset--spacing--s) + 0.5lh); transform: translateY(-50%); width: 8px; height: 8px; background: var(--pwr-accent); }
.pwr-b-csstory__grid { margin-block-start: var(--wp--preset--spacing--l); }
/* ── "What needed to be powered" (Monthly Checkpoint, 16 Oct) ──
   The challenge prose names the loads in passing; this is the formal place they are written,
   as a ruled list under the prose — the Key Applications rules, an orange square per item.
   It prints only when the field has lines, so a case study written before it stays as it was. */
.pwr-b-csstory__powered { max-width: 62ch; margin-top: var(--wp--preset--spacing--l); }
.pwr-b-csstory__powered-label {
	margin: 0;
	font-family: Syncopate, "Red Hat Display", sans-serif;
	font-weight: 700;
	font-size: var(--wp--preset--font-size--xs);
	letter-spacing: var(--wp--custom--tracking--eyebrow);
	text-transform: uppercase;
	color: var(--pwr-accent, var(--wp--preset--color--orange));
}
.pwr-b-csstory__powered ul { list-style: none; margin: var(--wp--preset--spacing--xs) 0 0; padding: 0; }
.pwr-b-csstory__powered li {
	position: relative;
	padding: var(--wp--preset--spacing--s) 0 var(--wp--preset--spacing--s) var(--wp--preset--spacing--m);
	border-bottom: 1px solid color-mix(in srgb, var(--wp--custom--ink--light) 25%, transparent);
	font-size: var(--wp--preset--font-size--l);
	font-weight: 300;
}
.pwr-b-csstory__powered li::before { content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%); width: 8px; height: 8px; background: var(--pwr-accent, var(--wp--preset--color--orange)); }
/* "The alternative" (22 Oct): the same label over the challenge's own prose, under the loads. */
.pwr-b-csstory__alt { max-width: 62ch; margin-top: var(--wp--preset--spacing--l); }
.pwr-b-csstory__alt > .pwr-b-csstory__powered-label { line-height: inherit; }
.pwr-b-csstory__alt > :not(.pwr-b-csstory__powered-label) { margin-top: var(--wp--preset--spacing--xs); }

/* Highlights follow straight on from the pair with no rule between (Wayne, 13 Oct). */
.pwr-b-csstory--highlights .pwr-b-csstory__title { text-align: center; }

/* ── the highlights run FOUR across, centred (Wayne, 14 Oct) ──
   At three they were 448px wide for a two-line note, which reads as a panel rather than a
   highlight. Flex with a four-across basis rather than four grid tracks: the sets run 3 to 6
   cards, so a grid would leave a hole in the first row at three and strand a single card at
   the LEFT of the second row at five. Every row centres instead — the device
   .pwr-b-grid--short and the hero stat cards already use.

   This is scoped to the highlights. The product pages Key Features grid wears the same
   .pwr-b-features--2col and is deliberately left at three: its cards carry a paragraph, not a
   line. */
.pwr-b-csstory--highlights .pwr-b-features {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
}
.pwr-b-csstory--highlights .pwr-b-features > li {
	/* border-box, because the basis is the TRACK width and the card carries 32px of padding
	   and a 1px border — the trap the hero stat cards hit this morning. */
	box-sizing: border-box;
	flex: 0 1 calc((100% - 3 * var(--wp--preset--spacing--m)) / 4);
	/* Glyph, title and note all on the centre line. The li is a flex COLUMN, so the glyph —
	   a block of its own — needs align-items as well as text-align. */
	text-align: center;
	align-items: center;
}
@media (max-width: 1023px) {
	.pwr-b-csstory--highlights .pwr-b-features > li { flex-basis: calc((100% - var(--wp--preset--spacing--m)) / 2); }
}
@media (max-width: 700px) {
	.pwr-b-csstory--highlights .pwr-b-features > li { flex-basis: 100%; }
}

/* The story walk-through (Todd x Wayne, 13 Oct): challenge beside its site photo, solution
   beside its product cards, the media carousel, the highlights. Text left, aside right, half
   and half. (The rules that used to sit between these sections went on 14 Oct — see above.) */
.pwr-b-csstory.has-aside,
.pwr-b-cssolution.has-aside { display: grid; gap: var(--wp--preset--spacing--xl); align-items: start; }
@media (min-width: 861px) {
	.pwr-b-csstory.has-aside,
	.pwr-b-cssolution.has-aside { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.pwr-b-cssolution .pwr-b-csstory__prose { max-width: 62ch; }

/* Story photo: the galleries' notch and caption badge. The WIDTH is fixed and the height is
   the photo's own (Website To-Do #47, Wayne 23 Oct: "they need to be a fixed width and let
   height be what it is — some pics are deliberately portrait"). It was cropped to 4:3, which
   cut a portrait site photo down to its middle third. */
.pwr-b-storyphoto {
	position: relative; overflow: hidden;
	clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--wp--custom--chamfer)), calc(100% - var(--wp--custom--chamfer)) 100%, 0 100%);
}
/* In flow, not pinned over the frame: the photo's own height is what sizes the frame now. */
.pwr-b-storyphoto .pwr-b-gallery__link { position: relative; display: block; height: auto; }
.pwr-b-storyphoto img { display: block; width: 100%; height: auto; transition: transform 0.4s ease; }

/* Solution cards: one or two across in the aside, room above each for its hanging render. */
.pwr-b-cssolution__cards { row-gap: var(--wp--preset--spacing--xxl); padding-top: var(--wp--preset--spacing--xl); }
.pwr-b-cssolution__cards--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* A phone stacks the pair, at the lone card's width: side by side each track is ~163px at 390,
   and the card's spec rows ("437kVA / 607A") cannot go under ~197 — the second card ran 16px
   off the screen and the whole page scrolled sideways (20 Oct). */
@media (max-width: 599px) {
	.pwr-b-cssolution__cards--2 { grid-template-columns: minmax(0, 1fr); max-width: 360px; }
}
.pwr-b-cssolution__cards--1 { max-width: 360px; }
/* A lone card hugs the right of the aside (Wayne, 14 Oct) — the aside IS the right half, so
   the card belongs at its outer edge, not floating in the middle of the column. Only while
   the two columns exist: stacked on a phone the aside is the full width and a card pushed
   right would just look lost. */
@media (min-width: 861px) {
	.pwr-b-cssolution__cards--1 { margin-inline-start: auto; }
}
/* Green, not orange, beside the solution (web catch-up, 23 Oct): the case study reads orange =
   problem, green = solution, and orange cards here broke that. This section only — the same
   card stays orange on the series and product pages. The eyebrow is the mid green, which
   holds on the white card where the light green washes out; the button is the Green solid
   button's fill and hover. Four classes to beat the default fill in the theme's buttons.css. */
.pwr-b-cssolution .pwr-b-prodcard .pwr-b-eyebrow { color: var(--wp--preset--color--green); }
.pwr-b-cssolution .pwr-b-prodcard .pwr-b-prodcard__cta.wp-element-button {
	--pwr-btn-bg: var(--wp--preset--color--green-light);
	color: var(--wp--preset--color--contrast);
}
.pwr-b-cssolution .pwr-b-prodcard .pwr-b-prodcard__cta.wp-element-button:is(:hover, :focus-visible) {
	--pwr-btn-bg: var(--wp--preset--color--green);
}
/* The text link that replaced the button (2 Nov): mid green here too, and it stays green on
   hover — the palette has no deeper green to step to, and the orange-dark of every other
   card's hover would break the section's green. The arrow still travels. */
.pwr-b-cssolution .pwr-b-prodcard .pwr-b-textlink,
.pwr-b-cssolution .pwr-b-prodcard:hover .pwr-b-textlink,
.pwr-b-cssolution .pwr-b-prodcard .pwr-b-textlink:hover { color: var(--wp--preset--color--green); }

/* Three or more units: the row becomes the site carousel (render.php decides), two across in
   the aside with the rest arrowed in.

   Two things the shared carousel cannot know from here. Its 2-up/3-up steps are VIEWPORT
   media queries, and this track lives in half a rail — at 1024px up it would have drawn three
   container-class renders into a half-width column. And its `overflow-x: auto` makes the other
   axis `auto` too, which would crop the renders hanging above the cards: the clip edge is the
   PADDING box, so the clearance has to be padding on the track, pulled back up by the same
   amount so the section's rhythm is unchanged. */
@media (min-width: 861px) {
	.pwr-carousel .pwr-b-cssolution__cards.pwr-carousel__track {
		grid-auto-columns: calc((100% - var(--wp--preset--spacing--m)) / 2);
	}
}
.pwr-carousel .pwr-b-cssolution__cards.pwr-carousel__track {
	padding-top: var(--wp--preset--spacing--xxl);
	margin-top: calc(var(--wp--preset--spacing--xl) - var(--wp--preset--spacing--xxl));
}

/* Media carousel: the video and photos as equal frames (carousel.css sets the columns).
   Every slide is ONE shape — the photo and the video both — so a row reads as a set rather
   than as a collage (Wayne, 14 Oct: "the images here need to be the same size"). The ratio
   belongs to the slide, not to how many there are: it used to go 16:9 on a single slide and
   4:3 otherwise, which made the same photograph a different shape on two case studies. */
.pwr-b-csmedia { margin-block: 0; padding-block: 0 var(--wp--preset--spacing--xl); }
.pwr-b-csmedia__track { list-style: none; margin: 0; padding: 0; gap: var(--wp--preset--spacing--m); }
.pwr-b-csmedia__slide { margin: 0; }
/* HEIGHT is the constant, not the column width — the product strip's model, and the token is
   its (--pwr-gal-h, declared on .pwr-b-gallery, which this section carries). The slides used
   to take a fraction of the rail and derive their height from it, so two photos on a wide
   screen came out 513px tall against the product gallery's 380 (Wayne, 14 Oct). Now every
   frame is the same height as every other gallery frame on the site, the column is as wide as
   the frame, and as many fit as the rail allows — the rest scroll, which is what the carousel
   was already for. */
/* max-content, not auto: `auto` columns share the free space, so two frames in a wide track
   came out 684px wide with the 499px photo adrift inside. max-content makes each column
   exactly as wide as its frame. */
.pwr-b-csmedia .pwr-carousel .pwr-carousel__track {
	grid-auto-columns: max-content;
	/* SAFE centre: the frames are content-width now, so a short set left a hole on the right
	   of the rail. Plain `center` would centre an overflowing track too, and the overflow
	   past the start edge is unreachable — the first frame could not be scrolled back to.
	   `safe` centres while it fits and falls back to start once it does not. */
	justify-content: safe center;
}
/* The product gallery's model (Wayne, 23 Oct: "like the product gallery — images all the same
   height but respect the aspect ratio"): HEIGHT is the constant, each photo is as wide as its
   own shape makes it, never cropped, so a portrait shot is a narrow frame, not a cut-down one.
   (Earlier the same day, #47, the width was fixed and the heights varied — not the look.) */
/* The height goes on the IMAGE itself, not as 100% of the slide: the track sizes its columns
   to their content (max-content), and a percentage height inside content-sized boxes has
   nothing to resolve against — it came out 17px short and the frame wider than the photo. */
.pwr-b-csmedia .pwr-b-storyphoto { width: max-content; }
.pwr-b-csmedia .pwr-b-storyphoto img { height: var(--pwr-gal-h); width: auto; max-width: none; }
/* The video has no inner wrapper — the slide IS the frame — so its ratio goes on the slide:
   16:9 at the same height as the photos. */
.pwr-b-csmedia .pwr-b-csmedia__slide--video { height: var(--pwr-gal-h); width: auto; aspect-ratio: 16 / 9; }
.pwr-b-csmedia__slide--video { position: relative; background: var(--wp--preset--color--contrast); }
.pwr-b-csmedia__slide--video :is(iframe, video) { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; object-fit: cover; }

/* The case study quote sits between the solution and the highlights (sheet #128, 29 Oct), as
   its own band — printed by acf/case-study-solution after its section. It led the media
   section 17–29 Oct. Two classes because quotes.css carries `.pwr-b-testimonials{margin:0}`
   and the theme stylesheet prints AFTER this plugin's, so one class loses the margin. */
.pwr-b-csquote.pwr-b-cssolution__quote { margin-block: 0; padding-block: var(--wp--preset--spacing--l); }
/* The "Watch the video" jump from the hero lands here; clear the pinned header bar. */
.pwr-b-csmedia__anchor { scroll-margin-top: calc(var(--pwr-bar-h, 94px) + var(--wp--preset--spacing--m)); }

/* ── product description: the Description field, large ──
   Large, because it is the one paragraph that has to carry the product on its own before the
   features and applications start breaking it down — the same step the hero's intro sits at,
   so the two read as one voice rather than two sizes of body copy. It inherits its colour
   from the surface, like Key Applications beside it. */
/* The measure is on the PARAGRAPH, not the container. The container's width is set to the
   1200px content rail by the theme's page-width rules, which print after this file and win —
   the same specificity story buttons.css documents. Nothing else targets this paragraph, so
   the measure holds there. 54ch is the figure the quotes use: around 60 characters a line.
   Without it the description set at 150 characters a line. */
.pwr-b-proddesc {
	color: inherit;
	/* Centred for now (Wayne, 25 Sep) — `margin-inline: auto` on the paragraph as well, or the
	   54ch measure would centre its text inside a box still pinned left. */
	text-align: center;
}
.pwr-b-proddesc p {
	margin: 0;
	/* XL (1.375rem), not L. L is 1.125rem — a notch over body copy and not what anyone means
	   by large. XL is the size the testimonial quotes set at, which is the right company for
	   it: the one paragraph on the page asking to be read rather than scanned. */
	font-size: var(--wp--preset--font-size--xl);
	line-height: var(--wp--custom--leading--xl);
	font-weight: 300;
	max-width: 54ch;
	margin-inline: auto;
}
.pwr-b-proddesc p + p { margin-top: var(--wp--preset--spacing--s); }

/* ── key applications (no card: plain copy on whatever surface it is given) ──
   It was a dark chamfered panel with white text. Wayne, 25 Sep: lose the card on all
   instances and work on white. It now paints nothing and inherits the surface, so the same
   block reads correctly beside Key Features on a white page.

   The bullets go ORANGE, not the light green they were. That is the design system's rule
   rather than a preference: light green is the accent on DARK surfaces, orange leads on
   light ones. Light green squares on white measure 2.3:1 and all but disappear. */
/* ── key applications (product) / application intro (application single) ──
   Two blocks, one treatment: copy left a type step up, the list right as rules with an orange
   square. The application single went fields-only on 16 Oct and its intro is the same shape
   the product page already had, so the rules below are shared rather than copied. */
.pwr-b-key-applications, .pwr-b-app-intro { color: inherit; }
.pwr-b-key-applications p, .pwr-b-app-intro p { color: inherit; font-weight: 300; }
/* Two columns since 2 Oct (meeting: bullets move into a column beside the copy, intro bumps a
   step): render.php splits the one WYSIWYG field at its first <ul>. */
.pwr-b-key-applications__cols,
.pwr-b-app-intro__cols {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	gap: var(--wp--preset--spacing--xl);
	align-items: start;
}
@media (max-width: 781px) {
	.pwr-b-key-applications__cols,
	.pwr-b-app-intro__cols { grid-template-columns: minmax(0, 1fr); gap: var(--wp--preset--spacing--m); }
}
.pwr-b-key-applications__copy p,
.pwr-b-app-intro__copy p { font-size: var(--wp--preset--font-size--l); line-height: var(--wp--custom--leading--l); }
.pwr-b-key-applications ul,
.pwr-b-app-intro ul { list-style: none; margin: var(--wp--preset--spacing--s) 0 0; padding: 0; }
/* The list column's top lines up with the copy column's (Website To-Do, Applications, 28 Oct:
   "alignment on the individual page"). The margin reset sat ABOVE the rule it was resetting at
   the same specificity, so the later margin won and the list started 16px low, and the first
   row's own top padding pushed its words another 16px down. Both come off here, after. */
.pwr-b-key-applications__list > ul,
.pwr-b-app-intro__list > ul { margin-top: 0; }
.pwr-b-key-applications__list > ul > li:first-child,
.pwr-b-app-intro__list > ul > li:first-child { padding-top: 0; }
/* the bullet is centred on the row's box; with no top padding that box is taller below the
   words than above, so move the bullet up by half the padding that went */
.pwr-b-key-applications__list > ul > li:first-child::before,
.pwr-b-app-intro__list > ul > li:first-child::before { top: calc(50% - var(--wp--preset--spacing--s) / 2); }
.pwr-b-key-applications ul li,
.pwr-b-app-intro ul li { position: relative; padding: var(--wp--preset--spacing--s) 0 var(--wp--preset--spacing--s) var(--wp--preset--spacing--m); border-bottom: 1px solid color-mix(in srgb, var(--wp--custom--ink--light) 25%, transparent); color: inherit; }
.pwr-b-key-applications ul li:last-child,
.pwr-b-app-intro ul li:last-child { border-bottom: 0; }
.pwr-b-key-applications ul li::before,
.pwr-b-app-intro ul li::before { content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%); width: 8px; height: 8px; background: var(--wp--preset--color--orange); }

/* ── two-col + panels (still used by misc layouts) ── */
.pwr-b-2col { display: grid; grid-template-columns: 1fr 1fr; gap: var(--wp--preset--spacing--l); }
@media (max-width: 860px) { .pwr-b-2col { grid-template-columns: 1fr; } }
.pwr-b-panel { border: 1px solid var(--wp--preset--color--grey-bright); padding: var(--wp--preset--spacing--m); }
.pwr-b-panel--dark { background: var(--wp--preset--color--contrast); color: var(--wp--preset--color--base); border-color: var(--wp--preset--color--contrast);
	clip-path: polygon(0 0, 100% 0, 100% calc(100% - 22px), calc(100% - 22px) 100%, 0 100%); }
.pwr-b-panel--dark .pwr-b-eyebrow { color: var(--wp--preset--color--orange); }
.pwr-b-panel p { margin: var(--wp--preset--spacing--s) 0 0; font-size: var(--wp--preset--font-size--s); line-height: 1.6; }

/* ── testimonial (mockup: quote mark, italic light quote, orange attribution) ── */
/* Testimonials — the LOOK lives in the theme, assets/css/quotes.css, shared with the
   core/quote block styles so the two cannot drift. Read that file's header before adding
   anything here: it is an enqueued stylesheet too, and a rule here targeting .pwr-testimonial
   would print after it and win silently. Only the block's own outer spacing belongs here. */
.pwr-b-testimonials { margin: 0; }

/* ── CTA band (dark; orange primary button carries ink text per mockup) ── */
/* No notch: it is a full-bleed band now, and a chamfer against the viewport edge reads as
   damage — same reasoning as the pinned header bar (Wayne, 9 Sep). */
.pwr-b-cta { background: var(--wp--preset--color--contrast); color: var(--wp--preset--color--base); padding-block: var(--wp--preset--spacing--xl); }
.pwr-b-cta h2, .pwr-b-cta h3 { color: var(--wp--preset--color--base); margin: 0; font-size: var(--wp--custom--type--subheading); text-transform: uppercase; font-weight: 900; line-height: 1.1; }
.pwr-b-cta p { margin: var(--wp--preset--spacing--s) 0 0; color: rgba(255,255,255,0.85); font-weight: 300; max-width: 560px; }
.pwr-b-cta__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--wp--preset--spacing--s); }
.pwr-b-cta__contact { font-size: var(--wp--preset--font-size--s); color: rgba(255,255,255,0.7); }
.pwr-b-cta__contact strong { color: var(--wp--preset--color--base); }

/* ── service pillars (mockup home: numbered hairline rows) ── */
.pwr-b-pillars { display: flex; flex-direction: column; list-style: none; margin: 0; padding: 0; counter-reset: pillar; }
.pwr-b-pillar { display: flex; align-items: baseline; gap: var(--wp--preset--spacing--s); padding: var(--wp--preset--spacing--s) 0; border-bottom: 1px solid color-mix(in srgb, var(--wp--custom--ink--light) 25%, transparent); counter-increment: pillar; }
.pwr-b-pillar:last-child { border-bottom: 0; }
.pwr-b-pillar::before { content: "0" counter(pillar); font-family: Syncopate, "Red Hat Display", sans-serif; font-weight: 700; font-size: var(--wp--preset--font-size--xs); letter-spacing: var(--wp--custom--tracking--eyebrow); color: var(--wp--preset--color--orange); width: 34px; flex: none; }
.pwr-b-pillar h3 { font-size: var(--wp--preset--font-size--m); font-weight: 700; text-transform: uppercase; margin: 0; color: var(--wp--custom--ink--light); }
.pwr-b-pillar p { margin: 4px 0 0; font-size: var(--wp--preset--font-size--s); font-weight: 300; color: var(--wp--preset--color--grey-light); }
/* Rows that open (20 Oct): the title is a <details> summary with the FAQs' "+" turning to "×"
   at the right; the paragraph it opens to is white body copy under the title, on the number's
   indent. The row's number stays on the <li>, so it lines up whether a row opens or not. */
.pwr-b-pillar__details { flex: 1; min-width: 0; }
.pwr-b-pillar__details > summary {
	cursor: pointer;
	list-style: none;
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--wp--preset--spacing--s);
}
.pwr-b-pillar__details > summary::-webkit-details-marker { display: none; }
.pwr-b-pillar__details > summary::after {
	content: "+";
	flex: none;
	font-weight: 900;
	font-size: var(--wp--preset--font-size--xl);
	line-height: 1;
	color: var(--wp--preset--color--orange);
	transition: transform var(--wp--custom--motion--base) var(--wp--custom--motion--ease-standard);
}
.pwr-b-pillar__details[open] > summary::after { transform: rotate(45deg); }
.pwr-b-pillar__details > summary:hover h3 { color: var(--wp--preset--color--orange); }
.pwr-b-pillar__details > summary:focus-visible { outline: none; box-shadow: var(--wp--preset--shadow--focus); }
.pwr-b-pillar__details > p {
	margin: var(--wp--preset--spacing--xs) 0 var(--wp--preset--spacing--xs);
	max-width: 62ch;
	font-size: var(--wp--preset--font-size--m);
	line-height: 1.65;
	color: var(--wp--custom--ink--light);
}
.pwr-b-service-pillars > .pwr-b-viewall { margin-top: var(--wp--preset--spacing--l); }

/* ── brand-case protection ── */
.keep-case { text-transform: none; }

/* ── editor placeholder ── */
.pwr-b-placeholder { border: 1px dashed var(--wp--preset--color--grey-light); color: var(--wp--preset--color--grey); font-size: var(--wp--preset--font-size--s); padding: var(--wp--preset--spacing--m); text-align: center; }

/* The dark-band card variant (grey-dark cards on painted ink bands) retired with the
   1 Oct meeting's universal card rule: every card is WHITE on every ground, and the
   painted bands themselves are being removed. */

/* ── fixes ── */
.wp-block-group.has-contrast-background-color :where(h1,h2,h3,h4,h5,h6),
.wp-block-group.has-grey-dark-background-color :where(h1,h2,h3,h4,h5,h6),
.wp-block-group.has-orange-dark-background-color :where(h1,h2,h3,h4,h5,h6) { color: inherit; }

/* ══ case study single page (handoff §3, mock "Case Study v2" option 1a) ══
   Every band paints its own background and owns its own padding; margins BETWEEN bands are
   zero (standing layout rule). Chamfer + shadow always pair via a wrapper element, because
   clip-path clips box-shadow on the element it is applied to. */

/* The §3.4 proof band (acf/case-study-proof) left on 19 Oct 2026: its key results are the
   Stat Counters now (blocks/stat-counters, assets/stats/), which took the figure's type and
   put it on the ground above the challenge. */

/* The gallery section is the container its title and frames measure against. The old grid
   gallery (acf/case-study-gallery) left on 17 Oct 2026; the marquee and the case study media
   carousel both still carry the class. */
.pwr-b-gallery { container-type: inline-size; }

/* ── §3.6 location band (Leaflet when there are coordinates — see render.php and map.css) ── */
.pwr-b-csmap { position: relative; background: var(--wp--preset--color--grey-bright); min-height: 320px; display: flex; align-items: center; padding: var(--wp--preset--spacing--xl) var(--wp--preset--spacing--gutter); }
.pwr-b-csmap__surface {
	position: absolute; inset: 0;
	background:
		linear-gradient(var(--wp--preset--color--base) 0 2px, transparent 2px) 0 0 / 100% 96px,
		linear-gradient(90deg, var(--wp--preset--color--base) 0 2px, transparent 2px) 0 0 / 140px 100%;
	opacity: .9;
}
.pwr-b-csmap__card {
	position: relative;
	background: var(--wp--preset--color--contrast);
	color: var(--wp--preset--color--base);
	padding: var(--wp--preset--spacing--m) var(--wp--preset--spacing--l);
	clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--wp--custom--chamfer-sm)), calc(100% - var(--wp--custom--chamfer-sm)) 100%, 0 100%);
}
.pwr-b-csmap__card .pwr-b-eyebrow { color: var(--wp--preset--color--orange); } /* orange, 15 Oct review */
.pwr-b-csmap__label { margin: var(--wp--preset--spacing--xs) 0 0; font-weight: 900; font-size: var(--wp--custom--type--subheading); text-transform: uppercase; }
/* A phone steps the place name down: "WOOLLOONGABBA, QLD" at the subheading size plus the card's
   padding is ~364px in a 320px column at 360, and the page scrolled sideways (20 Oct). The
   break is the backstop for a longer name still. */
.pwr-b-csmap__card { max-width: 100%; box-sizing: border-box; }
.pwr-b-csmap__label { overflow-wrap: break-word; }
@media (max-width: 599px) {
	.pwr-b-csmap__label { font-size: var(--wp--preset--font-size--xl); }
}

/* ── §3.1 case study hero: breadcrumb, oversized title, snapshot lead ── */
/* ══ the crumb ══════════════════════════════════════════════════════════════════════════
   One component for every single view — see ps_render_crumbs(). It has to sit on two very
   different grounds (the case study's ink hero, the post's light band), so the colour comes from
   `currentColor` at reduced opacity rather than a fixed value: it inherits whatever the band has
   already set its text to and stays legible on both. */
.pwr-b-crumbs {
	margin: 0 0 var(--wp--preset--spacing--m);
	font-family: Syncopate, "Red Hat Display", sans-serif;
	font-weight: 700;
	font-size: var(--wp--preset--font-size--xs);
	letter-spacing: var(--wp--custom--tracking--eyebrow);
	text-transform: uppercase;
	color: color-mix(in srgb, currentColor 70%, transparent);
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--wp--preset--spacing--xs);
}
.pwr-b-crumbs a {
	color: inherit;
	text-decoration: none;
	transition: color var(--wp--custom--motion--base, .15s) var(--wp--custom--motion--ease-standard, ease);
}
/* A crumb is never underlined (Wayne, 28 Oct: "links are required and will stay, but in some
   places it's underlined — remove the underline"). The theme's cover-blocks.css underlines every
   link inside a cover at 0,2,1, and the template crumbs (application pages, Learning articles)
   sit in a hero cover, so the rule above lost there. `:root` + the button guard outranks it. */
:root .pwr-b-crumbs a:not(.wp-element-button),
:root .pwr-b-crumbs a:not(.wp-element-button):hover { text-decoration: none; }
.pwr-b-crumbs a:hover { color: currentColor; }
.pwr-b-crumbs a:focus-visible { outline: none; box-shadow: var(--wp--preset--shadow--focus); }
.pwr-b-crumbs__sep { opacity: .55; }
/* The crumb is a flex row, so core's text-align class can't centre it on its own. */
.pwr-b-crumbs.has-text-align-center { justify-content: center; }

/* The last level on a case study is its application — the one crumb worth pulling out, because
   it is the lateral move rather than the way back up. */
.pwr-b-crumbs__accent { color: var(--wp--preset--color--green-light); }
.pwr-b-crumbs__accent:hover { color: var(--wp--preset--color--green-bright); }
/* on a light ground the light green does not hold, so it takes the ink-safe orange */
.pwr-b-crumbs--light .pwr-b-crumbs__accent { color: var(--wp--preset--color--orange); }
.pwr-b-crumbs--light .pwr-b-crumbs__accent:hover { color: var(--wp--preset--color--orange-dark); }
/* The series' positioning line, now a standfirst under its title rather than an eyebrow above it. */
.pwr-b-srow__scale {
	margin: var(--wp--preset--spacing--s) 0 0;
	font-size: var(--wp--preset--font-size--l);
	font-weight: 300;
	line-height: 1.5;
	color: color-mix(in srgb, currentColor 82%, transparent);
	max-width: 46ch;
}

.pwr-b-cshead .pwr-b-cshead__title {
	margin: 0;
	font-weight: 900;
	text-transform: uppercase;
	line-height: 1.06;
	letter-spacing: var(--wp--custom--tracking--tight);
	font-size: var(--wp--custom--type--title);
	color: var(--wp--preset--color--base);
	max-width: 22ch;
}
.pwr-b-cshead__snapshot {
	margin: var(--wp--preset--spacing--m) 0 0;
	max-width: 58ch;
	font-weight: 300;
	font-size: var(--wp--preset--font-size--l);
	line-height: 1.55;
	color: rgba(255, 255, 255, 0.85);
}

/* ── case study as a series row (Wayne, 7 Oct) ────────────────────────────────────────────
   The case-study hero emits the pwr-b-srow cover skeleton, so the whole composition —
   cover, rail, columns, straddling cards — comes from the series CSS. Only the copy
   column's payload is new: Client and Application as labelled pairs where a series shows
   its intro and highlights. */
.pwr-b-srow__meta {
	display: flex;
	flex-wrap: wrap;
	gap: var(--wp--preset--spacing--m) var(--wp--preset--spacing--xl);
	margin: 0;
}

.pwr-b-srow__meta-cell dt,
.pwr-b-srow__products-label {
	font-size: var(--wp--preset--font-size--xs);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: var(--wp--custom--tracking--eyebrow);
	color: color-mix(in srgb, currentColor 60%, transparent);
}

/* The cards' column header — the same label voice as Client/Application opposite, so the
   row reads as one labelled set. */
.pwr-b-srow__products-label { margin: 0 0 var(--wp--preset--spacing--m); }

/* A centred image block must actually centre: core gives figure.aligncenter display:table
   (shrink-wrapped to the image) and the theme's figure reset zeroes the auto margins that
   were doing the centring — so every icon in the restored case-study stacks sat left of its
   centred heading (Wayne, 7 Oct). Measured: figure display:table, margin-left 0. */
.wp-block-image.aligncenter { margin-inline: auto; }

/* A stat card in a Columns row fills its column, so a row of glyph cards shares one bottom
   edge whatever each card's text runs to (the case-study highlight rows). */
.wp-block-column > .wp-block-group.is-style-pwr-stat {
	height: 100%;
	box-sizing: border-box;
}

/* Glyphs on a stat card take the accent, exactly as the key-features grid colours them —
   the frame <span> is named too, for the same inheritance reason documented there. */
.is-style-pwr-stat .pwr-glyph-block,
.is-style-pwr-stat .pwr-glyph-block .pwr-glyph-frame {
	color: var(--wp--preset--color--orange);
}

.pwr-b-srow__meta-cell dd {
	margin: var(--wp--preset--spacing--xs) 0 0;
	font-size: var(--wp--preset--font-size--m);
}

.pwr-b-srow__meta-cell dd a {
	color: var(--wp--preset--color--green-light);
	text-decoration: none;
}
.pwr-b-srow__meta-cell dd a:hover,
.pwr-b-srow__meta-cell dd a:focus-visible { color: var(--wp--preset--color--green-bright); }

/* The snapshot sits UNDER the meta — a longer one is what balances the copy column against
   the cards. Base ink at L, a step up from the series intro's grey-light body (Wayne, 7 Oct):
   on a case study this paragraph IS the standfirst, not supporting copy. */
.pwr-b-srow__intro--snapshot { margin-top: var(--wp--preset--spacing--l); }
.pwr-b-srow__intro--snapshot p {
	color: var(--wp--preset--color--base);
	/* xl, the one LARGE READING size (Wayne, 14 Oct type sweep): the statement opens the
	   page, so it cannot be set below the Challenge/Solution prose and the quote it
	   introduces — all three sit on xl now. */
	font-size: var(--wp--preset--font-size--xl);
	font-weight: 300;
	line-height: 1.5;
}

/* The cards hug the right edge, so their label does too (Wayne, 7 Oct). */
.pwr-b-srows--cs .pwr-b-srow__products-label { text-align: right; }

/* Case-study titles run far longer than series names, so the cover's measure opens up —
   the series keeps its tight 14ch lockup.

   Centred over the cover (Wayne, 14 Oct), crumb and all: a left crumb above a centred title
   reads as a mistake. text-align centres the LINES inside the box; the title still carries its
   26ch measure, so the box needs the auto margins too, and the crumb is a flex row that
   text-align cannot move at all. */
.pwr-b-srows--cs .pwr-b-srow__title { max-width: 26ch; }
.pwr-b-srows--cs .pwr-b-srow__cover .pwr-b-srow__rail { text-align: center; }
.pwr-b-srows--cs .pwr-b-srow__title { margin-inline: auto; }
.pwr-b-srows--cs .pwr-b-srow__cover .pwr-b-crumbs { justify-content: center; }

/* The bare card (render + name, nothing else): the name centres under the stage in the
   base ink and stays there — the card's dip is the hover. */
/* Bottom-aligned, so a row of mixed-aspect renders still sets its names on one baseline. */
.pwr-b-prodcard--bare .pwr-b-prodcard__anchor {
	gap: var(--wp--preset--spacing--s);
	height: 100%;
	justify-content: flex-end;
}
.pwr-b-prodcard--bare .pwr-b-prodcard__stage { display: block; }
.pwr-b-prodcard--bare .pwr-b-prodcard__title {
	margin: 0;
	text-align: center;
	font-size: var(--wp--preset--font-size--m);
}
.pwr-b-prodcard--bare .pwr-b-prodcard__title a { color: var(--wp--preset--color--base); }

/* Every card at the 4-across size (Wayne, 7 Oct — superseding the count-based scaling of
   the same day): the grid is always four tracks and fills from the left, so one render on
   a one-unit project is the same size as each of four. No straddle: the row sits level
   with the copy BELOW the cover, the levelling the light series variant uses. Scoped under
   --cs so the series' own card sizing keeps its straddle and widths. */
.pwr-b-srows--cs .pwr-b-srow__cards {
	max-width: none;
	margin-top: 0;
	grid-template-columns: repeat(4, minmax(0, 1fr));
}

/* 15 Oct (Wayne), superseding the centred column of 14 Oct: the body is a ROW — the key
   statement on the left, ranged left, with Client / Application / Location beside it as a
   DATA TABLE. The title stays centred over the cover; only the body below it is a row. */
@container (min-width: 881px) {
	.pwr-b-srows--cs .pwr-b-srow__columns { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	.pwr-b-srows--cs .pwr-b-srow__copy { max-width: none; }
}

/* The statement is ranged left now, so it drops the centring and the auto margins the centred
   cut needed. NO measure of its own: it runs to its column, which is what makes it the same
   width as the challenge prose below (Wayne, 15 Oct). That prose carries a 62ch cap, but the
   cap never bites — 62ch at 22px is 886px and its column is 632 — so the column is the real
   measure for both, and matching it is matching them. A cap here would only make the two
   disagree again. */
.pwr-b-srows--cs .pwr-b-srow__intro--snapshot {
	margin-top: 0;
	text-align: left;
}
.pwr-b-srows--cs .pwr-b-srow__intro--snapshot p {
	font-size: var(--wp--preset--font-size--xxl);
	line-height: var(--wp--custom--leading--xxl);
	max-width: none;
	margin-inline: 0;
	text-wrap: balance;
}

/* The facts table — the same object as a minimalist product card's spec rows
   (.pwr-b-prodcard__row, further down): a small grey-light label left, a bold light value
   right, an ink-light hairline between. That is the table this site already uses for facts on
   the dark ground, and two tables that looked nearly alike would be worse than one. */
.pwr-b-srows--cs .pwr-b-srow__meta--rows {
	display: block;
	margin: 0;
	align-self: start;
}
.pwr-b-srows--cs .pwr-b-srow__meta--rows .pwr-b-srow__meta-cell {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--wp--preset--spacing--s);
	padding-block: var(--wp--preset--spacing--s);
	border-bottom: 1px solid color-mix(in srgb, var(--wp--custom--ink--light) 25%, transparent);
}
.pwr-b-srows--cs .pwr-b-srow__meta--rows .pwr-b-srow__meta-cell:first-child { padding-top: 0; }
.pwr-b-srows--cs .pwr-b-srow__meta--rows dt {
	margin: 0;
	font-size: var(--wp--preset--font-size--xs);
	font-weight: 300;
	letter-spacing: var(--wp--custom--tracking--wide);
	text-transform: uppercase;
	color: var(--wp--preset--color--grey-light);
}
.pwr-b-srows--cs .pwr-b-srow__meta--rows dd {
	margin: 0;
	font-size: var(--wp--preset--font-size--s);
	font-weight: 700;
	text-align: right;
	color: var(--wp--custom--ink--light);
}
.pwr-b-srows--cs .pwr-b-srow__meta--rows dd a { color: inherit; text-decoration: none; }
.pwr-b-srows--cs .pwr-b-srow__meta--rows dd a:is(:hover, :focus-visible) { color: var(--wp--preset--color--green-light); }

/* "Watch the video" (Monthly Checkpoint, 16 Oct): the jump to the media section. Since the
   web catch-up of 20 Oct it sits in the right column under the facts table, ranged right with
   the table's values. The button is the outline Primary through do_blocks(); this only
   places it. */
.pwr-b-srows--cs .pwr-b-srow__aside { align-self: start; min-width: 0; }
.pwr-b-srows--cs .pwr-b-srow__jump { margin-top: var(--wp--preset--spacing--l); text-align: right; }
.pwr-b-srows--cs .pwr-b-srow__jump .wp-block-button { display: inline-block; }

/* The quote, centred under the group. All this owns is where it sits: `single` is the house
   quote and centres itself, and quotes.css already holds its words to a 54ch measure (see its
   "a measure, not a width" note). No max-width here — the element is also a .pwr-b-srow__rail,
   and a second max-width would collapse the rail rather than the text. */
.pwr-b-srows--cs .pwr-b-srow__quote {
	margin-top: var(--wp--preset--spacing--xl);
}

/* A partial row floats RIGHT (Wayne, 7 Oct): the first card starts at the track that leaves
   no empty track after it, and auto-placement carries the rest forward — so one unit sits
   hard against the right edge, two take the last two tracks, in field order. */
.pwr-b-srows--cs .pwr-b-srow__cards > li:first-child:nth-last-child(1) { grid-column-start: 4; }
.pwr-b-srows--cs .pwr-b-srow__cards > li:first-child:nth-last-child(2) { grid-column-start: 3; }
.pwr-b-srows--cs .pwr-b-srow__cards > li:first-child:nth-last-child(3) { grid-column-start: 2; }

@container (max-width: 880px) {
	.pwr-b-srows--cs .pwr-b-srow__cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	/* two tracks here: a lone card still hugs the right, everything else flows naturally */
	.pwr-b-srows--cs .pwr-b-srow__cards > li:first-child { grid-column-start: auto; }
	.pwr-b-srows--cs .pwr-b-srow__cards > li:first-child:nth-last-child(1) { grid-column-start: 2; }
}

/* Editor-only migration note (includes/editor-notes.php). Stripped from front-end
   output by ps_cm_strip_editor_notes(), so these rules only ever paint in the editor. */
.pwr-editor-note {
	border: 2px dashed #c2410c;
	border-left-width: 6px;
	background: #fff7ed;
	color: #7c2d12;
	padding: 0.9rem 1.1rem;
	margin-block: 1.25rem;
	font-size: 0.9rem;
	line-height: 1.5;
	text-transform: none;
}

.pwr-editor-note strong {
	color: #9a3412;
	letter-spacing: var(--wp--custom--tracking--tight);
}

.pwr-editor-note em:last-of-type {
	display: inline-block;
	margin-top: 0.4rem;
	opacity: 0.75;
}


/* ══ product hero + spec card FUSION (handoff v2, mock "Spec Table" 4a/4d/4b) ══
   The render is anchored to the CARD, not the panel: half of it hangs above the card's top
   edge at any width, and the two move as one object. Supersedes v1 §5's optional hanging
   card, which left the render to the hero block and the pull-up to an editor.

   The pair is coupled by these three values, defined ONCE so hero and card cannot drift. */
.pwr-b-prodhero,
.pwr-b-specs-fuse {
	--pwr-card-pull: var(--wp--preset--spacing--xxl);
	/* The product page's ceiling — see "the render ceiling" above. Lower than the cards' 65
	   because the page's render sits directly under an h1 with no room above it, where a
	   card's hangs over an edge. Raising it is the change that was tried on 16 Sep and put the
	   render over the title. */
	--pwr-render-ceil: 54;
	/* Clear ink between the hero copy and the top of the render. Steps up a tier with the
	   product single's gap and lift below it (Wayne, 16 Sep) so the render has matching air
	   on both sides — at 80% width it is a big object and the old value read as a pinch. */
	--pwr-render-clear: var(--wp--preset--spacing--xl);
	--pwr-fuse-pad: var(--wp--preset--spacing--l);
	/* The card's rail, as the browser will actually compute it. The handoff baked 904px in
	   PHP; that is only true above ~1064px viewport, so the reserve was over-shooting by
	   ~110px lower down. This tracks the real width at every size. */
	--pwr-fuse-rail: min(1000px, 100vw - var(--pwr-fuse-pad) * 2);
	/* the ground depth, in page units: the panel cannot read the card's cqw (see below). × the
	   step, as the anchor's ground is — see "each unit straddles at its own middle". */
	--pwr-render-half-page: calc(var(--pwr-fuse-rail) * var(--pwr-render-on, 0) * var(--pwr-render-ceil) * var(--pwr-render-step, 1) / 100 / 2);
}

/* v2 §2 / §6.4: no render → no fusion. The card sits flush under the panel with normal
   padding, and there is no gap where the render would have been. */
.pwr-b-prodhero.pwr-b-fuse--norender { padding-block-end: var(--wp--preset--spacing--xl); }
/* three classes so this is order-independent against the pull-up rule below, which is
   itself :root + two classes to beat WP's layout margins */
:root .pwr-b-specs-fuse.pwr-b-specs-fuse.pwr-b-fuse--norender { margin-block-start: 0; }

/* ── The render ceiling: how big the fleet is drawn ──────────────────────────────────────
   The scale is BAKED INTO THE FILES (Wayne, 26 Oct). Every render is a "fleet frame": one
   1920 square, the unit standing on the bottom edge, drawn at its rung of the ladder — the
   container units 1565px tall, Apex 90%, LX/MX 80%, eGEN 70%. So every file is drawn at the
   SAME width and the hierarchy comes out of the pixels. It replaced the stepped CSS width of
   15 Oct, which scaled each unit about its own centre: side by side, the bases (and so the
   ground shadows) no longer lined up. Frames come in via `wp powershift fleetframes`.

       --pwr-render-w      = ceiling × ratio ÷ fill      the frame's width
       --pwr-render-ground = ceiling ÷ 2                 how far the ground line sits below
                                                         the edge a render hangs from

   `--pwr-render-ceil` is the TALLEST unit's drawn height as a percentage of its container. It
   is a design token, one per surface, and it is the ONLY number to touch if the whole fleet
   should be drawn bigger or smaller. `--pwr-render-fill` is the tallest unit's share of its
   frame (1565 / 1920) — a property of the file set, not a design value; change it only if the
   frames are re-made on a different canvas. Everything else — the card's top padding, the
   minimalist stage, the ground disc, the hero's reserve — follows from these two.

   The ground line sits where the tallest unit's base sat when renders were centred on the
   edge, so a container unit is drawn exactly where it was; the smaller ones now stand on the
   same line instead of floating at their own centre. One ground line for every product is
   also what makes a row's top padding, and so its titles, line up again.

   The ladder step (`--pwr-render-step`, PS_RENDER_STEPS) no longer sizes the render. It sizes
   only the ground disc, which is drawn by CSS and has to follow the unit, not the frame. A
   step of 0 still means "no render": `--pwr-render-on` collapses the width and the ground to
   0, which is what the --norender rules already expect. */
.pwr-b-prodcard__anchor,
.pwr-b-prodhero,
.pwr-b-specs-fuse__anchor {
	--pwr-render-fill: 0.815;
	--pwr-render-on: min(1, var(--pwr-render-step, 0) * 100);
	--pwr-render-w: calc(var(--pwr-render-on) * var(--pwr-render-ceil, 65) * var(--pwr-render-ar, 1) / var(--pwr-render-fill));
}

/* ── How a render sits apart from everything around it ───────────────────────────────────
   Two distances, both theme.json spacing presets, shared by every place a render is drawn —
   the product card, the minimalist card in a series row (the /our-products scroller) and the
   product page's hero/spec fusion — so the three cannot drift (Wayne, 16 Sep).

   --pwr-render-gap   the clear space between the render and the next piece of content.
   --pwr-render-lift  how far the shadow sits below the unit. The bigger this is, the higher
                      the unit reads as floating, because the shadow is the only cue for how
                      far off the ground it is.

   The disc itself is one gradient defined once here rather than repeated per context. */
.pwr-b-prodcard__anchor,
.pwr-b-specs-fuse__anchor {
	--pwr-render-disc: radial-gradient(ellipse at center,
		color-mix(in srgb, var(--wp--preset--color--contrast) 22%, transparent) 0%,
		color-mix(in srgb, var(--wp--preset--color--contrast) 8%, transparent) 45%,
		transparent 72%);
}

/* Card scale. */
.pwr-b-prodcard__anchor {
	--pwr-render-gap:  var(--wp--preset--spacing--l);   /* 48px */
	--pwr-render-lift: var(--wp--preset--spacing--s);   /* 16px */
}

/* Product single scale (Wayne, 16 Sep). The page is simply bigger — its render is ~800px wide
   against a card's ~460 — so the card's distances read as cramped under it and the rhythm has
   to step up with everything else. Both tokens go up one tier and keep the 3:1 gap-to-lift
   ratio the cards use, so the two scales are the same shape rather than two arrangements.
   It also fixes a real collision: the disc is 12% of the render's height, so at card spacing
   its lower tail reached ~16px past where the spec table started. At this scale it clears. */
.pwr-b-specs-fuse__anchor {
	--pwr-render-gap:  var(--wp--preset--spacing--xl);   /* clamp(56px, 8vw, 96px) */
	--pwr-render-lift: var(--wp--preset--spacing--l);   /* 32px */
}

/* ── the ink panel ── */
/* The hero's photograph, when the product has a gallery: the first image, behind the lockup.
   `background-image` on the section itself rather than an <img>, because there is nothing to
   describe — it is a surface, not content, and the same photo is in the gallery below with
   its caption.

   The scrim is TWO layers over it, and both are doing a job. A vertical gradient weights the
   ink where the type sits and lets the photograph read at the edges; a flat 45% wash under it
   guarantees a floor whatever the photo is, because these are shot on bright sand, concrete
   and sky, and a gradient alone leaves the intro sitting on the bright half of one.

   Measured on five products by sampling the brightest pixel behind the lockup: the backdrop
   lands between rgb(33,52,63) and rgb(39,55,64), so white type runs 12.3:1 to 14.1:1 — past
   AAA on all of them. If the wash is ever lightened, re-measure rather than assume: the
   headroom is in that flat layer, not in the gradient. */
.pwr-b-prodhero {
	background: var(--wp--preset--color--contrast);
	padding-block-start: var(--wp--preset--spacing--l);
	/* Reserve = the pull-up + half the render + clear ink above it. --pwr-render-half-page is
	   printed by PHP: the panel is a SIBLING of the card and cannot read the card's cqw
	   (v2 §3.2 gotcha), so the browser cannot compute this one. */
	padding-block-end: calc(var(--pwr-card-pull) + var(--pwr-render-half-page) + var(--pwr-render-clear));
}
.pwr-b-prodhero .pwr-b-eyebrow { color: var(--wp--preset--color--orange); } /* orange, 15 Oct review: eyebrows are orange everywhere, green only inside a green card */

.pwr-b-prodhero--photo {
	position: relative;
	background-image:
		/* finishes on the ground itself (was 0.96), so the photo has no lower edge (Wayne, 12 Oct) */
		linear-gradient(to bottom, rgb(15 33 44 / 0.72) 0%, rgb(15 33 44 / 0.86) 55%, rgb(15 33 44 / 1) 100%),
		linear-gradient(rgb(15 33 44 / 0.45), rgb(15 33 44 / 0.45)),
		var(--pwr-hero-img);
	background-size: cover, cover, cover;
	background-position: center, center, center 30%;
	background-repeat: no-repeat;
}

/* The Description, under the intro (ticket #826). The intro is the one-line headline at L, so
   the paragraph steps down to M to read as what follows it, not as a second headline. It
   keeps the 85% white the intro uses, and a reading measure (the rail is 1000px). */
.pwr-b-prodhero__desc {
	max-width: 60ch;
	color: color-mix(in srgb, var(--wp--preset--color--base) 85%, transparent);
}
.pwr-b-prodhero__desc p {
	margin: 0;
	font-size: var(--wp--preset--font-size--m);
	font-weight: 300;
	line-height: 1.6;
}
.pwr-b-prodhero__desc p + p { margin-top: var(--wp--preset--spacing--s); }

/* The product hero band is dark, but nothing on this rail ever declared a text colour — the title
   and intro each set their own, so it never showed. The crumb takes its colour from currentColor
   (so one component can sit on ink or on paper), which turned it into ink-on-ink and made it
   invisible. Declaring it here fixes the crumb and closes the hole for anything added later. */
.pwr-b-prodhero__rail {
	color: var(--wp--preset--color--base);
	max-width: 1000px;
	margin-inline: auto;
	padding-inline: var(--wp--preset--spacing--l);
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center; /* centred in THIS BLOCK ONLY — v2 §4.1 */
	gap: var(--wp--preset--spacing--s);
}
.pwr-b-prodhero .pwr-b-prodhero__title {
	margin: 0;
	/* v2 asked for a `billboard` size above `display` (clamp(2.75rem, 6.4vw, 6rem)); the
	   18 Sep token audit put every title on the one type scale instead, so this is the
	   display token — the same top step the home hero uses. */
	font-size: var(--wp--custom--type--display);
	font-weight: 900;
	line-height: 1.02;
	text-transform: uppercase;
	color: var(--wp--preset--color--base);
}
/* Scoped to the suffix class, NOT `span` — brand-case wraps eGEN in its own span. */
.pwr-b-prodhero__title .pwr-b-prodhero__suffix { color: var(--wp--preset--color--orange); }
/* the smaller unit of a series pair: its model in light green (ps_product_title_accent, 2 Nov) */
.pwr-b-prodhero__title .pwr-b-prodhero__suffix--green { color: var(--wp--preset--color--green-light); }
.pwr-b-prodhero__intro {
	margin: 0;
	max-width: 640px;
	font-weight: 300;
	font-size: var(--wp--preset--font-size--l);
	line-height: 1.55;
	color: color-mix(in srgb, var(--wp--preset--color--base) 85%, transparent);
}

/* ── the fused card ── */
/* `:root` + doubled class on purpose. WP's layout rule for siblings is
   `:root :where(.is-layout-*) > *` — 0,2,0 — and it is printed after this file, so a single
   class silently loses the tie and the card does not pull up at all: the render then sits
   BELOW the panel edge instead of straddling it, and the intro gap comes out as
   clear + pull. Same trap as the header bar (THEME-MERGES §2.11). */
:root .pwr-b-specs-fuse.pwr-b-specs-fuse {
	max-width: 1000px;
	margin-inline: auto;
	margin-block-start: calc(-1 * var(--pwr-card-pull));
	padding-inline: var(--wp--preset--spacing--l);
	position: relative;
	z-index: 1;
	overflow: visible; /* v2 §4.5: any overflow:hidden up the tree decapitates the render */
}
.pwr-b-specs-fuse__anchor {
	container-type: inline-size; /* 1cqw = 1% of the card's width */
	position: relative;
	overflow: visible;
	/* the ground line's depth below the card's top edge — the single value the rest is built from.

	   EACH UNIT STRADDLES AT ITS OWN MIDDLE (Wayne, 28 Oct: the small units "look silly how they
	   overlap the white box"). Until today the ground sat half the TALLEST unit's height below
	   the edge for every product, the cards' one-ground-line rule. A container unit straddled
	   the edge at its middle, but an eGEN (70%) stood with 71% of itself inside the card and
	   only its lid over the edge. × the step puts every unit's middle on the edge. The cards
	   keep the shared ground line, because they sit side by side and their titles must line
	   up; the product page shows one unit, so nothing here has to line up with anything. */
	--pwr-render-ground: calc(var(--pwr-render-on, 0) * var(--pwr-render-ceil) * var(--pwr-render-step, 1) * 1cqw / 2);
}
.pwr-b-specs-fuse__render {
	position: absolute;
	left: 50%;
	bottom: 100%;
	transform: translate(-50%, var(--pwr-render-ground)); /* the frame's bottom edge on the ground line */
	width: calc(var(--pwr-render-w, 0) * 1cqw);
	height: auto;
	display: block;
	z-index: 2; /* on top of the card */
	filter: none; /* client decision: the render never gets a shadow (v2 §4.4) */
	pointer-events: none; /* the frame's empty headroom reaches up over the hero's copy */
}
.pwr-b-specs-fuse .pwr-b-specs-shadow {
	position: relative;
	z-index: 1;
}
/* the card gets out of the render's way. Derived, never editorial (v2 §4.3). */
.pwr-b-specs-fuse .pwr-b-specs {
	padding-block-start: calc(var(--pwr-render-ground) + var(--pwr-render-gap));
}
/* The unit's shadow. `--pwr-render-ground` is exactly where the render's base lands, so the disc
   is placed there and then pushed down by the lift. ::before rather than a wrapper: it is the
   anchor's first child, so at the same z-index it paints under the render and over the card.
   This is a ground shadow under the unit, not a drop-shadow on the render — the rule that the
   render itself never carries one (v2 §4.4) still holds, and `filter: none` above still says so.
   Sized by the ladder step, not the frame: every frame is the same width, the units are not. */
.pwr-b-specs-fuse__anchor::before {
	content: "";
	position: absolute;
	left: 50%;
	top: calc(var(--pwr-render-ground) + var(--pwr-render-lift));
	width: calc(var(--pwr-render-w, 0) * 1cqw * 0.84 * var(--pwr-render-step, 0));
	height: calc(var(--pwr-render-ceil) * 1cqw * 0.12 * var(--pwr-render-step, 0));
	transform: translate(-50%, -50%);
	background: var(--pwr-render-disc);
	z-index: 2;
	pointer-events: none;
}

@media (max-width: 1023px) {
	.pwr-b-prodhero,
	.pwr-b-specs-fuse {
		--pwr-card-pull: var(--wp--preset--spacing--xl);
		--pwr-render-clear: var(--wp--preset--spacing--xl);
	}
	.pwr-b-prodhero .pwr-b-prodhero__title { font-size: var(--wp--custom--type--display); }
}

@media (max-width: 639px) {
	.pwr-b-prodhero,
	.pwr-b-specs-fuse {
		--pwr-card-pull: var(--wp--preset--spacing--xl);
		--pwr-render-clear: var(--wp--preset--spacing--l);
	}
	.pwr-b-prodhero .pwr-b-prodhero__title { font-size: var(--wp--custom--type--title); } /* 4rem */
	.pwr-b-prodhero,
	.pwr-b-specs-fuse { --pwr-fuse-pad: 20px; }
	.pwr-b-specs-fuse { padding-inline: var(--pwr-fuse-pad); }
	/* v2 §3.3 used to force compact units back to two-thirds here, because the old field drew
	   them at a THIRD and a third-width render at phone size is a stamp. The fleet ladder's
	   floor is 0.70, not 0.33, so there is nothing left to rescue and the override is gone —
	   with it the `!important`, which was only ever needed to beat the inline width PHP used
	   to print. Top-step units are unchanged on phones; the small ones are now visibly
	   smaller, which is the hierarchy doing its job on a screen showing one unit at a time.
	   If that reads as too small, raise `--pwr-render-ceil` here and nowhere else. */
	.pwr-b-specs-fuse .pwr-b-specs { padding-block-start: calc(var(--pwr-render-ground) + var(--wp--preset--spacing--s)); }
}

/* the fusion is a screen device (v2 §3.4) */
@media print {
	.pwr-b-specs-fuse { margin-block-start: 0; }
	.pwr-b-prodhero { padding-block-end: var(--wp--preset--spacing--l); }
	.pwr-b-specs-fuse__render {
		position: static;
		transform: none;
		width: 40%;
		margin: 0 auto var(--wp--preset--spacing--m);
	}
	.pwr-b-specs-fuse .pwr-b-specs { padding-block-start: var(--wp--preset--spacing--l); }
}


/* ── headings that live INSIDE their block, so a heading can never be separated from the
      thing it labels (Wayne, 9 Sep) ── */
.pwr-b-key-features__title,
.pwr-b-key-applications__title {
	margin: 0 0 var(--wp--preset--spacing--m);
	font-size: var(--wp--custom--type--heading);
	font-weight: 900;
	text-transform: uppercase;
	line-height: 1.1;
}

/* Centred heading above the cards, the "All case studies" link centred BELOW them (Wayne,
   2 Oct — the link used to ride the heading row, top right). The heading takes the surface's
   own colour rather than theme.json's elements.h2 ink — two classes, because that is what it
   takes to beat `:root :where(h2)` reliably. */
.pwr-b-case-studies .pwr-b-case-studies__head {
	text-align: center;
	margin-bottom: var(--wp--preset--spacing--m);
}
.pwr-b-case-studies .pwr-b-case-studies__title {
	margin: 0;
	font-size: var(--wp--custom--type--heading);
	font-weight: 900;
	text-transform: uppercase;
	line-height: 1.1;
	color: inherit;
}
/* The listing blocks' own head since 19 Oct (Wayne): a centred eyebrow over the title with
   the second half of the title in orange — "Proven ON SITE", "Power for EVERY APPLICATION" —
   the home page's composition, carried by the blocks so every placement agrees. The case
   studies grid wears its older class names for the same thing; both are styled here. */
.pwr-b-listing__head,
.pwr-b-case-studies .pwr-b-case-studies__head { text-align: center; margin-bottom: var(--wp--preset--spacing--m); }
.pwr-b-listing__eyebrow,
.pwr-b-case-studies .pwr-b-case-studies__eyebrow { margin: 0 0 var(--wp--preset--spacing--s); }
.pwr-b-listing__title {
	margin: 0;
	font-size: var(--wp--custom--type--heading);
	font-weight: 900;
	text-transform: uppercase;
	line-height: 1.1;
	color: inherit;
}
.pwr-b-listing__accent,
.pwr-b-case-studies .pwr-b-case-studies__accent { color: var(--wp--preset--color--orange); }
/* Closing a page's content straight onto the hire band (the product page: the grid is the last
   block in <main>), the grid brings the band's breathing room itself — the case study template
   gets it from a padded group around the block (web catch-up follow-up, 23 Oct: "Proven on
   site needs more padding before the hire form"). Code, because the product template is the
   wp_template 4695 override in the database. */
main > .pwr-b-case-studies:last-child { padding-bottom: var(--wp--preset--spacing--xl); }
/* "Explore the range" on a product page (row 144): the dark page's ground, not the light band
   the full products grid paints, and the hire band's breathing room now it closes <main>. */
.pwr-b-products.pwr-b-products--others {
	background: none;
	padding: 0 0 var(--wp--preset--spacing--xl);
}
/* The carousel's overflow-x clips the other axis too, at the PADDING box, so the renders that
   rise above their cards need padding on the track to show in — the Solution carousel's fix.
   Not pulled back up as that one is: the room is also what keeps them clear of the heading. */
.pwr-b-products--others .pwr-carousel .pwr-carousel__track {
	padding-top: var(--wp--preset--spacing--xxl);
}
/* The "view all …" foot, on every listing block that has one (ps_render_viewall). The old
   .pwr-b-case-studies__foot name stays alongside it: the case studies grid is not the only
   block with one any more. */
.pwr-b-viewall,
.pwr-b-case-studies__foot {
	text-align: center;
	margin-top: var(--wp--preset--spacing--l);
}
/* The load-more sentinel on the case studies index (assets/loadmore/loadmore.js): an empty
   strip the observer watches, which says "Loading…" in the eyebrow voice while a page is
   fetched. Tall enough to be reached before the pager would have been. */
.pwr-b-loadmore {
	min-height: var(--wp--preset--spacing--xl);
	margin-top: var(--wp--preset--spacing--l);
	text-align: center;
	font-family: Syncopate, "Red Hat Display", sans-serif;
	font-weight: 700;
	font-size: var(--wp--preset--font-size--xs);
	letter-spacing: var(--wp--custom--tracking--eyebrow);
	text-transform: uppercase;
	color: var(--wp--preset--color--grey-light);
}
/* The foot button. `display: inline-block` because .wp-block-button is normally a flex item
   of a .wp-block-buttons wrapper; on its own it would take the whole row and the centring
   above would have nothing to centre. */
.pwr-b-viewall .wp-block-button {
	display: inline-block;
	margin-top: 0;
}
/* The same job, hand-placed. "All applications" and "All testimonials" on the home page are
   editor paragraphs rather than block output, so they had no treatment at all and sat ranged
   left under a centred grid (Wayne, 14 Oct). This gives them the foot link's centring without
   a content edit, and covers the next one somebody writes.
   Scoped to a paragraph that FOLLOWS a block section and holds nothing but the link — a
   .pwr-b-textlink inside running copy, as Key Applications has, must stay where it is. */
section[class*="pwr-b-"] + p:has(> .pwr-b-textlink:only-child) {
	text-align: center;
}
/* Two H2s side by side at the same size fight, and neither wins (Wayne, 25 Sep). Key
   Features is the substantive half — six cards of it — so it keeps the heading size and Key
   Applications steps down to subheading. The hierarchy does the deciding rather than colour,
   which keeps working whatever surface the blocks are given.

   The title takes the surface's colour now that the panel is gone: two classes, because
   theme.json's elements.h2 ink is `:root :where(h2)` and one class is not reliably enough. */
.pwr-b-key-applications .pwr-b-key-applications__title {
	color: inherit;
}

/* The application intro leads the page under the hero, so its copy column carries no heading
   of its own — the page title in the cover above is the heading. */
.pwr-b-app-intro__copy > p:first-child { margin-top: 0; }

/* ── product page (ticket #826, Wayne 29 Sep) ──
   Key Features and Key Applications no longer share a row, so the Features-over-Applications size
   step above is gone and Key Applications takes the full heading size on its own band. The band
   colours are the template's own group settings (single-product, set in the Site Editor). */

/* Visually hidden, still read. Key Features keeps its H2 for the outline only. */
.pwr-sr-only {
	position: absolute !important;
	width: 1px; height: 1px;
	margin: -1px; padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* A band whose only block had nothing to show (a product with no testimonial, no case study)
   is an empty padded box: a dead gap on a coloured band. Blocks with no data print nothing on
   the front end, so the group is left with no element children. */
main .wp-block-group.alignfull.has-background:not(:has(*)) { display: none; }

/* ── the closing CTA panel: full-bleed surface, contents on the wide rail ── */
.pwr-b-cta {
	padding-inline: var(--wp--preset--spacing--gutter);
}
.pwr-b-cta__rail {
	max-width: var(--wp--style--global--wide-size, 1440px);
	margin-inline: auto;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--wp--preset--spacing--m);
}

/* secondary CTA: an outline, so the orange Hire now stays the single strongest thing here.
   Rebuilt 25 Sep 2026 on Wayne's button audit as the green outline; ORANGE since the 15 Oct
   design review ("How hire works → orange outline"; buttons are orange solid or orange outline,
   green only where a surface is explicitly green). This is the Secondary (Orange outline) of the
   named styles, hand-rolled because the CTA renders a bare <a class="wp-element-button">
   rather than a core Button block — the theme's /styles/button-*.json partials only reach
   .wp-block-button. The band is Deep Grey, which is the surface green is reserved for; the
   white ghost it replaces is not a tone the system allows any more.

   The ring is a ::before, not a border or an inset shadow, so the chamfer can be clipped out
   of it while the focus ring on the <a> itself stays whole. --pwr-btn-chamfer comes from the
   theme's assets/css/buttons.css; the fallback keeps the shape if the plugin is used without it.
   Three classes, because that file's base rule carries two. */
.wp-element-button.pwr-b-cta__secondary.pwr-b-cta__secondary {
	--pwr-btn-ring: calc(var(--pwr-btn-chamfer, 10px) + 0.8284px);
	position: relative;
	background: none;
	color: var(--wp--preset--color--orange);
	box-shadow: none;
}
.wp-element-button.pwr-b-cta__secondary.pwr-b-cta__secondary::before {
	content: "";
	position: absolute;
	inset: 0;
	background: var(--wp--preset--color--orange);
	clip-path: polygon(evenodd, var(--pwr-btn-chamfer, 10px) 0, 100% 0, 100% calc(100% - var(--pwr-btn-chamfer, 10px)), calc(100% - var(--pwr-btn-chamfer, 10px)) 100%, 0 100%, 0 var(--pwr-btn-chamfer, 10px), var(--pwr-btn-chamfer, 10px) 0, var(--pwr-btn-ring) 2px, 2px var(--pwr-btn-ring), 2px calc(100% - 2px), calc(100% - var(--pwr-btn-ring)) calc(100% - 2px), calc(100% - 2px) calc(100% - var(--pwr-btn-ring)), calc(100% - 2px) 2px, var(--pwr-btn-ring) 2px);
	pointer-events: none;
}
/* Hover deepens the button's own colour — for an outline that is the stroke AND the type
   (Wayne, 14 Oct). Green's deeper step is `green`; orange's is `orange-dark`. */
.wp-element-button.pwr-b-cta__secondary.pwr-b-cta__secondary:is(:hover, :focus-visible) {
	color: var(--wp--preset--color--orange-dark);
	transform: translateY(-1px);
}
.wp-element-button.pwr-b-cta__secondary.pwr-b-cta__secondary:is(:hover, :focus-visible)::before {
	background: var(--wp--preset--color--orange-dark);
}

/* ══ product cards — the fusion at card scale (Wayne, 9 Sep) ══
   Same device as the product page: the render straddles the card's top edge. The band is
   full-bleed with generous padding, because the top row's renders need clear air above them
   and the grid needs the wide rail to fit four. */
.pwr-b-products {
	padding-block: var(--wp--preset--spacing--xxl) var(--wp--preset--spacing--xl);
	padding-inline: var(--wp--preset--spacing--gutter);
	background: var(--wp--preset--color--grey-bright);
	overflow: visible; /* the renders poke out of their cards — never clip this */
}
.pwr-b-products__rail {
	max-width: var(--wp--style--global--wide-size, 1440px);
	margin-inline: auto;
}
/* Four across at wide width, down to ONE on a phone — unlike the case study tiles, a product
   card carries spec rows and a CTA, so two-up on a phone would crush it. */
.pwr-b-grid--prod {
	grid-template-columns: repeat(4, 1fr);
	/* room for each render's upper half, which hangs above its card */
	row-gap: var(--wp--preset--spacing--xxl);
}
@container (max-width: 1180px) { .pwr-b-grid--prod { grid-template-columns: repeat(3, 1fr); } }
@container (max-width: 880px)  { .pwr-b-grid--prod { grid-template-columns: repeat(2, 1fr); } }
@container (max-width: 560px)  { .pwr-b-grid--prod { grid-template-columns: 1fr; } }

.pwr-b-prodcard { list-style: none; display: flex; }
/* The same 2px dip every other card wears. It rides the ANCHOR, not the white panel: the
   render hangs above the panel out of flow, and the unit and its card have to move as one. */
.pwr-b-prodcard__anchor { transition: transform var(--wp--custom--motion--base) var(--wp--custom--motion--ease-standard); }
.pwr-b-prodcard:hover .pwr-b-prodcard__anchor { transform: translateY(-2px); }
@media (prefers-reduced-motion: reduce) { .pwr-b-prodcard:hover .pwr-b-prodcard__anchor { transform: none; } }
.pwr-b-prodcard__anchor {
	container-type: inline-size; /* 1cqw = 1% of the card */
	position: relative;
	overflow: visible;
	flex: 1;
	display: flex;
	/* The file's own ratio. Every fleet frame is square, so in practice 1 — kept as the file's
	   so a render supplied on another canvas is never squashed. */
	--pwr-render-box-ar: var(--pwr-render-ar, 1);
	/* The cards' ceiling — see "the render ceiling" above. Covers the standard card, the
	   minimalist card in a series row and the case-study hero's bare card, so a unit is the
	   same size wherever it is carded. */
	--pwr-render-ceil: 65;
	/* the ground line's depth below the card's top edge — the same for every product, which is
	   what keeps the top padding, and so the titles, level across a row */
	--pwr-render-ground: calc(var(--pwr-render-on, 0) * var(--pwr-render-ceil) * 1cqw / 2);
}
.pwr-b-prodcard__render {
	position: absolute;
	left: 50%;
	bottom: 100%;
	transform: translate(-50%, var(--pwr-render-ground)); /* the frame's bottom edge on the ground line */
	width: calc(var(--pwr-render-w, 0) * 1cqw);
	aspect-ratio: var(--pwr-render-box-ar);
	height: auto;
	object-fit: contain;
	display: block;
	z-index: 2;
	filter: none; /* the render never gets a shadow — §2.12 rule 4 */
	pointer-events: none; /* the frame's empty headroom reaches up over whatever sits above the card */
}
.pwr-b-prodcard__shadow {
	filter: drop-shadow(var(--wp--preset--shadow--m));
	position: relative;
	z-index: 1;
	flex: 1;
	display: flex;
}
.pwr-b-prodcard__card {
	background: var(--wp--preset--color--base);
	clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--wp--custom--chamfer)), calc(100% - var(--wp--custom--chamfer)) 100%, 0 100%);
	/* derived, never editorial: the render's lower half sits inside this padding */
	padding: calc(var(--pwr-render-ground) + var(--pwr-render-gap)) var(--wp--preset--spacing--m) var(--wp--preset--spacing--l);
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--xs);
	flex: 1;
}
.pwr-b-prodcard--norender .pwr-b-prodcard__card { padding-top: var(--wp--preset--spacing--l); }
/* The standard card's ground shadow, placed and layered exactly like the fusion's. The
   minimalist card is excluded: its render is in flow, so its disc belongs to the stage. */
.pwr-b-prodcard:not(.pwr-b-prodcard--minimal) .pwr-b-prodcard__anchor::before {
	content: "";
	position: absolute;
	left: 50%;
	top: calc(var(--pwr-render-ground) + var(--pwr-render-lift));
	/* by the ladder step, not the frame — see the fusion's disc */
	width: calc(var(--pwr-render-w, 0) * 1cqw * 0.84 * var(--pwr-render-step, 0));
	height: calc(var(--pwr-render-ceil) * 1cqw * 0.12 * var(--pwr-render-step, 0));
	transform: translate(-50%, -50%);
	background: var(--pwr-render-disc);
	z-index: 2;
	pointer-events: none;
}

.pwr-b-prodcard .pwr-b-eyebrow { color: var(--wp--preset--color--orange); }
.pwr-b-prodcard__title {
	margin: 0;
	font-size: var(--wp--preset--font-size--xl);
	font-weight: 900;
	line-height: 1.1;
	text-transform: uppercase;
}
.pwr-b-prodcard__title a { color: var(--wp--preset--color--contrast); text-decoration: none; }
.pwr-b-prodcard__intro {
	margin: 0;
	font-size: var(--wp--preset--font-size--s);
	font-weight: 300;
	line-height: 1.5;
	color: var(--wp--preset--color--grey);
}
.pwr-b-prodcard__specs {
	margin: var(--wp--preset--spacing--s) 0 0;
	display: grid;
	gap: 0;
}
.pwr-b-prodcard__row {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--wp--preset--spacing--s);
	padding-block: var(--wp--preset--spacing--xs);
	border-bottom: 1px solid var(--wp--preset--color--grey-bright);
}
.pwr-b-prodcard__row dt {
	margin: 0;
	font-size: var(--wp--preset--font-size--xs);
	letter-spacing: var(--wp--custom--tracking--wide);
	text-transform: uppercase;
	color: var(--wp--preset--color--grey-light);
}
.pwr-b-prodcard__row dd {
	margin: 0;
	font-size: var(--wp--preset--font-size--s);
	font-weight: 700;
	color: var(--wp--preset--color--contrast);
	text-align: right;
}
.pwr-b-prodcard__cta {
	margin-top: auto;
	align-self: flex-start;
	margin-block-start: var(--wp--preset--spacing--m);
}
/* The text-link foot on every product card since 2 Nov (it was the minimalist card's alone;
   the standard card had a button). Pinned to the bottom of the card whatever the specs'
   height; the minimalist rules further down still add their own spacing. */
.pwr-b-prodcard__foot {
	display: flex;
	align-items: baseline;
	margin-top: auto;
	margin-block-start: var(--wp--preset--spacing--m);
}
.pwr-b-prodcard__foot .pwr-b-textlink { margin-top: 0; }

/* The current product in a row of the whole range (row 144): the card looks like the others
   (Wayne, 3 Nov: no grey, no glow), but it does not dip on hover and its foot says where you are. */
.pwr-b-prodcard.pwr-b-prodcard--current:hover .pwr-b-prodcard__anchor { transform: none; }
/* Unlinked, the title would take the dark page's white heading ink (the link carried the
   card's own) and vanish on the white card. Doubled class to beat `:root :where(h3)`. */
.pwr-b-prodcard--current .pwr-b-prodcard__title.pwr-b-prodcard__title { color: var(--wp--preset--color--contrast); }
.pwr-b-prodcard__here {
	font-weight: 800;
	font-size: var(--wp--preset--font-size--s);
	text-transform: uppercase;
	letter-spacing: var(--wp--custom--tracking--normal);
	color: var(--wp--preset--color--grey);
}

/* The home page's series tiles reuse the product card, but they sit in a core Columns block
   rather than the product grid — and core Columns keeps four across until its own ~782px
   stack point, squeezing each card to ~195px on the way. Give it the middle step the product
   grid has, so it reads 4 → 2 → 1 like everything else. */
/* Bounded ABOVE 782px on purpose: below that core already stacks the columns to one, and an
   unscoped `flex-basis: 50% !important` was overriding that and forcing two-up on phones.
   `flex-wrap` needs !important too — core sets `nowrap` above 782px, and without beating it
   the columns just compress instead of wrapping. */
@media (min-width: 783px) and (max-width: 1100px) {
	.wp-block-columns:has(.pwr-series-card) { flex-wrap: wrap !important; }
	.wp-block-columns:has(.pwr-series-card) > .wp-block-column {
		flex-basis: calc(50% - var(--wp--preset--spacing--m)) !important;
		flex-grow: 0 !important;
	}
}
/* the tile's own row gap when it wraps — the renders need clear air above each row */
.wp-block-columns:has(.pwr-series-card) { row-gap: var(--wp--preset--spacing--xxl); }

/* ── notched card/row (group block style `pwr-notch`) ──
   For wrapping a run of content — the FAQ accordion is the first use. clip-path cannot be
   paired with box-shadow on the same element, and a block style variation has no wrapper to
   hang a drop-shadow on, so this one is defined by its notch and a hairline, not a shadow. */
.wp-block-details.is-style-pwr-notch,
.wp-block-group.is-style-pwr-notch {
	background: var(--wp--preset--color--base);
	color: var(--wp--preset--color--contrast);
	border: 1px solid var(--wp--preset--color--grey-bright);
	clip-path: polygon(
		0 0,
		100% 0,
		100% calc(100% - var(--wp--custom--chamfer)),
		calc(100% - var(--wp--custom--chamfer)) 100%,
		0 100%
	);
	padding: var(--wp--preset--spacing--l) var(--wp--preset--spacing--l) var(--wp--preset--spacing--xl);
}
@media (max-width: 639px) {
	.wp-block-details.is-style-pwr-notch,
	.wp-block-group.is-style-pwr-notch {
		padding: var(--wp--preset--spacing--m);
		--wp--custom--chamfer: 18px;
	}
}

/* One card PER FAQ (Wayne): tighter padding than a full-width notched row, the question as
   the clickable summary, and the answer revealed beneath it. */
.wp-block-details.is-style-pwr-notch {
	padding: var(--wp--preset--spacing--m) var(--wp--preset--spacing--l);
}
.wp-block-details.is-style-pwr-notch + .wp-block-details.is-style-pwr-notch {
	margin-top: var(--wp--preset--spacing--s);
}
.wp-block-details.is-style-pwr-notch > summary {
	cursor: pointer;
	font-weight: 800;
	font-size: var(--wp--preset--font-size--l);
	line-height: 1.35;
	color: var(--wp--preset--color--contrast);
	list-style: none;
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--wp--preset--spacing--s);
}
.wp-block-details.is-style-pwr-notch > summary::-webkit-details-marker { display: none; }
.wp-block-details.is-style-pwr-notch > summary::after {
	content: "+";
	flex: none;
	font-weight: 900;
	font-size: var(--wp--preset--font-size--xl);
	line-height: 1;
	color: var(--wp--preset--color--orange);
	transition: transform var(--wp--custom--motion--base) var(--wp--custom--motion--ease-standard);
}
.wp-block-details.is-style-pwr-notch[open] > summary::after { transform: rotate(45deg); }
.wp-block-details.is-style-pwr-notch > summary:hover { color: var(--wp--preset--color--orange-dark); }
.wp-block-details.is-style-pwr-notch > :not(summary) {
	margin-top: var(--wp--preset--spacing--s);
	font-weight: 300;
	line-height: 1.65;
	color: var(--wp--preset--color--grey);
}

/* ── FAQ as GLASS (Wayne, 2 Oct) ──
   The notch recipe above, re-inked for the glass surface the render wraps each item in
   (.pwr-glass-dark + .pwr-glass-surface + a real cast — see the theme's glass.css for the
   three-layer contract). The item paints NO surface of its own: the wrapper's stroke and
   blurred fill are the card, and these rules only set type, the marker and the spacing. */
.pwr-b-faqs__item + .pwr-b-faqs__item { margin-top: var(--wp--preset--spacing--s); }
.pwr-b-faq {
	padding: var(--wp--preset--spacing--m) var(--wp--preset--spacing--l);
	color: var(--pwr-glass-ink, var(--wp--custom--ink--light));
}
.pwr-b-faq > summary {
	cursor: pointer;
	font-weight: 800;
	font-size: var(--wp--preset--font-size--l);
	line-height: 1.35;
	list-style: none;
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--wp--preset--spacing--s);
}
.pwr-b-faq > summary::-webkit-details-marker { display: none; }
.pwr-b-faq > summary::after {
	content: "+";
	flex: none;
	font-weight: 900;
	font-size: var(--wp--preset--font-size--xl);
	line-height: 1;
	color: var(--pwr-glass-accent, var(--wp--preset--color--orange));
	transition: transform var(--wp--custom--motion--base) var(--wp--custom--motion--ease-standard);
}
.pwr-b-faq[open] > summary::after { transform: rotate(45deg); }
.pwr-b-faq > summary:hover { color: var(--pwr-glass-accent, var(--wp--preset--color--orange)); }
.pwr-b-faq > summary:focus-visible { outline: none; box-shadow: var(--wp--preset--shadow--focus); }
.pwr-b-faq > :not(summary) {
	margin-top: var(--wp--preset--spacing--s);
	font-weight: 300;
	line-height: 1.65;
	/* The answer is in the glass's full ink, not the soft grey (design review, 15 Oct: "the
	   copy in the FAQ list should be white — it's grey over a glass card, hard to read"). */
	color: var(--pwr-glass-ink, var(--wp--custom--ink--light));
}

/* ── Series cards ─────────────────────────────────────────────────────────────
   Deliberately the case study card's shape — photo up top, short read below —
   because it is the same job: show the WORK. A series card shows the kit on a
   real site (the use), where a product card shows the unit's render.

   Built on the plain .pwr-b-card, not .pwr-b-card--cs: the case study variant
   is dark-on-grey, and these sit white with a coloured rule at the top. The
   accent arrives as --pwr-accent on the element, derived from the series' own
   order, so the colour ramp cannot disagree with the load sequence. */
.pwr-b-series__rail { container-type: inline-size; }

.pwr-b-grid--series { grid-template-columns: repeat(4, 1fr); align-items: stretch; }
@container (max-width: 1180px) { .pwr-b-grid--series { grid-template-columns: repeat(2, 1fr); } }
@container (max-width: 620px)  { .pwr-b-grid--series { grid-template-columns: 1fr; } }

.pwr-b-card--series {
	flex: 1;
	position: relative;
	border-top: 6px solid var(--pwr-accent, var(--wp--preset--color--orange));
	transition: box-shadow var(--wp--custom--motion--base) var(--wp--custom--motion--ease-standard),
	            transform var(--wp--custom--motion--base) var(--wp--custom--motion--ease-standard);
}
/* the dip comes from .pwr-b-card — this card adds no hover of its own */
.pwr-b-card--series:focus-within { box-shadow: var(--wp--preset--shadow--focus); }

/* Same out-of-flow trick as the case study card: an aspect-ratio box that is
   also a flex item gets stretched to the row height instead of keeping its ratio. */
.pwr-b-card--series .pwr-b-card__media { position: relative; aspect-ratio: 4 / 3; overflow: hidden; }
.pwr-b-card--series .pwr-b-card__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; }
.pwr-b-card--series .pwr-b-card__img--placeholder { position: absolute; inset: 0; }

.pwr-b-card--series .pwr-b-eyebrow { color: var(--pwr-accent, var(--wp--preset--color--orange)); margin: 0; }
.pwr-b-card--series .pwr-b-title a::after { content: ""; position: absolute; inset: 0; }
/* `xl`, the house card title — the applications, case study, product and Learning cards all
   set theirs at it (Wayne, 20 Oct: "one size too large"). Without a size of its own the h3
   fell through to theme.json's h3, 33px. */
.pwr-b-card--series .pwr-b-title { font-size: var(--wp--preset--font-size--xl); line-height: 1.3; }

.pwr-b-series__rating {
	margin: 0;
	font-weight: 700;
	color: var(--wp--preset--color--grey);
	font-size: var(--wp--preset--font-size--m);
}

.pwr-b-series__highlights { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--wp--preset--spacing--xs); }
.pwr-b-series__highlights li { display: flex; align-items: center; gap: var(--wp--preset--spacing--s); }
/* The icon files are authored on `--icon-ink` (strokes) and `--icon-accent` (the one filled
   shape), which is why they are inlined rather than linked. Both tokens are orange on every
   series (web catch-up, 20 Oct: "all icons orange") — they used to take the series' accent, so
   eGEN's read green beside orange everywhere else. The accent still dresses the card's top rule. */
.pwr-b-series__icon { flex: 0 0 auto; display: inline-flex;
	--icon-ink: var(--wp--preset--color--orange);
	--icon-accent: var(--wp--preset--color--orange); }
.pwr-b-series__icon svg { width: 1.5rem; height: 1.5rem; display: block; }
/* A glyph (the default since 30 Oct) strokes in currentColor and sizes itself from the px
   passed in (ps_series_bullet_icon); the rule above would squash it, so it steps aside. */
.pwr-b-series__icon { color: var(--wp--preset--color--orange); }
.pwr-b-series__icon .pwr-glyph-block svg { width: 100%; height: 100%; }

/* Load scale above the row. Decorative and aria-hidden: the card order carries the
   same meaning for anyone not seeing it. */
.pwr-b-series__scale {
	display: flex;
	align-items: center;
	gap: var(--wp--preset--spacing--s);
	margin: 0 0 var(--wp--preset--spacing--m);
	font-family: Syncopate, 'Red Hat Display', sans-serif;
	font-weight: 700;
	font-size: var(--wp--preset--font-size--xs);
	text-transform: uppercase;
	letter-spacing: var(--wp--custom--tracking--eyebrow);
	color: var(--wp--preset--color--grey);
}
/* A thin grey line between the two labels — not the colour ramp (Wayne, 20 Oct: "doesn't need
   the rainbow line, just the thin grey line"). The cards still wear the ramp; the line only
   says "this runs from one to the other". The block's --reversed class still flips the labels'
   order; the line has no direction to flip. */
.pwr-b-series__scale-track {
	flex: 1;
	height: 1px;
	background: color-mix(in srgb, var(--wp--preset--color--grey) 55%, transparent);
}
@container (max-width: 620px) { .pwr-b-series__scale { display: none; } }

/* On a dark band the card body has to come back to the light palette. */
.has-contrast-background-color .pwr-b-card--series { background: var(--wp--preset--color--base); }

/* ── Series hero ──────────────────────────────────────────────────────────────
   The series CPT has no block editor, so the head of the page comes from the
   record's fields rather than from post-content. */
.pwr-b-serieshero__rail { max-width: var(--wp--style--global--wide-size, 1440px); margin: 0 auto; }
.pwr-b-serieshero .pwr-b-eyebrow { color: var(--pwr-accent, var(--wp--preset--color--orange)); margin: 0; }
.pwr-b-serieshero__title {
	margin: var(--wp--preset--spacing--s) 0 0;
	font-family: var(--wp--preset--font-family--red-hat-display);
	font-size: var(--wp--custom--type--title);
	font-weight: 900;
	text-transform: uppercase;
	letter-spacing: var(--wp--custom--tracking--tight);
	line-height: 1.02;
	color: var(--wp--preset--color--base);
}
.pwr-b-serieshero__rating {
	margin: var(--wp--preset--spacing--s) 0 0;
	font-size: var(--wp--preset--font-size--xl);
	font-weight: 700;
	color: var(--pwr-accent, var(--wp--preset--color--orange));
}
.pwr-b-serieshero__intro {
	margin-top: var(--wp--preset--spacing--m);
	max-width: var(--wp--style--global--content-size, 840px);
	color: var(--wp--preset--color--grey-bright);
}
.pwr-b-serieshero__intro > :first-child { margin-top: 0; }
.pwr-b-serieshero__intro > :last-child { margin-bottom: 0; }

/* ── Series rows ──────────────────────────────────────────────────────────────
   The products index, and the head of every range page: the same row in both
   places. Cover with the USE photo up top (borrowing the cover block's scrim,
   white type and min-height rather than the block itself, which cannot be driven
   from a loop of records), then positioning copy on the left and this series'
   product cards on the right — the product card exactly as it draws everywhere
   else. Cards pull up over the cover's bottom edge so the two halves read as one
   row rather than two bands. */
.pwr-b-srows { position: relative; }

.pwr-b-srow { position: relative; }
.pwr-b-srow__rail {
	max-width: var(--wp--style--global--wide-size, 1440px);
	margin: 0 auto;
	padding-left: var(--wp--preset--spacing--gutter);
	padding-right: var(--wp--preset--spacing--gutter);
}

/* Cover: the cover block's language. 480px is core/cover's min-height in theme.json. */
.pwr-b-srow__cover {
	position: relative;
	box-sizing: border-box; /* so 480 is the whole band, not 480 + both paddings (measured 704) */
	min-height: 480px;
	display: flex;
	align-items: flex-start;
	padding-top: var(--wp--preset--spacing--xl);
	padding-bottom: var(--wp--preset--spacing--xxl);
	background-color: var(--wp--preset--color--contrast);
	background-size: cover;
	background-position: center;
	color: var(--wp--preset--color--base);
	isolation: isolate;
	/* A title's longest word can be a hair wider than a phone's column ("CONSTRUCTION" is
	   350.5px at the title size in a 350px rail at 390) and the page then scrolls sideways by
	   1px. `clip`, not `hidden`: no scroll container, and the half-pixel lost is side bearing. */
	overflow-x: clip;
}
.pwr-b-srow__cover::before {
	content: "";
	position: absolute;
	inset: 0 0 -2px; /* past the bottom edge with the video: no hairline at a fractional height */
	/* The protection scrim, plus a bottom fade that finishes on the page ground so the photo
	   has no hard lower edge (Wayne, 12 Oct — every header image melts into the ground). The
	   scrim alone ends at 25% and left a line where the cover met the body. */
	background:
		linear-gradient(180deg, transparent 55%, var(--wp--preset--color--contrast) 100%),
		var(--wp--preset--gradient--scrim-dark);
	z-index: -1;
}
/* The series' header video (hero_video), under the scrim: the cover isolates, the scrim is its
   ::before at -1, so the video at -2 sits between the scrim and the cover's own background. */
.pwr-b-srow__video {
	position: absolute;
	/* 2px past the bottom (Website To-Do / ticket #1112, 3 Nov: "a random line on the Apex
	   series hero"): at a fractional cover height the video stopped a third of a pixel short and
	   the featured image under it showed as a hairline. The scrim gets the same overhang. */
	inset: 0 0 -2px;
	width: 100%;
	height: calc(100% + 2px);
	object-fit: cover;
	z-index: -2;
	pointer-events: none;
}
/* Reduced motion: no moving picture at all. The featured image is still the cover's
   background, so the head looks as it did before the video. */
@media (prefers-reduced-motion: reduce) {
	.pwr-b-srow__video { display: none; }
}
.pwr-b-srow__cover--noimage::before {
	background: linear-gradient(180deg, var(--wp--preset--color--contrast) 0%, var(--wp--preset--color--grey-dark) 100%);
}
.pwr-b-srow__cover .pwr-b-srow__rail { width: 100%; }
/* The block bleeds to the browser top and the header bar floats over it (the theme's
   clearance rule skips .pwr-b-srows), so the FIRST cover on a page clears the bar from
   inside — its own padding grows by the bar's height, which --pwr-bar-h tracks per
   breakpoint. Later rows on the index sit under the previous row, not the bar. */
/* "First row" is a row not preceded by another row — NOT :first-child, because on the index
   the load-scale div comes first and :first-child silently skipped row one (measured: its
   eyebrow 2px under the bar while the series page, which has no scale div, cleared by 64). */
.pwr-b-srows > .pwr-b-srow:not(.pwr-b-srow ~ .pwr-b-srow) .pwr-b-srow__cover {
	padding-top: calc(var(--pwr-bar-h, 94px) + var(--wp--preset--spacing--xl));
}
.pwr-b-srow__cover .pwr-b-eyebrow { color: var(--wp--preset--color--orange); margin: 0; }
/* TALLER HEADERS on the series, case study and application pages (meeting, 3 Nov: the photos
   read letterboxed): 600px, the height of every page's hero cover, from tablet up; phones keep
   their own height. The application header is a core cover with min-height 440 written into
   its template (and the wp_template row that shadows it), hence the !important over that
   inline style — CSS, so launch day needs no template change on any environment. */
@media (min-width: 782px) {
	.single-series .pwr-b-srows > .pwr-b-srow:not(.pwr-b-srow ~ .pwr-b-srow) .pwr-b-srow__cover,
	.single-case_study .pwr-b-srows > .pwr-b-srow:not(.pwr-b-srow ~ .pwr-b-srow) .pwr-b-srow__cover { min-height: 600px; }
	.single-application main > .wp-block-cover:first-child { min-height: 600px !important; }
}
/* An application with fewer than two case studies prints no case study grid, so its FAQs open
   the lower band — and kept the band's top padding (xl) AND their own top margin (l) under the
   intro band's bottom padding: 176px of nothing between the list and "FAQs" (meeting, 3 Nov,
   Tourism & Events). When the FAQs come first and nothing (no gallery) sits between the intro
   and that band, the margin goes and the band starts at the intro's own gap. */
.single-application main > .wp-block-group.alignfull:has(> .pwr-b-app-intro) + .wp-block-group.alignfull:has(> .wp-block-group.alignwide > .wp-block-group:first-child .pwr-b-faqs) {
	padding-top: var(--wp--preset--spacing--l) !important;
}
.single-application main .wp-block-group.alignwide > .wp-block-group:first-child:has(.pwr-b-faqs) { margin-top: 0 !important; }
.single-application main .wp-block-group.alignwide > .wp-block-group:first-child .pwr-b-faqs__title:first-child { margin-top: 0; }
.pwr-b-srow__title {
	margin: var(--wp--preset--spacing--s) 0 0;
	max-width: 14ch;
	font-family: var(--wp--preset--font-family--red-hat-display);
	font-size: var(--wp--custom--type--title);
	font-weight: 900;
	text-transform: uppercase;
	letter-spacing: var(--wp--custom--tracking--tight);
	line-height: 1.02;
	color: var(--wp--preset--color--base);
}

/* Body: on the page ground (1 Oct: bands go, the dark ground carries the page),
   two columns at wide. The cards pull up into the cover. */
.pwr-b-srow__body {
	padding-top: var(--wp--preset--spacing--l);
	padding-bottom: var(--wp--preset--spacing--xxl);
	container-type: inline-size;
}
.pwr-b-srow__columns {
	display: grid;
	grid-template-columns: minmax(260px, 1fr) minmax(0, 2fr);
	gap: var(--wp--preset--spacing--xl);
	align-items: start;
}
.pwr-b-srow__copy { display: flex; flex-direction: column; gap: var(--wp--preset--spacing--s); max-width: 42ch; }
/* White, as the body copy is everywhere else on the dark ground (Wayne, 20 Oct) — grey-light
   read as secondary beside the highlights. */
.pwr-b-srow__intro { color: var(--wp--custom--ink--light); }
.pwr-b-srow__intro > :first-child { margin-top: 0; }
.pwr-b-srow__intro > :last-child { margin-bottom: 0; }
/* The rule sits ABOVE the highlights now, between them and the description (web catch-up,
   20 Oct) — it used to close the list off from the rating, which has moved up under the title.
   The highlights step up a size, icon and label both, now they are the column's last word. */
.pwr-b-srow__copy .pwr-b-series__highlights {
	gap: var(--wp--preset--spacing--s);
	padding-top: var(--wp--preset--spacing--m);
	border-top: 1px solid color-mix(in srgb, var(--wp--custom--ink--light) 25%, transparent);
	font-size: var(--wp--preset--font-size--l);
}
.pwr-b-srow__copy .pwr-b-series__icon svg { width: 2.25rem; height: 2.25rem; }
.pwr-b-srow__copy .pwr-b-series__icon .pwr-glyph-block svg { width: 100%; height: 100%; }
/* The range's rating, under the title in orange on every ground (web catch-up, 20 Oct). */
.pwr-b-srow__rating {
	margin: var(--wp--preset--spacing--s) 0 0;
	font-weight: 700;
	font-size: var(--wp--preset--font-size--xl);
	color: var(--wp--preset--color--orange);
}
.pwr-b-srow__cta { align-self: flex-start; margin-top: var(--wp--preset--spacing--xs); }

/* As many columns as cards, up to three; four or more go 2×2. The count is a modifier
   class set by PHP — repeat() will not take var() for its count and the whole declaration
   is dropped when tried, leaving one auto track. Capped per column and right-justified so
   one card sits at the edge rather than stretching across the whole column. Negative top
   margin lifts the cards over the cover's bottom edge; the row gap leaves room for each
   render's upper half, which hangs above its card. */
.pwr-b-srow__cards {
	/* width MUST be definite. margin-left:auto puts a grid item on fit-content sizing, and
	   the product card's __anchor carries container-type:inline-size, which makes its
	   intrinsic inline size ZERO — so a grid sized by its cards' content collapses to
	   nothing (measured: two tracks of 0px, ul 24px wide). 100% sizes the tracks from the
	   column; max-width caps it; the auto margin then right-aligns what is left. */
	width: 100%;
	margin-left: auto;
	margin-top: calc(-1 * (var(--wp--preset--spacing--l) + var(--wp--preset--spacing--xxl)));
	position: relative;
	z-index: 1;
	align-items: start;
	row-gap: var(--wp--preset--spacing--xxl);
}
.pwr-b-srow__cards--1 { grid-template-columns: minmax(0, 1fr); max-width: 360px; }
.pwr-b-srow__cards--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: calc(2 * 360px + var(--wp--preset--spacing--m)); }
.pwr-b-srow__cards--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); max-width: none; }

@container (max-width: 880px) {
	.pwr-b-srow__columns { grid-template-columns: 1fr; gap: var(--wp--preset--spacing--l); }
	.pwr-b-srow__cards, .pwr-b-srow__cards--1, .pwr-b-srow__cards--3 { margin-left: 0; max-width: none; margin-top: calc(-1 * var(--wp--preset--spacing--xl)); grid-template-columns: repeat(2, minmax(0, 1fr)); }
	.pwr-b-srow__copy { max-width: none; }
}
@container (max-width: 560px) {
	.pwr-b-srow__cards, .pwr-b-srow__cards--2 { grid-template-columns: 1fr; }
}

/* ── Series rows: light variant (the products index) ─────────────────────────
   Second comp, 11 Sep: no photography on the index. Each row on white, the title
   block opening the left column, cards level with it on the right, a hairline
   between rows. The 'cover' variant (range pages) keeps the photo cover above and
   is untouched by everything below. */
/* No band of its own since 1 Oct — the row sits straight on the dark ground. The hairline
   between rows spans the row's CONTENT, copy-left to cards-right (Wayne, 20 Oct: "wide width
   only", then "why is the line pulled in from the content?"). It is drawn by the content grid,
   not the section: on the products page the marker rail steps the column in from the left
   (blocks.css, the 1281+ shortfall rule), so a line centred on the viewport missed the content
   at most widths. An absolutely positioned grid child with a grid-column takes that grid area
   as its containing block, so left:0/right:0 land exactly on the content edges; `top` lifts it
   through the body's xl top padding to the row's top edge. */
.pwr-b-srow--light + .pwr-b-srow--light .pwr-b-srow__columns { position: relative; }
.pwr-b-srow--light + .pwr-b-srow--light .pwr-b-srow__columns::before {
	content: "";
	position: absolute;
	grid-column: 1 / -1;
	grid-row: 1;
	left: 0;
	right: 0;
	top: calc(-1 * var(--wp--preset--spacing--xl));
	height: 1px;
	background: color-mix(in srgb, var(--wp--custom--ink--light) 25%, transparent);
	pointer-events: none;
}

/* No cover to lift the cards into, so the body carries the whole row's vertical rhythm
   and the cards sit level with the title block instead of pulling up. */
.pwr-b-srow--light .pwr-b-srow__body {
	padding-top: var(--wp--preset--spacing--xl);
	padding-bottom: var(--wp--preset--spacing--xl);
}
.pwr-b-srow--light .pwr-b-srow__cards { margin-top: 0; }
.pwr-b-srow--light .pwr-b-srow__columns { align-items: start; }

/* Title block on white: eyebrow orange on every row (the comp is explicit — the accent
   ramp is the rail's job, not the eyebrow's; grey type for LX & MX read as muted), title in
   ink at the h2 size (the cover keeps the h1 `title` size — it has a photo to fill),
   footprint in grey. */
.pwr-b-srow--light .pwr-b-srow__copy .pwr-b-eyebrow { color: var(--wp--preset--color--orange); margin: 0; }
.pwr-b-srow--light .pwr-b-srow__title {
	margin: var(--wp--preset--spacing--xs) 0 0;
	font-size: var(--wp--custom--type--heading);
	color: var(--wp--custom--ink--light);
}
/* Space between the title block and the copy that follows it in the same column. */
/* The footprint line used to sit between the title and the copy; with it gone the title is
   always what the intro follows. */
.pwr-b-srow--light .pwr-b-srow__title + .pwr-b-srow__intro { margin-top: var(--wp--preset--spacing--s); }


/* The first light row is also the first thing under the floating header bar. The theme's
   clearance rule skips .pwr-b-srows, so clear it here — same var the cover variant uses.
   spacing|100, not |70: the product renders hang about a quarter of a card's width ABOVE
   their cards, so the copy column clearing the bar is not enough — at |70 the first row's
   renders sat inside the bar (measured: card top 142, render top ~56, bar to 94). |100 puts
   them at ~136 at desktop; the cover variant never hit this because its cards sit under a
   photo. Scoped to :first-child — with a cover block above the rows (the products page,
   16 Sep) the cover takes the bar and the row needs no clearance. */
.pwr-b-srows:first-child > .pwr-b-srow--light:not(.pwr-b-srow ~ .pwr-b-srow) .pwr-b-srow__body {
	padding-top: calc(var(--pwr-bar-h, 94px) + var(--wp--preset--spacing--xxl));
}

/* ── Product card: minimalist variant ─────────────────────────────────────────
   Same width and the same facts table as the standard card, but no surface: no
   chamfer, border or shadow. The render is in flow and as wide as the card, sitting
   on a soft gradient disc that reads as the unit hovering. No series eyebrow (the
   series row already names it) and a text link instead of a button, bottom right.
   Built for the series rows; the products-grid block and the home tiles keep the
   standard card. */
/* The standard anchor is a ROW flexbox with one in-flow child (the render is absolute).
   The minimalist render is in flow, so without this the stage and the card sat side by
   side — measured: li 360 wide, card 140 wide and starting at the li's right edge. */
.pwr-b-prodcard--minimal .pwr-b-prodcard__anchor { flex-direction: column; }
.pwr-b-prodcard--minimal .pwr-b-prodcard__shadow { filter: none; width: 100%; }
.pwr-b-prodcard--minimal .pwr-b-prodcard__card {
	background: transparent;
	clip-path: none;
	padding: var(--pwr-render-gap) 0 0; /* render is in flow, so no derived top padding */
}

/* The stage: the box the render sits in, with the disc beneath.
   The image is ABSOLUTE inside it, not in flow — the same reason the case study card's media
   is: aspect-ratio on a box is only a preference, and in-flow content overrides it. In flow,
   the render inflated to 360×1500 from a 360×360 file and dragged the stage with it, pushing
   the facts 1500px down the page. Out of flow, the stage is exactly its declared box and the
   render fits inside it.

   Width is `--pwr-render-w`, the frame's width, centred, so a minimalist card draws its render
   at the same size as every other card. The stage is only as tall as the TALLEST unit (the
   frame's ratio ÷ fill): every frame carries the same empty band above that, so the stage
   crops it rather than stacking dead space over the row. Same stage for every product, so a
   row's titles stay level and every unit stands on the stage's bottom edge (Wayne, 26 Oct). */
.pwr-b-prodcard--minimal .pwr-b-prodcard__stage {
	position: relative;
	width: calc(var(--pwr-render-w, 100) * 1%);
	margin-inline: auto;
	aspect-ratio: calc(var(--pwr-render-box-ar) / var(--pwr-render-fill));
	isolation: isolate;
}
.pwr-b-prodcard--minimal .pwr-b-prodcard__render {
	/* undo the standard card's hang (bottom:100% + translate) and stand on the stage's floor;
	   the frame's empty top runs up out of the stage, which is transparent and harmless */
	position: absolute;
	inset: auto 0 0 0;
	transform: none;
	width: 100%;
	height: auto;
	max-height: none;
	aspect-ratio: var(--pwr-render-box-ar);
	object-fit: contain;
	object-position: center bottom;
	z-index: 1;
}
/* The disc. An ellipse of the ink colour fading out, sat just under the render's base and
   pushed a little below it so the unit reads as lifted off it. color-mix keeps it on the
   palette token rather than a hard-coded rgba. Static: Wayne wants the shadow but no hover
   effect on the unit. Sized by the ladder step: the stage is the same for every unit. */
.pwr-b-prodcard--minimal .pwr-b-prodcard__stage::after {
	content: "";
	position: absolute;
	inset-inline: calc(50% - 42% * var(--pwr-render-step, 1));
	bottom: calc(-1 * var(--pwr-render-lift)); /* dropped away from the unit so it floats */
	height: calc(12% * var(--pwr-render-step, 1));
	transform: translateY(45%);
	background: var(--pwr-render-disc);
	z-index: 0;
	pointer-events: none;
}

/* Text link, bottom right, pinned to the foot so a row of cards stays even. */
/* Card foot: Datasheet on the left, View product on the right (client 18 Sep, placement
   Wayne). The foot takes the auto margin the lone link used to carry, so the row still sits
   at the bottom of the card whatever the spec table's height. `.pwr-b-textlink` sets
   margin-top:auto for its usual column context — inside this row that auto margin would drop
   the link to the row's bottom edge, so it is zeroed here. */
.pwr-b-prodcard--minimal .pwr-b-prodcard__foot {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: var(--wp--preset--spacing--s);
	margin-top: auto;
	margin-block-start: var(--wp--preset--spacing--s);
}
.pwr-b-prodcard--minimal .pwr-b-prodcard__foot .pwr-b-textlink { margin-top: 0; }
.pwr-b-prodcard--minimal .pwr-b-prodcard__link { margin-left: auto; } /* stays right when a unit has no datasheet */

/* The minimalist card has no surface, and since 1 Oct the ground beneath it is Deep Grey —
   the base rules above dress the card for its white plate (ink title, ink values, grey-bright
   hairlines), which vanished here (measured on /our-products/ and the range pages: invisible
   titles and spec values, Wayne 2 Oct). The no-surface variant flips its own text. */
.pwr-b-prodcard--minimal .pwr-b-prodcard__title a { color: var(--wp--custom--ink--light); }
.pwr-b-prodcard--minimal .pwr-b-prodcard__intro { color: var(--wp--preset--color--grey-light); }
.pwr-b-prodcard--minimal .pwr-b-prodcard__row { border-bottom-color: color-mix(in srgb, var(--wp--custom--ink--light) 25%, transparent); }
.pwr-b-prodcard--minimal .pwr-b-prodcard__row dd { color: var(--wp--custom--ink--light); }
/* Datasheet wears .pwr-b-textlink--download: a PDF downloads, it does not lead on. */

/* Nothing hangs above a minimalist card, so the series rows need neither the tall row gap
   the hanging render wanted nor the extra bar clearance on the first light row.

   The bar clearance below is scoped to `:first-child` because it only applies when the rows
   ARE the top of the page and the fixed header floats over them. With a cover above (the
   products page, 16 Sep) the cover takes the bar and the first row needs no clearance at all —
   so the selector stops matching on its own rather than needing a page-specific override. */
.pwr-b-srow__cards { row-gap: var(--wp--preset--spacing--l); }
.pwr-b-srows:first-child > .pwr-b-srow--light:not(.pwr-b-srow ~ .pwr-b-srow) .pwr-b-srow__body {
	padding-top: calc(var(--pwr-bar-h, 94px) + var(--wp--preset--spacing--l));
}

/* ── The marker rail ─────────────────────────────────────────────────────────
   A line down the side of a stack of sections, with each section's EYEBROW set on the line
   beside its heading instead of above its title. Built for the products index — the four
   ranges read as one ramp, "Whole compound" at the top down to "Site shed / remote office"
   — but it belongs to no page: any stack of sections can wear it (Wayne, 15 Oct 2026).

   Markup contract:
     .pwr-b-rail             an optional container around the stack. A step carries the
                             geometry itself, so a page whose sections have no wrapper to take
                             a class — anything built in the editor — needs only the steps; the
                             container is where a whole stack's dials are turned at once.
     .pwr-b-rail__step       one section on the line. Sets --pwr-accent (its own colour) and
                             --pwr-accent-next (the next step's), so the segments blend into
                             one ramp, and --pwr-rail-top: how far down its own box its
                             heading sits, which is where the label lines up.
     .pwr-b-rail__marker     the label's track down the step, and .pwr-b-rail__label the label
                             inside it. Positioned against the STEP, so the marker must be a
                             child of the step and not of an inner column — a sticky or
                             otherwise positioned column would take over as its containing
                             block and carry it off the line.
     .pwr-b-eyebrow--railed  the in-flow eyebrow the label was taken from. Hidden while the
                             rail is drawn and printed as usual below it, so the narrow
                             screens that have no room for a rail, and screen readers, still
                             get that line of type. The label itself is aria-hidden.

   The line sits LEFT of the headings (Wayne, 15 Oct — it was the right gutter), in the
   margin beside the content: --pwr-rail-split is how far from the chrome edge to the text
   edge it sits (0.5 = the midpoint), so it scales with the screen instead of hugging the
   heading at a fixed offset on a wide display. --pwr-rail-mask is the colour behind the
   label, which has to be the surface the stack sits on — the page ground by default; a
   stack on its own band sets it. */
/* Every dial is DECLARED here, on the container and on the step alike, so either can stand
   alone and no rule below has to carry a fallback. It is written this way because the other
   way bit: left undeclared, a `var(--dial)` with no fallback makes the whole declaration
   invalid at computed-value time, and the property falls to its INITIAL value rather than the
   one it was overriding. `padding-left` on the index fell to 0 instead of the gutter, which
   cost the rows their left margin between 1281 and 1536 and ran the copy under the labels on
   a 1512 laptop (16 Oct). One name per dial, always declared, no fallbacks to forget.

   Tune a dial ON THE STEPS — `.my-stack .pwr-b-rail__step { --pwr-rail-split: 0.3 }`. Setting
   it on the container alone does nothing: the step declares its own and that shadows what it
   would inherit. */
.pwr-b-rail,
.pwr-b-rail__step {
	/* The content's left edge: the margin beside the 1440 column, plus the gutter. */
	--pwr-text-edge: calc(max(0px, (100% - var(--wp--style--global--wide-size, 1440px) - 2 * var(--wp--preset--spacing--gutter)) / 2) + var(--wp--preset--spacing--gutter));
	--pwr-rail-split: 0.5; /* how far from the chrome edge to the text edge the line sits */
	--pwr-rail-x: max(var(--wp--preset--spacing--m), calc(var(--pwr-text-edge) * var(--pwr-rail-split)));
	--pwr-rail-w: 3px; /* the line's weight */
	--pwr-rail-pad: var(--wp--preset--spacing--s); /* the mask's breath above and below the label */
	--pwr-rail-mask: var(--wp--preset--color--contrast); /* the surface the stack sits on */
	--pwr-rail-top: 0px; /* how far down a step its heading sits */
	position: relative;
}

.pwr-b-rail__step::before {
	content: "";
	position: absolute;
	left: var(--pwr-rail-x);
	top: -1px; /* covers the hairline between steps so the line reads unbroken */
	bottom: 0;
	width: var(--pwr-rail-w);
	background: linear-gradient(180deg,
		var(--pwr-accent, var(--wp--preset--color--orange)),
		var(--pwr-accent-next, var(--pwr-accent, var(--wp--preset--color--orange))));
	pointer-events: none;
}

/* NO OVERHANG at the top (design review, 15 Oct: "there shouldn't be that tiny little bit of
   line overhanging at the very top above where Powrbank Max is — same on all the other pages").
   The first step's segment starts where its label starts, level with the heading, instead of
   at the step's top edge. The LAST step still runs to its bottom: "end at the green" is the
   ramp's last colour, not a cut (Wayne, 15 Oct), and the two pages must agree. The class is
   set by whoever draws the stack — series-rows/render.php and includes/rail.php — because a
   top-level group in page content has no container to be :first-child of. */
.pwr-b-rail__step--first::before {
	top: calc(var(--pwr-rail-top) - var(--pwr-rail-pad));
}

/* The scroll tick (Monthly Checkpoint, 16 Oct): a short horizontal line from the rail toward
   the content, beside the section being read — "it's not the eyebrow that moves, it's the
   little notch in the line that joins the eyebrow". It grows out of the line on the current
   step (assets/rail/rail.js sets .is-current) at the label's own height, so it points at the
   label, and folds back as the next step takes over. Width is a dial like the rest; it is
   declared on the step so a stack can lengthen it. */
.pwr-b-rail,
.pwr-b-rail__step { --pwr-rail-tick: 28px; }
/* The tick is its OWN sticky element (ps_render_rail_label prints it beside the label inside
   the marker), pinned at the same offset as the label so the two ride together. It is not a
   pseudo on the label, because the label is rotated 180° in a vertical writing mode and a
   pseudo inherits that geometry — "right" becomes the page's left and "top" its bottom. Zero
   height so it takes no room in the marker's flow and the label's own pin is unaffected; the
   bar is its ::before. It sits UNDER the label (z-index 1 to the label's 2): the first few
   pixels run beneath the label's mask and the tick emerges from the label's edge, which is
   what makes it read as a notch joining the words rather than a dash beside them. */
.pwr-b-rail__tick {
	position: sticky;
	top: var(--pwr-rail-pin, calc(var(--pwr-bar-h, 94px) + var(--wp--preset--spacing--l) - var(--pwr-rail-pad)));
	display: block;
	height: 0;
	width: 0;
	z-index: 1;
	pointer-events: none;
}
.pwr-b-rail__tick::before {
	content: "";
	position: absolute;
	/* The marker's left IS the line's centre; rail.js moves the pulse a little off it
	   (--pwr-rail-tick-x) so it starts in the clear, as it ends short of the heading. */
	left: var(--pwr-rail-tick-x, 0px);
	/* Level with the middle of the heading's first line, measured by rail.js (23 Oct); the
	   fallback, without the script, is the label's own first letters. */
	top: var(--pwr-rail-tick-y, calc(var(--pwr-rail-pad) + 0.45em));
	width: var(--pwr-rail-tick);
	height: var(--pwr-rail-w);
	background: var(--pwr-rail-ink, var(--pwr-accent, var(--wp--preset--color--orange)));
	clip-path: inset(0 100% 0 0); /* nothing showing until it pulses */
}
/* A PULSE, not a standing tick (Wayne, 23 Oct: "once it hits that padding it detaches from the
   left rail and reduces its size down to nothing — a pulse on a line that shoots out from the
   rail, not a permanent line"). The bar is its full length throughout and the clip does the
   moving: it uncovers from the line out to the far end, then lets go of the line and closes up
   behind itself to nothing at that end. Once per arrival — the animation restarts each time a
   step becomes current, and ends invisible. */
@keyframes pwr-rail-pulse {
	0%   { clip-path: inset(0 100% 0 0); }
	45%  { clip-path: inset(0 0 0 0); }
	100% { clip-path: inset(0 0 0 100%); }
}
.pwr-b-rail__step.is-current > .pwr-b-rail__marker > .pwr-b-rail__tick::before {
	animation: pwr-rail-pulse 1100ms var(--wp--custom--motion--ease-standard) both;
}
/* The pulse meets the heading (Wayne, 23 Oct: "line up with and stretch out the heading it is
   paired with"): rail.js sets --pwr-rail-tick on each step to the distance from the line to
   short of the heading's first letter, so it shoots out toward it. Where the heading does not
   pin, a sticky tick would ride down the section and leave it behind, so on those steps it
   stays put at the top of the marker, beside the heading. */
.pwr-b-rail__step--tick-at-heading > .pwr-b-rail__marker > .pwr-b-rail__tick {
	position: absolute;
	top: 0;
	left: 0;
}
/* A pulse is all motion and only decoration, so with reduced motion there is none. */
@media (prefers-reduced-motion: reduce) {
	.pwr-b-rail__step.is-current > .pwr-b-rail__marker > .pwr-b-rail__tick::before { animation: none; }
}

/* The label RIDES its step. The marker is the track — it opens at the step's heading and ends
   with the step — and the label is sticky inside it, so it sets off beside the heading, holds
   under the header bar for as long as its section is the one being read, and is carried off by
   the section's end as the next one's label arrives. On the index the copy column pins at the
   same offset, so the label and the heading it belongs to stay side by side the whole way
   down; where nothing pins, the label is simply the marker for the section you are in.
   Nail it to the heading instead with --pwr-rail-pin: auto. */
.pwr-b-rail__marker {
	position: absolute;
	left: calc(var(--pwr-rail-x) + var(--pwr-rail-w) / 2);
	top: calc(var(--pwr-rail-top) - var(--pwr-rail-pad));
	bottom: 0;
	width: 0;
	pointer-events: none;
}

/* On the line, not beside it: the mask under the type is what lets the label interrupt the
   ramp. The label is drawn in the colour of the LINE WHERE IT SITS (Wayne, 15 Oct) — it opens
   at the top of its own segment, so that is the step's accent. While a pinned label travels
   its segment the line behind it is already blending into the next step's colour and the
   label holds its own; tracking that blend needs the scroll timeline, not a declaration.
   Where an accent is too dark to read as type on the ground, the step hands the label a
   legible sibling instead — see ps_rail_step_style(). */
.pwr-b-rail__label {
	position: sticky;
	/* Level with where a pinned heading holds — the copy column's own --pwr-pin-top, less the
	   padding the mask opens above the glyphs. */
	top: var(--pwr-rail-pin, calc(var(--pwr-bar-h, 94px) + var(--wp--preset--spacing--l) - var(--pwr-rail-pad)));
	display: block;
	width: max-content;
	transform: translateX(-50%) rotate(180deg);
	writing-mode: vertical-rl; /* with the half turn: bottom→top, the way a left-hand spine reads */
	margin: 0;
	font-family: Syncopate, 'Red Hat Display', sans-serif;
	font-weight: 700;
	font-size: var(--wp--preset--font-size--xs);
	text-transform: uppercase;
	letter-spacing: var(--wp--custom--tracking--eyebrow);
	color: var(--pwr-rail-ink, var(--pwr-accent, var(--wp--preset--color--orange)));
	white-space: nowrap;
	padding: var(--pwr-rail-pad) 0;
	background: var(--pwr-rail-mask);
	pointer-events: none;
	z-index: 2; /* over the tick, so the notch emerges from under the words */
}

/* The line runs the WHOLE stack, every rail, every page: top of the first step to the bottom
   of the last, past the final label. "End at the green" (Wayne, 15 Oct) is the ramp's last
   colour, not a cut — a stop at the last label was tried that day and taken off the same day,
   because a line that ends on one page and runs on on another is two lines. */

/* No margin to put a rail in: the line and its labels go, and the eyebrows come back to
   doing their own job above the titles. Wide desktops only, from 1600 (web catch-up, 23 Oct:
   "hide it sooner" — at 1440 the margin is too thin for the line to mean anything). It was
   1281 until that morning, then 1440 (Wayne: "the whole rail disappears on probably tablet
   size down"). Three rules move together: this pair and the products index step-in below. */
@media (min-width: 1600px) {
	.pwr-b-eyebrow--railed { display: none; }
	/* The heading after a hidden eyebrow still matched the flow layout's `* + *` and kept a
	   48px top margin, so it sat below the photo's top edge (web catch-up, 23 Oct). With the
	   eyebrow gone it is the first thing in its column and starts flush. */
	.pwr-b-eyebrow--railed.pwr-b-eyebrow--railed + * { margin-block-start: 0; }
}
@media (max-width: 1599px) {
	.pwr-b-rail__step::before,
	.pwr-b-rail__marker { display: none; }
}

/* ── The products index on the rail: the load-scale ramp ─────────────────────
   The index is read as a progression from larger sites to smaller loads, so its steps are
   the series rows and the labels are each range's scale line, in the customer's words. The
   line runs the top of the FIRST row to the bottom of the LAST — the full height of the
   index (client, 18 Sep 2026: "extend the line so it runs the full length of the page").

   The two end captions ("Larger sites" / "Smaller loads") came off on 15 Oct: the top one
   stood exactly where the first row's own label now goes, and with four labels naming the
   scale outright the pair had nothing left to add. */
.pwr-b-srow--light {
	/* Where a row's heading sits: the body's top padding plus the title's own margin. The
	   eyebrow used to add its line here too; on the rail it takes no height. */
	--pwr-rail-top: calc(var(--wp--preset--spacing--xl) + var(--wp--preset--spacing--xs));
	/* And where it holds once the copy column pins: that column's --pwr-pin-top (see the
	   stacked-cards block below) plus the same title margin, less the mask's padding. The two
	   offsets are what keep the label and its heading side by side the whole way down a row. */
	--pwr-rail-pin: calc(var(--pwr-bar-h, 94px) + var(--wp--preset--spacing--l) + var(--wp--preset--spacing--xs) - var(--pwr-rail-pad));
}
/* the first light row clears the header bar, so its heading is lower — only when the rows are
   the top of the page (see the body padding rule above); with a cover block over them it is not. */
.pwr-b-srows:first-child > .pwr-b-srow--light:not(.pwr-b-srow ~ .pwr-b-srow) {
	--pwr-rail-top: calc(var(--pwr-bar-h, 94px) + var(--wp--preset--spacing--l) + var(--wp--preset--spacing--xs));
}
/* Until the screen is wide enough for a margin the line can live in, the rows' content steps
   in far enough to leave the rail and its labels a clear spacing|60 beside the copy. Light
   rows only — covers and the rest of the page keep the standard gutter.

   No upper breakpoint, and that matters: this was a 1281–1536 window, chosen when the rail was
   in the RIGHT gutter, and at 1537 the step-in switched off while the natural margin was still
   half a pixel — the labels went back to crowding the headings until about 1670. The step-in
   is now the SHORTFALL: what the rail needs from the viewport edge, less what the centred
   column's own margin already gives, and never less than the gutter. It reaches zero on its
   own wherever the margin has grown enough, so there is no edge to fall off. */
@media (min-width: 1600px) {
	.pwr-b-rail .pwr-b-srow--light .pwr-b-srow__rail {
		padding-left: max(
			var(--wp--preset--spacing--gutter),
			calc(var(--pwr-rail-x) + var(--pwr-rail-w) + var(--wp--preset--spacing--l)
				- (var(--pwr-text-edge) - var(--wp--preset--spacing--gutter)))
		);
	}
}

/* ── EXPERIMENT: stacked cards, pinned copy (Wayne, 11 Sep 2026) ─────────────
   "Stack the product cards in each row; as the user scrolls, each section's heading and
   left-hand data pins just below the menu while the stacked products keep scrolling, then
   the next section scrolls up and pins."
   Scoped entirely to `.pwr-b-srows--stacked`, which series-rows/render.php adds behind the
   `ps/series_rows_stacked` filter — one filter, or deleting this block, rolls it back.

   How the pin works: the copy is a grid item with `align-self: start`, so its own box is
   content-height, but its CONTAINING BLOCK is its grid area, which is as tall as the row's
   tallest track — the stacked cards. `position: sticky` is bounded by the containing
   block, so the copy rides along for exactly the row's height and is carried off by the
   row's end, at which point the next row's copy takes over. No JS.
   The header bar is position:fixed and --pwr-bar-h (header.css, per breakpoint) is where
   its bottom edge sits, so the pin is that plus a breath — the first cut pinned at 48px from
   the viewport top and the eyebrow slid under the bar (screenshot). --pwr-pin-top is the
   one dial.
   Two-column layout only: below the 880 container width the columns stack and a pinned
   copy would sit on top of its own cards. */
@container (min-width: 881px) {
	.pwr-b-srows--stacked .pwr-b-srow--light .pwr-b-srow__copy {
		--pwr-pin-top: calc(var(--pwr-bar-h, 94px) + var(--wp--preset--spacing--l));
		position: sticky;
		top: var(--pwr-pin-top);
		align-self: start;
	}
	/* Two equal halves with a generous gutter between (Wayne: "essentially 50% each — keep
	   the padding generous"). Both halves fill: the copy drops its 42ch measure (the intro
	   paragraphs are short; at 1600 the half is ~75ch of body text — put a max-width back
	   here if that reads long). */
	.pwr-b-srows--stacked .pwr-b-srow--light .pwr-b-srow__columns {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
		column-gap: var(--wp--preset--spacing--xxl);
	}
	.pwr-b-srows--stacked .pwr-b-srow--light .pwr-b-srow__copy { max-width: none; }
	/* One column, the full half. The count modifiers (--1/--2/--3) all collapse to this. */
	.pwr-b-srows--stacked .pwr-b-srow--light .pwr-b-srow__cards,
	.pwr-b-srows--stacked .pwr-b-srow--light .pwr-b-srow__cards--1,
	.pwr-b-srows--stacked .pwr-b-srow--light .pwr-b-srow__cards--2,
	.pwr-b-srows--stacked .pwr-b-srow--light .pwr-b-srow__cards--3 {
		grid-template-columns: minmax(0, 1fr);
		max-width: none;
		margin-inline: 0;
		row-gap: var(--wp--preset--spacing--xl);
	}
}

/* ── jump links (Website To-Do, 2 Nov 2026): buttons to the headings below, centred, wrapping ──
   The buttons are the house button styles; this only lays the row out. The headings they land
   on clear the pinned header bar, and the jump scrolls smoothly unless reduced motion is set. */
.pwr-b-jumplinks { margin-block: var(--wp--preset--spacing--l); }
/* ONE LINE, always (Wayne, 2 Nov: six FAQ categories wrapped to two rows on desktop and six on
   a phone). Compact buttons; when the row still does not fit it scrolls sideways instead of
   wrapping, with a fade on the right edge on narrower screens to say there is more. Centred
   when it fits: auto margins on the end buttons, not justify-content, because a centred row
   that overflows loses its start off the left edge where it cannot be scrolled to. */
.pwr-b-jumplinks .pwr-b-jumplinks__list.wp-block-buttons {
	flex-wrap: nowrap;
	justify-content: flex-start;
	gap: var(--wp--preset--spacing--xs);
	overflow-x: auto;
	scroll-snap-type: x proximity;
	scrollbar-width: none;
	padding-block: 4px; /* room for the focus ring inside the scroller */
}
.pwr-b-jumplinks .pwr-b-jumplinks__list::-webkit-scrollbar { display: none; }
.pwr-b-jumplinks .pwr-b-jumplinks__list > :first-child { margin-inline-start: auto; }
.pwr-b-jumplinks .pwr-b-jumplinks__list > :last-child { margin-inline-end: auto; }
.pwr-b-jumplinks .pwr-b-jumplinks__list > .wp-block-button { flex: none; scroll-snap-align: start; }
.pwr-b-jumplinks .wp-block-button .wp-block-button__link {
	padding: 10px 16px;
	font-size: var(--wp--preset--font-size--xs);
	white-space: nowrap;
}
@media (max-width: 1100px) {
	.pwr-b-jumplinks .pwr-b-jumplinks__list.wp-block-buttons {
		-webkit-mask-image: linear-gradient(to right, #000 calc(100% - 40px), transparent);
		mask-image: linear-gradient(to right, #000 calc(100% - 40px), transparent);
	}
}
.wp-block-post-content :is(h2, h3, h4)[id] { scroll-margin-top: calc(var(--pwr-bar-h, 94px) + var(--wp--preset--spacing--m)); }
@media (prefers-reduced-motion: no-preference) {
	html:has(.pwr-b-jumplinks) { scroll-behavior: smooth; }
}
