/* ============================================================
   OYSTREMEDIA — GLOBAL SHARED STYLES
   ============================================================
   Loaded on EVERY page BEFORE the page's own <style> block, so
   page-scoped overrides always win by cascade order.

   Contains ONLY the truly shared rules:
     1. Design tokens (canonical vocabulary = solutions/component set)
     2. Scoped box-model + link safety for the shared partials
     3. Navbar (.nav*)
     4. Footer (footer, .footer-*, .signup-*)
     5. Contact form overlay (#form-overlay / #form-close / #form-frame)

   Per-page config points (override in the page's own <style>):
     --nav-z       nav stacking context   (default 100; homepage/about/contact: 200, projects: 95)
     --nav-gutter  nav horizontal padding (default var(--gutter) fluid clamp; the nav tracks the shared gutter on every page)

   Behavior config lives on <body data-*> and is read by
   /assets/js/global.js — see that file's header.
   ============================================================ */

/* ================================================
   1. DESIGN TOKENS
   ================================================ */
:root {
  /* Layout gutter — shared by navbar + footer + page sections.
     Fluid: 70px at ≥1440 (4.861vw = 70/1440), floors at 32px on
     desktop; the ≤768 block below steps it to the 20px mobile gutter.
     Replaces the old 70/56/32 steps — no more gutter jumps on zoom. */
  --gutter: clamp(32px, 4.861vw, 70px);

  /* --content-max-width (1520px cap) removed — the
     footer's model is the site standard: full width minus the fluid
     gutter at every viewport, on every page. */

  /* Ultra-wide cap (proportional-scaling system):
     content stops growing past 1920px and stays centered — the
     mouthwash.studio container model. Applied on .nav__container,
     .footer-inner and each page's section wrappers. Distinct from the
     removed --content-max-width: this only guards >1920 monitors and
     changes nothing at ≤1920. */
  --container-max: 1920px;

  /* Uniform white space below the homepage service panels
     (space between each Bento grid and its panel's bottom edge). */
  --section-spacing-block: 40px;

  /* Navbar tokens */
  --color-black:            #000000;
  --color-white:            #ffffff;
  --color-surface-light:    #f5f5f5;
  --color-surface-dark:     #111111;
  --font-family:            'Inter', sans-serif;
  --font-weight-medium:     500;
  --font-weight-semibold:   600;
  --font-size-nav:          15px;
  --font-size-contact:      14px;
  --line-height-nav:        24px;
  --line-height-contact:    22.5px;
  --nav-height:             80px;
  --nav-pad-y:              16px;
  --nav-logo-gap:           44px;
  --nav-link-gap:           10px;
  --nav-link-px:            12px;
  --nav-link-py:            4px;
  --nav-contact-px:         14px;
  --nav-contact-py:         6px;
  --nav-radius:             32px;
  --nav-logo-size:          80px;
  --nav-transition:         0.4s ease;

  /* Nav button blur — colorless frosted glass. Every button just blurs
     whatever is behind it: no fill, no tint, no border. Over flat areas the
     buttons read as plain text (the default look); over imagery/content the
     blur shows. */
  --nav-pill-blur:          12px;

  /* ================================================
     CANONICAL TYPE SCALE  (docs/typography-system.md)
     Consumed via CLASSES only — never bare h1/h2/p
     selectors — so the footer (class-scoped) is safe.
     Desktop values here; ≤768 overrides below.
     ================================================ */
  --type-display: clamp(48px, 6.5vw, 96px); /* hero + CTA */
  --type-h2:      clamp(40px, 4.9vw, 68px); /* section heading (ecosystem = reference) */
  --type-h3:      clamp(28px, 3.2vw, 40px); /* sub / group heading */
  --type-body:    18px;                     /* editorial body — floor, never below */
  --type-small:   15px;                     /* caption / supporting */
  --type-tag:     clamp(13px, 1vw, 16px);   /* eyebrow / label (Space Mono) */
}

/* 1439/1279 gutter steps removed — the clamp() above scales
   continuously between 70px and 32px instead of jumping. */
