/* project-single.css — the stat rail of the project single template (`elementor_library` 877).
 *
 * Hares: *"I realized that if you leave the Stat Pages all blank, we still are left off with the divider lines
 * between them. Those should be hidden as well if all are left blank."*
 *
 * ── ⛔ THE CONSTRAINT, BEFORE ANYTHING ELSE: THE HAIRLINE ONLY. NEVER THE HEIGHT ────────────────
 * These rules remove the DIVIDER and nothing else. An empty stat row keeps its own 24px padding and
 * stays in the layout, exactly as it does today. That is not an oversight to be tidied up later —
 * it is `docs/DECISIONS.md` **M34(c)** (Hares, 2026-08-14): *empty slots render **blank**, no
 * collapse logic*. `scripts/pages/work-360/stat_box.mjs` exists solely to keep the number honest and
 * measured it on 2026-08-14: a filled stat 3 is 96.4px with 2 children, an empty one is 24.0px with
 * 0 children, and the block below therefore moves up ~72px. That ~72px is a design consequence
 * Hares was shown and accepted.
 * ⛔ So do NOT add `--padding-top: 0` / `--padding-bottom: 0` here. An earlier draft of this file
 * had both lines; they are collapse logic, they are ruled out, and re-adding them would silently
 * overturn a decision by making the page look "finished".
 * ⭐ Removing a border is not collapsing a row: heights are byte-for-byte what they were, so M34(c)
 * holds unchanged while the newer, more specific instruction above is satisfied.
 *
 * ── WHAT ELEMENTOR ACTUALLY RENDERS FOR A BLANK STAT ───────────────────────────────────────────
 * Each stat is a container (`cs-stat1/2/3`) holding a value heading and a label heading, both bound
 * to `tw_stat1_value` / `tw_stat1_label` … by dynamic tag. When the field is empty Elementor drops
 * the heading widget ENTIRELY — no `elementor-widget` wrapper, nothing — but still prints the
 * container, and the container still carries its compiled border. So a project with no stats renders
 * three empty rows separated by two visible hairlines. Measured on `/work/doordash-commercial/`
 * (all six fields empty); the containers hold a whitespace text node, so they are **not** `:empty`
 * and `:empty` cannot be the test. `:has(.elementor-widget)` is.
 *
 * ── SPECIFICITY, WHICH IS THE WHOLE GAME HERE ──────────────────────────────────────────────────
 * ⚠️ §6 records this trap costing time repeatedly, so the arithmetic is written out. Read out of
 * `wp-content/uploads/elementor/css/post-877.css`:
 *
 *   the compiled per-element value  .elementor-877 .elementor-element.elementor-element-cs-stat1  (0,3,0)
 *   RULE 1                          .elementor-element.cs-col-r > .e-con:not(:has(.elementor-widget))  (0,4,0)
 *   RULE 2                          …:not(:has(~ .e-con .elementor-widget))                       (0,5,0)
 *
 * `:not()` and `:has()` take the specificity of their most specific argument, so rule 2's
 * `~ .e-con .elementor-widget` contributes (0,2,0) on top of the (0,3,0) compound. Both beat the
 * compiled value OUTRIGHT, so neither depends on stylesheet ORDER — which matters because Elementor
 * prints per-post CSS at render time while this file is enqueued in the head. A rule that merely
 * tied at (0,3,0) would win or lose depending on which stylesheet printed first.
 *
 * ⚠️ `cs-stat1/2/3` CARRY NO DISTINGUISHING CLASS OF THEIR OWN. Served, each one reads
 * `tw-mobile-column elementor-element elementor-element-cs-stat1 e-con-full e-flex e-con e-child`
 * — so the only per-stat handle is the id-derived `elementor-element-cs-stat1`, and everything
 * else on that list is shared with 21 other containers on the same page (`tw-mobile-column`
 * included). Hence the child is targeted STRUCTURALLY, as `.cs-col-r > .e-con`. That works because
 * `cs-col-r` has `content_width: full`, so the stats are DIRECT children with no `.e-con-inner`
 * between them. If anyone ever flips that container to `boxed`, both rules stop matching and the
 * dividers come back; they do not silently mis-target something else.
 *
 * ── WHY BOTH THE PROPERTY AND THE VARIABLES ────────────────────────────────────────────────────
 * Elementor writes the border twice: a longhand `border-width` on the element (the hairline you
 * actually see) and `--border-*-width` custom properties, which `frontend.min.css` consumes on
 * `.e-con:before` to size and offset the background-overlay pseudo. Zeroing the longhand alone
 * removes the line but leaves the pseudo sized for a border that is no longer there. Same
 * belt-and-braces reason `mobile-flex-guard.css` sets both `--flex-wrap` and `flex-wrap`.
 *
 * ── DEGRADATION IS SAFE ────────────────────────────────────────────────────────────────────────
 * ⚠️ An engine without `:has()` drops BOTH rules as invalid and the current behaviour — dividers
 * always on — survives unchanged. Nothing else in the file depends on them. This is the same
 * property `docs/CONVENTIONS.md` §6b records for `:focus-within:not(:has(:focus-visible))`
 * — named, not line-numbered, because a line number goes stale on the first insertion above it:
 * put the `:has()` on the rule that SUPPRESSES, never on the rule that draws, or the engine that
 * cannot parse it loses the thing you wanted.
 *
 * ── REACH ──────────────────────────────────────────────────────────────────────────────────────
 * ⚠️ Every rule here is scoped to `.cs-col-r`, a class that exists only in template 877. The file
 * therefore cannot touch any other page even where it is enqueued. Keep it that way: an unscoped
 * `.e-con:not(:has(.elementor-widget))` would collapse empty spacer containers site-wide.
 */

