/* ============================================================
   EARTHBORN NORDIC SAUNAS — stylesheet
   Organized by: Reset > Variables > Base > Header/Nav > Hero >
   Intro > Craft rows > Testimonial > CTA > Footer > Utilities
   ============================================================ */

/* ---------- Reset ---------- */
*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  /* Stops the page bouncing/rubber-banding past its own top, bottom, or
     sides, and stops any stray wider-than-viewport element from letting
     the whole page pan left/right. Only set here, not on body too —
     setting it on both turns body into its OWN separate scroll box
     nested inside html (since html having its own overflow stops body's
     from propagating to the viewport the normal way), which breaks
     ordinary page scrolling instead of just blocking the bounce. */
  overflow-x: hidden;
  overscroll-behavior: none;
}

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

a {
  color: inherit;
  text-decoration: none;
}

ul {
  list-style: none;
}

/* ---------- Variables ---------- */
:root {
  /* Palette — warm, muted, drawn from the photography rather than bright hardcoded color */
  /* Every "black" on the site — dark section backgrounds, buttons, and
     body/heading text — is this exact charcoal grey, not a near-pure
     black, so they all read as one consistent ink/backdrop color rather
     than several slightly different near-blacks */
  --color-charcoal: #2e2c2a;
  --color-charcoal-soft: #37342f;  /* slightly lighter panel/hover tone */
  --color-cedar: #241a12;          /* deep cedar-brown, for testimonial */
  --color-cream: #f6f1e6;          /* warm off-white, never pure white */
  --color-cream-soft: #efe7d6;

  --color-text-dark: #2e2c2a;
  --color-text-light: #f6f1e6;
  --color-muted-on-dark: rgba(246, 241, 230, 0.62);
  --color-muted-on-light: rgba(27, 23, 18, 0.6);

  /* Type */
  --font-serif: 'Fraunces', Georgia, serif;
  /* Site-wide default — same Helvetica Neue-alike stack used on the
     Rental page's "How it works" heading, just without that heading's
     own specific weight/letter-spacing/scale, which stay scoped there */
  --font-sans: 'Helvetica Neue', Helvetica, Arial, -apple-system, BlinkMacSystemFont, sans-serif;
  /* Old-typewriter feel for the footer credit line, but Courier Prime
     instead of Special Elite — same genre, deliberately cleaner/more
     legible (Special Elite bakes in an inked, distressed texture that
     reads as harsh at small sizes; Courier Prime is a modern redraw of
     the classic typewriter face made for actual readability). Loaded via
     the Google Fonts <link> in each page's <head>, alongside Fraunces */
  --font-typewriter: 'Courier Prime', 'Courier New', Courier, monospace;

  /* Spacing */
  --section-padding: clamp(5rem, 12vw, 10rem);
  --edge-padding: clamp(1.5rem, 6vw, 5rem);
  /* Split evenly above/below the Section 2 intro text, so the hero-to-text
     gap and the text-to-craft-grid gap end up identical */
  --story-gap: clamp(6rem, 11vw, 10.5rem);

  --header-height: 88px;
}

/* Site-wide weight/tracking, matching the "How it works" heading on the
   Rental page — !important because dozens of rules below set their own
   font-weight (and a few set letter-spacing) for their own reasons, and
   this is meant to unify ALL of them rather than just the ones that
   happen not to already have an opinion. Font-size is untouched — every
   element keeps its own. Deliberately doesn't touch `transform`: unlike
   weight/spacing, transform is doing real functional work elsewhere
   (carousel positioning, both scroll-jacks, header show/hide, hover
   states), and !important would override even the inline transforms
   script.js sets for those. */
*,
*::before,
*::after {
  font-weight: 550 !important;
  letter-spacing: -0.03em !important;
}

/* Carved back out to a plain, un-bolded weight — everywhere the site-wide
   550 above reads as too heavy. More specific than the universal rule
   above, so this wins despite both using !important. Letter-spacing is
   left alone; only weight is being dialed back here. Covers: the header,
   the Rental page's "How it works" body copy (not its headings) and its
   hero's tagline, every "Earthborn" logo/wordmark, the footer, the
   homepage hero's subheadline and "Start Your Sauna" link, the
   testimonial quotes, the homepage closing CTA's subtext, Our Story's
   labels, hero subheadline, story copy, and profile bios, all of Our
   Work, and Contact's intro paragraph, form section heading, field
   labels, and interest dropdown. */
.site-header,
.site-header *,
.how-it-works__intro,
.rental-hero-v2__tagline,
.rental-hero-v2__tagline *,
.how-it-works__step-copy p,
.how-it-works__pricing-intro,
.pricing-tier__rate,
.pricing-tier__note,
.pricing-tier__area,
.wordmark,
.site-footer__wordmark,
.hwork__title,
.site-footer,
.site-footer *,
.hero .hero__subheadline,
.hero .link-arrow,
.testimonial__quote,
.cta__subtext,
.team .label,
.story-hero .hero__subheadline,
.story-copy,
.profile__bio,
.hwork,
.hwork *,
.consult__intro,
.consult__intro *,
.contact-form__heading,
.form-field label,
.form-field select,
.form-field select option {
  font-weight: 400 !important;
}

