/* ==========================================================================
   Paper Air — the house design language

   The system is Starting Point; the look it wears is Paper Air, and the two
   are separate on purpose: the system gains releases, the look gains
   refinements, and neither should be mistaken for the other.

   Influences, for talking about it: Apple's own type stack, concentric radii,
   the translucent capsule, tactile press. It is not a copy of anything — these
   greys, these hairlines, this voice.

   THE FIVE COMMANDMENTS
   1. NO ICONS in an action row, in navigation, or in a list of channels.
      Words in the menu voice scan faster than pictures that need decoding.
      An icon survives only where a word cannot do the job. `.acts` is the
      mechanism: put it on a row and its buttons become menu-voice pills with
      their icons hidden.
   2. THE MENU VOICE is the one costume for an action chip — 600 11.5px, .5px
      tracking, uppercase, 10px/15px, a full pill. Hover lifts by 1px; press
      scales to .95. Reduced motion stills both, always.
   3. Uppercase micro-pills are for ACTION CHIPS, never form submits. "Save"
      and "Add this date" stay sentence case in a standard .btn — capitals on
      a form shout.
   4. COLOUR MEANS SOMETHING. One filled button per surface: the thing
      somebody came to do. Everything else is neutral. A tone lives in a 5px
      dot, not a painted box, and the accent is never spent on a label.
   5. One component, one recipe. A surface that wants the look wears the
      shared class or copies the exact spec. It does not approximately match.

   Every value here is a token, and Admin → Look writes a handful of them per
   site. Nothing in this file needs editing to give a site its own colours,
   heading face or spacing.
   ========================================================================== */

/* Poppins is bundled rather than fetched, so no site built from this makes a
   request to a font host on every page load — which is a privacy question for
   a client site, not only a speed one. It is offered as a heading face in
   Admin → Look; it is deliberately not the body face any more. See --font.

   Its @font-face rules are LINKED from each layout, not @imported here. An
   @import is found only after this file has arrived and been parsed, which
   makes it a second request in series before anything can be drawn. */

/* --------------------------------------------------------------------------
   1. Tokens — light is the base, dark overrides only what changes.
   -------------------------------------------------------------------------- */
:root {
  color-scheme: light;

  /* Paper: warm off-whites rather than pure white, so the page reads as a
     printed sheet rather than a screen. */
  --paper:        #faf7f0;
  --paper-raised: #fffdf8;
  --paper-sunk:   #f2eee4;
  --paper-edge:   #e6e0d2;
  --paper-line:   #ded7c6;

  /* THE FOOTER HAS ITS OWN COLOUR, and deliberately not --paper-sunk.
     (Dominic, 29 Sept 2026: white in light, black in dark.)

     It used to share the recessed tone, which is also worn by code blocks,
     table headings, disabled fields and every hover state on the site — 39
     places. Making the footer white by moving that token would have taken all
     of them with it, and a hover the same colour as the page is not a hover.
     So the footer is its own token: one name, one job, changed without
     touching anything else.

     Note this runs against the rest of the palette on purpose. Everything
     else here is a warm off-white so the page reads as a printed sheet; the
     footer is now the one true white, which makes it advance rather than
     recede. That is the intended effect, not an oversight. */
  --footer-bg:    #ffffff;
  /* The hover tint inside the footer. It has to be a step away from
     --footer-bg, not from the page, or the hover disappears — which is what
     happened the moment the footer went white and black. */
  --footer-tint:  #f1efe9;

  /* Ink */
  --ink:          #24211c;
  --ink-soft:     #514a3f;
  --ink-muted:    #6a6355;   /* 5.6:1 on paper — readable, not just decorative */
  --ink-faint:    #736b5c;   /* 4.6:1 — the quietest text still has to be read */

  /* The organisation’s blue */
  --accent:       #0053c2;
  --accent-hover: #00429b;
  --accent-soft:  #e6eefc;
  --accent-ink:   #00306f;
  /* THE INK THAT SITS ON THE ACCENT, and it is not always white.
     A dark theme needs a lighter accent to lift off the page, and white text
     on a light blue is about 2.3:1 — a filled button nobody outside this room
     can read. So the ink flips with the theme: white on a dark fill, the page
     colour on a light one. Every filled button reaches for this rather than
     typing #fff, which is what let the dark theme ship unreadable. */
  --on-accent:    #ffffff;

  /* Supporting */
  --green:        #2c7a52;
  --green-soft:   #e3f1e9;
  --amber:        #a86a1a;
  --amber-soft:   #fbf0dd;
  --red:          #a83232;
  --red-soft:     #fbe7e5;

  /* THE KEYBOARD FOCUS RING — ink, not the accent.
     A ring has to reach 3:1 against what is behind it or a keyboard user
     cannot see where they are, and the accent is a colour each site chooses:
     a pale one fails outright. Ink measures about 15:1 on any page colour the
     contrast check will allow, and it is the one value that cannot be made
     wrong by a choice on the Look screen. */
  --focus:        var(--ink);

  /* THE BOUNDARY OF A CONTROL, and nothing else.
     WCAG 1.4.11 asks 3:1 of any visual boundary somebody needs to see in
     order to operate something. The hairline that separates a card is about
     1.2:1 on paper — decorative, and rightly so — but every text field and
     every radio was drawn in it too, which made them invisible rectangles to
     a low-vision visitor. This is for the things you have to aim at: inputs,
     textareas, selects, radios, checkboxes. IT IS NOT FOR CARDS — painting
     every hairline on the site this dark would wreck the look for no gain. */
  --edge-ctl:     #8b8577;

  /* Light greys. The paper palette is warm all the way through; the enquiry
     widget wants a cooler, quieter surface behind the conversation so the
     white bubbles sitting on it have something to lift off. */
  --grey-1:       #eeeef1;
  --grey-2:       #e2e2e6;
  --grey-3:       #cfcfd6;
  --grey-ink:     #6d6c70;

  /* Depth. Paper casts a soft, warm shadow — never a grey drop shadow. */
  --shadow-sm: 0 1px 2px rgba(60, 48, 30, .06), 0 1px 1px rgba(60, 48, 30, .04);
  --shadow:    0 2px 6px rgba(60, 48, 30, .07), 0 1px 2px rgba(60, 48, 30, .05);
  --shadow-lg: 0 12px 32px rgba(60, 48, 30, .10), 0 3px 8px rgba(60, 48, 30, .06);

  /* GEOMETRY — tightened for Paper Air.
     Was 6/10/16, which is a lot of curve at small sizes. Big radii read
     friendly and consumer; tighter ones read precise, which is what a site
     somebody runs their organisation from wants. --radius is the one Admin →
     Look writes, and the others follow from it, so a site that chooses softer
     corners gets them everywhere at once rather than in patches.

     CONCENTRIC RADII: when one rounded thing sits inside another, the inner
     radius is the outer radius minus the gap between them. --r-in does that
     arithmetic; use it rather than guessing, or the two curves fight. */
  --radius-sm: 8px;
  --radius:    12px;
  --radius-lg: 16px;
  --radius-xl: 22px;
  --radius-pill: 999px;
  --r-in: calc(var(--radius-lg) - var(--space-3));

  --space-1: calc(.25rem * var(--density));  --space-2: calc(.5rem * var(--density));
  --space-3: calc(.75rem * var(--density));  --space-4: calc(1rem * var(--density));
  --space-5: calc(1.5rem * var(--density));  --space-6: calc(2rem * var(--density));
  --space-7: calc(3rem * var(--density));    --space-8: calc(4rem * var(--density));
  --space-9: calc(6rem * var(--density));

  --measure: 68ch;
  /* What a line of body text widens to on a page that fills its column.
     68ch is the comfortable reading measure; this is the generous one, used
     where a page has pictures that want the room and the writing should not
     be left in a narrow strip beside them.

     In rem rather than ch on purpose. A ch is the width of a "0" in the
     element's OWN font, so 90ch on an <h2> comes out wider than the page —
     headings never cap and sit flush left while the paragraphs under them are
     centred. One fixed measure keeps a heading and its paragraph on the same
     left edge, which is the whole point. */
  --measure-wide: 61rem;
  --container: 1440px;
  --container-narrow: 760px;

  /* THE BODY FACE, and why it is what it is.

     Apple-native first: a Mac and an iPhone draw this in San Francisco, which
     is the single change that makes an interface read as belonging on the
     machine rather than having been dropped onto it. SF Pro cannot legally be
     self-hosted, so the stack is how a Mac gets it natively.

     Then Inter, for anyone who has it. Then POPPINS, which this site already
     carries — deliberately ahead of Segoe UI, because a Windows visitor
     falling through to Segoe gets the one face on this list that was not
     chosen. Poppins is bundled, so that costs no request.

     Body text is not offered in Admin → Look on purpose: a site can be tried
     in a different heading voice without anybody being able to make it hard to
     read. */
  --font: -apple-system, BlinkMacSystemFont, 'Inter', 'Poppins', 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  /* HOW BOLD A BOLD IS.
     An inline <b> at 700 against body copy reads as shouting. 530 is
     unmistakably emphasis and stops there; 500 starts to disappear. --w-lab is
     a <b> being used as a small block label, which needs a little more to hold
     a line on its own. Use the tokens; do not type a number. */
  --w-em:  530;
  --w-lab: 600;

  /* THE TYPE SCALE. Seven steps and nothing below 13px.
     A stylesheet with twenty-five hand-typed font sizes is not a design, it is
     twenty-five decisions taken on twenty-five different days — and a wall of
     competing small text is what makes a page feel busy. If something wants a
     size that is not here, the honest fix is almost always the nearest step. */
  --fs-fine:  .8125rem;   /* legal, footnotes, timestamps — the floor       */
  --fs-sm:    .9rem;      /* nav, meta, captions, card sub-text             */
  --fs-lead:  clamp(1.08rem, 1rem + .34vw, 1.3rem);   /* intros, standfirsts */
  --fs-display: clamp(2.4rem, 1.8rem + 3vw, 3.4rem);  /* a price, a big stat */

  /* Headings have tokens of their own so one site can wear a different face
     without the body text moving. Admin → Look writes these; the stylesheet
     never needs editing for it. --heading-scale multiplies every heading size
     at once, so a face that runs small can be brought up in one number. */
  --heading-font:    var(--font);
  --heading-weight:  600;
  --heading-tracking: -.018em;
  --heading-scale:   1;

  /* THE AIR IN PAPER AIR: one number for how much room the page gets, and
     everything else multiplied by it. Air is the cheapest premium signal
     there is, and the clamps mean the extra room appears on a desktop and
     quietly goes away on a phone, where it would only mean more scrolling. */
  --density: 1;
  --sp-section: calc(clamp(64px, 7vw, 104px) * var(--density));
  --sp-block:   calc(clamp(28px, 3vw, 44px) * var(--density));
  --font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;

  --header-h: 68px;
  --ease: cubic-bezier(.4, 0, .2, 1);
}

:root[data-theme="dark"] {
  color-scheme: dark;

  /* Dark paper: a warm charcoal, not a blue-black, so it still reads as paper. */
  --paper:        #171512;
  --paper-raised: #201d19;
  --paper-sunk:   #121110;
  --paper-edge:   #322d26;
  --paper-line:   #3d372e;
  --footer-bg:    #000000;
  --footer-tint:  #1c1c1c;

  --ink:          #f0ebe1;
  --ink-soft:     #ccc4b6;
  --ink-muted:    #a9a190;
  --ink-faint:    #968e7e;

  --accent:       #6aa5ff;
  --accent-hover: #8ebcff;
  --accent-soft:  #10203c;
  --accent-ink:   #b9d3ff;
  /* Ink on the accent, not white: #6aa5ff carries near-black at 8.9:1 and
     white at 2.3. See --on-accent above. */
  --on-accent:    #10131a;

  --green:        #6bbd8f;
  --green-soft:   #17281f;
  --amber:        #d9a24e;
  --amber-soft:   #2c2318;
  --red:          #e08a80;
  --red-soft:     #2e1c1a;

  --focus:        var(--ink);
  /* Lighter than its light-mode twin for the same reason: 3:1 against a dark
     field, not against a pale one. */
  --edge-ctl:     #7d776c;

  --grey-1:       #1c1a17;
  --grey-2:       #26231f;
  --grey-3:       #3a352d;
  --grey-ink:     #9a9287;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .3);
  --shadow:    0 2px 8px rgba(0, 0, 0, .35);
  --shadow-lg: 0 14px 36px rgba(0, 0, 0, .45);
}

/* Respect the operating system when the visitor has expressed no preference. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --paper:        #171512;
    --paper-raised: #201d19;
    --paper-sunk:   #121110;
    --paper-edge:   #322d26;
    --paper-line:   #3d372e;
    --footer-bg:    #000000;
    --footer-tint:  #1c1c1c;
    --ink:          #f0ebe1;
    --ink-soft:     #ccc4b6;
    --ink-muted:    #a9a190;
    --ink-faint:    #968e7e;
    --accent:       #6aa5ff;
    --accent-hover: #8ebcff;
    --accent-soft:  #10203c;
    --accent-ink:   #b9d3ff;
    --on-accent:    #10131a;
    --green:        #6bbd8f;  --green-soft: #17281f;
    --amber:        #d9a24e;  --amber-soft: #2c2318;
    --red:          #e08a80;  --red-soft:   #2e1c1a;
    --focus:        var(--ink);
    --edge-ctl:     #7d776c;
    --grey-1: #1c1a17;  --grey-2: #26231f;  --grey-3: #3a352d;  --grey-ink: #9a9287;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .3);
    --shadow:    0 2px 8px rgba(0, 0, 0, .35);
    --shadow-lg: 0 14px 36px rgba(0, 0, 0, .45);
  }
}

/* --------------------------------------------------------------------------
   2. Reset and base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

/* Several components below set `display`, which would otherwise win against
   the browser default for [hidden]. Anything marked hidden stays hidden. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  font-family: var(--font);
  /* A little larger on a wide screen, where the reader is further away and
     the measure is longer. */
  font-size: clamp(1rem, .97rem + .16vw, 1.075rem);
  line-height: 1.68;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;

  /* The faintest paper grain. Two tiny gradients, no image request. */
  background-image:
    radial-gradient(circle at 15% 12%, rgba(140, 120, 80, .028), transparent 45%),
    radial-gradient(circle at 85% 78%, rgba(90, 120, 140, .022), transparent 42%);
  background-attachment: fixed;
}

h1, h2, h3, h4, h5, h6 {
  margin: 0 0 var(--space-4);
  font-family: var(--heading-font);
  font-weight: var(--heading-weight);
  line-height: 1.22;
  letter-spacing: var(--heading-tracking);
  color: var(--ink);
  text-wrap: balance;
}
/* A big heading wants tighter tracking and a tighter leading than a small one:
   at 3rem, 1.22 line-height opens a gap you can drive a bus through. These are
   the display sizes, so they get the display treatment. */
h1 { font-size: calc(clamp(2rem, 1.4rem + 2.6vw, 3.1rem) * var(--heading-scale)); font-weight: calc(var(--heading-weight) + 100); letter-spacing: calc(var(--heading-tracking) - .008em); line-height: 1.07; }
h2 { font-size: calc(clamp(1.6rem, 1.24rem + 1.6vw, 2.35rem) * var(--heading-scale)); letter-spacing: calc(var(--heading-tracking) - .004em); line-height: 1.12; }
h3 { font-size: calc(clamp(1.2rem, 1.08rem + .6vw, 1.5rem) * var(--heading-scale)); }
h4 { font-size: calc(1.1rem * var(--heading-scale)); }
h5, h6 { font-size: calc(1rem * var(--heading-scale)); }

p  { margin: 0 0 var(--space-4); text-wrap: pretty; }
a  { color: var(--accent); text-decoration-color: color-mix(in srgb, var(--accent) 35%, transparent); text-underline-offset: .18em; transition: color .15s var(--ease); }
a:hover { color: var(--accent-hover); text-decoration-color: currentColor; }

/* Emphasis, not shouting. See --w-em. */
strong, b { font-weight: var(--w-em); }
small { font-size: var(--fs-fine); }

img, video, svg, iframe { max-width: 100%; height: auto; }
img { border-radius: var(--radius); display: block; }

hr { border: 0; border-top: 1px solid var(--paper-line); margin: var(--space-6) 0; }

blockquote {
  margin: var(--space-5) 0;
  padding: var(--space-4) var(--space-5);
  background: var(--paper-sunk);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  color: var(--ink-soft);
}
blockquote p:last-child { margin-bottom: 0; }

code, pre { font-family: var(--font-mono); font-size: .9em; }
code { background: var(--paper-sunk); padding: .15em .4em; border-radius: var(--radius-sm); }
pre { background: var(--paper-sunk); padding: var(--space-4); border-radius: var(--radius); overflow-x: auto; border: 1px solid var(--paper-edge); }
pre code { background: none; padding: 0; }

ul, ol { margin: 0 0 var(--space-4); padding-left: 1.35em; }
li { margin-bottom: .4em; }
li::marker { color: var(--ink-faint); }

/* Every interactive thing gets a visible focus ring. The plugins this
   replaces set outline:none on inputs, which fails WCAG outright. */
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--accent); color: var(--on-accent); padding: var(--space-3) var(--space-4);
  border-radius: 0 0 var(--radius) 0; font-weight: 600;
}
.skip-link:focus { left: 0; }

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

/* --------------------------------------------------------------------------
   3. Layout
   -------------------------------------------------------------------------- */
.container { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: var(--space-5); }
.row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); }
@media (max-width: 640px) { .row-2 { grid-template-columns: 1fr; } }
.container-narrow { max-width: var(--container-narrow); }
.section { padding-block: clamp(var(--space-6), 6vw, calc(var(--space-8) * 1.35)); }
.section-sunk { background: var(--paper-sunk); border-block: 1px solid var(--paper-edge); }

.stack > * + * { margin-top: var(--space-4); }
.grid { display: grid; gap: var(--space-5); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.cluster { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; }
.between { display: flex; flex-wrap: wrap; gap: var(--space-4); align-items: center; justify-content: space-between; }

/* The content column fills the width it is given. Pictures, figures, tables
   and anything else laid out as a block run the whole of it, and the writing
   widens to a generous measure and centres under them.

   It used to be the other way round: everything was capped at the reading
   measure and pinned to the left, so a page with photographs on it showed
   734px of content and 680px of empty paper beside it, and looked like a page
   whose right-hand column had failed to load. The rule the people and partner
   pages already followed — wording at a readable width, the grid gets the
   whole column — is simply the rule for every page now. */
.prose { max-width: none; margin-inline: auto; }
.prose > p,
.prose > h1, .prose > h2, .prose > h3, .prose > h4, .prose > h5, .prose > h6,
.prose > ul, .prose > ol, .prose > dl,
.prose > blockquote, .prose > details {
  max-width: var(--measure-wide);
  margin-inline: auto;
}
.prose > h2 { margin-top: var(--space-7); }
.prose > h3 { margin-top: var(--space-6); }
/* The picture at the head of a page or a post: one shape, filled. */
.page-shot {
  width: 100%; aspect-ratio: 16 / 9; object-fit: cover;
  margin-bottom: var(--space-6); box-shadow: var(--shadow); border-radius: var(--radius-lg);
}
.prose img { margin-block: var(--space-5); box-shadow: var(--shadow); }
/* A picture that arrived with no size attribute is shown at its own size, up
   to the width of the column. Forcing 3/2 and cover did not reserve space
   usefully — the browser does that itself the moment the file's own
   dimensions arrive — it just cropped whatever was not landscape and blew
   small pictures up past their real size. max-width and height:auto come
   from the img rule near the top of this file. */
.prose img:not([width]) { width: auto; }
.prose figure { margin: var(--space-5) 0; }
/* The figure spans the column, but a picture smaller than the column is not
   stretched to fill it — blowing a 480px photograph up to 1400 only makes it
   soft. It sits centred instead, on the same axis as the writing, rather than
   pinned to the left edge with a field of paper beside it. */
.prose figure > img, .prose figure > picture { margin-inline: auto; }
.prose figure > picture { display: block; }
.prose figure > picture > img { margin-inline: auto; }
.prose figcaption { font-size: .875rem; color: var(--ink-muted); margin-top: var(--space-2); }
.prose table { width: 100%; border-collapse: collapse; margin: var(--space-5) 0; }
.prose th, .prose td { padding: var(--space-3); border-bottom: 1px solid var(--paper-line); text-align: left; }
.prose th { font-weight: 600; background: var(--paper-sunk); }
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* --------------------------------------------------------------------------
   4. The paper card — the motif the whole site is built from
   -------------------------------------------------------------------------- */
.card {
  background: var(--paper-raised);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-5);
  transition: box-shadow .2s var(--ease), transform .2s var(--ease), border-color .2s var(--ease);
}
.card-link:hover, a.card:hover { box-shadow: var(--shadow-lg); transform: translateY(-2px); border-color: color-mix(in srgb, var(--accent) 30%, var(--paper-edge)); }
.card-flush { padding: 0; overflow: hidden; }
.card-body { padding: var(--space-5); }

.eyebrow {
  display: inline-flex; align-items: center; gap: .4rem;
  font-size: .72rem; font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: var(--accent); margin-bottom: var(--space-3);
}
.lead { font-size: clamp(1.08rem, 1rem + .34vw, 1.24rem); color: var(--ink-soft); line-height: 1.6; max-width: 62ch; }
.muted { color: var(--ink-muted); }
.faint { color: var(--ink-faint); }

/* --------------------------------------------------------------------------
   5. Buttons
   -------------------------------------------------------------------------- */
/* THE BUTTON. A capsule, 530 weight, and a press you can feel.

   ONE FILLED BUTTON PER SURFACE. .btn-primary is the thing somebody came to
   do; everything else on the screen is neutral and quiet. Two filled buttons
   beside each other is two answers to "what do I press", which is the same as
   none — and it spends the accent, which then means nothing anywhere else.

   Sentence case, always. Capitals belong to the action chips in .acts, never
   to a form submit: SAVE shouts and Save does not. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  padding: .7rem 1.35rem;
  font: inherit; font-weight: var(--w-em); font-size: .95rem; line-height: 1.2;
  letter-spacing: -.012em;
  border: 1px solid transparent; border-radius: var(--radius-pill);
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: background .15s var(--ease), border-color .15s var(--ease),
              transform .12s var(--ease), box-shadow .15s var(--ease), opacity .14s var(--ease);
}
/* Tactile press. Scale rather than a nudge downwards: a button that moves down
   under the finger looks pressed; one that also shrinks feels pressed. */
