/* ============================================================================
   CASE STUDY TEMPLATE  ("Format B" — scannable summary, expandable story)

   Every case study uses the same skeleton. Consistency is itself a
   credibility signal.

       .csCover     the page head, then role / year / disciplines as facts
       .csSummary   situation / role / outcome as ruled rows — the 40-second read
       .csFull      the full story, chaptered, with a sticky chapter rail
       .csTurn      one dark break for the hardest decision
       .csNext      never dead-end

   To add a case study, copy work/west-music.astro — it is the reference
   implementation of this markup.

   Brought onto the 2026 art direction along with /field-notes: the red
   .eyebrow kickers are section marks, the three equal summary columns with a
   red rule apiece are ruled rows, the boxes around the outcome and the
   attribution are rules, and the chapter headings finally carry a type class —
   an unclassed <h2> renders at body size and weight 300, so every heading
   inside the full story was rendering as running text. See
   DESIGN-EXTENDING.md.

   Spacing is one --space-* token per value throughout. Every token in that
   scale is already a clamp, so the dozen clamp(--space-x, Nvw, --space-y)
   wrappers this file carried only obscured which value won — most of them
   resolved to their own minimum at every width the site is ever read at.

   THIS FILE DECLARES NO TYPE. It used to set a font-family, a size, a weight
   or a tracking in seven places — the summary, the phases, the rail, the turn,
   the disclosure hint, the attribution, the onward row — which is the rule
   `CLAUDE.md` opens with, broken seven times in one stylesheet. Every one of
   those is a class in the markup now (`.body`, `.bodySmall`, `.labelSmall`,
   `.headingMedium`, `.displaySmall`), and what is left here is layout, colour
   and spacing. The single exception is `.csReflection`, and it is commented.

   Measures come from the type classes too. A component that also declares
   `max-width: 62ch` is declaring type by another name, and this file was doing
   it at five different character counts.
   ============================================================================ */