body {
  font-family: var(--font-sans);
  font-weight: 300;
  color: var(--color-text-dark);
  background: var(--color-cream);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* ---------- Shared type utilities ---------- */
.label {
  display: inline-block;
  font-family: var(--font-sans);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  /* Quiet by design — the tracking/weight already read as a label
     without needing an accent color to do the work */
  color: var(--color-muted-on-light);
  margin-bottom: 1.25rem;
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-block;
  padding: 1rem 2.5rem;
  font-family: var(--font-sans);
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.15em;
  text-align: center;
  border: 1px solid currentColor;
  transition: background-color 0.3s ease, color 0.3s ease, transform 0.3s ease;
}

.btn--outline {
  color: #fff;
  background: transparent;
}

.btn--outline:hover {
  background: #fff;
  color: var(--color-text-dark);
}

.btn--filled {
  color: var(--color-text-dark);
  background: #fff;
  border-color: #fff;
}

.btn--filled:hover {
  background: var(--color-cream-soft);
  transform: translateY(-2px);
}

/* The other two variants assume a dark background (white border/fill) —
   this is the inverse, for a solid button sitting on a light section */
.btn--dark {
  color: #fff;
  background: var(--color-charcoal);
  border-color: var(--color-charcoal);
}

.btn--dark:hover {
  background: var(--color-charcoal-soft);
  transform: translateY(-2px);
}

.btn--large {
  padding: 1.15rem 3rem;
  font-size: 0.9rem;
}

/* ---------- Fade-in-on-scroll ---------- */
.fade-in {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.9s ease, transform 0.9s ease;
}

.fade-in.is-visible {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .fade-in {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ---------- Hero entrance (runs once on load) ----------
   For text/images already visible above the fold on page load — .fade-in
   above only fires once its IntersectionObserver sees an element scroll
   INTO view, which never happens for content that's visible immediately,
   so this is a plain CSS animation instead, playing automatically as soon
   as the page renders.

   No fade — this is a mask reveal, not a fade. .hero-rise-mask is the
   clipping window (sized to exactly fit its content, via normal block
   flow — transform never affects that sizing) and .hero-rise-inner is
   the actual text/image, which starts shifted down by its own full
   height (100%), i.e. entirely below the mask's visible window. As the
   animation runs, it rises back to translateY(0): at first, only a thin
   sliver right at the mask's own bottom edge is visible (the very top of
   the content, on the way up from being fully hidden), and progressively
   more comes into view underneath it until the whole thing has risen
   into its resting position — never a partial, see-through/faded state
   the way opacity would give. The delay variants stagger a group of
   these (headline, then subhead/button, then photo) instead of popping
   in all at once. */
.hero-rise-mask {
  overflow: hidden;
}

@keyframes hero-rise {
  from {
    transform: translateY(100%);
  }
  to {
    transform: translateY(0);
  }
}

.hero-rise-inner {
  display: block;
  animation: hero-rise 1.1s ease-out both;
}

.hero-rise-inner--delay-1 {
  animation-delay: 0.18s;
}

.hero-rise-inner--delay-2 {
  animation-delay: 0.36s;
}

@media (prefers-reduced-motion: reduce) {
  .hero-rise-inner,
  .hero-rise-inner--delay-1,
  .hero-rise-inner--delay-2 {
    animation: none;
  }
}

/* ============================================================
   HEADER / NAV
   ------------------------------------------------------------
   No solid background bar — the header stays fully transparent (just the
   logo, wordmark text and nav links sitting directly over whatever's
   behind them). (mix-blend-mode: difference was tried here first, but
   browsers commonly promote position: fixed elements to their own
   compositor layer, which breaks blend-mode's view of the actual content
   scrolling underneath — it never engaged in testing.) Instead, script.js
   checks which section is currently behind the header and toggles
   .site-header--on-light, which these rules use to flip the logo, nav
   links and menu icon from white to black. It separately toggles
   .site-header--collapsed once the page has scrolled down past the
   header's own height, which slides the whole bar — logo included — up
   and out of view; scrolling back up at any point brings it straight
   back. */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: var(--header-height);
  z-index: 100;
  background: transparent;
  transition: transform 0.35s ease;
}

.site-header--collapsed {
  transform: translateY(-100%);
}

.site-header__inner {
  max-width: 1400px;
  height: 100%;
  margin: 0 auto;
  padding: 0 var(--edge-padding);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* .wordmark is a flex row so the header can place a logo mark beside the
   text; font/color properties live here (not on .wordmark__text) so they
   still cascade down correctly for .wordmark--footer, which has no logo
   image and no .wordmark__text wrapper — just plain text. */
.wordmark {
  display: flex;
  align-items: stretch;
  gap: 0.6rem;
  font-family: var(--font-sans);
  /* Same size, weight, and tracking as .site-nav__list a (the page titles
     beside it) — only the footer/Our Work "Earthborn" instances keep
     their own larger sizes, per request. */
  font-size: 0.88rem;
  font-weight: 500;
  /* !important: beats the site-wide -0.03em rule above, which at this
     wordmark's sizes was pulling letters into each other instead of
     keeping the wide, deliberately spaced-out tracking a logo wants */
  letter-spacing: 0.14em !important;
  color: #fff;
  line-height: 1.4;
}

.wordmark__logo {
  /* Explicit height matching the two-line text block below — height: 100%
     doesn't work here since the flex row's own height is indefinite, which
     was rendering this at its native 238x274px (way too big) instead of
     stretching to match the text. Reduced along with the text's own
     smaller size above. */
  height: 1.85rem;
  width: auto;
  flex-shrink: 0;
  transition: filter 0.3s ease;
}

.wordmark__text {
  display: flex;
  flex-direction: column;
  /* Without this, being a direct match for the site-wide `*` rule, this
     span renders at that rule's own weight/spacing instead of inheriting
     .wordmark's actual values — which then breaks .wordmark-rt-gap's own
     `inherit` below it too, since it inherits from this element, not
     .wordmark directly. */
  font-weight: inherit !important;
  letter-spacing: inherit !important;
}

/* Wraps "Earthborn" (including the .wordmark-rt-gap span inside it) as one
   single unit. Required — without this wrapper, "Ear" / .wordmark-rt-gap /
   "hborn" sit directly inside .wordmark__text's flex column as three
   separate (two of them anonymous) flex items, so flex-direction: column
   was stacking them as three separate lines instead of running them
   together as one word. white-space: nowrap is just a safety net now that
   they're one flex item; the real fix is this wrapper existing at all. */
.wordmark__main {
  white-space: nowrap;
  font-weight: inherit !important;
  letter-spacing: inherit !important;
}

.wordmark__sub {
  font-size: 0.5rem;
  font-weight: 400;
  letter-spacing: 0.28em !important;
  opacity: 0.65;
}

/* Scoped to the header specifically (not .wordmark--footer, which sits on
   its own permanently-dark background and doesn't need this at all) */
.site-header .wordmark {
  transition: color 0.3s ease;
}

.site-nav__list {
  display: flex;
  gap: clamp(1.5rem, 3vw, 3rem);
}

.site-nav__list a {
  /* Slightly bigger than before for readability */
  font-size: 0.88rem;
  font-weight: 500;
  /* !important: beats the site-wide -0.03em rule, which was cramming
     these letters together instead of the wider, easier-to-read tracking
     meant for nav labels */
  letter-spacing: 0.14em !important;
  color: #fff;
  padding-bottom: 4px;
  border-bottom: 1px solid transparent;
  transition: color 0.3s ease, border-color 0.25s ease, opacity 0.25s ease;
}

.site-nav__list a:hover {
  border-bottom-color: rgba(255, 255, 255, 0.6);
}

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 32px;
  height: 32px;
  background: none;
  border: none;
  cursor: pointer;
}

.nav-toggle span {
  display: block;
  height: 1px;
  width: 100%;
  background: #fff;
  transition: background-color 0.3s ease;
}

/* Toggled by script.js when the section behind the header has a light
   (white) background */
.site-header--on-light .wordmark,
.site-header--on-light .site-nav__list a {
  color: var(--color-text-dark);
}

/* The logo mark is a solid white PNG, so inverting it flips it to solid
   black — same trick, applied to an image instead of text color */
.site-header--on-light .wordmark__logo {
  filter: invert(1);
}

.site-header--on-light .site-nav__list a:hover {
  border-bottom-color: rgba(43, 42, 40, 0.6);
}

.site-header--on-light .nav-toggle span {
  background: var(--color-text-dark);
}

/* ============================================================
   SECTION 1 — HERO
   ------------------------------------------------------------
   Structure:
   .hero            tall wrapper (150vh) — the extra 50vh over one screen
                     is exactly the sticky range below: .hero__media (a
                     normal-flow, 100vh element) is the only element
                     contributing to that flow height, so it stays glued
                     to the top for the first 50vh of scroll and then
                     releases, right on schedule with .hero__text below.
   .hero__media     The photo — position: sticky; top: 0, one screen tall.
                     Because it's 100vh inside a 150vh parent, it sticks
                     for the first 50vh scrolled and then unsticks, scrolling
                     away normally the rest of the way. It has to be the
                     page's only in-flow content here (.hero__text is
                     absolutely placed, out of flow) for the sticky math
                     to land on that exact 50vh figure.
   .hero__text      Absolutely placed over the photo, same top-left spot,
                     so it starts perfectly overlaid on it. It isn't
                     sticky — it just scrolls at the normal 1:1 rate, which
                     is what makes it read as "the text scrolls up over a
                     still photo": 50vh of scroll moves the (100vh-tall)
                     text up by 50vh, i.e. exactly half of it out of view
                     — the same scroll distance the photo needed to reach
                     its release point, so both happen together.
   ============================================================ */
.hero {
  position: relative;
  width: 100%;
  height: 150vh;
}

.hero__media {
  position: sticky;
  top: 0;
  height: 100vh;
  width: 100%;
  overflow: hidden;
}

.hero__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 32%;
  display: block;
}

.hero__overlay {
  position: absolute;
  inset: 0;
  /* Vignette top+bottom for text legibility, plus a touch more on the left where the headline sits */
  background:
    linear-gradient(to bottom, rgba(10,8,6,0.55) 0%, rgba(10,8,6,0.05) 32%, rgba(10,8,6,0.12) 58%, rgba(10,8,6,0.55) 100%),
    linear-gradient(90deg, rgba(10,8,6,0.35) 0%, rgba(10,8,6,0) 45%);
}

.hero__text {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  width: 100%;
  height: 100vh;
  padding: calc(var(--header-height) + 2.5rem) var(--edge-padding) 3rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  pointer-events: none; /* re-enabled on the links below so the rest of the layer stays click-through */
}

.hero__text a {
  pointer-events: auto;
}

.hero__headline {
  max-width: 900px;
  font-family: var(--font-sans);
  /* Left as-is (not !important) on purpose — the actual rendered text
     (inside .hero-rise-inner) ends up governed by the site-wide `*` rule
     (550 / -0.03em) instead of the 800 / -0.01em written here, since a
     direct match always beats inheriting from this element. That's the
     look this heading has always had on-screen; kept deliberately rather
     than "corrected" to match what's declared here. */
  font-weight: 800;
  font-size: clamp(2.6rem, 7vw, 5.6rem);
  line-height: 0.98;
  letter-spacing: -0.01em;
  color: #fff;
  /* This is also .hero-rise-mask (overflow: hidden) — the line-height is
     tighter than 1, so descenders (the "g" in "began", etc.) naturally
     extend a little below the line box. That was harmless with overflow
     visible; with the mask clipping it, it needs this bit of headroom so
     they don't get cut off. */
  padding-bottom: 0.15em;
}

/* Groups the headline with the subheadline/CTA so they can sit close
   together — .hero__text's own space-between still pushes this whole
   group up top and the scroll indicator down to the bottom */
.hero__top {
  display: flex;
  flex-direction: column;
  gap: clamp(1.25rem, 2.5vw, 2rem);
}

.hero__lower {
  align-self: flex-end;
  max-width: 380px;
  text-align: left;
}

.hero__subheadline {
  font-size: clamp(1rem, 1.3vw, 1.15rem);
  font-weight: 300;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.85);
}

/* Text-with-arrow link, no button box */
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: 1.5rem;
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 500;
  color: #fff;
  transition: gap 0.25s ease;
}

.link-arrow__icon {
  transition: transform 0.25s ease;
}

.link-arrow:hover {
  gap: 0.9rem;
}

.link-arrow:hover .link-arrow__icon {
  transform: translateX(3px);
}

.scroll-indicator {
  align-self: center;
  width: 24px;
  height: 40px;
  display: flex;
  align-items: flex-start;
  justify-content: center;
}

.scroll-indicator__line {
  width: 1px;
  height: 28px;
  background: rgba(255, 255, 255, 0.75);
  animation: scrollPulse 2.2s ease-in-out infinite;
}

@keyframes scrollPulse {
  0%   { transform: scaleY(0.3); transform-origin: top; opacity: 0.4; }
  50%  { transform: scaleY(1);   transform-origin: top; opacity: 1;   }
  100% { transform: scaleY(0.3); transform-origin: top; opacity: 0.4; }
}

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

/* ============================================================
   SECTION 2 — INTRO STATEMENT
   ------------------------------------------------------------
   One wide, centered paragraph — no heading. script.js wraps each word in
   its own span on load; .intro__word starts muted grey and picks up
   .is-revealed (full dark color) word-by-word as the reader scrolls, tied
   to scroll position via getBoundingClientRect (see script.js).
   ============================================================ */
.intro {
  background: #fff;
  padding-top: var(--story-gap);
  padding-right: var(--edge-padding);
  padding-bottom: calc(var(--story-gap) / 2);
  padding-left: var(--edge-padding);
  display: flex;
  justify-content: center;
}

.intro__text {
  max-width: 1300px;
  text-align: center;
  font-family: var(--font-sans);
  font-size: clamp(1.1rem, 1.9vw, 1.55rem);
  font-weight: 400;
  line-height: 1.55;
}

.intro__word {
  color: rgba(27, 23, 18, 0.3);
  transition: color 0.2s ease;
}

.intro__word.is-revealed {
  color: var(--color-text-dark);
}