/* RULE 1 — an empty stat row draws no rule. It KEEPS its padding (M34c, see the header).
 * This is the case Hares reported: all six fields blank. */
.elementor-element.cs-col-r > .e-con:not(:has(.elementor-widget)) {
	border-width: 0;
	--border-top-width: 0px;
	--border-right-width: 0px;
	--border-bottom-width: 0px;
	--border-left-width: 0px;
}

/* RULE 2 — the LAST stat that has content: its bottom rule is a separator with nothing left to
 * separate. Without this, rule 1 alone fixes only the all-blank case and leaves a trailing hairline
 * under stat 1 the moment one stat is filled and two are left blank — one keystroke away, because
 * these are the fields being filled in right now.
 *
 * ⛔ TOGETHER THE TWO RULES SAY EXACTLY THIS, AND THE WORDING MATTERS BECAUSE AN EARLIER ONE WAS
 * WRONG IN FIVE FILES:
 *
 *       a populated stat draws a hairline below it  IFF  some populated stat FOLLOWS it,
 *       and — at phone widths only — a rail with NO populated stat at all takes up no space.
 *
 * ⛔⛔ THAT SENTENCE, IN THIS FILE, IS THE CANONICAL ONE. The same mechanism is restated in SEVEN
 * other places — the 0.4.52 changelog, `Project_Modal::enqueue()`, `stat_rail_check.mjs`,
 * `stat_rail_break.sh`, `docs/DECISIONS.md` M34(g), `docs/CONVENTIONS.md` §6c and
 * `PROJECT-NOTES.md` §2. (`guard-failures-evidence.md` narrates how earlier versions were wrong;
 * `gate-reading-evidence.md` is about an instrument artefact and states no rule at all.)
 * **Four `branch-auditor` passes produced nine blocking findings on this branch, and FOUR of the
 * nine were a copy of this sentence gone stale or written as half the rule** — the ADJACENT
 * wording, the shape/rule table, and §2 twice. The other five were different records-vs-code
 * defects. NINE SELECTORS do not need eight narrators. (This said "332 bytes" until 0.4.54, when
 * rules 3 and 4 made it 1,104 — a byte count in prose rots at every release and nothing turns
 * red, so the count here is one that rots USEFULLY: a tenth selector means the narrators below
 * need re-reading anyway.)
 * ⚠️ The first version of this paragraph said "every one of them", which was 5/9 wrong — an
 * overstatement inside the sentence marked canonical, on the day it was written.
 * ⚠️ If the rule changes, change it HERE first, then reduce the other statements to POINTERS rather
 * than retellings. Do not add a tenth.
 *
 * It is NOT "between two ADJACENT populated stats". `branch-auditor` (2026-08-26) enumerated all
 * eight fill shapes against that sentence and found it false for **stat 1 filled, stat 2 blank,
 * stat 3 filled**, where stat 1 keeps its hairline. ⭐ The SENTENCE was the defect, not the CSS:
 * that hairline is the only separator those two populated stats have, and deleting it leaves the
 * rail showing two stats with no divider at all — worse than what Hares reported. Judged from a
 * screenshot of the rendered rail, not argued. The blank row cannot close up to even the spacing,
 * because M34(c) forbids collapsing it.
 * ⚠️ All eight shapes are enumerated and asserted in `scripts/pages/work-360/stat_rail_check.mjs`.
 * Change either rule and read that table before rewriting this paragraph.
 *
 * `cs-stat3` matches rule 2 permanently and harmlessly — it is compiled with no border anyway. */