@media (max-width: 768px)  {
  :root {
    --gutter: 20px;
    /* Headings keep scaling via their own clamp() floors (48/40/28px) —
       continuous across 768, no boundary jump. Only the fixed-size roles
       step down on mobile. */
    --type-body:  16px;
    --type-small: 14px;
    --type-tag:   13px;
  }
}

/* ================================================
   2. SCOPED SAFETY RULES
   No global reset here — each page keeps its own.
   These only guarantee the shared partials render
   identically regardless of the host page's reset.
   ================================================ */
.nav, .nav *, .nav *::before, .nav *::after,
footer, footer *, footer *::before, footer *::after,
#form-overlay, #form-overlay * {
  box-sizing: border-box;
}

.nav a,
footer a {
  text-decoration: none;
  color: inherit;
}

/* Screen-reader-only. Used for the <h1> on pages whose visual headline is a
   video or a card deck (homepage, solutions) — the heading has to exist for
   crawlers and assistive tech without altering the design.

   Deliberately NOT display:none / visibility:hidden: those remove the element
   from the accessibility tree and are discounted by search engines. The
   clip-path + 1px box keeps it rendered but imperceptible. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ================================================
   3. NAVBAR
   ================================================ */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: var(--nav-z, 100);
  color: var(--color-black);
  /* opacity/transform/visibility kept in the transition so the scroll-up
     reveal animates on pages that don't define their own .nav transition
     (solutions, contact). index.php and projects.php override this. */
  transition:
    color      var(--nav-transition),
    opacity    0.32s ease,
    transform  0.32s ease,
    visibility 0.32s ease;
}

.nav--light { color: var(--color-white); }

/* Button text follows the theme (color: inherit below): black over light
   backdrops, white over dark ones — so a colorless blur stays legible in
   both, without any fill behind the text. */

.nav__wrapper {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 64px;
  padding: var(--nav-pad-y) 0;
}

.nav__container {
  display: flex;
  align-items: center;
  gap: var(--nav-logo-gap);
  width: 100%;
  /* full width minus the fluid gutter — same model as .footer-inner,
     so nav and footer edges align at every viewport; both cap together
     at --container-max on ultra-wide screens */
  max-width: var(--container-max);
  margin-inline: auto;
  max-height: var(--nav-height);
  padding: 0 var(--nav-gutter, var(--gutter));
}

.nav__logo-link {
  display: inline-flex;
  align-items: center;
}

.nav__logo-wrap { flex-shrink: 0; }

.nav__logo-img {
  display: block;
  width: 74px;
  height: auto;
  filter: invert(1);
  transition: filter var(--nav-transition);
}

.nav--light .nav__logo-img { filter: none; }

.nav__links {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--nav-link-gap);
  justify-content: flex-end;
  flex: 1;
}

/* ── Nav buttons: default plain-text links + a colorless blur. No fill, no
   tint, no border — each button just blurs whatever is behind it. Text
   follows the theme (color: inherit) so it stays legible over both light and
   dark backdrops. No active-page marker: the selected page carries only its
   .nav__link--active class (a DOM hook, no visual). ── */
.nav__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--nav-link-py) var(--nav-link-px);
  border-radius: var(--nav-radius);
  font-weight: var(--font-weight-medium);
  font-size: var(--font-size-nav);
  line-height: var(--line-height-nav);
  white-space: nowrap;
  color: inherit;
  backdrop-filter: blur(var(--nav-pill-blur));
  -webkit-backdrop-filter: blur(var(--nav-pill-blur));
  transition: opacity 0.2s ease;
}

@media (hover: hover) {
  .nav__link:hover { opacity: 0.65; }
}

.nav__contact {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--nav-contact-py) var(--nav-contact-px);
  border-radius: var(--nav-radius);
  overflow: hidden;
  font-weight: var(--font-weight-semibold);
  font-size: var(--font-size-contact);
  line-height: var(--line-height-contact);
  white-space: nowrap;
  /* Solid grey fill + white text — the one nav button that is not colorless.
     The blur stays on top of it, so it still picks up motion from whatever
     scrolls behind the semi-opaque edges. Explicit white (not `inherit`)
     because the light-theme pages would otherwise render black on grey. */
  background-color: #646464;
  color: #ffffff;
  backdrop-filter: blur(var(--nav-pill-blur));
  -webkit-backdrop-filter: blur(var(--nav-pill-blur));
  transition: opacity 0.2s ease;
}