@media (prefers-reduced-motion: reduce) {
  .intro__word {
    transition: none;
  }
}

/* ============================================================
   SECTION 3 — CRAFTSMANSHIP DETAIL GRID
   ------------------------------------------------------------
   Two explicit columns (.craft__col), each a plain flex column stacking
   its own 3 photos — NOT CSS multi-column, because that lets the browser
   "balance" items between columns however it likes, silently scrambling
   the 01-06 numbering. This way the left/right split is deterministic
   (left = 01-03, right = 04-06) while the two columns can still run to
   different total heights (the scattered, non-aligned look).
   --craft-gap is used for both the column-gap AND each item's
   margin-bottom, so every gap stays exactly equal.
   "--wide" items use a landscape aspect ratio, "--tall" items use a
   portrait one — mixing the two is what creates the staggered look.
   ============================================================ */
.craft {
  background: #fff;
  color: var(--color-text-dark);
  padding-top: calc(var(--story-gap) / 2);
  padding-right: var(--edge-padding);
  padding-bottom: var(--section-padding);
  padding-left: var(--edge-padding);
}

.craft__grid {
  --craft-gap: clamp(1.25rem, 3vw, 2.25rem);
  max-width: 1150px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: start;
  column-gap: var(--craft-gap);
}

.craft__col {
  display: flex;
  flex-direction: column;
}

.craft__item {
  position: relative;
  overflow: hidden;
  margin-bottom: var(--craft-gap);
}

/* Outer-edge offsets, more dramatic on wider screens — the left column's
   items stay flush against the central gutter on the right and vary on the
   left; the right column mirrors that. Scoped to each .craft__col so the
   nth-child count is per-column (1-3), not global (1-6). */
@media (min-width: 561px) {
  .craft__col:first-child .craft__item { margin-right: 0; }
  .craft__col:first-child .craft__item:nth-child(1) { margin-left: 0; }
  .craft__col:first-child .craft__item:nth-child(2) { margin-left: clamp(3rem, 9vw, 6rem); }
  .craft__col:first-child .craft__item:nth-child(3) { margin-left: clamp(1.5rem, 5vw, 3rem); }

  .craft__col:last-child .craft__item { margin-left: 0; }
  .craft__col:last-child .craft__item:nth-child(1) { margin-right: clamp(1.5rem, 5vw, 3rem); }
  .craft__col:last-child .craft__item:nth-child(2) { margin-right: 0; }
  .craft__col:last-child .craft__item:nth-child(3) { margin-right: clamp(3rem, 9vw, 6rem); }
}

.craft__item--wide {
  aspect-ratio: 4 / 3;
}

.craft__item--tall {
  aspect-ratio: 3 / 4;
}

.craft__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.craft__item figcaption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 1.25rem 1.5rem;
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  color: #fff;
  /* Same family as the hero's "Start Your Sauna" link-arrow */
  font-family: var(--font-sans);
  font-size: clamp(1.2rem, 2vw, 1.5rem);
  font-weight: 600;
  background: linear-gradient(to top, rgba(10, 8, 6, 0.72) 0%, rgba(10, 8, 6, 0) 65%);
}

.craft__num {
  font-family: var(--font-sans);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  opacity: 0.75;
  transition: opacity 0.3s ease;
}

.craft__caption-text {
  display: inline-block;
  transition: transform 0.3s ease;
}

/* On hover: the number fades out in place, and the caption text slides left
   to fill the space it left behind. Reverses automatically on mouse-out. */
.craft__item:hover .craft__num {
  opacity: 0;
}

.craft__item:hover .craft__caption-text {
  transform: translateX(-1.95rem);
}

/* ============================================================
   SECTION 4 — TESTIMONIAL
   ------------------------------------------------------------
   A horizontal scroll-jack: .testimonial is a tall wrapper (200vh),
   .testimonial__viewport is pinned via position: sticky so only one
   quote shows at a time, and script.js translates .testimonial__track
   sideways as the user scrolls through the wrapper's extra height.
   The wrapper is kept short (200vh = only 100vh of extra scroll room
   for both transitions) so the quotes don't feel far apart.
   ============================================================ */
.testimonial {
  position: relative;
  height: 200vh;
  background: var(--color-charcoal);
}

.testimonial__viewport {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
}

/* Big, barely-there backdrop label, top-left of the section. Same bold sans
   as the hero headline, but low-opacity so it reads as texture, not a
   competing headline. script.js gives it a much smaller translateX than
   the track, so it drifts only slightly as the testimonials slide across. */
.testimonial__bg-label {
  position: absolute;
  top: clamp(2rem, 7vw, 4rem);
  left: var(--edge-padding);
  z-index: 0;
  max-width: min(90vw, 640px);
  font-family: var(--font-sans);
  font-weight: 800;
  font-size: clamp(2rem, 6vw, 4.5rem);
  line-height: 1.05;
  letter-spacing: -0.01em;
  color: rgba(255, 255, 255, 0.08);
  pointer-events: none;
}

.testimonial__track {
  position: relative;
  z-index: 1;
  display: flex;
  height: 100%;
  width: 300%;
}

.testimonial__quote {
  flex: 0 0 33.3333%;
  width: 33.3333%;
  height: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  /* Extra top padding (vs. bottom) biases the centered content downward,
     away from .testimonial__bg-label sitting in the top-left corner */
  padding: clamp(7rem, 14vw, 10rem) clamp(1.5rem, 8vw, 6rem) clamp(2rem, 5vw, 3rem);
  /* Justified so the text lines up flush on both the left and right edges */
  text-align: justify;
  /* Same font as the hero headline, rather than the serif used elsewhere */
  font-family: var(--font-sans);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(1.1rem, 2vw, 1.5rem);
  line-height: 1.6;
  color: var(--color-text-light);
}

.testimonial__author {
  display: block;
  margin-top: 1.5rem;
  text-align: left;
  font-family: var(--font-sans);
  font-style: normal;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  color: var(--color-muted-on-dark);
}

@media (prefers-reduced-motion: reduce) {
  /* Skip the scroll-jack animation, but keep the section exactly one
     screen tall rather than letting it grow to fit all 3 quotes stacked
     — any quotes that don't fit are reachable with a normal (non-jacked)
     scroll inside the section itself instead. */
  .testimonial {
    height: 100vh;
  }

  .testimonial__viewport {
    position: static;
    height: 100%;
    overflow-y: auto;
    overflow-x: hidden;
  }

  /* Decorative only (aria-hidden) — drop it rather than let it overlap
     the now-normal-flow, stacked quotes */
  .testimonial__bg-label {
    display: none;
  }

  .testimonial__track {
    display: block;
    width: 100%;
    height: auto;
    transform: none !important;
  }

  .testimonial__quote {
    width: 100%;
    height: auto;
    padding: 2.5rem var(--edge-padding);
  }
}

/* ============================================================
   SECTION 5 — CLOSING CTA
   ============================================================ */
.cta {
  position: relative;
  padding: var(--section-padding) var(--edge-padding);
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  overflow: hidden;

  /* Swap this file to change the closing CTA background image */
  background-image: url('images/man-in-sauna.png');
  background-size: cover;
  background-position: center;
}

.cta__overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(rgba(10,8,6,0.35), rgba(10,8,6,0.6));
}

.cta__content {
  position: relative;
  z-index: 2;
  max-width: 720px;
  margin: 0 auto;
}

.cta__headline {
  /* Sans, matching the hero headline/testimonials, rather than the serif
     used just in Section 2 */
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: clamp(2.1rem, 4.4vw, 3.2rem);
  line-height: 1.2;
  color: #fff;
}

.cta__subtext {
  margin-top: 1.25rem;
  /* Matches .hero__subheadline exactly */
  font-size: clamp(1rem, 1.3vw, 1.15rem);
  font-weight: 300;
  color: rgba(255, 255, 255, 0.85);
}

.cta .link-arrow {
  margin-top: 2.5rem;
}

/* Layered directly over the CTA photo — no background box of its own,
   just white text sitting on the photo (which already has .cta__overlay's
   dark gradient behind it for legibility) */
.cta__tagline {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  padding: 1.5rem var(--edge-padding);
  text-align: left;
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: clamp(0.95rem, 1.6vw, 1.15rem);
  color: #fff;
}

/* ============================================================
   FOOTER
   ============================================================ */
.site-footer {
  background: var(--color-charcoal);
  color: var(--color-muted-on-dark);
  padding: 3rem var(--edge-padding);
}

.site-footer__inner {
  max-width: 1400px;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
  align-items: center;
  justify-content: space-between;
}

.wordmark--footer {
  font-size: 0.9rem;
  letter-spacing: 0.18em !important;
  color: var(--color-text-light);
}

.site-footer__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  font-size: 0.85rem;
  letter-spacing: 0.02em;
}

.site-footer__meta a:hover {
  color: var(--color-text-light);
}

.site-footer__meta .dot {
  opacity: 0.5;
}

/* ---------- Expanded footer — every page except Our Work, which keeps
   the plain .site-footer above. Two bands: a small white tagline strip,
   then a dark block exactly a full screen tall (not just at-least — a
   min-height let real content push it taller than one screen at some
   viewports, which scrolled the tagline strip out of view before you
   ever reached the bottom of the page), so scrolling to the very bottom
   shows almost nothing but the footer, tagline strip included. ---------- */
.site-footer--expanded {
  background: none;
  padding: 0;
  display: flex;
  flex-direction: column;
  height: 100vh;
}

.site-footer__tagline {
  background: #fff;
  color: var(--color-text-dark);
  padding: 1.5rem var(--edge-padding);
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: clamp(0.95rem, 1.6vw, 1.15rem);
}

