/* ---- common/styles/brand.css ---- */
/* ==========================================================================
   brand.css — The CB Creative design tokens
   Single source of truth for brand styling: color, type, spacing scale.
   Every page/section/common stylesheet should reference these variables
   rather than hardcoding values.
   ========================================================================== */

:root {

  /* ---- Color ---- */
  --color-paper: #F6F3EC;   /* cream — primary light background, and text-on-dark */
  --color-ink: #1C1F16;     /* near-black — primary dark background, and text-on-light */
  --color-forest: #2F3A1F;  /* brand dark green — accents on light backgrounds */
  --color-sage: #93A876;    /* brand light green — accents on dark backgrounds */

  --color-muted-on-light: #6E6B5D; /* body copy over paper */
  --color-muted-on-dark: #9A9789;  /* body copy over ink/forest */
  --color-border: #D9D5C9;         /* hairlines, input underlines, dashed borders */
  --color-cta-bg: #1E2614;         /* slightly deeper green used only behind the CTA band */
  --color-nav-hairline: #D8D5CA;   /* thin bottom border on the light/frosted nav */
  --color-forest-deep: #12160C;    /* darkest green — service card 1 & 2 photo overlays */
  --color-error: #8A2F22;          /* form error text; 7.5:1 on paper */

  /* R,G,B triplets (no rgb() wrapper) for --color-paper/--color-ink, so any
     stylesheet can drop them into rgba(var(--color-paper-rgb), 0.2) for a
     translucent tint instead of re-deriving/hardcoding the same triplet. */
  --color-paper-rgb: 246, 243, 236;
  --color-ink-rgb: 28, 31, 22;
  --color-sage-rgb: 147, 168, 118;
  --color-forest-rgb: 47, 58, 31;

  /* ---- Motion ---- */
  /* Signature "ease out, settle" curve used for every scroll-reveal and
     hover-pop transition on the site — one place to retune the feel. */
  --ease-reveal: cubic-bezier(0.16, 1, 0.3, 1);

  /* ---- Font families ---- */
  --font-serif: 'Playfair Display', Georgia, serif;       /* headlines */
  --font-serif-accent: 'Cormorant Garamond', serif;       /* italic accents, quote marks */
  --font-sans: 'Geist', system-ui, -apple-system, sans-serif; /* body copy, UI, labels */

  /* ---- Font sizes — desktop scale ---- */
  --fs-hero-headline: 3.5rem;       /* 64px */
  --fs-section-headline: 3.25rem; /* 52px */
  --fs-card-title: 3rem;          /* 48px */
  --fs-quote: 3rem;               /* 48px */
  --fs-body-lg: 1.0625rem;        /* 17px */
  --fs-body: 1rem;                /* 16px */
  --fs-body-sm: 0.9375rem;        /* 15px */
  --fs-label: 0.6875rem;          /* 11px — eyebrows, buttons, form labels */
  --fs-caption: 0.625rem;         /* 10px */

  /* ---- Font sizes — mobile scale ---- */
  --fs-hero-headline-mobile: 2.5rem; /* 40px */
  --fs-section-headline-mobile: 2.375rem; /* 38px */
  --fs-card-title-mobile: 2rem;   /* 32px */

  /* ---- Letter spacing ---- */
  --ls-label: 0.14em;   /* eyebrows */
  --ls-button: 0.12em;  /* buttons */
  --ls-caption: 0.1em;

  /* ---- Line height ---- */
  --lh-tight: 1.08;
  --lh-headline: 1.15;
  --lh-body: 1.6;
  --lh-body-loose: 1.75;

  /* ---- Spacing scale ---- */
  --space-xs: 0.5rem;   /* 8px */
  --space-sm: 1rem;     /* 16px */
  --space-md: 1.5rem;   /* 24px */
  --space-lg: 2rem;     /* 32px */
  --space-xl: 3rem;     /* 48px */
  --space-2xl: 4rem;    /* 64px */
  --space-3xl: 6rem;    /* 96px */

  /* ---- Layout ---- */
  --page-max-width: 1440px;
  --gutter-desktop: 96px;
  --gutter-mobile: 24px;
  --nav-height: 92px;
  --nav-height-mobile: 76px;

  /* ---- Radius (used only for the portfolio card treatment) ---- */
  --radius-card: 20px;
  --radius-card-mobile: 18px;
}

