/* ==========================================================================
   Design tokens — edit these to rebrand the whole site.
   Identity: Bricolage Grotesque (display) + IBM Plex Sans (body) + IBM Plex
   Mono (mono/labels), cyan-blue accent (see --color-signal-* below).
   Display face swapped from Space Grotesk -> Bricolage Grotesque 2026-08-05
   (impeccable overused-font finding; operator chose the replacement).
   Accent swapped from the original Signal-Rose pink -> cyan-blue 2026-08-06
   (operator request) — variable names kept as --color-signal-* since every
   component file reads them by that name.

   2026-08-05 refinement (impeccable /layout craft layer): committed a
   dark-grey visual world (was light "paper") and moved the whole site off the
   top-nav/stacked-sections skeleton to a FIXED LEFT SIDEBAR RAIL with an
   independently-scrolling content column — the split-screen family flagged as
   validated-but-unused in [[site-builder-layout-lineage]]. Identity preserved:
   same fonts, same accent (pink at the time), same copy and behavior. Single
   theme (style-guide "Page Theme Lock") — no prefers-color-scheme fork now
   that dark is the committed default.
   See resources/style-guide.md for the mechanical scale this build inherits.
   ========================================================================== */

@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("../fonts/bricolage-grotesque-variable.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("../fonts/ibm-plex-sans-variable.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-400.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-500.woff2") format("woff2");
}

:root {
  /* Color — committed dark-grey world. Rail is a shade darker than the content
     column so the fixed navigation reads as a distinct plane. Cyan-blue is
     the single accent (swapped from Signal Rose pink 2026-08-06, operator
     request — variable names kept as --color-signal-* since every component
     file reads them): the deep --color-signal for fills (buttons, marks,
     contrast-verified ~5.4:1 for white button text), the brighter
     --color-signal-bright for text/links/headings on the dark surface
     (~9.8:1, clears AA with room to spare). Component files under
     resources/components/ read the semantic --color-primary / --color-bg-alt /
     --color-white / --color-border / --color-text-muted names, so they inherit
     this palette without modification. */
  --color-signal: #0e7490;         /* accent fill (buttons, marks) — cyan-700 */
  --color-signal-bright: #22d3ee;  /* accent for text/links/headings on dark — cyan-400, clears AA */
  --color-signal-dark: #155e75;    /* hover darken on fills — cyan-800 */
  --color-on-signal: #ffffff;

  --color-primary: var(--color-signal);

  --color-bg: #17191d;             /* content column — base */
  --color-bg-alt: #1d2026;         /* recessed section band */
  --color-surface: #23272e;        /* raised card surface */
  --color-surface-2: #2a2f37;      /* raised-on-raised (form inputs, close btn) */
  --color-rail: #101216;           /* fixed sidebar rail — darker plane */
  --color-white: var(--color-surface);   /* legacy alias used by shared components */
  --color-text: #e9eaec;
  --color-text-muted: #9aa1ab;     /* ~6:1 on --color-bg */
  --color-border: #2b3038;
  --color-border-strong: #363c45;
  --color-danger: #f2777a;          /* error states — readable red on the dark surfaces */

  /* Typography */
  --font-display: "Bricolage Grotesque", ui-sans-serif, system-ui, sans-serif;
  --font-body: "IBM Plex Sans", -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-heading: var(--font-display);
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --fs-sm:   clamp(0.875rem, 0.853rem + 0.094vw, 0.938rem);
  --fs-base: clamp(1rem, 0.967rem + 0.141vw, 1.094rem);
  --fs-lg:   clamp(1.25rem, 1.206rem + 0.188vw, 1.375rem);
  --fs-xl:   clamp(1.563rem, 1.496rem + 0.282vw, 1.75rem);
  --fs-2xl:  clamp(1.953rem, 1.849rem + 0.446vw, 2.25rem);
  --fs-3xl:  clamp(2.441rem, 2.289rem + 0.651vw, 2.875rem);
  --fs-4xl:  clamp(2.75rem, 2.2rem + 2.4vw, 4.25rem);
  --fw-normal: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;

  /* Spacing — multiple-of-4 scale */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;
  --space-9: 96px;

  /* Radius */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-xl: 16px;
  --radius-pill: 999px;

  /* Depth — offset + soft blur, deeper on a dark world so raised surfaces read */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .35), 0 1px 3px rgba(0, 0, 0, .30);
  --shadow-md: 0 6px 18px rgba(0, 0, 0, .40);
  --shadow-lg: 0 20px 48px rgba(0, 0, 0, .55);
  --glow-signal: 0 0 0 1px color-mix(in srgb, var(--color-signal) 45%, transparent),
                 0 18px 44px color-mix(in srgb, var(--color-signal) 22%, transparent);

  --dur-fast: 150ms;
  --dur-base: 250ms;
  --dur-slow: 500ms;
  --ease-standard: cubic-bezier(.2, 0, 0, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);

  --banner-height: 0px;    /* was 36px to clear the preview-banner bar; that bar is removed in this accepted final/ copy, so this collapses to 0 rather than leaving an empty gap at the top of every page */
  --rail-w: 264px;         /* fixed sidebar width (desktop) */
  --stack-step: 20px;      /* per-card top offset step in the "how it works" sticky stack */
}

