/*
Theme Name: Leading Lights
Theme URI: https://leadinglights.com.au
Author: Zenova Ventures
Author URI: https://zenovaventures.com
Description: Block theme for Leading Lights Early Intervention, Phillip ACT. Warm off-white ground, indigo for anything that is read, watercolour artwork throughout. Built to be maintained by whoever comes next.
Version: 1.4.48
Requires at least: 6.5
Tested up to: 6.9
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: leading-lights
Tags: block-theme, accessibility-ready, one-column, custom-colors, custom-logo
*/

/*
  Almost everything lives in theme.json. This file carries the theme header
  that WordPress requires, plus the handful of rules that theme.json cannot
  express yet.

  Section order:
    1.  Tokens and base
    2.  Accessibility
    3.  Header
    3b. Header treatments: the indigo plate and the three that use it
    3c. Header treatments: the full-width band
    4.  Section rhythm: the width, ground and spacing scale
    5.  Banner
    6.  Cards
    7.  Reading pages
    8.  Tables
    9.  Staff grid
    10. Blog
    11. Footer
    12. Motion
*/

/* ---------------------------------------------------------------------------
   1. Tokens and base
   --------------------------------------------------------------------------- */

:root {
	/* --ll-sun and --ll-moon lived here as aliases for the banner SVG's moon.
	   The moon came out on 28 Aug (see patterns/banner.php) and nothing else
	   ever referenced them, so they went with it. The bands reach the palette
	   through --wp--preset--color--* directly. */

	/* The three widths. Everything on the site is one of these, which is what
	   stops the page reading as one stack of identical bands. */
	/* Rohan, 4 Sep, marked up on the running page: the writing should run from
	   where "Services" starts in the nav to where "Blogs" finishes. Measured on
	   the page at a 1875px window that span is 1037px, against the 720px this
	   was. MUST MATCH theme.json's contentSize, which is what actually lays the
	   blocks out; this token only mirrors it for the rules that read a width. */
	--ll-text: 1040px;
	--ll-wide: 1140px;

	/* Section rhythm. Related blocks sit at --ll-gap-near, separate sections at
	   --ll-gap-far. Nothing should invent a third value. */
	--ll-gap-near: var(--wp--preset--spacing--60);
	--ll-gap-far: var(--wp--preset--spacing--70);

	/*
	   MOBILE AUDIT, 7 Sep. These two were the same on a 375px phone as on a
	   1440px desktop: 96px above and below every section. Measured on the front
	   page, that was 192px of padding on each of seven sections, 1,344px of
	   nothing, on a page already 10.6 screens long. The Acknowledgement section
	   was 287px tall to hold two lines of text.

	   Air that reads as generous at 1440 reads as a hole at 375, because the
	   reader is paying for it in scroll rather than in glance. Stepping the two
	   tokens is the whole fix: every section, every card gap and every heading
	   rhythm on the site is expressed in these, so nothing else has to change
	   and nothing can drift out of step with them.

	   The override is a plain media query below, not nested in here. Nested CSS
	   is new enough that a browser without it would drop the rule and silently
	   serve a phone the desktop rhythm, which is the exact class of failure
	   this audit was looking for.
	*/

	/* THE THREE PICTURE SIZES.
	   Measured first: every painting was rendering at 1140x851, and the sticky
	   header is 218px, so on a 1280x720 laptop only 502px of page is visible.
	   Each painting was 170% of that, and there were eighteen of them at exactly
	   the same size. Scrolled to the middle of one, a visitor saw no text at all.

	   Nothing is cropped. The ink in these paintings was measured: the tightest,
	   place.jpg, tolerates 1.35:1 against a native 1.34:1, so there is no crop
	   headroom in the set and an object-fit: cover would cut children out of five
	   of them. The picture only ever gets smaller, never trimmed.

	   Expressed as WIDTHS, because core ships
	   .wp-block-image.alignwide img { width: 100% } and that outranks anything
	   sane we can say about the img. Capping the figure lets core's rule do the
	   work: the picture fills a narrower figure and keeps its own 4:3. Capping the
	   img's height instead just squashes it, which is what the first attempt did.

	   vh so the picture answers to the screen it is on, clamped so it neither
	   collapses on a short window nor takes over a tall one. The vh figures are
	   the heights we want multiplied by the 1200/896 these are drawn at. Below
	   about 600px wide the column is narrower than all of these and the container
	   wins, which is why phones were already right and are untouched. */
	--ll-art-full: clamp(380px, 58vh, 620px);
	--ll-art-half: clamp(340px, 46vh, 645px);
	--ll-art-spot: clamp(230px, 31vh, 440px);
	--ll-art-open: clamp(440px, 72vh, 820px);

	/* THE MASTHEAD, and the three numbers that follow from it.

	   Bel, 4 Sep: "The name of the company is lost because of how little it
	   is", and "three to four times bigger". 244px is her reference, the width
	   the site-logo block was set to.

	   The lockup is 1.4163:1, measured off assets/logo.png at 1024 x 723, so
	   every 100px of logo width costs 70.6px of header height. That is the
	   whole constraint, and it is why this ships at 2x rather than at her 3x:

	     x     logo w   logo h   header at rest   of a 720 window   wordmark cap
	     1.0    244      172        296px             41%              18.3px
	     1.5    366      258        382px             53%              27.5px
	     2.0    488      345        469px             65%              36.7px
	     2.5    610      431        555px             77%              45.9px
	     3.0    732      517        641px             89%              55.0px
	     4.0    976      689        813px            113%              73.4px

	   Cap height is the "L" of Leading, measured at 77px on the 1024px file.

	   1.75x IS ROHAN'S CALL, 5 Sep, down from the 3x of the day before. The
	   reason is her master: measured off it, the moon is 48% of the ribbon's
	   depth, which is what puts the walking figures AMONG the bands rather than
	   towering over them. At 3x the moon was 91% of the ribbon and the logo sat
	   clean below it. 1.75x is the size that reproduces her composition. The two
	   halves of Bel's brief do not both fit: "three or four times" and "the
	   entire top quarter of the page" are different instructions, and a quarter
	   of a 1280x720 laptop is 180px, smaller than the header was already. She
	   said the multiple twice and the quarter once, so the multiple wins.

	   WHAT IT COSTS, so nobody rediscovers it: the header rests at about 420px
	   on a 1280 laptop rather than 639px, so the front page's first words are
	   back above the fold. The sticky rule in 3c is what makes that
	   survivable. The logo row parks above the viewport on the first scroll and
	   only --ll-bar-h stays pinned, so the tall header is the opening view and
	   nothing else. Nothing downstream moves: --ll-header-h resolves to
	   --ll-bar-h under these treatments, so the artwork ceiling and the scroll
	   padding are computed off the bar and never saw the logo.

	   TO CHANGE THE SCALE, move the vw and the cap together. 38vw reaches 2x on
	   a 1280 laptop; 33vw is 1.75x there. The 427px cap is 1.75x, so raising the
	   vw alone does nothing above 1294px, where the cap takes over.
	   Everything else on this page follows from these two numbers. */
	/* 7 SEP: 427px to 480px, the other half of the change above. 3.15x the
	   original 152px rather than 2.8x. It buys 3.8px of cap height on the
	   wordmark and costs 37px of header, which is the trade Rohan took: with
	   the menu at 26px the name now leads it 1.87 to 1.

	   The ribbon's -8 translate is swept against the lockup and this moves the
	   lockup. Resweep it if any light comes off its band. */
	--ll-logo-w: clamp(280px, 36vw, 480px);

	/* 1.4163 is assets/logo.png, 1024 x 723. RECOMPUTE IT if
	   build/make-header-logos.py ever changes the crop; it is written as a
	   literal rather than a var because calc() dividing by a custom property is
	   the one piece of this arithmetic with any browser history. */
	--ll-logo-h: calc(var(--ll-logo-w) / 1.4163);

	/* "At least like double in size", said three separate times. Today's nav is
	   medium, 17px, so 2.125rem is exactly double and the clamp reaches it at
	   about 1400px. Below that the row physically will not take it: the seven
	   labels measure 31.29em in Nunito Sans 400, so at 1280 the widest that
	   fits beside the button is about 31.4px, which is 1.85x. Shortening two
	   labels is the only way to reach 2x on a 1280 laptop and that is a content
	   decision, not a CSS one.

	   The 1.25rem floor is still above today's 17px. It is reached below about
	   816px, where 31.29em of labels no longer fits on one line at any size we
	   would want; the nav wraps to two lines there instead, and --ll-nav-row
	   grows with it so the arithmetic below stays true.

	   7 SEP: THE CEILING CAME DOWN FROM 2.125rem TO 1.625rem, and the reason is
	   the thing double was competing with. Measured on the running page, "About
	   Us" had a cap height of 23.9px against 30.4px for "Leading Lights": the
	   practice name was 1.27x its own menu. A wordmark normally sits 2 to 3x.

	   The name cannot answer that by growing. Measured off assets/logo.png, the
	   words are 73px of a 723px lockup, a tenth of its height, and there is 7%
	   transparent margin, so there is nothing to crop. Doubling the name means
	   roughly doubling a header that already takes half of a 900px screen. The
	   only thing that can move is the menu.

	   26px is still half again the 17px Bel objected to, and it puts the nav
	   back under the section headings, which are x-large at 32px. At 34px the
	   menu was larger than every heading on every page of the site. */
	--ll-nav-size: clamp(1.25rem, 2.45vw, 1.625rem);
	--ll-nav-row: 3rem;

	/*
	   7 SEP: BOTH CAME DOWN TO BRING THE HEADER BACK UNDER HALF THE SCREEN.

	   The lockup went to 480px on the same day and took the header to 488px,
	   which is 54% of a 900px laptop: over half the first screen spent before
	   a word of the page. These two are the only slack in it, because the
	   lockup itself is 339px of that 488 and Rohan has just chosen that size.

	   The row gap can afford to lose the most. logo.png carries 43px of
	   transparent margin under the lockup out of 723, which renders as 20px of
	   air below the wordmark that no rule can see, so 8px here still reads as
	   about 28px between the name and the menu.

	   Anything that changes these has to be swept against the ribbon: the bands
	   are anchored to the top of the header and the lockup is not, so moving
	   the padding slides the lockup through them. Checked after this change,
	   all seven lights still sit on a band.
	*/
	--ll-head-pad: 0.875rem;
	--ll-head-rowgap: 0.5rem;

	/* Resting and pinned heights, COMPUTED rather than measured, so they can
	   never fall out of step with the four numbers above. The pinned height is
	   what a reader has on screen for the whole page; the resting height is
	   only what they see before they scroll.

	   The pin shows the BOTTOM --ll-bar-h pixels of the header, so what goes
	   into that number is what a reader sees, in order from the bottom up: the
	   header's own bottom padding, the nav row, and then as much of the gap
	   above the nav as we ask for. Asking for the whole gap is what makes the
	   nav sit level, 24px of clear space above and 24px below. Measured with
	   half the gap first, which put the nav 12px from the top of an 89px bar
	   and 24px from the bottom, and it reads as pushed up. 100px at every
	   width, down from 240px today. */
	--ll-header-rest: calc(var(--ll-head-pad) * 2 + var(--ll-logo-h) + var(--ll-head-rowgap) + var(--ll-nav-row));
	--ll-bar-h: calc(var(--ll-head-pad) + var(--ll-nav-row) + var(--ll-head-rowgap));

	/* Her plan B ground. See section 3c. */
	--ll-warm: var(--wp--preset--color--moon);

	/* The resting height of the sticky header, which is dead space on every
	   screen: 1.5rem of padding top and bottom, around a 252px logo at 1.36:1
	   sitting on a plate with 0.85rem of its own padding. Measured, not derived:
	   240px at 1280x720.

	   THIS IS THE FOUR TREATMENTS IN SECTION 3b ONLY. The three band treatments
	   in 3c override it on the body with --ll-bar-h, because under those the
	   masthead scrolls away and the 100px pinned bar is the header a reader
	   actually has on screen. See the body rule in section 7.

	   RECOMPUTE THIS if either the logo cap or the plate padding in section 3
	   changes. It went 234 to 240 when the plate padding was evened up, and the
	   ceiling below is measured against whatever is left of the window. */
	--ll-header-h: 240px;

	/* THE CEILING has moved to the body rule in section 7. It reads
	   --ll-header-h, and --ll-header-h now depends on the header treatment,
	   which is a class on the body. Declared here it would have resolved
	   against the :root value and the override would have arrived too late. */

	--ll-hairline: rgba(32, 23, 100, 0.12);
	--ll-card-shadow: 0 1px 2px rgba(32, 23, 100, 0.05), 0 18px 40px -28px rgba(32, 23, 100, 0.4);
}

/* The mark comes down on a phone, where 38vw of a 375px screen would be 142px
   and the clamp floor would otherwise put a 300px logo on a 375px screen.

   599.98 rather than 600 throughout this file's new rules, because that is
   where core switches the navigation burger on and every boundary here has to
   land on the same pixel. See the phone block in section 3c. */
@media (max-width: 599.98px) {
	:root {
		--ll-logo-w: min(64vw, 260px);
	}
}

/* The one window where the labels have to wrap. 31.29em at the 1.25rem floor
   is 626px of text, and the bar runs out of room at about 700px once the gaps
   are counted, so from the burger breakpoint at 600px up to 720px the nav takes
   two lines. The row it sits in has to be two lines tall or the pinned bar in
   section 3c would clip it. Below 600px the burger replaces the labels
   entirely and the row goes back to one line, which is why this has a floor as
   well as a ceiling, and why the floor is core's 600px and not 601. Everything downstream is computed from this token, so the
   resting height, the sticky offset and the artwork ceiling all follow. */
@media (min-width: 600px) and (max-width: 720px) {
	:root {
		--ll-nav-row: 5.5rem;
	}
}

/* Images should never overflow their container. */
img,
svg,
video {
	max-width: 100%;
	height: auto;
}

/* Watercolour artwork sits on the warm ground, so give it the same tone
   behind it while it loads rather than a flash of white. */
.wp-block-image img {
	background-color: var(--wp--preset--color--ground-tint);
}

/* See the note on --ll-gap-near / --ll-gap-far above. */
@media (max-width: 720px) {
	:root {
		--ll-gap-near: 2.25rem;
		--ll-gap-far: 3.25rem;
	}
}

/* ---------------------------------------------------------------------------
   2. Accessibility
   --------------------------------------------------------------------------- */

/* Keyboard focus must always be visible. The palette is pale, so the ring is
   indigo with an offset rather than a tint that would disappear on a wash. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline: 2px solid var(--wp--preset--color--indigo);
	outline-offset: 2px;
	border-radius: 2px;
}

/* On the indigo enquiry band an indigo ring is invisible. The footer used to be
   listed here too; it is the page ground now, where sun #FFF9C7 measures 1.06:1
   and the ring would not exist. */
.ll-section--dark :where(a, button, summary):focus-visible {
	outline-color: var(--wp--preset--color--sun);
}

/* Skip link, hidden until focused. */
.skip-link {
	position: absolute;
	left: -9999px;
	top: 0;
	z-index: 100000;
	padding: 0.75rem 1.25rem;
	background: var(--wp--preset--color--indigo);
	color: var(--wp--preset--color--ground);
	text-decoration: none;
	font-weight: 600;
}

.skip-link:focus {
	left: 0;
}

/* ---------------------------------------------------------------------------
   3. Header

   Was a centred logo stacked over a centred nav, which gave the page no axis
   and no call to action. Now a left mark and a right cluster, and it sticks so
   navigation survives the 5,000px team page.
   --------------------------------------------------------------------------- */

/* The sticky belongs on the template part's own wrapper, not on .ll-header.
   A template part with a tagName renders an element of its own around the
   pattern, and a sticky child can only travel inside its parent's box. That box
   is exactly the height of the header, so the sticky had nowhere to go and the
   bar scrolled off the top of every page, measured at -596px on the front page
   at a scroll of 700. The wrapper carries the sticky; .ll-header keeps the
   paint, the padding and the is-stuck class motion.js sets.

   The band treatments in 3c give this a NEGATIVE top, so the logo row scrolls
   away and only the nav row pins. That is not a shrink and does not touch the
   jitter documented below: the element keeps one height at every scroll
   position, so the document height never changes and scroll anchoring has
   nothing to correct. */
.wp-site-blocks > header.wp-block-template-part {
	position: sticky;
	top: 0;
	z-index: 90;
}

/* The admin bar is fixed on desktop and would sit over a sticky header. */
.admin-bar .wp-site-blocks > header.wp-block-template-part {
	top: 32px;
}

@media (max-width: 782px) {
	.admin-bar .wp-site-blocks > header.wp-block-template-part {
		top: 46px;
	}
}

/* Anything jumped to by a fragment has to clear the pinned bar. There was no
   scroll-padding at all before this, which was already wrong for the in-page
   links and is load-bearing for the jump list going onto Services.

   ONE RULE, ON THE SCROLL CONTAINER. scroll-padding here and scroll-margin on
   a target ADD, so a second offset added anywhere else lands a jumped-to
   heading twice as far down the page as it should be. If a target ever needs
   more room, change this number. */
html {
	scroll-padding-top: calc(var(--ll-bar-h) + 1rem);
}

/* The four treatments in 3b never scroll their masthead away, so a fragment has
   to clear the whole 240px rather than the 100px pinned bar. scroll-padding only
   does anything on the scroll container, which is the root element, and the
   treatment is a class on the body, so the root has to ask about its own body.
   A browser without :has() ignores this and keeps the line above, which is the
   right answer for the default treatment. */
html:has(body:is(.ll-header-plate, .ll-header-plate-scroll, .ll-header-swap, .ll-header-solid)) {
	scroll-padding-top: calc(var(--ll-header-h) + 1rem);
}

/* On a phone no treatment pins a nav row, they all carry the whole masthead
   (see the 600px block in 3c), so the offset is the mark plus its padding for
   all seven. Both selectors are repeated so this matches the weight of the
   :has() rule above and wins on source order rather than by accident. The
   value is a little generous for the four in 3b, whose logo cap is smaller,
   and a jump landing 30px low on a phone is not worth a third rule. */
@media (max-width: 599.98px) {
	html,
	html:has(body:is(.ll-header-plate, .ll-header-plate-scroll, .ll-header-swap, .ll-header-solid)) {
		scroll-padding-top: calc(var(--ll-head-pad) * 2 + var(--ll-logo-h) + 1rem);
	}
}

