/* ==========================================================================
   Psychotherapist Theme – Main Stylesheet
   Mobile-first, RTL (CSS logical properties), WCAG 2.1 AA
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. CSS Custom Properties (Design Tokens)
   -------------------------------------------------------------------------- */
:root {
  /* Colors – "Sand & Stone" palette (palette board round 2, approved 2026-08-17).
     The organising idea is a THREE-RUNG WARM NEUTRAL LADDER rather than the
     white-vs-dark binary this file used to have. Depth comes from lightness
     order, not from borders, and every neutral sits inside one narrow hue band
     (37°–38°) so the page never changes temperature on the way down. The hero
     photograph is the origin of the palette, not an exception to it.

     Ladder:  recessed 85%  <  ground 92%  <  raised 97%  (all hue 37°–38°)
     Grounds ~90% of any viewport; ink ~8%; terracotta is a budgeted accent. */
  --color-primary:        #9E4C34;   /* terracotta, from the woven sphere – CTAs, links, small marks. 5.1:1 on ground (AA) */
  --color-primary-dark:   #7F3B27;   /* terracotta deep – hover/focus, AND all coloured text on the recessed band. 7.1:1 (AAA) */
  --color-primary-medium: #9E4C34;   /* kept for legacy rules – same as terracotta */
  --color-primary-light:  #C9A183;   /* decorative only, never text */
  --color-secondary:      #75565C;   /* muted mauve (hue 350°) – the counterweight: quotes, labels, informational accents.
                                        Cooler than terracotta so "information" still reads apart from "action", but still
                                        on the warm side of the wheel, so it does not break the single-temperature rule.
                                        Replaces the old spruce #4E6C76, which was hue 195° and read as foreign. 5.6:1 (AA) */
  --color-secondary-dark: #5C4247;   /* hover/focus */
  --color-secondary-light: #B49AA0;  /* decorative use only */
  --color-accent:         #EBD7BB;   /* soft warm tint (plates behind photos) */

  /* --- the neutral ladder ------------------------------------------------ */
  --color-bg-light:       #E9DECB;   /* RECESSED (85%) – alternating section ground. Ink 11.8:1.
                                        Note: the accent is only 4.46:1 here because accent and ground share a hue
                                        family, so coloured text on .bg-light uses --color-primary-dark instead
                                        (6.17:1). See the .bg-light rule in section 6. */
  --color-bg:             #F5EDE0;   /* GROUND (92%) – the page itself. Sand, not paper: light enough to read on,
                                        dark enough that the raised rung above it reads as lit. */
  --color-surface:        #FCF8F1;   /* RAISED (97%) – cards, form fields, filter pills, the floating dock.
                                        This rung is what replaced pure #ffffff. A card lighter than its ground
                                        does not need an outline to exist, which is why borders are hairlines now. */
  --color-bg-dark:        #332A20;   /* roasted umber (hue 32°) – footer: the warm anchor closing a warm page.
                                        Replaces #212B30, which was hue 200° and closed the page cold. */

  --color-text:           #2B2118;   /* ink – deep umber, hue 28° at 28% saturation. Deliberately NOT neutral grey:
                                        text is the thing the eye is fixed on, so cool ink chills the whole page
                                        even when the grounds are warm. 13.6:1 on ground (AAA) */
  --color-text-light:     #5C4E41;   /* soft ink – 6.9:1 (AA) */
  /* The hero used to carry two softened inks of its own, for type sitting on a
     veiled photograph. The split hero puts its title on open sky instead and
     uses --color-text at full strength there, measuring 9.9:1 or better behind
     every line, so both tokens were retired. */
  --color-text-muted:     #6D5F50;
  --color-white:          #FCF8F1;   /* NOT pure white. Same value as --color-surface: this token now means
                                        "the lightest tint in the palette", used for labels on dark/coloured
                                        grounds. Pure #ffffff survives in exactly one place – the WhatsApp
                                        glyph – because that is brand colour, not palette. */
  --color-border:         #DED0B9;
  --color-error:          #A03B29;
  --color-success:        #3B6A4E;

  /* Typography */
  --font-serif:   'Frank Ruhl Libre', 'David Libre', 'Times New Roman', serif;
  --font-primary: 'Heebo', 'Arial Hebrew', Arial, sans-serif;
  --font-size-base: 1.0625rem;     /* 17px – body */
  --font-size-sm:   0.875rem;      /* 14px */
  --font-size-lg:   1.125rem;      /* 18px */
  --font-size-xl:   1.25rem;       /* 20px */
  --font-size-2xl:  clamp(1.6rem, 1.2rem + 2vw, 2.25rem);   /* section headings */
  --font-size-3xl:  clamp(1.9rem, 1.4rem + 2.5vw, 2.75rem);
  --font-size-4xl:  clamp(2.25rem, 1.6rem + 3.2vw, 3.4rem);
  --font-size-display: clamp(3.1rem, 1.8rem + 7.5vw, 5.75rem); /* hero name */
  --line-height:    1.7;

  /* Spacing */
  --space-xs:   0.25rem;
  --space-sm:   0.5rem;
  --space-md:   1rem;
  --space-lg:   1.5rem;
  --space-xl:   2rem;
  --space-2xl:  3rem;
  --space-3xl:  4rem;
  --space-4xl:  6rem;
  /* "space after paragraph" – a touch more air between prose paragraphs
     than the default, applied consistently across every section. */
  --space-paragraph: 1.4rem;
  /* Gap between a big section heading and the copy it introduces. Deliberately
     tighter than --space-xl (the old value) so the heading reads as attached to
     its own content, while the much larger --section-pad above it still keeps
     the section-to-section separation obvious. Change here = change site-wide. */
  --space-heading-gap: 1.5rem;

  /* Layout */
  --container-max:   1200px;
  --container-pad:   1rem;
  --header-height:   64px;
  --section-pad:     3rem;
  /* Space reserved at the foot of the page for the fixed contact dock.
     The dock measures 60x115px and sits 20px off the bottom, so it permanently
     owns the last 135px of the viewport – and at the document's resting
     position (max scroll) that band is the end of the footer. Measured at
     360/390/414/480/560/640/768/900/1024/1200px, it trapped .footer-copyright
     and .footer-credit underneath itself on every one of them.
     118px + the footer's own 32px = 150px, which clears the dock with a small
     gap. Released at >=1440 (see the bottom of this file), where the centred
     footer text no longer reaches the dock's column.
     The dock itself is never hidden: it is a primary contact channel. */
  --dock-clearance:  118px;

  /* Borders */
  --radius-sm:   4px;
  --radius-md:   8px;
  --radius-lg:   16px;
  --radius-full: 9999px;

  /* Shadows – mixed from the palette's ink (#2B2118), not neutral black.
     A black shadow over a warm ground reads as grey haze, which was a large
     part of the old "technical project" feel. */
  --shadow-sm:  0 1px 3px rgba(43, 33, 24, .07);
  --shadow-md:  0 4px 12px rgba(43, 33, 24, .09);
  --shadow-lg:  0 8px 24px rgba(43, 33, 24, .11);

  /* Transitions */
  --transition: 200ms ease;
}

/* --------------------------------------------------------------------------
   2. Reset & Base
   -------------------------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
  font-size: 100%;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* `hidden` first as the fallback, then `clip`. `clip` is the stronger of the
     two here: it does not turn the root into a scroll container, so there is
     nothing left for a finger drag or a momentum fling to pan – which is what
     let the page slide sideways on touch devices even though `hidden` was
     already set. `overscroll-behavior-x: none` kills the rubber-band too. */
  overflow-x: hidden;
  overflow-x: clip;
  overscroll-behavior-x: none;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }
}

body {
  font-family: var(--font-primary);
  font-size: var(--font-size-base);
  line-height: var(--line-height);
  color: var(--color-text);
  background-color: var(--color-bg);
  direction: rtl;
  overflow-x: hidden;
  overflow-x: clip;
  overscroll-behavior-x: none;
}

img,
picture,
video,
iframe {
  max-width: 100%;
  display: block;
}

img {
  height: auto;
}

button,
input,
select,
textarea {
  font-family: inherit;
  font-size: inherit;
}

a {
  color: var(--color-primary);
  text-decoration: underline;
}

a:hover,
a:focus-visible {
  color: var(--color-primary-dark);
}