/* ==========================================================================
   Reset & base
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; }
img, svg { max-width: 100%; display: block; }
/* Make the hidden attribute win over component display rules (e.g. flex). */
[hidden] { display: none !important; }

body {
  font-family: var(--font-body);
  font-size: var(--fs-base);
  line-height: 1.6;
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: var(--font-heading);
  line-height: 1.1;
  letter-spacing: -0.02em;
  font-weight: var(--fw-bold);
  color: var(--color-text);
  margin: 0 0 var(--space-4);
  text-wrap: balance;
}
h1 { font-size: var(--fs-4xl); letter-spacing: -0.03em; }
h2 { font-size: var(--fs-2xl); }
h3 { font-size: var(--fs-xl); letter-spacing: -0.01em; }
p { margin: 0 0 var(--space-4); color: var(--color-text-muted); }

a { color: var(--color-signal-bright); }

:focus-visible {
  outline: 3px solid var(--color-signal-bright);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* Content-column inner wrapper. 2026-08-06: centered within the content
   column (was deliberately left-anchored for an editorial rhythm during the
   rail redesign, but that read as a big empty gap on the right on wide
   screens — reported and reverted). 1120px matches the value used before the
   rail redesign and by every other build's .container. */
.container {
  width: 100%;
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 var(--space-7);
}
.container-narrow { max-width: 620px; }
.container-narrow h1 { text-align: center; }
.container-narrow p { text-align: center; }

.section { padding: var(--space-8) 0; }
.section-head { max-width: 56ch; margin-bottom: var(--space-7); }
.section-head p { margin-bottom: 0; }

/* Extra breathing room above a section-head that follows directly after a
   gallery grid (e.g. "Mobile examples" above gallery-bento-second) — the
   grid's own tiles have no bottom margin, so without this the heading sits
   flush against them (operator-flagged, 2026-08-17). */
.section-head-spaced { margin-top: var(--space-9); padding-top: var(--space-7); border-top: 1px solid var(--color-border); }
.work-more { margin-top: var(--space-7); justify-content: center; text-align: center;}
/* Opt-in modifier, not the .section-head default — applied to the page-title
   header on about.html and example.html only. index.html's in-page headers
   ("How it works", "Examples") and about.html's "Our story"/"Our values"
   stay left-anchored, matching the rest of the page's editorial rhythm. */
.section-head-center { margin-left: auto; margin-right: auto; text-align: center; span { color: var(--color-signal-bright); } }

/* Shared "muted italic small-print caption" look for .gallery-disclaimer
   (footnote under the example gallery — shown work may not still reflect the
   featured business's current state) and .social-disclaimer (defined near
   the footer social row below) — only their margin differs by context. */
.gallery-disclaimer,
.social-disclaimer {
  text-align: center;
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
  font-style: italic;
}
.gallery-disclaimer { margin-top: var(--space-5); }

/* ==========================================================================
   Shell — fixed left rail + independently-scrolling content column.
   .site-shell is a plain wrapper; full-height/sticky-footer comes from
   .site-content below, so the shell needs no height rule of its own.
   ========================================================================== */
.site-rail {
  position: fixed;
  top: var(--banner-height);
  left: 0;
  bottom: 0;
  width: var(--rail-w);
  z-index: 90;
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  padding: var(--space-6) var(--space-5);
  background: var(--color-rail);
  border-right: 1px solid var(--color-border);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.rail-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.logo {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  letter-spacing: -0.02em;
  color: var(--color-text);
  text-decoration: none;
}
.logo span { color: var(--color-signal-bright); }

/* Primary nav — vertical list on the rail (desktop). Keeps the .nav-menu /
   .nav-toggle class contract js/main.js drives, so the mobile drawer works
   with no JS change. */
.nav-menu {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  flex: 1 1 auto;   /* fills the rail so .rail-spacer can pin the CTA to the bottom */
}
/* Desktop: the inner wrapper is transparent to layout — its children (the
   links/spacer/CTA) act as direct flex items of .nav-menu, same as before
   this wrapper existed. Mobile below turns it into the actual collapsing
   surface (see the grid-template-rows technique in the 1023px query). */
.nav-menu-inner { display: contents; }
.nav-menu a:not(.btn) {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-md);
  color: var(--color-text-muted);
  text-decoration: none;
  font-weight: var(--fw-medium);
  border-left: 2px solid transparent;   /* 2px is the design's own marker, not a stray accent bar */
  transition: color var(--dur-fast) var(--ease-standard),
              background-color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}
.nav-menu a:not(.btn):hover {
  color: var(--color-text);
  background: color-mix(in srgb, var(--color-text) 6%, transparent);
}
.nav-menu a.is-current {
  color: var(--color-text);
  border-left-color: var(--color-signal-bright);
  background: color-mix(in srgb, var(--color-signal) 12%, transparent);
}

/* Spacer pushes the CTA + meta to the bottom of the rail on desktop. */
.rail-spacer { flex: 1 1 auto; min-height: var(--space-5); }

.rail-cta { width: 100%; }

/* Hamburger — hidden on desktop, shown on the mobile top-bar rail. */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
}
.nav-toggle span {
  display: block;
  width: 22px;
  height: 2px;
  margin: 0 auto;
  background: var(--color-text);
  border-radius: 1px;
  transition: transform var(--dur-fast) var(--ease-standard), opacity var(--dur-fast) var(--ease-standard);
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Content column — offset by the rail on desktop, scrolls with the page. */
.site-content {
  margin-left: var(--rail-w);
  padding-top: var(--banner-height);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
.site-content > main { flex: 1 0 auto; }

/* ---- Mobile: rail collapses to a fixed top bar + drawer ---- */
@media (max-width: 1023px) {
  .site-rail {
    right: 0;
    bottom: auto;
    width: auto;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 0 var(--space-4);
    padding: var(--space-3) var(--space-4);
    border-right: none;
    border-bottom: 1px solid var(--color-border);
    overflow: visible;
  }
  .rail-top { flex: 1 1 auto; }
  .nav-toggle { display: flex; margin-left: auto; }

  .nav-menu {
    flex-basis: 100%;
    order: 3;
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    transition: grid-template-rows var(--dur-base) var(--ease-standard), opacity var(--dur-fast) var(--ease-standard);
  }
  .nav-menu.is-open { grid-template-rows: 1fr; opacity: 1; margin-top: var(--space-3); }
  /* .nav-menu is now the grid track that animates; .nav-menu-inner (display:
     contents on desktop, see above) becomes the real flex column and the
     overflow-hidden mask so content doesn't spill past the collapsing row. */
  .nav-menu-inner {
    display: flex;
    flex-direction: column;
    gap: 0;
    overflow: hidden;
    min-height: 0;
  }
  .nav-menu a:not(.btn) {
    border-left: none;
    border-radius: 0;
    padding: var(--space-3) var(--space-2);
    border-bottom: 1px solid var(--color-border);
  }
  .nav-menu a.is-current { background: none; color: var(--color-signal-bright); }
  .rail-spacer { display: none; }
  .rail-cta { margin: var(--space-4) 0 var(--space-2); }

  .site-content {
    margin-left: 0;
    padding-top: calc(var(--banner-height) + var(--space-8));   /* clears the fixed top bar */
  }

  /* In-page anchor targets (e.g. hero's "See examples" -> #examples) need the
     same clearance so the fixed top bar doesn't cover the section heading
     right after the jump. */
  #examples { scroll-margin-top: calc(var(--banner-height) + var(--space-6)); }
}

/* ==========================================================================
   Buttons (resources/components/cta-button/)
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  font: inherit;
  font-weight: var(--fw-semibold);
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: transform var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard),
              background-color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}
.btn-primary {
  background: var(--color-signal);
  color: var(--color-on-signal);
}
.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
  background: var(--color-signal-dark);
}
.btn-secondary {
  background: var(--color-surface);
  color: var(--color-text);
  border-color: var(--color-border-strong);
}
.btn-secondary:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-sm);
  border-color: var(--color-signal-bright);
}
.btn-ghost {
  background: transparent;
  color: var(--color-signal-bright);
  border-color: color-mix(in srgb, var(--color-signal-bright) 55%, transparent);
  
}
.btn-ghost:hover { background: color-mix(in srgb, var(--color-signal) 14%, transparent); }

/* ==========================================================================
   Hero — large left-anchored statement in the content column
   ========================================================================== */
.hero { padding: var(--space-9) 0 var(--space-8); text-align: center;}
/* No max-width cap on desktop: the heading runs the full container width and
   only wraps where text-wrap:balance (base h1 rule) actually needs it to,
   instead of being artificially forced narrow. (2026-08-06: was capped at
   18ch for a deliberate 2-line hero statement; operator asked for it to
   expand fully instead.) */
/* Below ~540px the main clamp's font-size grows faster than the container
   shrinks, forcing an awkward 3-line break instead of a clean one. A
   smaller, purpose-fit clamp (verified 2-line-safe at 320/375/500/539px)
   takes over in this range only — desktop is unaffected. */
@media (max-width: 539px) {
  .hero h1 {
    font-size: clamp(2.25rem, 0.85rem + 6.2vw, 3rem);
  }
}
/* Highlight a single word/phrase in the hero headline — cyan accent + a touch
   heavier than the heading's own bold, echoing the logo's own two-tone
   treatment (EZ/Builder in the same color). Scoped to .hero h1 only. */
.hero h1 span {
  color: var(--color-signal-bright);
  font-weight: 800;
}
.hero-lead {
  max-width: 52ch;
  margin-left: auto;
  margin-right: auto;
  text-align: center;
  font-size: var(--fs-lg);
  font-style: italic;
  color: var(--color-text-muted);
  margin-bottom: var(--space-6);
}
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
  margin-bottom: var(--space-8);
}

/* Understated meta row — replaces the old floating "hero-metric" stat card
   (big-number template the craft floor refuses). Same placeholder content,
   presented as a quiet rule-separated mono row instead of three accent
   numbers demanding the eye. */
.hero-meta {
  list-style: none;
  margin: 0;
  padding: var(--space-5) 0 0;
  border-top: 1px solid var(--color-border);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-7);
  justify-content: center;
}
.hero-meta li {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
}
.hero-meta b {
  align-items: center;
  font-family: var(--font-mono);
  font-weight: var(--fw-medium);
  font-size: var(--fs-base);
  color: var(--color-text);
  letter-spacing: -0.01em;
}

