/* ---------------------------------------------------------------------------
   KJA Sukartha — site styles
   Implements the Claude Design system (Home.dc.html) on top of the tokens in
   assets/css/tokens/. theme.json covers what it can express; this file covers
   the rest — band backgrounds, the two-column splits, numbered habits, pull
   quotes, header and footer.
   --------------------------------------------------------------------------- */

/* --- Baseline ------------------------------------------------------------ */

body {
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}

::selection { background: var(--orange-100); color: var(--ink-900); }

:focus-visible {
	outline: 2px solid var(--focus-ring);
	outline-offset: 2px;
	border-radius: var(--radius-xs);
}

a { text-underline-offset: 3px; text-decoration-thickness: 1px; text-decoration-color: var(--leaf-300); transition: var(--transition-color); }
a:hover { text-decoration-color: var(--orange-300); }

/* --- Wrap ----------------------------------------------------------------
   The design's container. Every band holds one of these.

   This is deliberately NOT WordPress's constrained layout. Constrained applies
   `margin-inline: auto !important` to every child, so any element with its own
   max-width — a paragraph at its measure, a heading at 22ch — gets centred in
   the column instead of sitting left. The `!important` means CSS can't undo it.
   A plain flow container plus this wrapper gives the design's behaviour: the
   container centres, its contents stay left and keep their own measure. */

.wrap {
	max-width: var(--container-max);
	margin-inline: auto;
	padding-inline: var(--space-7);
}
@media (max-width: 600px) {
	.wrap { padding-inline: var(--space-5); }
}

/* The design's base.css measure, applied the same way. */
p { max-width: var(--measure); }
.wp-block-column p { max-width: none; }

/* Root padding is 0 (see theme.json): `.wrap` owns the gutter, and any root
   padding would be doubled by the negative margins WordPress gives `alignfull`
   children — which is what pushed the page wider than the viewport on mobile.
   Pages not yet rebuilt as bands are still constrained-layout sections, so
   they need that gutter handed back to them directly. */
.wp-block-post-content > .wp-block-group.is-layout-constrained {
	padding-inline: var(--space-7);
}
@media (max-width: 600px) {
	.wp-block-post-content > .wp-block-group.is-layout-constrained { padding-inline: var(--space-5); }
}

/* Bands own all vertical rhythm, so block gap must not apply between them —
   at the root (header / main / footer) or between the sections inside the
   content. Either one shows as a stripe of page background wherever the bands
   either side are a different colour. */
.wp-site-blocks > *,
.wp-block-post-content > * { margin-block-start: 0; margin-block-end: 0; }

/* --- Bands ---------------------------------------------------------------
   Each top-level section on a page is a full-width group carrying one of these
   classes. They own their own background and vertical rhythm; the .wrap inside
   supplies the container. */

.band {
	padding-block: var(--section-y);
}
.band-tight { padding-block: var(--space-11); }
.band-page    { background: var(--surface-page); }
.band-card    { background: var(--surface-card); }
.band-sunken  { background: var(--surface-sunken); }
.band-inverse { background: var(--surface-inverse); }
.band-forest  { background: var(--surface-forest); }
.band-tan     { background: var(--paper-200); }

.rule-top    { border-top: var(--border-width) solid var(--border-hairline); }
.rule-bottom { border-bottom: var(--border-width) solid var(--border-hairline); }

/* Dark bands flip every text colour they contain. */
.band-inverse, .band-forest,
.band-inverse p, .band-forest p { color: var(--text-inverse-muted); }
.band-inverse :is(h1, h2, h3, h4, blockquote, cite, strong),
.band-forest  :is(h1, h2, h3, h4, blockquote, cite, strong) { color: var(--text-inverse); }
.band-inverse .wp-block-separator,
.band-forest  .wp-block-separator { color: var(--border-inverse); }

/* --- Typographic helpers ------------------------------------------------- */

.lead  { font-size: var(--text-lg);   line-height: var(--leading-relaxed); color: var(--text-body); max-width: 62ch; text-wrap: pretty; }

/* Headings run short in this design — the measure is part of the look. */
.band h2 { max-width: 24ch; text-wrap: pretty; }
.band h2.is-18ch { max-width: 18ch; }
.band h2.is-26ch { max-width: 26ch; }
/* The mockups have two kinds of lead. `.lead` is the sans one under a page
   title; `.lead-display` is the serif sentence that opens a section. */
.lead-display {
	font-family: var(--font-display);
	font-size: 1.65rem;
	line-height: 1.45;
	color: var(--text-heading);
	max-width: 34ch;
	text-wrap: pretty;
}
@media (min-width: 640px) { .lead-display { font-size: 2rem; } }
.band-forest .lead-display, .band-inverse .lead-display { color: var(--text-inverse); }

.eyebrow {
	font-size: var(--text-2xs);
	letter-spacing: var(--tracking-eyebrow);
	text-transform: uppercase;
	color: var(--text-muted);
}
.measure-tight :is(h1, h2, h3) { max-width: 24ch; }

/* --- Hero ---------------------------------------------------------------- */

.kja-hero {
	position: relative;
	overflow: hidden;
	background: var(--surface-card);
	border-bottom: var(--border-width) solid var(--border-hairline);
	padding-top: var(--space-12);
	padding-bottom: var(--space-11);
}

/* The butterfly mark, bled off the bottom-left corner at 6% opacity. */
.kja-hero::before {
	content: "";
	position: absolute;
	left: -160px;
	bottom: -180px;
	width: 620px;
	height: 620px;
	background: url("../img/logo-mark.png") no-repeat center / contain;
	opacity: 0.06;
	pointer-events: none;
}

.kja-hero > * { position: relative; }
.kja-hero h1 { font-size: var(--text-5xl); line-height: var(--leading-tight); max-width: 13ch; }
.kja-hero .lead { max-width: 52ch; }

/* --- Column splits -------------------------------------------------------
   The design expresses these as CSS grid, e.g. `minmax(0,1.05fr) minmax(0,1fr)`.
   WordPress columns are FLEXBOX, and `fr` is a grid-only unit — putting a ratio
   in a column's width attribute yields `flex-basis:1.05fr`, which is invalid,
   so the column falls back to auto with flex-grow:0 and collapses to one
   character per line.

   Flex ratios are the faithful translation: `flex: <ratio> 1 0` distributes free
   space exactly as `fr` does, and unlike percentages it needs no correction for
   the gap. `min-width:0` is the `minmax(0,…)` part — without it a flex item
   refuses to shrink below its content and long words force overflow. */

