/* ==========================================================================
   Bluefort Stats Strip — Figma 6497:32

   A row of statistics whose numbers count up, separated by hairline rules.

   Every rule that names a package class is written TWICE in one selector list:
   the bare form and the `#top #wrap_all` form. Enfold scopes everything to
   #top, and its generated colour CSS sits at (2,1,1) — two ids — so a single
   `#top` prefix loses. Both halves, always, including the defensive ones.

   No !important. No margin or padding shorthand: a shorthand in a type rule
   outranks the longhand rules that own the rhythm, and the failure presents as
   "the rule isn't working".

   1. Registered properties
   2. Tokens
   3. Cap-height trimming
   4. The strip
   5. Items and rules
   6. Type
   7. The counting numbers
   8. Responsive
   ========================================================================== */

/* 1. Registered properties ==================================================
   The trim tokens MUST be registered: getComputedStyle().getPropertyValue()
   on an unregistered custom property returns the literal calc() token stream,
   so parseFloat(...) || 0 is 0 — and every harness that converts border boxes
   to cap boxes silently stops converting.

   --bf-fb is the ONE fallback switch (§7.2): 0 where `text-box` is supported,
   1 where it is not. It is deliberately project-wide, not package-scoped, so
   a second Bluefort package shares the same switch.
   ========================================================================== */

@property --bf-fb {
	syntax: "<number>";
	inherits: true;
	initial-value: 0;
}

@property --bf-stats-tt {
	syntax: "<length>";
	inherits: false;
	initial-value: 0px;
}

@property --bf-stats-tb {
	syntax: "<length>";
	inherits: false;
	initial-value: 0px;
}

/* 2. Tokens ================================================================
   Colour and margin tokens are re-emitted INLINE by the render. The values
   below are their fallbacks and must match it value for value — asserted by
   preview/validate.php.
   ========================================================================== */

.bf-stats,
#top #wrap_all .bf-stats {
	--bf-fb: 0;

	/* Re-emitted inline by the render. */
	--bf-stats-mt: 0px;
	--bf-stats-mb: 0px;
	--bf-stats-value-c: #024EA3;
	--bf-stats-label-c: #1C1C1C;
	--bf-stats-rule-c: #E8E8E8;

	/* The responsive ramp reads the margin into this and never re-declares
	   the token itself — re-declaring it would discard the panel value. */
	--bf-stats-mt-now: var(--bf-stats-mt);

	--bf-stats-font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

	/* 64px across the design's 1408px frame — 4.5455% of it — so the number
	   keeps the design's proportion to its own column at EVERY container
	   width. Sized against the viewport instead, the type stayed 64px inside
	   Enfold's narrower container while the columns shrank, and "7 Gates"
	   wrapped onto two lines on a page where nothing looked wrong at the
	   design's own width. A size that ramps is a clamp(), not a media query —
	   there is then no ramp to keep in step. */
	--bf-stats-value-fs: clamp(2.25rem, 4.5455cqi, 4rem);
	--bf-stats-value-lh-ratio: 1.0625;   /* 68 / 64 */
	--bf-stats-value-ls: -0.05em;        /* −3.2px at 64px */

	--bf-stats-label-fs: 1rem;
	--bf-stats-label-lh-ratio: 1.5;      /* 24 / 16 */

	/* 80 across a 1408px frame, on both sides of a 1px rule. A PERCENTAGE, not
	   a length: a grid's percentage column-gap resolves against the grid's own
	   content box, so the design's proportions hold at every container width.
	   Written as a length it takes a larger share of Enfold's narrower
	   container than of the design's frame — which squeezed the columns until
	   a number wrapped, on a page where nothing looked wrong at the design's
	   own width. */
	--bf-stats-gap: 4.6818%;
	--bf-stats-row-gap: 48px;
	--bf-stats-stack: 32px;              /* number cap box → label cap box */

	/* How far a number may lean into the gap before it wraps instead. The
	   design's own longest value fills its column to the pixel (Figma's "7
	   Gates" is 231px in a 231px frame), and the designer let the first label
	   overhang by 9px each side for the same reason, so a little headroom is
	   the design's behaviour rather than a licence. */
	--bf-stats-value-bleed: 8px;
	--bf-stats-rule-w: 1px;

	/* Inter, read from the variable font: upem 2048, hhea ascender 1984,
	   descender −494, cap height 1490, USE_TYPO_METRICS set. Design constants
	   — never overridden by a harness. */
	--bf-stats-asc: 0.96875;
	--bf-stats-desc: 0.2412109375;
	--bf-stats-cap: 0.7275390625;

	/* Unregistered copies the trim reads. A harness overrides THESE to force
	   its own fallback pass; the constants above also feed rhythm values, so
	   overriding them wholesale would move the design and report the
	   difference as a trim bug. */
	--bf-stats-asc-t: var(--bf-stats-asc);
	--bf-stats-desc-t: var(--bf-stats-desc);
	--bf-stats-cap-t: var(--bf-stats-cap);
}