/* ==========================================================================
   Scrolling stack — "how it works" (resources/components/scrolling-stack/)
   ========================================================================== */
.stack-section { background: var(--color-bg-alt); }
.stack { display: flex; flex-direction: column; gap: var(--space-7); padding-bottom: var(--space-4); }
.stack-card {
  position: sticky;
  top: calc(var(--banner-height) + var(--space-5));
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
  padding: var(--space-7);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  min-height: 200px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
/* card 1 inherits the base .stack-card top; 2–4 step down for the stacked peek */
.stack-card:nth-child(2) { top: calc(var(--banner-height) + var(--space-5) + var(--stack-step)); }
.stack-card:nth-child(3) { top: calc(var(--banner-height) + var(--space-5) + var(--stack-step) * 2); }
.stack-card:nth-child(4) { top: calc(var(--banner-height) + var(--space-5) + var(--stack-step) * 3); }
.stack-card h3 {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-signal-bright);
  margin-bottom: var(--space-3);
}
.stack-card p { margin: 0; font-size: var(--fs-lg); color: var(--color-text); }
@media (prefers-reduced-motion: no-preference) {
  .stack-card { transition: transform var(--dur-base) var(--ease-standard); }
}
@media (max-width: 640px) {
  .stack-card { position: static; box-shadow: var(--shadow-md); }
}