/* ---- common/styles/base.css ---- */
/* ==========================================================================
   base.css — shared component primitives
   Buttons, eyebrows, dividers, numbered markers used across nav, footer,
   and every homepage section. Reference brand.css tokens only.
   ========================================================================== */

/* Border-box everywhere. Without this, `width`/`height` describe only the
   content box, so any element with both an explicit size and padding or a
   border renders wider than asked for and overflows its container — the
   cause of several bugs on this site (a full-width button spilling 74px
   past the page gutter, panels with padding overflowing their column).
   Setting it once here means those cases just work, rather than needing a
   box-sizing declaration remembered at each call site. */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  overflow-x: hidden;
  max-width: 100%;
}

body {
  /* Browsers apply an 8px default margin on body — this was never reset,
     so every full-bleed element on the site has had a ~8px cream sliver on
     all four edges the whole time (most visible against a dark full-bleed
     photo, like the contact page, but this was very likely also the root
     cause of the long-standing "white border on the right side" issue
     noticed earlier on the homepage). */
  margin: 0;
  font-family: var(--font-sans);
  font-weight: 300;
  color: var(--color-ink);
  background: var(--color-paper);
  line-height: var(--lh-body);
  overflow-x: hidden;
  max-width: 100%;
}

/* Nav is fixed/out-of-flow (see navigation.css), so every page's content
   needs top padding to clear it — except the home page, which wants its
   hero photo to bleed up behind the translucent nav (see pages/home/home.css). */
main#main-content {
  padding-top: var(--nav-height);
}

@media (max-width: 768px) {
  main#main-content {
    padding-top: var(--nav-height-mobile);
  }
}

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

a {
  color: inherit;
}

/* Visible keyboard-focus ring, sitewide — the browser default outline still
   works without this, but a branded ring in sage is easier to spot against
   both the dark and light sections. :focus-visible (not :focus) so it only
   shows for keyboard/AT navigation, not on every mouse click. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible {
  outline: 2px solid var(--color-sage);
  outline-offset: 3px;
}

/* Sage only passes contrast (3:1 non-text minimum) against the dark ink
   sections it was designed for — against the site's light/paper sections
   it drops to ~2.3:1. Swap to forest (10.8:1 on paper) anywhere the focus
   ring lands on a light background instead. */
.services :focus-visible,
.contact :focus-visible,
.nav--light :focus-visible {
  outline-color: var(--color-forest);
}

h1, h2, h3 {
  font-family: var(--font-serif);
  font-weight: 400;
  line-height: var(--lh-headline);
}

h1, h2 {
  letter-spacing: 5px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 1000;
  background: var(--color-ink);
  color: var(--color-paper);
  padding: 12px 20px;
  font-size: var(--fs-label);
  letter-spacing: var(--ls-caption);
  text-transform: uppercase;
}

.skip-link:focus {
  left: 12px;
  top: 12px;
}

/* ---- Eyebrow (small caps label + accent line) ---- */

.eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  font-family: var(--font-sans) !important;
  font-weight: 500 !important;
  font-size: var(--fs-label) !important;
  letter-spacing: var(--ls-label) !important;
  text-transform: uppercase;
  margin-bottom: var(--space-md);
}

.eyebrow__line {
  display: inline-block;
  width: 18px;
  height: 1px;
  background: currentColor;
  flex-shrink: 0;
}

/* Eyebrow color context: dark sections use sage, light sections use forest */
.hero .eyebrow,
.portfolio-teaser .eyebrow,
.why-not-ai .eyebrow,
.about .eyebrow,
.contact__info .eyebrow,
.thank-you .eyebrow {
  color: var(--color-sage);
}

.services .eyebrow {
  color: var(--color-forest);
}

/* ---- Divider ---- */

.divider {
  border: none;
  height: 1px;
  width: 48px;
  background: var(--color-border);
  margin: var(--space-lg) 0;
}

.why-not-ai .divider {
  background: rgba(var(--color-paper-rgb), 0.2);
  width: 100%;
  max-width: none;
  margin: var(--space-sm) 0;
}

.about .divider,
.contact__info .divider,
.thank-you .divider {
  background: rgba(var(--color-paper-rgb), 0.2);
}