/*
   THE HEADER MUST NOT CHANGE HEIGHT ON SCROLL. Paint only.

   It used to shrink: padding-block dropped to 0.85rem and the logo cap from
   224px to 156px, taking the header from 218px to 146px. That produced a
   permanent jitter at the top of every page, reported from the Contact page.

   The mechanism, measured: this header is position:sticky, which stays in the
   document flow, so shrinking it by 72px shortened the document by exactly 72px
   (2947 to 2875 on Contact). The browser's scroll anchoring, overflow-anchor is
   auto by default, then moved scrollY by about the same 72px to keep the view
   steady. That carried scrollY back across the 8px threshold, the class toggled
   again, and the two fought each other every frame. The logo shook hardest
   because it was the element resizing most.

   This is not fixable by tuning the threshold. Saving vertical space by
   shrinking an in-flow sticky element MEANS changing the document height, and
   any threshold the height change straddles will oscillate. The only ways to
   keep a shrink are position:fixed with reserved padding, or a wrapper of fixed
   height, and both give back the space they were trying to save.

   So: border and shadow on scroll, nothing that touches layout. If the resting
   height is too tall, and 218px of sticky bar is a lot, the fix is a wider,
   shorter logo lockup, not a scroll animation. The mark is 4:3, so its width is
   what sets the header's height.
*/
.ll-header {
	padding-block: var(--wp--preset--spacing--40);
	border-bottom: 1px solid transparent;
	transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

/* Set from motion.js once the page has moved. Lifts the bar off the page once
   it is actually overlapping content. Paint only: no size, no spacing. */
.ll-header.is-stuck {
	border-bottom-color: var(--ll-hairline);
	box-shadow: 0 6px 20px -18px rgba(32, 23, 100, 0.55);
}

.ll-header__bar {
	gap: var(--wp--preset--spacing--50);
	flex-wrap: nowrap;
}

/* The lockup is 4:3, so every extra pixel of width costs three quarters of a
   pixel of header height. At 170px the wordmark inside it was under 11px cap
   height, which is why it read as a smudge rather than a name. The size is
   constant: it no longer tightens on scroll, because that resize is what drove
   the jitter documented above. */
.ll-header__logo {
	margin: 0;
	flex: 0 0 auto;
	min-width: 0;
	max-width: clamp(214px, 21vw, 272px);

	/* The reversed lockup rides here as a background, under the site-logo img.
	   Three of the four treatments below want cream letters, because in all
	   three the mark sits on indigo; only the swap treatment shows the ink one,
	   and only until the page moves.

	   Background rather than a second img so the header markup stays a plain
	   wp:site-logo block. The img keeps the alt text and stays in the
	   accessibility tree at opacity 0, so the logo is still named either way.
	   Both files are cropped to one shared box by build/make-header-logos.py,
	   which is what lets them cross-fade without the wordmark shifting. */
	background-image: url( assets/logo-reversed.png );
	background-size: contain;
	background-position: left center;
	background-repeat: no-repeat;
}

@media (prefers-reduced-motion: reduce) {
	.ll-header {
		transition: none;
	}
}

.ll-header__logo img {
	display: block;
	width: 100%;
	height: auto;
	background: none;
}

.ll-header__right {
	gap: var(--wp--preset--spacing--50);
}

/* The nav sits on the baseline of the mark, not centred under it. Seven items
   plus a button is a lot for one row, so it stays on one line and the spacing
   tightens rather than the menu wrapping. */
.ll-nav .wp-block-navigation__container:not(.has-modal-open) {
	flex-wrap: nowrap;
	gap: clamp(0.9rem, 1.6vw, 1.5rem);
}

/*
   The mark under a nav item is drawn from the centre out rather than switched
   on. It was a border-bottom that changed colour, which is a light switch: the
   full-width rule simply exists or does not.

   Two things fall out of doing it with a pseudo-element instead. It can be
   animated, which a border colour cannot be in any way that reads as drawing.
   And it takes `currentColor`, so the indigo-bar treatments in section 3b get
   the right underline colour for free from the colour they already set on the
   link. Three duplicated border-colour rules went when this arrived.
*/
.ll-nav .wp-block-navigation-item__content {
	position: relative;
	text-decoration: none;
	padding-block: 0.35rem;
	transition: color 0.2s ease;
	white-space: nowrap;
}

.ll-nav .wp-block-navigation-item__content::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 2px;
	background: currentColor;
	transform: scaleX(0);
	transform-origin: center;
	transition: transform 0.18s cubic-bezier(0.22, 1, 0.36, 1);
}

.ll-nav .wp-block-navigation-item__content:hover {
	color: var(--wp--preset--color--plum-deep);
}

.ll-nav .wp-block-navigation-item__content:hover::after,
.ll-nav .wp-block-navigation-item__content:focus-visible::after,
.ll-nav .current-menu-item > .wp-block-navigation-item__content::after {
	transform: scaleX(1);
}

/* The current page is a statement of fact rather than a response to the
   pointer, so it does not animate in on load. */
.ll-nav .current-menu-item > .wp-block-navigation-item__content::after {
	transition: none;
}

.ll-header__cta .wp-block-button__link {
	padding: 0.6rem 1.25rem;
	font-size: var(--wp--preset--font-size--small);
	white-space: nowrap;
}

/* Seven nav items and a button stop fitting well before the burger appears.
   The wp-block-buttons class is carried for weight: WordPress sets the layout
   to flex at a higher specificity, and that would keep the button visible. */
@media (max-width: 1180px) {
	.ll-header__cta.wp-block-buttons {
		display: none;
	}
}

@media (max-width: 600px) {
	/* Constant, like the desktop cap. The is-stuck variant that used to sit
	   here took 168px down to 132px, which moved the header 27px and gave phones
	   the same scroll jitter as desktop. See the note above .ll-header. */
	.ll-header__logo {
		max-width: 186px;
	}
}

/* ---------------------------------------------------------------------------
   3b. HEADER TREATMENTS

   Four ways of bringing the indigo lockup into the header, all four live, one
   active at a time. Switch by defining LL_HEADER_STYLE in wp-config.php or by
   editing the default in functions.php:

       plate         indigo panel behind the logo, cream bar, never changes
       plate-scroll  the panel at rest, whole bar fills to indigo on scroll  <- active
       swap          today's ink logo on cream, cross-fading to the reversed
                     lockup once the page moves
       solid         indigo bar on every page at every scroll position

   plate-scroll is the default because it is the only one that satisfies both
   halves of the brief: the indigo logo is the first thing a visitor sees, and
   the page they see it on is still warm cream.

   THE RULE FROM SECTION 3 STILL HOLDS: none of these may change the header's
   height on scroll. Every scrolled state here changes colour, shadow and
   opacity only. In particular the plate's padding does not move between states
  , box-sizing is border-box, so dropping its inline padding would widen the
   image inside the same cap and take the header's height with it, which is the
   exact jitter documented above.
   --------------------------------------------------------------------------- */

/* The ink logo only ever shows for the swap treatment, at rest. Everywhere else
   the background lockup is the one that should be seen. */
.ll-header-plate .ll-header__logo img,
.ll-header-plate-scroll .ll-header__logo img,
.ll-header-solid .ll-header__logo img {
	opacity: 0;
}

.ll-header-swap .ll-header__logo img {
	opacity: 1;
	transition: opacity 0.4s ease;
}

.ll-header-swap .ll-header.is-stuck .ll-header__logo img {
	opacity: 0;
}

/* The plate.
   A soft moon-yellow bloom was tried here first and it did nothing at all.
   Measured after the fact and obvious in hindsight: the moon is #FCE096 and the
   page ground is #FCFAF3, so a wash of the first at 26% over the second is a
   colour change of a couple of levels. A warm glow on a warm cream is invisible.

   What works is a hard warm line exactly at the boundary, where the contrast is
   against the indigo rather than against the cream, plus a normal lift shadow to
   sit the plate on the page rather than in it. The ring is the moon colour, so
   the warmth still comes out of the logo's own palette. */
.ll-header-plate .ll-header__logo,
.ll-header-plate-scroll .ll-header__logo {
	background-color: var(--wp--preset--color--indigo);
	border-radius: 12px;

	/*
	   ONE VALUE, ON PURPOSE. This was 0.65rem 1rem, which is 10px above and
	   below against 16px left and right. Because the artwork is cropped tight
	   to its own ink on all four sides, that asymmetry lands directly on the
	   drawing: the wordmark sat 16px off the right edge and the figures had
	   10px of sky, so the plate read as padded at the top and cramped at the
	   bottom right. "Leading Lights" and "Early Intervention" both end within a
	   few pixels of the frame, so the right edge is the one that shows it.

	   It is SMALL because the logo file now carries an even 44px margin of its
	   own around the solid ink, about 4.3% per side, put there by
	   build/make-header-logos.py. The gap you see is that plus this, and the
	   file's share is what makes it even: the artwork used to sit hard against
	   the right and bottom edges and hazy at the top, so no value here could
	   have looked uniform on all four sides.

	   Anything that changes this changes the header's height, which sets
	   --ll-header-h in section 1, which sets the artwork ceiling. The three
	   numbers move together.
	*/
	padding: 0.35rem;
	box-shadow:
		0 0 0 1px rgba(252, 224, 150, 0.55),
		0 10px 30px -12px rgba(32, 23, 100, 0.5);
	transition: background-color 0.45s ease, box-shadow 0.45s ease;
}

/* Once the bar itself is indigo the plate has nothing to distinguish it from,
   so it dissolves. Colour and shadow only, the padding stays put. */
.ll-header-plate-scroll .ll-header.is-stuck .ll-header__logo {
	background-color: transparent;
	box-shadow: none;
}

/* The indigo bar. !important because core emits
   .has-ground-background-color { background-color: ... !important } for the
   preset colour class the header block carries, and that beats specificity. */
.ll-header-solid .ll-header,
.ll-header-plate-scroll .ll-header.is-stuck,
.ll-header-swap .ll-header.is-stuck {
	background-color: var(--wp--preset--color--indigo) !important;
	border-bottom-color: transparent;
	transition: background-color 0.45s ease, box-shadow 0.2s ease;
}

/* Everything that sat on cream has to be re-set for indigo: the nav, its hover
   and current-page marks, the button, and the burger on a phone. */
.ll-header-solid .ll-header .wp-block-navigation-item__content,
.ll-header-plate-scroll .ll-header.is-stuck .wp-block-navigation-item__content,
.ll-header-swap .ll-header.is-stuck .wp-block-navigation-item__content {
	color: var(--wp--preset--color--ground);
}

/* Only the colour needs saying now. The underline is currentColor, so it
   follows, and the current-page mark follows the link colour set above. */
.ll-header-solid .ll-header .wp-block-navigation-item__content:hover,
.ll-header-plate-scroll .ll-header.is-stuck .wp-block-navigation-item__content:hover,
.ll-header-swap .ll-header.is-stuck .wp-block-navigation-item__content:hover {
	color: var(--wp--preset--color--moon);
}

/* The filled indigo button disappears into an indigo bar, so it inverts to the
   moon yellow. That is the one warm note left in the scrolled state, and it is
   also the highest-contrast pairing in the palette: 13.9:1. */
.ll-header-solid .ll-header .ll-header__cta .wp-block-button__link,
.ll-header-plate-scroll .ll-header.is-stuck .ll-header__cta .wp-block-button__link,
.ll-header-swap .ll-header.is-stuck .ll-header__cta .wp-block-button__link {
	background-color: var(--wp--preset--color--moon);
	color: var(--wp--preset--color--indigo);
}

.ll-header-solid .ll-header .wp-block-navigation__responsive-container-open,
.ll-header-plate-scroll .ll-header.is-stuck .wp-block-navigation__responsive-container-open,
.ll-header-swap .ll-header.is-stuck .wp-block-navigation__responsive-container-open {
	color: var(--wp--preset--color--ground);
}

@media (prefers-reduced-motion: reduce) {
	.ll-header__logo,
	.ll-header__logo img,
	.ll-header-solid .ll-header,
	.ll-header-plate-scroll .ll-header.is-stuck,
	.ll-header-swap .ll-header.is-stuck {
		transition: none;
	}
}

/*
   The mobile overlay was rendering as the raw WordPress default: a pure white
   sheet with pure black links, on a site that has no white and no black in it.
*/
.wp-block-navigation__responsive-container.has-modal-open {
	background-color: var(--wp--preset--color--ground) !important;
	padding: var(--wp--preset--spacing--60) var(--wp--preset--spacing--50);
}

.wp-block-navigation__responsive-container.has-modal-open .wp-block-navigation__responsive-container-content {
	justify-content: center;
	align-items: flex-start;
}

.wp-block-navigation__responsive-container.has-modal-open .wp-block-navigation__container {
	gap: var(--wp--preset--spacing--40);
	width: 100%;
}

.wp-block-navigation__responsive-container.has-modal-open .wp-block-navigation-item {
	width: 100%;
	border-bottom: 1px solid var(--ll-hairline);
}

.wp-block-navigation__responsive-container.has-modal-open .wp-block-navigation-item__content::after {
	display: none;
}

.wp-block-navigation__responsive-container.has-modal-open .wp-block-navigation-item__content {
	color: var(--wp--preset--color--indigo) !important;
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--x-large);
	font-weight: 600;
	text-decoration: none;
	padding-block: 0.5rem;
	border-bottom: 0;
	width: 100%;
}

.wp-block-navigation__responsive-container-close,
.wp-block-navigation__responsive-container-open {
	color: var(--wp--preset--color--indigo);
}

/* ---------------------------------------------------------------------------
   3c. HEADER TREATMENTS: THE FULL-WIDTH BAND

   Bel, 4 Sep, on the indigo plate all four treatments in 3b use: "It feels
   school based ... intrusive on a page that has such beautiful, warm colors.
   Why don't you get rid of the blue?" And on what replaces it: the sweeps from
   her email signature, running the full width, "it could go across the entire
   top" including behind the nav, with "not having like a square that sits
   around the logo".

       band-sage   sage above, plum below   <- default
       band-plum   plum above, sage below
       warm-block  her plan B: one warm colour edge to edge, no sweeps

   THREE STRUCTURAL CHANGES. Read these before editing any number here.

   1. THE BAR BECOMES TWO ROWS. The seven nav labels measure 31.29em in Nunito
      Sans 400, so at the size she asked for they are over 1000px of text
      before a single gap. No logo of any size can share that row. The logo
      takes row one, the nav and the button row two, done by wrapping the
      existing flex row so parts/header.html stays one bar for all seven
      treatments.

   2. THE STICKY OFFSET GOES NEGATIVE. The header is 639px tall at rest on a
      1280 laptop and could not be pinned at that height. It comes to rest at
      top: -(rest - bar), which parks the logo row above the viewport and
      leaves the nav row and its padding on screen. Measured: the logo's lower
      edge lands exactly on the top of the window, with 24px of clear space
      above the labels and 24px below. NOTHING RESIZES, which is the rule
      section 3 sets out: one height at every scroll position, so the document
      height never changes and the oscillation documented there cannot start.
      The persistent header goes 240px to 100px.

   3. --ll-header-h BECOMES THE PINNED HEIGHT for these three, set on body in
      section 7. It feeds the artwork ceiling, and the ceiling is about the page
      a reader can see while reading, which is now 140px taller. ARTWORK.md
      section 8 carries the new numbers.

   :is() carries the weight of its most specific argument, so every
   :is(.ll-header-band-sage, ...) below counts as one class and nothing here
   silently outranks 3b. Written that way because otherwise each of these rules
   is three near-identical selectors and the section stops being readable.
   --------------------------------------------------------------------------- */

/* ---- the sweep ---- */

/* Absolute, so it can never touch the bar's height. max-width is reset because
   .ll-header is a constrained layout and core caps every child at 1140px; this
   one has to reach both edges. inset:0 resolves against the padding box, which
   is the full width of the header including its global padding, so the ribbon
   runs edge to edge on the screen rather than stopping at the text column. */
.ll-sweep {
	position: absolute;
	inset: 0;
	max-width: none;
	margin: 0;
	overflow: hidden;
	pointer-events: none;
	z-index: 0;
	display: none;
}

.ll-header-band-sage .ll-sweep,
.ll-header-band-plum .ll-sweep,
.ll-footer .ll-sweep {
	display: block;
}

/* Plan B replaces the sweeps rather than sitting behind them. */
.ll-header-warm-block .ll-footer .ll-sweep {
	display: none;
}

/*
   preserveAspectRatio="none" on purpose. Her ribbon is drawn at 5.06:1 and the
   bar is nearer 2:1; stretching it vertically is what lets it fill the bar edge
   to edge, which is what "across the entire top" needs. The stretch is affine,
   so the taper ratios, the apex at fx 0.63 and both tips survive unchanged.
*/
.ll-sweep svg {
	position: absolute;
	inset: 0 0 auto 0;
	width: 100%;
	height: 100%;
	display: block;
}

/*
   FILL THE HEADER, KEEP HER RATIO. Two things have to be true at once and this
   is the only setting where both are.

   Her ribbon is 5.06:1. preserveAspectRatio="none" used to squash it into the
   header's 2.9:1, a 1.7x vertical stretch that turned two fine tapering bands
   into slabs. So the ratio is preserved, and `slice` scales the ribbon up until
   it covers the box, cropping the overflow off the right.

   THE CROP IS THE POINT, not a compromise. Measured on her master, the moon is
   48% of the ribbon's height. Fitting the whole ribbon into the header instead
   made it 90%, so the logo towered over bands half the size they should be, and
   the moon sat clean below them: "ours sits differently to the moon". Covering
   the header puts the moon back at about 46% and the bands back among the
   figures, which is where their contrast comes from in her artwork.

   xMin anchors the left edge, so the end of the ribbon the lockup sits on holds
   still at every window width and the far tail is what gets cropped. That is
   also how her signature is cropped in her own email.
*/
/*
   HER RIBBON, AT HER OWN PROPORTIONS, WHOLE.

   Rohan, 5 Sep, against her master: "I want the flow at the top of the page to
   follow the symmetry in this image ... the proportions and the movement of the
   line and its sizing, curvature and style from left to right."

   So nothing is distorted any more. An earlier pass compressed the paths to 0.55
   on x to force the tapering tail into the window, and that changed the very
   thing being asked for: the curvature. The paths are back to the values
   measured off her master and `meet` fits the whole ribbon across the width, so
   the full left to right movement is visible, tail included, at her ratio.

   Width leads and height follows. The header is taller than the ribbon, so the
   lower part of it is cream. That is the trade for showing her curve whole, and
   it is the one that keeps the line looking like hers.

   The viewBox is 360 tall, not her 316: the third band, which is ours and not
   in her two band master, reaches y 345 and would otherwise be clipped.
*/
.ll-header .ll-sweep svg {
	height: auto;
	top: 0;
	bottom: auto;
}

/*
   THE SWEEP OFFSET TRACKS THE WINDOW, 7 Sep.

   The lockup clamps at 480px and the ribbon does not: it is width-driven, so
   the two scale at different rates and no single offset can hold them in the
   same relationship. The attribute in header.html is a fallback; this is the
   real value.

   Swept in the browser at five widths, two user units at a time, testing all
   seven lights against every band fill. The window where nothing is bare:

     900px   44 to 48
     1024px  34 to 40
     1200px  24 to 30
     1335px  18 to 24
     1400px   8 to 16

   Linear in the window width and close to exact below the logo's clamp, which
   is where nearly every laptop sits. Above the clamp the lockup stops growing
   while the ribbon keeps going, so the offset has to fall away faster.

   HONEST LIMIT: past about 1450px no offset covers all seven, because by then
   the ribbon is tall enough that the fixed lockup rides too high in it. That
   predates today; the same was true of the old geometry, which had two lights
   bare at 1200 and one at 1000 with its own swept value. Tying the ribbon's
   height to --ll-logo-h rather than to the window would fix it properly, at
   the cost of cropping her sweep on wide screens, which is a trade for Bel.
*/
.ll-header .ll-sweep__shift {
	transform: translateY(clamp(0px, calc(97.9px - 5.77vw), 52px));
}

