/* ==========================================================================
   Clive Nichols - site styles
   Replaces the WordPress theme's style.css + responsive.css + flexslider.css.
   No framework, no jQuery dependency. Layout uses grid/flex rather than the
   original's floats, Isotope and Masonry.
   ========================================================================== */

/* Albert Sans, self-hosted (variable weight, latin + latin-ext subsets from
   Google Fonts v4). No third-party request, and nothing to disclose. */

@font-face {
  font-family: "Albert Sans";
  font-style: normal;
  font-weight: 300 800;
  font-display: swap;
  src: url("/assets/fonts/albert-sans-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* While Albert Sans loads (font-display: swap), text sets in a local face
   adjusted to its metrics, so the swap doesn't reflow the page. */
@font-face {
  font-family: "Albert Sans Fallback";
  src: local("Arial"), local("Helvetica Neue"), local("Helvetica");
  size-adjust: 103.7%;
  ascent-override: 95%;
  descent-override: 25%;
  line-gap-override: 0%;
}

@font-face {
  font-family: "Albert Sans";
  font-style: normal;
  font-weight: 300 800;
  font-display: swap;
  src: url("/assets/fonts/albert-sans-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --bg: #fff;
  /* The theme used #939393 (2.85:1 on white - fails WCAG AA). #666 keeps
     the grey character a click darker, at 5.7:1. */
  --text: #666;
  --text-strong: #515151;
  --text-hover: #000;
  --rule: #e6e6e6;
  --surface: #f0f0f0;
  --text-soft: #767676;       /* unselected tabs, "shortlisted" - 4.5:1 on white, the AA floor */
  --rule-strong: #d8d8d8;     /* hovered control borders, field borders */
  --surface-strong: #e8e8e8;  /* pressed controls */
  --but-hover: #dad8da;
  --ink: #111;                /* the map pins */
  --logo-filter: none;        /* the grey marks stay as drawn on white */
  --solid-bg: #515151;        /* the active half of a toggle */
  --solid-text: #fff;
  --solid-hover: #000;
  --stroke: #8c8c8c;          /* outlined buttons: a stroke that reads as a button, no fill */
  --ok: #2f8f5b;              /* the one green: a sent message. White tick on it in either theme */
  color-scheme: light;

  /* The page fills the window, up to a cap for very large screens (the
     original theme was 1170px). The one value to change if this wants tuning. */
  --container: 1920px;
  --radius: 4px; /* buttons, fields and controls: a gentle corner */
  --tip-radius: 4px; /* tooltips, hover labels and map popups: one corner for all of them */
  --gutter: 50px;
  --col-gap: 24px;            /* the one column gap: every layout grid and card grid uses it */
  --header-h: 70px; /* the nav row was 55 - room above and below the logo and nav */

  --font: "Albert Sans", "Albert Sans Fallback", -apple-system, BlinkMacSystemFont, "Segoe UI",
    Helvetica, Arial, sans-serif;
  --tracking: 0.03em;
  /* The one small-caps label: dropdown and footer headings, section
     labels, row titles, the hero's credit. Never another size. */
  --label-size: 0.75rem;
  --label-tracking: 0.12em;
  /* Hover motion: a long, quiet ease-out for photographs; colour in 0.25s */
  --ease-soft: cubic-bezier(0.25, 0.46, 0.45, 0.94);
  --zoom: 1.02;
}

/* Dark mode: the system setting, unless the footer toggle has chosen.
   site.js stores the choice and a tiny inline script applies it before
   paint. Photographs stay as they are; type, rules and surfaces invert. */
:root[data-theme="dark"] {
  --bg: #121212;
  --text: #adadad;
  --text-strong: #d6d6d6;
  --text-hover: #fff;
  --rule: #333;
  --surface: #222;
  --text-soft: #858585;
  --rule-strong: #4a4a4a;
  --surface-strong: #2c2c2c;
  --but-hover: #333;
  --ink: #f0f0f0;
  --logo-filter: invert(1) brightness(1.15);
  --solid-bg: #f0f0f0;        /* the button flips: light on dark */
  --solid-text: #121212;
  --solid-hover: #fff;
  --stroke: #7a7a7a;
  color-scheme: dark;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #121212;
    --text: #adadad;
    --text-strong: #d6d6d6;
    --text-hover: #fff;
    --rule: #333;
    --surface: #222;
    --text-soft: #858585;
    --rule-strong: #4a4a4a;
    --surface-strong: #2c2c2c;
    --but-hover: #333;
    --ink: #f0f0f0;
    --logo-filter: invert(1) brightness(1.15);
    --solid-bg: #f0f0f0;        /* the button flips: light on dark */
    --solid-text: #121212;
    --solid-hover: #fff;
    --stroke: #7a7a7a;
    color-scheme: dark;
  }
}


*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  /* The theme ran at 0.9rem (14.4px); 1rem is a touch larger and the
     whole scale is em-based, so headings and buttons follow. */
  font: 400 1rem / normal var(--font);
  letter-spacing: var(--tracking);
  -webkit-font-smoothing: antialiased;
  text-size-adjust: 100%;
}

body.noscroll {
  overflow: hidden;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

/* Text links: a soft underline that firms up on hover. Follows currentColor,
   so it's grey on white pages and white on the cover. */

a {
  color: var(--text-strong);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: rgb(81 81 81 / 0.35);
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
  transition: color 0.2s ease, text-decoration-color 0.2s ease;
}

a:hover,
a:focus-visible {
  color: var(--text-hover);
  text-decoration-color: currentColor;
}

/* Controls and image links are not "text links" */
.nav a,
.logo,
.btn,
.but a,
.card a,
.related a,
.rightbox a,
.visually-hidden {
  text-decoration: none;
}

/* Heading scale copied from the theme: everything is light-weight. */

/* Measured from the live site: headings inherit the body grey, weight 400,
   and carry only bottom padding. */

h1, h2, h3, h4, h5, h6 {
  font-weight: 400;
  color: inherit;
  margin: 0;
}

h1 {
  font-size: 1.8em;
  padding-bottom: 10px;
}

h2 {
  font-size: 1.286em;
  padding: 0 0 10px;
}

h3 {
  font-size: 1.286em;
  padding: 0 0 20px;
}

p {
  margin: 0 0 20px;
  line-height: 1.7;
}

:where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--text-strong);
  outline-offset: 3px;
}

/* Layout ------------------------------------------------------------------ */

.wrap {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: 40px; /* level with .wrap-mid, so header, content and footer share an edge */
}

.wrap-mid {
  max-width: var(--container);
  margin-inline: auto;
  padding: 40px;
}

/* The design grid, for checking layouts: twelve columns in the page width
   with the site's column gap between them. Off unless the page is opened
   with ?grid or Shift+G is pressed (site.js). Faint, and never in the way. */
.grid-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: none;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: 40px;
  pointer-events: none;
}

body.show-grid .grid-overlay {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--col-gap);
}

.grid-overlay span {
  height: 100%;
  background: rgb(255 0 90 / 0.05);
  border-inline: 1px solid rgb(255 0 90 / 0.2);
}

