/* ==========================================================================
   Kath Landreth Counselling & Supervision — Placeholder Visual Identity
   --------------------------------------------------------------------------
   Business name, palette, and typography below are PLACEHOLDERS.
   Everything visual is driven by the CSS variables in :root, so swapping
   in real branding later should mean editing this one block only.
   ========================================================================== */

:root {
  /* --- Colour palette (placeholder: warm, muted, calming) --- */
  --color-bg: #faf6f0;            /* warm cream page background */
  --color-bg-alt: #f1e9dc;        /* soft sand for alternating sections */
  --color-surface: #ffffff;       /* cards / form surface */
  --color-primary: #7c9473;       /* muted sage green */
  --color-primary-dark: #5f7657;  /* deeper sage for hover/emphasis */
  --color-accent: #c98b6b;        /* soft terracotta accent */
  --color-accent-dark: #b06f4f;
  --color-text: #3b352e;          /* warm charcoal-brown, not pure black */
  --color-text-muted: #6b6259;
  --color-border: #e4d9c8;
  --color-focus: #4f7db3;         /* accessible focus ring, kept distinct from palette */

  /* --- Typography (placeholder: system fonts, no third-party font loads) --- */
  --font-heading: Georgia, "Iowan Old Style", "Palatino Linotype", "Book Antiqua", serif;
  --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  /* --- Spacing / shape --- */
  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-lg: 24px;
  --shadow-soft: 0 6px 24px rgba(59, 53, 46, 0.08);
  --max-width: 1100px;
}

/* --- Reset / base --- */
* {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--font-body);
  background: var(--color-bg);
  color: var(--color-text);
  line-height: 1.65;
  font-size: 17px;
  animation: page-fade-in 0.6s ease;
}

@keyframes page-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  body {
    animation: none;
  }
}

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

a {
  color: var(--color-primary-dark);
}

a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 2px;
}

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  line-height: 1.25;
  margin: 0 0 0.6em;
  color: var(--color-text);
  font-weight: 600;
}

h1 { font-size: clamp(2rem, 5vw, 2.9rem); text-wrap: balance; }
h2 { font-size: clamp(1.5rem, 3.5vw, 2.1rem); }
h3 { font-size: clamp(1.15rem, 2.5vw, 1.4rem); }

p { margin: 0 0 1em; }

main ul, main ol {
  margin: 0 0 1em;
  padding-left: 1.4em;
  color: var(--color-text);
}

main li + li {
  margin-top: 0.3em;
}

code {
  font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
  background: var(--color-bg-alt);
  padding: 0.15em 0.4em;
  border-radius: 4px;
  font-size: 0.9em;
}

.container {
  width: 100%;
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 1.25rem;
}

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

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--color-primary-dark);
  color: #fff;
  padding: 0.75em 1.25em;
  border-radius: 0 0 var(--radius-sm) 0;
  z-index: 100;
}

.skip-link:focus {
  left: 0;
}

/* --- Buttons --- */
.btn {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 600;
  text-decoration: none;
  padding: 0.85em 1.7em;
  border-radius: 999px;
  border: 2px solid transparent;
  cursor: pointer;
  transition: transform 0.15s ease, background-color 0.15s ease, color 0.15s ease;
}

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

.btn-primary:hover {
  background: var(--color-primary-dark);
}

.btn-secondary {
  background: transparent;
  border-color: var(--color-primary);
  color: var(--color-primary-dark);
}

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

.btn:hover {
  transform: translateY(-1px);
}

.btn-group {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
}

@media (max-width: 840px) {
  /* Keep button pairs side by side on phones instead of stacking:
     shrink padding/font just enough that two buttons fit one row. */
  .btn-group {
    flex-wrap: nowrap;
    gap: 0.6rem;
  }

  .btn-group .btn {
    flex: 1 1 0;
    min-width: 0;
    padding: 0.75em 0.6em;
    font-size: 0.88rem;
    text-align: center;
    line-height: 1.25;
    /* When one label wraps to two lines it stretches the row (and
       therefore the other button) taller; inline-block content just
       sits at the top of that extra height by default, so a
       single-line label like "Read about Kath" looked pinned to the
       top instead of centred. Flex + centering fixes that regardless
       of how many lines either label ends up wrapping to. */
    display: flex;
    align-items: center;
    justify-content: center;
  }
}