@media (min-width: 1334px) {
	.ll-header .ll-sweep__shift {
		transform: translateY(clamp(0px, calc(199.6px - 13.4vw), 52px));
	}
}

/*
   MOBILE AUDIT, 7 Sep. HER RIBBON HELD ITS RATIO AND LOST ITS COMPOSITION.

   height:auto on a 1600x360 viewBox means the ribbon's height is the window's
   width over 4.44. At 1440 that is a 324px sweep against a 301px lockup and
   the bands cross the mark from its top to 87% down, which is the relationship
   measured off Bel's master. At 375 the same rule gives an 84px sweep against
   a 169px lockup: the bands clip the top fifth of the logo and the moon hangs
   below them on bare cream. The ribbon shrinks 2.5 times faster than the logo
   does, so the two drift apart the narrower the window gets.

   Nothing here distorts anything. preserveAspectRatio in the markup is now
   xMinYMin slice, and the only new thing is a floor under the height:

     - at 890px and wider, the floor is under the natural height, the scale is
       identical on both axes, and slice and meet draw exactly the same picture.
       Desktop is untouched.
     - below 890px the box is taller than the ribbon's natural height, so slice
       scales the ribbon up to cover it and crops the right-hand tail. The
       curve, the taper and the moon keep the proportions they have at 1440.

   The tail is what is paid. That is the same crop Bel's own email signature
   carries, and it is the trade this file already made once for the same
   reason: on a 375px screen you can show the whole 5.06:1 sweep or you can
   keep the lockup sitting in it, not both.
*/
@media (max-width: 889.98px) {
	.ll-header .ll-sweep svg {
		height: clamp(150px, 54vw, 210px);
	}
}

/*
   HER MEASURED STRENGTHS, not invented ones. The sage band in her master
   composites to #D9E9E2 on the page ground, which is sage at alpha 0.456. The
   outer band sits a step weaker at 0.40 because in her file that band is
   sun-pale, far paler than either colour she has asked for up here, and at
   equal strength the two read as one thick band rather than as two.

   Indigo on either measures 12.2:1 and 11.6:1, so the nav and the wordmark are
   never the problem. The lockup's pale figures are: a sage figure on the sage
   band is 1.30:1. That is her own worry, "you don't want to lose the images",
   and the geometry is what answers it: the ribbon crosses the mark about 44%
   down, so the upper figures walk out of the band onto clear ground exactly as
   they do in her signature.

   Nothing here drifts. A sticky element never leaves the screen, so motion in
   it would be permanent, which is why this ribbon is still while the page is.
*/
.ll-header .ll-sweep__band--1 { fill: var(--wp--preset--color--sage); fill-opacity: 0.40; }
.ll-header .ll-sweep__band--2 { fill: var(--wp--preset--color--plum); fill-opacity: 0.456; }

/* The third band is sun-pale, which makes the header carry the same three
   colours as the footer: "same three colours for consistency". It sits at the
   BOTTOM of the ribbon rather than the top, which is also Bel's own rule from
   the call: no yellow up top, "and then lower on the page, you've got the
   yellow, the green and the purple." Opacity matches the footer's sun-pale. */
.ll-header .ll-sweep__band--3 { fill: var(--wp--preset--color--sun-pale); fill-opacity: 0.386; }

.ll-header-band-plum .ll-header .ll-sweep__band--1 { fill: var(--wp--preset--color--plum); }
.ll-header-band-plum .ll-header .ll-sweep__band--2 { fill: var(--wp--preset--color--sage); }
.ll-header-band-plum .ll-header .ll-sweep__band--3 { fill: var(--wp--preset--color--sun-pale); fill-opacity: 0.386; }

/*
   MOBILE AUDIT, 7 Sep. The button that opens the menu is the single most
   important control on a phone and it was a bare 24x24 SVG with no padding:
   the smallest tap target on the site. 24px is the floor WCAG 2.5.8 will pass
   at AA and it is well under the 44px Apple and the AAA target ask for.

   Grown by padding rather than by scaling the glyph, so the icon stays the
   size Bel signed off and only the area around it becomes tappable.
*/
.wp-block-navigation__responsive-container-open {
	min-width: 44px;
	min-height: 44px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* Pull back by the padding so the icon stays on the same optical line as
	   the logo and does not push the bar wider. */
	margin-right: -10px;
}

/* Same for the close button inside the opened overlay. */
.wp-block-navigation__responsive-container-close {
	min-width: 44px;
	min-height: 44px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
}

/* ---- the bar ---- */

:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .ll-header {
	position: relative;
	isolation: isolate;
	padding-block: var(--ll-head-pad);
}

:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .ll-header__bar {
	position: relative;
	z-index: 1;
	max-width: min(100%, 1560px);
	flex-wrap: wrap;
	row-gap: var(--ll-head-rowgap);
	column-gap: 0;
}

/*
   ROW ONE: the mark, on its own line and left, where her signature puts it.
   No plate, no ring, no radius, and background-image cleared: section 3 rides
   the reversed cream lockup as a background under the img, and on a pale band
   it would ghost through every transparent pixel of the ink one.

   The basis is 100% and the cap is on the IMG, not on the wrapper. A wrapper
   capped at 560px has a hypothetical main size of 560px, and flexbox breaks
   lines on the hypothetical size, so the nav would come back up beside it.
*/
/*
   THE TWO MISSING TITTLES, MATCHED TO THE ONE SHE ALREADY DREW.

   Her wordmark has no dot on the i of "Leading" or the i of "Lights"; both are
   replaced by soft glows, because in the mark the dots are the lights. At header
   size those glows vanish on cream and it reads as "Leadng Lghts", which looks
   like a font that failed rather than a decision. Rohan, 4 Sep: "maybe all i
   dots can be the same to make them visible?"

   THE REFERENCE IS HER OWN. The i of "Intervention" DOES carry a dot, and these
   two are copied from it rather than invented, which is why an earlier attempt
   in grey was wrong and got pulled:

       her dot        5.5px across, 12.5px above its stem, ink (17, 8, 92)
       scale factor   1.586, the ratio of the two lines' stem heights, 46 to 29
       so these       8.7px across, 19.8px above their stems, same ink

   Positions are the STEMS, measured off assets/logo.png: the two i stems are 7px
   wide, centred at 56.74% and 77.29%, both topping out at y 498. The glows sit
   slightly above and left of that, so following the glows put the dots adrift of
   the letters they belong to.

   ON TOP OF THE IMAGE, NOT BEHIND IT, and that is not a style choice. The first
   attempt painted them as a background on the img and nothing appeared: sampled
   off the canvas, the pixels at both tittle positions are (249, 252, 250) at
   alpha 253. The glow is opaque, so there is nothing to show through. They have
   to be drawn over the top.

   The anchor is used rather than .ll-header__logo because the wrapper is a flex
   item 1560px wide against the image's 732, so percentages on the wrapper put
   both dots about twice as far right as they belong. The anchor is the image box
   exactly.

   assets/logo.png is untouched and stays untouched. Percentages track
   --ll-logo-w at every size. If the logo is ever recropped by
   build/make-header-logos.py, remeasure. TO REMOVE, delete this rule.
*/
/* position only. DO NOT add display:block here: the anchor is already the image
   box at 732px, and making it block stretches it to the 1560px wrapper, which
   drops the logo out of the layout and throws both dots onto the wrong box. */
:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .ll-header__logo a {
	position: relative;
}

:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .ll-header__logo a::after {
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	background-image:
		radial-gradient(circle closest-side, #110858 0 62%, rgba(17, 8, 88, 0) 76%),
		radial-gradient(circle closest-side, #110858 0 62%, rgba(17, 8, 88, 0) 76%);
	background-repeat: no-repeat, no-repeat;
	background-size: 1.05% 1.49%, 1.05% 1.49%;
	background-position: 56.74% 65.53%, 77.29% 65.53%;
}

:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .ll-header__logo {
	flex: 0 0 100%;
	max-width: none;
	background-image: none;
	background-color: transparent;
	border-radius: 0;
	box-shadow: none;
	padding: 0;
}

:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .ll-header__logo img {
	width: min(var(--ll-logo-w), 88vw);
	max-width: 100%;
	height: auto;
}

/* ROW TWO: the nav across the width, the button at the far right. min-height
   rather than height, because the row has to be free to be taller than
   --ll-nav-row on a narrow window; the token is sized so it never needs to be
   on a laptop, which is what keeps the pinned bar exact. */
:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .ll-header__right {
	flex: 1 1 100%;
	justify-content: space-between;
	min-height: var(--ll-nav-row);
	gap: var(--wp--preset--spacing--40);
}

/* The grow has to be on the NAV, not on the list inside it. The nav is the flex
   item of .ll-header__right, so without this it shrinks to the width of its own
   labels, all of the row's slack collects in one gap before the button, and the
   seven labels read as one run-on line. With it the nav takes the row and the
   space-between below is what opens the labels out. */
:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .ll-nav {
	flex: 1 1 auto;
	min-width: 0;
}

:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .ll-nav .wp-block-navigation__responsive-container:not(.has-modal-open) {
	width: 100%;
}

:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .ll-nav .wp-block-navigation__container:not(.has-modal-open) {
	justify-content: space-between;
	gap: 0.5rem;
}

/*
   "At least like double in size", said three separate times.

   Scoped on the responsive container rather than on the list, so the phone
   overlay keeps the x-large it is given at the end of 3b. That rule is (0,3,0)
   and this one is heavier, so without the :not() the overlay would render its
   labels at the bar size instead.

   padding-block comes down from the 0.35rem section 3 sets. At the top of the
   clamp the label is 34px on a 1.2 line, which is 40.8px, and 0.35rem each
   side takes the row to exactly 52px, the same as --ll-nav-row with nothing
   left over. 0.3rem leaves 1.6px of headroom, and headroom is what stops a
   sliver of the logo row showing under the pinned bar.
*/
:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .ll-nav .wp-block-navigation__responsive-container:not(.has-modal-open) .wp-block-navigation-item__content {
	font-size: var(--ll-nav-size);
	line-height: 1.2;
	padding-block: 0.3rem;
	color: var(--wp--preset--color--indigo);
}

/* The one window where the labels will not fit on a line. See --ll-nav-row in
   section 1, which grows to two lines' worth over the same range so the pinned
   bar still clears them. space-between would spread the second line's three
   labels across the whole bar, so the wrapped state packs left instead. */
@media (min-width: 600px) and (max-width: 720px) {
	:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .ll-nav .wp-block-navigation__responsive-container:not(.has-modal-open) .wp-block-navigation__container {
		flex-wrap: wrap;
		justify-content: flex-start;
		gap: 0.4rem 1.25rem;
	}
}

/* ---- the negative sticky offset ---- */

:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .wp-site-blocks > header.wp-block-template-part {
	top: calc(var(--ll-bar-h) - var(--ll-header-rest));
}

.admin-bar:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .wp-site-blocks > header.wp-block-template-part {
	top: calc(32px + var(--ll-bar-h) - var(--ll-header-rest));
}

@media (max-width: 782px) {
	.admin-bar:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .wp-site-blocks > header.wp-block-template-part {
		top: calc(46px + var(--ll-bar-h) - var(--ll-header-rest));
	}
}

/*
   ONE ROW AGAIN ON A PHONE, and no negative offset.

   Below 600px core hides the labels behind the burger, so the second row would
   be a whole bar of empty cream with one icon pushed to its left corner by the
   space-between the wide layout needs. The mark and the burger go back on one
   line, which is the shape a phone header has anyway.

   The pin comes off with it. The masthead here is 218px on a 375px screen,
   against 197px on the same screen today, so it is short enough to stay whole
   and scrolling half a logo away would be worse than carrying all of it. That
   also puts these three back on the same behaviour as the four in 3b, which
   is one less thing to hold in your head when reading a phone bug report.

   Placed after the offset rules above deliberately: same specificity, so it is
   source order that decides.

   599.98 and not 600. Core switches the burger on at min-width: 600px, so a
   rule that stops at max-width: 600px overlaps it by one pixel and that pixel
   renders the labels inside the phone layout: seven of them on a 552px bar,
   overflowing sideways. The fraction also closes the gap a 599.5px viewport
   would otherwise fall into.
*/
@media (max-width: 599.98px) {
	:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .ll-header__bar {
		column-gap: var(--wp--preset--spacing--30);
	}

	:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .ll-header__logo {
		flex: 0 1 auto;
	}

	:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .ll-header__right {
		flex: 0 0 auto;
		min-height: 0;
	}

	:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .wp-site-blocks > header.wp-block-template-part {
		top: 0;
	}

	.admin-bar:is(.ll-header-band-sage, .ll-header-band-plum, .ll-header-warm-block) .wp-site-blocks > header.wp-block-template-part {
		top: 46px;
	}
}

/* ---- plan B ---- */

/*
   Her words, unprompted, if the mark will not hold against the sweeps: "make a
   block colour across the top of the page entirely and change it to be the
   warmest colour we can where the logo works against it, and match that with
   the bottom part as well. But I would run it as a full border and not just
   the square."

   moon #FCE096 is the warmest slug in the palette and the logo does work
   against it, but the measurement is worth recording: the lockup's own disc is
   sun-pale #FFFCB7, 1.16:1 against moon, so the disc reads as a glow rather
   than as a disc. Everything that carries the name holds, and the white lights
   above the i's appear for the first time, because on cream they are white on
   near-white.

   plum-deep with the reversed lockup was drawn and measured too. It is not the
   answer: the reversed wordmark is indigo inside a cream halo, which on a mid
   plum reads as an outline rather than as a word, and a full-width dark band is
   the opposite of "I would make it kind of lighter colours overall".

   !important because the header and the footer both carry a preset background
   class, and core emits those with !important.
*/
.ll-header-warm-block .ll-header,
.ll-header-warm-block .ll-footer {
	background-color: var(--ll-warm) !important;
}

/* The footer headings are plum-deep, which was chosen against the page ground
   where it measures 4.65:1. Moon is a darker ground and the same colour falls
   to 4.14:1, under AA for the 14px these are set at. Indigo on moon is 12.2:1.
   Same decision as section 11's, re-measured for the ground plan B puts them
   on, rather than a second colour choice. !important for the same reason as
   the ground above: the heading carries has-plum-deep-color and core emits
   every preset colour class with !important. */
.ll-header-warm-block .ll-footer__head {
	color: var(--wp--preset--color--indigo) !important;
}

/* ---------------------------------------------------------------------------
   4. Section rhythm

   The single biggest fix in this pass. Previously every band on the front page
   was 760px wide, transparent, and exactly one spacing step from its
   neighbours, so nothing grouped and nothing separated. Now a section declares
   both its width and its ground, and the spacing follows from that.
   --------------------------------------------------------------------------- */

.ll-section {
	padding-block: var(--ll-gap-far);
}

/* Two sections on the same ground do not need two lots of padding between
   them, or the page grows enormous vertical holes. */
.ll-section + .ll-section {
	padding-top: 0;
}

/* A change of ground is its own separator, so the padding comes back. */
/*
   Listed with :is() so a new ground only has to be added in one place. The old
   form repeated every modifier twice and was already six selectors long.
*/
/*
   --glow belongs in this list and was missing from it, which is the failure the
   comment above was written to prevent. It paints a ground like the rest, but
   it does it with a gradient on a pseudo-element rather than a background-color
   on the section, so it did not look like one when the list was last edited.

   The effect, on the front page: the testimonials sat directly after another
   section, took padding-top: 0 from the rule above, and never got it back. The
   heading was flush against the top edge of its own coloured band while 96px of
   the same colour sat below the cards. Rohan: "not centered between the
   colours."
*/
:is(.ll-section--tint, .ll-section--quote, .ll-section--dark, .ll-section--glow,
    .ll-section--sage, .ll-section--sky, .ll-section--plum, .ll-section--sun) + .ll-section,
.ll-section + :is(.ll-section--tint, .ll-section--quote, .ll-section--dark, .ll-section--glow,
    .ll-section--sage, .ll-section--sky, .ll-section--plum, .ll-section--sun) {
	padding-top: var(--ll-gap-far);
}

.ll-section--tint {
	background-color: var(--wp--preset--color--ground-tint);
}

.ll-section--quote {
	background-image: var(--wp--preset--gradient--sunrise);
}

.ll-section--dark {
	background-color: var(--wp--preset--color--indigo);
}

/* ---------------------------------------------------------------------------
   Section grounds in her own colours.

   Bel, twice, opening and closing the first meeting: "I don't want it to look
   clinical, they're all white", and "[not an]other white clinical allied health
   thing." Measured on the build before this pass, her palette covered 5.9% of
   the front page and white or near-white covered 75%. The pastels were doing
   3px rules and 8px bullets while white did the grounds.

   These put her colours under whole sections instead. Each one is the palette
   colour let down into the warm ground, so it reads as a wash rather than a
   block of paint: strong enough to see, well short of primary. The hex is a
   fallback for anything without color-mix; the mix is what keeps the wash
   correct if she ever changes the source colour.
   --------------------------------------------------------------------------- */

.ll-section--sage {
	background-color: #DEEBE6;
	background-color: color-mix(in srgb, var(--wp--preset--color--sage) 42%, var(--wp--preset--color--ground));
}

.ll-section--sky {
	background-color: #E2ECF3;
	background-color: color-mix(in srgb, var(--wp--preset--color--sky) 42%, var(--wp--preset--color--ground));
}

.ll-section--plum {
	background-color: #EFE3EA;
	background-color: color-mix(in srgb, var(--wp--preset--color--plum) 42%, var(--wp--preset--color--ground));
}

.ll-section--sun {
	background-color: #FEFBE4;
	background-color: color-mix(in srgb, var(--wp--preset--color--sun) 55%, var(--wp--preset--color--ground));
}

/* The opening painting on the front page. The banner above it already carries
   its own bottom padding and fades into the cream, so a full section step here
   would leave a hole between the tagline and the picture. */
.ll-section--opening {
	padding-top: var(--wp--preset--spacing--50);
}

/* A sun bloom, for a section that carries words rather than pictures.
   theme.json has defined a "glow" gradient since the first build and nothing
   ever referenced it. The testimonial band is the longest stretch of the front
   page with no artwork in it, 1,386px measured, and a painting would compete
   with the quotes. This is light rather than a picture, which is the right
   register there and is also the palette answer to "the light comes from the
   child". Drawn on a pseudo-element so the bloom can bleed past the section
   edges instead of ending on a band line. */
.ll-section--glow {
	position: relative;
	isolation: isolate;
}

.ll-section--glow::before {
	content: "";
	position: absolute;
	inset: 0;
	background-image: var(--wp--preset--gradient--glow);
	background-repeat: no-repeat;
	background-position: 50% 42%;
	background-size: 130% 155%;
	opacity: 0.6;
	z-index: -1;
	pointer-events: none;
}

.ll-section--opening .ll-figure {
	margin-block: 0;
}

.ll-section__head {
	margin-bottom: var(--ll-gap-near);
}

/*
   Audit, 7 Sep. .ll-section__head is a constrained group, so core capped its
   heading at contentSize while the steps, cards, awards and testimonials
   beside it are alignwide and run to wideSize. Measured on the front page at
   1440px: "Our approach" inked at x=192 and "Get appointment", directly under
   it, at x=142. The heading was the only thing in the section not on the
   section's own edge. Same 50px step on the About Us awards row.

   The content is what sets the edge here, so the head joins it rather than the
   other way round.
*/
.ll-section__head > * {
	max-width: none;
	margin-inline: 0;
}

.ll-section__title {
	margin: 0;
}

/*
   Audit, 7 Sep. .ll-section__title is xx-large, which is right on the front
   page: the banner headline above it is 68px, so the scale reads 68 / 48 / 32.
   Inside a page body the title above it is itself 48px, so on About Us the
   section heading and the page heading were the same size and neither led.
   One step down inside post-content restores 48 / 32 / 22 on every interior
   page without touching the front page, which renders its patterns straight
   into the template rather than through post-content.
*/
.wp-block-post-content .ll-section__title {
	/*
	   !important because core emits its preset font-size utilities with it, so
	   .has-xx-large-font-size beats any specificity this rule could reach for.
	   Matching core's weapon is the only way to win, and this is the one place
	   the theme does it for a size.
	*/
	font-size: var(--wp--preset--font-size--x-large) !important;
}

/*
   Audit, 7 Sep. An alignfull band inside a page body carries its own interior
   rail. On the front page that rail is the page's rail and everything agrees.
   On About Us the page is on the 1040px measure and the awards band was on
   1140px, so the badges and their heading sat 51px to the left of every
   paragraph above and below them. Inside post-content the band keeps its
   full-width tint and takes the page's measure for its contents.

   Paired with the breakout below: the band's ground reaches both edges of the
   screen and its contents hold the column. One without the other gives either
   a floating rectangle or a full-width band with its text against the glass.
*/
.wp-block-post-content .ll-section.alignfull > .wp-block-group,
.wp-block-post-content .ll-section.alignfull > .ll-awards,
.wp-block-post-content .ll-section.alignfull > .ll-awards__cred {
	max-width: var(--ll-text);
	margin-inline: auto;
}

/* No ch cap. The supporting line shares its heading's left edge; a 52ch cap on
   a centred layout put it 17px to the right of the heading above it. */
.ll-section__head p {
	margin-top: 0.4rem;
}

.ll-section__more {
	margin-top: var(--ll-gap-near);
}

.ll-section__more a {
	text-decoration: none;
	border-bottom: 2px solid var(--wp--preset--color--plum);
	padding-bottom: 2px;
	font-weight: 600;
}

.ll-section__more a:hover {
	border-bottom-color: var(--wp--preset--color--plum-deep);
}

/* The opening line under the banner. Sets the measure for the whole page. */
.ll-lede {
	max-width: 30ch;
	margin-inline: auto;
	line-height: 1.35;
	color: var(--wp--preset--color--indigo);
	text-wrap: balance;
}

.ll-pullquote {
	max-width: 22ch;
	margin-inline: auto;
	line-height: 1.2;
	color: var(--wp--preset--color--indigo);
	text-wrap: balance;
}

.ll-pullquote__cite {
	margin-top: var(--wp--preset--spacing--40);
	text-transform: uppercase;
	letter-spacing: 0.12em;
}

.ll-section--dark__lede {
	max-width: 46ch;
	margin-inline: auto;
	margin-top: var(--wp--preset--spacing--40);
}

/* A filled button on indigo disappears. */
.is-style-onDark .wp-block-button__link {
	background-color: var(--wp--preset--color--sun);
	color: var(--wp--preset--color--indigo);
}

.is-style-onDark .wp-block-button__link:hover {
	background-color: var(--wp--preset--color--moon);
	color: var(--wp--preset--color--indigo);
}

/* ---------------------------------------------------------------------------
   5. Banner

   Her email signature has two pale bands sweeping diagonally behind the logo.
   That is the gesture here. It is not an arc, and the wordmark is not repeated,
   both of which she asked for directly.
   --------------------------------------------------------------------------- */

.ll-banner {
	position: relative;
	isolation: isolate;
	background-color: var(--wp--preset--color--ground);
}

/* The negative margin that used to lap this over the artwork's fade went with
   the artwork. The tagline now sits on clear cream under the header ribbon. */
/*
   MOBILE AUDIT, 7 Sep. The banner was 485px on an 812px phone, 60% of the
   first screen, and 160px of that was padding with another 80px in gaps
   against 245px of actual headline, line and buttons. Below is the same
   composition with the air scaled to the screen it is on.
*/
@media (max-width: 600px) {
	/*
	   !important because patterns/banner.php sets this padding inline through
	   the block's own spacing attributes, and an inline style beats any
	   selector. Changing the pattern instead would move the desktop banner
	   Bel has already signed off, so the override lives here and stops at
	   600px. If the banner's block padding is ever edited, edit this too.
	*/
	.ll-banner__inner {
		padding-top: var(--wp--preset--spacing--50) !important;
		padding-bottom: var(--wp--preset--spacing--60) !important;
	}

	.ll-banner__inner > * + * {
		margin-top: var(--wp--preset--spacing--50) !important;
	}
}

.ll-banner__inner {
	position: relative;
}

/* "Above where it says where every part of who your child is matters, but
   larger." Kept large, and on clear ground. */
.ll-banner__tagline {
	max-width: 18ch;
	margin-inline: auto;
	color: var(--wp--preset--color--indigo);
	text-wrap: balance;
}

.ll-banner__sub {
	max-width: 44ch;
	margin-inline: auto;
}

.ll-banner__buttons {
	margin-top: var(--wp--preset--spacing--50);
	gap: var(--wp--preset--spacing--40);
}

/*
   Every grid in this theme is a wp:group, and WordPress's flow layout gives
   each child a top margin of one block gap, skipping the first child. That is
   right for a stack and wrong for a grid: it pushed every card after the first
   down by 24px, so the first card in each row sat proud of its neighbours and
   the row tops were ragged. The grid's own gap does the spacing.
*/
.ll-staffgrid > *,
.ll-cardgrid > *,
.ll-postgrid > *,
.ll-awards > *,
.ll-contact > * {
	margin-block: 0;
}

/* ---------------------------------------------------------------------------
   6. Cards

   Grid rather than columns, so every card in a row is the same height and the
   bottoms line up however long the wording runs. This was the ragged-bottom
   problem on both the service panels and the staff grid.
   --------------------------------------------------------------------------- */

.ll-cardgrid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--wp--preset--spacing--50);
}

