/* Homepage. Specs: DESIGN.md sections 9 and 11. */

/* ---------- Hero ---------- */
/* The hero is its own size container: every hero length below is in cqi of the content width,
   so the composition scales with it (1cqi = 3.35 px on the 375 board, 13.28 px on the 1440 one). */
.hero {
	container-type: inline-size;
	display: grid;
	max-width: calc(var(--content-max) + 2 * var(--gutter));
	margin-inline: auto;
	padding: 24px var(--gutter) 56px;
	overflow: clip;
}
.hero-title-box { order: 1; }
/* Text sits above the art layers, as on the boards. */
.hero-title-box, .hero-sub, .hero-lead { position: relative; z-index: 2; }
.hero-title {
	display: flex; flex-direction: column;
	font-size: 25.97cqi; font-weight: 200; line-height: 1; letter-spacing: -0.022em; color: var(--ink);
}
.ht-b { font-weight: 700; color: var(--sage); -webkit-text-stroke: max(1.5px, 0.012em) var(--forest); paint-order: stroke fill; }

.hero-copy { display: contents; }
.hero-sub {
	order: 2; margin-top: 12px;
	font-family: var(--font-sans); font-weight: 300; letter-spacing: -0.02em; font-size: 26px; line-height: 1.3; color: var(--ink);
}

/* The shield gallery (DESIGN.md section 10, "New hero"; lab station herox): real catalogue shields in columns, hard
   edges with a fine rule above and below. Phone: a 110cqi band of 4 columns under the button, full bleed. */
.hx-stage {
	position: relative; order: 3; height: 110cqi; margin-top: 32px; margin-inline: calc(-1 * var(--gutter));
	display: flex; justify-content: center; align-items: center; gap: 20px; overflow: hidden; pointer-events: none;
	box-shadow: inset 0 1px 0 var(--line), inset 0 -1px 0 var(--line);
}
.hx-col { flex: none; width: calc((100cqi - 3 * 20px) / 4); }
.hx-col:nth-child(n + 5) { display: none; }
.hx-col-in { display: flex; flex-direction: column; gap: 20px; }
@media (prefers-reduced-motion: no-preference) {
	/* The hero intro's start, from the first paint, until motion/hero.js takes over (.is-moving, in the same task as its
	   from() / set() calls): nothing jumps when it starts (CLS, port 7). If motion never runs, the fail-safe shows the
	   static design after 3 s (core.js: if GSAP fails, the page is the static design). */
	.js .hero:not(.is-moving) .hx-col { opacity: 0; transform: translateY(24px); animation: hst-failsafe 0s 3s forwards; }
	.js .hero:not(.is-moving) .hx-col:nth-child(even) { transform: translateY(-24px); }
	.js .hero:not(.is-moving) :is(.hero-sub, .hero-body, .btn-hero) { opacity: 0; transform: translateY(26px); animation: hst-failsafe 0s 3s forwards; }
	.js .hero:not(.is-moving) .hero-spec .spec { opacity: 0; transform: translateY(22px); animation: hst-failsafe 0s 3s forwards; }
	/* The title's slide in (session 48): the words start apart. */
	.js .hero:not(.is-moving) .ht-a { transform: translateX(-14%); animation: hst-failsafe 0s 3s forwards; }
	.js .hero:not(.is-moving) .ht-b { transform: translateX(14%); animation: hst-failsafe 0s 3s forwards; }
}
@keyframes hst-failsafe { to { opacity: 1; transform: none; } }
/* One shield pitch of margin moves a centred column down by half a shield: a staggered wall, not a grid. */
.hx-col:nth-child(even) { margin-top: calc((100cqi - 3 * 20px) / 4 * 1.2 + 20px); }
.hx-shield {
	display: block; max-width: none; width: 100%; height: auto; aspect-ratio: 5 / 6; object-fit: contain;
	filter: drop-shadow(0 8px 14px rgba(41, 41, 41, 0.14));
}

.hero-lead {
	order: 4; margin-top: 40px; padding-top: 24px; border-top: 1px solid var(--line);
	display: flex; flex-direction: column; gap: 24px;
}
.hero-body { font-family: var(--font-sans); font-weight: 300; font-size: 16px; line-height: 1.6; color: var(--quiet); }
.btn-hero { min-height: 51px; padding: 12px 32px; font-size: 16px; font-weight: 300; }
/* Phone: the button under the subtitle, before the gallery, so it is on the first screen (session 38); the body keeps
   its place under the gallery and takes over the lead's rule. */
@media (max-width: 767.98px) {
	.btn-hero { width: 100%; }
	.hero-lead { display: contents; }
	.btn-hero { order: 2; margin-top: 24px; }
	.hero-body { order: 4; margin-top: 40px; padding-top: 24px; border-top: 1px solid var(--line); }
}

/* Crop marks round the title row (Technical): short rules in the gutters level with its top, two short verticals
   above its corners - the trim marks of a printed sheet; the gallery's rule is its bottom edge. */
.hero-title-box { position: relative; }
.hero-title-box::before {
	content: ""; position: absolute; pointer-events: none;
	left: calc(-1 * var(--gutter) + 4px); right: calc(-1 * var(--gutter) + 4px); top: -24px; height: 25px;
	--m: linear-gradient(var(--line), var(--line));
	background:
		var(--m) left 0 bottom 0 / calc(var(--gutter) - 12px) 1px, var(--m) right 0 bottom 0 / calc(var(--gutter) - 12px) 1px,
		var(--m) left calc(var(--gutter) - 4px) top 0 / 1px 16px, var(--m) right calc(var(--gutter) - 4px) top 0 / 1px 16px;
	background-repeat: no-repeat;
}

.hero-spec {
	position: relative; order: 5; margin-top: 72px; padding-top: 28px; border-top: 1px solid var(--line);
	display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px 0;
}
/* Ruler ticks hanging from the strip's rule: every 8 px, a longer one every 40 px. */
.hero-spec::before {
	content: ""; position: absolute; left: 0; right: 0; top: 0; height: 9px; pointer-events: none;
	clip-path: inset(0 var(--ruler-r, 0%) 0 0); /* revealed with the rule (js/motion/hero.js) */
	background:
		linear-gradient(to right, var(--line) 1px, transparent 1px) 0 0 / 40px 9px repeat-x,
		linear-gradient(to right, var(--line) 1px, transparent 1px) 0 0 / 8px 4px repeat-x;
}
/* The cells boxed by rules: 2 x 2 on a phone, four in a row from 768. */
.spec { display: flex; flex-direction: column; gap: 8px; padding-inline: 16px; }
.spec:nth-child(odd) { padding-left: 0; }
.spec:nth-child(even) { box-shadow: inset 1px 0 0 var(--line); }
.spec-val { font-family: var(--font-mono); font-weight: 300; letter-spacing: -0.02em; font-size: 22px; line-height: 1.2; color: var(--ink); white-space: nowrap; }
.spec-lbl { font-family: var(--font-mono); font-size: 11px; font-weight: 400; letter-spacing: 0.06em; order: 2; line-height: 1.4; text-transform: uppercase; color: var(--sage-deep); }
.spec-note { font-family: var(--font-mono); font-size: 13px; font-weight: 400; letter-spacing: 0.02em; order: 3; line-height: 1.6; color: var(--quiet); }
@media (max-width: 767.98px) { .spec-note { text-wrap: balance; } } /* as the headings (main.css) */
@media (max-width: 767.98px) { .hero-spec { margin-top: 48px; } } /* 72 until session 60 (owner: "a bit upwards") */

/* From 768: the title on one line 24 px under the nav, the gallery as tall as the first screen allows (session 38),
   the copy on a cream card cut into it, the strip in four ruled cells. */