:focus-visible {
  outline: 3px solid var(--color-primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* Prevent horizontal scroll */
body,
.site-main,
.container {
  max-width: 100%;
}

/* --------------------------------------------------------------------------
   3. Accessibility Utilities
   -------------------------------------------------------------------------- */
.screen-reader-text,
.sr-only {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0,0,0,0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

.skip-link {
  position: fixed;
  inset-block-start: -100%;
  inset-inline-start: 50%;
  transform: translateX(50%);
  background: var(--color-primary);
  color: var(--color-white);
  padding: var(--space-sm) var(--space-lg);
  z-index: 9999;
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  text-decoration: none;
  font-weight: 700;
}

/* The skip link carries .screen-reader-text too, and that rule clamps it to a
   clipped 1x1 box with !important. Moving it into view was therefore not
   enough: on focus it was still 1x1 at the top edge and no keyboard user ever
   saw it. Everything clamped there has to be released here, with the same
   !important, or the clamp wins. WCAG 2.4.1 expects this link to be visible
   once it has focus - it was reachable the whole time, just invisible. */
/* :focus-visible is listed as well, and not only for symmetry: the generic
   `a:hover, a:focus-visible` rule further up sets the deep terracotta, and at
   (0,1,1) it outranks `.skip-link` at (0,1,0) - so the white this link asks for
   never reached it. Nobody could see that while the link was clipped to 1x1;
   the moment it became visible it was deep terracotta ON terracotta, 1.38:1.
   Pairing the class with a pseudo-class here makes it (0,2,0), which wins
   without reaching for !important. Measured after: 5.26:1. */
.skip-link:focus,
.skip-link:focus-visible {
  color: var(--color-white);
  inset-block-start: 0;
  position: fixed !important;
  width: auto !important;
  height: auto !important;
  margin: 0 !important;
  padding: var(--space-sm) var(--space-lg) !important;
  overflow: visible !important;
  clip: auto !important;
  clip-path: none !important;
  white-space: normal !important;
}

/* --------------------------------------------------------------------------
   4. Layout Utilities
   -------------------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}

.text-center { text-align: center; }
.text-start  { text-align: start; }

.bg-light { background-color: var(--color-bg-light); }
.bg-dark  { background-color: var(--color-bg-dark); color: var(--color-white); }

/* ACCESSIBILITY RULE – coloured text on the recessed rung.
   The accent (#9E4C34) and the recessed ground (#E9DECB) share a hue family,
   so the accent only reaches 4.46:1 there – under AA. Inside .bg-light
   sections every piece of coloured text therefore steps down to the deeper
   terracotta, which restores it to 6.17:1. This adds no new hue to the
   palette: --color-primary-dark is already the hover colour, so the two read
   as the same colour to the eye.

   Solid buttons are deliberately excluded – their own background carries the
   contrast, and darkening them here would make them look like a hover state.
   If you add a new coloured-text component, add it to this list. */
.bg-light a:not(.btn),
.bg-light .cta-quiet,
.bg-light .btn-link,
.bg-light .workshop-readmore,
.bg-light .contact-link:hover {
  color: var(--color-primary-dark);
}

.bg-light .cta-quiet {
  border-block-end-color: rgba(127, 59, 39, 0.45);
}

.bg-light .btn-outline {
  color: var(--color-primary-dark);
  border-color: var(--color-primary-dark);
}

.bg-light .cta-featured {
  color: var(--color-primary-dark);
  border-color: var(--color-primary-dark);
}

/* --------------------------------------------------------------------------
   5. Typography
   -------------------------------------------------------------------------- */
h1, h2, h3, h4, h5, h6 {
  line-height: 1.25;
  font-weight: 700;
  color: var(--color-text);
}

/* Display faces: serif carries h1/h2, Heebo stays for smaller levels */
h1, h2 {
  font-family: var(--font-serif);
  line-height: 1.18;
  text-wrap: balance;
}

h1 { font-size: var(--font-size-3xl); }
h2 { font-size: var(--font-size-2xl); }
h3 { font-size: var(--font-size-xl); }
h4 { font-size: var(--font-size-lg); }

p { margin-block-end: var(--space-paragraph); }
p:last-child { margin-block-end: 0; }

/* ── Justified body copy – DESKTOP ONLY ────────────────────────────────────
   Running prose only: bio paragraphs, section intros, card summaries and WP
   page content. Deliberately NOT applied to headings, buttons, nav items,
   form labels, or the short bullet lists – justifying a 2–4 word line only
   stretches it into nonsense.

   Hebrew has no automatic hyphenation, so a justified line can only be
   stretched at the word gaps. Measured on the real copy, a narrow column
   tears open: at 360px the worst line ran 9.7x the natural space width, with
   107 lines past 2x – visible rivers. At 900px and up the same paragraphs sit
   at 1.5x–2.0x and the justification reads as intended, so the rule is gated
   behind the breakpoint rather than applied site-wide.

   .workshop-details is excluded at every width on purpose: its column is only
   ~490px even on desktop, still narrow enough to produce ragged gaps.
   Re-measure at 360/390/414 before lowering this breakpoint or adding a
   selector.

   .page-en is excluded for a different reason than width: the English page
   is specified left-aligned, never justified. Excluding it here - rather
   than out-specifying it further down - keeps the intent readable and stops
   the two rules racing on specificity. */
@media (min-width: 900px) {
  .about-bio p,
  .about-bio-extra p,
  .section-intro p,
  .audience-text,
  .workshops-notify-text,
  .psychotherapy-intro p,
  .psychotherapy-content > p,
  .card-excerpt,
  .card-excerpt p,
  .site-main:not(.front-page) article:not(.page-en) p {
    text-align: justify;
  }
}

.section-heading {
  font-family: var(--font-serif);
  font-size: var(--font-size-2xl);
  margin-block-end: var(--space-heading-gap);
}

/* Quiet closing CTA – standing pattern for the end of a content unit:
   a soft question in running text, the action as a clay link. */
.cta-quiet-wrap {
  margin-block-start: var(--space-xl);
  color: var(--color-text-light);
}

/* Variant holding only the link: the lead-in question is the last paragraph of
   the body copy above (About). Without this the button would drift away from
   the question it answers – the wrap's own top margin stacks on top of the
   body copy's bottom margin. */
.cta-quiet-wrap-bare {
  margin-block-start: 0;
}

.cta-quiet {
  color: var(--color-primary-dark);
  font-weight: 500;
  text-decoration: none;
  border-block-end: 1px solid rgba(158, 76, 52, 0.4);
  padding-block-end: 2px;
  white-space: nowrap;
  transition: color var(--transition), border-color var(--transition);
}

.cta-quiet:hover,
.cta-quiet:focus-visible {
  color: var(--color-primary);
  border-block-end-color: currentColor;
}

/* Featured CTA – the FIRST invitation in the reading flow (end of About).
   One step above the quiet link, one step below a solid button:
   clay ghost outline with a whisper of clay tint. Only used once per page. */
.cta-featured {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  margin-block-start: var(--space-sm);
  padding: 0.7rem 1.5rem;
  min-height: 44px;
  border: 1.5px solid var(--color-primary);
  border-radius: var(--radius-md);
  background-color: rgba(158, 76, 52, 0.06);
  color: var(--color-primary-dark);
  font-weight: 500;
  text-decoration: none;
  transition: background-color var(--transition), color var(--transition), border-color var(--transition);
}

.cta-featured:hover,
.cta-featured:focus-visible {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-white);
}

/* Small spruce label above a section heading */
.section-kicker {
  display: block;
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--color-secondary);
  margin-block-end: var(--space-sm);
}

.section-intro {
  max-width: 680px;
  margin-inline: auto;
  margin-block-end: var(--space-2xl);
  color: var(--color-text-light);
  font-size: var(--font-size-lg);
}

/* --------------------------------------------------------------------------
   6. Buttons
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  padding: 0.75rem 1.5rem;
  min-height: 44px;
  min-width: 44px;
  border: 1.5px solid transparent;
  border-radius: var(--radius-md);
  font-size: 1rem;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--transition), color var(--transition), border-color var(--transition), transform var(--transition);
  line-height: 1;
  white-space: nowrap;
}

.btn:active {
  transform: scale(.98);
}

.btn-primary {
  background-color: var(--color-primary);
  color: var(--color-white);
  border-color: var(--color-primary);
}

.btn-primary:hover,
.btn-primary:focus-visible {
  background-color: var(--color-primary-dark);
  border-color: var(--color-primary-dark);
  color: var(--color-white);
}

.btn-secondary {
  background-color: var(--color-secondary);
  color: var(--color-white);
  border-color: var(--color-secondary);
}

.btn-secondary:hover,
.btn-secondary:focus-visible {
  background-color: var(--color-secondary-dark);
  border-color: var(--color-secondary-dark);
  color: var(--color-white);
}

.btn-outline {
  background-color: transparent;
  color: var(--color-primary);
  border-color: var(--color-primary);
}

.btn-outline:hover,
.btn-outline:focus-visible {
  background-color: var(--color-primary);
  color: var(--color-white);
}

.btn-whatsapp {
  background-color: var(--color-primary);
  color: var(--color-white);
  border-color: var(--color-primary);
}

.btn-whatsapp:hover,
.btn-whatsapp:focus-visible {
  background-color: var(--color-primary-dark);
  border-color: var(--color-primary-dark);
  color: var(--color-white);
}

.btn-sm   { padding: 0.5rem 1rem; font-size: var(--font-size-sm); }
.btn-lg   { padding: 1rem 2rem; font-size: var(--font-size-lg); }

.btn-link {
  background: none;
  border: none;
  color: var(--color-primary);
  cursor: pointer;
  padding: var(--space-sm) 0;
  font-size: var(--font-size-base);
  text-decoration: underline;
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: 44px;
}

/* --------------------------------------------------------------------------
   7. Site Header
   -------------------------------------------------------------------------- */
.site-header {
  position: sticky;
  inset-block-start: 0;
  z-index: 1000;
  background-color: rgba(247, 240, 229, 0.97);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-block-end: 1px solid var(--color-border);
  height: var(--header-height);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  height: 100%;
  padding-inline: var(--container-pad);
  max-width: var(--container-max);
  margin-inline: auto;
}

/* Logo */
.site-logo {
  min-width: 0;
  overflow: hidden;
  max-width: 55%;
}

.logo-text-link {
  text-decoration: none;
  display: flex;
  flex-direction: column;
  line-height: 1.2;
  min-height: 44px;
  justify-content: center;
  overflow: hidden;
}

.logo-name {
  font-family: var(--font-serif);
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Hidden below the desktop breakpoint: the phone header carries the name on
   its own. It comes back at 1040, where it is joined to the name on one line
   and becomes the second half of the header title. */
.logo-title {
  display: none;
  font-size: 0.72rem;
  color: var(--color-text-light);
  font-weight: 500;
  letter-spacing: 0.05em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.site-logo img {
  max-height: 48px;
  width: auto;
}

/* Desktop nav – hidden on mobile */
.site-nav {
  display: none;
  flex: 1;
}

.nav-list,
.mobile-nav-list,
.footer-nav-list {
  list-style: none;
  display: flex;
  gap: var(--space-lg);
  flex-wrap: wrap;
}

.nav-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--color-text);
  text-decoration: none;
  font-weight: 500;
  padding-block: var(--space-xs);
  border-block-end: 2px solid transparent;
  transition: border-color var(--transition), color var(--transition);
  white-space: nowrap;
}

.nav-link:hover,
.nav-link:focus-visible {
  color: var(--color-primary);
  border-block-end-color: var(--color-primary);
}

.nav-link.active {
  color: var(--color-primary);
  border-block-end-color: var(--color-primary);
  font-weight: 700;
}

/* Header CTA */
.header-cta {
  display: none;
}

/* End cluster: language switch + hamburger.
   Without this wrapper the bar's space-between would push a third child to the
   middle; with it the bar is back to two items - the logo and this. */
.header-end {
  display: flex;
  align-items: center;
  gap: 2px;
}

/* The phone-only language switch. 44px square, so it is a real touch target
   next to the hamburger rather than a word to aim at, and it carries the plain
   ink colour - a second accent here would compete with the one CTA the page
   is built around. It disappears at 1040, where .site-nav takes over and ends
   with the same link. */
.header-lang {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  font-family: var(--font-primary);
  font-size: 0.9375rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--color-text);
  text-decoration: none;
  direction: ltr;
  transition: color var(--transition);
}

.header-lang:hover,
.header-lang:focus-visible {
  color: var(--color-primary);
}

/* Hamburger */
.nav-toggle {
  display: flex;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: 2px solid transparent;
  cursor: pointer;
  padding: var(--space-sm);
  border-radius: var(--radius-sm);
  min-width: 44px;
  min-height: 44px;
  align-items: center;
  justify-content: center;
  z-index: 1100;
}

.nav-toggle:focus-visible {
  outline: 3px solid var(--color-primary);
  outline-offset: 2px;
}

.hamburger-bar {
  display: block;
  width: 24px;
  height: 2px;
  background-color: var(--color-text);
  border-radius: var(--radius-full);
  transition: transform var(--transition), opacity var(--transition);
}

.nav-toggle[aria-expanded="true"] .hamburger-bar:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}
.nav-toggle[aria-expanded="true"] .hamburger-bar:nth-child(2) {
  opacity: 0;
}
.nav-toggle[aria-expanded="true"] .hamburger-bar:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

/* Nav Overlay – backdrop behind off-canvas panel.
   Mixed from the palette's ink rather than pure black, so the dimmed page
   behind the open menu stays warm instead of going grey. */
.nav-overlay {
  position: fixed;
  inset: 0;
  background-color: rgba(43, 33, 24, 0.5);
  z-index: 800;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 300ms ease, visibility 300ms ease;
}