@media (max-width: 900px) {
	.ll-cardgrid {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (max-width: 600px) {
	.ll-cardgrid {
		grid-template-columns: 1fr;
	}
}

.ll-card {
	display: flex;
	flex-direction: column;
	border-radius: 5px;
	overflow: hidden;
	padding: 0;
	box-shadow: var(--ll-card-shadow);
}

/* The painting used to be clipped by the card frame at the top. It now fills
   the full card width and sits flush. */
.ll-card__img {
	margin: 0;
	width: 100%;
	max-width: none;
	display: block;
	overflow: hidden;
}

.ll-card__img img {
	display: block;
	width: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	background: none;
}

.ll-card__body {
	padding: var(--wp--preset--spacing--50);
	flex: 1;
}

/*
   A constrained group gives every child margin-inline:auto. Inside a flex
   column that quietly centres any heading narrower than its card, so names sat
   centred above left-aligned roles. Card interiors are flush left.
*/
.ll-card__body > *,
.ll-staff__body > * {
	max-width: none;
	margin-inline: 0;
	width: 100%;
}

.ll-card__body h3 {
	margin: 0;
}

.ll-card__body p {
	margin: 0;
	max-width: none;
}

/* ---------------------------------------------------------------------------
   6b. The approach steps

   Four steps from first call to first session, on the front page. The audit
   against the live site found this section missing and it was the only
   substantive content the rebuild did not have.

   An ordered list, not a card grid, because the order carries the meaning: you
   cannot connect with a key worker before you have met anyone. The numbers come
   from a CSS counter so nobody can renumber three and forget the fourth.

   The line between the steps is on a pseudo-element, so it is decoration and
   never reaches the accessibility tree, and it is drawn rather than simply
   appearing. That is the one place on this site where motion explains something
   instead of decorating it: the line IS the sequence.
   --------------------------------------------------------------------------- */

.ll-steps {
	list-style: none;
	counter-reset: ll-step;
	margin: var(--ll-gap-near) 0 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: var(--wp--preset--spacing--40);
}

.ll-step {
	counter-increment: ll-step;
	position: relative;
	padding-top: 3.6rem;
}

/* The numbered disc. */
.ll-step::before {
	content: counter(ll-step);
	position: absolute;
	top: 0;
	left: 0;
	width: 2.4rem;
	height: 2.4rem;
	border-radius: 50%;
	background: var(--wp--preset--color--indigo);
	color: var(--wp--preset--color--moon);
	font-family: var(--wp--preset--font-family--display);
	font-size: 1.15rem;
	font-weight: 600;
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 2;
}

/*
   The rule running from this step's disc to the next one's. Inset by the disc
   at one end and a little short at the other, so it reads as joining two
   things rather than underlining them.

   scaleX from a left origin is what lets it draw. It is held at 0 only when
   the reveal machinery is armed, which means a visitor with JavaScript blocked
   never gets a line stuck at zero width.
*/
.ll-step:not(:last-child)::after {
	content: "";
	position: absolute;
	top: 1.15rem;
	left: 2.9rem;
	right: calc(-1 * var(--wp--preset--spacing--40));
	height: 2px;
	background: var(--wp--preset--color--plum);
	z-index: 1;
}

.js.ll-motion-ready .ll-reveal .ll-step:not(:last-child)::after {
	transform: scaleX(0);
	transform-origin: left center;
}

.js .ll-reveal.is-in .ll-step:not(:last-child)::after {
	transform: scaleX(1);
	transition: transform 0.55s cubic-bezier(0.65, 0, 0.35, 1);
}

/* Each segment waits for the one before it, so the line travels rather than
   all three drawing at once. Offset past the step's own rise. */
.js .ll-reveal.is-in .ll-step:nth-child(1)::after { transition-delay: 0.30s; }
.js .ll-reveal.is-in .ll-step:nth-child(2)::after { transition-delay: 0.72s; }
.js .ll-reveal.is-in .ll-step:nth-child(3)::after { transition-delay: 1.14s; }

.ll-step__title {
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--large);
	font-weight: 600;
	color: var(--wp--preset--color--indigo);
	margin: 0 0 0.4rem;
	line-height: 1.25;
	text-wrap: balance;
	/*
	   Audit, 7 Sep. Two of the four titles wrap to a second line and two do
	   not, so the bodies underneath started 28px apart across a row that reads
	   left to right as one sequence. Two lines of room for every title puts
	   all four bodies on the same line. Falls back to auto below 860px, where
	   the steps stack and there is no row to align to.
	*/
	min-height: calc(2 * 1.25em);
}

@media (max-width: 860px) {
	.ll-step__title {
		min-height: 0;
	}
}

.ll-step__body {
	margin: 0;
	font-size: var(--wp--preset--font-size--medium);
	color: var(--wp--preset--color--muted);
	line-height: 1.65;
}

/*
   Below 860px four columns puts three words on a line. It becomes a vertical
   list and the connecting rule turns with it, running down the left margin
   from one disc to the next, which is the same diagram rotated rather than a
   different one.
*/
@media (max-width: 860px) {
	.ll-steps {
		grid-template-columns: 1fr;
		gap: var(--wp--preset--spacing--50);
	}

	.ll-step {
		padding-top: 0;
		padding-left: 3.6rem;
		min-height: 2.4rem;
	}

	.ll-step:not(:last-child)::after {
		top: 2.9rem;
		left: 1.15rem;
		right: auto;
		width: 2px;
		height: calc(100% - 2.9rem + var(--wp--preset--spacing--50));
		transform-origin: top center;
	}

	.js.ll-motion-ready .ll-reveal .ll-step:not(:last-child)::after {
		transform: scaleY(0);
	}

	.js .ll-reveal.is-in .ll-step:not(:last-child)::after {
		transform: scaleY(1);
	}
}

/* ---------------------------------------------------------------------------
   6c. THE SERVICES PAGE: TEN FLIP CARDS

   Bel, 4 Sep: "if we had a tab or a box and it said functional integrated
   medicinal nutrition, you click on it and it flips over and there's your
   information. That way the page becomes less busy", and then "I like that idea
   better". Rohan asked for it on 5 Sep over the disclosure that shipped first.

   WHY IT WORKS AS A GRID AND NOT AS FULL WIDTH BANDS. The objection raised on
   the call was real: a full width card has to carry its face text large enough
   to read as a card front, which throws it out of proportion with every heading
   above it. In a grid the cards are a third of the width, so the name sits at an
   ordinary heading size and the longest body, 135 words, still fits its back.

   BOTH FACES SHARE ONE GRID CELL rather than being absolutely positioned, so
   each card is as tall as its taller face and no back can be clipped. The
   bodies run 61 to 135 words, a 2.2x spread, so a fixed height would either cut
   the longest or leave the shortest half empty.

   WITHOUT JAVASCRIPT NOTHING IS HIDDEN. Every rule that hides a face is scoped
   to .js, which functions.php only adds once a script runs, so the page falls
   back to ten headings each followed by its paragraph. The toggles are real
   buttons, so Enter, Space and a screen reader all work without any extra code,
   and aria-expanded is kept in step by motion.js.
   --------------------------------------------------------------------------- */

/* The jump list, unchanged. Ordinary in-page anchors; html { scroll-padding-top }
   in section 1 already clears the pinned bar. */
.ll-svc-jump {
	margin-block: var(--wp--preset--spacing--50) var(--wp--preset--spacing--60);
}

.ll-svc-jump__list {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6rem 0.75rem;
	margin: 0;
}

.ll-svc-jump__list a {
	display: inline-block;
	padding: 0.5rem 1rem;
	border: 1px solid var(--wp--preset--color--sage-deep);
	border-radius: 999px;
	color: var(--wp--preset--color--indigo);
	text-decoration: none;
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.3;
}

.ll-svc-jump__list a:hover,
.ll-svc-jump__list a:focus-visible {
	background: var(--wp--preset--color--sage);
}

/* ONE HEIGHT FOR ALL TEN, and the long backs scroll inside themselves. Rohan,
   5 Sep: "I think they can't be different sizes, having internal scroll for the
   longer ones is a better design choice." He is right. Letting each card take
   its own height made a ragged grid of ten different rectangles, because the
   bodies run 61 to 135 words. A fixed height and an overflow on the back keeps
   the grid even and costs only a scroll on the two or three longest. */
.ll-svc-grid {
	display: grid;
	/*
	   MOBILE AUDIT, 7 Sep. min(420px, 100%), not a bare 420px. auto-fit only
	   decides how many columns fit; the minmax minimum is a hard floor that
	   ignores the container, so on a 375px phone the grid measured 327px wide
	   and its single column measured 420px, and every service card ran 93px
	   past its own grid. The min() caps the floor at the container's width,
	   which is the standard guard and the one .ll-form already uses.
	*/
	grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr));
	gap: var(--wp--preset--spacing--40);
	margin-block: var(--wp--preset--spacing--50);
}

.ll-flip {
	perspective: 1400px;
	scroll-margin-top: calc(var(--ll-header-h) + 1rem);
}

/*
   Both faces in cell 1/1, and one fixed height for every card on the page.

   Audit, 7 Sep. The height went 26rem to 34rem when the back copy moved off
   small onto the body size. Rohan's rule for these cards was "they can't be
   different sizes, having internal scroll for the longer ones is a better
   design choice", and at 26rem with larger type eight of the ten were
   scrolling, which is not the longer ones, it is most of them. At 34rem the
   count is back to three: the two 700-word panels and the one that runs to
   1,032 characters, which is nearly twice the next longest.
*/
.ll-flip__inner {
	display: grid;
	height: 34rem;
	transition: transform 0.55s cubic-bezier(0.2, 0.7, 0.3, 1);
	transform-style: preserve-3d;
}

.ll-flip__front,
.ll-flip__back {
	grid-area: 1 / 1;
	padding: var(--wp--preset--spacing--50);
	border-radius: 3px;
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
	/* min-height 0 so a face can be shorter than its content and the fixed card
	   height wins. Without it the longest name, "Integrative Developmental
	   Specialist" over three lines, pushed its card 24px past every other. */
	min-height: 0;
	overflow: hidden;
}

/* The front carries a name and a button against a back carrying a paragraph, so
   left alone it is mostly empty. Centring it means the name sits on the colour
   rather than clinging to the top of it. */
.ll-flip__front {
	justify-content: center;
}

/* The back is the only face that can overflow, so it is the only one that
   scrolls. padding-right keeps the text off the scrollbar where one appears. */
.ll-flip__back {
	overflow-y: auto;
	overscroll-behavior: contain;
	padding-right: calc(var(--wp--preset--spacing--50) - 0.5rem);
}

.ll-flip--sage .ll-flip__front { background: var(--wp--preset--color--sage); }
.ll-flip--plum .ll-flip__front { background: var(--wp--preset--color--plum); }
.ll-flip--sun  .ll-flip__front { background: var(--wp--preset--color--sun-pale); }
.ll-flip__back { background: var(--wp--preset--color--ground-tint); }

/* margin and padding zeroed explicitly, and scoped through .ll-flip__front so
   this outranks the theme's generic heading rhythm. That rule was putting 64px
   above and 16px inside every card name, which is right for a page and wrong
   for a card face. */
.ll-flip__front .ll-flip__name,
.ll-flip__back .ll-flip__backname {
	margin: 0;
	padding: 0;
}

.ll-flip__name {
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--x-large);
	line-height: 1.15;
	color: var(--wp--preset--color--indigo);
	text-wrap: balance;
	/*
	   Audit, 7 Sep. Same stagger as the approach steps: the two long service
	   names wrap and pushed their flip control 18px below the control on the
	   card beside them. Two lines reserved on every card front. */
	min-height: calc(2 * 1.15em);
}

/* 880px, not 600px: .ll-svc-grid is auto-fit minmax(420px), so it drops to one
   column just under 880 and from there down there is no row to align to. */