.site-footer__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: var(--color-charcoal);
  color: var(--color-text-light);
  /* Top padding matches .site-footer__tagline's own bottom padding
     (1.5rem), so the gap above "Earthborn" equals the gap below the
     tagline text */
  padding: 1.5rem var(--edge-padding) clamp(2.5rem, 5vw, 4rem);
}

/* Manual nudge between just the "r" and "t" in "Earthborn" — wraps the
   "t" in the header and footer wordmarks (see the HTML), pushing it (and
   everything after it) further right, purely by adding space — nothing
   about the "t" itself changes. Doesn't touch Our Work's centered title,
   which handles this pair via its own letter-spacing instead. em-based
   so it scales with each instance's own font-size automatically.
   font-weight/letter-spacing: inherit !important is required here —
   without it this span, being a direct match for the site-wide `*` rule,
   would render at that rule's values instead of matching the surrounding
   letters (which only get the parent wordmark's actual weight/spacing
   through inheritance) — which was making the "t" visibly thinner and
   differently-spaced than the rest of the word. */
.wordmark-rt-gap {
  margin-left: 0.05em;
  font-weight: inherit !important;
  letter-spacing: inherit !important;
}

.site-footer__wordmark {
  font-family: var(--font-sans);
  /* Matches the hero headline's (.hero__headline, home + Our Story) actual
     on-screen weight/spacing exactly — 550 / -0.03em, the site-wide
     default values, not the 800 / -0.01em written directly on that rule
     (which a separate bug stopped from ever actually rendering — see the
     comment there). !important to beat .site-footer * above. */
  font-weight: 550 !important;
  font-size: clamp(3.5rem, 11vw, 10rem);
  line-height: 1;
  letter-spacing: -0.03em !important;
  color: #fff;
}

/* Pushed to the bottom of the (flex, at-least-one-screen-tall) body, so
   the big wordmark sits at the top with a large gap below it, same as
   the reference layout */
.site-footer__columns {
  margin-top: auto;
  padding-top: clamp(4rem, 10vw, 8rem);
  max-width: 1400px;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 2rem;
  align-items: start;
}

/* Small credit line — lives inside the signup column, right under the
   email box. A negative margin-bottom cancels out this element's own
   height from the column's counted box, so it doesn't grow the grid row
   (still governed by the taller nav column) or shrink the flexible gap
   above .site-footer__columns — it just renders into the empty space
   .site-footer__body's own bottom padding already reserved, which was
   previously unused. Courier Prime — a cleaner, far more legible take on
   the Special Elite typewriter look this replaced; "BH Studio" steps up
   to a second, larger treatment — see .site-footer__credit below. */
.site-footer__legal {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  justify-content: flex-start;
  text-align: left;
  gap: 0.4em;
  margin-top: 0;
  margin-bottom: -4rem;
  font-family: var(--font-typewriter);
  font-weight: 400;
  font-size: 0.72rem;
  line-height: 1.4;
  letter-spacing: 0.01em;
  color: var(--color-muted-on-dark);
}

.site-footer__legal-divider {
  opacity: 0.45;
}

.site-footer__credit {
  font-family: var(--font-serif);
  font-style: normal;
  font-weight: 500;
  font-size: 1.2em;
  letter-spacing: 0;
  color: var(--color-text-light);
  text-decoration: none;
}

.site-footer__credit:hover {
  text-decoration: underline;
}

.site-footer__col {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  font-size: 0.95rem;
}

.site-footer__col a:hover {
  color: #fff;
}

.site-footer__signup-heading {
  font-weight: 600;
  font-size: 1rem;
  max-width: 18rem;
}

.site-footer__signup {
  margin-top: 1.5rem;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  background: #fff;
  border-radius: 999px;
  padding: 0.35rem;
  max-width: 22rem;
}

.site-footer__signup input {
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  padding: 0.65rem 1rem;
  font-family: var(--font-sans);
  font-size: 0.9rem;
  color: var(--color-text-dark);
  outline: none;
}

.site-footer__signup input::placeholder {
  color: #b3aea3;
}

.site-footer__signup button {
  flex: none;
  border: none;
  border-radius: 999px;
  background: var(--color-charcoal);
  color: #fff;
  padding: 0.65rem 1.5rem;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 0.85rem;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.site-footer__signup button:hover {
  background: var(--color-charcoal-soft);
}

@media (max-width: 720px) {
  .site-footer__columns {
    grid-template-columns: 1fr;
    gap: 2.5rem;
  }

  /* The negative margin-bottom above only works on desktop, where it hides
     inside slack borrowed from the taller nav column beside it. Stacked to
     one column here, there's no such slack — a large negative margin would
     make the text spill out past the footer's own background instead of
     being absorbed. Small, real spacing instead; a little extra height at
     this breakpoint is an acceptable trade for not visibly breaking. */
  .site-footer__legal {
    margin-top: 0.75rem;
    margin-bottom: 0;
  }
}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 560px) {
  /* Single column on small screens — stacks .craft__col 1 then .craft__col 2,
     so 01-06 still reads top-to-bottom in order. The outer-edge offset rules
     are scoped to (min-width: 561px) already, so no reset is needed here. */
  .craft__grid {
    grid-template-columns: 1fr;
  }
}

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

  .site-nav__list {
    position: absolute;
    top: var(--header-height);
    left: 0;
    width: 100%;
    flex-direction: column;
    gap: 0;
    background: rgba(23, 20, 15, 0.97);
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.35s ease;
  }

  .site-nav__list.is-open {
    max-height: 320px;
  }

  .site-nav__list li {
    border-top: 1px solid rgba(255, 255, 255, 0.08);
  }

  .site-nav__list a {
    display: block;
    padding: 1.1rem var(--edge-padding);
  }

  .hero__headline {
    max-width: 100%;
  }

  .hero__lower {
    max-width: 100%;
  }
}

/* ============================================================
   SUBPAGES — Our Story / Our Work / Contact
   ------------------------------------------------------------
   Everything below reuses the homepage design tokens: the same
   palette variables, Inter type scale, .label / .link-arrow /
   .fade-in / .btn components, and the transparent header. Pages
   whose sections are light flip the header via .section--light
   exactly like the homepage; photos that scroll under the header
   are tagged data-header-photo (script.js treats them like the
   homepage's .craft__item photos).
   ============================================================ */

/* Current-page indicator in the nav — the same underline the
   links already show on hover, held permanently */
.site-nav__list a.is-active {
  border-bottom-color: rgba(255, 255, 255, 0.6);
}

.site-header--on-light .site-nav__list a.is-active {
  border-bottom-color: rgba(0, 0, 0, 0.6);
}

/* ---------- Our Story cover hero — plain full-bleed photo, no card.
   Text reuses the homepage hero's own classes (.hero__text /
   .hero__headline / .hero__bottom / .hero__lower / .hero__subheadline) so
   the fonts/weights stay identical to the homepage. The .story-hero__text
   rules below override just the ALIGNMENT for this page: both blocks
   cluster together on the right, right-aligned, close together — unlike
   the homepage's top-left headline / bottom-right subtext split. No
   section--light — like the homepage's photo hero, the header just stays
   in its default white state over the photo. ---------- */
.story-hero {
  position: relative;
  height: 100vh;
  min-height: 34rem;
  overflow: hidden;
}

.story-hero__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.story-hero__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 40%;
}

/* Both children (headline, then the subtext block) flush to the same
   right edge, clustered near the bottom with a small gap instead of the
   homepage's space-between split */
.story-hero__text {
  align-items: flex-end;
  justify-content: flex-end;
  gap: 1.25rem;
}

.story-hero__text .hero__headline {
  max-width: 640px;
  text-align: right;
}

.story-hero__text .hero__lower {
  text-align: right;
}

/* ---------- Shared section shells ---------- */
.section-inner {
  max-width: 1150px; /* matches .craft__grid */
  margin: 0 auto;
}

/* Section heading style shared by subpage sections — same weight/scale
   family as .cta__headline */
.section-heading {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: clamp(1.9rem, 3.5vw, 2.8rem);
  line-height: 1.2;
}

/* ============================================================
   OUR STORY
   ============================================================ */

/* Story copy — same treatment as the homepage's .intro__text: centered,
   plain weight (forced un-bold above), word-by-word scroll reveal via
   script.js instead of a static color. */
.story-copy {
  font-family: var(--font-sans);
  font-size: clamp(1.1rem, 1.9vw, 1.55rem);
  font-weight: 400;
  line-height: 1.55;
  text-align: center;
}

.story-copy-section {
  background: #fff;
  padding: var(--section-padding) var(--edge-padding);
}

/* Single centered column, stacked paragraphs — matches .intro's layout */
.story-copy-section__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.75rem;
  max-width: 1300px;
  margin: 0 auto;
}

/* Full-bleed breather banner — "Heat. Cold. Rest. Repeat." */
.story-banner {
  position: relative;
  height: 60vh;
  min-height: 420px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  overflow: hidden;
}

.story-banner__image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.story-banner__overlay {
  position: absolute;
  inset: 0;
  background: rgba(10, 8, 6, 0.45);
}

.story-banner__text {
  position: relative;
  z-index: 2;
  padding: 0 var(--edge-padding);
  /* Same voice as the hero headline */
  font-family: var(--font-sans);
  font-weight: 800;
  font-size: clamp(1.9rem, 5.5vw, 4.2rem);
  line-height: 1.1;
  letter-spacing: 0.02em;
  color: #fff;
}

/* Team — alternating founder/craftsman profiles */
.team {
  background: #fff;
  padding: var(--section-padding) var(--edge-padding);
}

.team .section-heading {
  margin-bottom: clamp(3rem, 6vw, 5rem);
  max-width: 640px;
}