.nav-overlay[aria-hidden="false"] {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

/* Mobile Nav – off-canvas panel, slides in from the left.
   Being `position: fixed`, the parked panel never joins the document's
   scrollable width (verified in both Blink and WebKit). `contain: layout paint`
   makes that explicit and stops the closed panel painting or laying out
   anything past the viewport edge, so no engine can pan to it. */
.mobile-nav {
  position: fixed;
  inset-block-start: var(--header-height);
  inset-block-end: 0;
  left: 0;
  width: 78%;
  max-width: 340px;
  /* Raised rung: the off-canvas panel floats above the dimmed page, so it sits
     on the surface tint rather than the page ground. */
  background-color: var(--color-surface);
  z-index: 900;
  overflow-y: auto;
  overflow-x: hidden;
  contain: layout paint;
  box-shadow: var(--shadow-lg);
  transform: translateX(-100%);
  transition: transform 300ms ease, visibility 300ms ease;
  visibility: hidden;
}

.mobile-nav[aria-hidden="true"] {
  transform: translateX(-100%);
  visibility: hidden;
  pointer-events: none;
}

.mobile-nav[aria-hidden="false"] {
  transform: translateX(0);
  visibility: visible;
}

/* X close button inside panel */
.nav-close {
  align-self: flex-start;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 2px solid transparent;
  cursor: pointer;
  min-width: 44px;
  min-height: 44px;
  border-radius: var(--radius-sm);
  font-size: 1.5rem;
  line-height: 1;
  color: var(--color-text);
  transition: color var(--transition), border-color var(--transition);
}

.nav-close:hover,
.nav-close:focus-visible {
  color: var(--color-primary);
  border-color: var(--color-primary);
}

.mobile-nav-inner {
  padding: var(--space-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  min-height: 100%;
}

.mobile-nav-list {
  flex-direction: column;
  gap: 0;
}

.mobile-nav-list li {
  border-block-end: 1px solid var(--color-border);
}

.mobile-nav-list .nav-link {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding-block: var(--space-md);
  font-size: var(--font-size-lg);
  border-block-end: none;
  width: 100%;
}

.mobile-nav-cta {
  margin-block-start: auto;
  padding-block-start: var(--space-md);
}

/* --------------------------------------------------------------------------
   8. Sections – Common
   -------------------------------------------------------------------------- */
.section {
  padding-block: var(--section-pad);
  scroll-margin-block-start: var(--header-height);
}

/* --------------------------------------------------------------------------
   9. Section 1: Hero
   -------------------------------------------------------------------------- */
.site-banner {
  background-color: var(--color-primary);
  color: var(--color-white);
  padding-block: var(--space-sm);
  text-align: center;
  font-size: var(--font-size-sm);
}

.banner-link {
  color: var(--color-white);
  text-decoration: none;
  font-weight: 500;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  padding-block: var(--space-xs);
}

.banner-link:hover {
  text-decoration: underline;
}

/* Split hero – a landscape strip carrying the title, with the portrait beside
   it on desktop and below it on phones. DT3-A and T2+ from the hero board.

   THE PORTRAIT FILES (2026-09-25, sheet 09: the hero steps back). Two crops
   of portrait_large.png, both entirely inside the photograph - the wider
   Myself_new file around it is generatively expanded and none of it is
   used. They are no longer the same framing:
   - hero-portrait-mobile.webp (below 768px) is step 1: 10% further back than
     before, the eyes exactly where they were. Crown 13.0%, eyes 38.2%, lower
     edge of the beard 68.1%, V of the tee 87.8%; skull 28.9-63.3% across,
     ears 25.3-71.4%.
   - hero-portrait.webp (768px and up) is step 2: 20% further back, the last
     step that keeps the eyes where they were. Crown 15.8%, eyes 38.2%,
     beard 64.8%; skull 31.2-61.9% across, ears 28.0-69.1%.
   Both keep the old ratio (808x937, 1131x1311), so no markup changed. Every
   landmark was measured on these crops; do not carry figures from the old
   Portrait2 export (crown 10.2, V 93.3), which framed the head tighter.

   THE CONSTRAINT. The head is never cut and always has air over it: at
   least 4.2% of the file between the crown and the top edge. On a tall phone
   and on every tablet and desktop the panel is taller than the file's ratio,
   so the whole height shows and the only crop is at the sides.

   THE SEAM. Where the strip meets the portrait the two photographs are 24
   degrees apart in hue but 30 points apart in saturation - the mismatch is
   depth, not colour. So the landscape is masked out below the ridge (the
   last 17% of the strip; 197px across on desktop) and what it dissolves into
   is an air layer built from the portrait's own out-of-focus background.
   Note the direction: the desktop
   ramp runs `to left` because .hero-land is the first flex child and lands on
   the right in RTL. The hero is not rendered on the LTR English page. */
.section-hero {
  position: relative;
  padding-block: 0;
  background-color: var(--color-bg);
}

/* The stacked hero is exactly one screen, and the two panels split it 40/60 at
   every screen height (client decision, 2026-09-18). It has to be a definite
   height, not a minimum: a percentage flex-basis resolves against nothing on
   an auto-height container, and the panels would fall back to their content.
   Before this, only the portrait was pinned - to 5/6 of the WIDTH - and the
   strip took whatever was left, so the ratio drifted with the screen's height
   and hit 40/60 only on a 390x844 phone. */
.hero-split {
  display: flex;
  flex-direction: column;
  height: calc(100vh - var(--header-height));
  height: calc(100svh - var(--header-height));
}

/* --- Landscape strip + title --- */

/* 40% of the hero, on every phone. On a 390x844 phone that is the same 312px
   the T2+ type was measured on, so the reference viewport is unchanged; what
   changes is every other height, where the strip used to be whatever the
   portrait left over (241px at 375x667, 230px at 360x640 - and 150px, its old
   floor, on the shortest screens).

   The old floor of min(26vh, 150px) is gone with the elastic basis that needed
   it: a fixed share cannot overflow the fold, because the two shares are one
   screen by construction. The portrait tablets below restore both. */
.hero-land {
  position: relative;
  flex: 0 0 40%;
  min-height: 0;
  overflow: hidden;
}

/* The air the landscape dissolves into. A flat colour does not work here even
   when it is sampled exactly right (#8A824D is the portrait's first visible
   row): what sits on the other side of the seam is texture, and a uniform
   field beside a photograph always reads as two surfaces. So the air is the
   portrait's own bokeh - the same file, magnified 6.7x and blurred, sampled
   from its top 8%, which is above the crown and holds no features. The crown
   starts at 13.0% of the phone file (10.2% before the step back of sheet 09,
   so the room the 15px blur needs to keep hair out of it only grew). The gradient on top
   only nudges the average toward the seam row.

   It sits 60px outside the panel on three sides so the blur is fully formed
   at the edge instead of fading into transparency; .hero-land's overflow
   clips it. Measured seam step after this: 12 luminance points, which is
   photographic grain - a visible line starts around 25. */
.hero-land::before {
  content: '';
  position: absolute;
  z-index: 0;
  left: -60px;
  right: -60px;
  bottom: -60px;
  height: 300px;
  background-image:
    linear-gradient(to bottom, rgba(138, 130, 77, 0) 0%, rgba(138, 130, 77, 0.16) 52%, rgba(138, 130, 77, 0.32) 100%),
    url('../images/hero-portrait-mobile.webp');
  background-repeat: no-repeat;
  background-size: auto, 2600px auto;
  background-position: center, 46% 0;
  filter: blur(15px);
}

/* Sheet 06, chosen 2026-09-17: the sky takes more of the strip, and the fade
   starts at the ridge instead of 60px up in the sky.

   The crop lives in the file. The two landscape files the stacked layouts use
   (hills_sky-mobile.webp below 768, hills_sky-tablet.webp above) keep the top
   1048 of hills_sky.webp's 1280 rows, so the ridge (row 865) sits at 82.5% of
   the file and the hills band is 60% of what the previous crop gave. On a
   390x844 phone: sky 258px, hills 54px, where it was 222px and 90px. The
   desktop split keeps the uncropped file - see its block below.

   object-position's vertical value is set to that same 83% on purpose. For
   object-fit: cover, putting the anchor at the ridge's own share of the file
   puts the ridge at that share of the BOX, whatever the box's shape: a tall
   strip shows the file's whole height and the value does nothing; a wide one
   (short phones, every stacked tablet) crops sky off the top and hills off
   the bottom in exactly the ratio that keeps the ridge at 83%. So the mask
   below can be a fixed percentage and still start on the ridge everywhere,
   which a centred crop would not - on 360x640 it would put the ridge at 92%.

   The ramp's shape is the shipped one, compressed from 48% of the strip into
   the 17% under the ridge: the same eased stops, so no Mach band where it
   starts. Measured on the sheet: the fade no longer dims the sky above the
   ridge (9.2 luminance points against 24.3), and the seam step is unchanged,
   because the mask still ends transparent and the air layer is what meets the
   portrait. */
.hero-land-img {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 83%;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 83%, rgba(0, 0, 0, 0.97) 85.13%, rgba(0, 0, 0, 0.88) 87.6%, rgba(0, 0, 0, 0.68) 90.79%, rgba(0, 0, 0, 0.42) 93.98%, rgba(0, 0, 0, 0.18) 97.17%, rgba(0, 0, 0, 0.04) 98.94%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 0%, #000 83%, rgba(0, 0, 0, 0.97) 85.13%, rgba(0, 0, 0, 0.88) 87.6%, rgba(0, 0, 0, 0.68) 90.79%, rgba(0, 0, 0, 0.42) 93.98%, rgba(0, 0, 0, 0.18) 97.17%, rgba(0, 0, 0, 0.04) 98.94%, transparent 100%);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
}

/* The title sits on open sky, not on a scrim: the ridge line is at 83% of the
   strip and the block never reaches it. Top offsets are percentages of the
   panel, so the breathing room above the ridge scales with the panel instead
   of drifting as the viewport changes. */
.hero-brand {
  position: absolute;
  inset-inline: 0;
  top: 14.1%; /* 44px into the 312px strip */
  z-index: 2;
  margin: 0;
  padding-inline: 20px;
  text-align: center;
}

/* 700, chosen 2026-09-17. A real face, not synthesised: Frank Ruhl Libre is
   declared variable 400-700. It runs 2.5% wider than 500 at 390 and 1440 and
   still holds one line; the block's height does not change. */
.hero-name {
  display: block;
  font-family: var(--font-serif);
  font-size: clamp(1.5rem, 7.69vw, 2.125rem); /* 30px at 390 */
  font-weight: 700;
  line-height: 1.1;
  color: var(--color-text);
}

.hero-descriptor {
  display: block;
  font-family: var(--font-primary);
  font-size: clamp(0.9rem, 4.36vw, 1.25rem); /* 17px at 390 */
  font-weight: 500;
  letter-spacing: 0.08em;
  line-height: 1.4;
  color: var(--color-text);
  margin-block-start: 9px;
}

/* Kept to one line by design. A fourth item in this list breaks it onto two
   rows on a phone long before desktop feels anything. */
.hero-subtitle {
  display: block;
  font-family: var(--font-primary);
  font-size: clamp(0.7rem, 3.08vw, 0.875rem); /* 12px at 390 */
  font-weight: 400;
  letter-spacing: 0.02em;
  line-height: 1.5;
  color: var(--color-text);
  margin-block-start: 10px;
}

/* --- Portrait panel --- */

/* 60% of the hero, the other half of the fixed split. It replaces
   aspect-ratio: 5/6, which pinned the panel to 1.2x the WIDTH: that held the
   crop steady across phone widths but let the ratio drift with height, which
   is what the client asked to end.

   What the fixed share costs, measured. A 60% panel is taller than the file's
   own 1.1592 ratio only while the screen is tall enough; below about 700px of
   hero the panel is wider than that, the file is scaled by width, and a band
   of it is cropped away vertically. On a 390x844 phone nothing changes (the
   panel is still 390x468 and the whole file height shows). At 375x667 the
   window is 83.2% of the file, at 390x664 79.6%. Since the step back of
   sheet 09 that is enough for the head: the crown sits at 13.0% of the phone
   file, and measured on 360x640, 375x667 and 390x664 the window leaves 8.0,
   8.1 and 7.1% of air over it (the rule is 4.2) and ends at 87.8, 88.1 and
   85.5% - at the point of the V (87.8%), never above the beard (68.1%). */
.hero-portrait {
  position: relative;
  flex: 0 0 60%;
  width: 100%;
  overflow: hidden;
  /* A size container, so the two layers below can find the head at any
     panel shape (cqw / cqh). Every layout here sizes this panel from outside
     - a flex share, a row, or width + aspect-ratio - so containing its size
     changes nothing about it. */
  container-type: size;
  /* The crop, as fractions: read by the image's object-position and by the
     layers' mask-position, which must always agree. */
  --px: 0.5;
  --py: 0.29;
  /* The FILE served below 768px (hero-portrait-mobile.webp): its ratio, the
     centre of the head and the head's height (crown to the lower edge of the
     beard), all as shares of the file. The file block below swaps them. */
  --ar: 0.8623;
  --fx: 0.4606;
  --fy: 0.4056;
  --fh: 0.5507;
  --mask: url('../images/hero-portrait-mask-mobile.png');
  --mask-soft: url('../images/hero-portrait-mask-mobile-soft.png');
  /* The layout: which edge meets the landscape, and the scale-dependent sizes. */
  --guard: linear-gradient(to bottom, transparent 0%, #000 14%);
  --vig-size: 85% 70%;
}

/* "Light and focus", chosen 2026-09-25 (sheet 09): treatments of the
   SURROUNDINGS, each at strength 1, where alone it reads as a photograph -
   a soft warm light behind the head, and edges that settle. None of it
   touches the figure: each layer carries a mask
   of the figure (built from the photograph, 16 source px of margin) that is
   fully transparent over it, so the pixels of the person are left exactly as
   they are (measured: at most 1/255 of change anywhere outside the layers'
   reach, i.e. rounding).

   Two masks, because two kinds of treatment. The light switches on slowly,
   over 240 source px: switched on within 80, it left an untreated ring
   hugging the head, which is exactly what makes a photograph read as
   processed. The vignette is near zero around the head anyway and keeps the
   tighter mask. Both are intersected with --guard, which is zero on the edge
   that meets the landscape, so the soft seam is left as it was.

   The masks line up with the photograph because mask-size: cover and
   mask-position do the same arithmetic as object-fit: cover and
   object-position. The gradients cannot use that trick (a gradient has no
   ratio of its own), so the head is located the long way round: the rendered
   image is max(panel width, panel height x ratio) wide, and the head sits at
   its share of that.

   The combination chosen also had a background blur (backdrop-filter). It
   was removed the same day, in every browser: Playwright's WebKit on Windows
   has no backdrop-filter at all, so whether iOS Safari honours the figure
   mask on a backdrop blur could not be verified, and if it did not, the face
   itself would be blurred. It was by far the weakest of the three: taking
   it out moved the panel by 0.4 of 255 on average, 4-5% of what the layers
   change together. Do not bring it back without checking
   that case on a real iPhone. */
.hero-portrait::before,
.hero-portrait::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  --rw: max(100cqw, 100cqh * var(--ar));
  --rh: calc(var(--rw) / var(--ar));
  --hx: calc((100cqw - var(--rw)) * var(--px) + var(--rw) * var(--fx));
  --hy: calc((100cqh - var(--rh)) * var(--py) + var(--rh) * var(--fy));
  --hr: calc(var(--rh) * var(--fh));
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-size: cover, 100% 100%;
          mask-size: cover, 100% 100%;
  -webkit-mask-position: calc(var(--px) * 100%) calc(var(--py) * 100%), 0 0;
          mask-position: calc(var(--px) * 100%) calc(var(--py) * 100%), 0 0;
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}

.hero-portrait::before {
  z-index: 1;
  -webkit-mask-image: var(--mask-soft), var(--guard);
          mask-image: var(--mask-soft), var(--guard);
  background: radial-gradient(circle at var(--hx) var(--hy), rgba(255, 249, 236, 0.12) 0, rgba(255, 249, 236, 0.084) calc(var(--hr) * 0.75), rgba(255, 249, 236, 0) calc(var(--hr) * 1.55));
}

.hero-portrait::after {
  z-index: 2;
  -webkit-mask-image: var(--mask), var(--guard);
          mask-image: var(--mask), var(--guard);
  background: radial-gradient(ellipse var(--vig-size) at var(--hx) var(--hy), rgba(22, 26, 14, 0) 38%, rgba(22, 26, 14, 0.077) 72%, rgba(22, 26, 14, 0.14) 100%);
}

/* Both hero images are taken out of flow. <picture> is an inline wrapper with
   no size of its own, so an in-flow img resolves height:100% against nothing
   and falls back to its intrinsic ratio – which then drives the panel's height
   instead of the other way round, and object-fit has no overflow left to crop.
   Positioning against the panel puts the layout back in charge: the panel's
   height comes from its aspect-ratio on phones and from the row on desktop,
   and the crop window follows from that. */
.hero-portrait-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* 29% down, not centred. On a phone tall enough for the 60% panel to be
     taller than the file's 1.1592 ratio - 390x844 and up - there is no
     vertical overflow at all and this value does nothing: the whole height
     shows, 61px of air over the crown, 57px under the V. It only acts on
     short screens, where the fixed share crops a band away and the question is
     which band. Centring put the crown exactly on the top edge at 390x664
     (0% of air, hair touching it). 29% is the aim that still leaves the
     client's 4.2% of air over the crown at the worst of the phone heights
     measured on the file it was set for (4.28% at 390x664). On the step-1
     file of sheet 09 the same aim leaves 7.1-8.1%, see .hero-portrait
     above, so it was kept. */
  object-position: calc(var(--px) * 100%) calc(var(--py) * 100%);
}

/* --- Portrait tablets: stacked, with the portrait as a centred column ---

   Approved 2026-09-16. Between the phone and the desktop split the hero stacks,
   but it cannot simply reuse the phone rule: that rule pins the portrait to 5/6
   of the WIDTH, and at 768 wide that is a 921.6px panel inside a 952px hero. It
   overflows by 119.6px (26px at 820). So on a tablet the portrait stops being
   full bleed and becomes a centred column.

   72% is the chosen width. Measured on the board at 768x1024: the head lands at
   406px (462px at 82%, 350px at 62%, 305px at 54%) and the strip keeps 288px.
   The head stays whole at every one of those widths, because a narrower column
   is taller relative to its own width and so shows the file's full height.

   The width carries a floor of its own. A narrow-but-short window - 760x800 is
   the real case - would take 72% and still not fit once the strip claims its
   150px minimum. Capping the width at (hero - 150) / 1.2 leaves the strip that
   much in every case, since the column's height is 1.2x its width.

   The landscape is no longer aimed separately here. It used to be aimed at the
   top, because a wide strip scales the file by width and a centred crop put
   the dark hills where the title needed sky - and with the uncropped file,
   the top aim left no ridge in view at all (at 768x1024 it sat 58px below the
   strip). The base rule's 83% now keeps the ridge at 83% of this strip too,
   with the fade starting on it, the same composition as a phone.

   THE SEAM HERE IS NOT ~50. That figure came from the 2026-09-16 board, whose
   trial CSS sized the air layer at 666.7vw; what shipped kept the phone's
   2600px, and on it the seam measures 0.9 / 1.7 / 2.1 / 5.4 at 768 / 820 /
   834 / 1024 wide (phones: 7.5-13.5), the same in Chromium and WebKit.
   Compared column by column instead of as one averaged row, tablets still
   come out below phones (26-31 against 35-37). There is nothing to retune. */
@media (min-width: 600px) and (max-aspect-ratio: 1/1) {
  /* The 40/60 split is a phone decision. A tablet keeps the geometry approved
     on 2026-09-16: the portrait is a centred column pinned to its own width,
     and the strip takes the rest with its floor. */
  .hero-land {
    flex: 1 1 auto;
    min-height: min(26vh, 150px);
  }

  .hero-portrait {
    flex: 0 0 auto;
    aspect-ratio: 5 / 6;
    width: min(72%, calc((100svh - var(--header-height) - 150px) / 1.2));
    margin-inline: auto;
  }
}

/* The FILE, not the layout: this is the <source media> in section-home.php,
   which serves hero-portrait.webp from 768px - to stacked tablets and to the
   split alike. It is a wider crop than the phone's (sheet 09: phones at step
   1, desktop at step 2), so the head sits at other shares of it and the
   figure masks are its own. Keep this query identical to the markup's. */
@media (min-width: 768px) {
  .hero-portrait {
    --ar: 0.8627;
    --fx: 0.4649;
    --fy: 0.4031;
    --fh: 0.4907;
    --mask: url('../images/hero-portrait-mask.png');
    --mask-soft: url('../images/hero-portrait-mask-soft.png');
  }

  /* The air layer is built from the portrait file itself, so it has to be
     the file on screen. Until the two crops diverged it did not matter which;
     with the phone file here, stacked tablets measured a seam step of 11-15
     instead of 1.6-5.4. The split replaces this rule with its own below. */
  .hero-land::before {
    background-image:
      linear-gradient(to bottom, rgba(138, 130, 77, 0) 0%, rgba(138, 130, 77, 0.16) 52%, rgba(138, 130, 77, 0.32) 100%),
      url('../images/hero-portrait.webp');
  }
}

/* --- Desktop: DT3-A, two panels ---

   Width alone is the wrong switch here. The stacked phone composition needs a
   tall viewport: the strip and a portrait at the source's own window add up to
   more height than a wide-but-short window has, which is how a narrowed
   desktop window ended up showing the strip and nothing below the head. The
   second condition hands those viewports to the split layout, which is built
   for exactly that shape.

   The floor is 600px, not 480. At 480 the aspect condition reached right into
   the phone range: a 510px-wide window flipped between the two layouts on a
   single pixel of height (680px tall gave the split and a 219px portrait
   column, 681px gave the stacked layout and a 510px one). Nothing about a
   510px viewport should depend on how tall the window is. 600 keeps every
   phone width in the stacked branch whatever its height, and leaves the aspect
   condition to do its job only where the wide-but-short problem is real. */

/* 2026-09-16: the width floor moved 768 -> 1040 and the aspect floor 3/4 -> 1/1,
   so that PORTRAIT TABLETS no longer take the split - they stack instead (see
   the block above). What still has to reach this query is every landscape
   window, which is what the 1/1 branch is for: 900x700 and 1024x768 are the
   wide-but-short shapes the original guard was written for, and both are
   landscape, so both still land here. 1024x1366 (iPad Pro portrait) is below
   1040 and stacks; 1112x1600 clears 1040 but is flatter than 3/4, so it stacks
   too. */
@media (min-width: 1040px) and (min-aspect-ratio: 3/4), (min-width: 600px) and (min-aspect-ratio: 1/1) {
  /* The hero is exactly one viewport, never more: it is the first thing the
     page shows and it has to land whole. */
  .hero-split {
    flex-direction: row;
    height: calc(100svh - var(--header-height));
    min-height: 0;
  }

  .hero-land {
    flex: 1 1 57%;
    min-height: 0;
  }

  /* Same idea rotated. The seam is .hero-land's LEFT edge (it is the first
     flex child, so RTL puts it on the right), and the air layer becomes a
     480px column against it. The bokeh is sampled from the file's right 22% -
     past the ear, which reaches 69.1% of the desktop file - and from above the shoulder, so no
     feature enters it, and the gradient over it carries the colour actually
     measured down the portrait's visible edge column (x 93% at 1440x900):
     olive foliage for six bands, the blue-grey shirt for the last two. The
     stops are the eight bands measured down that column, placed at their
     centres and shifted 6.4% for the 60px the layer overhangs the panel. At
     0.55 alpha the layer sat between the two photographs instead of matching
     either, and read as a pale stripe; at 0.9 it lands on the portrait's own
     edge where the mask holds it opaque, and the mask hands the rest over to
     the landscape. */
  .hero-land::before {
    left: -60px;
    right: auto;
    top: -60px;
    bottom: -60px;
    width: 480px;
    height: auto;
    background-image:
      linear-gradient(to bottom, rgba(119, 122, 68, 0.9) 0%, rgba(119, 122, 68, 0.9) 11.8%, rgba(89, 92, 46, 0.9) 22.7%, rgba(100, 103, 60, 0.9) 33.6%, rgba(79, 84, 50, 0.9) 44.5%, rgba(93, 98, 54, 0.9) 55.4%, rgba(102, 105, 51, 0.9) 66.3%, rgba(76, 74, 84, 0.92) 77.2%, rgba(53, 52, 72, 0.92) 88.1%, rgba(53, 52, 72, 0.92) 100%),
      url('../images/hero-portrait.webp');
    background-size: auto, 2200px auto;
    background-position: center, 100% -200px;
    filter: blur(18px);
    -webkit-mask-image: linear-gradient(to left, transparent 8%, rgba(0, 0, 0, 0.55) 45%, #000 82%);
            mask-image: linear-gradient(to left, transparent 8%, rgba(0, 0, 0, 0.55) 45%, #000 82%);
  }

  /* 197px = 24% of the 820px panel. The ramp has to reach full transparency
     along the whole seam: an elliptical field that stayed 40% opaque partway
     down measured a 70-point step, against 6 for this one.

     The split keeps the WHOLE photograph (hills_sky.webp, ridge at 67.6%) and
     its centred aim. The sky-over-hills crop chosen for the stacked layouts
     was tried here and not kept: on this panel it fixed nothing - there is no
     vertical fade to shorten, and the title already clears the ridge by 257px
     at 1440x900 - while what it cut off was the sunlit grass in the
     foreground, fully visible, not faded. It also narrowed the view from 66%
     of the file's width to 54%. */
  .hero-land-img {
    object-position: center;
    -webkit-mask-image: linear-gradient(to left, #000 0%, #000 76%, rgba(0, 0, 0, 0.95) 81%, rgba(0, 0, 0, 0.82) 86%, rgba(0, 0, 0, 0.58) 91%, rgba(0, 0, 0, 0.3) 96%, transparent 100%);
            mask-image: linear-gradient(to left, #000 0%, #000 76%, rgba(0, 0, 0, 0.95) 81%, rgba(0, 0, 0, 0.82) 86%, rgba(0, 0, 0, 0.58) 91%, rgba(0, 0, 0, 0.3) 96%, transparent 100%);
  }

  /* The cap on the panel's WIDTH keeps the hero to one viewport without
     cutting the head. It was sized for an earlier source; with the current
     1131x1311 file the vertical rule cannot bind here at all: 0.78 keeps the panel at least 1.28x
     as tall as it is wide, above the file's own 1.1592, so the file is always
     scaled by height and its whole height is in view. The cap stays for the
     composition. At 1440x900 the 43% column is 619px against a 643px cap, so
     the design size is untouched; it is only short or very wide windows that
     bite, and there the landscape panel takes the width the portrait gives
     up. */
  /* min-width is a floor under the column, not part of the composition. With
     the whole height always in view, the crop that can hurt is at the SIDES,
     and the visible share of the width is width ÷ (height × 0.8627). The 0.35
     was derived on Portrait2, whose skull spanned 26.5-64.8% (40.3% of the
     width with 1% of margin a side). The step-2 file of sheet 09 frames the
     head smaller - skull 31.2-61.9% - so at the floor it now keeps the skull
     with 4-6% to spare, and was left as it is. A fixed 305px left portrait tablets below it -
     36.9% at 820x1180, into the hair - so the floor follows the height.

     Skull, not ears, on purpose: that is the protection the previous file
     gave (portrait tablets lost the ear tips with it too). Holding the ears
     takes 0.46 × height, and that was measured: at 768x1024 the strip drops
     from 438px to 330px and the subtitle breaks onto two lines. On a
     landscape window the floor does nothing - at 1440x900 it asks for 288px
     of a 619px column. */
  .hero-portrait {
    flex: 0 0 43%;
    width: auto;
    min-width: max(305px, calc((100svh - var(--header-height)) * 0.35));
    max-width: min(680px, calc((100svh - var(--header-height)) * 0.78));
    aspect-ratio: auto;
  }

  /* Aimed across, not down: the whole height is always in view, so the
     vertical value does nothing. 43% centres the side crop on the skull
     (46.5% across in the step-2 file) whenever the floor above is what sets
     the width. */
  .hero-portrait {
    --px: 0.43;
    --py: 0.5;
    /* The landscape meets this panel on its right (RTL), so the guard runs
       from there. */
    --guard: linear-gradient(to left, transparent 0%, #000 14%);
    --vig-size: 80% 72%;
  }

  .hero-brand {
    inset-inline: 5.4% auto; /* 44px into the 820px panel */
    top: 14.5%;              /* 120px into the 824px panel */
    max-width: 89.2%;
    padding-inline: 0;
    text-align: start;
  }

  .hero-name {
    font-size: clamp(2.75rem, 5.28vw, 4.75rem); /* 76px at 1440 */
  }

  .hero-descriptor {
    font-size: clamp(1.15rem, 2.08vw, 1.875rem); /* 30px at 1440 */
    margin-block-start: 10px;
  }

  .hero-subtitle {
    font-size: clamp(0.9rem, 1.46vw, 1.3125rem); /* 21px at 1440 */
    margin-block-start: 14px;
  }
}

/* --------------------------------------------------------------------------
   10. Section 2: About
   -------------------------------------------------------------------------- */
.section-about {
  background-color: var(--color-bg);
}

.about-inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xl);
  align-items: center;
}

.about-image-wrap {
  max-width: 360px;
  width: 100%;
  flex-shrink: 0;
  position: relative;
}

.about-image {
  display: block;
  position: relative;
  width: auto;       /* lets both dimensions scale together when constrained */
  max-width: 100%;   /* never overflow the wrap */
  max-height: 50vh;  /* mobile: never taller than half the viewport */
  height: auto;
  margin-inline: auto;
  border-radius: var(--radius-md);
}

/* The bio used to get its measure for free by sharing the row with a 380px
   portrait. With the picture gone it is the only child of .about-inner, so the
   line length has to be held here instead – otherwise it runs the full 1136px
   of the container. Centred rather than pinned to the inline-start edge: alone
   in the section, an off-centre column reads as a leftover from a two-column
   layout. */
.about-content {
  flex: 1;
  max-width: 44rem;
  margin-inline: auto;
}

.about-bio {
  margin-block-end: var(--space-lg);
  color: var(--color-text-light);
  line-height: var(--line-height);
}

.about-bio-extra {
  margin-block-end: var(--space-lg);
  color: var(--color-text-light);
}

.read-more-btn {
  margin-block-end: var(--space-xl);
}

.read-more-icon {
  display: inline-block;
  transition: transform var(--transition);
}

[aria-expanded="true"] .read-more-icon {
  transform: rotate(180deg);
}

.credentials-heading {
  font-size: var(--font-size-lg);
  margin-block-end: var(--space-md);
  color: var(--color-secondary);
}

.credentials-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.credentials-list li {
  padding-inline-start: var(--space-lg);
  position: relative;
  color: var(--color-text-light);
}

.credentials-list li::before {
  content: '✓';
  position: absolute;
  inset-inline-start: 0;
  color: var(--color-secondary);
  font-weight: 700;
}

/* --------------------------------------------------------------------------
   11. Section 3: Psychotherapy (retired section – styles kept for restore)
   -------------------------------------------------------------------------- */
.psychotherapy-inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xl);
}

.psychotherapy-image-wrap {
  max-width: 380px;
  width: 100%;
  margin-inline: auto;
}

.psychotherapy-image-wrap img {
  border-radius: var(--radius-lg);
  width: 100%;
  object-fit: cover;
  box-shadow: var(--shadow-md);
}

.methods-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-block: var(--space-lg);
}

.methods-list li {
  padding-inline-start: var(--space-lg);
  position: relative;
  color: var(--color-text-light);
}

.methods-list li::before {
  content: '•';
  position: absolute;
  inset-inline-start: 0;
  color: var(--color-secondary);
  font-size: 1.25rem;
  line-height: 1.4;
}

/* Testimonials removed from the site – styles deleted with the section. */

/* --------------------------------------------------------------------------
   12. Section 4: For Whom
   -------------------------------------------------------------------------- */
/* Warm editorial plates on the cool mist ground – soft paper slabs,
   a short clay dash as the only mark, serif titles. Calm, adult, warm. */
/* One warm paper panel holding a compact two-column bulleted list – far more
   concentrated and lighter than a plate per item, while keeping the warm
   surface the design language is built on.

   The list goes to two columns when its longest line fits in one of them,
   not at a fixed breakpoint. It used to switch at 640px, where a column
   leaves 216px for text: "מגוון רחב של קשיים והתמודדויות" needs 233px and
   "רצון בצמיחה ובהתפתחות אישית" 225px, so both broke onto two lines from 640
   to 674. 16.5rem is the longest item (14.6rem) plus its 1.5rem indent, with
   0.4rem to spare; below that a column simply is not offered, and the list
   stays one column up to 687px. Being in rem, the switch follows the text if
   the browser's font size is raised, which a px breakpoint would not. The
   panel's 820px cap means it never reaches three columns. A new item longer
   than the current longest needs this value raised. */
.audience-list {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16.5rem), 1fr));
  gap: var(--space-md) var(--space-2xl);
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  box-shadow: 0 1px 2px rgba(43, 33, 24, 0.04), 0 10px 28px rgba(43, 33, 24, 0.06);
  margin-block: var(--space-xl) var(--space-2xl);
  max-width: 820px; /* keeps the two columns close instead of drifting apart */
}