@media (max-width: 880px) {
	.ll-flip__name {
		min-height: 0;
	}
}

.ll-flip__backname {
	margin: 0;
	padding: 0;
	font-family: var(--wp--preset--font-family--display);
	/*
	   Audit, 7 Sep. The name was x-large on the front and medium on the back,
	   so flipping a card shrank its title from 32px to 17px and it stopped
	   reading as the title of anything. large keeps the front's authority
	   without stealing room from the copy underneath. */
	font-size: var(--wp--preset--font-size--large);
	line-height: 1.2;
	color: var(--wp--preset--color--indigo);
	margin-bottom: 0.6rem;
}

/*
   Audit, 7 Sep. The service descriptions and the staff stories are the
   substance of these two pages and they were set at small, the same step as an
   award caption and the Acknowledgement. medium is the body size everywhere
   else on the site and it is what this copy should have been all along.
*/
.ll-flip__back p {
	margin: 0;
	font-size: var(--wp--preset--font-size--medium);
	line-height: 1.6;
}

.ll-flip__toggle {
	align-self: flex-start;
	padding: 0.4rem 0;
	background: none;
	border: 0;
	border-bottom: 1px solid currentColor;
	color: var(--wp--preset--color--indigo);
	font: inherit;
	font-size: var(--wp--preset--font-size--small);
	cursor: pointer;
}

/* Only from here down is anything hidden, and only once a script has run. */
.js .ll-flip__front,
.js .ll-flip__back {
	backface-visibility: hidden;
	-webkit-backface-visibility: hidden;
}

/*
   Audit, 7 Sep. Sixteen of the twenty staff stories are taller than their card,
   the longest by more than double, and the back scrolls. Nothing said so: the
   copy simply stopped at the last visible line. A mask fades the final rows
   away while there is more below and clears once the reader reaches the end,
   so the card reads as continuing rather than as finished.
*/
.ll-flip__back {
	--ll-fade: 2.2rem;
	mask-image: linear-gradient(
		to bottom,
		#000 0,
		#000 calc(100% - var(--ll-fade)),
		rgba(0, 0, 0, 0.06) 100%
	);
	-webkit-mask-image: linear-gradient(
		to bottom,
		#000 0,
		#000 calc(100% - var(--ll-fade)),
		rgba(0, 0, 0, 0.06) 100%
	);
}

.ll-flip__back.is-scrolled-end,
.ll-flip__back:not(.is-scrollable) {
	--ll-fade: 0rem;
	mask-image: none;
	-webkit-mask-image: none;
}

.js .ll-flip__back { transform: rotateY(180deg); }
.js .ll-flip.is-flipped .ll-flip__inner { transform: rotateY(180deg); }

@media (prefers-reduced-motion: reduce) {
	.ll-flip__inner { transition: none; }
}



/* ---------------------------------------------------------------------------
   7. Reading pages

   Our History, Services, Service Fees and Join The Team carry 600 words each.
   They were arriving as two competing body styles: one lead paragraph at 22px
   in a washed lavender, then everything else at 17px in near black. Same size,
   same colour, one deliberate step for the lead.
   --------------------------------------------------------------------------- */

.wp-block-post-content {
	/*
	   Audit, 7 Sep. This was a bare 1.125rem, the only literal body size in the
	   stylesheet, sitting 1px away from the medium preset used by staff roles,
	   award titles and contact values. Two body sizes a pixel apart is not a
	   scale, it is a near miss. medium is now 1.125rem in theme.json and this
	   uses the token, so there is one body size and one place to change it.
	*/
	font-size: var(--wp--preset--font-size--medium);
	line-height: 1.7;
}

/*
   ONE READING MEASURE. This is the rule that keeps the column readable.

   Every text-level block takes its width from post-content's constrained
   layout, which is theme.json's contentSize, the same value as --ll-text.
   Nothing here sets its own width, so every paragraph, heading, list, table
   and aside shares one left edge and one right edge.

   NOTE, 7 Sep: contentSize was 720px when this was written and is 1040px now,
   widened so a painting's edges land on the same two lines as the paragraph
   above it. At 18px that measure runs 116 characters to the line, measured,
   against a comfortable maximum of about 75. Text and artwork are locked
   together by design, so the measure cannot be narrowed on its own without
   undoing that alignment. Narrowing both is a live decision, not an oversight.

   THE PAINTINGS NO LONGER BREAK IT, and that is a change from how this was
   built. They used to run to alignwide's 1140px, which is where the 851px
   height came from and why one picture was 170% of a laptop's visible page.
   Getting a 4:3 painting under 500px at 1140 wide needs a crop past 2.3:1, and
   the ink in this set was measured: the tightest, place.jpg, tolerates 1.35:1
   against a native 1.34:1. There is no crop in the library that wide, so the
   breakout had to go rather than the pictures.

   What replaces it reads better anyway. normalise_paper.py put every painting's
   paper on the page ground, so a picture set inside the measure has no edge at
   all: the drawing sits on the page like a plate in a book. Only the front-page
   opening picture still runs wider than the text, which is the one place a
   painting is meant to be an event.

   DO NOT give a text block its own max-width in `ch`. That was the previous
   arrangement and it is why this needed fixing. A `ch` is relative to the
   element's own font size, so 68ch on a paragraph, 60ch on the standfirst and
   24ch on an h2 resolved to three different widths, and a constrained layout
   centres each one independently. Measured on Service Fees at 1440px, that
   produced six different left edges on a single page: 142, 316, 345, 352, 395
   and 454. Headings sat 109px to the right of the paragraphs beneath them.

   If a block genuinely needs to be narrower, indent it from the shared edge
   with padding, so the column edge survives.
*/

/* First paragraph after a page title reads as a standfirst. Larger and looser,
   the same colour and the same measure as the text under it. The size step is
   the difference; the width is not. */
.wp-block-post-content > p:first-of-type {
	font-size: var(--wp--preset--font-size--large);
	line-height: 1.5;
}

.wp-block-post-content > * + * {
	margin-top: var(--wp--preset--spacing--40);
}

/* Headings need air above them and closeness to what follows, or a long page
   reads as one undifferentiated block. */
.wp-block-post-content h2 {
	margin-top: var(--ll-gap-near);
	margin-bottom: var(--wp--preset--spacing--30);
	position: relative;
	padding-top: var(--wp--preset--spacing--30);
	text-wrap: balance;
}

/*
   A plum rule marking a section. Bel: "a plum, like a mauve sort of colour,
   that's a nice colour that doesn't show up much."

   It used to sit above every single h2, which meant 27 of them stacked up on
   Service Fees. It now marks the first heading of a run only.
*/
.wp-block-post-content h2::before {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	width: 3rem;
	height: 3px;
	border-radius: 2px;
	background: var(--wp--preset--color--plum);
}

/* NOT ON A CARD. The rule above puts a 3rem plum hairline over every h2 in the
   post content, which is the page's section marker and is right there. On a
   flip card it lands above the service name inside its own colour block and
   reads as a stray mark: "the lines on the top of the start of the services
   don't work. please remove." */
/* Scoped through .wp-block-post-content so it outranks the rule below, which is
   (0,1,2) on specificity. A bare .ll-flip__name::before is (0,1,1) and loses,
   which is how this looked fixed while the line was still on the page. */
.wp-block-post-content .ll-flip__name::before,
.wp-block-post-content .ll-flip__backname::before,
.wp-block-post-content .ll-staff__name::before {
	content: none;
}

.wp-block-post-content h2 + h2::before,
.wp-block-post-content h2 + h3::before {
	display: none;
}

.wp-block-post-content h2 + h2 {
	margin-top: var(--wp--preset--spacing--40);
	padding-top: 0;
}

.wp-block-post-content h3 {
	margin-top: var(--wp--preset--spacing--50);
	margin-bottom: var(--wp--preset--spacing--20);
}

.wp-block-post-content h2 + p,
.wp-block-post-content h3 + p {
	margin-top: 0;
}

/*
   Lists were rendering as bare runs. Give them a plum marker and room.

   The :not() list is required. A pattern that builds its own layout out of a
   <ul> and lands inside page content, awards and contact details today, was
   getting all of this applied on top of its own rules and at a higher
   specificity. The awards row lost its grid and stacked four full-width badges
   down the page, and every award card picked up a 1.6rem bullet indent and a
   plum dot. Add any future list-based pattern to this list.
*/
.wp-block-post-content ul:not(.ll-awards, .ll-contact) {
	list-style: none;
	padding-left: 0;
	display: flex;
	flex-direction: column;
	gap: 0.6rem;
}

.wp-block-post-content ul:not(.ll-awards, .ll-contact) li {
	position: relative;
	padding-left: 1.6rem;
}

.wp-block-post-content ul:not(.ll-awards, .ll-contact) li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0.62em;
	width: 0.5rem;
	height: 0.5rem;
	border-radius: 50%;
	background: var(--wp--preset--color--plum-deep);
}

/*
   A section pattern designed to be full bleed carries 24px of its own inline
   padding so it can breathe against the viewport edge. Appended into page
   content it cannot reach that edge, it is already inset by the wide measure,
   and the padding then pushed its contents 24px right of every painting on the
   page. Inside post-content it shares the column edge instead.
*/
.wp-block-post-content .ll-section {
	padding-inline: 0;
}

/*
   A long single-column list of short items wastes two thirds of the page and
   reads as a wall. Anything past six items runs in columns instead.
*/
/*
   Two columns inside the reading measure, not four across the wide measure.
   At --ll-wide this list was the only text on the page running to 1140px, the
   same width as a painting, which made it read as a mistake rather than a
   device. It now holds the shared column edge like everything else.
*/
.wp-block-post-content ul.is-style-ll-columns {
	display: grid;
	/* Guarded the same way as .ll-svc-grid: 16rem clears a 375px phone today,
	   but a bare floor is one padding change away from overflowing. */
	grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
	gap: 0.6rem var(--wp--preset--spacing--50);
}

/* Artwork placed through a reading page, breaking the text into stretches that
   can actually be read. */
.ll-figure {
	margin-block: var(--ll-gap-near);
}

/* motion.js has always set --ll-ry and --ll-rx on .ll-tilt from the pointer
   position, but nothing ever consumed them, so the tilt Bel is used to seeing
   on her current site did nothing. This is the missing half. Pointer devices
   only, because motion.js does not attach the listeners otherwise, and the
   fallback of 0deg means the rule is inert until a pointer moves over it. */
.ll-tilt {
	transform: perspective(1200px)
		rotateX(var(--ll-rx, 0deg))
		rotateY(var(--ll-ry, 0deg));
	transition: transform 220ms ease-out;
	transform-style: preserve-3d;
	will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
	.ll-tilt {
		transform: none;
		transition: none;
	}
}

/* The cap goes on the figure and the picture is left alone. Nothing is cropped,
   so no child loses their head to an object-fit rule.

   border-radius went with it. normalise_paper.py put every painting's paper on
   #FCFAF3, the page ground, so there is no longer an edge for a corner to round:
   the drawing sits on the page rather than in a box. The rounded corner was
   drawing a rectangle that is no longer there. */
/* The full tier fills the measure, so a painting's edges land on the same two
   lines as the paragraph above it. That is the alignment Rohan drew.

   --ll-art-ceiling still caps it, and that is what keeps the 28 Aug fix alive:
   at 1040 wide a 4:3 painting is 776px tall, which is more than a 1280x720
   laptop can show, so on short windows the ceiling wins and the picture stays
   under the fold. On a tall window it grows to the measure. */
/*
   3. THE PAINTINGS DID NOT REACH THE TEXT EDGES. Rohan marked this up on this
      very page on 4 Sep, arrows from the painting out to both edges of the
      column: "align the image size". The height ceiling was quietly undoing it
      again, capping the picture at 952px inside a 1040px column, 44px short on
      each side, so every painting sat inset from the text above and below it.

      The ceiling still exists and still matters: it is what keeps a 4:3
      painting from being taller than a laptop can show. It just no longer
      decides the width when it is narrower than the measure. It caps the
      HEIGHT now, which is what it was always for, and the width holds the
      column edges.
*/
/*
   THE MEASURE, not the ceiling and not alignwide. Three states were possible
   here and only one is right:

     min(100%, --ll-art-ceiling)   952px, 44px inside the text on each side
     100% of an alignwide parent   1140x851, which is the 170%-of-the-fold
                                   picture the note above was written about
     var(--ll-text)                1040px, the text's own two edges

   The pictures are marked alignwide in the block markup, so without a cap they
   take the wide rail rather than the column, which is how lifting the ceiling
   put them straight back to 1140x851. The measure is the cap.

   WHAT THIS GIVES UP: at 1040 wide a 4:3 painting is 780px tall, against 711
   before, and that is more than a 900px laptop shows under the header. The
   ceiling was protecting against exactly that. Rohan asked for the picture to
   hold the text's edges on 4 Sep, marked up on this page, and again by asking
   why the fee page is not symmetrical, so the edges win and the height is the
   price. If the height matters more, the ceiling goes back and the pictures sit
   inset again.
*/
.wp-block-image.ll-figure {
	max-width: var(--ll-text);
	margin-inline: auto;
}

/* width:100%, not auto. The first attempt capped the img's HEIGHT and left its
   width auto, which let it fall back to its intrinsic 640px inside a 1040px
   figure: narrower than before rather than wider. The picture fills the column
   and its height follows. */
.wp-block-image.ll-figure img {
	width: 100%;
	height: auto;
}

.wp-block-image.ll-figure img {
	display: block;
	border-radius: 0;
}

/*
   Audit, 7 Sep. The post banner is a painting like any other now that the 21/9
   crop is gone, so it takes the same ceiling: it fills the measure on a tall
   window and stays under the fold on a short one.
*/
.ll-pagehead__img {
	/*
	   The measure as well as the ceiling. The featured image block sits in the
	   template rather than inside .ll-pagebody, so with only the ceiling on it
	   it ran to 1156px against the post's 1040px text and started 59px to the
	   left of the first paragraph: the same misalignment the 21/9 crop was
	   hiding. min() of the two puts its edges on the text's two edges and
	   still lets the ceiling win on a short window.
	*/
	max-width: min(100%, var(--ll-text), var(--ll-art-ceiling));
	margin-inline: auto;
}

.ll-pagehead__img img {
	display: block;
	border-radius: 5px;
}

/* Section marker rather than a full stop. Long pages get one full-size picture
   and these for everything after it.

   THE THREE TIERS NOW ALL FILL THE MEASURE, 4 Sep. Rohan marked up the Service
   Fees page with arrows from the painting out to both edges of the text: "align
   the image size". A spot at 440px under a 1040px column read as lost rather
   than as a grace note. The tiers still exist and --ll-art-half and
   --ll-art-spot are still set, because the ceiling and the per-page
   --ll-art-scale both still bite, but the width they resolve to is the measure.
   If the three sizes are ever wanted back as three sizes, this is the rule to
   undo, not the tokens. */
.wp-block-image.ll-figure--half {
	max-width: var(--ll-text);
}

/* A grace note, for pages that are a phone number or a fee table and where a
   full-size painting was most of the page. Contact was 78 words carrying a
   picture that took 29% of the scroll. */
.wp-block-image.ll-figure--spot {
	max-width: var(--ll-text);
}

/* The opening picture on the front page is the one image on the site that is
   allowed to be an event, and it is the only one a visitor sees before they
   know what the practice does. Bigger than a full, still under the fold. */
.ll-section--opening .wp-block-image.ll-figure {
	max-width: var(--ll-art-open);
}

/* PER-PAGE SCALE.
   Bel, 28 Aug, page by page: About Us up 25%, Services up 10%, Resources up
   40%, Contact up 45%. The tiers stay one system and each page multiplies it,
   rather than four pages each inventing their own width, a tier still means
   the same thing everywhere, it is just louder on some pages than others.

   Note where the multiply happens. It CANNOT go on :root next to --ll-art-full,
   because custom properties substitute at the element that declares them: a
   --ll-art-full defined on :root has already resolved --ll-art-scale to the
   :root value, and a body-level override arrives too late to change it. The
   multiply has to sit at the point of use, where the figure inherits the scale
   from its own body. Getting this backwards produces a rule that looks right,
   parses clean, and does nothing.

   Page classes come from ll_body_class() in functions.php; core does not ship
   a slug class for pages.

   These are requests, not guarantees: --ll-art-ceiling caps the result, so a
   page asks for more and gets as much of it as fits. On a 1280x720 laptop that
   means the smaller tiers grow as asked and the full tier does not, because a
   full painting was already at the ceiling before any of this. Measured, at
   1280x720:

     About Us  place  half  295 -> 369px   reading  full  418 -> 498px (capped)
     Services  three halves 295 -> 325px   wheelchair    418 -> 459px
     Resources painting spot 194 -> 272px
     Contact   dandelion spot 194 -> 281px

   The full tier goes UP under the band treatments rather than down. Nothing
   about the 60% rule moved; the header the ceiling subtracts did, from 240px
   to 100px, so the visible page is 620px rather than 480px. 498px wide is 372px
   tall against 620px, which is exactly the line. Under the four treatments in
   3b the header is 240px again and these read 391px, capped, as before. On a
   taller window the ceiling lifts and the scales get their full effect. */
body {
	--ll-art-scale: 1;

	/* MOVED HERE FROM :root, 4 Sep, and the move is the point. The ceiling
	   reads --ll-header-h, and --ll-header-h now depends on which header
	   treatment is active, which is a class on the body. A custom property
	   substitutes at the element that DECLARES it, so a ceiling declared on
	   :root would have resolved --ll-header-h to the :root value and a body
	   override would have arrived too late. Same trap as --ll-art-scale, see
	   the note just above, and it looks right and parses clean either way.

	   THE CEILING ITSELF. No painting may take more than 60% of the page a
	   visitor can actually see, which is the window minus the header that is
	   on screen while they read.

	   Written as a WIDTH like everything else here, so the 0.804 is two things
	   multiplied: 0.6 for the share of the visible page, and 1200/896 to turn
	   the height we want into the width that produces it.

	   This is what stops the per-page scales above from running away. A page
	   can ask for 45% more and get it, right up until the picture would fill
	   the screen, and then it stops. Without the cap Contact at 1.45 put a spot
	   painting at 69% of the visible page and About Us at 1.25 put a full one
	   at 78%, which is the fault the tiers were introduced to fix.

	   Not applied to --ll-art-open: the front page's opening painting is the
	   one image on the site that is meant to be an event, and it is signed off
	   as such. It is also the only picture on the site above this line. */
	--ll-art-ceiling: calc((100vh - var(--ll-header-h)) * 0.804);
}

/* Under the band treatments the only header a reader has on screen while they
   read is the pinned nav row, so that is what the ceiling should subtract. The
   four in 3b never scroll their masthead away and keep the 240px on :root. */
body.ll-header-band-sage,
body.ll-header-band-plum,
body.ll-header-warm-block {
	--ll-header-h: var(--ll-bar-h);
}