/* --- Header / Nav --- */
.site-header {
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-border);
  position: sticky;
  top: 0;
  z-index: 50;
  /* iOS Safari has a longstanding bug where a sticky element's paint
     lags behind its actual (correct) position while the address bar
     is collapsing/expanding mid-scroll, leaving a gap that briefly
     shows the page scrolling underneath. Promoting it to its own
     compositor layer is the standard fix — it stops relying on Safari
     to repaint the sticky element in step with the toolbar animation. */
  transform: translateZ(0);
}

.nav-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.25rem;
  max-width: var(--max-width);
  margin: 0 auto;
}

.site-brand {
  font-family: var(--font-heading);
  font-size: 1.25rem;
  font-weight: 700;
  text-decoration: none;
  color: var(--color-text);
}

.site-brand .brand-sub {
  display: block;
  font-family: var(--font-body);
  font-size: 0.7rem;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
}

.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  width: 44px;
  height: 44px;
  padding: 0;
  cursor: pointer;
}

.nav-toggle .icon-bar {
  display: block;
  width: 20px;
  height: 2px;
  background: var(--color-text);
  position: relative;
}

.nav-toggle .icon-bar::before,
.nav-toggle .icon-bar::after {
  content: "";
  position: absolute;
  left: 0;
  width: 20px;
  height: 2px;
  background: var(--color-text);
}

.nav-toggle .icon-bar::before { top: -6px; }
.nav-toggle .icon-bar::after { top: 6px; }

.nav-links {
  list-style: none;
  display: flex;
  gap: 1.75rem;
  margin: 0;
  padding: 0;
}

.nav-links a {
  text-decoration: none;
  color: var(--color-text);
  font-weight: 500;
}

.nav-links a[aria-current="page"] {
  color: var(--color-primary-dark);
  border-bottom: 2px solid var(--color-primary);
}

.nav-links a:hover {
  color: var(--color-primary-dark);
}

@media (max-width: 780px) {
  .nav-toggle {
    display: inline-flex;
  }

  .nav-links {
    display: none;
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    flex-direction: column;
    gap: 0;
    background: var(--color-bg);
    border-bottom: 1px solid var(--color-border);
    box-shadow: var(--shadow-soft);
  }

  .nav-links.is-open {
    display: flex;
  }

  .nav-links li {
    border-top: 1px solid var(--color-border);
  }

  .nav-links a {
    display: block;
    padding: 1em 1.25rem;
  }
}

@media (min-width: 781px) {
  .nav-toggle {
    display: none;
  }
}

/* --- Hero --- */
.hero {
  padding: 4rem 0 2rem;
  background: linear-gradient(180deg, var(--color-bg-alt) 0%, var(--color-bg) 100%);
}

@media (max-width: 840px) {
  /* Sticky header already anchors the top of the page; a full 4rem of
     extra padding above the eyebrow pill just pushes content down.
     Bottom padding is reduced further still — most noticeable on
     Contact & Booking, whose hero has no buttons to fill that
     trailing space, so even a modest bottom padding reads as a big
     gap before the form. */
  .hero {
    padding-top: 1.5rem;
    padding-bottom: 0.5rem;
  }
}

.hero-inner {
  display: grid;
  gap: 2.5rem;
  align-items: center;
}

@media (min-width: 860px) {
  .hero-inner {
    grid-template-columns: 1.1fr 0.9fr;
  }
}

.hero-eyebrow {
  display: inline-block;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-primary-dark);
  background: rgba(124, 148, 115, 0.12);
  padding: 0.35em 0.9em;
  border-radius: 999px;
  margin-bottom: 1.1rem;
}