.btn:active { transform: scale(.97); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .55; cursor: not-allowed; transform: none; }

/* The accent button takes its lift from a tinted shadow rather than a
   gradient: a gradient on a capsule this size reads as a button from 2014. */
.btn-primary { background: var(--accent); color: var(--on-accent);
  box-shadow: 0 2px 8px color-mix(in srgb, var(--accent) 16%, transparent),
              0 10px 30px -8px color-mix(in srgb, var(--accent) 16%, transparent); }
.btn-primary:hover { background: var(--accent-hover); color: var(--on-accent); }

.btn-secondary { background: var(--paper-raised); color: var(--ink); border-color: var(--paper-line); }
.btn-secondary:hover { background: var(--paper-sunk); color: var(--ink); border-color: var(--ink-faint); }

/* Ink on paper: the inverse of everything around it, which is what makes it
   the one thing on a pale band you can press without spending the accent. */
.btn-ink { background: var(--ink); color: var(--paper); }
.btn-ink:hover { background: var(--ink); color: var(--paper); opacity: .86; transform: translateY(-1px); }

.btn-ghost { background: transparent; color: var(--ink-soft); }
.btn-ghost:hover { background: var(--paper-sunk); color: var(--ink); }

.btn-whatsapp { background: #1f8a4c; color: #fff; }
.btn-whatsapp:hover { background: #1a7541; color: #fff; }

.btn-danger { background: var(--red); color: #fff; }
.btn-danger:hover { filter: brightness(.92); color: #fff; }

.btn-sm { padding: .4rem .8rem; font-size: .85rem; }
.btn-lg { padding: .9rem 1.8rem; font-size: 1.05rem; }
/* The second button beside a filled one.

   It was glass — white on 12% white, for use over a photograph — and
   wherever it landed on paper instead it measured about 1.2:1 and could not
   be read at all. The cards module uses it four times on plain pages, which
   is where that showed. So the glass is kept for the places it was drawn
   for, and named: the hero, and a call-to-action panel wearing a colour or
   the ink. Everywhere else it is an ordinary outlined button. */
.btn-quiet {
  background: transparent; color: var(--ink);
  border-color: var(--edge-ctl);
}
.btn-quiet:hover { background: var(--paper-sunk); color: var(--ink); border-color: var(--ink-faint); }

.hero .btn-quiet,
.on-dark .btn-quiet,
.cta-panel.is-accent .btn-quiet,
.cta-panel.is-dark .btn-quiet {
  background: rgba(255, 255, 255, .12); color: #fff;
  border-color: rgba(255, 255, 255, .55);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
.hero .btn-quiet:hover,
.on-dark .btn-quiet:hover,
.cta-panel.is-accent .btn-quiet:hover,
.cta-panel.is-dark .btn-quiet:hover { background: rgba(255, 255, 255, .22); color: #fff; border-color: #fff; }
.btn-block { width: 100%; }

.icon { flex: none; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; vertical-align: -.15em; }

/* --------------------------------------------------------------------------
   6. Forms
   -------------------------------------------------------------------------- */
.field { margin-bottom: var(--space-4); }
.field > label, .label {
  display: block; margin-bottom: var(--space-2);
  font-weight: 600; font-size: .9rem; color: var(--ink-soft);
}
.hint { font-size: .82rem; color: var(--ink-muted); margin-top: var(--space-2); }
/* What a field is complaining about, said next to the field. */
.field-error { font-size: .82rem; color: var(--red); margin-top: var(--space-2); font-weight: 600; }
/* A column of ticks — the parts of the site somebody looks after. */
.check-stack { display: grid; gap: var(--space-3); }
.check-stack .check span { font-size: .9rem; }
/* The honeypot: off-screen for people, filled in by robots. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.error-text { font-size: .85rem; color: var(--red); margin-top: var(--space-2); font-weight: 500; }

input[type="text"], input[type="email"], input[type="password"], input[type="tel"],
input[type="url"], input[type="search"], input[type="number"], input[type="date"],
input[type="datetime-local"], input[type="time"], select, textarea {
  width: 100%;
  padding: .65rem .85rem;
  font: inherit; font-size: .95rem;
  color: var(--ink);
  background: var(--paper-raised);
  /* The boundary of a control, not a decorative hairline — see --edge-ctl.
     These are the things somebody has to aim at. */
  border: 1px solid var(--edge-ctl);
  border-radius: var(--radius);
  transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
input:hover:not(:disabled), select:hover:not(:disabled), textarea:hover:not(:disabled) { border-color: var(--ink-faint); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
input[aria-invalid="true"], .has-error input, .has-error textarea { border-color: var(--red); }
textarea { min-height: 8rem; resize: vertical; line-height: 1.6; }
select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%23857c6c' stroke-width='1.75' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right .85rem center;
  padding-right: 2.4rem;
}
input:disabled, select:disabled, textarea:disabled { background: var(--paper-sunk); color: var(--ink-muted); cursor: not-allowed; }

.check { display: flex; gap: var(--space-3); align-items: flex-start; cursor: pointer; }
.check input[type="checkbox"], .check input[type="radio"] {
  flex: none; width: 1.15rem; height: 1.15rem; margin-top: .18rem; accent-color: var(--accent); cursor: pointer;
  /* Same reasoning as the fields: a tick box drawn in the card hairline is a
     box a low-vision visitor cannot find. */
  outline-color: var(--edge-ctl);
}
.check span { font-size: .93rem; color: var(--ink-soft); }

fieldset { border: 1px solid var(--paper-edge); border-radius: var(--radius); padding: var(--space-4); margin: 0 0 var(--space-4); }
legend { padding-inline: var(--space-2); font-weight: 600; font-size: .9rem; color: var(--ink-soft); }

/* Chip-style multi-select, used for the enquiry topics */
.chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.chip { position: relative; }
.chip input { position: absolute; opacity: 0; width: 0; height: 0; }
.chip span {
  display: inline-block; padding: .5rem 1rem;
  font-size: .9rem; font-weight: 500;
  background: var(--paper-raised); color: var(--ink-soft);
  border: 1px solid var(--paper-line); border-radius: var(--radius-pill);
  cursor: pointer; transition: all .15s var(--ease); user-select: none;
}
.chip span:hover { border-color: var(--accent); color: var(--accent); }
.chip input:checked + span { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.chip input:focus-visible + span { outline: 2px solid var(--focus); outline-offset: 2px; }

/* --------------------------------------------------------------------------
   7. Badges, pills and notices
   -------------------------------------------------------------------------- */
.badge {
  display: inline-flex; align-items: center; gap: .35em;
  padding: .22rem .6rem; font-size: .76rem; font-weight: 600;
  letter-spacing: .01em; border-radius: var(--radius-pill);
  background: var(--paper-sunk); color: var(--ink-soft); border: 1px solid var(--paper-edge);
  white-space: nowrap;
}
.badge-accent { background: var(--accent-soft); color: var(--accent-ink); border-color: color-mix(in srgb, var(--accent) 25%, transparent); }
.badge-green  { background: var(--green-soft); color: var(--green); border-color: color-mix(in srgb, var(--green) 25%, transparent); }
.badge-amber  { background: var(--amber-soft); color: var(--amber); border-color: color-mix(in srgb, var(--amber) 25%, transparent); }
.badge-red    { background: var(--red-soft); color: var(--red); border-color: color-mix(in srgb, var(--red) 25%, transparent); }

.notice {
  display: flex; gap: var(--space-3); align-items: flex-start;
  padding: var(--space-4);
  border: 1px solid var(--paper-edge); border-left-width: 3px;
  border-radius: var(--radius); background: var(--paper-raised);
  margin-bottom: var(--space-4);
}
.notice p:last-child { margin-bottom: 0; }
.notice .icon { margin-top: .18rem; }
.notice-success { border-left-color: var(--green); background: var(--green-soft); color: var(--green); }
.notice-error   { border-left-color: var(--red);   background: var(--red-soft);   color: var(--red); }
.notice-warn    { border-left-color: var(--amber); background: var(--amber-soft); color: var(--amber); }
.notice-info    { border-left-color: var(--accent); background: var(--accent-soft); color: var(--accent-ink); }
.notice strong { color: inherit; }

/* --------------------------------------------------------------------------
   8. Site header and footer
   -------------------------------------------------------------------------- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--paper-raised) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(12px);
  -webkit-backdrop-filter: saturate(1.4) blur(12px);
  border-bottom: 1px solid var(--paper-edge);
}
.site-header .container { display: flex; align-items: center; gap: var(--space-4); min-height: var(--header-h); }

.brand { display: flex; align-items: center; gap: var(--space-3); text-decoration: none; color: var(--ink); font-weight: 700; letter-spacing: -.02em; }
.brand:hover { color: var(--ink); }
.brand-mark { flex: none; width: 38px; height: 38px; border-radius: 50%; background: var(--accent); display: grid; place-items: center; color: var(--on-accent); }
.brand-name { font-size: 1.02rem; line-height: 1.15; }
.brand-sub { display: block; font-size: .72rem; font-weight: 500; color: var(--ink-muted); letter-spacing: .04em; }

.nav { display: flex; align-items: center; gap: 2px; margin-left: auto; }
.nav a {
  padding: .5rem .7rem; font-size: .9rem; font-weight: 500; white-space: nowrap;
  color: var(--ink-soft); text-decoration: none; border-radius: var(--radius-sm);
  transition: background .15s var(--ease), color .15s var(--ease);
}
.nav a:hover { background: var(--paper-sunk); color: var(--ink); }
.nav a[aria-current="page"] { color: var(--accent); background: var(--accent-soft); }

.theme-toggle {
  flex: none; width: 38px; height: 38px; display: grid; place-items: center;
  background: transparent; border: 1px solid var(--paper-line); border-radius: var(--radius);
  color: var(--ink-soft); cursor: pointer; transition: all .15s var(--ease);
}
.theme-toggle:hover { background: var(--paper-sunk); color: var(--ink); border-color: var(--ink-faint); }
.theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-sun { display: block; }
  :root:not([data-theme="light"]) .theme-toggle .icon-moon { display: none; }
}

/* The language switcher in the menu bar, on a site in more than one
   language. Closed it is a globe and the language's two letters; open it is
   the list of languages, each written in its own language. (v1.19) */
.lang-switch { position: relative; flex: none; }
.lang-switch-btn {
  display: inline-flex; align-items: center; gap: .35rem; height: 38px; padding: 0 .6rem;
  background: transparent; border: 1px solid var(--paper-line); border-radius: var(--radius);
  color: var(--ink-soft); font: inherit; font-size: .82rem; font-weight: 600;
  letter-spacing: .04em; cursor: pointer; transition: all .15s var(--ease);
}
.lang-switch-btn:hover { background: var(--paper-sunk); color: var(--ink); border-color: var(--ink-faint); }
.lang-switch-menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 60; min-width: 10rem;
  margin: 0; padding: .3rem; list-style: none; display: none;
  background: var(--paper); border: 1px solid var(--paper-line);
  border-radius: var(--radius); box-shadow: var(--shadow-2, 0 8px 24px rgba(0,0,0,.12));
}
.lang-switch[data-open] .lang-switch-menu { display: block; }
.lang-switch-menu a {
  display: block; padding: .45rem .6rem; font-size: .9rem; border-radius: var(--radius-sm);
  color: var(--ink-soft); text-decoration: none;
}
.lang-switch-menu a:hover { background: var(--paper-sunk); color: var(--ink); }
.lang-switch-menu a[aria-current="true"] { color: var(--accent); background: var(--accent-soft); }


/* --------------------------------------------------------------------------
   The scrolling banner (v1.28.0)

   Two identical lists slide left together by half the track. When the first
   has gone, the second is exactly where the first started, so it reads as one
   line going round rather than a list that restarts.

   It stops on hover — a line somebody is trying to read should not run away
   from them — and for anybody who has asked their machine for less movement it
   does not scroll at all: the same items, wrapped, standing still.
   -------------------------------------------------------------------------- */
.ticker {
  overflow: hidden;
  border-bottom: 1px solid var(--paper-line);
  font-size: .88rem;
}
.ticker-track { display: flex; width: max-content; animation: ticker-slide var(--ticker-speed, 40s) linear infinite; }
.ticker:hover .ticker-track, .ticker:focus-within .ticker-track { animation-play-state: paused; }
.ticker-list {
  display: flex; align-items: center; gap: var(--space-6);
  margin: 0; padding: .55rem var(--space-6) .55rem 0; list-style: none; white-space: nowrap;
}
.ticker-list li { display: flex; align-items: center; gap: var(--space-3); }
.ticker-list li::after { content: '·'; opacity: .45; }
.ticker-list a { color: inherit; text-decoration: none; border-bottom: 1px solid currentColor; }
.ticker-list a:hover { opacity: .8; }

@keyframes ticker-slide { from { transform: translateX(0); } to { transform: translateX(-50%); } }

.ticker-ink    { background: var(--ink); color: var(--paper); border-bottom-color: transparent; }
.ticker-accent { background: var(--accent); color: var(--on-accent); border-bottom-color: transparent; }
.ticker-quiet  { background: var(--paper); color: var(--ink-soft); }

@media (prefers-reduced-motion: reduce) {
  .ticker-track { animation: none; width: 100%; }
  .ticker-list { flex-wrap: wrap; white-space: normal; }
  .ticker-list[aria-hidden="true"] { display: none; }
}

.nav-toggle { display: none; }

.site-footer {
  margin-top: var(--space-8);
  padding-block: var(--space-7) var(--space-5);
  background: var(--footer-bg, var(--paper-sunk));
  border-top: 1px solid var(--paper-edge);
  font-size: .92rem;
}
.site-footer h3 { font-size: .8rem; text-transform: uppercase; letter-spacing: .09em; color: var(--ink-muted); margin-bottom: var(--space-3); }
.site-footer ul { list-style: none; padding: 0; margin: 0; }
.site-footer li { margin-bottom: var(--space-2); }
/* A footer link is an icon and a label, so it should break as one thing. As
   plain inline the label wrapped to the next line and left the icon stranded
   on the line above — at 1024px, 768px and 480px on this site. The space in
   the markup between the two is a white-space-only text node, which a flex
   container drops, so the gap below is the only spacing. */
.site-footer a { color: var(--ink-soft); text-decoration: none;
  display: inline-flex; align-items: baseline; gap: .45em; }
.site-footer a svg { flex: none; align-self: center; }
.site-footer a:hover { color: var(--accent); }
/* Three things on one line: the organisation's copyright, who built it, and the way
   in. The credit sits in the middle and stays quiet. */
.footer-credit { color: var(--ink-faint); }
/* The version, quieter still than the credit beside it: there for whoever
   needs it, invisible to everybody who does not. */
.footer-version { color: var(--ink-faint); font-variant-numeric: tabular-nums; opacity: .75;
  display: inline-flex; align-items: center; gap: .5rem; }
.footer-version form { display: inline; }
.footer-version button { display: inline-flex; align-items: center; gap: .3rem;
  font: inherit; font-size: .82em; color: inherit; cursor: pointer;
  background: none; border: 1px solid var(--paper-edge); border-radius: 999px;
  padding: .12rem .6rem; line-height: 1.6; }
.footer-version button:hover { color: var(--accent); border-color: currentColor; }
.footer-credit a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--paper-edge); }
.footer-credit a:hover { color: var(--accent); border-bottom-color: currentColor; }
.footer-bottom {
  margin-top: var(--space-6); padding-top: var(--space-4);
  border-top: 1px solid var(--paper-edge);
  display: flex; flex-wrap: wrap; gap: var(--space-4);
  justify-content: space-between; color: var(--ink-muted); font-size: .85rem;
}

/* --------------------------------------------------------------------------
   9. Hero
   -------------------------------------------------------------------------- */
/* The banner itself is described in section 11, with the rest of the top of
   the site. What is left here is the two-column block the enquiry page uses,
   which shares the name but nothing else. */
.hero-grid { display: grid; gap: clamp(var(--space-5), 4vw, var(--space-7)); align-items: center; grid-template-columns: 1fr; }
@media (min-width: 900px) { .hero-grid { grid-template-columns: 1.05fr .95fr; } }

.banner-strip {
  background: var(--accent); color: var(--on-accent);
  padding: var(--space-2) 0; font-size: .9rem; text-align: center;
}
.banner-strip a { color: #fff; font-weight: 600; }
.banner-strip p { margin: 0; }

/* --------------------------------------------------------------------------
   10. The button set (rebuilt link manager)
   -------------------------------------------------------------------------- */
.link-groups { display: grid; gap: var(--space-6); }
.link-group-title {
  display: flex; align-items: center; gap: var(--space-3);
  font-size: 1.05rem; font-weight: 600; margin-bottom: var(--space-4);
  padding-left: var(--space-3); border-left: 4px solid var(--group-colour, var(--accent));
}
.link-row { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: var(--space-3); }
.link-button {
  display: flex; flex-direction: column; gap: .15rem;
  padding: var(--space-4);
  background: var(--paper-raised); color: var(--ink);
  border: 1px solid var(--paper-edge); border-top: 3px solid var(--group-colour, var(--accent));
  border-radius: var(--radius); text-decoration: none;
  box-shadow: var(--shadow-sm);
  transition: transform .18s var(--ease), box-shadow .18s var(--ease), border-color .18s var(--ease);
}
.link-button:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); color: var(--ink); }
.link-button .date { font-size: .8rem; font-weight: 600; color: var(--group-colour, var(--accent)); letter-spacing: .02em; }
.link-button .label { font-weight: 500; line-height: 1.35; color: inherit; margin: 0; }
.link-button .icon { margin-top: var(--space-2); color: var(--ink-faint); align-self: flex-end; }

/* --------------------------------------------------------------------------
   11. The enquiry widget — the rebuilt WhatsApp hero
   -------------------------------------------------------------------------- */
.enquiry {
  background: var(--paper-raised);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.enquiry-head {
  padding: var(--space-5);
  background: linear-gradient(135deg, var(--accent), color-mix(in srgb, var(--accent) 70%, #1b4a5e));
  color: #fff;
}
.enquiry-head h2 { color: #fff; margin-bottom: var(--space-2); font-size: 1.35rem; }
.enquiry-head p { margin: 0; opacity: .92; font-size: .95rem; }
.enquiry-body { padding: var(--space-5); }

.enquiry-price {
  display: inline-flex; align-items: baseline; gap: .3rem;
  padding: .3rem .8rem; margin-bottom: var(--space-4);
  background: var(--green-soft); color: var(--green);
  border-radius: var(--radius-pill); font-weight: 600; font-size: .9rem;
}

.enquiry-step { display: none; }
.enquiry-step.is-active { display: block; animation: fade-up .35s var(--ease); }
@keyframes fade-up { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

.enquiry-progress { display: flex; gap: var(--space-2); margin-bottom: var(--space-5); }
.enquiry-progress i {
  flex: 1; height: 3px; border-radius: 2px; background: var(--paper-line); transition: background .3s var(--ease);
}
.enquiry-progress i.done { background: var(--accent); }

.enquiry-success { text-align: center; padding: var(--space-5) 0; }
.enquiry-success .tick {
  width: 56px; height: 56px; margin: 0 auto var(--space-4);
  border-radius: 50%; background: var(--green-soft); color: var(--green);
  display: grid; place-items: center;
}
/* The step that actually delivers the enquiry. It is given the weight of an
   instruction rather than of a spare link, because on a phone this tap is
   what puts the message in the organisation's inbox. */
.enquiry-send {
  margin: var(--space-5) 0 var(--space-4);
  padding: var(--space-4);
  background: var(--paper-sunk);
  border-radius: var(--radius);
}
.enquiry-send-label {
  margin: 0 0 var(--space-3);
  font-size: .78rem; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-faint);
}
.enquiry-wa-btn { display: inline-flex; align-items: center; gap: .5rem; }
@media (max-width: 520px) { .enquiry-wa-btn { display: flex; width: 100%; justify-content: center; } }
.enquiry-send-note {
  margin: var(--space-3) 0 0; font-size: .88rem; line-height: 1.5; color: var(--ink-soft);
}
.enquiry-blocked {
  display: flex; align-items: flex-start; gap: .45rem;
  margin: var(--space-3) 0 0; padding: var(--space-3);
  background: var(--paper); border-radius: var(--radius-sm);
  font-size: .86rem; line-height: 1.5; text-align: left; color: var(--ink-soft);
}
.enquiry-blocked .icon { flex: none; margin-top: 2px; color: var(--amber); }
/* Only when the browser blocked the tab: a slow, quiet ring, so the eye goes
   to the button without the page starting to flash at anybody. */
.enquiry-wa-btn.is-waiting { animation: wa-waiting 2.4s ease-in-out 3; }
@keyframes wa-waiting {
  0%, 100% { box-shadow: 0 0 0 0 rgba(31, 138, 76, 0); }
  50%      { box-shadow: 0 0 0 6px rgba(31, 138, 76, .18); }
}
@media (prefers-reduced-motion: reduce) { .enquiry-wa-btn.is-waiting { animation: none; } }

.enquiry-ref {
  display: inline-block; margin-top: var(--space-3);
  padding: .35rem .8rem; background: var(--paper-sunk);
  border-radius: var(--radius-sm); font-family: var(--font-mono); font-size: .9rem;
}

/* --------------------------------------------------------------------------
   12. Post and policy lists
   -------------------------------------------------------------------------- */
.post-card { display: flex; flex-direction: column; height: 100%; }
.post-card .thumb { aspect-ratio: 16 / 10; overflow: hidden; background: var(--paper-sunk); }
.post-card .thumb img { width: 100%; height: 100%; object-fit: cover; border-radius: 0; transition: transform .4s var(--ease); }
.post-card:hover .thumb img { transform: scale(1.03); }
.post-card .card-body { flex: 1; display: flex; flex-direction: column; }
.post-card h3 { font-size: 1.12rem; margin-bottom: var(--space-2); }
.post-card h3 a { color: var(--ink); text-decoration: none; }
.post-card h3 a:hover { color: var(--accent); }
.post-card .meta { font-size: .82rem; color: var(--ink-muted); margin-top: auto; padding-top: var(--space-3); }

.policy-group { margin-bottom: var(--space-6); }
.policy-item {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4);
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius); margin-bottom: var(--space-2);
  text-decoration: none; color: var(--ink);
  transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.policy-item:hover { border-color: var(--accent); box-shadow: var(--shadow); color: var(--ink); }
.policy-item .icon { color: var(--accent); flex: none; }
.policy-item .title { font-weight: 500; }
.policy-item .meta { margin-left: auto; font-size: .8rem; color: var(--ink-muted); white-space: nowrap; }

/* --------------------------------------------------------------------------
   13. Pagination
   -------------------------------------------------------------------------- */
.pagination { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-top: var(--space-6); }
.pagination a, .pagination span {
  min-width: 2.4rem; padding: .45rem .7rem; text-align: center;
  border: 1px solid var(--paper-line); border-radius: var(--radius-sm);
  text-decoration: none; color: var(--ink-soft); font-size: .9rem; background: var(--paper-raised);
}
.pagination a:hover { border-color: var(--accent); color: var(--accent); }
.pagination .current { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }
.pagination .gap { border: 0; background: none; color: var(--ink-faint); min-width: auto; padding-inline: .2rem; }
.pagination .summary { margin-left: auto; border: 0; background: none; color: var(--ink-muted); font-size: .85rem; }

@media (max-width: 640px) {
  .pagination .summary { margin-left: 0; width: 100%; order: -1; padding: 0 0 var(--space-2); }
}

/* --------------------------------------------------------------------------
   14. Responsive navigation
   -------------------------------------------------------------------------- */
/* The bar holds every top-level item on one row down to about 1150px and no
   further. Below that it used to wrap, so that is where it becomes the burger
   instead. The button and the panel used to switch at 1080 and 1024 — fifty
   pixels where the burger showed and the full bar was still there beside it. */
/* Where the bar gives up and becomes a burger. 1150 suits a starter's short
   menu. A site with many top-level items, or long ones, has to raise this —
   measure where its own bar runs out of room rather than guessing, and move
   all three of these together. The club site runs at 1499 with nine items. */
@media (max-width: 1150px) {
  .nav-toggle {
    display: grid; place-items: center; margin-left: auto;
    width: 40px; height: 40px; background: transparent;
    border: 1px solid var(--paper-line); border-radius: var(--radius);
    color: var(--ink-soft); cursor: pointer;
  }
  .nav {
    position: fixed; inset: var(--header-h) 0 auto 0;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--paper-raised); border-bottom: 1px solid var(--paper-edge);
    box-shadow: var(--shadow-lg); padding: var(--space-3);
    max-height: calc(100vh - var(--header-h)); overflow-y: auto;
    margin-left: 0;
  }
  .nav:not(.is-open) { display: none; }
  .nav a { padding: var(--space-3) var(--space-4); border-radius: var(--radius); }
  .site-header .container { flex-wrap: wrap; }
  .theme-toggle { order: 2; }
}

/* --------------------------------------------------------------------------
   15. Print — it is paper, after all
   -------------------------------------------------------------------------- */
@media print {
  .site-header, .site-footer, .theme-toggle, .nav-toggle, .pagination, .enquiry, .no-print { display: none !important; }
  body { background: #fff; color: #000; }
  .card { box-shadow: none; border: 1px solid #ccc; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: .8em; color: #555; }
}

/* ==========================================================================
   16. The organisation site proper — a Paper reading of the layout the organisation already
       has: a photographic banner with the crest on it, the menu underneath,
       the blog down the right, the coloured button groups, then the policies.
   ========================================================================== */

/* ---------- banner ---------- */
/* ---------- sections arriving as you reach them ---------- */
/* Eight pixels and a fade. Anything more and a page of ordinary organisation news
   starts to feel like a slideshow. Nothing is hidden unless the script is
   there to show it again, so the page reads fine with no script at all. */
.will-arrive {
  opacity: 0; transform: translateY(8px);
  transition: opacity .5s var(--ease), transform .5s var(--ease);
}
.will-arrive.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .will-arrive { opacity: 1; transform: none; transition: none; }
}
@media print {
  .will-arrive { opacity: 1 !important; transform: none !important; }
}

/* ---------- the top of the site ---------- */
/*
 * One photograph, one thing to do. The home page gives it room; every other
 * page gets a slim band of the same picture, because those pages arrive with
 * their own heading and a visitor who is already inside does not need selling
 * to a second time.
 */
.site-head { position: relative; }
.hero {
  position: relative;
  min-height: clamp(140px, 17vw, 220px);
  display: grid; align-items: end;
  background: var(--paper-sunk) center/cover no-repeat;
  overflow: hidden;
}
/* The banner photograph. A background cannot carry a srcset, so the choice is
   made here from the properties the page hands down: the original for anything
   that cannot read WebP, the large copy on a wide screen, the small one on a
   phone — which was otherwise downloading a 1600-pixel photograph to fill a
   strip 220 pixels tall. */
.hero { background-image: var(--banner); }
@supports (background-image: image-set(url("i.webp") type("image/webp"))) {
  .hero { background-image: image-set(var(--banner-lg, var(--banner)) type("image/webp")); }
  @media (max-width: 700px) {
    .hero { background-image: image-set(var(--banner-sm, var(--banner-lg, var(--banner))) type("image/webp")); }
  }
}
.site-head.is-home .hero {
  min-height: clamp(340px, 42vw, 560px);
}
/* The scrim: dark at the foot where the words sit, clearing towards the top
   so the photograph is still a photograph. */
.hero::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  /* Two washes: one down the picture, one in from the left where the words
     are. Either alone leaves a bright picture somewhere behind a sentence. */
  background:
    linear-gradient(to right, rgba(12, 18, 24, .74) 0%, rgba(12, 18, 24, .52) 34%, rgba(12, 18, 24, .12) 62%, rgba(12, 18, 24, 0) 80%),
    linear-gradient(to bottom, rgba(12, 18, 24, .30) 0%, rgba(12, 18, 24, .04) 30%, rgba(12, 18, 24, .48) 100%);
}
.site-head:not(.is-home) .hero::after {
  background: linear-gradient(to bottom, rgba(14, 20, 26, .30) 0%, rgba(14, 20, 26, .45) 100%);
}
.hero-inner {
  position: relative; z-index: 1;
  padding: clamp(var(--space-6), 6vw, var(--space-8)) var(--space-5);
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3);
  color: #fff;
}
.hero-crest { display: block; margin-bottom: var(--space-2); }
.hero-crest img {
  width: clamp(84px, 9vw, 116px); height: auto;
  filter: drop-shadow(0 4px 14px rgba(0, 0, 0, .45));
}
.hero-title {
  margin: 0; color: #fff; max-width: 18ch; text-wrap: balance;
  font-size: clamp(2rem, 4.6vw, 3.4rem); line-height: 1.04; letter-spacing: -.025em;
  text-shadow: 0 2px 20px rgba(0, 0, 0, .45);
}
.hero-sub {
  margin: 0; max-width: 44ch; color: rgba(255, 255, 255, .96);
  font-size: clamp(1rem, 1.5vw, 1.2rem); line-height: 1.45; font-weight: 500;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 2px 18px rgba(0, 0, 0, .55);
}
.hero-do { display: flex; flex-wrap: wrap; gap: var(--space-3); margin: var(--space-3) 0 0; }

/* ---------- menu bar, sitting under the banner ---------- */
/* ---------- the bar under the picture, which follows you down ---------- */
.menubar {
  background: color-mix(in srgb, var(--paper-raised) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(10px); -webkit-backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--paper-edge);
  position: sticky; top: 0; z-index: 60;
  transition: box-shadow .2s var(--ease);
}
.site-head.is-stuck .menubar { box-shadow: 0 6px 20px rgba(60, 48, 30, .10); }
/* The bar is allowed to be wider than the page's content. The content column
   stops at the container width because that is as wide as writing should run;
   a row of navigation has no such reason, and capping it there left the menu
   with a few pixels to spare no matter how big the screen was — which is why
   it wrapped on a wide monitor and nothing anybody did to the menu helped. */
.menubar .container { display: flex; align-items: center; gap: var(--space-3); min-height: 60px; max-width: 1760px; }
/* The crest in the bar. On the home page it waits until the hero has gone
   by; everywhere else it is simply there. */
.bar-crest { display: flex; align-items: center; flex: 0 0 auto; text-decoration: none; color: var(--ink); font-weight: 600; }
/* The organisation's mark is white line art with nothing behind it, which vanishes on
   a pale bar. It gets its own disc of organisation blue to sit on — the same mark,
   legible in both themes. */
.bar-crest img {
  width: 40px; height: 40px; object-fit: contain; padding: 4px;
  background: var(--accent); border-radius: 50%;
  box-shadow: 0 1px 3px rgba(60, 48, 30, .18);
}
/* The organisation's name in words, when that is what the bar carries. Sized to sit
   with the menu rather than compete with it, and allowed on to a second line
   on a narrow screen, where the alternative is squeezing the menu out. */
.bar-word {
  font-family: var(--font);
  font-size: clamp(.95rem, .88rem + .3vw, 1.12rem);
  font-weight: 700; line-height: 1.12; letter-spacing: -.015em;
  color: var(--ink); max-width: 11ch;
}
.bar-crest.has-word { gap: var(--space-3); }
@media (min-width: 700px) { .bar-word { max-width: none; white-space: nowrap; } }

.site-head.is-home .bar-crest {
  opacity: 0; transform: translateY(-4px); pointer-events: none;
  transition: opacity .22s var(--ease), transform .22s var(--ease);
}
.site-head.is-home.is-stuck .bar-crest { opacity: 1; transform: none; pointer-events: auto; }
.menu { display: flex; align-items: center; gap: 10px; margin: 0 0 0 auto; padding: 0; list-style: none; flex-wrap: wrap; justify-content: flex-end; }
.menu > li { position: relative; }
.menu > li > a, .menu .submenu a {
  display: flex; align-items: center; gap: .3rem;
  padding: .55rem .7rem; font-size: .93rem; font-weight: 500;
  color: var(--ink-soft); text-decoration: none; border-radius: var(--radius-sm);
  white-space: nowrap; transition: background .15s var(--ease), color .15s var(--ease);
}
.menu > li > a:hover, .menu > li:focus-within > a { background: var(--paper-sunk); color: var(--ink); }
.menu > li > a[aria-current="page"] { color: var(--accent); font-weight: 600; }

/* ---------- the one menu item that is asking to be pressed ----------
   A plain row of text links has no first among equals, so the thing the site
   most wants a visitor to open reads the same as the policy area. This makes
   one item a filled pill — bigger, in the site's colour, with a small mark
   beside it — and gives it a nudge every so often.

   The nudge is one animation on a long loop rather than a timer in
   JavaScript: fourteen seconds, of which about one and a half is movement and
   the rest is the button sitting still. Long enough that it reads as "look
   over here" rather than as something broken. */
.menu > li > a.nav-feature {
  background: var(--accent); color: var(--on-accent);
  font-weight: 600; font-size: .95rem;
  padding: .58rem 1rem;
  box-shadow: 0 2px 10px color-mix(in srgb, var(--accent) 34%, transparent);
  transform-origin: 50% 65%;
  animation: nav-nudge 14s ease-in-out infinite;
}
.menu > li > a.nav-feature svg { color: currentColor; }
.menu > li > a.nav-feature:hover,
.menu > li > a.nav-feature:focus-visible {
  background: var(--accent-hover); color: var(--on-accent);
  box-shadow: 0 4px 16px color-mix(in srgb, var(--accent) 42%, transparent);
  /* It stops the moment somebody goes for it: a target that moves under the
     pointer is a target you miss. */
  animation-play-state: paused;
}
/* Already there — nothing left to advertise. */
.menu > li > a.nav-feature[aria-current="page"] { animation: none; }

@keyframes nav-nudge {
  0%, 84%, 100% { transform: none; }
  86% { transform: rotate(-4deg)   scale(1.05); }
  88% { transform: rotate(3.2deg)  scale(1.05); }
  90% { transform: rotate(-2.2deg) scale(1.04); }
  92% { transform: rotate(1.4deg)  scale(1.03); }
  94% { transform: rotate(-.6deg)  scale(1.01); }
}

/* Somebody who has asked for less movement gets the pill and none of the
   nudge. The pill is what makes it stand out; the nudge is the extra. */
@media (prefers-reduced-motion: reduce) {
  .menu > li > a.nav-feature { animation: none; }
}

/* Between the burger and a wide screen the bar has to hold every top-level
   item — and now the pill as well. Tighter padding there, and the site's name
   steps back to its crest, rather than letting the bar wrap onto a second row
   and read as a mistake. */
/* Where there is a crest to stand in for it, the name steps back and the menu
   gets the 240px. Only where there is one: a site with no mark would be left
   with an empty corner. */
@media (min-width: 1151px) {
  .menubar .bar-crest:has(img) .bar-word { display: none; }
  .menubar .bar-crest:has(img).has-word { gap: 0; }
}

.menu .caret { transition: transform .18s var(--ease); }
.menu > li:hover > a .caret, .menu > li:focus-within > a .caret { transform: rotate(180deg); }

.submenu {
  position: absolute; top: 100%; left: 0; z-index: 70;
  min-width: 232px; margin: 0; padding: var(--space-2); list-style: none;
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
  opacity: 0; visibility: hidden; transform: translateY(-6px);
  transition: opacity .16s var(--ease), transform .16s var(--ease), visibility .16s;
}
.menu > li:hover > .submenu, .menu > li:focus-within > .submenu,
.submenu li:hover > .submenu, .submenu li:focus-within > .submenu {
  opacity: 1; visibility: visible; transform: none;
}
/* The last dropdown in the bar opens to the left of its button rather than the
   right. Anchored at left:0 it hung past the edge of the screen at around
   1040–1250 wide, and because a hidden element still counts toward how wide
   the page is, EVERY page scrolled sideways by that much — with nothing
   visible to explain why. The selector is "the last top-level item that has a
   dropdown", so it keeps up if the menu is rearranged. */
.menu > li:has(> .submenu):not(:has(~ li > .submenu)) > .submenu { left: auto; right: 0; }

.submenu li { position: relative; }
.submenu a { padding: .5rem .7rem; font-size: .9rem; }
.submenu a:hover { background: var(--paper-sunk); color: var(--ink); }
.submenu .submenu { top: -.4rem; left: 100%; }

@media (max-width: 1150px) {
  .menubar .container { justify-content: space-between; }
  /* The pill still marks it out in the panel, but a block of colour jigging
     about in a list of plain links is a different thing from a button nudging
     in a bar: there is nothing around it to nudge against. */
  .menu > li > a.nav-feature { animation: none; }
  .menu {
    position: absolute; inset: 56px 0 auto 0; margin: 0;
    flex-direction: column; align-items: stretch; justify-content: flex-start;
    background: var(--paper-raised); border-bottom: 1px solid var(--paper-edge);
    box-shadow: var(--shadow-lg); padding: var(--space-3);
    max-height: calc(100vh - 56px); overflow-y: auto;
  }
  .menu:not(.is-open) { display: none; }
  .submenu {
    position: static; opacity: 1; visibility: visible; transform: none;
    box-shadow: none; border: 0; border-left: 2px solid var(--paper-edge);
    border-radius: 0; margin-left: var(--space-4); padding: 0 0 0 var(--space-2);
  }
  .menu .caret { margin-left: auto; }
}

/* ---------- the home page: content left, blog right ---------- */
/* minmax(0, 1fr) rather than 1fr: a bare fr track is min-width:auto, so one
   child that will not shrink — the enquiry card, on a narrow phone — pushes
   the whole column past the edge of the screen and the page scrolls
   sideways. */
.home-split { display: grid; gap: clamp(var(--space-5), 4vw, var(--space-7)); grid-template-columns: minmax(0, 1fr); align-items: start; }
@media (min-width: 1000px) { .home-split { grid-template-columns: minmax(0, 1.55fr) minmax(300px, .95fr); } }
@media (max-width: 999px) { .blog-rail { position: static; } }
/* The films and pictures on the home page, two across rather than three so
   each one is big enough to see. One per row once there is no room for two. */
.media-grid { display: grid; grid-template-columns: 1fr; gap: clamp(var(--space-4), 3vw, var(--space-6)); }
@media (min-width: 760px) { .media-grid { grid-template-columns: 1fr 1fr; } }

/* An item is the tile and, under it, whatever was written about it. The
   wording sits below rather than over the picture: text laid over a photograph
   is legible until somebody uploads a pale one, and then it is not. */
.media-item { margin: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.media-caption { font-size: .92rem; color: var(--ink-soft); }
.media-caption p { margin: 0 0 var(--space-3); }
.media-caption p:last-child { margin-bottom: 0; }
.media-caption .btn { margin-top: var(--space-1); }

/* A film is a still with a play badge until somebody asks for it. Every tile
   is the same shape, so the column stays level whatever the clips are, and
   nothing downloads while the page is only being read. */
.media-tile {
  position: relative; display: block; width: 100%; padding: 0;
  aspect-ratio: 16 / 9; border: 0; border-radius: var(--radius); overflow: hidden;
  /* With a still it shows the still; without one, the site's own accent
     rather than a grey rectangle — so no thumbnail still looks deliberate. */
  background:
    var(--tile-still, none) center/cover no-repeat,
    linear-gradient(140deg, var(--accent) 0%, var(--accent-ink) 100%);
  box-shadow: var(--shadow-sm);
  transition: transform .18s var(--ease), box-shadow .18s var(--ease);
}
button.media-tile { cursor: pointer; }
.media-tile::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(to bottom, rgba(12, 18, 24, .10) 0%, rgba(12, 18, 24, .55) 100%);
}
/* A picture with nothing written across it needs no scrim: it is only there so
   white lettering stays readable. */
.media-tile.is-picture:not(:has(.media-name))::after { background: none; }
button.media-tile:hover, button.media-tile:focus-visible { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
button.media-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.media-play {
  position: absolute; z-index: 1; inset: 0; margin: auto;
  width: 58px; height: 58px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .92); color: var(--accent);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .28);
  transition: transform .18s var(--ease), background .18s var(--ease);
}
button.media-tile:hover .media-play { transform: scale(1.07); background: #fff; }
.media-name {
  position: absolute; z-index: 1; left: 0; right: 0; bottom: 0;
  padding: var(--space-4); text-align: left;
  color: #fff; font-size: .9rem; font-weight: 600; line-height: 1.35;
  text-shadow: 0 1px 8px rgba(0, 0, 0, .5);
}

/* The film itself, once asked for: black, centred, and closed with Escape or
   a click anywhere off it. */
.video-box {
  position: fixed; inset: 0; z-index: 90; border: 0; padding: 0; margin: 0;
  width: 100%; max-width: 100%; height: 100%; max-height: 100%;
  background: rgba(8, 12, 16, .88); display: grid; place-items: center;
}
.video-box::backdrop { background: rgba(8, 12, 16, .88); }
.video-box video { width: min(94vw, 1200px); max-height: 84vh; border-radius: var(--radius); background: #000; box-shadow: var(--shadow-lg); }
.video-close {
  position: absolute; top: var(--space-4); right: var(--space-4);
  width: 2.6rem; height: 2.6rem; border-radius: 50%; cursor: pointer;
  background: rgba(255, 255, 255, .14); color: #fff; border: 1px solid rgba(255, 255, 255, .4);
  display: grid; place-items: center;
}
.video-close:hover { background: rgba(255, 255, 255, .26); }
@media (prefers-reduced-motion: reduce) {
  .media-tile, .media-tile:hover, .media-play, .media-tile:hover .media-play { transform: none; }
}
/* The affiliation badges, under the enquiry form and in the same column. The
   strip is one wide image (1581px across), so it is stretched to the width of
   the column rather than sitting at whatever size it happens to be. */
.awards-strip { margin-top: var(--space-5); }
.awards-strip p { margin: 0; }
.awards-strip img { display: block; width: 100%; max-width: 100%; height: auto; border-radius: var(--radius); }
/* A badge strip that came across without its size on it still holds its own
   height, so the writing underneath does not jump when the picture lands. */
.awards-strip img:not([width]) { aspect-ratio: 8 / 1; object-fit: contain; }
.awards-strip + .prose { margin-top: var(--space-6); }

/* ---------- the buttons and the organisation videos, side by side ---------- */
/* Neither of these two needs the whole page to itself, and both looked
   oversized with it. They share the width on a wide screen and stack, buttons
   first, once there is no longer room for the pair. */
.home-duo { display: grid; gap: clamp(var(--space-5), 4vw, var(--space-7)); grid-template-columns: 1fr; align-items: start; }
@media (min-width: 1200px) {
  /* Three fifths to the buttons, two to the clips: the buttons are the part
     people come to press, and they are the part that needs the room. */
  .home-duo:not(.is-single) { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); }
  /* Half the width fits fewer buttons across, so the minimum comes down to
     keep each one the size it is elsewhere rather than stretching two of them
     across the column. */
  .home-duo:not(.is-single) .btn-grid { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
  /* The clips stay two across in the column. Each one is a preview that goes
     full screen when it is played, so a pair that keeps the column beside the
     buttons rather than running far past them is the better trade. */
  .home-duo:not(.is-single) .media-grid { grid-template-columns: 1fr 1fr; }
}
.duo-part { min-width: 0; }
.duo-part > :first-child { margin-top: 0; }
.duo-part .btn-group:last-child { margin-bottom: 0; }
.duo-part .band-intro h2, .duo-part > h2 { margin-top: 0; }

.band-intro { margin-bottom: var(--space-6); }
.band-intro > :last-child, .band-outro > :last-child { margin-bottom: 0; }
.band-outro { margin-top: var(--space-6); }
.band-intro h2, .band-outro h2 { margin-bottom: var(--space-3); }

.blog-rail { position: sticky; top: 76px; }
.blog-rail h2 { font-size: 1.15rem; padding-bottom: var(--space-3); border-bottom: 2px solid var(--accent); }
/* A post is a card, and the whole card is the link. */
.blog-rail-posts { display: grid; gap: var(--space-3); margin-top: var(--space-4); }
.rail-post {
  display: flex; gap: var(--space-4); align-items: center;
  padding: var(--space-3); border-radius: var(--radius); text-decoration: none;
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  transition: transform .16s var(--ease), box-shadow .16s var(--ease), border-color .16s var(--ease);
}
.rail-post:hover { transform: translateY(-1px); border-color: var(--paper-line); box-shadow: var(--shadow); }
.rail-shot {
  flex: 0 0 76px; height: 76px; border-radius: var(--radius-sm); overflow: hidden; background: var(--paper-sunk);
}
.rail-shot img { width: 100%; height: 100%; object-fit: cover; display: block; margin: 0; box-shadow: none; }
.rail-words { display: block; min-width: 0; }
.rail-title {
  display: block; font-size: .95rem; font-weight: 600; line-height: 1.35; color: var(--ink);
  /* Two lines, then an ellipsis: the rail is narrow and a long title would
     otherwise push everything else down the page. */
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.rail-post:hover .rail-title { color: var(--accent); }
.rail-post .when { display: block; margin-top: var(--space-2); font-size: .78rem; color: var(--ink-muted); }
@media (prefers-reduced-motion: reduce) { .rail-post, .rail-post:hover { transform: none; } }

/* ---------- the notice strip at the top of the home page ---------- */
.notice-post {
  display: grid; gap: var(--space-5); align-items: center; grid-template-columns: 1fr;
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow-sm);
}
@media (min-width: 760px) { .notice-post { grid-template-columns: minmax(0, 280px) 1fr; } }
.notice-post img {
  width: 100%; height: 100%; border-radius: 0;
  /* A frame of a fixed shape, filled by the picture. Whatever is uploaded —
     a collage, a portrait, a wide landscape — the card is the same size and
     the page does not jump as it loads. */
  aspect-ratio: 4 / 3; max-height: 260px; object-fit: cover; object-position: center;
}
.notice-post .body { padding: var(--space-5); }

/* ---------- coloured button groups (the link manager, rebuilt) ---------- */
.btn-group { margin-bottom: var(--space-6); }
.btn-group-title {
  display: flex; align-items: center; gap: var(--space-3);
  font-size: 1.02rem; font-weight: 600; margin-bottom: var(--space-3);
  padding-left: var(--space-3); border-left: 4px solid var(--group-colour, var(--accent));
}
.btn-group-title .count { font-size: .78rem; font-weight: 500; color: var(--ink-muted); }
.btn-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(228px, 1fr)); gap: var(--space-3); }
/* The organisation's buttons.
   Flat colour reads as a coloured box; a little depth reads as something to
   press. The sheen is a hairline of light along the top edge and a whisper of
   shade at the foot — the same trick a printed button uses — and the shadow
   is tinted with the group's own colour rather than grey, so a red row casts
   a warm shadow and a blue one a cool shadow. It lifts a couple of pixels
   under the cursor and sits back down when pressed. Nothing moves far. */
.organisation-btn {
  display: flex; flex-direction: column; gap: .2rem;
  padding: var(--space-4);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .10), rgba(255, 255, 255, 0) 42%, rgba(0, 0, 0, .07)),
    var(--group-colour, var(--accent));
  color: var(--group-ink, #fff);
  /* No drawn edge — a dark outline round a coloured button is the oldest tell
     in web design. The shape comes from the colour, the sheen and the shadow.
     The border stays as transparent so the box measures the same as before. */
  border: 1px solid transparent;
  border-radius: 8px;
  text-decoration: none; text-align: center;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .16),
    0 1px 2px color-mix(in srgb, var(--group-colour, var(--accent)) 30%, transparent),
    0 3px 8px color-mix(in srgb, var(--group-colour, var(--accent)) 18%, transparent);
  transition: transform .18s var(--ease), box-shadow .18s var(--ease), filter .18s var(--ease);
  will-change: transform;
}
.organisation-btn:hover {
  transform: translateY(-2px);
  filter: brightness(1.04) saturate(1.02);
  color: var(--group-ink, #fff);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .20),
    0 2px 4px color-mix(in srgb, var(--group-colour, var(--accent)) 30%, transparent),
    0 10px 20px color-mix(in srgb, var(--group-colour, var(--accent)) 24%, transparent);
}
/* Pressed: back down onto the page, with the light gone from the top edge. */
.organisation-btn:active {
  transform: translateY(0) scale(.995);
  transition-duration: .06s;
  box-shadow:
    inset 0 2px 4px rgba(0, 0, 0, .16),
    0 1px 2px color-mix(in srgb, var(--group-colour, var(--accent)) 30%, transparent);
}
.organisation-btn:focus-visible {
  outline: 2px solid var(--group-colour, var(--accent));
  outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
  .organisation-btn, .organisation-btn:hover, .organisation-btn:active { transform: none; transition: box-shadow .18s var(--ease); }
}
/* The generic form `.label` sets a colour and a margin. A button's label
   carries the same class name, so both are restated here — otherwise the
   wording on a coloured button takes the form label's grey and is close to
   unreadable on the darker groups. */
.organisation-btn .label { font-weight: 700; line-height: 1.3; font-size: .97rem; color: inherit; margin: 0; }
/* A button colour nobody will be able to read. Amber rather than red: the
   colour still saves and still works, it just should not. */
.colour-warn {
  margin-top: var(--space-2); max-width: 22rem;
  color: var(--amber); font-weight: 600;
}
.colour-warn .icon { vertical-align: -2px; }

/* The fade is set from PHP (CLUB_DESC_FADE) so the admin can warn about a
   colour using the contrast this text will really have. At .85 it cost
   about a fifth of the contrast, enough on its own to take a perfectly
   reasonable button colour under the readable minimum. */
.organisation-btn .desc { font-size: .8rem; opacity: var(--club-desc-fade, .92); line-height: 1.35; color: inherit; margin: 0; }
/* A pale button needs a visible edge rather than a shadow alone, and its
   sheen has to come from shade rather than light or it disappears. */
/* A pale button has nothing to lift off the paper, so it gets the faintest
   ring of its own colour rather than a dark line — enough to hold its shape
   against a light background, not enough to read as an outline. */
.organisation-btn.is-pale {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .26), rgba(255, 255, 255, 0) 48%, rgba(0, 0, 0, .09)),
    var(--group-colour, var(--accent));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .40),
    0 1px 2px color-mix(in srgb, var(--group-colour, var(--accent)) 50%, transparent),
    0 4px 10px color-mix(in srgb, var(--group-colour, var(--accent)) 32%, transparent);
}
.organisation-btn.is-pale:hover {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .44),
    0 2px 4px color-mix(in srgb, var(--group-colour, var(--accent)) 50%, transparent),
    0 10px 22px color-mix(in srgb, var(--group-colour, var(--accent)) 36%, transparent);
}