/* ---- Photo panel with Ken Burns zoom ----
   A full-bleed background photo that slowly zooms (see @keyframes below),
   clipped by a static frame (.photo-bg) so the zoom and the dark vignette
   overlay (::after) never move — only the photo itself does. Originated on
   the homepage quote section; reused wherever a panel wants a "living"
   photo behind its text instead of a flat image. Parent needs
   position: relative (and usually overflow: hidden) for this to fill it. */
.photo-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background-color: var(--color-ink);
}

.photo-bg__photo {
  position: absolute;
  inset: 0;
  /* Gradient fallback shows if the photo is ever missing; the inline
     background-image set per-usage paints over it. */
  background-image: linear-gradient(160deg, var(--color-forest), var(--color-ink));
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  animation: photo-bg-zoom 10s ease-in-out infinite alternate;
  will-change: transform;
}

@keyframes photo-bg-zoom {
  from {
    transform: scale(1);
  }
  to {
    transform: scale(1.18);
  }
}

@media (prefers-reduced-motion: reduce) {
  .photo-bg__photo {
    animation: none;
  }
}

.photo-bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--color-ink);
  opacity: 0.75;
  box-shadow: inset 0px -6px 20px 0px rgba(0, 0, 0, 0.25), inset 0px 8px 24px 0px rgba(0, 0, 0, 0.35);
}

/* ---- Numbered markers (about list, service cards) ---- */

.num {
  font-family: var(--font-serif-accent);
  font-style: italic;
  color: var(--color-sage);
  font-size: 0.95rem;
  flex-shrink: 0;
}

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

.btn {
  display: inline-block;
  position: relative;
  z-index: 0;
  overflow: hidden;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-label);
  letter-spacing: var(--ls-button);
  text-transform: uppercase;
  text-decoration: none;
  text-align: center;
  padding: 18px 36px;
  border: 1px solid transparent;
  transition: color 0.35s ease, border-color 0.2s ease;
  cursor: pointer;
}

.btn::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--color-ink);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.35s ease;
}

.btn:hover::before {
  transform: scaleX(1);
}

.btn--primary,
.btn--outline {
  background: var(--color-paper);
  color: var(--color-ink);
  border-color: var(--color-ink);
}

.btn--primary:hover,
.btn--outline:hover {
  color: var(--color-paper);
  border-color: var(--color-sage);
}

/* Transparent with a hairline border and cream text — for sitting over a
   dark photo, where the solid cream fill of .btn--primary would be too
   heavy. Shares the base .btn hover sweep, so the ink fill still wipes in
   from the left on hover; the border brightens to full paper to match. */
.btn--ghost {
  background: transparent;
  color: var(--color-paper);
  border-color: rgba(var(--color-paper-rgb), 0.5);
}

.btn--ghost:hover {
  color: var(--color-paper);
  border-color: var(--color-paper);
}

.btn--link {
  padding: 18px 0;
  color: var(--color-paper);
  border: none;
  position: relative;
}

.btn--link::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 12px;
  height: 1px;
  background: currentColor;
  opacity: 0.4;
  transition: opacity 0.2s ease;
}

.btn--link:hover::after {
  opacity: 1;
}

.btn--full {
  display: block;
  width: 100%;
}

.text-link {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--fs-label);
  letter-spacing: var(--ls-button);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--color-sage);
  border-bottom: 1px solid rgba(var(--color-sage-rgb), 0.4);
  padding-bottom: 4px;
  transition: border-color 0.2s ease;
}

.text-link:hover {
  border-color: var(--color-sage);
}

/* ---- Section transitions ----
   A soft blurred band straddling the seam between stacked homepage
   sections, so hard background-color/photo edges (e.g. cream -> ink)
   blend rather than cut sharply. Purely decorative — no pointer events. */

.about,
.services,
.portfolio-teaser,
.quote,
.why-not-ai,
.cta,
.gallery {
  position: relative;
}

.about::before,
.services::before,
.portfolio-teaser::before,
.quote::before,
.gallery::before {
  content: '';
  position: absolute;
  top: -60px;
  left: 0;
  right: 0;
  height: 120px;
  backdrop-filter: blur(24px);
  -webkit-backdrop-filter: blur(24px);
  -webkit-mask-image: linear-gradient(to bottom, transparent, black 40%, black 60%, transparent);
  mask-image: linear-gradient(to bottom, transparent, black 40%, black 60%, transparent);
  pointer-events: none;
  z-index: 5;
}