.audience-item {
  position: relative;
  padding-inline-start: var(--space-lg);
}

/* delicate clay dot, aligned to the first line */
.audience-item::before {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0.62em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-primary);
}

.audience-title {
  display: block;
  font-family: var(--font-primary);
  font-size: var(--font-size-base);
  font-weight: 500;
  line-height: 1.7;
  color: var(--color-text);
}

.audience-text {
  color: var(--color-text-light);
  font-size: var(--font-size-sm);
  line-height: 1.7;
  max-width: 48ch;
  margin-block-start: var(--space-xs);
}

/* Start-aligned variant of the section intro (editorial sections) */
.section-intro-start {
  margin-inline: 0;
  max-width: 62ch;
}

/* Subsection headings inside merged sections (workshops / courses) */
/* The rule under the group heading (chosen 2026-09-18, sheet 07 option א with
   the client's variation: the rule runs the length of the heading rather than
   stopping at a 44px stub).

   It used to be a hairline that left the heading and ran to the end of the
   line, which read as leftover space rather than as a decision. Now the
   heading shrinks to its own text (width: fit-content) and the rule is that
   text's width. Two consequences worth keeping: the two headings get rules of
   different lengths, because their words differ - that is the point, the mark
   belongs to the heading and not to the column; and the rule is the action
   colour at 3px rather than a 1px border, so it reads as a mark somebody made
   rather than as a divider the layout produced. */
