/* Self-hosted Alte Haas Grotesk Bold for the JACKFISH wordmark. It used to work
   only on machines that had the font INSTALLED (like the laptop this site is
   built on); phones fell back to a substitute. Serving the file makes the logo
   render the same everywhere. Latin subset only (the wordmark is plain caps). */
@font-face {
  font-family: 'Alte Haas Grotesk';
  src: url('../assets/fonts/AlteHaasGrotesk-Bold.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: 'Fustat';
  src: url('../assets/fonts/Fustat-Variable.woff2') format('woff2-variations'),
       url('../assets/fonts/Fustat-Variable.woff2') format('woff2');
  font-weight: 200 800;
  font-style: normal;
  font-display: block; /* preloaded in each page's <head>, so it arrives before first paint; block avoids the visible fallback-to-Fustat swap */
}

/* =========================================================
   Jackfish Creative, Design Tokens
   Pulled from Figma (file: Jackfish, node 43:37 "Nav Bar")
   ========================================================= */
:root {
  /* Colors, brand palette. Text everywhere (headings AND body copy) uses
     --color-text (dark brown), never black/charcoal, --color-text-body
     used to be a separate #000000 token for body copy specifically; that's
     gone now, body copy just uses --color-text like everything else. */
  --color-bg: #fffefa;
  --color-text: #291c17;
  --color-text-mid: #423028; /* secondary brown from the palette, not yet used anywhere specific, available for muted/secondary text */
  --color-cream: #ebe2d7; /* palette's beige/cream, used where light text needs to sit on a dark-brown background (e.g. the hero) */
  --color-bone-light: #f9f5ef; /* Light Bone: about 70% of the way from Bone to the page White. Let's Build card background. Added Oct 12, 2026. */
  --color-text-muted: #7a685d; /* lighter brown for tertiary text like "starting at"; 5.25:1 on white, so it still passes */
  --color-cream-muted: #a89a8c; /* a step down from Bone, for small labels on Dark; 6.0:1 */
  --color-surface: #f6f6f6; /* card/box background on the light page */
  --color-accent: #ff5524;
  --color-accent-text: var(--color-bg);

  /* Typography */
  --font-heading: 'Alte Haas Grotesk', sans-serif; /* the wordmark font, self-hosted above. Titles only. */
  --font-body: 'Fustat', -apple-system, 'SF Pro Text', sans-serif; /* self-hosted above; system fonts only if it fails to load */

  /* Whole scale stepped down one notch on Oct 11, 2026 (was 400/500/600/700). */
  --font-weight-regular: 300;
  --font-weight-medium: 400;
  --font-weight-semibold: 500;
  --font-weight-bold: 600;
  /* Oct 11, 2026: tracking loosened site-wide (was -4% / -2%), body leading 1.35 -> 1.4, subtitle 1.2 -> 1.25. Display (hero) tracking is unchanged. */
  --tracking-tight: -0.01em; /* -4% letter-spacing, applied to headings/body copy that use --font-body (not the logo, which keeps its own Figma-specific tracking) */
  --tracking-loose: 0; /* used only by --text-body, still tight by normal standards, but noticeably looser than --tracking-tight, which reads as cramped at this small a size */

  /* DISPLAY (hero only), set by one rule: the longest rotator word
     ("custom home", 6.047em wide at weight 700 / -0.04em tracking, measured)
     spans 75% of the viewport on phones and 50% from 768px up, capped at
     the 1440px value so it stops growing on very wide screens.
     phones: 0.75 / 6.047 = 12.4vw.  768px+: 0.50 / 6.047 = 8.27vw (max 119px). */
  --text-display: 12.4vw;
  --leading-display: 0.95;
  --tracking-display: -0.04em;

  --text-heading-lg: clamp(2.25rem, 1.786rem + 1.905vw, 3.5rem); /* 36 -> 56px, large section titles ("The Blueprint"). Set Oct 4, 2026. */
  --leading-heading-lg: 1.1;
  --text-heading: clamp(2rem, 1.629rem + 1.524vw, 3rem); /* 32 -> 48px, section titles */
  --leading-heading: 1.1;
  --tracking-heading: -0.01em; /* headings, subtitles, titles, nav links, buttons: same as the original site */
  --tracking-body: 0; /* body copy: card text, captions. Same as the original --tracking-loose. */
  --text-subtitle: clamp(1.5rem, 1.314rem + 0.762vw, 2rem); /* 24 -> 32px, subheads */
  --leading-subtitle: 1.25;
  --text-title: clamp(1.25rem, 1.157rem + 0.381vw, 1.5rem); /* 20 -> 24px, card titles */
  --leading-title: 1.25;
  --text-body: clamp(1rem, 0.954rem + 0.19vw, 1.125rem); /* 16 -> 18px, body, nav links, buttons, inputs */
  --leading-body: 1.4; /* was 1.5. Short blocks (cards) read better tighter; 1.35 is still inside the standard 1.3-1.5 range. */
  --text-statement: 1.375rem; /* 22px on phones; from 768px up it is the subtitle size (set below). The dark brown statement strip. */
  --leading-statement: 1.25;
  --text-tag: 0.875rem; /* 14px flat, tags, labels, captions. */
  --leading-tag: 1.4;
  --text-micro: 0.75rem; /* 12px flat, the floor. Pills and badges only, never real reading text. */
  --leading-micro: 1.3;

  /* Spacing: 8-point grid (4 allowed for tight pairs). Stepped, not fluid,
     so every value stays on the grid. Larger values from 768px up (below).

     SITE SPACING RULES (all pages, Oct 12, 2026): use these tokens, no literal px.
     - Page side margin ........................ --space-inline
     - Top of a page (first section) ........... calc(var(--nav-height) + var(--space-xl))  (home hero adds --space-hero-top instead)
     - White section between two coloured ones . --space-section top and bottom
     - Two white sections in a row ............. --space-xl between them (top padding on the second only)
     - Dark brown / orange strips and footers .. --space-xl top and bottom
     - Gaps inside a section ................... --space-lg between blocks, --space-md or --space-sm inside a block,
                                                 --space-xs / --space-2xs for label and icon pairs
     Allowed exceptions: optical nudges in em (logo, overlays), and a few 4-point component values (nav pill, buttons). */
  --space-2xs: 4px;
  --space-xs: 8px;
  --space-heading-gap: 8px;
  --space-sm: 16px;
  --space-md: 24px;
  --space-lg: 32px;
  --space-xl: 48px;
  --space-inline: 16px; /* page side margin */
  --space-section: 64px; /* vertical padding of a section */
  --space-hero-top: 260px; /* gap from the nav to the hero headline (phones). Off the 8px grid on purpose, set by eye. */

  --space-nav-pad: 16px; /* nav top/bottom padding (sides use --space-inline) */
  --space-menu-gap: 32px;
  --logo-width: 88px; /* wordmark width, matches the Figma phone frame */
  --fish-width: 80px; /* size of a dropped fish */
  /* Motion: one language for the whole site. Same curve everywhere, three
     durations, tiny distances. Only opacity and small movement. */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --dur-fast: 0.2s;
  --dur-base: 0.5s;
  --dur-slow: 0.8s;
  --reveal-distance: 12px;
  --stagger: 70ms;
  --tap-target: 44px; /* minimum size of anything tappable (Apple 44; WCAG AA floor is 24) */
  --content-max-width: 1440px;

  /* Radius */
  --radius-btn: 8px;
  --radius-card: 6px; /* cards, panels, image tiles: stepped down from 8px on Oct 11, 2026 */
  --radius-card-inner: var(--radius-card); /* a card nested inside a panel: same radius, so every card and image matches */
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--color-bg);
  font-family: var(--font-body);
  color: var(--color-text);
}

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