.wp-block-columns.split { gap: var(--space-10); }
.wp-block-columns.split > .wp-block-column { flex: 1 1 0; min-width: 0; }

.split-hero       > .wp-block-column:nth-child(2) { flex-grow: 1.02; }
.split-foundation > .wp-block-column:nth-child(1) { flex-grow: 1.05; }
.split-kitchen    > .wp-block-column:nth-child(1) { flex-grow: 1.1; }
.split-rail       > .wp-block-column:nth-child(1) { flex-grow: 1.4; }
.split-rail       > .wp-block-column:nth-child(2) { flex-grow: 0.6; }
.split-footer     > .wp-block-column:nth-child(1) { flex-grow: 1.6; }

@media (max-width: 900px) {
	.wp-block-columns.split { flex-wrap: wrap !important; }
	.wp-block-columns.split > .wp-block-column { flex: 1 1 100% !important; }
}

/* --- Media panels --------------------------------------------------------
   Photography is not shot yet. Until it is, the slots render as labelled
   placeholders at the design's intended heights so the composition is honest
   about what is missing. Replacing one is a normal image block swap. */

.media-panel {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: var(--space-7);
	border: 1px dashed var(--border-strong);
	border-radius: var(--radius-md);
	background: var(--surface-sunken);
	color: var(--text-muted);
	font-size: var(--text-sm);
	line-height: var(--leading-normal);
	text-align: center;
	text-wrap: pretty;
}
.media-panel.is-tall  { min-height: 440px; }
.media-panel.is-taller { min-height: 480px; }
.media-panel.is-tallest { min-height: 520px; }
.band-inverse .media-panel { background: #1e2c24; border-color: var(--border-inverse); color: var(--text-inverse-muted); }

.media-panel .wp-block-image,
.media-panel img { border-radius: var(--radius-md); }

/* A full-bleed band that is nothing but one image. */
.media-band { min-height: 440px; background: var(--surface-sunken); }

/* --- Habits (the numbered 2x2 grid) -------------------------------------- */

.habit {
	padding-top: var(--space-5);
	border-top: var(--border-width-thick) solid var(--paper-300);
}
.habit .habit-num {
	font-family: var(--font-display);
	font-size: var(--text-xs);
	letter-spacing: var(--tracking-wide);
	color: var(--orange-600);
	margin: 0;
}
.habit h3 {
	margin: var(--space-3) 0 var(--space-4);
	font-size: var(--text-2xl);
	line-height: var(--leading-snug);
	max-width: 22ch;
}
.habit p { font-size: var(--text-sm); margin: 0; max-width: none; }

/* Two stacked columns blocks stand in for the design's 2x2 grid, so the row
   gap has to be restored explicitly — block gap alone is much tighter. */
.band .wp-block-columns.split + .wp-block-columns.split { margin-top: var(--space-10); }

/* --- Pull quote ----------------------------------------------------------
   Matches the design's PullQuote: no quote glyph of its own, a 3px orange rule
   on the left, serif at h3/h2 size, attribution in body sans below. */

.wp-block-quote.pullquote {
	margin: 0;
	padding: 0 0 0 var(--space-7);
	border: 0;
	border-left: var(--rule-width-accent) solid var(--rule-accent);
	max-width: var(--measure-wide);
	font-style: normal;
}
.wp-block-quote.pullquote p {
	font-family: var(--font-display);
	font-size: var(--text-2xl);
	line-height: var(--leading-normal);
	letter-spacing: var(--tracking-tight);
	color: var(--text-heading);
	max-width: none;
	text-wrap: pretty;
}
.wp-block-quote.pullquote.is-lg p { font-size: var(--text-3xl); }
.wp-block-quote.pullquote cite {
	display: block;
	margin-top: var(--space-5);
	font-family: var(--font-body);
	font-size: var(--text-sm);
	font-style: normal;
	color: var(--text-muted);
}
.band-inverse .wp-block-quote.pullquote p { color: var(--text-inverse); }
.band-inverse .wp-block-quote.pullquote cite { color: var(--text-inverse-muted); }

/* --- Sidebar rail (Practice and theory) ---------------------------------- */

.rail {
	border-left: var(--border-width) solid var(--border-hairline);
	padding-left: var(--space-7);
}
.rail .rail-title {
	font-family: var(--font-display);
	font-size: var(--text-xl);
	color: var(--text-heading);
	margin: 0;
}
.rail .rail-sub { font-size: var(--text-sm); color: var(--text-muted); margin: var(--space-1) 0 0; }
.rail-pending { color: var(--text-muted); margin-top: var(--space-3); max-width: none; }
.rail-box {
	border: 1px dashed var(--border-strong);
	border-radius: var(--radius-md);
	padding: var(--space-5);
}
@media (max-width: 900px) {
	.rail { border-left: 0; border-top: var(--border-width) solid var(--border-hairline); padding-left: 0; padding-top: var(--space-7); }
}

/* --- Buttons -------------------------------------------------------------
   theme.json styles the primary fill. These are the design's other variants. */

.wp-block-button.is-style-secondary .wp-block-button__link {
	background: transparent;
	color: var(--action-secondary-text);
	border: var(--border-width) solid var(--action-secondary-border);
}
.wp-block-button.is-style-secondary .wp-block-button__link:hover {
	background: var(--action-secondary-hover-bg);
	color: var(--action-secondary-text);
}
.wp-block-button.is-style-ghost .wp-block-button__link {
	background: transparent;
	color: var(--text-heading);
	padding-inline: 0;
	text-decoration: underline;
	text-underline-offset: 4px;
	text-decoration-color: var(--border-strong);
}
.wp-block-button.is-style-ghost .wp-block-button__link:hover {
	background: transparent;
	color: var(--text-link-hover);
	text-decoration-color: var(--orange-300);
}
.band-inverse .wp-block-button__link { }
.wp-block-buttons { gap: var(--space-4); }

/* --- Header --------------------------------------------------------------- */

.site-header {
	position: sticky;
	top: 0;
	z-index: 20;
	border-bottom: var(--border-width) solid var(--border-hairline);
	padding-block: var(--space-4);
}

/* The translucent blur lives on a pseudo-element, not on the header itself.
   `backdrop-filter` makes an element a containing block for `position: fixed`
   descendants — which trapped WordPress's mobile navigation overlay inside the
   header's box, so the menu opened clipped to a sliver at the top of the page.
   On a pseudo-element it contains nothing and the overlay covers the viewport. */
.site-header::before {
	content: "";
	position: absolute;
	inset: 0;
	background: rgba(250, 246, 238, 0.88);
	backdrop-filter: saturate(140%) blur(10px);
	-webkit-backdrop-filter: saturate(140%) blur(10px);
	pointer-events: none;
}
.site-header > * { position: relative; }
.site-header > .wp-block-group { max-width: var(--container-max); margin-inline: auto; padding-inline: var(--space-7); }
@media (max-width: 600px) {
	.site-header > .wp-block-group { padding-inline: var(--space-5); }
}
/* --- Language switcher ---------------------------------------------------
   EN / ID, set in the eyebrow style the rest of the site uses for small caps.
   The current language stays visible rather than being hidden, so the pair
   always reads as a choice rather than as a single stray link. */

.lang-switcher {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	list-style: none;
	margin: 0;
	padding: 0;
}
.lang-switcher .lang-item + .lang-item { padding-left: var(--space-3); border-left: var(--border-width) solid var(--border-hairline); }
.lang-switcher a {
	font-size: var(--text-2xs);
	letter-spacing: var(--tracking-eyebrow);
	text-transform: uppercase;
	color: var(--text-muted);
	text-decoration: none;
}
.lang-switcher a:hover,
.lang-switcher a:focus-visible { color: var(--text-accent); }
.lang-switcher .current-lang a { color: var(--text-heading); }

/* The header's booking button. Sized here rather than as block attributes so a
   phone can have a smaller one — an inline style would win over any media
   query. */
.site-header .wp-block-button__link {
	padding: 8px 16px;
	font-size: 0.8125rem;
}

/* On a phone the header carries a logo, a menu button, the switcher and the
   booking button, and they do not all fit. Two things give: the switcher shows
   only the language you can switch *to* (the one you are reading is evident
   from the page), and the button is set a size down. */
@media (max-width: 600px) {
	.site-header .lang-switcher .current-lang { display: none; }
	.site-header .lang-switcher .lang-item { padding-left: 0; border-left: 0; }
	.site-header > .wp-block-group > .wp-block-group { gap: var(--space-4); }
	.site-header .wp-block-button__link { padding: 7px 12px; font-size: 0.75rem; }
}

.site-header .custom-logo { height: 40px; width: auto; }
.site-header .wp-block-site-logo,
.site-header .custom-logo-link { display: block; line-height: 0; }

.site-header .wp-block-navigation .wp-block-navigation-item__content {
	font-size: var(--text-sm);
	color: var(--text-body);
	text-decoration: none;
	padding-bottom: 3px;
	border-bottom: var(--border-width-thick) solid transparent;
	transition: var(--transition-color);
}
.site-header .wp-block-navigation .current-menu-item > .wp-block-navigation-item__content {
	color: var(--text-heading);
	font-weight: var(--weight-bold);
	border-bottom-color: var(--rule-accent);
}
.site-header .wp-block-navigation .wp-block-navigation-item__content:hover { color: var(--text-heading); }

/* --- Mobile navigation overlay -------------------------------------------
   The header's nav is right-justified, and core drives the overlay's alignment
   from `--navigation-layout-justification-setting`, which that justification
   sets to flex-end — so the open menu stacked hard against the right edge and
   ran off it. Re-pointing the custom property is cleaner than out-specifying
   core's several layered selectors.

   Core's own overlay padding is `clamp(1rem, <root padding>, 20rem)`, and our
   root padding is 0, so it collapses to a bare 1rem. */

.wp-block-navigation .wp-block-navigation__responsive-container.is-menu-open,
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container {
	--navigation-layout-justification-setting: flex-start;
	--navigation-layout-align: flex-start;
}
.wp-block-navigation .wp-block-navigation__responsive-container.is-menu-open {
	padding: var(--space-11) var(--space-7) var(--space-9);
}
@media (max-width: 600px) {
	.wp-block-navigation .wp-block-navigation__responsive-container.is-menu-open { padding-inline: var(--space-5); }
}

/* Core's white overlay background is set at (0,3,0); match it to win. */
.wp-block-navigation:not(.has-background) .wp-block-navigation__responsive-container.is-menu-open {
	background-color: var(--surface-page);
	color: var(--text-heading);
}

.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container { gap: var(--space-5); }
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item__content {
	font-family: var(--font-display);
	font-size: var(--text-2xl);
	color: var(--text-heading);
	border-bottom: 0;
	padding-bottom: 0;
}
.wp-block-navigation__responsive-container.is-menu-open .current-menu-item > .wp-block-navigation-item__content { color: var(--text-accent); }

/* --- Footer --------------------------------------------------------------- */

.site-footer {
	background: var(--surface-inverse);
	color: var(--text-inverse-muted);
	padding: var(--space-11) 0 var(--space-7);
}
.site-footer :is(p, li, address, div) { color: var(--text-inverse-muted); }
.site-footer .footer-brand img { height: 38px; width: auto; }
.site-footer .footer-brand figure { margin: 0; }
.site-footer h2, .site-footer h3, .site-footer h4 {
	font-family: var(--font-body);
	font-size: var(--text-2xs);
	font-weight: var(--weight-bold);
	letter-spacing: var(--tracking-eyebrow);
	text-transform: uppercase;
	color: var(--text-inverse);
	margin: 0 0 var(--space-4);
}
.site-footer a { color: var(--text-inverse-muted); text-decoration: none; font-size: var(--text-sm); line-height: var(--leading-loose); }
.site-footer a:hover { color: var(--text-inverse); text-decoration: underline; }
.site-footer .wp-block-navigation { gap: 0; }
.site-footer .footer-bottom {
	margin-top: var(--space-10);
	padding-top: var(--space-5);
	border-top: var(--border-width) solid var(--border-inverse);
	font-size: var(--text-xs);
	color: var(--ink-500);
}
.site-footer .footer-bottom p { color: var(--ink-500); font-size: var(--text-xs); margin: 0; }

/* --- Floating WhatsApp button --------------------------------------------
   Rendered on wp_footer by functions.php so it appears on every route.
   Green is WhatsApp's own brand colour, deliberately not a KJA token — it
   reads as the third-party affordance it is. */

.kja-wa-fab {
	position: fixed;
	right: 1.25rem;
	bottom: calc(1.25rem + env(safe-area-inset-bottom, 0px));
	z-index: 30;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 3.5rem;
	height: 3.5rem;
	border-radius: var(--radius-pill);
	background: #25d366;
	color: #fff;
	box-shadow: var(--shadow-md);
	text-decoration: none;
	transition: var(--transition-lift);
}
.kja-wa-fab:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); color: #fff; }
.kja-wa-fab svg { width: 1.65rem; height: 1.65rem; fill: currentColor; }