.subsection-heading {
  font-family: var(--font-serif);
  font-size: 1.45rem;
  font-weight: 700;
  color: var(--color-text);
  margin-block: var(--space-2xl) var(--space-xl);
  display: block;
  width: fit-content;
}

.subsection-heading::after {
  content: '';
  display: block;
  width: 100%;
  height: 3px;
  border-radius: 2px;
  background: var(--color-primary);
  margin-block-start: 10px;
}

/* --------------------------------------------------------------------------
   13b. Workshops & Courses – ongoing-facilitation cards + notify form
   -------------------------------------------------------------------------- */
/* Subtitle sits tight under the section heading */
.section-heading:has(+ .section-subtitle) {
  margin-block-end: var(--space-xs);
}

.section-subtitle {
  margin-block-end: var(--space-lg);
  font-size: var(--font-size-lg);
  font-weight: 500;
  color: var(--color-secondary);
  letter-spacing: 0.01em;
}

.workshops-grid {
  list-style: none;
  display: grid;
  grid-template-columns: 1fr;
  /* Natural-height cards, top-aligned: expanding one accordion must not
     stretch its row-mate and leave an empty gap. */
  align-items: start;
  gap: var(--space-lg);
  /* Vertical rhythm belongs to .workshops-group now: the grid is preceded by
     its own subsection heading, which carries the gap above, and the group
     carries the gap below. Leaving it here too would double both. */
  margin-block: 0;
}

/* Two marked runs instead of one undifferentiated six. What tires a reader in
   this section is the repetition, not the length - the split is what gives
   permission to skip the half that is not addressed to them. */
.workshops-group:last-of-type {
  margin-block-end: var(--space-3xl);
}

/* Between the two groups (chosen 2026-09-18, sheet 07 option ב). What marked
   the change of audience was a 48px gap against the 24px between cards - the
   strongest break in the section was twice the weakest, which is not enough to
   read as "a different list starts here". So: double the air, and a small mark
   in the middle of it. Three dots, the middle one in the action colour and the
   outer two in its light tint - punctuation, not a rule. A rule across the
   column is the same device the group heading already uses, and two rules
   30px apart would fight. */
.workshops-group + .workshops-group {
  margin-block-start: var(--space-3xl);
}

.workshops-group + .workshops-group::before {
  content: '';
  display: block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-primary);
  box-shadow: -16px 0 0 var(--color-primary-light), 16px 0 0 var(--color-primary-light);
  margin: 0 auto var(--space-2xl);
}