/* ---------- the policy grid ---------- */
.policy-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--space-4); }
.policy-col {
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg); overflow: hidden; display: flex; flex-direction: column;
}
.policy-col-head {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-4); border-bottom: 1px solid var(--paper-edge);
  background: var(--paper-sunk);
}
.policy-col-head h3 { margin: 0; font-size: .98rem; }
.policy-col-head .count {
  margin-left: auto; min-width: 1.5rem; padding: 0 .4rem;
  background: var(--accent-soft); color: var(--accent-ink);
  border-radius: var(--radius-pill); font-size: .76rem; font-weight: 700; text-align: center; line-height: 1.5rem;
}
.policy-col-body { padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2); flex: 1; }
.policy-doc {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3); border: 1px solid var(--paper-edge); border-radius: var(--radius);
  text-decoration: none; color: var(--ink); font-size: .9rem; line-height: 1.35;
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.policy-doc:hover { border-color: var(--accent); background: var(--paper-sunk); color: var(--ink); }
.policy-doc .icon { flex: none; color: var(--red); margin-top: .1rem; }
.policy-doc.is-link .icon { color: var(--accent); }
.policy-doc .lock { margin-left: auto; flex: none; color: var(--ink-faint); }
.policy-doc.is-locked { opacity: .82; }