/* ---- common/navigation/navigation.css ---- */
/* ---- Navigation (common component) ---- */

.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: var(--nav-height);
  box-sizing: border-box;
  padding: 0 var(--gutter-desktop);
  border-bottom: 1px solid rgba(var(--color-ink-rgb), 0.1);
  box-shadow: 0px 8px 24px 0px rgba(0, 0, 0, 0.22);
}

/* Translucent overlay layer, separated so opacity/blur don't affect the
   logo/links sitting on top of it. Default = dark overlay for sitting on
   top of the hero photo. .nav--light = frosted cream, for sections with
   no photo behind the nav (portfolio, contact). */
.nav::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to bottom, rgba(var(--color-ink-rgb), 0.88), rgba(var(--color-ink-rgb), 0));
  backdrop-filter: blur(28px);
  -webkit-backdrop-filter: blur(28px);
}

.nav--light::before {
  background: var(--color-paper);
  opacity: 0.82;
}

.nav__logo {
  display: block;
  line-height: 0;
}

.nav__logo img {
  height: 64px;
  width: auto;
}

.nav__links ul {
  display: flex;
  gap: 40px;
  list-style: none;
}

.nav__links a {
  position: relative;
  display: inline-block;
  padding-bottom: 6px;
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--color-paper);
  transition: color 0.2s ease;
}

.nav--light .nav__links a {
  color: var(--color-ink);
}

.nav__links a:hover {
  color: var(--color-sage);
}

.nav__links a::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: var(--color-sage);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.25s ease;
}

.nav__links a[aria-current='page'] {
  color: var(--color-sage);
}

.nav--light .nav__links a[aria-current='page'] {
  color: var(--color-forest);
}

.nav__links a[aria-current='page']::after {
  background: var(--color-sage);
  transform: scaleX(1);
}

.nav--light .nav__links a[aria-current='page']::after {
  background: var(--color-forest);
}

.nav__hamburger {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  padding: 4px;
  cursor: pointer;
}

.nav__hamburger span {
  display: block;
  width: 22px;
  height: 1px;
  background: var(--color-paper);
  transition: background 0.2s ease, transform 0.3s ease, opacity 0.2s ease;
}

.nav--light .nav__hamburger span {
  background: var(--color-ink);
}

/* Morph into an X when the mobile menu is open. */
.nav__hamburger[aria-expanded='true'] span:nth-child(1) {
  transform: translateY(6px) rotate(45deg);
}

.nav__hamburger[aria-expanded='true'] span:nth-child(2) {
  opacity: 0;
}

.nav__hamburger[aria-expanded='true'] span:nth-child(3) {
  transform: translateY(-6px) rotate(-45deg);
}

.mobile-menu {
  display: none;
  flex-direction: column;
  gap: 40px;
  position: fixed;
  top: var(--nav-height-mobile);
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 99;
  background: var(--color-ink);
  padding: var(--space-lg) var(--gutter-mobile);
}

.mobile-menu.open {
  display: flex;
}

/* The nav's drop shadow is meant to lift it off the hero photo behind it —
   with the mobile menu open, that same shadow bleeds onto the menu's solid
   background and reads as a gap. Suppress it while the menu is open. */
.nav:has(~ .mobile-menu.open) {
  box-shadow: none;
}

.mobile-menu a {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--fs-body-sm);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--color-paper);
}

@media (max-width: 768px) {
  .nav {
    height: var(--nav-height-mobile);
    padding: 0 var(--gutter-mobile);
  }

  /* The mobile nav bar is 76px tall, so the logo can comfortably take ~54px
     and still leave breathing room above and below. At 42px it read as
     undersized next to the hamburger and lost the wordmark's legibility. */
  .nav__logo img {
    height: 54px;
  }

  .nav__links {
    display: none;
  }

  .nav__hamburger {
    display: flex;
  }
}

/* ---- common/footer/footer.css ---- */
/* ---- Footer (common component) ---- */

.site-footer {
  position: relative;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: var(--color-ink);
  padding: var(--space-lg) var(--gutter-desktop);
  border-top: 1px solid rgba(var(--color-paper-rgb), 0.1);
}