/* In-page links (nav and footer "Projects" on the home page) glide to their section
   instead of jumping. Off for visitors who prefer reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  font-size: var(--text-body);
  line-height: var(--leading-body);
  letter-spacing: var(--tracking-body);
  font-weight: var(--font-weight-medium); /* body copy default */
}

h1, h2, h3 { text-wrap: balance; }
p { max-width: 66ch; } /* 50-75 characters per line is the readable measure */

:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

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

/* Read by screen readers and search engines, not shown on screen. */
.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;
}

/* Anchor targets for in-page nav links (Projects, plus the project pages'
   "Back to Projects" links) sit right under the fixed nav, without this,
   jumping to them via hash leaves their top edge hidden behind the pill
   nav. */
#projects,
#projects-feature {
  scroll-margin-top: calc(var(--nav-height, 88px) + var(--space-lg, 24px));
}

/* ===== Nav Bar =====
   .nav-wrap is fixed and full-width purely to center .navbar; it has no
   visual styling of its own and ignores pointer events so it never blocks
   clicks on the page content beside a narrower pill nav. */
.nav-wrap {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  display: flex;
  justify-content: center;
  pointer-events: none;
}

.navbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-nav-pad) var(--space-inline);
  background: transparent; /* painted by .navbar::before so the pill can have a rough edge */
  pointer-events: auto;
  /* Positioning context for the mobile dropdown menu (.menu, once it's
     absolutely positioned at ≤700px, see the mobile breakpoint below). */
  position: relative;
  isolation: isolate;
  width: 100%;
  max-width: 100%;
  margin-top: 0;
  border-radius: 0;
  box-shadow: none;
  /* Scroll-driven "traditional bar -> floating pill" morph, JS (main.js)
     toggles .is-pill once you scroll past a small threshold. Transitioning
     these specific properties (not a transform) keeps the layout, and
     --nav-height, which the hero section depends on, accurate throughout
     the animation, not just at the two end states. */
  transition: max-width 0.4s cubic-bezier(0.22, 1, 0.36, 1),
              margin-top 0.4s cubic-bezier(0.22, 1, 0.36, 1),
              padding 0.4s cubic-bezier(0.22, 1, 0.36, 1),
              border-radius 0.4s cubic-bezier(0.22, 1, 0.36, 1),
              box-shadow 0.4s ease;
}

.navbar::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: var(--color-bg);
}

.navbar.is-pill {
  max-width: 900px;
  margin-top: var(--space-sm);
  /* Uniform on top/bottom/right (12px). Left gets a touch more (20px) as an
     optical correction: the bold "JACKFISH" wordmark's own letter-spacing/
     bearing made equal padding read as tighter on the left than the same
     amount felt on the right side. This is padding-only, so the bar's
     total width (capped by max-width) and its centering within .nav-wrap
     are unaffected. */
  padding: 12px 20px; /* 20px left and right so the logo and the menu icon sit the same distance from the pill edges; 12px top and bottom (4-point grid) */
  /* 20px instead of a full pill (999px), rounded enough to read as its own
     floating shape, but closer to the 8px radius used on buttons/cards
     elsewhere, so it feels like the same design language rather than an
     unrelated pill shape that clashes with the squarer "Build My Brand"
     button sitting right inside it. */
  border-radius: 20px;
  /* Softer, closer shadow, the previous one read as a harsh dark edge on
     the white-on-white nav; this is lower-opacity and tighter to the bar
     so it registers as a subtle lift rather than an obvious hard outline. */
  filter: drop-shadow(0 2px 5px rgba(41, 28, 23, 0.07)); /* drop-shadow follows the clipped shape, a box-shadow would not */
}

/* Desktop pill: 12px above, below and to the right of the Get started button, 20px on the
   logo side. 12px is the pill's 20px corner radius minus the button's 8px, so the pill's
   curve runs concentric with the button's corner and wraps it evenly. Phones keep the
   even 20px sides, since the hamburger icon sits on the right there. */
@media (min-width: 768px) {
  .navbar.is-pill {
    padding: 12px 12px 12px 20px;
  }
}

@media (max-width: 900px) {
  .navbar.is-pill {
    /* Base .navbar is width:100% always; max-width:900px above is what
       normally keeps the pill from touching the screen edges, but that
       only works while the viewport is WIDER than 900px (so the max-width
       is the binding constraint rather than the 100%). Below 900px,
       without this, the pill reverts to plain edge-to-edge width with no
       margin at all, this needs to cover the exact same threshold where
       max-width stops helping, or there's a gap band in between where
       neither rule provides any margin (which is what was happening when
       this used to only kick in at 700px, leaving 700-900px with none). */
    width: calc(100% - 48px); /* 24px of page showing on each side, a little inside the 16px content margin */
  }
}

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