@media print { .kja-wa-fab { display: none; } }

.visually-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* --- Reveal on scroll ----------------------------------------------------
   Ported from the Magic Patterns mockup, which used framer-motion:
   `initial {opacity:0, y:18}` -> `whileInView {opacity:1, y:0}`, 0.6s on
   cubic-bezier(.22,1,.36,1), fired once, 80px before the element is in view.

   Progressive enhancement: the classes alone do nothing, so content is visible
   if the script never runs. `armed` — which sets the hidden start state — is
   added by JS only when motion is allowed. */

.reveal.armed,
.reveal-stagger.armed > * {
	opacity: 0;
	transform: translateY(18px);
}
.reveal.armed.in,
.reveal-stagger.armed.in > * {
	opacity: 1;
	transform: none;
	transition: opacity var(--duration-reveal) var(--ease-reveal),
	            transform var(--duration-reveal) var(--ease-reveal);
}

/* Children come in one after another. `--reveal-i` is set by the script;
   `--reveal-step` lets a group tune its own rhythm (the mockup used 60ms for
   the partner pair and 50ms across each row of the roster). */
.reveal-stagger.armed.in > * {
	transition-delay: calc(var(--reveal-base, 0ms) + var(--reveal-i, 0) * var(--reveal-step, 60ms));
}
.reveal-stagger.is-roster { --reveal-step: 50ms; }