/* ---------- the enquiry widget, in the shape the organisation knows ---------- */
.enquiry-wrap {
  background: var(--paper-raised);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg); overflow: hidden;
  box-shadow: var(--shadow-lg), 0 0 0 1px color-mix(in srgb, var(--accent) 8%, transparent);
}

/* THE INVITATION STRIP. Rebuilt for Paper Air, and it needed rebuilding twice
   over.

   It was a three-stop gradient in three literal blues — #0046a6, #0a63d8,
   #2f83ee — typed into the stylesheet. Two things wrong with that on a
   starter. The first is plain: a site that chose green on the Look screen got
   a blue strip on its home page and no way to change it, which is exactly the
   thing this starter says it does not do. The second is Paper Air's fourth
   commandment — colour means something, one filled thing per surface, and a
   tone lives in a dot rather than a painted box. A painted block of accent
   with an accent button inside it spends the colour twice and says nothing
   with either.

   So: a quiet band, ink on paper, and ONE accent button — which is the thing
   somebody came to press. It is the same material as every other quiet
   surface on the site, which is what makes the button on it unmissable. */
.enquiry-cta {
  position: relative;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4);
  padding: var(--space-5);
  background: var(--paper-sunk);
  color: var(--ink);
  border-bottom: 1px solid var(--paper-edge);
}
/* One bloom, near-white at the top, so the band reads as lit from above
   rather than tinted. Percentages, so it scales with the band. */
.enquiry-cta::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(110% 80% at 22% -10%,
              color-mix(in srgb, var(--paper-raised) 85%, transparent), transparent 68%);
}
.enquiry-cta > * { position: relative; }
.enquiry-said { flex: 1; min-width: 15rem; }
.enquiry-cta p { margin: 0; }
.enquiry-headline {
  font-family: var(--heading-font);
  font-weight: var(--heading-weight);
  font-size: clamp(1.06rem, 1.7vw, 1.28rem); line-height: 1.2;
  letter-spacing: var(--heading-tracking);
}
/* Not .enquiry-price — that name is already the green pill on the enquiry page. */
.cta-price {
  margin-top: .3rem !important; font-size: var(--fs-sm); font-weight: 500; color: var(--ink-muted);
}

.enquiry-status {
  display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  padding: var(--space-3); font-size: .86rem; color: var(--grey-ink);
  background: var(--grey-1); border-bottom: 1px solid var(--paper-edge);
}
.enquiry-status strong { color: var(--ink-soft); }
.enquiry-status .dot {
  width: 9px; height: 9px; border-radius: 50%; background: var(--green);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--green) 22%, transparent);
  animation: pulse-dot 2.4s var(--ease) infinite;
}
@keyframes pulse-dot {
  0%, 100% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--green) 22%, transparent); }
  50%      { box-shadow: 0 0 0 6px color-mix(in srgb, var(--green) 8%, transparent); }
}

.enquiry-cols { display: grid; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 820px) { .enquiry-cols { grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); } }
/* Everything on the right of the widget — the dates, then the quick topics —
   is one column, so the grid beside the conversation stays two wide however
   many panels end up in it. */
.side-col { display: flex; flex-direction: column; min-width: 0; }
.side-col > .topics + .topics { padding-top: 0; }
.side-col > .next-dates { border-bottom: 1px solid var(--paper-edge); }

/* ---------- the conversation, with its own scroll arrows ---------- */
.chat-pane {
  min-width: 0;
  position: relative;
  background: var(--grey-1);
  border-bottom: 1px solid var(--paper-edge);
  /* The column beside it decides how tall this row is, and how tall that
     column is depends on how many dates are listed. The conversation fills
     whatever height it is given rather than stopping short of it. */
  display: flex; flex-direction: column; min-height: 0;
}
@media (min-width: 820px) { .chat-pane { border-bottom: 0; border-right: 1px solid var(--paper-edge); } }

/* Paper fading in at the top and bottom of the scroll, so a part-scrolled
   conversation looks cut off rather than simply ending. */
.chat-pane::before, .chat-pane::after {
  content: ''; position: absolute; left: 0; right: 0; height: 28px;
  pointer-events: none; z-index: 2; opacity: 0; transition: opacity .2s var(--ease);
}
.chat-pane::before { top: 0; background: linear-gradient(to bottom, var(--grey-1), transparent); }
.chat-pane::after { bottom: 0; background: linear-gradient(to top, var(--grey-1), transparent); }
.chat-pane[data-at-top="0"]::before,
.chat-pane[data-at-end="0"]::after { opacity: 1; }

.chat {
  display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-5) var(--space-5) var(--space-5) var(--space-4);
  flex: 1 1 auto; min-height: 0;
  max-height: 360px; overflow-y: auto; scroll-behavior: smooth;
  overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--grey-3) transparent;
}
/* Side by side, the row is as tall as the taller column and the cap comes off:
   the conversation shows as much of itself as there is room for, rather than
   stopping at 360px and leaving the rest of the column blank. Stacked, the cap
   stays — there the conversation would otherwise run the length of the page. */
@media (min-width: 820px) { .chat { max-height: none; } }

.chat::-webkit-scrollbar { width: 6px; }
.chat::-webkit-scrollbar-track { background: transparent; }
.chat::-webkit-scrollbar-thumb { background: var(--grey-3); border-radius: var(--radius-pill); }
.chat:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }

.chat-arrows {
  position: absolute; right: var(--space-3); bottom: var(--space-3); z-index: 3;
  display: flex; flex-direction: column; gap: 6px;
}
.chat-arrow {
  width: 30px; height: 30px; display: grid; place-items: center;
  border-radius: 50%; cursor: pointer;
  background: var(--paper-raised); border: 1px solid var(--grey-3);
  color: var(--grey-ink); box-shadow: var(--shadow-sm);
  transition: background .15s var(--ease), color .15s var(--ease),
              border-color .15s var(--ease), opacity .15s var(--ease);
}
.chat-arrow:hover:not(:disabled) { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.chat-arrow:active:not(:disabled) { transform: scale(.92); }
.chat-arrow:disabled { opacity: .3; cursor: default; }
/* The arrows are driven by script, and are pointless when the conversation
   fits, so they appear only once it is known there is something to scroll. */
.chat-arrows { display: none; }
.chat-pane[data-scrollable="1"] .chat-arrows { display: flex; }

.bubble {
  display: flex; gap: var(--space-3); align-items: flex-start;
  align-self: flex-start; max-width: 94%;
  animation: bubble-in .4s var(--ease) both;
}
.bubble .who {
  flex: none; width: 28px; height: 28px; margin-top: 2px;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--accent-soft); color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
}
.bubble .said {
  padding: var(--space-3) var(--space-4);
  background: var(--paper-raised); border: 1px solid var(--grey-2);
  border-radius: 4px var(--radius-lg) var(--radius-lg) var(--radius-lg);
  font-size: .92rem; line-height: 1.55; color: var(--ink-soft);
  box-shadow: var(--shadow-sm);
}
.bubble .said strong { color: var(--ink); }
.bubble .when { display: block; margin-top: .35rem; font-size: .7rem; color: var(--ink-faint); text-align: right; }
@keyframes bubble-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.bubble:nth-child(2) { animation-delay: .25s; }
.bubble:nth-child(3) { animation-delay: .5s; }
.bubble:nth-child(4) { animation-delay: .75s; }

/* ---------- the quick topics, on the right ---------- */
.topics { padding: var(--space-5) var(--space-4); }
.topics h3 {
  display: flex; align-items: center; gap: .4rem;
  font-size: .74rem; letter-spacing: .11em; text-transform: uppercase;
  color: var(--ink-muted); margin-bottom: var(--space-2);
}
.topics > p { font-size: .85rem; color: var(--ink-muted); margin-bottom: var(--space-4); }
.topic-list { display: flex; flex-direction: column; gap: var(--space-2); }
/* ---------- when the next sessions are ---------- */
/* Two places show the same list: a tickable version in the widget's right-hand
   column, and a fuller one on the enquiry page. They share the wording classes
   so a change to one does not quietly leave the other behind. */
.next-dates .topic-list { gap: var(--space-2); }
/* Written to out-rank .topic, which is a single pill-shaped line and sits
   later in this file — these rows are three lines tall and need square-ish
   corners and a tick that lines up with the first of them. */
.topic.date-topic { align-items: flex-start; border-radius: var(--radius); padding: .65rem .9rem; }
.topic.date-topic .box { margin-top: 1px; }
.date-said { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.date-when { font-weight: 600; display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.date-where { font-size: .84rem; color: var(--ink-muted); font-weight: 400; }
.date-note  { font-size: .82rem; color: var(--ink-muted); font-weight: 400; }
.date-soon {
  font-size: .68rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  background: var(--accent-soft); color: var(--accent-ink);
  padding: .12rem .45rem; border-radius: var(--radius-pill);
}
.date-soon.is-full { background: var(--grey-2); color: var(--ink-muted); }
.topic.is-full { opacity: .62; cursor: default; }
.topic.is-full:hover, .topic.is-full:focus-within { transform: none; border-color: var(--grey-2); background: var(--grey-1); }

/* The list on the enquiry page, where there is room to set it out properly. */
.date-list { margin-top: var(--space-6); }
.date-list h2 { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-3); }
.date-intro { color: var(--ink-muted); max-width: 52ch; margin-bottom: var(--space-4); }
.dates { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.dates .date {
  display: flex; align-items: flex-start; gap: var(--space-4);
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg); padding: var(--space-4);
  box-shadow: var(--shadow-sm);
}
.dates .date.is-full { opacity: .7; }
.date-box {
  flex: none; width: 54px; text-align: center; line-height: 1.05;
  background: var(--accent); color: var(--on-accent); border-radius: var(--radius);
  padding: .45rem 0 .5rem;
}
.date-day { display: block; font-size: 1.5rem; font-weight: 700; }
.date-month { display: block; font-size: .7rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; opacity: .9; }
.date-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.date-head { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

.topic {
  display: flex; align-items: center; gap: var(--space-3);
  padding: .7rem 1rem; cursor: pointer;
  background: var(--grey-1); border: 1px solid var(--grey-2);
  border-radius: var(--radius-pill); font-size: .93rem; font-weight: 500;
  transition: background .15s var(--ease), border-color .15s var(--ease),
              color .15s var(--ease), transform .1s var(--ease); user-select: none;
}
.topic:hover { border-color: var(--accent); background: var(--paper-raised); transform: translateX(2px); }
.topic input { position: absolute; opacity: 0; width: 0; height: 0; }
.topic .box {
  flex: none; width: 20px; height: 20px; border-radius: 5px;
  border: 2px solid var(--grey-3); display: grid; place-items: center;
  transition: all .15s var(--ease);
}
.topic .box .icon { opacity: 0; color: #fff; }
.topic:has(input:checked) { background: var(--accent); border-color: var(--accent); color: var(--on-accent); transform: none; }
.topic:has(input:checked) .box { background: #fff; border-color: #fff; }
.topic:has(input:checked) .box .icon { opacity: 1; color: var(--accent); }
.topic:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 2px; }
.topic:has(input:disabled) { opacity: .5; cursor: not-allowed; }

.enquiry-entry { padding: var(--space-5) var(--space-4); border-top: 1px solid var(--paper-edge); background: var(--grey-1); }
.name-gate {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3);
  padding: var(--space-3); margin-bottom: var(--space-3);
  border: 2px solid var(--amber); border-radius: var(--radius); background: var(--amber-soft);
}
.name-gate label { font-weight: 600; font-size: .9rem; color: var(--ink); white-space: nowrap; margin: 0; }
.name-gate input { flex: 1; min-width: 9rem; }
.name-gate.is-done { border-color: var(--green); background: var(--green-soft); }
.name-hint { font-size: .82rem; color: var(--amber); margin: 0 0 var(--space-3); }

.say { display: flex; gap: var(--space-2); align-items: center; transition: opacity .15s var(--ease); }
/* Waiting on a name. Dimmed on purpose, so a box that will not take a word
   looks like one, rather than looking broken. */
.say.is-waiting { opacity: .55; }
.say.is-waiting input, .say.is-waiting .send { cursor: not-allowed; }
.say input { flex: 1; border-radius: var(--radius-pill); }
/* WhatsApp's own green, and one of the two places in this stylesheet where a
   literal colour is right: it is somebody else's brand, people recognise it,
   and repainting it in the site's accent would make the one button on the page
   whose colour is already understood look like something else. See also
   .btn-whatsapp. */
.send {
  flex: none; width: 44px; height: 44px; border-radius: 50%; border: 0;
  background: #1f8a4c; color: #fff; display: grid; place-items: center; cursor: pointer;
  transition: background .15s var(--ease), transform .1s var(--ease);
}
.send:hover { background: #1a7541; }
.send:active { transform: scale(.94); }
.send:disabled { opacity: .5; cursor: not-allowed; }

.enquiry-alt { padding: var(--space-4); text-align: center; font-size: .88rem; color: var(--grey-ink); background: var(--paper-raised); border-top: 1px solid var(--paper-edge); }
.enquiry-wa-note {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4); background: var(--green-soft); color: var(--green);
  font-size: .88rem; border-top: 1px solid var(--paper-edge);
}
.enquiry-wa-note span:first-of-type { flex: 1; min-width: 14rem; }
.enquiry-wa-note .wa-get { display: flex; gap: var(--space-2); margin-left: auto; }

/* ---------- the module sign-in buttons ---------- */
.module-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3);
  margin-top: var(--space-4); padding-top: var(--space-4);
  border-top: 1px dashed var(--paper-line);
  font-size: .85rem; color: var(--ink-muted);
}
.module-bar .btn { margin-left: auto; }
.module-login {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .35rem .8rem; font-size: .82rem; font-weight: 600;
  background: var(--paper-sunk); color: var(--ink-muted);
  border: 1px solid var(--paper-line); border-radius: var(--radius-pill); text-decoration: none;
}
.module-login:hover { border-color: var(--accent); color: var(--accent); }
/* The two doors sit together and stay on the right of the bar. */
.module-doors { display: inline-flex; align-items: center; gap: var(--space-3); margin-left: auto; flex-wrap: wrap; }
.module-ask { font-size: .8rem; color: var(--ink-faint); text-decoration: none; border-bottom: 1px solid var(--paper-line); }
.module-ask:hover { color: var(--accent); border-bottom-color: currentColor; }

/* ---------- partner logos ---------- */
/* ---------- partners and sponsors, along the foot of every page ---------- */
/* Shown across the top of the public site while it is hidden — and only to
   somebody signed in, since nobody else gets this far. */