@media (hover: hover) {
  .nav__contact:hover { opacity: 0.85; }
}

/* The blurred buttons ghost as pale rectangles while the nav fades out in
   Chrome (backdrop-filter + opacity/visibility animation). Dropping the
   filter for the duration of the hide kills the artifact; it re-applies the
   instant the class comes off, so the reveal is fully blurred again. */
.nav--hidden .nav__link,
.nav--hidden .nav__contact {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* Same reasoning for Contact's solid fill: left on, it would ride the fade
   out as a grey rectangle instead of ghosting away with the rest of the nav. */
.nav--hidden .nav__contact {
  background-color: transparent;
}

/* Shared hidden state — pages that already define their own .nav--hidden
   (index.php, projects.php) override this via source order; pages without one
   (solutions.php, contact.php) inherit it. */
.nav--hidden {
  opacity: 0;
  visibility: hidden;
  transform: translateY(-12px);
  pointer-events: none;
}

/* ── Mobile nav toggle (hamburger) — hidden on desktop.
   Oval translucent pill (mouthwash.studio-style): bars use currentColor so
   they flip with the nav--light theme automatically; the pill background
   flips alongside via the .nav--light override below. ── */
.nav__toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 6px;   /* even gap: 2+6+2 = 10px stack centers on a whole pixel in the
                 40px button, so both bars render crisp + identical thickness */
  width: 56px;
  height: 40px;
  padding: 0;
  border-radius: 999px;
  background-color: rgba(0, 0, 0, 0.06);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: none;
  cursor: pointer;
  color: inherit;
  transition: background-color var(--nav-transition), opacity 0.2s ease;
}

.nav--light .nav__toggle { background-color: rgba(255, 255, 255, 0.16); }

.nav__toggle:active { opacity: 0.75; }

.nav__toggle-bar {
  display: block;
  width: 18px;
  height: 2px;        /* both bars identical (lower bar is the reference) */
  flex: 0 0 auto;     /* never let flex resize either bar */
  border-radius: 1px;
  background: currentColor;
  transition: transform 0.3s ease, opacity 0.3s ease;
}

/* Open state: rotate the two bars into an ✕ around the button's center
   (stack is 2+6+2=10px tall, centered in the 40px button — 4px is exactly
   half that stack, so each bar's own center lands on the button's center). */
.nav__toggle.is-open .nav__toggle-bar:first-child {
  transform: translateY(4px) rotate(45deg);
}
.nav__toggle.is-open .nav__toggle-bar:last-child {
  transform: translateY(-4px) rotate(-45deg);
}

/* While the mobile menu is open: lift the whole nav above the overlay
   (9999) so the pill stays visible and clickable as the close control,
   force the light pill background for contrast against the overlay's
   #050505, and hide the main nav's own logo — the overlay ships its own
   (m-menu__top), so this avoids a duplicate mark and any flicker from the
   scroll-driven .nav--logo-hidden state while scroll is locked. */
body.menu-open .nav {
  z-index: 10000;
}
body.menu-open .nav__toggle {
  background-color: rgba(255, 255, 255, 0.16);
}
body.menu-open .nav__logo-link {
  opacity: 0;
  pointer-events: none;
}

@media (max-width: 768px) {
  .nav__links { display: none; }
  .nav__toggle { display: inline-flex; }
  /* Logo left, burger right, identical 20px gutter on EVERY page. Set as an
     explicit value (not var(--nav-gutter)) so per-page --nav-gutter:70px pins
     can't leak onto mobile via source order. */
  .nav__container {
    justify-content: space-between;
    gap: 0;
    padding-left: 20px;
    padding-right: 20px;
  }

  /* Logo fades out once scrolling starts — the toggle (always reachable,
     module 9 in global.js) stays; class is set on #main-nav. */
  .nav__logo-link {
    transition: opacity 0.3s ease, transform 0.3s ease;
  }
  .nav--logo-hidden .nav__logo-link {
    opacity: 0;
    transform: translateY(-6px);
    pointer-events: none;
  }
}