.site-footer__logo {
  display: block;
  line-height: 0;
}

.site-footer__logo img {
  height: 44px;
  width: auto;
}

.site-footer__copy {
  font-family: var(--font-sans);
  font-size: var(--fs-label);
  color: var(--color-sage);
}

@media (max-width: 768px) {
  .site-footer {
    padding: var(--space-lg) var(--gutter-mobile);
    flex-direction: column;
    gap: var(--space-md);
    text-align: center;
  }

  /* Soft blend from the CTA section above into the footer, replacing the
     hard border-top with a gentle blurred seam on mobile. */
  .site-footer {
    border-top: none;
  }

  .site-footer::before {
    content: '';
    position: absolute;
    top: -60px;
    left: 0;
    right: 0;
    height: 120px;
    backdrop-filter: blur(24px);
    -webkit-backdrop-filter: blur(24px);
    -webkit-mask-image: linear-gradient(to bottom, transparent, black 40%, black 60%, transparent);
    mask-image: linear-gradient(to bottom, transparent, black 40%, black 60%, transparent);
    pointer-events: none;
    z-index: 5;
  }
}

/* ---- common/reveal/reveal.css ---- */
/* ---- Scroll reveal (common) ----
   Elements marked [data-reveal] start faded out, slightly lowered, and
   slightly scaled down, then ease up to full size as they scroll into
   view (see reveal.js) — reads as the content gently zooming into place
   rather than just fading. Purely decorative — content is never hidden
   if JS doesn't run. */

[data-reveal] {
  opacity: 0;
  transform: translateY(28px) scale(0.94);
  transition: opacity 1.4s var(--ease-reveal),
    transform 1.4s var(--ease-reveal);
  will-change: opacity, transform;
}

[data-reveal].is-visible {
  opacity: 1;
  transform: translateY(0) scale(1);
}

/* Stagger multi-item groups so they cascade in rather than popping
   together all at once. */
.service-card:nth-child(2) {
  transition-delay: 0.12s;
}

.service-card:nth-child(3) {
  transition-delay: 0.24s;
}

.portfolio-card:nth-child(2) {
  transition-delay: 0.08s;
}

.portfolio-card:nth-child(3) {
  transition-delay: 0.16s;
}

.portfolio-card:nth-child(4) {
  transition-delay: 0.24s;
}

.faq-row:nth-child(3) {
  transition-delay: 0.08s;
}

.faq-row:nth-child(4) {
  transition-delay: 0.16s;
}

.field:nth-child(2) {
  transition-delay: 0.05s;
}

.field:nth-child(3) {
  transition-delay: 0.1s;
}

.field:nth-child(4) {
  transition-delay: 0.15s;
}

.field:nth-child(5) {
  transition-delay: 0.2s;
}

.field:nth-child(6) {
  transition-delay: 0.25s;
}

/* (7) is the file upload — previously missed, so it arrived before the
   fields above it. */
.field:nth-child(7) {
  transition-delay: 0.3s;
}

/* The submit button sits below the fold of the form's own scroll container,
   so unlike the fields above it, it's never part of the arrival cascade — it
   reveals only once the user scrolls down to reach it. Making them then sit
   through the full 1.4s fade reads as the button being slow to load. */
.form > .btn--full {
  transition-delay: 0.06s;
  transition-duration: 0.5s;
}

/* No motion for anyone who's asked for it, and no invisible content if JS
   never runs (script removes .no-js from <html> as soon as it executes). */