/* ==========================================================================
   Gallery bento — asymmetric placeholder-photo grid (one featured tile +
   two equal tiles below), click-to-enlarge via the lightbox.
   Exact cell count for the content (3 tiles -> 3 cells), never a row-spanning
   tall cell against shorter stacked siblings — that leaves dead space.
   ========================================================================== */
.gallery-bento {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--space-6);
}
.gallery-bento-second { margin-top: var(--space-6); }
.gallery-bento .placeholder-box:nth-child(1) { grid-column: 1 / 7; aspect-ratio: 21 / 9; }
/* 2026-08-16: a 4th tile (Fieldstone Studio) joined the original 3 — the row-1
   grid moved from "one full-width banner + 2 equal halves" to "banner + 3
   equal thirds" so the new tile gets its own cell instead of squeezing into
   a leftover 1-col auto-placement (which read far narrower than its
   siblings). Exact cell count for the content, per the rule above. */
.gallery-bento .placeholder-box:nth-child(2) { grid-column: 1 / 3; }
.gallery-bento .placeholder-box:nth-child(3) { grid-column: 3 / 5; }
.gallery-bento .placeholder-box:nth-child(4) { grid-column: 5 / 7; }
@media (max-width: 767px) {
  .gallery-bento { grid-template-columns: 1fr; }
  .gallery-bento .placeholder-box:nth-child(1),
  .gallery-bento .placeholder-box:nth-child(2),
  .gallery-bento .placeholder-box:nth-child(3),
  .gallery-bento .placeholder-box:nth-child(4) { grid-column: 1; aspect-ratio: 4 / 3; }
}

