/* Modernist — design-system tokens and component classes.
   Originally ported verbatim from the Claude Design "Modernist" system
   (project a22da24f-1988-437c-8db8-f074eacd9755); the token block below has
   since been retuned for the "Sea & Sky" direction (airy sky-tinted ground,
   sea-blue accent, a brighter cyan second accent, Work Sans, visible corner
   radius). The site is deliberately light only: see the "light only" note
   under the token block. This replaces the "Cardboard & Copper" warm kraft/brass direction
   at the client's request for a lighter, happier register; the *method* for
   deriving it (every pairing computed against the 4.5:1 AA floor, never
   eyeballed) carried over unchanged, which is why the ratios quoted
   throughout this file still hold. The font is
   loaded self-hosted through wp_enqueue_style() with preconnect + display=
   swap for better Core Web Vitals (see functions.php, ec_enqueue_assets),
   not Google Fonts' own @import. Retune tokens here. */

:root {
  /* One palette, light, for everyone. See the "light only" note below the
     token block for why there is no dark counterpart and why this line is
     load-bearing rather than decorative. */
  color-scheme: light;

  --color-bg: #eaf4fb;
  --color-surface: #d5e8f7;
  --color-text: #0e2839;
  /* Accent: sea blue, chosen (not eyeballed) to clear 4.5:1 against both
     --color-bg and --color-accent-100 on its own — it reads 5.04:1 and
     5.02:1. That test is the reason this is a deep sea blue rather than the
     bright sky blue the direction is named for: the light, happy end of this
     hue family lives in the 300/400 ramp steps, which is where the page
     actually shows it (the CTA band, the ghost numerals, hover states),
     while the base token has to stay dark enough to carry --color-inverse
     text on top of it. An earlier lapis blue on this site failed exactly
     that check at 3.98:1, which is why so much of the CSS leans on the
     accent-700 step; this palette does not repeat that mistake. Body-copy
     accents still use the 700 step: it needs the extra margin for small text
     in more places than the base color appears. */
  --color-accent: #106da3;
  --color-accent-2: #1c9dbd;
  --color-divider: color-mix(in srgb, #0e2839 40%, transparent);
  /* Fixed light value for content that sits on a photo, an accent-filled
     shape or another non-page surface (a video's running-time badge, the
     play-triangle icon, the checked-radio ring, the home hero's kicker and
     H1). Distinct from --color-bg on purpose even though both are light:
     --color-bg is the page, and these things are not on the page. Reaching
     for --color-bg here would read as "the light one" and would be wrong the
     moment either value moves. */
  --color-inverse: #f7fcff;

  /* Tonal ramps — a shared cool hue family (blue-grey neutral, sea-blue
     accent, cyan second accent) at a shared lightness scale, so the same step
     of any role matches the others in visual value. The lightness scale is
     the one the previous warm palette used, step for step: keeping it is what
     lets a hue change stay a hue change, rather than quietly restating every
     value relationship the layout was built on. */
  --color-neutral-100: #f5f9fc;
  --color-neutral-200: #e6eef5;
  --color-neutral-300: #cfdce8;
  --color-neutral-400: #a9bed2;
  --color-neutral-500: #849cb4;
  --color-neutral-600: #68809a;
  --color-neutral-700: #52667c;
  --color-neutral-800: #3c4d60;
  --color-neutral-900: #293745;

  --color-accent-100: #e8f4fc;
  --color-accent-200: #c7e4f7;
  --color-accent-300: #93cbee;
  --color-accent-400: #55aade;
  --color-accent-500: #2489c4;
  --color-accent-600: #106da3;
  --color-accent-700: #0d5680;
  --color-accent-800: #0a4160;
  --color-accent-900: #072c42;

  --color-accent-2-100: #e6f6fb;
  --color-accent-2-200: #c2e9f4;
  --color-accent-2-300: #8ed6ea;
  --color-accent-2-400: #4fbcd8;
  --color-accent-2-500: #1c9dbd;
  --color-accent-2-600: #107e9b;
  --color-accent-2-700: #0d647c;
  --color-accent-2-800: #0a4b5d;
  --color-accent-2-900: #073542;

  --font-heading: "Work Sans", "Work Sans Fallback", system-ui, sans-serif;
  --font-heading-weight: 800;
  --font-body: "Work Sans", "Work Sans Fallback", system-ui, sans-serif;

  --space-1: 4.0px;
  --space-2: 8.0px;
  --space-3: 12.0px;
  --space-4: 16.0px;
  --space-6: 24.0px;
  --space-8: 32.0px;

  /* A considered "decelerate" curve — the default `ease` most of the theme's
     interactive states used (or, in most cases, had no transition at all)
     reads as mechanical/instant next to this. Used everywhere a hover/focus/
     active state changes a colour or transform. Deliberately not used on
     :focus-visible outlines themselves — an animated focus ring delays the
     one piece of feedback that needs to be immediate. */
  --ease-premium: cubic-bezier(0.22, 1, 0.36, 1);

  /* The visible-radius language, replacing an earlier direction's sharp
     0-radius geometry — echoes the harp's curves rather than a gallery/
     blueprint aesthetic. Carried across the Sea & Sky repaint unchanged. Doesn't
     reach photos/video frames on its own; see site.css's .ec-slot/
     .ec-vid__frame, which need an explicit border-radius since they never
     referenced this token even when it was 0. */
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;

  /* Elevation — derived from the ground: soft ink-tinted shadows, mixed
     from --color-text rather than pure black so they read as the page's own
     shade. There is one ground, so there is one elevation ramp. */
  --shadow-sm: 0 1px 2px color-mix(in srgb, #0e2839 14%, transparent);
  --shadow-md: 0 3px 10px color-mix(in srgb, #0e2839 16%, transparent);
  --shadow-lg: 0 12px 32px color-mix(in srgb, #0e2839 22%, transparent);
}

/* — light only, on purpose —
   This site has ONE palette: the light one defined above. The
   @media (prefers-color-scheme: dark) block that used to sit here, with its
   own recomputed sea-blue ramp, was removed in September 2026 at the client's
   request: she prefers the light palette and wants every visitor to see it,
   including the ones whose operating system is set to dark. Please do not
   "restore" dark mode as a kindness. Removing it is the decision, not an
   oversight, and re-adding it silently changes what a visitor sees against
   the client's stated wish.

   `color-scheme: light` on :root, at the top of the token block, is the other
   half of that decision and is not optional. Without it a visitor on a
   dark-set OS keeps UA-rendered chrome in dark: form controls, the caret,
   autofill backgrounds, scrollbars and the default canvas, all painted dark
   against a page that is deliberately light. The contact form is where that
   shows first (dark fields, near-invisible text). The declaration tells the
   UA this document is light, so those controls follow the page.

   What survives from the deleted block's reasoning, because it still governs
   rules further down this file: base --color-accent (#106da3) is a
   *background/graphic* tone, paired with the fixed-light --color-inverse on
   top (buttons, the CTA band, the play-button disc, the checked-radio fill).
   Used as small foreground text on --color-bg it reads 5.04:1, which passes
   AA but is the thinnest margin in the theme, so every foreground accent role
   (the `a` rule, :focus-visible, .tag-outline, .card-kicker, .btn-ghost, the
   .radio states, the nav's current-page link) references --color-accent-700
   (#0d5680, 7.08:1 on --color-bg, AAA) instead. That indirection was
   introduced for dual-mode safety and is kept for contrast alone.

   AAA is a claim about THAT colour, not about the site. The inline-link and
   focus-ring accent-700 clears 7:1 on --color-bg and 6.28:1 on
   --color-surface; the 70%-ink muted family that carries table headers,
   .text-muted, form labels, .ec-meta, the footer's fine print and the phone
   tables' cell labels sits at 5.44:1, which is AA, and .ec-intro at 75% ink
   reaches 6.37:1. So: AAA for the link colour, AA everywhere else. Raising
   the muted family to about 78% ink would clear 7:1 site-wide, and would
   also close most of the visible distance between muted text and body text,
   which is a hierarchy decision for the client rather than a defect. */

body {
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
}
h1, h2, h3, h4 { font-family: var(--font-heading); font-weight: var(--font-heading-weight); }

/* No .grayscale rule in THIS file: it used to scope the sea-blue duotone onto
   photo-slot images, and before that carried a grayscale/contrast filter of
   its own. Both are gone and photographs render untinted, so nothing here
   claims the class. It is not dead, though: site.css keeps a hover and focus
   lift on `.grayscale .ec-slot img` for photographs that are real links, so
   the wrapper survives in the views as that hook and nothing else. */

/* ══════════════════════════════════════════════════════════════════════════
   Components — built with the tokens above. Plain CSS
   on plain HTML: no JavaScript, no build step. Each class is documented in
   readme.md and demonstrated in foundations/ and components/.
   ══════════════════════════════════════════════════════════════════════ */

*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font-size: 15px; line-height: 1.55; font-weight: 400; }
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  line-height: 1.12; letter-spacing: -0.015em; margin: 0 0 var(--space-2);
}
h1 { font-size: 42px; }
h2 { font-size: 32px; }
h3 { font-size: 25px; }
h4 { font-size: 20px; }
h5 { font-size: 16px; }
h6 { font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase; }
p { margin: 0 0 var(--space-3); }
/* accent-700, not the base accent: the base is chosen dark enough to work
   as a *background* (buttons, the CTA band) with --color-inverse text on
   top; used directly as small foreground text or a focus outline it reads
   5.04:1 against --color-bg and 4.47:1 against --color-surface. Those are
   two different answers and only one of them passes: base --color-accent is
   AA on the page ground and BELOW AA on a .card. Nothing in the rendered DOM
   pairs them today, so this is a trap rather than a failure, and the
   constraint is the point of this comment: --color-accent is a background
   tone, and as foreground text it may only be used on --color-bg.
   700 (#0d5680) is the step verified for the foreground role on both, at
   7.08:1 on --color-bg — AAA for this one colour, which is not a claim
   about the site as a whole; see the AAA paragraph in the "light only" note
   above the body rule. */
/* A full-strength underline at the browser's default thickness reads as
   heavier than the text it sits under; premium editorial type usually backs
   the line off so it carries as a quiet wayfinding cue rather than a bar.
   currentcolor (not a token) so this tracks whatever `color` any more
   specific hover rule sets, without duplicating that logic here. */
a {
  color: var(--color-accent-700); text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, currentcolor 50%, transparent);
  transition: color .2s var(--ease-premium), text-decoration-color .2s var(--ease-premium);
}
a:hover { text-decoration-color: currentcolor; }
img { display: block; max-width: 100%; }
figure { margin: 0; }
figcaption {
  font-size: 11px; margin-top: var(--space-1);
  /* 70%, matching .text-muted below: 55% reads 3.49:1 against --color-bg,
     under the 4.5:1 AA floor for 11px text. This rule was originally bumped
     to 65% under an older palette (4.95:1 there) but a later repaint left it
     at only 4.67:1 — passing, but thinner margin than
     everything else this same pass standardized on. 70% clears 5.44:1. */
  color: color-mix(in srgb, var(--color-text) 70%, transparent);
}
/* 70%, not 55%: at 55% this muted text reads 3.49:1 on --color-bg, under the
   4.5:1 AA floor for normal text (Lighthouse's bundled axe misses it because
   it doesn't resolve color-mix; axe-core 4.13 flags it). 70% clears 5.44:1 on
   --color-bg and 5.12:1 on --color-surface, still visibly secondary to body
   text. Same reasoning for the .table th, .ec-meta and inline muted notes
   bumped from 55/60% in this pass. */
.text-muted { color: color-mix(in srgb, var(--color-text) 70%, transparent); }
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--color-accent-700); outline-offset: 2px; }
::selection { background: color-mix(in srgb, var(--color-accent) 30%, transparent); }