@media (prefers-reduced-motion: reduce) {
  [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

.no-js [data-reveal] {
  opacity: 1;
  transform: none;
}

/* ---- pages/contact/contact.css ---- */
/* ---- Contact page ---- */

/* Split-screen layout, pinned to exactly one viewport (minus the nav).
   Rather than letting the whole page scroll and faking a "pinned" photo
   panel with position: sticky (which fights with align-items: stretch —
   the outer flex item ends up taller than the sticky box actually
   sticking within it, leaving bare cream space below the panel), the
   section itself is capped at the viewport height and never grows. The
   info panel is then genuinely static — no scrolling, no sticky mechanics,
   nothing to get out of sync. Only the form column scrolls, internally,
   within its own box. */
.contact {
  /* Single source of truth for the split — the info panel takes the
     remainder, and the scroll cue lines up with the form column's left
     edge without the three drifting out of sync. */
  --form-col: 55%;
  position: relative;
  display: flex;
  flex-wrap: nowrap;
  height: calc(100vh - var(--nav-height));
  background: var(--color-paper);
  overflow: hidden;
}

/* Fixed to the full height of .contact (flex stretch), clipped so the
   Ken Burns photo's zoom never bleeds past this panel's edges. Nothing
   here ever moves. */
.contact__info {
  position: relative;
  flex: 1 1 calc(100% - var(--form-col));
  min-width: 0;
  overflow: hidden;
}

.contact__info-content {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--space-3xl) var(--gutter-desktop);
}

.contact__info-content h1 {
  font-size: var(--fs-hero-headline);
  color: var(--color-paper);
  margin: var(--space-md) 0 var(--space-lg);
}

/* Scoped to its own class, not a bare `> p` combinator — this panel's
   .eyebrow is also a <p>, and a tag-based selector would leak these
   overrides (margin especially) onto it too. */
.contact__info-body {
  font-size: var(--fs-body-sm);
  color: var(--color-muted-on-dark);
  line-height: var(--lh-body-loose);
  max-width: 32em;
  margin: 0 0 var(--space-sm);
}

/* Tighter than the default .divider margin (var(--space-lg) top+bottom) —
   in this panel it's a short breath between the body copy and the detail
   list, not a full section break. */
.contact__info-content .divider {
  margin: var(--space-sm) 0;
}

.contact__details {
  list-style: none;
  margin: var(--space-xs) 0 0;
  padding: 0;
}

.contact__details li {
  font-size: var(--fs-body-sm);
  color: var(--color-muted-on-dark);
  line-height: var(--lh-body-loose);
}

/* The only part of this section that scrolls — its own internal
   scrollbar, independent of the info panel and the page's outer scroll. */
.contact__form-panel {
  flex: 1 1 var(--form-col);
  min-width: 0;
  height: 100%;
  overflow-y: auto;
  padding: var(--space-3xl) var(--gutter-desktop);
}

/* ---- Scroll cue ----
   The form scrolls inside its own column, which is easy to miss — the page
   itself doesn't move, so there's no scrollbar travel to hint at it. A soft
   shadow plus a small chevron over the bottom of the column signals there's
   more below. It lives on .contact rather than inside the scrolling panel so
   it stays pinned to the column's bottom edge instead of scrolling away. */
.contact__scroll-cue {
  position: absolute;
  bottom: 0;
  left: calc(100% - var(--form-col));
  right: 0;
  height: 100px;
  z-index: 2;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: var(--space-sm);
  /* Ink at partial alpha, so it darkens the cream without tinting it. */
  background: linear-gradient(
    to top,
    rgba(var(--color-ink-rgb), 0.5) 0%,
    rgba(var(--color-ink-rgb), 0.28) 35%,
    rgba(var(--color-ink-rgb), 0.1) 70%,
    rgba(var(--color-ink-rgb), 0) 100%
  );
  /* Never intercepts clicks on the fields it sits over. */
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.35s ease;
}

/* Toggled from contact.js — hidden once the form is scrolled to the bottom,
   and when the form is short enough not to scroll at all. */
.contact__scroll-cue.is-hidden {
  opacity: 0;
}

.contact__scroll-cue svg {
  width: 22px;
  height: 22px;
  /* Near-solid ink — at the darkest part of the gradient a translucent
     stroke muddies into the background rather than reading as a mark. */
  stroke: rgba(var(--color-ink-rgb), 0.85);
  animation: scroll-cue-nudge 2.4s ease-in-out infinite;
}

@keyframes scroll-cue-nudge {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(4px); }
}

@media (prefers-reduced-motion: reduce) {
  .contact__scroll-cue svg {
    animation: none;
  }
}

.form {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

/* Visually hidden rather than display: none — some bots specifically skip
   display:none/visibility:hidden fields since real forms use that pattern
   for honeypots too. Off-screen positioning plus aria-hidden + tabindex="-1"
   on the input (see contact.html) keeps it invisible and unreachable for
   real users and assistive tech either way. */
.field--hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.field label {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--color-ink);
}