.elementor-element.cs-col-r > .e-con:not(:has(~ .e-con .elementor-widget)) {
	border-bottom-width: 0;
	--border-bottom-width: 0px;
}

/* RULE 3 — A WHOLLY EMPTY RAIL TAKES NO SPACE ON A PHONE (0.4.53).
 *
 * Hares, 2026-08-26, asked directly whether the blank rail's height should close up on a phone:
 * *"Yes, please"*. ⛔ THIS IS THE SECOND AMENDMENT TO M34(c) AND IT IS DELIBERATELY THE NARROWEST
 * ONE THAT ANSWERS HIM. He was asked about, and agreed to, exactly one case: **every stat field
 * blank, phones only.** So:
 *   · a PARTLY filled rail is untouched at every width — M34(c) still holds there, and a blank row
 *     between two filled ones still keeps its 24/48/24 padding;
 *   · DESKTOP is untouched even when the rail is wholly empty. There it costs nothing: the rail is
 *     the shortest of three columns (96px against the head's 456px), so its height is invisible.
 * ⚠️ Widening this to "collapse any blank row" would overturn M34(c) rather than amend it, and he
 * has not been asked that. Do not.
 *
 * ── WHY `display:none` AND NOT ZEROED PADDING ───────────────────────────────────────────
 * Zeroing `--padding-top`/`--padding-bottom` removes the 96px and leaves the rail as a 0px flex
 * item — which still collects `cs-head`'s **40px row-gap**. Measured on `/work/doordash-commercial/`
 * at 390px before the change: head 1012.1px, rail 96px, row-gap 40px. Removing the item removes
 * both, which is what he asked for; zeroing the padding would have left 40px behind and looked
 * like the fix had half worked.
 * ⚠️ Nothing is lost to assistive tech: the container is empty by construction — Elementor prints
 * no heading widget at all for an empty dynamic tag (see the top of this file).
 *
 * ── SPECIFICITY ─────────────────────────────────────────────────────────────────
 * `display` on a container is consumed by Elementor's own `.e-con { display: var(--display) }` at
 * (0,1,0). This rule is (0,3,0) and declares `display` directly, so it wins without touching the
 * variable. ⚠️ Do NOT "fix" it by setting `--display: none` instead — that value is not valid for
 * the property in every consumer Elementor may add later, and the direct declaration is the
 * narrower change.
 *
 * ⚠️ 767px is THE SITE'S OWN mobile breakpoint, the same one `class-mobile-flex-guard.php` uses.
 * It is not a number picked for this rule. */
@media (max-width: 767px) {

	.elementor-element.cs-col-r:not(:has(.elementor-widget)) {
		display: none;
	}
}