/* — rules — */
.hr {
  height: 2px; border: 0; margin: var(--space-4) 0;
  background: var(--color-divider);
}

/* — buttons — */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  cursor: pointer; text-decoration: none;
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 14px; line-height: 1.2; color: var(--color-text); /* matches the .input's 14px —
     the pair sits side by side in sign-up rows */
  background: transparent; border: 1px solid transparent;
  padding: var(--space-2) calc(var(--space-3) * 1.2);
  border-radius: var(--radius-md);
  transition: background-color .2s var(--ease-premium), border-color .2s var(--ease-premium),
    color .2s var(--ease-premium), box-shadow .2s var(--ease-premium), transform .15s var(--ease-premium);
}
.btn svg { display: block; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
/* Tactile press feedback — absent everywhere before this, on any control.
   Faster than the colour/shadow transitions above (.15s vs .2s) so the
   "give" reads as an immediate response to the press, not part of the same
   settle. Scales from center on any .btn shape, .btn-block included. */
.btn:active { transform: scale(0.97); }
/* .btn is inline-flex by intent, but a flex item's display is blockified, so
   inside .card (a column flex container) the button stretched to the full card
   width — 996px on desktop for the teaching and events CTAs, which is not what
   inline-flex asks for. .btn-block is the deliberate full-width opt-in and is
   excluded, which is also the evidence the stretch was accidental: a separate
   opt-in only makes sense if the default is not already full width. */
.card > .btn:not(.btn-block) { align-self: start; }
/* color: --color-inverse, not --color-bg — this sits on the accent fill
   itself, not the page. --color-inverse is the fixed light value for type on
   a non-page surface (see its own comment in the token block); --color-bg is
   the page, and using it here would tie the button's label to a token that
   describes something the button is not sitting on. The pair reads 5.44:1. */
/* Shadow only on -primary, not -secondary/-ghost: the whole point of the
   three-tier hierarchy is that primary carries the most visual weight, so
   giving every tier the same lift would blur the distinction it exists to
   make. Ink-tinted --shadow-sm/md (not accent-tinted), so this reads
   correctly regardless of which background the button sits on — the plain
   page, the nav, or the CTA band's gradient. */
/* --color-accent-600 IS --color-accent (#106da3), so hovering to the 600 step
   was a no-op: the button changed its shadow and nothing else. Hover goes a
   real step darker, which is both a visible affordance and a contrast gain for
   the white label: 5.44:1 at rest, 7.64:1 hovered, 10.49:1 active. The nav's
   Booking chip overrides all three in site.css and is unaffected. */
.btn-primary { background: var(--color-accent); color: var(--color-inverse); box-shadow: var(--shadow-sm); }
.btn-primary:hover { background: var(--color-accent-700); box-shadow: var(--shadow-md); }
.btn-primary:active { background: var(--color-accent-800); }
.btn-secondary { border-color: var(--color-divider); }
.btn-secondary:hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.btn-secondary:active { background: color-mix(in srgb, var(--color-text) 14%, transparent); }
.btn-ghost { color: var(--color-accent-700); padding-inline: var(--space-1); }
.btn-ghost:hover { background: color-mix(in srgb, var(--color-accent) 10%, transparent); }
.btn-ghost:active { background: color-mix(in srgb, var(--color-accent) 18%, transparent); }
.btn-icon { width: 36px; height: 36px; padding: 0; }
.btn-block { width: 100%; margin-top: var(--space-2); justify-content: flex-start; text-align: left; }

/* — forms — */
.field > label {
  display: block; font-size: 12px; margin-bottom: 5px;
  color: color-mix(in srgb, var(--color-text) 70%, transparent);
}
.input {
  width: 100%; min-height: 36px; padding: 6px 10px; font: inherit;
  font-size: 14px; color: var(--color-text); caret-color: var(--color-accent);
  background: var(--color-surface);
  border: 1px solid var(--color-divider); border-radius: var(--radius-md);
  transition: border-color .2s var(--ease-premium);
}
/* The one remaining AA failure on the site, and it was a hole rather than a
   wrong value: .input named no ::placeholder colour at all, so the contact
   form's five placeholders rendered at Chrome/Firefox's UA default #757575
   on the field's --color-surface ground, 3.67:1. 70% ink is the value every
   other muted role in this theme was standardised on and reads 5.09:1 on
   --color-surface, which keeps a placeholder clearly secondary to the 100%
   ink of what the visitor types into it while staying above the 4.5:1 floor.
   Both selectors are needed: ::placeholder is the standard, ::-ms-input-
   placeholder is what legacy Edge still answers to, and a UA that does not
   recognise one of them drops that whole rule rather than the declaration. */
.input::placeholder { color: color-mix(in srgb, var(--color-text) 70%, transparent); opacity: 1; }
.input::-ms-input-placeholder { color: color-mix(in srgb, var(--color-text) 70%, transparent); }
.input:hover { border-color: color-mix(in srgb, var(--color-text) 45%, transparent); }
.input:focus-visible { border-color: var(--color-accent-700); outline-offset: 0; }
textarea.input { min-height: 90px; resize: vertical; }
.radio { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-size: 14px; }
.radio input, .seg-opt input {
  position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;
}
.radio .dot {
  width: 16px; height: 16px; flex: none; border-radius: 50%;
  border: 1.5px solid var(--color-divider);
  transition: border-color .2s var(--ease-premium), background-color .2s var(--ease-premium);
}
.radio:hover .dot { border-color: var(--color-accent-700); }
.radio input:checked + .dot {
  /* Fill stays the base accent (paired with the fixed-light --color-inverse
     ring below, same reasoning as the `a` rule's comment) rather than 700 —
     the point here is a solid brand-colour disc, not small foreground text. */
  border-color: var(--color-accent); background: var(--color-accent);
  box-shadow: inset 0 0 0 4px var(--color-inverse);
}
.radio input:focus-visible + .dot { outline: 2px solid var(--color-accent-700); outline-offset: 2px; }
.seg {
  display: inline-flex; overflow: hidden;
  border: 1px solid var(--color-divider); border-radius: var(--radius-md);
}
.seg-opt {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 12px; font-size: 13px; cursor: pointer;
  transition: background-color .2s var(--ease-premium), color .2s var(--ease-premium);
}
.seg-opt + .seg-opt { border-left: 1px solid var(--color-divider); }
.seg-opt:has(input:checked) { background: var(--color-accent); color: var(--color-inverse); }
.seg-opt:not(:has(input:checked)):hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.seg-opt:has(input:focus-visible) { outline: 2px solid var(--color-accent-700); outline-offset: -2px; }

/* — cards — */
.card {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-3); border-radius: var(--radius-md); background: var(--color-surface);
  /* --shadow-md existed as a token from the start but had no live consumer
     (.elev-* is unused everywhere, spot-checked) until now — gives the
     teaching/press callout cards real physical presence instead of reading
     as a flat colour swatch. */
  box-shadow: var(--shadow-md);
}
.card-kicker { font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-accent-700); }
.card-title {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 17px; line-height: 1.2;
}
.card-body { margin: 0; font-size: 13px; opacity: 0.8; flex: 1; }
.card-meta {
  display: flex; align-items: center; gap: 6px; font-size: 11px;
  /* 70%, not 50%: at 50% this reads 3.04:1 on --color-bg and 2.94:1 on
     --color-surface (where a card actually sits), well under the 4.5:1 AA
     floor for 11px text. Same value and same reasoning as .text-muted and
     .table th above — no component should be able to reach for a muted ink
     that fails the audit just because it has no live consumer today. */
  color: color-mix(in srgb, var(--color-text) 70%, transparent);
}
.elev-sm { box-shadow: var(--shadow-sm); }
.elev-md { box-shadow: var(--shadow-md); }
.elev-lg { box-shadow: var(--shadow-lg); }