@media (max-width: 745px) {
  .grid-overlay {
    padding-inline: 20px;
  }

  body.show-grid .grid-overlay {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* Fluid container - the portfolio and feature listings use this, and it is
   deliberately NOT capped at --container. */
.wrap-wide {
  margin: 0 40px;
  padding: 40px 0;
}

/* Single-page text (privacy policy, copyright). The theme caps the measure
   at 600px for readability - long-form text is NOT widened with the page. */
.container {
  max-width: 600px;
  margin-inline: auto;
  padding: 50px 0;
}

.text {
  max-width: 600px;
  margin-inline: auto;
  padding: 10px;
}

/* Lists in the long-form text: the bullet on the text's left edge, the
   lines hanging clear of it, a little air between the items */
.container ul {
  margin: 0 0 1.2em;
  padding: 0;
  list-style: none;
}

.container ul li {
  position: relative;
  padding-left: 1.3em;
  line-height: 1.7; /* as the paragraphs */
}

.container ul li::before {
  content: "\2022";
  position: absolute;
  left: 0.15em;
  color: var(--text);
}

.container ul li + li {
  margin-top: 0.5em;
}

main {
  margin-top: var(--header-h);
}

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

.site-header {
  position: fixed;
  inset: 0 0 auto;
  z-index: 100;
  background: var(--bg);
  border-bottom: 1px solid var(--rule);
  transition: background 0.35s ease, border-color 0.35s ease, color 0.35s ease,
              transform 0.4s cubic-bezier(0.2, 0.7, 0.2, 1);
}

/* On the project pages: slid away on a long scroll down, back on the
   first scroll up (site.js) */
.site-header--away.is-hidden {
  transform: translateY(-100%);
}

@media (prefers-reduced-motion: reduce) {
  .site-header {
    transition: background 0.35s ease, border-color 0.35s ease, color 0.35s ease; /* no slide, just there or not */
  }
}

.site-header > .wrap {
  position: relative; /* the Services panel hangs from the header's own edge */
  display: flex;
  align-items: center;
  gap: 20px;
  min-height: var(--header-h);
}

/* Logo left; nav, menu button and contact gather on the right */
.site-header .logo {
  margin-right: auto;
}

.logo img {
  width: 160px;
  max-width: 180px;
}

.nav ul {
  display: flex;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav a {
  display: inline-block;
  padding: 6px 10px;
  border-radius: var(--radius);
  white-space: nowrap; /* the outbound arrow stays on the word's line */
  transition: background 0.25s var(--ease-soft), color 0.25s var(--ease-soft);
}

.nav a:hover {
  color: var(--text-hover); /* the word darkens; no fill */
}

.nav a[aria-current="page"] {
  background: var(--surface);
}

/* The outbound arrow after "Library": inline, off the baseline a touch */
.nav__out {
  display: inline-block;
  width: 0.68em; /* scales with the type: the phone menu is larger */
  height: 0.68em;
  margin-left: 0.22em; /* the whole gap: the markup carries no space before it */
  vertical-align: middle; /* centred on the x-height, like the lowercase beside it */
}

/* Its stroke follows the type's weight where that is set (the light phone menu) */
.nav__out path {
  stroke-width: var(--out-stroke, 0.9);
}

/* The bag after "Shop": drawn at its own size (not the type's) so its
   strokes land on whole pixels, close beside the word */
.nav__bag {
  display: inline-block;
  width: 16px;
  height: 16px;
  margin-left: 3px;
  vertical-align: -0.2em;
  shape-rendering: geometricPrecision;
}

.nav__bag path {
  stroke-width: var(--out-stroke, 1.25);
}

/* Services drops its areas down on hover or keyboard focus. The panel
   sits flush under the header (positioned from the header's wrap, not the
   item). The item itself runs the header's full height, so the pointer
   can travel straight down from the word into the panel without leaving
   it. */
@media (min-width: 901px) {
  .nav {
    align-self: stretch;
  }

  .nav ul {
    height: 100%;
  }

  .nav > ul > li { /* the top row only: the panel's rows stay block, full width */
    display: flex;
    align-items: center;
  }
}

.nav .nav__sub {
  display: flex; /* two columns, side by side */
  position: absolute;
  top: 100%; /* against the header's edge, redrawing its rule as the panel's own top */
  right: 40px; /* level with the header's content edge; never leaves the window */
  z-index: 5;
  margin: 0;
  padding: 0;
  background: var(--bg);
  border: 1px solid var(--rule);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 0.2s ease, transform 0.28s cubic-bezier(0.2, 0.7, 0.2, 1), visibility 0s linear 0.28s;
}

.has-sub:hover .nav__sub,
.has-sub:focus-within .nav__sub {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}

.nav__col {
  min-width: 240px;
  padding: 14px 0 16px;
}

.nav__col + .nav__col {
  border-left: 1px solid var(--rule);
}

.nav__col ul {
  display: block;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Rows settle in one after another */
.nav__sub li {
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 0.25s ease, transform 0.3s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.has-sub:hover .nav__sub li,
.has-sub:focus-within .nav__sub li {
  opacity: 1;
  transform: none;
}

.nav__sub li:nth-child(1) { transition-delay: 0.03s; }
.nav__sub li:nth-child(2) { transition-delay: 0.06s; }
.nav__sub li:nth-child(3) { transition-delay: 0.09s; }
.nav__sub li:nth-child(4) { transition-delay: 0.12s; }
.nav__sub li:nth-child(5) { transition-delay: 0.15s; }
.nav__sub li:nth-child(6) { transition-delay: 0.18s; }

.nav__sub li a {
  display: block;
  margin: 2px 12px; /* the row's fill sits inside the column, rounded, a breath between rows */
  padding: 9px 14px;
  border-radius: var(--radius);
  color: var(--text-strong);
  white-space: nowrap;
  transition: background 0.25s var(--ease-soft), color 0.25s var(--ease-soft);
}

.nav__sub li a:hover,
.nav__sub li a[aria-current="page"] {
  background: var(--surface);
  color: var(--text-hover);
}

/* On an area's own page, Services reads as the current section */
.nav a.is-section {
  background: var(--surface);
}

.nav .nav__sub-group {
  display: block;
  margin: 0 0 6px;
  padding: 8px 26px 10px;
  white-space: nowrap;
  font-size: var(--label-size);
  font-weight: 600;
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--text);
  text-decoration: none;
  transition: none;
}

/* Grey SVG marks: the header logo, client marks, the Instagram glyph and
   the flower go light in dark mode (the overlay header's own white rule
   still wins) */
.logo img,
.logo-cell img,
#ig,
.flower {
  filter: var(--logo-filter);
}

/* Colour-scheme switch in the footer bar: an icon (sun, moon, half disc)
   and the word for the current choice; a click moves to the next. */
.theme-toggle__button {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text);
  font: inherit;
  letter-spacing: var(--tracking);
  cursor: pointer;
  transition: color 0.2s ease;
}

.theme-toggle__button:hover,
.theme-toggle__button:focus-visible {
  color: var(--text-strong);
}

.theme-toggle__icon {
  display: block;
}

.theme-toggle__icon[hidden] {
  display: none;
}

/* Menu toggle - hidden on desktop, shown at the original 900px breakpoint */

.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 34px; /* the Instagram mark's box, so the two hover alike */
  height: 34px;
  padding: 0;
  border: 0;
  border-radius: var(--radius);
  background: none;
  color: var(--text-strong);
  cursor: pointer;
  transition: background 0.2s ease;
}

.nav-toggle:hover,
.nav-toggle:focus-visible {
  background: var(--surface);
}

.nav-toggle__bars {
  position: relative;
}

/* Three bars to a centred X in two beats: the outer bars first slide in
   onto the middle one, then the pair rotates into the X while the middle
   bar fades. Closing runs the same two beats backwards. The independent
   `translate` and `rotate` properties make the beats possible - each gets
   its own delay, which a single `transform` cannot do. The second beat
   starts just before the first lands, so it reads as one movement. */

.nav-toggle {
  --ease-bar: cubic-bezier(0.65, 0, 0.35, 1);
}

.nav-toggle .nav-toggle__bars,
.nav-toggle .nav-toggle__bars::before,
.nav-toggle .nav-toggle__bars::after {
  content: "";
  display: block;
  width: 16px;
  height: 1.5px;
  background: currentColor;
  transition: background 0.2s ease;
}

.nav-toggle .nav-toggle__bars::before,
.nav-toggle .nav-toggle__bars::after {
  rotate: 0deg;
  transition: rotate 0.16s var(--ease-bar) 0s,
              translate 0.16s var(--ease-bar) 0.11s,
              background 0.2s ease;
}

.nav-toggle .nav-toggle__bars::before { translate: 0 -5.5px; }
.nav-toggle .nav-toggle__bars::after  { translate: 0 4px; }

/* Middle bar: cleared by background, not opacity or scale - those would
   take the two pseudo-elements with it and leave no close button. It
   comes back as the bars spread again, hidden under them until then. */
.nav-toggle[aria-expanded="true"] .nav-toggle__bars {
  background: transparent;
  transition: background 0.1s ease 0.1s;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars::before,
.nav-toggle[aria-expanded="true"] .nav-toggle__bars::after {
  transition: translate 0.16s var(--ease-bar) 0s,
              rotate 0.16s var(--ease-bar) 0.11s,
              background 0.2s ease;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars::before {
  translate: 0 0;
  rotate: 45deg;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars::after {
  translate: 0 -2px;
  rotate: -45deg;
}

/* While the menu is open the header goes solid, matching the white panel
   below it. site.js sets .is-menu-open. */
.site-header.is-menu-open {
  background: var(--bg);
  border-bottom-color: var(--rule);
  color: var(--text-strong);
}

.site-header.is-menu-open .nav-toggle[aria-expanded="true"] .nav-toggle__bars {
  background: transparent;
}

.logo img {
  transition: filter 0.35s ease;
}

/* Hero slider -------------------------------------------------------------
   The original sized this with JS on every resize; calc() does it for free. */

.hero-slider {
  position: relative;
  height: calc(100vh - 105px);
  min-height: 320px;
  overflow: hidden;
}

.hero-slide {
  position: absolute;
  inset: 0;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
  opacity: 0;
  transition: opacity 0.8s ease;
}

.hero-slide[aria-hidden="false"] {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .hero-slide {
    transition: none;
  }
}

/* Pull-quote centred on a slide (e.g. "Britain's best garden photographer") */

.hero-quote {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 var(--gutter);
  color: #fff;
  pointer-events: none;
  transition: opacity 0.8s ease, transform 0.8s ease;
}

.hero-slide[aria-hidden="true"] .hero-quote {
  transform: translateY(10px);
}

.hero-quote blockquote {
  margin: 0;
  max-width: 100%;
}

/* Curly quotes as ornaments, in a serif, above and below the words */
.hero-quote__mark {
  display: block;
  height: 0.55em;
  font-family: Georgia, "Times New Roman", "Hoefler Text", serif;
  font-size: clamp(3.2rem, 8vw, 6rem);
  line-height: 1;
  color: rgb(255 255 255 / 0.75);
  user-select: none;
}

.hero-quote__mark--open {
  margin-bottom: 0.18em;
}

.hero-quote__mark--close {
  margin-top: 0.05em;
  margin-bottom: 0.25em;
}

/* Width is set here, in the quote's own type size - on the blockquote a
   ch unit would be measured against 16px text and wrap one word per line. */
.hero-quote p {
  max-width: 20ch;
  margin: 0 auto 0.6em;
  font-size: clamp(1.7rem, 4.2vw, 3.1rem);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: 0.01em;
  text-wrap: balance;
}

.hero-quote cite {
  font-style: normal;
  font-size: var(--label-size);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  font-weight: 600;
  opacity: 0.85;
}

/* Arrows on the left, caption right-aligned */

.hero-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  margin: 15px auto;
}

.hero-bar .flex-caption {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  text-align: right;
  line-height: 1.5;
  transition: opacity 0.3s ease;
}

/* A long caption is cut with an ellipsis rather than wrapping the tray */
.flex-caption__text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* site.js sets this while the slide changes, then swaps the text.
   A quiet fade only - no movement. */
.hero-bar .flex-caption.is-changing {
  opacity: 0;
}

/* "Buy the book" - outlined in white on the tray */
.flex-caption__link {
  display: inline-block;
  flex: none;
  margin-left: 18px;
  padding: calc(0.43em - 1px) calc(1.1em - 1px);
  border: 1px solid rgb(255 255 255 / 0.8);
  border-radius: var(--radius);
  background: none;
  color: #fff;
  font-weight: 600;
  line-height: 1.25;
  white-space: nowrap;
  text-decoration: none;
  transition: border-color 0.2s ease, color 0.2s ease;
}

.flex-caption__link:hover,
.flex-caption__link:focus-visible {
  border-color: #fff;
  color: #fff;
  text-decoration: none;
}

/* A linked slide is clickable in a circle over its centre - where the
   subject (the book) sits - not edge to edge, so brushing the image
   doesn't read as a link. Invisible; the cursor label announces it. */
.hero-slide__link {
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(55vmin, 480px);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  z-index: 2;
  cursor: pointer;
}

/* Label that follows the cursor over a linked slide.
   The outer element only moves (site.js sets a translate3d each frame); the
   inner label does the fade/scale, so the two never fight. No backdrop
   blur - re-blurring under a moving element is what made it stutter. */
.cursor-tip {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 50;
  pointer-events: none;
  will-change: transform;
}

.cursor-tip__label {
  display: block;
  padding: 0.6em 1.1em;
  border-radius: var(--tip-radius);
  background: rgb(0 0 0 / 0.6);
  color: #fff;
  font-weight: 600;
  white-space: nowrap;
  opacity: 0;
  transform: translate(18px, 18px) scale(0.9);
  transform-origin: 0 0;
  transition: opacity 0.28s ease, transform 0.35s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.cursor-tip.is-visible .cursor-tip__label {
  opacity: 1;
  transform: translate(18px, 18px) scale(1);
}

.flex-caption__link[hidden] {
  display: none;
}

.hero-nav {
  display: none; /* the arrows only do anything with site.js */
  gap: 8px;
  flex: none;
}

.js .hero-nav {
  display: flex;
}

.hero-nav button {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text-strong);
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.hero-nav button svg {
  display: block;
}

.hero-nav button:hover {
  background: var(--surface);
  color: var(--text-hover);
  border-color: var(--rule-strong);
}

.hero-nav button:active {
  background: var(--surface-strong);
}

/* Cover page (the homepage) ------------------------------------------------
   The image fills the viewport below the header; the caption/arrow
   tray is a glass strip over its bottom edge. The site footer sits below
   the fold as normal. */

.cover main {
  position: relative;
}

.cover .hero-slider {
  height: 100vh;
  height: 100dvh;
  min-height: 0;
}

/* The tray: full-width strip, contents aligned to the header's column */

.cover .hero-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  max-width: none;
  margin: 0;
  padding: 12px max(var(--gutter), calc((100% - var(--container)) / 2 + var(--gutter)));
  background: rgb(0 0 0 / 0.28);
  border-top: 1px solid rgb(255 255 255 / 0.18);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  backdrop-filter: blur(14px) saturate(1.2);
  color: #fff;
}

.cover .hero-nav button {
  background: rgb(255 255 255 / 0.1);
  border-color: rgb(255 255 255 / 0.4);
  color: #fff;
}

.cover .hero-nav button:hover {
  background: rgb(255 255 255 / 0.25);
  border-color: rgb(255 255 255 / 0.7);
  color: #fff;
}

/* Homepage hero: one photograph, the pitch over it -------------------------- */
/* The picture takes the whole fold, header aside: nothing sits under it
   waiting to be noticed. Outranks .cover .hero-slider's 100dvh; the min
   and max keep it sane on short and very tall screens. */
.cover .hero-slider.home-hero {
  height: calc(100vh - var(--header-h));
  height: calc(100dvh - var(--header-h));
  min-height: 560px;
  max-height: 1000px;
}

.home-hero .hero-slide {
  transition: opacity 0.6s ease;
}

.home-hero .hero-slide[aria-hidden="true"] {
  opacity: 0;
  pointer-events: none;
}

/* Scrim so the words hold on any picture */
/* Shade gathers at the foot of the picture, where the words sit; the rest
   of the photograph stands forward */
.home-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to top, rgb(0 0 0 / 0.55), rgb(0 0 0 / 0.18) 40%, rgb(0 0 0 / 0.08) 70%, transparent);
  pointer-events: none;
}

/* Two sets of words share the corner - the calendar's while it holds the
   hero, the quote once it hands over - so each is faded rather than
   swapped, and the hidden one is taken out of the tab order. */
.home-hero__text[data-hero-text] {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.6s ease, visibility 0s linear 0.6s;
}

/* No transition of its own: during the opening the intro's is the one
   that should apply, and it carries the lift as well as the fade. A
   transition declared here would outrank it and the words would rise in
   one jump while only the opacity eased. */
.home-hero__text[data-hero-text].is-showing {
  visibility: visible;
}

/* During the opening the words belong to the intro, which fades and lifts
   them with the header and the page. This rule is more specific than the
   intro's, so without the guard it would hold them at full opacity and
   they would simply appear. */
html:not([data-intro]) .home-hero__text[data-hero-text].is-showing {
  opacity: 1;
}

/* An outlined NEW badge above the title: the words are the loud part, so
   the badge is drawn rather than filled. Over a photograph both the rule
   and the text are white, not the theme's ink. */
.home-hero__badge {
  display: inline-block;
  margin-bottom: 14px;
  padding: 2px 6px 1px;
  border: 1px solid rgb(255 255 255 / 0.75);
  background: none;
  color: #fff;
  font-size: var(--label-size);
  font-weight: 600;
  letter-spacing: var(--label-tracking);
  line-height: 1.2;
  text-transform: uppercase;
}

/* The calendar's title is two lines of its own, not a quote */
.home-hero__text--takeover .home-hero__title {
  max-width: 30ch;
}

.home-hero__title-line {
  display: block;
}

/* White, so it reads as the one thing to press on the picture. Its own
   colours, not the theme's: it sits on a photograph in either mode. */
.home-hero__text .home-hero__cta {
  margin: 20px 0 0;
  background: #fff;
  color: #111;
}

.home-hero__text .home-hero__cta:hover,
.home-hero__text .home-hero__cta:focus-visible {
  background: rgb(255 255 255 / 0.85);
  color: #111;
}

/* The quote, small, bottom left in the content column */
.home-hero__text {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 34px;
  z-index: 2;
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 40px;
  color: #fff;
}

.home-hero__text .hero-quote__mark {
  display: block;
  height: 0.5em;
  font-size: 3.4rem;
  line-height: 1;
  color: rgb(255 255 255 / 0.7);
}

.home-hero__text .hero-quote__mark--open {
  margin-bottom: 0.2em;
}

.home-hero__title {
  max-width: 22ch;
  margin: 0;
  padding: 0;
  font-size: clamp(1.4rem, 2.4vw, 2rem);
  font-weight: 400;
  line-height: 1.2;
  color: #fff;
}

.home-hero__cite {
  margin: 18px 0 0;
  font-size: var(--label-size);
  font-weight: 600;
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: rgb(255 255 255 / 0.85);
}

/* Aligned to the content column, like the header and the sections below */
.home-hero__caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 34px; /* on the credit line's baseline, opposite it */
  line-height: 1.2;
  z-index: 2;
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 40px 0 45%; /* the right side only, clear of the quote block */
  font-size: 0.8125em;
  color: rgb(255 255 255 / 0.75);
  text-align: right;
  white-space: nowrap; /* one line: a right-aligned wrap would step */
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A small info mark after the caption, as on the project pictures */
.home-hero__info {
  display: inline-block;
  width: 15px;
  height: 15px;
  margin-left: 10px;
  vertical-align: -3px;
  opacity: 0.85;
}

.home-hero__info svg {
  display: block;
  width: 100%;
  height: 100%;
}

.home-hero__caption a {
  margin-left: 10px;
  color: #fff;
}

/* The opening (every visit to the home) ---------------------------------
   A curtain in the page's ground over everything, the flower mark pushing
   in at the centre with a band of light rising through it while the hero
   photograph loads; then the photograph fades in full-screen and, as its
   frame settles into the hero's box, the curtain goes and the header and
   the page's copy fade up with it. site.js runs the sequence; without
   scripting data-intro is never set. */
.intro {
  display: none;
}

html {
  --intro-settle: 1.1s;
  --intro-ease: cubic-bezier(0.7, 0, 0.2, 1);
}

html[data-intro] .intro,
.intro.is-settling {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: var(--bg);
  pointer-events: none;
}

.intro.is-settling {
  z-index: 90; /* under the header, which fades up over the settling frame */
  background: transparent; /* the page shows through as the frame settles */
}

/* No scrolling until the opening has finished: while the curtain is up
   and while the frame is still settling into the hero (site.js keeps
   .intro-done on until then) */
html[data-intro],
html[data-intro] body,
html.intro-done,
html.intro-done body {
  overflow: hidden;
}

/* Everything but the hero waits, then arrives together as the frame
   settles: the header slides down from above, its bottom edge on the
   frame's top edge the whole way (the frame lands where the header ends,
   so the same distance on the same curve keeps them touching); the words
   over the hero fade up and the page below rises to meet the hero. All on
   the frame's own curve and clock. */
html[data-intro] main > :not(.home-hero),
html[data-intro] .site-footer,
html[data-intro] .home-hero__text,
html[data-intro] .home-hero__caption {
  opacity: 0;
}

html[data-intro] .site-header {
  transform: translateY(-100%);
}

html[data-intro] main > :not(.home-hero),
html[data-intro] .site-footer {
  transform: translateY(72px);
}

html[data-intro] .home-hero__text,
html[data-intro] .home-hero__caption {
  transform: translateY(28px);
}

html.intro-done .site-header,
html.intro-done main > :not(.home-hero),
html.intro-done .site-footer,
html.intro-done .home-hero__text,
html.intro-done .home-hero__caption {
  transition: opacity var(--intro-settle) var(--intro-ease),
              transform var(--intro-settle) var(--intro-ease);
}

/* The words over the hero sit above the settling frame so they arrive
   with everything else, not once the frame has uncovered them */
html.intro-done .home-hero__text,
html.intro-done .home-hero__caption {
  z-index: 95;
}

/* The page's own fade-in is an opacity animation on main, which would
   hold the words in a stacking context beneath the frame; the opening
   does its own fading. Stays off once played, or it would run late. */
html[data-intro] main,
html.intro-played main {
  animation: none;
}

/* The mark: the flower in the site grey, its own shape intersected with a
   travelling band of opacity, so light seems to pass down through the petals.
   Pushes in from small on arrival, scales away as the frame settles. */
.intro__mark {
  position: absolute;
  z-index: 1; /* over the photograph while it fades in */
  left: 50%;
  top: 50%;
  width: clamp(64px, 6vw, 72px); /* never smaller than a phone needs */
  aspect-ratio: 458.265 / 915.223;
  transform: translate(-50%, -50%) scale(0.6);
  opacity: 0;
  background: color-mix(in srgb, var(--text) 55%, var(--bg));
  -webkit-mask: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjQsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iNDU4LjI2NXB4IiBoZWlnaHQ9IjkxNS4yMjNweCIgdmlld0JveD0iMCAwIDQ1OC4yNjUgOTE1LjIyMyIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNDU4LjI2NSA5MTUuMjIzIg0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiM5MzkzOTMiIGQ9Ik0yMjAuMDc4LDM0MS4xMDNjMCwwLDcxLjg1LDAuMDIxLDc3LjcwNyw3OC43OTZjNi4xMTksODIuMzM4LTUwLjM5MSwxNzQuMDc0LTUwLjM5MSwyNTMuMjE1DQoJYzAsNjUuMTY2LDI1LjkzOSwyMDUuMDYsMjUuOTM5LDIwNS4wNnMtNzAuMzYxLTEzNi43NjctNjUuODY3LTIxNi40OTljNi4yNDQtMTEwLjYzNSw2MS43MzItMTM0Ljc0LDU4Ljc4My0yMjQuNjkyDQoJQzI2My41MzUsMzU0LjE2OCwyMjAuMDc4LDM0MS4xMDMsMjIwLjA3OCwzNDEuMTAzeiIvPg0KPHBhdGggZmlsbD0iIzkzOTM5MyIgZD0iTTIzNi44MzcsODcwLjg1M2MwLDAtNTQuMzMyLTE0NC4zNTgtMTE4LjA0OS0xNTYuMzM2Yy02MC44MzYtMTEuNDM4LTEwNS4zODMsMTkuODQ3LTEwNS4zODMsMTkuODQ3DQoJYzAuMDE2LDAsNTIuNzM0LTYuODM2LDEwOC4wNTcsMTcuNzkzQzE3MC4zNzgsNzczLjg5MywyMzYuODM3LDg3MC44NTMsMjM2LjgzNyw4NzAuODUzeiIvPg0KPHBhdGggZmlsbD0iIzkzOTM5MyIgZD0iTTQ0NC44NTksNTc2Ljc4OGMwLDAtOTkuMDk4LTIxLjE4Ni0xMzIuNjk5LDUzLjAyMWMtNDAuNTksODkuNjE0LTExLjQyNiwyMjAuMjM0LTExLjQyNiwyMjAuMjM0DQoJczE3LjA5LTEzMS4xODUsNDYuOTg0LTE5OC4wODVDMzc3LjU4NSw1ODUuMTEyLDQ0NC44NTksNTc2Ljc4OCw0NDQuODU5LDU3Ni43ODh6Ii8+DQo8cGF0aCBmaWxsPSIjOTM5MzkzIiBkPSJNMTczLjgyMiw0MC4zNTdjMCwwLDI4LjQ5LDE1LjUzMywyNy42NzgsNDkuMTk3Yy0wLjYyMSwyNS43MzItNDIuODkzLDkzLjAwNS00Mi42MzEsMTM1LjU2DQoJYzAuMTY2LDI4Ljg2MSwyNS4wODQsNTcuMzk4LDgyLjEwNCw1Ny4zOThjMzAuNTk4LDAsODguMTQxLTkuNTM4LDc5Ljg4NS00Ni4zM2MtNS4wMzEtMjIuNDMyLTQzLjMxOC0xMDcuMzA2LTQ2LjkzLTE0OC42NzQNCgljLTMuOTcxLTQ1LjQzNSwxNy43NjYtNjAuNzE5LDE3Ljc2Ni02MC43MTlzLTQuMTQ4LDQyLjIzLTAuOTUxLDYyLjEwNGMzLjQ4NiwyMS43Nyw1Mi4zNzUsMTE4LjE3Myw1OC4zNTUsMTM4LjAyNw0KCWM4LjYyOSwyOC42MTMtMTEuNjYsNzguODk5LTk3LjM2MSw4NC43ODRjLTk0LjIwNSw2LjQ3MS0xMjEuMDEyLTQ0LjkxMS0xMTkuOTM2LTgyLjczOGMxLjA4OC0zOC43MDIsNTAuNDE2LTExMi4xMzYsNTMuMzExLTEzNC4yODUNCglDMTg4LjQxOSw2OS40MTgsMTczLjgyMiw0MC4zNTcsMTczLjgyMiw0MC4zNTd6Ii8+DQo8L3N2Zz4NCg==") center / contain no-repeat,
                linear-gradient(to top, rgb(0 0 0 / 0.3) 0 20%, #000 50%, rgb(0 0 0 / 0.3) 80% 100%) 0 0 / 100% 300% no-repeat;
  -webkit-mask-composite: source-in;
  mask: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjQsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iNDU4LjI2NXB4IiBoZWlnaHQ9IjkxNS4yMjNweCIgdmlld0JveD0iMCAwIDQ1OC4yNjUgOTE1LjIyMyIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNDU4LjI2NSA5MTUuMjIzIg0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiM5MzkzOTMiIGQ9Ik0yMjAuMDc4LDM0MS4xMDNjMCwwLDcxLjg1LDAuMDIxLDc3LjcwNyw3OC43OTZjNi4xMTksODIuMzM4LTUwLjM5MSwxNzQuMDc0LTUwLjM5MSwyNTMuMjE1DQoJYzAsNjUuMTY2LDI1LjkzOSwyMDUuMDYsMjUuOTM5LDIwNS4wNnMtNzAuMzYxLTEzNi43NjctNjUuODY3LTIxNi40OTljNi4yNDQtMTEwLjYzNSw2MS43MzItMTM0Ljc0LDU4Ljc4My0yMjQuNjkyDQoJQzI2My41MzUsMzU0LjE2OCwyMjAuMDc4LDM0MS4xMDMsMjIwLjA3OCwzNDEuMTAzeiIvPg0KPHBhdGggZmlsbD0iIzkzOTM5MyIgZD0iTTIzNi44MzcsODcwLjg1M2MwLDAtNTQuMzMyLTE0NC4zNTgtMTE4LjA0OS0xNTYuMzM2Yy02MC44MzYtMTEuNDM4LTEwNS4zODMsMTkuODQ3LTEwNS4zODMsMTkuODQ3DQoJYzAuMDE2LDAsNTIuNzM0LTYuODM2LDEwOC4wNTcsMTcuNzkzQzE3MC4zNzgsNzczLjg5MywyMzYuODM3LDg3MC44NTMsMjM2LjgzNyw4NzAuODUzeiIvPg0KPHBhdGggZmlsbD0iIzkzOTM5MyIgZD0iTTQ0NC44NTksNTc2Ljc4OGMwLDAtOTkuMDk4LTIxLjE4Ni0xMzIuNjk5LDUzLjAyMWMtNDAuNTksODkuNjE0LTExLjQyNiwyMjAuMjM0LTExLjQyNiwyMjAuMjM0DQoJczE3LjA5LTEzMS4xODUsNDYuOTg0LTE5OC4wODVDMzc3LjU4NSw1ODUuMTEyLDQ0NC44NTksNTc2Ljc4OCw0NDQuODU5LDU3Ni43ODh6Ii8+DQo8cGF0aCBmaWxsPSIjOTM5MzkzIiBkPSJNMTczLjgyMiw0MC4zNTdjMCwwLDI4LjQ5LDE1LjUzMywyNy42NzgsNDkuMTk3Yy0wLjYyMSwyNS43MzItNDIuODkzLDkzLjAwNS00Mi42MzEsMTM1LjU2DQoJYzAuMTY2LDI4Ljg2MSwyNS4wODQsNTcuMzk4LDgyLjEwNCw1Ny4zOThjMzAuNTk4LDAsODguMTQxLTkuNTM4LDc5Ljg4NS00Ni4zM2MtNS4wMzEtMjIuNDMyLTQzLjMxOC0xMDcuMzA2LTQ2LjkzLTE0OC42NzQNCgljLTMuOTcxLTQ1LjQzNSwxNy43NjYtNjAuNzE5LDE3Ljc2Ni02MC43MTlzLTQuMTQ4LDQyLjIzLTAuOTUxLDYyLjEwNGMzLjQ4NiwyMS43Nyw1Mi4zNzUsMTE4LjE3Myw1OC4zNTUsMTM4LjAyNw0KCWM4LjYyOSwyOC42MTMtMTEuNjYsNzguODk5LTk3LjM2MSw4NC43ODRjLTk0LjIwNSw2LjQ3MS0xMjEuMDEyLTQ0LjkxMS0xMTkuOTM2LTgyLjczOGMxLjA4OC0zOC43MDIsNTAuNDE2LTExMi4xMzYsNTMuMzExLTEzNC4yODUNCglDMTg4LjQxOSw2OS40MTgsMTczLjgyMiw0MC4zNTcsMTczLjgyMiw0MC4zNTd6Ii8+DQo8L3N2Zz4NCg==") center / contain no-repeat,
        linear-gradient(to top, rgb(0 0 0 / 0.3) 0 20%, #000 50%, rgb(0 0 0 / 0.3) 80% 100%) 0 0 / 100% 300% no-repeat;
  mask-composite: intersect;
  animation: intro-push 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) 0.1s forwards,
             intro-rise 0.8s linear infinite;
}

@keyframes intro-push {
  to { transform: translate(-50%, -50%) scale(1); opacity: 1; }
}

/* The band of light travels down the flower, a wide soft wave */
@keyframes intro-rise {
  from { -webkit-mask-position: center, 0 0;    mask-position: center, 0 0; }
  to   { -webkit-mask-position: center, 0 100%; mask-position: center, 0 100%; }
}

/* The flower goes as the photograph arrives; the two never overlap */
.intro.is-image .intro__mark {
  animation: intro-away 0.45s cubic-bezier(0.4, 0, 0.6, 1) forwards,
             intro-rise 0.8s linear infinite;
}

@keyframes intro-away {
  from { transform: translate(-50%, -50%) scale(1); opacity: 1; }
  to   { transform: translate(-50%, -50%) scale(1.25); opacity: 0; }
}

.intro__image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  background: center / cover no-repeat; /* the hero's own framing */
  opacity: 0;
  transform: scale(1.12); /* arrives large and eases down to full bleed */
  transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.2, 0.6, 0.2, 1);
}

.intro.is-image .intro__image {
  opacity: 1;
  transform: none;
}

/* The hero's shade gathers on the frame as it settles, so the frame
   matches the hero exactly when it lands and nothing pops in after */
.intro__image::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgb(0 0 0 / 0.55), rgb(0 0 0 / 0.18) 40%, rgb(0 0 0 / 0.08) 70%, transparent);
  opacity: 0;
  transition: opacity var(--intro-settle) var(--intro-ease);
}

.intro.is-settling .intro__image::after {
  opacity: 1;
}

/* Homepage sections */
.home > .home-section {
  padding: 60px 0;
  border-bottom: 0; /* each row's title carries its own rule */
  scroll-margin-top: calc(var(--header-h) + 50px); /* under the fixed header, with room above the title */
}

.home > .home-section:last-child {
  padding-bottom: 0;
}

.home > .services-intro {
  border-bottom: 0; /* the ruled title beneath is the divider */
}

.home-section__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 40px;
  padding-bottom: 32px;
}

.home-section__bar h2 {
  padding: 0;
}

.home-section__bar p {
  margin: 0;
  color: var(--text);
}

/* Home grids: three across, wrapping (five subjects make 3 + 2, four
   audiences 3 + 1) */