/* Second row: mobile-example tiles (CrumbHub's video walkthrough, and now
   Basecamp Fitness's app-shell capture). Two tiles centered as a pair
   (middle four of six columns) rather than the old single-tile centered
   placement — see the 2026-08-08 note this replaces for why a lone tile
   was centered at one-third width instead of left-anchored; the same
   "don't left-anchor a partial row" logic now applies to a pair. */
.gallery-bento-second .placeholder-box:nth-child(1) { grid-column: 2 / 4; }
.gallery-bento-second .placeholder-box:nth-child(2) { grid-column: 4 / 6; }
@media (max-width: 767px) {
  .gallery-bento-second .placeholder-box:nth-child(1),
  .gallery-bento-second .placeholder-box:nth-child(2) { grid-column: 1; }
}

/* A tile holding a real dropped-in <img> (not the abstract placeholder
   texture) keeps one photo-friendly banner ratio at every breakpoint, taking
   priority (via source order, matching specificity) over both the desktop
   21/9-on-tile-1 rule above and the mobile 4/3-on-every-tile rule above —
   the mobile 4/3 crop was designed for the empty placeholder texture and
   would badly over-crop a real wide screenshot on narrow viewports. */
.gallery-bento .placeholder-box:has(img) {
  aspect-ratio: 16 / 9;
}