@media (min-width: 768px) {
	.hero {
		padding: 24px var(--gutter) 80px; grid-template-columns: minmax(0, 1fr);
		/* The gallery's top in the page: the nav, the hero's padding, the title row (its font size x line-height 1). */
		--hx-top: calc(64px + 24px + min(13.855cqi, 11.5rem));
	}
	.hero-title-box, .hx-stage, .hero-copy, .hero-spec { order: 0; grid-column: 1; }
	.hero-title-box { grid-row: 1; }
	.hero-title { flex-direction: row; gap: 0.185em; white-space: nowrap; font-size: min(13.855cqi, 11.5rem); }
	.hero-title-box::before { top: -18px; height: 19px; background-size: calc(var(--gutter) - 12px) 1px, calc(var(--gutter) - 12px) 1px, 1px 12px, 1px 12px; }

	/* 1328 : 600, or shorter so the copy card's button ends 24 px above the foot of the first screen; never under 360. */
	.hx-stage {
		grid-row: 2; margin-top: 0; gap: 32px; pointer-events: auto;
		height: max(360px, min(calc(100cqi * 600 / 1328), calc(100svh - var(--hx-top) - 24px)));
	}
	.hx-col { width: calc((100cqi - 9 * 32px) / 10); }
	.hx-col:nth-child(n + 5) { display: block; }
	.hx-col-in { gap: 32px; }
	.hx-col:nth-child(even) { margin-top: calc((100cqi - 9 * 32px) / 10 * 1.2 + 32px); }

	/* The copy card, bottom left, its top and right edges ruled. */
	.hero-copy {
		display: flex; flex-direction: column; gap: 20px; position: relative; z-index: 2; grid-row: 2; align-self: end;
		box-sizing: content-box; width: min(472px, 36cqi); margin-left: calc(-1 * var(--gutter)); padding: 32px 40px 0 var(--gutter);
		background: var(--ground); box-shadow: inset 0 1px 0 var(--line), inset -1px 0 0 var(--line);
	}
	.hero-sub { margin-top: 0; font-size: min(34px, 2.56cqi); line-height: 1.2; }
	.hero-lead { margin-top: 0; padding-top: 0; border-top: 0; gap: 20px; align-items: flex-start; }
	.hero-body { max-width: 400px; }

	.hero-spec { grid-row: 3; margin-top: 56px; grid-template-columns: repeat(4, minmax(0, 1fr)); }
	.spec, .spec:nth-child(odd) { padding-inline: 24px; }
	.spec:first-child { padding-left: 0; }
	.spec + .spec { box-shadow: inset 1px 0 0 var(--line); }
	.spec-val { font-size: 24px; }
}
/* A shield lifts under the pointer (fine pointers only). */
@media (min-width: 768px) and (hover: hover) and (pointer: fine) {
	.hx-shield { transition: transform 0.45s cubic-bezier(0.2, 0.8, 0.2, 1); }
	.hx-shield:hover { transform: translateY(-6px) scale(1.12); }
}
/* Tablet: four cells of ~170 px; "10 × 13 cm" in the mono needs 20 px type and 16 px padding to stay on one line. */
@media (min-width: 768px) and (max-width: 1023.98px) {
	.spec, .spec:nth-child(odd) { padding-inline: 16px; }
	.spec:first-child { padding-left: 0; }
	.spec-val { font-size: 20px; }
	.hero-sub { font-size: 22px; }
	.btn-hero { white-space: nowrap; }
}

/* ---------- Featured: lead + index (DESIGN.md section 11) ---------- */
.featured-body { display: grid; gap: 40px; }
.feat-lead { display: flex; flex-direction: column; }
/* The lead as shields (DESIGN.md section 10): a Muster layer over the sheet, and a row's image as an overlay (.show-img). */
.feat-lead-img { aspect-ratio: 335 / 268; position: relative; container-type: size; }
.feat-lead-img .show-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.feat-lead-info { display: flex; flex-direction: column; gap: 24px; margin-top: var(--media-gap); }
.feat-lead-text { display: flex; flex-direction: column; gap: 8px; }
.feat-lead-name { font-family: var(--font-sans); font-weight: 300; letter-spacing: -0.02em; font-size: 26px; line-height: 1.2; }
.feat-lead-name a, .feat-row-name a { text-decoration: none; }
.feat-lead-name a:hover, .feat-row-name a:hover { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; }
.feat-lead-price { font-family: var(--font-mono); font-weight: 300; letter-spacing: -0.02em; font-size: 24px; line-height: 1.3; }
.feat-lead .btn { width: 100%; }

.feat-row { position: relative; display: flex; gap: 16px; padding-block: 16px; } /* its rule: .rule--top */
.feat-thumb { flex: none; width: 96px; height: 78px; }
.feat-row-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.feat-row-name { font-size: 15px; font-weight: 500; line-height: 1.5; }
.feat-row-buy { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 4px; }
.feat-row-buy .btn { padding-inline: 16px; }
.feat-row-price { font-family: var(--font-mono); font-size: 13px; font-weight: 400; letter-spacing: 0.02em; line-height: 1.3; }