.audience-grid.audience-grid--home {
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.audience-grid--home .audience-card {
  aspect-ratio: 4 / 3;
}

.audience-grid--home .audience-card__text {
  padding: 20px;
}

.audience-grid--home .audience-card__text strong {
  font-size: 1.143em;
}

.audience-grid--home .audience-card__text span {
  font-size: 0.875em;
  line-height: 1.5;
}

/* The subject and audience cards mix portrait and landscape sources, so
   they share the projects' 3:2 frame, cropped to it */
#what-we-photograph .card .lqip,
#who-we-work-with .card .lqip,
.also .card .lqip {
  aspect-ratio: 3 / 2;
}

#what-we-photograph .card img,
#who-we-work-with .card img,
.also .card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

#what-we-photograph .card .lqip::before,
#who-we-work-with .card .lqip::before,
.also .card .lqip::before {
  inset: 0;
  background: var(--lqip) center / cover no-repeat;
  clip-path: none;
}

.home .services-clients .logo-row {
  margin-bottom: 30px;
}

.home .project-more {
  padding-top: 60px;
}

@media (max-width: 900px) {
  .audience-grid.audience-grid--home {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 745px) {
  .cover .hero-slider.home-hero {
    height: calc(100vh - var(--header-h));
    height: calc(100dvh - var(--header-h));
    min-height: 520px;
  }


  /* The caption is hidden on phones, so the quote sits in the corner with
     the same margin as its side */
  .home-hero__text {
    bottom: 20px;
    padding: 0 20px;
  }

  /* The words under the hero start the same distance in from the top as
     from the side */
  .home > .services-intro {
    padding-top: 0;
  }

  .audience-grid.audience-grid--home {
    grid-template-columns: 1fr;
  }
}

/* Grids -------------------------------------------------------------------
   .card-grid replaces the float-based .blog-entry / .module layout. */

.card-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--col-gap);
}

.card-grid--three {
  grid-template-columns: repeat(3, 1fr);
}

.card-grid--four {
  grid-template-columns: repeat(4, 1fr);
}

@media (max-width: 1100px) {
  .card-grid--four {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* The grid's gap is the gutter; the card itself carries no padding, so
   its edges sit on the column lines. */
.card {
  position: relative;
}

/* Hover: the photograph zooms softly inside its frame. The .lqip wrapper is
   the mask, so it must exist on every card - build.py always emits it. */
.card .lqip {
  display: block;
  overflow: hidden;
}

.card img {
  width: 100%;
  transition: transform 1.2s var(--ease-soft);
}

/* Zoom follows the image link only - on the shop cards the button row sits
   inside the card, and hovering a button should not move the picture. */
.card > a:hover img,
.card > a:focus-visible img,
.card > a:hover .lqip::before,
.card > a:focus-visible .lqip::before {
  transform: scale(var(--zoom));
}

.card__title {
  font-size: 1em; /* inherit body's 14.4px, not the h2 scale */
  font-weight: 400;
  margin: 5px 0 10px;
  padding: 0;
  color: var(--text-strong);
}

/* Home section cards: the title over the photograph's foot, on a soft
   darkening, in white - as the commission cards on the project pages */
.card-grid--inside .card > a {
  position: relative;
  display: block;
}

.card-grid--inside .card .lqip::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 45%, rgba(0, 0, 0, 0.55));
  pointer-events: none;
}

.card-grid--inside .card__title {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  margin: 0;
  padding: 24px;
  color: #fff;
  font-size: 1.286em;
}

.card-grid--inside .card > a:hover .card__title,
.card-grid--inside .card > a:focus-visible .card__title {
  color: #fff;
}

/* Services ---------------------------------------------------------------- */
/* The portfolio deck as a page: a full-bleed opener, the intro beside a
   portrait, a numbered index, five sections each with three photographs,
   the client list, and the deck's closing line over the sphere. */

.services-hero {
  position: relative;
  height: clamp(420px, 70vh, 760px); /* one expression, so the opening can animate to it */
  overflow: hidden;
  color: #fff;
}

/* The opening used to animate this box's height, from the window's down
   to its own. The photograph inside is cover-fitted, so every pixel of
   that was also a 24% rescale of the picture - a zoom, and on anything
   with a horizon or a wall in it a visibly jarring one. The frame now
   opens at its own height and only the words move. */

.js .services-hero__text {
  animation: head-in 0.9s var(--intro-ease) 0.15s both;
}

@media (prefers-reduced-motion: reduce) {
  .js .services-hero__text {
    animation: none;
  }
}

.services-hero .lqip,
.services-hero img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* The picture, taller than its frame, drifts down as the page scrolls up
   (site.js sets --parallax): a subtle depth, no edge ever showing */
.services-hero > .lqip,
.services-hero > img {
  top: -20%;
  bottom: auto;
  height: 140%;
  transform: translate3d(0, var(--parallax, 0px), 0);
  will-change: transform;
}

@media (prefers-reduced-motion: reduce), (max-width: 1099px) {
  .services-hero > .lqip,
  .services-hero > img {
    top: 0;
    height: 100%;
    transform: none;
  }
}

.services-hero img {
  object-fit: cover;
}

.services-hero .lqip::before {
  inset: 0;
  background: var(--lqip) center / cover no-repeat;
  clip-path: none;
}

.services-hero::after {
  content: "";
  position: absolute;
  inset: 30% 0 0;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.5));
  pointer-events: none;
}

.services-hero__text {
  position: absolute;
  inset: auto 0 0;
  z-index: 1;
  max-width: var(--container);
  margin-inline: auto;
  padding: 40px;
}

.services-hero__text h1 {
  max-width: 22em;
  margin: 0;
  padding: 0;
  font-size: clamp(1.4em, 2.6vw, 2.2em);
  line-height: 1.25;
  text-wrap: balance; /* two even lines, never a word on its own */
  color: #fff;
}

.services-hero__strap {
  max-width: 30em;
  margin: 10px 0 0;
  font-size: 1.143em;
  color: rgb(255 255 255 / 0.85);
}

/* The deck's small-caps section labels */
.services-index__label,
.service__label,
.services-clients dt,
.services-stats dt {
  margin: 0 0 12px;
  font-size: var(--label-size);
  font-weight: 600;
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--text);
}

.services > section,
.services > nav {
  padding: 50px 0;
  border-bottom: 1px solid var(--rule);
}

.services > :last-child {
  padding-bottom: 0; /* the wrapper's own 40px is the gap to the band below - the same as the sides */
  border-bottom: 0;
}

.services > section:has(+ .service-related) {
  border-bottom: 0; /* the next row's ruled title is the divider */
}

/* Intro: portrait beside the words */
.services-intro {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--col-gap);
  align-items: center;
}

.services-intro > :first-child {
  grid-column: span 4;
}

.services-intro > :last-child {
  grid-column: span 8;
}

/* The home's words alone, centred: ten of the twelve columns, so the lead
   holds to two lines */
.services-intro--centred > .services-intro__text {
  grid-column: 2 / span 10;
  padding: 36px 0;
  text-align: center;
}

.services-intro--centred .services-intro__text p {
  max-width: 28em;
  margin-inline: auto;
}

.services-intro--centred .services-stats {
  justify-content: center;
  margin: 0 0 24px; /* the numbers lead; the words follow */
}

.services-intro--centred .services-stats > div {
  align-items: center;
}

.services-intro--centred .services-intro__text p:last-child {
  margin-bottom: 0;
}

.services-intro__portrait img {
  width: 100%;
  height: auto;
}

.services-intro__lead {
  font-size: 1.286em;
  line-height: 1.5;
  text-wrap: balance; /* even lines, never a word on its own */
  color: var(--text-strong);
}

/* The home's lead wraps as written */
.services-intro--centred .services-intro__lead {
  text-wrap: initial;
  margin-bottom: 34px; /* air before the button */
}

.services-stats {
  display: flex;
  gap: 50px;
  margin: 44px 0 22px;
}

.services-stats > div {
  display: flex;
  flex-direction: column-reverse;
}

.services-stats dd {
  margin: 0 0 6px;
  font-size: 2.2em;
  line-height: 1;
  color: var(--text-strong);
}


/* Numbered index of the sections */
.services-index ol {
  list-style: none;
  margin: 0;
  padding: 0;
  columns: 2;
  column-gap: 60px;
}

.services-index li {
  break-inside: avoid;
  border-bottom: 1px solid var(--rule);
}

.services-index a {
  display: flex;
  align-items: baseline;
  gap: 24px;
  padding: 14px 0;
  font-size: 1.5em;
  color: var(--text-strong);
  text-decoration: none;
}

.services-index a:hover,
.services-index a:focus-visible {
  color: var(--text-hover);
}

.services-index__n {
  font-size: 0.5em;
  letter-spacing: 0.1em;
  color: var(--text);
}

/* One service: words left, three pictures right */
.service {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  gap: 30px var(--col-gap);
  scroll-margin-top: 80px;
}

.service h2 {
  font-size: 1.8em;
  padding-bottom: 12px;
}

.service__text p:last-child {
  max-width: 34em;
}

.service__gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.service__gallery a {
  display: block;
  aspect-ratio: 4 / 5;
  overflow: hidden;
}

.service__gallery .lqip,
.service__gallery img {
  display: block;
  width: 100%;
  height: 100%;
}

.service__gallery img {
  object-fit: cover;
  transition: transform 1.2s var(--ease-soft);
}

.service__gallery .lqip::before {
  inset: 0;
  background: var(--lqip) center / cover no-repeat;
  clip-path: none;
}

.service__gallery a {
  transition: box-shadow 0.25s var(--ease-soft);
}

.service__gallery a:hover,
.service__gallery a:focus-visible {
  box-shadow: 0 6px 22px rgb(0 0 0 / 0.22);
}

/* Clients */
.services-clients__title {
  padding-bottom: 32px;
}

.services-clients dl {
  margin: 0;
}

.services-clients > dl > div {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 8px var(--col-gap);
  padding: 16px 0;
  border-top: 1px solid var(--rule);
}

.services-clients dt {
  grid-column: span 4;
  margin: 0;
  padding-top: 3px;
}

.services-clients > dl > div > dd {
  grid-column: span 8;
}

.services-clients dd {
  margin: 0;
  font-size: 1.143em;
  line-height: 1.6;
  color: var(--text-strong);
}

/* Closing line on the page's own ground - the office banner's frame, no
   photograph, no fill */
.services-cta.office { /* outranks .office's fixed height, declared later */
  height: auto;
  min-height: 0;
  max-height: none;
  padding: 100px 0;
  background: none;
  color: var(--text-strong);
}

.services-cta.office::after {
  content: none; /* no photograph to shade (outranks .office::after, declared later) */
}

/* The band's three lines rise in one after another as it scrolls into
   view (site.js adds is-in); without scripting they simply show */
.js .services-cta:not(.is-in) .services-cta__text > div > * {
  opacity: 0;
  transform: translateY(14px);
}

.services-cta__text > div > * {
  transition: opacity 0.7s var(--ease-soft), transform 0.9s var(--ease-soft);
}

.services-cta__text > div > :nth-child(2) { transition-delay: 0.12s; }

@media (prefers-reduced-motion: reduce) {
  .js .services-cta:not(.is-in) .services-cta__text > div > * {
    opacity: 1;
    transform: none;
  }
}

/* Outranks .office__text (which is declared later): fills the banner and
   centres its block both ways */
.services-cta .office__text {
  position: static; /* in flow: the band is as tall as its words plus the padding */
  justify-content: center;
  align-items: center;
  text-align: center;
}

.services-cta__text > div {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.services-cta__text h2 {
  font-size: clamp(1.8em, 3.4vw, 3em);
  line-height: 1.1;
  padding: 0 0 44px;
  color: var(--text-strong);
  text-wrap: balance; /* even lines when it wraps, never a word on its own */
}

.services-cta__text .but {
  margin: 0;
}

/* On set, in black and white: a picture with a quiet caption, the width
   of what it sits in */
.bts {
  margin: 0;
}

.bts img {
  width: 100%;
  height: auto;
}

.bts figcaption {
  margin-top: 12px;
  font-size: var(--label-size);
  font-weight: 600;
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--text);
}

.audience-section__body .bts {
  max-width: 40em; /* the text's own measure, not the whole body */
  margin: 32px 0 36px;
}

/* Sub-page: one area of expertise */
.service h2 a {
  color: inherit;
  text-decoration: none;
}

.service h2 a:hover {
  color: var(--text-hover);
}

.service-intro {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 20px var(--col-gap);
  align-items: center;
}

/* The lead, and the button beneath it on the left */
.service-intro > :first-child {
  grid-column: span 8;
}

.service-intro > :last-child:not(:first-child) {
  grid-column: 1 / span 8;
}

.service-intro .services-intro__lead {
  margin: 0;
  max-width: 42em; /* short of the eight columns, so even the shortest lead runs to two lines */
}

.service-intro p:last-child {
  margin: 20px 0 0;
  justify-self: start;
}

/* Every deck photograph, in flowing columns at their own proportions */
.service-gallery {
  columns: 3; /* three columns of four: the column gap between and below */
  column-gap: var(--col-gap);
}

/* Fewer than three pictures: one column, the full width */
.service-gallery--one {
  columns: 1;
}

/* site.js has dealt the pictures into three columns that end level */
.service-gallery.is-columns {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--col-gap);
  columns: auto;
}

.service-gallery__col > a:last-child {
  margin-bottom: 0;
}

.service-gallery a {
  display: block;
  margin-bottom: var(--col-gap);
  break-inside: avoid;
  overflow: hidden;
}

.service-gallery .lqip,
.service-gallery img {
  display: block;
  width: 100%;
  height: auto;
}

/* No scale on hover here. At 1.2s the zoom is slow enough to read as an
   arrival rather than a response to the cursor, so scrolling a gallery
   with the pointer anywhere over it sets photographs swelling and
   subsiding one after another - which looks like some of them animating
   in and others not, depending only on where the mouse happens to be.
   The frame lifts instead: the picture itself never moves. */
.service-gallery a {
  transition: box-shadow 0.25s var(--ease-soft);
}

.service-gallery a:hover,
.service-gallery a:focus-visible {
  box-shadow: 0 6px 22px rgb(0 0 0 / 0.22);
}

.service-related > h2 {
  padding-bottom: 32px;
}


.services-index--other ol {
  columns: 1;
}

@media (max-width: 900px) {
  .services-intro,
  .home .services-intro, /* outranks the desktop two-column home rule */
  .service,
  .services-clients > dl > div {
    grid-template-columns: 1fr;
  }

  /* One column: the twelve-column spans above no longer apply (the same
     selectors, so these outrank them) */
  .services-intro > :first-child,
  .services-intro > :last-child,
  .services-intro--centred > .services-intro__text,
  .service-intro > :first-child,
  .service-intro > :last-child:not(:first-child),
  .services-clients dt,
  .services-clients > dl > div > dd {
    grid-column: auto;
  }

  .services-intro__portrait {
    max-width: 320px;
  }

  /* Phones: the words first, then a small portrait beneath them */
  .home .services-intro__portrait {
    order: 1;
    max-width: 200px;
    aspect-ratio: 4 / 5;
  }

  .home .services-intro__text {
    padding: 0;
  }

  /* The stats wrap rather than run off the side */
  .services-stats {
    flex-wrap: wrap;
    gap: 24px 36px;
  }

  .services-index ol {
    columns: 1;
  }

  .service-intro {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 745px) {
  .services-hero {
    height: clamp(420px, 60vh, 760px);
  }

  .services-hero__text,
  .services-cta__text {
    padding: 24px 20px;
  }

  .service__gallery {
    grid-template-columns: repeat(2, 1fr);
  }

  .service-gallery {
    columns: 1; /* one photograph at a time, the full width */
  }

  /* Head and body stacked: air between the ticks and the words */
  .audience-section__head .ticks {
    margin-bottom: 28px;
  }

  .service__gallery a:last-child {
    display: none; /* two-up on phones; the third would sit alone */
  }
}

/* Who we work with ------------------------------------------------------- */

.audience-section {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 20px var(--col-gap);
}

.audience-section > :first-child {
  grid-column: span 4;
}

.audience-section > .audience-section__body {
  grid-column: span 8;
}

.audience-section h2 {
  font-size: 1.286em;
  padding: 0;
}

/* The about page's opening sections, spaced and ruled as on the client pages */
/* The band: a letterbox the window's width, bled out of the page margins,
   the picture taller than the frame and drifting a touch with the scroll
   (site.js); the caption at the right on the picture's foot */
.about-band {
  position: relative;
  height: 52vh;
  min-height: 300px;
  max-height: 620px;
  margin: 50px calc(50% - 50vw) 0;
  overflow: hidden;
  color: #fff;
}

.about-band .lqip,
.about-band img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.about-band img {
  object-fit: cover;
}

.about-band .lqip::before {
  inset: 0;
  background: var(--lqip) center / cover no-repeat;
  clip-path: none;
}

.about-band > .lqip,
.about-band > img {
  top: -15%;
  bottom: auto;
  height: 130%;
  transform: translate3d(0, var(--parallax, 0px), 0);
  will-change: transform;
}

@media (prefers-reduced-motion: reduce), (max-width: 1099px) {
  .about-band > .lqip,
  .about-band > img {
    top: 0;
    height: 100%;
    transform: none;
  }
}

@media (max-width: 745px) {
  .about-band {
    margin-top: 30px;
  }
}

.about-sections {
  margin-top: 10px; /* the first row's own padding does the rest */
}

.about-sections > section {
  padding: 50px 0;
  border-bottom: 1px solid var(--rule);
}


.about-sections > section:last-child {
  border-bottom: 0;
}

/* The client marks' heading, level with the sections above; the grid
   sits close beneath it */
.logos-title {
  margin: 60px 0 0;
  padding: 0;
}

.logos-title + .client-logos {
  margin-top: 32px;
}

/* The studio's address and number, each on its own line with air between */
.about-contact {
  margin: 24px 0 0;
  padding: 0;
  list-style: none;
}

.about-contact li + li {
  margin-top: 10px;
}

.about-contact li.about-contact__where {
  display: grid;
  grid-template-columns: 1fr 1fr; /* the address, the company beside it */
  gap: 10px var(--col-gap);
  margin-top: 36px; /* a step below the lines that copy */
  max-width: 40em;
  line-height: 1.7;
}

.about-contact__address {
  color: var(--text); /* as the company's lines beside it */
}

/* The map link under the address: the same quiet link as the lines above */
.about-contact__map {
  display: inline-block;
  margin-top: 8px;
  white-space: nowrap;
  color: var(--text-strong); /* as any link in the copy; a step up under the pointer */
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
  transition: text-decoration-color 0.25s var(--ease-soft);
}

.about-contact__map:hover,
.about-contact__map:focus-visible {
  color: var(--text-hover);
  text-decoration-color: currentColor;
}

.about-contact__company {
  color: var(--text);
}

/* The clock row: its two cells join the address/company grid */
.about-contact__time:not([hidden]) {
  display: contents;
}

.about-contact__time-label,
.about-contact__time time {
  margin-top: 40px;
  align-self: center; /* the words level with the middle of the cells */
  white-space: nowrap;
  color: var(--text);
}

.about-contact__time-label {
  font-size: var(--label-size);
  font-weight: 600;
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
}

.about-contact__time time {
  display: inline-flex;
  align-items: center;
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}

/* The clock's figures: each in a fixed cell, crossfading when it changes */
/* A ticker: every figure and colon in its own cell, the cells sharing
   their hairlines as the client marks do; only the figure fades when it
   changes, the cell stands */
/* A ticker: each pair of figures in joined cells, the colons free between
   the pairs; only the figure fades when it changes, the cell stands */
.clock {
  display: inline-flex;
  align-items: center;
  margin-right: 0.9em; /* a clear step before the zone */
}

.clock__digit,
.clock__sep {
  display: inline-block;
  line-height: 1;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.clock__digit {
  width: 1.5em;
  padding: 0.3em 0;
  border: 1px solid var(--rule);
  border-left: 0;
  color: var(--text-strong);
}

/* Each pair is one rounded box: the first figure carries the left edge and
   corners, the second the right */
.clock__digit:first-child,
.clock__sep + .clock__digit {
  border-left: 1px solid var(--rule);
  border-radius: 4px 0 0 4px;
}

.clock__digit:last-child,
.clock__digit:has(+ .clock__sep) {
  border-radius: 0 4px 4px 0;
}

.clock__sep {
  width: 0.7em;
  color: var(--text);
}

.clock__glyph {
  display: inline-block;
}

/* Row titles - Selected clients, Selected projects, What we photograph,
   Related projects and the rest - are the site's small-caps label, centred
   on a hairline that runs out to either side */
.home-section__bar h2,
.services-clients__title,
.service-related > h2,
.logos-title,
.project-more__bar h2,
.project-services > h2,
.also__title {
  display: flex;
  flex: 1; /* in a bar with a button, the rule runs up to the button */
  align-items: center;
  gap: 24px;
  font-size: var(--label-size);
  font-weight: 600;
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  text-align: center;
  color: var(--text);
}

.home-section__bar h2::before,
.services-clients__title::before,
.service-related > h2::before,
.logos-title::before,
.project-more__bar h2::before,
.project-services > h2::before,
.also__title::before,
.home-section__bar h2::after,
.services-clients__title::after,
.service-related > h2::after,
.logos-title::after,
.project-more__bar h2::after,
.project-services > h2::after,
.also__title::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--rule);
}

/* Ticked features under a section heading */
.ticks {
  margin: 18px 0 0;
  padding: 0;
  list-style: none;
  max-width: 22em;
}

.ticks li {
  position: relative;
  padding: 5px 0 5px 28px;
  color: var(--text-strong);
}