.logo {
  display: flex;
  align-items: center;
  min-height: var(--tap-target);
  margin: 0;
}

.logo img {
  display: block;
  width: var(--logo-width);
  height: auto;
}

.menu {
  display: flex;
  align-items: center;
  gap: var(--space-menu-gap);
}

.nav-hamburger {
  display: none; /* shown only ≤700px, see mobile breakpoint below */
  flex-direction: column;
  justify-content: center;
  align-items: flex-end; /* bars sit flush with the right margin; the tap area extends left/up/down */
  gap: var(--space-2xs);
  width: var(--tap-target);
  height: var(--tap-target);
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

.nav-hamburger span {
  display: block;
  width: 28px;
  height: 3px;
  border-radius: 3px;
  background: var(--color-text);
  transition: transform 0.25s ease, opacity 0.2s ease;
}

/* Two bars rotate into an X once open */
.nav-hamburger[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(4px) rotate(45deg);
}
.nav-hamburger[aria-expanded="true"] span:nth-child(2) {
  transform: translateY(-4px) rotate(-45deg);
}

.menu-link {
  /* Was a Figma-exact 15.979px, an oddly specific one-off matching
     nothing else on the site. --text-body's own max (16px) is close
     enough that this is visually identical at rest, but now it's a
     shared, fluid, intentional value instead of a stray export number. */
  font-size: var(--text-body);
  font-weight: var(--font-weight-medium); /* nav stepped back up one notch, Oct 11, 2026 */
  letter-spacing: var(--tracking-heading);
  color: var(--color-text);
  white-space: nowrap;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Was bumped to 14px 24px chasing CTA-sizing research, but that read as
     too padded/bulky in practice, reverted to the original, tighter
     size. Kept a touch more horizontal room than the original 16px (18px)
     for a slightly less cramped click target without the puffy look. */
  min-height: var(--tap-target);
  padding: 10px 20px;
  border-radius: var(--radius-btn);
  line-height: var(--leading-body); /* <a> buttons inherited 1.4 but <button>s fell back to "normal"; now both match */
  font-family: inherit; /* <button> doesn't inherit the page font on its own, so the footer button was rendering in the system font */
  font-size: var(--text-body); /* was the same one-off 15.979px as .menu-link */
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-heading);
  white-space: nowrap;
  border: none;
  cursor: pointer;
  /* Shared hover animation for both .btn-primary buttons: a fill color
     sweeps in from the bottom on hover rather than an instant color swap.
     Done with a pseudo-element rather than animating
     background-color directly, since background-color transitions just
     cross-fade in place, they can't produce a directional wipe. The
     pseudo sits behind the label (z-index below the text's own stacking
     context, established via position:relative below) and is clipped to
     the button's rounded corners by overflow:hidden. Each button sets its
     own fill color via --btn-fill on its own rule. */
  position: relative;
  isolation: isolate;
  transition: color 0.3s ease, transform var(--dur-fast) var(--ease-out);
}

.btn:active {
  transform: scale(0.97); /* a small press-down */
}

.btn::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: var(--btn-fill);
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform 0.3s ease;
}

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

/* Resting colour of the button, behind the hover fill. */
.btn::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -2;
  border-radius: inherit;
  background: var(--btn-bg, transparent);
}

/* A plain text node paints ABOVE an absolutely-positioned pseudo-element
   in CSS's stacking order (in-flow inline content comes before positioned
   descendants at z-index:auto), so without this, the fill sweep would
   cover the label instead of sitting behind it. The label is wrapped in a
   <span> in the HTML specifically so it can take an explicit z-index and
   guarantee it stays on top. */
.btn > span {
  position: relative;
  z-index: 1;
}

/* The nav's Get started button keeps its original weight after the site-wide step down. */
.menu .btn {
  font-weight: var(--font-weight-semibold);
}

.btn-primary {
  --btn-fill: var(--color-text);
  --btn-bg: var(--color-accent);
  background: transparent; /* the colour is painted by .btn::after so it can have a rough edge */
  color: var(--color-accent-text); /* white, by choice. 3.16:1 on orange is below the 4.5:1 standard for button text; known and accepted. */
}

@media (min-width: 768px) {
  :root {
    --text-display: min(calc((100vw - 2 * var(--space-inline)) / 15), 119px);
    --space-sm: 24px;
    --space-md: 32px;
    --space-lg: 48px;
    --space-xl: 64px;
    --space-inline: 24px;
    --space-section: 96px;
    --space-nav-pad: 24px;
    --space-hero-top: 144px;
    --text-statement: var(--text-subtitle); /* desktop: same as the section subtitles */
    --leading-statement: var(--leading-subtitle);
    --logo-width: 120px;
    --fish-width: 120px;
  }
}

@media (min-width: 1280px) {
  :root {
    --space-inline: 32px;
    --space-section: 128px;
    --space-hero-top: 176px;
  }
}

/* ===== Hero =====
   Redesign (mobile-first): no box, plain page background, left-aligned
   3-line headline. Line 2 is the rotating industry word, which slides
   horizontally (in from the right, out to the left) inside a clip that
   runs edge to edge so words leave through the screen edge, not the
   24px text margin. Desktop treatment comes after the mobile pass. */
.hero {
  position: relative;
  padding: calc(var(--nav-height, 88px) + var(--space-hero-top)) var(--space-inline) var(--space-lg);
}

.hero-heading {
  margin: 0;
  font-family: var(--font-body);
  font-weight: var(--font-weight-bold);
  /* 7% under --text-display: with the longest word ("custom home") the old size left under 1% of room, so a
     scrollbar or the looser tracking pushed the line past the page edge on wide desktops. */
  font-size: calc(var(--text-display) * 0.93);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  color: var(--color-text);
}

.hero-heading-text {
  display: block;
}

.hero-line-1,
.hero-line-2 {
  display: block;
}

/* r and a in "brand." touch at this size/tracking, see index.html split */
.hero-word-br {
  margin-right: 1px;
}

/* The rotating word's box is exactly one line tall (a little taller than
   --leading-display for descenders, pulled back with negative margin) and
   as wide as the active word (JS sets the width). Words slide out of the
   box and fade, so the box edge is the clip. Phones: horizontal, left to
   right (old word out to the right, next one in from the left). Desktop:
   the same motion, vertical (see the 768px block below). */