@media (prefers-reduced-motion: reduce) {
	.reveal, .reveal.armed,
	.reveal-stagger.armed > * { opacity: 1; transform: none; }
}

/* --- Pairing diagram: drawn connectors -----------------------------------
   The mockup animates each connector's `pathLength` from 0 to 1. The CSS
   equivalent is a dash the length of the path: with `pathLength="1"` on the
   path, a dasharray and offset of 1 hides it exactly, and easing the offset to
   0 draws it. Delays match the mockup's 0.15s + 0.1s per line. */

.pairing.armed .pairing-fan path {
	stroke-dasharray: 1;
	stroke-dashoffset: 1;
}
.pairing.armed.in .pairing-fan path {
	stroke-dashoffset: 0;
	transition: stroke-dashoffset var(--duration-draw) var(--ease-out);
}
.pairing.armed.in .pairing-fan path:nth-child(1) { transition-delay: 150ms; }
.pairing.armed.in .pairing-fan path:nth-child(2) { transition-delay: 250ms; }
.pairing.armed.in .pairing-fan path:nth-child(3) { transition-delay: 350ms; }

/* The client box leads; the three people follow once the lines have drawn. */
.pairing.armed .pairing-client { opacity: 0; transform: translateY(12px); }
.pairing.armed.in .pairing-client {
	opacity: 1;
	transform: none;
	transition: opacity 500ms var(--ease-reveal), transform 500ms var(--ease-reveal);
}
.pairing-nodes { --reveal-base: 300ms; --reveal-step: 100ms; }

@media (prefers-reduced-motion: reduce) {
	.pairing.armed .pairing-fan path { stroke-dashoffset: 0; }
	.pairing.armed .pairing-client { opacity: 1; transform: none; }
}

/* --- Mobile --------------------------------------------------------------- */

@media (max-width: 900px) {
	:root { --section-y: var(--space-11); }
	.kja-hero h1 { font-size: var(--text-4xl); }
	.kja-hero::before { width: 420px; height: 420px; left: -130px; bottom: -140px; }
}

/* ===========================================================================
   TEAM PAGE
   From the Magic Patterns mockup (magicpatterns.com/c/8bthqxo6x1tqndt1pbynme),
   mapped onto this project's tokens. The mockup carries its own palette and a
   different display face; see the theme README for what was reconciled.
   =========================================================================== */

/* A soft green ground — the mockup's `frond-wash`. */
.band-green { background: var(--surface-green-soft); }

/* --- Page intro ---------------------------------------------------------- */

.page-intro h1 { font-size: var(--text-5xl); line-height: 1.06; max-width: 16ch; }
.page-intro .lead { margin-top: var(--space-8); max-width: 46ch; }

/* --- Portraits -----------------------------------------------------------
   Photography is 3:4 with a transparent ground; the mockup frames at 4:5 and
   sits the subject on the bottom edge, so the figure is contained rather than
   cropped and never gets its head cut off. */

.person-photo {
	aspect-ratio: 4 / 5;
	background: var(--surface-card);
	overflow: hidden;
	/* Grid rather than block flow: core/image and core/post-featured-image both
	   wrap the picture in a figure, and a percentage height on a figure inside
	   an auto-height parent resolves to auto — the portrait then sets its own
	   height and the aspect box stops being the aspect box. As a grid the
	   figure stretches to the frame instead. */
	display: grid;
}
.person-photo .wp-block-image,
.person-photo .wp-block-post-featured-image {
	margin: 0;
	min-height: 0;
}
.person-photo img {
	width: 100%;
	height: 100%;
	object-fit: contain;
	object-position: bottom;
	display: block;
}

/* Awaiting a real photograph. Deliberately not a stock or generated face. */
.person-photo.is-empty {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: var(--space-5);
	border: 1px dashed var(--border-strong);
	background: var(--surface-sunken);
	color: var(--text-muted);
	font-size: var(--text-xs);
	text-align: center;
	text-wrap: pretty;
}
.person-photo.is-capped { max-width: 16rem; margin-inline: auto; }
@media (min-width: 900px) { .person-photo.is-capped { margin-inline: 0 auto; } }