.field__optional {
  text-transform: none;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--color-muted-on-light);
}

.field input,
.field textarea {
  font: inherit;
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  color: var(--color-ink);
  border: none;
  border-bottom: 1px solid var(--color-border);
  background: transparent;
  padding: var(--space-sm) 0;
  transition: border-color 0.2s ease;
}

.field input::placeholder,
.field textarea::placeholder {
  color: var(--color-muted-on-light);
  opacity: 0.7;
}

.field input:hover,
.field textarea:hover {
  border-color: var(--color-ink);
}

.field input:focus-visible,
.field textarea:focus-visible {
  outline: none;
  border-color: var(--color-forest);
  /* Stacks a second 1px line on the existing border so the focus state
     reads as a bolder ~2px indicator without shifting layout. */
  box-shadow: 0 1px 0 0 var(--color-forest);
}

.field textarea {
  resize: vertical;
  min-height: 120px;
  line-height: var(--lh-body);
}

/* File upload — a dashed drop zone; hover/drag states use the same sage
   accent as everything else on the site so it reads as one design system,
   not a bolted-on widget. */
.upload-zone {
  position: relative;
  text-align: center;
  padding: var(--space-lg);
  border: 1px dashed var(--color-border);
  transition: border-color 0.2s ease, background-color 0.2s ease;
}

.upload-zone:hover {
  border-color: var(--color-forest);
}

.upload-zone.is-dragover {
  border-color: var(--color-forest);
  background: rgba(var(--color-forest-rgb), 0.06);
}

.upload-zone input[type='file'] {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}

.upload-zone__label {
  font-size: var(--fs-body-sm);
  color: var(--color-ink);
  margin-bottom: var(--space-xs);
}

.upload-zone__sub {
  font-size: var(--fs-caption);
  color: var(--color-muted-on-light);
}

.upload-zone__files {
  margin-top: var(--space-sm);
  font-size: var(--fs-caption);
  color: var(--color-forest);
  word-break: break-word;
}

.form .btn--full {
  margin-top: var(--space-sm);
  align-self: flex-start;
  min-width: 220px;
}

.form .btn--full:disabled {
  cursor: wait;
  opacity: 0.6;
}

/* Never display: none, even while empty — a live region that's hidden at
   the moment its text arrives often isn't announced at all, which would
   defeat the point of role="status". An empty <p> generates no line box,
   so it's already 0px tall; the only thing it costs is the parent .form's
   flex gap, and the negative margin below cancels exactly that. */
.form__status {
  margin: 0;
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body-loose);
}

.form__status:empty {
  margin-top: calc(-1 * var(--space-lg));
}

.form__status--success {
  color: var(--color-forest);
}

.form__status--error {
  /* Not a brand token — this is the one place on the site that needs to
     read as an error, and it's tuned dark enough to clear 4.5:1 on paper. */
  color: var(--color-error);
}

@media (max-width: 768px) {
  /* The fixed-viewport/internal-scroll split is a desktop enhancement —
     on a stacked mobile layout it should just be one normal page that
     scrolls in the usual way, photo panel included. */
  .contact {
    flex-direction: column;
    height: auto;
    overflow: visible;
  }

  .contact__info-content h1 {
    font-size: var(--fs-hero-headline-mobile);
  }

  .contact__info {
    flex-basis: auto;
    height: auto;
    min-height: 420px;
  }

  .contact__info-content {
    height: auto;
    padding: var(--space-xl) var(--gutter-mobile);
  }

  .contact__form-panel {
    flex-basis: auto;
    height: auto;
    overflow-y: visible;
    padding: var(--space-xl) var(--gutter-mobile);
  }

  .form .btn--full {
    align-self: stretch;
  }

  /* Mobile has no internal scroll — the whole page moves — but touch
     scrollbars are transient or absent, so the shadow still earns its keep.
     Pinned to the viewport bottom and full-width, since the layout is a
     single stacked column here. */
  .contact__scroll-cue {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    height: 80px;
  }

  /* On mobile the cue only ever shows over the dark forest panel — it hides
     as soon as the user scrolls toward the cream form — so the chevron is
     cream here rather than ink, which would vanish against the photo. */
  .contact__scroll-cue svg {
    stroke: rgba(var(--color-paper-rgb), 0.8);
  }
}