.ticks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  width: 16px;
  height: 16px;
  background: currentColor;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 8.5l3.2 3L13 4.5' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px 16px no-repeat;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 8.5l3.2 3L13 4.5' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px 16px no-repeat;
  color: var(--text);
}

.audience-section__body p {
  max-width: 40em;
}

.audience-section__body p:last-child {
  margin-bottom: 0;
}

/* A section's own link, a step below its words */
.audience-section__link {
  margin-top: 20px;
}

.audience-section__link .but {
  margin: 0;
}

/* Numbered steps as a timeline: a vertical line down the left, a numbered
   node on it for each step */
.steps {
  position: relative;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
}

/* The line runs the length of the list and fades out at either end,
   rather than stopping dead at the first and last rings */
.steps::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 12px;
  width: 1px;
  background: linear-gradient(to bottom, transparent, var(--rule-strong) 15%, var(--rule-strong) 85%, transparent);
}

.steps li {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 4px 0;
  padding: 0 0 26px 52px;
  counter-increment: step;
}

.steps li:last-child {
  padding-bottom: 0;
}

.steps li::before {
  content: counter(step, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: 1px;
  box-sizing: border-box;
  width: 25px;
  height: 25px;
  border: 1px solid var(--rule-strong);
  border-radius: 50%;
  background: var(--bg);
  font-size: 0.625em;
  line-height: 23px;
  letter-spacing: 0.04em;
  text-align: center;
  color: var(--text);
}

/* The step's name reads a size up from its words, in the darkest grey */
.steps strong {
  font-size: 1.125em;
  font-weight: 400;
  line-height: 1.3;
  color: var(--text-hover);
}

.steps li > span {
  max-width: 40em; /* the body measure */
  color: var(--text);
}

/* Questions: native disclosure, styled; the lines hold to a reading width */
.faq {
  margin: 0;
  max-width: 44em;
}

.faq details {
  border-top: 1px solid var(--rule);
}

.faq details:last-child {
  border-bottom: 1px solid var(--rule);
}

.faq summary {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  padding: 14px 0;
  list-style: none;
  color: var(--text-strong);
  cursor: pointer;
  user-select: none; /* a double-click opens and closes; it should not select */
  -webkit-tap-highlight-color: transparent;
}

/* site.js animates the answer's height; the answer itself just fades.
   The space under the answer lives on the body, not the paragraph, so
   the section's last-paragraph rule can't take it away. */
.faq .faq__body {
  overflow: hidden; /* no padding of its own: at height 0 it is truly closed */
}

.faq .faq__body p {
  margin: 0;
  padding: 2px 0 18px;
}

.faq details p {
  transition: opacity 0.3s ease;
}

.faq details:not([open]) p,
.faq details.is-closing p {
  opacity: 0;
}

.faq summary::-webkit-details-marker {
  display: none;
}

.faq summary::after {
  content: "+";
  flex: none;
  font-size: 1.2em;
  line-height: 1;
  color: var(--text);
  transition: transform 0.35s cubic-bezier(0.65, 0, 0.35, 1);
}

.faq details[open] summary::after {
  transform: rotate(45deg);
}

.faq summary:hover {
  color: var(--text-hover);
}

.faq details p {
  max-width: 40em;
}

.audience-links .but {
  margin: 0 8px 10px 0;
}

/* Index page: four pictures with a line each */
.services-intro--plain {
  display: block;
}

.audience-index__title {
  max-width: 24em;
  font-size: clamp(1.4em, 2.6vw, 2.2em);
  line-height: 1.25;
  padding: 0;
  color: var(--text-strong);
}

.audience-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--col-gap);
}

.audience-card {
  position: relative;
  display: block;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  color: #fff;
  text-decoration: none;
}

.audience-card .lqip,
.audience-card img {
  display: block;
  width: 100%;
  height: 100%;
}

.audience-card img {
  object-fit: cover;
  transition: transform 1.2s var(--ease-soft);
}

.audience-card .lqip::before {
  inset: 0;
  background: var(--lqip) center / cover no-repeat;
  clip-path: none;
}

.audience-card:hover img,
.audience-card:focus-visible img {
  transform: scale(var(--zoom));
}

.audience-card::after {
  content: "";
  position: absolute;
  inset: 40% 0 0;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.55));
  pointer-events: none;
}

.audience-card__text {
  position: absolute;
  inset: auto 0 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 24px;
}

/* The site's link hover darkens text; over a photograph it stays white */
.audience-card,
.audience-card:hover,
.audience-card:focus-visible,
.audience-card__text strong {
  color: #fff;
  text-decoration: none;
}

.audience-card__text strong {
  font-size: 1.286em;
  font-weight: 400;
}

.audience-card__text span {
  color: rgb(255 255 255 / 0.85);
}

/* Under a portfolio project: the next three, and the way to commission */
.project-footer {
  padding-top: 0;
  padding-bottom: 20px;
}

.project-more,
.project-services {
  clear: both;
  padding: 50px 0 0;
}

.project-more__bar {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding-bottom: 32px;
}

/* View all sits over the rule's right end, on a patch of the page's ground,
   so the title stays centred on the page rather than on what is left of it */
.project-more__bar .but,
.project-more__bar .but.booking,
.project-more__bar .but.booking:hover,
.project-more__bar .but.booking:focus-visible {
  position: absolute;
  inset: 0 0 32px auto;
  margin: auto 0;
  height: fit-content;
  background: var(--bg); /* outranks the outlined button's own "no fill" */
  box-shadow: 0 0 0 16px var(--bg);
}

.project-more__bar h2 {
  padding: 0; /* the bar carries the 32px beneath */
}

.project-services > h2 {
  padding: 0 0 32px; /* no bar here: the title carries it */
}


@media (max-width: 745px) {
  .project-more__bar {
    flex-direction: column;
    align-items: stretch; /* the title keeps the full width, so its rule runs out */
    gap: 4px;
  }

  .project-more__bar h2 {
    flex: none;
  }

  .project-more__bar .but {
    position: static;
    margin: 8px 0 0;
    align-self: flex-start;
    box-shadow: none;
  }
}


.project-services {
  padding-bottom: 20px;
}

/* Client marks on a service or audience page: the about page's ruled
   cells, one static row, no cycling. They follow the intro without a rule
   or heading between. */
.services > .service-intro {
  padding-top: 0; /* the wrapper's own 40px under the hero, the same as the sides */
}

.services > .service-intro:has(+ .service-logos) {
  padding-bottom: 50px;
  border-bottom: 0;
}

.services > .service-logos {
  padding-top: 0;
}

.logo-row {
  display: grid;
  grid-template-columns: repeat(6, 1fr); /* six across; more than six cycle through (site.js) */
  border-top: 1px solid var(--rule);
  border-left: 1px solid var(--rule);
}

.logo-row .logo-cell {
  display: grid;
  place-items: center;
  min-height: 100px;
  padding: 20px;
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}

.logo-row .logo-cell img {
  transition: filter 0.25s ease;
}