/* ================================================
   3b. MOBILE MENU OVERLAY (.m-menu)
   Full-screen menu opened by .nav__toggle ≤768px.
   Markup ships in components/navbar/navbar.php;
   behavior in /assets/js/global.js.
   Each link carries its service accent via --m-link-color.
   ================================================ */
.m-menu {
  position: fixed;
  inset: 0;
  z-index: 9999;             /* above every page layer, below #form-overlay (100000) */
  display: flex;
  flex-direction: column;
  background: #050505;
  padding:
    calc(env(safe-area-inset-top, 0px) + 18px)
    var(--gutter)
    calc(env(safe-area-inset-bottom, 0px) + 34px);
  transform: translateY(-102%);
  visibility: hidden;
  transition: transform 0.5s cubic-bezier(0.7, 0, 0.2, 1), visibility 0s linear 0.5s;
  /* safety net: on short viewports (small phones, landscape) the stack can
     exceed 100dvh — scroll rather than clip Contact off the bottom edge */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.m-menu.open {
  transform: translateY(0);
  visibility: visible;
  transition: transform 0.5s cubic-bezier(0.7, 0, 0.2, 1);
}

/* Overlay only exists on mobile — never paints on desktop even if .open sticks */
@media (min-width: 769px) {
  .m-menu { display: none; }
}

.m-menu__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 44px;
}

.m-menu__logo img {
  display: block;
  width: 74px;
  height: auto;
  -webkit-user-drag: none;
  user-select: none;
}

/* Links sit high (below the logo row); Contact is pushed toward the bottom
   (margin-top:auto) but the sizing below is deliberately conservative so
   all 6 links + Contact fit within a small phone's viewport without relying
   on the .m-menu overflow-scroll fallback above. */
.m-menu__links {
  margin-top: clamp(24px, 6vh, 64px);
  display: flex;
  flex-direction: column;
  flex: 1;
}

.m-menu__link {
  font-family: 'Anton', sans-serif;   /* display voice — matches the site's section headings */
  font-weight: 400;                   /* Anton ships in 400 only */
  font-size: clamp(34px, 10.5vw, 54px);
  line-height: 1.06;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: #ffffff;
  padding: 6px 0;
  /* entrance fade/slide staggered below */
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity 0.5s ease,
    transform 0.5s cubic-bezier(0.16, 1, 0.3, 1),
    color 0.25s ease;
}

.m-menu.open .m-menu__link { opacity: 1; transform: none; }
.m-menu.open .m-menu__link:nth-child(1) { transition-delay: 0.12s; }
.m-menu.open .m-menu__link:nth-child(2) { transition-delay: 0.17s; }
.m-menu.open .m-menu__link:nth-child(3) { transition-delay: 0.22s; }
.m-menu.open .m-menu__link:nth-child(4) { transition-delay: 0.27s; }
.m-menu.open .m-menu__link:nth-child(5) { transition-delay: 0.32s; }
.m-menu.open .m-menu__link:nth-child(6) { transition-delay: 0.37s; }

/* Press feedback + current-page state — each link lights up in its own
   service accent (--m-link-color ships inline in navbar.php) */