.hero-rotator {
  position: relative;
  display: inline-block;
  vertical-align: bottom;
  height: 1.2em;
  margin: -0.125em 0;
  overflow: hidden;
  transition: width 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

.hero-rotator-clip {
  display: contents;
}

.hero-rotator-word {
  position: absolute;
  top: 0;
  left: 0;
  white-space: nowrap;
  color: var(--color-accent);
  line-height: 1.2;
}

/* One word at a time: the old word finishes leaving (0.35s) before the next
   starts arriving (JS waits for the exit), so two words never overlap.
   Each state's own transition sets that move's timing. */
.hero-rotator-word.is-waiting {
  transform: translateX(-100%);
  opacity: 0;
  transition: none;
}

.hero-rotator-word.is-active {
  transform: translate(0, 0);
  opacity: 1;
  transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1),
              opacity 0.3s ease-out;
}

.hero-rotator-word.is-leaving {
  transform: translateX(100%);
  opacity: 0;
  transition: transform 0.35s cubic-bezier(0.64, 0, 0.78, 0),
              opacity 0.3s ease-in;
}

@media (prefers-reduced-motion: reduce) {
  .hero-rotator,
  .hero-rotator-word,
  .hero-rotator-word.is-active,
  .hero-rotator-word.is-leaving {
    transition: none;
  }
}

/* Fish game: the empty band above the headline is a click target. Each
   click drops an orange fish at a random tilt (-45 to 45 degrees). Fish are
   decorative, ignore the pointer, and are gone on reload. The band is
   clipped so a fish never spills outside it. */
.fish-field {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: calc(var(--nav-height, 88px) + var(--space-hero-top));
  overflow: hidden;
}

.fish {
  position: absolute;
  width: var(--fish-width);
  height: auto;
  pointer-events: none;
  user-select: none;
  animation: fish-pop 0.35s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

@keyframes fish-pop {
  from { opacity: 0; scale: 0.5; }
  to { opacity: 1; scale: 1; }
}

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

/* Desktop hero (768px+): one line, "Building your [word] brand.", still
   left-aligned. Same rotation as phones but VERTICAL (old word up and out,
   next one up from below). The box animates to each word's width, so only
   the orange word and "brand." ever move. --text-display is derived so the
   longest version, "Building your custom home brand.", always fits.
   Only the vertical edges clip: the clip-path overshoots left/right so a
   glyph's overhang (negative tracking) is never cut off. */
@media (min-width: 768px) {
  .hero-heading-text {
    white-space: nowrap;
  }

  .hero-line-1,
  .hero-line-2 {
    display: inline-block; /* inline-block so the opening animation can move them */
  }

  .hero-rotator {
    overflow: visible;
    clip-path: inset(0 -1em);
  }

  .hero-rotator-word.is-waiting {
    transform: translateY(100%);
  }

  .hero-rotator-word.is-leaving {
    transform: translateY(-100%);
  }
}

.project-overlay {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  /* Same inset on left, right and bottom. line-height 1 so no extra line
     leading sits under the text and makes the bottom gap look bigger. */
  padding: var(--space-sm);
  /* Optical bottom: the line box still has descender room under the
     baseline, so trim that much off the padding. Visible gap from the
     baseline to the bottom edge then equals the left and right gaps. */
  padding-bottom: calc(var(--space-sm) - 0.25em);
  color: #fff;
  font-size: var(--text-tag); /* phones: one step below body; desktop goes back to body (below) */
  line-height: 1;
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-heading);
}

.project-overlay span {
  font: inherit;
}

/* Pull the name left by the first letter's side bearing so the ink, not the
   text box, sits on the 16px inset. */
.project-overlay > span:first-child {
  margin-left: -0.08em;
}

.project-overlay-cta {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
}

.project-overlay-arrow {
  width: 1em;
  height: auto;
  flex: none;
}

/* ===== Projects (grid) =====
   Phones: three cards stacked, every video the same 4:3 rectangle.
   From 768px: Rocco's is the tall card on the left, DWA and Slate stack on the right,
   and the bottom of Rocco's lines up with the bottom of Slate.
   Name and "View Project" sit on the video (.project-overlay), white on a soft dark fade. */
.work {
  padding: 0 var(--space-inline) var(--space-section);
}

.work-grid-home {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-sm);
  margin: 0;
  padding: 0;
  list-style: none;
}

.work-card {
  display: flex;
  flex-direction: column;
}

.work-link {
  flex: 1;
  display: flex;
  flex-direction: column;
  color: inherit;
  text-decoration: none;
}

.work-media {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--color-surface);
}

.work-media video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.work-media::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 40%;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
  pointer-events: none;
}

/* The name is an h3 for structure, but reads at the overlay's size. */
.work-name {
  margin: 0 0 0 -0.08em;
  font: inherit;
  color: inherit;
}

@media (prefers-reduced-motion: no-preference) {
  .work-media video {
    transition: scale var(--dur-slow) var(--ease-out);
  }

  .work-link:hover .work-media video {
    scale: 1.03;
  }

  .work-link:hover .project-overlay-arrow {
    translate: 3px 0;
  }
}

@media (min-width: 768px) {
  .work-grid-home {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-md);
  }

  /* Right-hand cards set the row heights; Rocco's spans both rows and its video fills them. */
  .work-card:not(.work-card--lead) .work-media {
    aspect-ratio: 16 / 7;
  }

  .work-card--lead {
    grid-column: 1;
    grid-row: 1 / 3;
  }

  .work-card--lead .work-media {
    flex: 1;
    aspect-ratio: auto;
    min-height: 0;
  }
}

/* ===== Statement =====
   The hero's supporting line, directly under the headline. */
.statement-text {
  margin: 0;
  max-width: none; /* runs the full width, wraps wherever the screen ends */
  font-size: var(--text-subtitle);
  line-height: var(--leading-subtitle);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-heading);
  text-wrap: wrap; /* plain greedy wrapping: every line is filled to the edge */
  color: var(--color-text);
}

.hero-static .statement-text {
  margin-top: var(--space-md);
}

/* ===== Contact form =====
   Rides on the Common Builds layout (.builds/.builds-list-wrap/
   .builds-list-card); only the form internals live here. */