.hero-lede {
  font-size: 1.15rem;
  color: var(--color-text-muted);
  max-width: 46ch;
}

.hero-image-slot {
  background: var(--color-bg-alt);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-lg);
  aspect-ratio: 4 / 3;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-muted);
  font-size: 0.9rem;
  text-align: center;
  padding: 1.5rem;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.hero-image-slot:hover {
  transform: translateY(-4px);
  box-shadow: 0 10px 28px rgba(59, 53, 46, 0.14);
}

@media (max-width: 840px) {
  /* This is still just placeholder text in a box, so on phones it
     shrinks to a short banner strip instead of a near-square block
     that eats most of the first screen. Widen this back out (or
     remove the override) once a real photo replaces the placeholder. */
  .hero-image-slot {
    aspect-ratio: 21 / 9;
    padding: 1rem;
    font-size: 0.8rem;
  }
}

/* --- Generic sections --- */
.section {
  padding: 3.5rem 0;
}

/* Web only: the section right after a hero (most noticeable on
   Contact & Booking, whose hero has no buttons to fill the trailing
   space) gets a smaller top padding than sections generally do,
   closing up the gap between the intro text and whatever follows. */
.hero + .section {
  padding-top: 2rem;
}

@media (max-width: 840px) {
  /* Full desktop padding between every stacked section adds up to a
     lot of dead scrolling on phones, where sections stack in a single
     column anyway. */
  .section {
    padding: 2.25rem 0;
  }

  /* The section right after a hero gets an even smaller top padding:
     combined with the hero's own reduced bottom padding, this closes
     the gap noticeable on Contact & Booking between the intro text and
     the form (Home's hero has buttons filling that space, so it's less
     visible there, but the tighter spacing suits it too). */
  .hero + .section {
    padding-top: 1rem;
  }
}

.section-alt {
  background: var(--color-bg-alt);
}

.section-heading {
  max-width: 62ch;
  margin-bottom: 2rem;
}

.section-heading p {
  color: var(--color-text-muted);
}

/* --- Cards / grid --- */
.card-grid {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: 1fr;
}

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