/* Both columns are an even 1fr/1fr split — the SAME two widths for every
   profile, normal or reversed, rather than each row's photo column
   sizing itself to that row's own photo (fit-content). That's what makes
   the column boundary land at the exact same X position on every row:
   photo-left-edge on Stephen's/Mason's rows, text-left-edge on Nick's —
   all four of those line up with each other and with the section
   heading, and stay lined up at any viewport width, since it's a
   percentage split rather than a fixed number. The trade-off is more
   empty space around narrower photos (the column is generally wider
   than any single photo needs), in exchange for that alignment. */
.profile {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(0.75rem, 1.5vw, 1.25rem);
  /* Both columns start at the same top edge — the photo, and (see below)
     the box .profile__text-inner actually sticks within, rather than
     the old vertically-centered pairing */
  align-items: start;
}

/* Stephen's row specifically (he's always the first .profile in the
   section) keeps its own wider photo-to-text gap, restored to how it was
   two edits ago — :not(.profile--reverse) alone isn't enough to single
   him out any more now that Mason is a second non-reversed profile too;
   without :first-of-type here, Mason would inherit this same wider gap. */
.profile:first-of-type {
  gap: clamp(1.75rem, 3.5vw, 3rem);
}

/* Space between each person's whole card, not the gap within one —
   smaller than before, and (being one shared rule) already the same
   between every pair of cards. */
.profile + .profile {
  margin-top: clamp(2.5rem, 5vw, 4rem);
}

/* Driven by height, with width left to auto so it's exactly the image's
   own natural aspect ratio — no cropping, and (unlike width: 100% +
   max-height, which was capping height without reducing width to match,
   leaving transparent margins on either side that made the box bigger
   without the visible photo actually growing) no wasted empty space in
   the box either. Sits at the start (left) edge of its own grid column
   by default, which is what lines the photo up with the section heading
   above on Stephen's/Mason's rows. */
.profile__media img {
  height: clamp(38rem, 88vh, 56rem);
  width: auto;
  max-width: 100%;
  display: block;
  object-fit: contain;
}

/* Mirror the second profile so the page doesn't read as a repeated card —
   moves the photo to the second (right-hand) column instead of the first.
   justify-self: end pins it to that column's own right edge (rather than
   sitting flush against its own left, which is the block default) —
   since Stephen's/Mason's text fills their column edge-to-edge on wrapped
   lines (the column itself, at half the row's width, is narrower than
   .profile__bio's own max-width, so the column is what actually limits
   the wrap, not the max-width), that right edge is where their text
   visually ends too. Nick's photo keeps its own real proportions
   (nothing here resizes or crops it) — this only changes where within
   its column it sits, which is what brings its right edge close to that
   same line without needing to touch the image itself. */
.profile--reverse .profile__media {
  order: 2;
  justify-self: end;
}

/* Stretched to the full height of the row (i.e., to match the photo,
   which is normally the taller of the two columns) purely so
   .profile__text-inner has room to stick within it — see below. */
.profile__text {
  align-self: stretch;
}

/* As the page scrolls the photo out via the top of the screen, this
   sticks in place instead of scrolling at the same rate, so it reads as
   staying put while the (taller) photo continues past it — then
   releases and scrolls away normally again once its own bottom edge
   reaches .profile__text's, which — since that box is exactly as tall as
   the photo and both start at the same top — happens exactly when this
   text's bottom lines up with the photo's bottom, not before.
   `top` is set well below the header's own height (not just a small
   fixed gap) for two reasons: it's what the text sticks at, so this
   also keeps the reappearing header (it slides back in on any upward
   scroll) from ever overlapping the pinned text, and a bigger `top`
   value on its own makes the stick point trigger with less scrolling —
   i.e., a bit earlier — without changing the release point at all. */
.profile__text-inner {
  position: sticky;
  top: calc(var(--header-height) + 1.5rem);
}

/* Bigger than the base .label — these are each profile's role ("Founder &
   lead craftsman" / "Craftsman"), which reads better with more presence
   than the section-level eyebrow above uses */
.profile__text-inner .label {
  font-size: 1.05rem;
  letter-spacing: 0.12em;
}

.profile__name {
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  font-weight: 700;
  line-height: 1.2;
}

/* Much wider than the text column really needs so the same word count
   wraps to fewer lines — a longer, shorter box instead of a narrow,
   tall one */
.profile__bio {
  margin-top: 1.25rem;
  color: var(--color-muted-on-light);
  max-width: 100ch;
}

/* ============================================================
   OUR WORK — horizontal stepped gallery
   ------------------------------------------------------------
   The page is a fixed full-viewport stage (body.work-horizontal disables
   normal scrolling). script.js converts wheel / swipe / arrow-key input
   into discrete slide steps: the incoming slide's photos glide in from
   the right edge and settle, the outgoing slide's exit left. The wordmark
   stays dead-center throughout. Each slide carries its stage color in
   data-bg and its ink color in data-theme — exactly 3 fixed swatches
   (white / light grey / black) snapped between with no cross-fade, while
   the text ink still eases to stay legible through the cut.
   Layering: every photo sits UNDER the wordmark — including the
   edge-placed ones (slides tagged .hwork__slide--over), which used to cut
   across on top of it but no longer do.
   ============================================================ */
body.work-horizontal {
  height: 100vh;
  overflow: hidden;
}

.hwork {
  position: fixed;
  inset: 0;
  background: #fff;
  /* Exactly 3 fixed swatches (white / light grey / black), set per-slide via
     data-bg in the HTML — no transition here, so the stage snaps between
     them instead of cross-fading. Ink color still eases (below) so text
     stays legible through the cut. */
  overflow: hidden;
}

/* --- Fixed center wordmark (never moves) --- */
.hwork__wordmark {
  position: absolute;
  inset: 0;
  z-index: 30;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 var(--edge-padding);
  pointer-events: none;
  color: #fff;
  transition: color 0.9s ease;
}

.hwork__title {
  /* Same font, weight, and tracking as .site-nav__list a / .wordmark (the
     page titles) — scaled up to this page's own centerpiece size, which
     stays its own value per request rather than matching their size too.
     Was previously matched to .site-footer__wordmark's bold/tight-tracking
     combo to stop "rt" from touching at this size — if that resurfaces
     with this lighter weight, that's the fallback to return to. */
  font-family: var(--font-sans);
  font-weight: 500 !important;
  font-size: clamp(2.6rem, 9vw, 7rem);
  line-height: 0.95;
  letter-spacing: 0.14em !important;
}

/* The visible text actually renders inside .hero-rise-inner (added for the
   page-load reveal animation), which — being a direct match for the
   site-wide `*` rule — was rendering at -0.03em/400 no matter what was set
   on .hwork__title itself; that parent rule only reaches it by
   inheritance, which direct matches always beat. Same fix as
   .rental-hero-v2__tagline * / .consult__intro * further up. */
.hwork__title .hero-rise-inner {
  font-weight: inherit !important;
  letter-spacing: inherit !important;
}

.hwork__subtitle {
  margin-top: 0.6rem;
  font-size: clamp(0.65rem, 1.2vw, 0.85rem);
  font-weight: 500;
  /* Same em value as .hwork__title — since em scales with each element's
     own font-size already, matching the number keeps both proportional
     rather than one reading tighter or looser than the other */
  letter-spacing: 0.14em !important;
  opacity: 0.8;
}

.hwork__subtitle .hero-rise-inner {
  font-weight: inherit !important;
  letter-spacing: inherit !important;
}

/* Light stage: every fixed element flips to dark ink */
.hwork--light .hwork__wordmark,
.hwork--light .hwork__slide,
.hwork--light .hwork__ui {
  color: var(--color-text-dark);
}

/* --- Slides --- */
.hwork__slide {
  position: absolute;
  inset: 0;
  z-index: 20; /* centered photos sit under the wordmark */
  pointer-events: none;
  color: #fff;
  transition: color 0.9s ease;
}

.hwork__slide--over {
  /* Used to sit above the wordmark (z-index: 40); now kept behind it like
     every other slide, so text always stays readable and on top */
  z-index: 20;
}

.hwork__img {
  position: absolute;
  overflow: hidden;
  /* Photos park off the right edge until their slide activates */
  transform: translateX(120vw);
  opacity: 0;
  transition: transform 1.05s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.9s ease;
}

/* The second photo of a pair trails the first slightly */
.hwork__img + .hwork__img {
  transition-delay: 0.12s;
}

.hwork__slide.is-active .hwork__img {
  transform: translateX(0);
  opacity: 1;
}

.hwork__slide.is-past .hwork__img {
  transform: translateX(-130vw);
  opacity: 0;
}

.hwork__img img {
  width: 100%;
  height: 100%;
  /* contain, not cover — the box below is now sized to each photo's real
     aspect ratio, so nothing is ever cropped off the top/bottom or sides */
  object-fit: contain;
  display: block;
}

/* --- Photo placements: centered (wordmark layers on top) ---
   Each box's aspect-ratio matches its actual photo's real pixel dimensions
   (checked directly against the source files), so the full image always
   fits with no cropping. Only WIDTH is set explicitly; height is derived
   from aspect-ratio. max-height is a safety cap for portrait photos on
   short/wide viewports — contain means it just adds a little letterboxing
   there instead of ever cropping. */
.hwork__img--center {
  /* DSC04843.webp (1920x1080) / DSC05543.webp (1500x844) — both ~16:9 */
  aspect-ratio: 16 / 9;
  width: min(50vw, 720px);
  max-height: 60vh;
  left: calc(50% - min(25vw, 360px));
  top: 20vh;
}

.hwork__img--center-tall {
  /* IMG_9728.webp (750x1000) — 3:4 */
  aspect-ratio: 3 / 4;
  width: min(30vw, 400px);
  max-height: 70vh;
  left: calc(50% - min(15vw, 200px));
  top: 15vh;
}