@layer components {

/* --- Cover ----------------------------------------------------------------- */

/* The same composition as .pageHead in 07-shell.css: one section mark on the
   full measure, the title in the seven columns, the deck dropped to its
   baseline in the five. A case study opens the way every other page opens —
   it used to be a centred 900px stack under a red .eyebrow, which was the last
   place on the site where a page head still looked like the old one.

   Not literally .pageHead, because this head carries the meta list and the
   hero under it and clears the nav by a different amount; the seven-and-five
   grid is the part worth sharing, and it is four declarations. */
.csCover {
	padding-block: calc(var(--nav-height) + var(--space-2xl)) var(--space-2xl);

	.csCoverHead {
		display: grid;
		grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
		column-gap: var(--space-2xl);
		row-gap: var(--space-l);
		align-items: end;

		.indexLabel {
			grid-column: 1 / -1;
			margin-bottom: var(--space-l);
		}
		/* No max-width on either. .display carries 20ch and .lede carries
		   60ch, and both are already inside a track narrower than that — a
		   second, tighter character count here was a measure set in two places
		   that then had to agree. */
		& h1 { margin: 0; }
		.lede { margin: 0; }

		@media only screen and (max-width: 860px) {
			/* minmax(0, 1fr) — see .pageHeadInner in 07-shell.css. A bare fr
			   track floors at min-content, so one long word in a .display
			   title widens the column and the deck under it. */
			grid-template-columns: minmax(0, 1fr);
			align-items: start;
		}
	}

	/* Role, year, team, disciplines: the quiet fact band the page opens with.
	   Base grid comes from .metaGrid in 07-shell.css. A hairline, not the
	   mark's rule — this is a list, not a section boundary. */
	.csMeta {
		margin-top: var(--space-xl);
		padding-top: var(--space-l);
		border-top: 1px solid var(--color-rule);
	}
}

/* The hero takes the same radius as every other media frame on the site. It
   carried --radius-lg, which made the single largest well on the page the
   roundest thing on it. */
.csHero {
	margin-top: var(--space-xl);
}

/* --- Summary: the 40-second read ------------------------------------------- */

/* Three hairline rows — the term in the margin column, the answer in the wide
   one — under one section mark.

   It was three equal columns, each opened by a 2px rule in the identity red.
   Both halves of that were wrong: an evenly weighted repeating unit takes no
   position on what is in it, and three red rules stacked at the top of the
   page spend the whole colour ration before the story starts. The red is now
   the disclosure control and nothing else.

   No type declarations here. The paragraphs were re-declaring body's own size
   and weight, which is the rule about components and type with the numbers
   that happened to match. */
.csSummary {
	display: grid;
	/* The step down from the section mark above it. */
	margin-top: var(--space-l);

	& > div {
		display: grid;
		grid-template-columns: minmax(0, 3fr) minmax(0, 9fr);
		column-gap: var(--space-2xl);
		align-items: baseline;
		padding-block: var(--space-l);
		border-bottom: 1px solid var(--color-rule); /* divider */

		&:last-child { border-bottom: 0; }
	}
	/* The term is pinned to the first row of the margin column and every
	   paragraph to the wide one, rather than letting auto-placement decide. A
	   cell with a second paragraph would otherwise flow it back into column 1
	   — under the term, in the margin — which is the kind of failure that only
	   shows up on the case study somebody writes next year. */
	& h3 {
		grid-column: 1;
		grid-row: 1;
		margin: 0;
		color: var(--color-text-3);
	}
	/* Voice and measure come from .body in the markup; this is the rhythm
	   between paragraphs, in the paragraph's own ems. */
	& p {
		grid-column: 2;
		margin: 0 0 1em;

		&:last-child { margin-bottom: 0; }
	}
	/* Weight comes from the base rule on <strong> now. Colour is the part that
	   is genuinely contextual: full ink against the row's secondary. */
	& strong { color: var(--color-text-1); }

	@media only screen and (max-width: 767px) {
		& > div {
			grid-template-columns: 1fr;
			row-gap: var(--space-2xs);
		}
		/* Both spans have to be released with the columns, or a paragraph
		   pinned to line 2 of a one-column grid generates an implicit track
		   and the row stops being one column wide. */
		& h3, & p { grid-column: 1; }
	}
}

/* --- The full-story disclosure ---------------------------------------------
   A native <details>, so it works with no JavaScript.                        */

/* One rule, on the top edge only. The bottom one landed directly on .csNext's
   own top rule, so the story closed on two hairlines a pixel apart — which
   reads as a rendering mistake rather than as a boundary. */
.csFull {
	border-top: 1px solid var(--color-rule);

	& > summary {
		list-style: none;
		cursor: pointer;
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: var(--space-m);
		padding-block: var(--space-l);
		transition: color 0.2s ease;

		&::-webkit-details-marker { display: none; }

		.csFullLabel {
			color: var(--color-text-1);
			transition: color 0.2s ease;
		}
		/* Voice is .labelSmall in the markup — the meta tier, which is what
		   "about a ten minute read" is. All that is left here is when to drop
		   it: on a narrow summary the label and the hint compete for the row,
		   and the label is the one you can act on. */
		.csFullHint {
			@media only screen and (max-width: 640px) { display: none; }
		}
		/* a plus that becomes a minus on open */
		.csFullIcon {
			width: 34px;
			height: 34px;
			flex-shrink: 0;
			border-radius: 50%;
			border: 2px solid var(--color-brand);
			display: inline-flex;
			align-items: center;
			justify-content: center;
			position: relative;
			transition: transform 0.3s ease, background-color 0.3s ease;

			&::before, &::after {
				content: "";
				position: absolute;
				background: var(--color-brand);
				border-radius: 2px;
				transition: transform 0.3s ease, opacity 0.3s ease;
			}
			&::before { width: 13px; height: 2.5px; }
			&::after  { width: 2.5px; height: 13px; }
		}

		&:hover .csFullLabel { color: var(--color-action-text); }
		&:hover .csFullIcon { transform: scale(1.08); }
	}

	&[open] > summary .csFullIcon::after {
		transform: rotate(90deg);
		opacity: 0;
	}

	/* No padding-bottom on .csFullBody. Every .csChapter already closes on
	   --space-3xl, so the last chapter and the body were stacking two full
	   bands' worth of space before the onward row. */
}

/* --- Chapter layout & sticky rail ------------------------------------------ */

.csLayout {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--space-xl);

	@media only screen and (min-width: 1100px) {
		grid-template-columns: 200px minmax(0, 1fr);
		gap: var(--space-4xl);
	}
}

/* The rail only appears when there is room for it beside the text.
   scripts.js adds .isActive to the chapter currently in view. */