.contact {
  /* Standalone page: clear the fixed nav, then the same 96px the other sections use. */
  padding-top: calc(var(--nav-height, 88px) + 32px);
  min-height: 70vh;
}

.contact-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-md); /* larger gap BETWEEN fields/groups; title-to-field gap stays tight (.contact-field's --space-2xs) */
  width: 100%;
  padding: 12px;
}

.contact-status {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--text-body);
  color: #a89a90;
}

.footer-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  flex: 1 1 320px;
  max-width: 560px;
}

/* Wide screens: the form is centered vertically in the whole orange footer
   (not just beside the logo/links), so it's taken out of the flow and pinned
   to the footer's vertical middle. Under 900px it goes back into the flow
   (see the block below). */
@media (min-width: 901px) {
  

  .footer-form {
    position: absolute;
    top: 50%;
    right: var(--space-inline);
    transform: translateY(-50%);
    width: min(560px, 50%);
  }
}

@media (max-width: 900px) {
  
  

  .footer-form {
    flex: none;
    width: 100%;
    max-width: none;
  }

  

  

  
}

/* The ONE hard "stacked vs. grid" breakpoint (see the matching comment
   above .hero-heading), everything below here (nav's hamburger menu,
   footer stacking, and the Projects/Services/Builds grid->stacked switch)
   snaps together at the same 767px edge, matching the 4-tier system:
   320-480 and 481-767 both get this stacked/compact treatment; 768-1024
   and 1024+ both get the full grid/inline treatment (the base rules,
   with no override needed here). */
@media (max-width: 767px) {
  .nav-hamburger {
    display: flex;
  }

  /* The menu becomes a dropdown panel anchored under the nav bar (which
     has position:relative for exactly this), rather than the links
     squeezing into/wrapping within the bar itself. Collapsed via
     max-height (not display:none) so the open/close has something to
     actually transition. */
  .menu {
    position: absolute;
    top: calc(100% + var(--space-xs)); /* floats just below the bar so all four corners can be round */
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    max-height: 0;
    overflow: hidden;
    opacity: 0;
    visibility: hidden;
    background: var(--color-bg);
    border-radius: 20px;
    box-shadow: 0 12px 24px rgba(41, 28, 23, 0.1);
    transition: max-height 0.3s ease, opacity 0.2s ease, visibility 0s linear 0.3s;
  }

  .menu.is-open {
    max-height: 400px;
    opacity: 1;
    visibility: visible;
    padding: 8px 24px 24px;
    transition: max-height 0.3s ease, opacity 0.2s ease, visibility 0s linear 0s;
  }

  .menu .menu-link {
    padding: var(--space-sm) 0;
    border-bottom: 1px solid rgba(41, 28, 23, 0.08);
  }

  .menu .btn-primary {
    margin-top: var(--space-sm);
    justify-content: center;
  }

}

@media (max-width: 767px) and (prefers-reduced-motion: reduce) {
  .menu,
  .menu.is-open {
    transition: none;
  }
}

@media (min-width: 768px) {
  .project-overlay {
    font-size: var(--text-body);
  }
}

.blueprint-title {
  margin: 0;
  font-size: var(--text-heading-lg);
  line-height: var(--leading-heading-lg);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--tracking-heading);
  color: var(--color-accent);
}

.blueprint-subtitle {
  margin: 0;
  font-size: var(--text-subtitle);
  line-height: var(--leading-subtitle);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-heading);
  color: var(--color-cream); /* Bone */
}

/* ===== Let's Build =====
   White section with its own --space-section above and below. Intro (title,
   subtitle, button) on top, then the four package cards (.build-card, further down). */
.pricing {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-xl);
  padding: var(--space-section) var(--space-inline);
}

/* Title, subtitle and button as one block (proximity: the button belongs to the copy). */
.pricing-intro {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-md);
}

.section-header {
  display: flex;
  flex-direction: column;
  gap: var(--space-heading-gap);
}

.section-title {
  margin: 0;
  font-size: var(--text-heading-lg);
  line-height: var(--leading-heading-lg);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--tracking-heading);
  color: var(--color-accent); /* orange on white is 3.16:1, large text only */
}

.section-subtitle {
  margin: 0;
  font-size: var(--text-subtitle);
  line-height: var(--leading-subtitle);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-heading);
  color: var(--color-text);
}

/* ===== Footer (rebuilt on the library) =====
   Full-bleed orange. White text, as before: white on orange is 3.16:1, which
   is below the 4.5:1 standard for small text (known and accepted, same as the
   button). Phones: everything stacked. 768px up: logo and links left, form
   right. Fields are 44px tall tap targets. */
.site-footer {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  padding: var(--space-xl) var(--space-inline);
  background: var(--color-accent);
  color: var(--color-accent-text);
}

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

.site-footer-brand {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0; /* logo and links are 44px tap targets, so their own height already gives the spacing */
}

.site-footer-logo {
  display: flex;
  align-items: center;
  min-height: var(--tap-target);
  margin-bottom: var(--space-sm); /* room between the fish and the links */
}

.site-footer-logo img {
  display: block;
  width: 64px; /* the fish icon, smaller than the nav wordmark */
  height: auto;
}

@media (min-width: 768px) {
  .site-footer-logo img {
    width: 80px;
  }
}

.site-footer-nav {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.site-footer-link {
  display: inline-flex;
  align-items: center;
  min-height: 32px; /* tighter than the 44px standard on purpose: still above the WCAG AA 24px minimum */
  font-size: var(--text-body);
  line-height: var(--leading-body);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-heading);
  color: var(--color-accent-text);
  transition: opacity 0.2s ease;
}

.site-footer-link:hover {
  opacity: 0.75;
}

.site-footer-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  width: 100%;
}

.site-footer-lead {
  margin: 0;
  font-size: var(--text-title); /* stepped down from subtitle on Oct 12, 2026: it overpowered the fields */
  line-height: var(--leading-title);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-heading);
  color: var(--color-accent-text);
}

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

.site-footer-fields input {
  width: 100%;
  min-height: var(--tap-target);
  padding: 0 16px; /* same side padding as the contact form fields */
  border: 0;
  border-radius: var(--radius-btn);
  background: var(--color-bg);
  font: inherit;
  font-size: var(--text-body);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-body);
  color: var(--color-text);
}