.m-menu__link.is-lit,
.m-menu__link:active { color: var(--m-link-color, #ffffff); }
.m-menu__link.is-active { color: var(--m-link-color, #ffffff); opacity: 0.85; }

/* Contact = the bottom CTA slot */
.m-menu__link--contact { margin-top: auto; }

@media (prefers-reduced-motion: reduce) {
  .m-menu { transition: none; }
  .m-menu__link { transition: opacity 0.18s ease; opacity: 1; transform: none; }
}

/* ================================================
   4. FOOTER (global component)
   Pinned behind .page-content (z 0 vs z 2) —
   revealed as the page scrolls past its end.
   ================================================ */
footer {
  position: sticky;
  bottom: 0;
  z-index: 0;
  width: 100%;
  background: var(--color-surface-dark);
  overflow: hidden;
  padding-top: 40px;
  color: #fff;
  margin-top: 0;
  display: block;
  font-family: 'Inter', sans-serif;
}

/* Footer graphics: clickable via their <a>, but never draggable or selectable.
   The inner graphic is pointer-locked (so a drag can't grab/ghost it); the
   wrapping links are re-enabled below so social + nav arrows still click. */
footer img {
  -webkit-user-drag: none;
  user-drag: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  pointer-events: none;
}
.footer-profiles a,
.footer-nav a { pointer-events: auto; }

/* Fallback: a footer taller than the viewport would get its top cut off
   by the pin-reveal — JS switches it back into normal flow */
footer.footer--flow { position: static; }

/* Entrance fade — .visible is added by the footer-reveal JS once uncovered */
footer [data-reveal] {
  opacity: 0;
  transform: translateY(36px);
  transition: opacity .72s ease, transform .72s ease;
}
footer [data-reveal][data-delay="1"] { transition-delay: .1s; }
footer [data-reveal].visible { opacity: 1; transform: none; }

/* No-JS fallback for this rule (.visible is only ever added by script) ships
   as a <noscript><style> override in components/head-meta.php's <head>. */

.footer-inner {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding: 0 var(--gutter);
}

.footer-main {
  display: flex;
  align-items: flex-start;
}

.footer-left {
  width: 45%;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
}

.footer-intro {
  display: flex;
  flex-direction: column;
  gap: clamp(18px, 2.4vw, 31px);
  padding-top: 6px;
}

.footer-heading {
  font-size: clamp(26px, 3.56vw, 46.3px);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1;
  color: #fff;
}

.footer-email {
  font-size: clamp(18px, 2.9vw, 37.7px);
  font-weight: 500;
  letter-spacing: -0.025em;
  line-height: 1;
  color: #fff;
  display: inline-block;
  transition: opacity 0.25s;
  text-decoration: none;
}
@media (hover: hover) {
  .footer-email:hover { opacity: 0.7; }
}

.footer-profiles {
  margin-top: clamp(20px, 2vw, 25px);
  padding-top: clamp(12px, 1.5vw, 20px);
  display: flex;
  width: 200px;
  justify-content: space-between;
  align-items: center;
}

.footer-profiles a {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  transition: opacity 0.25s;
  color: inherit;
  text-decoration: none;
}
@media (hover: hover) {
  .footer-profiles a:hover { opacity: 0.5; }
}

.footer-profiles img { width: 100%; height: 100%; display: block; }

.footer-right {
  flex: 1;
  display: flex;
  align-items: flex-start;
}

.footer-nav {
  flex: 0 0 clamp(160px, 20vw, 264px);
  display: flex;
  flex-direction: column;
  gap: clamp(14px, 1.54vw, 20px);
}

.footer-nav a {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: clamp(13px, 1.15vw, 15px);
  font-weight: 500;
  color: #fff;
  line-height: 1;
  white-space: nowrap;
  transition: opacity 0.25s;
  text-decoration: none;
}
@media (hover: hover) {
  .footer-nav a:hover { opacity: 0.55; }
}

.footer-nav .arrow { width: 10px; height: 11px; flex-shrink: 0; display: block; }

.footer-signup {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 2.46vw, 32px);
}

.signup-label {
  font-size: clamp(13px, 1.18vw, 15.3px);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1;
  color: #fff;
  white-space: nowrap;
}

.signup-body {
  display: flex;
  flex-direction: column;
  gap: clamp(16px, 1.85vw, 24px);
}

.signup-text {
  font-size: clamp(13px, 1.18vw, 15.4px);
  font-weight: 500;
  line-height: 1.45;
  color: #fff;
}

.signup-form {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid #fff;
  height: 42px;
  padding-bottom: 1px;
  transition: border-color 0.25s;
}
.signup-form:focus-within { border-color: rgba(255,255,255,0.7); }

.signup-form input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  color: #fff;
  font-family: 'Inter', sans-serif;
  font-size: clamp(13px, 1.19vw, 15.5px);
  font-weight: 500;
  padding: 11px 0;
  line-height: normal;
}
.signup-form input::placeholder { color: rgba(255,255,255,0.5); }

.signup-form button {
  background: transparent;
  border: none;
  color: #fff;
  font-family: 'Inter', sans-serif;
  font-size: 16px;
  font-weight: 500;
  padding: 11px 0 11px 15px;
  cursor: pointer;
  transition: opacity 0.25s;
}
@media (hover: hover) {
  .signup-form button:hover { opacity: 0.55; }
}

.signup-form button:disabled { opacity: 0.4; cursor: default; }

.signup-status {
  min-height: 1.3em; /* reserve the line so text appearing doesn't shift layout */
  font-size: 12.5px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.6);
}
.signup-status[data-state="error"] { color: #ff8a8a; }
.signup-status[data-state="ok"]    { color: #fff; }

.footer-bottom {
  margin-top: 80px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-bottom: 0;
}

.footer-copyright {
  font-size: clamp(12px, 1.23vw, 16px);
  font-weight: 400;
  color: #f7f7f7;
  letter-spacing: 0.373px;
}

.footer-legal {
  display: flex;
  gap: clamp(16px, 1.92vw, 25px);
  align-items: center;
}

.footer-legal a {
  font-size: clamp(12px, 1.23vw, 16px);
  font-weight: 400;
  color: #f7f7f7;
  letter-spacing: 0.373px;
  transition: opacity 0.25s;
  text-decoration: none;
}
@media (hover: hover) {
  .footer-legal a:hover { opacity: 0.7; }
}

.footer-wordmark {
  position: relative;
  width: 100%;
  overflow: hidden;
  height: 0;
  padding-bottom: 13%;
  font-size: 0;
  line-height: 0;
  margin-top: 40px;
}

.footer-wordmark-inner {
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  display: flex;
  align-items: flex-start;
}

.footer-wordmark-inner img { width: 100%; height: auto; flex-shrink: 0; display: block; opacity: 0.6; }

/* ── Footer responsive ──
   860 + 600 queries folded onto the structural 768 breakpoint:
   above 768 the two-column footer scales fluidly via its
   clamp() type/gaps; at ≤768 everything stacks in one step. */
@media (max-width: 768px) {
  .footer-main { flex-direction: column; gap: clamp(40px, 7vw, 56px); }
  .footer-left  { width: 100%; }
  .footer-right { width: 100%; flex-direction: column; gap: clamp(36px, 8vw, 48px); }
  .footer-nav { flex: none; width: 100%; }
  .footer-bottom { flex-direction: column; align-items: flex-start; gap: 16px; }
}

/* ── Mobile footer — clean stacked layout (≤768) ──
   No markup change: the shared footer partial is re-laid-out via CSS.
   Reading order stays: contact CTA → sitemap → newsletter → meta →
   full-bleed wordmark anchored flush to the bottom edge.
   Compact left-arrow nav, no hairline dividers (matches approved mockup). */
@media (max-width: 768px) {
  footer { padding-top: 44px; }

  .footer-main { gap: 36px; }

  /* 1 · Contact CTA — the editorial hero of the footer (no divider) */
  .footer-left {
    padding-bottom: 24px;
  }
  .footer-intro { gap: 14px; }
  .footer-heading { font-size: clamp(30px, 9vw, 40px); }
  .footer-email  { font-size: clamp(20px, 5.6vw, 24px); }

  /* socials sit under the email, packed left near the LinkedIn icon (not
     stretched full-width); ~44px tall tap target via vertical padding, 24px icon */
  .footer-profiles { width: 100%; justify-content: flex-start; gap: 18px; margin-top: 22px; margin-left: 0; }
  .footer-profiles a { width: 24px; height: 24px; padding: 10px 0; box-sizing: content-box; }
  .footer-profiles img { width: 24px; height: 24px; }

  /* 2 · Sitemap — compact list, arrow-left before label, no dividers */
  .footer-right { gap: 44px; }
  .footer-nav { gap: 13px; width: 100%; }
  .footer-nav a {
    align-items: center;
    gap: 8px;
    padding: 0;
    font-size: 18px;
    font-weight: 500;
  }
  .footer-nav a:active { opacity: 0.55; }
  .footer-nav .arrow { width: 12px; height: 13px; opacity: 1; }

  /* 3 · Newsletter — ≥16px input keeps iOS from auto-zooming on focus */
  .signup-form input { font-size: 16px; }

  /* 4 · Meta row — copyright + legal above the wordmark (no divider).
     Legal row spans full width: Privacy Policy left, Terms & Agreement right. */
  .footer-bottom {
    margin-top: 44px;
  }
  .footer-legal { width: 100%; justify-content: space-between; }

  /* 5 · Full-bleed wordmark — the signature anchor. Breaks the gutter to
     span edge-to-edge and sits flush against the bottom of the screen. */
  .footer-wordmark {
    width: calc(100% + 2 * var(--gutter));
    margin-left: calc(-1 * var(--gutter));
    margin-top: 48px;
    margin-bottom: 0;
    padding-bottom: 14%;
  }
  .footer-wordmark-inner img { opacity: 0.9; }
}

/* ================================================
   5. CONTACT FORM OVERLAY
   ================================================ */
#form-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 100000;
  background: #0a0a0a;
}

#form-overlay.open {
  display: block;
  animation: overlayReveal 0.45s cubic-bezier(0.25, 1, 0.5, 1) both;
}

@keyframes overlayReveal {
  from { opacity: 0; transform: scale(1.015); }
  to   { opacity: 1; transform: scale(1); }
}

#form-close {
  position: fixed;
  top: 22px;
  right: 26px;
  background: rgba(255,255,255,0.07);
  border: 1px solid rgba(255,255,255,0.16);
  color: rgba(255,255,255,0.85);
  font-family: 'Inter', sans-serif;
  font-size: 1rem;
  font-weight: 400;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  cursor: pointer;
  z-index: 100001;
  display: flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s cubic-bezier(0.25,1,0.5,1), color 0.2s ease;
  line-height: 1;
}