.hidden-note {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: var(--space-3); padding: var(--space-2) var(--space-4);
  background: var(--amber); color: #fff; font-size: .82rem; font-weight: 600;
}
.hidden-note a { color: #fff; text-underline-offset: 3px; }

/* ---------- the organisation's people: leaders, team ---------- */
/* Every photograph fills the same frame whatever size it was uploaded at, so
   the page reads as a set of people rather than a column of odd pictures.
   Cover rather than contain, biased a little above centre, because these are
   portraits and the face is nearer the top than the middle. */
.people-grid {
  display: grid; gap: var(--space-5); margin: var(--space-6) 0;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.person {
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
}
.prose .person img {
  display: block; width: 100%; height: auto;
  /* Square, because every headshot the organisation uploads is square — 282x282 and
     up. A 4/3 frame cut a quarter off the top and bottom of each face. The
     ratio is kept rather than dropped so the cards stay a uniform height,
     and cover with the position below only comes into play if somebody ever
     uploads a landscape photo. */
  aspect-ratio: 1 / 1; object-fit: cover; object-position: center 28%;
  margin: 0; border-radius: 0; box-shadow: none; background: var(--paper-sunk);
}
.person-body { padding: var(--space-5); }
.person-body > :last-child { margin-bottom: 0; }
.person h3 { margin: 0 0 var(--space-2); font-size: 1.05rem; line-height: 1.3; }
.person-role {
  margin: 0 0 var(--space-3); font-size: .8rem; font-weight: 600; line-height: 1.4;
  color: var(--accent);
}
.person p { font-size: .9rem; margin-bottom: var(--space-3); }
/* The wording above the grid keeps a readable measure; the grid gets the
   whole column. */
.prose:has(.people-grid) { max-width: none; }
.prose:has(.people-grid) > p, .prose:has(.people-grid) > h2 { max-width: var(--measure); }

/* ---------- partners and sponsors, on their own page ---------- */
/* The footer keeps its quiet strip; this is the same list with room to
   breathe. Every logo sits in a box of the same height with the name under
   it, so a wide banner and a square badge line up as a grid rather than
   stepping up and down the page. */
/* A page whose wording introduces a grid keeps the wording at a readable
   width but lets the grid have the whole column — otherwise nine logos sit in
   a narrow strip with half the page empty beside them. */
.prose:has(.partner-cards) { max-width: none; }
.prose:has(.partner-cards) > p { max-width: var(--measure); }
.partner-cards {
  list-style: none; margin: var(--space-6) 0; padding: 0;
  display: grid; gap: var(--space-4);
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}
.partner-card {
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  transition: transform .16s var(--ease), box-shadow .16s var(--ease);
}
.partner-card a { display: block; text-decoration: none; color: inherit; height: 100%; }
.partner-card:has(a):hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
/* The logo box. Its height is set per card from the shape of the logo in it:
   a round crest at the same height as a wordmark reads as far bigger, because
   the wordmark is a thin band of ink and the crest is a solid block of it. */
.partner-logo {
  display: flex; align-items: center; justify-content: center;
  height: 120px; padding: var(--space-5) var(--space-5) var(--space-3);
}
.partner-logo img {
  max-width: 100%; max-height: var(--logo-h, 100%);
  width: auto; height: auto; object-fit: contain;
}
.partner-name {
  display: block; padding: 0 var(--space-4) var(--space-4);
  text-align: center; font-size: .85rem; font-weight: 600; color: var(--ink-soft);
}

.partners-strip { margin-top: var(--space-6); padding-top: var(--space-5); border-top: 1px solid var(--paper-edge); }
.partners-title {
  margin: 0 0 var(--space-4); text-align: center;
  font-size: .72rem; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-faint);
}
.partners {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: clamp(var(--space-4), 3.5vw, var(--space-6));
}
.partners li { display: flex; }
.partners a { display: flex; }
.partners img {
  max-height: var(--logo-h, 46px); max-width: 150px; width: auto; object-fit: contain; border-radius: 0;
  /* The logos arrive in a dozen different colourways. Draining most of the
     colour out settles them into one quiet row, and the full colour comes
     back on hover so a sponsor is never permanently greyed out. */
  filter: grayscale(1); opacity: .6;
  transition: filter .2s var(--ease), opacity .2s var(--ease);
}
.partners img:hover, .partners a:focus-visible img { filter: none; opacity: 1; }
:root[data-theme="dark"] .partners img { filter: grayscale(1) brightness(1.6); opacity: .55; }
:root[data-theme="dark"] .partners img:hover { filter: brightness(1.05); opacity: 1; }
@media (max-width: 640px) { .partners img { max-height: calc(var(--logo-h, 46px) * .74); } }

/* ---------- what's on ---------- */
/* A date and what happens on it, side by side where there is room and stacked
   where there is not. The date column is fixed so the titles line up. */
.event-list { list-style: none; padding: 0; margin: 0; }
.event-row {
  display: flex; flex-wrap: wrap; gap: var(--space-4);
  padding: var(--space-5) 0; border-top: 1px solid var(--paper-line);
}
.event-row:last-child { border-bottom: 1px solid var(--paper-line); }
.event-when { flex: 0 0 11rem; display: flex; flex-direction: column; gap: .15rem; }
.event-what { flex: 1 1 16rem; min-width: 0; }
.event-what h2 { font-size: 1.1rem; margin: 0 0 var(--space-2); }
.event-what p { margin: 0 0 var(--space-2); }
.event-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3);
  font-size: .88rem; color: var(--ink-muted); }
.event-meta span { display: inline-flex; align-items: center; gap: .35em; }

/* ---------------------------------------------------------------------------
   The member area: the list of lessons, and one lesson.

   A locked lesson is shown rather than hidden, so the list has two states and
   both have to look deliberate — the locked one dimmed and unclickable, not
   broken. The number down the left is what makes it read as a course.
   --------------------------------------------------------------------------- */
.member-head { margin-bottom: var(--space-6); }
.member-head h1 { margin-bottom: var(--space-2); }
.member-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3);
  margin-top: var(--space-7); padding-top: var(--space-5);
  border-top: 1px solid var(--paper-line);
}

.lesson-list { list-style: none; padding: 0; margin: 0; counter-reset: lesson; }
.lesson-row {
  display: flex; gap: var(--space-4); align-items: flex-start;
  padding: var(--space-5) 0; border-top: 1px solid var(--paper-line);
}
.lesson-row:last-child { border-bottom: 1px solid var(--paper-line); }
.lesson-num {
  flex: 0 0 2.25rem; height: 2.25rem;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; background: var(--paper-sunk); color: var(--ink-muted);
  font-size: .9rem; font-weight: 600; font-variant-numeric: tabular-nums;
}
.lesson-what { flex: 1 1 auto; min-width: 0; }
.lesson-what h2 { font-size: 1.1rem; margin: 0 0 var(--space-2); }
.lesson-what h2 a { color: inherit; text-decoration: none; }
.lesson-what h2 a:hover { color: var(--accent); }
.lesson-what p { margin: 0 0 var(--space-2); }
.lesson-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3);
  font-size: .88rem; color: var(--ink-muted);
}
.lesson-meta span { display: inline-flex; align-items: center; gap: .35em; }
.lesson-row.is-locked .lesson-what h2 { color: var(--ink-muted); }
.lesson-row.is-locked .lesson-num { background: transparent; border: 1px dashed var(--paper-edge); }

.lesson-foot {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--space-4); margin-top: var(--space-7);
  padding-top: var(--space-5); border-top: 1px solid var(--paper-line);
}
.lesson-nav { display: flex; flex-wrap: wrap; gap: var(--space-4); font-size: .92rem; }
.lesson-nav a { display: inline-flex; align-items: center; gap: .35em; }

/* A 16:9 box that keeps its shape on a phone. */
.video-frame {
  position: relative; aspect-ratio: 16 / 9; max-width: 100%;
  margin-block: var(--space-5); border-radius: var(--radius-lg); overflow: hidden;
  background: var(--paper-sunk);
}
.video-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

@media (max-width: 32rem) {
  .lesson-row { gap: var(--space-3); }
  .lesson-num { flex-basis: 1.9rem; height: 1.9rem; font-size: .82rem; }
}

/* ---------------------------------------------------------------------------
   Things for sale.

   Each offer is a self-contained card with its own form: the price, what it
   is, and the button, without a step in between. A shop that makes somebody
   click through to a second page before they can see the price loses them on
   the first page.
   --------------------------------------------------------------------------- */
.offer-grid {
  display: grid; gap: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
  margin-top: var(--space-6);
}
.offer {
  position: relative;
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg); padding: var(--space-5);
  display: flex; flex-direction: column; gap: var(--space-3);
}
.offer h2 { font-size: 1.2rem; margin: 0; }
.offer-badge {
  position: absolute; top: var(--space-4); right: var(--space-4);
  font-size: .72rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  padding: .25rem .55rem; border-radius: 999px;
  background: var(--accent-soft); color: var(--accent-ink);
}
.offer-img {
  width: 100%; aspect-ratio: 16 / 9; object-fit: cover;
  border-radius: var(--radius); margin: 0;
}
.offer-price { font-size: 1.5rem; font-weight: 700; margin: 0; color: var(--ink); }
.offer-summary { margin: 0; color: var(--ink-soft); }
.offer-body { margin: 0; font-size: .95rem; }
/* The form sits at the bottom whatever length the description is, so a row of
   offers has its buttons on one line. */
.offer .buy-form { margin-top: auto; padding-top: var(--space-4); border-top: 1px solid var(--paper-line); }

.buy-form .row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.buy-form .field { margin-bottom: var(--space-3); }
.buy-price { font-weight: 700; opacity: .85; margin-left: .2em; }
.buy-note { display: flex; align-items: flex-start; gap: .4em; margin-top: var(--space-3); }
.buy-note svg { flex: 0 0 auto; margin-top: .18em; }

@media (max-width: 32rem) {
  .buy-form .row-2 { grid-template-columns: 1fr; }
}

/* ===========================================================================
   Page sections (blocks)

   A page built from sections is a stack of full-width bands, each bringing its
   own container. That is the point of them: a wall of logos or a coloured
   call-to-action wants the whole width, which a single column of prose can
   never give it.

   Spacing is owned by the stack rather than by each section, so two sections
   of different kinds sitting next to each other are always the same distance
   apart — the thing that goes wrong first when every block sets its own
   margins.
   =========================================================================== */
.block-stack > .block { padding-block: clamp(var(--space-6), 5vw, var(--space-8)); }
.block-stack > .block:first-child { padding-top: var(--space-5); }
/* Alternating grounds would fight the page's own sections, so a band is tinted
   only when it is asking for something. */
.block-stack > .block + .block { border-top: 1px solid var(--paper-line); }
.block-stack > .block-cta + .block,
.block-stack > .block + .block-cta { border-top: 0; }
.block > .container > h2 { margin-top: 0; margin-bottom: var(--space-4); }
.block > .container > .lead { margin-bottom: var(--space-5); }
/* The section option that says "the full width of the page" means the writing
   as well, not just the pictures — otherwise it would say nothing that the
   ordinary column does not already do. */
.prose-wide { max-width: none; }
.prose-wide > p,
.prose-wide > h1, .prose-wide > h2, .prose-wide > h3,
.prose-wide > h4, .prose-wide > h5, .prose-wide > h6,
.prose-wide > ul, .prose-wide > ol, .prose-wide > dl,
.prose-wide > blockquote, .prose-wide > details { max-width: none; }

/* A page whose own header is followed straight away by a section does not need
   the header's bottom margin as well as the stack's top padding. */
.section.has-blocks { padding-bottom: 0; }

/* ---- picture and words ---- */
.media-text {
  display: grid; gap: clamp(var(--space-5), 4vw, var(--space-7));
  align-items: center; grid-template-columns: 1fr;
}
@media (min-width: 52rem) {
  .media-text { grid-template-columns: 1fr 1fr; }
  /* The picture is first in the markup so it reads first on a phone, where a
     photograph above the words is the right order. On a wide screen it can be
     moved without changing that. */
  .block-media-text.is-right .media-text-figure { order: 2; }
}
.media-text-figure img {
  width: 100%; height: auto; border-radius: var(--radius-lg);
  box-shadow: var(--shadow); margin: 0;
}
.media-text-words > :first-child { margin-top: 0; }
.media-text-words h2 { margin-bottom: var(--space-3); }

/* ---- quote ---- */
.pull-quote { margin: 0; }
/* The global blockquote is the inset one used inside prose — tinted, with a
   rule down the left. A pull quote is not that, so everything it inherits is
   put back before anything is added. */
.pull-quote blockquote {
  margin: 0; padding: 0; border: 0; background: none; border-radius: 0;
  font-size: clamp(1.25rem, 1.1rem + .8vw, 1.7rem);
  line-height: 1.4; color: var(--ink); font-weight: 500;
}
.pull-quote blockquote p { margin: 0; }
.pull-quote blockquote p::before { content: '\201C'; color: var(--accent); }
.pull-quote blockquote p::after  { content: '\201D'; color: var(--accent); }
.pull-quote figcaption {
  display: flex; align-items: center; gap: var(--space-3);
  margin-top: var(--space-5); font-size: .95rem;
}
.pull-quote figcaption > span { display: flex; flex-direction: column; }
.pull-quote-face img {
  width: 3rem; height: 3rem; border-radius: 50%; object-fit: cover; margin: 0;
  box-shadow: var(--shadow-sm);
}

/* ---- wall of logos ---- */
.logo-cards {
  list-style: none; padding: 0; margin: 0; display: grid;
  gap: clamp(var(--space-4), 3vw, var(--space-5));
  grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
}
.logo-card {
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg); padding: var(--space-5);
  display: flex; flex-direction: column; gap: var(--space-3); text-align: center;
}
/* A logo box of a fixed height with the mark centred inside it: the only way a
   row of logos looks deliberate when they arrive as wildly different shapes,
   which they always do. */
/* The box every mark is centred in. Its height comes from the block, adjusted
   for the shape of each logo — see Images::markScale(). The fallback is there
   for a logo whose size could not be read. */
.logo-card-mark {
  display: grid; place-items: center; height: var(--logo-h, 5rem);
}
.logo-card-mark img {
  max-height: var(--logo-h, 5rem); max-width: 100%; width: auto; height: auto;
  object-fit: contain; margin: 0; box-shadow: none;
}
.logo-card-name { font-size: 1rem; margin: 0; }
.logo-card-name a { color: inherit; text-decoration: none; }
.logo-card-name a:hover { color: var(--accent); text-decoration: underline; }
.logo-card-note { margin: 0; font-size: .9rem; color: var(--ink-soft); }

.logo-strip {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: clamp(var(--space-5), 4vw, var(--space-7));
}
.logo-strip img {
  max-height: var(--logo-h, 3rem); width: auto; margin: 0; box-shadow: none;
  opacity: .78; transition: opacity .18s var(--ease);
}
.logo-strip a:hover img { opacity: 1; }

/* ---- grid of cards ---- */
.block-card-grid {
  list-style: none; padding: 0; margin: 0; display: grid;
  gap: clamp(var(--space-4), 3vw, var(--space-5));
  grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr));
}
@media (min-width: 60rem) {
  .block-card-grid { grid-template-columns: repeat(var(--cols, 3), 1fr); }
}
.block-card {
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg); overflow: hidden;
  display: flex; flex-direction: column;
}
.block-card-img img {
  width: 100%; aspect-ratio: 16 / 10; object-fit: cover; margin: 0;
  box-shadow: none; border-radius: 0;
}
.block-card h3 { font-size: 1.05rem; margin: var(--space-4) var(--space-4) 0; }
.block-card p { margin: var(--space-2) var(--space-4) 0; color: var(--ink-soft); font-size: .94rem; }
.block-card-link { margin-top: auto; padding: var(--space-4); font-size: .92rem; }
.block-card-link a { display: inline-flex; align-items: center; gap: .35em; }

/* ---- pictures ---- */
.gallery-grid {
  list-style: none; padding: 0; margin: 0; display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fill, minmax(min(14rem, 100%), 1fr));
}
.gallery-grid figure { margin: 0; }
.gallery-grid img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  border-radius: var(--radius); margin: 0;
}
.gallery-grid figcaption { font-size: .85rem; color: var(--ink-muted); margin-top: var(--space-2); }

/* ---- the gallery (v1.22.0) ----
   Four layouts from one list: an even grid, a mosaic that keeps every
   picture's own shape, one large picture first, and a row that scrolls
   sideways. Columns are a ceiling, not a promise — a phone gets fewer. */
.gallery-intro { color: var(--ink-soft); max-width: 62ch; margin: 0 0 var(--space-5); }
.gallery { --g-cols: 3; --g-ratio: 4 / 3; --g-gap: var(--space-3);
  list-style: none; padding: 0; margin: 0; }
.gallery--cols-2 { --g-cols: 2; }
.gallery--cols-4 { --g-cols: 4; }
.gallery--square   { --g-ratio: 1 / 1; }
.gallery--portrait { --g-ratio: 3 / 4; }
.gallery--wide     { --g-ratio: 16 / 9; }
.gallery figure { margin: 0; }
.gallery picture { display: block; }
.gallery figcaption { font-size: .85rem; color: var(--ink-muted); margin-top: var(--space-2); line-height: 1.4; }

.gallery-link {
  display: block; position: relative; overflow: hidden;
  border-radius: var(--radius); background: var(--paper-sunk);
  cursor: zoom-in; outline-offset: 3px;
}
.gallery-link img {
  display: block; width: 100%; height: auto; margin: 0;
  border-radius: 0; box-shadow: none;
  transition: transform .5s cubic-bezier(.2, .7, .3, 1), opacity .3s;
}
.gallery-link:hover img { transform: scale(1.035); }
.gallery-link:focus-visible { outline: 3px solid var(--accent); }

/* The even grid, and the large-first layout: every picture the same crop. */
.gallery--grid, .gallery--feature {
  display: grid; gap: var(--g-gap);
  grid-template-columns: repeat(min(var(--g-cols), 2), minmax(0, 1fr));
}
@media (min-width: 60rem) {
  .gallery--grid, .gallery--feature { grid-template-columns: repeat(var(--g-cols), minmax(0, 1fr)); gap: var(--space-4); }
}
@media (max-width: 26rem) {
  .gallery--grid.gallery--cols-2 { grid-template-columns: 1fr; }
}
.gallery--grid .gallery-link img, .gallery--feature .gallery-link img {
  aspect-ratio: var(--g-ratio); object-fit: cover;
}
.gallery--feature .is-big { grid-column: 1 / -1; }
@media (min-width: 60rem) {
  .gallery--feature .is-big { grid-column: span 2; grid-row: span 2; }
  .gallery--feature.gallery--cols-2 .is-big { grid-column: 1 / -1; grid-row: auto; }
  /* The big one fills exactly the two rows beside it, whatever its own
     shape. Its picture is taken out of the flow so it cannot decide the
     height of those rows — a portrait photograph first used to stretch
     them to twice their size. */
  .gallery--feature .is-big figure { height: 100%; display: grid; grid-template-rows: minmax(0, 1fr) auto; }
  .gallery--feature .is-big .gallery-link { min-height: 12rem; }
  .gallery--feature .is-big .gallery-link picture,
  .gallery--feature .is-big .gallery-link img { position: absolute; inset: 0; width: 100%; height: 100%; }
  .gallery--feature .is-big .gallery-link img { aspect-ratio: auto; object-fit: cover; }
  .gallery--feature.gallery--cols-2 .is-big figure { display: block; height: auto; }
  .gallery--feature.gallery--cols-2 .is-big .gallery-link { aspect-ratio: 16 / 9; }
}

/* The mosaic: columns, so each picture keeps its own height. The order runs
   down the first column, then the next — which is why the arrows in the
   viewer follow the list, not the eye. */
.gallery--mosaic { column-count: min(var(--g-cols), 2); column-gap: var(--g-gap); }
@media (min-width: 60rem) { .gallery--mosaic { column-count: var(--g-cols); column-gap: var(--space-4); } }
.gallery--mosaic .gallery-item { break-inside: avoid; margin-bottom: var(--g-gap); }
@media (min-width: 60rem) { .gallery--mosaic .gallery-item { margin-bottom: var(--space-4); } }

/* One row that scrolls sideways, each picture snapping into place. */
.gallery--strip {
  display: grid; grid-auto-flow: column; gap: var(--space-4);
  grid-auto-columns: min(80%, 26rem);
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; padding-bottom: var(--space-3);
  scrollbar-width: thin;
}
.gallery--strip.gallery--cols-2 { grid-auto-columns: min(88%, 36rem); }
.gallery--strip.gallery--cols-4 { grid-auto-columns: min(70%, 20rem); }
.gallery--strip .gallery-item { scroll-snap-align: start; }
.gallery--strip .gallery-link img { aspect-ratio: var(--g-ratio); object-fit: cover; }

@media (prefers-reduced-motion: reduce) {
  .gallery-link img { transition: none; }
  .gallery-link:hover img { transform: none; }
}

/* ---- the viewer ----
   A <dialog>, so focus, Escape and the backdrop come from the browser. Always
   dark whatever the theme: a photograph is judged against black. */