.site-footer-fields input::placeholder {
  color: var(--color-text-muted); /* 5.25:1 on white */
  opacity: 1;
}

.site-footer-fields input:focus-visible {
  outline: 2px solid var(--color-text);
  outline-offset: 2px;
}

.site-footer-btn {
  --btn-fill: var(--color-bg);
  --btn-bg: var(--color-text);
  width: 100%;
  background: transparent;
  color: var(--color-accent-text);
}

.site-footer-btn:hover {
  color: var(--color-text);
}

.site-footer-bottom {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

.site-footer-location,
.site-footer-copyright {
  margin: 0;
  font-size: var(--text-tag);
  line-height: var(--leading-tag);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-body);
  color: var(--color-accent-text);
}

@media (min-width: 768px) {
  .site-footer-main {
    flex-direction: row;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-xl);
  }

  .site-footer-form {
    width: min(520px, 50%);
  }

  .site-footer-fields {
    flex-direction: row;
  }

  .site-footer-fields input {
    flex: 1 1 0;
    min-width: 0;
  }

  .site-footer-btn {
    width: auto;
    flex: none;
  }
}

/* ===== Contact page (rebuilt on the library) =====
   Phones: header, then the form. 768px up: header left, form right, like
   Common Builds. Form sits in a Surface grey panel; fields and pills are white 44px tap targets.
   Selected pills are Dark with Bone text (12.9:1). White on orange would be
   3.16:1, too low for 16px text. */
.contact {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  padding: calc(var(--nav-height, 88px) + var(--space-xl)) var(--space-inline) var(--space-section);
}

/* 768px up: two columns. Header and "Other ways" stack in the left column
   (in the blank space beside the form); the form panel spans both rows on
   the right. On phones the DOM order wins: header, form, then "Other ways". */
@media (min-width: 768px) {
  .contact {
    display: grid;
    grid-template-columns: 1fr 2.3fr;
    grid-template-rows: auto 1fr; /* row 1 is just the header, so "Other ways" sits right under it */
    column-gap: var(--space-xl);
    row-gap: var(--space-lg);
    align-items: start;
    align-content: start;
  }

  .contact-intro {
    grid-column: 1;
    grid-row: 1;
  }

  .contact-other {
    grid-column: 1;
    grid-row: 2;
  }

  .contact-panel {
    grid-column: 2;
    grid-row: 1 / span 2;
    min-width: 0;
  }
}

/* Grey panel around the whole form, like Common Builds; fields and pills are white inside it. */
.contact-panel {
  width: 100%;
  padding: var(--space-md);
  background: var(--color-surface);
  border-radius: var(--radius-card);
}

.cf {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.cf-honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.cf-row {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

@media (min-width: 768px) {
  .cf-row {
    flex-direction: row;
  }

  .cf-row .cf-field {
    flex: 1 1 0;
    min-width: 0;
  }
}

.cf-field,
.cf-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
}

.cf-field > span,
.cf-group > legend {
  padding: 0;
  font-size: var(--text-tag);
  line-height: var(--leading-tag);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-body);
  color: var(--color-text-mid);
}

.cf-group > legend {
  margin-bottom: var(--space-xs);
}

.cf-field input,
.cf-field textarea {
  width: 100%;
  min-height: var(--tap-target);
  padding: 10px 16px; /* 16px sides like the pills at every width (--space-sm grows to 24 on desktop and pushed the text in); 10px top so notes text sits evenly */
  border: 0;
  border-radius: 8px;
  background: var(--color-bg);
  font: inherit;
  font-size: var(--text-body);
  line-height: var(--leading-body);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-body);
  color: var(--color-text);
}

.cf-field textarea {
  min-height: 120px;
  resize: vertical;
}

.cf-pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
}

.cf-pill {
  position: relative;
  display: inline-flex;
}

/* The real checkbox/radio stays focusable but invisible; the span is the pill. */
.cf-pill input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.cf-pill span {
  display: inline-flex;
  align-items: center;
  min-height: 40px; /* phones: a touch under 44 so the pills read lighter; still well above the WCAG 24px minimum */
  padding: 0 14px;
  border-radius: 999px;
  background: var(--color-bg);
  font-size: 0.9375rem; /* 15px on phones, between tag and body; desktop drops to tag size below */
  line-height: 1;
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-body);
  color: var(--color-text);
  transition: background-color 0.2s ease, color 0.2s ease;
}

.cf-pill input:checked + span {
  background: var(--color-text);
  color: var(--color-cream);
}

.cf-pill input:focus-visible + span {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.cf-submit {
  display: flex;
  flex-direction: column-reverse;
  align-items: stretch;
  gap: var(--space-sm);
}

.cf-submit .btn {
  width: 100%;
}

/* Sent: green, no hover fill, no press. #2f7d4f with White text is 5:1. */
.cf-submit .btn.is-sent {
  --btn-bg: #2f7d4f;
  --btn-fill: #2f7d4f;
  cursor: default;
  transform: none;
}

.cf-status {
  margin: 0;
  font-size: var(--text-body);
  line-height: var(--leading-body);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-body);
  color: var(--color-text);
}

@media (min-width: 768px) {
  .cf-submit {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }

  .cf-submit .btn {
    width: auto;
    flex: none;
  }
}

/* Desktop: the whole form fits on one laptop screen (about 800px tall) without scrolling.
   Pills drop to tag size at 36px tall (pointer devices; phones use 40px),
   gaps tighten one step, and the notes box starts at three lines. Oct 12, 2026. */
@media (min-width: 768px) {
  .contact {
    padding-top: calc(var(--nav-height, 88px) + var(--space-md));
  }

  .cf {
    gap: var(--space-sm);
  }

  .cf-row {
    gap: var(--space-sm);
  }

  .cf-group > legend {
    margin-bottom: var(--space-2xs);
  }

  .cf-pill span {
    min-height: 36px;
    padding: 0 16px;
    font-size: var(--text-tag);
  }

  .cf-field textarea {
    min-height: 88px;
  }
}

/* "Other ways to reach us": email and phone, shown plainly. Values are 20px/600
   dark with an orange underline (orange text would be too faint at this size). */
.contact-other {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-top: var(--space-md); /* extra room above, on top of the stack gap */
}