@media (max-width: 900px) {
  .logo-row,
  .logo-row.logo-cycle {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (max-width: 745px) {
  .logo-row,
  .logo-row.logo-cycle {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* "Also" - the other areas / audiences, as project cards */
.also__title {
  padding-bottom: 32px;
}

/* The strip on the services index */
.audience-strip__list {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0 20px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.audience-strip__list a {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 16px 0;
  border-top: 1px solid var(--rule);
  color: var(--text);
  text-decoration: none;
}

.audience-strip__list strong {
  font-weight: 400;
  font-size: 1.143em;
  color: var(--text-strong);
}

.audience-strip__list a:hover strong {
  color: var(--text-hover);
}

@media (max-width: 900px) {
  .audience-section {
    grid-template-columns: 1fr;
    gap: 10px;
  }

  .audience-section > :first-child,
  .audience-section > .audience-section__body {
    grid-column: auto;
  }

  .audience-grid--four,
  .audience-grid--three {
    grid-template-columns: repeat(2, 1fr);
  }

  .audience-strip__list {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 745px) {
  .audience-grid,
  .audience-strip__list {
    grid-template-columns: 1fr;
  }

  .audience-grid--four,
  .audience-grid--three {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Garden map ------------------------------------------------------------- */
/* An ordinary page: a panel of words on the left, the map boxed beside
   it, inside the page width. The panel introduces the set; a chosen pin
   swaps it for that garden (site.js). List view drops the map for the
   table below the panel. */

.garden-map {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--col-gap);
  align-items: start;
}

.garden-map > .garden-panel {
  grid-column: span 3;
}

.garden-map > .garden-map__canvas {
  grid-column: span 9;
}

/* Outranks MapLibre's own .maplibregl-map { position: relative }, which
   arrives after this sheet. */
.garden-map .garden-map__canvas {
  position: relative;
  height: clamp(480px, 72vh, 760px);
  border: 1px solid var(--rule);
  background: var(--surface);
  overflow: hidden;
}

.garden-panel {
  position: sticky;
  top: calc(var(--header-h) + 24px);
}

.garden-panel__intro,
.garden-panel__detail {
  padding: 0;
}

.garden-panel__label {
  margin: 0 0 8px;
  color: var(--text);
  font-size: var(--label-size);
  font-weight: 600;
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
}

.garden-panel h1 {
  margin: 0 0 12px;
  color: var(--text-strong);
  font-size: 1.286em;
  font-weight: 400;
  line-height: 1.3;
}

.garden-panel p {
  margin: 0 0 12px;
}

.garden-panel__note {
  color: var(--text-strong);
}

/* Map / List: two words, each outlined; the active one solid, its stroke
   the same colour as its fill so no grey rim shows round it */
.garden-view {
  display: inline-flex;
  margin-top: 6px;
}

.garden-view button {
  appearance: none;
  margin: 0;
  padding: calc(0.4em - 1px) calc(1em - 1px);
  border: 1px solid var(--stroke);
  background: none;
  color: var(--text);
  font: inherit;
  font-weight: 400;
  line-height: 1.25;
  letter-spacing: var(--tracking);
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
}

/* The pair reads as one control: corners on the outer ends only */
.garden-view button:first-child {
  border-radius: var(--radius) 0 0 var(--radius);
}

.garden-view button:last-child {
  border-radius: 0 var(--radius) var(--radius) 0;
}

.garden-view button + button {
  margin-left: -1px; /* the two strokes share an edge */
}

.garden-view button[aria-pressed="true"] {
  position: relative; /* its stroke sits over the neighbour's shared edge */
  border-color: var(--solid-bg);
  background: var(--solid-bg);
  color: var(--solid-text);
}

.garden-view button:not([aria-pressed="true"]):hover {
  color: var(--text-strong);
}

/* A chosen garden: photograph, name, place, the links; "All gardens" back */

.garden-panel__back {
  appearance: none;
  display: inline-block;
  margin: 0 0 14px;
  padding: 0 0 0 16px;
  border: 0;
  background: none;
  color: var(--text);
  font: inherit;
  letter-spacing: var(--tracking);
  cursor: pointer;
  position: relative;
}

.garden-panel__back::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 50%;
  width: 7px;
  height: 7px;
  border-left: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transform: translateY(-50%) rotate(45deg);
}

.garden-panel__back:hover {
  color: var(--text-strong);
}

.garden-panel__name {
  margin: 18px 0 4px;
  color: var(--text-strong);
  font-size: 1.15em;
  font-weight: 400;
}

.garden-panel .garden-panel__place {
  margin: 0 0 16px;
  color: var(--text);
}

/* One outlined button, then a plain underlined link level with its middle */
.garden-panel .garden-panel__links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  margin: 0;
}

.garden-panel .garden-panel__links .but {
  margin: 0;
}

/* The list: the same gardens as a table, grouped by region. Hidden while
   the map is showing; the page itself in list view, without scripting, or
   if the map fails. */
.garden-list {
  display: none;
}


.garden-map.is-list .garden-map__canvas,
.garden-map.is-failed .garden-map__canvas,
:root:not(.js) .garden-map .garden-map__canvas {
  display: none;
}


/* The list takes the map's place: the same nine columns, the panel
   staying put on the left */
.garden-map.is-list .garden-list,
.garden-map.is-failed .garden-list,
:root:not(.js) .garden-map .garden-list {
  display: block;
  grid-column: span 9;
}

:root:not(.js) .garden-view,
:root:not(.js) .garden-table__map,
.garden-map.is-failed .garden-view,
.garden-map.is-failed .garden-table__map {
  display: none;
}

/* The table: set like the awards table on the about page */
.garden-table {
  width: 100%;
  min-width: 680px; /* scrolls sideways inside .garden-list when narrower */
  border-collapse: collapse;
  border-top: 1px solid var(--rule);
  font-size: 0.875em;
  line-height: 1.5;
}

.garden-table th,
.garden-table td {
  padding: 10px 14px 10px 0;
  border-bottom: 1px solid var(--rule);
  text-align: left;
  vertical-align: top;
}

.garden-table thead th {
  position: sticky;
  top: var(--header-h); /* under the site header */
  z-index: 1;
  padding-top: 12px;
  padding-bottom: 12px;
  background: var(--bg);
  /* Collapsed borders don't travel with a sticky cell: the rule is drawn
     inside instead, and it holds at the bottom edge while stuck */
  border-bottom: 0;
  box-shadow: inset 0 -1px var(--rule);
  font-weight: 600;
  font-size: var(--label-size);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--text);
}

.garden-table__region th {
  padding-top: 26px;
  padding-bottom: 8px;
  font-weight: 600;
  color: var(--text-strong);
}

.garden-table__name {
  position: relative; /* the hover photograph hangs off it */
  color: var(--text-strong);
  white-space: nowrap;
}

.garden-table__name a {
  color: inherit; /* the site's underline, in the name's colour */
}

.garden-table__none {
  color: var(--text-soft);
}

/* Type: a chip in a light stroke, the site's corner on it */
.chip {
  display: inline-block;
  padding: 1px 8px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  font-size: 0.857em; /* 12px */
  line-height: 1.5;
  white-space: nowrap;
}

/* Nothing wraps: the table pushes out rather than stacking words; the one
   long column, the designer, is cut with an ellipsis and its full text is
   in the cell's title. (Narrow screens: see the 900px query.) */
.garden-table th,
.garden-table td {
  white-space: nowrap;
}

.garden-table__designer {
  position: relative;
  max-width: 130px;
}

.garden-table__designer-text {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A tooltip: a small dark label above whatever carries it */
.tip {
  position: absolute;
  left: 0;
  bottom: calc(100% - 4px);
  z-index: 3;
  padding: 5px 9px;
  border-radius: var(--tip-radius);
  background: var(--solid-bg);
  color: var(--solid-text);
  font-size: 0.8125rem;
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: var(--tracking);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translateY(3px);
  transition: opacity 0.15s ease, transform 0.15s ease;
}

/* Only a cut name gets the tooltip (site.js marks the cell) */
.garden-table__designer.is-clipped:hover .tip {
  opacity: 1;
  transform: none;
}

/* The place is the way to the map: set as a link, underlined like one */
.garden-table__map {
  appearance: none;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text);
  font: inherit;
  letter-spacing: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
  cursor: pointer;
  transition: color 0.2s ease, text-decoration-color 0.2s ease;
}

.garden-table__map:hover,
.garden-table__map:focus-visible {
  color: var(--text-strong);
  text-decoration-color: currentColor;
}

/* The cover photograph follows the pointer while the row is hovered - a
   small print, off to the lower right of it, never over what is being read
   (site.js sets --x and --y; without it there is no hover print) */
.js .garden-table__thumb {
  position: fixed;
  left: var(--x, -999px);
  top: var(--y, -999px);
  z-index: 2;
  width: 200px;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background: var(--surface);
  box-shadow: 0 6px 24px rgb(0 0 0 / 0.2);
  opacity: 0;
  transform: translate(18px, 18px) scale(0.98);
  transition: opacity 0.15s ease, transform 0.15s ease;
  pointer-events: none;
}

.garden-table__thumb {
  display: none;
}

.js .garden-table__thumb {
  display: block;
}

.garden-table__thumb .lqip,
.garden-table__thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.js .garden-table__row:hover .garden-table__thumb {
  opacity: 1;
  transform: translate(18px, 18px) scale(1);
}

@media (max-width: 900px) {
  /* One column: the words (and a chosen garden) first, the map beneath -
     the heading says what the page is before it is handed a map. */
  .garden-map {
    grid-template-columns: minmax(0, 1fr);
    gap: 28px;
  }

  .garden-map > .garden-panel,
  .garden-map > .garden-map__canvas,
  .garden-map.is-list .garden-list,
  .garden-map.is-failed .garden-list,
  :root:not(.js) .garden-map .garden-list {
    grid-column: auto;
  }

  /* No sideways scrolling: a scroll box would pin the column heads to
     itself rather than the page. The table fits instead - the designer
     column goes, the words wrap - so the heads stay put under the header. */
  .garden-list {
    padding-bottom: 20px;
  }

  .garden-table {
    min-width: 0;
  }

  .garden-table th,
  .garden-table td {
    white-space: normal;
  }

  .garden-table th:nth-child(3),
  .garden-table__row td:nth-child(3) {
    display: none;
  }

  .garden-map .garden-map__canvas {
    height: clamp(360px, 60vh, 560px);
  }

  .garden-panel {
    position: static;
  }
}

/* Phones: garden, place and whether you can visit - the type goes too */
@media (max-width: 600px) {
  .garden-table th:nth-child(4),
  .garden-table__row td:nth-child(4) {
    display: none;
  }
}

/* Grey map: the Positron style, drained of its remaining colour. Map and
   pins fade up together once the tiles have arrived. */
.garden-map .maplibregl-canvas {
  filter: grayscale(1);
  opacity: 0;
  transition: opacity 0.6s ease;
}

.garden-map .maplibregl-marker {
  opacity: 0;
  transition: opacity 0.4s ease 0.3s;
}

.garden-map.is-ready .maplibregl-canvas,
.garden-map.is-ready .maplibregl-marker {
  opacity: 1;
}

/* Zoom buttons bottom right, in the same dress as the slider's arrows */
.garden-map .maplibregl-ctrl-bottom-right {
  right: 16px;
  bottom: 16px; /* the same distance from the edge each way */
}

.garden-map .maplibregl-ctrl-bottom-right .maplibregl-ctrl-group {
  display: flex;
  gap: 8px;
  margin: 0;
  background: none;
  border-radius: var(--radius);
  box-shadow: none;
}

.garden-map .maplibregl-ctrl-group button {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text-strong);
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.garden-map .maplibregl-ctrl-group button + button {
  border-top: 1px solid var(--rule);
}

/* MapLibre rounds the first and last button of a group; ours are square */
.garden-map .maplibregl-ctrl-group button:first-child,
.garden-map .maplibregl-ctrl-group button:last-child,
.garden-map .maplibregl-ctrl-group button:only-child,
.garden-map .maplibregl-ctrl-group button:focus,
.garden-map .maplibregl-ctrl-group button:focus:first-child,
.garden-map .maplibregl-ctrl-group button:focus:last-child,
.garden-map .maplibregl-ctrl-group button:focus:only-child {
  border-radius: 0;
}

.garden-map .maplibregl-ctrl-group button:focus-visible {
  box-shadow: none;
  outline: 2px solid var(--text-strong);
  outline-offset: 2px;
}

/* :not(:disabled) matches MapLibre's own hover rule's specificity, which
   otherwise wins and paints a translucent grey over the button. */
.garden-map .maplibregl-ctrl-group button:not(:disabled):hover {
  background: var(--surface);
  border-color: var(--rule-strong);
}

.garden-map .maplibregl-ctrl-group button:not(:disabled):active {
  background: var(--surface-strong);
}

/* Plus/minus: a light, hairline mark, a touch larger than the arrows.
   Selectors carry `.maplibregl-ctrl button.` to outrank MapLibre's own
   icon rules, which are written that way. */
.garden-map .maplibregl-ctrl-group button .maplibregl-ctrl-icon {
  width: 28px;
  height: 28px;
  /* The mark is a mask filled with the current colour, so it follows the
     text colour - legible in dark mode too - and darkens on hover */
  background: var(--text);
  -webkit-mask: var(--mark) center / 28px 28px no-repeat;
  mask: var(--mark) center / 28px 28px no-repeat;
  transition: background 0.2s ease;
}

.garden-map .maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon,
.garden-map .maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon {
  background-image: none; /* MapLibre's own black icon would show through the mask */
}

.garden-map .maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon {
  --mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 5v14M5 12h14' fill='none' stroke='%23000' stroke-width='1.1' stroke-linecap='round'/%3E%3C/svg%3E");
}

.garden-map .maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon {
  --mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M5 12h14' fill='none' stroke='%23000' stroke-width='1.1' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* Darken the mark on hover, as the arrows' chevrons do */
.garden-map .maplibregl-ctrl-group button:not(:disabled):hover .maplibregl-ctrl-icon {
  background: var(--text-strong);
}

.garden-map .maplibregl-ctrl-bottom-left {
  left: 16px;
  bottom: 16px;
}

/* MapLibre's own .maplibregl-ctrl.maplibregl-ctrl-attrib sets a white pill
   with black type, ties with a one-class-plus-one selector and arrives
   after this sheet - so it won, and in dark mode the credit was pale grey
   on white. The extra class settles it on specificity rather than order. */
.garden-map .maplibregl-ctrl.maplibregl-ctrl-attrib {
  opacity: 0.7;
  font-size: 11px;
  background: var(--bg); /* the page's own ground, dark or light */
  color: var(--text);
}

.garden-map .maplibregl-ctrl-attrib a {
  color: var(--text-strong);
}

/* The (i) that opens the credit is a dark glyph baked into MapLibre's own
   stylesheet; on a dark pill it would be a hole. The same invert the grey
   client marks take. */
.garden-map .maplibregl-ctrl-attrib-button {
  filter: var(--logo-filter);
}

/* Pins: a small black dot with a white edge, nothing else; it grows a
   little on hover/active. It's a <button>, so every UA default that would
   squash it is reset. The marker wrapper also carries the hover label. */
.garden-marker {
  position: relative;
  width: 12px;
  height: 12px;
}

/* A hovered marker (and its label) rises above its neighbours; an open
   popup sits above everything. */
.garden-map .maplibregl-marker:hover {
  z-index: 2;
}

.garden-map .maplibregl-popup {
  z-index: 3;
}

.garden-pin {
  appearance: none;
  box-sizing: border-box;
  width: 12px;
  height: 12px;
  padding: 0;
  margin: 0;
  border: 2px solid var(--bg);
  border-radius: 50%;
  background: var(--text-hover); /* dark on the light map, light on the dark one */
  cursor: pointer;
  transition: transform 0.25s ease;
}

/* Dark mode: the same grey map, turned over - land dark, water darker,
   the names light. One filter on the canvas; no second style to load. */
:root[data-theme="dark"] .garden-map .maplibregl-canvas {
  filter: grayscale(1) invert(1) brightness(0.82) contrast(1.05);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .garden-map .maplibregl-canvas {
    filter: grayscale(1) invert(1) brightness(0.82) contrast(1.05);
  }
}

.garden-pin:hover,
.garden-marker.is-active .garden-pin {
  transform: scale(1.35);
}

/* The name, above the pin, on hover - until the popup takes over */
.garden-marker__label {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  padding: 4px 9px;
  border-radius: var(--tip-radius);
  background: var(--bg);
  color: var(--text-strong);
  font-size: 0.8125rem;
  line-height: 1.3;
  letter-spacing: var(--tracking);
  white-space: nowrap;
  box-shadow: 0 2px 10px rgb(0 0 0 / 0.15);
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, 4px);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.garden-marker:hover .garden-marker__label {
  opacity: 1;
  transform: translate(-50%, 0);
}

.garden-marker.is-active .garden-marker__label {
  opacity: 0;
}

/* Popups rise and fade in */
.garden-map .maplibregl-popup {
  animation: popup-in 0.3s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}

@keyframes popup-in {
  from {
    opacity: 0;
    translate: 0 8px;
  }
}

/* Popups: photograph on top, name and place under it, link to the pictures */
.garden-map .maplibregl-popup {
  max-width: 260px;
}

.garden-map .maplibregl-popup-content {
  width: 260px;
  padding: 0;
  overflow: hidden;
  border-radius: var(--tip-radius);
  background: var(--bg);
  color: var(--text);
  box-shadow: 0 4px 24px rgb(0 0 0 / 0.18);
  font: 400 0.9375rem / 1.5 var(--font); /* MapLibre's own 12px is too small here */
  letter-spacing: var(--tracking);
}

.garden-popup__image {
  display: block;
  position: relative;
  aspect-ratio: 3 / 2;
  background: var(--lqip) center / cover no-repeat, var(--surface);
  overflow: hidden;
}

.garden-popup__image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.5s ease;
}

.garden-popup__image img.is-loaded {
  opacity: 1;
}

.garden-popup__text {
  display: block; /* a span inside the popup - block so the padding is real */
  padding: 14px 16px 16px;
}

.garden-popup__name {
  display: block;
  color: var(--text-strong);
  font-size: 1.05em;
  font-weight: 400; /* the site's headings are light; <strong> is for the outline */
}

.garden-popup__place {
  display: block;
  color: var(--text);
}

/* .but--small (later in the file) zeroes margins; this must outrank it */
.garden-popup__text .garden-popup__link {
  margin-top: 8px;
  text-decoration: none;
}

.garden-map .maplibregl-popup-close-button {
  top: 6px;
  right: 6px;
  width: 26px;
  height: 26px;
  padding: 0;
  border-radius: 50%;
  background: var(--surface);
  color: var(--text-strong);
  font-size: 18px;
  line-height: 26px;
  transition: background 0.2s ease;
}

.garden-map .maplibregl-popup-close-button:hover {
  background: var(--surface-strong);
}

.garden-map .maplibregl-popup-anchor-bottom .maplibregl-popup-tip {
  border-top-color: var(--bg);
}

.garden-map .maplibregl-popup-anchor-top .maplibregl-popup-tip {
  border-bottom-color: var(--bg);
}

.garden-map .maplibregl-popup-anchor-left .maplibregl-popup-tip {
  border-right-color: var(--bg);
}

.garden-map .maplibregl-popup-anchor-right .maplibregl-popup-tip {
  border-left-color: var(--bg);
}

/* Contact button in the header, and the form dialog it opens ------------- */

/* Outlined: a stroke, no fill; the stroke darkens on hover */
.site-header .nav-contact {
  flex: none;
  margin: 0;
  padding: 0.35em 0.9em;
  border: 1px solid var(--stroke);
  border-radius: var(--radius);
  background: none;
  color: var(--text-strong);
  font-weight: 400;
  line-height: 1.25;
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.site-header .nav-contact:hover,
.site-header .nav-contact:focus-visible {
  background: var(--surface);
  border-color: var(--text-strong);
  color: var(--text-strong);
}

.contact {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: 20px;
  background: rgb(0 0 0 / 0.55);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  backdrop-filter: blur(14px) saturate(1.2);
  opacity: 0;
  transition: opacity 0.25s ease;
}

.contact[hidden] {
  display: none;
}

.contact.is-open {
  opacity: 1;
}

.contact__panel:focus {
  outline: none; /* the panel takes focus on open; the ring belongs on the controls inside */
}

.contact__panel {
  position: relative;
  width: min(520px, 100%);
  max-height: 100%;
  overflow: auto;
  padding: 32px; /* the same on every side */
  border-radius: 8px; /* a softer corner than the controls', it being a sheet */
  background: var(--bg);
  box-shadow: 0 20px 60px rgb(0 0 0 / 0.35);
  transform: translateY(12px);
  transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.contact.is-open .contact__panel {
  transform: none;
}

.contact__panel h2 {
  font-size: 1.8em;
  line-height: 1.2;
  padding: 0 40px 24px 0; /* clear of the close button; air before the first field */
  color: var(--text-strong);
}

.contact__field {
  display: block;
  margin-bottom: 22px;
}

.contact__field > span {
  display: block;
  margin-bottom: 6px;
  color: var(--text-strong);
  line-height: 1.4;
}

/* A star after the label of a field that must be filled */
.contact__req {
  margin-left: 0.2em;
  color: var(--text);
}

.contact__field input,
.contact__field select,
.contact__field textarea {
  width: 100%;
  padding: 0.55em 0.75em;
  line-height: 1.4;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  background: var(--surface); /* a light fill, so the fields read as fields */
  color: var(--text-strong);
  font: inherit;
  letter-spacing: var(--tracking);
  transition: border-color 0.2s ease;
}

.contact__field textarea {
  resize: vertical;
  min-height: 96px;
}

.contact__field input:focus,
.contact__field select:focus,
.contact__field textarea:focus {
  outline: none;
  border-color: var(--text-strong);
  background: var(--bg); /* the field you are in goes to the page's ground */
}

/* The choice of who is writing: the browser's select, in the site's frame */
.contact__field select {
  appearance: none;
  padding-right: 2.2em;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5l3.5 3.5 3.5-3.5' fill='none' stroke='%23737373' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.75em center;
  background-size: 12px;
  cursor: pointer;
}

/* The site's own list for the choice of who is writing (site.js): a
   button like a field, the options beneath as the nav's dropdown has them */
.picker {
  position: relative;
}

.picker__button {
  display: block;
  width: 100%;
  padding: 0.55em 2.2em 0.55em 0.75em;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text-strong);
  font: inherit;
  letter-spacing: var(--tracking);
  line-height: 1.4;
  text-align: left;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5l3.5 3.5 3.5-3.5' fill='none' stroke='%23737373' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.75em center;
  background-size: 12px;
  transition: border-color 0.2s ease, background-color 0.2s ease;
}

.picker__button.is-empty {
  color: var(--text);
}

.picker__button:hover,
.picker__button[aria-expanded="true"],
.picker__button:focus-visible {
  border-color: var(--text-strong);
  background-color: var(--bg);
  outline: none;
}

/* The marks beside the choices: the type's own colour, a little lighter
   than the words so they read as a cue rather than a second voice */
.picker__icon {
  flex: none;
  width: 1.15em;
  height: 1.15em;
  color: var(--text);
}

.picker__button,
.picker__list li {
  display: flex;
  align-items: center;
  gap: 0.6em;
}

.picker__button.is-empty .picker__icon,
.picker__option--empty .picker__icon {
  display: none;
}

.picker__list {
  position: absolute;
  z-index: 3;
  inset: calc(100% - 1px) 0 auto;
  margin: 0;
  padding: 6px 0;
  list-style: none;
  background: var(--bg);
  border: 1px solid var(--text-strong);
  border-radius: 0 0 var(--radius) var(--radius); /* as the button it hangs from */
}

/* While the list is open the button's bottom corners square off to meet it */
.picker__button[aria-expanded="true"] {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

.picker__list[hidden] {
  display: none;
}

.picker__list li {
  padding: 9px 0.75em;
  color: var(--text-strong);
  cursor: pointer;
  transition: background 0.25s var(--ease-soft), color 0.25s var(--ease-soft);
}

.picker__list li:hover,
.picker__list li:focus,
.picker__list li[aria-selected="true"] {
  background: var(--surface);
  color: var(--text-hover);
  outline: none;
}

.picker__option--empty {
  color: var(--text);
}

/* Name and email side by side; the kind's questions one under another */
.contact__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 16px;
}

.contact__extra[hidden] {
  display: none;
}

.contact__actions {
  display: flex;
  align-items: center;
  justify-content: space-between; /* Send left, the address box right */
  flex-wrap: wrap;
  gap: 12px 18px;
  margin-top: 8px;
}



.contact__actions .contact__send {
  margin: 0; /* outranks .but's bottom margin, which would sink it in the row */
  background: var(--text-hover); /* solid: black on the light sheet, white on the dark */
  color: var(--bg);
}

.contact__actions .contact__send:hover,
.contact__actions .contact__send:focus-visible {
  background: var(--solid-bg);
  color: var(--solid-text);
}

.contact__alt {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 0.9em;
}

/* Copy the address: a small outlined-pages glyph after it; a tick once
   copied. The address and the glyph share one tooltip. */
.contact__copy-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 36px; /* the same box as Send */
  padding: 0 6px 0 12px;
  border-radius: var(--radius);
  background: var(--surface);
  font-size: 1rem;
  transition: background 0.2s ease;
}

/* Plain (the footer, the about page): an underlined line like its neighbours, no box,
   the copy glyph only appearing on hover (or focus), the tooltip once copied */
.contact__copy-wrap--plain {
  height: auto;
  margin: 0;
  padding: 0;
  background: none;
  font-size: inherit;
}

.contact__copy-wrap.contact__copy-wrap--plain:hover,
.contact__copy-wrap.contact__copy-wrap--plain:focus-within {
  background: none; /* outranks the dialog box's hover fill */
}

/* A number with nothing to link to is a button that reads as the line */
.contact__address--plain {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  letter-spacing: inherit;
  cursor: pointer;
}

.contact__copy-wrap--plain .contact__address {
  color: var(--text-strong); /* as any link in the copy */
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
}

.contact__copy-wrap--plain .contact__address:hover,
.contact__copy-wrap--plain .contact__address:focus-visible,
.contact__copy-wrap--plain:hover .contact__address {
  color: var(--text-hover); /* the link hover, the underline filling to match */
  text-decoration-line: underline; /* the dialog box's hover would remove it; the shorthand would reset the 1px thickness */
  text-decoration-thickness: 1px;
  text-decoration-color: currentColor;
}

.contact__copy-wrap--plain .contact__copy {
  opacity: 0;
  transform: translateX(-6px); /* slides in from the word as it fades up */
  transition: opacity 0.25s ease, transform 0.3s cubic-bezier(0.2, 0.7, 0.2, 1), color 0.2s ease;
}

.contact__copy-wrap--plain:hover .contact__copy,
.contact__copy-wrap--plain:focus-within .contact__copy,
.contact__copy-wrap--plain.is-copied .contact__copy {
  opacity: 1;
  transform: none;
}

.contact__copy-wrap:hover,
.contact__copy-wrap:focus-within {
  background: var(--but-hover);
}

.contact__address {
  color: var(--text-strong);
  text-decoration: none;
}

.contact__address:hover,
.contact__address:focus-visible {
  color: var(--text-strong);
  text-decoration: none;
}

.contact__tip {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  padding: 5px 9px;
  border-radius: var(--tip-radius);
  background: var(--solid-bg);
  color: var(--solid-text);
  font-size: 0.8125rem;
  line-height: 1.3;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, 3px);
  transition: opacity 0.15s ease, transform 0.15s ease;
}

/* The tooltip confirms a copy; it does not announce one (the button's
   label does that for assistive tech) */
.contact__copy-wrap.is-copied .contact__tip {
  opacity: 1;
  transform: translate(-50%, 0);
}

.contact__copy {
  appearance: none;
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text);
  cursor: pointer;
  transition: color 0.2s ease;
}

.contact__copy-wrap:hover .contact__copy,
.contact__copy:focus-visible {
  color: var(--text-strong);
}

.contact__copy svg {
  display: block;
}

.contact__copy-wrap.is-copied .contact__copy path {
  d: path("M3.5 8.5l3 3 6-7"); /* the same stroke, redrawn as a tick */
}

/* With scripting the address copies: an ordinary pointer, nothing odd */
.js .contact__address {
  cursor: pointer;
}

:root:not(.js) .contact__copy {
  display: none;
}

/* Once sent, the form gives way to the thanks (nothing left to press twice) */
.contact__form.is-sent > :not(.contact__done) {
  display: none;
}

/* Only the heading is the panel's business - the thanks centres itself
   below, so a stale class can never reach the form's own labels. */
/* Out of sight, not out of the document: the dialog is labelled by it. */
.contact__panel.is-sent h2 {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.contact__done {
  margin: 0 auto;
  padding: 28px 0 8px;
  max-width: 42ch;      /* a measure, rather than the panel's full width */
  line-height: 1.5;
  text-align: center;
  text-wrap: pretty;
  color: var(--text);
}

/* The word first, what happened under it: the thanks is the heading the
   panel no longer has. */
.contact__done-title {
  display: block;
  margin-bottom: 4px;
  font-size: 1.6em;
  line-height: 1.2;
  color: var(--text-strong);
}

.contact__done-note {
  display: block;
  font-size: 1.0625em;
}

.contact__done::before {
  content: "";
  display: block;
  width: 34px;
  height: 34px;
  margin: 0 auto 18px;
  border-radius: 50%;
  background: var(--ok) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.6 8.4l3 2.9 5.8-6.1' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 18px 18px no-repeat;
}

.contact__error {
  margin: 20px 0 0;
}

.contact__error {
  color: var(--text-strong);
}

/* The trap field: off the page for people, present for bots */
.contact__trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Close: the same X as the lightbox */
.contact__close {
  position: absolute;
  top: 14px;
  right: 14px;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: var(--radius);
  background: none;
  color: var(--text-strong);
  cursor: pointer;
  transition: background 0.2s ease;
}

.contact__close:hover {
  background: var(--surface);
}

/* The X: two strokes laid over the same centred line, turned either way */
.contact__close span {
  position: relative;
  display: block;
  width: 18px;
  height: 2px;
}

.contact__close span::before,
.contact__close span::after {
  content: "";
  position: absolute;
  inset: 0;
  background: currentColor;
}

.contact__close span::before { rotate: 45deg; }
.contact__close span::after  { rotate: -45deg; }

/* Lightbox ---------------------------------------------------------------
   Full-screen photograph over a dark glass; the arrows are the slider's,
   in their cover-page dress. site.js builds it on first use. */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  background: rgb(0 0 0 / 0.9);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  backdrop-filter: blur(14px) saturate(1.2);
  color: #fff;
  opacity: 0;
  transition: opacity 0.25s ease;
}

/* display: grid above would beat the hidden attribute's UA display: none,
   leaving an invisible overlay that swallows every click on the page. */
.lightbox[hidden] {
  display: none;
}

.lightbox.is-open {
  opacity: 1;
}

/* Pinned to the overlay's edges (a percentage height in the overlay's
   auto-sized grid row would resolve to nothing) with one definite cell,
   so the picture's max-height holds and a portrait fits the window */
.lightbox__figure {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template: minmax(0, 1fr) / minmax(0, 1fr);
  place-items: center;
  margin: 0;
  padding: 80px 100px;
}

.lightbox__img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  box-shadow: 0 20px 60px rgb(0 0 0 / 0.5);
  transform: scale(0.96);
  opacity: 0;
  transition: opacity 0.25s ease, transform 0.35s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.lightbox.is-open .lightbox__img {
  opacity: 1;
  transform: none;
}

/* Slide change: out to one side, in from the other */
.lightbox.is-open .lightbox__img.is-out-left  { opacity: 0; transform: translateX(-40px); transition-duration: 0.18s; }
.lightbox.is-open .lightbox__img.is-out-right { opacity: 0; transform: translateX(40px); transition-duration: 0.18s; }
.lightbox.is-open .lightbox__img.is-in-right  { opacity: 0; transform: translateX(40px); transition: none; }
.lightbox.is-open .lightbox__img.is-in-left   { opacity: 0; transform: translateX(-40px); transition: none; }

.lightbox__nav {
  position: absolute;
  z-index: 2; /* above the pinned figure, which would otherwise take the pointer */
  left: 0;
  right: 0;
  top: 50%;
  justify-content: space-between;
  padding: 0 var(--gutter);
  transform: translateY(-50%);
  pointer-events: none;
}

/* No stroke: a quiet translucent square that brightens under the pointer */
.lightbox__nav button,
.lightbox__close {
  pointer-events: auto;
  background: rgb(255 255 255 / 0.12);
  border: 0;
  color: #fff;
  transition: background 0.25s var(--ease-soft);
}

.lightbox__nav button:hover,
.lightbox__nav button:focus-visible,
.lightbox__close:hover,
.lightbox__close:focus-visible {
  background: rgb(255 255 255 / 0.3);
  border: 0;
  color: #fff;
}

.lightbox__nav button:active,
.lightbox__close:active {
  background: rgb(255 255 255 / 0.4);
}

.lightbox--single .lightbox__nav,
.lightbox--single .lightbox__count {
  display: none;
}

/* Close: the same X the menu burger ends on */
.lightbox__close {
  position: absolute;
  z-index: 2;
  top: 20px;
  right: var(--gutter);
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: var(--radius);
  cursor: pointer;
}

/* The X: two strokes laid over the same centred line, turned either way */
.lightbox__close span {
  position: relative;
  display: block;
  width: 20px;
  height: 2px;
}

.lightbox__close span::before,
.lightbox__close span::after {
  content: "";
  position: absolute;
  inset: 0;
  background: currentColor;
}

.lightbox__close span::before { rotate: 45deg; }
.lightbox__close span::after  { rotate: -45deg; }

.lightbox__count {
  position: absolute;
  bottom: 24px;
  left: 0;
  right: 0;
  margin: 0;
  text-align: center;
  color: rgb(255 255 255 / 0.7);
  font-size: 0.875em;
}

/* Shop ----------------------------------------------------------------- */

/* Product pages (kept from WooCommerce as references): the words on the
   left, the cover and its spreads on the right, and stock as a chip.
   The gallery comes first in the markup, so the columns are assigned
   rather than left to source order. */
/* A product page is a layout, not a column of reading: it takes the page's
   width rather than the container's 600px measure */
.container:has(> .product.type-product) {
  max-width: none;
}

.product.type-product {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  align-items: start;
  gap: 50px;
}

.product.type-product > .woocommerce-product-gallery {
  grid-area: 1 / 2;
}

.product.type-product > .summary {
  grid-area: 1 / 1;
}

/* The column is wide; the reading measure is not */
.product.type-product > .summary > * {
  max-width: 34em;
}

/* Anything else the page carries - the related strip, the clearing div -
   runs the full width beneath the pair */
.product.type-product > :not(.woocommerce-product-gallery):not(.summary) {
  grid-column: 1 / -1;
}

@media (max-width: 745px) {
  /* Stacked: the words, then the cover under them */
  .product.type-product {
    grid-template-columns: minmax(0, 1fr);
    gap: 22px;
  }

  .product.type-product > .woocommerce-product-gallery,
  .product.type-product > .summary {
    grid-area: auto;
  }

  .product.type-product > .summary {
    order: -1;
  }
}

.woocommerce-product-gallery__wrapper {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 20px;
}

.woocommerce-product-gallery__image {
  flex: 1 1 calc(25% - 8px);
  min-width: 0;
}

.woocommerce-product-gallery__image:first-child {
  flex-basis: 100%;
}

.woocommerce-product-gallery__image img {
  width: 100%;
}

/* The photographs open in the lightbox: on hover the picture eases in
   inside its frame and an expand mark rises in the corner */
.woocommerce-product-gallery__image a {
  display: block;
  position: relative;
  overflow: hidden;
  cursor: zoom-in;
}

.woocommerce-product-gallery__image .lqip {
  overflow: hidden;
}

.woocommerce-product-gallery__image img {
  transition: transform 1.2s var(--ease-soft);
}

.woocommerce-product-gallery__image a:hover img,
.woocommerce-product-gallery__image a:focus-visible img {
  transform: scale(var(--zoom));
}

.woocommerce-product-gallery__image a::after {
  content: "";
  position: absolute;
  right: 12px;
  bottom: 12px;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: rgb(0 0 0 / 0.45) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9 4H4v5M15 4h5v5M9 20H4v-5M15 20h5v-5' fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 18px 18px no-repeat;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.3s ease, transform 0.3s ease;
  pointer-events: none;
}

.woocommerce-product-gallery__image a:hover::after,
.woocommerce-product-gallery__image a:focus-visible::after {
  opacity: 1;
  transform: none;
}

/* The small spreads get a smaller mark */
.woocommerce-product-gallery__image:not(:first-child) a::after {
  width: 28px;
  height: 28px;
  right: 8px;
  bottom: 8px;
  background-size: 14px 14px;
}

.product .stock {
  display: inline-block;
  margin: 0 0 20px;
  padding: 0.35em 0.8em;
  border-radius: 0;
  background: var(--surface);
  color: var(--text-strong);
  font-size: var(--label-size);
  line-height: 1.4;
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  font-weight: 600;
}

.shop-intro {
  max-width: 600px;
  margin: 0 0 44px;
}

.shop-grid {
  row-gap: 30px;
}

/* Under each product: title + price stacked on the left, button on the
   right. The grid stretches every card in a row to the same height, the
   row is pushed to the card's bottom and everything in it sits on that
   bottom edge, so the buttons line up across the row whatever the titles
   do. */
.product-card {
  container-type: inline-size;
  display: flex;
  flex-direction: column;
}

.product-card__row {
  display: flex;
  align-items: center; /* the button centred on the title and price together */
  justify-content: space-between;
  gap: 16px;
  margin-top: auto;
  padding-top: 16px;
}

.product-card__info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

/* In a narrow column the button would squeeze the title into a word per
   line. Drop it below the text instead, aligned left. */
@container (max-width: 320px) {
  .product-card__row {
    flex-wrap: wrap;
    align-items: flex-start;
  }

  .product-card__info {
    flex: 1 1 100%;
  }

  .product-card__row .but {
    margin-top: 4px;
  }
}

.product-card__info .card__title {
  margin: 0;
}

.product-card__info .card__title a {
  color: var(--text-strong);
  text-decoration: none;
}

.product-card__info .card__title a:hover {
  color: var(--text-hover);
}

.product-price {
  color: var(--text-strong);
}

/* Both shop buttons share one fixed height, whatever they contain */
.product-card__row .but {
  display: inline-flex;
  align-items: center;
  flex: none;
  height: 2.25em;
  margin: 0;
  padding-block: 0;
}

/* "Buy on Amazon" with the small smile mark after it, all in white */
.but--amazon {
  gap: 0.5em;
}

.but--amazon .amazon-mark {
  display: block;
  width: 0.95em;
  height: 0.95em;
  margin-top: 0.05em;
}

/* Gallery - CSS columns instead of the Masonry script */

.gallery {
  column-count: 3;
  column-gap: 20px;
}

.gallery > * {
  break-inside: avoid;
  margin-bottom: 20px;
}

/* Content ----------------------------------------------------------------- */

.prose {
  max-width: 720px;
  margin-inline: auto;
}

.prose h2 {
  font-weight: 400;
  font-size: 1.15rem;
  line-height: 1.7;
  color: var(--text);
}

/* Lists set like the paragraphs around them: the same leading, a small
   dash for a marker, a little air between items */
.prose ul {
  margin: 0 0 1.4em;
  padding: 0;
  list-style: none;
}

.prose li {
  position: relative;
  margin: 0 0 0.6em;
  padding-left: 1.4em;
  line-height: inherit;
}

.prose li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.85em;
  width: 0.7em;
  height: 1px;
  background: var(--text-soft);
}

.prose li strong {
  font-weight: 600;
  color: var(--text-strong);
}

.hero-intro {
  text-align: center;
  padding: 20px 0 40px;
}

.hero-intro .flower {
  width: 46px;
  margin: 0 auto 20px;
}

/* Buttons ----------------------------------------------------------------- */

.btn {
  display: inline-block;
  border: 0;
  border-radius: var(--radius);
  padding: 0.618em 1em;
  background: var(--surface);
  color: var(--text-strong);
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
}

.btn--primary {
  background: var(--text-strong);
  color: #fff;
}

.btn--primary:hover {
  background: var(--text-hover);
  color: #fff;
}

/* The theme's own button class - used by the workshop cards
   (Book / Info / View Map) and carried over in the content markup. */

.but {
  display: inline-block;
  border: 0;
  border-radius: var(--radius);
  padding: 0.5em 1em;
  margin: 0 0 10px;
  background: var(--surface);
  color: var(--text-strong);
  font: inherit; /* a <button> would otherwise fall back to the system face */
  letter-spacing: var(--tracking);
  font-weight: 400; /* every button: the text weight, as the header's Contact */
  line-height: 1.25; /* room for ascenders and descenders, so the word sits centred */
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
}

/* Small variant: the map popups */
.but--small {
  padding: 0.4em 0.85em;
  margin: 0;
  font-size: 0.875em;
}

.but a {
  color: inherit;
}

.but:hover {
  background-color: var(--but-hover); /* not the shorthand: "Info" keeps its sized arrow */
}

/* The primary button: outlined, no fill. Both forms exist in the
   markup: the theme wraps an <a> inside the .but, the shop makes the <a>
   itself the .but - so the colour is set on the button and a nested link.
   The box keeps its size: the stroke replaces a pixel of padding. */
.but.booking,
.but.booking a {
  background: none;
  color: var(--text-strong);
}

.but.booking {
  padding: calc(0.5em - 1px) calc(1em - 1px);
  border: 1px solid var(--stroke);
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

/* Hover: a light grey fill inside the stroke, the same grey the filled
   buttons rest on - a clear lift, not a flip to solid */
.but.booking:hover,
.but.booking:focus-visible,
.but.booking:hover a,
.but.booking:focus-visible a {
  background: var(--surface);
  border-color: var(--text-strong);
  color: var(--text-strong);
}

a.but {
  text-decoration: none;
}

/* "Info" carries a small triangle that flips once the panel is open.
   Inline SVG rather than the theme's PNGs - crisp at any density, no
   request, and the colour is right here in the stylesheet. */
.more {
  padding-right: 35px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 12 7'%3E%3Cpath d='M0 0h12L6 7z' fill='%23515151'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 13px center;
  background-size: 10px 6px;
}

.more.less {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 12 7'%3E%3Cpath d='M0 7h12L6 0z' fill='%23515151'/%3E%3C/svg%3E");
}

/* Video embeds - replaces fitVids */

.video {
  aspect-ratio: 16 / 9;
}

.video iframe {
  width: 100%;
  height: 100%;
  border: 0;
}

/* Footer ------------------------------------------------------------------ */
/* The flower mark, then columns of links; a bar beneath with the small
   print on the left and the colour-scheme switch on the right. */

.site-footer {
  padding: 40px 0; /* the same 40px above the Instagram bar as beside it */
  border-top: 1px solid var(--rule);
}

/* Follow: the Instagram bar, a grey box on the page width (and the letter
   beside it once wired up), then the columns */
.footer__follow {
  margin-bottom: 40px;
}

.footer__follow > .wrap {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--col-gap);
}

.footer__follow .footer__ig { grid-column: 1 / -1; }
/* With the letter beside it (held back until it is wired up): 7 and 5 */
.footer__follow > .wrap:has(> .footer__letter) > .footer__ig { grid-column: span 7; }
.footer__follow .footer__letter { grid-column: span 5; }

/* The letter: a box like the Instagram bar's, the field and button in a row */
.footer__letter {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 14px;
  margin: 0;
  padding: 22px 28px;
  border: 1px solid var(--rule-strong);
}

.footer__letter-title {
  margin: 0 0 2px;
  padding: 0;
  font-size: 1em;
  font-weight: 600;
  color: var(--text-strong);
}

.footer__letter-text p {
  margin: 0;
  color: var(--text);
}

.footer__letter-row {
  display: flex;
  gap: 10px;
}

.footer__letter-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0 0.75em;
  height: 36px;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text-strong);
  font: inherit;
  letter-spacing: var(--tracking);
  transition: border-color 0.2s ease;
}