/* 3. Cap-height trimming ===================================================
   Figma positions this strip by CAP boxes: the 64px number is 47px tall, not
   68px, and the 32px gap below it is cap box to last baseline.

   `text-box: trim-both cap alphabetic` does that natively. Everywhere else the
   same result comes from negative block margins computed from the font
   metrics — ratios, not pixels, so they track the clamp() above.

     half-leading = (line box − (asc + desc) × font-size) ÷ 2
     trim-top     = half-leading + (asc − cap) × font-size
     trim-bottom  = half-leading + desc × font-size

   Note this is the full formula, not (lh − fs × cap) ÷ 2: the short form is
   only equal to it when asc − desc happens to equal cap.
   ========================================================================== */

.bf-stats .bf-stats-t,
#top #wrap_all .bf-stats .bf-stats-t {
	/* A block container with inline content — never a flex row, a list or a
	   padded box. The property trims an element's own inline formatting
	   context; a container of block boxes has none, so the declaration is a
	   silent no-op in one branch while the fallback's margins still apply. */
	display: block;

	--bf-stats-lead: calc((var(--bf-stats-lh) - (var(--bf-stats-asc-t) + var(--bf-stats-desc-t)) * var(--bf-stats-fs)) / 2);
	--bf-stats-tt: calc(var(--bf-fb) * (var(--bf-stats-lead) + (var(--bf-stats-asc-t) - var(--bf-stats-cap-t)) * var(--bf-stats-fs)));
	--bf-stats-tb: calc(var(--bf-fb) * (var(--bf-stats-lead) + var(--bf-stats-desc-t) * var(--bf-stats-fs)));

	text-box-trim: trim-both;
	text-box-edge: cap alphabetic;
	text-box: trim-both cap alphabetic;

	margin-block-start: calc(-1 * var(--bf-stats-tt));
	margin-block-end: calc(-1 * var(--bf-stats-tb));
}

/* `@supports not (shorthand)` is not "no support": `text-box` shipped as the
   two longhands first, so a browser that is ALREADY trimming answers true to
   `not (text-box: …)` and would get the fallback on top of a trim that has
   already happened. Guard on both. One declaration in the block, so a harness
   can undo exactly one thing to force the other branch. */
@supports not ((text-box: trim-both cap alphabetic) or (text-box-trim: trim-both)) {

	.bf-stats,
	#top #wrap_all .bf-stats {
		--bf-fb: 1;
	}
}

/* 4. The strip and its rules ==============================================
   The dividers are real grid items in 1px tracks, not pseudo-elements. That
   makes their position fall out of the grid instead of being restated as an
   offset — and an offset cannot be written at all once the gap is a
   percentage, because a percentage on the divider would resolve against the
   ITEM's width rather than the container's.

   A divider is never a border: a border would add to the item's width and push
   the columns off the design's own numbers.
   ========================================================================== */

.bf-stats,
#top #wrap_all .bf-stats {
	display: grid;

	/* So the number above can size itself against THIS box rather than the
	   viewport. A container cannot query itself, and nothing here does — the
	   cqi is read by the children. */
	container-type: inline-size;

	grid-template-columns: minmax(0, 1fr);
	column-gap: var(--bf-stats-gap);
	row-gap: var(--bf-stats-row-gap);
	align-items: stretch;
	margin-top: var(--bf-stats-mt-now);
	margin-bottom: var(--bf-stats-mb);
	font-family: var(--bf-stats-font);
}

/* One stat is one column and no divider. Stated rather than inherited from the
   block above, so the --n* set the render can emit and the set the stylesheet
   answers for are the same list, checkable in both directions. */
.bf-stats--n1,
#top #wrap_all .bf-stats--n1 {
	grid-template-columns: minmax(0, 1fr);
}