@media (hover: hover) {
  #form-close:hover {
    background: rgba(255,255,255,0.14);
    border-color: rgba(255,255,255,0.3);
    color: #fff;
    transform: scale(1.1);
  }
}

#form-frame {
  width: 100%;
  height: 100%;
  border: none;
  display: block;
}

@media (max-width: 768px) {
  /* clear the iPhone notch / status bar */
  #form-close {
    top: calc(env(safe-area-inset-top, 0px) + 14px);
    right: 16px;
  }
}

/* ================================================
   6. PAGE TRANSITIONS — papertiger-style sheet
   Exit sheet + dim are injected by global.js module 13;
   the enter cover class/colour is stamped pre-paint by
   components/page-boot.php. Every layer animates
   transform/opacity only (compositor-friendly — zero
   layout work, desktop + mobile identical cost).
   ================================================ */

/* Depth cue on the OUTGOING page: a scrim dims it beneath the rising
   sheet. Deliberately NOT a scale-down — a transform on <body> would
   detach the fixed nav and every pinned/sticky set piece mid-animation. */
.page-exit-dim {
  position: fixed; inset: 0; z-index: 100001;
  background: #000; opacity: 0;
  pointer-events: none;
  transition: opacity .55s ease;
}
.page-exit-dim.on { opacity: .30; }