.lightbox {
  position: fixed; inset: 0; width: 100vw; height: 100dvh; max-width: none; max-height: none;
  margin: 0; padding: 0; border: 0; background: rgba(8, 8, 8, .96); color: #fff;
  overscroll-behavior: contain;
}
.lightbox::backdrop { background: rgba(8, 8, 8, .96); }
.lightbox[open] { display: grid; grid-template-rows: auto 1fr auto; animation: lb-in .2s ease-out; }
@keyframes lb-in { from { opacity: 0; } to { opacity: 1; } }
.lightbox-bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-4); font-size: .9rem; color: rgba(255, 255, 255, .78);
}
.lightbox-stage {
  position: relative; min-height: 0; overflow: hidden; display: grid; place-items: center;
  grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr);
  padding: 0 clamp(var(--space-2), 5vw, 5rem); touch-action: pan-y;
}
.lightbox-img {
  /* A grid row of 1fr has no height of its own to be a percentage of, so the
     picture is held to the screen instead: the whole height less the bar
     above and the caption below. Found with a portrait room photograph,
     which ran off the bottom of the screen. */
  max-width: 100%; max-height: calc(100dvh - 8.5rem); width: auto; height: auto; object-fit: contain;
  margin: 0; border-radius: 4px; box-shadow: none; user-select: none; -webkit-user-drag: none;
  transition: opacity .18s;
}
.lightbox-img.is-loading { opacity: .25; }
.lightbox-cap {
  min-height: 3.2rem; padding: var(--space-3) var(--space-4) var(--space-4);
  text-align: center; font-size: .95rem; line-height: 1.45; color: rgba(255, 255, 255, .88);
  max-width: 70ch; margin-inline: auto;
}
.lightbox button {
  appearance: none; border: 0; cursor: pointer; color: #fff;
  background: rgba(255, 255, 255, .1); border-radius: 999px;
  display: inline-grid; place-items: center; width: 44px; height: 44px;
  transition: background .15s;
}
.lightbox button:hover { background: rgba(255, 255, 255, .22); }
.lightbox button:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.lightbox-prev, .lightbox-next { position: absolute; top: 50%; transform: translateY(-50%); width: 52px; height: 52px; }
.lightbox-prev { left: clamp(4px, 1.5vw, var(--space-5)); }
.lightbox-next { right: clamp(4px, 1.5vw, var(--space-5)); }
.lightbox-prev[hidden], .lightbox-next[hidden] { display: none; }
@media (max-width: 40rem) {
  .lightbox-prev, .lightbox-next { top: auto; bottom: -3.6rem; transform: none; background: rgba(255, 255, 255, .14); }
  .lightbox-stage { padding: 0; }
  .lightbox-cap { padding-inline: 4.5rem; }
}
@media (prefers-reduced-motion: reduce) { .lightbox[open] { animation: none; } .lightbox-img { transition: none; } }
html.lightbox-open { overflow: hidden; }

/* The admin: "add several at once" on a list of pictures. */
.repeater-bulk { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); margin-top: var(--space-3); }
.repeater-bulk-status { font-size: .88rem; color: var(--ink-muted); margin: 0; }
.repeater-bulk-status.is-bad { color: var(--red); }
.repeater-bulk-status.is-good { color: var(--green, var(--ink-soft)); }
.repeater.is-dropping { outline: 2px dashed var(--accent); outline-offset: 6px; border-radius: var(--radius); }

/* ---- numbered steps ---- */
.step-list {
  list-style: none; padding: 0; margin: 0; counter-reset: step;
  display: grid; gap: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
}
/* The number is a flex item rather than an absolutely positioned one. It used
   to sit on top of the heading whenever the heading wrapped, because absolute
   positioning takes the badge out of the flow and nothing reserves its height
   reliably across font sizes. */
.step-list > li {
  counter-increment: step;
  display: grid; grid-template-columns: 2.25rem 1fr; gap: var(--space-3);
  align-items: start;
}
.step-list > li::before {
  content: counter(step);
  width: 2.25rem; height: 2.25rem; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--accent); color: var(--on-accent);
  font-weight: 700; font-size: .95rem; font-variant-numeric: tabular-nums;
}
/* Both the heading and the text belong in the second column. Without saying
   so, the text flows into the next grid cell — which is the 2.25rem badge
   column, and gives you one word per line. */
.step-list > li > * { grid-column: 2; }
.step-list h3 { font-size: 1.05rem; margin: .3rem 0 var(--space-2); }
.step-list p { margin: 0; color: var(--ink-soft); }

/* ---- questions and answers ---- */
.faq-list { border-top: 1px solid var(--paper-line); }
.faq-item { border-bottom: 1px solid var(--paper-line); }
.faq-item > summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  padding: var(--space-4) 0; cursor: pointer; font-weight: 600;
  list-style: none;
}
.faq-item > summary::-webkit-details-marker { display: none; }
.faq-item > summary:hover { color: var(--accent); }
.faq-item > summary svg { flex: 0 0 auto; color: var(--ink-muted); transition: transform .18s var(--ease); }
.faq-item[open] > summary svg { transform: rotate(180deg); }
.faq-answer { padding-bottom: var(--space-4); color: var(--ink-soft); max-width: 68ch; }
.faq-answer p { margin: 0; }

/* ---- numbers ---- */
.stat-row {
  list-style: none; padding: 0; margin: 0; display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr));
  text-align: center;
}
.stat-value {
  display: block; font-size: clamp(1.9rem, 1.4rem + 2vw, 3rem);
  font-weight: 700; line-height: 1.05; color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.stat-label { display: block; margin-top: var(--space-2); color: var(--ink-soft); font-size: .92rem; }

/* ---- call to action ---- */
.cta-panel {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--space-5); padding: clamp(var(--space-5), 4vw, var(--space-7));
  border-radius: var(--radius-lg);
}
.cta-panel h2 { margin: 0 0 var(--space-2); }
.cta-panel p { margin: 0; }
.cta-panel > div { flex: 1 1 22rem; }
.cta-buttons { display: flex; flex-wrap: wrap; gap: var(--space-3); }
/* A painted panel is the one place Paper Air's "colour means something" rule
   is deliberately set aside: this is the last word on a page, it carries one
   sentence and one button, and there is no second thing on it for the colour
   to compete with. It is a room, not a control. One per page — the moment
   there are two, both stop meaning anything.

   The gradient is built from the site's own accent rather than typed in, so a
   site that chose green gets a green panel. */
.cta-panel.is-accent {
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent-ink) 100%);
  color: #fff;
}
.cta-panel.is-accent h2, .cta-panel.is-accent p { color: #fff; }
/* Ink, taken from the site's own text colour rather than a literal charcoal:
   a site with a warm near-black and a site with a cool one should not both
   get somebody else's #14181d. */
.cta-panel.is-dark { background: var(--ink); color: var(--paper); }
.cta-panel.is-dark h2, .cta-panel.is-dark p { color: var(--paper); }
.cta-panel.is-dark .btn-on-colour { background: var(--paper); color: var(--ink); }
.cta-panel.is-quiet { background: var(--paper-sunk); border: 1px solid var(--paper-edge); }
/* A solid white button reads on both the accent and the dark panel, where the
   site's own primary colour would disappear into the background. */
.btn-on-colour { background: #fff; color: var(--accent-ink); }
.btn-on-colour:hover { background: #fff; color: var(--accent); box-shadow: var(--shadow); }

/* ---- a row of buttons ---- */
.button-row { display: flex; flex-wrap: wrap; gap: var(--space-3); margin: 0; }

/* ---- video or map ---- */
.embed-frame {
  position: relative; aspect-ratio: 16 / 9; max-width: 100%;
  border-radius: var(--radius-lg); overflow: hidden; background: var(--paper-sunk);
}
.embed-frame.is-map { aspect-ratio: 16 / 10; }
.embed-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.embed-caption { font-size: .88rem; color: var(--ink-muted); margin-top: var(--space-3); }

/* A logo is usually dark ink on nothing, drawn to sit on white paper. On a
   dark page that makes it invisible — and a client cannot fix it, because the
   file is whatever their funder sent them. So in dark mode every logo gets a
   pale plate to sit on, which is what a printed page would have given it. */
:root[data-theme="dark"] .logo-card-mark,
:root[data-theme="dark"] .logo-strip img {
  background: #fff; border-radius: var(--radius); padding: var(--space-3);
}
:root[data-theme="dark"] .logo-strip img { padding: var(--space-2); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .logo-card-mark,
  :root:not([data-theme="light"]) .logo-strip img {
    background: #fff; border-radius: var(--radius); padding: var(--space-3);
  }
  :root:not([data-theme="light"]) .logo-strip img { padding: var(--space-2); }
}

/* ==========================================================================
   PAPER AIR — THE SHARED UTILITIES

   Four reusable pieces, so every control surface conforms without copying
   recipes around. Wearing the class is always preferred to copying the spec:
   one component, one recipe.

   .acts   — an ACTION ROW. Dresses its .btn children as menu-voice pills and
             hides their icons. It styles only the buttons, never the
             container, so it composes with whatever layout the row already
             has. Colour still comes from the .btn variant underneath, so
             hierarchy survives the costume change.
   .tray   — a segmented setting tray: a quiet inset trough, flat segments,
             and ONE raised active pill.
   .glass  — a surface that floats over content: translucent where the browser
             supports it, a solid sunk panel where it does not.
   .studio — the presentation register for a flagship message: the quiet
             dot-tag, the headline, the hairline before the action, and one
             ink button.
   ========================================================================== */

/* --- .acts — the action row, and the menu voice --------------------------
   Commandments 1 and 2. The icons go because a word in this voice scans
   faster than a picture that has to be decoded, and because eight little
   glyphs in a row is eight things competing with the one that matters. */
.acts .btn, .acts .btn-sm {
  font: var(--w-lab) .719rem/1 var(--font);
  letter-spacing: .5px;
  text-transform: uppercase;
  padding: .625rem .9375rem;
  border-radius: var(--radius-pill);
  transition: transform .15s var(--ease), box-shadow .15s var(--ease),
              border-color .14s var(--ease), background .14s var(--ease),
              color .14s var(--ease), opacity .14s var(--ease);
}
.acts .btn .icon, .acts .btn svg { display: none; }
.acts .btn:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-sm), 0 6px 16px -10px rgba(10, 11, 12, .28);
}
.acts .btn:active { transform: scale(.95); }
/* A row of chips wraps rather than scrolls; a scrolling action row hides the
   action at the end of it. */
.acts { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }

/* --- .tray — the segmented setting tray ---------------------------------- */
.tray {
  display: inline-flex; gap: 3px;
  background: var(--paper-sunk); padding: 4px;
  border-radius: var(--r-in);
  border: 1px solid var(--paper-edge);
  box-shadow: inset 0 1px 3px rgba(10, 11, 12, .06);
}
.tray > button, .tray > a {
  display: inline-flex; align-items: center; justify-content: center;
  border: none; background: transparent; color: var(--ink-muted);
  font: var(--w-lab) var(--fs-sm)/1 var(--font);
  padding: .44rem .8rem; border-radius: var(--radius-sm);
  cursor: pointer; text-decoration: none;
  transition: color .14s var(--ease), box-shadow .14s var(--ease);
}
.tray > button:hover, .tray > a:hover { color: var(--ink); }
/* One raised pill, and only one. The trough is flat so the raised one is
   unmistakable — which is the whole job of a tray. */
.tray > .on, .tray > [aria-selected="true"] {
  background: var(--paper-raised); color: var(--ink);
  box-shadow: 0 1px 2px rgba(10, 11, 12, .10),
              0 6px 16px -10px rgba(10, 11, 12, .28),
              inset 0 0 0 1px var(--paper-edge);
}

/* --- .glass — a bar that floats over content ----------------------------- */
.glass {
  background: var(--paper-sunk);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius-xl);
  box-shadow: inset 0 1px 3px rgba(10, 11, 12, .06);
}
/* The fallback above is the rule, not the exception: a solid tray is a
   perfectly good bar, and this only ever adds to it. */
@supports ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
  .glass {
    background: color-mix(in srgb, var(--paper-raised) 62%, transparent);
    -webkit-backdrop-filter: blur(18px) saturate(1.7);
    backdrop-filter: blur(18px) saturate(1.7);
    border-color: color-mix(in srgb, var(--paper-raised) 70%, transparent);
    box-shadow: 0 1px 1px rgba(10, 11, 12, .05),
                0 12px 32px -14px rgba(10, 11, 12, .28),
                inset 0 1px 0 color-mix(in srgb, var(--paper-raised) 80%, transparent);
  }
}

/* --- .studio — the presentation register ---------------------------------
   For the one flagship message on a page: what this organisation is for, the
   thing being launched, the invitation at the foot. What used to be done here
   with gradients and glows is done with SPACE and TYPE SIZE, which is what
   actually reads as considered.

   NOT A SECOND HOME PAGE. One sentence, one button, one fact. The moment it
   grows a second column it stops being a flagship message and starts being
   another thing to read past. */
.studio {
  position: relative; isolation: isolate; overflow: hidden;
  background: var(--paper-sunk);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-6) var(--space-5);
  /* An inset hairline rather than a border: no layout cost, and it holds the
     edge on a pale page where the fill alone would not separate it. */
  box-shadow: inset 0 0 0 1px var(--paper-edge);
}
/* One bloom, not two washes, and near-white at the top so the panel reads as
   lit from above rather than tinted. Percentages, so it scales with the panel. */
.studio::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(110% 80% at 22% -10%,
              color-mix(in srgb, var(--paper-raised) 85%, transparent), transparent 68%);
}
/* The quiet dot-tag. Small capitals are the tell of a tool's interface rather
   than a brochure's — and the tone lives in the DOT, never in a painted box
   and never in the word. Commandment 4. */
.studio-tag {
  display: inline-flex; align-items: center; gap: .44rem;
  font: 700 .66rem/1 var(--font-mono);
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-muted); margin-bottom: var(--space-4);
}
.studio-tag::before {
  content: ""; width: 5px; height: 5px; border-radius: 50%;
  background: currentColor; flex: none;
}
.studio-tag.is-accent { color: var(--accent-ink); }
.studio-tag.is-green  { color: var(--green); }
.studio-tag.is-amber  { color: var(--amber); }

.studio-h {
  font-size: calc(clamp(1.4rem, 1.1rem + 1.4vw, 1.9rem) * var(--heading-scale));
  font-weight: var(--heading-weight); letter-spacing: -.036em;
  line-height: 1.14; margin: 0 0 var(--space-3);
}
.studio-p { color: var(--ink-soft); max-width: 58ch; margin: 0; }
.studio-p b { color: var(--ink); font-weight: var(--w-em); }

/* The hairline before the action underlines the SENTENCE, not the page —
   which is why it fades out rather than running the full width. */
.studio-rule {
  height: 1px; border: 0; margin: var(--space-5) 0 var(--space-4);
  background: linear-gradient(90deg, var(--paper-line), transparent 70%);
}
.studio-acts { display: flex; gap: var(--space-3); flex-wrap: wrap; align-items: center; }
.studio-note { font-size: var(--fs-fine); color: var(--ink-muted); }

/* --- tactile press, and stilling it -------------------------------------
   Every transform in Paper Air is a nicety. Somebody who has asked their
   machine for less movement gets none of them, and loses nothing but the
   movement: the hover and press still change colour and shadow. */
@media (prefers-reduced-motion: reduce) {
  .acts .btn, .acts .btn:hover, .acts .btn:active,
  .btn:active, .btn-ink:hover,
  .card-link:hover, a.card:hover { transform: none; }
}

/* ==========================================================================
   NOTICES, THE WHATSAPP BUTTON AND THE LEGAL LINE — v1.16.0
   Harvested from shoutwebsites.com and rebuilt in Paper Air: no painted
   boxes, one accent, tone in a 5px dot, and every movement stoppable.
   ========================================================================== */

/* --- the announcement bar ------------------------------------------------
   Ink on paper, not a coloured strip: it sits above every page, and a bar in
   the accent colour would be the loudest thing on every one of them. */
.nbar {
  display: flex; align-items: center; gap: var(--space-2);
  background: var(--ink); color: var(--paper);
  font-size: var(--fs-sm); line-height: 1.4;
  padding: .55rem var(--space-3) .55rem var(--space-5);
}
.nbar[hidden] { display: none; }
.nbar-track { flex: 1; min-width: 0; overflow: hidden; }
.nbar-one { margin: 0; text-align: center; }
.nbar-link { color: inherit; font-weight: var(--w-lab); text-decoration: underline;
  text-underline-offset: .2em; text-decoration-color: color-mix(in srgb, currentColor 45%, transparent); }
.nbar-link:hover { color: inherit; text-decoration-color: currentColor; }

/* Two copies of the run side by side, moved left by exactly one copy: the
   seam is invisible and the loop never jumps. The script sets the duration
   from the run's own width, so three short lines do not race past and ten
   long ones do not crawl: the reading speed stays the same either way. */
.nbar.is-scrolling .nbar-track {
  display: flex;
  mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
}
.nbar-run {
  display: flex; gap: 3rem; margin: 0; padding: 0 3rem 0 0; list-style: none;
  flex: none; white-space: nowrap;
  animation: nbar-scroll 38s linear infinite;
}
.nbar-run li { margin: 0; }
.nbar-run li::before { content: ""; display: inline-block; width: 5px; height: 5px; border-radius: 50%;
  background: currentColor; opacity: .5; margin-right: 1rem; vertical-align: middle; }
@keyframes nbar-scroll { to { transform: translateX(-100%); } }
/* Stoppable three ways (WCAG 2.2.2): under the pointer, on keyboard focus,
   and by the button. */
.nbar:hover .nbar-run, .nbar:focus-within .nbar-run, .nbar.is-paused .nbar-run { animation-play-state: paused; }
/* And not moving at all for anybody who has asked for less motion — the
   run wraps into ordinary lines instead, with the copy hidden. */
@media (prefers-reduced-motion: reduce) {
  .nbar-run { animation: none; white-space: normal; flex-wrap: wrap; gap: .25rem 2rem; padding: 0; }
  .nbar-run[aria-hidden="true"] { display: none; }
  .nbar.is-scrolling .nbar-track { mask-image: none; }
}
.nbar-btn {
  flex: none; display: grid; place-items: center; width: 30px; height: 30px;
  border: 0; border-radius: var(--radius-pill); cursor: pointer;
  background: transparent; color: inherit; opacity: .75;
}
.nbar-btn:hover { opacity: 1; background: color-mix(in srgb, currentColor 12%, transparent); }
.nbar-btn svg { fill: currentColor; }
.nbar-btn .icon { fill: none; }
.nbar-btn[aria-pressed="true"] svg rect:first-child { transform: none; }

/* --- the what's-new panel ------------------------------------------------ */
.npanel { position: fixed; left: var(--space-4); bottom: var(--space-4); z-index: 60; }
/* The tab is a menu-voice pill: 600 at 11.5px, uppercase, tracked. */
.npanel-tab {
  display: inline-flex; align-items: center; gap: .5rem;
  font: var(--w-lab) .719rem/1 var(--font); letter-spacing: .5px; text-transform: uppercase;
  padding: .7rem 1rem; border-radius: var(--radius-pill); cursor: pointer;
  color: var(--ink); background: var(--paper-raised); border: 1px solid var(--paper-edge);
  box-shadow: 0 1px 2px rgba(10,11,12,.10), 0 6px 16px -10px rgba(10,11,12,.28);
  transition: transform .15s var(--ease), box-shadow .15s var(--ease);
}
.npanel-tab:hover { transform: translateY(-1px); }
.npanel-tab:active { transform: scale(.95); }
.npanel-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-faint); }
.npanel.is-new .npanel-dot { background: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
.npanel-count { font-variant-numeric: tabular-nums; color: var(--ink-muted); }
.npanel.is-open .npanel-tab { visibility: hidden; }

/* The box is the frosted surface. Solid where blur is not supported, which is
   a perfectly good panel on its own. */
.npanel-box {
  position: absolute; left: 0; bottom: 0; width: min(360px, calc(100vw - 2 * var(--space-4)));
  max-height: min(70vh, 560px); overflow: auto;
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius-xl); box-shadow: var(--shadow-lg);
}
@supports ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
  .npanel-box {
    background: color-mix(in srgb, var(--paper-raised) 86%, transparent);
    -webkit-backdrop-filter: blur(24px) saturate(1.7); backdrop-filter: blur(24px) saturate(1.7);
  }
}
.npanel-box[hidden] { display: none; }
.npanel.is-open .npanel-box { animation: npanel-in .22s var(--ease); }
@keyframes npanel-in { from { opacity: 0; transform: translateY(8px) scale(.98); } }
@media (prefers-reduced-motion: reduce) { .npanel.is-open .npanel-box { animation: none; } .npanel-tab:hover, .npanel-tab:active { transform: none; } }
.npanel-head { display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-4) var(--space-4) var(--space-2) var(--space-5); position: sticky; top: 0; }
.npanel-head h2 { font-size: 1.05rem; margin: 0; }
.npanel-head h2:focus { outline: none; }
.npanel-close { display: grid; place-items: center; width: 32px; height: 32px; border: 0; cursor: pointer;
  border-radius: var(--radius-pill); background: transparent; color: var(--ink-muted); }
.npanel-close:hover { background: var(--paper-sunk); color: var(--ink); }
.npanel-list { list-style: none; margin: 0; padding: 0 var(--space-5) var(--space-5); }
.npanel-item { padding: var(--space-3) 0 var(--space-3) var(--space-4); margin: 0; position: relative;
  border-top: 1px solid var(--paper-edge); }
.npanel-item:first-child { border-top: 0; }
/* The tone lives in the dot and nowhere else. Commandment 4. */
.npanel-item::before { content: ""; position: absolute; left: 0; top: calc(var(--space-3) + .5em);
  width: 5px; height: 5px; border-radius: 50%; background: var(--ink-faint); }
.npanel-item.tone-accent::before { background: var(--accent); }
.npanel-item.tone-green::before  { background: var(--green); }
.npanel-item.tone-amber::before  { background: var(--amber); }
.npanel-item strong { display: block; font-weight: var(--w-lab); line-height: 1.35; }
.npanel-item p { margin: .25rem 0 0; font-size: var(--fs-sm); color: var(--ink-soft); }
.npanel-item a { display: inline-block; margin-top: .35rem; font-size: var(--fs-sm); font-weight: var(--w-lab); }

/* --- the floating WhatsApp button ----------------------------------------
   WhatsApp's own green: somebody else's brand, recognised on sight, and one
   of the two places a literal colour is right (see .send). */