.workshop-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  /* Raised rung, and a border reduced to a hairline: with the card lighter
     than its ground, lightness does the separating and the outline no longer
     has to. This is what stops the cards reading as boxes. */
  background: var(--color-surface);
  border: 1px solid rgba(43, 33, 24, 0.07);
  border-radius: var(--radius-lg);
  /* 24, not 32. Six cards in a row multiply every padding decision by twelve
     edges; at 32 the inner air was doing nothing the card's own lightness was
     not already doing, and it cost 124px of scroll on a phone. */
  padding: var(--space-lg);
  box-shadow: 0 1px 2px rgba(43, 33, 24, 0.04), 0 10px 28px rgba(43, 33, 24, 0.06);
}

.workshop-card-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
}

/* Topic chip – spruce (informational), distinct from clay actions */
.workshop-topic {
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--color-secondary);
  background: rgba(117, 86, 92, 0.12);
  padding: 0.25rem 0.7rem;
  border-radius: var(--radius-full);
  letter-spacing: 0.02em;
}

/* Ongoing-activity signal – a calm clay dot with a soft pulsing ring */
.workshop-recurring {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.78rem;
  color: var(--color-text-muted);
}

.workshop-recurring-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-primary);
  flex-shrink: 0;
  animation: ws-pulse 2.6s ease-out infinite;
}

@keyframes ws-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(158, 76, 52, 0.35); }
  70%  { box-shadow: 0 0 0 6px rgba(158, 76, 52, 0); }
  100% { box-shadow: 0 0 0 0 rgba(158, 76, 52, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .workshop-recurring-dot { animation: none; }
}

.workshop-card-title {
  font-family: var(--font-serif);
  font-size: 1.35rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--color-text);
}

.workshop-card-desc {
  color: var(--color-text-light);
  font-size: var(--font-size-base);
  line-height: 1.7;
  flex: 1; /* pushes the button to a common baseline across cards */
  margin-block-end: 0; /* the card's flex gap already spaces this */
}

/* The notify button moved off the card face and into the accordion panel, so
   it is a block-flow child now and align-self has nothing to act on. The
   panel's own `* + *` rule already spaces it; this only buys it a little more
   air than a paragraph gets, because it is an action and not another line. */
.workshop-details-inner > .workshop-notify-btn {
  margin-block-start: var(--space-lg);
}

/* Notify / mailing-list block – mist plate for gentle separation on paper */
.workshops-notify {
  max-width: 560px;
  margin-inline: auto;
  padding: var(--space-2xl);
  background: var(--color-bg-light);
  border-radius: var(--radius-lg);
  text-align: center;
  scroll-margin-block-start: calc(var(--header-height) + var(--space-md));
}

.workshops-notify-heading {
  font-family: var(--font-serif);
  font-size: var(--font-size-xl);
  margin-block-end: var(--space-sm);
  color: var(--color-text);
}

.workshops-notify-text {
  color: var(--color-text-light);
  max-width: 46ch;
  margin-inline: auto;
  margin-block-end: var(--space-xl);
}

.workshops-notify-form {
  text-align: start;
}

/* ── Card essence line (closed state) ─────────────────────────────────────── */
.workshop-card-summary {
  margin: 0;
  color: var(--color-text-light);
  font-size: var(--font-size-base);
  line-height: 1.7;
}

/* ── "קראו עוד" disclosure – a quiet clay text link. Hidden until JS upgrades
   the card to an accordion, so no-JS visitors keep the panel open. ────────── */
.workshop-readmore {
  display: none;
  align-items: center;
  align-self: flex-start;
  gap: 0.4rem;
  min-height: 44px;
  padding: var(--space-xs) 0;
  background: none;
  border: 0;
  color: var(--color-primary);
  font-family: var(--font-primary);
  font-size: var(--font-size-base);
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.workshop-card.is-accordion .workshop-readmore {
  display: inline-flex;
}

.workshop-readmore:hover,
.workshop-readmore:focus-visible {
  color: var(--color-primary-dark);
}

.workshop-readmore-icon {
  display: inline-block;
  font-size: 0.9em;
  transition: transform var(--transition);
}

.workshop-readmore[aria-expanded="true"] .workshop-readmore-icon {
  transform: rotate(180deg);
}

/* ── Accordion panel – smooth height via grid-template-rows (0fr↔1fr).
   Open by default; JS collapses collapsible cards on load. ────────────────── */
.workshop-details {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows 340ms ease;
}

.workshop-card.is-accordion:not(.is-open) .workshop-details {
  grid-template-rows: 0fr;
}

.workshop-details-inner {
  min-height: 0;
  overflow: hidden;
  padding-block-start: var(--space-md);
  border-block-start: 1px solid var(--color-border);
}

/* Keep the panel out of the a11y tree + tab order while collapsed, but only
   after the collapse animation has finished. */
.workshop-card.is-accordion:not(.is-open) .workshop-details-inner {
  visibility: hidden;
  transition: visibility 0s linear 340ms;
}

.workshop-details-inner > * {
  margin-block: 0;
}

.workshop-details-inner > * + * {
  margin-block-start: var(--space-md);
}

.workshop-details-inner p {
  color: var(--color-text-light);
  line-height: 1.75;
}

.workshop-details-label {
  font-weight: 700;
  color: var(--color-text);
}

.workshop-details-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.workshop-details-list li {
  position: relative;
  padding-inline-start: 1.25rem;
  color: var(--color-text-light);
  line-height: 1.7;
}

.workshop-details-list li::before {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  top: 0.62em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-secondary);
}

@media (prefers-reduced-motion: reduce) {
  .workshop-details,
  .workshop-details-inner,
  .workshop-readmore-icon {
    transition: none;
  }
}

@media (min-width: 700px) {
  .workshops-grid { grid-template-columns: repeat(2, 1fr); }
}

/* Three columns, so a group of three is exactly one row and the split reads as
   two bands rather than a 2+1 stagger. Measured at 1440: the container is
   1136px, so a card settles at (1136 - 48) / 3 = 363px - wider than the 330px
   the two-column layout gives a card at 700. Below 1040 a group wraps 2+1,
   which is still legible; three columns there would take the card under 300. */
@media (min-width: 1040px) {
  .workshops-grid { grid-template-columns: repeat(3, 1fr); }
}

.for-whom-cta {
  margin-block-start: var(--space-2xl);
}

/* --------------------------------------------------------------------------
   13. Cards (shared: Workshops, Courses, Resources)
   -------------------------------------------------------------------------- */
.cards-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-lg);
  list-style: none;
  margin-block-end: var(--space-2xl);
}

.card {
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  transition: transform var(--transition), box-shadow var(--transition);
  display: flex;
  flex-direction: column;
}

.card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.card-image-wrap {
  position: relative;
  overflow: hidden;
  aspect-ratio: 3/2;
}

.card-image-wrap img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 400ms ease;
}

.card:hover .card-image-wrap img {
  transform: scale(1.03);
}

.card-badges {
  position: absolute;
  inset-block-end: var(--space-sm);
  inset-inline-start: var(--space-sm);
  display: flex;
  gap: var(--space-xs);
  flex-wrap: wrap;
}

.badge {
  background: var(--color-secondary);
  color: var(--color-white);
  font-size: 0.75rem;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: var(--radius-full);
}

.card-body {
  padding: var(--space-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  flex: 1;
}

.card-title {
  font-size: var(--font-size-lg);
  font-weight: 700;
  line-height: 1.3;
}

.card-meta {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
}

.card-excerpt {
  color: var(--color-text-light);
  font-size: var(--font-size-sm);
  flex: 1;
}

.card-details {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
}

.card .btn {
  align-self: flex-start;
  margin-block-start: var(--space-sm);
}

/* Resource type badge */
.resource-type-badge {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--color-secondary);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* Video facade */
.card-video-facade {
  position: relative;
  cursor: pointer;
  overflow: hidden;
  aspect-ratio: 4/3;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #000;
}

.card-video-facade img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: .85;
  transition: opacity var(--transition);
}

.card-video-facade:hover img,
.card-video-facade:focus-visible img {
  opacity: .7;
}

.play-icon {
  position: absolute;
  font-size: 3rem;
  color: var(--color-white);
  background: rgba(0,0,0,.5);
  width: 64px;
  height: 64px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform var(--transition);
}

.card-video-facade:hover .play-icon {
  transform: scale(1.1);
}

/* Embed iframe */
.embed-iframe-wrap {
  position: relative;
  aspect-ratio: 16/9;
  width: 100%;
}

.embed-iframe-wrap iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: none;
}

/* --------------------------------------------------------------------------
   14. Course Filter
   -------------------------------------------------------------------------- */
.courses-filter {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-block-end: var(--space-xl);
}

.filter-btn {
  padding: 0.5rem 1.25rem;
  min-height: 44px;
  border: 2px solid var(--color-border);
  border-radius: var(--radius-full);
  background: var(--color-surface);
  color: var(--color-text);
  font-size: var(--font-size-sm);
  font-weight: 500;
  cursor: pointer;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}

.filter-btn:hover,
.filter-btn:focus-visible {
  border-color: var(--color-primary);
  color: var(--color-primary);
}

.filter-btn.active,
.filter-btn[aria-pressed="true"] {
  background: var(--color-primary);
  color: var(--color-white);
  border-color: var(--color-primary);
}

.course-card[hidden] {
  display: none;
}

/* --------------------------------------------------------------------------
   15. Section 7: Content / Resources
   -------------------------------------------------------------------------- */
.newsletter-section {
  background: var(--color-primary);
  color: var(--color-white);
  border-radius: var(--radius-lg);
  padding: var(--space-2xl);
  text-align: center;
  margin-block-start: var(--space-2xl);
}

.newsletter-heading {
  color: var(--color-white);
  margin-block-end: var(--space-md);
}

.newsletter-section p {
  color: rgba(255,255,255,.85);
  margin-block-end: var(--space-xl);
}

.newsletter-fields {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  margin-block-end: var(--space-md);
}

/* --------------------------------------------------------------------------
   16. Section 8: Contact
   -------------------------------------------------------------------------- */
.section-contact {
  background-color: var(--color-bg-light);
}

/* The one raised object on the page (chosen 2026-09-18, sheet 07 option א).
   The section sits on the recessed rung, which "למי הטיפול מתאים" already
   uses, and until now nothing inside it was lifted except the form's own
   fields - so the single moment where somebody decides to make contact looked
   like another content section. Lifting the details and the form onto the
   raised rung uses the language the cards already speak, one step further up:
   this is the only place on the page where that rung carries a whole block
   rather than a card.

   Note for anyone editing colours here: the section keeps .bg-light, so the
   accessibility rule in section 6 still steps coloured text down to
   --color-primary-dark. On the raised panel that is no longer required (the
   accent clears AA there on its own) but it is still correct - measured 7.5:1
   inside the panel - so it is left alone rather than unpicked. */
.contact-inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xl);
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  box-shadow: 0 1px 2px rgba(43, 33, 24, 0.04), 0 18px 44px rgba(43, 33, 24, 0.09);
}

.contact-details {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

.contact-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
}

.contact-icon {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  color: var(--color-secondary);
  margin-block-start: 11px;
}

.contact-link {
  color: var(--color-text);
  font-size: var(--font-size-lg);
  text-decoration: none;
  font-weight: 500;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
}

.contact-link:hover {
  color: var(--color-primary);
}

.contact-location {
  color: var(--color-text-light);
}


.contact-form-wrap {
  flex: 1;
}

/* The clinic photograph. It sits here rather than higher up the page because
   this is the only point where "what will it be like to walk in" is a live
   question, and the location line two columns over is the sentence it answers.
   Measured against the alternatives that placed it after "למי הטיפול מתאים":
   those put it 2.8 screens from the hero portrait, where the same seated,
   front-on frame reads as a second portrait competing with it; here it is 8.8
   screens away and reads as the room.

   On desktop it is the third column of .contact-inner and is sized to fit
   INSIDE the height the details and the form already set - 382px against the
   row's ~799px - so the section stays 959px and the page stays 6350px. That is
   the whole argument for this placement: it is the only one that costs the
   page nothing. Everything below protects that.

   The plate scale on phones is the point, not a saving. At container width the
   same picture reads as a second hero portrait and the caption stops doing its
   work; at 76% the frame keeps its own edges - the shelf, the lamp, the sofa -
   and the reader sees a room instead of a face. +425px on a 390px phone, all
   of it after "סדנאות וקורסים", so nothing above it moves.

   The scale lives on the IMG, not on the figure. That looks redundant on
   phones, where the figure is the only thing on its line, but it is what lets
   the figure be a full-width flex item in the middle range below without the
   picture growing to match: a flex-basis on the figure would fight a width on
   it, and the loser would be the wrap. */