@media (min-width: 768px) {
	.featured-body { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
	.feat-lead-img { aspect-ratio: 644 / 500; }
	.feat-lead-info { align-items: flex-start; } /* the button under the price; beside it only from 1440 (below) */
	.feat-lead .btn { width: auto; flex: none; }

	/* Rows 128 tall: thumb 128 x 100, text, the outline button at the right. */
	.feat-row { position: relative; align-items: center; gap: 24px; min-height: 128px; padding-block: 0; }
	.feat-thumb { width: 128px; height: 100px; }
	.feat-row-text { padding-right: 146px; }
	.feat-row-name { font-size: 16px; }
	.feat-row-buy { padding-top: 0; }
	.feat-row .btn { position: absolute; right: 0; top: 50%; transform: translateY(-50%); padding-inline: 24px; }
}

/* ---------- Collections: faction index + panel, shield types (DESIGN.md section 11) ---------- */
.collections { background: var(--band); box-shadow: inset 0 -1px 0 var(--line-forest); }
.collections .label { color: var(--ink); }
.collections .label-rule { background: var(--line-forest); }
.coll-body { display: flex; flex-direction: column; gap: 32px; }
.feature-panel { order: -1; display: flex; flex-direction: column; }
/* Muster (DESIGN.md section 10): the faction's shields as a roll of arms, up to 4 a row, centred; sized from the well
   (container units): padding 8 % of its shorter side, gap 4 % of its width, a cell at most 1.25 times as tall as wide. */
.panel-well { height: 268px; display: flex; position: relative; container-type: size; background: linear-gradient(to bottom, #8AA79F, #6E8C83); }
.panel-well > img { width: 100%; height: 100%; object-fit: contain; }
.muster {
	position: absolute; inset: 0; padding: 8cqmin; pointer-events: none;
	display: flex; flex-wrap: wrap; justify-content: center; align-content: center; gap: 4cqw;
	--cw: calc((100cqw - 16cqmin - (var(--cols) - 1) * 4cqw) / var(--cols) - 0.5px);
}
.panel-well > img[hidden], .muster[hidden] { display: none; }
.muster .muster-shield { /* (0,2,0): beats .well img in the Featured lead */
	width: var(--cw); height: min(1.25 * var(--cw), (100cqh - 16cqmin - (var(--rows) - 1) * 4cqw) / var(--rows));
	object-fit: contain; filter: drop-shadow(0 6px 8px rgba(20, 36, 31, 0.22));
}
.panel-caption {
	min-height: 160px; padding: 24px 20px; background: var(--forest);
	display: flex; flex-direction: column; justify-content: center; align-items: flex-start; gap: 8px;
}
.panel-title { font-size: 26px; font-weight: 500; line-height: 1.2; color: var(--ground); }
.panel-era { font-family: var(--font-mono); font-size: 13px; font-weight: 400; letter-spacing: 0.02em; line-height: 1.6; color: var(--ground); }

.faction-index { box-shadow: inset 0 1px 0 var(--line-forest); }
.faction-row {
	display: flex; align-items: center; justify-content: space-between; gap: 16px;
	min-height: 44px; padding: 0 12px; box-shadow: inset 0 -1px 0 var(--line-forest);
	color: var(--ink); text-decoration: none;
}
.fac-name { font-size: 16px; font-weight: 300; line-height: 1.5; }
.fac-count { font-family: var(--font-mono); font-size: 13px; font-weight: 400; letter-spacing: 0.02em; flex: none; line-height: 1.45; }
.faction-row.is-active { background: var(--forest); color: var(--ground); }
.faction-row.is-active .fac-name { font-weight: 500; }
.faction-row:focus-visible { outline-color: var(--ink); outline-offset: -2px; }

.view-all-row { display: flex; justify-content: flex-end; margin-top: 40px; }
.shield-block { margin-top: 40px; display: grid; gap: 20px; }
.shield-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px 15px; }
.shield-type { display: flex; flex-direction: column; gap: 4px; color: var(--ink); text-decoration: none; }
.shield-type:hover .st-name, .faction-row:not(.is-active):hover .fac-name { text-decoration: underline; text-underline-offset: 4px; text-decoration-thickness: 1px; }
.st-name { font-size: 16px; line-height: 1.3; }
.st-count { font-family: var(--font-mono); font-size: 13px; font-weight: 400; letter-spacing: 0.02em; line-height: 1.45; }
/* Shield marks (DESIGN.md section 10): a small real shield (or, for a caparison, a cloth square) above each type's name;
   beside it from 1280 (below that the columns are too narrow: names wrapped, "Hospitaller" overflowed at 800). */
.shield-grid { --mk: 36px; }
.st-mark { width: var(--mk); height: var(--mk); margin-bottom: 6px; display: flex; align-items: center; justify-content: flex-start; }
.st-mark img { width: auto; max-width: 100%; max-height: 100%; filter: drop-shadow(0 4px 5px rgba(20, 36, 31, 0.22)); }
.st-mark--cloth img { width: 82%; border-radius: 3px; }
@media (min-width: 768px) {
	.shield-grid { --mk: 44px; }
}
@media (min-width: 1280px) {
	.shield-grid { --mk: 48px; }
	.shield-type.has-mark { display: grid; grid-template-columns: var(--mk) minmax(0, 1fr); column-gap: 16px; row-gap: 4px; align-items: center; }
	.has-mark .st-name { align-self: end; }
	.has-mark .st-count { align-self: start; }
	.st-mark { grid-row: 1 / span 2; margin-bottom: 0; justify-content: center; }
}

@media (min-width: 768px) {
	.coll-body { flex-direction: row; gap: 40px; align-items: flex-start; }
	.coll-body > * { flex: 1 1 0; min-width: 0; }
	.feature-panel { order: 0; }
	.panel-well { height: 500px; }
	.panel-caption { min-height: 140px; padding: 0 48px; }
	.faction-row { min-height: 40px; padding: 0 16px; }
	.fac-name { font-size: 20px; }
	.view-all-row, .shield-block { margin-top: 48px; }
	.shield-block { gap: 24px; }
	.shield-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 20px 40px; }
	.st-name { font-size: 20px; }
}

/* ---------- Compatibility: panel + index (DESIGN.md section 10, session 48) ----------
   As Featured and Play: one visual beside its text. The panel is a Muster of the shown range's shields in Featured's
   lead well and crop marks; the index is Featured's - a rule above each row and under the last. From 768 a row's hover,
   focus or tap shows its range (js/collections.js). Phone: the panel first, the index under it, as Featured and
   Collections (session 59, owner); the panel keeps the first range. Content-proof: names and notes wrap, the row grows;
   a range without cut-outs leaves the well empty. */
.compat-title { max-width: 960px; }
.compatibility .section-head { margin-bottom: var(--head-gap); } /* the index starts with a rule: no optical offset */
.compat-body { display: grid; gap: 48px; }
.compat-well { position: relative; aspect-ratio: 644 / 500; container-type: size; }
.ranges { display: block; } /* the last row's .rule--bottom closes it */
.range { position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; padding-block: 24px; cursor: default; }
.range-count { font-family: var(--font-mono); font-size: 13px; font-weight: 400; letter-spacing: 0.02em; line-height: 1.4; color: var(--sage-deep); }
.range-name { font-family: var(--font-sans); font-weight: 300; letter-spacing: -0.02em; font-size: 22px; line-height: 1.2; } /* the h3 sizes */
.range.is-active .range-name { font-weight: 500; } /* the range in the panel, as the faction list's active row */
.range-note { max-width: 520px; color: var(--quiet); }
.range-link { margin-top: 16px; white-space: nowrap; } /* the outline button (owner, session 37) */

@media (min-width: 768px) {
	.compat-body { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px; align-items: start; }
	.range { padding-block: 40px; }
	.range-name { font-size: 26px; }
}

/* ---------- Testimonials: the open layout (DESIGN.md section 10; lab station tm-open) ----------
   No panel, no box: the lead quote as large type on the section's sand, the mark above it trimmed to its ink, the
   company and person under a short bar; the other quotes as Learn's step columns - a rule over each, three across
   from 768 on a shared subgrid, so every quote starts on one line however the names wrap. The greens are the accent. */
.testimonials { background: var(--ground-alt); box-shadow: inset 0 -1px 0 var(--line); }
.who { display: flex; flex-direction: column; gap: 8px; }
.testimonials cite { font-family: var(--font-sans); font-weight: 300; letter-spacing: -0.02em; font-style: normal; line-height: 1.2; }
.hl-person, .rv-person { font-family: var(--font-mono); font-size: 11px; font-weight: 400; letter-spacing: 0.06em; line-height: 1.4; text-transform: uppercase; color: var(--quiet); }
.testimonials blockquote { margin: 0; }

.hl { display: block; }
.hl-mark {
	display: block; height: 0.36em; margin-bottom: 24px; overflow: visible;
	font-family: var(--font-serif); font-weight: 700; font-size: var(--fs-display-mark); line-height: 0.8; color: var(--sage);
}
.hl-content { margin: 0; display: flex; flex-direction: column; gap: 16px; }
.hl-quote { font-family: var(--font-serif); font-style: italic; font-weight: 300; font-size: var(--fs-display-quote); line-height: 1.4; letter-spacing: -0.02px; color: var(--ink); }
.hl-who { display: flex; align-items: center; gap: 16px; padding-top: 16px; }
.hl-bar { flex: none; width: 32px; height: 2px; background: var(--sage); }
.hl-co { font-size: 22px; color: var(--ink); }

.reviews { display: grid; gap: 32px; margin-top: var(--head-gap); }
.review { position: relative; margin: 0; display: flex; flex-direction: column; gap: 16px; padding-top: 16px; }
.rv-co { font-size: 22px; color: var(--ink); }
.rv-quote { font-family: var(--font-serif); font-style: italic; font-weight: 300; font-size: var(--fs-display-quote-sm); line-height: 1.6; color: var(--ink); }

@media (min-width: 768px) {
	.hl-mark { margin-bottom: 32px; }
	.hl-content { gap: 40px; }
	.hl-quote { max-width: calc((100% - 11 * var(--col-gap)) * 0.75 + 8 * var(--col-gap)); line-height: 1.35; } /* 9 of 12 columns */
	.hl-who { gap: 20px; padding-top: 0; }
	.hl-bar { width: 40px; }
	.hl-co { font-size: 26px; }

	/* Any number of quotes (owner, session 51): the template picks the columns (--tcols here, --cols from 1280) so no
	   quote sits alone on a row where it can be helped, and marks the items below the first row (rv-t2 / rv-d2). */
	.reviews { grid-template-columns: repeat(var(--tcols, 3), minmax(0, 1fr)); grid-template-rows: auto auto; column-gap: 40px; row-gap: 0; }
	.reviews > li { display: grid; grid-row: span 2; grid-template-rows: subgrid; }
	.reviews > .rv-t2 { margin-top: var(--group-gap); }
	.review { display: grid; grid-row: span 2; grid-template-rows: subgrid; row-gap: 20px; padding-top: 24px; }
	.rv-co { font-size: 26px; }
}
@media (min-width: 1280px) {
	.reviews { grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr)); }
	.reviews > .rv-t2 { margin-top: 0; }
	.reviews > .rv-d2 { margin-top: var(--group-gap); }
	.reviews--4 .rv-co { font-size: 22px; } /* four across: the smaller names */
}

/* ---------- Learn: steps (main.css) + newest posts (DESIGN.md section 11) ---------- */