.csRail {
	display: none;

	@media only screen and (min-width: 1100px) {
		display: block;
		position: sticky;
		top: calc(var(--nav-height) + var(--space-l));
		align-self: start;
		max-height: calc(100vh - var(--nav-height) - var(--space-l) * 2);
		overflow-y: auto;
	}

	& h2 {
		color: var(--color-text-3);
		margin: 0 0 var(--space-m);
	}
	& ol {
		list-style: none;
		margin: 0;
		padding: 0;
		counter-reset: chapter;
	}
	& li {
		counter-increment: chapter;
		margin-bottom: var(--space-s);
	}
	/* Grid, not flex, so the numeral and the chapter name share a baseline
	   instead of being nudged onto one with a padding-top guess. The numeral
	   track is a fixed 2ch of tabular figures, so every title in the rail
	   starts on the same vertical line whatever digits precede it.

	   Voice is .bodySmall in the markup. These are sentence-case links, and
	   they were set in the condensed label cut at a second size — which made a
	   nine-item rail read as nine tiny labels rather than as a list of chapter
	   names. */
	& a {
		display: grid;
		grid-template-columns: 2ch minmax(0, 1fr);
		gap: var(--space-s);
		align-items: baseline;
		color: var(--color-text-3);
		transition: color 0.2s ease;

		&::before {
			content: counter(chapter, decimal-leading-zero);
			font-variant-numeric: tabular-nums;
			color: var(--color-text-3);
		}
		&:hover { color: var(--color-action-text); }
		/* Colour only. A weight change in a sticky rail reflows the item under
		   the cursor as you scroll past it. */
		&.isActive {
			color: var(--color-text-1);

			&::before { color: var(--color-action-text); }
		}
	}
}

.csBody { min-width: 0; }

/* No scroll-margin-top. `html` already carries
   `scroll-padding-top: calc(var(--nav-height) + var(--space-s))` in
   03-base.css, which is the site-wide answer to "don't land under the fixed
   nav" — a second offset here just dropped every chapter anchor an extra
   --space-3xl below where it was asked to go. */
.csChapter {
	padding-bottom: var(--space-3xl);

	/* The chapter number is meta, not a kicker: .labelSmall in tertiary ink,
	   the same tier the work cards' meta line and every caption sit at. It was
	   a red .eyebrow with .muted bolted on to take the red back off it — seven
	   of them down one page.

	   The number stays. It is one of the few places on the site where an
	   ordinal does structural work: the chapters are read in order and the
	   sticky rail counts them, which is exactly the test the home page's
	   numbered section marks failed. */
	& > .csChapterHead {
		margin-bottom: var(--space-l);

		/* The number's gap to the heading is set in the number's own ems, not
		   in a space token: it is text sitting directly above other text, so
		   the step should track the label's size rather than the page rhythm. */
		.labelSmall {
			display: block;
			margin-bottom: 1em;
		}
		& h2 { margin: 0; }
	}
	.prose { margin-bottom: var(--space-xl); }

	/* Spacing between blocks. Direct children only: these must not reach
	   figures nested inside a .mediaPair, .mediaTrio or .mediaScroller, or the
	   second item in every group picks up a top margin and drops out of
	   alignment with the first. */
	& > .mediaFrame + .mediaFrame,
	& > .mediaFrame + .mediaPair,
	& > .mediaPair + .mediaFrame,
	& > .mediaPair + .mediaPair,
	& > .mediaTrio + .mediaFrame,
	& > .mediaFrame + .mediaTrio,
	& > .mediaScroller + .mediaFrame,
	& > .mediaFrame + .mediaScroller,
	& > .capList + .mediaFrame {
		margin-top: var(--space-l);
	}
	/* The outcome quote used to carry style="margin-top:56px" on the markup —
	   a hand-set pixel value on both case studies, which is the one thing the
	   space scale exists to stop. */
	& > .csOutcome + .pullQuote {
		margin-top: var(--space-2xl);
	}

	& > .prose + .mediaFrame,
	& > .prose + .mediaPair,
	& > .prose + .mediaTrio,
	& > .prose + .mediaScroller {
		margin-top: var(--space-l);
	}
	& > .mediaFrame + .prose,
	& > .mediaPair + .prose,
	& > .mediaTrio + .prose,
	& > .mediaScroller + .prose {
		margin-top: var(--space-l);
	}
}