@media (min-width: 960px) {
  .card-grid-3 {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* --- Animated hover outline ---
   Draws a green ring that sweeps around the container's edge on hover,
   starting and finishing at the top (12 o'clock), with an eased S-curve
   (slow to start, fast through the middle, slow to finish). Implemented
   as a conic-gradient on a pseudo-element, masked down to a ring shape;
   @property is what makes the custom property (and therefore the
   gradient's sweep) animatable instead of snapping instantly. Shared by
   every "lift" container: cards, the note box, the hero image slot, and
   the about-page portrait slot.

   Duration is set per-element via --outline-duration (see
   initHoverOutlineTiming in js/main.js), which measures each element's
   own perimeter so the ring sweeps at a constant visual speed rather
   than a fixed time — otherwise the same 0.5s that suits a small card
   makes the sweep look rushed on a much wider container like the note
   box. The 0.5s fallback only applies if JS hasn't run yet. */
@property --outline-pct {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}

.card,
.note-box,
.hero-image-slot,
.portrait-slot {
  position: relative;
}

.card::after,
.note-box::after,
.hero-image-slot::after,
.portrait-slot::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 2px;
  background: conic-gradient(var(--color-primary) var(--outline-pct, 0%), transparent 0%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
  transition: --outline-pct var(--outline-duration, 0.5s) cubic-bezier(0.65, 0, 0.35, 1);
}

.card:hover::after,
.portrait-slot:hover::after,
.note-box:hover::after,
.hero-image-slot:hover::after {
  --outline-pct: 100%;
}

.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 1.75rem;
  box-shadow: var(--shadow-soft);
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.2s ease;
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 10px 28px rgba(59, 53, 46, 0.14);
}

.card h3 {
  margin-bottom: 0.5em;
}

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

/* Clickable cards (e.g. Home's "What Kath offers") wrap only their
   heading in a link, then stretch it over the full card with ::after
   so the whole card is clickable — not just the heading text. The
   animated hover-outline ring above needs to live on .card itself
   rather than the <a>: browsers don't animate a transition on a
   registered custom property (--outline-pct) when it's declared on a
   focusable element, they just snap straight to the end value instead
   of sweeping. Keeping the link non-card-sized sidesteps that. */
.card-link {
  color: inherit;
  text-decoration: none;
  -webkit-user-drag: none;
}

.card-link::after {
  content: "";
  position: absolute;
  inset: 0;
}

@media (max-width: 840px) {
  /* Used on the Home page's "What Kath offers" cards, which become a
     carousel on mobile (see initOfferingsCarousel in js/main.js).
     Full desktop type size made each card's own carousel slide (and
     therefore the whole carousel, which sizes itself to the tallest
     slide) taller than it needs to be; a smaller, tighter card keeps
     the same compact-rectangle feel the grid has on desktop. */
  .card-compact {
    padding: 1.25rem;
  }

  .card-compact h3 {
    font-size: 1.05rem;
  }

  .card-compact p {
    font-size: 0.92rem;
  }

  /* About page's Services carousel specifically: its placeholder copy
     runs noticeably longer than Home's offering cards, so on top of
     the smaller type above, clamp to a few lines with an ellipsis. That
     caps every card at roughly the same, close-to-square height
     regardless of how long an individual service's text is, instead of
     the tallest one dictating a much taller carousel for all of them. */
  #services-carousel .card-compact p {
    display: -webkit-box;
    -webkit-line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
}

/* --- Carousel (shared component) ---
   Used by the About & Services page's Services carousel (always on)
   and the Home page's "What Kath offers" carousel (mobile-only, see
   initOfferingsCarousel in js/main.js). Base rules (no JS-active
   class) intentionally match the plain .card-grid layout, so the
   cards stay fully visible and readable if JavaScript fails to load,
   or on Home above the mobile breakpoint. JS adds "is-carousel" to the
   root element once it activates carousel mode, which is what
   switches the track over to absolute-positioned, circular slide
   layout below. */
.carousel-track {
  list-style: none;
  margin: 0;
  padding: 0;
}

.carousel-slide {
  margin: 0;
}

.carousel {
  position: relative;
}

.carousel-viewport {
  overflow: visible;
}

.carousel-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-soft);
  color: var(--color-text);
  font-size: 1.5rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.carousel-arrow:hover {
  background: var(--color-primary);
  color: #fff;
}

.carousel-arrow-prev {
  left: 0;
}

.carousel-arrow-next {
  right: 0;
}

.carousel-dots {
  display: flex;
  justify-content: center;
  gap: 0.6rem;
  margin-top: 1.75rem;
}

/* `.carousel-arrow`/`.carousel-dots` set `display` unconditionally, which
   has the same specificity as the browser's own `[hidden] { display:
   none }` rule — with equal specificity the later (author) rule wins,
   so plain `hidden` silently did nothing on these. This bumps the
   specificity so toggling `hidden` in JS (see setupCarousel in
   js/main.js) actually hides them, which matters once a carousel can
   be switched off again (the mobile-only "What Kath offers" one). */
.carousel-arrow[hidden],
.carousel-dots[hidden] {
  display: none;
}

.carousel-dot {
  width: 10px;
  height: 10px;
  padding: 0;
  border-radius: 50%;
  border: 2px solid var(--color-primary);
  background: transparent;
  cursor: pointer;
  transition: background-color 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
}

.carousel-dot:hover {
  border-color: var(--color-primary-dark);
}

.carousel-dot.is-active {
  background: var(--color-primary);
  transform: scale(1.2);
}

/* Carousel mode: only applied once JS confirms it initialised */
.carousel.is-carousel .carousel-viewport {
  /* overflow-x/overflow-y set to different keywords (e.g. hidden + visible)
     makes browsers resolve "visible" to "auto" per spec, which draws a
     real scrollbar. Keep overflow uniformly hidden instead, and reserve
     vertical padding as headroom for the active card's hover-lift
     (translateY) and its larger hover box-shadow, so neither gets
     clipped against the top/bottom edge. */
  overflow: hidden;
  padding: 16px 3.25rem;
  /* Fade the off-screen slides out gradually near the arrows instead of
     hard-clipping them at the overflow edge. */
  -webkit-mask-image: linear-gradient(
    to right,
    transparent 0,
    #000 70px,
    #000 calc(100% - 70px),
    transparent 100%
  );
  mask-image: linear-gradient(
    to right,
    transparent 0,
    #000 70px,
    #000 calc(100% - 70px),
    transparent 100%
  );
  cursor: grab;
  /* Let vertical page scroll pass through untouched; horizontal drags
     are handled entirely by JS pointer events (see setupCarousel in
     js/main.js) instead of the browser's own touch panning. */
  touch-action: pan-y;
}

.carousel.is-carousel .carousel-track {
  display: block;
  position: relative;
  min-height: 200px;
  transition: height 0.4s ease;
}

.carousel.is-dragging .carousel-viewport {
  cursor: grabbing;
  user-select: none;
}

.carousel.is-dragging .carousel-slide {
  /* Track the pointer 1:1 while dragging instead of animating toward
     it — the transition comes back for the snap/settle once the drag
     ends and render() sets the final resting position. */
  transition: none;
}

.carousel.is-carousel .carousel-slide {
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(100%, 480px);
  transform: translate(-50%, -50%) translateX(var(--slide-offset, 0px)) scale(var(--slide-scale, 1));
  opacity: var(--slide-opacity, 1);
  filter: blur(var(--slide-blur, 0px));
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.4s ease, filter 0.4s ease;
  pointer-events: none;
}

.carousel.is-carousel .carousel-slide.is-active {
  pointer-events: auto;
  z-index: 2;
}

@media (max-width: 840px) {
  .carousel.is-carousel .carousel-viewport {
    padding: 16px 2.75rem;
    -webkit-mask-image: linear-gradient(
      to right,
      transparent 0,
      #000 44px,
      #000 calc(100% - 44px),
      transparent 100%
    );
    mask-image: linear-gradient(
      to right,
      transparent 0,
      #000 44px,
      #000 calc(100% - 44px),
      transparent 100%
    );
  }

  .carousel-arrow {
    width: 38px;
    height: 38px;
    font-size: 1.25rem;
  }
}

/* --- Swipe hint (mobile only, both carousels — see config.swipe in
   js/main.js). Once the carousel scrolls into view, JS keeps the
   active slide nudging left/right on a loop (a pause between each
   pass) and keeps a "Swipe" label up over the centre of the card,
   until the visitor actually interacts, at which point the arrows
   settle from a solid button down to a faint overlay, since swipe is
   the primary way to browse here. All of this is invisible outside
   this breakpoint regardless of when JS fires it, so the always-on
   Services carousel still behaves exactly as before at desktop
   widths. */
.carousel-hint {
  display: none;
}

@media (max-width: 840px) {
  .carousel-hint {
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    inset: 0;
    margin: auto;
    width: fit-content;
    height: fit-content;
    background: rgba(59, 53, 46, 0.55);
    color: #fff;
    font-size: 0.95rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    padding: 0.6em 1.4em;
    border-radius: 999px;
    opacity: 0;
    pointer-events: none;
    z-index: 5;
    transition: opacity 0.5s ease;
  }

  .carousel-hint.is-visible {
    opacity: 1;
  }

  @keyframes carousel-swipe-nudge {
    0%, 100% {
      transform: translate(-50%, -50%) translateX(var(--slide-offset, 0px)) scale(var(--slide-scale, 1));
    }
    30% {
      transform: translate(-50%, -50%) translateX(calc(var(--slide-offset, 0px) - 16px)) scale(var(--slide-scale, 1));
    }
    65% {
      transform: translate(-50%, -50%) translateX(calc(var(--slide-offset, 0px) + 16px)) scale(var(--slide-scale, 1));
    }
  }

  .carousel.is-carousel .carousel-slide.is-active.show-swipe-nudge {
    animation: carousel-swipe-nudge 1.1s ease;
  }

  /* Arrows start as a soft, semi-transparent overlay rather than solid
     buttons, then fade further once the hint above has had a moment to
     register (or immediately, if the visitor swipes or taps before
     then — see cancelHint() in js/main.js). Still fully clickable at
     any opacity. */
  .carousel.is-carousel .carousel-arrow {
    background: rgba(255, 255, 255, 0.75);
    box-shadow: none;
    transition: opacity 0.8s ease, background-color 0.2s ease;
  }

  .carousel.is-carousel .carousel-arrow.is-faded {
    opacity: 0.4;
  }
}

/* --- Reassurance / trust strip --- */
.trust-strip {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: 1fr;
  text-align: left;
}

@media (min-width: 720px) {
  .trust-strip {
    grid-template-columns: repeat(3, 1fr);
  }
}

.trust-item {
  padding: 1.25rem;
}

.trust-item h3 {
  font-size: 1.05rem;
  margin-bottom: 0.4em;
}

.trust-item p {
  color: var(--color-text-muted);
  font-size: 0.95rem;
  margin-bottom: 0;
}

@media (max-width: 840px) {
  .trust-strip {
    gap: 0.5rem;
  }

  .trust-item {
    padding: 0.5rem 0;
  }
}

/* --- Bio layout --- */
.bio-layout {
  display: grid;
  gap: 2rem;
  align-items: start;
}

@media (min-width: 780px) {
  .bio-layout {
    grid-template-columns: 0.8fr 1.2fr;
  }
}

.portrait-slot {
  background: var(--color-bg-alt);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-lg);
  aspect-ratio: 3 / 4;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-muted);
  font-size: 0.9rem;
  text-align: center;
  padding: 1.5rem;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.portrait-slot:hover {
  transform: translateY(-4px);
  box-shadow: 0 10px 28px rgba(59, 53, 46, 0.14);
}

@media (max-width: 840px) {
  /* Mobile only: read the bio before the (still-placeholder) portrait,
     and shrink that portrait to the same short banner treatment as the
     Home page's hero image, instead of a near-full-screen block. */
  .portrait-slot {
    order: 2;
    aspect-ratio: 21 / 9;
    padding: 1rem;
    font-size: 0.8rem;
  }

  .bio-content {
    order: 1;
  }

  /* Shortens the bio so the (still-placeholder) portrait below appears
     sooner in the scroll; the secondary paragraph is supplementary
     detail, not the core intro. */
  .bio-secondary {
    display: none;
  }
}

/* --- Confidentiality note box --- */
.note-box {
  background: var(--color-surface);
  border-radius: var(--radius-sm);
  padding: 1.5rem 1.75rem;
  box-shadow: var(--shadow-soft);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.note-box:hover {
  transform: translateY(-4px);
  box-shadow: 0 10px 28px rgba(59, 53, 46, 0.14);
}

.note-box h3 {
  margin-bottom: 0.5em;
}

.note-box p:last-child {
  margin-bottom: 0;
}

/* Confidentiality box on about.html: full copy on desktop, a shorter
   version with a Privacy Policy link on mobile (see the HTML comment
   next to these paragraphs for why this needs two versions rather than
   one). */
.confidentiality-short {
  display: none;
}

@media (max-width: 840px) {
  .confidentiality-full {
    display: none;
  }

  .confidentiality-short {
    display: block;
  }
}

/* --- Contact page --- */
.contact-layout {
  display: grid;
  gap: 2.5rem;
}

.contact-layout > * {
  /* Grid items default to min-width: auto, which lets a long unbroken
     token (an email address, a URL) force its column wider than the
     container's own padding-box instead of wrapping — the form and
     "What happens next" card were overflowing past the right padding
     because of exactly this. Overriding it lets them shrink properly. */
  min-width: 0;
}

@media (min-width: 900px) {
  .contact-layout {
    grid-template-columns: 1fr 0.8fr;
  }
}

.contact-form {
  position: relative;
  overflow: hidden;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 2rem;
  box-shadow: var(--shadow-soft);
  /* height only ever changes via inline style, and only on mobile (see
     submitViaFetch in js/main.js) — shrinking the form down to a
     compact fixed height right as it starts submitting, since the
     full-height form with every field showing is tall enough that the
     loading/success sequence could otherwise play out below the
     visitor's current scroll position on a narrow screen. */
  transition: background-color 0.9s ease, border-color 0.9s ease, height 0.5s cubic-bezier(0.65, 0, 0.35, 1);
}

/* --- Submit animation ---
   Faux (decorative, not tied to real network timing) loading sequence
   played on successful submission — see submitViaFetch in js/main.js
   for the class/timing choreography. .form-fields (every normal
   field, the submit button, the privacy notes) fades out and
   .form-submit-overlay fades in showing a spinning grey arc and a
   "Submitting" label; the container then fades to the site's green
   while the arc and label fade to white; the arc then closes into a
   full circle and a checkmark draws inside it, and the label switches
   to "Submitted" (that text swap is plain JS, not animated). Once the
   checkmark has visibly finished landing, .submit-success-group (the
   label + icon) rises slightly and .submit-followup-wrap grows open
   beneath it, fading in a short "what happens next" message — the
   group's rise isn't its own transition, it falls out of the overlay's
   flex column staying centred as the wrap grows from 0 to full height. */
.form-fields {
  transition: opacity 0.4s ease;
}

.contact-form.is-submitting .form-fields,
.contact-form.is-success .form-fields {
  opacity: 0;
  pointer-events: none;
}

.contact-form.is-success {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
}

.form-submit-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease;
}

.contact-form.is-submitting .form-submit-overlay,
.contact-form.is-success .form-submit-overlay {
  opacity: 1;
}

.submit-success-group {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
}

.submit-overlay-label {
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  transition: color 0.9s ease;
}

.contact-form.is-success .submit-overlay-label {
  color: #fff;
}

.submit-icon {
  overflow: visible;
}

@keyframes submit-spin {
  to {
    transform: rotate(360deg);
  }
}

.submit-icon-arc {
  stroke: var(--color-text-muted);
  stroke-linecap: round;
  stroke-dasharray: 28 85;
  transform-origin: 24px 24px;
  animation: submit-spin 0.9s linear infinite;
  transition: stroke 0.9s ease, opacity 0.3s ease 0.9s;
}

.contact-form.is-success .submit-icon-arc {
  stroke: #fff;
  opacity: 0;
}

.submit-icon-circle {
  stroke: #fff;
  stroke-dasharray: 113.1;
  stroke-dashoffset: 113.1;
  opacity: 0;
  transition: stroke-dashoffset 0.6s ease 0.9s, opacity 0.3s ease 0.9s;
}

.contact-form.is-success .submit-icon-circle {
  stroke-dashoffset: 0;
  opacity: 1;
}

.submit-icon-check {
  stroke: #fff;
  stroke-dasharray: 26;
  stroke-dashoffset: 26;
  opacity: 0;
  transition: stroke-dashoffset 0.4s ease 1.5s, opacity 0.2s ease 1.5s;
}

.contact-form.is-success .submit-icon-check {
  stroke-dashoffset: 0;
  opacity: 1;
}

/* Grid's fr-unit trick for animating from a 0 to an auto height,
   without JS having to measure the text first: the wrap is the grid
   (one row, sized 0fr), the paragraph is the sole row/grid item and
   needs its own overflow:hidden for the 0fr state to actually clip it. */
.submit-followup-wrap {
  display: grid;
  grid-template-rows: 0fr;
  width: 100%;
  transition: grid-template-rows 0.5s cubic-bezier(0.65, 0, 0.35, 1);
}

.contact-form.is-submitted .submit-followup-wrap {
  grid-template-rows: 1fr;
}

.submit-followup {
  overflow: hidden;
  margin: 0;
  padding: 0 1.5rem;
  max-width: 34ch;
  margin-inline: auto;
  text-align: center;
  font-size: 0.95rem;
  color: #fff;
  opacity: 0;
  transition: opacity 0.4s ease 0.15s;
}

.contact-form.is-submitted .submit-followup {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .submit-icon-arc {
    animation: none;
  }

  .submit-followup-wrap {
    transition-duration: 0.001s;
  }
}

.form-row {
  margin-bottom: 1.3rem;
}

.form-row label {
  display: block;
  font-weight: 600;
  margin-bottom: 0.4em;
  font-size: 0.95rem;
}

.form-row-checkbox {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
}

.form-row-checkbox input[type="checkbox"] {
  width: 1.1em;
  height: 1.1em;
  margin-top: 0.2em;
  flex-shrink: 0;
  accent-color: var(--color-primary);
}

.form-row-checkbox label {
  font-weight: 400;
  font-size: 0.92rem;
  color: var(--color-text-muted);
}

.form-row .hint {
  display: block;
  font-weight: 400;
  color: var(--color-text-muted);
  font-size: 0.85rem;
  margin-top: 0.3em;
}

.form-row input,
.form-row select,
.form-row textarea {
  width: 100%;
  font-family: var(--font-body);
  font-size: 1rem;
  padding: 0.75em 0.9em;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  color: var(--color-text);
}

.form-row select {
  appearance: auto;
}

.form-row input:focus,
.form-row select:focus,
.form-row textarea:focus {
  background: var(--color-surface);
}

.form-row textarea {
  resize: vertical;
  min-height: 130px;
}

/* Honeypot field: hidden from real visitors, present for basic bot filtering.
   Holds no personal data and nothing here is ever stored server-side. */
.form-honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.privacy-note {
  background: rgba(124, 148, 115, 0.1);
  border-radius: var(--radius-sm);
  padding: 1rem 1.2rem;
  font-size: 0.92rem;
  color: var(--color-text-muted);
  margin: 1rem 0 0;
}

.privacy-note-short {
  display: none;
}

@media (max-width: 840px) {
  /* Fine-print version: shorter copy, small type, moved below the
     submit button instead of taking up prominent space above the
     form's first field. */
  .privacy-note-full {
    display: none;
  }

  .privacy-note-short {
    display: block;
    background: none;
    padding: 0;
    margin: 0.75rem 0 0;
    font-size: 0.72rem;
    line-height: 1.4;
  }
}

.form-status {
  margin-top: 1rem;
  padding: 0.9em 1.1em;
  border-radius: var(--radius-sm);
  font-size: 0.95rem;
  display: none;
}

.form-status.is-visible {
  display: block;
}

.form-status.is-success {
  background: rgba(124, 148, 115, 0.18);
  color: var(--color-primary-dark);
}

.form-status.is-error {
  background: rgba(201, 139, 107, 0.18);
  color: var(--color-accent-dark);
}

/* --- Footer --- */
.site-footer {
  border-top: 1px solid var(--color-border);
  padding: 2.5rem 0;
  background: var(--color-bg-alt);
  color: var(--color-text-muted);
  font-size: 0.9rem;
}

.footer-inner {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: space-between;
  align-items: center;
}

.footer-links {
  list-style: none;
  display: flex;
  gap: 1.25rem;
  margin: 0;
  padding: 0;
}

.footer-links a {
  color: var(--color-text-muted);
}

.placeholder-flag {
  font-size: 0.78rem;
  color: var(--color-accent-dark);
  background: rgba(201, 139, 107, 0.12);
  border-radius: var(--radius-sm);
  padding: 0.2em 0.6em;
  display: inline-block;
  margin-left: 0.4em;
}