.posts-block { margin-top: var(--group-gap); display: grid; gap: 20px; }
.post-card { display: flex; gap: 16px; padding-block: 16px; box-shadow: inset 0 1px 0 var(--line); }
.post-card:last-child { box-shadow: inset 0 1px 0 var(--line), inset 0 -1px 0 var(--line); }
.post-img { flex: none; width: 112px; height: 84px; display: block; overflow: hidden; background: var(--ground-alt); }
.post-img img { width: 100%; height: 100%; object-fit: cover; }
.post-text { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
.post-meta { font-family: var(--font-mono); font-size: 11px; font-weight: 400; letter-spacing: 0.06em; line-height: 1.4; text-transform: uppercase; color: var(--quiet); }
.post-title { font-family: var(--font-sans); font-weight: 300; letter-spacing: -0.02em; font-size: 18px; line-height: 1.3; }
.post-title a { text-decoration: none; }
.post-card:hover .post-title a, .post-title a:focus-visible { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; }
.post-read { display: none; }

@media (min-width: 768px) {

	.posts-block { gap: 48px; }
	.post-list { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 40px; align-items: start; }
	.post-card, .post-card:last-child { flex-direction: column; gap: var(--media-gap); padding-block: 0; box-shadow: none; }
	.post-img { width: 100%; height: auto; aspect-ratio: 416 / 260; }
	.post-title { font-size: 22px; }
	.post-read {
		display: inline-flex; gap: 8px; padding-top: 16px; /* 8 + 16 = 24 above the link */
		font-size: 13px; font-weight: 500; line-height: 1.5; color: var(--sage-deep); text-decoration: none;
	}
	.post-arrow { font-weight: 300; }
}

/* ---------- Closing: custom work + catalogue on forest (DESIGN.md section 11) ---------- */
.closing { background: var(--forest); color: var(--ground); padding-block: var(--section-y); }
.closing .label { color: var(--on-forest); }
.closing .label-rule { background: var(--line-forest); }
.closing-title { margin-top: 20px; font-family: var(--font-sans); font-weight: 300; letter-spacing: -0.02em; font-size: var(--fs-display-closing); line-height: 1.1; }
.closing-row { margin-top: 20px; display: flex; flex-direction: column; gap: 24px; }
.closing-body { color: var(--on-forest); }
.closing .btn { width: 100%; }
.closing-cat { position: relative; margin-top: 40px; padding-top: 24px; display: flex; flex-direction: column; gap: 36px; }
.closing-cat-text { display: flex; flex-direction: column; gap: 12px; }
.closing-cat-title { font-family: var(--font-sans); font-weight: 300; letter-spacing: -0.02em; font-size: 22px; line-height: 1.27; }

@media (min-width: 768px) {
	.closing-title { margin-top: 24px; }
	.closing-row { margin-top: 24px; flex-direction: row; justify-content: space-between; align-items: flex-end; }
	.closing-body { max-width: 560px; }
	.closing .btn { width: auto; flex: none; }
	.closing .btn-accent { padding-inline: 36px; }
	.closing-cat { margin-top: 48px; flex-direction: row; justify-content: space-between; align-items: flex-end; }
	.closing-cat-title { font-size: 26px; }
}
.closing :focus-visible { outline-color: var(--ground); }

/* The blank shield (DESIGN.md section 10; lab station closing-shield): drawn, no image. Below 1024 small above the title;
   from 1024 centred on the 208 px button column (both buttons one width), its bottom on the title's bottom - or, when
   the title row is shorter than the shield, 16 px under the label row (never into it). */
.bshield { display: block; width: 50px; height: 56px; margin-top: 24px; overflow: visible; pointer-events: none; }
.bshield .bs-line { fill: none; stroke: var(--ground); stroke-width: 1.5px; vector-effect: non-scaling-stroke; stroke-linecap: round; stroke-linejoin: round; }
.bshield .bs-hatch line { stroke: var(--ground); stroke-width: 1.3; stroke-dasharray: 100; stroke-dashoffset: 100; }
@media (min-width: 768px) {
	.closing-row .btn, .closing-cat .btn { min-width: 208px; justify-content: center; }
}
@media (min-width: 1024px) {
	.closing > .wrap { position: relative; --bs-w: 80px; --bs-h: 90px; }
	.bshield {
		position: absolute; margin: 0; width: var(--bs-w); height: var(--bs-h);
		right: calc(var(--gutter) + (208px - var(--bs-w)) / 2);
		/* The label row is one 11 px line (1.4); the title sits 24 px under it, one line at 1.1. */
		top: max(calc(11px * 1.4 + 16px), calc(11px * 1.4 + 24px + 1.1 * var(--fs-display-closing) - var(--bs-h)));
	}
}
@media (min-width: 1280px) {
	.closing > .wrap { --bs-w: 100px; --bs-h: 112px; }
}

/* ---------- Tablet 768-1023: desktop layouts, mobile-sized parts where half a column is too narrow ---------- */
@media (min-width: 768px) and (max-width: 1023.98px) {
	/* Featured rows: the mobile row (price and button under the name); the 146 px button slot leaves no text room. */
	.feat-row { align-items: flex-start; gap: 16px; min-height: 0; padding-block: 16px; }
	.feat-thumb { width: 96px; height: 78px; }
	.feat-row-text { padding-right: 0; }
	.feat-row-buy { padding-top: 4px; }
	.feat-row .btn { position: static; transform: none; padding-inline: 16px; }

	/* Collections: the well keeps the board's proportion; faction names at the mobile size fit one line. */
	.panel-well { height: auto; aspect-ratio: 644 / 500; }
	.panel-caption { min-height: 120px; padding: 20px 24px; }
	.fac-name { font-size: 16px; }
	.st-name { font-size: 18px; }
}

/* The lead product name and the faction panel title stay at 26 under the fluid section h2 until it has grown enough:
   at 34 the lead name sat almost level with the h2 at 800 (34 / 37), and the panel title was larger than the phone h2
   (owner, session 33; DESIGN.md section 3). The panel title sits in its dark caption, away from the h2: 34 from 1024.
   The lead name sits right under the h2: 34 from 1280, where the h2 is 42. */
@media (min-width: 1024px) {
	.panel-title { font-size: 34px; }
}
@media (min-width: 1280px) {
	.feat-lead-name { font-size: 34px; }
}
/* The lead's button beside its text only where the lead column has reached its full 644 px: below that a long name
   ("City of Paris Pavise Shields") wrapped beside it, and the price and button sat 31-41 px lower for that product than
   for the others as the lead changed on hover (owner, session 33). Under the price they never move. */
@media (min-width: 1440px) {
	.feat-lead-info { flex-direction: row; justify-content: space-between; align-items: flex-end; }
}

/* ---------- Section spacing (DESIGN.md section 10, "Section spacing - 160"; lab station spacing) ----------
   Each side of the gap between two sections, Featured's bottom to the closing's top, is 160 at 1440 and up, scaling
   with the fluid --section-y (136 at 800, 120 at 375). The hero's bottom, Featured's top and the closing's bottom stay. */
:is(.collections, .play, .compatibility, .testimonials, .learn, .closing) { padding-top: calc(var(--section-y) * 160 / 96); }
:is(.featured, .collections, .play, .compatibility, .testimonials, .learn) { padding-bottom: calc(var(--section-y) * 160 / 96); }
/* A phone: 80 each side (session 55, owner) - 120 left almost a third of the screen empty between two sections. */
@media (max-width: 767.98px) {
	:is(.collections, .play, .compatibility, .testimonials, .learn, .closing) { padding-top: 80px; }
	:is(.featured, .collections, .play, .compatibility, .testimonials, .learn) { padding-bottom: 80px; }
}

/* Scroll picks on a phone (session 60, owner; js/pick.js): no hover, so each panel sticks to the top of the screen while
   its list scrolls past under it, and the scroll picks what it shows. Featured's lead is unboxed so its frame sticks over
   the index too. The crop marks sit 20 px out: the frames stop 20 px down, and a 20 px ground halo hides what passes. */
@media (max-width: 767.98px) {
	.featured-body { display: block; }
	.feat-lead, .feat-lead-info { display: contents; }
	.feat-index { margin-top: 40px; }
	/* Featured's caption (owner, as Collections' title under its panel): the shown product's code line, name and price stick
	   under the frame - the frame's height from its width (335 / 268) plus the media gap; the lead's button stays in place. */
	.feat-lead-text {
		position: sticky; z-index: 2; margin-top: var(--media-gap); padding-bottom: 8px; background: var(--ground);
		top: calc(44px + (100vw - 2 * var(--gutter)) * 268 / 335 + var(--media-gap));
		box-shadow: 0 0 0 20px var(--ground), 0 -24px 0 20px var(--ground);
	}
	.feat-lead-info > .btn, .feat-lead-info > a { margin-top: 24px; }
	/* 24 px clear of the screen's top (owner: the wells' light tops met the edge): the marks 24 down, the frames 44; the
	   ground (Collections: its band) fills the strip above, so what passes under is hidden. */
	.featured .crop-frame, .compat-frame { position: sticky; top: 44px; z-index: 3; /* over the caption's halo: its marks */ box-shadow: 0 0 0 20px var(--ground), 0 -24px 0 20px var(--ground); }
	.feature-panel { position: sticky; top: 24px; z-index: 2; box-shadow: 0 -24px 0 var(--band); }
}
/* A phone turned sideways (Codex audit, session 63): the panels size from the width, so stuck they filled a 375 px tall
   screen and pushed their caption off it. They scroll with the page there, and nothing is picked (pick.js `phone`).
   Orientation, not a height: Safari's bars change the height while scrolling, never the orientation. */
@media (max-width: 767.98px) and (orientation: landscape) {
	.feat-lead-text, .feature-panel { position: static; }
	/* Relative, not static (Codex's final audit L1): the frame holds its crop marks, or they span the section, 20 px out. */
	.featured .crop-frame, .compat-frame { position: relative; top: auto; }
}
/* What a panel shows is marked in its list (session 60, owner): the row tinted edge to edge (12 px past its text), its
   name medium; Featured's lead only its name, while the frame shows its shields. Collections' faction rows have their own. */
.feat-row, .range { transition: background-color 0.2s, box-shadow 0.2s; }
.feat-row.is-shown, .range.is-active { background: var(--ground-alt); box-shadow: -12px 0 0 var(--ground-alt), 12px 0 0 var(--ground-alt); }
.feat-row.is-shown .feat-row-name { font-weight: 500; }
@media (min-width: 768px) { .feat-lead-info.is-shown .feat-lead-name { font-weight: 500; } } /* a phone's stuck caption always names what is shown */
@media (prefers-reduced-motion: reduce) { .feat-row, .range { transition: none; } }

/* ---------- The homepage's own rules (Technical, DESIGN.md section 4) ---------- */
/* Featured: lead | index from 768 - the grid's own ::before (the recipe in main.css), the two cells placed explicitly
   so the rule never pushes one into another row. */
.featured-body::before {
	content: ""; display: none; justify-self: start; align-self: stretch; width: 1px;
	margin-left: calc(var(--col-gap) / -2 - 0.5px); background: var(--line); pointer-events: none;
}
@media (min-width: 768px) {
	.feat-lead { grid-column: 1; grid-row: 1; }
	.feat-index { grid-column: 2; grid-row: 1; }
	.featured-body::before { display: block; grid-column: 2; grid-row: 1; }
}
/* The shield types as ruled cells: forest lines on the band, none before a row's first (2 a row, gap 15; 4 from 768,
   gap 40). Learn's steps from 768 (gap 40). */
.shield-grid > li, .learn .step { position: relative; }
.shield-grid > li:not(:first-child)::before, .learn .step:not(:first-child)::before {
	content: ""; position: absolute; top: 0; bottom: 0; width: 1px; pointer-events: none;
}
.shield-grid > li:not(:first-child)::before { left: -7.5px; background: var(--line-forest); }
.shield-grid > li:nth-child(odd)::before { display: none; }
.learn .step:not(:first-child)::before { display: none; left: -20px; background: var(--line); }
@media (min-width: 768px) {
	.shield-grid > li:not(:first-child)::before { display: block; left: -20px; }
	.shield-grid > li:nth-child(4n + 1)::before { display: none; }
	.learn .step:not(:first-child)::before { display: block; }
}

/* ---------- The slab look and the slow stack (DESIGN.md section 10, "Section overlap"; lab st-overlap.js) ----------
   The look is static, always on: each section an opaque slab with rounded top corners (the one exception to square
   corners) and a shadow above its edge, as strong as the ground under it needs (about 3 CIELAB L* each); the footer
   overlaps the closing. js/motion/stack.js adds .has-stack: the sections hold (sticky) and move up at 75 %. */
/* --fov on both: the footer is outside <main>, and without it its overlap was 0, its corners over the page's cream (owner, session 60). */
#main, .site-footer { --fov: clamp(4.25rem, 9vw, 8.5rem); }
#main { --radius: 22px; }
@media (min-width: 768px) { #main { --radius: 28px; } }
.site-footer { --radius: 22px; }
@media (min-width: 768px) { .site-footer { --radius: 28px; } }
:is(.featured, .play, .compatibility, .learn) { --slab-bg: var(--ground); background: var(--ground); }
.collections { --slab-bg: var(--band); --slab-in: inset 0 -1px 0 var(--line-forest); }
.testimonials { --slab-bg: var(--ground-alt); --slab-in: inset 0 -1px 0 var(--line); }
.closing { --slab-bg: var(--forest); }
.site-footer { --slab-bg: var(--ground); }
.hero { isolation: isolate; } /* its layers stay inside it, never over a slab or its shadow */
:is(.featured, .collections, .testimonials, .closing) { --sh: 0.073; } /* over cream */
:is(.play, .compatibility) { --sh: 0.11; } /* over sage */
.play + .compatibility { --sh: 0.073; } /* over the game's cream */
.learn { --sh: 0.078; } /* over sand */
.site-footer { --sh: 0.38; } /* over forest */
:is(.featured, .collections, .play, .compatibility, .testimonials, .learn, .closing, .site-footer) {
	position: relative; z-index: 1; border-radius: var(--radius) var(--radius) 0 0;
	box-shadow: var(--slab-in, 0 0 #0000), 0 -6px 24px -4px rgb(27 36 31 / var(--sh));
}
.closing { padding-bottom: calc(var(--section-y) + var(--fov)); }
.site-footer { margin-top: calc(var(--fov) * -1); }

/* The stack (script only): each card holds once its top meets the top of the screen, a card taller than the screen
   once its bottom meets the foot (--cs-top); the next card paints over it. A held card moves up, so a same-colour tail
   as deep as the corners fills the next card's corners. */
.has-stack :is(.hero, .featured, .collections, .play, .compatibility, .testimonials, .learn) { position: sticky; top: var(--cs-top, 0px); }
.has-stack :is(.featured, .collections, .play, .compatibility, .testimonials, .learn) {
	box-shadow: var(--slab-in, 0 0 #0000), 0 var(--radius) 0 0 var(--slab-bg), 0 -6px 24px -4px rgb(27 36 31 / var(--sh));
}

/* ---------- The hero's motion (js/motion/hero.js) ---------- */
/* The spec strip's rule, drawn by scaling while the motion runs (its border is the static rule). */
.hero-spec.is-drawn { border-top-color: transparent; }
.spec-rule { position: absolute; left: 0; right: 0; top: -1px; height: 1px; background: var(--line); transform-origin: 0 50%; pointer-events: none; }
/* Section labels typing in (js/motion/labels.js): the block cursor - it takes no width (its negative margin), so
   nothing beside it moves as it steps (CLS 0, rule 4). */
.label-cursor { display: inline-block; width: 0.62em; height: 1.15em; margin: 0 calc(-0.62em - 1px) 0 1px; vertical-align: -0.2em; background: currentColor; opacity: 0.8; }

/* ---------- Printed rules on the homepage (js/motion/lines.js draws them) ---------- */
.learn .step { box-shadow: none; } /* its .rule--top; the product page's steps keep the inset rule */
.closing-cat > .rule { background: var(--line-forest); }
/* Column rules draw top down: their host carries --lnv (0 -> 1); 1 when no script runs. */
:is(.featured-body, .shield-grid > li, .learn .step)::before { transform: scaleY(var(--lnv, 1)); transform-origin: 50% 0; } /* the footer's: main.css */
/* Crossings, from 768 (columns side by side), as the game's corners: a line runs 12 px past the one it meets. */
@media (min-width: 768px) {
	.featured-body::before { margin-top: -12px; } /* the lead | index rule rises 12 px above the index's top line ... */
	.feat-index > .feat-row:first-child > .rule--top { left: -32px; } /* ... which runs left past it (gap 40 / 2 + 12) */
	.learn .step > .rule--top { left: -20px; right: -20px; } /* the steps' top lines join across the 40 px gaps */
	.learn .step:first-child > .rule--top { left: -12px; }
	.learn .step:last-child > .rule--top { right: -12px; }
	.learn .step:not(:first-child)::before { top: -12px; }
}
/* Add to cart confirm (js/motion/featured.js): the "Added" strip slides over the button. */
.add_to_cart_button[data-busy] { position: relative; overflow: hidden; }
.btn-msg { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: var(--forest); color: var(--ground); }
/* Learn's motion (js/motion/learn.js): the first paper may leave past a phone's edge without a sideways scroll; the
   numeral above its paper; the paper is decoration placed by script. */
.learn { overflow-x: clip; }
.learn .step-num { position: relative; z-index: 1; }
.paper {
	position: absolute; z-index: 0; background: #FFFDF9; box-shadow: 0 0 0 1px var(--line), 0 2px 6px rgba(41, 41, 41, 0.06);
	transform-origin: 30% 80%; pointer-events: none;
}
.post-arrow { display: inline-block; } /* so it can move on hover */
/* The footer's wordmark slide in: main.css (every page). */


/* ---------- Play - the shield game (DESIGN.md section 10, sessions 37 and 39; js/play.js, motion/play.js) ---------- */
/* A band of its own between Collections and Compatibility, put in place by js/play.js (no JS, no band). In the slow
   stack it is a card like the others (the stack rules above): cream, over the sage of Collections. */
.play [hidden] { display: none; } /* the theme's img { display: block } would beat the attribute */
/* For a spot question and the roll of arms the main shield steps back (a class: also without motion). */
.play.is-spot .play-decal, .play.is-end .play-decal { opacity: 0; }
@media (prefers-reduced-motion: no-preference) { .play-decal { transition: opacity 0.2s; } }

/* The frame (session 37, second layout - owner: the green mat "too boring", "take inspiration from github"): Universe's
   ruled cells. Hairlines in the ground's own line colour run past each corner by --x and cross there; inside, the shield
   cell | the question cell (stacked on a phone), and a ruled strip along the bottom: the round left, the action right. */
.play-frame { --x: 12px; position: relative; }
/* Raised off the band (session 52, owner: "distance the whole minigame component from the background"): a sheet a shade
   lighter than the ground under a soft layered shadow; the ruled lines are its edges and still cross past its corners. */
.play-frame {
	--card: #FDF8EF; /* half the first step from the ground (#FFFBF4 was too much, owner) */
	/* --fp: how far the sheet has lifted, 0 (the ground's own colour, no shadow) to 1; motion/play.js moves it in the
	   frame's build (owner: "color can change gradually over the whole surface"). Without motion it stays lifted. */
	background: color-mix(in srgb, var(--card) calc(var(--fp, 1) * 100%), var(--ground));
	box-shadow: 0 1px 2px rgb(27 36 31 / calc(0.03 * var(--fp, 1))), 0 8px 20px -8px rgb(27 36 31 / calc(0.06 * var(--fp, 1))), 0 28px 56px -24px rgb(27 36 31 / calc(0.09 * var(--fp, 1)));
}
.play-ln { position: absolute; background: var(--line); pointer-events: none; }
.play-ln--t, .play-ln--b { left: calc(-1 * var(--x)); right: calc(-1 * var(--x)); height: 1px; }
.play-ln--t { top: 0; }
.play-ln--b { bottom: 0; }
.play-ln--l, .play-ln--r { top: calc(-1 * var(--x)); bottom: calc(-1 * var(--x)); width: 1px; }
.play-ln--l { left: 0; }
.play-ln--r { right: 0; }
.play-cells { display: grid; }
@media (min-width: 768px) { .play-cells { grid-template-columns: 1fr 1fr; } }

/* The shield cell: the section's own ground, a fine grid behind the shield that fades out round it and drifts slower
   than the page (js/play.js; it overhangs the cell by 48 px top and bottom for the drift). The shield is never a control. */
.play-stage { position: relative; display: grid; place-items: center; min-height: 300px; overflow: hidden; }
@media (min-width: 768px) { .play-stage { min-height: 360px; } }
@media (min-width: 1024px) { .play-stage { min-height: 440px; } }
/* Depth: two grids drift at their own speeds (js/play.js), the coarse one further back (it overhangs 110 px for its
   200 px of drift), the fine one nearer (60 px). Edge to edge, no fade (owner, session 37). Sized from the cell's width
   (container units): --cols coarse squares across (8 from 1024, 4 below; the fine grid 5 to each), so the vertical
   lines fall exactly on the frame's side and the divider instead of a few px beside them. */
.play-stage { container-type: inline-size; --cols: 4; }
@media (min-width: 1024px) { .play-stage { --cols: 8; } }
.play-depth { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
/* Thin neutral grey (owner: "more subtle, thin grey lines"): --quiet at 14 % (coarse) and 7 % (fine), not the warm tan. */
.play-gridbg {
	--gl: rgb(102 102 102 / 0.14);
	position: absolute; left: 0; right: 0; background-position: 0 0;
	background-image: linear-gradient(var(--gl) 1px, transparent 1px), linear-gradient(90deg, var(--gl) 1px, transparent 1px);
}
.play-gridbg--coarse { top: -110px; bottom: -110px; background-size: calc(100cqw / var(--cols)) calc(100cqw / var(--cols)); }
.play-gridbg--fine { --gl: rgb(102 102 102 / 0.07); top: -60px; bottom: -60px; background-size: calc(100cqw / var(--cols) / 5) calc(100cqw / var(--cols) / 5); }
/* The crowd: smaller shields from other sheets in one row through the middle, placed and faded by js/play.js. */
.play-crowd { position: absolute; inset: 0; }
.play-c { position: absolute; top: 50%; translate: -50% -50%; width: auto; filter: grayscale(0.6); }
.play-c[hidden] { display: none; } /* the theme's img { display: block } would show the ones that do not fit */
.play-shield { position: relative; z-index: 1; perspective: 700px; }
.play-decal { display: block; }
/* The shadow of Muster's shields (Collections' panel: 0 6 8 on 121 px), scaled to this shield (owner, session 37). */
/* The shadow's offset, blur and strength are variables, so a shield change can lift it (js/play.js). */
.play-decal { position: relative; }
/* Session 39: the shield is the lesson now - larger (the set's images are 600 px tall), and its shadow also leans with
   the tilt (--sx, --lift; js/play.js). */
.play-decal img { --sy: 8px; --sb: 11px; --sa: 0.22; display: block; width: auto; height: 180px; filter: drop-shadow(var(--sx, 0px) calc(var(--sy) + var(--lift, 0px)) var(--sb) rgb(20 36 31 / var(--sa))); }
@media (min-width: 768px) { .play-decal img { height: 200px; } }
@media (min-width: 1024px) { .play-decal img { --sy: 10px; --sb: 13px; height: 260px; } }
.play-tilt { display: block; transform-style: preserve-3d; will-change: transform; }
/* Phones and tablets (session 58, owner on Android Chrome: from the game down every section blinked and images below it
   showed as broken while scrolling; gone without this 3D): no 3D space - the lean is for fine pointers only anyway. */
@media (hover: none) and (pointer: coarse) {
	.play-shield { perspective: none; }
	.play-tilt { transform-style: flat; will-change: auto; }
}
.play-pins { position: absolute; inset: 0; pointer-events: none; }
.play-pin { position: absolute; width: 0; height: 0; }
/* Crop marks outside the shield's corners (as on Featured's lead sheet). */
.play-marks { position: absolute; inset: -12px; pointer-events: none; }
.play-marks i { position: absolute; width: 0; height: 0; }
.play-marks .tl { top: 0; left: 0; } .play-marks .tr { top: 0; right: 0; }
.play-marks .bl { bottom: 0; left: 0; } .play-marks .br { bottom: 0; right: 0; }
.play-marks i::before, .play-marks i::after { content: ""; position: absolute; background: var(--quiet); opacity: 0.55; }
.play-marks i::before { width: 14px; height: 1px; top: 0; }
.play-marks i::after { width: 1px; height: 14px; left: 0; }
.play-marks :is(.tl, .bl)::before { right: 6px; } .play-marks :is(.tr, .br)::before { left: 6px; }
.play-marks :is(.tl, .tr)::after { bottom: 6px; } .play-marks :is(.bl, .br)::after { top: 6px; }

/* The question cell, ruled off from the shield: a vertical rule crossing the frame's top line from 768, a horizontal
   one crossing its sides on a phone. */
.play-panel { position: relative; display: grid; gap: 16px; align-content: center; padding: 32px 20px; }
.play-ln--d { top: 0; left: calc(-1 * var(--x)); right: calc(-1 * var(--x)); height: 1px; }
@media (min-width: 768px) {
	.play-panel { padding: 40px 32px; }
	.play-ln--d { top: calc(-1 * var(--x)); bottom: 0; left: 0; right: auto; width: 1px; height: auto; }
}
@media (min-width: 1024px) { .play-panel { padding: 48px 56px; } }
.play-q { margin: 0; font-family: var(--font-sans); font-weight: 300; letter-spacing: -0.02em; font-size: clamp(1.375rem, 1.287rem + 0.376vw, 1.625rem); line-height: 1.25; color: var(--ink); }
.play-say { margin: 0; min-height: 1.6em; font-size: 16px; line-height: 1.6; color: var(--ink); }
.play:not(.is-asking) .play-say:empty { display: none; } /* in a round it keeps its line */
/* Below 1024 the panel is narrow: a long faction name takes the answer to a second line - hold two. */
@media (max-width: 1023.98px) { .play.is-asking .play-say { min-height: 3.2em; } }
.play-body { display: grid; gap: 24px; justify-items: start; }
.play-note { margin: 0; max-width: 44ch; font-size: 16px; line-height: 1.6; color: var(--quiet); }
/* The start screen (session 52, owner: "empty and barebones"): a mono kicker over the heading, the lead, the three kinds
   of question as a ruled index like the answers' (mono number, Poppins title, a quiet example), Start under them. */
.play-kick { margin: 0; font-family: var(--font-mono, ui-monospace, monospace); font-size: 11px; line-height: 1.45; letter-spacing: 0.08em; text-transform: uppercase; color: var(--quiet); }
.play-kick:empty { display: none; }
.play-kick:not(:empty) + .play-q { margin-top: -6px; } /* kicker and heading read as one group */
.play-how { list-style: none; margin: 0; padding: 0; width: 100%; border-top: 1px solid var(--line); }
.play-how > li { display: grid; grid-template-columns: 2em 1fr; column-gap: 20px; row-gap: 2px; padding: 14px 0; border-bottom: 1px solid var(--line); }
.play-how-n { grid-row: span 2; padding-top: 5px; font-family: var(--font-mono, ui-monospace, monospace); font-size: 11px; letter-spacing: 0.06em; color: var(--quiet); }
.play-how-t { font-family: var(--font-sans); font-weight: 400; font-size: 18px; line-height: 1.35; letter-spacing: -0.01em; color: var(--ink); }
.play-how-d { font-size: 15px; line-height: 1.5; color: var(--quiet); }
.play-body > .play-start { margin-top: 8px; }
/* Session 54, the owner's layout: the start screen as one cell of shields over the text. The round's five shields in a
   row (js/play.js lineUp), the first question's in the middle and largest, the rest 0.78 and 0.62 of it; on a phone the
   middle one stands at the round's size and its neighbours run off the edges. The divider runs across at every width (as
   on a phone), the text is centred under the row, and the three kinds stand in ruled columns along the strip (stacked on
   a phone). */
.play.is-idle .play-cells { grid-template-columns: 1fr; }
.play.is-idle .play-shield { visibility: hidden; }
.play.is-idle .play-stage { min-height: 300px; }
/* padding-bottom: the middle shield's crop marks stay clear of the image note (two lines on a phone) */
.play-lineup { --lh: 180px; --lg: 16px; position: absolute; inset: 0; z-index: 1; display: none; align-items: center; justify-content: center; gap: var(--lg); padding-bottom: 40px; pointer-events: none; }
.play.is-idle .play-lineup { display: flex; }
/* Each side takes half of what the middle shield leaves, so the middle one is centred; on a phone they overflow outward. */
.play-lu-side { flex: 1 1 0; min-width: 0; display: flex; align-items: center; gap: var(--lg); }
.play-lu-side--l { justify-content: flex-end; }
.play-lu { position: relative; flex: none; margin: 0; }
.play-lu img { display: block; width: auto; height: calc(var(--lh) * 0.62); filter: drop-shadow(0 6px 9px rgb(20 36 31 / 0.2)); }
.play-lu--1 img { height: calc(var(--lh) * 0.78); }
.play-lu--0 img { height: var(--lh); filter: drop-shadow(0 8px 11px rgb(20 36 31 / 0.22)); }
.play.is-idle .play-ai { left: 50%; translate: -50% 0; width: max-content; text-align: center; }
.play.is-idle .play-panel { min-height: 0 !important; justify-items: center; text-align: center; } /* beats the round's held height */
.play.is-idle .play-body { justify-items: center; }
.play.is-idle .play-ln--d { top: 0; bottom: auto; left: calc(-1 * var(--x)); right: calc(-1 * var(--x)); width: auto; height: 1px; }
.play.is-idle .play-strip { padding: 0; min-height: 0; }
.play.is-idle .play-strip > :is(.play-score, .play-terms, .play-actions) { display: none; }
.play-strip .play-how { border-top: 0; }
.play-strip .play-how > li { padding: 16px 20px; border-bottom: 0; text-align: left; }
.play-strip .play-how > li + li { border-top: 1px solid var(--line); }
.play-ln--k1, .play-ln--k2 { display: none; top: calc(-1 * var(--x)); bottom: calc(-1 * var(--x)); width: 1px; }
.play-ln--k1 { left: calc(100% / 3); }
.play-ln--k2 { left: calc(200% / 3); }
@media (min-width: 768px) {
	.play.is-idle .play-stage { min-height: 320px; }
	.play-lineup { --lh: min(240px, 25cqw); --lg: clamp(16px, 2.6cqw, 36px); padding-bottom: 28px; }
	.play.is-idle .play-panel { padding-bottom: 44px; }
	.play.is-idle .play-ln--k1, .play.is-idle .play-ln--k2 { display: block; }
	.play-strip .play-how { display: grid; grid-template-columns: repeat(3, 1fr); }
	.play-strip .play-how > li { padding: 24px 32px 28px; }
	.play-strip .play-how > li + li { border-top: 0; }
}
@media (min-width: 1024px) { .play.is-idle .play-stage { min-height: 360px; } }
/* A phone (session 60, owner: the side shields ran into the frame's edges): three shields, the middle one and its
   neighbours, sized to the stage and 24 px clear of its sides; the outer two stay out. */
@media (max-width: 767.98px) {
	.play-lineup { --lh: min(180px, 36cqw); padding-inline: 24px; }
	.play-lu:not(.play-lu--0, .play-lu--1) { display: none; }
}
/* The picker as an index (owner: "do something with that one too"), like Featured's index and the faction list:
   hairlines between rows, a small mono key, the faction name in Poppins 300; on hover the name moves 8 px right.
   Answered: the right row in sage-deep with a mono "Right" at its end, a wrong pick struck through in grey. */
.play-opts { list-style: none; margin: 0; padding: 0; width: 100%; border-top: 1px solid var(--line); }
.play-opts > li { border-bottom: 1px solid var(--line); }
.play-opt {
	display: flex; align-items: center; gap: 20px; width: 100%; min-height: 64px; padding: 14px 12px;
	border: 0; border-radius: 0; background: none; color: var(--ink);
	font-family: var(--font-sans); font-weight: 300; font-size: 19px; line-height: 1.35; letter-spacing: -0.01em;
	text-align: left; cursor: pointer; transition: color 150ms, background-color 200ms;
}
@media (min-width: 1024px) { .play-opt { min-height: 72px; font-size: 22px; } }
.play-key, .play-mark { flex: none; font-family: var(--font-mono, ui-monospace, monospace); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--quiet); }
/* Answers that read as buttons (session 52, owner: the picker looked like a list of links): the letter in a small square,
   taller rows, the name larger; on hover or keyboard focus the row tints and the square fills sage. */
.play-key { display: grid; place-items: center; width: 32px; height: 32px; font-size: 12px; color: var(--ink); box-shadow: inset 0 0 0 1px var(--line); transition: background-color 200ms, color 200ms, box-shadow 200ms; }
.play-name { flex: 1; transition: transform 250ms cubic-bezier(0.2, 0.7, 0.2, 1); }
.play-mark { min-width: 4.5em; text-align: right; color: var(--sage-deep); }
.play-mark:empty { display: none; } /* the options make way for the lesson on an answer, so it stays empty; it squeezed the names */
.play-opt:hover:not(:disabled) .play-name { transform: translateX(8px); }
.play-opt:is(:hover, :focus-visible):not(:disabled) { background-color: rgb(88 115 100 / 0.07); }
.play-opt:is(:hover, :focus-visible):not(:disabled) .play-key { background-color: var(--sage); color: var(--ground); box-shadow: inset 0 0 0 1px var(--sage); }
.play-opt:active:not(:disabled) { background-color: rgb(88 115 100 / 0.13); }
.play-opt:disabled { cursor: default; }
.play-opt:disabled { color: var(--quiet); }
.play-slash { margin-inline: 0.04em; color: var(--quiet); } /* mono spaces were a full letter wide */
.play-total { margin: 0; font-family: var(--font-mono, ui-monospace, monospace); font-weight: 300; font-size: var(--fs-display-numeral); letter-spacing: -0.04em; line-height: 1; color: var(--ink); }

/* Session 39: reading the arms. Callouts: a dot on each part, a line to its label beside the shield (from 1280); below
   that, numbered dots and the labels listed in the panel. Labels in the mono, the heraldic name with a swatch. */
.play-co { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 2; pointer-events: none; overflow: visible; }
.play-co line { stroke: var(--ink); stroke-width: 1; }
.play-dot { fill: var(--card); stroke: var(--ink); stroke-width: 1; }
.play-core { fill: var(--ink); }
@media (max-width: 1279.98px) { .play-dot { fill: var(--ink); stroke: var(--card); stroke-width: 2; } }
.play-num { font-family: var(--font-mono, ui-monospace, monospace); font-size: 11px; fill: var(--card); }
.play-labels { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.play-lab { font-family: var(--font-mono, ui-monospace, monospace); font-size: 11px; line-height: 1.45; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink); }
.play-lab--l, .play-lab--r { position: absolute; }
.play-lab--l { text-align: right; }
.play-lab-n { display: none; }
.play-lab-k, .play-lab-en { display: block; color: var(--quiet); }
.play-lab-v { display: block; margin-top: 3px; }
.play-lab-v i { display: inline-block; width: 11px; height: 11px; margin-right: 7px; vertical-align: -1px; box-shadow: inset 0 0 0 1px rgb(41 41 41 / 0.3); }
.play-lab-v i.is-ermine { background: radial-gradient(circle at 50% 45%, #1f1f1f 0 1.6px, transparent 2px) #f2f0ea !important; }
.play-lab-en { margin-top: 2px; text-transform: none; letter-spacing: 0.02em; }
.play-legend { display: grid; width: 100%; border-top: 1px solid var(--line); }
.play-legend.screen-reader-text { width: 1px; } /* from 1280: for screen readers only (the stage is aria-hidden) */
.play-lab--row { display: grid; grid-template-columns: 24px 1fr; padding: 10px 0; border-bottom: 1px solid var(--line); }
.play-lab--row .play-lab-n { display: block; grid-row: span 3; }
/* The note on the images: the set was cleaned and enlarged for the game; the printed decal is the reference (owner). */
.play-ai { position: absolute; left: 16px; bottom: 12px; z-index: 2; margin: 0; max-width: calc(100% - 32px); font-family: var(--font-mono, ui-monospace, monospace); font-size: 10px; line-height: 1.4; letter-spacing: 0.06em; text-transform: uppercase; color: var(--quiet); }
/* The lesson: whose arms, when, the blazon (Lora italic: the heralds' own words, quoted), a line of history, the source. */
.play-lesson { display: grid; gap: 6px; width: 100%; }
.play-who { margin: 0; font-family: var(--font-sans); font-weight: 300; font-size: clamp(1.5rem, 1.3rem + 0.8vw, 2rem); line-height: 1.2; letter-spacing: -0.01em; color: var(--ink); }
.play-meta { margin: 0; font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; line-height: 1.45; letter-spacing: 0.06em; text-transform: uppercase; color: var(--quiet); }
.play-blazon { margin-top: 18px; padding: 16px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.play-blazon span { font-family: var(--font-mono, ui-monospace, monospace); font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--quiet); }
.play-blazon p { margin: 8px 0 0; font-family: var(--font-serif, "Lora", serif); font-style: italic; font-size: clamp(1.125rem, 1.05rem + 0.3vw, 1.375rem); line-height: 1.45; color: var(--ink); }
.play-story { margin: 14px 0 0; max-width: 54ch; font-size: 16px; line-height: 1.6; color: var(--ink); }
.play-src { justify-self: start; margin-top: 8px; }
.play.is-open .play-say { font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--sage-deep); }
/* Spot: three shields side by side, lettered; the main shield steps back. */
.play-trio { position: absolute; inset: 0; z-index: 1; display: flex; align-items: center; justify-content: center; gap: clamp(16px, 4cqw, 40px); pointer-events: none; }
.play-trio-s, .play-roll-s { margin: 0; display: grid; justify-items: center; gap: 10px; }
.play-trio-s img { height: 130px; width: auto; filter: drop-shadow(0 8px 11px rgb(20 36 31 / 0.22)); }
@media (min-width: 1024px) { .play-trio-s img { height: 170px; } }
.play-trio-s figcaption, .play-roll-s figcaption { font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink); }
/* The panel's content starts at the top, so the question stays put when the arms open below it. */
.play.is-asking .play-panel, .play.is-open .play-panel, .play.is-end .play-panel { align-content: start; }
/* One screen (session 56, owner: "the whole game frame to fit the screen" - on a phone the shield and Next were a screen
   and a half apart): in a round the frame is never taller than the screen under the nav (js/play.js scrolls the shield to
   72 px from the top). The strip (progress, Next) keeps its place at the foot; the question and the answer line stay,
   and a lesson longer than the space scrolls in its own box (the panel itself keeps its overhanging rule). From 768 the
   cells take the panel's held height (--held, js/play.js) and shrink from it to fit.
   A phone is sized for Safari's page with its bars shown (about 660 tall on an iPhone): in a round the shield cell is 200
   (the shield 150), the answers 48 tall, the panel's padding 20, the strip two rows - the round and the button, then the
   terms and the sheet link. Below 640 tall (an iPhone SE or mini in Safari) the frame keeps its natural height: the three
   answers would not all show. From 768 the same below 600 tall (Codex audit, session 63: a phone turned sideways,
   780x360, left the answers a 22 px box; the three answers fit from 600 at 780 / 1024 / 1280 wide). */
@media (min-width: 768px) and (min-height: 600px), (min-height: 640px) {
	.play:not(.is-idle) .play-frame { display: flex; flex-direction: column; max-height: calc(100svh - 80px); }
	.play:not(.is-idle) .play-cells { flex: 0 1 var(--held, auto); min-height: 0; grid-template-rows: auto minmax(0, 1fr); }
	.play:not(.is-idle) .play-strip { flex: none; }
	.play:not(.is-idle) .play-panel { min-height: 0 !important; display: flex; flex-direction: column; }
	/* Its padding holds what hangs out of the lesson - focus rings at the sides, the source link's 44 px hit area (12 px,
	   .text-link) at the foot - and its margin gives that back, so the held height still fits without a scroll. */
	.play:not(.is-idle) .play-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 0 6px 12px; margin: 0 -6px -12px; }
	.play:not(.is-idle) .play-opt:focus-visible { outline-offset: -3px; } /* inside the box, which clips */
}
@media (min-width: 768px) { .play:not(.is-idle) .play-cells { grid-template-rows: minmax(0, 1fr); } }
/* Short and wide (a phone turned sideways, session 63): the frame keeps its natural height, the panel the tallest
   lesson's, so the shield's cell sticks under the nav, one screen tall, and the shield stays beside the question, the
   answers and the lesson while they scroll. */
@media (min-width: 768px) and (max-height: 599.98px) {
	.play:not(.is-idle) .play-stage { position: sticky; top: 80px; align-self: start; height: calc(100svh - 80px); min-height: 0; }
}
@media (max-width: 767.98px) and (min-height: 640px) {
	/* Exactly one screen through the round: a frame that grew when the arms opened re-measured the slow stack (stack.js),
	   and the held section moved. */
	.play:not(.is-idle) .play-frame { height: calc(100svh - 80px); }
	.play:not(.is-idle) .play-cells { flex-grow: 1; }
	.play:not(.is-idle) .play-stage { min-height: 200px; padding-bottom: 32px; } /* the shield clear of the AI note */
	.play:not(.is-idle) .play-decal img { height: 150px; }
	.play:not(.is-idle) .play-trio-s img { height: 110px; }
	.play:not(.is-idle) .play-panel { padding-block: 20px; }
	.play.is-asking .play-say:empty { display: none; }
	.play:not(.is-idle) .play-opt { min-height: 48px; padding-block: 6px; }
	.play:not(.is-idle) .play-strip { display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: 45px 30px; grid-template-areas: "round act" "terms link"; align-items: center; gap: 8px 16px; padding-block: 12px; }
	.play:not(.is-idle) .play-actions { display: contents; }
	.play:not(.is-idle) .play-score { grid-area: round; }
	.play:not(.is-idle) .play-terms { grid-area: terms; }
	.play:not(.is-idle) .play-actions > button { grid-area: act; }
	.play:not(.is-idle) .play-actions > a { grid-area: link; justify-self: end; }
}
@media (min-width: 1280px) { .play.is-open .play-crowd { opacity: 0 !important; transition: opacity 0.3s; } } /* the labels need the room */
.play.is-spot .play-crowd, .play.is-end .play-crowd, .play.is-spot .play-marks, .play.is-end .play-marks { opacity: 0 !important; }
/* The roll of arms: the round's five in a row, each marked. */
.play-roll { position: absolute; inset: 0; z-index: 1; display: none; align-items: center; justify-content: center; flex-wrap: wrap; gap: 14px; padding: 24px; pointer-events: none; }
.play.is-end .play-roll { display: flex; }
.play-roll-s img { height: 56px; width: auto; filter: drop-shadow(0 6px 8px rgb(20 36 31 / 0.2)); }
@media (min-width: 768px) { .play-roll-s img { height: 84px; } }
.play-roll { flex-wrap: nowrap; padding-bottom: 44px; } /* one row, clear of the image note */
@media (min-width: 1024px) { .play-roll-s img { height: 110px; } }
.play-roll-s:not(.is-right) img { filter: grayscale(0.85) opacity(0.55); }
.play-roll-s:not(.is-right) figcaption { color: var(--quiet); }
.play-roll-s.is-right figcaption { color: var(--sage-deep); }
.play-rolls { list-style: none; margin: 0; padding: 0; width: 100%; border-top: 1px solid var(--line); }
.play-rolls li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.play-rolls-n { flex: 1 1 14em; font-weight: 300; font-size: 17px; }
.play-rolls-m { font-family: var(--font-mono, ui-monospace, monospace); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--quiet); min-width: 4.5em; }
/* The strip's terms: every one met this round, the newest in sage. */
.play-terms { flex: 1 1 0; min-width: 0; display: flex; align-items: center; gap: 10px; font-family: var(--font-mono, ui-monospace, monospace); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; }
.play-terms-k { flex: none; color: var(--quiet); }
.play-terms-row { display: flex; gap: 6px; min-width: 0; overflow: hidden; -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 48px), transparent); mask-image: linear-gradient(90deg, #000 calc(100% - 48px), transparent); }
.play-chip { flex: none; }
.play-chip { padding: 5px 7px; border: 1px solid var(--line); color: var(--ink); }
.play-chip.is-new { border-color: var(--sage); color: var(--sage-deep); }

/* The strip: ruled off by a line crossing the frame's sides; the round left (a data position), the action right. It
   holds the button's height in every state, so nothing moves when the answer comes. On a phone the round sits above
   the actions, the button first. */
.play-strip { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: 12px 32px; min-height: 77px; padding: 16px 20px; }
.play-ln--s { top: 0; left: calc(-1 * var(--x)); right: calc(-1 * var(--x)); height: 1px; }
.play-score { font-family: var(--font-mono, ui-monospace, monospace); font-size: 13px; letter-spacing: 0.02em; color: var(--ink); }
/* The round as five small shields (session 52): outlines to come (line colour), the one asked (ink), each answered one
   its own shield - greyed if missed, over a short sage bar if right. */
.play-pips { display: flex; align-items: flex-end; gap: 12px; }
.play-pip { position: relative; display: grid; place-items: end center; width: 26px; height: 32px; padding-bottom: 5px; }
.play-pip svg { width: 22px; height: 25px; overflow: visible; }
.play-pip path { fill: none; stroke: var(--line); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.play-pip.is-now path { stroke: var(--ink); }
.play-pip img { width: auto; max-width: 26px; height: 25px; object-fit: contain; filter: drop-shadow(0 2px 3px rgb(20 36 31 / 0.18)); }
.play-pip.is-missed img { filter: grayscale(0.85) opacity(0.5); }
.play-pip.is-right::after { content: ""; position: absolute; left: 4px; right: 4px; bottom: 0; height: 2px; background: var(--sage); }
@media (prefers-reduced-motion: no-preference) {
	.play-pip img.is-new { animation: play-pip-in 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) both; }
	@keyframes play-pip-in { from { opacity: 0; scale: 0.5; translate: 0 6px; } }
}
.play-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 16px 32px; margin-left: auto; min-height: 45px; }
@media (min-width: 768px) { .play-strip { padding: 16px 32px; } }
@media (max-width: 767.98px) {
	.play-strip { flex-direction: column; align-items: flex-start; }
	.play-score:empty { display: none; }
	.play-terms { width: 100%; flex: none; }
	.play-terms:empty { display: none; }
	.play-actions { margin-left: 0; gap: 16px 24px; }
	.play-actions .btn { order: -1; }
}