/* The sheet itself — tinted inline (global.js) with the DESTINATION
   page's opening background. Rounded top corners flatten as it docks.
   overflow:hidden clips the wordmark to the rounded corners. */
.page-exit-sheet {
  position: fixed; left: 0; right: 0; top: 0; bottom: -2px;
  z-index: 100002;
  transform: translateY(103%);
  border-radius: 24px 24px 0 0;
  overflow: hidden;
  box-shadow: 0 -20px 60px rgba(0, 0, 0, .35);
  transition: transform .58s cubic-bezier(.76, 0, .24, 1), border-radius .45s ease .18s;
  will-change: transform;
}
.page-exit-sheet.up { transform: translateY(0); border-radius: 0; }

/* Footer signature on the sheet: the OYSTRE wordmark rides the sheet's
   bottom edge with the SAME horizontal margins as the footer's own
   anchor — gutter inset on both sides, the box capped at --container-max
   and centered (i.e. the .footer-inner content width minus its 2×gutter
   padding). It appears as the sheet rises and hands off to the enter
   cover's identical mark on the next page. The SVG artwork is light
   (footer default); light sheets invert it, mirroring .nav__logo-img. */
.page-exit-sheet__mark {
  position: absolute;
  left: 50%; bottom: 0;
  transform: translateX(-50%);
  width: min(100% - 2 * var(--gutter), var(--container-max) - 2 * var(--gutter));
  height: auto;
  display: block;
  opacity: .6;
  pointer-events: none;
  -webkit-user-drag: none;
  user-select: none;
}
.page-exit-sheet--light .page-exit-sheet__mark { filter: invert(1); opacity: .5; }