/* — tags — */
/* text-decoration:none — .tag-outline's one live use (press.php's download
   links) is an <a>, which inherits the base `a` rule's underline otherwise.
   The pill's own border/fill already carries the "this is a control" signal;
   a line cutting through the label on top of that reads as unintended,
   the way it would on any chip/badge/button-shaped element. */
.tag {
  display: inline-flex; align-items: center; font-size: 11px;
  letter-spacing: 0.02em; padding: 3px 10px; text-decoration: none;
  border-radius: calc(var(--radius-md) * 0.75);
  transition: background-color .2s var(--ease-premium), color .2s var(--ease-premium), border-color .2s var(--ease-premium);
}
.tag-accent { background: var(--color-accent-100); color: var(--color-accent-800); }
.tag-accent-2 { background: var(--color-accent-2-100); color: var(--color-accent-2-800); }
.tag-neutral { background: var(--color-neutral-100); color: var(--color-neutral-800); }
.tag-outline { border: 1px solid var(--color-accent-700); color: var(--color-accent-700); }
/* .tag-outline's one live use (press.php's download links) had no hover
   feedback at all before this — not even a colour or cursor change. */
.tag-outline:hover { background: var(--color-accent-100); }

/* — navigation — */
.nav {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-bottom: 2px solid var(--color-divider);
}
.nav-brand {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 18px; margin-right: auto;
}
.nav a { color: inherit; text-decoration: none; font-size: 14px; }
/* accent-600, not the base accent: a hover-only shift needs to read as
   *different* from the resting/active accent-700 colour (see render.php's
   inline aria-current style), not just repeat it — 600 is the step verified
   as a distinguishable step lighter than 700 that still clears AA (5.04:1)
   against the bar's --color-bg ground. */