.hwork__img--center-narrow {
  /* DSC05303.webp (500x888) — a taller, narrower portrait than --center-tall */
  aspect-ratio: 500 / 888;
  width: min(24vw, 320px);
  max-height: 70vh;
  left: calc(50% - min(12vw, 160px));
  top: 15vh;
}

/* --- Photo placements: edges/corners, bleeding off-screen
   (their slide gets --over, so they cut across the wordmark). The negative
   inset offsets are a deliberate compositional choice — the box bleeding
   past the viewport edge — not to be confused with the photo itself being
   cropped, which contain + a matched aspect-ratio now prevents. --- */
.hwork__img--left-low {
  /* DSC05170.webp (500x888) — tall enough that it needs most of the
     viewport height at its full width to stay uncropped */
  aspect-ratio: 500 / 888;
  left: -4vw;
  bottom: -8vh;
  width: min(32vw, 420px);
  max-height: 94vh;
}

.hwork__img--top-right-sm {
  /* unnamed (9).jpg (640x360) — 16:9, replacing the old portrait
     DSC04848.webp, so the box's shape/size follow the new photo instead.
     Flush with the corner (0, not a negative edge-bleed) — with contain
     now matching the box's ratio exactly, any bleed past .hwork's
     overflow:hidden edge would cut into real photo pixels instead of
     empty letterbox space. */
  aspect-ratio: 16 / 9;
  right: 0;
  top: 0;
  width: min(40vw, 540px);
  max-height: 48vh;
}

.hwork__img--bottom-right {
  /* white sauna.webp (1500x1125) — 4:3 */
  aspect-ratio: 4 / 3;
  right: -4vw;
  bottom: -8vh;
  width: min(40vw, 560px);
  max-height: 58vh;
}

.hwork__img--top-left-sm {
  /* unnamed 6.jpg (640x360) — 16:9, replacing the old DSC04884.webp (also
     16:9, so the ratio itself didn't need to change). Flush with the
     corner, not a negative edge-bleed — see the .hwork__img--top-right-sm
     comment. */
  aspect-ratio: 16 / 9;
  left: 0;
  top: 0;
  width: min(40vw, 540px);
  max-height: 48vh;
}

.hwork__img--left-high {
  /* DSC05179.webp (2500x1407) — 16:9 */
  aspect-ratio: 16 / 9;
  left: -4vw;
  top: -6vh;
  width: min(42vw, 600px);
  max-height: 52vh;
}

.hwork__img--right-low-tall {
  /* DSC05839.webp (500x888) */
  aspect-ratio: 500 / 888;
  right: -3vw;
  bottom: -10vh;
  width: min(26vw, 340px);
  max-height: 76vh;
}

.hwork__img--bottom-left-tall {
  /* unnamed (7).jpg (480x640) — 3:4, replacing the old landscape
     IMG_0580.webp. Flush with the corner, not a negative edge-bleed — see
     the .hwork__img--top-right-sm comment above. */
  aspect-ratio: 3 / 4;
  left: 0;
  bottom: 0;
  width: min(30vw, 400px);
  max-height: 66vh;
}

.hwork__img--top-right-wide {
  /* unnamed (10).jpg (480x640) — 3:4 portrait, replacing the old landscape
     DSC05405.webp, so the box's shape/size follow the new photo instead.
     Flush with the corner, not a negative edge-bleed — see the
     .hwork__img--top-right-sm comment above. Width kept under the
     max-height cap at typical viewport heights so the box's ratio doesn't
     drift from the photo's. */
  aspect-ratio: 3 / 4;
  right: 0;
  top: 0;
  width: min(30vw, 400px);
  max-height: 66vh;
}

/* --- Outro slide content (bg is black by then, so ink inherits white).
   Hidden until its slide activates, then eases up into place --- */
.hwork__outro {
  position: absolute;
  left: 50%;
  transform: translate(-50%, 28px);
  bottom: clamp(3rem, 9vh, 6rem);
  width: max-content;
  max-width: 90vw;
  text-align: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.9s ease, transform 0.9s ease;
}

.hwork__slide.is-active .hwork__outro {
  opacity: 1;
  transform: translate(-50%, 0);
  pointer-events: auto;
  transition: opacity 0.9s ease 0.25s, transform 0.9s ease 0.25s;
}

.hwork__outro .link-arrow {
  color: inherit;
}

/* --- Fixed UI: slide counter + input hint --- */
.hwork__ui {
  position: absolute;
  z-index: 50;
  left: var(--edge-padding);
  right: var(--edge-padding);
  bottom: 2rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
  pointer-events: none;
  color: #fff;
  transition: color 0.9s ease;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.18em;
}

.hwork__hint {
  opacity: 0.6;
  transition: opacity 0.6s ease;
}

.hwork__hint.is-hidden {
  opacity: 0;
}

/* Show the right hint for the input device */
.hwork__hint--touch {
  display: none;
}

@media (pointer: coarse) {
  .hwork__hint--mouse {
    display: none;
  }

  .hwork__hint--touch {
    display: inline;
  }
}

@media (prefers-reduced-motion: reduce) {
  .hwork,
  .hwork__wordmark,
  .hwork__logo,
  .hwork__slide,
  .hwork__img,
  .hwork__ui {
    transition: none;
  }
}

/* Screen-reader-only helper */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Netlify Forms honeypot field — unlike .sr-only above, this should be
   invisible to everyone (sighted visitors and screen readers alike), not
   just visually hidden — it's a spam trap real visitors should never
   see or fill in, only left for bots to stumble into. */
.hidden {
  display: none;
}

/* ============================================================
   RENTAL
   ============================================================ */

/* ---------- Hero v2 — a big selected photo, a headline beside it, and a
   thumbnail strip along the bottom that swaps which photo is "selected"
   on click (see script.js). ---------- */
.rental-hero-v2 {
  background: #fff;
  padding: calc(var(--header-height) + 1rem) 0 clamp(2.5rem, 5vw, 4rem);
}

.rental-hero-v2__grid {
  /* Overrides .section-inner's centered max-width/margin — full-bleed
     with the same edge-padding inset .rental-hero-v2__thumbs uses below,
     so the headline's left edge lines up exactly with the first
     thumbnail's, instead of with section-inner's separately-centered one */
  max-width: none;
  margin: 0;
  padding: 0 var(--edge-padding);
  display: grid;
  grid-template-columns: 5fr 7fr;
  gap: clamp(1.5rem, 4vw, 3rem);
  /* Top-aligned so the headline starts at the top-left of this row,
     level with the top of the main photo beside it */
  align-items: start;
}

.rental-hero-v2__headline {
  font-family: var(--font-sans);
  font-weight: 800;
  /* Same size as .section-heading (e.g. "Check open dates" below) */
  font-size: clamp(1.9rem, 3.5vw, 2.8rem);
  line-height: 1.15;
  color: var(--color-text-dark);
}

.rental-hero-v2__tagline {
  margin-top: 1rem;
  /* Matches .how-it-works__intro exactly */
  font-size: 1.05rem;
  line-height: 1.65;
  color: var(--color-text-dark);
  max-width: 28rem;
}

.rental-hero-v2__main {
  border-radius: 0;
}

.rental-hero-v2__main img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  display: block;
  transition: opacity 0.2s ease;
}

/* Full-bleed row of thumbnails, cancelling .rental-hero-v2's own side
   padding the same way the old carousel used to */
.rental-hero-v2__thumbs {
  margin-top: clamp(1.5rem, 3vw, 2.5rem);
  /* Small, deliberate gap below the thumbnails and the bottom of the
     screen, on top of the section's own bottom padding */
  margin-bottom: clamp(1rem, 2.5vw, 2rem);
  padding: 0 var(--edge-padding);
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: clamp(0.5rem, 1.2vw, 0.85rem);
}

.rental-hero-v2__thumb {
  position: relative;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  border-radius: 0;
  overflow: hidden;
}

.rental-hero-v2__thumb img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  display: block;
}

/* White tint by default — a translucent white veil over the photo,
   rather than desaturating it — knocked back to fully clear only for the
   photo currently shown as the main image */
.rental-hero-v2__thumb::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0.65);
  transition: opacity 0.3s ease;
}

.rental-hero-v2__thumb:hover::after {
  opacity: 0.35;
}

.rental-hero-v2__thumb.is-active::after {
  opacity: 0;
}