/* The picture is printed twice and exactly one copy is ever displayed. 768 is
   the switch, and it is the contact ROW that sets it: at and above 768 the row
   exists, the picture joins it and the page grows by nothing; below 768 there
   is no row to join, and the picture would sit at 86.5% of the scroll - under
   the send button, past the point where a reader who filled the form has any
   reason to continue. The phone copy is the band after "למי הטיפול מתאים"
   instead, at 26% of the scroll. display:none, not visibility or clip: the
   hidden copy must leave the accessibility tree, or a screen reader meets the
   same photograph twice. */
.contact-clinic {
  display: none;
}

/* ── Phone: the band between "למי הטיפול מתאים" and "סדנאות וקורסים" ────────
   The raised rung of the ladder, full bleed. This is the whole answer to the
   picture reading as something floating: on the page ground it was an object
   with nothing under it, and here it has a shelf of its own - lighter than the
   recessed band it follows and lighter than the ground it hands over to, so
   the eye reads a lit step rather than a gap someone dropped a photo into. */
/* The shelf has no bottom edge (chosen 2026-09-18, sheet 07 option א). It used
   to end on a straight line against the section below, and the two grounds are
   only 1.10:1 apart in luminance - too close to read as a change, close enough
   to draw a line. Melting the shelf into the next ground over its last 42%
   removes the line without adding a mark, so the picture is the only object
   left in the band. The extra space below is what the dissolve needs: the
   gradient has to finish before the heading arrives, or the heading sits on a
   ground that is still moving. */
.clinic-band {
  background-color: transparent;
  background-image: linear-gradient(
    to bottom,
    var(--color-surface) 0%,
    var(--color-surface) 58%,
    var(--color-bg) 100%
  );
  padding-block: var(--space-2xl) var(--space-3xl);
  padding-inline: var(--space-lg);
}

.clinic-band-figure {
  margin: 0;
}

/* No aspect-ratio on either copy, on purpose: width/height on the <img> carry
   the exported file's own 1000x1159, and that intrinsic ratio is the one thing
   that must not drift - this crop was chosen by hand and is never re-cropped.
   object-fit is absent for the same reason. */
.clinic-band-figure img {
  display: block;
  width: 100%;
  max-width: 320px;
  height: auto;
  margin-inline: auto;
  border-radius: var(--radius-lg);
  box-shadow: 0 1px 2px rgba(43, 33, 24, 0.05), 0 14px 36px rgba(43, 33, 24, 0.10);
}

@media (min-width: 768px) {
  .clinic-band {
    display: none;
  }

  .contact-clinic {
    display: block;
    margin-block: var(--space-xl) 0;
  }

  .contact-clinic img {
    display: block;
    width: 76%;
    max-width: 300px;
    height: auto;
    border-radius: var(--radius-lg);
    box-shadow: 0 1px 2px rgba(43, 33, 24, 0.05), 0 14px 36px rgba(43, 33, 24, 0.10);
  }
}

/* The three-column row needs more width than the theme's desktop breakpoint
   offers, so it gets its own. Measured: the details column settles at ~344px
   and the figure takes 330px, which with two 48px gaps leaves the form
   1116 - 770 = 346px at a 1180px viewport. Below that the form is the one that
   pays - 190px at 1024 and 111px at 899 when this was tried at 900px - so
   between the row breakpoint and 1180 the figure takes a line of its own.

   `flex: 0 0 100%` is what forces that line. Relying on flex-wrap alone does
   not work: a flex item whose basis is auto shrinks to fit before it wraps,
   which is exactly how the form ended up at 111px. */
@media (min-width: 768px) {
  .contact-clinic {
    flex: 0 0 100%;
  }
}


@media (min-width: 1180px) {
  .contact-clinic {
    flex: 0 0 330px;
    margin-block: 0;
    margin-inline-start: auto;
  }

  .contact-clinic img {
    width: 100%;
    max-width: none;
  }
}

/* --------------------------------------------------------------------------
   17. Forms
   -------------------------------------------------------------------------- */
.psycho-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.form-heading {
  font-size: var(--font-size-xl);
  margin-block-end: var(--space-sm);
}

.form-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.form-group label {
  font-weight: 600;
  font-size: var(--font-size-sm);
  color: var(--color-text);
}

.required {
  color: var(--color-error);
  margin-inline-start: 2px;
}

.form-control {
  width: 100%;
  padding: 0.75rem 1rem;
  border: 2px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: var(--font-size-base);
  color: var(--color-text);
  background: var(--color-surface);
  transition: border-color var(--transition);
  min-height: 44px;
}

.form-control:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px rgba(158, 76, 52, .15);
}

.form-control.is-invalid {
  border-color: var(--color-error);
}

textarea.form-control {
  resize: vertical;
  min-height: 120px;
}

/* The slot is reserved under every field, empty or not, so that an error
   message appearing does not shift everything below it. That reservation is
   also the single biggest term in the space between two fields - see the
   contact form's override below. */
.field-error {
  font-size: var(--font-size-sm);
  color: var(--color-error);
  display: block;
  min-height: 1.2em;
}

/* Release the reservation while the slot is empty (contact form 2026-09-19,
   sheet 08 option ד; the workshops notify form 2026-09-20).

   The visible distance from a field's lower edge to the next label was three
   things, not one: 4px of group gap, 16.8px of empty error slot, and the
   form's own gap. Moving only the last of those changes 4px of 36.8 and
   cannot be seen - releasing the slot is what does the work.

   It shows up differently in the two forms, because the markup differs. Every
   field in the contact form carries an error span, so the reservation sat
   under all of them evenly and the whole form read as loose. The notify form
   has a span under its email field only, so it had one conspicuous gap - 36.8px
   between the address and "נושא / סדנה" against 16px between the name and the
   address - and that mismatch was the complaint.

   What it costs: a message is 23.8px tall, so when one appears the fields
   below it move down by that much, where the reservation used to absorb all
   but 7px. It happens after a submit that failed validation, under the field
   that failed.

   The footer newsletter is deliberately excluded and keeps its reservation. */
.psycho-form:not(.psycho-newsletter-form) .field-error:empty {
  min-height: 0;
}

/* The contact form also tightens its own gap, 16px -> 12px. The notify form
   keeps 16px: there the gap was never the problem, and leaving it means the
   distance from "נושא / סדנה" down to the button is untouched - both simply
   move up together. */
.section-contact .psycho-form {
  gap: 12px;
}

/* One last 4px in the notify form. Collapsing the slot leaves a zero-height
   box behind, and the group's own 4px gap still sits in front of it - so the
   run from the address to "נושא / סדנה" came out at 20px against the 16px
   between the name and the address. Pulling the empty slot back by exactly
   that gap makes the two equal. It applies only while the slot is empty: once
   it carries a message the selector stops matching, the margin returns to 0
   and the 4px is back where it belongs, under the field it describes.

   Not applied to the contact form: there the same run is the 16px the client
   approved from sheet 08, and cancelling the gap would quietly take it to
   12px. */
.workshops-notify-form .field-error:empty {
  margin-block-start: calc(var(--space-xs) * -1);
}

/* Honeypot – hidden from people, still fillable by bots.
   NOT parked off-screen: `inset-inline-start: -9999px` resolves to
   `right: -9999px` in RTL, which pushed a box ~10,000px past the page's right
   edge on every form. That phantom width was the source of the sideways drag
   on phones (the empty white expanse users could pull the page into). The box
   now stays at its static position inside the form and is hidden by clipping
   it to nothing, which costs no layout width at all. */
.psycho-hp {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
}

.form-submit {
  margin-block-start: var(--space-sm);
}

.form-submit-btn {
  width: 100%;
}

.btn-loading {
  display: none;
  width: 16px;
  height: 16px;
  border: 2px solid transparent;
  border-block-start-color: currentColor;
  border-radius: 50%;
  animation: spin .7s linear infinite;
}

.is-submitting .btn-loading {
  display: inline-block;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

.form-response {
  padding: var(--space-md);
  border-radius: var(--radius-md);
  font-weight: 500;
  display: none;
}

/* These panels MUST use solid backgrounds, not alpha tints. They render on the
   recessed band in the contact section and on the dark ground in the footer,
   and an alpha tint takes its lightness from whatever is underneath – over
   .bg-light the success tint composited down to 4.04:1, under AA. Solid keeps
   the ratio fixed wherever the panel appears.
   The success panel is a warm sage (hue 88°), not the old mint (hue 146°), so
   it stays inside the palette's temperature while still reading as "green". */
.form-response.success {
  background: #E8EEE1;
  color: #2E563D;              /* 7.06:1 (AAA) */
  display: block;
}

.form-response.error {
  background: #F5E4DC;
  color: var(--color-primary-dark);  /* 6.65:1 (AA) */
  display: block;
}

/* Newsletter form inside dark section */
.newsletter-section .form-control {
  background: rgba(255,255,255,.1);
  border-color: rgba(255,255,255,.3);
  color: var(--color-white);
}

.newsletter-section .form-control::placeholder {
  color: rgba(255,255,255,.6);
}

.newsletter-section .form-group label {
  color: var(--color-white);
}

.newsletter-section .form-submit-btn {
  background: var(--color-white);
  color: var(--color-primary);
  border-color: var(--color-white);
}

.no-items {
  color: var(--color-text-muted);
  font-style: italic;
  padding: var(--space-2xl) 0;
}

/* Section form wrapper */
.section-form-wrap {
  max-width: 540px;
  margin-inline: auto;
  margin-block-start: var(--space-2xl);
  padding: var(--space-xl);
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

/* --------------------------------------------------------------------------
   18. Footer
   -------------------------------------------------------------------------- */
.site-footer {
  background-color: var(--color-bg-dark);
  color: var(--color-white);
  padding-block: var(--space-3xl) calc(var(--space-xl) + var(--dock-clearance));
}

.footer-newsletter {
  text-align: center;
  margin-block-end: var(--space-3xl);
  max-width: 560px;
  margin-inline: auto;
}

/* The English page's clinic photograph, beside the newsletter (option ד).
   Stacked and centred on a phone, a row from 900px - where the picture costs
   the page only 63px of height because it stands next to the form instead of
   above it. No shadow: the footer ground is the dark ink, and the soft warm
   shadow the Hebrew page's plates carry has nothing to sit on here. */
.footer-en-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xl);
}

.footer-en-row .footer-newsletter {
  margin-block-end: 0;
}

.footer-clinic {
  margin: 0;
}

.footer-clinic img {
  display: block;
  width: 100%;
  max-width: 320px;
  height: auto;
  margin-inline: auto;
  border-radius: var(--radius-lg);
}

@media (min-width: 900px) {
  .footer-en-row {
    flex-direction: row;
    justify-content: center;
    align-items: center;
    gap: var(--space-3xl);
  }

  .footer-en-row .footer-newsletter {
    margin-inline: 0;
    text-align: start;
  }

  .footer-clinic img {
    max-width: 330px;
  }
}

.footer-newsletter-heading {
  color: var(--color-white);
  margin-block-end: var(--space-sm);
}

.footer-newsletter p {
  color: rgba(255,255,255,.7);
  margin-block-end: var(--space-lg);
}

/* Labels sit on the dark spruce footer – must stay legible (WCAG AA) */
.footer-newsletter .form-group label {
  color: rgba(252, 248, 241, 0.92);
}

.footer-newsletter .required {
  color: var(--color-primary-light);
}