.person-photo.is-round {
	aspect-ratio: 1;
	border-radius: var(--radius-pill);
	width: 6rem;
	margin-inline: auto;
	background: var(--surface-sunken);
}
/* Round crops fill the circle and hold the head at the top — `contain`, which
   the rectangular portraits want, letterboxes inside the circle instead. */
.person-photo.is-round img { object-fit: cover; object-position: top; }
.person-photo.is-round.is-empty { padding: var(--space-2); font-size: var(--text-2xs); }

.person-name { font-family: var(--font-display); font-size: 1.9rem; line-height: 1.15; color: var(--text-heading); margin: var(--space-6) 0 0; max-width: none; }
.person-role { font-size: var(--text-sm); color: var(--text-muted); margin: var(--space-2) 0 0; max-width: none; }
.person-bio  { margin-top: var(--space-5); }

/* --- Pairing diagram -----------------------------------------------------
   Client at the top, a fan of hairlines, three people beneath. The connectors
   are decorative: they carry no information the labels don't already give, so
   they are hidden from assistive tech and dropped on narrow screens. */

.pairing-client {
	display: inline-block;
	border: 1px solid var(--text-heading);
	padding: var(--space-3) var(--space-7);
	font-family: var(--font-display);
	font-size: var(--text-xl);
	color: var(--text-heading);
}
.pairing-fan { display: block; width: 100%; height: 4rem; color: var(--border-hairline); }
.pairing-stem { display: none; width: 1px; height: 2.5rem; margin-inline: auto; background: var(--border-hairline); }

.pairing-node { text-align: center; }
.pairing-node .pairing-label { font-family: var(--font-display); font-size: var(--text-xl); color: var(--text-heading); margin: var(--space-5) 0 0; max-width: none; }
.pairing-node .pairing-who   { font-size: var(--text-sm); color: var(--text-muted); margin: var(--space-1) 0 0; max-width: none; }
.pairing-node .pairing-note  { font-size: 0.95rem; line-height: var(--leading-relaxed); color: var(--text-body); margin: var(--space-4) auto 0; max-width: 19rem; }

@media (max-width: 780px) {
	.pairing-fan { display: none; }
	.pairing-stem { display: block; }
}

/* --- Roster grid --------------------------------------------------------- */

.roster-grid {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--space-9) var(--space-6);
}
@media (min-width: 640px) { .roster-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .roster-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.roster-star { color: var(--text-muted); }
.roster-clients {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	margin: var(--space-3) 0 0;
	font-size: 0.8rem;
	color: var(--text-body);
	max-width: none;
}
.roster-dots { display: flex; gap: 0.25rem; }
.roster-dots span { width: 0.375rem; height: 0.375rem; border-radius: var(--radius-pill); background: var(--orange-500); }

.roster .wp-block-column { min-width: 0; }
.roster-grid .roster-name,
.roster .roster-name { font-family: var(--font-display); font-size: 1.15rem; line-height: var(--leading-snug); color: var(--text-heading); margin: var(--space-4) 0 0; max-width: none; }
.roster-grid .roster-role,
.roster .roster-role { font-size: var(--text-sm); color: var(--text-muted); margin: var(--space-1) 0 0; max-width: none; }

.footnote {
	margin-top: var(--space-9);
	padding-top: var(--space-6);
	border-top: var(--border-width) solid var(--border-hairline);
	font-size: var(--text-sm);
	font-style: italic;
	color: var(--text-muted);
}

/* ===========================================================================
   SERVICES PAGE
   From the Magic Patterns mockup, mapped onto this project's tokens.
   =========================================================================== */

/* --- Service list --------------------------------------------------------
   A definition list, hairline-separated. The first row has no rule above it,
   so the section opens without a line under the page intro's own border. */

.service-list { margin: 0; --reveal-step: 40ms; }
.service-list > div {
	margin-block: 0;
	padding-block: var(--space-9);
	border-top: var(--border-width) solid var(--border-hairline);
}
.service-list > div:first-child { padding-top: 0; border-top: 0; }

.service-list :is(h2, h3) {
	margin: 0;
	max-width: none;
	font-family: var(--font-display);
	/* The rows were <dt> before, which does not take the heading element's 500. */
	font-weight: 400;
	font-size: 1.65rem;
	line-height: var(--leading-snug);
	letter-spacing: var(--tracking-tight);
	color: var(--text-heading);
}
.service-list p {
	margin: var(--space-4) 0 0;
	max-width: none;
	font-size: var(--text-base);
	line-height: var(--leading-relaxed);
	color: var(--text-body);
}

/* --- Software partners ---------------------------------------------------
   Four bordered cells: the platform's mark, its name, and a one-line note.

   Marks are Simple Icons, inlined rather than linked from their CDN — same
   reasoning as the webfonts, no third-party request — and stripped of their
   `<title>` so they read as decorative, since the name is right beneath them.
   They carry no fill, so `currentColor` themes them.

   ABSS has no Simple Icons entry. Its name is set in the display face in the
   mark slot instead, which is also why it is the one cell without a separate
   name line — the wordmark is the name. */

.partner-row {
	list-style: none;
	margin: var(--space-9) 0 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	border-top: var(--border-width) solid var(--border-hairline);
}
@media (min-width: 640px) { .partner-row { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.partner-row li {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-4);
	padding: var(--space-9) var(--space-5);
	text-align: center;
	border-bottom: var(--border-width) solid var(--border-hairline);
	border-right: var(--border-width) solid var(--border-hairline);
}
.partner-row li:nth-child(2n) { border-right: 0; }
@media (min-width: 640px) {
	.partner-row li { border-bottom: 0; }
	.partner-row li:nth-child(2n) { border-right: var(--border-width) solid var(--border-hairline); }
	.partner-row li:last-child { border-right: 0; }
}

.partner-mark {
	display: flex;
	align-items: center;
	justify-content: center;
	height: 3.5rem;
	color: var(--text-heading);
}
.partner-mark svg { height: 3rem; width: auto; opacity: 0.9; }
.partner-mark .partner-wordmark {
	font-family: var(--font-display);
	font-size: 2.2rem;
	line-height: 1;
	letter-spacing: var(--tracking-wide);
	color: var(--text-heading);
}

.partner-name {
	margin: 0;
	font-family: var(--font-display);
	font-size: var(--text-xl);
	line-height: 1;
	color: var(--text-heading);
	max-width: none;
}
/* ABSS has no logo, so its cell is one line shorter than the rest. Pushing the
   note to the bottom keeps all four notes on the same line. */
.partner-note {
	margin: var(--space-2) 0 0;
	margin-top: auto;
	padding-top: var(--space-2);
	font-size: var(--text-xs);
	line-height: var(--leading-snug);
	color: var(--text-muted);
	max-width: none;
}

/* --- Case study ----------------------------------------------------------
   On the forest ground: prose, then the quote against an ember rule, then a
   strip of foliage closing the band. */

.case-body { margin-top: var(--space-8); }
.case-body p { color: var(--text-inverse-muted); }

.case-quote {
	margin: var(--space-10) 0 0;
	padding-top: var(--space-9);
	border-top: var(--border-width) solid rgba(250, 246, 238, 0.15);
}
.case-quote__grid {
	display: grid;
	gap: var(--space-8);
	align-items: end;
}
@media (min-width: 640px) {
	.case-quote__grid { grid-template-columns: 1fr 12rem; gap: var(--space-10); }
}

/* The Wedoo wordmark is black artwork; on the forest ground it would vanish.
   brightness(0) flattens it to solid black, invert(1) turns that white — alpha
   is preserved either way, so the result is a clean white silhouette. */
.case-quote__logo { height: 1.5rem; width: auto; filter: brightness(0) invert(1); }

.case-quote blockquote {
	margin: var(--space-6) 0 0;
	font-family: var(--font-display);
	font-size: var(--text-xl);
	font-style: italic;
	line-height: 1.5;
	color: var(--text-inverse);
}
@media (min-width: 640px) { .case-quote blockquote { font-size: 1.6rem; } }
.case-quote figcaption {
	margin-top: var(--space-6);
	font-family: var(--font-body);
	font-size: var(--text-sm);
	font-style: normal;
	color: var(--text-inverse-muted);
}
.case-quote__portrait {
	display: block;
	width: 11rem;
	height: auto;
	margin-inline: auto;
	align-self: end;
}
@media (min-width: 640px) { .case-quote__portrait { width: 100%; margin-inline: 0; } }


/* ===========================================================================
   CONTACT PAGE
   The Magic Patterns mockup's treatment — contact points set large in the
   display face with an accent icon, details beside the map — applied to our
   page, which additionally carries a real Google embed and the Simply Schedule
   Appointments booking UI.
   =========================================================================== */

.contact-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--space-6);
}