@media (max-width: 900px) {
  .rental-hero-v2__grid {
    grid-template-columns: 1fr;
  }

  .rental-hero-v2__thumbs {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* ---------- How It Works — a scroll-jack: the left column (title +
   photo) stays sticky in place while the right column's copy scrolls up
   from underneath it, inside a viewport clipped to exactly the left
   column's height (see script.js, which measures and animates both). ---- */
.how-it-works {
  /* Small, equal gap above/below .how-it-works__photo — shared so the
     invisible spacer below can be given the exact same height */
  --hiw-photo-gap: clamp(1.5rem, 3vh, 2.5rem);
  position: relative;
  background: #fff;
}

.how-it-works__viewport {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  /* Any transform value turns this into the containing block for
     position: fixed descendants (.how-it-works__photo), confining that
     "fixed" photo to this box instead of the true page viewport — so it
     scrolls away normally before/after this section is stuck, rather
     than staying pinned to the screen for the rest of the page. */
  transform: translateZ(0);
}

/* Fills the sticky viewport's full height (not just tall enough for the
   left column) so .how-it-works__right, stretched below, reaches all the
   way to the true bottom of the screen — that's what lets the copy rise
   from the actual bottom of the screen rather than just the photo's own
   bottom edge. align-items:center keeps the left column (title + photo)
   vertically centered the way it was before this changed. */
.how-it-works__grid {
  width: 100%;
  height: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}

/* Invisible spacer — same height as .how-it-works__photo (via the shared
   --hiw-photo-gap) so it still occupies the grid's left column, keeping
   .how-it-works__right exactly where it's always been, and so
   script.js's measurement of this element's bottom edge lines up with
   the real (independently-positioned) photo's actual bottom edge. */
.how-it-works__left {
  height: calc(100vh - (2 * var(--hiw-photo-gap)));
}

/* The real, visible photo. position: fixed here is trapped to
   .how-it-works__viewport (see its transform above), so this behaves
   like position: absolute confined to that box — which is full viewport
   width and exactly matches the true viewport while the section is
   stuck. left: var(--edge-padding) therefore lines up exactly with the
   hero photo strip's own left edge above, which uses the same variable
   with no extra centered-container offset to account for. */
.how-it-works__photo {
  position: fixed;
  left: var(--edge-padding);
  top: var(--hiw-photo-gap);
  bottom: var(--hiw-photo-gap);
}

/* Sized by height only, width left auto — shows the photo at its real
   (portrait) aspect ratio instead of cropping it to fit a box. The
   max-width is just a safety ceiling so an unexpectedly wide image
   couldn't stretch across into the text column. */
.how-it-works__photo img {
  height: 100%;
  width: auto;
  max-width: 44vw;
  object-fit: contain;
  display: block;
}

.how-it-works__title {
  /* Between "65 Medium" (~500, read too thin) and 600 (read too heavy) */
  font-weight: 550;
  /* No text-transform — the markup already reads "How it" / "works",
     capital H only, so this just lets that natural casing show */
  /* Same size as .section-heading (e.g. "Check open dates" below) */
  font-size: clamp(1.9rem, 3.5vw, 2.8rem);
  line-height: 1.05;
  /* Tighter between letters */
  letter-spacing: -0.03em;
  /* A tiny bit taller, width unaffected — a plain font-size bump would
     widen the letters too */
  transform: scaleY(1.06);
  transform-origin: left top;
  color: var(--color-text-dark);
}

/* Layered over the photo instead of sitting above it — near the top-left
   corner, but inset enough to stay inside the photo's own box. Same
   font/weight/spacing/scale as before, just white so it reads over the
   image. */
.how-it-works__photo .how-it-works__title {
  position: absolute;
  top: clamp(1rem, 3vw, 2rem);
  left: clamp(1rem, 3vw, 2rem);
  color: #fff;
}

/* Stretches to the full row height (100% of the sticky viewport, since
   .how-it-works__grid is height:100% too) rather than matching the left
   column's own (shorter, centered) height — that's what lets the text
   track inside be visible rising all the way from the true bottom of the
   screen, not just from the photo's bottom edge. */
.how-it-works__right {
  align-self: stretch;
  position: relative;
  overflow: hidden;
}

.how-it-works__track {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  padding-right: var(--edge-padding);
}

.how-it-works__intro {
  font-size: 1.05rem;
  line-height: 1.65;
  color: var(--color-text-dark);
  max-width: 34rem;
}

.how-it-works__steps {
  margin-top: clamp(2rem, 4vw, 3rem);
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.how-it-works__step {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
}

/* A small circle tightly around the number — not a heading-scale number.
   Border width/colour matches the number's own (semi-bold) stroke weight
   instead of a thin, translucent hairline. */
.how-it-works__num {
  flex: none;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 50%;
  border: 2px solid var(--color-text-dark);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--color-text-dark);
}

.how-it-works__step-copy h3 {
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text-dark);
}

.how-it-works__step-copy p {
  margin-top: 0.35rem;
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--color-text-dark);
  max-width: 30rem;
}

.how-it-works__pricing {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  padding-top: clamp(2rem, 4vw, 2.5rem);
  border-top: 1px solid rgba(27, 23, 18, 0.14);
}

.how-it-works__pricing-heading {
  font-weight: 700;
  font-size: clamp(1.5rem, 2.6vw, 2rem);
  color: var(--color-text-dark);
}

.how-it-works__pricing-intro {
  margin-top: 0.75rem;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--color-text-dark);
  max-width: 30rem;
}

.pricing-tier {
  margin-top: clamp(1.5rem, 3vw, 2rem);
}

.pricing-tier h3 {
  font-weight: 600;
  font-size: 1.05rem;
  color: var(--color-text-dark);
}

.pricing-tier__rate {
  margin-top: 0.4rem;
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--color-text-dark);
}

.pricing-tier__note,
.pricing-tier__area {
  margin-top: 0.25rem;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--color-text-dark);
}

@media (prefers-reduced-motion: reduce) {
  .how-it-works {
    height: auto !important;
  }

  .how-it-works__viewport {
    position: static;
    height: auto;
    display: block;
  }

  /* The sticky viewport (and its transform trick trapping .how-it-works__photo
     to it) is off here, so drop the photo back into normal flow instead of a
     fixed/trapped position that would no longer have a 100vh box to anchor to */
  .how-it-works__left {
    height: auto;
  }

  .how-it-works__photo {
    position: relative;
    left: auto;
    top: auto;
    bottom: auto;
  }

  .how-it-works__photo img {
    height: auto;
    width: 100%;
    max-width: 100%;
  }

  .how-it-works__right {
    height: auto !important;
    overflow: visible;
    margin-top: clamp(2rem, 4vw, 3rem);
  }

  .how-it-works__track {
    position: static;
    transform: none !important;
  }
}

@media (max-width: 900px) {
  /* script.js's scroll-jack measure() sets an inline height on #howItWorks
     itself (.how-it-works), sized for the desktop sticky-viewport math —
     it has no idea this breakpoint drops that layout entirely, so left
     alone that stale, much-too-tall inline height was the real source of
     the huge empty gap before the booking section (not just the padding
     below, though that was real too — see .how-it-works__viewport). Same
     !important reset the reduced-motion branch already uses. */
  .how-it-works {
    height: auto !important;
  }

  /* --section-padding (up to 10rem/160px) is sized for full-bleed section
     breaks like the homepage's — reused here at mobile it stacks with
     .rental-booking's own top padding just below and reads as a big,
     unnaturally empty gap between the pricing copy and the booking form.
     A smaller, dedicated value instead, top and bottom. */
  .how-it-works__viewport {
    position: static;
    height: auto;
    padding: clamp(2.5rem, 8vw, 4rem) 0;
  }

  .how-it-works__grid {
    grid-template-columns: 1fr;
  }

  .how-it-works__left {
    height: auto;
    padding-left: var(--edge-padding);
    padding-right: var(--edge-padding);
  }

  /* Same reasoning as the reduced-motion override above — no sticky,
     100vh viewport to trap/anchor the photo to at this width, so it goes
     back into normal flow, full width, natural aspect ratio */
  .how-it-works__photo {
    position: relative;
    left: auto;
    top: auto;
    bottom: auto;
  }

  .how-it-works__photo img {
    height: auto;
    width: 100%;
    max-width: 100%;
  }

  .how-it-works__right {
    height: auto !important;
    overflow: visible;
    margin-top: clamp(2rem, 4vw, 3rem);
    padding: 0 var(--edge-padding);
  }

  .how-it-works__track {
    position: static;
    transform: none !important;
  }
}

/* ---------- Booking + waiver form (placeholder) and fake calendar ---------- */
.rental-booking {
  background: #fff;
  padding: var(--section-padding) var(--edge-padding);
}

.rental-booking__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2.5rem, 5vw, 5rem);
  align-items: start;
}

.rental-booking__form {
  margin-top: 2rem;
}

.rental-booking__step-heading {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: clamp(1.15rem, 2vw, 1.4rem);
  color: var(--color-text-dark);
  margin-bottom: 1.25rem;
}

.rental-booking__form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 1rem;
}

.rental-booking__divider {
  height: 1px;
  background: rgba(27, 23, 18, 0.14);
  margin-bottom: 2.5rem;
}

/* Sized to roughly match the old placeholder text + signature block's
   footprint, so this column stays close to its previous height and the
   sticky calendar (which measures its own column, not this one — see
   script.js) keeps behaving the same way. */
.rental-booking__waiver-embed {
  /* JotForm's welcome screen (PDF preview + "Start Filling" button) is
     vertically centered inside whatever height its iframe is given, so a
     short box pushes the button below the fold. Measured directly against
     the live form: the button needs ~687px of height to clear at a ~524px
     wide box (desktop), and up to ~1000px at the narrower widths this box
     takes on tablet/small-desktop or mobile — so 75rem (1200px) covers it
     with margin at every width this box actually renders at. */
  height: 75rem;
  border-radius: 0.6rem;
  overflow-y: auto;
  overflow-x: hidden;
  background: #eeece7;
  margin-bottom: 1.5rem;
}

.rental-booking__waiver-embed iframe {
  display: block;
  /* JotForm sets its own width/height inline via its resize script (and
     keeps re-setting it), so !important is needed to win here. min-height,
     not a fixed height, is deliberate: it guarantees the first (welcome)
     page is tall enough to show its button, WITHOUT capping later pages —
     JotForm's own resize script still grows the iframe taller than this
     floor for longer pages (guidelines, signature, etc). A fixed height
     here previously locked every page to the same size, and since the
     iframe has scrolling="no", any page whose real content ran taller than
     that fixed size became completely unreachable. */
  width: 100% !important;
  min-height: 100% !important;
  border: 0;
}

.rental-booking__checkbox {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0.5rem 0 1.75rem;
  font-size: 0.9rem;
  color: var(--color-muted-on-light);
  cursor: pointer;
}

.rental-booking__checkbox input {
  width: 1rem;
  height: 1rem;
  accent-color: var(--color-charcoal);
  cursor: pointer;
}