.contact-other-title {
  margin: 0;
  font-size: var(--text-subtitle);
  line-height: var(--leading-subtitle);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-heading);
  color: var(--color-text);
}

.contact-other-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs); /* the links' own 44px tap area already adds space between the two items */
  margin: 0;
  padding: 0;
  list-style: none;
}

.contact-other-list li {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

.contact-other-label {
  font-size: var(--text-tag);
  line-height: var(--leading-tag);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-body);
  color: var(--color-text-mid); /* same as the form field labels */
}

.contact-other-link {
  display: inline-flex;
  align-items: flex-start; /* text sits at the top of the 44px tap area, right under its label */
  align-self: flex-start;
  min-height: var(--tap-target);
  font-size: var(--text-title);
  line-height: var(--leading-title);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-heading);
  color: var(--color-text);
  text-decoration: underline;
  text-decoration-color: var(--color-accent);
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}

.contact-other-link:hover {
  color: var(--color-accent);
}

/* Desktop gets more room above "Other ways" (declared after the base rule so it wins). */
@media (min-width: 768px) {
  .contact-other {
    margin-top: var(--space-xl);
  }
}

/* Only the colour changes between sections get a hand-cut edge (see js/rough.js). Buttons, cards,
   images and the nav are plain rounded shapes. */
.ab-behind,
.plan,
.site-footer,
.pj-close {
  clip-path: var(--rough-clip, none);
}

/* ===== Titles in the logo font =====
   Only "The Blueprint", "Let's Build", "Let's talk" (contact) and the project page titles use Alte Haas Grotesk Bold, the font of
   the JACKFISH wordmark (same tight tracking as the footer wordmark). Everything else, including the
   home page headline, stays in Fustat. The logo font is wider and heavier, so these sit about 15% smaller. */
.blueprint-title,
.pricing .section-title,
.contact .section-title,
.not-found .section-title,
.legal .section-title {
  font-family: var(--font-heading);
  font-weight: 700; /* the only weight the font file has */
  letter-spacing: -0.03em;
  font-size: calc(var(--text-heading-lg) * 0.85);
}

/* ===== The Blueprint (.plan) =====
   Full-bleed Dark brown, hand-cut top and bottom edge (rough.js). Orange title, Bone subtitle,
   then one ruled row per service: name on the left, line on the right (stacked on phones). */
.plan {
  padding: var(--space-section) var(--space-inline);
  background: var(--color-text);
}

.plan-header {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-bottom: var(--space-xl);
}

.plan-list {
  margin: 0;
  padding: 0;
  list-style: none;
  border-bottom: 1px solid rgba(235, 226, 215, 0.35); /* Bone, faded */
}

.plan-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-xs);
  padding: var(--space-md) 0;
  border-top: 1px solid rgba(235, 226, 215, 0.35);
}

.plan-name {
  margin: 0;
  font-size: var(--text-title);
  line-height: var(--leading-title);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-heading);
  color: var(--color-cream); /* text on Dark is always Bone, never white */
}

.plan-desc {
  margin: 0;
  max-width: 44ch;
  font-size: var(--text-body);
  line-height: var(--leading-body);
  color: var(--color-cream);
}

@media (min-width: 768px) {
  .plan-row {
    grid-template-columns: 1fr 1fr;
    align-items: center; /* name and line sit in the vertical middle between the rules */
    gap: var(--space-lg);
    padding: var(--space-xl) 0;
  }
}

/* ===== Let's Build cards =====
   Bone cards, one column on phones, 2x2 from 768px. Name at the top; price and line sit
   together at the bottom. Orange price on Light Bone is about 2.9:1, under the 3:1 large-text minimum (trial, Oct 12, 2026). */
.build-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-sm);
  width: 100%;
}

.build-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  padding: var(--space-md);
  background: var(--color-bone-light);
  border-radius: var(--radius-card);
}

.build-name {
  margin: 0;
  font-size: var(--text-title);
  line-height: var(--leading-title);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-heading);
  color: var(--color-text);
}

.build-bottom {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-top: auto;
}

.build-price {
  display: flex;
  flex-direction: column;
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: calc(var(--text-heading) * 0.85);
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--color-accent);
}

.build-note {
  margin-bottom: var(--space-2xs);
  font-family: var(--font-body);
  font-weight: var(--font-weight-medium);
  font-size: var(--text-tag);
  line-height: var(--leading-tag);
  letter-spacing: 0;
  color: var(--color-text-mid);
}

.build-desc {
  margin: 0;
  max-width: 40ch;
  font-size: var(--text-body);
  line-height: var(--leading-body);
  color: var(--color-text-mid);
}

@media (min-width: 768px) {
  .build-grid {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-md);
  }

  .build-card {
    min-height: 280px;
    padding: var(--space-lg);
  }
}

/* ===== About page (Oct 12, 2026 rehaul) =====
   Hero photo and belief statement > three pillars > Behind Jackfish on Dark brown > Careers > footer.
   Text on Dark is Bone, never white. */
.ab-hero {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  padding: calc(var(--nav-height, 88px) + var(--space-xl)) var(--space-inline) 0;
}

.ab-hero-media {
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-card);
  display: block;
  width: 100%;
  height: auto;
  object-fit: cover;
  object-position: 55% 70%; /* keeps the laptop, hands and sketchbook in frame on the wide desktop crop */
  background: var(--color-surface); /* shows only for the moment before the photo arrives */
}

.ab-hero-text {
  margin: 0;
  max-width: 64ch;
  font-size: var(--text-subtitle);
  line-height: var(--leading-subtitle);
  font-weight: var(--font-weight-semibold); /* same as the home statement and section subtitles */
  letter-spacing: var(--tracking-heading);
  color: var(--color-text);
  text-wrap: pretty;
}

.ab-accent {
  color: var(--color-accent);
  font-weight: var(--font-weight-bold);
}

.ab-pillars {
  padding: var(--space-section) var(--space-inline);
}

.ab-pillar-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-xl);
  margin: 0;
  padding: 0;
  list-style: none;
}

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

.ab-pillar-title,
.ab-behind-title,
.ab-careers-title {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: calc(var(--text-heading) * 0.85); /* item-level titles (pillars), same size as the Let's Build prices */
  line-height: var(--leading-heading);
  letter-spacing: -0.03em;
}