.nav a:hover, .nav a[aria-current='page'] { color: var(--color-accent-600); }

/* — tables — */
.table { width: 100%; border-collapse: collapse; font-size: 14px; }
.table th {
  text-align: left; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  /* 70%, not 60%: 60% reads 4.03:1 on --color-bg, under 4.5:1 AA for this 11px
     header text (see .text-muted's note for why axe-core 4.13 catches this and
     Lighthouse's older bundled axe does not). 70% clears 5.44:1. */
  color: color-mix(in srgb, var(--color-text) 70%, transparent);
  padding: var(--space-2); border-bottom: 2px solid var(--color-accent-700);
}
.table td {
  padding: var(--space-2);
  border-bottom: 1px solid var(--color-divider);
}
.table tbody tr:hover { background: color-mix(in srgb, var(--color-text) 4%, transparent); }

/* — dialog — */
.dialog-backdrop {
  position: fixed; inset: 0; display: grid; place-items: center;
  padding: var(--space-4);
  background: color-mix(in srgb, var(--color-neutral-900) 50%, transparent);
}
.dialog {
  width: min(440px, 100%); display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-4); border-radius: var(--radius-lg);
  background: var(--color-surface); box-shadow: var(--shadow-lg);
}
.dialog-title {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 20px;
}
.dialog-body { font-size: 14px; opacity: 0.85; }
.dialog-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-2); }