.rental-booking__calendar {
  margin-top: 2rem;
  background: #eeece7;
  border-radius: 1rem;
  overflow: hidden;
  /* Google's own default embed size is 800x600 (4:3) — matching that
     ratio here keeps the iframe's own content from looking stretched or
     squashed while still scaling responsively with the column width. */
  aspect-ratio: 4 / 3;
}

.rental-booking__calendar iframe {
  display: block;
  width: 100%;
  height: 100%;
}

/* `top` is set in px by script.js, computed so the sticky point lands
   exactly when this column (heading + calendar) is vertically centered
   in the viewport — i.e. the gap above it equals the gap below it —
   rather than an arbitrary fixed offset. Once stuck there, it holds that
   centered position while .rental-booking__form-col (the taller column,
   thanks to align-items: start above) keeps scrolling past it, releasing
   normally once the grid row itself runs out of height beneath it. */
.rental-booking__calendar-col {
  position: sticky;
}

@media (max-width: 900px) {
  /* Same reasoning as .how-it-works__viewport's mobile padding above —
     the full --section-padding on top of that section's own bottom
     padding stacked into one big, unnaturally empty gap right where the
     pricing copy hands off to the booking form. */
  .rental-booking {
    padding-top: clamp(2.5rem, 8vw, 4rem);
  }

  .rental-booking__grid {
    grid-template-columns: 1fr;
  }

  /* Stacked single column — the calendar sticking centered only makes
     sense beside a taller column it can be passed by */
  .rental-booking__calendar-col {
    position: static;
  }
}

/* ============================================================
   CONTACT
   ============================================================ */
/* First section on the page now, so it clears the transparent header.
   One prominent two-column card (photo + process summary on the left,
   the form on the right), sitting in the site's standard centered
   container on a near-white, faintly grey page background. */
.contact-body {
  background: #fff;
  padding: calc(var(--header-height) + clamp(1rem, 3vw, 2rem)) 0 var(--section-padding);
}

/* Two columns, not wrapped in the site's usual centered .section-inner —
   the left column keeps the standard edge padding so its text lines up
   with the header/logo, but the right column (the photo) gets a much
   smaller right-hand padding so it reaches out much closer to the browser
   edge than the rest of the site's content. A generous max-width keeps
   this from stretching absurdly wide on very large screens.
   The photo has no intrinsic height of its own (its img is absolutely
   positioned — see .contact-photo below), so this row's height comes
   entirely from .contact-column's content, and the photo simply stretches
   to match it: top edge level with the H1, bottom edge level with the
   form-note at the end of the form. */
.contact-layout {
  display: grid;
  grid-template-columns: minmax(0, 560px) 1fr;
  gap: clamp(2rem, 4vw, 4.5rem);
  align-items: stretch;
  max-width: 1800px;
  margin: 0 auto;
  padding-left: var(--edge-padding);
  padding-right: clamp(1rem, 2.5vw, 2rem);
}

.contact-column {
  display: flex;
  flex-direction: column;
}

.contact-intro .consult__intro {
  margin-top: 1.25rem;
  color: var(--color-muted-on-light);
}

/* Thin rule under the intro paragraph, spanning the same width as
   everything else in the column so it lines up with the form below it */
.contact-divider {
  margin-top: 1.5rem;
  height: 1px;
  background: rgba(27, 23, 18, 0.12);
}

/* Sits directly on the page background — no card, no border — placed
   underneath the intro paragraph with a natural gap above it. Shares the
   column's full width, same as the intro text above it, so both align on
   the same left/right edges. */
.contact-form {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
}

.contact-form__heading {
  font-size: clamp(1.3rem, 2vw, 1.6rem);
  margin-bottom: 1.5rem;
}

.contact-photo {
  position: relative;
  overflow: hidden;
}

.contact-photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The sauna itself sits in the lower half of this tall source photo,
     with mostly empty sky above — center-cropping (the default) was
     cutting into the sauna to keep showing that empty sky. Anchoring to
     the bottom keeps the actual subject in frame instead. */
  object-position: center bottom;
  display: block;
}

.form-field {
  margin-bottom: 1.15rem;
}

.form-field label {
  display: block;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  color: var(--color-muted-on-light);
  margin-bottom: 0.45rem;
}

/* Filled, rounded input boxes in a light neutral grey — distinct from the
   white page background (not the site's warm cream), no outline of their
   own, so they still read as "answer boxes" purely through that fill */
.form-field input,
.form-field select,
.form-field textarea {
  width: 100%;
  background: #eeece7;
  border: none;
  border-radius: 0.6rem;
  padding: 0.65rem 0.85rem;
  font-family: var(--font-sans);
  font-size: 0.95rem;
  /* !important — beats the site-wide 550 rule, which was making both the
     placeholder/existing text AND whatever gets typed in render bold
     (typed text just inherits the field's own font-weight, so this one
     declaration covers both) */
  font-weight: 400 !important;
  color: var(--color-text-dark);
  transition: background-color 0.3s ease;
}

.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus {
  outline: none;
  background: #e4e2dc;
}

.form-field select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 2rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%231b1712' stroke-opacity='.55'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.85rem center;
  cursor: pointer;
}

.form-field textarea {
  resize: vertical;
  min-height: 6rem;
  line-height: 1.6;
}

.contact-form__submit {
  width: 100%;
  margin-top: 0.5rem;
  cursor: pointer;
  border-radius: 0.6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
}

.contact-form__submit-arrow {
  transition: transform 0.3s ease;
}

.contact-form__submit:hover .contact-form__submit-arrow {
  transform: translateX(4px);
}

.form-note {
  margin-top: 1.1rem;
  font-size: 0.85rem;
  color: var(--color-muted-on-light);
}

.form-note a {
  border-bottom: 1px solid rgba(27, 23, 18, 0.25);
  transition: color 0.3s ease, border-color 0.3s ease;
}

.form-note a:hover {
  color: var(--color-text-dark);
  border-bottom-color: var(--color-text-dark);
}

/* ---------- Form submission modal (Contact page) ---------- */
.form-modal {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--edge-padding);
}

.form-modal[hidden] {
  display: none;
}

.form-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(23, 20, 15, 0.6);
}

.form-modal__box {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 26rem;
  background: var(--color-cream);
  border-radius: 1rem;
  padding: clamp(2rem, 4vw, 2.75rem);
  text-align: center;
  box-shadow: 0 20px 60px rgba(23, 20, 15, 0.35);
}

.form-modal__message {
  font-family: var(--font-sans);
  font-size: 1.05rem;
  line-height: 1.6;
  color: var(--color-text-dark);
}

.form-modal__close {
  margin-top: 1.75rem;
}

/* ============================================================
   SUBPAGE RESPONSIVE
   ============================================================ */
@media (max-width: 900px) {
  .values__row {
    grid-template-columns: 4rem 1fr;
  }

  .values__text {
    grid-column: 2;
  }

  /* Stack instead of side-by-side — text column on top, photo below,
     given its own height since it no longer inherits one from the row.
     Also drop the asymmetric edge padding used for the desktop bleed —
     stacked, both columns share one width and should match the site's
     usual symmetric edges. */
  .contact-layout {
    grid-template-columns: 1fr;
    padding-right: var(--edge-padding);
  }

  .contact-photo {
    min-height: 24rem;
    order: -1;
  }
}

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

  /* Un-mirror the reversed profile once everything stacks */
  .profile--reverse .profile__media {
    order: 0;
  }

  /* The stick-while-the-photo-scrolls-past effect only makes sense
     side-by-side — stacked, the text should just scroll normally */
  .profile__text {
    align-self: auto;
  }

  .profile__text-inner {
    position: static;
  }

  /* Horizontal work stage: consolidate photo placements so the pairs
     read as one upper-left / one lower-right photo on small screens.
     No `height` overrides here — each class keeps its aspect-ratio from
     the base rule above, so height is still derived from width, and
     nothing gets cropped on mobile either. */
  .hwork__img--center {
    width: 82vw;
    max-height: 44vh;
    left: 9vw;
    top: 28vh;
  }

  .hwork__img--center-tall {
    width: 62vw;
    max-height: 52vh;
    left: 19vw;
    top: 24vh;
  }

  .hwork__img--center-narrow {
    width: 52vw;
    max-height: 50vh;
    left: 24vw;
    top: 22vh;
  }

  /* No max-height here — these groups mix landscape (16:9-ish) and tall
     portrait (0.563/0.75) photos, and mobile viewports are generally tall
     enough (portrait orientation) that width-based sizing alone keeps every
     photo a safe, sane height without needing a ceiling. */
  .hwork__img--left-low,
  .hwork__img--left-high,
  .hwork__img--top-left-sm,
  .hwork__img--bottom-left-tall {
    left: -6vw;
    right: auto;
    top: 6vh;
    bottom: auto;
    width: 46vw;
  }

  .hwork__img--top-right-sm,
  .hwork__img--bottom-right,
  .hwork__img--right-low-tall,
  .hwork__img--top-right-wide {
    right: -6vw;
    left: auto;
    bottom: 8vh;
    top: auto;
    width: 46vw;
  }

  /* The four swapped-in photos above now fill their frame edge-to-edge
     (contain matches the box's ratio exactly, no letterbox padding left to
     absorb a bleed), so give these a flush 0 corner instead of the negative
     edge-bleed the groups above use — otherwise this cuts into real photo
     content instead of empty space. */
  .hwork__img--top-left-sm,
  .hwork__img--bottom-left-tall {
    left: 0;
  }

  .hwork__img--top-right-sm,
  .hwork__img--top-right-wide {
    right: 0;
  }
}

@media (max-width: 560px) {
  .values__row {
    grid-template-columns: 1fr;
    gap: 0.75rem;
  }

  .values__text {
    grid-column: auto;
  }
}