/* Portrait mobile-viewport captures (586x836 — see README's "Video gallery
   tiles" section) are their own shape rather than the landscape ratios
   above (which would crop a portrait shot badly): CrumbHub's video-poster
   tile via [data-video], and any other phone-width screenshot tile (e.g.
   Basecamp Fitness's Mobile Examples tile, which has no video to key off)
   via the explicit .tile-portrait class instead. Higher specificity than
   both rules above via this extra selector. */
.gallery-bento .placeholder-box[data-video],
.gallery-bento .placeholder-box[data-video]:has(img),
.gallery-bento .placeholder-box.tile-portrait,
.gallery-bento .placeholder-box.tile-portrait:has(img) {
  aspect-ratio: 586 / 836;
}

/* Placeholder image tile — a textured photo mat framing whatever fills it.
   Hatch texture tuned light so it reads on the dark surface. To add a real
   photo, drop an image element inside a .placeholder-box (replacing the
   placeholder text); the rule below object-fit-covers it to fill the mat while
   the textured frame stays around it. */
.placeholder-box {
  position: relative;
  aspect-ratio: 4 / 3;
  padding: var(--space-2);
  background-color: var(--color-surface);
  background-image:
    repeating-linear-gradient(45deg, rgba(255, 255, 255, .035) 0 1px, transparent 1px 7px),
    repeating-linear-gradient(-45deg, rgba(255, 255, 255, .025) 0 1px, transparent 1px 7px);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: var(--color-text-muted);
  font-size: var(--fs-sm);
  overflow: hidden;
  cursor: pointer;
  transition: scale var(--dur-base) var(--ease-standard),
              box-shadow var(--dur-base) var(--ease-standard),
              border-color var(--dur-base) var(--ease-standard);
}
.placeholder-box:hover,
.placeholder-box:focus-visible {
  scale: 1.03;
  box-shadow: var(--shadow-lg);
  border-color: var(--color-border-strong);
  z-index: 2;
}
.placeholder-box img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius-md);
}

/* Video-example tile — grid stays a static poster/placeholder (never an
   autoplaying <video>); a play badge signals it opens a clip in the
   lightbox on click, matching this component's existing click-to-expand
   pattern. The lightbox itself is what plays the .mp4 — see js/main.js. */
.placeholder-box .play-badge {
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: 56px;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  background: var(--color-primary);
  box-shadow: var(--shadow-md);
  pointer-events: none;
}
.placeholder-box .play-badge svg { width: 20px; height: 20px; fill: var(--color-bg); margin-left: 3px; }

/* ==========================================================================
   CTA panel — raised, cyan-edged. Scoped to the CONTENT COLUMN (no more 100vw
   full-bleed, which would run under the fixed rail). (resources/components/banner/)
   ========================================================================== */
.banner-full-bleed {
  margin: var(--space-8) 0;
  padding: var(--space-8) var(--space-7);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-xl);
  box-shadow: var(--glow-signal);
  text-align: center;
}
.banner-full-bleed .container { max-width: 600px; padding: 0; }
.banner-full-bleed h2 { margin-bottom: var(--space-3); }
.banner-full-bleed p { color: var(--color-text-muted); margin-bottom: var(--space-6); }

.banner-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding: var(--space-3) var(--space-7);
  background: var(--color-bg-alt);
  border-bottom: 1px solid var(--color-border);
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
}

/* Confirmation banner — revealed on the homepage after a successful contact
   submit (js/main.js reads ?sent=1). Hidden by default via the [hidden] reset. */
.confirm-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-7);
  background: color-mix(in srgb, var(--color-signal) 16%, var(--color-bg-alt));
  border-bottom: 1px solid color-mix(in srgb, var(--color-signal-bright) 45%, var(--color-border));
  color: var(--color-text);
  font-size: var(--fs-base);
}
.confirm-banner p { margin: 0; color: var(--color-text); font-weight: var(--fw-medium); }
.confirm-banner-close {
  flex: none;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text-muted);
  font-size: 22px;
  line-height: 1;
  border-radius: var(--radius-sm);
  transition: color var(--dur-fast) var(--ease-standard);
}
.confirm-banner-close:hover { color: var(--color-text); }