/* Beside the booking widget the contact points are the alternative route, not
   the headline, so they step down a size and tighten up. */
.contact-list.is-compact { gap: var(--space-5); }
.contact-list.is-compact .contact-link { font-size: 1.15rem; }
.contact-list.is-compact .contact-icon { width: 1.05rem; height: 1.05rem; }
.contact-list.is-compact .contact-note { margin-left: 1.8rem; font-size: var(--text-xs); }
.contact-list.is-compact .contact-address { font-size: var(--text-sm); }

/* The rail that holds them, beside the scheduler. */
.contact-rail { border-left: var(--border-width) solid var(--border-hairline); padding-left: var(--space-8); }
.contact-rail > * + * { margin-top: var(--space-7); }
@media (max-width: 900px) {
	.contact-rail { border-left: 0; border-top: var(--border-width) solid var(--border-hairline); padding-left: 0; padding-top: var(--space-8); }
}

/* Each contact point is a target in its own right, so it is set at heading
   size rather than buried in a paragraph. */
.contact-link {
	display: inline-flex;
	align-items: center;
	gap: var(--space-3);
	font-family: var(--font-display);
	font-size: 1.6rem;
	line-height: 1.2;
	color: var(--text-heading);
	text-decoration: none;
	transition: var(--transition-color);
}
.contact-link:hover,
.contact-link:focus-visible { color: var(--text-accent); text-decoration: none; }