.footer__letter-input:focus {
  outline: none;
  border-color: var(--text-strong);
}

.footer__letter-button {
  flex: none;
  margin: 0;
  height: 36px;
}

.footer__letter-done {
  margin: 0;
  color: var(--text-strong);
}

/* Instagram: one bar, all of it the link. The mark, the invitation, an
   arrow that moves off to the right on hover. */
.footer__ig {
  display: grid; /* on the page grid, so the words start where the Site column does */
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--col-gap);
  align-items: center;
  margin: 0;
  padding: 26px 0; /* no side padding: the bar's columns are the page's; the discs and arrow inset themselves */
  background: var(--surface);
  color: var(--text-strong);
  text-decoration: none;
  transition: background 0.25s var(--ease-soft);
}

.footer__ig:hover,
.footer__ig:focus-visible {
  background: var(--surface-strong);
  color: var(--text-strong);
  text-decoration: none;
}

/* Clive's face and the Instagram mark: two discs of a size, the mark
   lapping the edge of the photograph */
.footer__ig-who {
  position: relative;
  display: block;
  flex: none;
  width: 78px; /* two 44px discs, overlapping by 10 */
  height: 44px;
}

.footer__ig-face {
  display: block;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  object-fit: cover;
}

/* The mark: drawn in the text colour on a disc of the page's ground, so
   it reads the same in both modes; a ring in the ground colour separates
   it from the photograph where they lap */
.footer__ig-mark {
  position: absolute;
  left: 34px;
  top: 0;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--bg); /* the page colour on the band's grey, the ring in the band's own */
  box-shadow: 0 0 0 2px var(--surface);
  color: var(--text-strong);
}

.footer__ig-mark svg {
  width: 26px; /* the glyph fills more of its disc */
  height: 26px;
}

.footer__ig-who {
  grid-area: 1 / 1 / 2 / 3;
  margin-left: 28px; /* inset from the box's edge */
}

.footer__ig-count {
  grid-area: 1 / 1 / 2 / 3; /* the mark's two columns, after the discs (the row named, or it would drop to a second) */
  justify-self: start;
  margin-left: calc(28px + 78px + 18px); /* after the discs */
  font-size: 1.4em;
  font-weight: 600;
  line-height: 1;
}

.footer__ig-text {
  grid-area: 1 / 3 / 2 / 12;
  font-size: 1.125em;
  line-height: 1.4;
}

.footer__ig-arrow {
  display: block;
  grid-area: 1 / 12 / 2 / 13;
  justify-self: end;
  margin-right: 28px;
  transition: transform 0.25s ease;
}

.footer__ig:hover .footer__ig-arrow,
.footer__ig:focus-visible .footer__ig-arrow {
  transform: translateX(6px);
}

.footer {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 32px var(--col-gap);
  align-items: start;
}

/* mark 2 · site 2 · subjects 2 · clients 3 · contact 3 */
.footer > :nth-child(1) { grid-column: span 2; }
.footer > :nth-child(2) { grid-column: span 2; }
.footer > :nth-child(3) { grid-column: span 2; }
.footer > :nth-child(4) { grid-column: span 3; }
.footer > :nth-child(5) { grid-column: span 3; }

.footer__mark {
  display: block;
  width: 56px;
}

.footer__mark img {
  display: block;
  width: 56px;
  height: auto;
}

.footer__col h2 {
  margin: 0 0 18px;
  padding: 0;
  font-size: var(--label-size);
  font-weight: 600;
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--text);
}

.footer__col ul {
  margin: 0;
  padding: 0;
  list-style: none;
  line-height: 1.5;
}

.footer__col li {
  margin: 0 0 10px;
}

.footer__col a {
  color: var(--text); /* the site's underline, in the footer's quieter grey */
}

.footer__bar {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 12px var(--col-gap);
  align-items: center;
  margin-top: 64px;
  padding-top: 28px;
  border-top: 1px solid var(--rule);
  font-size: 0.875em;
}

/* copyright 4 · links from the column Photography starts on (5) · switch on the right */
.footer__copy {
  grid-column: 1 / span 4;
  margin: 0;
  color: var(--text);
}

.footer__small {
  grid-column: 5 / span 6;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 28px;
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--text);
}

.footer__bar .theme-toggle {
  grid-column: 11 / span 2;
  justify-self: end;
}

/* At the bar's right edge the word comes first and the icon sits on the
   edge; once the bar stacks and the switch is left-aligned, the icon leads */
.footer__bar .theme-toggle__icon {
  order: 1;
}

.footer__small a {
  color: inherit;
}

/* Under 1320px the mark's two columns can't hold the discs and the count
   (28 + 78 + 18 + the figure): the Instagram bar is a plain row again */
@media (max-width: 1320px) {
  .footer__ig {
    display: flex;
    gap: 18px;
    padding: 26px 28px;
  }

  .footer__ig-who,
  .footer__ig-count {
    margin-left: 0;
  }

  .footer__ig-count {
    margin-right: 10px;
  }

  .footer__ig-text {
    flex: 1;
  }

  .footer__ig-arrow {
    margin-right: 0;
  }
}

/* Narrower than the five columns can take (the address is the long one):
   the mark and the three link columns on one row, Contact on the next;
   the small print wraps onto its own line under the copyright */
@media (max-width: 1200px) {
  .footer > :nth-child(1) { grid-column: span 2; }
  .footer > :nth-child(2) { grid-column: span 3; }
  .footer > :nth-child(3) { grid-column: span 3; }
  .footer > :nth-child(4) { grid-column: span 4; }
  .footer > :nth-child(5) { grid-column: 3 / span 10; }

  .footer__copy { grid-column: span 6; }
  .footer__bar .theme-toggle { grid-column: span 6; }
  .footer__small { grid-column: 1 / -1; grid-row: 2; }
}

/* From the tablet breakpoint the home intro reads from the left, as the
   stacked sections beneath it do */
@media (max-width: 900px) {
  .services-intro--centred > .services-intro__text {
    text-align: left;
  }

  .services-intro--centred .services-intro__text p {
    margin-inline: 0;
  }

  .services-intro--centred .services-stats {
    justify-content: flex-start;
  }

  .services-intro--centred .services-stats > div {
    align-items: flex-start;
  }
}

@media (max-width: 900px) {
  .footer__follow .footer__ig,
  .footer__follow .footer__letter {
    grid-column: 1 / -1;
  }

  .footer {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .footer > :nth-child(n) {
    grid-column: auto;
  }

  .footer > .footer__mark,
  .footer > :nth-child(5) {
    grid-column: 1 / -1; /* the mark first and Contact last, each on its own row - the address is long */
  }

  .footer__copy,
  .footer__bar .theme-toggle,
  .footer__small {
    grid-column: 1 / -1;
    grid-row: auto;
  }

  .footer__bar .theme-toggle {
    justify-self: start;
  }

  .footer__bar .theme-toggle__icon {
    order: 0;
  }
}

@media (max-width: 745px) {
  /* No room for the photograph's caption under the quote on a phone */
  .home-hero__caption {
    display: none;
  }

  .site-footer {
    padding: 20px 0 32px; /* level with the phone gutter */
  }

  .footer__follow {
    margin-bottom: 32px;
  }

  .footer__follow > .wrap {
    gap: 12px;
  }

  .footer__ig {
    display: flex; /* too narrow for the columns: a row again */
    gap: 14px;
    padding: 16px 18px;
  }

  .footer__ig-who,
  .footer__ig-count {
    margin-left: 0;
  }

  .footer__ig-text {
    flex: 1;
  }

  .footer__ig-arrow {
    margin-right: 0;
  }

  .footer__letter {
    padding: 16px 18px;
  }

  .logos-title {
    margin-top: 44px;
  }

  .footer__ig-text {
    font-size: 1em;
  }

  .footer__ig-who {
    width: 66px;
    height: 38px;
  }

  .footer__ig-face,
  .footer__ig-mark {
    width: 38px;
    height: 38px;
  }

  .footer__ig-mark {
    left: 28px;
  }

  .footer__ig-mark svg {
    width: 22px;
    height: 22px;
  }

  .footer__ig-count {
    font-size: 1.3em;
  }

  .footer__ig-arrow {
    width: 16px;
  }

  .footer__bar {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 12px 24px;
    margin-top: 44px;
  }

  .footer__copy {
    flex: 0 0 100%;
  }
}

@media (max-width: 480px) {
  .footer {
    gap: 28px 24px; /* still two columns: the words are short */
  }
}

/* /ui/ - the reference page ------------------------------------------------ */
.ui-head {
  max-width: 40em;
  padding-bottom: 20px;
}

.ui-sample {
  margin: 0;
  padding: 0;
}

.ui-h1 { font-size: 1.8em; }
.ui-h2 { font-size: 1.286em; }

.ui-stat {
  font-size: 2.2em;
  line-height: 1;
  color: var(--text-strong);
}

.ui-small {
  font-size: 0.875em;
}

.ui-type {
  margin: 0;
}

.ui-type__row {
  display: grid;
  grid-template-columns: 11em 1fr;
  gap: var(--col-gap);
  padding: 16px 0;
  border-bottom: 1px solid var(--rule);
}

.ui-type__row:first-child {
  padding-top: 0;
}

.ui-type dt {
  display: flex;
  flex-direction: column;
  gap: 4px;
  color: var(--text-strong);
}

.ui-spec {
  font-size: 0.8125em;
  color: var(--text);
}

.ui-type dd {
  margin: 0;
  min-width: 0;
}

.ui-note {
  margin: 20px 0 0;
  max-width: 40em;
  font-size: 0.875em;
}

.ui code,
.ui kbd {
  font: 0.9em / 1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--text-strong);
}

.ui-swatches {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 14px var(--col-gap);
  margin: 0;
  padding: 0;
  list-style: none;
}

.ui-swatches li {
  display: grid;
  grid-template-columns: 36px 1fr;
  grid-template-rows: auto auto;
  column-gap: 12px;
  align-items: center;
  font-size: 0.875em;
}

.ui-swatch {
  grid-row: 1 / 3;
  width: 36px;
  height: 36px;
  border: 1px solid var(--rule-strong);
}

.ui-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--col-gap);
  height: 48px;
  margin-bottom: 20px;
}

.ui-grid span {
  background: rgb(255 0 90 / 0.05);
  border-inline: 1px solid rgb(255 0 90 / 0.2);
}

.ui-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.ui-list li {
  padding: 4px 0;
}

.ui-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 18px;
  margin: 16px 0 0;
}

.ui-row .but {
  margin: 0;
}

.ui-form {
  max-width: 28em;
}

.ui-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--col-gap);
}

.ui-cols .ticks {
  margin-top: 0;
}

/* Each mark in a cell of the same size, centred, its caption beneath */
.ui-marks {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--col-gap);
}

.ui-marks figure {
  margin: 0;
}

.ui-marks .ui-mark {
  display: grid;
  place-items: center;
  height: 180px;
  padding: 24px;
  border: 1px solid var(--rule);
}

.ui-marks .ui-mark img,
.ui-marks .flower {
  margin: 0; /* sized by their width/height attributes, as on the site */
  max-height: 100%;
}

.ui-marks figcaption {
  margin-top: 12px;
  font-size: 0.875em;
}

.ui-icons {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 20px var(--col-gap);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 0.875em;
}

.ui-icons li {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}

.ui-icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 1px solid var(--rule);
  color: var(--text-strong);
}

.ui-icon svg {
  display: block;
}

.ui-burger {
  position: relative;
  display: block;
  width: 16px;
  height: 12px;
}

.ui-burger::before,
.ui-burger::after,
.ui-burger span {
  content: "";
  position: absolute;
  left: 0;
  width: 100%;
  height: 1.5px;
  background: currentColor;
}

.ui-burger::before { top: 0; }
.ui-burger span { top: 5.25px; }
.ui-burger::after { bottom: 0; }