/* The phases inside the Approach chapter ARE the shared ruled index — four
   peer steps with a numeral apiece — so they are .capList / .capRow now
   (07-shell.css) rather than .csPhases, a second numbered list with its own
   counter, its own 44px gutter and its own hand-set numeral voice.

   That leaves the site with exactly one display numeral and one label numeral:
   .capNum here and on /about, /for-agencies, /process, /contact and the home
   page, and the chapter ordinal, which is .labelSmall in the markup and a
   tabular counter in the rail. Three numbering systems, each written its own
   way, was the thing to fix.

   All this modifier does is pick the numeral's size. A chapter's rows are
   card-tier headings inside a story, not a page's spine, so the numeral steps
   down with them — that is a scale decision, which is why it is a custom
   property on the list rather than a re-declaration of the voice. */
.csPhaseList {
	--cap-num-size: var(--step-4);
	--cap-num-size-mobile: var(--step-2);
}

/* --- The turn ---------------------------------------------------------------
   The one deliberate break in the page's rhythm, and the section that gets you
   hired. It fills the content column rather than the viewport — a full-bleed
   breakout can't be centred reliably from inside the chapter grid.           */

.csTurn {
	color-scheme: dark;
	background: var(--page-surface-contrast);
	color: var(--color-text-1);
	/* One-up pairs, so the panel keeps the generous inset it wants on a wide
	   screen and steps down a whole level on a phone, where --space-2xl of
	   padding either side left the paragraph about forty characters wide.
	   --space-xl-2xl is 36→80px and --space-l-xl is 24→60px. */
	padding: var(--space-xl-2xl) var(--space-l-xl);
	/* No bottom margin. The chapter this sits in already closes on
	   --space-3xl, and the two were stacking into a gap twice the size of the
	   one between any other pair of chapters. */
	margin: 0;

	/* The section mark, in the ink of the field it is on: full white here and,
	   on the restrained theme where the turn goes red, full white again
	   through .onBrand. It was a red .eyebrow, which on this near-black ground
	   was the fourth thing on the page competing for the identity colour. */
	.indexLabel { margin-bottom: var(--space-l); }

	& h2 { margin: 0 0 var(--space-l); }
	/* Voice and measure are .body in the markup. The panel used to set 62ch
	   twice — once on the wrapper and once on the paragraphs — which also
	   pinned the section mark's rule to the text measure instead of letting it
	   run the width of the panel, the way every other section mark on the site
	   runs the width of its band. */
	& p {
		margin: 0 0 1.2em;

		&:last-child { margin-bottom: 0; }
	}
	/* Was `color: white`. The band sets color-scheme: dark, so
	   --color-text-1 resolves to the scheme's own near-white — and on
	   .csThemeRestrained's red turn it resolves through .onBrand instead,
	   which a hardcoded white could not do. Weight comes from the base rule. */
	& strong { color: var(--color-text-1); }
}

/* --- Outcome & reflection --------------------------------------------------- */

/* Figures divided by rules, not boxed in a card — the same argument the rate
   table on /for-agencies and /process settled. The numbers were inside a
   bordered, rounded, tinted panel, which is the one shape this redesign took
   off every other page. .proofRow and .proofItem already draw a rule over each
   figure; all this band has to do now is give them room. */
.csOutcome {
	.proofRow { margin-top: var(--space-xl); }
	.proofItem { border-top-color: var(--color-rule-strong); }
}

/* The structural rule, turned on its side: --space-2xs of full ink, the same
   weight that opens every section on the site. It was 2px of identity red,
   which is a fifth claim on the colour and reads as a pull quote rather than
   as the end of the story.

   The serif is hand-set rather than coming from a type class, like
   .statementClose in 07-shell.css: it is a one-off voice that exists in this
   one place, and adding a twelfth class to the closed set for a single
   paragraph would cost more than it explains. */
.csReflection {
	border-left: var(--space-2xs) solid var(--color-text-1);
	padding-left: var(--space-m);

	& p {
		font-family: var(--font-serif);
		font-size: var(--step-2);
		font-style: italic;
		line-height: 1.55;
		color: var(--color-text-2);
		margin: 0;
		max-width: 56ch;
	}
}

/* --- Next case study — never dead-end --------------------------------------- */

/* The onward row is a work card in everything but name, so it is built out of
   the same pieces: a section mark, a sub-display title, and an .artifactPlate
   for the well. The plate matters more here than anywhere — the name it
   carries is also the view-transition name of the hero it is about to become,
   so the thumbnail morphs into the next case study's cover instead of the two
   pages crossfading past each other. See "Cross-document view transitions" in
   CLAUDE.md; the pairing is declared inline on the markup, per instance.

   The well was a rounded, bordered box holding a placeholder rectangle. */