.bf-stats--n2,
#top #wrap_all .bf-stats--n2 {
	grid-template-columns: repeat(1, minmax(0, 1fr) var(--bf-stats-rule-w)) minmax(0, 1fr);
}

.bf-stats--n3,
#top #wrap_all .bf-stats--n3 {
	grid-template-columns: repeat(2, minmax(0, 1fr) var(--bf-stats-rule-w)) minmax(0, 1fr);
}

.bf-stats--n4,
#top #wrap_all .bf-stats--n4 {
	grid-template-columns: repeat(3, minmax(0, 1fr) var(--bf-stats-rule-w)) minmax(0, 1fr);
}

/* Five and six wrap to three across rather than shrinking the type further. */
.bf-stats--n5,
#top #wrap_all .bf-stats--n5 {
	grid-template-columns: repeat(2, minmax(0, 1fr) var(--bf-stats-rule-w)) minmax(0, 1fr);
}

.bf-stats--n6,
#top #wrap_all .bf-stats--n6 {
	grid-template-columns: repeat(2, minmax(0, 1fr) var(--bf-stats-rule-w)) minmax(0, 1fr);
}

/* 5. Items and rules ======================================================= */

.bf-stats .bf-stats__item,
#top #wrap_all .bf-stats .bf-stats__item {
	display: flex;
	flex-direction: column;
	align-items: center;

	/* Top, not centre. The design draws four items of identical height, so
	   centring and top-aligning look the same there — but the client's labels
	   will not all be the same number of lines, and centring then floats each
	   number to its own height. A number read off the design's own content is
	   a conditional, not a constant. */
	justify-content: flex-start;
	row-gap: var(--bf-stats-stack);
	text-align: center;
	overflow-wrap: break-word;
}

.bf-stats .bf-stats__rule,
#top #wrap_all .bf-stats .bf-stats__rule {
	display: block;
	width: var(--bf-stats-rule-w);
	background-color: var(--bf-stats-rule-c);
}

/* Dividers off keeps the geometry and drops the ink: the spacing between the
   columns is the design's whether or not a line is drawn in it, so turning
   them off must not reflow the strip. */
.bf-stats--norules .bf-stats__rule,
#top #wrap_all .bf-stats--norules .bf-stats__rule {
	visibility: hidden;
}

/* A divider that would start a row is display:none — removed from grid
   placement entirely, so the item after it lands in the first track. Every
   rule below is (0,2,0), so source order decides, and the blocks run
   widest-first. preview/validate.php asserts this set and the --n* set match
   in both directions. */
.bf-stats--n5 .bf-stats__rule--3,
#top #wrap_all .bf-stats--n5 .bf-stats__rule--3,
.bf-stats--n6 .bf-stats__rule--3,
#top #wrap_all .bf-stats--n6 .bf-stats__rule--3 {
	display: none;
}

/* 6. Type ================================================================== */

.bf-stats .bf-stats__value,
#top #wrap_all .bf-stats .bf-stats__value {
	--bf-stats-fs: var(--bf-stats-value-fs);
	--bf-stats-lh: calc(var(--bf-stats-value-lh-ratio) * var(--bf-stats-fs));

	margin-inline-start: 0;
	margin-inline-end: 0;
	padding-block-start: 0;
	padding-block-end: 0;
	padding-inline-start: 0;
	padding-inline-end: 0;
	max-width: 100%;
	font-family: var(--bf-stats-font);
	font-size: var(--bf-stats-fs);
	font-weight: 600;
	font-style: normal;
	line-height: var(--bf-stats-lh);
	letter-spacing: var(--bf-stats-value-ls);
	color: var(--bf-stats-value-c);
	text-align: center;

	/* Shrink-to-fit, allowed to lean a little into the gap. Left at the
	   column's width the design's longest value is a sub-pixel away from
	   wrapping; allowed to overhang without limit it would push past the last
	   column and give the page a horizontal scrollbar. */
	width: max-content;
	max-width: calc(100% + 2 * var(--bf-stats-value-bleed));
}