body.page-about-us    { --ll-art-scale: 1.25; }
body.page-services    { --ll-art-scale: 1.1; }
body.page-store       { --ll-art-scale: 1.4; }
body.page-contact     { --ll-art-scale: 1.45; }

.ll-figure figcaption {
	margin-top: var(--wp--preset--spacing--30);
	color: var(--wp--preset--color--muted);
	font-size: var(--wp--preset--font-size--small);
	max-width: 52ch;
}

/* A short aside pulled out of the run of text. */
.ll-aside {
	margin-block: var(--ll-gap-near);
	padding: var(--wp--preset--spacing--50);
	background: var(--wp--preset--color--ground-tint);
	border-left: 3px solid var(--wp--preset--color--plum);
	border-radius: 0 4px 4px 0;
}

/*
   margin:0 so a single-paragraph aside sits tight inside its padding. Every
   aside on the site held exactly one paragraph until the cancellation note
   gained a second on 7 Sep, and the two rendered with no gap at all between
   them: 22px copy running straight into 18px copy with nothing separating the
   thought. The gap goes between them rather than around them, so a one
   paragraph aside is unchanged.
*/
.ll-aside p {
	margin: 0;
}

.ll-aside p + p {
	margin-top: var(--wp--preset--spacing--30);
}

/* ---------------------------------------------------------------------------
   8. Tables

   The fee schedule arrived as 27 consecutive headings with no table at all, so
   a price and its footnote rendered identically. These rules assume it is now
   marked up as a table.
   --------------------------------------------------------------------------- */

.wp-block-table {
	overflow-x: auto;
	margin-block: var(--ll-gap-near);
}

.wp-block-table table {
	border-collapse: collapse;
	width: 100%;
	font-size: var(--wp--preset--font-size--medium);
}

/* A fee schedule is read down a column, so it gets horizontal rules only. The
   default block table draws a box around every cell, which turns a price list
   into a spreadsheet. */
.wp-block-table td,
.wp-block-table th {
	border: 0;
	border-bottom: 1px solid var(--ll-hairline);
	padding: 0.85rem 1rem 0.85rem 0;
	text-align: left;
	vertical-align: top;
}

.wp-block-table th {
	background: transparent;
	color: var(--wp--preset--color--muted);
	font-family: var(--wp--preset--font-family--body);
	font-weight: 600;
	font-size: var(--wp--preset--font-size--small);
	text-transform: uppercase;
	letter-spacing: 0.1em;
	border-bottom: 2px solid var(--wp--preset--color--plum);
	white-space: nowrap;
}

.wp-block-table tr:last-child td {
	border-bottom: 0;
}

/* The first cell is the thing being priced, so it leads. Ink rather than
   indigo, or every row label reads as a link. */
.wp-block-table td:first-child {
	color: var(--wp--preset--color--ink);
	font-weight: 600;
}

/* Money and durations line up and read as figures, not prose. */
.wp-block-table td:last-child,
.wp-block-table th:last-child {
	text-align: right;
	padding-right: 0;
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

.wp-block-table td:last-child {
	color: var(--wp--preset--color--ink);
	font-weight: 600;
}

.wp-block-table figcaption {
	margin-top: var(--wp--preset--spacing--30);
	color: var(--wp--preset--color--muted);
	font-size: var(--wp--preset--font-size--small);
	text-align: left;
	max-width: 60ch;
	line-height: 1.55;
}

/* A note under a price, set small so the figure above it stays dominant. */
.wp-block-table .ll-note {
	display: block;
	color: var(--wp--preset--color--muted);
	font-weight: 400;
	font-size: var(--wp--preset--font-size--small);
	margin-top: 0.2rem;
	white-space: normal;
}


/*
   MOBILE AUDIT, 7 Sep. The jump list at the top of Services is ten stacked
   navigation links at 36px, and the phone number in the footer base was 38px.
   Both are things a thumb works down a list to reach, so both go to 44.
*/
@media (max-width: 782px) {
	.ll-jump a,
	.ll-jumplist a,
	a[href^="#service-"] {
		min-height: 44px;
		display: flex;
		align-items: center;
	}

	.ll-footer__base a {
		display: inline-block;
		padding-block: 0.34rem;
	}
}

/*
   MOBILE AUDIT, 7 Sep. The footer navigation is a column of inline anchors
   23px tall with 8.8px between them: a 32px pitch on a list a thumb is meant
   to work down. Inline elements do not take vertical padding into the layout
   either, so nothing was separating the hit areas even when it looked like it
   was.

   inline-block plus padding takes each target to 44px and the list gap comes
   off, so the rhythm on screen is close to what it was and only the tappable
   area changes. Phones and small tablets only: on a desktop these are being
   clicked with a pointer and the tighter list reads better.
*/
@media (max-width: 782px) {
	.ll-footer a,
	.ll-footer__nav a,
	.ll-footer li > a {
		display: inline-block;
		padding-block: 0.62rem;
	}

	.ll-footer ul {
		gap: 0;
	}
}

/* ---------------------------------------------------------------------------
   9. Staff grid

   Bel likes this layout and said so, so the structure is unchanged. What was
   wrong was alignment and photo consistency. Photos vary in crop, background
   and head size, and a professional shoot was deferred on cost, so the frame
   does the unifying rather than the photograph.
   --------------------------------------------------------------------------- */

/*
   Three across, which is the layout Bel said she liked, but capped well short
   of the wide measure. Every staff photo we have is a 240px square thumbnail
   off the old site, so a 1140px row would upscale each face by half again. At
   960px the cards land near the native size and the photographs stay sharp.
   Pulling the full resolution originals is the real fix and is on the list.
*/
/* The extra classes are for weight, not decoration: WordPress sets alignwide
   to the 1140px wide size at the same specificity, and that rule would win. */
.wp-block-group.ll-staffgrid.alignwide {
	max-width: 960px;
	margin-inline: auto;
}

.ll-staffgrid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--wp--preset--spacing--50);
	margin-top: var(--ll-gap-near);
}

@media (max-width: 900px) {
	.ll-staffgrid {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (max-width: 560px) {
	.ll-staffgrid {
		grid-template-columns: 1fr;
	}
}

.ll-staff {
	display: flex;
	flex-direction: column;
	background: var(--wp--preset--color--white);
	border-radius: 5px;
	overflow: hidden;
	box-shadow: var(--ll-card-shadow);
	padding: 0;
}

/* One crop for all 22 photos, focal point set high so heads sit at a
   consistent height whatever the original framing was. */
/*
   WordPress sets .wp-block-image to width:fit-content, so each figure shrinks
   to the intrinsic width of its own file. The staff photos came off the old
   site at wildly different sizes, so that left some cards with a narrow photo
   and a dead strip beside it while others filled. The frame is what unifies
   this grid, so the figure is pinned to the full card width.
*/
.ll-staff__photo {
	margin: 0;
	width: 100%;
	max-width: none;
	display: block;
	position: relative;
	overflow: hidden;
	background: var(--wp--preset--color--ground-tint);
}

/*
   The photographs were taken in twenty two different places: white studio,
   black studio, outdoors, a patterned wall. We cannot reshoot them and we are
   not repainting anyone's background, so the next best thing is to show less
   of it and to land the bottom edge softly on the card instead of cutting it
   off. Costs nothing and alters no photograph.
*/
.ll-staff:not(.ll-staff--vacancy) .ll-staff__photo::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	/* Was 38% deep to 0.82 white, which reached far enough up the frame to wash
	   out chins and shoulders: "reduce the glow a little for the staff photos,
	   makes it hard to see." Shallower and weaker still lands the photograph
	   softly on the card, which is all this was ever for. */
	height: 26%;
	background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.58));
	pointer-events: none;
	z-index: 2;
}

/*
   THE BACKGROUNDS ARE NOW THE SAME BACKGROUND.

   A white radial wash used to live here: transparent over the face, opaque at
   the frame, pulling twenty-two mismatched backdrops toward the card. It cut
   the rendered spread by 69% and altered no file, and its own comment was
   honest that it was a treatment rather than a fix.

   It is gone because the fix arrived. scripts/normalise_staff.py segments each
   person out and composites them onto one ground, so the grid reads as one set
   because it IS one backdrop. Measured edge luminance across the set went from
   a range of 137 to a range of 61, and what is left of that is how much SHOULDER
   sits in the frame rather than any difference in backdrop.

   Leaving the wash on top would have hazed an already-uniform cream, so it went
   rather than being left lying. What stays is the bottom fade below, which is
   doing a different job: landing the photograph softly on the card.

   Still deliberately NOT a duotone or a desaturation, and the script does not
   touch a pixel of any person. Colour-keying the edges was tried and removed
   for the same reason, its failure mode was eating a face in proportion to how
   close that skin tone sat to that backdrop, which is precisely the wrong risk
   on a team the client wants shown as culturally diverse.

   The real fix is still the professional shoot; this is much closer to it.
*/

/* Square, because the sources are square. A portrait crop would take a fifth
   off the width of every face and upscale what was left.

   The slight scale crops in on the face, which shows less of whatever room the
   photograph happened to be taken in. */
.ll-staff__photo img {
	display: block;
	width: 100%;
	aspect-ratio: 1 / 1;
	object-fit: cover;
	object-position: 50% 18%;
	background: none;
	/* 1.06 to 1.13: a little more face, a little less of whatever room it was
	   taken in. Held back from going further because three of these are 240px
	   or smaller off the old site and go soft when pushed. */
	transform: scale(1.13);
	transform-origin: 50% 30%;
}

/* The vacancy card has no photograph, which left a hole in the grid. */
/*
   A card for somebody whose photograph has not been taken yet. Bel, 7 Sep:
   Samantha starts this week with no photo, and a photographer is coming in to
   redo every staff portrait, so this is a state the page will be in again.

   Distinct from the vacancy card on purpose. The vacancy is a gradient with a
   moon in it and says "this is a role"; this is the page ground with a single
   initial and says "this is a person, the portrait is coming". Same frame, so
   the row it sits in stays even.
*/
.ll-staff--pending .ll-staff__photo {
	background: var(--wp--preset--color--ground-tint);
	display: grid;
	place-items: center;
}

.ll-staff--pending .ll-staff__photo span {
	font-family: var(--wp--preset--font-family--display);
	font-size: 3.5rem;
	font-weight: 600;
	line-height: 1;
	color: var(--wp--preset--color--indigo);
	opacity: 0.22;
}

.ll-staff--vacancy .ll-staff__photo {
	aspect-ratio: 1 / 1;
	background: var(--wp--preset--gradient--sunrise);
	display: grid;
	place-items: center;
}

.ll-staff--vacancy .ll-staff__photo::after {
	content: "";
	width: 4.5rem;
	height: 4.5rem;
	border-radius: 50%;
	background: var(--wp--preset--color--moon);
	box-shadow: 0 0 0 0.75rem rgba(255, 249, 199, 0.5);
}

.ll-staff__body {
	padding: var(--wp--preset--spacing--50);
	display: flex;
	flex-direction: column;
	flex: 1;
	gap: 0.35rem;
}

/* One step up each, name and role, after the reading measure went 720px to
   1040px on 4 Sep. At the old sizes the cards read as small print against a
   much wider page: "font for staff images and details should be a little
   bigger to maintain page proportion." */
.ll-staff__name {
	margin: 0;
	font-size: var(--wp--preset--font-size--x-large);
}

/*
   The role was set in uppercase letterspaced plum at 700. At three and four
   lines it became the heaviest thing in the card and outweighed the name.
   Sentence case, lighter, and it steps back where it belongs.
*/
.ll-staff__role {
	margin: 0;
	color: var(--wp--preset--color--muted);
	font-size: var(--wp--preset--font-size--medium);
	line-height: 1.5;
	text-transform: none;
	letter-spacing: 0;
	font-weight: 500;
	flex: 1;
}

/*
   MEET THE TEAM USES THE SAME FLIP CARD as Services, so the site has one way of
   putting a paragraph behind a click rather than two. Rohan, 5 Sep: "can the
   meet the team also have flip cards to keep the styling of the site the same?"

   The differences from a service card are deliberate. The photograph is on the
   FRONT and never turns, because a grid of twenty two faces flipping away from
   the reader would lose the one thing the page is for; what turns is the story,
   which is exactly what Bel asked for: "you can click on each person and it
   comes up with their story". And the front is not centred, because it holds a
   portrait, a name, a role and post-nominals rather than a single title.
*/
/* Taller than a service card, because this one carries a portrait above four
   lines of text rather than a single title. At the services' old 26rem the
   photo was squeezed to the top of everyone's head. */
.ll-staff.ll-flip .ll-flip__inner {
	height: 34rem;
}

/*
   Audit, 7 Sep. The service card's back copy went up to the body size and this
   one deliberately did not follow.

   A service card is 508px wide, so its back has 436px of column and the body
   size sets about 50 characters to the line. A staff card is 320px wide in a
   three-up grid, so its back has 248px, and at the body size that is 27
   characters to the line: narrower than anything is meant to be read at, and a
   200-word story becomes 37 lines. Measured at 18px, Annabel's story ran
   1,320px inside a 672px card. small is the right size for this column, not a
   demotion of the copy.

   These stories scroll and there is no card height that stops them: 23 people
   at 320px wide cannot show 200 words each. That is why the fade above matters
   more here than on Services.
*/
.ll-staff.ll-flip .ll-flip__back p {
	font-size: var(--wp--preset--font-size--small);
}

.ll-staff.ll-flip .ll-flip__backname {
	font-size: var(--wp--preset--font-size--medium);
}

/* A share of the card rather than a square, so the body below always has room.
   At aspect-ratio 1/1 the photo took whatever it wanted and pushed the story
   control past the bottom edge on ten of the twenty cards. */
.ll-staff__photo {
	flex: 0 0 49%;
	margin: 0;
	overflow: hidden;
}

.ll-staff__photo img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: 50% 18%;
	display: block;
}

.ll-staff.ll-flip .ll-flip__front {
	justify-content: flex-start;
	padding: 0;
	background: var(--wp--preset--color--white);
	overflow: hidden;
}

.ll-staff.ll-flip .ll-flip__back {
	background: var(--wp--preset--color--ground-tint);
}

.ll-staff__body {
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
	padding: var(--wp--preset--spacing--40);
	flex: 1 1 auto;
	min-height: 0;
}

.ll-staff.ll-flip .ll-flip__toggle {
	margin-top: auto;
}

/* The post-nominals. Set apart from the role above it by weight and colour
   rather than by another size step, so a card reads name, then what they do,
   then what they hold, without three competing sizes. */
.ll-staff__creds {
	margin: 0.35rem 0 0;
	color: var(--wp--preset--color--indigo-soft);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
	letter-spacing: 0.01em;
	line-height: 1.4;
}

/* Pinned to the bottom of the card, so every story control in a row sits on
   the same line however long the role above it runs. */
.ll-staff .wp-block-details {
	margin-top: var(--wp--preset--spacing--40);
	border-top: 1px solid var(--ll-hairline);
	padding-top: var(--wp--preset--spacing--30);
}

.ll-staff .wp-block-details summary {
	font-size: var(--wp--preset--font-size--medium);
}

/* An open story used to push the whole grid row and leave a void beside the
   card. It now scrolls inside the card instead. */
.ll-staff .wp-block-details[open] > *:not(summary) {
	max-height: 22rem;
	overflow-y: auto;
	margin-top: var(--wp--preset--spacing--30);
	padding-right: 0.5rem;
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.65;
}

/* ---------------------------------------------------------------------------
   10. Blog

   The index was rendering excerpts into a 130px column, three words a line,
   with an unstyled default link under it.
   --------------------------------------------------------------------------- */

.ll-postgrid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--wp--preset--spacing--60);
}