.csNext {
	border-top: 1px solid var(--color-rule);

	/* The plate's ratio belongs to this component, the way .caseIndex owns its
	   two: the onward row is one shape wherever it appears. */
	.ratioNext { aspect-ratio: 3 / 2; }

	& a.csNextLink {
		display: grid;
		grid-template-columns: minmax(0, 1fr) 320px;
		gap: var(--space-2xl);
		align-items: end;
		padding-block: var(--space-2xl);
		color: var(--color-text-1);

		@media only screen and (max-width: 767px) {
			grid-template-columns: 1fr;
			align-items: start;
		}

		.csNextMedia {
			overflow: hidden;
			line-height: 0;

			& img {
				width: 100%;
				height: 100%;
				object-fit: cover;
				transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
			}
		}
		.indexLabel { margin-bottom: var(--space-l); }
		& h2 {
			margin: 0 0 var(--space-m);
			transition: color 0.25s ease;
		}
		& p { margin: 0 0 var(--space-l); }
		&:hover h2 { color: var(--color-action-text); }
		&:hover .csNextMedia img { transform: scale(1.04); }
		/* A well with no photograph in it yet still has to answer a hover: the
		   substrate comes up rather than the plate changing colour. Same move
		   .workCard makes in 07-shell.css. */
		&:hover .artifactPlate {
			--plate-rule: color-mix(in srgb, var(--color-on-brand) 13%, transparent);
		}
	}
}

/* --- Attribution note -------------------------------------------------------
   Subcontracted work needs an honest line about who did what.                */

.csAttribution {
	display: flex;
	align-items: flex-start;
	gap: var(--space-s);
	color: var(--color-text-3);
	/* A rule, not a box. It is a footnote to the paragraph above it, and a
	   rounded outlined panel gave a caveat the same shape the outcome figures
	   used to have. Voice and measure are .bodySmall on the note itself. */
	border-top: 1px solid var(--color-rule);
	padding-top: var(--space-m);

	/* The icon is sized and aligned off the text it sits beside, so it tracks
	   the note rather than the page. */
	& svg {
		width: 1.1em;
		flex-shrink: 0;
		fill: var(--color-text-3);
		margin-top: 0.2em;
	}
	& b { color: var(--color-text-2); }
	& a { text-decoration-line: underline; }
}

/* --- Per-project themes -----------------------------------------------------
   Case studies should not feel interchangeable. Each one gets a restrained
   shift in palette and rhythm. Put the theme class on a <div> wrapping <main>, so the page chrome shifts with it. */

/* Scene+ — darker and more restrained. Monochrome except the product. */
.csThemeRestrained {
	color-scheme: dark;
	background: var(--color-surface);
	color: var(--color-text-1);

	.page { background: transparent; }

	/* On this theme the turn goes red rather than near-black — near-black would
	   disappear into the page. */
	/* Colours come from .onBrand in 02-tokens. */
	.csTurn { background: var(--color-surface-brand); }
}

/* Matches the body behind a restrained-theme case study to the same forced
   dark it uses, so overscroll doesn't flash back to the visitor's own
   light/dark preference. Safe to force color-scheme here specifically
   because the whole page is already forced dark by .csThemeRestrained above
   — nothing on this page is relying on inheriting the visitor's real
   preference for body to also not force it. Deliberately reuses
   --color-surface rather than --page-surface-contrast: the latter is the
   *elevated* shade always-dark bands (footer, .ctaBand, .inverse) use to
   differentiate themselves from a dark-mode page — a different, lighter
   neutral that would visibly seam against this theme's own surface. */
body:has(.csThemeRestrained) {
	color-scheme: dark;
	background: var(--color-surface);
}

/* Dubuque Glass — warm and editorial, with sections coloured by the glass
   grades. Waiting for that case study to be written. */
.csThemeWarm {
	.section.flint { background: light-dark(hsl(180, 8%, 96%), hsl(180, 8%, 12%)); }
	.section.amber { background: light-dark(hsl(36, 45%, 94%), hsl(36, 30%, 12%)); }
	.section.green { background: light-dark(hsl(140, 20%, 94%), hsl(140, 18%, 11%)); }
}

} /* @layer components */