.footer-nav {
  margin-block-end: var(--space-xl);
}

.footer-nav-list {
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-md);
}

.footer-nav-list a {
  color: rgba(255,255,255,.8);
  text-decoration: none;
  font-size: var(--font-size-sm);
  min-height: 44px;
  min-width: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding-inline: var(--space-xs);
}

.footer-nav-list a:hover {
  color: var(--color-white);
}


.footer-bottom {
  border-block-start: 1px solid rgba(255,255,255,.1);
  padding-block-start: var(--space-lg);
  text-align: center;
}

.footer-copyright,
.footer-credit {
  color: rgba(255,255,255,.5);
  font-size: var(--font-size-sm);
  margin: 0;
}

.footer-credit {
  margin-block-start: var(--space-xs);
}

/* --------------------------------------------------------------------------
   19. Floating Action Buttons (phone + WhatsApp)
   -------------------------------------------------------------------------- */
/* Contact dock – one quiet paper pill, two one-tap actions.
   Physically bottom-RIGHT (client requirement; deliberate physical values,
   not logical properties, so RTL doesn't flip it). WhatsApp keeps its
   recognizable green on the glyph; the framing speaks the site's language. */
.float-stack {
  position: fixed;
  bottom: 1.25rem;
  right: 1.25rem;
  left: auto;
  z-index: 900;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  background-color: rgba(252, 248, 241, 0.92);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  padding: 0.3rem;
  box-shadow: 0 2px 14px rgba(43, 33, 24, 0.16);
}

.float-btn {
  width: 48px;
  height: 48px;
  border-radius: var(--radius-full);
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  transition: background-color var(--transition), color var(--transition);
  flex-shrink: 0;
}

/* hairline divider between the two actions */
.float-btn + .float-btn {
  position: relative;
  margin-top: 7px;
}

.float-btn + .float-btn::before {
  content: '';
  position: absolute;
  top: -4px;
  left: 10px;
  right: 10px;
  height: 1px;
  background: var(--color-border);
}

.float-btn--phone {
  color: var(--color-secondary);
}

.float-btn--phone:hover,
.float-btn--phone:focus-visible {
  background-color: var(--color-secondary);
  color: var(--color-bg);
}

.float-btn--whatsapp {
  color: #1BA05B; /* WhatsApp green, darkened to 3.1:1 on paper */
}

.float-btn--whatsapp:hover,
.float-btn--whatsapp:focus-visible {
  background-color: #1BA05B;
  /* True white, not --color-white: this pair is WhatsApp brand colour, so it
     must not drift with the palette and its measured contrast stays as-is. */
  color: #FFFFFF;
}

.float-btn-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
}

/* --------------------------------------------------------------------------
   20. Float Widget
   -------------------------------------------------------------------------- */
.float-widget {
  position: fixed;
  inset-block-end: 5rem;
  inset-inline-end: 1.5rem;
  z-index: 890;
  max-width: 320px;
}

.float-widget-toggle {
  width: 100%;
}

.float-widget-body {
  position: absolute;
  inset-block-end: calc(100% + var(--space-sm));
  inset-inline-end: 0;
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  box-shadow: var(--shadow-lg);
  width: 320px;
}


/* --------------------------------------------------------------------------
   22. Responsive – Tablet+ (min-width: 640px)
   -------------------------------------------------------------------------- */
@media (min-width: 640px) {
  :root {
    --container-pad: 1.5rem;
    --section-pad:   4rem;
    --header-height: 72px;
  }

  h1 { font-size: var(--font-size-4xl); }
  h2 { font-size: var(--font-size-3xl); }

  .cards-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .newsletter-fields {
    flex-direction: row;
  }

  /* flex-start, not the default stretch: a stretched .contact-clinic would be
     pulled to the form's full height and the image inside it would have to
     crop to fill. wrap lets the figure drop to its own line below 900px. */
  .contact-inner {
    flex-direction: row;
    align-items: flex-start;
    flex-wrap: wrap;
  }
}

/* --------------------------------------------------------------------------
   23. Responsive – Desktop (min-width: 900px)
   -------------------------------------------------------------------------- */
@media (min-width: 900px) {
  :root {
    --container-pad:  2rem;
    --section-pad:    5rem;
    --header-height:  76px;
  }

  .about-inner {
    flex-direction: row;
    align-items: flex-start;
  }

  .about-image-wrap {
    max-width: 380px;
  }

  .about-image {
    width: 100%;       /* fill the 380px wrap fully on desktop */
    max-height: none;  /* no viewport height cap needed on desktop */
    margin-inline: 0;
  }

  .cards-grid {
    grid-template-columns: repeat(3, 1fr);
  }

}

/* --------------------------------------------------------------------------
   23b. Responsive – Desktop Nav (min-width: 1040px)
   --------------------------------------------------------------------------
   1040, not 1024. A media query is evaluated against the layout viewport,
   which is the window minus a classic scrollbar – so a 1024px-wide window on
   Windows reports about 1008px and never reaches a 1024px threshold. Checking
   the header at "1024" therefore showed the hamburger, not the bar it was
   meant to show. 1040 clears the widest common scrollbar with room over.

   Everything below is desktop-only on purpose. The phone header – the stacked
   two-line logo and the hamburger – is defined outside this block and is not
   touched by any of it. */
@media (min-width: 1040px) {
  /* Switch from hamburger to horizontal desktop nav */
  .site-nav {
    display: flex;
    flex: 1;
  }

  .nav-toggle {
    display: none;
  }

  /* The desktop nav ends with the same language link, so the bar's copy would
     be a duplicate - and an accessibility one, two links to the same place with
     the same name. */
  .header-lang {
    display: none;
  }

  /* "בואו נדבר" is off the desktop bar. It still opens every phone menu
     (.mobile-nav-cta) and the floating dock is on every screen, so the button
     is not gone from the site - only from the row where it was crowding the
     title, the nav and the language switch into 26px of slack at 1040. */

  /* The requested breathing room between the title and the first nav item.
     .header-inner holds only the logo and the nav now, so this gap applies
     once, in one place. */
  .header-inner {
    gap: 56px;
  }

  /* The name and the professional descriptor are two separate spans in the
     markup, stacked on phones. Here they lie on one row and read as a single
     line. The separator is generated instead of written into either string,
     so no template text and no Customizer value changes to produce it. */
  .site-logo {
    flex: 0 0 auto;
    max-width: none;
    overflow: visible;
  }

  /* align-items, not justify-content. The stacked phone logo centres itself
     vertically with justify-content because its main axis runs down the page;
     turning the row horizontal handed that property to the horizontal axis and
     silently dropped the centring, which left the title sitting 9px above the
     nav it is supposed to sit level with. Both spans carry the same face, size
     and weight here, so centring the boxes lines up the baselines too. */
  .logo-text-link {
    flex-direction: row;
    align-items: center;
    overflow: visible;
  }

  .logo-title {
    display: block;
    font-family: var(--font-serif);
    font-size: 1.35rem;
    font-weight: 700;
    letter-spacing: normal;
    color: var(--color-text);
    overflow: visible;
  }

  .logo-title::before {
    content: ' - ';
    white-space: pre;
  }

  /* The nav opens right beside the title and stretches; the language switch is
     pushed to the far inline-end so it holds that corner on its own. It is
     always the last item – psycho_anchor_nav() appends it after the sections –
     and the margin sits on the <li> so the link keeps its 44px hit area.
     Scoped to .nav-list, which is the desktop bar only: the off-canvas panel
     renders as .mobile-nav-list and never matches. */
  .nav-list {
    flex-wrap: nowrap;
    width: 100%;
  }

  .nav-list > li:last-child {
    margin-inline-start: auto;
  }
}

/* From 1440px the footer's centred closing lines are narrow enough that they
   no longer reach the dock's column on the inline-end edge (verified by
   measuring the overlap at 1200 and 1440), so the reserved band is released
   and the footer returns to its normal bottom padding. If a longer credit
   line is ever added, re-check this threshold. */
@media (min-width: 1440px) {
  :root {
    --dock-clearance: 0px;
  }
}

/* --------------------------------------------------------------------------
   24. 404
   -------------------------------------------------------------------------- */
.error-404 .error-content {
  padding-block: clamp(5rem, 18vh, 10rem);
  max-width: 560px;
}

.error-404 h1 {
  margin-block-end: var(--space-md);
}

.error-404 p {
  color: var(--color-text-light);
  margin-block-end: var(--space-xl);
}

/* --------------------------------------------------------------------------
   25. English page (page-en.php)

   The only left-to-right reading column on the site. Everything structural
   is borrowed: .section supplies the vertical rhythm, .container the gutter,
   .section-heading the heading face and size, and body supplies the 1.7
   line-height and the --space-paragraph gap between paragraphs. Nothing here
   introduces a colour, a size, or a spacing step that the palette above does
   not already define.

   Direction is scoped to the article element in the template (lang="en"
   dir="ltr"); no rule in this file changes the document's RTL default, and
   no Hebrew page can match any selector below.
   -------------------------------------------------------------------------- */
/* The direction wrapper. It holds lang/dir and the shared text treatment, but
   no width: its children are full-bleed <section>s, so that the second one can
   paint its background edge to edge. The measure lives on .page-en-col. */
.page-en {
  /* Left aligned at every width. See the note on the justify block in
     section 5: this page is deliberately excluded from it. */
  text-align: left;

  /* Same ink as the site's other running prose (.about-bio). Headings keep
     the full-strength --color-text they inherit from the h1-h6 rule. */
  color: var(--color-text-light);
}

/* The reading column, one per section.

   The measure the stylesheet already uses for running copy (.section-intro).
   At the 17px body size it lands around 70 English characters per line -
   inside the comfortable range - and on mobile the container gutter takes
   over long before this cap is reached, so the line never runs edge to edge. */
.page-en-col {
  max-width: 680px;
  margin-inline: auto;
}

/* The break between "About Me" and "Research" is carried by the change of
   ground: "Research" sits on .bg-light, the recessed rung of the neutral
   ladder, which is the same alternating section ground the Hebrew page uses.
   No rule and no border is drawn, and no new colour is introduced - the two
   sections' own --section-pad supplies the breathing room on either side of
   the seam, so no extra heading margin is needed here.

   Contrast on the recessed rung: body copy --color-text-light reaches 6.02:1
   and headings --color-text 11.83:1, both past AA. The .bg-light rule in
   section 4 already steps coloured text (the closing .cta-quiet) down to
   --color-primary-dark, 6.17:1. */

/* The portrait reuses .about-image-wrap / .about-image wholesale, so its size,
   corners and lack of shadow are defined once, in section 10. Only the gap
   down to the heading is set here, matching the gap .about-inner puts between
   the picture and the copy on the Hebrew page. */
.page-en .about-image-wrap {
  margin-block-end: var(--space-2xl);
}

/* Return path to the Hebrew site, as the closing line of the column. */
.page-en-lang {
  margin-block-start: var(--space-2xl);
}

/* .cta-quiet is normally set inside running text, where the line box already
   gives it a comfortable height. Standing alone on its own line it measures
   only 28px, under the 44px minimum the rest of the site holds itself to
   (.btn, .nav-link, .footer-nav-list a all set it explicitly).

   The target is grown with an invisible overlay rather than with padding, so
   the underline stays tight against the word instead of dropping away from
   it. The overlay is sized absolutely, not by offsets: the link's own box is
   text-metric height (~27px, and it moves with the font), so offsets of -8px
   landed at 43px - a pixel under, for no visible reason. A centred 44x44
   minimum states the rule instead of arriving at it. */
.page-en-lang .cta-quiet {
  position: relative;
}

.page-en-lang .cta-quiet::after {
  content: '';
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
  min-width: 44px;
  height: 44px;
}

/* --------------------------------------------------------------------------
   26. Print
   -------------------------------------------------------------------------- */
@media print {
  .site-header,
  .whatsapp-float,
  .float-widget,
  .site-footer {
    display: none;
  }
  .section { padding: 1rem 0; }
}