.bf-stats .bf-stats__label,
#top #wrap_all .bf-stats .bf-stats__label {
	--bf-stats-fs: var(--bf-stats-label-fs);
	--bf-stats-lh: calc(var(--bf-stats-label-lh-ratio) * var(--bf-stats-fs));

	margin-inline-start: 0;
	margin-inline-end: 0;
	padding-block-start: 0;
	padding-block-end: 0;
	padding-inline-start: 0;
	padding-inline-end: 0;
	max-width: 100%;
	font-family: var(--bf-stats-font);
	font-size: var(--bf-stats-fs);
	font-weight: 400;
	font-style: normal;
	line-height: var(--bf-stats-lh);
	letter-spacing: normal;
	color: var(--bf-stats-label-c);
	text-align: center;
}

/* Enfold underlines and border-bottoms every link inside #top #wrap_all. */
.bf-stats .bf-stats__label a,
#top #wrap_all .bf-stats .bf-stats__label a {
	color: inherit;
	text-decoration: underline;
	border-bottom-width: 0;
	border-bottom-style: none;
}

/* 7. The counting numbers ==================================================
   Tabular figures and an inline-block box, so the script can pin the width to
   the FINAL figure before the count starts: 0 … 30 is never wider than 30, so
   nothing beside it moves while the number changes. The span's line-height is
   the parent's, so the line box — and therefore the cap box — is unchanged.
   ========================================================================== */

.bf-stats .bf-stats__num,
#top #wrap_all .bf-stats .bf-stats__num {
	display: inline-block;
	font-variant-numeric: tabular-nums;
	font-feature-settings: "tnum" 1;
	text-align: center;
	white-space: pre;
}

/* 8. Responsive ============================================================
   The ramp reads the inline margin token into --bf-stats-mt-now and never
   re-declares the token itself. With "Scale the top margin" off, the render
   omits .bf-stats--scale and none of these can match.
   ========================================================================== */

@media (max-width: 1199px) {

	.bf-stats--scale,
	#top #wrap_all .bf-stats--scale {
		--bf-stats-mt-now: calc(var(--bf-stats-mt) * 0.75);
	}
}

@media (max-width: 899px) {

	.bf-stats--scale,
	#top #wrap_all .bf-stats--scale {
		--bf-stats-mt-now: calc(var(--bf-stats-mt) * 0.6);
	}
}

/* Two across. Every divider that now starts a row goes; the one the desktop
   block hid comes back. Same specificity throughout, later in the file, so
   source order decides. The lower bound keeps this block out of the
   single-column range below, where it would win the same way against the rule
   that hides every divider. */
@media (min-width: 701px) and (max-width: 1100px) {

	.bf-stats--n3,
	#top #wrap_all .bf-stats--n3,
	.bf-stats--n4,
	#top #wrap_all .bf-stats--n4,
	.bf-stats--n5,
	#top #wrap_all .bf-stats--n5,
	.bf-stats--n6,
	#top #wrap_all .bf-stats--n6 {
		grid-template-columns: repeat(1, minmax(0, 1fr) var(--bf-stats-rule-w)) minmax(0, 1fr);
	}

	.bf-stats--n5 .bf-stats__rule--3,
	#top #wrap_all .bf-stats--n5 .bf-stats__rule--3,
	.bf-stats--n6 .bf-stats__rule--3,
	#top #wrap_all .bf-stats--n6 .bf-stats__rule--3 {
		display: block;
	}

	.bf-stats--n3 .bf-stats__rule--2,
	#top #wrap_all .bf-stats--n3 .bf-stats__rule--2,
	.bf-stats--n4 .bf-stats__rule--2,
	#top #wrap_all .bf-stats--n4 .bf-stats__rule--2,
	.bf-stats--n5 .bf-stats__rule--2,
	#top #wrap_all .bf-stats--n5 .bf-stats__rule--2,
	.bf-stats--n5 .bf-stats__rule--4,
	#top #wrap_all .bf-stats--n5 .bf-stats__rule--4,
	.bf-stats--n6 .bf-stats__rule--2,
	#top #wrap_all .bf-stats--n6 .bf-stats__rule--2,
	.bf-stats--n6 .bf-stats__rule--4,
	#top #wrap_all .bf-stats--n6 .bf-stats__rule--4 {
		display: none;
	}
}

/* One across. No vertical rule has anywhere to go. */
@media (max-width: 700px) {

	.bf-stats,
	#top #wrap_all .bf-stats {
		grid-template-columns: minmax(0, 1fr);
		row-gap: 40px;
	}

	.bf-stats .bf-stats__rule,
	#top #wrap_all .bf-stats .bf-stats__rule {
		display: none;
	}
}