/* Section titles: same size as "The Blueprint", "Let's Build" and "Let's talk". */
.ab-behind-title,
.ab-careers-title {
  font-size: calc(var(--text-heading-lg) * 0.85);
  line-height: var(--leading-heading-lg);
}

.ab-pillar-title {
  color: var(--color-accent);
}

.ab-pillar-text {
  margin: 0;
  max-width: 34ch;
  font-size: var(--text-body);
  line-height: var(--leading-body);
  color: var(--color-text-mid);
}

.ab-behind {
  padding: var(--space-section) var(--space-inline);
  background: var(--color-text);
}

.ab-behind-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-lg);
  align-items: center;
}

.ab-behind-media {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: var(--radius-card);
}

.ab-behind-copy {
  display: flex;
  flex-direction: column;
  gap: var(--space-heading-gap);
}

.ab-behind-title {
  color: var(--color-cream);
}

.ab-behind-text {
  margin: 0;
  max-width: 40ch;
  font-size: var(--text-title);
  line-height: var(--leading-title);
  color: var(--color-cream);
}

.ab-behind-text .ab-accent {
  font-weight: var(--font-weight-bold);
}

.ab-careers {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-heading-gap); /* title to subtitle, like every section header */
  padding: var(--space-section) var(--space-inline);
  text-align: center;
}

.ab-careers-title {
  color: var(--color-text);
}

.ab-careers-text {
  margin: 0;
  font-size: var(--text-subtitle); /* same as the subtitle under every other section title */
  line-height: var(--leading-subtitle);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-heading);
  color: var(--color-text);
}

@media (min-width: 768px) {
  .ab-hero-media {
    aspect-ratio: 16 / 6;
  }

  .ab-pillar-list {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-lg);
  }

  /* Narrower photo, text column sized to the copy, and the pair centred so the gap to the
     left edge matches the gap to the right edge. */
  .ab-behind-grid {
    grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
    gap: var(--space-xl);
    max-width: 1120px;
    margin: 0 auto;
  }

  .ab-behind-text {
    max-width: none;
  }
}

/* ===== 404 page =====
   Title, one line and a button, with enough height that the footer sits low. */
.not-found {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-lg);
  min-height: 70vh;
  padding: calc(var(--nav-height, 88px) + var(--space-xl)) var(--space-inline) var(--space-section);
}

/* ===== Privacy page =====
   Plain reading page: title, date, then short sections. Reading width capped at 66ch. */
.legal {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  padding: calc(var(--nav-height, 88px) + var(--space-xl)) var(--space-inline) var(--space-section);
}

.legal-updated {
  margin: 0;
  font-size: var(--text-tag);
  line-height: var(--leading-tag);
  color: var(--color-text-mid);
}

.legal-body {
  max-width: 66ch;
}

.legal-body h2 {
  margin: var(--space-lg) 0 var(--space-xs);
  font-size: var(--text-title);
  line-height: var(--leading-title);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-heading);
}

.legal-body p,
.legal-body ul {
  margin: 0 0 var(--space-sm);
  font-size: var(--text-body);
  line-height: 1.5; /* long reading text */
  color: var(--color-text-mid);
}

.legal-body ul {
  padding-left: 1.25em;
}

.legal-body strong {
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}

.legal-body a {
  color: var(--color-text);
  text-decoration: underline;
  text-decoration-color: var(--color-accent);
  text-underline-offset: 3px;
}

.site-footer-copyright a {
  margin-left: var(--space-sm);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ===== Motion =====
   Everything below only applies when html has .motion (added in <head> unless the
   visitor prefers reduced motion), so with reduced motion or no JS nothing is
   hidden and nothing moves. */

/* Scroll reveal: main.js tags elements with data-reveal and adds .is-revealed once
   as they come into view, a little later for each sibling. Uses the separate
   `translate` property, so it never clobbers an element's own transform. */
.motion [data-reveal] {
  opacity: 0;
  translate: 0 var(--reveal-distance);
  transition: opacity var(--dur-base) var(--ease-out),
              translate var(--dur-slow) var(--ease-out);
  transition-delay: var(--reveal-delay, 0s);
}

.motion [data-reveal].is-revealed {
  opacity: 1;
  translate: 0 0;
}

/* Opening on the home page: the headline lines come in one after another, then it goes still. */
@keyframes rise-in {
  from { opacity: 0; translate: 0 var(--reveal-distance); }
  to { opacity: 1; translate: 0 0; }
}

.motion .hero-line-1,
.motion .hero-rotator,
.motion .hero-line-2 {
  animation: rise-in var(--dur-slow) var(--ease-out) both;
}

.motion .hero-line-1 { animation-delay: 80ms; }
.motion .hero-rotator { animation-delay: 190ms; }
.motion .hero-line-2 { animation-delay: 300ms; }

.project-overlay-arrow {
  transition: translate var(--dur-base) var(--ease-out);
}

/* Links: the underline fades in on hover. */
.menu-link,
.site-footer-link {
  text-decoration: underline;
  text-decoration-color: transparent;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 5px;
  transition: text-decoration-color var(--dur-base) var(--ease-out);
}

.menu-link:hover,
.site-footer-link:hover {
  text-decoration-color: currentColor;
  opacity: 1;
}

/* Page changes cross-fade softly. The nav keeps its own name so it stays put. */
@view-transition {
  navigation: auto;
}

.nav-wrap {
  view-transition-name: site-nav;
}

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.35s;
  animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
}

@media (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: none;
  }
}

/* Until main.js has tagged the elements above, hide them the same way, so
   nothing flashes visible and then disappears on a slow load. (Keep this list
   in step with SELECTORS in the scroll reveal in main.js.) */
.motion:not(.reveal-ready) :is(.section-title, .section-subtitle, .statement-text, .blueprint-title, .blueprint-subtitle, .work-card, .plan-row, .build-card, .ab-hero-text, .ab-pillar, .ab-behind-media, .ab-behind-copy, .ab-careers, .contact-panel, .contact-other, .pricing-intro .btn, .site-footer-brand, .site-footer-form, .site-footer-bottom, .pj-title, .pj-hero-media, .pj-brief-copy p) {
  opacity: 0;
}