/* ==========================================================================
   Forms (Contact)
   ========================================================================== */
.contact-form {
  margin-top: var(--space-6);
  padding: var(--space-6);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}
@media (min-width: 768px) { .contact-form { padding: var(--space-7); } }
.contact-form .btn { margin-top: var(--space-2); }

.form-field { margin-bottom: var(--space-5); display: flex; flex-direction: column; gap: var(--space-2); }
.form-field label { font-weight: var(--fw-semibold); color: var(--color-text); }
.form-field input,
.form-field textarea {
  font: inherit;
  padding: var(--space-3);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  min-height: 44px;
  background: var(--color-surface-2);
  color: var(--color-text);
  transition: border-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard);
}
.form-field input::placeholder,
.form-field textarea::placeholder { color: var(--color-text-muted); }
.form-field input:focus,
.form-field textarea:focus {
  outline: none;
  border-color: var(--color-signal-bright);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-signal-bright) 24%, transparent);
}
.form-field textarea { min-height: 140px; resize: vertical; }

.form-honeypot { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form-status:not(:empty) {
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border: 1px solid color-mix(in srgb, var(--color-signal-bright) 38%, var(--color-border));
  background: color-mix(in srgb, var(--color-signal) 12%, var(--color-bg-alt));
  border-radius: var(--radius-md);
  font-weight: var(--fw-semibold);
  color: var(--color-text);
  animation: fade-up var(--dur-base) var(--ease-out);
}
/* Error result (js adds .is-error) — red tint instead of the neutral cyan. */
.form-status.is-error {
  border-color: color-mix(in srgb, var(--color-danger) 60%, var(--color-border));
  background: color-mix(in srgb, var(--color-danger) 15%, var(--color-bg-alt));
}
@keyframes fade-up { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: translateY(0); } }

/* ==========================================================================
   Legal pages (resources/components/legal/)
   ========================================================================== */
.legal-page { padding: var(--space-8) 0; }
.legal-page .container { max-width: 720px; }
.legal-page h2 { margin-top: var(--space-8); font-size: var(--fs-xl); }
.legal-page p { color: var(--color-text-muted); }

/* ==========================================================================
   Footer (resources/components/footer/) — sits at the foot of the content column
   ========================================================================== */
.site-footer {
  border-top: 1px solid var(--color-border);
  padding: var(--space-7) 0;
  color: var(--color-text-muted);
  font-size: var(--fs-sm);
  text-align: center;
}
.site-footer .container { display: flex; flex-direction: column; align-items: center; gap: var(--space-4); }
.footer-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-1) var(--space-4);
}
.footer-links a { color: inherit; text-decoration: none; }
.footer-links a:hover, .footer-links a:focus-visible { color: var(--color-text); text-decoration: underline; }

/* ==========================================================================
   Social links (resources/components/social-links/)
   ========================================================================== */
.social-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
}

/* Honest note under the footer's social icons — they currently point to
   placeholder hrefs (see resources/components/social-links/), so say so
   plainly rather than let them read as real, active accounts. Shared caption
   styling lives on the combined selector with .gallery-disclaimer above. */
.social-disclaimer { margin: var(--space-2) 0 0; }

.social-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  color: var(--color-signal-bright);
  transition: scale var(--dur-fast) var(--ease-standard),
              background var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}
.social-btn:hover,
.social-btn:focus-visible {
  scale: 1.08;
  background: var(--color-signal);
  color: var(--color-on-signal);
  border-color: var(--color-signal);
}
.social-btn svg {
  width: 20px;
  height: 20px;
  fill: currentColor;
}
@media (prefers-reduced-motion: reduce) {
  .social-btn { transition: none; }
}