@media (max-width: 745px) {
  .about-contact li.about-contact__where {
    grid-template-columns: 1fr;
    gap: 18px;
  }

  /* Stacked, the clock sits close under its words */
  .about-contact__time-label {
    margin-top: 30px;
  }

  .about-contact__time time {
    margin-top: -8px;
  }

  .contact__row {
    grid-template-columns: 1fr;
  }

  .ui-type__row,
  .ui-cols {
    grid-template-columns: 1fr;
    gap: 10px;
  }
}

/* Content classes carried over from the old markup ------------------------
   Same class names, reimplemented with flex/columns instead of floats. */

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

.visually-hidden:focus {
  position: static;
  width: auto;
  height: auto;
  margin: 0;
  padding: 10px;
  clip-path: none;
}

/* Two-up text blocks (portfolio intro, workshop cards) */

.wrap-mid:has(> .module) {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--col-gap);
}

/* 50% + 10px padding, exactly as the theme floated them */
.module {
  flex: 0 0 calc(50% - var(--col-gap) / 2);
}

/* Anything that isn't a module inside that flex container spans the full
   row - the about page's centred intro blocks live alongside the team. */
.wrap-mid:has(> .module) > :not(.module) {
  flex: 0 0 100%;
  width: 100%;
}

.team img {
  margin-bottom: 10px;
}

.module p {
  margin-right: 0;
}

/* A project's opening: the title, the words at a reading width, and the
   client and place in small caps beneath - one centred block above the
   photographs, each line arriving a beat after the last */
.wrap-mid.project-head {
  display: block;
  padding-top: 90px;
  padding-bottom: 30px;
  text-align: center;
}

.project-head .module-title {
  font-size: 1.8em;
  line-height: 1.25;
  margin: 0 0 20px;
  padding: 0;
  color: var(--text-strong);
  text-wrap: balance;
}

.project-head__lead {
  max-width: 36em;
  margin: 0 auto 14px;
  text-wrap: balance;
}

.project-head__meta {
  margin: 10px 0 0;
  font-size: var(--label-size);
  font-weight: 600;
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--text);
}

.js .project-head > * {
  animation: head-in 0.8s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}

.js .project-head > :nth-child(2) { animation-delay: 0.12s; }
.js .project-head > :nth-child(3) { animation-delay: 0.24s; }
.js .project-head > :nth-child(4) { animation-delay: 0.36s; }

@keyframes head-in {
  from {
    opacity: 0;
    transform: translateY(16px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .js .project-head > * {
    animation: none;
  }
}

@media (max-width: 745px) {
  .wrap-mid.project-head {
    padding-top: 40px;
  }
}

/* Project titles are promoted to <h1> for the document outline but keep
   the theme's h2 size. */
.module-title {
  font-size: 1.286em;
  padding: 0 0 10px;
}

/* The team (about page): the people modules in one row that scrolls
   sideways - wheel, swipe, click-drag (site.js) or the arrows above. */
.people {
  flex: 0 0 100%;
  width: 100%;
}

.people__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 0 0 16px;
}

.people__title {
  font-size: 1.286em;
  font-weight: 400;
  margin: 0;
  padding: 0;
  color: var(--text-strong);
}

/* The track bleeds to the viewport edges so the cards visibly run off to
   the right, but starts (and snaps) at the content's left edge: the
   negative margin takes it full-width, the same amount of padding brings
   the first card back into line. The section is a size container, so
   cqw units give its width from inside the track whatever property they
   sit in (percentages would resolve against different boxes). */
.people {
  container-type: inline-size;
}

.people__track {
  --bleed: calc(50vw - 50cqw);
  display: flex;
  gap: var(--col-gap);
  margin-inline: calc(50cqw - 50vw);
  padding-inline: var(--bleed);
  scroll-padding-inline-start: var(--bleed);
  /* Cards fade out through the bleed on both sides: fully there inside the
     content width, gone by the viewport edge */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 var(--bleed), #000 calc(100% - var(--bleed)), transparent);
  mask-image: linear-gradient(to right, transparent, #000 var(--bleed), #000 calc(100% - var(--bleed)), transparent);
  overflow-x: auto;
  overflow-y: hidden; /* a horizontal scroller only - never a vertical one */
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  cursor: grab;
}

/* Below the wide layouts the bleed is only the gutter: no fade, the cards
   run to the edge */
@media (max-width: 900px) {
  .people__track {
    -webkit-mask-image: none;
    mask-image: none;
  }
}

/* The bleed must not become page overflow (Windows scrollbars make 100vw
   a touch wider than the page) */
main {
  overflow-x: clip;
}

.people__track::-webkit-scrollbar {
  display: none;
}

.people__track:focus-visible {
  outline: 2px solid var(--text-strong);
  outline-offset: 4px;
}

.people__track.is-dragging {
  scroll-snap-type: none;
  scroll-behavior: auto;
  cursor: grabbing;
  user-select: none;
}

.people__item {
  flex: 0 0 calc((100cqw - 2 * var(--col-gap)) / 3); /* three across the content width, on the column lines; the fourth peeks in the bleed */
  scroll-snap-align: start;
}

/* Two across on tablets and small laptops, one (and a peek) on phones */
@media (max-width: 1024px) {
  .people__item {
    flex-basis: calc((100cqw - var(--col-gap)) / 2);
  }
}

/* Contact details over the office garden, edge to edge above the footer */
.office {
  position: relative;
  height: 60vh;
  min-height: 360px;
  max-height: 640px;
  overflow: hidden;
  color: #fff;
}

.office .lqip,
.office img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.office img {
  object-fit: cover;
}

/* Cover-cropped like the photograph; sized to the frame itself so the
   crops match. */
.office .lqip::before {
  inset: 0;
  background: var(--lqip) center / cover no-repeat;
  clip-path: none;
}

/* Scrim for the text; the photograph is bright at the bottom */
.office:not(.services-cta)::after {
  content: none; /* nothing over the photograph now */
}

.office::after {
  content: "";
  position: absolute;
  inset: 40% 0 0;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.55));
  pointer-events: none;
}

.office__text {
  position: absolute;
  inset: auto 0 0;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 16px 40px;
  max-width: var(--container);
  margin-inline: auto;
  padding: 40px;
  line-height: 1.7;
}

.office__text p {
  margin: 0;
  color: #fff;
}

/* Dragging must not turn into an image drag or a text selection */
.people__item img {
  -webkit-user-drag: none;
  pointer-events: none;
}

.hero-nav button:disabled {
  opacity: 0.35;
  cursor: default;
  background: var(--bg);
  color: var(--text-strong);
  border-color: var(--rule);
}

/* Image flow on portfolio detail pages */

.wrap-mid:has(> .post_box) {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--col-gap); /* between the photographs, across and down */
}

/* flex-grow stays 0: the original floated these, so a lone 62% box keeps its
   width instead of stretching to fill the row. */
.post_box {
  position: relative;
  flex: 0 0 100%;
}

/* Pairs share a row with the column gap between them: 4 + 8 columns, or
   6 + 6 - the widths are what those spans come to on the twelve-column grid */
.post_box.threequarter { flex-basis: calc((200% - var(--col-gap)) / 3); }
.post_box.half         { flex-basis: calc((100% - var(--col-gap)) / 2); }
.post_box.quarter      { flex-basis: calc((100% - 2 * var(--col-gap)) / 3); }

.post_box img {
  width: 100%;
}

/* On a project page the picture is a link into the lightbox; site.js
   drifts the narrower ones a few pixels with the scroll */
.post_box > a {
  display: block;
  will-change: transform;
}

/* The theme's float clearers: nothing floats now, and as flex items they
   would each take a row gap */
.clear {
  display: none;
}

/* Image details: an info button in the image's top-right corner, shown on
   hover (always on touch), opening a tooltip with the caption. */

.img-meta {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 3;
}

.img-info {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgb(0 0 0 / 0.45);
  color: #fff;
  cursor: pointer;
  opacity: 0;
  transform: scale(0.9);
  transition: opacity 0.25s ease, transform 0.25s ease, background 0.2s ease;
}

.post_box:hover .img-info,
.img-meta:focus-within .img-info,
.img-meta.is-open .img-info {
  opacity: 1;
  transform: none;
}

.img-info:hover,
.img-meta.is-open .img-info {
  background: rgb(0 0 0 / 0.7);
}

@media (hover: none) {
  .img-info {
    opacity: 1;
    transform: none;
  }
}

.img-tip {
  position: absolute;
  top: 38px; /* straight under the button, joined to it by the notch */
  right: 0;
  width: max-content;
  max-width: min(360px, 70vw);
  padding: 12px 14px;
  border-radius: var(--tip-radius);
  background: rgb(0 0 0 / 0.7);
  color: #fff;
  font-size: 0.875rem;
  line-height: 1.5;
  text-align: left;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity 0.22s ease, transform 0.22s ease, visibility 0s linear 0.22s;
}

.img-meta:hover .img-tip,
.img-meta:focus-within .img-tip,
.img-meta.is-open .img-tip {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}

/* The notch: a small square turned under the button's centre */
.img-tip::before {
  content: "";
  position: absolute;
  top: -5px;
  right: 11px;
  width: 10px;
  height: 10px;
  background: inherit;
  transform: rotate(45deg);
}

/* Workshop grid. Without JS this is a plain two-column CSS grid; site.js
   upgrades it to a masonry layout (shortest-column placement, like the
   theme's Masonry plugin) by positioning the items itself. */

.grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--col-gap);
  align-items: start;
}

.grid.is-masonry {
  display: block;
  position: relative;
}

.grid.is-masonry .grid-item {
  position: absolute;
  top: 0;
  left: 0;
}

.grid-sizer {
  display: none;
}

.no-events {
  max-width: 600px;
  margin: 10px auto 30px;
  text-align: center;
}

/* The theme preloads hover-state arrows in a hidden div. Without this rule
   they render as three loose 40px icons at the foot of every project page. */
.hide {
  display: none;
}

.border {
  display: block;
  position: relative;
  border-bottom: 1px solid var(--rule);
}

/* Previous / next project - thumbnails peek in at the edges and slide in */

.related {
  position: relative;
  clear: both;
  overflow: hidden;
  width: 100%;
  min-height: 100px;
}

.postprev,
.postnext {
  transition: margin 0.3s ease;
}

.postprev {
  float: right;
  margin-right: -120px;
}

.postnext {
  float: left;
  margin-left: -120px;
}

.postprev:hover { margin-right: 0; }
.postnext:hover { margin-left: 0; }

/* The thumbnails float; when wrapped for blur-up the wrapper floats instead */
.postprev img, .postprev .lqip { float: right; }
.postnext img, .postnext .lqip { float: left; }

.postprev .lqip img,
.postnext .lqip img {
  float: none;
}

.postprev img,
.postnext img {
  width: 149px;
  height: 100px;
  object-fit: cover;
}

.related .next {
  display: inline-block;
  padding: 40px 10px;
}

.allproj {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  padding: 39px 10px;
  white-space: nowrap;
}

.proj-icon {
  display: inline-block;
  width: 15px;
  height: 15px;
  margin-right: 6px;
  vertical-align: -2px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23515151'%3E%3Crect width='7' height='7' rx='1'/%3E%3Crect x='9' width='7' height='7' rx='1'/%3E%3Crect y='9' width='7' height='7' rx='1'/%3E%3Crect x='9' y='9' width='7' height='7' rx='1'/%3E%3C/svg%3E") no-repeat center / contain;
}

@media (max-width: 745px) {
  .related {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
  }

  .postprev,
  .postnext {
    float: none;
    margin: 0;
  }

  .allproj {
    position: static;
    transform: none;
    order: 3;
    width: 100%;
    text-align: center;
    padding: 20px 10px;
  }

  .related .next {
    padding: 10px;
  }
}

/* Workshop event details - collapsed until "more" is clicked */

.open_event {
  margin-bottom: 20px;
}

.info {
  margin: 0 0 12px; /* above the buttons now */
}

/* Only hidden once site.js is there to open it; without scripting the
   details simply show and the toggle is not offered. */
.js .info {
  display: none;
}

.js .info.is-open {
  display: block;
}

.more {
  cursor: pointer;
  font-family: inherit;
  font-size: inherit;
  letter-spacing: inherit;
}

:root:not(.js) .more {
  display: none;
}

/* The talk card matches the course beside it: same 3:2 frame, same rhythm. */
.talk-card .lqip,
.talk-card img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  margin-bottom: 10px;
}

.talk-card .lqip img {
  aspect-ratio: auto;
  height: 100%;
  margin: 0;
}

.talk-card h2 {
  margin-top: 1.1em; /* level with the course title, which sits under the theme's <br> */
}

/* Intro blocks */

.hero {
  text-align: center;
  padding-top: 60px;
}

/* The opening is the page's first thing: the section's own padding is the
   only air above it */
.hero:has(> .studio_hero--split) {
  padding-top: 0;
}

/* The About page opens as one movement, across and then down: the words,
   the portrait beside them a beat later, then the marks' title, the grid
   and the band. One beat between each, so it reads as a single arrival
   rather than five separate ones - change --beat to pace the whole thing. */
.js .studio_hero > *,
.js .hero ~ .logos-title,
.js .hero ~ .client-logos,
.js .hero ~ .about-band {
  --beat: 0.12s;
  animation: head-in 0.8s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}

.js .studio_hero > :nth-child(2) { animation-delay: var(--beat); }
.js .studio_hero--split > .studio_hero__words { animation-delay: 0s; }
.js .studio_hero--split > .studio_hero__portrait { animation-delay: var(--beat); }
.js .hero ~ .logos-title { animation-delay: calc(var(--beat) * 2); }
.js .hero ~ .client-logos { animation-delay: calc(var(--beat) * 3); }
.js .hero ~ .about-band { animation-delay: calc(var(--beat) * 4); }

@media (prefers-reduced-motion: reduce) {
  .js .studio_hero > *,
  .js .hero ~ .logos-title,
  .js .hero ~ .client-logos,
  .js .hero ~ .about-band {
    animation: none;
  }
}

.studio_hero {
  max-width: 760px;
  margin-inline: auto;
  padding-top: 10px;
}

/* The about page opens with Clive: the words on the left, the portrait
   beside them, on the page's own ground */
.studio_hero--split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.62fr);
  align-items: stretch; /* the words start at the top, the mark falls to the foot */
  gap: 60px;
  max-width: none; /* the page's own width, like the marks and the sections below */
  padding: 0;
  text-align: left;
}

.studio_hero--split .studio_hero__words {
  position: relative; /* the mark hangs from this column's corner */
  display: flex;
  flex-direction: column;
  margin: 0;
}

/* Clive's own hand, under the words he wrote */
.studio_hero__sign {
  display: block;
  width: 260px;
  height: auto;
  margin: 30px 0 0;
}

/* The lead's later paragraphs, set as the first one is */
.studio_hero__lead {
  margin: 0;
  padding: 0 0 10px;
  margin-top: 0.5em; /* a clear step between the paragraphs */
  font-size: 1.286em;
  line-height: 1.5;
  color: var(--text);
}

/* One measure for the lead, whatever the column does around it, and the
   ragging left to the browser's better line breaker */
.studio_hero--split .studio_hero__words h2,
.studio_hero--split .studio_hero__lead {
  max-width: 28em;
  text-wrap: pretty;
}

.studio_hero__portrait {
  margin: 0;
}

/* The blur-up wrapper carries a 10px tail meant to space stacked pictures
   apart. There is only one picture here, and the tail reads as a gap under
   the portrait that the words beside it do not share. The page sits inside
   .team, whose own .lqip rule sets that tail and would otherwise win on
   order, so this carries the extra class to outrank it. */
.studio_hero--split .studio_hero__portrait > .lqip {
  margin-bottom: 0;
}

/* The photograph is the tall one, so it sets the box's height and the space
   above and below it is the box's padding, top and bottom alike */

.studio_hero__portrait img {
  display: block;
  width: 100%;
  height: auto;
}

@media (max-width: 900px) {
  /* Stacked: the words, then the portrait, on the section's own gutter */
  .studio_hero--split {
    grid-template-columns: minmax(0, 1fr);
    gap: 28px;
    max-width: 760px;
    padding: 0;
    text-align: left;
  }

  /* Stacked, the hand does not earn its place: the words run straight
     into the photograph */
  .studio_hero--split .studio_hero__sign {
    display: none;
  }

  .studio_hero--split .studio_hero__words h2,
  .studio_hero--split .studio_hero__lead {
    max-width: none; /* the column is already narrower than the measure */
  }

  /* The mark sits in the flow here, so the column needs no room reserved */
  .studio_hero--split .studio_hero__words {
    padding-bottom: 0;
  }

  .studio_hero--split h2,
  .studio_hero--split .studio_hero__lead {
    font-size: 1.07em; /* the phone's narrower measure wants the smaller size */
  }

  /* The portrait runs the width of the screen and is cropped to it, rather
     than sitting in the column at its own tall proportions */
  .studio_hero--split .studio_hero__portrait {
    width: 100vw;
    max-width: none;
    margin-inline: calc(50% - 50vw);
  }

  .studio_hero--split .studio_hero__portrait img {
    height: clamp(380px, 68vh, 620px);
    object-fit: cover;
    object-position: center 35%; /* his face stays in the frame as it crops */
  }

  .studio_hero__portrait {
    max-width: 380px;
    margin-inline: auto;
  }
}

/* The ruled "Selected Clients" title spans the content width, like the
   logo grid beneath it; only the intro text keeps the 760px measure. */
.studio_hero:has(> .border) {
  max-width: none;
  padding-inline: 10px;
}

.studio_hero h1,
.studio_hero h2 {
  font-weight: 400;
  line-height: 1.5; /* a lead, not a heading: room between the lines */
  color: var(--text);
  padding: 0 0 10px;
}

.studio_hero h1 { font-size: 1.8em; }   /* 25.9px - the workshops intro */
.studio_hero h2 { font-size: 1.286em; } /* 18.5px - the about intro */

/* The opening reads a step above the body copy */
.studio_hero--split h2,
.studio_hero--split .studio_hero__lead {
  font-size: 1.25em; /* 20px */
}

.studio_hero p {
  margin-bottom: 5px;
}

.flower {
  width: 60px;
  height: 120px;
  margin: 0 auto 10px;
}

.studio_hero .flower {
  margin-bottom: 32px; /* air between the mark and the lead */
}

/* 404 - the about-page intro treatment, flower and all */
.not-found {
  padding-bottom: 60px;
}

.not-found h1 {
  font-size: 1.286em;
}

.not-found__actions {
  margin-top: 20px;
}

/* "Selected Clients" - a label sitting on a rule, over a 3-column list */

.borderTitle {
  display: inline-block;
  position: relative;
  top: -12px;
  padding: 0 20px;
  background: var(--bg);
}

.listResp {
  column-count: 3;
  line-height: 1.9; /* the client list reads better with air between rows */
}

/* Client marks: recoloured to the site grey at build (build.py grey_logo)
   so a dozen house styles sit together, in a ruled grid. Each cell draws
   its own right and bottom rule and the grid the top and left, so an
   incomplete last row leaves no empty boxes behind. */
.client-logos,
.wrap-mid:has(> .module) > .client-logos {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  /* Inside the 10px card gutter, level with the rule above. The wrapper's
     flex rule would otherwise force 100% + margins and overhang the right. */
  flex-basis: 100%;
  width: 100%;
  margin: 40px 0 30px;
  border-top: 1px solid var(--rule);
  border-left: 1px solid var(--rule);
}

.client-logos .logo-cell {
  display: grid;
  place-items: center;
  min-height: 110px;
  padding: 20px;
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}

/* site.js cycles the marks: the outgoing fades, the incoming fades in.
   The .js/.is-loaded forms outrank the site-wide image fade-in rule,
   which would otherwise hold the mark at full opacity. */
.logo-cycle .logo-cell[hidden] {
  display: none;
}

.logo-cycle .logo-cell img,
.js .logo-cycle .logo-cell img.is-loaded {
  transition: opacity 0.6s ease, filter 0.25s ease;
}

.logo-cycle .logo-cell.is-fading img,
.js .logo-cycle .logo-cell.is-fading img.is-loaded {
  opacity: 0;
}

/* Awards: one table, newest first -------------------------------------- */
.awards {
  flex: 0 0 100%;
  width: 100%;
  margin: 50px 0 20px;
}

.awards__title {
  font-size: 1.286em;
  padding-bottom: 16px;
  color: var(--text-strong);
}

/* Tabs: text with an underline on the selected one. Without scripting
   the tab row is hidden and the three tables stand under their headings;
   with it (.tabs--js) the headings go and the tabs do the naming. */
.tabs__list {
  display: none;
  gap: 28px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--rule);
}

.tabs--js .tabs__list {
  display: flex;
  margin-bottom: 0; /* the tab rule doubles as the table's top rule */
}

.tabs--js .awards__table {
  border-top: 0;
}

/* Links in the tables take the site's text-link style: the soft underline
   that firms up on hover (the site-wide `a` rule already does this; the
   colour comes from the table's grey) */
.awards__table a {
  color: inherit;
}