/* ══ THE PAGER ROW (`cs-nav`) ═════════════════════════════════════════════════════
 * A separate concern from the stat rail above; this file owns template 877's layout corrections,
 * not the rail alone.
 *
 * Hares, 2026-08-26, with a screenshot: *"the arrows are placed weirdly… For mobile: Have
 * '<- previous project' and 'next project ->' next to each other and the number under it but
 * centered"*.
 *
 * ── WHAT WAS MEASURED FIRST, AND IT CORRECTED THE BRIEF ───────────────────────────────
 * He also asked for TABLET to match desktop. Measured at nine widths on
 * `/work/doordash-commercial/`: the row is `row` / `space-between` / `center` from **768px all the
 * way up**, and only flips to `column` / `flex-start` at **767 and below**. Tablet already WAS
 * desktop; the screenshot was the ≤767 rule. So nothing here touches tablet — changing it would
 * have been a change he did not need and could not have seen.
 *
 * ── WHY GRID, WHEN THE OBVIOUS ANSWER IS `flex-wrap: wrap` ───────────────────────────
 * ⛔ `cs-nav` CARRIES `tw-mobile-column`, and `mobile-flex-guard.css` forces `flex-wrap: nowrap`
 * on it at ≤767 at **(0,4,0)** — deliberately, to stop Elementor dealing a column's children into
 * extra columns off the right edge of the phone. A wrap-based pager would have to out-specify that
 * guard and would quietly reopen the bug it exists to prevent. **`flex-wrap` is inert on a grid
 * container**, so `display: grid` sidesteps the conflict rather than winning it, and the guard
 * keeps doing its job for every other container on the page.
 * ⚠️ The `tw-mobile-column` tag on `cs-nav` is now a leftover: `class-mobile-flex-guard.php`
 * derives it from the PAGE DATA (`flex_direction_mobile: column`), which still says column even
 * though this rule overrides it. Harmless — the tag only sets `flex-wrap`, which grid ignores —
 * but the tidy fix is page data, and page data means a rebuild of 877, a snapshot and the
 * live-edit guard. Not worth it for a leftover class.
 *
 * ── SPECIFICITY ─────────────────────────────────────────────────────────────────
 *   Elementor's compiled mobile value  .elementor-877 .elementor-element.elementor-element-cs-nav
 *                                                                                       (0,3,0)
 *   THIS RULE                          .elementor-element.cs-nav.e-con.e-flex            (0,4,0)
 * The same idiom `mobile-flex-guard.css` documents. ⚠️ Elementor writes direction and alignment as
 * BOTH custom properties and longhands, so both are set — belt and braces against a future release
 * moving which element consumes them, which it has done before.
 *
 * ── THE PLACEMENT IS EXPLICIT, NOT AUTO-FLOW ─────────────────────────────────────
 * DOM order is prev, COUNT, next — the count sits between the two buttons. Auto-flow would put it
 * in cell 2 and push `next` onto row 2. Every child therefore names its own row and column, so the
 * layout does not depend on source order and a future reorder of 877 cannot silently scramble it. */
@media (max-width: 767px) {

	.elementor-element.cs-nav.e-con.e-flex {
		display: grid;
		--display: grid;
		grid-template-columns: auto auto;
		align-items: center;
		justify-content: space-between;
		--justify-content: space-between;
		row-gap: 16px;
		column-gap: 16px;
	}

	.elementor-element.cs-nav.e-con.e-flex > .elementor-element-cs-nav-prev {
		grid-row: 1;
		grid-column: 1;
		justify-self: start;
	}

	.elementor-element.cs-nav.e-con.e-flex > .elementor-element-cs-nav-next {
		grid-row: 1;
		grid-column: 2;
		justify-self: end;
	}

	/* Its own line, spanning both columns, centred across the whole row rather than under one
	   button. `text-align` is what centres the inline `.elementor-heading-title` inside it. */
	.elementor-element.cs-nav.e-con.e-flex > .cs-nav-count {
		grid-row: 2;
		grid-column: 1 / -1;
		text-align: center;
	}
}