/* Enter cover — html.page-enter is set BEFORE first paint, so the new
   page opens already covered in its own opening colour (the hand-off
   from the exit sheet is invisible). Two stacked layers that lift
   together: ::before = the colour sheet, ::after = the same bottom-
   anchored OYSTRE wordmark as the exit sheet (both are full-viewport
   boxes so translateY(-103%) moves them in lockstep). .page-enter-lift
   sends them away upward, revealing the page bottom-first. */
html.page-enter::before,
html.page-enter::after {
  content: '';
  position: fixed; left: 0; right: 0; top: -2px; bottom: 0;
  transform: translateY(0);
  transition: transform .64s cubic-bezier(.76, 0, .24, 1), border-radius .4s ease;
  will-change: transform;
}
html.page-enter::before {
  z-index: 100002;
  background: var(--page-enter-bg, #111111);
}
html.page-enter::after {
  z-index: 100003;
  background-image: url('/assets/icons/oystre-wordmark.svg');
  background-repeat: no-repeat;
  /* same horizontal margins as the footer wordmark: gutter inset both
     sides, capped at --container-max, centered — anchored to the bottom */
  background-position: center bottom;
  background-size: min(100% - 2 * var(--gutter), var(--container-max) - 2 * var(--gutter)) auto;
  opacity: .6;
  pointer-events: none;
}
html.page-enter--light::after { filter: invert(1); opacity: .5; }
html.page-enter.page-enter-lift::before,
html.page-enter.page-enter-lift::after {
  transform: translateY(-103%);
  border-radius: 0 0 26px 26px;
}
html.page-enter.page-enter-lift::before {
  box-shadow: 0 24px 60px rgba(0, 0, 0, .25);
}

/* ≤768: the footer wordmark goes full-bleed (breaks the gutter, edge to
   edge) — match it so the transition mark's margins track the footer at
   this breakpoint too. */
@media (max-width: 768px) {
  .page-exit-sheet__mark { width: 100%; }
  html.page-enter::after { background-size: 100% auto; }
}

@media (prefers-reduced-motion: reduce) {
  .page-exit-dim,
  .page-exit-sheet,
  html.page-enter::before,
  html.page-enter::after { display: none; }
}

/* ============================================================
   LENIS SMOOTH SCROLL (desktop only)
   ============================================================
   These rules apply ONLY while Lenis is running: the library adds
   `lenis lenis-smooth` to <html> on init (desktop, pointer:fine,
   ≥769px) and strips them on destroy. `scroll-behavior:auto` with
   !important neutralises any page-level `html{scroll-behavior:smooth}`
   (e.g. projects.php) while Lenis owns scrolling, so the two engines
   never fight — and native smooth is restored the moment Lenis is off
   (mobile / reduced-motion). Opt out per-element with
   `data-lenis-prevent` on internally-scrollable panels.
   ============================================================ */
html.lenis,
html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: hidden; }
.lenis.lenis-smooth iframe { pointer-events: none; }