.tabs--js .awards__title {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.tabs__tab {
  margin: 0 0 -1px;
  padding: 6px 0 12px;
  border: 0;
  background: none;
  color: var(--text-soft); /* the unselected tabs sit back; the selected one is dark */
  font: inherit;
  font-size: 1.125em;
  letter-spacing: var(--tracking);
  cursor: pointer;
  transition: color 0.2s ease;
}

.tabs__tab:hover {
  color: var(--text);
}

.tabs__tab[aria-selected="true"] {
  color: var(--text-hover); /* black: the selected tab is the darkest thing in the row */
}

.tabs__tab:focus:not(:focus-visible) {
  outline: none; /* a click is not a keyboard focus */
}

.tabs__panel[hidden] {
  display: none;
}

/* Rows beyond the first few wait behind "Load more" (script only) */
.js .awards__table tr[data-more] {
  display: none;
}

/* The portfolio: the older projects wait behind "View more" the same way */
.js .card-grid .card[data-more] {
  display: none;
}

.card-grid__more {
  display: none;
  margin: 40px 0 0;
  text-align: center;
}

.js .card-grid__more {
  display: block;
}

.card-grid__more .but {
  margin: 0;
}

.tabs__more {
  margin: 16px 0 0;
}

.tabs__more .but {
  margin: 0;
  font-size: 0.875em;
  letter-spacing: var(--tracking);
}

.tabs__more {
  display: none;
}

.js .tabs__more {
  display: block;
}

.tabs--js .tabs__panel {
  animation: panel-in 0.3s ease both;
}

@keyframes panel-in {
  from { opacity: 0; }
}

.awards__scroll {
  overflow-x: auto;
}

/* The tables sit on the page grid: each row is its own twelve-column grid
   with the site's gap, cells spanning columns (Awards 1 · 5 · 2 · 4, Books
   1 · 4 · 3 · 3 · 1, Press 1 · 3 · 8), so every column of words starts on
   a grid line. The row's rule is the row's own. Roles keep the table
   semantics that changing the display would otherwise lose. */
.awards__table {
  display: block;
  width: 100%;
  border-top: 1px solid var(--rule);
  font-size: 0.875em; /* 14px: reference material, a step down from body */
  line-height: 1.5;
}

.awards__table thead,
.awards__table tbody {
  display: block;
}

.awards__table tr {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--col-gap);
  border-bottom: 1px solid var(--rule);
}

.awards__table th,
.awards__table td {
  min-width: 0;
  padding: 9px 0;
  text-align: left;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.awards__table--awards tr > :nth-child(1) { grid-column: span 1; }
.awards__table--awards tr > :nth-child(2) { grid-column: span 5; }
.awards__table--awards tr > :nth-child(3) { grid-column: span 2; }
.awards__table--awards tr > :nth-child(4) { grid-column: span 4; }
.awards__table--books tr > :nth-child(1) { grid-column: span 1; }
.awards__table--books tr > :nth-child(2) { grid-column: span 4; }
.awards__table--books tr > :nth-child(3) { grid-column: span 3; }
.awards__table--books tr > :nth-child(4) { grid-column: span 3; }
.awards__table--books tr > :nth-child(5) { grid-column: span 1; }
.awards__table--press tr > :nth-child(1) { grid-column: span 1; }
.awards__table--press tr > :nth-child(2) { grid-column: span 3; }
.awards__table--press tr > :nth-child(3) { grid-column: span 8; }

.awards__table th {
  padding-top: 12px;
  padding-bottom: 8px;
  font-weight: 600;
  font-size: var(--label-size);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--text);
}

.awards__year {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.awards__what {
  color: var(--text-strong);
}

.awards__result {
  white-space: nowrap;
  color: var(--text);
}

.awards__result--winner {
  color: var(--text-strong);
}

.awards__result--shortlisted {
  color: var(--text-soft); /* a step lighter than body grey */
  font-style: italic; /* so the difference from a win does not rest on colour alone */
}

.awards__table td {
  color: var(--text);
}

.tabs__panel + .tabs__panel {
  margin-top: 44px;
}

.tabs--js .tabs__panel + .tabs__panel {
  margin-top: 0;
}

/* The Buy / Shop column sits on the table's right edge */
.awards__table--books td:last-child,
.awards__table--books th:last-child {
  text-align: right;
}

/* Hover darkens the mark only. A tinted cell would show through the
   white knockouts some marks rely on (the BMA emblem) and look broken. */

.client-logos img {
  display: block;
  width: auto;
  max-width: 100%; /* height per mark is set inline by build.py CLIENT_LOGOS */
  margin: 0; /* the about wrapper's .team img spacing would offset the mark */
  object-fit: contain;
  opacity: 0.85;
  transition: opacity 0.25s ease, filter 0.25s ease;
}


.list {
  display: inline-block;
  float: left;
  line-height: 1.7;
  padding-right: 10px;
}

.list:last-child {
  margin-bottom: 20px;
}

/* Motion -------------------------------------------------------------------
   Every rule here is gated on .js (set by site.js) or is pure CSS, so with
   scripting off nothing is ever hidden. */

html {
  scroll-behavior: smooth;
}

/* Cross-document crossfade between pages. No JS - browsers that don't
   support it simply navigate normally. */
@view-transition {
  navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 220ms;
}

main {
  animation: page-in 360ms ease-out both;
}

@keyframes page-in {
  from { opacity: 0; }
}

/* Touch: a finger needs about 44px and three of these are drawn smaller
   than that - the menu at 34, the header's Contact at 30 tall, the copy
   marks at 24. The hit area is grown around them rather than the control,
   so nothing moves and the header keeps its height. Only on a coarse
   pointer, so a mouse is unaffected. */
@media (pointer: coarse) {
  .nav-toggle,
  .site-header .but,
  .contact__copy {
    position: relative;
  }

  .nav-toggle::after,
  .site-header .but::after,
  .contact__copy::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: max(100%, 44px);
    height: max(100%, 44px);
    transform: translate(-50%, -50%);
  }
}

/* The followers figure carries the Instagram mark and goes to the account,
   so the number says where it came from. The mark sits on the text's own
   line, a little above the baseline, and takes the hover colour with it. */
.stat-ig {
  color: inherit;
  text-decoration: none;
}

.stat-ig:hover,
.stat-ig:focus-visible {
  color: var(--text-hover);
}

/* Images fade in as they arrive */

/* Transform is listed here too: this rule comes after .card img, and a
   bare `transition: opacity` would replace the card's zoom easing. */
.js main img {
  opacity: 0;
  transition: opacity 0.55s ease,
              transform 1.2s var(--ease-soft);
}

.js main img.is-loaded {
  opacity: 1;
}

/* Blur-up: a ~20px placeholder (inlined by build.py as --lqip) fills the
   image's box, heavily blurred, and the real image fades over it. Because
   every <img> carries width/height the box is final from first paint. */

.lqip {
  display: block;
  position: relative;
  overflow: hidden;
}

/* The placeholder must sit exactly under the photograph - any scale or
   offset shows as a jump when the two cross-fade. So: no scaling. The
   element is 12px larger than the frame on every side, carrying two copies
   of the tiny image - the exact one (sized to the frame, so it lines up
   pixel for pixel) over an enlarged one that only fills the 12px margin.
   The blur's soft edge then mixes with the image's own colours instead of
   with the page, and clip-path trims the result back to the frame. */
/* JS-only: site.js fades it out on load, so without scripts there is no
   placeholder and the photograph simply shows. */
.js .lqip::before {
  --bleed: 12px;
  content: "";
  position: absolute;
  inset: calc(var(--bleed) * -1);
  background:
    var(--lqip) center / calc(100% - var(--bleed) * 2) calc(100% - var(--bleed) * 2) no-repeat,
    var(--lqip) center / 100% 100% no-repeat;
  filter: blur(var(--bleed));
  clip-path: inset(var(--bleed));
  transition: opacity 0.6s ease 0.2s,
              transform 1.2s var(--ease-soft);
}

.lqip.is-loaded::before {
  opacity: 0;
}

.lqip img {
  display: block;
}

/* Spacing that used to sit on the <img> moves to the wrapper, so the
   placeholder never bleeds into the gap. */

.post_box .lqip::before {
  inset: calc(var(--bleed) * -1);
}

.post_box .lqip img {
  padding: 0;
}

.team .lqip {
  margin-bottom: 10px;
}

.team .lqip img {
  margin-bottom: 0;
}

/* Hero slides: blurred placeholder under a separate image layer that
   site.js fills as each slide comes up */

/* The hero crops to the viewport, so the placeholder must use cover too -
   matching the real image's crop rather than stretching. */
.hero-slide::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--lqip) center / cover no-repeat;
  filter: blur(18px); /* no scale: the crop must match the image's exactly */
}

.hero-slide__img {
  position: absolute;
  inset: 0;
  background: center / cover no-repeat;
  transition: opacity 0.7s ease;
}

.js .hero-slide__img {
  opacity: 0;
}

.js .hero-slide.is-loaded .hero-slide__img {
  opacity: 1;
}

/* Blocks rise in when scrolled into view; site.js staggers siblings */

.js .reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}

.js .reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* The service galleries were the one block on those pages with no arrival
   of its own: the opener's words moved, the cards at the foot moved, and
   the nine to sixteen photographs between them simply appeared. They rise
   in on the same curve now.

   Hung off the blur-up's own class rather than the scroll observer: the
   observer is set up before site.js rearranges the gallery into columns,
   and photographs that moved afterwards were left holding opacity 0. This
   cannot do that - the from-state exists only while the animation runs, so
   a photograph whose class never arrives is simply visible. Lazy loading
   gives the stagger, each one arriving as it is reached. */
@media (prefers-reduced-motion: no-preference) {
  .js .service-gallery .lqip.is-loaded,
  .js .service__gallery .lqip.is-loaded {
    animation: gallery-rise 0.6s var(--ease-soft) both;
  }
}

/* Deliberately no opacity: the blur-up already fades, and an animation
   that starts from opacity 0 leaves the photograph invisible if it never
   finishes - which a tab left in the background does. Stall this one and
   the picture is simply sitting 14px low. */
@keyframes gallery-rise {
  from { transform: translateY(14px); }
}

/* Slider caption panel eases in with its slide */

.hero-slide[aria-hidden="true"] @media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  main {
    animation: none;
  }

  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: none;
  }

  .garden-map .maplibregl-popup {
    animation: none;
  }

  .lightbox,
  .lightbox__img,
  .contact,
  .contact__panel {
    transition: none;
  }

  .js main img,
  .js .reveal,
    .lqip::before,
  .hero-slide__img,
  .hero-quote {
    transition: none;
    transform: none;
  }

  .cursor-tip__label,
  .card img,
  .nav-toggle,
  .nav-toggle .nav-toggle__bars,
  .nav-toggle .nav-toggle__bars::before,
  .nav-toggle .nav-toggle__bars::after {
    transition: none;
  }

  .nav,
  .nav a {
    animation: none;
  }

  .card:hover img {
    transform: none;
  }
}

/* Responsive - mirrors the original 900 / 745 / 450 breakpoints ------------ */

@media (max-width: 900px) {
  .nav-toggle {
    display: inline-flex;
    order: 1; /* after Contact: the menu button is the last thing on the right */
  }

  /* Full-screen takeover: the panel covers everything below the header. */
  .nav {
    position: fixed;
    inset: var(--header-h) 0 0;
    z-index: 90;
    display: flex;
    flex-direction: column;
    background: var(--bg);
    padding: var(--gutter);
    overflow-y: auto;
    overscroll-behavior: contain;
    animation: nav-fade 0.3s ease both;
  }

  /* Hidden until site.js opens it - and, with scripting present, hidden
     from the first paint too, so the panel never flashes as the page loads */
  .nav[hidden],
  .js .nav:not(.is-shown) {
    display: none;
  }

  /* On the way out the panel fades on the same curve (site.js holds it
     until the fade ends); the entries hold still rather than replay */
  .nav.is-closing {
    animation: nav-fade 0.22s ease both reverse;
    pointer-events: none;
  }

  .nav.is-closing a {
    animation: none;
  }

  /* Centred while it fits; once the list is taller than the panel (the
     services open) the auto margins give way and it scrolls from the top -
     align-items: center would have clipped the first entries. */
  .nav ul {
    flex-direction: column;
    align-items: center;
    gap: 0;
    width: 100%;
    margin: auto 0;
  }

  .nav li {
    width: 100%;
  }

  .nav a {
    display: block;
    padding: 0.5em 0;
    font-size: 1.8rem;
    font-weight: 300;
    --out-stroke: 0.55; /* the outbound arrow, as light as the type */
    line-height: 1.35;
    letter-spacing: 0;
    text-align: center;
  }

  /* The bag grows with the large type here, its stroke as light */
  .nav a .nav__bag {
    width: 0.9em;
    height: 0.9em;
    margin-left: 6px;
    vertical-align: -0.1em;
  }

  .nav a .nav__bag path {
    stroke-width: 0.8;
    color: var(--text-strong);
    border-radius: 0;
    /* Animation, not transition: it replays each time the panel is shown
       (display:none -> flex restarts animations, but not transitions). */
    animation: nav-item-in 0.45s cubic-bezier(0.2, 0.7, 0.2, 1) both;
  }

  .nav li:nth-child(1) a { animation-delay: 0.06s; }
  .nav li:nth-child(2) a { animation-delay: 0.12s; }
  .nav li:nth-child(3) a { animation-delay: 0.18s; }
  .nav li:nth-child(4) a { animation-delay: 0.24s; }
  .nav li:nth-child(5) a { animation-delay: 0.30s; }
  .nav li:nth-child(6) a { animation-delay: 0.36s; }
  .nav li:nth-child(7) a { animation-delay: 0.42s; }

  /* Services row: the word opens the areas beneath it. Collapsed until
     tapped; without scripting the list is simply shown. */
  .has-sub {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
  }

  .has-sub > a {
    width: auto;
  }

  .nav a.is-section {
    background: none;
    color: var(--text-strong);
  }

  /* The open panel: a quiet block between rules, two groups stacked */
  .nav .nav__sub {
    display: none;
    position: static;
    flex-basis: 100%;
    min-width: 0;
    margin: 6px auto 10px;
    padding: 0;
    border: 0;
    box-shadow: none;
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: none;
  }

  .has-sub.is-open .nav__sub,
  html:not(.js) .nav .nav__sub {
    display: flex;
    flex-direction: column; /* site.js eases the height open and shut */
  }

  .nav .nav__col {
    min-width: 0;
    width: 100%;
    padding: 10px 0 12px;
  }

  .nav .nav__col + .nav__col {
    border-left: 0;
  }

  .nav .nav__col ul {
    display: block;
  }

  /* The group headings: the small-caps label centred on a rule, as the
     row titles are */
  .nav .nav__sub .nav__sub-group {
    display: flex;
    align-items: center;
    gap: 24px;
    margin: 0 0 8px;
    padding: 0;
    font-size: var(--label-size); /* the dropdown's small caps, not the menu's size */
    font-weight: 600;
    text-align: center;
    white-space: nowrap;
    color: var(--text-strong);
    animation: none;
  }

  .nav .nav__sub .nav__sub-group::before,
  .nav .nav__sub .nav__sub-group::after {
    content: "";
    flex: 1;
    border-top: 1px solid var(--rule);
  }

  .nav__sub li {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .nav .nav__sub a {
    justify-content: center;
    margin: 0;
    padding: 0.28em 0;
    font-size: 1.15rem;
    font-weight: 300;
    color: var(--text-strong);
    animation: none;
  }

  .nav .nav__sub a:hover,
  .nav .nav__sub a[aria-current="page"] {
    color: var(--text-hover);
    background: none;
    padding-left: 0;
  }

  /* Beside the menu button, a touch tighter */
  .site-header > .wrap {
    gap: 14px;
  }

  .site-header .nav-contact {
    font-size: 0.9em;
  }

  /* The sheet becomes a tray: full width, sat on the bottom edge, rounded
     at the top, sliding up from below */
  .contact {
    padding: 0;
    align-items: end;
  }

  .contact__panel {
    width: 100%;
    max-height: calc(100% - 40px); /* a sliver of the page stays above it */
    padding: 22px 18px calc(22px + env(safe-area-inset-bottom));
    border-radius: 16px 16px 0 0;
    transform: translateY(100%);
  }

  @keyframes nav-fade {
    from { opacity: 0; }
  }

  @keyframes nav-item-in {
    from {
      opacity: 0;
      transform: translateY(16px);
    }
  }


  /* No hover pill at this size - it fights the large type */
  .nav a:hover,
  .nav a[aria-current="page"] {
    background: none;
    color: var(--text-hover);
  }

  .nav a[aria-current="page"] {
    font-weight: 400;
  }

  .gallery {
    column-count: 2;
  }
}

@media (max-width: 745px) {
  .module,
  .post_box,
  .post_box.quarter,
  .post_box.half,
  .post_box.threequarter {
    flex-basis: 100%;
  }

  .module p {
    margin-right: 0;
  }

  .people__track {
    gap: 16px;
  }

  /* Two-up hero slides: show the left-hand photograph whole rather than
     half of each. */
  .hero-slide--pair::before,
  .hero-slide--pair .hero-slide__img {
    background-position: left center;
  }

  .people__item {
    flex-basis: 85cqw;
  }

  .client-logos {
    grid-template-columns: repeat(2, 1fr);
    margin-inline: 0;
  }

  .awards {
    padding: 0;
    margin-top: 36px;
  }

  .awards__table {
    min-width: 560px; /* scrolls sideways inside .awards__scroll */
    font-size: 0.8125em;
  }

  .client-logos .logo-cell {
    min-height: 90px;
    padding: 16px 12px;
  }

  .office {
    height: 50vh;
  }

  .lightbox__figure {
    padding: 70px 16px 60px;
  }

  .lightbox__nav {
    top: auto;
    bottom: 60px;
    padding: 0 16px;
    transform: none;
  }

  .lightbox__close {
    right: 16px;
    top: 12px;
  }

  .office__text {
    padding: 20px;
  }

  .grid {
    grid-template-columns: 1fr;
  }

  :root {
    --gutter: 20px;
    --col-gap: 20px;
    --header-h: 58px; /* a little less above and below the logo on phones */
  }

  .wrap {
    padding-inline: 20px;
  }

  .hero-slider {
    height: 400px;
  }

  /* Shorter banner in portrait: `cover` crops far less of the picture's
     width, so more of each photograph is visible left and right. */
  .cover .hero-slider {
    height: 62vh;
    height: 62svh;
  }

  /* The grid sat 50px in from each edge on a 390px screen. */
  .wrap-wide {
    margin: 0 10px;
    padding: 16px 0;
  }

  /* Project pages: desktop's 40px wrapper plus 10px per block put the text
     50px in and left a 26px gulf between the two intro blocks. */
  .wrap-mid {
    padding: 20px;
  }

  .module {
    padding: 4px 0;
  }

  /* Photographs run edge to edge on a phone: one to a row, the wrapper's
     side padding given up so the pictures fill the screen; the rest of the
     page keeps its margins */
  .post_box,
  .post_box.threequarter,
  .post_box.half,
  .post_box.quarter {
    flex-basis: 100%; /* one photograph per row on a phone */
  }

  .wrap-mid:has(> .post_box) {
    padding-inline: 0;
    row-gap: 16px;
  }

  .card-grid,
  .card-grid--three,
  .card-grid--four {
    grid-template-columns: 1fr;
  }

  .social,
  .social.rightbox,
  .copy {
    width: 100%;
    padding: 10px 0;
    margin-left: 0;
    text-align: left;
  }

  /* Once the footer stacks, every line starts at the same left edge - the
     inline menu items carried a 10px left margin meant for the row layout. */
  .copy .inlineMenu {
    margin-left: 0;
    margin-right: 12px;
  }

  .rightbox a {
    padding: 4px 0;
  }

  .hero {
    text-align: left;
    padding: 10px;
  }

  /* The opening lines up with the logo and the sections, not 10px inside them */
  .hero:has(> .studio_hero--split) {
    padding-inline: 0;
  }

  .listResp {
    column-count: 2;
  }

  .list {
    display: block;
    float: none;
    width: 100%;
  }

  /* The caption is dropped on small screens - it wrapped to two lines and
     pushed the tray up over the photograph. Arrows and the button stay,
     side by side on one row. */
  .hero-bar {
    flex-direction: row;
    align-items: center;
    gap: 12px;
  }

  .hero-bar .flex-caption {
    width: auto;
    text-align: right;
    padding-bottom: 0;
  }

  .hero-bar .flex-caption__text {
    display: none;
  }

  .flex-caption__link {
    margin-left: 0;
  }
}

@media (max-width: 450px) {
  .hero-slider {
    height: 300px;
  }

  .cover .hero-slider {
    height: 58vh;
    height: 58svh;
  }

  .gallery {
    column-count: 1;
  }

  .wrap-mid {
    padding: 20px var(--gutter);
  }

  .site-footer {
    padding: 10px 0;
    line-height: 2.2;
  }

}