.wa-fab {
  position: fixed; right: var(--space-4); bottom: var(--space-4); z-index: 60;
  width: 56px; height: 56px; border-radius: 50%; display: grid; place-items: center;
  background: #1f8a4c; color: #fff;
  box-shadow: 0 2px 8px rgba(10,11,12,.18), 0 10px 26px -8px rgba(31,138,76,.5);
  transition: transform .15s var(--ease), box-shadow .15s var(--ease);
}
.wa-fab:hover { color: #fff; transform: translateY(-2px); }
.wa-fab:active { transform: scale(.95); }
@media (prefers-reduced-motion: reduce) { .wa-fab:hover, .wa-fab:active { transform: none; } }
/* On a phone both floating things share the bottom edge; keep them apart. */
@media (max-width: 520px) {
  .npanel { left: var(--space-3); bottom: var(--space-3); }
  .wa-fab { right: var(--space-3); bottom: var(--space-3); width: 52px; height: 52px; }
}

/* --- the proof row under the home page tagline --------------------------- */
.hero-proof {
  display: flex; flex-wrap: wrap; gap: .35rem 1.25rem;
  list-style: none; margin: 0; padding: 0;
  font-size: var(--fs-sm); font-weight: var(--w-em); color: rgba(255,255,255,.94);
  text-shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 2px 14px rgba(0, 0, 0, .5);
}
.hero-proof li { margin: 0; display: inline-flex; align-items: center; gap: .5rem; }
.hero-proof li::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; opacity: .7; }

/* --- the legal line in the footer ---------------------------------------- */
.footer-legal { font-size: var(--fs-fine); color: var(--ink-muted); margin: var(--space-6) 0 0;
  padding-top: var(--space-4); border-top: 1px solid var(--paper-edge); line-height: 1.6; }

/* --- reviews ------------------------------------------------------------- */
.rv-grid { list-style: none; margin: var(--space-5) 0 0; padding: 0;
  display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.rv-card { margin: 0; display: flex; flex-direction: column; gap: var(--space-3);
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg); padding: var(--space-5); }
.rv-card blockquote { margin: 0; padding: 0; background: none; border: 0; color: var(--ink); }
.rv-card blockquote p { margin: 0; line-height: 1.6; }
/* Stars in ink, not gold: they are information, and the page's one colour
   belongs to the thing somebody came to do. */
.rv-stars { display: inline-flex; gap: 2px; }
.rv-stars svg { fill: var(--ink); }
.rv-stars svg.off { fill: var(--paper-line); }
.rv-who { margin: auto 0 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: .15rem .6rem;
  font-size: var(--fs-sm); color: var(--ink-muted); }
.rv-who strong { color: var(--ink); font-weight: var(--w-lab); }
.rv-source::before { content: "·"; margin-right: .6rem; }
.rv-more { margin: var(--space-5) 0 0; }

/* --- comparison / price table ------------------------------------------- */
.cmp-grid { display: grid; gap: var(--space-4); margin-top: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); align-items: stretch; }
.cmp-card { position: relative; display: flex; flex-direction: column;
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg); padding: var(--space-5); }
/* The picked one says "this one" with an edge, not a painted box. */
.cmp-card.is-featured { border-color: var(--ink); box-shadow: 0 0 0 1px var(--ink), var(--shadow); }
.cmp-badge { position: absolute; top: calc(-1 * .75rem); left: var(--space-5);
  font: var(--w-lab) .7rem/1 var(--font); letter-spacing: .5px; text-transform: uppercase;
  padding: .4rem .7rem; border-radius: var(--radius-pill); background: var(--ink); color: var(--paper); }
.cmp-card h3 { margin: 0 0 var(--space-2); font-size: 1.1rem; }
.cmp-price { margin: 0; font-family: var(--heading-font); font-size: var(--fs-display);
  font-weight: var(--heading-weight); letter-spacing: -.035em; line-height: 1; }
.cmp-note { margin: .4rem 0 0; font-size: var(--fs-sm); color: var(--ink-muted); }
.cmp-list { list-style: none; margin: var(--space-4) 0 0; padding: var(--space-4) 0 0;
  border-top: 1px solid var(--paper-edge); display: grid; gap: .55rem; }
.cmp-list li { margin: 0; padding-left: 1.5rem; position: relative; font-size: var(--fs-sm); line-height: 1.45; }
.cmp-list li::before { position: absolute; left: 0; top: 0; font-weight: 700; }
.cmp-list li.is-yes::before { content: "✓"; color: var(--green); }
.cmp-list li.is-no { color: var(--ink-faint); text-decoration: line-through; text-decoration-color: color-mix(in srgb, currentColor 50%, transparent); }
.cmp-list li.is-no::before { content: "–"; text-decoration: none; }
.cmp-do { margin: auto 0 0; padding-top: var(--space-5); }
.cmp-foot { margin: var(--space-4) 0 0; font-size: var(--fs-fine); color: var(--ink-muted); }

/* ---- rooms and availability (v1.23.0) ----
   The public side: each room as a picture and its facts, with small month
   calendars beside it. Free nights are plain; taken nights are struck
   through and shaded, so the difference survives a greyscale screen and
   anybody who cannot tell two colours apart. */
.rooms-intro { color: var(--ink-soft); max-width: 62ch; margin: 0 0 var(--space-5); }
.rooms-nav { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: var(--space-3); margin: 0 0 var(--space-5); }
.rooms-legend { display: flex; justify-content: center; gap: var(--space-4); margin: 0; font-size: .88rem; color: var(--ink-muted); }
@media (max-width: 30rem) { .rooms-legend { grid-column: 1 / -1; grid-row: 2; justify-content: flex-start; } }
.rooms-legend span { display: inline-flex; align-items: center; gap: .45em; }
.rooms-key { display: inline-block; width: 1.1rem; height: 1.1rem; border-radius: 4px; border: 1px solid var(--paper-line); }
.rooms-key.is-free { background: var(--paper-raised); }
.rooms-key.is-taken { background: color-mix(in srgb, var(--red) 18%, var(--paper-raised)); border-color: color-mix(in srgb, var(--red) 45%, transparent); }

.rooms-list { display: grid; gap: clamp(var(--space-6), 5vw, var(--space-8)); }
.room { display: grid; gap: var(--space-5); padding-bottom: clamp(var(--space-6), 5vw, var(--space-8)); border-bottom: 1px solid var(--paper-line); }
.room:last-child { border-bottom: 0; padding-bottom: 0; }
@media (min-width: 60rem) { .room { grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); align-items: start; } }
.room-about { display: grid; gap: var(--space-4); }
@media (min-width: 40rem) and (max-width: 59.99rem) { .room-about { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; } }
.room-picture { border-radius: var(--radius-lg); overflow: hidden; background: var(--paper-sunk); }
.room-picture img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; margin: 0; border-radius: 0; box-shadow: none; }
.room-words h3 { margin: 0 0 var(--space-2); font-size: 1.3rem; }
.room-words p { margin: var(--space-3) 0 0; color: var(--ink-soft); }
.room-facts { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: .4rem; }
.room-facts li { font-size: .85rem; padding: .25rem .65rem; border-radius: 999px; background: var(--paper-sunk); border: 1px solid var(--paper-line); color: var(--ink-soft); }

.room-months { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr)); }
.room-cal { width: 100%; border-collapse: separate; border-spacing: 3px; table-layout: fixed; font-size: .82rem; margin: 0; }
.room-cal caption { text-align: left; font-weight: 600; padding: 0 0 var(--space-2) 3px; color: var(--ink); font-size: .92rem; }
.room-cal th { font-weight: 500; color: var(--ink-faint); font-size: .72rem; padding: 0 0 2px; text-align: center; border: 0; background: none; }
.room-cal td { padding: 0; text-align: center; border: 0; background: none; }
.room-cal td span { display: grid; place-items: center; aspect-ratio: 1; border-radius: 6px; border: 1px solid var(--paper-line); background: var(--paper-raised); color: var(--ink); font-variant-numeric: tabular-nums; }
.room-cal td.is-taken span {
  /* Taken nights in colour (v1.25.0): a soft red with the date struck
     through in a strong one, so they stand out at a glance and still read
     without colour. */
  background: color-mix(in srgb, var(--red) 18%, var(--paper-raised)); color: var(--red); font-weight: 600;
  text-decoration: line-through; border-color: color-mix(in srgb, var(--red) 45%, transparent);
}
.room-cal td.is-past span { background: transparent; border-color: transparent; color: var(--ink-faint); opacity: .45; }
.room-cal td.is-today span { box-shadow: inset 0 0 0 2px var(--accent); }

/* The admin side: rooms down the side, a month of nights across the top. */
.avail-nav { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-4); flex-wrap: wrap; }
.avail-nav h2 { margin: 0; }
.avail-scroll { overflow-x: auto; overscroll-behavior-x: contain; margin: 0 calc(var(--space-2) * -1); padding: 0 var(--space-2) var(--space-2); }
.avail-grid { border-collapse: separate; border-spacing: 2px; margin: 0; min-width: 100%; }
.avail-grid th, .avail-grid td { padding: 0; border: 0; background: none; text-align: center; }
.avail-grid thead th { font-size: .72rem; font-weight: 600; color: var(--ink-muted); line-height: 1.15; min-width: 1.9rem; padding-bottom: 4px; }
.avail-grid thead th span { display: block; font-weight: 400; color: var(--ink-faint); }
.avail-grid thead th.is-weekend { color: var(--ink); }
.avail-grid thead th.is-today { color: var(--accent); }
.avail-grid .avail-room { text-align: left; font-size: .88rem; font-weight: 600; padding-right: var(--space-3); white-space: nowrap; position: sticky; left: 0; background: var(--paper-raised); z-index: 1; }
.avail-night {
  appearance: none; display: block; width: 1.9rem; height: 2.2rem; margin: 0; padding: 0; cursor: pointer;
  border-radius: 5px; border: 1px solid var(--paper-line); background: var(--paper-raised);
  transition: transform .08s, background .12s;
}
.avail-night:hover { border-color: var(--edge-ctl, #8a8a8a); }
.avail-night:active { transform: scale(.92); }
.avail-night:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.avail-night.is-booked, .avail-key.is-booked { background: var(--accent); border-color: var(--accent); }
.avail-night.is-closed, .avail-key.is-closed { background: repeating-linear-gradient(135deg, var(--ink-faint) 0 2px, var(--paper-sunk) 2px 5px); border-color: var(--ink-faint); }
.avail-night.is-past { opacity: .45; }
.avail-night.is-saving { opacity: .5; }
.avail-legend { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); margin: var(--space-4) 0 0; font-size: .88rem; color: var(--ink-muted); }
.avail-legend span { display: inline-flex; align-items: center; gap: .45em; }
.avail-key { display: inline-block; width: 1rem; height: 1.15rem; border-radius: 4px; border: 1px solid var(--paper-line); background: var(--paper-raised); }
.avail-status { margin-left: auto; }
.avail-status.is-bad { color: var(--red); }
.room-panel-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.room-panel-head h2 { margin: 0 0 var(--space-4); }
.room-panel-order { display: flex; gap: 4px; }

/* ---- the name in the bar, as a picture (v1.25.0) ----
   As large as the bar allows: the bar grows to hold it rather than the
   lettering shrinking to fit a bar sized for a crest. */
.bar-crest.has-image { padding: 6px 0; }
.bar-crest .bar-name {
  display: block; height: clamp(34px, 4.2vw, 56px); width: auto; max-width: min(62vw, 340px);
  object-fit: contain; margin: 0; border-radius: 0; box-shadow: none;
}
/* Lettering drawn for a white page disappears on a dark one, so it gets a
   small white card of its own there. */
:root[data-theme="dark"] .bar-crest .bar-name { background: #fff; padding: 4px 8px; border-radius: 8px; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .bar-crest .bar-name { background: #fff; padding: 4px 8px; border-radius: 8px; }
}

/* ---- WhatsApp and social links at the foot of every page (v1.25.0) ---- */
.footer-social {
  list-style: none; margin: var(--space-6) 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--space-3);
}
.footer-social a {
  display: inline-flex; align-items: center; gap: .5em;
  padding: .5rem .9rem; border-radius: 999px; border: 1px solid var(--paper-line);
  color: var(--ink); text-decoration: none; font-size: .9rem; font-weight: 500;
  background: var(--paper-raised); transition: border-color .15s, background .15s;
}
.footer-social a:hover { border-color: var(--edge-ctl, var(--ink-faint)); background: var(--footer-tint, var(--paper-sunk)); }

/* The home page with no blog rail: the words take the whole width. (v1.25.0) */
@media (min-width: 1000px) { .home-split.home-split--solo { grid-template-columns: minmax(0, 1fr); } }
.home-split--solo > div > .prose { max-width: 72ch; }


/* Rooms (v1.25.0): two pictures side by side, and Earlier / Later sitting
   over the calendars rather than over the pictures. */
.room-pictures { display: grid; gap: var(--space-3); }
.room-pictures--2 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.room-pictures--2 .room-picture img { aspect-ratio: 3 / 4; }
@media (min-width: 60rem) {
  .rooms-nav-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); gap: var(--space-5); }
}
.rooms-nav-row > div:first-child:empty { display: none; }
@media (min-width: 60rem) { .rooms-nav-row > div:first-child:empty { display: block; } }

.gallery-more { margin: var(--space-5) 0 0; }

/* The booking form's extra questions (v1.30.0).

   A Yes/No that matters is drawn as two things you press, not two dots you
   aim at — the whole box is the target, which is the difference between a
   form that works on a phone in a car park and one that does not. */
.askbox { margin: 0 0 var(--space-4); }
.askbox > fieldset { margin-bottom: 0; }
.askbox legend { font-weight: 600; }

.pickrow { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-3); }
.pick { position: relative; flex: 1 1 10rem; cursor: pointer; }
.pick input { position: absolute; opacity: 0; width: 0; height: 0; }
.pick span {
  display: block; text-align: center; padding: .7rem 1rem;
  font-size: .95rem; font-weight: 500; color: var(--ink-soft);
  background: var(--paper); border: 1px solid var(--edge-ctl);
  border-radius: var(--radius); transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.pick:hover span { border-color: var(--accent); }
.pick input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Chosen, not shouted. ONE FILLED BUTTON PER SURFACE — the filled accent on
   this form belongs to "Put my name down", and a pair of filled answers above
   it spends that attention twice. The soft accent with its own ink is a
   designed pair in both themes; the inset ring does the work of a fill. */
.pick input:checked + span {
  background: var(--accent-soft); color: var(--accent-ink); font-weight: 600;
  border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent);
}

/* Hidden only where the script that reveals it is running. */
[data-js] .askbox--new { display: none; }
[data-js] .askbox--new.is-open { display: block; }

/* Two ways to finish one form, with the quieter one below the line. */
.orline { display: flex; align-items: center; gap: var(--space-3); color: var(--ink-muted); font-size: .85rem; }
.orline::before, .orline::after { content: ""; flex: 1 1 auto; height: 1px; background: var(--paper-edge); }

/* ==========================================================================
   THE SQUARES — SERVICES AND COURSES
   --------------------------------------------------------------------------
   The cards module shipped its markup with no stylesheet behind it: every
   class below was already being printed by _card_grid.php, cards.php and
   card.php, and none of them meant anything here. A site that switched the
   module on got an unstyled list of links.

   Harvested from the site the module itself came from, with its one mint
   palette taken out: everything that was painted in that site's own green is
   drawn in --accent, so the squares wear whatever colour the site chose on
   the Look screen rather than somebody else's brand.

   The grid does not stretch to make the rows even. Every square is the same
   height because the summary is capped at four lines, so a long description
   cannot push one square out of line with its neighbours.
   ========================================================================== */

.card-grid {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
  margin: var(--space-6) 0;
  padding: 0;
  list-style: none;
}
.card-grid > li { display: flex; }

.card-square {
  display: flex;
  flex-direction: column;
  width: 100%;
  background: var(--paper-raised);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  box-shadow: var(--shadow-sm);
  transition: transform .18s var(--ease), box-shadow .18s var(--ease), border-color .18s var(--ease);
}
.card-square:hover,
.card-square:focus-visible {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
  border-color: var(--accent);
  text-decoration: none;
}
/* The whole square is the link, so the focus ring belongs to the square and
   not to the words inside it. */
.card-square:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/* Every picture area is the same shape whether there is a picture in it or
   not. Giving the empty ones a shallower box made the cards in a row
   different heights, and a grid whose cards do not line up reads as broken
   rather than as varied. */
.card-square-media {
  aspect-ratio: 4 / 3;
  background: var(--paper-sunk);
  overflow: hidden;
  position: relative;
}
.card-square-media img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: center;
  display: block;
}

.card-square-body {
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  flex: 1;
}
.card-square h3 { margin: 0; font-size: 1.125rem; line-height: 1.3; }
.card-square-strap { margin: 0; color: var(--ink-soft); font-size: .95rem; }
.card-square-meta {
  margin: 0; font-size: .82rem; font-weight: 600;
  letter-spacing: .02em; color: var(--accent-ink);
}
/* Capped at four lines, which is what keeps the rows level. */
.card-square-summary {
  margin: 0; color: var(--ink-muted); font-size: .92rem; line-height: 1.55;
  display: -webkit-box; -webkit-line-clamp: 4; line-clamp: 4;
  -webkit-box-orient: vertical; overflow: hidden;
}
.card-square-more {
  margin-top: auto; padding-top: var(--space-3);
  font-weight: 600; color: var(--accent);
  display: inline-flex; align-items: center; gap: .35em;
}
.card-square-more .icon { transition: transform .18s var(--ease); }
.card-square:hover .card-square-more .icon { transform: translateX(3px); }

.card-square-flag {
  position: absolute; top: var(--space-3); left: var(--space-3);
  background: var(--accent); color: var(--on-accent);
  font-size: .72rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase;
  padding: .25em .7em; border-radius: var(--radius-pill);
}
/* Without a picture to sit on, "Popular" is a line of the card rather than a
   badge floating over a corner. */
.card-square-flag.is-inline {
  position: static; align-self: flex-start;
  margin-bottom: calc(var(--space-1) * -1);
}

/* ---- a card with no picture -----------------------------------------------
   The placeholder these cards used to carry was a block of colour with the
   site's mark in it. One looked deliberate; twelve down a services page
   looked like the page had failed to load. A card with nothing to show now
   shows nothing and leads with its title, under a short rule that keeps the
   colour present as an accent rather than as a surface. */
.card-square.is-textual .card-square-body { padding-top: var(--space-5); gap: var(--space-3); }
.card-square.is-textual .card-square-body::before {
  content: ''; display: block;
  width: 34px; height: 3px; border-radius: 2px;
  background: var(--accent); margin-bottom: var(--space-2);
}
/* The title carries the card on its own here, so it is allowed to be a
   little larger than one sitting under a photograph. */
.card-square.is-textual h3 { font-size: 1.22rem; }
/* The rule picks up the hover as the border does, so the whole square still
   reads as one object. */
.card-square.is-textual:hover .card-square-body::before,
.card-square.is-textual:focus-visible .card-square-body::before { background: var(--accent-hover); }

/* ---- the page behind a square --------------------------------------------- */
.crumbs { margin-bottom: var(--space-4); display: flex; align-items: center; gap: .4em; flex-wrap: wrap; font-size: .92rem; color: var(--ink-muted); }
.crumbs a { display: inline-flex; align-items: center; gap: .35em; }

.card-page-head { display: grid; gap: var(--space-5); margin-bottom: var(--space-6); }
@media (min-width: 800px) {
  .card-page-head { grid-template-columns: 1fr 1fr; align-items: center; }
  /* One column when the card has no picture, or the writing is squeezed into
     half a screen with nothing beside it. */
  .card-page-head:not(:has(img)) { grid-template-columns: 1fr; }
}
.card-page-head img { width: 100%; border-radius: var(--radius-lg); display: block; }

.card-facts { list-style: none; padding: 0; margin: var(--space-4) 0; display: grid; gap: var(--space-2); }
.card-facts li { display: flex; gap: var(--space-3); align-items: baseline; }
.card-facts .k { color: var(--ink-muted); min-width: 6.5em; font-size: .9rem; }

.card-nav { display: flex; justify-content: space-between; gap: var(--space-4); margin-top: var(--space-7); flex-wrap: wrap; }
/* These buttons carry a course title, and some of those are a sentence. On a
   narrow screen the pair stacks and each is allowed to wrap, or one button
   alone is wider than the phone. */
.card-nav .btn { max-width: 100%; white-space: normal; text-align: left; min-width: 0; }
@media (max-width: 599px) {
  .card-nav { flex-direction: column; align-items: stretch; }
  .card-nav .btn { justify-content: flex-start; }
}

/* "There is nothing here yet" — an answer, so it is given the weight of one
   rather than left as a stray sentence. */
.empty-note {
  display: flex; align-items: flex-start; gap: var(--space-3);
  margin: var(--space-6) 0; padding: var(--space-5);
  background: var(--paper-sunk);
  border: 1px dashed var(--paper-edge);
  border-radius: var(--radius);
  color: var(--ink-muted);
}
.empty-note svg { flex: 0 0 auto; margin-top: .1em; color: var(--accent); }

/* ==========================================================================
   THE DIARY — CHOOSING A TIME AND BOOKING IT
   --------------------------------------------------------------------------
   The screens at /book: the kinds of appointment, the strip of days, the
   times inside a day, and the summary of what is about to be booked.

   Same provenance as the squares above, and the same treatment: what was
   painted in one site's green is drawn in --accent here. A day with nothing
   free is shown greyed rather than hidden, because an empty Tuesday is
   information and a calendar that silently skips days looks broken.
   ========================================================================== */

.booking-types {
  display: grid; gap: var(--space-4);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
  padding: 0; list-style: none;
}
.booking-types > li { display: flex; }
.booking-type-card {
  display: flex; flex-direction: column; gap: var(--space-2);
  width: 100%; height: 100%;
  padding: var(--space-5);
  border: 1px solid var(--paper-edge); border-radius: var(--radius-lg);
  background: var(--paper-raised); text-decoration: none; color: inherit;
  transition: border-color .18s var(--ease), transform .18s var(--ease), box-shadow .18s var(--ease);
}
.booking-type-card:hover,
.booking-type-card:focus-visible {
  border-color: var(--accent); transform: translateY(-2px);
  box-shadow: var(--shadow-lg); text-decoration: none;
}
.booking-type-card h2, .booking-type-card h3 { margin: 0; font-size: 1.05rem; }
.booking-type-card .btn { margin-top: auto; align-self: flex-start; }

/* ---- the strip of days ---- */
.booking-cal { margin: var(--space-5) 0; }
/* It scrolls inside itself. Without min-width:0 a flex or grid child refuses
   to shrink below its content, and a fortnight of days pushes the whole page
   sideways instead of scrolling. */