.contact-icon {
	flex: none;
	width: 1.25rem;
	height: 1.25rem;
	color: var(--orange-500);
	stroke: currentColor;
	fill: none;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.contact-note {
	margin: var(--space-1) 0 0 2.1rem;
	font-size: var(--text-sm);
	color: var(--text-muted);
	max-width: none;
}

.contact-address {
	display: flex;
	gap: var(--space-3);
	font-style: normal;
	font-size: var(--text-base);
	line-height: var(--leading-relaxed);
	color: var(--text-body);
}
.contact-address .contact-icon { margin-top: 0.4rem; }
.contact-address span { display: block; }
.contact-address a { font-size: var(--text-sm); }

/* --- Map -----------------------------------------------------------------
   The embed had no styling at all, so the iframe fell back to its intrinsic
   300x150. A ratio box gives it a shape that holds at any width. */

.kja-map { margin: var(--space-7) 0 0; }
.kja-map-frame {
	border: var(--border-width) solid var(--border-hairline);
	background: var(--surface-sunken);
	aspect-ratio: 4 / 3;
}
.kja-map-frame iframe {
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
}
.kja-map figcaption { margin-top: var(--space-3); font-size: var(--text-sm); }

@media (min-width: 900px) { .kja-map-frame { aspect-ratio: 3 / 2; } }

/* --- Booking -------------------------------------------------------------
   Simply Schedule Appointments renders its own interface. This only gives it a
   card to sit on so it reads as part of the page; its internals are left alone
   so plugin updates cannot break our styling, or ours theirs. */

.booking-frame {
	margin-top: var(--space-8);
	padding: var(--space-7);
	background: var(--surface-card);
	border: var(--border-width) solid var(--border-hairline);
	border-radius: var(--radius-md);
}
@media (max-width: 600px) { .booking-frame { padding: var(--space-5); } }

/* ===========================================================================
   HOME (THE FIRM)
   From the Magic Patterns mockup. Deviations from it, both requested:
     - its generated office photo is dropped; the butterfly mark from the logo
       carries the top of the page instead;
     - its generated meeting photo is kept.
   =========================================================================== */

.hero-firm {
	position: relative;
	overflow: hidden;
	background: var(--surface-page);
	padding-top: var(--space-11);
	padding-bottom: var(--space-10);
}

/* The butterfly from the mark, bled off the right edge. It stands in for the
   office photograph the mockup had here, so it is larger and a touch more
   present than a watermark — but still well under the text in contrast. */
.hero-firm::before {
	content: "";
	position: absolute;
	right: -8rem;
	top: 50%;
	translate: 0 -50%;
	width: 40rem;
	height: 40rem;
	background: url("../img/logo-mark.png") no-repeat center / contain;
	opacity: 0.09;
	pointer-events: none;
}
.hero-firm > * { position: relative; }

.hero-firm h1 {
	font-size: 3rem;
	line-height: 1.02;
	max-width: 15ch;
}
@media (min-width: 640px) { .hero-firm h1 { font-size: 4.25rem; } }

/* The mockup sets one word of the headline in italic frond — the only place
   green appears as type on the site. */
.hero-firm h1 em { font-style: italic; color: var(--surface-forest); }

.hero-firm .lead { margin-top: var(--space-8); max-width: 44ch; }
.hero-firm .wp-block-buttons { margin-top: var(--space-8); }

.hero-caption {
	margin-top: var(--space-9);
	font-size: var(--text-sm);
	color: var(--text-muted);
	max-width: 52ch;
}

@media (max-width: 900px) {
	.hero-firm::before { width: 26rem; height: 26rem; right: -9rem; opacity: 0.07; }
}

/* --- Drop cap ------------------------------------------------------------
   Opens the firm's story. EB Garamond has the classical proportions this
   needs; the cap is set in ember to tie back to the mark. */

.drop-cap {
	font-family: var(--font-display);
	font-size: 1.5rem;
	line-height: 1.55;
	color: var(--text-heading);
	max-width: 40ch;
}
.drop-cap::first-letter {
	float: left;
	font-family: var(--font-display);
	font-size: 3.9rem;
	line-height: 0.82;
	padding: 0.12em 0.12em 0 0;
	color: var(--orange-500);
}

/* An inline aside set in the display face, a size up and italic. */
.emphasis {
	font-family: var(--font-display);
	font-size: 1.2em;
	font-style: italic;
	color: var(--text-heading);
}

/* --- How we work ---------------------------------------------------------
   Four passages, hairline-separated. Same shape as the services list, but the
   headings carry more weight because each one is a claim about the firm. */

.passage-list { --reveal-step: 40ms; }
.passage-list > article {
	margin-block: 0;
	padding-block: var(--space-9);
	border-top: var(--border-width) solid var(--border-hairline);
}
.passage-list > article:first-child { padding-top: 0; border-top: 0; }
.passage-list > article:last-child { padding-bottom: 0; }

.passage-list h2 {
	margin: 0;
	font-size: 1.75rem;
	line-height: 1.25;
	max-width: 26ch;
}
@media (min-width: 640px) { .passage-list h2 { font-size: 2rem; } }
.passage-list p { margin: var(--space-5) 0 0; }

/* --- Quote band ----------------------------------------------------------
   The kitchen quote, centred on the forest ground. */

.quote-band { padding-block: var(--space-11); }
@media (min-width: 640px) { .quote-band { padding-block: var(--space-13); } }

.quote-band blockquote {
	margin: 0;
	padding: 0;
	border: 0;
	font-family: var(--font-display);
	font-size: 1.75rem;
	font-style: italic;
	line-height: 1.35;
	/* core/quote is tracked tighter in theme.json; this quote is set optically. */
	letter-spacing: normal;
	color: var(--text-inverse);
}
@media (min-width: 640px) { .quote-band blockquote { font-size: 2.25rem; } }

/* core/quote holds its text in a paragraph, which would otherwise pick up the
   global measure and the band's muted body colour. */
.quote-band blockquote p {
	margin: 0;
	font: inherit;
	color: inherit;
	max-width: none;
}
.quote-band blockquote cite {
	display: block;
	margin-top: var(--space-8);
	font-family: var(--font-body);
	font-size: var(--text-sm);
	/* cite sits inside the blockquote, so it would inherit the quote's display
	   leading; the attribution is body copy and wants the body's. */
	line-height: var(--leading-relaxed);
	font-style: normal;
	color: var(--text-inverse-muted);
}

/* --- Inline quote --------------------------------------------------------
   The Wedoo line, against an ember rule. */

.inline-quote {
	margin: var(--space-10) 0 0;
	padding-left: var(--space-6);
	border-left: 2px solid var(--rule-accent);
}
.inline-quote p {
	margin: 0;
	font-family: var(--font-display);
	font-size: var(--text-2xl);
	font-style: italic;
	line-height: var(--leading-snug);
	color: var(--text-heading);
	max-width: none;
}
.inline-quote footer {
	margin-top: var(--space-4);
	font-family: var(--font-body);
	font-size: var(--text-sm);
	color: var(--text-muted);
}

.figure-plain { margin: var(--space-9) 0 0; }
.figure-plain img { display: block; width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover; }

/* ===========================================================================
   RESEARCH & PUBLICATIONS  (Team page)
   =========================================================================== */

/* ===========================================================================
   RECORDS RENDERED BY QUERY LOOP
   The People and Publications records reach the page through core Query Loop
   blocks, so these rules dress core's own markup: a <ul class="wp-block-post-
   template"> of <li class="wp-block-post">, post titles rendered as <p>, and
   one bound paragraph per field. Nothing here is bespoke to a custom block —
   the layout stays editable in the site editor.
   =========================================================================== */

/* core's flow layout puts a block-gap margin on every child but the first.
   Down a list that reads as spacing; across a grid row it drops every card but
   the leftmost by the same amount. These lists space themselves. */
.roster-grid > li,
.person-grid .wp-block-post-template > li,
.pub-list > li { margin-block: 0; }

/* Partners, two cards side by side. `.split` only styles core/columns, and the
   post template is a list. */
.person-grid .wp-block-post-template {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--space-10);
}
@media (min-width: 900px) {
	.person-grid .wp-block-post-template { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Role and credentials are two fields set as one line, with the separator
   drawn rather than written so neither field has to carry punctuation. */
.person-role,
.roster-role,
.pub-meta { gap: 0 0.5ch; }
.person-role p,
.roster-role p,
.pub-meta p,
.roster-clients p,
.pub-cites p { margin: 0; max-width: none; }
/* The separator hangs off the end of the preceding field rather than the start
   of the next, so a line that wraps does not begin with a stray middot. */
.person-role p:not(:last-child)::after,
.roster-role p:not(:last-child)::after,
.pub-meta p:not(:last-child)::after { content: " ·"; }

/* The asterisk the footnote explains, from the Provisional checkbox. */
.is-provisional .roster-name::after { content: " *"; color: var(--text-muted); }

/* Publication rows. The middle column is a group now rather than a span, and
   the citation figure is two bound paragraphs rather than a <b> and a label. */
.pub-title a { color: inherit; text-decoration: none; }
.pub-row:hover .pub-title a { opacity: 0.72; }
.pub-title a[target="_blank"]::after { content: " ↗"; font-size: 0.75em; color: var(--text-muted); }
.pub-journal { font-style: italic; }
.pub-cites .pub-cites-n {
	font-family: var(--font-display);
	font-weight: 400;
	font-size: 1.5rem;
	line-height: 1;
	color: var(--text-heading);
}
.pub-cites p + p {
	margin-top: var(--space-1);
	font-size: var(--text-2xs);
	letter-spacing: var(--tracking-eyebrow);
	text-transform: uppercase;
	color: var(--text-muted);
}

/* A bound field with nothing in it still renders its paragraph. An uncited
   paper would otherwise read "0 citations", so the row carries the flag and
   the figure is hidden outright. */
.wp-block-post:not(.has-citations) .pub-cites { display: none; }
.pub-note:empty { display: none; }

.research-metrics {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--space-8) var(--space-7);
	margin: 0;
	--reveal-step: 60ms;
}
@media (min-width: 760px) { .research-metrics { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.research-metrics > div { margin-block: 0; border-top: 2px solid var(--rule-accent); padding-top: var(--space-5); }
.research-metrics .metric-value {
	margin: 0;
	max-width: none;
	font-family: var(--font-display);
	font-size: 2.75rem;
	line-height: 1;
	color: var(--surface-forest);
}
@media (min-width: 640px) { .research-metrics .metric-value { font-size: 3.5rem; } }
.research-metrics .metric-label {
	margin-top: var(--space-4);
	margin-bottom: 0;
	font-size: var(--text-sm);
	line-height: var(--leading-snug);
	color: var(--text-muted);
	max-width: 22ch;
}

/* --- Publication rows ----------------------------------------------------
   Year in the margin, the paper in the middle, its citation count set as a
   figure on the right. */

.pub-list { list-style: none; margin: var(--space-10) 0 0; padding: 0; }
.pub-list > li { border-top: var(--border-width) solid var(--border-hairline); }

.pub-row {
	display: grid;
	gap: var(--space-2);
	padding-block: var(--space-7);
	color: inherit;
	text-decoration: none;
}
@media (min-width: 760px) {
	.pub-row { grid-template-columns: 4.5rem 1fr 7rem; gap: var(--space-6); align-items: start; }
}
a.pub-row:hover { opacity: 0.72; text-decoration: none; }

.pub-year { font-size: var(--text-sm); color: var(--text-muted); }
.pub-title {
	font-family: var(--font-display);
	font-size: 1.3rem;
	line-height: 1.35;
	color: var(--text-heading);
	margin: 0;
	/* The title used to be an inline span, where a max-width did nothing. As a
	   block it would wrap far too early, so the measure comes off. */
	max-width: none;
}
@media (min-width: 640px) { .pub-title { font-size: 1.45rem; } }
.pub-title .pub-ext { font-size: 0.75em; color: var(--text-muted); }

.pub-meta { margin: var(--space-3) 0 0; font-size: 0.9rem; line-height: var(--leading-relaxed); color: var(--text-body); max-width: 60ch; }
.pub-meta em { font-style: italic; }
.pub-note { margin: var(--space-1) 0 0; font-size: 0.85rem; font-style: italic; color: var(--text-muted); max-width: 60ch; }

/* The citation figure. On wide screens it sits right-aligned in its own
   column; stacked below the paper on narrow ones. */
.pub-cites { text-align: left; }
@media (min-width: 760px) { .pub-cites { text-align: right; } }

.pub-cites b {
	display: block;
	font-family: var(--font-display);
	font-weight: 400;
	font-size: 1.5rem;
	line-height: 1;
	color: var(--text-heading);
}
.pub-cites > span:not(.pub-level) {
	display: block;
	margin-top: var(--space-1);
	font-size: var(--text-2xs);
	letter-spacing: var(--tracking-eyebrow);
	text-transform: uppercase;
	color: var(--text-muted);
}

/* A level glyph rather than a bar: discrete marks read as an order of
   magnitude without implying the precision a measured bar would. The scale is
   logarithmic, because the counts run from 5 to 97 and a linear scale would
   flatten everything below the most-cited paper into nothing. */
.pub-level { display: flex; align-items: flex-end; gap: 2px; margin-top: var(--space-2); }
@media (min-width: 760px) { .pub-level { justify-content: flex-end; } }
/* `flex: none` matters twice over: without it the marks stretch to the height
   of the grid row and shrink below their 3px width. */
.pub-level i { flex: none; display: block; width: 3px; height: 0.85rem; background: var(--leaf-500); }

/* --- Disclosure ----------------------------------------------------------
   Native <details>, so it works without JavaScript and is keyboard- and
   screen-reader-accessible for free. */

.pub-more { border-top: var(--border-width) solid var(--border-hairline); }
.pub-more > summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	padding-block: var(--space-6);
	font-size: var(--text-sm);
	color: var(--text-body);
	cursor: pointer;
	list-style: none;
	transition: var(--transition-color);
}
.pub-more > summary::-webkit-details-marker { display: none; }
.pub-more > summary:hover { color: var(--text-heading); }
.pub-more > summary::after {
	content: "+";
	font-family: var(--font-display);
	font-size: 1.4rem;
	line-height: 1;
	color: var(--orange-500);
}
.pub-more[open] > summary::after { content: "–"; }
.pub-more .pub-list { margin-top: 0; }
.pub-more .pub-list > li:first-child { border-top: 0; }

.pub-scholar {
	display: inline-block;
	margin-top: var(--space-8);
	font-size: var(--text-sm);
}