/* ==========================================================================
   FAQ accordion — adapted from resources/components/accordion/ (independent
   expand/collapse, each item opens on its own). Uses a capped max-height
   transition rather than the component's default grid-template-rows 0fr/1fr
   trick — verified in-browser that the grid+overflow:hidden combination
   didn't actually clip the panel content here (computed box was 16px but
   the paragraph still rendered in full), so max-height is the more reliable
   choice for this short, rarely-toggled content instead of chasing that
   further. Toggled by js/main.js (classList + aria-expanded only, CSP-safe,
   no inline styles).
   ========================================================================== */
.accordion { max-width: 760px; }
.accordion-item { border-bottom: 1px solid var(--color-border); }
.accordion-item:first-child { border-top: 1px solid var(--color-border); }
.accordion-trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 44px;
  padding: var(--space-4) var(--space-2);
  background: none;
  border: none;
  font: inherit;
  font-size: var(--fs-lg);
  font-weight: 600;
  color: var(--color-text);
  text-align: left;
  cursor: pointer;
}
.accordion-icon {
  flex-shrink: 0;
  width: 12px;
  height: 12px;
  border-right: 2px solid var(--color-text-muted);
  border-bottom: 2px solid var(--color-text-muted);
  transform: rotate(45deg);
  transition: transform 0.2s ease;
}
.accordion-trigger[aria-expanded="true"] .accordion-icon { transform: rotate(-135deg); }
.accordion-panel {
  overflow: hidden;
  max-height: 0;
  transition: max-height 0.25s ease;
}
.accordion-panel.is-open { max-height: 400px; }
.accordion-panel-inner {
  padding: 0 var(--space-2) var(--space-4);
  color: var(--color-text-muted);
}
.accordion-panel-inner p { margin: 0; }
@media (prefers-reduced-motion: reduce) {
  .accordion-icon,
  .accordion-panel { transition: none; }
}

/* ==========================================================================
   Lightbox — click a gallery tile to open the photo full-size (js/main.js,
   class-toggle only, CSP-safe).
   ========================================================================== */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 9997;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--space-6) var(--space-5);
}
.lightbox.is-open { display: flex; }
body.lightbox-open { overflow: hidden; }

.lightbox-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, .78);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.lightbox-dialog {
  position: relative;
  max-width: min(94vw, 1400px);
  max-height: 92vh;
  padding: var(--space-3);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  animation: lightbox-in var(--dur-base) var(--ease-out);
}
.lightbox-content { display: flex; }
.lightbox-content img,
.lightbox-content video {
  display: block;
  max-width: 100%;
  max-height: calc(92vh - 2 * var(--space-3));
  object-fit: contain;
  border-radius: var(--radius-md);
}
.lightbox-placeholder {
  margin: 0;
  padding: var(--space-8) var(--space-7);
  color: var(--color-text-muted);
  font-size: var(--fs-lg);
  text-align: center;
}
.lightbox-close {
  position: absolute;
  top: calc(-1 * var(--space-6));
  right: 0;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  background: var(--color-surface-2);
  color: var(--color-text);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow-md);
}
@keyframes lightbox-in { from { opacity: 0; scale: .96; } to { opacity: 1; scale: 1; } }

/* ==========================================================================
   Scroll reveals — class-toggle only, CSP-safe (resources/components/scroll-reveal/)
   ========================================================================== */
.reveals-ready .reveal {
  opacity: 0;
  translate: 0 20px;
  transition: opacity var(--dur-slow) var(--ease-out), translate var(--dur-slow) var(--ease-out);
}
.reveals-ready .reveal.is-visible { opacity: 1; translate: none; }
.reveals-ready .reveal:nth-child(2) { transition-delay: 80ms; }
.reveals-ready .reveal:nth-child(3) { transition-delay: 160ms; }

/* ==========================================================================
   Reduced motion — disable transitions/animations, keep content visible.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
  .reveals-ready .reveal { opacity: 1 !important; translate: none !important; }
  .stack-card { position: static !important; }
}