.booking-cal, .booking-cal-days { min-width: 0; max-width: 100%; }
.booking-cal-days {
  display: flex; gap: var(--space-2); overflow-x: auto;
  padding: 0 0 var(--space-3); margin: 0; list-style: none;
  scroll-snap-type: x proximity; scrollbar-width: thin;
}
.booking-cal-day {
  flex: 0 0 auto; scroll-snap-align: start;
  min-width: 5.5rem; padding: var(--space-3) var(--space-4);
  border: 1px solid var(--paper-edge); border-radius: var(--radius);
  background: var(--paper-raised); text-align: center;
  text-decoration: none; color: inherit;
  display: flex; flex-direction: column; gap: .15em; line-height: 1.2;
}
.booking-cal-day:hover { border-color: var(--accent); text-decoration: none; }
.booking-cal-day .dom { display: block; font-size: 1.3rem; font-weight: 600; line-height: 1.15; }
.booking-cal-day .dow,
.booking-cal-day .mon {
  display: block; font-size: .78rem; color: var(--ink-muted);
  text-transform: uppercase; letter-spacing: .04em;
}
.booking-cal-day .how,
.booking-cal-day .free {
  display: block; margin-top: .25em; font-size: .72rem;
  color: var(--accent); font-weight: 600;
}
.booking-cal-day.is-selected {
  border-color: var(--accent); background: var(--accent);
  color: var(--on-accent); box-shadow: var(--shadow-sm);
}
.booking-cal-day.is-selected .dom,
.booking-cal-day.is-selected .dow,
.booking-cal-day.is-selected .mon,
.booking-cal-day.is-selected .how,
.booking-cal-day.is-selected .free { color: var(--on-accent); }
.booking-cal-day.is-empty { opacity: .45; pointer-events: none; }
.booking-cal-day.is-empty .how,
.booking-cal-day.is-empty .free { color: var(--ink-faint); }

/* ---- the times inside a day ---- */
.booking-slots {
  display: grid; gap: var(--space-2);
  grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
  list-style: none; padding: 0; margin: var(--space-4) 0;
}
.booking-slot {
  display: block; text-align: center; cursor: pointer;
  padding: var(--space-3);
  border: 1px solid var(--paper-edge); border-radius: var(--radius);
  background: var(--paper-raised); font-weight: 600; color: var(--ink);
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
/* The radio is the control; the chip around it is what anybody aims at. It is
   hidden rather than removed, so the keyboard and the screen reader still
   have a radio to work with. */
.booking-slot input { position: absolute; opacity: 0; pointer-events: none; }
.booking-slot:hover { border-color: var(--accent); }
.booking-slot.is-chosen,
.booking-slot:has(input:checked) {
  background: var(--accent); border-color: var(--accent-hover); color: var(--on-accent);
}
.booking-slot:has(input:focus-visible) { outline: 3px solid var(--focus); outline-offset: 2px; }
.booking-slots-empty {
  color: var(--ink-muted); padding: var(--space-5);
  background: var(--paper-sunk); border-radius: var(--radius);
}

/* ---- what is about to be booked, and what was ---- */
.booking-form { margin-top: var(--space-6); }
.booking-summary {
  background: var(--accent-soft); border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5); margin: var(--space-5) 0;
}
.booking-summary h2, .booking-summary h3 { margin: 0 0 var(--space-2); font-size: 1rem; }
/* Label and answer, rather than an indented list. */
.booking-summary dl,
.kv { display: grid; grid-template-columns: auto 1fr; gap: .4em var(--space-4); margin: 0; }
.kv { margin: var(--space-4) 0; }
.booking-summary dt, .kv dt { color: var(--ink-muted); font-size: .9rem; }
.booking-summary dd, .kv dd { margin: 0; font-weight: 600; }
@media (max-width: 480px) {
  .booking-summary dl, .kv { grid-template-columns: 1fr; gap: 0; }
  .booking-summary dd, .kv dd { margin-bottom: var(--space-3); }
}
.booking-confirm { text-align: center; padding: var(--space-7) 0; }
.booking-confirm .ref {
  display: inline-block;
  font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 1.05rem;
  background: var(--paper-sunk); padding: .4em .8em; border-radius: var(--radius-sm);
}

/* ==========================================================================
   DATES ON A COURSE PAGE (v1.36)
   --------------------------------------------------------------------------
   When, where, what it costs, how many places are left, and the button — on
   the page where somebody has just read what the course is.
   ========================================================================== */
.course-dates { margin: var(--space-7) 0 var(--space-6); }
.course-dates h2 { margin-bottom: var(--space-4); }
.date-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.date-row {
  display: grid; gap: var(--space-3); align-items: center;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) auto;
  padding: var(--space-4) var(--space-5);
  background: var(--paper-raised);
  border: 1px solid var(--paper-edge); border-radius: var(--radius);
}
.date-row.is-full { opacity: .65; }
.date-when strong { display: block; }
.date-where { color: var(--ink-muted); font-size: .92rem; }
.date-facts { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: baseline; }
.date-price { font-weight: 700; }
.date-left { color: var(--accent-ink); font-size: .9rem; font-weight: 600; }
.date-do { justify-self: end; }
.date-gone { color: var(--ink-muted); font-weight: 600; }
/* Three columns need a screen. Below that it reads as a block per date, with
   the button under the words rather than squeezed beside them. */
@media (max-width: 860px) {
  .date-row { grid-template-columns: 1fr; }
  .date-do { justify-self: start; }
}

/* ---------------------------------------------------------------------------
   What this site does — /features (v1.37.0)

   Every value here is a token, so this page follows the site's own colours and
   corners rather than carrying a look of its own.

   The one judgement worth writing down: an unused part keeps full contrast on
   its words. Only the tick and the tint go. Fading the text would say "you
   cannot have this", when the page exists to say the opposite.
   --------------------------------------------------------------------------- */
.features-head { max-width: var(--measure); margin-bottom: var(--space-7); }
.features-count {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  margin-top: var(--space-4); font-size: .9rem;
}
.features-key-on, .features-key-off { display: inline-flex; align-items: center; gap: .45em; }
.features-key-on::before, .features-key-off::before {
  content: ""; width: .7em; height: .7em; border-radius: var(--radius-pill);
}
.features-key-on::before  { background: var(--accent); }
.features-key-off::before { background: var(--paper-line); }
.features-key-on  { color: var(--accent-ink); font-weight: 500; }
.features-key-off { color: var(--ink-muted); }

.features-group { margin-bottom: var(--space-7); }
.features-group > h2 {
  font-size: .78rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-muted); font-weight: 500;
  padding-bottom: var(--space-2); margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--paper-line);
}
.features-grid {
  display: grid; gap: var(--space-3);
  grid-template-columns: repeat(auto-fill, minmax(255px, 1fr));
}

.feature {
  display: grid; align-content: start; gap: .35em;
  padding: var(--space-4);
  border: 1px solid var(--paper-edge); border-radius: var(--radius-lg);
  background: var(--paper-sunk); color: inherit; text-decoration: none;
  position: relative;
}
.feature.is-on { background: var(--paper-raised); border-color: var(--accent-soft); }
a.feature:hover { border-color: var(--accent); }
a.feature:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.feature-mark {
  display: grid; place-items: center; width: 22px; height: 22px;
  border-radius: var(--radius-pill); margin-bottom: .2em;
  background: var(--paper-edge); color: transparent;
}
.feature.is-on .feature-mark { background: var(--accent); color: var(--on-accent); }

.feature-name { font-weight: 600; line-height: 1.3; }
.feature-line { color: var(--ink-soft); font-size: .94rem; line-height: 1.5; }
.feature-state {
  margin-top: .5em; font-size: .78rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-faint);
  display: flex; flex-wrap: wrap; gap: .3em var(--space-3); align-items: center;
}
.feature.is-on .feature-state { color: var(--accent-ink); }
.feature-go { display: inline-flex; align-items: center; gap: .3em; }
a.feature:hover .feature-go { text-decoration: underline; }

.features-cta {
  margin-top: var(--space-6); padding: var(--space-5);
  border-radius: var(--radius-lg); background: var(--accent-soft);
}

@media (max-width: 520px) {
  .features-grid { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   The six panels brought over from a client site (v1.47.0)

   Every one wears the tokens, so a site that changes its colour or its
   spacing in Admin → Look changes these with it. Nothing here is a hard
   colour, and nothing here invents a second recipe for something the house
   already has: the figures reuse .stat-row, the buttons are .btn, the long
   words are .prose, the small print is .hint.
   -------------------------------------------------------------------------- */

/* ---- Opening panel ------------------------------------------------------ */
/* Three grid children rather than two, so a phone can take the picture out
   from underneath the figures and put it directly under the headline. */
.lede-grid {
  display: grid; gap: clamp(var(--space-5), 4vw, var(--space-7));
  grid-template-columns: 1fr; align-items: center;
}
.lede-pill {
  display: inline-block; margin: 0 0 var(--space-4);
  padding: .35rem .8rem; border-radius: 999px;
  background: var(--accent-soft); color: var(--accent-ink);
  font-size: .8rem; font-weight: 600; letter-spacing: .01em;
}
.lede-title {
  /* The one big move on a page. A site's opening panel is the only place
     type is allowed to be this size, and holding it back to section-heading
     size — which is what this used to be — is why an opening panel can read
     as a paragraph with ambition. A theme may push it further still. */
  margin: 0;
  font-size: clamp(2.6rem, 1.2rem + 5.6vw, 5.25rem);
  line-height: 1.02;
  letter-spacing: -.035em;
  text-wrap: balance;
}
.lede-accent { color: var(--accent); }
.lede-body {
  margin: var(--space-5) 0 0; max-width: 32em;
  font-size: clamp(1.05rem, 1rem + .35vw, 1.22rem); line-height: 1.65;
  color: var(--ink-soft);
}
.lede-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin: 0; }
.lede-stats { margin-top: var(--space-6); text-align: left; }
.lede-figure { border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow-lg); }
.lede-figure img { display: block; width: 100%; height: auto; }
.lede-figure.is-video { aspect-ratio: 16 / 9; }
.lede-figure.is-video iframe { display: block; width: 100%; height: 100%; border: 0; }

@media (min-width: 62rem) {
  .lede-grid {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
    grid-template-areas: "copy figure" "side figure";
    align-items: start; row-gap: var(--space-6);
  }
  .lede-copy   { grid-area: copy; }
  .lede-side   { grid-area: side; }
  .lede-figure { grid-area: figure; align-self: center; }
  .lede-stats  { text-align: left; grid-template-columns: repeat(auto-fit, minmax(min(7rem, 100%), 1fr)); }
}

/* ---- Photo band --------------------------------------------------------- */
/* The words sit in a panel rather than straight onto the picture, because
   text on a photograph is legible until somebody changes the photograph. */
.photoband { position: relative; }
.photoband-img { display: block; width: 100%; height: auto; max-height: 70vh; object-fit: cover; }
.photoband-words {
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
  padding: var(--space-5); margin: calc(var(--space-6) * -1) var(--space-5) 0;
  position: relative;
}
.photoband-words h2 { margin: 0; }
.photoband-words p  { margin: var(--space-3) 0 0; color: var(--ink-soft); }
.photoband-eyebrow {
  margin: 0 0 var(--space-2); font-size: .8rem; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: var(--ink-muted);
}
@media (min-width: 48rem) {
  .photoband.has-words .photoband-words {
    max-width: 34rem; margin-inline: auto;
  }
}

/* ---- Somebody's story --------------------------------------------------- */
.bio-grid { display: grid; gap: var(--space-6); grid-template-columns: 1fr; }
.bio-portrait { border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow); max-width: 20rem; }
.bio-portrait img { display: block; width: 100%; height: auto; }
.bio-words > h2 { margin-top: 0; }
.bio-inline { margin: var(--space-5) 0; }
.bio-inline img { display: block; width: 100%; height: auto; border-radius: var(--radius); box-shadow: var(--shadow); }
.bio-inline figcaption { margin-top: var(--space-2); font-size: .875rem; color: var(--ink-muted); }
.bio-creds-label {
  margin: var(--space-5) 0 var(--space-3); font-size: .8rem; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: var(--ink-muted);
}
.bio-creds { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-2); }
.bio-creds li {
  margin: 0; padding: .3rem .75rem; border-radius: 999px;
  background: var(--paper-sunk); border: 1px solid var(--paper-edge);
  font-size: .85rem; color: var(--ink);
}
.bio-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin: var(--space-5) 0 0; }
@media (min-width: 48rem) {
  .bio-grid { grid-template-columns: 20rem minmax(0, 1fr); align-items: start; gap: var(--space-7); }
  .bio-grid.no-portrait { grid-template-columns: minmax(0, 1fr); }
}

/* ---- Ways in ------------------------------------------------------------ */
.block-ways.is-tint { background: var(--accent-soft); }
.block-ways.is-warm { background: var(--paper-sunk); }
.block-ways.is-tint, .block-ways.is-warm { padding-block: var(--space-7); }
.ways-intro { max-width: 46em; color: var(--ink-soft); }
.ways-grid {
  list-style: none; margin: var(--space-6) 0 0; padding: 0;
  display: grid; gap: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
}
.way-card {
  margin: 0; position: relative; display: flex; flex-direction: column;
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
  padding: var(--space-5);
}
.way-card.has-badge { padding-top: var(--space-6); }
.way-card h3 { margin: 0; font-size: 1.15rem; }
.way-badge {
  position: absolute; top: calc(var(--space-3) * -1); left: var(--space-5);
  padding: .25rem .7rem; border-radius: 999px;
  background: var(--accent); color: #fff;
  font-size: .72rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
}
.way-price { margin: var(--space-2) 0 0; font-weight: 700; color: var(--accent); font-size: 1.05rem; }
.way-body  { margin: var(--space-3) 0 0; color: var(--ink-soft); font-size: .95rem; }
.way-do    { margin: var(--space-5) 0 0; padding-top: var(--space-3); margin-top: auto; }

/* ---- One offer, argued properly ---------------------------------------- */
.offerpanel {
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
  padding: clamp(var(--space-5), 3vw, var(--space-7));
}
.offerpanel-head h2 { margin: 0; }
.offerpanel-lead {
  margin: var(--space-3) 0 0; font-size: clamp(1.05rem, 1rem + .4vw, 1.3rem);
  font-weight: 600; color: var(--accent);
}
.offerpanel-body { display: grid; gap: var(--space-6); grid-template-columns: 1fr; margin-top: var(--space-5); }
.offerpanel-figure { display: flex; gap: var(--space-4); align-items: flex-start; margin: var(--space-5) 0; }
.offerpanel-round { flex: 0 0 auto; width: 140px; height: 140px; border-radius: 50%; object-fit: cover; box-shadow: var(--shadow); }
.offerpanel-points { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.offerpanel-points li { margin: 0; display: flex; gap: var(--space-2); align-items: flex-start; }
.offerpanel-points .icon { flex: 0 0 auto; margin-top: .2rem; color: var(--accent); }
.offerpanel-callout {
  margin-top: var(--space-5); padding: var(--space-4);
  background: var(--paper-sunk); border: 1px solid var(--paper-edge);
  border-radius: var(--radius);
}
.offerpanel-callout p:first-child { margin-top: 0; }
.offerpanel-callout p:last-child  { margin-bottom: 0; }
.offerpanel-prices {
  list-style: none; margin: var(--space-6) 0 0; padding: 0;
  display: grid; gap: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
}
.offerpanel-prices li {
  margin: 0; padding: var(--space-5); text-align: center;
  background: var(--paper); border: 1px solid var(--paper-edge); border-radius: var(--radius);
}
.offerpanel-prices h3 { margin: 0; font-size: 1.05rem; }
.offerpanel-price { margin: var(--space-2) 0 var(--space-4); font-size: 1.5rem; font-weight: 700; color: var(--accent); }
.offerpanel-price-note { margin: var(--space-2) 0 0; font-size: .9rem; color: var(--ink-soft); }
.offerpanel-quote {
  margin: var(--space-6) 0 0; padding: var(--space-5);
  background: var(--paper-sunk); border-radius: var(--radius); border: 1px solid var(--paper-edge);
}
.offerpanel-quote blockquote { margin: 0; font-size: 1.1rem; line-height: 1.5; }
.offerpanel-quote figcaption { margin-top: var(--space-3); font-weight: 600; color: var(--ink-muted); font-size: .9rem; }
.offerpanel-note { margin-top: var(--space-5); }
@media (min-width: 52rem) {
  .offerpanel-body { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); }
}

/* ---- Join the list ------------------------------------------------------ */
.joinform {
  display: grid; gap: var(--space-6); grid-template-columns: 1fr;
  background: var(--paper-raised); border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
  padding: clamp(var(--space-5), 3vw, var(--space-7));
}
.joinform-words h2 { margin-top: 0; }
.joinform-words p  { color: var(--ink-soft); margin-bottom: 0; }
.joinform-form input[type="text"],
.joinform-form input[type="email"] { width: 100%; }
.joinform-do { margin: var(--space-5) 0 0; }
/* Out of sight without display:none, which some bots check for. */
.joinform-trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
@media (min-width: 52rem) {
  .joinform { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center; gap: var(--space-7); }
}

/* --------------------------------------------------------------------------
   The sticky call to action, and two small things the new panels need.
   (v1.48.0)
   -------------------------------------------------------------------------- */

/* One thing, following somebody down the page. Deliberately a single pill
   rather than a bar: a bar across the bottom of a phone covers the content
   somebody is trying to read, and on iOS it fights the home indicator. */
.sticky-cta {
  position: fixed;
  right: var(--space-4);
  bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0px));
  z-index: 60;
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: .7rem 1.15rem;
  border-radius: 999px;
  /* --on-accent, not #fff. The accent is a deep blue in the light and a pale
     one at night, so a hard white here is 7.2:1 by day and 2.45:1 after dark —
     the one control on the page that follows you everywhere, unreadable in
     half the cases. The token already flips; this did not use it. */
  background: var(--accent); color: var(--on-accent);
  font-weight: 600; font-size: .95rem; text-decoration: none;
  box-shadow: var(--shadow-lg);
  transition: transform .15s ease, box-shadow .15s ease;
}
.sticky-cta:hover  { transform: translateY(-1px); color: var(--on-accent); }
.sticky-cta:active { transform: scale(.97); }
.sticky-cta:focus-visible { outline: 3px solid var(--accent-ink); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .sticky-cta, .sticky-cta:hover, .sticky-cta:active { transition: none; transform: none; }
}
/* Out of the way of anything that prints, and of a narrow phone in landscape
   where it would sit on top of the content. */
@media print { .sticky-cta { display: none; } }
@media (max-height: 26rem) { .sticky-cta { display: none; } }

.block-do { margin-top: var(--space-5); }
.buy-paused { margin: 0; }

/* --------------------------------------------------------------------------
   The announcement bar in the dark (v1.48.0)

   It was written as a straight inversion — ink background, paper text — which
   is right in the light and wrong at night, where it becomes the brightest
   thing on an otherwise dark page and pulls the eye away from the words
   underneath it. At night it is a raised band with a hairline instead, which
   still reads as "not the page" without shouting.
   -------------------------------------------------------------------------- */
:root[data-theme="dark"] .nbar {
  background: var(--paper-raised);
  color: var(--ink);
  border-bottom: 1px solid var(--paper-edge);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .nbar {
    background: var(--paper-raised);
    color: var(--ink);
    border-bottom: 1px solid var(--paper-edge);
  }
}

/* --------------------------------------------------------------------------
   The newsletter's own pages (v1.49.1)

   The archive, an issue, and the three small pages a subscribe link lands on
   came into Starting Point from a client site with that site's class names
   still on them — .wrap, .lede, .issue-card, .newsletter__card. None of those
   exist here, so all five drew with no gutter, no card and no maximum width:
   text flush to the left edge of the window and inputs the full width of a
   desktop. The markup now uses the house classes; what is left is the handful
   of things the house genuinely did not have — a grid of back issues.
   -------------------------------------------------------------------------- */

.issue-grid { margin-top: var(--space-6); }

.issue-card {
  display: flex; flex-direction: column;
  background: var(--paper-raised);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  overflow: hidden;
  text-decoration: none; color: inherit;
  transition: transform .15s var(--ease), box-shadow .15s var(--ease);
}
.issue-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
  color: inherit;
}
@media (prefers-reduced-motion: reduce) {
  .issue-card, .issue-card:hover { transition: none; transform: none; }
}
.issue-card__shot { display: block; }
.issue-card__shot img {
  display: block; width: 100%; height: 11rem; object-fit: cover;
}
.issue-card__body {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-5);
}
.issue-card__when {
  font-size: .72rem; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-muted);
}
.issue-card__title { font-size: 1.08rem; font-weight: 600; line-height: 1.3; color: var(--ink); }
.issue-card__summary { font-size: .92rem; color: var(--ink-soft); }
.issue-card__more {
  margin-top: var(--space-2); font-weight: 600; font-size: .92rem; color: var(--accent);
}
.issue-card:hover .issue-card__more { text-decoration: underline; text-underline-offset: .2em; }

/* One issue, read on the site. */
.issue-hero { border-radius: var(--radius-lg); overflow: hidden; margin-bottom: var(--space-6); }
.issue-hero img { display: block; width: 100%; height: auto; }
.issue-body { max-width: 68ch; margin-inline: auto; }
.issue-back { display: inline-flex; align-items: center; gap: .4rem; margin-bottom: var(--space-5); font-weight: 600; }

/* The sign-up panel at the foot of those pages. */
.newsletter-card {
  display: grid; gap: var(--space-6);
  padding: clamp(var(--space-5), 3vw, var(--space-7));
}
.newsletter-words h2 { margin-top: 0; }
.newsletter-words p  { color: var(--ink-soft); margin-bottom: 0; }
.newsletter-form input[type="text"],
.newsletter-form input[type="email"] { width: 100%; }
@media (min-width: 52rem) {
  .newsletter-card { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center; gap: var(--space-7); }
}

/* The sticky button, tucked away while something it would cover is on screen.
   Moved rather than hidden, so it is still in the page for anybody tabbing
   through, and so it comes back smoothly instead of blinking. (v1.49.2) */
.sticky-cta.is-tucked,
.sticky-cta.is-typing {
  transform: translateY(calc(100% + var(--space-5)));
  opacity: 0;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .sticky-cta.is-tucked, .sticky-cta.is-typing { transform: none; display: none; }
}