@media (max-width: 900px) {
	.ll-postgrid {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (max-width: 600px) {
	.ll-postgrid {
		grid-template-columns: 1fr;
	}
}

.ll-postcard {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

.ll-postcard__img {
	margin: 0 0 var(--wp--preset--spacing--30);
}

.ll-postcard__img img {
	width: 100%;
	border-radius: 5px;
	display: block;
}

.ll-postcard__date {
	color: var(--wp--preset--color--muted);
	text-transform: uppercase;
	letter-spacing: 0.12em;
	font-weight: 600;
	margin: 0;
}

.ll-postcard__title {
	margin: 0;
}

.ll-postcard__title a {
	text-decoration: none;
}

.ll-postcard__title a:hover {
	text-decoration: underline;
}

.ll-postcard__excerpt {
	margin: 0;
	color: var(--wp--preset--color--ink);
}

.ll-postcard__excerpt p {
	max-width: none;
}

.wp-block-post-excerpt__more-link {
	display: inline-block;
	margin-top: 0.5rem;
	text-decoration: none;
	font-weight: 600;
	border-bottom: 2px solid var(--wp--preset--color--plum);
	padding-bottom: 2px;
}

.wp-block-post-excerpt__more-link:hover {
	border-bottom-color: var(--wp--preset--color--plum-deep);
}

/* ---------------------------------------------------------------------------
   11. Footer

   Every link here was indigo on the indigo ground: contrast 1.00 to 1, so the
   list rendered as eleven empty bullets on every page of the site.
   --------------------------------------------------------------------------- */

/* WordPress puts its root block gap between main and the footer, which drew a
   cream line between the enquiry band and the footer. The zero stays now that
   the footer is light, for a different reason: the enquiry band's own bottom
   padding is the separator the two blocks need, and a cream sliver between a
   dark band and a light footer reads as a seam rather than as a gap.

   Targeted on the template-part wrapper, not the footer: a template part with a
   tagName renders inside .wp-block-template-part, so the footer itself is never
   a child of .wp-site-blocks and the gap is on the wrapper. */
.wp-site-blocks > .wp-block-template-part:last-child {
	margin-block-start: 0;
}

/*
   MATCHED TO THE HEADER, 4 Sep. Rohan asked whether the foot should align with
   the new top and she said "align with that, yes, 100%". The footer stops being
   the second dark ground on the site and becomes the other half of the border.

   Consequence worth knowing: patterns/contact-cta.php's comment, "It is the one
   dark ground on the site", is true again. It was one of two.
*/
.ll-footer {
	position: relative;
	isolation: isolate;
}

/* The sweep is absolute at z-index 0, so everything that must sit over it needs
   a stacking position of its own. */
.ll-footer > *:not(.ll-sweep) {
	position: relative;
}

/* Three bands here and two up top, which is her sentence exactly: "remove the
   dark blue and have the rainbow effect, but just with the green and the
   purple. And then lower on the page, you've got the yellow, the green and the
   purple." Strengths are her measured ones, see the header fills in 3c. */
/*
   THE FOOTER RIBBON IS THE HEADER'S RIBBON, 7 Sep. Rohan: "the footer rainbow
   stripes look nothing like the top and they look so bad."

   They did, and the cause was the same class of thing the header was fixed for
   on 4 Sep. The base rule gives every sweep svg height:100%, and the footer's
   own preserveAspectRatio is slice, so the ribbon was scaled to COVER a box
   about 400px tall: at 1320px wide that is a scale of 1.27 taken from the
   height rather than the width, which blew her 5.06:1 ribbon up to 2026px wide
   and then threw away the right-hand third of it. What was left was three fat
   arcs at the wrong scale, cropped mid-sweep, sharing nothing with the header
   but their colours.

   The header solved this with height:auto, which lets the width lead and the
   height follow at her proportions. The footer does the same now, anchored to
   the bottom of the footer instead of the top, so the sweep runs along the foot
   of the page the way the header's runs along the top.
*/
.ll-footer .ll-sweep svg {
	height: auto;
	top: auto;
	bottom: 0;
}

.ll-footer .ll-sweep__band--1 { fill: var(--wp--preset--color--sun-pale); fill-opacity: 0.386; }
.ll-footer .ll-sweep__band--2 { fill: var(--wp--preset--color--sage);     fill-opacity: 0.456; }
.ll-footer .ll-sweep__band--3 { fill: var(--wp--preset--color--plum);     fill-opacity: 0.456; }

.ll-footer a {
	color: var(--wp--preset--color--indigo);
	text-decoration: none;
	border-bottom: 1px solid var(--ll-hairline);
	padding-bottom: 1px;
	transition: color 0.2s ease, border-color 0.2s ease;
}

.ll-footer a:hover {
	color: var(--wp--preset--color--plum-deep);
	border-bottom-color: var(--wp--preset--color--plum-deep);
}

.ll-footer__head {
	margin: 0 0 var(--wp--preset--spacing--40);
}

/*
   There is deliberately no logo in the footer, and .ll-footer__mark is gone
   with it rather than left behind as dead weight. The header is sticky, so the
   mark is on screen for the whole page; the footer copy was the same lockup a
   second time, larger, on the same indigo ground and inside the same viewport.
   Full reasoning in parts/footer.html.

   Worth keeping from the version that was here, because it still explains the
   header: the lockup is a night scene, a moon and a rising path with lights
   above the i's. On cream those lights are white on near-white and contribute
   nothing, which is why the mark reads as a pale smudge on a light ground and
   why the four header treatments in section 3b put it on indigo instead.

   The three in 3c answer the same problem the other way, because Bel asked for
   the indigo to come off: a pale band behind it and twice the size. Measured,
   the cost is only the pale figures, 1.30:1 for a sage figure on the sage band.
   The wordmark itself is 12.2:1 and is the thing she said was getting lost.

   If a mark is ever wanted down here again, build a small one sitting in
   .ll-footer__base beside the copyright, reading as a colophon rather than a
   second masthead. Do not simply restore the large one.
*/

/*
   Now the only thing in its column, so it carries the weight the logo used to.
   Bel asked for this line to be set larger and it is the line she chose, so it
   is a better signature here than a repeat of the mark.
*/
/*
   Scoped, because .ll-footer p further down is (0,1,1) and would otherwise win
   the max-width and hold this at 40ch. At 48px that is over 1200px, so the
   tagline ran the full width of its column as one long line instead of
   stacking into the block it is meant to be.
*/
.ll-footer .ll-footer__tagline {
	margin: 0;
	max-width: 15ch;
	line-height: 1.12;
	text-wrap: balance;
}

.ll-footer__links {
	list-style: none;
	padding: 0;
	margin: 0;
	display: flex;
	flex-direction: column;
	gap: 0.55rem;
}

.ll-footer p {
	margin: 0 0 var(--wp--preset--spacing--30);
	max-width: 40ch;
}

/* The colour comes from here now rather than from an inline #4A3F86 in the
   markup. indigo-soft was picked to sit one step off an indigo ground; on cream
   it is a heavy line, and the hairline is the tone every other rule on the site
   uses.

   The opacity: 0.55 that was here never did anything. The separator carries
   has-alpha-channel-opacity, and core sets opacity: 1 on that class at a higher
   specificity than a single class of ours. Removed rather than fought. */
.ll-footer__rule {
	margin-block: var(--wp--preset--spacing--50);
	border: 0;
	height: 1px;
	background: var(--ll-hairline);
}

/*
   THE ACKNOWLEDGEMENT, front page only. Rohan: "move the acknowledgement of
   country to the bottom of the page, make it the same size as the Leading
   Lights Early Intervention and not too much spacing."

   It was briefly in the footer, which was wrong: the footer is the bottom of
   every page, and Bel's instruction is that this belongs on the first page
   alone. It is the last block on the front page instead, after the enquiry
   band, at the same small size as the copyright line under it in the footer.

   No band and no 96px of padding, which is what it used to have to carry two
   lines of type. It closes the page quietly.
*/
/* .ll-section.ll-ack, not .ll-ack: the adjacency rule in section 4 hands a
   section its top padding back when the band above it carries a ground, and the
   enquiry band above this one does. That gave it 96px above, which is the "too
   much spacing" this was moved to get away from. */
.ll-section.ll-ack {
	padding-block: var(--wp--preset--spacing--50) var(--wp--preset--spacing--40);
}

.ll-ack__text {
	margin: 0;
	max-width: 76ch;
	line-height: 1.6;
}

/* Centred, the way it was before it moved. It is the last line on the page,
   under a full-width band, so it closes rather than joining the text rail. */

.ll-footer__base {
	align-items: flex-start;
	gap: var(--wp--preset--spacing--50);
}

/* ---------------------------------------------------------------------------
   12. Forms

   One form on the site, on Contact. Fields match the live site's so a parent
   who has used the old one finds the same questions.

   Sized for a phone first: 16px on the inputs, because iOS Safari zooms the
   whole page in on focus for anything smaller and the visitor has to pinch
   back out to see what they typed.
   --------------------------------------------------------------------------- */

.ll-form-wrap {
	margin-top: var(--ll-gap-far);
}

.ll-form {
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--40);
	max-width: var(--ll-text);
}

/* Two to a row on a wide screen, stacked on a narrow one. minmax(0, 1fr)
   rather than 1fr: a bare 1fr floors at min-content, and a long autofilled
   value then pushes the column past the page. */
.ll-form__row {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
	gap: var(--wp--preset--spacing--40);
}

.ll-form__field {
	display: flex;
	flex-direction: column;
	gap: 0.4rem;
	margin: 0;
}

.ll-form__field label {
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
	color: var(--wp--preset--color--indigo);
}

.ll-form__req {
	font-weight: 400;
	opacity: 0.62;
}

.ll-form input,
.ll-form textarea {
	font: inherit;
	/* 1rem, not the small preset: anything under 16px makes iOS Safari zoom
	   the page when the field takes focus. */
	font-size: 1rem;
	/*
	   Audit, 7 Sep. width:100% on a content-box input meant every field was its
	   column plus 25.6px of padding plus 2px of border. The two-column rows
	   overlapped each other by 4px and the full-width fields ran 28px past the
	   right edge of every other element on the page. Nothing else in the theme
	   needed this because core sets border-box on its own blocks; these inputs
	   are raw markup inside a wp:html block and never got it.
	*/
	box-sizing: border-box;
	width: 100%;
	padding: 0.7rem 0.8rem;
	color: var(--wp--preset--color--indigo);
	background: #fff;
	border: 1px solid var(--ll-hairline);
	border-radius: 5px;
	transition: border-color 0.18s ease, box-shadow 0.18s ease;
}

.ll-form textarea {
	resize: vertical;
	min-height: 8rem;
}

/* A visible focus ring that is the brand colour rather than the browser's, and
   two things wide enough to see. Never remove the outline without replacing it:
   this is how a keyboard visitor knows where they are. */
.ll-form input:focus-visible,
.ll-form textarea:focus-visible {
	outline: none;
	border-color: var(--wp--preset--color--indigo);
	box-shadow: 0 0 0 3px rgba(32, 23, 100, 0.16);
}

.ll-form__actions {
	margin: 0;
}

.ll-form__actions button {
	cursor: pointer;
	border: none;
}

/* Off-screen rather than display: none. A bot reading the markup fills in
   anything it finds; display: none is the one thing the better ones skip. */
.ll-form__trap {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

/* Square, because a rounded corner on a single-sided border reads as a
   half-drawn box. */
.ll-form__note {
	padding: 0.9rem 1.1rem;
	border-left: 3px solid;
	margin-bottom: var(--wp--preset--spacing--40);
}

.ll-form__note--ok {
	background: rgba(176, 213, 206, 0.3);
	border-left-color: var(--wp--preset--color--sage-deep);
}

.ll-form__note--err {
	background: rgba(168, 76, 76, 0.1);
	border-left-color: #a84c4c;
}

/* ---------------------------------------------------------------------------
   12b. Motion that belongs to this site rather than to any site

   Two effects that come out of the material rather than out of a library.
   --------------------------------------------------------------------------- */

/*
   THE PAINTINGS BLEED IN.

   Every one of these is watercolour on paper and the paper is now the page
   ground, so the obvious reveal is the one the medium already does: a wash
   arriving across the sheet. A soft-edged mask, swept once.

   A gradient mask rather than clip-path because clip-path has a hard edge, and
   a hard edge travelling across a watercolour is a wipe out of a video editor.
   The 300% mask-size gives the gradient room to sit entirely off the image at
   the start, and the long soft ramp is what makes the leading edge bleed
   instead of cut.

   Scoped to .ll-figure, so it is the paintings only. Service card and staff
   photographs are not paint and are left alone.

   Keyed on the figure and not on a revealing ancestor, because .ll-reveal only
   exists on the front page: keyed the other way this applied to almost none of
   the artwork on the site. motion.js observes .ll-figure for the same reason.
*/
.js .ll-figure img {
	-webkit-mask-image: linear-gradient(105deg, #000 0 38%, rgba(0, 0, 0, 0.55) 52%, transparent 68%);
	mask-image: linear-gradient(105deg, #000 0 38%, rgba(0, 0, 0, 0.55) 52%, transparent 68%);
	-webkit-mask-size: 300% 100%;
	mask-size: 300% 100%;
	-webkit-mask-position: 100% 0;
	mask-position: 100% 0;
}

.js .ll-figure.is-in img {
	-webkit-mask-position: 0 0;
	mask-position: 0 0;
	transition: -webkit-mask-position 1.15s ease-out, mask-position 1.15s ease-out;
}

/*
   NO DRIFT ON THE RIBBON, and this is the second time that has been settled.
   A drift was written here once and deleted the same day, because the banner
   bands already had one in section 13. Those bands and that animation are both
   gone now, 4 Sep, with the ribbon moved into the header. Do not reintroduce a
   drift on the header ribbon: the header is sticky, so it never leaves the
   screen, and motion there is permanent rather than something a reader scrolls
   past. The reveals below are the movement Bel asked for.
*/

/* ---------------------------------------------------------------------------
   13. Motion

   Bel on the current site: "I like that the page is interactive, that there's
   some like things happening, but it's not just still." Kept small and slow,
   and all of it switches off under prefers-reduced-motion.
   --------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}

/*
   Only hide a reveal if JavaScript is actually going to bring it back.

   The .js guard alone was not enough. It says a script ran, not that the
   reveal mechanism works, and those came apart: inside a frame the observer
   this used to rely on existed, accepted every element, and never fired once,
   so all seven sections of the front page sat at opacity 0 for good. Anything
   that stops the reveal logic reaching an element -- a suspended callback, a
   viewport reporting no height, a script that throws before it gets there --
   used to mean a blank page.

   So the hidden state now waits for .ll-motion-ready, which motion.js sets on
   <html> only once it has a real viewport to measure and is about to start
   revealing. No class, nothing hidden, page readable. The animation is an
   enhancement and is finally written like one.
*/
.js.ll-motion-ready .ll-reveal {
	opacity: 0;
	transform: translateY(14px);
	transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}

.js.ll-motion-ready .ll-reveal.is-in {
	opacity: 1;
	transform: none;
}

@media (prefers-reduced-motion: reduce) {
	.js .ll-reveal {
		opacity: 1;
		transform: none;
	}
}

/* Staff cards: click to read the story, which is the behaviour Bel liked on
   the current team page. A disclosure rather than a modal, so it is keyboard
   reachable, survives printing, and needs no plugin. */
.wp-block-details summary {
	cursor: pointer;
	list-style: none;
	font-weight: 600;
	color: var(--wp--preset--color--indigo);
	display: flex;
	align-items: center;
	gap: 0.5rem;
	transition: color 0.2s ease;
}

.wp-block-details summary::-webkit-details-marker {
	display: none;
}

.wp-block-details summary::before {
	content: "";
	width: 0.4rem;
	height: 0.4rem;
	border-radius: 50%;
	background: var(--wp--preset--color--plum);
	flex: 0 0 auto;
}

.wp-block-details summary::after {
	content: "";
	width: 0.5rem;
	height: 0.5rem;
	border-right: 2px solid currentColor;
	border-bottom: 2px solid currentColor;
	transform: rotate(45deg) translate(-2px, -2px);
	transition: transform 0.25s ease;
	margin-left: auto;
}

.wp-block-details[open] summary::after {
	transform: rotate(-135deg) translate(-2px, -2px);
}

.wp-block-details summary:hover {
	color: var(--wp--preset--color--plum-deep);
}

.wp-block-details[open] > *:not(summary) {
	animation: ll-unfold 0.35s ease both;
}

@keyframes ll-unfold {
	from {
		opacity: 0;
		transform: translateY(-4px);
	}
}

@media (prefers-reduced-motion: reduce) {
	.wp-block-details[open] > *:not(summary) {
		animation: none;
	}
	.wp-block-details summary::after {
		transition: none;
	}
}

/* Testimonials move, as they do on the current site. Scroll-snap carries the
   movement, so it stays swipeable on a phone and needs no carousel library. */
.ll-carousel {
	display: flex;
	gap: var(--wp--preset--spacing--50);
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;
	scrollbar-width: none;
	padding-bottom: 0.5rem;
}

.ll-carousel::-webkit-scrollbar {
	display: none;
}

/*
   The scrollbar is hidden and there is no drag, so without these the only way
   through the testimonials with a mouse was to wait, and hovering stopped even
   that. Built by motion.js, so they exist only when they work.

   Right-aligned rather than flanking the track: three cards already run to both
   edges of the measure at 1140px, and arrows outside that would push the whole
   band wider than every other section on the page.
*/
.ll-carousel__nav {
	display: flex;
	justify-content: flex-end;
	gap: 0.5rem;
	margin-top: var(--wp--preset--spacing--40);
}

.ll-carousel__btn {
	width: 2.75rem;
	height: 2.75rem;
	border-radius: 50%;
	border: 1px solid var(--ll-hairline);
	background: var(--wp--preset--color--ground);
	color: var(--wp--preset--color--indigo);
	/*
	   Audit, 7 Sep. A bare button takes the browser's font, not the page's, so
	   these two were the only Arial on the site. font:inherit first, then the
	   size, or the shorthand resets what follows it.
	*/
	font: inherit;
	font-size: var(--wp--preset--font-size--medium);
	line-height: 1;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: background-color 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.ll-carousel__btn:hover {
	background: var(--wp--preset--color--moon);
	border-color: var(--wp--preset--color--moon);
}

.ll-carousel__btn:active {
	transform: scale(0.94);
}

/* 44px is the minimum comfortable target, which is why these are 2.75rem, and
   the focus ring is the brand's rather than the browser's. */
.ll-carousel__btn:focus-visible {
	outline: none;
	border-color: var(--wp--preset--color--indigo);
	box-shadow: 0 0 0 3px rgba(32, 23, 100, 0.18);
}

@media (prefers-reduced-motion: reduce) {
	.ll-carousel__btn {
		transition: none;
	}

	.ll-carousel__btn:active {
		transform: none;
	}
}

.ll-carousel > * {
	scroll-snap-align: center;
	flex: 0 0 min(100%, 26rem);
}

@media (min-width: 700px) {
	.ll-carousel > * {
		flex-basis: calc(50% - var(--wp--preset--spacing--50) / 2);
	}
}

@media (min-width: 1000px) {
	.ll-carousel > * {
		flex-basis: calc(33.333% - var(--wp--preset--spacing--50) * 2 / 3);
	}
}

/*
   Was pure white, which made these the largest white objects on the front page
   and put the one colour she named as the thing she did not want directly under
   the words of four happy parents. A plum wash instead, which also picks up the
   quote mark above.
*/
.ll-quote {
	background: #EFE3EA;
	background: color-mix(in srgb, var(--wp--preset--color--plum) 34%, var(--wp--preset--color--ground));
	border-radius: 5px;
	padding: var(--wp--preset--spacing--50);
	box-shadow: var(--ll-card-shadow);
	margin: 0;
	display: flex;
	flex-direction: column;
}

/* These run long, so they are set at reading size rather than display size,
   and the card stretches so a row of them stays level. */
.ll-quote__text {
	margin: 0 0 var(--wp--preset--spacing--40);
	padding: 0;
	border: 0;
	color: var(--wp--preset--color--ink);
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--medium);
	line-height: 1.6;
	flex: 1;
}

.ll-quote__text::before {
	content: "\201C";
	font-family: var(--wp--preset--font-family--display);
	font-size: 2.6rem;
	line-height: 0;
	color: var(--wp--preset--color--plum);
	vertical-align: -0.35em;
	margin-right: 0.15rem;
}

.ll-quote__cite {
	margin: 0;
	padding-top: var(--wp--preset--spacing--30);
	border-top: 1px solid var(--ll-hairline);
	display: flex;
	align-items: center;
	gap: 0.6rem;
	font-style: normal;
}

.ll-quote__stars {
	color: var(--wp--preset--color--moon);
	letter-spacing: 0.06em;
	font-size: 0.95rem;
	/* The pale gold needs an outline to read on white. */
	-webkit-text-stroke: 0.4px rgba(32, 23, 100, 0.35);
}

.ll-quote__name {
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	color: var(--wp--preset--color--indigo);
	font-size: var(--wp--preset--font-size--medium);
}

/* ---------------------------------------------------------------------------
   Contact details.

   Phone, address and hours were three stacked headings each carrying a section
   rule, which turned about twenty words into two screens of page. They are one
   group of short facts, so they read as one row.
   --------------------------------------------------------------------------- */

/*
   The reading-page rules further up set every ul inside post content to a
   flex column with a plum dot on each item. That is right for a run of prose
   and wrong here, and it beat this block on specificity, so the row rendered
   as a bulleted stack with full width rules through it. Hence the doubled
   selectors: they are for weight, not decoration.
*/
.ll-contact,
.wp-block-post-content ul.ll-contact {
	list-style: none;
	margin-block: var(--ll-gap-near) 0;
	padding: 0;
	/*
	   Audit, 7 Sep. This was max-width:none, which let the row fill the 1140px
	   wrapper while the page title and the paragraph above it stayed on the
	   1040px measure. "Call us" inked at x=143 and "Contact" at x=193, so the
	   three details hung 50px out to the left of everything else on the page.
	   The prose ul rule this block fights already excludes .ll-contact, so the
	   escape was buying nothing.
	*/
	max-width: var(--ll-text);
	margin-inline: auto;
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--wp--preset--spacing--60);
}

@media (max-width: 720px) {
	.ll-contact,
	.wp-block-post-content ul.ll-contact {
		grid-template-columns: 1fr;
		gap: var(--wp--preset--spacing--50);
	}
}

.ll-contact__item,
.wp-block-post-content ul.ll-contact > li {
	position: static;
	padding: 0;
	margin: 0;
	border: 0;
}

/* No dot. This is a label and a fact, not a list of points. */
.ll-contact__item::before,
.wp-block-post-content ul.ll-contact > li::before {
	content: none;
	display: none;
}

/* One short rule per item, the same mark the reading pages use above a
   section heading. There used to be a second, full width rule above this one
   as well, which marked nothing and simply drew a line across the page. */
.ll-contact__label,
.wp-block-post-content ul.ll-contact .ll-contact__label {
	margin: 0 0 0.4rem;
	padding-top: var(--wp--preset--spacing--30);
	position: relative;
	max-width: none;
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--wp--preset--color--muted);
}

.ll-contact__label::before {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	width: 3rem;
	height: 3px;
	border-radius: 2px;
	background: var(--wp--preset--color--plum);
}

.ll-contact__value,
.wp-block-post-content ul.ll-contact .ll-contact__value {
	margin: 0;
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--x-large);
	font-weight: 600;
	line-height: 1.3;
	color: var(--wp--preset--color--indigo);
	max-width: none;
}

/* No underline at rest. Sitting under a "Call us" label the number is plainly
   a number, and a permanent rule under it just read as another stray line. It
   is still a tel: link, so it still dials, and the underline comes back on
   hover and on keyboard focus. */
.ll-contact__value a {
	text-decoration: none;
	border-bottom: 2px solid transparent;
	padding-bottom: 2px;
	white-space: nowrap;
	transition: border-color 0.2s ease;
}

.ll-contact__value a:hover,
.ll-contact__value a:focus-visible {
	border-bottom-color: var(--wp--preset--color--plum-deep);
}

/* ---------------------------------------------------------------------------
   Awards. Bel: "When we've got all our awards on there, make it look really
   professional." The badges come from four different bodies and are wildly
   different shapes, so a fixed-height box does the aligning and each badge
   sits centred inside it.
   --------------------------------------------------------------------------- */

/*
   The second selector is load-bearing, not belt and braces. On About Us this
   pattern is appended inside post-content, where `.wp-block-post-content ul`
   sets display:flex and flex-direction:column at higher specificity than a
   bare `.ll-awards`. That silently beat the grid and stacked all four badges
   one per row down the page, each one full width. On the front page, where the
   pattern sits directly in the template, the same CSS looked correct, which is
   why it went unnoticed. Any list-based pattern that can land inside page
   content needs qualifying the same way. `.ll-contact` already is.
*/
.ll-awards,
.wp-block-post-content ul.ll-awards {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: var(--wp--preset--spacing--50);
}

@media (max-width: 900px) {
	.ll-awards,
	.wp-block-post-content ul.ll-awards {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (max-width: 480px) {
	.ll-awards,
	.wp-block-post-content ul.ll-awards {
		grid-template-columns: 1fr;
		max-width: 22rem;
		margin-inline: auto;
	}
}

.ll-award {
	text-align: center;
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
}

.ll-award__badge {
	height: 132px;
	display: grid;
	place-items: center;
	background: var(--wp--preset--color--white);
	border-radius: 5px;
	padding: var(--wp--preset--spacing--40);
	margin-bottom: var(--wp--preset--spacing--30);
	box-shadow: var(--ll-card-shadow);

	/*
	   A grid row sizes itself to its content by default, so the 153x300 trophy
	   grew the row to 406px and then height:100% on the image resolved against
	   that grown row rather than against this box. The badge stayed 132px and
	   the image hung 250px out the bottom, over the caption and the section
	   below. minmax(0, 1fr) is what stops a grid track exceeding its container.
	*/
	grid-template-rows: minmax(0, 1fr);
	grid-template-columns: minmax(0, 1fr);
	overflow: hidden;
}

/* The four badges come from four bodies at four aspect ratios, one of them
   153x300. Sizing by height alone let the tall one escape its box and land on
   the caption, so the image fills the frame and contains itself inside it. */
.ll-award__badge img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: contain;
	background: none;
}

.ll-award__title {
	margin: 0;
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	color: var(--wp--preset--color--indigo);
	font-size: var(--wp--preset--font-size--medium);
	line-height: 1.3;
}

.ll-award__detail {
	margin: 0;
	color: var(--wp--preset--color--muted);
	font-size: var(--wp--preset--font-size--small);
}

/*
   Cards lift. The current site uses Elementor's "grow" on the team cards and
   Bel likes it, so this matches that rather than inventing something quieter.

   The transition sits outside the hover query on purpose. It used to be inside,
   which meant a phone got no lift, no press feedback and no image drift, so on
   the device most parents actually arrive on the whole page was inert. Touch
   now gets the same movement on press.
*/
.ll-card,
.ll-staff {
	transition: transform 0.32s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.32s ease;
}

.ll-card__img img,
.ll-staff__photo img,
.ll-postcard__img img {
	transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Touch: a press state, since there is no hover to give feedback with. */
.ll-card:active,
.ll-staff:active,
.ll-postcard:active {
	transform: translateY(-3px) scale(0.995);
}

@media (hover: hover) {
	.ll-card:hover,
	.ll-staff:hover {
		transform: translateY(-4px);
		box-shadow: 0 1px 2px rgba(32, 23, 100, 0.05), 0 22px 44px -24px rgba(32, 23, 100, 0.45);
	}

	/* Paintings and photographs drift in very slightly behind their frame. */
	.ll-card:hover .ll-card__img img,
	.ll-postcard:hover .ll-postcard__img img {
		transform: scale(1.04);
	}

	/* Staff photos already sit at a slight crop, so the hover builds on it. */
	.ll-staff:hover .ll-staff__photo img {
		transform: scale(1.1);
	}
}

/*
   Children of a revealed grid arrive one after another rather than as one
   block. Six identical fades firing at once is what made the page read as
   static even though something was technically moving.
*/
.js .ll-reveal.is-in :is(.ll-cardgrid, .ll-postgrid, .ll-awards, .ll-carousel, .ll-steps) > * {
	animation: ll-rise 0.62s cubic-bezier(0.22, 1, 0.36, 1) both;
}

.js .ll-reveal.is-in :is(.ll-cardgrid, .ll-postgrid, .ll-awards, .ll-carousel, .ll-steps) > *:nth-child(2) { animation-delay: 0.09s; }
.js .ll-reveal.is-in :is(.ll-cardgrid, .ll-postgrid, .ll-awards, .ll-carousel, .ll-steps) > *:nth-child(3) { animation-delay: 0.18s; }
.js .ll-reveal.is-in :is(.ll-cardgrid, .ll-postgrid, .ll-awards, .ll-carousel, .ll-steps) > *:nth-child(4) { animation-delay: 0.27s; }

@keyframes ll-rise {
	from { opacity: 0; transform: translateY(18px); }
	to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
	/* The drawn line is the one bit of motion here that carries meaning, so it
	   still ends up drawn. It just arrives already drawn rather than travelling. */
	.js .ll-reveal .ll-step:not(:last-child)::after,
	.js .ll-reveal.is-in .ll-step:not(:last-child)::after {
		transform: none;
		transition: none;
	}

	.js .ll-reveal.is-in :is(.ll-cardgrid, .ll-postgrid, .ll-awards, .ll-carousel, .ll-steps) > * {
		animation: none;
	}

	.ll-card:active,
	.ll-staff:active,
	.ll-postcard:active {
		transform: none;
	}
}

@media (prefers-reduced-motion: reduce) {
	/* Remove the mask outright rather than trusting the global 0.01ms override
	   to snap it. A painting behind a mask that never sweeps is an invisible
	   painting, and this is the one rule here whose failure is a blank page
	   rather than a missing flourish. */
	.js .ll-figure img,
	.js .ll-figure.is-in img {
		-webkit-mask-image: none;
		mask-image: none;
		transition: none;
	}

	.ll-card,
	.ll-staff,
	.ll-card__img img,
	.ll-postcard__img img {
		transition: none !important;
		transform: none !important;
	}

	/* The staff crop is layout, not motion, so it stays. */
	.ll-staff__photo img {
		transition: none !important;
	}
}

/*
   MOBILE AUDIT, 7 Sep. THE FIXED HEIGHT EXISTS FOR THE GRID, AND ON A PHONE
   THERE IS NO GRID.

   Rohan's rule was "they can't be different sizes", and that is about cards
   sitting beside each other in a row: ten different rectangles across a page
   reads as a mistake. In one column nothing is beside anything, so the fixed
   height buys nothing and costs the worst interaction there is on a touch
   screen: a scrolling box inside a scrolling page. A thumb dragged over a card
   back scrolls the card, stops dead at its end, and the page underneath never
   moves. On Meet The Team that was sixteen of the twenty stories.

   THE FACES SWAP BY DISPLAY HERE, they do not rotate. Both faces share one
   grid cell, so an auto height is the height of the TALLER of the two, and
   simply unpinning the height gave every card a front face as tall as the copy
   hidden behind it: a 1,072px block of colour holding a title and a button.
   Taking the hidden face out of flow is what lets each side be its own height.

   The card, its colours, its type and the control all stay exactly as they are
   on desktop. What goes is a 3D Y-rotation of a full-width card, which is the
   heaviest thing on the page to composite and the least useful thing on a
   phone. Below 880px this is a card that turns over; above it, unchanged.

   Placed after both height rules on purpose: .ll-staff.ll-flip .ll-flip__inner
   is (0,3,0) and an earlier version of this block sat above it and lost.
*/
@media (max-width: 880px) {
	.ll-flip__inner {
		height: auto;
		min-height: 0;
		transform: none !important;
		transition: none;
	}

	/*
	   A floor on the staff card only. A service card with a short name is still
	   a reasonable rectangle, but a staff card carries a portrait, and one with
	   no story behind it collapsed to 222px in a column of 550px cards. That is
	   Samantha, who joined on 7 Sep without a photograph yet, and it will be
	   every card for a while when the new staff portraits are being taken.
	*/
	.ll-staff.ll-flip .ll-flip__inner {
		height: auto;
		min-height: 26rem;
		transform: none !important;
		transition: none;
	}

	.js .ll-flip__front,
	.js .ll-flip__back {
		transform: none !important;
		backface-visibility: visible;
		-webkit-backface-visibility: visible;
	}

	.js .ll-flip:not(.is-flipped) .ll-flip__back,
	.js .ll-flip.is-flipped .ll-flip__front {
		display: none;
	}

	.ll-flip__back {
		overflow: visible;
		mask-image: none;
		-webkit-mask-image: none;
	}

	/* The control that does all the work on this page. */
	.ll-flip__toggle {
		min-height: 44px;
	}

	.ll-flip__toggle--back {
		padding-inline: 0.7rem;
		margin-inline-start: -0.7rem;
	}
}

/* Her closing paragraph, moved here from the top of the page. */
.ll-svc-outro {
	margin-block-start: var(--wp--preset--spacing--60);
}

/*
   MOBILE AUDIT, 7 Sep. The phone number is the most important tap on this site
   for a parent who wants to talk to somebody, and it appears in the footer of
   every page and again in the contact details. It measured 105x38 and 153x32.
   Same for the "Read on" link under each post card at 60x30.

   tel: links get a comfortable target rather than a compliant one, because
   this is the one a worried parent taps one-handed.
*/
@media (max-width: 782px) {
	a[href^="tel:"],
	.wp-block-post-excerpt__more-link {
		display: inline-block;
		min-height: 44px;
		padding-block: 0.55rem;
	}

	.ll-contact__value a[href^="tel:"] {
		padding-block: 0.3rem;
	}
}

/*
   MOBILE AUDIT, 7 Sep. The seven links inside the opened menu overlay measured
   327x39. They are the whole navigation on a phone, stacked, and a stacked
   list is the case the 44px guidance exists for: the miss goes to whichever
   neighbour the thumb strays onto rather than to nothing at all.
*/
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item__content {
	display: flex;
	align-items: center;
	min-height: 44px;
}

/* ---------------------------------------------------------------------------
   The NDIS fee schedule

   Built 7 Sep from the service user agreement Bel sent through, replacing the
   figures scraped off the old site. Two things it has to carry that the plain
   block table does not: a support and its conditions in one cell, and two
   price columns that belong to the two NDIA registration codes rather than to
   two unrelated things.
   --------------------------------------------------------------------------- */

/*
   THE FEE TABLES, rebuilt 7 Sep. Rohan: "fee page looks very strange, no
   consistency."

   The pass before this one made it worse. It leant on the <strong> already in
   the copy and set it larger, which meant an 18px figure landed inline inside a
   14px line: "1.5 hours charged = $234.24" changed size and baseline halfway
   through the sentence, while the cell above it had its bold figure alone on
   its own line and the two cells below had no bold at all. Four cells in one
   column, four different shapes.

   EVERY PRICE CELL IS NOW THE SAME SHAPE: one amount, its unit, and an optional
   note under it. Nothing changes size inside a line, and the amount is always
   the first thing in the cell.

   The rows changed with it. Three of the four rows in the first table used to
   show the identical pair of hourly rates, so the table repeated $156.16 and
   $193.99 down the page while the thing that actually differed, the conditions,
   sat in small grey type. Where a row has a real total it now leads with it,
   $234.24 a session rather than an hourly rate the reader has to multiply, and
   the rate moves into the note where it belongs as workings.

   The two headings above the second and third tables are gone: each was a
   restatement of the table header directly beneath it.
*/

/* One size, one weight, one colour, always first in the cell. */
.ll-fee__amt {
	font-size: var(--wp--preset--font-size--medium);
	font-weight: 600;
	color: var(--wp--preset--color--ink);
	font-variant-numeric: tabular-nums;
}

.ll-fee__unit {
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--muted);
}

/* Conditions on their own line, under the figure, never inside it. */
.ll-fee__note {
	display: block;
	margin-top: 0.3rem;
	font-size: var(--wp--preset--font-size--small);
	font-weight: 400;
	line-height: 1.5;
	color: var(--wp--preset--color--muted);
}

.ll-fee__name {
	font-weight: 600;
	color: var(--wp--preset--color--ink);
}

/* The caption belongs to the table, not to its first column. */
.ll-fee figcaption {
	margin-top: var(--wp--preset--spacing--30);
	max-width: none;
}

.wp-block-post-content > h2 + .wp-block-table {
	margin-top: var(--wp--preset--spacing--30);
}

.ll-fee table {
	width: 100%;
	table-layout: fixed;
}

/* The header carries a code under a label, so it cannot be one nowrap line. */
.ll-fee th {
	white-space: normal;
	vertical-align: bottom;
	line-height: 1.35;
}

.ll-fee__code {
	display: block;
	font-weight: 400;
	letter-spacing: 0.04em;
	text-transform: none;
	opacity: 0.72;
}

/* The name of the support leads; its conditions sit under it as a note rather
   than as a second column nobody would scan. */
.ll-fee__note {
	display: block;
	margin-top: 0.3rem;
	font-weight: 400;
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.5;
	color: var(--wp--preset--color--muted);
}

/* Money reads as money: same figure width down the column, and the price
   columns share one treatment instead of only the last one being a fee. */
.ll-fee td:not(:first-child),
.ll-fee th:not(:first-child) {
	font-variant-numeric: tabular-nums;
	text-align: left;
	white-space: normal;
}

.ll-fee td:not(:first-child) {
	line-height: 1.55;
}

/* One rate for both ages, said once across both columns rather than printed
   twice and read as a coincidence. */
.ll-fee__span {
	text-align: center !important;
	background: var(--wp--preset--color--ground-tint);
}

/* The first column is the thing being priced and takes the room. */
.ll-fee th:first-child,
.ll-fee td:first-child {
	width: 46%;
}

.ll-fee--flat th:first-child,
.ll-fee--flat td:first-child {
	width: 58%;
}

/*
   Below 720px three columns at 327px would be about 100px each, which is
   narrower than "$193.99 per hour" can set. The table scrolls in its own
   container instead, which .wp-block-table already allows, and the first
   column is pinned wide enough to stay readable while it does.
*/
@media (max-width: 720px) {
	.ll-fee table {
		table-layout: auto;
		min-width: 34rem;
	}

	.ll-fee th:first-child,
	.ll-fee td:first-child,
	.ll-fee--flat th:first-child,
	.ll-fee--flat td:first-child {
		width: auto;
		min-width: 13rem;
	}
}

/* ---------------------------------------------------------------------------
   FULL-BLEED BANDS ON EVERY PAGE, 7 Sep

   Rohan: "can you take the borders to the whole page for all pages?"

   On the front page a tinted section is a direct child of main, so alignfull
   reaches both edges of the screen. Inside a page body it cannot: the same
   pattern sits in post-content (1140px) inside .ll-pagebody (1140px) inside
   main, and alignfull can only reach the nearest edge it is given. Measured on
   About Us, the awards band was a 1140px tinted rectangle starting 142px in
   and floating in the middle of the page, while the dark call to action at the
   foot of the same page ran edge to edge, because that one IS a child of main.
   One page, two treatments of the same device.

   contact-details.php has carried a note about this since it was written:
   "an alignfull wrapper cannot reach the edges and instead renders as a grey
   rectangle floating inside the column". This is that note answered.

   THE CLIP GOES ON main. The band is one scrollbar wider than the layout,
   because 100vw counts the scrollbar and the layout does not, so that overflow
   has to be clipped or every page gains a horizontal scrollbar.

   main, because it is already exactly the full layout width, so the clip falls
   where the band should end anyway; and because it is a sibling of the header
   rather than an ancestor, which keeps the clip off the sticky chain entirely.
   clip rather than hidden for the same reason: hidden makes a scroll container.

   The header sticks at top: -362.9px, so it rides up and pins its nav bar. That
   is easy to mistake for a broken sticky when you scroll and find the header at
   -363. It is not: measured at scrollY 600, 1200 and 2400 it holds -363 every
   time, with 125px of nav on screen, and a header that had come loose would
   read -600, -1200, -2400. Checked with the clip in place.
   --------------------------------------------------------------------------- */

main.wp-block-group {
	overflow-x: clip;
}

.wp-block-post-content > .ll-section.alignfull,
.wp-block-post-content > .wp-block-group.alignfull {
	/* 50% of the column out to 50% of the screen, both sides. */
	margin-inline: calc(50% - 50vw);
	width: 100vw;
	max-width: 100vw;

	/*
	   The contents are capped at the measure and centred, which gives them a
	   generous gutter on a desktop. On a phone the measure is wider than the
	   screen, so the cap stops biting and the badges ran to both edges with
	   nothing either side: measured on About Us at 375px, the awards row was
	   0 to 375. The band needs the page's own side padding of its own.
	*/
	padding-inline: var(--wp--preset--spacing--40);
}
